diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-04-05 11:23:36 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-04-05 11:23:36 +0000 |
commit | 4318fbda2a7ee51caafdc4eb1f8028a3f0605142 (patch) | |
tree | cddb50a81d7d1a628cc400519162080c6d87868e | |
parent | 36ea5120664550fae6d31f1c6f695e4f8975cb06 (diff) |
o Merge with Linux 2.1.91.
o First round of bugfixes for the SC/MC CPUs.
o FPU context switch fixes.
o Lazy context switches.
o Faster syscalls.
o Removed dead code.
o Shitloads of other things I forgot ...
280 files changed, 31617 insertions, 13031 deletions
diff --git a/Documentation/Configure.help b/Documentation/Configure.help index eddc729b8..7380424fe 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -26,35 +26,36 @@ # compile it and much more is contained in the Kernel-HOWTO, available # via ftp (user: anonymous) from sunsite.unc.edu in the directory # /pub/Linux/docs/HOWTO. Before you start compiling, make sure that -# you have the necessary versions of all programs; they are listed -# in Documentation/Changes. +# you have the necessary versions of all programs and libraries +# required to compile and run this kernel; they are listed in the file +# Documentation/Changes. Make sure to read the toplevel kernel README +# file as well. # -# Format of this file: description<nl>variable<nl>helptext<nl><nl>. -# If the question being documented is of type "choice", we list only -# the first occurring config variable. The help texts must not contain +# Format of this file: description<nl>variable<nl>helptext<nl><nl>. If +# the question being documented is of type "choice", we list only the +# first occurring config variable. The help texts must not contain # empty lines. Order of the help texts does not matter, however, no # variable should be documented twice: if it is, only the first # occurrence will be used by Configure. It is not absolutely necessary # that the one-line descriptions of the variables used here are -# exactly the same as the ones in the corresponding Config.in -# scripts. The lines in a help text should be indented two -# positions. Lines starting with `#' are ignored. To be nice to -# menuconfig, limit your lines to 70 characters. Use emacs' kfill.el -# to edit this file or you lose. +# exactly the same as the ones in the corresponding Config.in scripts. +# The lines in a help text should be indented two positions. Lines +# starting with `#' are ignored. To be nice to menuconfig, limit your +# lines to 70 characters. Use emacs' kfill.el to edit and ispell.el to +# spell check this file or you lose. # # If you add a help text to this file, please try to be as gentle as # possible. Don't use unexplained acronyms and generally write for the # hypothetical ignorant but intelligent user who has just bought a PC, # removed Windows, installed Linux and is now recompiling the kernel -# for the first time. Tell them what to do if they're -# unsure. Technical information should go in a README in the -# Documentation directory. Mention all the relevant READMEs and HOWTOs -# in the help text. +# for the first time. Tell them what to do if they're unsure. Technical +# information should go in a README in the Documentation directory. +# Mention all the relevant READMEs and HOWTOs in the help text. # # All this was shamelessly stolen from several different sources. Many -# thanks to all the contributors. Feel free to use these help texts -# in your own kernel configuration tools. The texts are copyrighted -# (c) 1995-1997 by Axel Boldt and others and governed by the GNU +# thanks to all the contributors. Feel free to use these help texts in +# your own kernel configuration tools. The texts are copyrighted (c) +# 1995-1998 by Axel Boldt and many others and are governed by the GNU # Public License. Prompt for development and/or incomplete code/drivers @@ -132,7 +133,7 @@ CONFIG_BLK_DEV_RAM Saying Y here will allow you to use a portion of your RAM memory as a block device, so that you can make filesystems on it, read and write to it and do all the other things that you can do with normal - block devices (such as harddrives). It is usually used to load and + block devices (such as hard drives). It is usually used to load and store a copy of a minimal root file system off of a floppy into RAM during the initial install of Linux. Note that the kernel command line option "ramdisk=XX" is now obsolete. For details, read @@ -155,7 +156,7 @@ CONFIG_BLK_DEV_LOOP Saying Y here will allow you to mount a file as a file system. This is useful if you want to check an ISO9660 file system before burning the CD, or want to use floppy images without first writing them to - floppy. This option also allows one to mount a filesystem with + floppy. This option also allows you to mount a filesystem with encryption. To use these features, you need a recent version of mount (available via ftp (user: anonymous) from ftp.win.tue.nl/pub/linux/util/). Note that this loop device has @@ -182,35 +183,38 @@ CONFIG_BLK_DEV_IDE This will use the full-featured IDE driver to control up to four IDE interfaces, each being able to serve a "master" and a "slave" device, for a combination of up to eight IDE disk/cdrom/tape/floppy - drives. Useful information about large (>540MB) IDE disks, - soundcard IDE ports, module support, and other topics, is - contained in Documentation/ide.txt. If you have one or more IDE - drives, say Y here. If your system has no IDE drives, or if memory + drives. Useful information about large (>540MB) IDE disks, sound + card IDE ports, module support, and other topics, is contained in + Documentation/ide.txt. For detailed information about hard drives, + consult the Disk-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you have one or more IDE + drives, say Y here. If your system has no IDE drives, or if memory requirements are really tight, you could say N here, and select the - "Old harddisk driver" instead to save about 13kB of memory in the - kernel. To fine-tune IDE drive/interface parameters for improved + "Old hard disk driver" instead to save about 13kB of memory in the + kernel. To fine-tune IDE drive/interface parameters for improved performance, look for the hdparm package at sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/ -Old harddisk (MFM/RLL/IDE) driver +Old hard disk (MFM/RLL/IDE) driver CONFIG_BLK_DEV_HD_ONLY - There are two drivers for MFM/RLL/IDE disks. Most people use the + There are two drivers for MFM/RLL/IDE disks. Most people use the newer enhanced driver, but this old one is still around for two - reasons. Some older systems have strange timing problems and seem - to work only with the old driver (which itself does not work with - some newer systems). The other reason is that the old driver is - smaller, since it lacks the enhanced functionality of the new one. - This makes it a good choice for systems with very tight memory - restrictions, or for systems with only older MFM/RLL/ESDI drives. - Choosing the old driver can save 13kB or so of kernel memory. If - you are unsure, then just choose the Enhanced IDE/MFM/RLL driver - instead of this one. + reasons. Some older systems have strange timing problems and seem to + work only with the old driver (which itself does not work with some + newer systems). The other reason is that the old driver is smaller, + since it lacks the enhanced functionality of the new one. This makes + it a good choice for systems with very tight memory restrictions, or + for systems with only older MFM/RLL/ESDI drives. Choosing the old + driver can save 13kB or so of kernel memory. If you are unsure, then + just choose the Enhanced IDE/MFM/RLL driver instead of this one. For + more detailed information, read the Disk-HOWTO, available via ftp + (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Use old disk-only driver on primary interface CONFIG_BLK_DEV_HD_IDE There are two drivers for MFM/RLL/IDE disks. Most people use just the new enhanced driver by itself. This option however installs the - old harddisk driver to control the primary IDE/disk interface in the + old hard disk driver to control the primary IDE/disk interface in the system, leaving the new enhanced IDE driver take care of only the 2nd/3rd/4th IDE interfaces. Doing this will prevent you from having an IDE/ATAPI CDROM or tape drive connected to the primary IDE @@ -222,9 +226,9 @@ CONFIG_BLK_DEV_HD_IDE Include IDE/ATA-2 DISK support CONFIG_BLK_DEV_IDEDISK - This will include enhanced support for MFM/RLL/IDE harddisks. If you + This will include enhanced support for MFM/RLL/IDE hard disks. If you have a MFM/RLL/IDE disk, and there is no special reason to use the - old harddisk driver instead, say Y. If you want to compile this + old hard disk driver instead, say Y. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called ide-disk.o. Do @@ -240,11 +244,11 @@ CONFIG_BLK_DEV_IDECD NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI double(2X), quad(4X), and six(6X) speed drives. At boot time, the CDROM drive will be identified along with other IDE devices, as - "hdb" or "hdc", or something similar. If this is your only CDROM - drive, you can say N to all other CDROM options, but be sure to say - Y to "ISO9660 cdrom filesystem support". Read the CDROM-HOWTO, - available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file + "hdb" or "hdc", or something similar (check the boot messages with + dmesg). If this is your only CDROM drive, you can say N to all + other CDROM options, but be sure to say Y to "ISO9660 cdrom + filesystem support". Read the CDROM-HOWTO, available via ftp (user: + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file Documentation/cdrom/ide-cd. Note that older versions of lilo (the linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so install lilo-16 or higher, available from @@ -260,11 +264,12 @@ CONFIG_BLK_DEV_IDETAPE to the SCSI protocol. At boot time, the tape drive will be identified along with other IDE devices, as "hdb" or "hdc", or something similar, and will be mapped to a character device such as - "ht0". Be sure to consult the drivers/block/ide-tape.c and - Documentation/ide.txt files for usage information. If you want to - compile the driver as a module ( = code which can be inserted in and - removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt. The module will be called ide-tape.o. + "ht0" (check the boot messages with dmesg). Be sure to consult the + drivers/block/ide-tape.c and Documentation/ide.txt files for usage + information. If you want to compile the driver as a module ( = code + which can be inserted in and removed from the running kernel + whenever you want), say M here and read + Documentation/modules.txt. The module will be called ide-tape.o. Include IDE/ATAPI FLOPPY support CONFIG_BLK_DEV_IDEFLOPPY @@ -274,11 +279,11 @@ CONFIG_BLK_DEV_IDEFLOPPY and the ATAPI ZIP (ATAPI PD-CD/CDR drives are not supported by this driver; support for PD-CD/CDR drives is available through the SCSI emulation). At boot time, the FLOPPY drive will be identified along - with other IDE devices, as "hdb" or "hdc", or something similar. If - you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ide-floppy.o. + with other IDE devices, as "hdb" or "hdc", or something similar + (check the boot messages with dmesg). If you want to compile the + driver as a module ( = code which can be inserted in and removed + from the running kernel whenever you want), say M here and read + Documentation/modules.txt. The module will be called ide-floppy.o. SCSI emulation support CONFIG_BLK_DEV_IDESCSI @@ -293,7 +298,7 @@ CONFIG_BLK_DEV_IDESCSI CMD640 chipset bugfix/support CONFIG_BLK_DEV_CMD640 - The CMD-Technologies CMD640 chip is used on many common 486 and + The CMD-Technologies CMD640 IDE chip is used on many common 486 and Pentium motherboards, usually in combination with a "Neptune" or "SiS" chipset. Unfortunately, it has a number of rather nasty design flaws that can cause severe data corruption under many common @@ -303,9 +308,9 @@ CONFIG_BLK_DEV_CMD640 systems. This driver will work automatically in PCI based systems (most new systems have PCI slots). But if your system uses VESA local bus (VLB) instead of PCI, you must also supply a kernel boot - parameter to enable the CMD640 bugfix/support: "ide0=cmd640_vlb" The - CMD640 chip is also used on add-in cards by Acculogic, and on the - "CSA-6400E PCI to IDE controller" that some people have. For + parameter to enable the CMD640 bugfix/support: "ide0=cmd640_vlb". + The CMD640 chip is also used on add-in cards by Acculogic, and on + the "CSA-6400E PCI to IDE controller" that some people have. For details, read Documentation/ide.txt. If unsure, say Y. CMD640 enhanced support @@ -335,15 +340,17 @@ CONFIG_BLK_DEV_IDEPCI Generic PCI bus-master DMA support CONFIG_BLK_DEV_IDEDMA - If your PCI IDE controller is capable of bus-master DMA - (Direct Memory Access) transfers (most newer systems are), - then you will want to say Y here to reduce CPU overhead. - With this option, Linux will automatically enable DMA transfers - in most cases, noting this with "DMA" appended to the drive - identification info. You can also use the "hdparm" utility to - enable DMA for drives which were not enabled automatically. - You can get the latest version of the hdparm utility via anonymous - FTP from sunsite.unc.edu/pub/Linux/system/hardware/ + If your PCI system uses IDE drive(s) (as opposed to SCSI, say) and + is capable of bus-master DMA operation (most Pentium PCI systems), + you will want to say Y here to reduce CPU overhead. With this + option, Linux will automatically enable DMA transfers in most cases, + noting this with "DMA" appended to the drive identification info. + You can also use the "hdparm" utility to enable DMA for drives which + were not enabled automatically. You can get the latest version of + the hdparm utility via anonymous FTP from + sunsite.unc.edu/pub/Linux/system/hardware/. Read the comments at the + beginning of drivers/block/idedma.c and the file + Documentation/ide.txt for more information. It is safe to say Y to this question. Other IDE chipset support @@ -427,7 +434,7 @@ CONFIG_BLK_DEV_ALI14XX I/O speeds to be set as well. See the Documentation/ide.txt and ali14xx.c files for more info. -XT harddisk support +XT hard disk support CONFIG_BLK_DEV_XD Very old 8 bit hard disk controllers used in the IBM XT computer. To include a driver for these, say Y. If you want to compile the driver @@ -438,22 +445,25 @@ CONFIG_BLK_DEV_XD Parallel port IDE device support CONFIG_PARIDE - There are many external CD-ROM and disk devices that connect - through your computer's parallel port. Most of them are actually - IDE devices using a parallel port IDE adapter. This option enables - the PARIDE subsystem which contains drivers for many of these - external drives. Read linux/Documentation/paride.txt for more - information. If you have enabled the parallel port support general - configuration option, you may share a single port between your - printer and other parallel port devices. Answer Y to build PARIDE - support into your kernel, or M if you would like to build it as a - loadable module. If your parallel port support is in a loadable - module, you must build PARIDE as a module. If you built PARIDE - support into your kernel, you may still build the individual - protocol modules and high-level drivers as loadable modules. To - use the PARIDE support, you must have this module as well as at - least one protocol module and one high-level driver. If you build - this support as a module, it will be called paride.o. + There are many external CD-ROM and disk devices that connect through + your computer's parallel port. Most of them are actually IDE devices + using a parallel port IDE adapter. This option enables the PARIDE + subsystem which contains drivers for many of these external drives. + Read linux/Documentation/paride.txt for more information. If you + have said Y to the "Parallel-port support" configuration option, you + may share a single port between your printer and other parallel port + devices. Answer Y to build PARIDE support into your kernel, or M if + you would like to build it as a loadable module. If your parallel + port support is in a loadable module, you must build PARIDE as a + module. If you built PARIDE support into your kernel, you may still + build the individual protocol modules and high-level drivers as + loadable modules. To use the PARIDE support, you must say Y or M + here and also to at least one high-level driver (e.g. "Parallel port + IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI + disks" etc.) and to at least one protocol driver (e.g. "ATEN EH-100 + protocol", "MicroSolutions backpack protocol", "DataStor Commuter + protocol" etc.). If you build this support as a module, it will be + called paride.o. Parallel port IDE disks CONFIG_PARIDE_PD @@ -465,22 +475,25 @@ CONFIG_PARIDE_PD must also have at least one parallel port protocol driver in your system. Among the devices supported by this driver are the SyQuest EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack - hardrives from MicroSolutions. + hard drives from MicroSolutions. Parallel port ATAPI CD-ROMs CONFIG_PARIDE_PCD This option enables the high-level driver for ATAPI CD-ROM devices - connected through a parallel port. If you chose to build PARIDE + connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the - parallel port ATAPI CD-ROM driver, otherwise you should answer M - to build it as a loadable module. The module will be called pcd.o. - You must also have at least one parallel port protocol driver in - your system. Among the devices supported by this driver are the - MicroSolutions backpack CD-ROM drives and the Freecom Power CD. + parallel port ATAPI CD-ROM driver, otherwise you should answer M to + build it as a loadable module. The module will be called pcd.o. You + must also have at least one parallel port protocol driver in your + system. Among the devices supported by this driver are the + MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If + you have such a CD-ROM drive, you should also say Y to "ISO9660 + cdrom filesystem support" below, because that's the filesystem used + on CDROMs. Parallel port ATAPI disks CONFIG_PARIDE_PF - This option enable the high-level driver for ATAPI disk devices + This option enables the high-level driver for ATAPI disk devices connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the parallel port ATAPI disk driver, otherwise you should answer M @@ -492,7 +505,7 @@ CONFIG_PARIDE_PF Parallel port ATAPI tapes CONFIG_PARIDE_PT - This option enable the high-level driver for ATAPI tape devices + This option enables the high-level driver for ATAPI tape devices connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the parallel port ATAPI disk driver, otherwise you should answer M @@ -584,7 +597,7 @@ CONFIG_PARIDE_KBIC OnSpec 90c20 protocol CONFIG_PARIDE_ON20 This option enables support for the (obsolete) 90c20 parallel port - IDE protocol from OnSpec (often marketted under the ValuStore brand + IDE protocol from OnSpec (often marketed under the ValuStore brand name). If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable module. The module will @@ -594,7 +607,7 @@ CONFIG_PARIDE_ON20 OnSpec 90c26 protocol CONFIG_PARIDE_ON26 This option enables support for the 90c26 parallel port IDE protocol - from OnSpec Electronics (often marketted under the ValuStore brand + from OnSpec Electronics (often marketed under the ValuStore brand name). If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable module. The module will @@ -603,17 +616,18 @@ CONFIG_PARIDE_ON26 Multiple devices driver support CONFIG_BLK_DEV_MD - This driver lets you combine several harddisk partitions into one - logical block device. Information about how and why to use it and the - necessary tools are available over ftp (user: anonymous) from - sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package - and the md-FAQ. Please read drivers/block/README.md. If unsure, say - N. + This driver lets you combine several hard disk partitions into one + logical block device. Information about how and why to use it and + the necessary tools are available over ftp (user: anonymous) from + sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package and the + md-FAQ. Please read drivers/block/README.md and the relevant section + of the Disk-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If unsure, say N. Linear (append) mode CONFIG_MD_LINEAR If you say Y here, then your multiple devices driver will be able to - use the so-called linear mode, i.e. it will combine the harddisk + use the so-called linear mode, i.e. it will combine the hard disk partitions by simply appending one to the other. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and @@ -623,7 +637,7 @@ CONFIG_MD_LINEAR RAID-0 (striping) mode CONFIG_MD_STRIPED If you say Y here, then your multiple devices driver will be able to - use the so-called raid0 mode, i.e. it will combine the harddisk + use the so-called raid0 mode, i.e. it will combine the hard disk partitions into one logical device in such a fashion as to fill them up evenly, one chunk here and one chunk there. This will increase the throughput rate if the partitions reside on distinct disks. If @@ -736,60 +750,64 @@ CONFIG_BINFMT_IRIX Networking support CONFIG_NET - Unless you really know what you are doing, you should say Y - here. The reason is that some programs need kernel networking - support even if you configure a stand-alone machine that won't be - connected to any other computer. If you are upgrading from an older - kernel, you should consider updating your networking tools too - because changes in the kernel and the tools often go hand in hand; - see http://www.inka.de/sites/lina/linux/NetTools/index_en.html for - details. + Unless you really know what you are doing, you should say Y here. + The reason is that some programs need kernel networking support even + when running on a stand-alone machine that isn't connected to any + other computer. If you are upgrading from an older kernel, you + should consider updating your networking tools too because changes + in the kernel and the tools often go hand in hand. The tools are + contained in the package net-tools, the location and version number + of which is given in Documentation/Changes. Fast switching (read help!) CONFIG_NET_FASTROUTE - Enable direct NIC-to-NIC data transfers. - *** This option is NOT COMPATIBLE with several important *** - *** networking options: especially CONFIG*FIREWALL. *** + Enables direct NIC-to-NIC data transfers, which is fast. + *** This option is NOT COMPATIBLE with several important *** + *** networking options: especially CONFIG*FIREWALL. *** However, it will work with all options in CONFIG_IP_ADVANCED_ROUTER - section (except for CONFIG_IP_ROUTE_TOS). At the moment few of devices - supports it (tulip is one of them, modified 8390 can be found at - ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). - Remember, short cuts make long delays :-), say N. + section (except for CONFIG_IP_ROUTE_TOS). At the moment, few devices + support fast switching (tulip is one of them, modified 8390 can be + found at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). If + unsure, say N. Forwarding between high speed interfaces CONFIG_NET_HW_FLOWCONTROL This option enables NIC hardware throttling during periods of - extremal congestion. At the moment only couple of device drivers - support it (really, one --- tulip, modified 8390 can be found - at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). - Really, this option is applicable to any machine attached - to enough fast network, and even 10Mb NIC - is able to kill not very slow box, sort of 120MHz Pentium. - However, do not enable this option, if you did not experienced + extremal congestion. At the moment only a couple of device drivers + support it (really only one ---tulip, modified 8390 can be found at + ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). Really, this + option is applicable to any machine attached to a fast enough + network, and even a 10Mb NIC is able to kill a not very slow box, + such as a 120MHz Pentium. + However, do not enable this option, if you did not experience any serious problems. Network aliasing CONFIG_NET_ALIAS This will allow you to set multiple network addresses on the same low-level network device driver. Typically used for services that - act differently based on the address they listen on - (e.g. "multihosting" or "virtual domains" on the web server apache - and the ftp server wuftpd) or for connecting to different logical - networks through the same physical interface (most commonly an - ethernet networking card). This is the generic part, later when - configuring network protocol options you will be asked for - protocol-specific aliasing support, and you will have to say Y to at - least one of them. See Documentation/networking/alias.txt for more - info. If you need this feature (for any protocol, like IP) say Y; - if unsure, say N. + act differently based on the address they listen on (e.g. + "multihosting" or "virtual domains" or "virtual hosting services" on + the web server apache and the ftp server wuftpd -- read the + Virtual-Services-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO) or for connecting to + different logical networks through the same physical interface (most + commonly an Ethernet networking card). This is the generic part, + later when configuring network protocol options you will be asked + for protocol-specific aliasing support, and you will have to say Y + to at least one of them, most likely "IP: aliasing support". See + Documentation/networking/alias.txt for more info. If you need this + feature (for any protocol, like IP) say Y; if unsure, say N. Socket filtering CONFIG_FILTER The Linux Socket Filter is derived from the Berkeley Packet Filter. - Through Socket Filtering you can have the kernel decide whether the - data is good and to continue processing it. Linux Socket Filtering - works on all socket types except TCP for now. See the text file - linux/Documentation/networking/filter.txt for more information. + If you say Y here, user-space programs can attach a filter onto any + socket and thereby tell the kernel that it should allow or disallow + certain types of data to get through the socket. Linux Socket + Filtering works on all socket types except TCP for now. See the text + file linux/Documentation/networking/filter.txt for more information. + If unsure, say N. Network firewalls CONFIG_FIREWALL @@ -817,7 +835,7 @@ CONFIG_SYN_COOKIES Normal TCP/IP networking is open to an attack known as "SYN flooding". This denial-of-service attack prevents legitimate remote users from being able to connect to your computer and requires very little work - from the attacker, who can operate from anywhere on the internet. + from the attacker, who can operate from anywhere on the Internet. SYN cookies provide protection against this type of attack. With this option turned on, the TCP/IP stack will use a cryptographic challenge protocol known as SYN cookies to enable legitimate users @@ -842,10 +860,12 @@ Alpha system type CONFIG_ALPHA_AVANTI Find out what type of Alpha motherboard you have. You will probably want to read the Linux/Alpha homepage on the WWW at - http://www.azstarnet.com/~axplinux/ (To browse the WWW, you need to + http://www.azstarnet.com/~axplinux/ (to browse the WWW, you need to have access to a machine on the Internet that has a program like - lynx or netscape). For this question, it suffices to give a unique - prefix of the option you want to choose. The choices: + lynx or netscape) and also the Alpha-HOWTO, available via ftp (user: + anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. For this + question, it suffices to give a unique prefix of the option you want + to choose. The choices: ** Avanti: This is for Mustang (AS200), M3 (AS250), Avanti (AS400) AlphaStations. These usually come with a TGA graphics adapter, so you'll want to say Y to "TGA Console support", below, if you @@ -915,11 +935,13 @@ CONFIG_SERIAL_EXTENDED Support more than 4 serial ports CONFIG_SERIAL_MANY_PORTS Enable this option if you have dumb serial boards other than the - four standard COM 1/2/3/4 ports. This may happen if you have an AST - FourPort, Accent Async, Boca, or other custom serial port hardware - which acts similar to standard serial port hardware. If you only - use the standard COM 1/2/3/4 ports, you can say N here to save some - memory. + four standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini), or other custom serial + port hardware which acts similar to standard serial port hardware. + If you only use the standard COM 1/2/3/4 ports, you can say N here + to save some memory. Support for sharing serial interrupts CONFIG_SERIAL_SHARE_IRQ @@ -1006,6 +1028,12 @@ CONFIG_PCI_OPTIMIZE Y if you think it might help, but try turning it off if you experience any problems with the PCI bus. N is the safe answer. +Backward-compatible /proc/pci +CONFIG_PCI_OLD_PROC + If you say Y here and to the "/proc filesystem support" below, you + will get a directory /proc/pci with information about your PCI + hardware. If unsure, say Y. + MCA support CONFIG_MCA MicroChannel Architecture is found in some IBM PS/2 machines and @@ -1030,25 +1058,38 @@ CONFIG_SYSVIPC or with the program info ("man info"). Saying Y here enlarges your kernel by about 7kB. Just say Y. +BSD Process Accounting +CONFIG_BSD_PROCESS_ACCT + If you say Y here, a user level program will be able to instruct the + kernel (via a special system call) to write process accounting + information to a file: whenever a process exits, information about + that process will be appended to the file by the kernel. The + information includes things such as creation time, owning user, + command name, memory usage, controlling terminal etc. (the complete + list is in the struct acct in include/linux/acct.h). It is up to the + user level program to do useful things with this information. This + is generally a good idea, so say Y. + Sysctl support CONFIG_SYSCTL - The sysctl interface provides a means of dynamically changing certain - kernel parameters and variables on the fly without requiring a - recompile of the kernel or reboot of the system. The primary interface - consists of a system call, but if the /proc filesystem is enabled, a - tree of modifiable sysctl entries will be generated beneath the - /proc/sys directory. Note that enabling this option will enlarge the - kernel by at least 8kB. As it is generally a good thing, you probably - want to say Y here unless building a kernel for install/rescue disks - or your system is very limited in memory. + The sysctl interface provides a means of dynamically changing + certain kernel parameters and variables on the fly without requiring + a recompile of the kernel or reboot of the system. The primary + interface consists of a system call, but if the /proc filesystem is + enabled, a tree of modifiable sysctl entries will be generated + beneath the /proc/sys directory. They are explained in the files in + Documentation/sysctl/. Note that enabling this option will enlarge + the kernel by at least 8kB. As it is generally a good thing, you + probably want to say Y here unless building a kernel for + install/rescue disks or your system is very limited in memory. Kernel support for ELF binaries CONFIG_BINFMT_ELF ELF (Executable and Linkable Format) is a format for libraries and executables used across different architectures and operating systems. This option will enable your kernel to run ELF binaries and - enlarge it by about 2kB. ELF support under Linux is quickly - replacing the traditional Linux a.out formats (QMAGIC and ZMAGIC) + enlarge it by about 2kB. ELF support under Linux has now all but + replaced the traditional Linux a.out formats (QMAGIC and ZMAGIC) because it is portable (this does *not* mean that you will be able to run executables from different architectures or operating systems!) and makes building run-time libraries very easy. Many new @@ -1115,9 +1156,9 @@ CONFIG_BINFMT_JAVA will be called binfmt_java.o. The complete functionality of this Java support is also provided by the more general option "Kernel support for MISC binaries", - below. This option is therefore considered obsolete and you probably - want to say N here and Y to "Kernel support for MISC binaries" if - you're interested in Java. + below. This option is therefore considered obsolete and you should + say N here and Y to "Kernel support for MISC binaries" if you're + interested in transparently executing Java programs. Kernel support for Linux/Intel ELF binaries CONFIG_BINFMT_EM86 @@ -1140,11 +1181,13 @@ CONFIG_BINFMT_MISC (CONFIG_BINFMT_JAVA) or "Kernel support for Linux/Intel ELF binaries" (CONFIG_BINFMT_EM86), as this is a more general solution. You can do other nice things, too. Read - Documentation/binfmt_misc.txt to learn how to use this feature. + Documentation/binfmt_misc.txt to learn how to use this feature, and + Documentation/java.txt for information about how to include Java + support. You must enable the "proc filesystem support" (CONFIG_PROC_FS) to use this part of the kernel. You may answer M for module support and later load the module when - you have use for it. + you have use for it; the module is called binfmt_misc.o. If you don't know what to answer at this point, say Y. Solaris binary emulation @@ -1166,10 +1209,21 @@ CONFIG_M386 (=586) and Pentium Pro (=686). In rare cases, it can make sense to specify "Pentium" even if running on a 486: the kernel will be smaller but slower. - If you have a multiple processor machine and want Linux to use all - the processors in parallel, set the SMP variable in the toplevel - kernel Makefile. - If you don't know what to do, say "386". + If you have a single processor machine, make sure that the line + "SMP=1" at the top of the toplevel kernel Makefile is commented out; + if you have a multi processor machine and want Linux to use all the + processors in parallel (Symmetric Multi Processing), make sure that + the line "SMP=1" is not commented out and read Documentation/smp and + Documentation/IO-APIC.txt and the SMP-FAQ on the WWW at + http://www.irisa.fr/prive/mentre/smp-faq/ (to browse the WWW, you + need to have access to a machine on the Internet that has a programs + like lynx or netscape). People using multiprocessor machines should + also say Y to "Enhanced Real Time Clock Support", below. + If you want to compile a kernel that should work on both single + processor and multi processor machines, it is possible to set + SMP=1. The "Advance Power Management" code (see configuration option + below) will not work in that scenario, though. + If you don't know what to do, choose "386". Video mode selection support CONFIG_VIDEO_SELECT @@ -1189,9 +1243,11 @@ CONFIG_VIDEO_SELECT Parallel-port support CONFIG_PARPORT If you want to use devices connected to your parallel port (the - connector at the computers with 25 holes), e.g. printer, Zip drive, - PLIP link etc., then you need to enable this option; please read - Documentation/parport.txt and drivers/misc/BUGS-parport. For + connector at the computer with 25 holes), e.g. printer, Zip drive, + PLIP link (Parallel Line Internet Protocol is mainly used to create + a mini network by connecting the parallel ports of two local + machines) etc., then you need to say Y here; please read + Documentation/parport.txt and drivers/misc/BUGS-parport. For extensive information about drivers for many devices attaching to the parallel port see http://www.torque.net/linux-pp.html on the WWW (To browse the WWW, you need to have access to a machine on the @@ -1201,18 +1257,26 @@ CONFIG_PARPORT want to compile parallel port support as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module - will be called parport.o. If you have more than one parallel port - and want to specify which port and IRQ to use by this driver at + will be called parport.o. If you have more than one parallel port + and want to specify which port and IRQ to be used by this driver at module load time, read Documentation/networking/net-modules.txt. + If unsure, say Y. PC-style hardware CONFIG_PARPORT_PC - You should enable this option if you have a PC-style parallel - port. All IBM PC compatible computers and some Alphas have PC-style - parallel ports. This code is also available as a module. If you - want to it as a module ( = code which can be inserted in and removed - from the running kernel whenever you want), say M here and read + You should say Y here if you have a PC-style parallel port. All IBM + PC compatible computers and some Alphas have PC-style parallel + ports. This code is also available as a module. If you want to it as + a module ( = code which can be inserted in and removed from the + running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called parport_pc.o. + If unsure, say Y. + +Support foreign hardware +CONFIG_PARPORT_OTHER + Say Y here if you want to be able to load driver modules to support + other non-standard types of parallel ports. This causes a + performance loss, so most people say N. Sun Ultra/AX-style hardware CONFIG_PARPORT_AX @@ -1220,25 +1284,6 @@ CONFIG_PARPORT_AX Ultra/AX machines. This code is also available as a module (say M), called parport_ax.o. If in doubt, saying N is the safe plan. -Foreign parallel hardware -CONFIG_PARPORT_OTHER - Say Y here if you want to be able to load driver modules to support - other types of parallel port. This causes a performance loss, so most - people say N. - -Compile the kernel into the ELF object format -CONFIG_ELF_KERNEL - ELF (Executable and Linkable Format) is a format for libraries and - executables used across different architectures and operating - systems. This option will cause the resulting kernel to be in ELF - format, which is generally desirable, so say Y. However, it only - works if your compiler and linker can produce ELF code. - -Is your ELF compiler an extra compiler -CONFIG_EXTRA_ELF_COMPILER - If you have a linuxelf-gcc as opposed to linux-gcc, say Y, otherwise - N. - Generate little endian code CONFIG_CPU_LITTLE_ENDIAN If your compiler is mipsel-linux-gcc or mipsel-linuxelf-gcc (as @@ -1260,7 +1305,7 @@ CONFIG_PNP Auto-probe for parallel devices CONFIG_PNP_PARPORT - Some IEEE-1284 conformant parallel-port devices can identify + Some IEEE-1284 conforming parallel-port devices can identify themselves when requested. Say Y to enable this feature, or M to compile it as a module (parport_ieee1284.o). If in doubt, say N. @@ -1304,10 +1349,10 @@ CONFIG_PNP_ISA_COMPAT PnP Legacy device support CONFIG_PNP_LEGACY Before PnP ISA was standardized, several "jumperless", or - "soft-configurable" boards were finding there way onto the market. + "soft-configurable" boards were finding their way onto the market. These cards used somewhat proprietary mechanisms for configuring IRQs, DMAs, IO addresses, and memory ranges. These devices (mainly - network cards, but also some sound card) can be configured as any + network cards, but also some sound cards) can be configured as any other PnP device can by saying Y here, if appropriate drivers for these devices are available. @@ -1325,20 +1370,21 @@ CONFIG_PNP_SYSCTL PnP auto-configures all devices on startup CONFIG_PNP_BOOTINIT This option will allow the PnP subsystem to automatically configure - all the PnP devices it finds upon system startup (or at least - attempt to). This is useful if you have older driver which do not use + all the PnP devices it finds upon system startup (or at least + attempt to). This is useful if you have older drivers which do not use the Linux-PnP system to configure PnP devices, and which you need - configured by PnP in order to use. + to be configured by PnP before you can use them. Enable loadable module support CONFIG_MODULES - Kernel modules are small pieces of compiled code which can be - inserted in or removed from the running kernel, using the - programs insmod and rmmod. This is described in the file - Documentation/modules.txt. Modules can be device drivers, file - systems, binary executable formats, and so on. If you think that - you may want to make use of modules with this kernel in the future, - then say Y here. If unsure, say Y. + Kernel modules are small pieces of compiled code which can be + inserted in or removed from the running kernel, using the programs + insmod and rmmod. This is described in the file + Documentation/modules.txt, including the fact that you have to say + "make modules" in order to compile the modules. Modules can be + device drivers, file systems, binary executable formats, and so + on. If you think that you may want to make use of modules with this + kernel in the future, then say Y here. If unsure, say Y. Set version information on all symbols for modules CONFIG_MODVERSIONS @@ -1356,10 +1402,14 @@ CONFIG_MODVERSIONS Kernel module loader support CONFIG_KMOD - This feature allows the kernel to load modules for itself. When - a part of the kernel needs a module, it runs modprobe with the - appropriate arguments. Say Y here and read about configuring it - in Documentation/kmod.txt. (this is a replacement of kerneld) + Normally when you have selected some drivers and/or filesystems to + be created as loadable modules, you also have the responsibility to + load the corresponding modules (using the programs insmod or + modprobe) before you can use them. If you say Y here however, the + kernel will be able to load modules for itself. When a part of the + kernel needs a module, it runs modprobe with the appropriate + arguments. (This is a replacement for kerneld.) Say Y here and read + about configuring it in Documentation/kmod.txt. ARP daemon support (EXPERIMENTAL) CONFIG_ARPD @@ -1399,7 +1449,7 @@ CONFIG_IP_MULTICAST This is code for addressing several networked computers at once, enlarging your kernel by about 2 kB. You need multicasting if you intend to participate in the MBONE, a high bandwidth network on top - of the internet which carries audio and video broadcasts. More + of the Internet which carries audio and video broadcasts. More information about the MBONE is on the WWW at http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW, you need to have access to a machine on the Internet that has a @@ -1456,7 +1506,7 @@ CONFIG_IP_ROUTE_VERBOSE verbose messages regarding the routing, for example warnings about received packets which look strange and could be evidence of an attack or a misconfigured system somewhere. The information is - handled by the klogd demon which is responsible for kernel messages + handled by the klogd daemon which is responsible for kernel messages ("man klogd"). IP: large routing tables @@ -1492,10 +1542,17 @@ CONFIG_IP_FIREWALL sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will need the ipfwadm tool (available via ftp (user: anonymous) from ftp.xos.nl/pub/linux/ipfwadm/) to allow selective blocking of - internet traffic based on type, origin and destination. The - firewalling code will only work if you say Y to "/proc filesystem - support" below and IP forwarding is enabled in your kernel; do this - from within a boot-time script like so: + Internet traffic based on type, origin and destination; this type of + firewall is called a "packet filter". The other type of firewall, + "proxy-based" ones, is more secure but more intrusive and more + bothersome to set up; it inspects the network traffic much more + closely and has knowledge about the higher level protocols, which + packet filters lack. Proxy-based firewalls don't need support by the + kernel, but they are often combined with a packet filter, which only + works if you say Y here. + The firewalling code will only work if you say Y to "/proc + filesystem support" below and IP forwarding is enabled in your + kernel; do this from within a boot-time script like so: echo "1" > /proc/sys/net/ipv4/ip_forwarding after the /proc filesystem has been mounted. You need to say Y to "IP firewalling" in order to be able to use IP @@ -1526,7 +1583,7 @@ CONFIG_IP_ACCT This keeps track of your IP network traffic and produces some statistics. Usually, you only want to say Y here if your box will be a router or a firewall for some local network. For the latter, you - need to say Y to IP firewalling. The data is accessible with "cat + need to say Y to "IP firewalling". The data is accessible with "cat /proc/net/ip_acct", so you want to say Y to the /proc filesystem below, if you say Y here. To specify what exactly should be recorded, you need the tool ipfwadm (available via ftp (user: @@ -1551,9 +1608,9 @@ CONFIG_IP_PNP_BOOTP say Y here. In case the boot ROM of your network card was designed for booting Linux and does BOOTP itself, providing all necessary information on the kernel command line, you can say N here. If - unsure, say Y. Note that in case you want to use BOOTP, a BOOTP - server must be operating on your network. Read - Documentation/nfsroot.txt for details. + unsure, say Y. Note that if you want to use BOOTP, a BOOTP server + must be operating on your network. Read Documentation/nfsroot.txt + for details. RARP support CONFIG_IP_PNP_RARP @@ -1561,8 +1618,8 @@ CONFIG_IP_PNP_RARP some other computer over the net via NFS and you want the IP address of your computer to be discovered automatically at boot time using the RARP protocol (an older protocol which is being obsoleted by - BOOTP and DHCP), say Y here. Note that in case you want to use RARP, - a RARP server must be operating on your network. Read + BOOTP and DHCP), say Y here. Note that if you want to use RARP, a + RARP server must be operating on your network. Read Documentation/nfsroot.txt for details. IP: tunneling @@ -1588,23 +1645,23 @@ CONFIG_NET_IPGRE another protocol and sending it over a channel that understands the encapsulating protocol. This particular tunneling driver implements GRE (Generic Routing Encapsulation) and at this time allows - encapsulating of IPv4 or IPv6 over existing IPv4 - infrastructure. This driver is useful if the other endpoint is a - Cisco router: Cisco likes GRE much better than the other Linux - tunneling driver ("IP: tunneling" above). In addition, GRE allows - multicast redistribution through the tunnel. + encapsulating of IPv4 or IPv6 over existing IPv4 infrastructure. + This driver is useful if the other endpoint is a Cisco router: Cisco + likes GRE much better than the other Linux tunneling driver ("IP: + tunneling" above). In addition, GRE allows multicast redistribution + through the tunnel. IP: broadcast GRE over IP CONFIG_NET_IPGRE_BROADCAST One application of GRE/IP is to construct a broadcast WAN (Wide Area - Network), which looks like a normal1 ethernet LAN (Local Area + Network), which looks like a normal Ethernet LAN (Local Area Network), but can be distributed all over the Internet. If you want to do that, say Y here and to "IP: multicast routing" below. IP: firewall packet logging CONFIG_IP_FIREWALL_VERBOSE This gives you information about what your firewall did with - packets it received. The information is handled by the klogd demon + packets it received. The information is handled by the klogd daemon which is responsible for kernel messages ("man klogd"). IP: transparent proxying @@ -1630,14 +1687,19 @@ CONFIG_IP_MASQUERADE local net are completely invisible to the outside world, even though they can reach the outside and can be reached. This makes it possible to have the computers on the local network participate on - the internet even if they don't have officially registered IP - addresses. (This last problem can also be solved by connecting the + the Internet even if they don't have officially registered IP + addresses. (This last problem can also be solved by connecting the Linux box to the Internet using SLiRP [SLiRP is a SLIP/PPP emulator that works if you have a regular dial up shell account on some UNIX computer; get it via ftp (user: anonymous) from - ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].) Details - on how to set things up are contained in the IP Masquerading FAQ, - available at http://www.indyramp.com/masq/. If you say Y here, then + ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].) The IP + masquerading code will only work if you say Y to "/proc filesystem + support" below and IP forwarding is enabled in your kernel; you can + do this from within a boot-time script like so: echo "1" > + /proc/sys/net/ipv4/ip_forwarding after the /proc filesystem has been + mounted. Details on how to set things up are contained in the IP + Masquerade mini-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you say Y here, then the modules ip_masq_ftp.o (for ftp transfers through the firewall), ip_masq_irc.o (for irc chats through the firewall), and ip_masq_raudio.o (for realaudio downloads through the firewall) will @@ -1708,25 +1770,28 @@ CONFIG_IP_ALWAYS_DEFRAG IP: aliasing support CONFIG_IP_ALIAS Sometimes it is useful to give several IP addresses to a single - physical network interface (= serial port or ethernet card). The + physical network interface (= serial port or Ethernet card). The most common case is that you want to serve different WWW or ftp documents to the outside according to which of your host names was used to connect to you. This is called "multihosting" or "virtual - domains" and is explained in detail on the WWW at - http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you - need to have access to a machine on the Internet that has a program - like lynx or netscape). Another scenario would be that there are two - logical networks living on your local ethernet and you want to - access them both with the same ethernet card. The configuration of - these alias addresses is done with a special name syntax explained - in Documentation/networking/alias.txt. If you want this, say Y. Most - people don't need it and say N. + domains" or "virtual hosting services" and is explained in detail on + the WWW at http://www.thesphere.com/~dlp/TwoServers/ (to browse the + WWW, you need to have access to a machine on the Internet that has a + program like lynx or netscape) and also in the + Virtual-Hosting-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Another scenario would be + that there are two logical networks living on your local Ethernet + and you want to access them both with the same Ethernet card. The + configuration of these alias addresses is done with a special name + syntax explained in Documentation/networking/alias.txt and in the + IP-Alias mini-HOWTO. If you want this, say Y. Most people don't need + it and say N. IP: multicast routing CONFIG_IP_MROUTE This is used if you want your machine to act as a router for IP packets that have several destination addresses. It is needed on the - MBONE, a high bandwidth network on top of the internet which carries + MBONE, a high bandwidth network on top of the Internet which carries audio and video broadcasts. In order to do that, you would most likely run the program mrouted. Information about the multicast capabilities of the various network cards is contained in @@ -1755,14 +1820,14 @@ PC/TCP compatibility mode CONFIG_INET_PCTCP If you have been having difficulties telneting to your Linux machine from a DOS system that uses (broken) PC/TCP networking software (all - versions up to OnNet 2.0) over your local ethernet try saying Y + versions up to OnNet 2.0) over your local Ethernet try saying Y here. Everyone else says N. People having problems with NCSA telnet should see the file linux/Documentation/networking/ncsa-telnet. Reverse ARP CONFIG_INET_RARP Since you asked: if there are (usually diskless or portable) - machines on your local network that know their hardware ethernet + machines on your local network that know their hardware Ethernet addresses but don't know their IP addresses upon startup, they can send out a Reverse Address Resolution Protocol (RARP) request to find out their own IP addresses. Diskless Sun 3 machines use this @@ -1850,7 +1915,7 @@ CONFIG_SKB_LARGE process can require a lot more memory for network buffers and thus this option is best only used on machines with 16Mb of memory or higher. Unless you are using long links with end to end speeds of over 2Mbit - a second or satellite links this option will make no difference to + a second or satellite links this option will make no difference to performance. Unix domain sockets @@ -1869,17 +1934,16 @@ CONFIG_UNIX The IPv6 protocol CONFIG_IPV6 This is experimental support for the next version of the Internet - Protocol IP version 6 (also called IPng "IP next - generation"). Features of this new protocol include: expanded - address space, authentication and privacy, and seamless - interoperability with the current version of IP (IP version 4). For - general information about IPv6, see - http://playground.sun.com/pub/ipng/html/ipng-main.html (to browse - the WWW, you need to have access to a machine on the Internet that - has a program like lynx or netscape); for specific information about - IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ and - the file net/ipv6/README in the kernel source. If you want to use - IPv6, please upgrade to the newest net-tools as given in + Protocol: IP version 6 (also called IPng "IP next generation"). + Features of this new protocol include: expanded address space, + authentication and privacy, and seamless interoperability with the + current version of IP (IP version 4). For general information about + IPv6, see http://playground.sun.com/pub/ipng/html/ipng-main.html (to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape); for specific information + about IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ + and the file net/ipv6/README in the kernel source. If you want to + use IPv6, please upgrade to the newest net-tools as given in Documentation/Changes. The IPv6 support is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called @@ -1906,13 +1970,13 @@ The IPX protocol CONFIG_IPX This is support for the Novell networking protocol, IPX, commonly used for local networks of Windows machines. You need it if you want - to access Novell Netware file or print servers using the Linux + to access Novell NetWare file or print servers using the Linux Novell client ncpfs (available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/system/filesystems/) or from within the Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former, you'll also have to say Y to "NCP filesystem support", below. To - turn your Linux box into a fully featured Netware file server and + turn your Linux box into a fully featured NetWare file server and IPX router, say Y here and fetch either lwared from sunsite.unc.edu:/pub/Linux/system/network/daemons/ or mars_nwe from ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the @@ -1970,7 +2034,7 @@ CONFIG_ATALK http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on the WWW for details (to browse the WWW, you need to have access to a machine on the Internet that has a program like lynx or - netscape). EtherTalk is the name used for appletalk over ethernet + netscape). EtherTalk is the name used for appletalk over Ethernet and the cheaper and slower LocalTalk is appletalk over a proprietary apple network using serial links. Ethertalk and Localtalk are fully supported by Linux. The NET-2-HOWTO, available via ftp (user: anonymous) @@ -1988,7 +2052,7 @@ CONFIG_IPDDP networking available. This feature is experimental. With this driver, you can either encapsulate IP inside Appletalk (e.g. if your Linux box is stuck on an appletalk only network) or decapsulate - (e.g. if you want your Linux box to act as a internet gateway for a + (e.g. if you want your Linux box to act as a Internet gateway for a zoo of appletalk connected Macs). You decide which one of the two you want in the following two questions; you can say Y to only one of them. Please see Documentation/networking/ipddp.txt for more @@ -2067,7 +2131,7 @@ CONFIG_AX25 microphone input and speaker output) supporting the KISS protocol or one of the various SCC cards that are supported by the Ottawa PI, the Gracilis Packetwin or the generic Z8530 driver. Another option - are the Baycom modem serial and parallel port hacks or the soundcard + are the Baycom modem serial and parallel port hacks or the sound card modem (supported by their own drivers). If you say Y here, you also have to say Y to one of those drivers. Information about where to get supporting software for Linux amateur radio as well as @@ -2123,7 +2187,7 @@ AX.25 over Ethernet CONFIG_BPQETHER AX.25 is the protocol used for computer communication over amateur radio. If you say Y here, you will be able to send and receive AX.25 - traffic over ethernet (also called "BPQ AX.25"), which could be + traffic over Ethernet (also called "BPQ AX.25"), which could be useful if some other computer on your local network has a direct amateur radio connection. @@ -2168,9 +2232,9 @@ CONFIG_X25 network either with a dedicated network card using the X.21 protocol (not yet supported by Linux) or one can do X.25 over a standard telephone line using an ordinary modem (say Y to "X.25 async driver" - below) or over ethernet using an ordinary ethernet card and either + below) or over Ethernet using an ordinary Ethernet card and either the 802.2 LLC protocol (say Y to "802.2 LLC" below) or LAPB over - ethernet (say Y to "LAPB Data Link Driver" and "LAPB over Ethernet + Ethernet (say Y to "LAPB Data Link Driver" and "LAPB over Ethernet driver" below). If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -2185,8 +2249,8 @@ CONFIG_LAPB it is used to transport higher level protocols (mostly X.25 Packet Layer, the higher part of X.25, but others are possible as well). Usually, LAPB is used with specialized X.21 network cards, but - Linux currently supports LAPB only over ethernet connections. If you - want to use LAPB connections over ethernet, say Y here and to "LAPB + Linux currently supports LAPB only over Ethernet connections. If you + want to use LAPB connections over Ethernet, say Y here and to "LAPB over Ethernet driver" below. Read Documentation/networking/lapb-module.txt for technical details. If you want to compile this driver as a module though ( = code which @@ -2197,35 +2261,36 @@ CONFIG_LAPB 802.2 LLC (VERY EXPERIMENTAL) CONFIG_LLC This is a Logical Link Layer protocol used for X.25 connections over - ethernet, using ordinary ethernet cards. + Ethernet, using ordinary Ethernet cards. Bridging (EXPERIMENTAL) CONFIG_BRIDGE If you say Y here, then your Linux box will be able to act as an - ethernet bridge, which means that the different ethernet segments it - is connected to will appear as one ethernet to the - participants. Several such bridges can work together to create even - larger networks of ethernets using the IEEE802.1 spanning tree - algorithm. As this is a standard, Linux bridges will interwork - properly with other third party bridge products. In order to use - this, you'll need the bridge configuration tools available via ftp - (user: anonymous) from shadow.cabi.net in /pub/Linux. Note that if - your box acts as a bridge, it probably contains several ethernet - devices, but the kernel is not able to recognize more than one at - boot time without help; for details read the Ethernet-HOWTO, available - via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/. - The Bridging code is still in test. If unsure, say N. + Ethernet bridge, which means that the different Ethernet segments it + is connected to will appear as one Ethernet to the participants. + Several such bridges can work together to create even larger + networks of Ethernets using the IEEE802.1 spanning tree algorithm. + As this is a standard, Linux bridges will interwork properly with + other third party bridge products. In order to use this, you'll need + the bridge configuration tools available via ftp (user: anonymous) + from shadow.cabi.net in /pub/Linux. Please read the Bridge + mini-HOWTO for more information. Note that if your box acts as a + bridge, it probably contains several Ethernet devices, but the + kernel is not able to recognize more than one at boot time without + help; for details read the Ethernet-HOWTO, available via ftp (user: + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. The + Bridging code is still in test. If unsure, say N. Packet socket CONFIG_PACKET The Packet protocol is used by applications which communicate directly with network devices without an intermediate network - protocol implemented in the kernel, e.g. tcpdump. If you want that - they work, choose Y. This driver is also available as a module - called af_packet.o ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt. If - unsure, say Y. + protocol implemented in the kernel, e.g. tcpdump. If you want them + to work, choose Y. This driver is also available as a module called + af_packet.o ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say Y. Kernel/User network link driver CONFIG_NETLINK @@ -2234,13 +2299,14 @@ CONFIG_NETLINK able to read from and write to character special files in the /dev directory having major mode 36. So far, the kernel uses it to publish some network related information if you say Y to "Routing - messages", below. It is also used by the firewall code if you say Y - to "Kernel/User network link driver" further down. You also need to - say Y here if you want to use arpd, a daemon that helps keep the - internal ARP cache (a mapping between IP addresses and hardware - addresses on the local network) small. The ethertap device, which - lets user space programs read and write raw ethernet frames, also - needs the network link driver. If unsure, say Y. + messages", below. It is also used by the firewall code to publish + information about possible attacks if you say Y to "IP: firewall + packet netlink device" further down. You also need to say Y here if + you want to use arpd, a daemon that helps keep the internal ARP + cache (a mapping between IP addresses and hardware addresses on the + local network) small. The ethertap device, which lets user space + programs read and write raw Ethernet frames, also needs the network + link driver. If unsure, say Y. Routing messages CONFIG_RTNETLINK @@ -2256,7 +2322,7 @@ CONFIG_NETLINK_DEV SCSI support? CONFIG_SCSI - If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or + If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or any other SCSI device under Linux, say Y and make sure that you know the name of your SCSI host adapter (the card inside your computer that "speaks" the SCSI protocol), because you will be asked for @@ -2274,18 +2340,19 @@ CONFIG_SCSI SCSI disk support CONFIG_BLK_DEV_SD - If you want to use a SCSI harddisk or the SCSI or parallel port + If you want to use a SCSI hard disk or the SCSI or parallel port version of the IOMEGA ZIP drive under Linux, say Y and read the - SCSI-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI - CDROMs. This driver is also available as a module ( = code which can - be inserted in and removed from the running kernel whenever you - want). The module will be called sd_mod.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt. Do not compile this driver as a module if - your root filesystem (the one containing the directory /) is located - on a SCSI disk. In this case, do not compile the driver for your - SCSI host adapter (below) as a module either. + SCSI-HOWTO and the Disk-HOWTO, both available via ftp (user: + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for + SCSI CDROMs. This driver is also available as a module ( = code + which can be inserted in and removed from the running kernel + whenever you want). The module will be called sd_mod.o. If you want + to compile it as a module, say M here and read + Documentation/modules.txt and Documentation/scsi.txt. Do not compile + this driver as a module if your root filesystem (the one containing + the directory /) is located on a SCSI disk. In this case, do not + compile the driver for your SCSI host adapter (below) as a module + either. SCSI tape support CONFIG_CHR_DEV_ST @@ -2312,15 +2379,15 @@ CONFIG_BLK_DEV_SR Enable vendor-specific extensions (for SCSI CDROM) CONFIG_BLK_DEV_SR_VENDOR This enables the usage of vendor specific SCSI commands. This is - required to support multisession CD's on with old NEC/TOSHIBA - cdrom drives (and HP Writers). If you have such a drive and get - the first session only, try to say Y here; everybody else says N. + required to support multisession CD's with old NEC/TOSHIBA cdrom + drives (and HP Writers). If you have such a drive and get the first + session only, try saying Y here; everybody else says N. SCSI generic support CONFIG_CHR_DEV_SG If you want to use SCSI scanners, synthesizers or CD-writers or just - about anything having "SCSI" in its name other than harddisks, - CDROMs or tapes, say Y here. Those won't be supported by the kernel + about anything having "SCSI" in its name other than hard disks, + CDROMs or tapes, say Y here. These won't be supported by the kernel directly, so you need some additional software which knows how to talk to these devices using the SCSI protocol. For CD-writers, you would need the program cdwrite, available via ftp (user: anonymous) @@ -2360,7 +2427,7 @@ CONFIG_SCSI_LOGGING find them in the source: drivers/scsi/scsi.c), and this allows you to select the types of information you want, and the level allows you to select the level of verbosity. If you say 'N' here, it may - be harder to track down some types of scsi problems. If you say 'Y' + be harder to track down some types of SCSI problems. If you say 'Y' here your kernel will be somewhat larger, but there should be no noticeable performance impact as long as you have logging turned off. @@ -2426,8 +2493,8 @@ CONFIG_SCSI_AIC7XXX Enable tagged command queueing CONFIG_AIC7XXX_TAGGED_QUEUEING This option allows you to enable tagged command queueing for this - driver. Some scsi devices do not properly support this - feature. Tagged command queueing will improve performance. + driver. Some SCSI devices do not properly support this feature. + Tagged command queueing will improve performance. Override driver defaults for commands per LUN CONFIG_OVERRIDE_CMDS @@ -2532,7 +2599,7 @@ CONFIG_SCSI_EATA_PIO This driver supports all EATA-PIO protocol compliant SCSI Host Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant host adapters could also use this driver but are discouraged from - doing so, since this driver only supports harddisks and lacks + doing so, since this driver only supports hard disks and lacks numerous features. You might want to have a look at the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this @@ -2560,7 +2627,7 @@ CONFIG_SCSI_U14_34F_LINKED_COMMANDS This is a feature of SCSI-2 which improves performance: the host adapter can send a whole list of commands to a device in one batch. Some SCSI devices might not implement this properly, so the - save answer is N. + safe answer is N. maximum number of queued commands CONFIG_SCSI_U14_34F_MAX_TAGS @@ -2596,7 +2663,7 @@ CONFIG_SCSI_GENERIC_NCR5380 Enable NCR53c400 extensions CONFIG_SCSI_GENERIC_NCR53C400 - This enables certain optimizations for the NCR53c400 scsi cards. You + This enables certain optimizations for the NCR53c400 SCSI cards. You might as well try it out. Note that this driver will only probe for the Trantor T130B in its default configuration; you might have to pass a command line option to the kernel at boot time if it doesn't @@ -2651,28 +2718,28 @@ CONFIG_SCSI_NCR53C8XX This is the BSD ncr driver adapted to linux for the NCR53C8XX family of PCI-SCSI controllers. This driver supports parity checking, tagged command queuing, Fast-20 data transfer up to 20 MB/s with - narrow scsi devices and 40 MB/s with wide scsi devices. + narrow SCSI devices and 40 MB/s with wide SCSI devices. Please read drivers/scsi/README.ncr53c8xx for more information. Linux/i386, Linux/Alpha and Linux/PPC are supported by this driver. synchronous data transfers frequency CONFIG_SCSI_NCR53C8XX_SYNC - SCSI-2 specifications allow scsi devices to negotiate a synchronous + SCSI-2 specifications allow SCSI devices to negotiate a synchronous transfer period of 25 nano-seconds or more. The transfer period value is 4 times the agreed transfer period. So, data can be transferred at a 10 MHz frequency, allowing 10 - MB/second throughput with 8 bits scsi-2 devices and 20 MB/second + MB/second throughput with 8 bits SCSI-2 devices and 20 MB/second with wide16 devices. This frequency can be used safely with - differential devices but may cause problems with singled-ended + differential devices but may cause problems with single-ended devices. Specify 0 if you want to only use asynchronous data transfers. Otherwise, specify a value between 5 and 10. Commercial O/Ses generally use 5 Mhz frequency for synchronous transfers. It is a reasonable default value. - However, a flawless singled-ended scsi bus supports 10 MHz data - transfers. Regardless the value chosen in the Linux configuration, - the synchronous period can be changed after boot-up through the - /proc/scsi file system. The generic command is: + However, a flawless single-ended SCSI bus supports 10 MHz data + transfers. Regardless of the value chosen in the Linux + configuration, the synchronous period can be changed after boot-up + through the /proc/scsi file system. The generic command is: echo "setsync #target period" >/proc/scsi/ncr53c8xx/0 Use a 25 ns period for 10 Mhz synchronous data transfers. If you don't know what to do now, go with the default. @@ -2684,16 +2751,16 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED Intel-based hardware. Under Linux/Alpha and Linux/PPC only normal IO is currently supported by the driver and so, this option has no effect. On Linux/PPC MMIO and normal IO are done the same (all IO - is memory mapped) so you loose nothing by using normal IO. The normal + is memory mapped) so you lose nothing by using normal IO. The normal answer therefore is N. Try Y only if you have problems. not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT - This option is only provided for safety if you suspect some scsi + This option is only provided for safety if you suspect some SCSI device of yours to not support properly the target-disconnect feature. In that case, you would say Y here. In general however, to not allow targets to disconnect is not reasonable if there is more - than 1 device on a scsi bus. The normal answer therefore is N. + than 1 device on a SCSI bus. The normal answer therefore is N. detect and read serial NVRAMs CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT @@ -2717,7 +2784,7 @@ CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT enable tagged command queuing CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE This option allows you to enable tagged command queuing support at - linux start-up. Some scsi devices do not properly support this + linux start-up. Some SCSI devices do not properly support this feature. The suggested method is to say N here and to use the "settags" control command after boot-up to enable this feature: echo "settags 2 4" >/proc/scsi/ncr53c8xx/0 @@ -2742,16 +2809,16 @@ CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT wiring. These General Purpose Input/Output pins can be used for vendor specific features or implementation of the standard SYMBIOS features. Genuine SYMBIOS boards use GPIO0 in output for controller - LED and GPIO3 bit as a flag indicating singled-ended/differential + LED and GPIO3 bit as a flag indicating single-ended/differential interface. If all the boards of your system are genuine SYMBIOS boards or use BIOS and drivers from SYMBIOS, you would want to enable this option. The driver behaves correctly on my system with this option enabled. (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev 0x12). This option must be set to N if your system has at least one - 53C8XX based scsi board with a vendor-specific BIOS (example: Tekram + 53C8XX based SCSI board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F). - However, if all your non Symbios compatible boards have NvRAM, + However, if all your non Symbios compatible boards have NVRAM, setting option "detect and read serial NVRAMs" (CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT) above allows the driver to distinguish Symbios compatible boards from other ones. So, you can @@ -2761,53 +2828,64 @@ CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT IBMMCA SCSI support CONFIG_SCSI_IBMMCA This is support for the IBM SCSI adapter found in many of the PS/2 - series. CONFIG_MCA must be set for this to work. If the adapter - isn't found during boot (a common problem for models 56, 57, 76, and - 77) you'll need to use the 'ibmmcascsi=<pun>', where <pun> is the id - of the SCSI subsystem (usually 7, but if that doesn't work check your - reference diskette). Owners of model 95 with a LED-matrix-display - can in addition activate some activity info like under OS/2, but more - informative, by setting 'ibmmcascsi=display' as additional kernel- - parameter. + series computers. These machines have an MCA bus, so you need to say + Y to "MCA support" as well and read Documentation/mca.txt. + If the adapter isn't found during boot (a common problem for models + 56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel + option, where <pun> is the id of the SCSI subsystem (usually 7, but + if that doesn't work check your reference diskette). Owners of model + 95 with a LED-matrix-display can in addition activate some activity + info like under OS/2, but more informative, by setting + 'ibmmcascsi=display' as an additional kernel parameter. Try "man + bootparam" or see the documentation of your boot loader about how to + pass options to the kernel. The lilo procedure is also explained in + the SCSI-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called ibmmca.o. Standard SCSI-order CONFIG_IBMMCA_SCSI_ORDER_STANDARD - In the PC-world and in most modern SCSI-BIOS-setups, SCSI-harddisks - are assigned to the driveletters, starting with the lowest SCSI-id + In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks + are assigned to the drive letters, starting with the lowest SCSI-id (physical number - pun) to be drive C:, as seen from DOS and similar - operating systems. When looking into papers, describing the + operating systems. When looking into papers describing the ANSI-SCSI-standard, this assignment of drives appears to be wrong. - The SCSI-standard follows a hardware-hierarchy which says, that + The SCSI-standard follows a hardware-hierarchy which says that id 7 has the highest priority and id 0 the lowest. Therefore, the - hostadapters are still today everywhere placed as SCSI-id 7 by - default. In the SCSI-standard, the driveletters express the priority - of the disk. C: should be the harddisk or a partition on it, with the + host adapters are still today everywhere placed as SCSI-id 7 by + default. In the SCSI-standard, the drive letters express the priority + of the disk. C: should be the hard disk, or a partition on it, with the highest priority. This must therefore be the disk with the highest SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the original definition of the SCSI-standard as also industrial- and process-control-machines, like VME-CPUs running under realtime-OSs (e.g. LynxOS, OS9) do. - If you like to run Linux on your MCA-machine with the same assignment, - of harddisks, as seen from e.g. DOS or OS/2 on your machine, which - is in addition conform to the SCSI-standard, you must say 'y' here. + If you like to run Linux on your MCA-machine with the same assignment + of hard disks as seen from e.g. DOS or OS/2 on your machine, which + is in addition conformant to the SCSI-standard, you must say Y here. This is also necessary for MCA-Linux-users who want to keep downward- compatibility to older releases of the IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than June 1997). - If you like to have the lowest SCSI-id assigned as drive C:, as modern - SCSI-BIOS do, which is not conform to the standard, but widely spread - and common in the PC-world of today, you must say 'n' here. + If you like to have the lowest SCSI-id assigned as drive C:, as + modern SCSI-BIOSes do, which does not conform to the standard, but + is widespread and common in the PC-world of today, you must say N + here. If unsure, say Y. -Reset SCSI-devices at boottime +Reset SCSI-devices at boot time CONFIG_IBMMCA_SCSI_DEV_RESET - By default, SCSI-devices are reset, when the machine is powered on. + By default, SCSI-devices are reset when the machine is powered on. However, some devices exist, like special-control-devices, - SCSI-CNC-machines, SCSI-printer or scanners of older type, that - do not reset, when switched on. If you say 'y' here, each device - along your SCSI-bus will get a reset-command after it has been - probed, while the kernel is booting. Say always 'n' here, if you - have no such strange SCSI-devices on your bus. If you say 'y' and - some more modern devices, like harddisks, do not like too much - resets, your system will hang when booting. + SCSI-CNC-machines, SCSI-printer or scanners of older type, that do + not reset when switched on. If you say Y here, each device connected + to your SCSI-bus will be issued a reset-command after it has been + probed, while the kernel is booting. This may cause problems with + more modern devices, like hard disks, which do not appreciate these + reset commands, and can cause your system to hang. So say Y only if + you know that one of your older devices needs it; N is the safe + answer. Always IN2000 SCSI support CONFIG_SCSI_IN2000 @@ -2960,14 +3038,14 @@ CONFIG_SCSI_EATA_TAGGED_QUEUE This is a feature of SCSI-2 which improves performance: the host adapter can send several SCSI commands to a device's queue even if previous commands haven't finished yet. Some SCSI devices don't - implement this properly, so the save answer is N. + implement this properly, so the safe answer is N. enable elevator sorting CONFIG_SCSI_EATA_LINKED_COMMANDS This is a feature of SCSI-2 which improves performance: the host adapter can send a whole list of commands to a device in one batch. Some SCSI devices might not implement this properly, so the - save answer is N. + safe answer is N. maximum number of queued commands CONFIG_SCSI_EATA_MAX_TAGS @@ -3059,12 +3137,12 @@ CONFIG_SCSI_DEBUG is that many hard to reproduce problems can be tested in a controlled environment where there is reduced risk of losing important data. This is primarily of use to people trying to debug the middle and upper - layers of the scsi subsystem. If unsure, say N. + layers of the SCSI subsystem. If unsure, say N. Network device support? CONFIG_NETDEVICES - You can say N here in case you don't intend to connect to any other - computer at all or all your connections will be either via UUCP + You can say N here if you don't intend to connect to any other + computer at all or if all your connections will be either via UUCP (UUCP is a protocol to forward mail and news between unix hosts over telephone lines; read the UUCP-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO) or dialing up a @@ -3073,20 +3151,22 @@ CONFIG_NETDEVICES dial up shell account on some Internet connected Unix computer. Read http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse the WWW, you need to have access to a machine on the Internet that - has a program like lynx or netscape)). You'll have to say Y if your + has a program like lynx or netscape)). You'll have to say Y if your computer contains a network card that you want to use under linux - (make sure you know its name because you will be asked for it and read - the Ethernet-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO; or if you - want to use SLIP (Serial Line Internet Protocol is the protocol used - to send Internet traffic over telephone lines or nullmodem cables) - or CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better - and newer replacement for SLIP) or PLIP (Parallel Line Internet - Protocol is mainly used to create a mini network by connecting the - parallel ports of two local machines) or AX.25/KISS (protocol for - sending internet traffic over radio links). Make sure to read the - NET-2-HOWTO. Eventually, you will have to read Olaf Kirch's - excellent book "Network Administrator's Guide", to be found in - sunsite.unc.edu:/pub/Linux/docs/LDP. If unsure, say Y. + (make sure you know its name because you will be asked for it and + read the Ethernet-HOWTO (especially if you plan to use more than one + network card under linux), available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use + SLIP (Serial Line Internet Protocol is the protocol used to send + Internet traffic over telephone lines or nullmodem cables) or CSLIP + (compressed SLIP) or PPP (Point to Point Protocol, a better and + newer replacement for SLIP) or PLIP (Parallel Line Internet Protocol + is mainly used to create a mini network by connecting the parallel + ports of two local machines) or AX.25/KISS (protocol for sending + Internet traffic over radio links). Make sure to read the + NET-2-HOWTO. Eventually, you will have to read Olaf Kirch's + excellent and free book "Network Administrator's Guide", to be found + in sunsite.unc.edu:/pub/Linux/docs/LDP. If unsure, say Y. Dummy net driver support CONFIG_DUMMY @@ -3097,7 +3177,7 @@ CONFIG_DUMMY programs. If you use SLIP or PPP, you might want to say Y here. Read about it in the Network Administrator's Guide, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Since - this thing comes often handy, the default is Y. It won't enlarge + this thing often comes in handy, the default is Y. It won't enlarge your kernel either. What a deal. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -3214,7 +3294,7 @@ Shortwave radio modem driver CONFIG_HFMODEM This experimental driver is used by a package (to be released) that implements the shortwave radio protocols RTTY, Sitor (Amtor), - Pactor 1 and GTOR using a standard PC soundcard. If unsure, + Pactor 1 and GTOR using a standard PC sound card. If unsure, say N. Shortwave radio modem driver support for SoundBlaster and compatible cards @@ -3275,8 +3355,8 @@ LAPB over Ethernet driver CONFIG_LAPBETHER This is a driver for a pseudo device (typically called /dev/lapb0) which allows you to open an LAPB point-to-point connection to some - other computer on your ethernet network. In order to do this, you - need to say Y or M to the driver for your ethernet card as well as + other computer on your Ethernet network. In order to do this, you + need to say Y or M to the driver for your Ethernet card as well as to "LAPB Data Link Driver". If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -3319,7 +3399,7 @@ CONFIG_SCC_TRXECHO High-speed (DMA) SCC driver for AX.25 CONFIG_DMASCC This is a driver for high-speed SCC boards (used to connect your - computer to your amateur radio and send internet traffic over the + computer to your amateur radio and send Internet traffic over the radio), i.e. those supporting DMA on one port. Currently, only Ottawa PI/PI2 boards (see http://hydra.carleton.ca/info/pi2.html) and Gracilis PackeTwin boards (see http://www.paccomm.com/; to @@ -3395,10 +3475,10 @@ CONFIG_BAYCOM_SER_HDX Documentation/modules.txt. This is recommended. The module will be called baycom_ser_hdx.o. -Soundcard modem driver for AX.25 +Sound card modem driver for AX.25 CONFIG_SOUNDMODEM This experimental driver allows a standard SoundBlaster or - WindowsSoundSystem compatible soundcard to be used as a packet radio + WindowsSoundSystem compatible sound card to be used as a packet radio modem (NOT as a telephone modem!), to send digital traffic over amateur radio. To configure the driver, use the sethdlc, smdiag and smmixer utilities available in the standard ax25 utilities @@ -3412,16 +3492,16 @@ CONFIG_SOUNDMODEM Documentation/modules.txt. This is recommended. The module will be called soundmodem.o. -Soundcard modem support for SoundBlaster and compatible cards +Sound card modem support for SoundBlaster and compatible cards CONFIG_SOUNDMODEM_SBC This option enables the soundmodem driver to use SoundBlaster and compatible cards. If you have a dual mode card (i.e. a WSS cards with a SoundBlaster emulation) you should say N here and Y to - "Soundcard modem support for WSS and Crystal cards", below, because + "Sound card modem support for WSS and Crystal cards", below, because this usually results in better performance. This option also supports SB16/32/64 in full duplex mode. -Soundcard modem support for WSS and Crystal cards +Sound card modem support for WSS and Crystal cards CONFIG_SOUNDMODEM_WSS This option enables the soundmodem driver to use WindowsSoundSystem compatible cards. These cards feature a codec chip from either @@ -3431,13 +3511,13 @@ CONFIG_SOUNDMODEM_WSS CS423x chips. If you don't need full duplex operation, do not enable it to save performance. -Soundcard modem support for 1200 baud AFSK modulation +Sound card modem support for 1200 baud AFSK modulation CONFIG_SOUNDMODEM_AFSK1200 This option enables the soundmodem driver 1200 baud AFSK modem, compatible to popular modems using TCM3105 or AM7911. The demodulator requires about 12% of the CPU power of a Pentium 75 CPU per channel. -Soundcard modem support for 2400 baud AFSK modulation (7.3728MHz crystal) +Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal) CONFIG_SOUNDMODEM_AFSK2400_7 This option enables the soundmodem driver 2400 baud AFSK modem, compatible to TCM3105 modems (over-)clocked with a 7.3728MHz @@ -3448,7 +3528,7 @@ CONFIG_SOUNDMODEM_AFSK2400_7 with many transceiver designs and the fact that the TCM3105 (if used) is operated widely outside its specifications. -Soundcard modem support for 2400 baud AFSK modulation (8MHz crystal) +Sound card modem support for 2400 baud AFSK modulation (8MHz crystal) CONFIG_SOUNDMODEM_AFSK2400_8 This option enables the soundmodem driver 2400 baud AFSK modem, compatible to TCM3105 modems (over-)clocked with an 8MHz crystal. @@ -3459,26 +3539,26 @@ CONFIG_SOUNDMODEM_AFSK2400_8 with many transceiver designs and the fact that the TCM3105 (if used) is operated widely outside its specifications. -Soundcard modem support for 2666 baud AFSK modulation +Sound card modem support for 2666 baud AFSK modulation CONFIG_SOUNDMODEM_AFSK2666 This option enables the soundmodem driver 2666 baud AFSK modem. This modem is experimental, and not compatible to anything else I know of. -Soundcard modem support for 4800 baud 8PSK modulation +Sound card modem support for 4800 baud 8PSK modulation CONFIG_SOUNDMODEM_PSK4800 This option enables the soundmodem driver 4800 baud 8PSK modem. This modem is experimental, and not compatible to anything else I know of. -Soundcard modem support for 4800 baud HAPN-1 modulation +Sound card modem support for 4800 baud HAPN-1 modulation CONFIG_SOUNDMODEM_HAPN4800 This option enables the soundmodem driver 4800 baud HAPN-1 compatible modem. This modulation seems to be widely used 'down under' and in the Netherlands. Here, nobody uses it, so I could not test if it works. It is compatible to itself, however :-) -Soundcard modem support for 9600 baud FSK G3RUH modulation +Sound card modem support for 9600 baud FSK G3RUH modulation CONFIG_SOUNDMODEM_FSK9600 This option enables the soundmodem driver 9600 baud FSK modem, compatible to the G3RUH standard. The demodulator requires about 4% @@ -3491,7 +3571,7 @@ Serial port KISS driver for AX.25 CONFIG_MKISS KISS is the protocol used to send IP traffic over AX.25 radio connections, somewhat similar to SLIP for telephone lines. Say Y - here if you intend to send internet traffic over amateur radio, + here if you intend to send Internet traffic over amateur radio, using some device connected to your machine's serial port. In that case, you also have to say Y to "Amateur Radio AX.25 Level 2" support. If you want to compile this driver as a module ( = code @@ -3503,21 +3583,26 @@ PLIP (parallel port) support CONFIG_PLIP PLIP (Parallel Line Internet Protocol) is used to create a reasonably fast mini network consisting of two (or, rarely, more) - local machines. The PLIP driver has two modes, mode 0 and mode - 1. The parallel ports (the connectors at the computers with 25 - holes) are connected with "null printer" or "Turbo Laplink" cables - which can transmit 4 bits at a time (mode 0) or with special PLIP - cables, to be used on bidirectional parallel ports only, which can - transmit 8 bits at a time (mode 1); you can find the wiring of these - cables in Documentation/networking/PLIP.txt. The cables can be up to - 15m long. Mode 0 works also if one of the machines runs DOS/Windows - and has some PLIP software installed, e.g. the Crynwr PLIP packet - driver (http://www.kanren.net/pktdrvr-info.html; to browse the WWW, - you need to have access to a machine on the Internet that has a - program like lynx or netscape) and winsock or NCSA's telnet. If you - want to use PLIP, say Y and read the PLIP mini-HOWTO, available via - ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini - as well as the NET-2-HOWTO in + local machines. A PLIP link from a Linux box is a popular means to + install a Linux distribution on a machine which doesn't have a CDROM + drive (a minimal system has to be transferred with floppies + first). The kernels on both machines need to have this PLIP option + enabled for this to work. + The PLIP driver has two modes, mode 0 and mode 1. The parallel ports + (the connectors at the computers with 25 holes) are connected with + "null printer" or "Turbo Laplink" cables which can transmit 4 bits + at a time (mode 0) or with special PLIP cables, to be used on + bidirectional parallel ports only, which can transmit 8 bits at a + time (mode 1); you can find the wiring of these cables in + Documentation/networking/PLIP.txt. The cables can be up to 15m + long. Mode 0 works also if one of the machines runs DOS/Windows and + has some PLIP software installed, e.g. the Crynwr PLIP packet driver + (http://www.kanren.net/pktdrvr-info.html; to browse the WWW, you + need to have access to a machine on the Internet that has a program + like lynx or netscape) and winsock or NCSA's telnet. If you want to + use PLIP, say Y and read the PLIP mini-HOWTO, available via ftp + (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as + well as the NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the PLIP protocol was changed and this PLIP driver won't work together with the PLIP support in Linux versions 1.0.x. This option enlarges your kernel @@ -3531,7 +3616,7 @@ EQL (serial line load balancing) support CONFIG_EQUALIZER If you have two serial connections to some other computer (this usually requires two modems and two telephone lines) and you use - SLIP (= the protocol for sending internet traffic over telephone + SLIP (= the protocol for sending Internet traffic over telephone lines) or PPP (= a better SLIP) on them, you can make them behave like one double speed connection using this driver. Naturally, this has to be supported at the other end as well, either with a similar @@ -3548,14 +3633,14 @@ CONFIG_ETHERTAP driver", above) and create a character special file /dev/tap0 with major number 36 and minor number 16 using mknod ("man mknod"), you will be able to have a user space program read and write raw - ethernet frames from/to that special file. tap0 can be configured - with ifconfig and route like any other ethernet device but it is not + Ethernet frames from/to that special file. tap0 can be configured + with ifconfig and route like any other Ethernet device but it is not connected to any physical LAN; everything written by the user to /dev/tap0 is treated by the kernel as if it had come in from a LAN to the device tap0; everything the kernel wants to send out over the device tap0 can instead be read by the user from /dev/tap0: the user mode program replaces the LAN that would be attached to an ordinary - ethernet device. Please read the file + Ethernet device. Please read the file Documentation/networking/ethertap.txt for more information. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The @@ -3566,7 +3651,7 @@ CONFIG_ETHERTAP Frame Relay (DLCI) support CONFIG_DLCI This is support for the frame relay protocol; frame relay is a fast - low-cost way to connect to a remote internet access provider or to + low-cost way to connect to a remote Internet access provider or to form a private wide area network. The one physical line from your box to the local "switch" (i.e. the entry point to the frame relay network, usually at the phone company) can carry several logical @@ -3606,6 +3691,28 @@ CONFIG_SDLA sdla.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +WAN Router +CONFIG_WAN_ROUTER + Wide Area Networks (WANs), such as X.25, frame relay and leased + lines, are used to interconnect Local Area Networks (LANs) over vast + distances with data transfer rates significantly higher than those + achievable with commonly used asynchronous modem connections. + Usually, a quite expensive external device called a `WAN router' is + needed to connect to a WAN. + As an alternative, WAN routing can be built into the Linux + kernel. With relatively inexpensive WAN interface cards available + on the market, a perfectly usable router can be built for less than + half the price of an external router. If you have one of those + cards (with appropriate WAN Link Driver) and wish to use your Linux + box as a WAN router, you may say 'Y' to this option. You will also + need a wan-tools package available via FTP (user: anonymous) from + ftp.sangoma.com. Read Documentation/networking/wan-router.txt for + more information. + WAN routing support is always built as a module ( = code which can + be inserted in and removed from the running kernel whenever you + want). The module is called wanrouter.o. For general information + about modules read Documentation/modules.txt. + CPU is too slow to handle full bandwidth CONFIG_CPU_IS_SLOW ### @@ -3618,7 +3725,7 @@ CONFIG_NET_SCHED devices, it has to make a decision which one to send first. This is especially important if some of the network devices are real time devices that need a certain minimum data flow rate. There are - several different algorithms how to do this "fairly"; they are + several different algorithms for how to do this "fairly"; they are called packet schedulers. You can attach different schedulers to different network devices. If you want to stick to the default scheduling algorithm, say N here. If you want to experiment with a @@ -3709,82 +3816,78 @@ CONFIG_NET_SCH_PRIO ### schedulers? ### -WAN Router -CONFIG_WAN_ROUTER - Wide Area Networks (WANs), such as X.25, frame relay and leased - lines, are used to interconnect Local Area Networks (LANs) over vast - distances with data transfer rates significantly higher than those - achievable with commonly used asynchronous modem connections. - Usually, a quite expensive external device called `WAN router' is - needed to connect to WAN. - As an alternative, WAN router can be build into Linux kernel. - With relatively inexpensive WAN interface cards available on the - market, a perfectly usable router can be built for less than half a - price of an external router. If you have one of those cards (with - appropriate WAN Link Driver) and wish to use your Linux box as a WAN - router, you may say 'Y' to this option. You will also need a - wan-tools package available via FTP (user: anonymous) from - ftp.sangoma.com. Read Documentation/networking/wan-router.txt for - more information. - WAN router is always built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - For general information about modules read Documentation/modules.txt. +Network code profiler +CONFIG_NET_PROFILE + If you say Y here and to "/proc filesystem support" below, some + obscure and undocumented information about the network code's + performance will be written to /proc/net/profile. If you don't know + what it is about, you don't need it: say N. WAN Drivers CONFIG_WAN_DRIVERS - Say 'Y' to this option if you are planning to use your Linux box - as a WAN router ( = device used to interconnect local area networks - over wide area communication links, such as leased lines and public - data networks, e.g. X.25 and frame relay) and you will be offered a - list of WAN drivers currently available. For more information, read - Documentation/networking/wan-router.txt. + Say Y to this option if you are planning to use your Linux box as a + WAN ( = Wide Area Network) router ( = device used to interconnect + local area networks over wide area communication links, such as + leased lines and public data networks, e.g. X.25 and frame relay) + and you will be offered a list of WAN drivers currently available. + For more information, read + Documentation/networking/wan-router.txt. Note that the answer to + this question won't directly affect the kernel: saying N will just + cause this configure script to skip all the questions about WAN + drivers. If unsure, say N. Sangoma WANPIPE(tm) multiprotocol cards CONFIG_VENDOR_SANGOMA - WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com) - is a family of intelligent multiprotocol WAN adapter with data - transfer rates up to T1 (1.544 Mbps). They are also known as - Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503 - or S508. If you have one of these cards, say 'Y' to this option. - WANPIPE driver is always built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - For general information about modules read Documentation/modules.txt. + WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com; to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape) is a family of intelligent + multiprotocol WAN adapters with data transfer rates up to T1 (1.544 + Mbps). They are also known as Synchronous Data Link Adapters (SDLA) + and designated S502E(A), S503 or S508. These cards support the X.25, + Frame Relay, and PPP protocols. If you have one or more of these + cards, say Y to this option and read + Documentation/networking/wanpipe.txt. The next questions will ask + you about the protocols you want the driver to support. The driver + will be compiled as a module ( = code which can be inserted in and + removed from the running kernel whenever you want). The module will + be called wanpipe.o. For general information about modules read + Documentation/modules.txt. Maximum number of cards CONFIG_WANPIPE_CARDS - Enter number of WANPIPE adapters installed in your machine. The - driver can support up to 8 cards. You may enter more that you + Enter number of WANPIPE adapters installed in your machine. The + driver can support up to 8 cards. You may enter more than you actually have if you plan to add more cards in the future without re-compiling the driver, but remember that in this case you'll waste some kernel memory (about 1K per card). WANPIPE X.25 support CONFIG_WANPIPE_X25 - Say 'Y' to this option, if you are planning to connect WANPIPE - card to an X.25 network. If you say 'N', the X.25 support will not - be included in the driver (saves about 16K of kernel memory). + Say Y to this option if you are planning to connect a WANPIPE card + to an X.25 network. You should then also have said Y to "CCITT X.25 + Packet Layer" and "LAPB Data Link Driver", above. If you say N, the + X.25 support will not be included in the driver (saves about 16K of + kernel memory). WANPIPE Frame Relay support CONFIG_WANPIPE_FR - Say 'Y' to this option, if you are planning to connect WANPIPE - card to a frame relay network. If you say 'N', the frame relay + Say Y to this option if you are planning to connect a WANPIPE card + to a frame relay network. You should then also have said Y to "Frame + Relay (DLCI) support", above. If you say N, the frame relay support will not be included in the driver (saves about 16K of kernel memory). WANPIPE PPP support CONFIG_WANPIPE_PPP - Say 'Y' to this option, if you are planning to connect WANPIPE - card to a leased line using Point-to-Point protocol (PPP). If you - say 'N', the PPP support will not be included in the driver (saves + Say Y to this option if you are planning to connect a WANPIPE card + to a leased line using Point-to-Point protocol (PPP). You should + then also have said Y to "PPP (point-to-point) support", above. If + you say N, the PPP support will not be included in the driver (saves about 16K of kernel memory). - - Sun LANCE Ethernet support - CONFIG_SUN_LANCE - This is support for lance ethernet cards on Sun workstations such as Sun LANCE Ethernet support CONFIG_SUN_LANCE - This is support for lance ethernet cards on Sun workstations such as + This is support for lance Ethernet cards on Sun workstations such as the Sparcstation IPC (any Sparc with a network interface 'le0' under SunOS basically). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel @@ -3794,7 +3897,7 @@ CONFIG_SUN_LANCE Sun Intel Ethernet support CONFIG_SUN_INTEL - This is support for the intel ethernet cards on some Sun workstations + This is support for the Intel Ethernet cards on some Sun workstations (all those with a network interface 'ie0' under SunOS). Ethernet (10 or 100Mbit) @@ -3804,9 +3907,9 @@ CONFIG_NET_ETHERNET companies. 10-base-2 or Thinnet (10 Mbps over coaxial cable, linking computers in a chain), 10-base-T (10 Mbps over twisted pair telephone cable, linking computers to a central hub) and - 100-base-<whatever> (100 Mbps) are common types of ethernet. If your + 100-base-<whatever> (100 Mbps) are common types of Ethernet. If your Linux machine will be connected to an Ethernet and you have an - ethernet network card installed in your computer, say Y here and + Ethernet network card installed in your computer, say Y here and read the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this question won't directly affect the kernel: saying N will just cause @@ -3815,17 +3918,17 @@ CONFIG_NET_ETHERNET Western Digital/SMC cards CONFIG_NET_VENDOR_SMC - If you have a network (ethernet) card belonging to this class, say Y + If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the - answer to this question doesn't directly affect the kernel: - saying N will just cause this configure script to skip all the - questions about Western Digital cards. If you say Y, you will be - asked for your specific card in the following questions. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this + question doesn't directly affect the kernel: saying N will just + cause this configure script to skip all the questions about Western + Digital cards. If you say Y, you will be asked for your specific + card in the following questions. WD80*3 support CONFIG_WD80x3 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -3836,14 +3939,14 @@ CONFIG_WD80x3 SMC Ultra support CONFIG_ULTRA - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module will be - called smc-ultra.o. If you want to compile it as a module, say M - here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + smc-ultra.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. Important: There have been many reports that, with some motherboards mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, such as some BusLogic models) causes corruption problems with many @@ -3851,8 +3954,8 @@ CONFIG_ULTRA but keep it in mind if you have such a SCSI card and have problems. SMC Ultra32 EISA support -CONFIG_ULTRA - If you have a network (ethernet) card of this type, say Y and read +CONFIG_ULTRA32 + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed @@ -3877,18 +3980,18 @@ CONFIG_SMC9194 Racal-Interlan (Micom) NI cards CONFIG_NET_VENDOR_RACAL - If you have a network (ethernet) card belonging to this class, such + If you have a network (Ethernet) card belonging to this class, such as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer - to this question doesn't directly affect the kernel: - saying N will just cause this configure script to skip all the - questions about NI cards. If you say Y, you will be asked for your - specific card in the following questions. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause this configure script to skip all + the questions about NI cards. If you say Y, you will be asked for + your specific card in the following questions. NI5010 support CONFIG_NI5010 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that this is still experimental code. This driver is also available @@ -3900,46 +4003,46 @@ CONFIG_NI5010 NI5210 support CONFIG_NI52 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ni52.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + Documentation/networking/net-modules.txt. NI6510 support CONFIG_NI65 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ni65.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + Documentation/networking/net-modules.txt. AMD LANCE and PCnet (AT1500 and NE2100) support CONFIG_LANCE - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some LinkSys cards are of this type. 3COM cards CONFIG_NET_VENDOR_3COM - If you have a network (ethernet) card belonging to this class, say Y + If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about 3COM cards. If you say Y, you will be asked for your specific card in the - following questions. + following questions. 3c501 support CONFIG_EL1 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, consider buying a new card, since the 3c501 is slow, broken, and obsolete: you will have @@ -3953,7 +4056,7 @@ CONFIG_EL1 3c503 support CONFIG_EL2 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -3964,7 +4067,7 @@ CONFIG_EL2 3c505 support CONFIG_ELPLUS - Information about this network (ethernet) card can be found in + Information about this network (Ethernet) card can be found in Documentation/networking/3c505.txt. If you have a card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to @@ -3976,7 +4079,7 @@ CONFIG_ELPLUS 3c507 support CONFIG_EL16 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -3987,7 +4090,7 @@ CONFIG_EL16 3c523 support CONFIG_ELMC - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -3998,19 +4101,20 @@ CONFIG_ELMC 3c509/3c579 support CONFIG_EL3 - If you have a network (ethernet) card belonging to the 3Com + If you have a network (Ethernet) card belonging to the 3Com EtherLinkIII series, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. The module will be called - 3c509.o. If your card is not working you may need to use the DOS setup - disk to disable Plug & Play mode, and to select the default media type. + 3c509.o. If your card is not working you may need to use the DOS + setup disk to disable Plug & Play mode, and to select the default + media type. 3c590 series (592/595/597) "Vortex" support CONFIG_VORTEX - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is in Documentation/networking/vortex.txt and in the comments at the @@ -4018,12 +4122,11 @@ CONFIG_VORTEX module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - 3c59x.o. + Documentation/networking/net-modules.txt. Other ISA cards CONFIG_NET_ISA - If your network (ethernet) card hasn't been mentioned yet and its + If your network (Ethernet) card hasn't been mentioned yet and its bus system (that's the way the components of the card talk to each other) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you know the name of your card. Read the Ethernet-HOWTO, available via @@ -4032,7 +4135,7 @@ CONFIG_NET_ISA directly affect the kernel: saying N will just cause this configure script to skip all the remaining ISA network card questions. If you say Y, you will be asked for your specific card in the following - questions. + questions. Generic ARCnet support CONFIG_ARCNET @@ -4044,7 +4147,7 @@ CONFIG_ARCNET below. You might also want to have a look at the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO - (even though ARCnet is not really ethernet). This driver is also + (even though ARCnet is not really Ethernet). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called arcnet.o. If you want to compile it as a module, say M here @@ -4053,7 +4156,7 @@ CONFIG_ARCNET Enable arc0e (ARCnet "ether-encap" packet format) CONFIG_ARCNET_ETH - This allows you to use "ethernet encapsulation" with your ARCnet + This allows you to use "Ethernet encapsulation" with your ARCnet card via the virtual arc0e device. You only need arc0e if you want to talk to nonstandard ARCnet software, specifically, DOS/Windows-style "NDIS" drivers. You do not need to say Y here to @@ -4123,7 +4226,7 @@ CONFIG_ARCNET_COM20020 Cabletron E21xx support CONFIG_E2100 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4134,8 +4237,8 @@ CONFIG_E2100 CS89x0 support CONFIG_CS89x0 - Support for CS89x0 chipset based ethernet cards. If you have a - network (ethernet) card of this type, say Y and read the + Support for CS89x0 chipset based Ethernet cards. If you have a + network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO as well as Documentation/networking/cs89x0.txt. If you want to compile this as @@ -4147,7 +4250,7 @@ CONFIG_CS89x0 DEPCA support CONFIG_DEPCA - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO as well as drivers/net/depca.c. If you want to compile this as a module ( = @@ -4158,7 +4261,7 @@ CONFIG_DEPCA EtherWorks 3 support CONFIG_EWRK3 - This driver supports the DE203, DE204 and DE205 network (ethernet) + This driver supports the DE203, DE204 and DE205 network (Ethernet) cards. If this is for you, say Y and read Documentation/networking/ewrk3.txt in the kernel source as well as the Ethernet-HOWTO, available via ftp (user: anonymous) from @@ -4171,13 +4274,13 @@ CONFIG_EWRK3 SEEQ8005 support CONFIG_SEEQ8005 - This is a driver for the SEEQ 8005 network (ethernet) card. If this + This is a driver for the SEEQ 8005 network (Ethernet) card. If this is for you, read the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. AT1700 support CONFIG_AT1700 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4188,21 +4291,21 @@ CONFIG_AT1700 FMV-181/182/183/184 support CONFIG_FMV18X - If you have a Fujitsu FMV-181/182/183/184 network (ethernet) card, + If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called fmv18x.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you use FMV-183 or + Documentation/networking/net-modules.txt. If you use an FMV-183 or FMV-184 and it is not working, you may need to disable Plug & Play mode of the card. EtherExpressPro support CONFIG_EEXPRESS_PRO - If you have a network (ethernet) card of this type, say Y. Note - however that the EtherExpressPro 100 ethernet card has its own + If you have a network (Ethernet) card of this type, say Y. Note + however that the EtherExpressPro 100 Ethernet card has its own separate driver. Please read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted @@ -4213,7 +4316,7 @@ CONFIG_EEXPRESS_PRO EtherExpress support CONFIG_EEXPRESS - If you have an EtherExpress16 network (ethernet) card, say Y and + If you have an EtherExpress16 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the Intel EtherExpress16 card used to be regarded as a very poor choice @@ -4228,7 +4331,7 @@ CONFIG_EEXPRESS AT&T WaveLAN & DEC RoamAbout DS support CONFIG_WAVELAN The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS) is - a Radio LAN (wireless ethernet-like Local Area Network) using the + a Radio LAN (wireless Ethernet-like Local Area Network) using the radio frequencies 900 MHz and 2.4 GHz. This driver support the ISA version of the Wavelan card. A driver for the pcmcia hardware is available in David Hinds's pcmcia @@ -4248,7 +4351,7 @@ CONFIG_WAVELAN HP PCLAN+ (27247B and 27252A) support CONFIG_HPLAN_PLUS - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4259,7 +4362,7 @@ CONFIG_HPLAN_PLUS HP PCLAN (27245 and other 27xxx series) support CONFIG_HPLAN - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4270,7 +4373,7 @@ CONFIG_HPLAN HP 10/100VG PCLAN (ISA, EISA, PCI) support CONFIG_HP100 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the @@ -4281,9 +4384,9 @@ CONFIG_HP100 NE2000/NE1000 support CONFIG_NE2000 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Many ethernet cards without a + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Many Ethernet cards without a specific driver are compatible with NE2000. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be @@ -4293,7 +4396,7 @@ CONFIG_NE2000 SK_G16 support CONFIG_SK_G16 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. @@ -4302,22 +4405,21 @@ CONFIG_NET_EISA This is another class of network cards which attach directly to the bus. If you have one of those, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you are unsure, say Y. - Note that the answer to this question doesn't - directly affect the kernel: saying N will just cause this configure - script to skip all the questions about this class of network - cards. If you say Y, you will be asked for your specific card in the - following questions. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this + question doesn't directly affect the kernel: saying N will just + cause this configure script to skip all the questions about this + class of network cards. If you say Y, you will be asked for your + specific card in the following questions. If you are unsure, say Y. AMD PCnet32 (VLB and PCI) support CONFIG_PCNET32 - If you have a PCnet32 or PCnetPCI based network (ethernet) card, say + If you have a PCnet32 or PCnetPCI based network (Ethernet) card, say Y here and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Ansel Communications EISA 3200 support CONFIG_AC3200 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4328,7 +4430,7 @@ CONFIG_AC3200 Racal-Interlan EISA ES3210 support CONFIG_ES3210 - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the @@ -4337,9 +4439,9 @@ CONFIG_ES3210 Documentation/modules.txt as well as Documentation/networking/net-modules.txt. -Apricot Xen-II on board ethernet +Apricot Xen-II on board Ethernet CONFIG_APRICOT - If you have a network (ethernet) controller of this type, say Y and + If you have a network (Ethernet) controller of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the @@ -4350,7 +4452,7 @@ CONFIG_APRICOT Generic DECchip & DIGITAL EtherWORKS PCI/EISA CONFIG_DE4X5 - This is support for the DIGITAL series of PCI/EISA ethernet + This is support for the DIGITAL series of PCI/EISA Ethernet cards. These include the DE425, DE434, DE435, DE450 and DE500 models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in @@ -4364,7 +4466,7 @@ CONFIG_DE4X5 DECchip Tulip (dc21x4x) PCI support CONFIG_DEC_ELCP - This driver is developed for the SMC EtherPower series ethernet + This driver is developed for the SMC EtherPower series Ethernet cards and also works with cards based on the DECchip 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are of this type. (If your card is NOT SMC EtherPower 10/100 PCI @@ -4383,7 +4485,7 @@ CONFIG_DEC_ELCP Digi Intl. RightSwitch support CONFIG_DGRS This is support for the Digi International RightSwitch series of - PCI/EISA ethernet switch cards. These include the SE-4 and the SE-6 + PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is @@ -4392,29 +4494,29 @@ CONFIG_DGRS from the running kernel whenever you want). The module will be called dgrs.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + Documentation/networking/net-modules.txt. EtherExpressPro/100 support CONFIG_EEXPRESS_PRO100 - If you have an Intel EtherExpressPro 100 PCI network (ethernet) + If you have an Intel EtherExpressPro 100 PCI network (Ethernet) card, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called eepro100.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + Documentation/networking/net-modules.txt. ICL EtherTeam 16i/32 support CONFIG_ETH16I - If you have a network (ethernet) card of this type, say Y and read + If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called eth16i.o. If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. + Documentation/networking/net-modules.txt. TI ThunderLAN support (EXPERIMENTAL) CONFIG_TLAN @@ -4428,44 +4530,44 @@ CONFIG_TLAN Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network - (ethernet) card, and this is the Linux driver for it. Note that the + (Ethernet) card, and this is the Linux driver for it. Note that the IBM Thinkpad 300 is compatible with the Z-Note and is also supported by this driver. Read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Pocket and portable adapters CONFIG_NET_POCKET - Cute little network (ethernet) devices which attach to the parallel + Cute little network (Ethernet) devices which attach to the parallel port ("pocket adapters"), commonly used with laptops. If you have one of those, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to plug a network card into the PCMCIA slot of your laptop instead (PCMCIA is the standard for credit card size extension cards - used by all modern laptops), look in - cb-iris.stanford.edu:/pub/pcmcia and say N here. Note that the - answer to this question doesn't directly affect the kernel: saying N - will just cause this configure script to skip all the questions - about this class of network devices. If you say Y, you will be - asked for your specific device in the following questions. If you - plan to use more than one network device under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + used by all modern laptops), look on the ftp site (user: anonymous) + cb-iris.stanford.edu:/pub/pcmcia and say N here. + Laptop user want to read the Linux Laptop homepage at + http://www.cs.utexas.edu/users/kharker/linux-laptop/ (to browse the + WWW, you need to have access to a machine on the Internet that has a + program like lynx or netscape). + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause this configure script to skip all + the questions about this class of network devices. If you say Y, + you will be asked for your specific device in the following + questions. AT-LAN-TEC/RealTek pocket adapter support CONFIG_ATP - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read drivers/net/atp.c as well as the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. If - you plan to use more than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use - this driver, you should have said N to the Parallel Printer support, - because the two drivers don't like each other. + you intend to use this driver, you should have said N to the + Parallel Printer support, because the two drivers don't like each + other. D-Link DE600 pocket adapter support CONFIG_DE600 - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read Documentation/networking/DLINK.txt as well as the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is @@ -4478,7 +4580,7 @@ CONFIG_DE600 D-Link DE620 pocket adapter support CONFIG_DE620 - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read Documentation/networking/DLINK.txt as well as the Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is @@ -4492,14 +4594,19 @@ CONFIG_DE620 Token Ring driver support CONFIG_TR Token Ring is IBM's way of communication on a local network; the - rest of the world uses ethernet. If you are connected to a token - ring network and want to use your Token Ring card under Linux, say Y. - Most people can say N here. + rest of the world uses Ethernet. To participate on a Token Ring + network, you need a special Token ring network card. If you are + connected to such a Token Ring network and want to use your Token + Ring card under Linux, say Y here and read the Token-Ring + mini-HOWTO, available via ftp (user:anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Most people can say N here. IBM Tropic chipset based adapter support CONFIG_IBMTR This is support for all IBM Token Ring cards that don't use DMA. If - you have such a beast, say Y, otherwise N. Warning: this driver will + you have such a beast, say Y and read the Token-Ring mini-HOWTO, + available via ftp (user:anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Warning: this driver will almost definitely fail if more than one active Token Ring card is present. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -4522,7 +4629,7 @@ CONFIG_SHAPER FDDI driver support CONFIG_FDDI Fiber Distributed Data Interface is a high speed local area network - design; essentially a replacement for high speed ethernet. FDDI can + design; essentially a replacement for high speed Ethernet. FDDI can run over copper or fiber. If you are connected to such a network and want a driver for the FDDI card in your computer, say Y here (and then also Y to the driver for your FDDI card, below). Most people @@ -4656,12 +4763,12 @@ CONFIG_SBPCD2 linux/include/linux/sbpcd.h before compiling the new kernel. Read the file Documentation/cdrom/sbpcd. -Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support +Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support CONFIG_AZTCD This is your driver if you have an Aztech CDA268-01A, Orchid CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or CR540 CDROM drive. This driver - just like all these CDROM drivers - - is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech + - is NOT for CDROM drives with IDE/ATAPI interfaces, such as Aztech CDA269-031SE. If you say Y here, you should also say Y to "ISO9660 cdrom filesystem support" below, because that's the filesystem used on CDROMs. Please also read the file Documentation/cdrom/aztcd. This @@ -4734,14 +4841,14 @@ Soft configurable cdrom interface card support CONFIG_CDI_INIT If you want to include boot-time initialization of any cdrom interface card that is software configurable, say Y here. Currently - only the ISP16/MAD16/Mozart soundcards with built-in cdrom + only the ISP16/MAD16/Mozart sound cards with built-in cdrom interfaces are supported. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about these CDROM drives. ISP16/MAD16/Mozart soft configurable cdrom interface support CONFIG_ISP16_CDI - These are soundcards with with built-in cdrom interfaces using the + These are sound cards with with built-in cdrom interfaces using the OPTi 82C928 or 82C929 chips. Say Y here to have them detected and possibly configured at boot time. In addition, You'll have to say Y to a driver for the particular cdrom drive you have attached to the @@ -4764,7 +4871,7 @@ CONFIG_DCACHE_PRELOAD Quota support CONFIG_QUOTA If you say Y here, you will be able to set per user limits for disk - usage (also called diskquotas). Currently, it works only for the + usage (also called disk quotas). Currently, it works only for the ext2 filesystem. You need additional software in order to use quota support; for details, read the Quota mini-HOWTO, available via ftp (user: anonymous) in @@ -4774,7 +4881,7 @@ CONFIG_QUOTA Online mirror support CONFIG_OMIRR omirr is a package for _symmetric_ mirroring of files over the - internet. In contrast to rdist, the online mirror daemon (omirrd) + Internet. In contrast to rdist, the online mirror daemon (omirrd) is running all the time and transfers any changes on the file system as soon as possible to all other servers. Symmetric means that all servers have equal rights in changing a file: the last changer of a @@ -4924,11 +5031,11 @@ CONFIG_TR_SYSNAME Minix fs support CONFIG_MINIX_FS Minix is a simple operating system used in many classes about - OS's. The minix filesystem (= method to organize files on a harddisk + OS's. The minix filesystem (= method to organize files on a hard disk partition or a floppy disk) was the original filesystem for Linux, has been superseded by the second extended filesystem ext2fs but is still used for root/boot and other floppies or ram disks since it is - leaner. You don't want to use it on your harddisk because of certain + leaner. You don't want to use it on your hard disk because of certain built-in restrictions. This option will enlarge your kernel by about 25 kB. Everyone should say Y or M so that they are able to read this common floppy format. If you want to compile this as a module ( = @@ -4941,7 +5048,7 @@ CONFIG_MINIX_FS Second extended fs support CONFIG_EXT2_FS This is the de facto standard Linux filesystem (= method to organize - files on a storage device) for harddisks. You want to say Y, unless + files on a storage device) for hard disks. You want to say Y, unless you intend to use Linux exclusively from inside a DOS partition using the umsdos filesystem. The advantage of the latter is that you can get away without repartitioning your hard drive (which often @@ -4994,23 +5101,25 @@ CONFIG_JOLIET fat fs support CONFIG_FAT_FS If you want to use one of the FAT-based filesystems (the MS-DOS, - VFAT (Windows'95) and UMSDOS (used to run Linux on top of an + VFAT (Windows 95) and UMSDOS (used to run Linux on top of an ordinary DOS partition) filesystems), then you must include FAT support. This is not a filesystem in itself, but it provides the - foundation for the other filesystems. This option will enlarge your - kernel by about 24 kB. If unsure, say Y. If you want to compile this - as a module however ( = code which can be inserted in and removed - from the running kernel whenever you want), say M here and read - Documentation/modules.txt. The module will be called fat.o. Note - that if you compile the FAT support as a module, you cannot compile - any of the FAT-based filesystems into the kernel - they will have to - be modules as well. The filesystem of your root partition cannot be - a module, so don't say M here if you intend to use UMSDOS as your - root filesystem. + foundation for the other filesystems. It is now also becoming + possible to read and write compressed FAT filesystems; read + Documentation/filesystems/fat_cvf.txt for details. This option will + enlarge your kernel by about 24 kB. If unsure, say Y. If you want to + compile this as a module however ( = code which can be inserted in + and removed from the running kernel whenever you want), say M here + and read Documentation/modules.txt. The module will be called fat.o. + Note that if you compile the FAT support as a module, you cannot + compile any of the FAT-based filesystems into the kernel - they will + have to be modules as well. The filesystem of your root partition + cannot be a module, so don't say M here if you intend to use UMSDOS + as your root filesystem. msdos fs support CONFIG_MSDOS_FS - This allows you to mount MSDOS partitions of your harddrive (unless + This allows you to mount MSDOS partitions of your hard drive (unless they are compressed; to access compressed MSDOS partitions under Linux, you can either use the DOS emulator DOSEMU, described in the DOSEMU-HOWTO, available via ftp (user: anonymous) at @@ -5024,10 +5133,10 @@ CONFIG_MSDOS_FS which doesn't require the msdos filesystem support. If you want to use umsdos, the Unix-like filesystem on top of DOS, which allows you to run Linux from within a DOS partition without repartitioning, - you'll have to say Y or M here. If your have Windows'95 or Windows + you'll have to say Y or M here. If you have Windows 95 or Windows NT installed on your MSDOS partitions, you should use the VFAT filesystem instead, or you will not be able to see the long - filenames generated by Windows'95 / Windows NT. This option will + filenames generated by Windows 95 / Windows NT. This option will enlarge your kernel by about 7 kB. If unsure, say Y. This will only work if you said Y to "fat fs support" as well. If you want to compile this as a module however ( = code which can be inserted in @@ -5037,10 +5146,10 @@ CONFIG_MSDOS_FS vfat fs support CONFIG_VFAT_FS - This allows you to mount MSDOS partitions of your harddrive. It + This allows you to mount MSDOS partitions of your hard drive. It will let you use filenames in a way compatible with the long - filenames used by Windows'95 and Windows NT fat-based (not NTFS) - partitions. It does not support Windows'95 compressed filesystems. + filenames used by Windows 95 and Windows NT fat-based (not NTFS) + partitions. It does not support Windows 95 compressed filesystems. You cannot use the VFAT filesystem for your root partition; use UMSDOS instead. This option enlarges your kernel by about 10 kB and it only works if you said Y to the "fat fs support" above. Please read @@ -5053,7 +5162,7 @@ CONFIG_VFAT_FS umsdos: Unix like fs on top of std MSDOS fs CONFIG_UMSDOS_FS Say Y here if you want to run Linux from within an existing DOS - partition of your harddrive. The advantage of this is that you can + partition of your hard drive. The advantage of this is that you can get away without repartitioning your hard drive (which often implies backing everything up and restoring afterwards) and hence you're able to quickly try out Linux or show it to your friends; the @@ -5075,37 +5184,36 @@ CONFIG_UMSDOS_FS CONFIG_PROC_FS This is a virtual filesystem providing information about the status of the system. "Virtual" means that it doesn't take up any space on - your harddisk: the files are created on the fly when you access - them. Also, you cannot read the files with less: you need to use - more or cat. The filesystem is explained in the Kernel Hacker's - Guide at http://www.redhat.com:8080/HyperNews/get/khg.html on the - WWW (to browse the WWW, you need to have access to a machine on the - Internet that has a program like lynx or netscape), and also on the - proc(8) manpage ("man 8 proc"). This option will enlarge your - kernel by about 18 kB. It's totally cool; for example, "cat - /proc/interrupts" gives information about what the different IRQs - are used for at the moment (there is a small number of Interrupt - ReQuest lines in your computer that are used by the attached devices - to gain the CPU's attention - often a source of trouble if two - devices are mistakenly configured to use the same IRQ). Several - programs depend on this, so everyone should say Y here. + your hard disk: the files are created on the fly when you access + them. Also, you cannot read the files with older version of the + program less: you need to use more or cat. The filesystem is + explained in the Kernel Hacker's Guide at + http://www.redhat.com:8080/HyperNews/get/khg.html on the WWW (to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape), and also on the proc(8) + manpage ("man 8 proc"). This option will enlarge your kernel by + about 18 kB. It's totally cool; for example, "cat /proc/interrupts" + gives information about what the different IRQs are used for at the + moment (there is a small number of Interrupt ReQuest lines in your + computer that are used by the attached devices to gain the CPU's + attention - often a source of trouble if two devices are mistakenly + configured to use the same IRQ). Several programs depend on this, so + everyone should say Y here. NFS filesystem support CONFIG_NFS_FS If you are connected to some other (usually local) Unix computer - (using SLIP, PLIP, PPP or ethernet) and want to mount files residing + (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing on that computer (the NFS server) using the Network File Sharing protocol, say Y. "Mounting files" means that the client can access the files with usual UNIX commands as if they were sitting on the - client's harddisk. For this to work, the server must run the + client's hard disk. For this to work, the server must run the programs nfsd and mountd (but does not need to have NFS filesystem support enabled). NFS is explained in the Network Administrator's Guide, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man - nfs". There is also a NFS-FAQ in - sunsite.unc.edu:/pub/Linux/docs/faqs which presumes that you know - the basics of NFS already. If you say Y here, you should have said Y - to TCP/IP networking also. This option would enlarge your kernel by + sunsite.unc.edu:/pub/Linux/docs/LDP, on its man page: "man nfs", and + in the NFS-HOWTO. If you say Y here, you should have said Y to + TCP/IP networking also. This option would enlarge your kernel by about 27 kB. This filesystem is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called nfs.o. If you want to @@ -5113,34 +5221,35 @@ CONFIG_NFS_FS Documentation/modules.txt. If you configure a diskless machine which will mount its root filesystem over nfs (in order to do that, check out the netboot package, available via ftp (user: anonymous) from - sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with "tar - xzvf filename", and say Y to "Root file system on NFS" below), then - you cannot compile this driver as a module. If you don't know what - all this is about, say N. + sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with + "tar xzvf filename", and say Y to "Root file system on NFS" below), + then you cannot compile this driver as a module. If you don't know + what all this is about, say N. Root file system on NFS CONFIG_ROOT_NFS If you want your Linux box to mount its whole root filesystem from some other computer over the net via NFS (presumably because your - box doesn't have a harddisk), say Y. Read Documentation/nfsroot.txt + box doesn't have a hard disk), say Y. Read Documentation/nfsroot.txt for details. Most people say N here. NFS server support CONFIG_NFSD If you want your Linux box to act as a NFS *server*, so that other - computers on your local network which support NFS can access files - on your box transparently, you have two options: you can use the - self-contained user space program nfsd, in which case you should say - N here, or you can say Y and use this new experimental kernel based - NFS server. The advantage of the kernel based solution is that it is - faster; it might not be completely stable yet, though. You will need - the support software from the linux-nfs package available at - ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/. - The nfs server is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). The module is called nfsd.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say N. + computers on your local network which support NFS can access certain + directories on your box transparently, you have two options: you can + use the self-contained user space program nfsd, in which case you + should say N here, or you can say Y and use this new experimental + kernel based NFS server. The advantage of the kernel based solution + is that it is faster; it might not be completely stable yet, though. + You will need the support software from the linux-nfs package + available at ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/. + Please read the NFS-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. The nfs server is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module is called + nfsd.o. If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say N. BOOTP support CONFIG_RNFS_BOOTP @@ -5168,9 +5277,9 @@ CONFIG_RNFS_RARP OS/2 HPFS filesystem support (read only) CONFIG_HPFS_FS OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS - is the filesystem used for organizing files on OS/2 harddisk + is the filesystem used for organizing files on OS/2 hard disk partitions. Say Y if you want to be able to read files from an OS/2 - HPFS partition of your harddrive. OS/2 floppies however are in + HPFS partition of your hard drive. OS/2 floppies however are in regular MSDOS format, so you don't need this option in order to be able to read them. Read Documentation/filesystems/hpfs.txt. This filesystem is also available as a module ( = code which can be @@ -5201,14 +5310,14 @@ CONFIG_NTFS_RW System V and Coherent filesystem support CONFIG_SYSV_FS - SCO, Xenix and Coherent are commercial Unix systems for intel + SCO, Xenix and Coherent are commercial Unix systems for Intel machines. Saying Y here would allow you to read and write to and - from their floppies and harddisk partitions. If you have a floppy or - harddisk partition like that, it is probable that they contain + from their floppies and hard disk partitions. If you have a floppy or + hard disk partition like that, it is probable that they contain binaries from those other Unix systems; in order to run these binaries, you will want to install iBCS2 (iBCS2 [Intel Binary Compatibility Standard] is a kernel module which lets you run SCO, - Xenix, Wyse, Unix Ware, Dell Unix and System V programs under Linux + Xenix, Wyse, UnixWare, Dell Unix and System V programs under Linux and is often needed to run commercial software, most prominently WordPerfect. It's in tsx-11.mit.edu:/pub/linux/BETA). If you only intend to mount files from some other Unix over the network using @@ -5226,6 +5335,40 @@ CONFIG_SYSV_FS read Documentation/modules.txt. The module will be called sysv.o. If you haven't heard about all of this before, it's safe to say N. +Amiga FFS filesystem support +CONFIG_AFFS_FS + If you say Y here, you will be able to mount floppies and hard drive + partitions which were formatted with the Amiga FFS filesystem. Full + read-write support is available for most versions of FFS, see + Documentation/filesystems/affs.txt for details. This filesystem + support is also available as a module ( = code which can be inserted + in and removed from the running kernel whenever you want). The + module is called affs.o. If you want to compile it as a module, say M + here and read Documentation/modules.txt. + +Apple Macintosh filesystem support (experimental) +CONFIG_HFS_FS + If you say Y here, you will be able to mount Macintosh-formatted + floppy disks and hard drive partitions with full read-write access. + Please read fs/hfs/HFS.txt to learn about the available mount + options. This filesystem support is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). The module is called hfs.o. If you want to + compile it as a module, say M here and read + Documentation/modules.txt. + +ROM filesystem support +CONFIG_ROMFS_FS + This is a very small read-only filesystem mainly intended for + initial ram disks of installation disks, but it could be used for + other read-only media as well. Read + Documentation/filesystems/romfs.txt for details. This filesystem + support is also available as a module ( = code which can be inserted + in and removed from the running kernel whenever you want). The + module is called romfs.o. If you want to compile it as a module, say + M here and read Documentation/modules.txt. If you don't know whether + you need it, then you don't need it: say N. + Kernel automounter support (experimental) CONFIG_AUTOFS_FS The automounter is a tool to automatically mount remote filesystems @@ -5245,7 +5388,7 @@ BSD UFS filesystem support (read only) CONFIG_UFS_FS BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD and NeXTstep) use a filesystem called UFS. Some System V Unixes can - create and mount harddisk partitions and diskettes using this + create and mount hard disk partitions and diskettes using this filesystem as well. Saying Y here allows you to mount these partitions and diskettes read-only. If you only intend to mount files from some other Unix over the network using NFS, you don't @@ -5265,7 +5408,7 @@ CONFIG_UFS_FS BSD disklabel (FreeBSD partition tables) support CONFIG_BSD_DISKLABEL - FreeBSD uses its own harddisk partition scheme on your PC. It + FreeBSD uses its own hard disk partition scheme on your PC. It requires only one entry in the primary partition table of your disk and manages it similarly to DOS extended partitions, putting in its first sector a new partition table in disklabel format. Saying Y @@ -5276,7 +5419,7 @@ CONFIG_BSD_DISKLABEL SMD disklabel (Sun partition tables) support CONFIG_SMD_DISKLABEL - Like most systems, SunOS uses its own harddisk partition table + Like most systems, SunOS uses its own hard disk partition table format, incompatible with all others. Saying Y here allows you to read these partition tables and further mount SunOS disks read-only from within Linux if you have also said Y to "BSD ufs filesystem @@ -5288,31 +5431,49 @@ CONFIG_SMD_DISKLABEL preferably "info tar"). If you don't know what all this is about, say N. +Solaris (x86) partition table support +CONFIG_SOLARIS_X86_PARTITION + Say Y here if you have a hard drive that will be accessed from Linux + and from Solaris x86. This is NOT for Solaris on the sparc + architecture. + +ADFS filesystem support (read only) (EXPERIMENTAL) +CONFIG_ADFS_FS + Acorn Disc Filing System is the standard filesystem of the Risc OS + operating system which runs on Acorn's StrongARM Risc PC computers. + If you say Y here, Linux will be able to read from ADFS partitions + on hard drives and from ADFS-formatted floppy disks. This code is + also available as a module called adfs.o ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + Documentation/modules.txt. + Macintosh partition map support CONFIG_MAC_PARTITION - Say Y here if you want your Linux system to be able to read - the partition tables of Macintosh hard drives, and thus use - partitions on those drives. + Say Y here if you want your Linux system to be able to read the + partition tables of Macintosh hard drives, and thus use partitions + on those drives. SMB filesystem support (to mount WfW shares etc..) CONFIG_SMB_FS - SMB (Server Message Buffer) is the protocol Windows for Workgroups + SMB (Server Message Block) is the protocol Windows for Workgroups (WfW), Windows 95, Windows NT and Lan Manager use to share files and printers over local networks. Saying Y here allows you to mount their filesystems (often called "shares" in this context) and access them just like any other unix directory. Currently, this works only if the Windows machines use TCP/IP as the underlying transport - protocol, and not Netbeui. For details, read - Documentation/filesystems/smbfs.txt. Note: if you just want your - box to act as an SMB *server* and make files and printing services - available to Windows clients (which need to have a TCP/IP stack), - you don't need to say Y here; you can use the program samba - (available via ftp (user: anonymous) in + protocol, and not Netbeui. For details, read + Documentation/filesystems/smbfs.txt and the SMB-HOWTO, available via + ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. + Note: if you just want your box to act as an SMB *server* and make + files and printing services available to Windows clients (which need + to have a TCP/IP stack), you don't need to say Y here; you can use + the program samba (available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/system/network/samba) for that. General information about how to connect Linux, Windows machines and Macs is on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW, you need to have access to a machine on the Internet that has a - program like lynx or netscape). If you want to compile the SMB + program like lynx or netscape). If you want to compile the SMB support as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called smbfs.o. Most @@ -5320,22 +5481,23 @@ CONFIG_SMB_FS Coda filesystem support CONFIG_CODA_FS - CODA is an advanced network filesystem. It has support for - disconnected operation for laptops, read/write server replication, - persistent client caches and write back caching. - By saying Y here you are compiling kernel support for Coda clients - into the Linux kernel. You will need user level code as well, both - for the client and server. Servers are currently user level, - i.e. need no kernel support. For technical information, read + CODA is an advanced network filesystem, similar to NFS in that it + enables you to mount filesystems of a remote server and access them + with regular Unix commands as if they were sitting on your hard + disk. It has support for disconnected operation for laptops, + read/write server replication, persistent client caches and write + back caching. By saying Y here you are compiling kernel support for + Coda clients into the Linux kernel. You will need user level code as + well, both for the client and server. Servers are currently user + level, i.e. need no kernel support. For technical information, read Documentation/filesystems/coda.txt. If you want to compile the coda client support as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read - Documentation/modules.txt. The module will be called coda.o. - For further information see http://www.coda.cs.cmu.edu (to browse - the WWW, you need to have access to a machine on the Internet that - has a program like lynx or netscape) or contact Peter Braam - <braam@cs.cmu.edu>. + whenever you want), say M here and read Documentation/modules.txt. + The module will be called coda.o. For further information see + http://www.coda.cs.cmu.edu (to browse the WWW, you need to have + access to a machine on the Internet that has a program like lynx or + netscape) or contact Peter Braam <braam@cs.cmu.edu>. SMB Win95 bug work-around CONFIG_SMB_WIN95 @@ -5348,7 +5510,7 @@ NCP filesystem support (to mount NetWare volumes) CONFIG_NCP_FS NCP (NetWare Core Protocol) is a protocol that runs over IPX and is used by Novell NetWare clients to talk to file servers. It is to IPX - what nfs is to tcp/ip, if that helps. Saying Y here allows you to + what NFS is to TCP/IP, if that helps. Saying Y here allows you to mount NetWare file server volumes and to access them just like any other Unix directory. For details, please read the file Documentation/filesystems/ncpfs.txt in the kernel source and the @@ -5360,84 +5522,71 @@ CONFIG_NCP_FS Packet signatures CONFIG_NCPFS_PACKET_SIGNING - NCP allows to sign packets for stronger security. If you want + NCP allows packets to be signed for stronger security. If you want security, say Y. Normal users can leave it off. To be able to use packet signing you must use ncpfs > 2.0.12. Proprietary file locking CONFIG_NCPFS_IOCTL_LOCKING - Allows locking of records on remote volumes. Say N unless you have special - applications which are able to utilize this locking scheme. + Allows locking of records on remote volumes. Say N unless you have + special applications which are able to utilize this locking scheme. Clear remove/delete inhibit when needed CONFIG_NCPFS_STRONG - Allows manipulation of files flagged as Delete or Rename Inhibit. - To use this feature you must mount volumes with the ncpmount parameter + Allows manipulation of files flagged as Delete or Rename Inhibit. To + use this feature you must mount volumes with the ncpmount parameter "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not mounting volumes with -f 444. Use NFS namespace when available CONFIG_NCPFS_NFS_NS - Allows you to utilize NFS namespace on NetWare servers. It brings you - case sensitive filesystems. Say Y. You can disable it at mount-time with - the -N nfs parameter of ncpmount. + Allows you to utilize NFS namespace on NetWare servers. It brings + you case sensitive filenames. Say Y. You can disable it at + mount-time with the `-N nfs' parameter of ncpmount. Use OS2/LONG namespace when available CONFIG_NCPFS_OS2_NS - Allows you to utilize OS2/LONG namespace on NetWare servers. Filenames - in this namespace are limited to 255 characters, they are case - insensitive, and case in names is preserved. - Say Y. You can disable it at mount time with the -N os2 parameter of - ncpmount. + Allows you to utilize OS2/LONG namespace on NetWare servers. + Filenames in this namespace are limited to 255 characters, they are + case insensitive, and case in names is preserved. Say Y. You can + disable it at mount time with the -N os2 parameter of ncpmount. Allow mounting of volume subdirectories CONFIG_NCPFS_MOUNT_SUBDIR - Allows you to mount not only whole servers or whole volumes, but also - subdirectory from a volume. It can be used to reexport data and so on. - There is no reason why to say N, so Y is recommended unless you count - every byte. + Allows you to mount not only whole servers or whole volumes, but + also subdirectories from a volume. It can be used to reexport data + and so on. There is no reason to say N, so Y is recommended unless + you count every byte. To utilize this feature you must use ncpfs-2.0.12 or newer. NDS interserver authentication domains CONFIG_NCPFS_NDS_DOMAINS - This allows storing NDS private keys into kernel space where it can be - used to authenticate another server as interserver NDS accesses need - it. You must use ncpfs-2.0.12.1 or newer to utilize this feature. - Say Y if you are using NDS connections to NetWare servers. Do not say Y - if security is primary for you because root can read your session - key (from /proc/kcore). + This allows storing NDS private keys in kernel space where they + can be used to authenticate another server as interserver NDS + accesses need it. You must use ncpfs-2.0.12.1 or newer to utilize + this feature. Say Y if you are using NDS connections to NetWare + servers. Do not say Y if security is primary for you because root + can read your session key (from /proc/kcore). Amiga FFS filesystem support CONFIG_AFFS_FS - The Fast File System (FFS) is the common filesystem used on - harddisks by Amiga(tm) Systems since AmigaOS Version 1.3 - (34.20). With this driver you can also mount diskfiles used by Bernd - Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/; - to browse the WWW, you need to have access to a machine on the - Internet that has a program like lynx or netscape). If you want to - do the latter, you will also need to say Y to "Loop device support", - above. Say Y if you want to be able to read and write files from and - to an Amiga FFS partition on your harddrive. Amiga floppies however - cannot be read with this driver due to an incompatibility of the - floppy controller used in an Amiga and the standard floppy - controller in PCs and workstations. Read - Documentation/filesystems/affs.txt and fs/affs/Changes. This filesystem is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module is called - affs.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say N. - -ROM filesystem support -CONFIG_ROMFS_FS - This is a very small read-only filesystem mainly intended for - initial ram disks of installation disk, but it could be used for - other read-only media as well. Read - Documentation/filesystems/romfs.txt for details. This filesystem is - also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). The module is - called romfs.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. If you don't know whether you - need it, then you don't need it: say N. + The Fast File System (FFS) is the common filesystem used on hard + disks by Amiga(tm) Systems since AmigaOS Version 1.3 (34.20). With + this driver you can also mount diskfiles used by Bernd Schmidt's + Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/; to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape). If you want to do the latter, + you will also need to say Y to "Loop device support", above. Say Y + if you want to be able to read and write files from and to an Amiga + FFS partition on your hard drive. Amiga floppies however cannot be + read with this driver due to an incompatibility of the floppy + controller used in an Amiga and the standard floppy controller in + PCs and workstations. Read Documentation/filesystems/affs.txt and + fs/affs/Changes. This filesystem is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). The module is called affs.o. If you want to + compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say N. nls: Native language codepages and Unicode support CONFIG_NLS @@ -5488,7 +5637,7 @@ CONFIG_NLS_CODEPAGE_775 codepage if you want to be able to read/write these filenames on DOS/Windows partitions correctly. This does apply to the filenames only, not to the file contents. You can include several codepages; - say Y here if you want to include the DOS codepage that is used for + say Y here if you want to include the DOS codepage that is used for the Baltic Rim Languages. If unsure, say N. nls codepage 850 @@ -5754,15 +5903,16 @@ CONFIG_VT_CONSOLE Software generated cursor CONFIG_SOFTCURSOR If you say Y here, you'll be able to do lots of nice things with the - cursors of your virtual consoles -- for example to turn them into - non-blinking block cursors which are more visible on laptop screens. + cursors of your virtual consoles -- for example turn them into + non-blinking block cursors which are more visible on laptop screens, + or change their color depending on the virtual console you're on. See Documentation/VGA-softcursor.txt for more information. Standard/generic serial support CONFIG_SERIAL This selects whether you want to include the driver for the standard serial ports. People who might say N here are those that are - setting up dedicated ethernet WWW/ftp servers, or users that have + setting up dedicated Ethernet WWW/ftp servers, or users that have one of the various bus mice instead of a serial mouse. (Note that the Cyclades and Stallion multi serial port drivers do not need this driver built in for them to work.) If you want to compile this @@ -5950,16 +6100,16 @@ CONFIG_PRINTER you want to compile this driver as a module however ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module - will be called lp.o. If you have several parallel ports, you - should specify the base address for the port to use by the printer - with the "lp" kernel command line option. (Try "man bootparam" or - see the documentation of your boot loader (lilo or loadlin) about - how to pass options to the kernel at boot time. The lilo procedure - is also explained in the SCSI-HOWTO, available via ftp (user: - anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard - base addresses as well as the syntax of the "lp" command line option - can be found in drivers/char/lp.c. If you have more than 3 printers, - you need to increase the LP_NO variable in lp.c. + will be called lp.o. If you have several parallel ports, you should + specify the base address for the port to be used by the printer with + the "lp" kernel command line option. (Try "man bootparam" or see the + documentation of your boot loader (lilo or loadlin) about how to + pass options to the kernel at boot time. The lilo procedure is also + explained in the SCSI-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses + as well as the syntax of the "lp" command line option can be found + in drivers/char/lp.c. If you have more than 3 printers, you need to + increase the LP_NO variable in lp.c. CONFIG_PRINTER_READBACK If your printer conforms to IEEE 1284, it may be able to provide a @@ -6136,11 +6286,11 @@ CONFIG_ZFTAPE Regardless of whether you say Y or M here, an additional runtime loadable module called `zft-compressor.o' which contains code to support user transparent on-the-fly compression based on Ross - William's lzrw3 algorithm will be produced. If you have enabled - the kernel module loader (i.e. have said `Y' to CONFIG_KMOD) then - `zft-compressor.o' will be loaded automatically by zftape when - needed. - Despite of its name zftape does NOT use compression by default. The + William's lzrw3 algorithm will be produced. If you have enabled the + kernel module loader (i.e. have said Y to "Kernel module loader + support", above) then `zft-compressor.o' will be loaded + automatically by zftape when needed. + Despite its name, zftape does NOT use compression by default. The file Documentation/ftape.txt contains a short description of the most important changes in the file system interface compared to previous versions of ftape. The ftape home page @@ -6175,7 +6325,7 @@ Number of DMA buffers CONFIG_FT_NR_BUFFERS Please leave this at `3' unless you REALLY know what you are doing. It is not necessary to change this value. Values below 3 make - the proper use of ftape impossible, values greater than 3 are waste + the proper use of ftape impossible, values greater than 3 are a waste of memory. You can change the amount of DMA memory used by ftape at runtime with "mt -f /dev/qft0 setdrvbuffer #NUMBUFFERS". Each buffer wastes 32kb of memory. Please note that this memory cannot be @@ -6183,8 +6333,8 @@ CONFIG_FT_NR_BUFFERS Procfs entry for ftape CONFIG_FT_PROC_FS - Optional. Saying `Y' will result in creation of a file - `/proc/ftape' under the proc file system. This files can be viewed + Optional. Saying `Y' will result in creation of a directory + `/proc/ftape' under the proc file system. The files can be viewed with your favorite pager (i.e. use "more /proc/ftape/history" or "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The file will contain some status information about the inserted @@ -6329,16 +6479,16 @@ CONFIG_FT_FDC_DMA FDC FIFO Threshold before requesting DMA service CONFIG_FT_FDC_THR Set the FIFO threshold of the FDC. If this is higher the DMA - controller may serve the FCD after a higher latency time. If this is - lower, less DMA transfers occur leading to less bus contention. + controller may serve the FDC after a higher latency time. If this is + lower, fewer DMA transfers occur leading to less bus contention. You may try to tune this if ftape annoys you with "reduced data rate because of excessive overrun errors" messages. However, this - doesn't seem to have too much an effect. + doesn't seem to have too much effect. If unsure, don't touch the initial value, i.e. leave it at "8". FDC maximum data rate CONFIG_FT_FDC_MAX_RATE - With some mother board/FDC combinations ftape will not be able to + With some motherboard/FDC combinations ftape will not be able to run your FDC/tape drive combination at the highest available speed. If this is the case you'll encounter "reduced data rate because of excessive overrun errors" messages and lots of retries @@ -6369,20 +6519,25 @@ CONFIG_APM notification of APM "events" (e.g., battery status change). Supporting software is available; for more information, read the Battery Powered Linux mini-HOWTO available via ftp (user: - anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - This driver does not spin down disk drives (see hdparm(8) for that); - and it doesn't turn off VESA-compliant "green" monitors. This - driver does not support the TI 4000M TravelMate and the ACER - 486/DX4/75 because they don't have compliant BIOSes. Many "green" - desktop machines also don't have compliant BIOSes, and this driver - will cause those machines to panic during the boot phase (typically, - these machines are using a data segment of 0040, which is reserved - for the Linux kernel). Generally, if you don't have a battery in - your machine, there isn't much point in using this driver and you - should say N. If you get random kernel OOPSes or reboots that don't - seem to be related to anything, try disabling/enabling this - option. Some other things to try when experiencing seemingly random, - "weird" problems: + anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. This + driver does not spin down disk drives (see the hdparm(8) manpage + ("man 8 hdparm") for that), and it doesn't turn off VESA-compliant + "green" monitors. This driver does not support the TI 4000M + TravelMate and the ACER 486/DX4/75 because they don't have compliant + BIOSes. Many "green" desktop machines also don't have compliant + BIOSes, and this driver will cause those machines to panic during + the boot phase (typically, these machines are using a data segment + of 0040, which is reserved for the Linux kernel). + If you are running Linux on a laptop, you may also want to read the + Linux Laptop homepage on the WWW at + http://www.cs.utexas.edu/users/kharker/linux-laptop/ (to browse the + WWW, you need to have access to a machine on the Internet that has a + program like lynx or netscape). + Generally, if you don't have a battery in your machine, there isn't + much point in using this driver and you should say N. If you get + random kernel OOPSes or reboots that don't seem to be related to + anything, try disabling/enabling this option. Some other things to + try when experiencing seemingly random, "weird" problems: 1) passing the "no-hlt" option to the kernel 2) passing the "no-387" option to the kernel 3) passing the "floppy=nodma" option to the kernel @@ -6404,16 +6559,17 @@ Enable APM at boot time CONFIG_APM_DO_ENABLE Enable APM features at boot time. From page 36 of the APM BIOS specification: "When disabled, the APM BIOS does not automatically - power manage devices, enter the Standby State, enter the Suspend State, - or take power saving steps in response to CPU Idle calls." This driver - will make CPU Idle calls when Linux is idle (unless this feature is - turned off -- see below). This should always save battery power, but - more complicated APM features will be dependent on your BIOS - implementation. You may need to turn this option off if your computer - hangs at boot time when using APM support, or if it beeps continuously - instead of suspending. Turn this off if you have a NEC UltraLite Versa - 33/C or a Toshiba T400CDT. This is off by default since most machines - do fine without this feature. + power manage devices, enter the Standby State, enter the Suspend + State, or take power saving steps in response to CPU Idle calls." + This driver will make CPU Idle calls when Linux is idle (unless this + feature is turned off -- see "Do CPU IDLE calls", below). This + should always save battery power, but more complicated APM features + will be dependent on your BIOS implementation. You may need to turn + this option off if your computer hangs at boot time when using APM + support, or if it beeps continuously instead of suspending. Turn + this off if you have a NEC UltraLite Versa 33/C or a Toshiba + T400CDT. This is off by default since most machines do fine without + this feature. Do CPU IDLE calls CONFIG_APM_CPU_IDLE @@ -6428,20 +6584,21 @@ CONFIG_APM_CPU_IDLE Enable console blanking using APM CONFIG_APM_DISPLAY_BLANK Enable console blanking using the APM. Some laptops can use this to - turn off the LCD backlight when the VC screen blanker blanks the - screen. Note that this is only used by the VC screen blanker, and - won't turn off the backlight when using X11 (this also doesn't have - anything to do with your VESA-compliant power-saving monitor). - Further, this option doesn't work for all laptops -- it might not turn - off your backlight at all, or it might print a lot of errors to the - console, especially if you are using gpm. + turn off the LCD backlight when the screen blanker of the Linux + virtual console blanks the screen. Note that this is only used by + the virtual console screen blanker, and won't turn off the backlight + when using the X Window system. This also doesn't have anything to + do with your VESA-compliant power-saving monitor. Further, this + option doesn't work for all laptops -- it might not turn off your + backlight at all, or it might print a lot of errors to the console, + especially if you are using gpm. Power off on shutdown CONFIG_APM_POWER_OFF Enable the ability to power off the computer after the Linux kernel is halted. You will need software (e.g., a suitable version of the - halt(8) command) to cause the computer to power down. Recent - versions of the sysvinit package available from + halt(8) command ("man 8 halt")) to cause the computer to power down. + Recent versions of the sysvinit package available from ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/ (user: anonymous) contain support for this ("halt -p" shuts down Linux and powers off the computer). As with the other APM options, this @@ -6449,10 +6606,11 @@ CONFIG_APM_POWER_OFF Ignore multiple suspend/standby events CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - This option is necessary on the Thinkpad 560, but should work on all - other laptops. When the APM BIOS returns multiple suspend or standby - events while one is already being processed they will be ignored. - Without this the Thinkpad 560 has troubles with apmd, and pcmcia-cs. + This option is necessary on the IBM Thinkpad 560, but should work on + all other laptops. When the APM BIOS returns multiple suspend or + standby events while one is already being processed they will be + ignored. Without this the Thinkpad 560 has troubles with the user + level daemon apmd, and with the PCMCIA package pcmcia-cs. Watchdog Timer Support CONFIG_WATCHDOG @@ -6552,11 +6710,17 @@ CONFIG_RTC generate signals from as low as 1Hz up to 8192Hz, and can also be used as a 24 hour alarm. It reports status information via the file /proc/rtc and its behaviour is set by various ioctls on - /dev/rtc. People running SMP (= multiprocessor) versions of Linux - should say Y here to read and set the RTC clock in a SMP compatible - fashion. (They should also read Documentation/smp.) If you think you - have a use for such a device (such as periodic data sampling), then - say Y here, and go read the file Documentation/rtc.txt for details. + /dev/rtc. + People running SMP (= multiprocessor) versions of Linux should say Y + here to read and set the RTC clock in a SMP compatible + fashion. (They should also Documentation/smp and + Documentation/IO-APIC.txt and the SMP-FAQ on the WWW at + http://www.irisa.fr/prive/mentre/smp-faq/ (to browse the WWW, you + need to have access to a machine on the Internet that has a programs + like lynx or netscape)). + If you think you have a use for such a device (such as periodic data + sampling), then say Y here, and go read the file + Documentation/rtc.txt for details. Tadpole ANA H8 Support CONFIG_H8 @@ -6599,30 +6763,39 @@ CONFIG_JOYSTICK Sound card support CONFIG_SOUND - If you have a Sound Card in your Computer, i.e. if it can say more + If you have a sound card in your computer, i.e. if it can say more than an occasional beep, say Y. Be sure to have all the information about your sound card and its configuration down (I/O port, interrupt and DMA channel), because you will be asked for it. You want to read the Sound-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. There is also some - information in various README files in drivers/sound. If you want - to compile this as a module ( = code which can be inserted in and - removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt. I'm told that even without a sound - card, you can make your computer say more than an occasional beep, - by programming the PC speaker. Kernel patches and programs to do - that are at + information in various README files in drivers/sound, esp. in + Readme.cards which you should read first to find out whether your + card is supported by Linux. + If you have a PnP sound card and you want to configure it at boot + time using the ISA PnP tools (read + http://www.roestock.demon.co.uk/isapnptools/ (to browse the WWW, you + need to have access to a machine on the Internet that has a program + like lynx or netscape)), then you need to compile the sound card + support as a module ( = code which can be inserted in and removed + from the running kernel whenever you want) and load that module + after the PnP configuration is finished. To do this, say M here and + read Documentation/modules.txt as well as + drivers/sound/Readme.modules; the module will be called sound.o. + I'm told that even without a sound card, you can make your computer + say more than an occasional beep, by programming the PC speaker. + Kernel patches and programs to do that are at sunsite.unc.edu:/pub/Linux/kernel/patches/console/pcsndrv-X.X.tar.gz, to be extracted with "tar xzvf filename". ProAudioSpectrum 16 support CONFIG_PAS Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16. Don't answer Y if you have some other - card made by Media Vision or Logitech since they are not PAS16 - compatible. + 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have + some other card made by Media Vision or Logitech since they are not + PAS16 compatible. -SoundBlaster (SB, SBPro, SB16, clones) support +100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support CONFIG_SB Answer Y if you have an original SoundBlaster card made by Creative Labs or a 100% hardware compatible clone (like the @@ -6630,16 +6803,18 @@ CONFIG_SB cards look at the card specific instructions in the drivers/sound/Readme.cards file before answering this question. For an unknown card you may answer Y if the card claims to be - SoundBlaster compatible. + SoundBlaster compatible. If you have an SB AWE 32 or SB AWE 64, say + Y here and to "Additional lowlevel drivers" and to "SB32/AWE + support" below. Are you using the IBM Mwave "emulation" of SB ? CONFIG_SB_MWAVE - The IBM Mwave can do whats loosely describable as emulation of an 8bit - soundblaster if you load the right firmware from DOS warm boot and pray - and your machine happens to like you. Say Y if you are doing this as the - IRQ test normally fails on the mwave emulation. If you'd like real MWAVE - support phone IBM (425-556-8822) and ask them why they still haven't - released any documentation. + The IBM Mwave can do what's loosely describable as emulation of an + 8bit SoundBlaster card if you load the right firmware from DOS warm + boot and pray and your machine happens to like you. Say Y if you are + doing this as the IRQ test normally fails on the Mwave emulation. If + you'd like real MWAVE support phone IBM (425-556-8822) and ask them + why they still haven't released any documentation. [http://204.200.238.31/cgi-bin/link.pl?co=i&cl=/ts/ibm/contact.html] Generic OPL2/OPL3 FM synthesizer support @@ -6648,10 +6823,13 @@ CONFIG_ADLIB Answering Y is usually a safe and recommended choice, however some cards may have software (TSR) FM emulation. Enabling FM support with these cards may cause trouble (I don't currently know of any such - cards, however). + cards, however). If unsure, say Y. Loopback MIDI device support CONFIG_VMIDI +### +### somebody please fill this in. +### Gravis Ultrasound support CONFIG_GUS @@ -6661,7 +6839,7 @@ CONFIG_GUS MPU-401 support (NOT for SB16) CONFIG_MPU401 Be careful with this question. The MPU401 interface is supported by - all soundcards. However, some natively supported cards have their + all sound cards. However, some natively supported cards have their own driver for MPU401. Enabling this MPU401 option with these cards will cause a conflict. Also, enabling MPU401 on a system that doesn't really have a MPU401 could cause some trouble. If your card @@ -6717,7 +6895,7 @@ CONFIG_MSS Ensoniq Soundscape support CONFIG_SSCAPE - Answer Y if you have a soundcard based on the Ensoniq SoundScape + Answer Y if you have a sound card based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, Spea and Reveal (Reveal makes also other cards). @@ -6764,19 +6942,15 @@ CONFIG_YM3812 Sun Audio support CONFIG_SUN_AUDIO - This is support for the soundcards on Sun workstations. The code + This is support for the sound cards on Sun workstations. The code does not exist yet, so you might as well say N here. -SB32/AWE support -CONFIG_AWE32_SYNTH - Say Y here if you have a SB32 or SB AWE soundcard. See - drivers/sound/lowlevel/README.awe for more info. - Additional low level drivers CONFIG_LOWLEVEL_SOUND - If you need additional low level sound drivers which are not part - of USS/Lite (UNIX Sound System), say Y. The only such driver at - present is the ACI driver for the miroSOUND PCM12 and PCM20. + If you need additional low level sound drivers which have not yet + appeared, say Y. The answer to this question does not directly + affect the kernel; saying Y will simply cause this configure script + to present you with more options. If unsure, say Y. ACI mixer (miroPCM12) CONFIG_ACI_MIXER @@ -6789,6 +6963,13 @@ CONFIG_ACI_MIXER also controls the radio tuner on this card, however this is not yet supported in this software. +SB32/AWE support +CONFIG_AWE32_SYNTH + Say Y here if you have a SoundBlaster SB32, AWE32-PnP, SB AWE64 or + similar sound card. See drivers/sound/lowlevel/README.awe and the + Soundblaster-AWE mini-HOWTO, available via ftp (user: anonymous) + from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini for more info. + Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600) CONFIG_AEDSP16 Answer Y if you have a Gallant's Audio Excel DSP 16 card. This card @@ -6941,7 +7122,7 @@ CONFIG_HISAX_16_0 S0-8 and many compatibles. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for Teles 16.3 or PNP or PCMCIA CONFIG_HISAX_16_3 @@ -6949,14 +7130,14 @@ CONFIG_HISAX_16_3 the Teles/Creatix PnP and the Teles PCMCIA. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for AVM A1 (Fritz) CONFIG_HISAX_AVM_A1 This enables HiSax support for the AVM A1 (aka "Fritz"). See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for Elsa ISA cards CONFIG_HISAX_ELSA_PCC @@ -6964,21 +7145,21 @@ CONFIG_HISAX_ELSA_PCC for the Elsa Quickstep series cards. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for Elsa PCMCIA card CONFIG_HISAX_ELSA_PCMCIA This enables HiSax support for the Elsa PCMCIA cards. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for ITK ix1-micro Revision 2 CONFIG_HISAX_IX1MICROR2 This enables HiSax support for the ITK ix1-micro Revision 2 card. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + non-standard IRQ/port/shmem settings. HiSax Support for EURO/DSS1 CONFIG_HISAX_EURO @@ -7319,7 +7500,7 @@ Atari native SCSI support CONFIG_ATARI_SCSI If you have an Atari with built-in NCR5380 SCSI controller (TT, Falcon, ...) say Y to get it supported. Of course also, if you have - an compatible SCSI controller (e.g. for Medusa). This driver is also + a compatible SCSI controller (e.g. for Medusa). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called atari_scsi.o. If you want to compile it as a module, say M here and @@ -7573,9 +7754,9 @@ CONFIG_SCSI_MAC53C94 want). If you want to compile it as a module, say M here and read Documentation/modules.txt. -MACE (Power Mac ethernet) support +MACE (Power Mac Ethernet) support CONFIG_MACE - Power Macintoshes and clones with ethernet built-in on the + Power Macintoshes and clones with Ethernet built-in on the motherboard will usually use a MACE (Medium Access Control for Ethernet) interface. Say Y to include support for the MACE chip. @@ -7600,18 +7781,20 @@ CONFIG_VIDEO_BT848 Quickcam BW Video For Linux CONFIG_VIDEO_BWQCAM - Say Y have if you have such a thing. This driver is also available - as a module called bw-qcam.o ( = code which can be inserted in and - removed from the running kernel whenever you want). If you want to - compile it as a module, say M here and read + Say Y have if you the black and white version of the QuickCam + camera. See the next option for the color version. This driver is + also available as a module called bw-qcam.o ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read Documentation/modules.txt. Colour QuickCam Video For Linux CONFIG_VIDEO_CQCAM - This is the video4linux driver for the colour version of the Connectix - Quickcam. If you have one of these cameras, say Y here, otherwise say N. - This driver does not work with the original monochrome Quickcam, - Quickcam VC or QuickClip. It is also available as a module (c-qcam.o). + This is the video4linux driver for the colour version of the + Connectix Quickcam. If you have one of these cameras, say Y here, + otherwise say N. This driver does not work with the original + monochrome Quickcam, Quickcam VC or QuickClip. It is also available + as a module (c-qcam.o). Mediavision Pro Movie Studio Video For Linux CONFIG_VIDEO_PMS @@ -7621,22 +7804,26 @@ CONFIG_VIDEO_PMS it as a module, say M here and read Documentation/modules.txt. # need an empty line after last entry, for sed script in Configure. - +# +# A couple of things I keep forgetting: +# capitalize: Internet, Intel, SCSI, NetWare, PCI, IRQ, DMA +# two words: hard drive, hard disk, sound card +# other: it's safe to save. # # This is used by ispell.el: # # LocalWords: CONFIG coprocessor DX Pentium SX lilo loadlin HOWTO ftp sunsite # LocalWords: unc edu docs emu README kB BLK DEV FD Thinkpad fd MFM RLL IDE gz -# LocalWords: cdrom harddisk diskless netboot nfs xzvf ATAPI MB harddrives ide -# LocalWords: HD harddisks CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios +# LocalWords: cdrom diskless netboot nfs xzvf ATAPI MB ide pavia rubini pl pd +# LocalWords: HD CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios cezar ATEN # LocalWords: ISA EISA Microchannel VESA BIOSes IPC SYSVIPC ipc Ctrl dmesg hlt # LocalWords: BINFMT Linkable http ac uk jo html GCC Sparc AVANTI CABRIOLET EB # LocalWords: netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP -# LocalWords: genksyms INET loopback gatewaying ethernet internet PPP ARP Arp +# LocalWords: genksyms INET loopback gatewaying ethernet PPP ARP Arp MEMSIZE # LocalWords: howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip # LocalWords: proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS # LocalWords: telneting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl -# LocalWords: Mb SKB IPX Novell Netware dosemu Appletalk DDP ATALK tapedrive +# LocalWords: Mb SKB IPX Novell dosemu Appletalk DDP ATALK tapedrive vmalloc # LocalWords: SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA # LocalWords: buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext # LocalWords: QLOGIC qlogic TMC seagate Trantor ultrastor FASST wd NETDEVICES @@ -7649,21 +7836,21 @@ CONFIG_VIDEO_PMS # LocalWords: TR Sony CDU caddyless cdu Mitsumi MCD cd mcd XA MultiSession CDA # LocalWords: Matsushita Panasonic SBPCD Soundblaster Longshine sbpcd Aztech # LocalWords: Okano Wearnes AZTCD CDD SE aztcd sonycd Goldstar GSCD Philips fs -# LocalWords: LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos harddrive mtools +# LocalWords: LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos mtools Cichocki # LocalWords: std softlinks umssync NetworkFileSharing nfsd mountd CDs HPFS TI -# LocalWords: hpfs SYSV SCO intel iBCS Wyse WordPerfect tsx mit unixes sysv NR +# LocalWords: hpfs SYSV SCO iBCS Wyse WordPerfect tsx mit unixes sysv NR irisa # LocalWords: SMB WfW Cyclades async mux Logitech busmouse MouseSystem aka AST # LocalWords: PSMOUSE Compaq trackballs Travelmate Inport ATIXL ATI busmice ld # LocalWords: gpm config QIC DYNCONF FTAPE Stor Ftape ftape pcsndrv manpage NT # LocalWords: readprofile diskdrives org com masq EtherTalk tcp netrom sunacm # LocalWords: misc AIC aic pio nullmodems scc Portmaster eql GIS PhotoCDs MCDX -# LocalWords: mcdx gscd optcd sjcd ISP soundcard hdparm Workgroups Lan samba +# LocalWords: mcdx gscd optcd sjcd ISP hdparm Workgroups Lan samba PARIDE PCD # LocalWords: filesystems smbfs ATA ppp PCTech RZ www powerquest txt CMD ESDI # LocalWords: chipset FB multicast MROUTE appletalk ifconfig IBMTR multiport # LocalWords: Multisession STALDRV EasyIO EC EasyConnection ISTALLION ONboard # LocalWords: Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem # LocalWords: carleton Deskstation DECstation SUNFD JENSEN Noname XXXM SLiRP -# LocalWords: pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm +# LocalWords: pppd Zilog ZS SRM bootloader ez mainmenu rarp ipfwadm paride pcd # LocalWords: RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff # LocalWords: dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress # LocalWords: ICL EtherTeam ETH IDESCSI TXC SmartRAID SmartCache httpd sjc dlp @@ -7673,7 +7860,7 @@ CONFIG_VIDEO_PMS # LocalWords: Vertos Genoa Funai hsfs NCP NetWare tgz APM apm ioctls UltraLite # LocalWords: TravelMate CDT LCD backlight VC RPC Mips DECStation AXP barlow # LocalWords: PMAX MILO Alphas Multia Tseng linuxelf endian mipsel mips drv HT -# LocalWords: KERNELD kerneld callouts AdvanSys advansys diskquotas Admin WDT +# LocalWords: KERNELD kerneld callouts AdvanSys advansys Admin WDT DataStor EP # LocalWords: wdt hdb hdc bugfix SiS vlb Acculogic CSA DTC dtc Holtek ht QDI # LocalWords: QD qd UMC umc ALI ali lena fnet fr homepage azstarnet axplinux # LocalWords: Avanti XL AlphaStations Jensen DECpc AXPpci UDB Cabriolet MCA RC @@ -7741,7 +7928,7 @@ CONFIG_VIDEO_PMS # LocalWords: SYMBIOS COMPAT SDMS rev ASUS Tekram HX VX API ibmmcascsi ASY asy # LocalWords: loader's PCnetPCI automounter AUTOFS amd autofs VT Gallant's Pnp # LocalWords: AEDSP aedsp enskip tik Sysctl sysctl PARPORT parport pnp IDs EPP -# LocalWords: Autoprobe conformant bart patrickr HDLS READBACK AB usr DAMA DS +# LocalWords: Autoprobe bart patrickr HDLS READBACK AB usr DAMA DS SparQ aten # LocalWords: Symbios PCscsi tmscsim RoamAbout GHz Hinds's contrib mathematik # LocalWords: darmstadt okir DIGIEPCA International's Xem digiepca epca bootup # LocalWords: zorro CAPI AVMB capi avmb VP SYN syncookies EM em pc Ethertalk @@ -7767,16 +7954,23 @@ CONFIG_VIDEO_PMS # LocalWords: DMASCC paccomm dmascc addr cfg oevsv oe kib picpar FDX baudrate # LocalWords: baudrates fdx HDX hdx PSK kanren frforum QoS SCHED CBQ SCH sched # LocalWords: sch cbq CSZ Shenker Zhang csz SFQ sfq TBF tbf PFIFO fifo PRIO RW -# LocalWords: prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu +# LocalWords: prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu NIC # LocalWords: Braam braam Schmidt's freiburg nls codepages codepage Romanian # LocalWords: Slovak Slovenian Sorbian Nordic iso Catalan Faeroese Galician SZ # LocalWords: Valencian Slovene Esperanto Estonian Latvian Byelorussian KOI mt # LocalWords: charset Inuit Greenlandic Sami Lappish koi SOFTCURSOR softcursor -# LocalWords: Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's +# LocalWords: Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's inr # LocalWords: Iomega's LBFM claus ZFTAPE VFS zftape zft William's lzrw DFLT kb # LocalWords: MTSETBLK MTIOCTOP qft setblk zftape's tar's afio's setdrvbuffer # LocalWords: Procfs Exabyte's THR FCD sysvinit init PSC pscwdt VMIDI Euro SAB # LocalWords: Mostek Fastlane PowerMac PReP PMAC PowerPC Macintoshes Starmax # LocalWords: PowerStack Starmaxes MCOMMON DEVICETREE ATY IMS IMSTT videodev # LocalWords: BT Hauppauge STB bttv Quickcam BW BWQCAM bw qcam Mediavision PMS -# LocalWords: pms +# LocalWords: pms Avatar Freecom Imation Superdisk BPCK bpck COMM comm DSTR ru +# LocalWords: dstr EPAT EPEZ epat EPIA epia FreeCom FRPW frpw KingByte KBIC HW +# LocalWords: KingByte's kbic OnSpec ValuStore FASTROUTE fastroute FLOWCONTROL +# LocalWords: struct APIC realtime OSs LynxOS CNC tmp cvf HFS hfs ADFS Risc os +# LocalWords: StrongARM adfs ncpmount namespace SUBDIR reexport NDS kcore FT +# LocalWords: interserver BLKSZ NUMBUFFERS apmd Tadpole ANA roestock QuickCam +# LocalWords: isapnptools Colour CQCAM colour Connectix QuickClip prive mentre +# LocalWords: KMOD kmod conformant utexas kharker UnixWare Mwave cgi cl ts ibm diff --git a/Documentation/sound/CS4232 b/Documentation/sound/CS4232 new file mode 100644 index 000000000..b4906b148 --- /dev/null +++ b/Documentation/sound/CS4232 @@ -0,0 +1,23 @@ + +insmod sound +insmod ad1848 +insmod uart401 +insmod cs4232 io=* irq=* dma=* dma2=* + +This configures the crystal CS423x sound chip and activates its DSP +functions. On some cards the non PnP setup the board attempts to do fails. +If you have problems use the kernel PnP facilities. + +io is the I/O address of the WSS (normally 0x534) +irq is the IRQ of this device +dma/dma2 are the DMA channels. DMA2 may well be 0 + + +To get midi synth facilities add + +insmod opl3 io=* + +io= I/O address of the OPL3 synthesizer. This will be shown in /proc/sys/pnp +and is normally 0x388 + + diff --git a/Documentation/sound/OPL3 b/Documentation/sound/OPL3 new file mode 100644 index 000000000..2468ff827 --- /dev/null +++ b/Documentation/sound/OPL3 @@ -0,0 +1,6 @@ +A pure OPL3 card is nice and easy to configure. Simply do + +insmod opl3 io=0x388 + +Change the I/O address in the very unlikely case this card is differently +configured diff --git a/Documentation/sound/Soundblaster b/Documentation/sound/Soundblaster new file mode 100644 index 000000000..9ca8db777 --- /dev/null +++ b/Documentation/sound/Soundblaster @@ -0,0 +1,38 @@ + +insmod sound +insmod uart401 +insmod sb ... + +This loads the driver for the soundblaster and assorted clones. Cards that +are covered by other drivers should not be using with this driver. + +The soundblaster module takes the following arguments + +io I/O address of the soundblaster chip +irq IRQ of the soundblaster chip +dma 8bit DMA channel for the soundblaster +dma16 16bit DMA channel for SB16 and equivalent cards +mpu_io I/O for MPU chip if present + +mad16=1 Set when loading this as part of the MAD16 setup only +trix=1 Set when loading this as part of the Audiotrix setup only +pas2=1 Set when loading this as part of the Pas2 setup only +sm_games=1 Set if you have a Logitech soundman games +acer=1 Set this to detect cards in some ACER notebooks +mwave_bug=1 Set if you are trying to use this driver with mwave (see on) + +You may well want to load the opl3 driver for synth music on most SB and +clone SB devices + +insmod opl3 io=0x388 + +Using Mwave + +To make this driver work with Mwave you must set mwave_bug. You also need +to warmboot from DOS/Windows with the required firmware loaded under this +OS. IBM are being difficult about documenting how to load this firmware. + +Avance Logic ALS007 + +This card isnt currently supported. I have patches to merge however that +add limited support. diff --git a/Documentation/stallion.txt b/Documentation/stallion.txt index aff00034e..5f1a95977 100644 --- a/Documentation/stallion.txt +++ b/Documentation/stallion.txt @@ -4,8 +4,8 @@ Stallion Multiport Serial Driver Readme Copyright (C) 1994-1998, Stallion Technologies (support@stallion.com). -Version: 5.4.3 -Date: 04FEB98 +Version: 5.4.5 +Date: 23MAR98 @@ -31,12 +31,12 @@ the latest version of the driver utility package. Other sites that usually have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their mirrors. -ftp.stallion.com:/drivers/ata5/Linux/stallion-5.4.2.tar.gz -tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.4.2.tar.gz -sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.2.tar.gz +ftp.stallion.com:/drivers/ata5/Linux/v544.tar.gz +tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.4.4.tar.gz +sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.4.tar.gz As of the printing of this document the latest version of the driver -utility package is 5.4.2. If a later version is now available then you +utility package is 5.4.4. If a later version is now available then you should use the latest version. If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index b74141091..baf3f0700 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -25,10 +25,14 @@ Currently, these files are in /proc/sys/kernel: - inode-max - inode-nr - inode-state +- kmod_unload_delay ==> Documentation/kmod.txt +- modprobe ==> Documentation/kmod.txt - osrelease - ostype - panic - printk +- real-root-dev ==> Documentation/initrd.txt +- reboot-cmd ==> SPARC specific - securelevel - version diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 818d00f40..f2ef26fff 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -19,6 +19,7 @@ Currently, these files are in /proc/sys/vm: - buffermem - freepages - overcommit_memory +- pagecache - swapctl - swapout_interval @@ -93,17 +94,16 @@ buffermem: The three values in this file correspond to the values in the struct buffer_mem. It controls how much memory should -be used for buffer and cache memory. Note that memorymapped -files are also counted as cache memory... +be used for buffer memory. The values are: min_percent -- this is the minumum percentage of memory - that should be spent on buffer + page cache -borrow_percent -- when Linux is short on memory, and buffer - and cache use more than this percentage of - memory, free pages are stolen from them + that should be spent on buffer memory +borrow_percent -- when Linux is short on memory, and the + buffer cache uses more memory, free pages + are stolen from it max_percent -- this is the maximum amount of memory that - can be used for buffer and cache memory + can be used for buffer memory ============================================================== freepages: @@ -176,6 +176,18 @@ static inline int vm_enough_memory(long pages) ============================================================== +pagecache: + +This file does exactly the same as buffermem, only this +file controls the struct page_cache, and thus controls +the amount of memory allowed for memory mapping of files. + +You don't want the minimum level to be too low, otherwise +your system might thrash when memory is tight or fragmentation +is high... + +============================================================== + swapctl: This file contains no less than 8 variables. @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 90 +SUBLEVEL = 91 ARCH = mips @@ -249,11 +249,10 @@ newversion: fi include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion - @if [ -f .name ]; then \ - echo \#define UTS_VERSION \"\#`cat .version`-`cat .name` `date`\"; \ - else \ - echo \#define UTS_VERSION \"\#`cat .version` `date`\"; \ - fi >> .ver + @echo -n \#define UTS_VERSION \"\#`cat .version` > .ver + @if [ -n "$(SMP)" ] ; then echo -n " SMP" >> .ver; fi + @if [ -f .name ]; then echo -n \-`cat .name` >> .ver; fi + @echo ' '`date`'"' >> .ver @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> .ver @echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> .ver diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c index 9fbc46fd8..af8971834 100644 --- a/arch/alpha/kernel/bios32.c +++ b/arch/alpha/kernel/bios32.c @@ -813,7 +813,7 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, */ static inline void eb66p_fixup(void) { - static char irq_tab[5][5] __initdata = { + static char irq_tab[5][5] __initlocaldata = { {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ @@ -869,7 +869,7 @@ static inline void eb66p_fixup(void) #if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) static inline void alphapc164_fixup(void) { - static char irq_tab[7][5] __initdata = { + static char irq_tab[7][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ { 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */ { 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */ @@ -900,7 +900,7 @@ static inline void alphapc164_fixup(void) */ static inline void cabriolet_fixup(void) { - static char irq_tab[5][5] __initdata = { + static char irq_tab[5][5] __initlocaldata = { { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ @@ -956,7 +956,7 @@ static inline void cabriolet_fixup(void) */ static inline void eb66_and_eb64p_fixup(void) { - static char irq_tab[5][5] __initdata = { + static char irq_tab[5][5] __initlocaldata = { {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ @@ -1005,7 +1005,7 @@ static inline void eb66_and_eb64p_fixup(void) */ static inline void mikasa_fixup(void) { - static char irq_tab[8][5] __initdata = { + static char irq_tab[8][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 17, SCSI */ { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ @@ -1076,7 +1076,7 @@ static inline void mikasa_fixup(void) */ static inline void noritake_fixup(void) { - static char irq_tab[13][5] __initdata = { + static char irq_tab[13][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ { -1, -1, -1, -1, -1}, /* IdSel 19, PPB */ @@ -1140,7 +1140,7 @@ static inline void noritake_fixup(void) */ static inline void alcor_fixup(void) { - static char irq_tab[6][5] __initdata = { + static char irq_tab[6][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ @@ -1195,7 +1195,7 @@ static inline void alcor_fixup(void) */ static inline void xlt_fixup(void) { - static char irq_tab[7][5] __initdata = { + static char irq_tab[7][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ @@ -1266,7 +1266,7 @@ static inline void xlt_fixup(void) #ifdef CONFIG_ALPHA_SABLE static inline void sable_fixup(void) { - static char irq_tab[9][5] __initdata = { + static char irq_tab[9][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ { 32+0, 32+0, 32+0, 32+0, 32+0}, /* IdSel 0, TULIP */ { 32+1, 32+1, 32+1, 32+1, 32+1}, /* IdSel 1, SCSI */ @@ -1349,7 +1349,7 @@ static inline void sable_fixup(void) #ifdef CONFIG_ALPHA_MIATA static inline void miata_fixup(void) { - static char irq_tab[18][5] __initdata = { + static char irq_tab[18][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ {16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8}, /* IdSel 14, DC21142 */ { -1, -1, -1, -1, -1}, /* IdSel 15, EIDE */ @@ -1420,7 +1420,7 @@ static inline void miata_fixup(void) #ifdef CONFIG_ALPHA_SX164 static inline void sx164_fixup(void) { - static char irq_tab[5][5] __initdata = { + static char irq_tab[5][5] __initlocaldata = { /*INT INTA INTB INTC INTD */ { 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */ { 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */ @@ -1461,7 +1461,7 @@ static inline void sio_fixup(void) * that they use the default INTA line, if they are interrupt * driven at all). */ - static const char pirq_tab[][5] __initdata = { + static const char pirq_tab[][5] __initlocaldata = { #ifdef CONFIG_ALPHA_P2K { 0, 0, -1, -1, -1}, /* idsel 6 (53c810) */ {-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */ diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 4823c1546..95ce9fb14 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -205,7 +205,7 @@ BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15) #ifdef __SMP__ /* - * The IO-APIC (persent only in SMP boards) has 8 more hardware + * The IO-APIC (present only in SMP boards) has 8 more hardware * interrupt pins, for all of them we define an IRQ vector: * * raw PCI interrupts 0-3, basically these are the ones used @@ -1003,7 +1003,7 @@ unsigned long probe_irq_on (void) for (i=0; i<NR_IRQS; i++) for (j=0; j<NR_CPUS; j++) if (kstat.irqs[j][i] != probe_irqs[j][i]) - irqs &= ~(i<<1); + irqs &= ~(1UL << i); return irqs; } @@ -1018,7 +1018,7 @@ int probe_irq_off (unsigned long irqs) sum += kstat.irqs[j][i]; sum -= probe_irqs[j][i]; } - if (sum && (irqs & (i<<1))) { + if (sum && (irqs & (1UL << i))) { if (irq_found != -1) { irq_found = -irq_found; goto out; diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 6f2245790..6ba4e0ff8 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -171,6 +171,8 @@ int cpu_idle(void *unused) asmlinkage int sys_idle(void) { + if (current->pid != 0) + return -EPERM; cpu_idle(NULL); return 0; } diff --git a/arch/i386/lib/locks.S b/arch/i386/lib/locks.S index 30a5bc432..67f1caa51 100644 --- a/arch/i386/lib/locks.S +++ b/arch/i386/lib/locks.S @@ -16,10 +16,10 @@ ENTRY(__lock_kernel) jnc 3f sti 2: - btl %dl, SYMBOL_NAME(smp_invalidate_needed) + btl %edx, SYMBOL_NAME(smp_invalidate_needed) jnc 0f lock - btrl %dl, SYMBOL_NAME(smp_invalidate_needed) + btrl %edx, SYMBOL_NAME(smp_invalidate_needed) jnc 0f pushl %eax movl %cr3, %eax diff --git a/arch/m68k/amiga/amiga_ksyms.c b/arch/m68k/amiga/amiga_ksyms.c index 0eae9cfdb..9206b8e92 100644 --- a/arch/m68k/amiga/amiga_ksyms.c +++ b/arch/m68k/amiga/amiga_ksyms.c @@ -1,8 +1,8 @@ +#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/zorro.h> #include <asm/amigahw.h> -#include <linux/pci.h> extern volatile u_short amiga_audio_min_period; extern u_short amiga_audio_period; @@ -17,6 +17,7 @@ EXPORT_SYMBOL(amiga_colorclock); EXPORT_SYMBOL(amiga_chip_alloc); EXPORT_SYMBOL(amiga_chip_free); EXPORT_SYMBOL(amiga_chip_avail); +EXPORT_SYMBOL(amiga_chip_size); EXPORT_SYMBOL(amiga_audio_period); EXPORT_SYMBOL(amiga_audio_min_period); @@ -25,7 +26,3 @@ EXPORT_SYMBOL(zorro_get_board); EXPORT_SYMBOL(zorro_config_board); EXPORT_SYMBOL(zorro_unconfig_board); EXPORT_SYMBOL(zorro_unused_z2ram); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/m68k/amiga/zorro.c b/arch/m68k/amiga/zorro.c index 4f007daf3..7f2cd8c47 100644 --- a/arch/m68k/amiga/zorro.c +++ b/arch/m68k/amiga/zorro.c @@ -651,7 +651,8 @@ BEGIN_PROD(APOLLO_3) END BEGIN_PROD(PETSOFF_LP) - PROD("Delfina", DSP, PETSOFF_LP_DELFINA) + PROD("Delfina", AUDIO, PETSOFF_LP_DELFINA) + PROD("Delfina Lite", AUDIO, PETSOFF_LP_DELFINA_LITE) END BEGIN_PROD(UWE_GERLACH) diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index bf792da5c..8c512a714 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -117,7 +117,7 @@ static u_short ataplain_map[NR_KEYS] __initdata = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 }; -static u_short atashift_map[NR_KEYS] = { +static u_short atashift_map[NR_KEYS] __initdata = { 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf008, 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, @@ -136,7 +136,7 @@ static u_short atashift_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 }; -static u_short atactrl_map[NR_KEYS] = { +static u_short atactrl_map[NR_KEYS] __initdata = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -155,7 +155,7 @@ static u_short atactrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 }; -static u_short atashift_ctrl_map[NR_KEYS] = { +static u_short atashift_ctrl_map[NR_KEYS] __initdata = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -174,7 +174,7 @@ static u_short atashift_ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 }; -static u_short ataalt_map[NR_KEYS] = { +static u_short ataalt_map[NR_KEYS] __initdata = { 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf808, 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, @@ -212,7 +212,7 @@ static u_short atashift_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 }; -static u_short atactrl_alt_map[NR_KEYS] = { +static u_short atactrl_alt_map[NR_KEYS] __initdata = { 0xf200, 0xf200, 0xf200, 0xf800, 0xf81b, 0xf81c, 0xf81d, 0xf81e, 0xf81f, 0xf87f, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, @@ -767,14 +767,14 @@ void atari_kbd_leds (unsigned int leds) __initfunc(int atari_keyb_init(void)) { /* setup key map */ - memcpy (plain_map, ataplain_map, sizeof(plain_map)); - key_maps[1] = atashift_map; + memcpy(plain_map, ataplain_map, sizeof(plain_map)); + memcpy(shift_map, atashift_map, sizeof(shift_map)); key_maps[2] = 0; /* ataaltgr_map */ - key_maps[4] = atactrl_map; - key_maps[5] = atashift_ctrl_map; - key_maps[8] = ataalt_map; + memcpy(ctrl_map, atactrl_map, sizeof(ctrl_map)); + memcpy(shift_ctrl_map, atashift_ctrl_map, sizeof(shift_ctrl_map)); + memcpy(alt_map, ataalt_map, sizeof(alt_map)); key_maps[9] = atashift_alt_map; - key_maps[12] = atactrl_alt_map; + memcpy(ctrl_alt_map, atactrl_alt_map, sizeof(ctrl_alt_map)); key_maps[13] = atashift_ctrl_alt_map; keymap_count = 8; diff --git a/arch/m68k/atari/atari_ksyms.c b/arch/m68k/atari/atari_ksyms.c index 86e34fb7f..bd22bf672 100644 --- a/arch/m68k/atari/atari_ksyms.c +++ b/arch/m68k/atari/atari_ksyms.c @@ -1,5 +1,5 @@ +#include <linux/config.h> #include <linux/module.h> -#include <linux/pci.h> #include <asm/ptrace.h> #include <asm/traps.h> @@ -42,7 +42,3 @@ EXPORT_SYMBOL(ikbd_mouse_rel_pos); EXPORT_SYMBOL(ikbd_mouse_disable); EXPORT_SYMBOL(atari_microwire_cmd); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c index 1e81eb18e..8a3921c8d 100644 --- a/arch/m68k/atari/time.c +++ b/arch/m68k/atari/time.c @@ -83,14 +83,14 @@ static void mste_write(struct MSTE_RTC *val) #define RTC_READ(reg) \ ({ unsigned char __val; \ - outb(reg,&tt_rtc.regsel); \ + writeb(reg,&tt_rtc.regsel); \ __val = tt_rtc.data; \ __val; \ }) #define RTC_WRITE(reg,val) \ do { \ - outb(reg,&tt_rtc.regsel); \ + writeb(reg,&tt_rtc.regsel); \ tt_rtc.data = (val); \ } while(0) diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 4b90b6b02..e6c984bcb 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -37,6 +37,10 @@ if [ "$CONFIG_VME" = "y" ]; then # bool 'BVME4000 and BVME6000 support' CONFIG_BVME6000 fi +if [ "$CONFIG_PCI" = "y" ]; then + bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC +fi + comment 'Processor type' bool '68020 support' CONFIG_M68020 bool '68030 support' CONFIG_M68030 @@ -61,6 +65,7 @@ comment 'General setup' bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF @@ -138,6 +143,9 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED fi +if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then + bool ' Boot support (linear, striped)' CONFIG_MD_BOOT +fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD @@ -188,7 +196,7 @@ if [ "$CONFIG_AMIGA" = "y" ]; then bool 'A4000T SCSI support' CONFIG_A4000T_SCSI bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI - bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI +# bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI # bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI fi fi diff --git a/arch/m68k/kernel/m68k_defs.head b/arch/m68k/kernel/m68k_defs.head new file mode 100644 index 000000000..437028bc6 --- /dev/null +++ b/arch/m68k/kernel/m68k_defs.head @@ -0,0 +1,5 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ + +#define TS_MAGICKEY 0x5a5a5a5a diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 3e6721d69..025e11c0f 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -1,3 +1,4 @@ +#include <linux/config.h> #include <linux/module.h> #include <linux/linkage.h> #include <linux/sched.h> @@ -60,6 +61,6 @@ EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__up_wakeup); -#if CONFIG_PCI +#ifdef CONFIG_PCI EXPORT_SYMBOL(pci_devices); #endif diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index fade7ffa6..090f060ad 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -439,7 +439,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; @@ -477,7 +477,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~PF_TRACESYS; tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16); @@ -494,7 +494,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) long tmp; ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); diff --git a/arch/m68k/mac/ksyms.c b/arch/m68k/mac/ksyms.c index 573e6f78e..c1cc4fcd0 100644 --- a/arch/m68k/mac/ksyms.c +++ b/arch/m68k/mac/ksyms.c @@ -1,12 +1,8 @@ +#include <linux/config.h> #include <linux/module.h> -#include <linux/pci.h> #include <asm/ptrace.h> #include <asm/traps.h> /* Hook for mouse driver */ extern void (*mac_mouse_interrupt_hook) (char *); EXPORT_SYMBOL(mac_mouse_interrupt_hook); - -#if CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/m68k/mac/mackeyb.c b/arch/m68k/mac/mackeyb.c index fe3a860c8..f338253f3 100644 --- a/arch/m68k/mac/mackeyb.c +++ b/arch/m68k/mac/mackeyb.c @@ -16,6 +16,7 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/interrupt.h> +#include <linux/init.h> /* keyb */ #include <linux/keyboard.h> #include <linux/random.h> @@ -39,7 +40,6 @@ #define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */ /* end keyboard_input stuff */ -#include <asm/keyboard.h> #include <linux/kbd_kern.h> #include <linux/kbd_ll.h> @@ -81,7 +81,7 @@ static unsigned char dont_repeat[128] = { /* * Mac private key maps */ -u_short mac_plain_map[NR_KEYS] = { +u_short mac_plain_map[NR_KEYS] __initdata = { 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035, @@ -100,7 +100,7 @@ u_short mac_plain_map[NR_KEYS] = { 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short mac_shift_map[NR_KEYS] = { +u_short mac_shift_map[NR_KEYS] __initdata = { 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025, @@ -119,7 +119,7 @@ u_short mac_shift_map[NR_KEYS] = { 0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short mac_altgr_map[NR_KEYS] = { +u_short mac_altgr_map[NR_KEYS] __initdata = { 0xf914, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, 0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, @@ -138,7 +138,7 @@ u_short mac_altgr_map[NR_KEYS] = { 0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short mac_ctrl_map[NR_KEYS] = { +u_short mac_ctrl_map[NR_KEYS] __initdata = { 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012, 0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d, @@ -157,7 +157,7 @@ u_short mac_ctrl_map[NR_KEYS] = { 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short mac_shift_ctrl_map[NR_KEYS] = { +u_short mac_shift_ctrl_map[NR_KEYS] __initdata = { 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012, 0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, @@ -176,7 +176,7 @@ u_short mac_shift_ctrl_map[NR_KEYS] = { 0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c, }; -u_short mac_alt_map[NR_KEYS] = { +u_short mac_alt_map[NR_KEYS] __initdata = { 0xf861, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878, 0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872, 0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835, @@ -195,7 +195,7 @@ u_short mac_alt_map[NR_KEYS] = { 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -u_short mac_ctrl_alt_map[NR_KEYS] = { +u_short mac_ctrl_alt_map[NR_KEYS] __initdata = { 0xf801, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818, 0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812, 0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, @@ -216,15 +216,6 @@ u_short mac_ctrl_alt_map[NR_KEYS] = { extern unsigned int keymap_count; -#if 0 -ushort *mac_key_maps[MAX_NR_KEYMAPS] = { - mac_plain_map, mac_shift_map, mac_altgr_map, 0, - mac_ctrl_map, mac_shift_ctrl_map, 0, 0, - mac_alt_map, 0, 0, 0, - mac_ctrl_alt_map, 0 -}; -#endif - /* * Misc. defines for testing */ @@ -603,23 +594,19 @@ int mac_kbdrate(struct kbd_repeat *k) return 0; } -int mac_keyb_init(void) +__initfunc(int mac_keyb_init(void)) { static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req; volatile int ct; /* setup key map */ - key_maps[0] = mac_plain_map; - key_maps[1] = mac_shift_map; - key_maps[2] = mac_altgr_map; - key_maps[4] = mac_ctrl_map; - key_maps[5] = mac_shift_ctrl_map; - key_maps[8] = mac_alt_map; - /* key_maps[9] = atashift_alt_map; */ - key_maps[12] = mac_ctrl_alt_map; - /* key_maps[13] = atashift_ctrl_alt_map; */ memcpy (plain_map, mac_plain_map, sizeof(plain_map)); - keymap_count = 7; + memcpy(shift_map, mac_shift_map, sizeof(shift_map)); + memcpy(altgr_map, mac_altgr_map, sizeof(altgr_map)); + memcpy(ctrl_map, mac_ctrl_map, sizeof(ctrl_map)); + memcpy(shift_ctrl_map, mac_shift_ctrl_map, sizeof(shift_ctrl_map)); + memcpy(alt_map, mac_alt_map, sizeof(alt_map)); + memcpy(ctrl_alt_map, mac_ctrl_alt_map, sizeof(ctrl_alt_map)); /* initialize mouse interrupt hook */ mac_mouse_interrupt_hook = NULL; diff --git a/arch/mips/jazz/g364.c b/arch/mips/jazz/g364.c index 92522dcd1..3b9788133 100644 --- a/arch/mips/jazz/g364.c +++ b/arch/mips/jazz/g364.c @@ -10,6 +10,8 @@ * Olivetti M700-10 ie. an Inmos G364 based card in a dedicated video slot, * 2MB dual ported VRAM with a 64 bit data path, 256 color lookup table, * palette of 16.7M and a user definable 64x64 hardware cursor. + * + * $Id: g364.c,v 1.7 1998/03/27 08:53:38 ralf Exp $ */ #include <linux/config.h> #include <linux/sched.h> diff --git a/arch/mips/jazz/hw-access.c b/arch/mips/jazz/hw-access.c index 8765be1a7..c9c423a66 100644 --- a/arch/mips/jazz/hw-access.c +++ b/arch/mips/jazz/hw-access.c @@ -7,9 +7,10 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: hw-access.c,v 1.5 1997/12/29 00:06:49 tsbogend Exp $ + * $Id: hw-access.c,v 1.6 1998/03/04 08:29:09 ralf Exp $ */ #include <linux/delay.h> +#include <linux/init.h> #include <linux/linkage.h> #include <linux/types.h> #include <linux/mm.h> @@ -173,7 +174,7 @@ static unsigned char jazz_read_status(void) return jazz_kh->command; } -void jazz_keyboard_setup(void) +__initfunc(void jazz_keyboard_setup(void)) { kbd_read_input = jazz_read_input; kbd_write_output = jazz_write_output; diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index afabc6156..413eb8a2f 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -7,7 +7,7 @@ * * Copyright (C) 1994, 1995 by Ralf Baechle * - * $Id: entry.S,v 1.10 1998/03/26 07:39:09 ralf Exp $ + * $Id: entry.S,v 1.8 1998/03/27 04:47:53 ralf Exp $ */ /* @@ -104,7 +104,8 @@ LEAF(spurious_interrupt) cfc1 a1,fcr31; \ li a2,~(0x3f<<12); \ and a2,a1; \ - ctc1 a2,fcr31; + ctc1 a2,fcr31; \ + STI #define __BUILD_clear_ade(exception) \ MFC0 t0,CP0_BADVADDR; \ REG_S t0,PT_BVADDR(sp); \ diff --git a/arch/mips/kernel/fpe.c b/arch/mips/kernel/fpe.c index ee47f016f..8491c95d1 100644 --- a/arch/mips/kernel/fpe.c +++ b/arch/mips/kernel/fpe.c @@ -6,7 +6,7 @@ * * Copyright (C) 1997 Ralf Baechle * - * $Id: fpe.c,v 1.1 1997/08/11 04:17:18 ralf Exp $ + * $Id: fpe.c,v 1.2 1997/12/01 17:57:26 ralf Exp $ */ #include <linux/kernel.h> #include <linux/module.h> @@ -18,7 +18,7 @@ #include <asm/branch.h> #include <asm/ptrace.h> -MODULE_AUTHOR("Ralf Baechle <ralf@gnu.ai.mit.edu>"); +MODULE_AUTHOR("Ralf Baechle <ralf@gnu.org>"); MODULE_DESCRIPTION("Experimental floating point exception handler"); MODULE_SUPPORTED_DEVICE("MIPS FPU"); diff --git a/arch/mips/kernel/irixinv.c b/arch/mips/kernel/irixinv.c index 3fa785b1d..105b29f23 100644 --- a/arch/mips/kernel/irixinv.c +++ b/arch/mips/kernel/irixinv.c @@ -5,9 +5,10 @@ * * Miguel de Icaza, 1997. * - * $Id$ + * $Id: irixinv.c,v 1.2 1997/12/06 23:52:04 ralf Exp $ */ #include <linux/mm.h> +#include <linux/init.h> #include <linux/slab.h> #include <asm/uaccess.h> #include <asm/inventory.h> @@ -52,8 +53,7 @@ dump_inventory_to_user (void *userbuf, int size) return inventory_items * sizeof (inventory_t); } -void -init_inventory (void) +__initfunc(void init_inventory (void)) { /* gross hack while we put the right bits all over the kernel * most likely this will not let just anyone run the X server diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 45d58730c..64c8cff16 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -7,7 +7,7 @@ * * Copyright (C) 1994 - 1998 by Ralf Baechle and others. * - * $Id: process.c,v 1.7 1998/03/22 20:43:43 ralf Exp $ + * $Id: process.c,v 1.7 1998/03/27 04:47:55 ralf Exp $ */ #include <linux/config.h> #include <linux/errno.h> @@ -51,10 +51,22 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) void exit_thread(void) { + /* Forget lazy fpu state */ + if (last_task_used_math == current) { + set_cp0_status(ST0_CU1, ST0_CU1); + __asm__ __volatile__("cfc1\t$0,$31"); + last_task_used_math = NULL; + } } void flush_thread(void) { + /* Forget lazy fpu state */ + if (last_task_used_math == current) { + set_cp0_status(ST0_CU1, ST0_CU1); + __asm__ __volatile__("cfc1\t$0,$31"); + last_task_used_math = NULL; + } } void release_thread(struct task_struct *dead_task) @@ -69,6 +81,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childksp = (unsigned long)p + KERNEL_STACK_SIZE - 32; + if (last_task_used_math == current) { + set_cp0_status(ST0_CU1, ST0_CU1); + r4xx0_save_fp(p); + } /* set up new TSS. */ childregs = (struct pt_regs *) childksp - 1; *childregs = *regs; @@ -84,14 +100,13 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, regs->regs[3] = 0; } if (childregs->cp0_status & ST0_CU0) { - childregs->regs[28] = p; + childregs->regs[28] = (unsigned long) p; childregs->regs[29] = childksp; p->tss.current_ds = KERNEL_DS; } else { childregs->regs[29] = usp; p->tss.current_ds = USER_DS; } - p->tss.ksp = childksp; p->tss.reg29 = (unsigned long) childregs; p->tss.reg31 = (unsigned long) ret_from_sys_call; @@ -100,7 +115,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, * switching for most programs since they don't use the fpu. */ p->tss.cp0_status = read_32bit_cp0_register(CP0_STATUS) & - ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU|ST0_ERL|ST0_EXL); + ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU); childregs->cp0_status &= ~(ST0_CU3|ST0_CU2|ST0_CU1); p->mm->context = 0; diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index ebd657d4c..b8d604d2d 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -342,8 +342,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) struct pt_regs *regs; unsigned long tmp; - regs = (struct pt_regs *) - (child->tss.ksp - sizeof(struct pt_regs)); + regs = (struct pt_regs *) ((unsigned long) child + + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); tmp = 0; /* Default return value. */ if(addr < 32 && addr >= 0) { tmp = regs->regs[addr]; @@ -398,8 +398,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) struct pt_regs *regs; int res = 0; - regs = (struct pt_regs *) - (child->tss.ksp - sizeof(struct pt_regs)); + regs = (struct pt_regs *) ((unsigned long) child + + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); if(addr < 32 && addr >= 0) { regs->regs[addr] = data; } else if(addr >= 32 && addr < 64) { diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S index f4470e54d..b2b55bf0b 100644 --- a/arch/mips/kernel/r2300_switch.S +++ b/arch/mips/kernel/r2300_switch.S @@ -6,7 +6,7 @@ * Multi-cpu abstraction and macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300_switch.S,v 1.3 1998/03/23 06:34:37 ralf Exp $ + * $Id: r2300_switch.S,v 1.3 1998/03/27 04:47:55 ralf Exp $ */ #include <asm/asm.h> #include <asm/bootinfo.h> @@ -83,8 +83,8 @@ MODE_ALIAS = 0x00e0 # uncachable, dirty, valid FPU_RESTORE($28, t0) 1: CPU_RESTORE_NONSCRATCH($28) - lw t0,THREAD_KSP($28) # Restore status register + addiu t0, $28, KERNEL_STACK_SIZE-32 sw t0,kernelsp jr ra - mtc0 a2,CP0_STATUS + mtc0 a2,CP0_STATUS # Restore status register END(r2300_resume) diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S index 72638d462..c37b90612 100644 --- a/arch/mips/kernel/r4k_fpu.S +++ b/arch/mips/kernel/r4k_fpu.S @@ -5,12 +5,12 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996 by Ralf Baechle + * Copyright (C) 1996, 1998 by Ralf Baechle * * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4k_fpu.S,v 1.3 1997/12/01 16:56:06 ralf Exp $ + * $Id: r4k_fpu.S,v 1.3 1997/12/01 17:57:30 ralf Exp $ */ #include <asm/asm.h> #include <asm/fpregdef.h> @@ -18,65 +18,63 @@ #include <asm/offset.h> #include <asm/regdef.h> +#define EX(a,b) \ +9: a,##b; \ + .section __ex_table,"a"; \ + PTR 9b,bad_stack; \ + .previous + .set noreorder .set mips3 /* Save floating point context */ LEAF(r4k_save_fp_context) mfc0 t1,CP0_STATUS - sll t2,t1,2 - bgez t2,2f sll t2,t1,5 - cfc1 t1,fcr31 bgez t2,1f - nop + cfc1 t1,fcr31 /* Store the 16 odd double precision registers */ - sdc1 $f1,(SC_FPREGS+8)(a0) - sdc1 $f3,(SC_FPREGS+24)(a0) - sdc1 $f5,(SC_FPREGS+40)(a0) - sdc1 $f7,(SC_FPREGS+56)(a0) - sdc1 $f9,(SC_FPREGS+72)(a0) - sdc1 $f11,(SC_FPREGS+88)(a0) - sdc1 $f13,(SC_FPREGS+104)(a0) - sdc1 $f15,(SC_FPREGS+120)(a0) - sdc1 $f17,(SC_FPREGS+136)(a0) - sdc1 $f19,(SC_FPREGS+152)(a0) - sdc1 $f21,(SC_FPREGS+168)(a0) - sdc1 $f23,(SC_FPREGS+184)(a0) - sdc1 $f25,(SC_FPREGS+200)(a0) - sdc1 $f27,(SC_FPREGS+216)(a0) - sdc1 $f29,(SC_FPREGS+232)(a0) - sdc1 $f31,(SC_FPREGS+248)(a0) + EX(sdc1 $f1,(SC_FPREGS+8)(a0)) + EX(sdc1 $f3,(SC_FPREGS+24)(a0)) + EX(sdc1 $f5,(SC_FPREGS+40)(a0)) + EX(sdc1 $f7,(SC_FPREGS+56)(a0)) + EX(sdc1 $f9,(SC_FPREGS+72)(a0)) + EX(sdc1 $f11,(SC_FPREGS+88)(a0)) + EX(sdc1 $f13,(SC_FPREGS+104)(a0)) + EX(sdc1 $f15,(SC_FPREGS+120)(a0)) + EX(sdc1 $f17,(SC_FPREGS+136)(a0)) + EX(sdc1 $f19,(SC_FPREGS+152)(a0)) + EX(sdc1 $f21,(SC_FPREGS+168)(a0)) + EX(sdc1 $f23,(SC_FPREGS+184)(a0)) + EX(sdc1 $f25,(SC_FPREGS+200)(a0)) + EX(sdc1 $f27,(SC_FPREGS+216)(a0)) + EX(sdc1 $f29,(SC_FPREGS+232)(a0)) + EX(sdc1 $f31,(SC_FPREGS+248)(a0)) /* Store the 16 even double precision registers */ 1: - sdc1 $f0,(SC_FPREGS+0)(a0) - sdc1 $f2,(SC_FPREGS+16)(a0) - sdc1 $f4,(SC_FPREGS+32)(a0) - sdc1 $f6,(SC_FPREGS+48)(a0) - sdc1 $f8,(SC_FPREGS+64)(a0) - sdc1 $f10,(SC_FPREGS+80)(a0) - sdc1 $f12,(SC_FPREGS+96)(a0) - sdc1 $f14,(SC_FPREGS+112)(a0) - sdc1 $f16,(SC_FPREGS+128)(a0) - sdc1 $f18,(SC_FPREGS+144)(a0) - sdc1 $f20,(SC_FPREGS+160)(a0) - sdc1 $f22,(SC_FPREGS+176)(a0) - sdc1 $f24,(SC_FPREGS+192)(a0) - sdc1 $f26,(SC_FPREGS+208)(a0) - sdc1 $f28,(SC_FPREGS+224)(a0) - sdc1 $f30,(SC_FPREGS+240)(a0) - sw t1,SC_FPC_CSR(a0) + EX(sdc1 $f0,(SC_FPREGS+0)(a0)) + EX(sdc1 $f2,(SC_FPREGS+16)(a0)) + EX(sdc1 $f4,(SC_FPREGS+32)(a0)) + EX(sdc1 $f6,(SC_FPREGS+48)(a0)) + EX(sdc1 $f8,(SC_FPREGS+64)(a0)) + EX(sdc1 $f10,(SC_FPREGS+80)(a0)) + EX(sdc1 $f12,(SC_FPREGS+96)(a0)) + EX(sdc1 $f14,(SC_FPREGS+112)(a0)) + EX(sdc1 $f16,(SC_FPREGS+128)(a0)) + EX(sdc1 $f18,(SC_FPREGS+144)(a0)) + EX(sdc1 $f20,(SC_FPREGS+160)(a0)) + EX(sdc1 $f22,(SC_FPREGS+176)(a0)) + EX(sdc1 $f24,(SC_FPREGS+192)(a0)) + EX(sdc1 $f26,(SC_FPREGS+208)(a0)) + EX(sdc1 $f28,(SC_FPREGS+224)(a0)) + EX(sdc1 $f30,(SC_FPREGS+240)(a0)) + EX(sw t1,SC_FPC_CSR(a0)) cfc1 t0,$0 # implementation/version jr ra .set nomacro - sw t0,SC_FPC_EIR(a0) - .set macro -2: - jr ra - .set nomacro - nop + EX(sw t0,SC_FPC_EIR(a0)) .set macro END(r4k_save_fp_context) @@ -90,56 +88,51 @@ LEAF(r4k_save_fp_context) * stack frame which might have been changed by the user. */ LEAF(r4k_restore_fp_context) - mfc0 t1,CP0_STATUS - sll t0,t1,2 - bgez t0,2f - sll t0,t1,5 - + mfc0 t1, CP0_STATUS + sll t0,t1,5 bgez t0,1f - lw t0,SC_FPC_CSR(a0) + EX(lw t0,SC_FPC_CSR(a0)) + /* Restore the 16 odd double precision registers only * when enabled in the cp0 status register. */ - ldc1 $f1,(SC_FPREGS+8)(a0) - ldc1 $f3,(SC_FPREGS+24)(a0) - ldc1 $f5,(SC_FPREGS+40)(a0) - ldc1 $f7,(SC_FPREGS+56)(a0) - ldc1 $f9,(SC_FPREGS+72)(a0) - ldc1 $f11,(SC_FPREGS+88)(a0) - ldc1 $f13,(SC_FPREGS+104)(a0) - ldc1 $f15,(SC_FPREGS+120)(a0) - ldc1 $f17,(SC_FPREGS+136)(a0) - ldc1 $f19,(SC_FPREGS+152)(a0) - ldc1 $f21,(SC_FPREGS+168)(a0) - ldc1 $f23,(SC_FPREGS+184)(a0) - ldc1 $f25,(SC_FPREGS+200)(a0) - ldc1 $f27,(SC_FPREGS+216)(a0) - ldc1 $f29,(SC_FPREGS+232)(a0) - ldc1 $f31,(SC_FPREGS+248)(a0) + EX(ldc1 $f1,(SC_FPREGS+8)(a0)) + EX(ldc1 $f3,(SC_FPREGS+24)(a0)) + EX(ldc1 $f5,(SC_FPREGS+40)(a0)) + EX(ldc1 $f7,(SC_FPREGS+56)(a0)) + EX(ldc1 $f9,(SC_FPREGS+72)(a0)) + EX(ldc1 $f11,(SC_FPREGS+88)(a0)) + EX(ldc1 $f13,(SC_FPREGS+104)(a0)) + EX(ldc1 $f15,(SC_FPREGS+120)(a0)) + EX(ldc1 $f17,(SC_FPREGS+136)(a0)) + EX(ldc1 $f19,(SC_FPREGS+152)(a0)) + EX(ldc1 $f21,(SC_FPREGS+168)(a0)) + EX(ldc1 $f23,(SC_FPREGS+184)(a0)) + EX(ldc1 $f25,(SC_FPREGS+200)(a0)) + EX(ldc1 $f27,(SC_FPREGS+216)(a0)) + EX(ldc1 $f29,(SC_FPREGS+232)(a0)) + EX(ldc1 $f31,(SC_FPREGS+248)(a0)) /* * Restore the 16 even double precision registers * when cp1 was enabled in the cp0 status register. */ -1: ldc1 $f0,(SC_FPREGS+0)(a0) - ldc1 $f2,(SC_FPREGS+16)(a0) - ldc1 $f4,(SC_FPREGS+32)(a0) - ldc1 $f6,(SC_FPREGS+48)(a0) - ldc1 $f8,(SC_FPREGS+64)(a0) - ldc1 $f10,(SC_FPREGS+80)(a0) - ldc1 $f12,(SC_FPREGS+96)(a0) - ldc1 $f14,(SC_FPREGS+112)(a0) - ldc1 $f16,(SC_FPREGS+128)(a0) - ldc1 $f18,(SC_FPREGS+144)(a0) - ldc1 $f20,(SC_FPREGS+160)(a0) - ldc1 $f22,(SC_FPREGS+176)(a0) - ldc1 $f24,(SC_FPREGS+192)(a0) - ldc1 $f26,(SC_FPREGS+208)(a0) - ldc1 $f28,(SC_FPREGS+224)(a0) - ldc1 $f30,(SC_FPREGS+240)(a0) +1: EX(ldc1 $f0,(SC_FPREGS+0)(a0)) + EX(ldc1 $f2,(SC_FPREGS+16)(a0)) + EX(ldc1 $f4,(SC_FPREGS+32)(a0)) + EX(ldc1 $f6,(SC_FPREGS+48)(a0)) + EX(ldc1 $f8,(SC_FPREGS+64)(a0)) + EX(ldc1 $f10,(SC_FPREGS+80)(a0)) + EX(ldc1 $f12,(SC_FPREGS+96)(a0)) + EX(ldc1 $f14,(SC_FPREGS+112)(a0)) + EX(ldc1 $f16,(SC_FPREGS+128)(a0)) + EX(ldc1 $f18,(SC_FPREGS+144)(a0)) + EX(ldc1 $f20,(SC_FPREGS+160)(a0)) + EX(ldc1 $f22,(SC_FPREGS+176)(a0)) + EX(ldc1 $f24,(SC_FPREGS+192)(a0)) + EX(ldc1 $f26,(SC_FPREGS+208)(a0)) + EX(ldc1 $f28,(SC_FPREGS+224)(a0)) + EX(ldc1 $f30,(SC_FPREGS+240)(a0)) jr ra ctc1 t0,fcr31 - -2: jr ra - nop END(r4k_restore_fp_context) diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index cb7f9891f..765de0d85 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -5,6 +5,8 @@ * * Multi-cpu abstraction and macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r4k_switch.S,v 1.3 1998/04/04 13:59:38 ralf Exp $ */ #include <asm/asm.h> #include <asm/bootinfo.h> @@ -13,6 +15,7 @@ #include <asm/fpregdef.h> #include <asm/mipsconfig.h> #include <asm/mipsregs.h> +#include <asm/offset.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> @@ -25,43 +28,139 @@ .set mips3 .align 5 LEAF(r4xx0_resume) - mfc0 t1, CP0_STATUS - sw t1, THREAD_STATUS($28) - ori t2, t1, 0x1f - xori t2, t2, 0x1e - mtc0 t2, CP0_STATUS + mfc0 t1, CP0_STATUS # fp exception boundary + sll t0, t1, 2 + bgez t0, 1f + nop + cfc1 zero, fcr31 +1: sw t1, THREAD_STATUS($28) CPU_SAVE_NONSCRATCH($28) - sll t2, t1, 2 # Save floating point state - bgez t2, 2f - sw ra, THREAD_REG31($28) - sll t2, t1, 5 - bgez t2, 1f - swc1 $f0, (THREAD_FPU + 0x00)($28) - FPU_SAVE_16ODD($28) -1: - FPU_SAVE_16EVEN($28, t1) # clobbers t1 -2: + sw ra, THREAD_REG31($28) + + /* + * The order of restoring the registers takes care of the race + * updating $28, $29 and kernelsp without disabling ints. + */ move $28, a0 + CPU_RESTORE_NONSCRATCH($28) + addiu t0, $28, KERNEL_STACK_SIZE-32 + sw t0, kernelsp lw a3, TASK_MM($28) lw a2, THREAD_STATUS($28) lw a3, MM_CONTEXT(a3) - ori t1, a2, 1 # restore fpu, pipeline magic + mtc0 a2, CP0_STATUS andi a3, a3, 0xff - xori t1, t1, 1 - mtc0 a3, CP0_ENTRYHI - mtc0 t1, CP0_STATUS - sll t0, a2, 2 - bgez t0, 2f - sll t0, a2, 5 + jr ra + mtc0 a3, CP0_ENTRYHI + END(r4xx0_resume) + +/* + * Do lazy fpu context switch. Saves FPU context to the process in a0 + * and loads the new context of the current process. + */ + +#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) + +LEAF(r4xx0_lazy_fpu_switch) + mfc0 t0, CP0_STATUS # enable cp1 + li t3, 0x20000000 + or t0, t3 + mtc0 t0, CP0_STATUS + + beqz a0, 2f # Save floating point state + nor t3, zero, t3 + lw t1, ST_OFF(a0) # last thread looses fpu + and t1, t3 + sw t1, ST_OFF(a0) + sll t2, t1, 5 + bgez t2, 1f + sdc1 $f0, (THREAD_FPU + 0x00)(a0) + FPU_SAVE_16ODD(a0) +1: + FPU_SAVE_16EVEN(a0, t1) # clobbers t1 +2: + + sll t0, t0, 5 # load new fp state bgez t0, 1f - lwc1 $f0, (THREAD_FPU + 0x00)($28) + ldc1 $f0, (THREAD_FPU + 0x00)($28) FPU_RESTORE_16ODD($28) 1: - FPU_RESTORE_16EVEN($28, t0) # clobbers t0 -2: - CPU_RESTORE_NONSCRATCH($28) - lw t0, THREAD_KSP($28) - sw t0, kernelsp + .set reorder + FPU_RESTORE_16EVEN($28, t0) # clobbers t0 jr ra - mtc0 a2, CP0_STATUS - END(r4xx0_resume) + END(r4xx0_lazy_fpu_switch) + +/* + * Save a thread's fp context. + */ + .set noreorder +LEAF(r4xx0_save_fp) + mfc0 t0, CP0_STATUS + sll t1, t0, 5 + bgez t1, 1f # 16 register mode? + nop + FPU_SAVE_16ODD(a0) +1: + FPU_SAVE_16EVEN(a0, t1) # clobbers t1 + jr ra + sdc1 $f0, (THREAD_FPU + 0x00)(a0) + END(r4xx0_save_fp) + +/* + * Load the FPU with signalling NANS. This bit pattern we're using has + * the property that no matter wether considered as single or as double + * precission represents signaling NANS. + * + * We initialize fcr31 to rounding to nearest, no exceptions. + */ + +#define FPU_DEFAULT 0x00000600 + +LEAF(r4xx0_init_fpu) + mfc0 t0, CP0_STATUS + li t1, 0x20000000 + or t0, t1 + mtc0 t0, CP0_STATUS + sll t0, t0, 5 + + li t1, FPU_DEFAULT + ctc1 t1, fcr31 + + bgez t0, 1f # 16 / 32 register mode? + li t0, -1 + + dmtc1 t0, $f1 + dmtc1 t0, $f3 + dmtc1 t0, $f5 + dmtc1 t0, $f7 + dmtc1 t0, $f9 + dmtc1 t0, $f11 + dmtc1 t0, $f13 + dmtc1 t0, $f15 + dmtc1 t0, $f17 + dmtc1 t0, $f19 + dmtc1 t0, $f21 + dmtc1 t0, $f23 + dmtc1 t0, $f25 + dmtc1 t0, $f27 + dmtc1 t0, $f29 + dmtc1 t0, $f31 + +1: dmtc1 t0, $f0 + dmtc1 t0, $f2 + dmtc1 t0, $f4 + dmtc1 t0, $f6 + dmtc1 t0, $f8 + dmtc1 t0, $f10 + dmtc1 t0, $f12 + dmtc1 t0, $f14 + dmtc1 t0, $f16 + dmtc1 t0, $f18 + dmtc1 t0, $f20 + dmtc1 t0, $f22 + dmtc1 t0, $f24 + dmtc1 t0, $f26 + dmtc1 t0, $f28 + jr ra + dmtc1 t0, $f30 + END(r4xx0_init_fpu) diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 007e95452..954e8bfb0 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -4,7 +4,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996 Ralf Baechle * - * $Id: signal.c,v 1.17 1998/03/26 07:39:10 ralf Exp $ + * $Id: signal.c,v 1.11 1998/03/27 04:47:55 ralf Exp $ + * + * XXX Handle lazy fp context switches correctly. */ #include <linux/config.h> #include <linux/sched.h> @@ -125,17 +127,10 @@ asmlinkage void restore_sigcontext(struct pt_regs *regs, struct sigcontext *context) { long long reg; - int i; + int owned_fp; __get_user(regs->cp0_epc, &context->sc_pc); - /* - * Restore all integer registers. - */ - for(i = 31;i >= 0;i--) { - __get_user(reg, &context->sc_regs[i]); - regs->regs[i] = (int) reg; - } __get_user(reg, &context->sc_mdhi); regs->hi = (int) reg; __get_user(reg, &context->sc_mdlo); @@ -156,11 +151,15 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *context) restore_gp_reg(31); #undef restore_gp_reg - /* - * FP depends on what FPU in what mode we have. Best done in - * Assembler ... - */ - restore_fp_context(context); + /* FP depends on what FPU in what mode we have. */ + __get_user(owned_fp, &context->sc_ownedfp); +#if 0 + if (owned_fp) { + restore_fp_context(context); + last_task_used_math = current; + } +#endif +restore_fp_context(context); } /* @@ -191,8 +190,16 @@ asmlinkage int sys_sigreturn(struct pt_regs regs) (regs.regs[29] & (SZREG - 1))) goto badframe; +#if 1 + if (__get_user(blocked.sig[0], &context->sc_sigset[0]) || + __get_user(blocked.sig[1], &context->sc_sigset[1]) || + __get_user(blocked.sig[2], &context->sc_sigset[2]) || + __get_user(blocked.sig[3], &context->sc_sigset[3])) + goto badframe; +#else if (__copy_from_user(&blocked, &context->sc_sigset, sizeof(blocked))) goto badframe; +#endif sigdelsetmask(&blocked, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); @@ -251,6 +258,8 @@ setup_trampoline(unsigned int *code) static void inline setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc, sigset_t *set) { + int owned_fp; + __put_user(regs->cp0_epc, &sc->sc_pc); __put_user(regs->cp0_status, &sc->sc_status); /* Status register */ @@ -266,13 +275,29 @@ setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc, sigset_t *set) save_gp_reg(31); #undef save_gp_reg - save_fp_context(sc); /* cpu dependant */ __put_user(regs->hi, &sc->sc_mdhi); __put_user(regs->lo, &sc->sc_mdlo); __put_user(regs->cp0_cause, &sc->sc_cause); - __put_user((regs->cp0_status & ST0_CU1) != 0, &sc->sc_ownedfp); - __copy_to_user(sc->sc_sigset, set, sizeof(*set)); + owned_fp = (current == last_task_used_math); + __put_user(owned_fp, &sc->sc_ownedfp); + +#if 0 + if (current->used_math) { /* fp is active. */ + set_cp0_status(ST0_CU1, ST0_CU1); + save_fp_context(sc); /* cpu dependant */ + last_task_used_math = NULL; + regs->cp0_status &= ~ST0_CU1; + current->used_math = 0; + } +#endif +set_cp0_status(ST0_CU1, ST0_CU1); +save_fp_context(sc); /* cpu dependant */ + + __put_user(set->sig[0], &sc->sc_sigset[0]); + __put_user(set->sig[1], &sc->sc_sigset[1]); + __put_user(set->sig[2], &sc->sc_sigset[2]); + __put_user(set->sig[3], &sc->sc_sigset[3]); } static void inline diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 629a07836..b554cad39 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -8,11 +8,12 @@ * Copyright 1994, 1995, 1996, 1997 by Ralf Baechle * Modified for R3000 by Paul M. Antoine, 1995, 1996 * - * $Id: traps.c,v 1.12 1998/03/26 07:39:11 ralf Exp $ + * $Id: traps.c,v 1.9 1998/03/27 04:47:56 ralf Exp $ */ #include <linux/config.h> #include <linux/init.h> #include <linux/mm.h> +#include <linux/sched.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -229,6 +230,9 @@ int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) } #endif +/* + * XXX Delayed fp exceptions when doing a lazy ctx switch XXX + */ void do_fpe(struct pt_regs *regs, unsigned long fcr31) { #ifdef CONFIG_MIPS_FPE_MODULE @@ -257,6 +261,7 @@ void do_fpe(struct pt_regs *regs, unsigned long fcr31) printk("Unimplemented exception at 0x%08lx in %s.\n", regs->cp0_epc, current->comm); } + if (compute_return_epc(regs)) goto out; force_sig(SIGFPE, current); @@ -354,11 +359,24 @@ void do_cpu(struct pt_regs *regs) unsigned int cpid; cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; - if (cpid == 1) { - regs->cp0_status |= ST0_CU1; + if (cpid != 1) + goto bad_cid; + + regs->cp0_status |= ST0_CU1; + if (last_task_used_math == current) goto out; + + if (current->used_math) { /* Using the FPU again. */ + r4xx0_lazy_fpu_switch(last_task_used_math); + } else { /* First time FPU user. */ + + r4xx0_init_fpu(); + current->used_math = 1; } + last_task_used_math = current; + return; +bad_cid: lock_kernel(); force_sig(SIGILL, current); unlock_kernel(); @@ -369,8 +387,9 @@ void do_vcei(struct pt_regs *regs) { lock_kernel(); /* - * Only possible on R4[04]00[SM]C. No handler because I don't have - * such a cpu. Theory says this exception doesn't happen. + * Theory says this exception doesn't happen. + * + * Murphy is right. It does happen ... */ panic("Caught VCEI exception - should not happen"); unlock_kernel(); @@ -380,10 +399,11 @@ void do_vced(struct pt_regs *regs) { lock_kernel(); /* - * Only possible on R4[04]00[SM]C. No handler because I don't have - * such a cpu. Theory says this exception doesn't happen. + * Theory says this exception doesn't happen. + * + * Murphy is right. It does happen ... */ - panic("Caught VCE exception - should not happen"); + panic("Caught VCED exception - should not happen"); unlock_kernel(); } @@ -527,8 +547,14 @@ __initfunc(void trap_init(void)) case CPU_R4400MC: case CPU_R4000SC: case CPU_R4400SC: - /* XXX The following won't work because we _cannot_ - * XXX perform any load/store before the VCE handler. + /* + * The following won't work because we _cannot_ perform any + * load/store before the VCE handler. We deal with this + * by checking for for vced / vcei exceptions before doing + * the generic exception handling thing. This costs us + * several instructions, therefore there should be a special + * handler for those CPUs which have these exceptions. + * */ set_except_vector(14, handle_vcei); set_except_vector(31, handle_vced); diff --git a/arch/mips/ld.script.big b/arch/mips/ld.script.big index 88da74972..7181d0886 100644 --- a/arch/mips/ld.script.big +++ b/arch/mips/ld.script.big @@ -37,6 +37,15 @@ SECTIONS } =0 _etext = .; PROVIDE (etext = .); + + /* Startup code */ + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); /* Align double page for init_task_union */ + __init_end = .; + .fini : { *(.fini) } =0 .reginfo : { *(.reginfo) } /* Adjust the address for the data segment. We want to adjust up to @@ -74,6 +83,7 @@ SECTIONS .sdata : { *(.sdata) } _edata = .; PROVIDE (edata = .); + __bss_start = .; _fbss = .; .bss : diff --git a/arch/mips/ld.script.little b/arch/mips/ld.script.little index 26464d9f7..7e638d739 100644 --- a/arch/mips/ld.script.little +++ b/arch/mips/ld.script.little @@ -37,6 +37,15 @@ SECTIONS } =0 _etext = .; PROVIDE (etext = .); + + /* Startup code */ + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); /* Align double page for init_task_union */ + __init_end = .; + .fini : { *(.fini) } =0 .reginfo : { *(.reginfo) } /* Adjust the address for the data segment. We want to adjust up to @@ -74,6 +83,7 @@ SECTIONS .sdata : { *(.sdata) } _edata = .; PROVIDE (edata = .); + __bss_start = .; _fbss = .; .bss : diff --git a/arch/mips/mm/andes.c b/arch/mips/mm/andes.c index 529c12465..05150aa83 100644 --- a/arch/mips/mm/andes.c +++ b/arch/mips/mm/andes.c @@ -3,13 +3,12 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: andes.c,v 1.3 1998/03/21 08:03:53 ralf Exp $ + * $Id: andes.c,v 1.3 1998/03/22 23:27:14 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> - #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -89,10 +88,10 @@ static void andes_add_wired_entry(unsigned long entrylo0, unsigned long entrylo1 static void andes_user_mode(struct pt_regs *regs) { - return regs->cp0_status & ST0_KSU == KSU_USER; + return (regs->cp0_status & ST0_KSU) == KSU_USER; } -void ld_mmu_andes(void) +__initfunc(void ld_mmu_andes(void)) { flush_cache_all = andes_flush_cache_all; flush_cache_mm = andes_flush_cache_mm; diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 3e309e438..dd2fcbad2 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -5,9 +5,10 @@ * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle * - * $Id: init.c,v 1.4 1998/03/21 08:01:45 ralf Exp $ + * $Id: init.c,v 1.4 1998/03/22 23:27:15 ralf Exp $ */ #include <linux/config.h> +#include <linux/init.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/head.h> @@ -155,13 +156,13 @@ void show_mem(void) extern unsigned long free_area_init(unsigned long, unsigned long); -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) { pgd_init((unsigned long)swapper_pg_dir); return free_area_init(start_mem, end_mem); } -void mem_init(unsigned long start_mem, unsigned long end_mem) +__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) { int codepages = 0; int datapages = 0; @@ -178,7 +179,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) high_memory = (void *)end_mem; /* clear the zero-page */ - clear_page(empty_zero_page); + clear_page((unsigned long)empty_zero_page); /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); @@ -225,9 +226,20 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) return; } +extern char __init_begin, __init_end; + void free_initmem(void) { - /* To be written */ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + atomic_set(&mem_map[MAP_NR(addr)].count, 1); + free_page(addr); + } + printk("Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); } void si_meminfo(struct sysinfo *val) diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index 9f4624ba6..f8730ad26 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -3,9 +3,9 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: loadmmu.c,v 1.5 1998/03/03 16:57:25 ralf Exp $ + * $Id: loadmmu.c,v 1.6 1998/03/22 23:27:15 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -61,7 +61,7 @@ extern void ld_mmu_r6000(void); extern void ld_mmu_tfp(void); extern void ld_mmu_andes(void); -void loadmmu(void) +__initfunc(void loadmmu(void)) { switch(mips_cputype) { case CPU_R2000: diff --git a/arch/mips/mm/r2300.c b/arch/mips/mm/r2300.c index 822cb1a1a..925c5222b 100644 --- a/arch/mips/mm/r2300.c +++ b/arch/mips/mm/r2300.c @@ -3,9 +3,9 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300.c,v 1.3 1997/07/29 22:54:51 tsbogend Exp $ + * $Id: r2300.c,v 1.4 1998/03/22 23:27:15 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -258,7 +258,7 @@ static int r2300_user_mode(struct pt_regs *regs) return !(regs->cp0_status & 0x4); } -void ld_mmu_r2300(void) +__initfunc(void ld_mmu_r2300(void)) { clear_page = r2300_clear_page; copy_page = r2300_copy_page; diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c index 2cd1c9236..0bfa42c3a 100644 --- a/arch/mips/mm/r4xx0.c +++ b/arch/mips/mm/r4xx0.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4xx0.c,v 1.13 1998/03/18 17:18:13 ralf Exp $ + * $Id: r4xx0.c,v 1.14 1998/03/22 23:27:16 ralf Exp $ * * To do: * @@ -11,10 +11,10 @@ * - many of the bug workarounds are not efficient at all, but at * least they are functional ... */ +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> -#include <linux/autoconf.h> #include <asm/bcache.h> #include <asm/io.h> @@ -65,9 +65,19 @@ struct bcache_ops *bcops = &no_sc_ops; #define dcache_waybit (dcache_size >> 1) /* - * Zero an entire page. We have three flavours of the routine available. - * One for CPU with 16byte, with 32byte cachelines plus a special version - * with nops which handles the buggy R4600 v1.x. + * Zero an entire page. Basically a simple unrolled loop should do the + * job but we want more performance by saving memory bus bandwidth. We + * have five flavours of the routine available for: + * + * - 16byte cachelines and no second level cache + * - 32byte cachelines second level cache + * - a version which handles the buggy R4600 v1.x + * - a version which handles the buggy R4600 v2.0 + * - Finally a last version without fancy cache games for the SC and MC + * versions of R4000 and R4400. Cache instructions are quite expensive + * and I guess using them for both the primary and the second level cache + * wouldn't be worth the effort. + * This needs to be verified by benchmarking. */ static void r4k_clear_page_d16(unsigned long page) @@ -231,6 +241,58 @@ static void r4k_clear_page_r4600_v2(unsigned long page) restore_flags(flags); } +static void r4k_clear_page(unsigned long page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tsd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE) + :"$1","memory"); +} + +static void r4k_clear_page(unsigned long page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tsd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE) + :"$1","memory"); +} + /* * This is still inefficient. We only can do better if we know the @@ -489,6 +551,114 @@ static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) restore_flags(flags); } +static void r4k_copy_page(unsigned long to, unsigned long from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%8\n" + "1:\tlw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "daddiu\t%0,64\n\t" + "daddiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE)); +} + +static void r4k_copy_page(unsigned long to, unsigned long from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%8\n" + "1:\tlw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "daddiu\t%0,64\n\t" + "daddiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE)); +} + /* * If you think for one second that this stuff coming up is a lot * of bulky code eating too many kernel cache lines. Think _again_. @@ -1951,9 +2121,9 @@ r4k_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size) a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { - flush_scache_line(addr); /* Hit_Writeback_Inv_SD */ - if (addr == end) break; - addr += sc_lsize; + flush_scache_line(a); /* Hit_Writeback_Inv_SD */ + if (a == end) break; + a += sc_lsize; } } @@ -2006,9 +2176,9 @@ r4k_dma_cache_inv_sc(unsigned long addr, unsigned long size) a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { - flush_scache_line(addr); /* Hit_Writeback_Inv_SD */ - if (addr == end) break; - addr += sc_lsize; + flush_scache_line(a); /* Hit_Writeback_Inv_SD */ + if (a == end) break; + a += sc_lsize; } } @@ -2373,7 +2543,7 @@ static void r4k_add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, } /* Detect and size the various r4k caches. */ -static void probe_icache(unsigned long config) +__initfunc(static void probe_icache(unsigned long config)) { icache_size = 1 << (12 + ((config >> 6) & 7)); ic_lsize = 16 << ((config >> 4) & 1); @@ -2382,7 +2552,7 @@ static void probe_icache(unsigned long config) icache_size >> 10, ic_lsize); } -static void probe_dcache(unsigned long config) +__initfunc(static void probe_dcache(unsigned long config)) { dcache_size = 1 << (12 + ((config >> 6) & 7)); dc_lsize = 16 << ((config >> 4) & 1); @@ -2397,7 +2567,7 @@ static void probe_dcache(unsigned long config) * the cache sizing loop that executes in KSEG1 space or else * you will crash and burn badly. You have been warned. */ -static int probe_scache(unsigned long config) +__initfunc(static int probe_scache(unsigned long config)) { extern unsigned long stext; unsigned long flags, addr, begin, end, pow2; @@ -2481,7 +2651,7 @@ static int probe_scache(unsigned long config) return 1; } -static void setup_noscache_funcs(void) +__initfunc(static void setup_noscache_funcs(void)) { unsigned int prid; @@ -2524,8 +2694,6 @@ static void setup_scache_funcs(void) case 16: switch(dc_lsize) { case 16: - clear_page = r4k_clear_page_d16; - copy_page = r4k_copy_page_d16; flush_cache_all = r4k_flush_cache_all_s16d16i16; flush_cache_mm = r4k_flush_cache_mm_s16d16i16; flush_cache_range = r4k_flush_cache_range_s16d16i16; @@ -2533,8 +2701,6 @@ static void setup_scache_funcs(void) flush_page_to_ram = r4k_flush_page_to_ram_s16d16i16; break; case 32: - clear_page = r4k_clear_page_d32; - copy_page = r4k_copy_page_d32; flush_cache_all = r4k_flush_cache_all_s16d32i32; flush_cache_mm = r4k_flush_cache_mm_s16d32i32; flush_cache_range = r4k_flush_cache_range_s16d32i32; @@ -2546,8 +2712,6 @@ static void setup_scache_funcs(void) case 32: switch(dc_lsize) { case 16: - clear_page = r4k_clear_page_d16; - copy_page = r4k_copy_page_d16; flush_cache_all = r4k_flush_cache_all_s32d16i16; flush_cache_mm = r4k_flush_cache_mm_s32d16i16; flush_cache_range = r4k_flush_cache_range_s32d16i16; @@ -2555,8 +2719,6 @@ static void setup_scache_funcs(void) flush_page_to_ram = r4k_flush_page_to_ram_s32d16i16; break; case 32: - clear_page = r4k_clear_page_d32; - copy_page = r4k_copy_page_d32; flush_cache_all = r4k_flush_cache_all_s32d32i32; flush_cache_mm = r4k_flush_cache_mm_s32d32i32; flush_cache_range = r4k_flush_cache_range_s32d32i32; @@ -2567,8 +2729,6 @@ static void setup_scache_funcs(void) case 64: switch(dc_lsize) { case 16: - clear_page = r4k_clear_page_d16; - copy_page = r4k_copy_page_d16; flush_cache_all = r4k_flush_cache_all_s64d16i16; flush_cache_mm = r4k_flush_cache_mm_s64d16i16; flush_cache_range = r4k_flush_cache_range_s64d16i16; @@ -2576,8 +2736,6 @@ static void setup_scache_funcs(void) flush_page_to_ram = r4k_flush_page_to_ram_s64d16i16; break; case 32: - clear_page = r4k_clear_page_d32; - copy_page = r4k_copy_page_d32; flush_cache_all = r4k_flush_cache_all_s64d32i32; flush_cache_mm = r4k_flush_cache_mm_s64d32i32; flush_cache_range = r4k_flush_cache_range_s64d32i32; @@ -2588,8 +2746,6 @@ static void setup_scache_funcs(void) case 128: switch(dc_lsize) { case 16: - clear_page = r4k_clear_page_d16; - copy_page = r4k_copy_page_d16; flush_cache_all = r4k_flush_cache_all_s128d16i16; flush_cache_mm = r4k_flush_cache_mm_s128d16i16; flush_cache_range = r4k_flush_cache_range_s128d16i16; @@ -2597,8 +2753,6 @@ static void setup_scache_funcs(void) flush_page_to_ram = r4k_flush_page_to_ram_s128d16i16; break; case 32: - clear_page = r4k_clear_page_d32; - copy_page = r4k_copy_page_d32; flush_cache_all = r4k_flush_cache_all_s128d32i32; flush_cache_mm = r4k_flush_cache_mm_s128d32i32; flush_cache_range = r4k_flush_cache_range_s128d32i32; @@ -2608,6 +2762,8 @@ static void setup_scache_funcs(void) }; break; } + clear_page = r4k_clear_page; + copy_page = r4k_copy_page; dma_cache_wback_inv = r4k_dma_cache_wback_inv_sc; dma_cache_inv = r4k_dma_cache_inv_sc; } @@ -2637,7 +2793,7 @@ static int r4k_user_mode(struct pt_regs *regs) } -void ld_mmu_r4xx0(void) +__initfunc(void ld_mmu_r4xx0(void)) { unsigned long config = read_32bit_cp0_register(CP0_CONFIG); diff --git a/arch/mips/mm/r6000.c b/arch/mips/mm/r6000.c index d656c897c..748fa3293 100644 --- a/arch/mips/mm/r6000.c +++ b/arch/mips/mm/r6000.c @@ -1,9 +1,9 @@ -/* $Id: r6000.c,v 1.2 1997/07/29 22:54:52 tsbogend Exp $ +/* $Id: r6000.c,v 1.4 1998/03/27 08:53:42 ralf Exp $ * r6000.c: MMU and cache routines for the R6000 processors. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -167,7 +167,7 @@ static int r6000_user_mode(struct pt_regs *regs) return !(regs->cp0_status & 0x4); } -void ld_mmu_r6000(void) +__initfunc(void ld_mmu_r6000(void)) { flush_cache_all = r6000_flush_cache_all; flush_cache_mm = r6000_flush_cache_mm; diff --git a/arch/mips/mm/tfp.c b/arch/mips/mm/tfp.c index 931661f82..d1701c03a 100644 --- a/arch/mips/mm/tfp.c +++ b/arch/mips/mm/tfp.c @@ -1,9 +1,9 @@ -/* $Id: tfp.c,v 1.2 1997/07/29 22:54:53 tsbogend Exp $ +/* $Id: tfp.c,v 1.4 1998/03/27 08:53:42 ralf Exp $ * tfp.c: MMU and cache routines specific to the r8000 (TFP). * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -90,7 +90,7 @@ static int tfp_user_mode(struct pt_regs *regs) return regs->cp0_status & ST0_KSU == KSU_USER; } -void ld_mmu_tfp(void) +__initfunc(void ld_mmu_tfp(void)) { flush_cache_all = tfp_flush_cache_all; flush_cache_mm = tfp_flush_cache_mm; diff --git a/arch/mips/sgi/kernel/indy_hpc.c b/arch/mips/sgi/kernel/indy_hpc.c index 92d0ba669..eb00fe55c 100644 --- a/arch/mips/sgi/kernel/indy_hpc.c +++ b/arch/mips/sgi/kernel/indy_hpc.c @@ -1,8 +1,11 @@ -/* $Id: indy_hpc.c,v 1.4 1996/06/29 07:06:50 dm Exp $ +/* * indy_hpc.c: Routines for generic manipulation of the HPC controllers. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: indy_hpc.c,v 1.2 1998/03/27 08:53:43 ralf Exp $ */ +#include <linux/init.h> #include <asm/addrspace.h> #include <asm/ptrace.h> @@ -38,7 +41,7 @@ void sgihpc_write2_modify(int set, int clear) hpc3mregs->write2 = write2; } -void sgihpc_init(void) +__initfunc(void sgihpc_init(void)) { unsigned long sid, crev, brev; diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c index 0726ee179..950744328 100644 --- a/arch/mips/sgi/kernel/indy_int.c +++ b/arch/mips/sgi/kernel/indy_int.c @@ -4,10 +4,10 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_int.c,v 1.5 1997/12/01 17:57:38 ralf Exp $ + * $Id: indy_int.c,v 1.6 1998/03/17 22:07:41 ralf Exp $ */ #include <linux/config.h> - +#include <linux/init.h> #include <linux/errno.h> #include <linux/kernel_stat.h> #include <linux/signal.h> @@ -417,7 +417,7 @@ void free_irq(unsigned int irq, void *dev_id) printk("Trying to free free IRQ%d\n",irq); } -void init_IRQ(void) +__initfunc(void init_IRQ(void)) { irq_setup(); } @@ -495,7 +495,7 @@ int probe_irq_off (unsigned long irqs) return 0; } -void sgint_init(void) +__initfunc(void sgint_init(void)) { int i; #ifdef CONFIG_REMOTE_DEBUG diff --git a/arch/mips/sgi/kernel/indy_mc.c b/arch/mips/sgi/kernel/indy_mc.c index 449bb5b41..c34cc48f2 100644 --- a/arch/mips/sgi/kernel/indy_mc.c +++ b/arch/mips/sgi/kernel/indy_mc.c @@ -1,8 +1,11 @@ -/* $Id: indy_mc.c,v 1.5 1996/06/29 07:06:51 dm Exp $ +/* * indy_mc.c: Routines for manipulating the INDY memory controller. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: indy_mc.c,v 1.2 1998/03/27 08:53:44 ralf Exp $ */ +#include <linux/init.h> #include <asm/addrspace.h> #include <asm/ptrace.h> @@ -43,7 +46,7 @@ static inline char *mconfig_string(unsigned long val) }; } -void sgimc_init(void) +__initfunc(void sgimc_init(void)) { unsigned long tmpreg; diff --git a/arch/mips/sgi/kernel/indy_sc.c b/arch/mips/sgi/kernel/indy_sc.c index 3cee74968..b7466339a 100644 --- a/arch/mips/sgi/kernel/indy_sc.c +++ b/arch/mips/sgi/kernel/indy_sc.c @@ -4,10 +4,10 @@ * Copyright (C) 1997 Ralf Baechle (ralf@gnu.org), * derived from r4xx0.c by David S. Miller (dm@engr.sgi.com). * - * $Id: indy_sc.c,v 1.5 1998/03/26 07:33:13 ralf Exp $ + * $Id: indy_sc.c,v 1.2 1998/03/27 04:47:57 ralf Exp $ */ #include <linux/config.h> - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -161,7 +161,7 @@ static void indy_sc_disable(void) " : "=r" (tmp1), "=r" (tmp2), "=r" (tmp3)); } -static inline int indy_sc_probe(void) +__initfunc(static inline int indy_sc_probe(void)) { volatile unsigned int *cpu_control; unsigned short cmd = 0xc220; @@ -257,7 +257,7 @@ struct bcache_ops indy_sc_ops = { indy_sc_wback_invalidate }; -void indy_sc_init(void) +__initfunc(void indy_sc_init(void)) { if (indy_sc_probe()) { indy_sc_enable(); diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c index 7d9e041f3..a1270e7f3 100644 --- a/arch/mips/sgi/kernel/indy_timer.c +++ b/arch/mips/sgi/kernel/indy_timer.c @@ -3,10 +3,10 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_timer.c,v 1.6 1998/03/17 22:07:41 ralf Exp $ + * $Id: indy_timer.c,v 1.7 1998/03/22 23:27:17 ralf Exp $ */ - #include <linux/errno.h> +#include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/param.h> @@ -195,7 +195,7 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, )*60 + sec; /* finally seconds */ } -unsigned long get_indy_time(void) +__initfunc(static unsigned long get_indy_time(void)) { struct indy_clock *clock = INDY_CLOCK_REGS; unsigned int year, mon, day, hour, min, sec; @@ -240,7 +240,7 @@ unsigned long get_indy_time(void) #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) -void indy_timer_init(void) +__initfunc(void indy_timer_init(void)) { struct sgi_ioc_timers *p; volatile unsigned char *tcwp, *tc2p; @@ -307,4 +307,3 @@ void do_settimeofday(struct timeval *tv) time_esterror = MAXPHASE; sti(); } - diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c index c8c8afc89..6b2c1846e 100644 --- a/arch/mips/sgi/kernel/setup.c +++ b/arch/mips/sgi/kernel/setup.c @@ -3,8 +3,9 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: setup.c,v 1.6 1997/12/01 17:57:38 ralf Exp $ + * $Id: setup.c,v 1.7 1998/03/04 08:47:27 ralf Exp $ */ +#include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -50,7 +51,7 @@ static unsigned char sgi_read_status(void) return sgi_kh->command; } -static void sgi_keyboard_setup(void) +__initfunc(static void sgi_keyboard_setup(void)) { kbd_read_input = sgi_read_input; kbd_write_output = sgi_write_output; @@ -58,12 +59,12 @@ static void sgi_keyboard_setup(void) kbd_read_status = sgi_read_status; } -static void sgi_irq_setup(void) +__initfunc(static void sgi_irq_setup(void)) { sgint_init(); } -void sgi_setup(void) +__initfunc(void sgi_setup(void)) { char *ctype; diff --git a/arch/mips/sgi/kernel/system.c b/arch/mips/sgi/kernel/system.c index affb009f2..f27d2ce05 100644 --- a/arch/mips/sgi/kernel/system.c +++ b/arch/mips/sgi/kernel/system.c @@ -3,8 +3,9 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: system.c,v 1.3 1997/09/13 02:19:18 ralf Exp $ + * $Id: system.c,v 1.4 1997/12/01 17:57:39 ralf Exp $ */ +#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> @@ -56,7 +57,7 @@ static struct smatch sgi_cputable[] = { #define NUM_CPUS 9 /* for now */ -static enum sgi_mach string_to_mach(char *s) +__initfunc(static enum sgi_mach string_to_mach(char *s)) { int i; @@ -71,7 +72,7 @@ static enum sgi_mach string_to_mach(char *s) return (enum sgi_mach) 0; } -static int string_to_cpu(char *s) +__initfunc(static int string_to_cpu(char *s)) { int i; @@ -90,7 +91,7 @@ static int string_to_cpu(char *s) * We' call this early before loadmmu(). If we do the other way around * the firmware will crash and burn. */ -void sgi_sysinit(void) +__initfunc(void sgi_sysinit(void)) { pcomponent *p, *toplev, *cpup = 0; int cputype = -1; diff --git a/arch/mips/sgi/kernel/time.c b/arch/mips/sgi/kernel/time.c index 1f5137c27..7dc5a4d53 100644 --- a/arch/mips/sgi/kernel/time.c +++ b/arch/mips/sgi/kernel/time.c @@ -1,13 +1,14 @@ -/* $Id: time.c,v 1.1 1996/06/08 12:07:08 dm Exp $ +/* $Id: time.c,v 1.2 1998/03/27 08:53:45 ralf Exp $ * time.c: Generic SGI time_init() code, this will dispatch to the * appropriate per-architecture time/counter init code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) */ +#include <linux/init.h> extern void indy_timer_init(void); -void time_init(void) +__initfunc(void time_init(void)) { /* XXX assume INDY for now XXX */ indy_timer_init(); diff --git a/arch/mips/sgi/prom/cmdline.c b/arch/mips/sgi/prom/cmdline.c index 35b6b48ac..43f1c315c 100644 --- a/arch/mips/sgi/prom/cmdline.c +++ b/arch/mips/sgi/prom/cmdline.c @@ -1,9 +1,11 @@ -/* $Id: cmdline.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ +/* * cmdline.c: Kernel command line creation using ARCS argc/argv. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: cmdline.c,v 1.3 1998/03/27 08:53:46 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> @@ -14,7 +16,7 @@ extern char arcs_cmdline[CL_SIZE]; -char *prom_getcmdline(void) +__initfunc(char *prom_getcmdline(void)) { return &(arcs_cmdline[0]); } @@ -29,7 +31,7 @@ static char *ignored[] = { }; #define NENTS(foo) ((sizeof((foo)) / (sizeof((foo[0]))))) -void prom_init_cmdline(void) +__initfunc(void prom_init_cmdline(void)) { char *cp; int actr, i; diff --git a/arch/mips/sgi/prom/console.c b/arch/mips/sgi/prom/console.c index 3f4d69f45..bfbba24e4 100644 --- a/arch/mips/sgi/prom/console.c +++ b/arch/mips/sgi/prom/console.c @@ -1,12 +1,14 @@ -/* $Id: console.c,v 1.1 1996/06/04 00:57:05 dm Exp $ +/* * console.c: SGI arcs console code. * * Copyright (C) 1996 David S. Miller (dm@sgi.com) + * + * $Id: console.c,v 1.2 1998/03/27 08:53:46 ralf Exp $ */ - +#include <linux/init.h> #include <asm/sgialib.h> -void prom_putchar(char c) +__initfunc(void prom_putchar(char c)) { long cnt; char it = c; @@ -14,7 +16,7 @@ void prom_putchar(char c) romvec->write(1, &it, 1, &cnt); } -char prom_getchar(void) +__initfunc(char prom_getchar(void)) { long cnt; char c; diff --git a/arch/mips/sgi/prom/env.c b/arch/mips/sgi/prom/env.c index 5aff47efd..c972c8400 100644 --- a/arch/mips/sgi/prom/env.c +++ b/arch/mips/sgi/prom/env.c @@ -1,20 +1,22 @@ -/* $Id: env.c,v 1.2 1996/06/08 04:48:41 dm Exp $ +/* * env.c: ARCS environment variable routines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: env.c,v 1.2 1998/03/27 08:53:46 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <asm/sgialib.h> -char *prom_getenv(char *name) +__initfunc(char *prom_getenv(char *name)) { return romvec->get_evar(name); } -long prom_setenv(char *name, char *value) +__initfunc(long prom_setenv(char *name, char *value)) { return romvec->set_evar(name, value); } diff --git a/arch/mips/sgi/prom/file.c b/arch/mips/sgi/prom/file.c index b62d33dda..b8911d595 100644 --- a/arch/mips/sgi/prom/file.c +++ b/arch/mips/sgi/prom/file.c @@ -1,58 +1,59 @@ -/* $Id: file.c,v 1.1 1996/06/08 04:47:22 dm Exp $ +/* * file.c: ARCS firmware interface to files. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: file.c,v 1.2 1998/03/27 08:53:47 ralf Exp $ */ - +#include <linux/init.h> #include <asm/sgialib.h> -long prom_getvdirent(unsigned long fd, struct linux_vdirent *ent, unsigned long num, - unsigned long *cnt) +__initfunc(long prom_getvdirent(unsigned long fd, struct linux_vdirent *ent, unsigned long num, unsigned long *cnt)) { return romvec->get_vdirent(fd, ent, num, cnt); } -long prom_open(char *name, enum linux_omode md, unsigned long *fd) +__initfunc(long prom_open(char *name, enum linux_omode md, unsigned long *fd)) { return romvec->open(name, md, fd); } -long prom_close(unsigned long fd) +__initfunc(long prom_close(unsigned long fd)) { return romvec->close(fd); } -long prom_read(unsigned long fd, void *buf, unsigned long num, unsigned long *cnt) +__initfunc(long prom_read(unsigned long fd, void *buf, unsigned long num, unsigned long *cnt)) { return romvec->read(fd, buf, num, cnt); } -long prom_getrstatus(unsigned long fd) +__initfunc(long prom_getrstatus(unsigned long fd)) { return romvec->get_rstatus(fd); } -long prom_write(unsigned long fd, void *buf, unsigned long num, unsigned long *cnt) +__initfunc(long prom_write(unsigned long fd, void *buf, unsigned long num, unsigned long *cnt)) { return romvec->write(fd, buf, num, cnt); } -long prom_seek(unsigned long fd, struct linux_bigint *off, enum linux_seekmode sm) +__initfunc(long prom_seek(unsigned long fd, struct linux_bigint *off, enum linux_seekmode sm)) { return romvec->seek(fd, off, sm); } -long prom_mount(char *name, enum linux_mountops op) +__initfunc(long prom_mount(char *name, enum linux_mountops op)) { return romvec->mount(name, op); } -long prom_getfinfo(unsigned long fd, struct linux_finfo *buf) +__initfunc(long prom_getfinfo(unsigned long fd, struct linux_finfo *buf)) { return romvec->get_finfo(fd, buf); } -long prom_setfinfo(unsigned long fd, unsigned long flags, unsigned long msk) +__initfunc(long prom_setfinfo(unsigned long fd, unsigned long flags, unsigned long msk)) { return romvec->set_finfo(fd, flags, msk); } diff --git a/arch/mips/sgi/prom/init.c b/arch/mips/sgi/prom/init.c index 6b6167efd..c18d5deb2 100644 --- a/arch/mips/sgi/prom/init.c +++ b/arch/mips/sgi/prom/init.c @@ -1,9 +1,11 @@ -/* $Id: init.c,v 1.6 1996/06/10 16:38:33 dm Exp $ +/* * init.c: PROM library initialisation code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: init.c,v 1.2 1998/03/27 08:53:47 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <asm/sgialib.h> @@ -19,7 +21,7 @@ unsigned short prom_vers, prom_rev; extern void prom_testtree(void); -int prom_init(int argc, char **argv, char **envp) +__initfunc(int prom_init(int argc, char **argv, char **envp)) { struct linux_promblock *pb; diff --git a/arch/mips/sgi/prom/memory.c b/arch/mips/sgi/prom/memory.c index cb392a805..b6a212f87 100644 --- a/arch/mips/sgi/prom/memory.c +++ b/arch/mips/sgi/prom/memory.c @@ -1,10 +1,12 @@ -/* $Id: memory.c,v 1.5 1996/06/10 16:38:33 dm Exp $ +/* * memory.c: PROM library functions for acquiring/using memory descriptors * given to us from the ARCS firmware. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: memory.c,v 1.2 1998/03/27 08:53:47 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/sched.h> @@ -18,7 +20,7 @@ /* #define DEBUG */ -struct linux_mdesc *prom_getmdesc(struct linux_mdesc *curr) +__initfunc(struct linux_mdesc *prom_getmdesc(struct linux_mdesc *curr)) { return romvec->get_mdesc(curr); } @@ -38,12 +40,12 @@ static char *mtypes[8] = { static struct prom_pmemblock prom_pblocks[PROM_MAX_PMEMBLOCKS]; -struct prom_pmemblock *prom_getpblock_array(void) +__initfunc(struct prom_pmemblock *prom_getpblock_array(void)) { return &prom_pblocks[0]; } -static void prom_setup_memupper(void) +__initfunc(static void prom_setup_memupper(void)) { struct prom_pmemblock *p, *highest; @@ -60,7 +62,7 @@ static void prom_setup_memupper(void) #endif } -void prom_meminit(void) +__initfunc(void prom_meminit(void)) { struct linux_mdesc *p; int totram; @@ -104,7 +106,7 @@ void prom_meminit(void) } /* Called from mem_init() to fixup the mem_map page settings. */ -void prom_fixup_mem_map(unsigned long start, unsigned long end) +__initfunc(void prom_fixup_mem_map(unsigned long start, unsigned long end)) { struct prom_pmemblock *p; int i, nents; diff --git a/arch/mips/sgi/prom/misc.c b/arch/mips/sgi/prom/misc.c index b6ccd60c1..8d7c300c7 100644 --- a/arch/mips/sgi/prom/misc.c +++ b/arch/mips/sgi/prom/misc.c @@ -2,8 +2,11 @@ * misc.c: Miscellaneous ARCS PROM routines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: misc.c,v 1.5 1998/03/27 08:53:47 ralf Exp $ */ #include <linux/config.h> +#include <linux/init.h> #include <linux/kernel.h> #include <asm/bcache.h> @@ -82,7 +85,7 @@ struct linux_sysid *prom_getsysid(void) return romvec->get_sysid(); } -void prom_cacheflush(void) +__initfunc(void prom_cacheflush(void)) { romvec->cache_flush(); } diff --git a/arch/mips/sgi/prom/printf.c b/arch/mips/sgi/prom/printf.c index 02e7e4734..dbc0c8dc1 100644 --- a/arch/mips/sgi/prom/printf.c +++ b/arch/mips/sgi/prom/printf.c @@ -1,18 +1,19 @@ -/* $Id: printf.c,v 1.1 1996/06/04 00:57:06 dm Exp $ +/* * printf.c: Putting things on the screen using SGI arcs * PROM facilities. * * Copyright (C) 1996 David S. Miller (dm@sgi.com) + * + * $Id: printf.c,v 1.2 1998/03/27 08:53:48 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <asm/sgialib.h> static char ppbuf[1024]; -void -prom_printf(char *fmt, ...) +__initfunc(void prom_printf(char *fmt, ...)) { va_list args; char ch, *bptr; diff --git a/arch/mips/sgi/prom/salone.c b/arch/mips/sgi/prom/salone.c index 4f120af3a..f363aedeb 100644 --- a/arch/mips/sgi/prom/salone.c +++ b/arch/mips/sgi/prom/salone.c @@ -1,24 +1,25 @@ -/* $Id: salone.c,v 1.1 1996/06/08 04:47:22 dm Exp $ +/* * salone.c: Routines to load into memory and execute stand-along * program images using ARCS PROM firmware. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: salone.c,v 1.2 1998/03/27 08:53:48 ralf Exp $ */ - +#include <linux/init.h> #include <asm/sgialib.h> -long prom_load(char *name, unsigned long end, unsigned long *pc, unsigned long *eaddr) +__initfunc(long prom_load(char *name, unsigned long end, unsigned long *pc, unsigned long *eaddr)) { return romvec->load(name, end, pc, eaddr); } -long prom_invoke(unsigned long pc, unsigned long sp, long argc, - char **argv, char **envp) +__initfunc(long prom_invoke(unsigned long pc, unsigned long sp, long argc, char **argv, char **envp)) { return romvec->invoke(pc, sp, argc, argv, envp); } -long prom_exec(char *name, long argc, char **argv, char **envp) +__initfunc(long prom_exec(char *name, long argc, char **argv, char **envp)) { return romvec->exec(name, argc, argv, envp); } diff --git a/arch/mips/sgi/prom/tags.c b/arch/mips/sgi/prom/tags.c index d408822d0..1de56376d 100644 --- a/arch/mips/sgi/prom/tags.c +++ b/arch/mips/sgi/prom/tags.c @@ -1,10 +1,12 @@ -/* $Id: tags.c,v 1.5 1996/06/24 07:12:22 dm Exp $ +/* * tags.c: Initialize the arch tags the way the MIPS kernel setup * expects. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: tags.c,v 1.2 1998/03/27 08:53:48 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> @@ -43,7 +45,7 @@ tag_def taglist_sgi_indy[] = { /* XXX COLOSTOMY BAG!!!! XXX */ }; -void prom_setup_archtags(void) +__initfunc(void prom_setup_archtags(void)) { tag_def *tdp = &taglist_sgi_indy[0]; tag *tp; diff --git a/arch/mips/sgi/prom/time.c b/arch/mips/sgi/prom/time.c index 9a836b810..616e253bf 100644 --- a/arch/mips/sgi/prom/time.c +++ b/arch/mips/sgi/prom/time.c @@ -1,17 +1,19 @@ -/* $Id: time.c,v 1.1 1996/06/08 04:47:23 dm Exp $ +/* * time.c: Extracting time information from ARCS prom. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: time.c,v 1.2 1998/03/27 08:53:49 ralf Exp $ */ - +#include <linux/init.h> #include <asm/sgialib.h> -struct linux_tinfo *prom_gettinfo(void) +__initfunc(struct linux_tinfo *prom_gettinfo(void)) { return romvec->get_tinfo(); } -unsigned long prom_getrtime(void) +__initfunc(unsigned long prom_getrtime(void)) { return romvec->get_rtime(); } diff --git a/arch/mips/sgi/prom/tree.c b/arch/mips/sgi/prom/tree.c index 1cefd4964..414e1dacd 100644 --- a/arch/mips/sgi/prom/tree.c +++ b/arch/mips/sgi/prom/tree.c @@ -1,48 +1,50 @@ -/* $Id: tree.c,v 1.4 1996/06/08 04:48:41 dm Exp $ +/* * tree.c: PROM component device tree code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: tree.c,v 1.2 1998/03/27 08:53:49 ralf Exp $ */ - +#include <linux/init.h> #include <asm/sgialib.h> #define DEBUG_PROM_TREE -pcomponent *prom_getsibling(pcomponent *this) +__initfunc(pcomponent *prom_getsibling(pcomponent *this)) { if(this == PROM_NULL_COMPONENT) return PROM_NULL_COMPONENT; return romvec->next_component(this); } -pcomponent *prom_getchild(pcomponent *this) +__initfunc(pcomponent *prom_getchild(pcomponent *this)) { return romvec->child_component(this); } -pcomponent *prom_getparent(pcomponent *child) +__initfunc(pcomponent *prom_getparent(pcomponent *child)) { if(child == PROM_NULL_COMPONENT) return PROM_NULL_COMPONENT; return romvec->parent_component(child); } -long prom_getcdata(void *buffer, pcomponent *this) +__initfunc(long prom_getcdata(void *buffer, pcomponent *this)) { return romvec->component_data(buffer, this); } -pcomponent *prom_childadd(pcomponent *this, pcomponent *tmp, void *data) +__initfunc(pcomponent *prom_childadd(pcomponent *this, pcomponent *tmp, void *data)) { return romvec->child_add(this, tmp, data); } -long prom_delcomponent(pcomponent *this) +__initfunc(long prom_delcomponent(pcomponent *this)) { return romvec->comp_del(this); } -pcomponent *prom_componentbypath(char *path) +__initfunc(pcomponent *prom_componentbypath(char *path)) { return romvec->component_by_path(path); } @@ -72,7 +74,7 @@ static char *iflags[] = { "input", "output" }; -static void dump_component(pcomponent *p) +__initfunc(static void dump_component(pcomponent *p)) { prom_printf("[%p]:class<%s>type<%s>flags<%s>ver<%d>rev<%d>", p, classes[p->class], types[p->type], @@ -81,7 +83,7 @@ static void dump_component(pcomponent *p) p->key, p->amask, (int)p->cdsize, (int)p->ilen, p->iname); } -static void traverse(pcomponent *p, int op) +__initfunc(static void traverse(pcomponent *p, int op)) { dump_component(p); if(prom_getchild(p)) @@ -90,7 +92,7 @@ static void traverse(pcomponent *p, int op) traverse(prom_getsibling(p), 1); } -void prom_testtree(void) +__initfunc(void prom_testtree(void)) { pcomponent *p; diff --git a/arch/mips/sni/hw-access.c b/arch/mips/sni/hw-access.c index 5bc31e103..744518971 100644 --- a/arch/mips/sni/hw-access.c +++ b/arch/mips/sni/hw-access.c @@ -5,11 +5,12 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996, 1997 by Ralf Baechle + * Copyright (C) 1996, 1997, 1998 by Ralf Baechle * - * $Id: hw-access.c,v 1.3 1997/07/29 17:46:46 ralf Exp $ + * $Id: hw-access.c,v 1.3 1997/12/01 17:57:39 ralf Exp $ */ #include <linux/delay.h> +#include <linux/init.h> #include <linux/kbdcntrlr.h> #include <linux/kernel.h> #include <linux/linkage.h> @@ -182,7 +183,7 @@ static unsigned char sni_read_status(void) return inb(KBD_STATUS_REG); } -void sni_rm200_keyboard_setup(void) +__initfunc(void sni_rm200_keyboard_setup(void)) { kbd_read_input = sni_read_input; kbd_write_output = sni_write_output; diff --git a/arch/mips/sni/io.c b/arch/mips/sni/io.c index f62fdc2d7..8a97b80a8 100644 --- a/arch/mips/sni/io.c +++ b/arch/mips/sni/io.c @@ -4,6 +4,8 @@ * for more details. * * Low level I/O functions for SNI. + * + * $Id: io.c,v 1.2 1998/03/27 08:53:50 ralf Exp $ */ #include <linux/string.h> #include <asm/mipsconfig.h> diff --git a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c index 8c2d773cd..e2255b127 100644 --- a/arch/mips/sni/pci.c +++ b/arch/mips/sni/pci.c @@ -5,11 +5,10 @@ * * SNI specific PCI support for RM200/RM300. * - * $Id: pci.c,v 1.3 1998/01/14 05:01:51 ralf Exp $ + * $Id: pci.c,v 1.3 1998/03/04 08:47:29 ralf Exp $ */ #include <linux/config.h> #include <linux/bios32.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/types.h> #include <asm/byteorder.h> diff --git a/arch/mips/tools/offset.c b/arch/mips/tools/offset.c index 9474472c9..3ccd434d3 100644 --- a/arch/mips/tools/offset.c +++ b/arch/mips/tools/offset.c @@ -4,7 +4,7 @@ * Copyright (C) 1996 David S. Miller * Made portable by Ralf Baechle * - * $Id: offset.c,v 1.7 1998/03/26 07:28:02 ralf Exp $ + * $Id: offset.c,v 1.6 1998/03/27 04:47:58 ralf Exp $ */ #include <linux/types.h> @@ -104,7 +104,6 @@ void output_thread_defines(void) offset("#define THREAD_BVADDR ", struct task_struct, tss.cp0_badvaddr); offset("#define THREAD_ECODE ", struct task_struct, tss.error_code); offset("#define THREAD_TRAPNO ", struct task_struct, tss.trap_no); - offset("#define THREAD_KSP ", struct task_struct, tss.ksp); offset("#define THREAD_PGDIR ", struct task_struct, tss.pg_dir); offset("#define THREAD_MFLAGS ", struct task_struct, tss.mflags); offset("#define THREAD_CURDS ", struct task_struct, tss.current_ds); diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 9e3c6165d..2c866eed8 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -413,6 +413,7 @@ SYSCALL(execve) SYSCALL(open) SYSCALL(close) SYSCALL(waitpid) +SYSCALL(delete_module) /* Why isn't this a) automatic, b) written in 'C'? */ diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 60bcb042d..4cdcc28a3 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -45,11 +45,16 @@ * major/minor handling that came with kdev_t. It seems to work for * the time being, but I can't guarantee that it will stay like * that when we start using 16 (24?) bit minors. + * + * restructured jan 1997 by Joerg Dorchain + * - Fixed Bug accessing multiple disks + * - some code cleanup + * - added trackbuffer for each drive to speed things up + * - fixed some race conditions (who finds the next may send it to me ;-) */ -#ifdef MODULE #include <linux/module.h> -#endif + #include <linux/sched.h> #include <linux/fs.h> #include <linux/fcntl.h> @@ -61,7 +66,7 @@ #include <linux/types.h> #include <linux/delay.h> #include <linux/string.h> -#include <linux/mm.h> +#include <linux/slab.h> #include <linux/init.h> #include <asm/setup.h> @@ -82,20 +87,9 @@ #define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */ #endif -/* prototypes */ - -static int amiga_read(int,unsigned char *, unsigned long, int); -static void amiga_write(int, unsigned long, unsigned char *, int); -static int dos_read(int, unsigned char *, unsigned long, int); -static void dos_write(int, unsigned long, unsigned char *,int); -static ushort dos_crc(void *, int, int, int); -static void fd_probe(int); - - /* * Defines */ -#define MAX_SECTORS 22 /* * Error codes @@ -107,6 +101,11 @@ static void fd_probe(int); #define FD_NOTACTIVE 3 /* unit is not active */ #define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */ +#define MFM_NOSYNC 1 +#define MFM_HEADER 2 +#define MFM_DATA 3 +#define MFM_TRACK 4 + /* * Floppy ID values */ @@ -115,8 +114,9 @@ static void fd_probe(int); #define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */ #define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */ -static int fd_def_df0 = 0; /* default for df0 if it doesn't identify */ +static long int fd_def_df0 = 0; /* default for df0 if it doesn't identify */ +MODULE_PARM(fd_def_df0,"l"); /* * Macros @@ -147,26 +147,34 @@ static int floppy_sizes[256]={880,880,880,880,720,720,720,720,}; static int floppy_blocksizes[256]={0,}; /* hardsector size assumed to be 512 */ +static int amiga_read(int), dos_read(int); +static void amiga_write(int), dos_write(int); static struct fd_data_type data_types[] = { { "Amiga", 11 , amiga_read, amiga_write}, { "MS-Dos", 9, dos_read, dos_write} }; /* current info on each unit */ -static struct amiga_floppy_struct unit[FD_MAX_UNITS]; +static struct amiga_floppy_struct unit[FD_MAX_UNITS] = {{ 0,}}; -static struct timer_list flush_track_timer; +static struct timer_list flush_track_timer[FD_MAX_UNITS]; static struct timer_list post_write_timer; static struct timer_list motor_on_timer; static struct timer_list motor_off_timer[FD_MAX_UNITS]; static int on_attempts; -/* track buffer */ -static int lastdrive = -1; -static int savedtrack = -1; +/* Synchronization of FDC access */ +/* request loop (trackbuffer) */ +static volatile int fdc_busy = -1; +static volatile int fdc_nested = 0; +static struct wait_queue *fdc_wait = NULL; + +static struct wait_queue *motor_wait = NULL; + +static volatile int selected = -1; /* currently selected drive */ + static int writepending = 0; static int writefromint = 0; -static unsigned char trackdata[MAX_SECTORS * 512]; static char *raw_buf; #define RAW_BUF_SIZE 30000 /* size of raw disk data */ @@ -177,21 +185,8 @@ static char *raw_buf; * request. */ static volatile char block_flag = 0; -static volatile int selected = 0; static struct wait_queue *wait_fd_block = NULL; -/* Synchronization of FDC access */ -/* request loop (trackbuffer) */ -static volatile int fdc_busy = -1; -static volatile int fdc_nested = 0; -static struct wait_queue *fdc_wait = NULL; -/* hardware */ -static volatile int hw_busy = -1; -static volatile int hw_nested = 0; -static struct wait_queue *hw_wait = NULL; - -static struct wait_queue *motor_wait = NULL; - /* MS-Dos MFM Coding tables (should go quick and easy) */ static unsigned char mfmencode[16]={ 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, @@ -211,13 +206,6 @@ static struct wait_queue *ms_wait = NULL; */ #define MAX_ERRORS 12 -/* - * 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; - /* Prevent "aliased" accesses. */ static int fd_ref[4] = { 0,0,0,0 }; static int fd_device[4] = { 0,0,0,0 }; @@ -231,61 +219,15 @@ static int fd_device[4] = { 0,0,0,0 }; /* Current error count. */ #define CURRENT_ERRORS (CURRENT->errors) -static void get_fdc(int drive) -{ -unsigned long flags; - - drive &= 3; - save_flags(flags); - cli(); - if (fdc_busy != drive) - while (!(fdc_busy < 0)) - sleep_on(&fdc_wait); - fdc_busy = drive; - fdc_nested++; - restore_flags(flags); -} - -static inline void rel_fdc(void) -{ -#ifdef DEBUG - if (fdc_nested == 0) - printk("fd: unmatched rel_fdc\n"); -#endif - fdc_nested--; - if (fdc_nested == 0) { - fdc_busy = -1; - wake_up(&fdc_wait); - } -} -static void get_hw(int drive) -{ -unsigned long flags; - drive &= 3; - save_flags(flags); - cli(); - if (hw_busy != drive) - while (!(hw_busy < 0)) - sleep_on(&hw_wait); - hw_busy = drive; - hw_nested++; - restore_flags(flags); -} +/* + * Here come the actual hardware access and helper functions. + * They are not reentrant and single threaded because all drives + * share the same hardware and the same trackbuffer. + */ -static inline void rel_hw(void) -{ -#ifdef DEBUG - if (hw_nested == 0) - printk("fd: unmatched hw_rel\n"); -#endif - hw_nested--; - if (hw_nested == 0) { - hw_busy = -1; - wake_up(&hw_wait); - } -} +/* Milliseconds timer */ static void ms_isr(int irq, void *dummy, struct pt_regs *fp) { @@ -314,53 +256,102 @@ static void ms_delay(int ms) } } -/* - * Functions - */ -/*====================================================================== - Turn off the motor of the given drive. Unit must already be active. - Returns standard floppy error code. -======================================================================*/ -static void fd_motor_off(unsigned long drive) +/* Hardware semaphore */ + +/* returns true when we would get the semaphore */ +static inline int try_fdc(int drive) +{ + drive &= 3; + return ((fdc_busy < 0) || (fdc_busy == drive)); +} + +static void get_fdc(int drive) +{ +unsigned long flags; + + drive &= 3; +#ifdef DEBUG + printk("get_fdc: drive %d fdc_busy %d fdc_nested %d\n",drive,fdc_busy,fdc_nested); +#endif + save_flags(flags); + cli(); + while (!try_fdc(drive)) + sleep_on(&fdc_wait); + fdc_busy = drive; + fdc_nested++; + restore_flags(flags); +} + +static inline void rel_fdc(void) +{ +#ifdef DEBUG + if (fdc_nested == 0) + printk("fd: unmatched rel_fdc\n"); + printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested); +#endif + fdc_nested--; + if (fdc_nested == 0) { + fdc_busy = -1; + wake_up(&fdc_wait); + } +} + +static void fd_select (int drive) { - unsigned long flags; unsigned char prb = ~0; drive&=3; - get_hw(drive); - save_flags(flags); - cli(); +#ifdef DEBUG + printk("selecting %d\n",drive); +#endif + if (drive == selected) + return; + get_fdc(drive); + selected = drive; if (unit[drive].track % 2 != 0) prb &= ~DSKSIDE; + if (unit[drive].motor == 1) + prb &= ~DSKMOTOR; ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); ciab.prb = prb; prb &= ~SELMASK(drive); ciab.prb = prb; - udelay (1); + rel_fdc(); +} + +static void fd_deselect (int drive) +{ + unsigned char prb; + unsigned long flags; + + drive&=3; +#ifdef DEBUG + printk("deselecting %d\n",drive); +#endif + if (drive != selected) { + printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected); + return; + } + + get_fdc(drive); + save_flags (flags); + sti(); + + selected = -1; + + prb = ciab.prb; prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); ciab.prb = prb; - selected = -1; - unit[drive].motor = 0; - rel_hw(); -#ifdef MODULE -/* -this is the last interrupt for any drive access, happens after -release. So we have to wait until now to decrease the use count. -*/ - if (fd_ref[drive] == 0) - MOD_DEC_USE_COUNT; -#endif - restore_flags(flags); + restore_flags (flags); + rel_fdc(); + } static void motor_on_callback(unsigned long nr) { - nr &= 3; - if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) { - unit[nr].motor = 1; wake_up (&motor_wait); } else { motor_on_timer.expires = jiffies + HZ/10; @@ -368,39 +359,25 @@ static void motor_on_callback(unsigned long nr) } } -static int motor_on(int nr) +static int fd_motor_on(int nr) { - unsigned long flags; - unsigned char prb = ~0; - nr &= 3; - get_hw(nr); - save_flags (flags); - cli(); + del_timer(motor_off_timer + nr); if (!unit[nr].motor) { + unit[nr].motor = 1; + fd_select(nr); + del_timer(&motor_on_timer); motor_on_timer.data = nr; motor_on_timer.expires = jiffies + HZ/2; add_timer(&motor_on_timer); - on_attempts = 10; - - prb &= ~DSKMOTOR; - if (unit[nr].track % 2 != 0) - prb &= ~DSKSIDE; - ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); - ciab.prb = prb; - prb &= ~SELMASK(nr); - ciab.prb = prb; - selected = nr; - - while (!unit[nr].motor) - sleep_on (&motor_wait); + on_attempts = 10; + sleep_on (&motor_wait); + fd_deselect(nr); } - rel_hw(); - restore_flags(flags); if (on_attempts == 0) { on_attempts = -1; @@ -416,72 +393,58 @@ static int motor_on(int nr) return 1; } -static void floppy_off (unsigned int nr) -{ - nr&=3; - del_timer(motor_off_timer+nr); - motor_off_timer[nr].expires = jiffies + 3*HZ; - add_timer(motor_off_timer+nr); -} - -static void fd_select (int drive) +static void fd_motor_off(unsigned long drive) { - unsigned char prb = ~0; +long calledfromint; +#ifdef MODULE +long decusecount; + decusecount = drive & 0x40000000; +#endif + calledfromint = drive & 0x80000000; drive&=3; - if (drive == selected) + if (calledfromint && !try_fdc(drive)) { + /* We would be blocked in an interrupt, so try again later */ + motor_off_timer[drive].expires = jiffies + 1; + add_timer(motor_off_timer + drive); return; - get_hw(drive); - selected = drive; + } + unit[drive].motor = 0; + fd_select(drive); + udelay (1); + fd_deselect(drive); - if (unit[drive].track % 2 != 0) - prb &= ~DSKSIDE; - if (unit[drive].motor == 1) - prb &= ~DSKMOTOR; - ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); - ciab.prb = prb; - prb &= ~SELMASK(drive); - ciab.prb = prb; - rel_hw(); +#ifdef MODULE +/* +this is the last interrupt for any drive access, happens after +release (from floppy_off). So we have to wait until now to decrease +the use count. +*/ + if (decusecount) + MOD_DEC_USE_COUNT; +#endif } -static void fd_deselect (int drive) +static void floppy_off (unsigned int nr) { - unsigned char prb; - unsigned long flags; - - drive&=3; - if (drive != selected) - return; - - get_hw(drive); - save_flags (flags); - sti(); - - selected = -1; - - prb = ciab.prb; - prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); - ciab.prb = prb; - - restore_flags (flags); - rel_hw(); - +int drive; + + drive = nr & 3; + del_timer(motor_off_timer + drive); + motor_off_timer[drive].expires = jiffies + 3*HZ; + /* called this way it is always from interrupt */ + motor_off_timer[drive].data = nr | 0x80000000; + add_timer(motor_off_timer + nr); } -/*====================================================================== - Seek the drive to track 0. - The drive must be active and the motor must be running. - Returns standard floppy error code. -======================================================================*/ static int fd_calibrate(int drive) { unsigned char prb; int n; drive &= 3; - get_hw(drive); - if (!motor_on (drive)) + get_fdc(drive); + if (!fd_motor_on (drive)) return 0; fd_select (drive); prb = ciab.prb; @@ -511,46 +474,45 @@ static int fd_calibrate(int drive) if ((ciaa.pra & DSKTRACK0) == 0) break; if (--n == 0) { - printk (KERN_ERR "calibrate failed, turning motor off\n"); + printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive); fd_motor_off (drive); unit[drive].track = -1; - rel_hw(); + rel_fdc(); return 0; } } unit[drive].track = 0; ms_delay(unit[drive].type->settle_time); - rel_hw(); + rel_fdc(); + fd_deselect(drive); return 1; } -/*====================================================================== - Seek the drive to the requested cylinder. - The drive must have been calibrated at some point before this. - The drive must also be active and the motor must be running. -======================================================================*/ static int fd_seek(int drive, int track) { unsigned char prb; int cnt; +#ifdef DEBUG + printk("seeking drive %d to track %d\n",drive,track); +#endif drive &= 3; - get_hw(drive); + get_fdc(drive); if (unit[drive].track == track) { - rel_hw(); + rel_fdc(); return 1; } - if (!motor_on(drive)) { - rel_hw(); + if (!fd_motor_on(drive)) { + rel_fdc(); return 0; } - fd_select (drive); if (unit[drive].track < 0 && !fd_calibrate(drive)) { - rel_hw(); + rel_fdc(); return 0; } + fd_select (drive); cnt = unit[drive].track/2 - track/2; prb = ciab.prb; prb |= DSKSIDE | DSKDIREC; @@ -565,7 +527,8 @@ static int fd_seek(int drive, int track) ms_delay (unit[drive].type->side_time); unit[drive].track = track; if (cnt == 0) { - rel_hw(); + rel_fdc(); + fd_deselect(drive); return 1; } do { @@ -578,119 +541,189 @@ static int fd_seek(int drive, int track) } while (--cnt != 0); ms_delay (unit[drive].type->settle_time); - rel_hw(); + rel_fdc(); + fd_deselect(drive); return 1; } -static void encode(unsigned long data, unsigned long *dest) +static unsigned long fd_get_drive_id(int drive) { - unsigned long data2; + int i; + ulong id = 0; - data &= 0x55555555; - data2 = data ^ 0x55555555; - data |= ((data2 >> 1) | 0x80000000) & (data2 << 1); + drive&=3; + get_fdc(drive); + /* set up for ID */ + MOTOR_ON; + udelay(2); + SELECT(SELMASK(drive)); + udelay(2); + DESELECT(SELMASK(drive)); + udelay(2); + MOTOR_OFF; + udelay(2); + SELECT(SELMASK(drive)); + udelay(2); + DESELECT(SELMASK(drive)); + udelay(2); - if (*(dest - 1) & 0x00000001) - data &= 0x7FFFFFFF; + /* loop and read disk ID */ + for (i=0; i<32; i++) { + SELECT(SELMASK(drive)); + udelay(2); - *dest = data; + /* read and store value of DSKRDY */ + id <<= 1; + id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */ + + DESELECT(SELMASK(drive)); + } + + rel_fdc(); + + /* + * RB: At least A500/A2000's df0: don't identify themselves. + * As every (real) Amiga has at least a 3.5" DD drive as df0: + * we default to that if df0: doesn't identify as a certain + * type. + */ + if(drive == 0 && id == FD_NODRIVE) + { + id = fd_def_df0; + printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0); + } + /* return the ID value */ + return (id); } -static void encode_block(unsigned long *dest, unsigned long *src, int len) +static void fd_block_done(int irq, void *dummy, struct pt_regs *fp) { - int cnt, to_cnt = 0; - unsigned long data; + if (block_flag) + custom.dsklen = 0x4000; - /* odd bits */ - for (cnt = 0; cnt < len / 4; cnt++) { - data = src[cnt] >> 1; - encode(data, dest + to_cnt++); + if (block_flag == 2) { /* writing */ + writepending = 2; + post_write_timer.expires = jiffies + 1; /* at least 2 ms */ + post_write_timer.data = selected; + add_timer(&post_write_timer); } - - /* even bits */ - for (cnt = 0; cnt < len / 4; cnt++) { - data = src[cnt]; - encode(data, dest + to_cnt++); + else { /* reading */ + block_flag = 0; + wake_up (&wait_fd_block); } } -unsigned long checksum(unsigned long *addr, int len) +static void raw_read(int drive) { - unsigned long csum = 0; + drive&=3; + get_fdc(drive); + while (block_flag) + sleep_on(&wait_fd_block); + fd_select(drive); + /* setup adkcon bits correctly */ + custom.adkcon = ADK_MSBSYNC; + custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST; - len /= sizeof(*addr); - while (len-- > 0) - csum ^= *addr++; - csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555); + custom.dsksync = MFM_SYNC; - return csum; -} + custom.dsklen = 0; + custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); + custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; + custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; -struct header { - unsigned char magic; - unsigned char track; - unsigned char sect; - unsigned char ord; - unsigned char labels[16]; - unsigned long hdrchk; - unsigned long datachk; -}; + block_flag = 1; -static unsigned long *putsec(int disk, unsigned long *raw, int track, int cnt, - unsigned char *data) + while (block_flag) + sleep_on (&wait_fd_block); + + custom.dsklen = 0; + fd_deselect(drive); + rel_fdc(); +} + +static int raw_write(int drive) { - struct header hdr; - int i; + ushort adk; - disk&=3; - *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA; - raw++; - *raw++ = 0x44894489; + drive&=3; + get_fdc(drive); /* corresponds to rel_fdc() in post_write() */ + if ((ciaa.pra & DSKPROT) == 0) { + rel_fdc(); + return 0; + } + while (block_flag) + sleep_on(&wait_fd_block); + fd_select(drive); + /* clear adkcon bits */ + custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC; + /* set appropriate adkcon bits */ + adk = ADK_SETCLR|ADK_FAST; + if ((ulong)unit[drive].track >= unit[drive].type->precomp2) + adk |= ADK_PRECOMP1; + else if ((ulong)unit[drive].track >= unit[drive].type->precomp1) + adk |= ADK_PRECOMP0; + custom.adkcon = adk; - hdr.magic = 0xFF; - hdr.track = track; - hdr.sect = cnt; - hdr.ord = unit[disk].sects-cnt; - for (i = 0; i < 16; i++) - hdr.labels[i] = 0; - hdr.hdrchk = checksum((ulong *)&hdr, - (char *)&hdr.hdrchk-(char *)&hdr); - hdr.datachk = checksum((ulong *)data, 512); + custom.dsklen = DSKLEN_WRITE; + custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); + custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; + custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; - encode_block(raw, (ulong *)&hdr.magic, 4); - raw += 2; - encode_block(raw, (ulong *)&hdr.labels, 16); - raw += 8; - encode_block(raw, (ulong *)&hdr.hdrchk, 4); - raw += 2; - encode_block(raw, (ulong *)&hdr.datachk, 4); - raw += 2; - encode_block(raw, (ulong *)data, 512); - raw += 256; + block_flag = 2; + return 1; +} - return raw; +/* + * to be called at least 2ms after the write has finished but before any + * other access to the hardware. + */ +static void post_write (unsigned long drive) +{ +#ifdef DEBUG + printk("post_write for drive %ld\n",drive); +#endif + drive &= 3; + custom.dsklen = 0; + block_flag = 0; + writepending = 0; + writefromint = 0; + unit[drive].dirty = 0; + wake_up(&wait_fd_block); + fd_deselect(drive); + rel_fdc(); /* corresponds to get_fdc() in raw_write */ } -/*========================================================================== - amiga_write converts track/labels data to raw track data -==========================================================================*/ -static void amiga_write(int disk, unsigned long raw, unsigned char *data, - int track) +/* + * The following functions are to convert the block contents into raw data + * written to disk and vice versa. + * (Add other formats here ;-)) + */ + +static unsigned long scan_sync(unsigned long raw, unsigned long end) { - unsigned int cnt; - unsigned long *ptr = (unsigned long *)raw; + ushort *ptr = (ushort *)raw, *endp = (ushort *)end; - disk&=3; - /* gap space */ - for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++) - *ptr++ = 0xaaaaaaaa; + while (ptr < endp && *ptr++ != 0x4489) + ; + if (ptr < endp) { + while (*ptr == 0x4489 && ptr < endp) + ptr++; + return (ulong)ptr; + } + return 0; +} - /* sectors */ - for (cnt = 0; cnt < unit[disk].sects; cnt++) - ptr = putsec (disk, ptr, track, cnt, data + cnt*512); - *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8; - raw = (unsigned long)ptr + 2; +static inline unsigned long checksum(unsigned long *addr, int len) +{ + unsigned long csum = 0; + + len /= sizeof(*addr); + while (len-- > 0) + csum ^= *addr++; + csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555); + + return csum; } static unsigned long decode (unsigned long *data, unsigned long *raw, @@ -713,45 +746,29 @@ static unsigned long decode (unsigned long *data, unsigned long *raw, return (ulong)raw; } -#define MFM_NOSYNC 1 -#define MFM_HEADER 2 -#define MFM_DATA 3 -#define MFM_TRACK 4 - -/*========================================================================== - scan_sync - looks for the next start of sector marked by a sync. d3 is the - sector number (10..0). When d3 = 10, can't be certain of a - starting sync. -==========================================================================*/ -static unsigned long scan_sync(unsigned long raw, unsigned long end) -{ - ushort *ptr = (ushort *)raw, *endp = (ushort *)end; - - while (ptr < endp && *ptr++ != 0x4489) - ; - if (ptr < endp) { - while (*ptr == 0x4489 && ptr < endp) - ptr++; - return (ulong)ptr; - } - return 0; -} +struct header { + unsigned char magic; + unsigned char track; + unsigned char sect; + unsigned char ord; + unsigned char labels[16]; + unsigned long hdrchk; + unsigned long datachk; +}; -/*========================================================================== - amiga_read reads a raw track of data into a track buffer -==========================================================================*/ -static int amiga_read(int drive, unsigned char *track_data, - unsigned long raw, int track) +static int amiga_read(int drive) { + unsigned long raw; unsigned long end; int scnt; unsigned long csum; struct header hdr; drive&=3; + raw = (long) raw_buf; end = raw + unit[drive].type->read_size; - for (scnt = 0;scnt < unit[drive].sects; scnt++) { + for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { if (!(raw = scan_sync(raw, end))) { printk (KERN_INFO "can't find sync for sector %d\n", scnt); return MFM_NOSYNC; @@ -778,24 +795,24 @@ static int amiga_read(int drive, unsigned char *track_data, } /* verify track */ - if (hdr.track != track) { - printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, track); + if (hdr.track != unit[drive].track) { + printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track); return MFM_TRACK; } - raw = decode ((ulong *)(track_data + hdr.sect*512), + raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512), (ulong *)raw, 512); - csum = checksum((ulong *)(track_data + hdr.sect*512), 512); + csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512); if (hdr.datachk != csum) { printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n", hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt, hdr.datachk, csum); printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n", - ((ulong *)(track_data+hdr.sect*512))[0], - ((ulong *)(track_data+hdr.sect*512))[1], - ((ulong *)(track_data+hdr.sect*512))[2], - ((ulong *)(track_data+hdr.sect*512))[3]); + ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0], + ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1], + ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2], + ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]); return MFM_DATA; } } @@ -803,6 +820,89 @@ static int amiga_read(int drive, unsigned char *track_data, return 0; } +static void encode(unsigned long data, unsigned long *dest) +{ + unsigned long data2; + + data &= 0x55555555; + data2 = data ^ 0x55555555; + data |= ((data2 >> 1) | 0x80000000) & (data2 << 1); + + if (*(dest - 1) & 0x00000001) + data &= 0x7FFFFFFF; + + *dest = data; +} + +static void encode_block(unsigned long *dest, unsigned long *src, int len) +{ + int cnt, to_cnt = 0; + unsigned long data; + + /* odd bits */ + for (cnt = 0; cnt < len / 4; cnt++) { + data = src[cnt] >> 1; + encode(data, dest + to_cnt++); + } + + /* even bits */ + for (cnt = 0; cnt < len / 4; cnt++) { + data = src[cnt]; + encode(data, dest + to_cnt++); + } +} + +static unsigned long *putsec(int disk, unsigned long *raw, int cnt) +{ + struct header hdr; + int i; + + disk&=3; + *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA; + raw++; + *raw++ = 0x44894489; + + hdr.magic = 0xFF; + hdr.track = unit[disk].track; + hdr.sect = cnt; + hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt; + for (i = 0; i < 16; i++) + hdr.labels[i] = 0; + hdr.hdrchk = checksum((ulong *)&hdr, + (char *)&hdr.hdrchk-(char *)&hdr); + hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512); + + encode_block(raw, (ulong *)&hdr.magic, 4); + raw += 2; + encode_block(raw, (ulong *)&hdr.labels, 16); + raw += 8; + encode_block(raw, (ulong *)&hdr.hdrchk, 4); + raw += 2; + encode_block(raw, (ulong *)&hdr.datachk, 4); + raw += 2; + encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512); + raw += 256; + + return raw; +} + +static void amiga_write(int disk) +{ + unsigned int cnt; + unsigned long *ptr = (unsigned long *)raw_buf; + + disk&=3; + /* gap space */ + for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++) + *ptr++ = 0xaaaaaaaa; + + /* sectors */ + for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) + ptr = putsec (disk, ptr, cnt); + *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8; +} + + struct dos_header { unsigned char track, /* 0-80 */ side, /* 0-1 */ @@ -811,22 +911,12 @@ unsigned char track, /* 0-80 */ unsigned short crc; /* on 68000 we got an alignment problem, but this compiler solves it by adding silently adding a pad byte so data won't fit - and this cost about 3h to discover.... */ + and this took about 3h to discover.... */ unsigned char gap1[22]; /* for longword-alignedness (0x4e) */ }; /* crc routines are borrowed from the messydos-handler */ -static inline ushort dos_hdr_crc (struct dos_header *hdr) -{ -return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */ -} - -static inline ushort dos_data_crc(unsigned char *data) -{ -return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */ -} - /* excerpt from the messydos-device ; The CRC is computed not only over the actual data, but including ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb). @@ -937,6 +1027,16 @@ for (i=data_d3; i>=0; i--) { return (crch<<8)|crcl; } +static inline ushort dos_hdr_crc (struct dos_header *hdr) +{ +return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */ +} + +static inline ushort dos_data_crc(unsigned char *data) +{ +return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */ +} + static inline unsigned char dos_decode_byte(ushort word) { register ushort w2; @@ -965,30 +1065,28 @@ return ((ulong)raw); #ifdef DEBUG static void dbg(unsigned long ptr) { -printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr, - ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]); + printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr, + ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]); } #endif -/******************************************************************* - this reads a raw track of data into trackbuffer for ms-disks -*******************************************************************/ -static int dos_read(int drive, unsigned char *track_data, - unsigned long raw, int track) +static int dos_read(int drive) { unsigned long end; + unsigned long raw; int scnt; unsigned short crc,data_crc[2]; struct dos_header hdr; drive&=3; + raw = (long) raw_buf; end = raw + unit[drive].type->read_size; - for (scnt=0;scnt<unit[drive].sects;scnt++) { + for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { do { /* search for the right sync of each sec-hdr */ if (!(raw = scan_sync (raw, end))) { printk(KERN_INFO "dos_read: no hdr sync on track %d, unit %d for sector %d\n", - track,drive,scnt); + unit[drive].track,drive,scnt); return MFM_NOSYNC; } #ifdef DEBUG @@ -1008,15 +1106,15 @@ static int dos_read(int drive, unsigned char *track_data, printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc); return MFM_HEADER; } - if (hdr.track != track/unit[drive].type->heads) { + if (hdr.track != unit[drive].track/unit[drive].type->heads) { printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", hdr.track, - track/unit[drive].type->heads); + unit[drive].track/unit[drive].type->heads); return MFM_TRACK; } - if (hdr.side != track%unit[drive].type->heads) { + if (hdr.side != unit[drive].track%unit[drive].type->heads) { printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", hdr.side, - track%unit[drive].type->heads); + unit[drive].track%unit[drive].type->heads); return MFM_TRACK; } @@ -1029,7 +1127,7 @@ static int dos_read(int drive, unsigned char *track_data, #endif if (!(raw = scan_sync (raw, end))) { printk(KERN_INFO "dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n", - track, drive, scnt, hdr.sec); + unit[drive].track, drive, scnt, hdr.sec); return MFM_NOSYNC; } #ifdef DEBUG @@ -1043,19 +1141,19 @@ static int dos_read(int drive, unsigned char *track_data, } raw+=2; /* skip data mark (included in checksum) */ - raw = dos_decode((unsigned char *)(track_data + (hdr.sec - 1) * 512), (ushort *) raw, 512); + raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512); raw = dos_decode((unsigned char *)data_crc,(ushort *) raw,4); - crc = dos_data_crc(track_data + (hdr.sec - 1) * 512); + crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512); if (crc != data_crc[0]) { printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n", hdr.track, hdr.side, hdr.sec, hdr.len_desc, scnt,data_crc[0], crc); printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n", - ((ulong *)(track_data+(hdr.sec-1)*512))[0], - ((ulong *)(track_data+(hdr.sec-1)*512))[1], - ((ulong *)(track_data+(hdr.sec-1)*512))[2], - ((ulong *)(track_data+(hdr.sec-1)*512))[3]); + ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0], + ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1], + ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2], + ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]); return MFM_DATA; } } @@ -1086,8 +1184,7 @@ for (i = 0; i < len; i++) { } } -static unsigned long *ms_putsec(int drive, unsigned long *raw, int track, int cnt, - unsigned char *data) +static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt) { static struct dos_header hdr={0,0,0,2,0, {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}}; @@ -1104,8 +1201,8 @@ for(i=0;i<6;i++) *raw++=0x44895554; /* fill in the variable parts of the header */ -hdr.track=track/unit[drive].type->heads; -hdr.side=track%unit[drive].type->heads; +hdr.track=unit[drive].track/unit[drive].type->heads; +hdr.side=unit[drive].track%unit[drive].type->heads; hdr.sec=cnt+1; hdr.crc=dos_hdr_crc(&hdr); @@ -1122,11 +1219,11 @@ for(i=0;i<6;i++) *raw++=0x44895545; /* data */ -dos_encode_block((ushort *)raw,(unsigned char *)data,512); +dos_encode_block((ushort *)raw,(unsigned char *)unit[drive].trackbuf+cnt*512,512); raw+=256; /*data crc + jd's special gap (long words :-/) */ -crc[0]=dos_data_crc(data); +crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512); dos_encode_block((ushort *) raw,(unsigned char *)crc,4); raw+=2; @@ -1137,14 +1234,10 @@ for(i=0;i<38;i++) return raw; /* wrote 652 MFM words */ } - -/************************************************************** - builds encoded track data from trackbuffer data -**************************************************************/ -static void dos_write(int disk, unsigned long raw, unsigned char *data, - int track) +static void dos_write(int disk) { int cnt; +unsigned long raw = (unsigned long) raw_buf; unsigned long *ptr=(unsigned long *)raw; disk&=3; @@ -1168,183 +1261,40 @@ for (cnt=0;cnt<20;cnt++) *ptr++=0x92549254; /* sectors */ -for(cnt=0;cnt<unit[disk].sects;cnt++) - ptr=ms_putsec(disk,ptr,track,cnt,data+cnt*512); +for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) + ptr=ms_putsec(disk,ptr,cnt); *(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */ } -static void request_done(int uptodate) -{ - timer_active &= ~(1 << FLOPPY_TIMER); - end_request(uptodate); -} - /* - * floppy-change is never called from an interrupt, so we can relax a bit - * here, sleep etc. Note that floppy-on tries to set current_DOR to point - * to the desired drive, but it will probably not survive the sleep if - * several floppies are used at the same time: thus the loop. + * Here comes the high level stuff (i.e. the filesystem interface) + * and helper functions. + * Normally this should be the only part that has to be adapted to + * different kernel versions. */ -static int amiga_floppy_change(kdev_t dev) -{ - int drive = dev & 3; - int changed; - static int first_time = 1; - - if (MAJOR(dev) != MAJOR_NR) { - printk(KERN_CRIT "floppy_change: not a floppy\n"); - return 0; - } - if (first_time) - changed = first_time--; - else { - get_hw(drive); - fd_select (drive); - changed = !(ciaa.pra & DSKCHANGE); - fd_deselect (drive); - rel_hw(); +/* FIXME: this assumes the drive is still spinning - + * which is only true if we complete writing a track within three seconds + */ +static void flush_track_callback(unsigned long nr) +{ + nr&=3; + writefromint = 1; + if (!try_fdc(nr)) { + /* we might block in an interrupt, so try again later */ + flush_track_timer[nr].expires = jiffies + 1; + add_timer(flush_track_timer + nr); + return; } - - if (changed) { - fd_probe(dev); - unit[drive].track = -1; - selected = -1; - savedtrack = -1; - writepending = 0; /* if this was true before, too bad! */ + get_fdc(nr); + (*unit[nr].dtype->write_fkt)(nr); + if (!raw_write(nr)) { + printk (KERN_NOTICE "floppy disk write protected\n"); writefromint = 0; - return 1; - } - return 0; -} - -static __inline__ void copy_buffer(void *from, void *to) -{ - ulong *p1,*p2; - int cnt; - - p1 = (ulong *)from; - p2 = (ulong *)to; - - for (cnt = 0; cnt < 512/4; cnt++) - *p2++ = *p1++; -} - -static void raw_read(int drive, int track, char *ptrack, int len) -{ - drive&=3; - get_hw(drive); - /* setup adkcon bits correctly */ - custom.adkcon = ADK_MSBSYNC; - custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST; - - custom.dsksync = MFM_SYNC; - - custom.dsklen = 0; -#if 0 - ms_delay (unit[drive].type->side_time); -#endif - custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack); - custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN; - custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN; - - block_flag = 1; - - while (block_flag == 1) - sleep_on (&wait_fd_block); - - custom.dsklen = 0; - rel_hw(); -} - -static int raw_write(int drive, int track, char *ptrack, int len) -{ - ushort adk; - - drive&=3; - if ((ciaa.pra & DSKPROT) == 0) - return 0; - - get_hw(drive); /* corresponds to rel_hw() in post_write() */ - /* clear adkcon bits */ - custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC; - /* set appropriate adkcon bits */ - adk = ADK_SETCLR|ADK_FAST; - if ((ulong)track >= unit[drive].type->precomp2) - adk |= ADK_PRECOMP1; - else if ((ulong)track >= unit[drive].type->precomp1) - adk |= ADK_PRECOMP0; - custom.adkcon = adk; - - custom.dsklen = DSKLEN_WRITE; -#if 0 - ms_delay (unit[drive].type->side_time); -#endif - custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack); - custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; - custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; - - block_flag = 2; - return 1; -} - -static void post_write (unsigned long dummy) -{ - custom.dsklen = 0; - writepending = 0; - writefromint = 0; - rel_hw(); /* corresponds to get_hw() in raw_write */ -} - -static int get_track(int drive, int track) -{ - int error, errors; - - drive&=3; - get_hw(drive); - if (!motor_on(drive)) { - rel_hw(); - return -1; - } - fd_select(drive); - errors = 0; - while (errors < MAX_ERRORS) { - if (!fd_seek(drive, track)) { - rel_hw(); - return -1; /* we can not calibrate - no chance */ - } - if ((lastdrive == drive) && (savedtrack == track)) { - rel_hw(); - return 0; - } - lastdrive = drive; - raw_read(drive, track, raw_buf, unit[drive].type->read_size); - savedtrack = -1; - error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track); - if (error == 0) { - savedtrack = track; - rel_hw(); - return 0; - } - if (error == MFM_TRACK) - unit[drive].track = -1; - errors++; + writepending = 0; } - rel_hw(); - return -1; -} - -static void flush_track_callback(unsigned long nr) -{ - nr&=3; - writefromint = 1; - (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack); - if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) { - printk (KERN_NOTICE "floppy disk write protected\n"); - writefromint = 0; - writepending = 0; - } + rel_fdc(); } static int non_int_flush_track (unsigned long nr) @@ -1354,12 +1304,18 @@ unsigned long flags; nr&=3; writefromint = 0; del_timer(&post_write_timer); + get_fdc(nr); + if (!fd_motor_on(nr)) { + writepending = 0; + rel_fdc(); + return 0; + } save_flags(flags); cli(); if (writepending != 2) { restore_flags(flags); - (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack); - if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) { + (*unit[nr].dtype->write_fkt)(nr); + if (!raw_write(nr)) { printk (KERN_NOTICE "floppy disk write protected in write!\n"); writepending = 0; return 0; @@ -1367,13 +1323,50 @@ unsigned long flags; while (block_flag == 2) sleep_on (&wait_fd_block); } - else + else { restore_flags(flags); - ms_delay(2); /* 2 ms post_write delay */ - post_write(0); + ms_delay(2); /* 2 ms post_write delay */ + post_write(nr); + } + rel_fdc(); return 1; } +static int get_track(int drive, int track) +{ + int error, errcnt; + + drive&=3; + if (unit[drive].track == track) + return 0; + get_fdc(drive); + if (!fd_motor_on(drive)) { + rel_fdc(); + return -1; + } + + if (unit[drive].dirty == 1) { + del_timer (flush_track_timer + drive); + non_int_flush_track (drive); + } + errcnt = 0; + while (errcnt < MAX_ERRORS) { + if (!fd_seek(drive, track)) + return -1; + raw_read(drive); + error = (*unit[drive].dtype->read_fkt)(drive); + if (error == 0) { + rel_fdc(); + return 0; + } + /* Read Error Handling: recalibrate and try again */ + unit[drive].track = -1; + errcnt++; + } + rel_fdc(); + return -1; +} + static void redo_fd_request(void) { unsigned int cnt, block, track, sector; @@ -1388,10 +1381,7 @@ static void redo_fd_request(void) repeat: if (!CURRENT) { - if (fdc_busy < 0) - printk(KERN_CRIT "FDC access conflict!"); - rel_fdc(); - CLEAR_INTR; + /* Nothing left to do */ return; } @@ -1401,74 +1391,64 @@ static void redo_fd_request(void) if (CURRENT->bh && !buffer_locked(CURRENT->bh)) panic(DEVICE_NAME ": block not locked"); - probing = 0; device = MINOR(CURRENT_DEVICE); - if (device > 3) { + if (device < 8) { /* manual selection */ drive = device & 3; floppy = unit + drive; } else { /* Auto-detection */ - /* printk("redo_fd_request: can't handle auto detect\n");*/ - /* printk("redo_fd_request: default to normal\n");*/ +#ifdef DEBUG + printk("redo_fd_request: can't handle auto detect\n"); + printk("redo_fd_request: default to normal\n"); +#endif drive = device & 3; floppy = unit + drive; } - save_flags (flags); - cli(); - if (drive != selected && writepending) { - del_timer (&flush_track_timer); - restore_flags (flags); - if (!non_int_flush_track (selected)) { - end_request(0); - goto repeat; - } - } else - restore_flags (flags); - /* Here someone could investigate to be more efficient */ for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) { #ifdef DEBUG - printk("fd: sector %d + %d requested\n",CURRENT->sector,cnt); + printk("fd: sector %ld + %d requested for %s\n",CURRENT->sector,cnt, + (CURRENT->cmd==READ)?"read":"write"); #endif block = CURRENT->sector + cnt; if ((int)block > floppy->blocks) { - request_done(0); + end_request(0); goto repeat; } - track = block / floppy->sects; - sector = block % floppy->sects; + track = block / (floppy->dtype->sects * floppy->type->sect_mult); + sector = block % (floppy->dtype->sects * floppy->type->sect_mult); data = CURRENT->buffer + 512 * cnt; +#ifdef DEBUG + printk("access to track %d, sector %d, with buffer at 0x%08lx\n", + track, sector, data); +#endif - save_flags (flags); - cli(); - if (track != savedtrack && writepending) { - del_timer (&flush_track_timer); - restore_flags (flags); - if (!non_int_flush_track (selected)) { - end_request(0); - goto repeat; - } - } else - restore_flags (flags); + if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) { + printk(KERN_WARNING "do_fd_request: unknown command\n"); + end_request(0); + goto repeat; + } + if (get_track(drive, track) == -1) { + end_request(0); + goto repeat; + } switch (CURRENT->cmd) { case READ: - if (get_track(drive, track) == -1) { - end_request(0); - goto repeat; - } - copy_buffer(trackdata + sector * 512, data); + memcpy(data, unit[drive].trackbuf + sector * 512, 512); break; case WRITE: - if (get_track(drive, track) == -1) { + memcpy(unit[drive].trackbuf + sector * 512, data, 512); + + /* keep the drive spinning while writes are scheduled */ + if (!fd_motor_on(drive)) { end_request(0); goto repeat; } - copy_buffer(data, trackdata + sector * 512); /* * setup a callback to write the track buffer * after a short (1 tick) delay. @@ -1476,33 +1456,25 @@ static void redo_fd_request(void) save_flags (flags); cli(); - if (writepending) - /* reset the timer */ - del_timer (&flush_track_timer); + unit[drive].dirty = 1; + /* reset the timer */ + del_timer (flush_track_timer + drive); - writepending = 1; - flush_track_timer.data = drive; - flush_track_timer.expires = jiffies + 1; - add_timer (&flush_track_timer); + flush_track_timer[drive].expires = jiffies + 1; + add_timer (flush_track_timer + drive); restore_flags (flags); break; - - default: - printk(KERN_WARNING "do_fd_request: unknown command\n"); - request_done(0); - goto repeat; } } CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; - request_done(1); + end_request(1); goto repeat; } static void do_fd_request(void) { - get_fdc(CURRENT_DEVICE & 3); redo_fd_request(); } @@ -1512,53 +1484,48 @@ static int fd_ioctl(struct inode *inode, struct file *filp, int drive = inode->i_rdev & 3; static struct floppy_struct getprm; struct super_block * sb; - unsigned long flags; switch(cmd){ case HDIO_GETGEO: { struct hd_geometry loc; loc.heads = unit[drive].type->heads; - loc.sectors = unit[drive].sects; + loc.sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult; loc.cylinders = unit[drive].type->tracks; loc.start = 0; if (copy_to_user((void *)param, (void *)&loc, - sizeof(struct hd_geometry))) + sizeof(struct hd_geometry))) return -EFAULT; break; } case FDFMTBEG: - get_hw(drive); + get_fdc(drive); if (fd_ref[drive] > 1) { - rel_hw(); + rel_fdc(); return -EBUSY; } fsync_dev(inode->i_rdev); - if (motor_on(drive) == 0) { - rel_hw(); + if (fd_motor_on(drive) == 0) { + rel_fdc(); return -ENODEV; } if (fd_calibrate(drive) == 0) { - rel_hw(); + rel_fdc(); return -ENXIO; } floppy_off(drive); - rel_hw(); + rel_fdc(); break; case FDFMTTRK: if (param < unit[drive].type->tracks * unit[drive].type->heads) { get_fdc(drive); - get_hw(drive); - fd_select(drive); - if (fd_seek(drive,param)!=0){ - savedtrack=param; - memset(trackdata, FD_FILL_BYTE, - unit[drive].sects*512); + if (fd_seek(drive,param) != 0){ + memset(unit[drive].trackbuf, FD_FILL_BYTE, + unit[drive].dtype->sects * unit[drive].type->sect_mult * 512); non_int_flush_track(drive); } floppy_off(drive); - rel_hw(); rel_fdc(); } else @@ -1575,33 +1542,27 @@ static int fd_ioctl(struct inode *inode, struct file *filp, memset((void *)&getprm, 0, sizeof (getprm)); getprm.track=unit[drive].type->tracks; getprm.head=unit[drive].type->heads; - getprm.sect=unit[drive].sects; + getprm.sect=unit[drive].dtype->sects * unit[drive].type->sect_mult; getprm.size=unit[drive].blocks; if (copy_to_user((void *)param, - (void *)&getprm, - sizeof(struct floppy_struct))) + (void *)&getprm, + sizeof(struct floppy_struct))) return -EFAULT; break; case BLKGETSIZE: return put_user(unit[drive].blocks,(long *)param); + break; case FDSETPRM: case FDDEFPRM: return -EINVAL; - case FDFLUSH: - save_flags(flags); - cli(); - if ((drive == selected) && (writepending)) { - del_timer (&flush_track_timer); - restore_flags(flags); - non_int_flush_track(selected); - } - else - restore_flags(flags); + case FDFLUSH: /* unconditionally, even if not needed */ + del_timer (flush_track_timer + drive); + non_int_flush_track(drive); break; #ifdef RAW_IOCTL case IOCTL_RAW_TRACK: if (copy_to_user((void *)param, raw_buf, - unit[drive].type->read_size)) + unit[drive].type->read_size)) return -EFAULT; else return unit[drive].type->read_size; @@ -1613,76 +1574,16 @@ static int fd_ioctl(struct inode *inode, struct file *filp, return 0; } -/*====================================================================== - Return unit ID number of given disk -======================================================================*/ -static unsigned long get_drive_id(int drive) -{ - static int called = 0; - int i; - ulong id = 0; - - drive&=3; - get_hw(drive); - /* set up for ID */ - MOTOR_ON; - udelay(2); - SELECT(SELMASK(drive)); - udelay(2); - DESELECT(SELMASK(drive)); - udelay(2); - MOTOR_OFF; - udelay(2); - SELECT(SELMASK(drive)); - udelay(2); - DESELECT(SELMASK(drive)); - udelay(2); - - /* loop and read disk ID */ - for (i=0; i<32; i++) { - SELECT(SELMASK(drive)); - udelay(2); - - /* read and store value of DSKRDY */ - id <<= 1; - id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */ - - DESELECT(SELMASK(drive)); - } - - selected = -1; - rel_hw(); - - /* - * RB: At least A500/A2000's df0: don't identify themselves. - * As every (real) Amiga has at least a 3.5" DD drive as df0: - * we default to that if df0: doesn't identify as a certain - * type. - */ - if(drive == 0 && id == FD_NODRIVE) - { - id = fd_def_df0; - printk("%sfd: drive 0 didn't identify, setting default %08lx\n", - (called == 0)? KERN_NOTICE:"", (ulong)fd_def_df0); - if (called == 0) - called++; - } - /* return the ID value */ - return (id); -} - static void fd_probe(int dev) { unsigned long code; int type; int drive; - int system; drive = dev & 3; - code = get_drive_id(drive); + code = fd_get_drive_id(drive); /* get drive type */ - unit[drive].type = NULL; for (type = 0; type < num_dr_types; type++) if (drive_types[type].code == code) break; @@ -1694,39 +1595,13 @@ static void fd_probe(int dev) return; } - unit[drive].type = &drive_types[type]; + unit[drive].type = drive_types + type; unit[drive].track = -1; unit[drive].disk = -1; unit[drive].motor = 0; unit[drive].busy = 0; unit[drive].status = -1; - - - system=(dev & 4)>>2; - unit[drive].dtype=&data_types[system]; - unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult; - unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks* - unit[drive].sects; - - floppy_sizes[MINOR(dev)] = unit[drive].blocks >> 1; - -} - -__initfunc(static void probe_drives(void)) -{ - int drive,found; - - printk(KERN_INFO "FD: probing units\n" KERN_INFO "found "); - found=0; - for(drive=0;drive<FD_MAX_UNITS;drive++) { - fd_probe(drive); - if (unit[drive].type->code != FD_NODRIVE) { - printk("fd%d ",drive); - found=1; - } - } - printk("%s\n",(found==0)?" no drives":""); } /* @@ -1741,7 +1616,7 @@ static int floppy_open(struct inode *inode, struct file *filp) int system; unsigned long flags; - drive = inode->i_rdev & 3; + drive = MINOR(inode->i_rdev) & 3; old_dev = fd_device[drive]; if (fd_ref[drive]) @@ -1751,17 +1626,20 @@ static int floppy_open(struct inode *inode, struct file *filp) if (unit[drive].type->code == FD_NODRIVE) return -ENODEV; - if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) { + if (filp && filp->f_mode & 3) { + check_disk_change(inode->i_rdev); + if (filp->f_mode & 2 ) { int wrprot; - get_hw(drive); + get_fdc(drive); fd_select (drive); wrprot = !(ciaa.pra & DSKPROT); fd_deselect (drive); - rel_hw(); + rel_fdc(); if (wrprot) return -EROFS; + } } save_flags(flags); @@ -1777,55 +1655,87 @@ static int floppy_open(struct inode *inode, struct file *filp) if (old_dev && old_dev != inode->i_rdev) invalidate_buffers(old_dev); - if (filp && filp->f_mode) - check_disk_change(inode->i_rdev); - system=(inode->i_rdev & 4)>>2; unit[drive].dtype=&data_types[system]; - unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult; unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks* - unit[drive].sects; + data_types[system].sects*unit[drive].type->sect_mult; + floppy_sizes[MINOR(inode->i_rdev)] = unit[drive].blocks >> 1; -printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name, - data_types[system].name); + printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive, + unit[drive].type->name, data_types[system].name); return 0; } static int floppy_release(struct inode * inode, struct file * filp) { - unsigned long flags; +#ifdef DEBUG struct super_block * sb; +#endif + int drive = MINOR(inode->i_rdev) & 3; fsync_dev(inode->i_rdev); + +#ifdef DEBUG + /* This is now handled in floppy_change, but still useful for debugging */ sb = get_super(inode->i_rdev); if (sb) invalidate_inodes(sb); invalidate_buffers(inode->i_rdev); - save_flags (flags); - cli(); - if ((inode->i_rdev & 3) == selected && writepending) { - del_timer (&flush_track_timer); - restore_flags (flags); - non_int_flush_track (selected); - } else - restore_flags (flags); +#endif + + if (unit[drive].dirty == 1) { + del_timer (flush_track_timer + drive); + non_int_flush_track (drive); + } - if (!fd_ref[inode->i_rdev & 3]--) { + if (!fd_ref[drive]--) { printk(KERN_CRIT "floppy_release with fd_ref == 0"); - fd_ref[inode->i_rdev & 3] = 0; + fd_ref[drive] = 0; } #ifdef MODULE /* the mod_use counter is handled this way */ - floppy_off (inode->i_rdev & 3); + floppy_off (drive | 0x40000000); #endif return 0; } -__initfunc(void amiga_floppy_setup (char *str, int *ints)) +/* + * floppy-change is never called from an interrupt, so we can relax a bit + * here, sleep etc. Note that floppy-on tries to set current_DOR to point + * to the desired drive, but it will probably not survive the sleep if + * several floppies are used at the same time: thus the loop. + */ +static int amiga_floppy_change(kdev_t dev) { - printk ("amiflop: Setting default df0 to %x\n", ints[1]); - fd_def_df0 = ints[1]; + int drive = MINOR(dev) & 3; + int changed; + static int first_time = 1; + + if (MAJOR(dev) != MAJOR_NR) { + printk(KERN_CRIT "floppy_change: not a floppy\n"); + return 0; + } + + if (first_time) + changed = first_time--; + else { + get_fdc(drive); + fd_select (drive); + changed = !(ciaa.pra & DSKCHANGE); + fd_deselect (drive); + rel_fdc(); + } + + if (changed) { + fd_probe(drive); + unit[drive].track = -1; + unit[drive].dirty = 0; + writepending = 0; /* if this was true before, too bad! */ + writefromint = 0; + return 1; + } + return 0; } static struct file_operations floppy_fops = { @@ -1844,24 +1754,40 @@ static struct file_operations floppy_fops = { NULL, /* revalidate */ }; -static void fd_block_done(int irq, void *dummy, struct pt_regs *fp) +__initfunc(void amiga_floppy_setup (char *str, int *ints)) { - if (block_flag) - custom.dsklen = 0x4000; - - block_flag = 0; - wake_up (&wait_fd_block); + printk (KERN_INFO "amiflop: Setting default df0 to %x\n", ints[1]); + fd_def_df0 = ints[1]; +} - if (writefromint) { - /* - * if it was a write from an interrupt, - * we will call post_write from here - */ - writepending = 2; - post_write_timer.expires = 1; /* at least 2 ms */ - add_timer(&post_write_timer); - } +__initfunc(static int fd_probe_drives(void)) +{ + int drive,drives,nomem; + printk(KERN_INFO "FD: probing units\n" KERN_INFO "found "); + drives=0; + nomem=0; + for(drive=0;drive<FD_MAX_UNITS;drive++) { + fd_probe(drive); + if (unit[drive].type->code != FD_NODRIVE) { + drives++; + if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) { + printk("no mem for "); + unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */ + drives--; + nomem = 1; + } + printk("fd%d ",drive); + } + } + if ((drives > 0) || (nomem == 0)) { + if (drives == 0) + printk("no drives"); + printk("\n"); + return drives; + } + printk("\n"); + return -ENOMEM; } __initfunc(int amiga_floppy_init(void)) @@ -1870,11 +1796,36 @@ __initfunc(int amiga_floppy_init(void)) if (!AMIGAHW_PRESENT(AMI_FLOPPY)) return -ENXIO; - if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { - printk("Unable to get major %d for floppy\n",MAJOR_NR); + printk("fd: Unable to get major %d for floppy\n",MAJOR_NR); return -EBUSY; } + if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE)) == NULL) { + printk("fd: cannot get chip mem buffer\n"); + unregister_blkdev(MAJOR_NR,"fd"); + return -ENOMEM; + } + + if (request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL) != 0) { + printk("fd: cannot get irq for dma\n"); + amiga_chip_free(raw_buf); + unregister_blkdev(MAJOR_NR,"fd"); + return -EBUSY; + } + if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL) != 0) { + printk("fd: cannot get irq for timer\n"); + free_irq(IRQ_FLOPPY, NULL); + amiga_chip_free(raw_buf); + unregister_blkdev(MAJOR_NR,"fd"); + return -EBUSY; + } + if (fd_probe_drives() < 1) { /* No usable drives */ + free_irq(IRQ_AMIGA_CIAA_TB, NULL); + free_irq(IRQ_FLOPPY, NULL); + amiga_chip_free(raw_buf); + unregister_blkdev(MAJOR_NR,"fd"); + return -ENXIO; + } /* initialize variables */ motor_on_timer.next = NULL; @@ -1886,18 +1837,17 @@ __initfunc(int amiga_floppy_init(void)) motor_off_timer[i].next = NULL; motor_off_timer[i].prev = NULL; motor_off_timer[i].expires = 0; - motor_off_timer[i].data = i; + motor_off_timer[i].data = i|0x80000000; motor_off_timer[i].function = fd_motor_off; + flush_track_timer[i].next = NULL; + flush_track_timer[i].prev = NULL; + flush_track_timer[i].expires = 0; + flush_track_timer[i].data = i; + flush_track_timer[i].function = flush_track_callback; unit[i].track = -1; } - flush_track_timer.next = NULL; - flush_track_timer.prev = NULL; - flush_track_timer.expires = 0; - flush_track_timer.data = 0; - flush_track_timer.function = flush_track_callback; - post_write_timer.next = NULL; post_write_timer.prev = NULL; post_write_timer.expires = 0; @@ -1909,9 +1859,6 @@ __initfunc(int amiga_floppy_init(void)) blk_size[MAJOR_NR] = floppy_sizes; - timer_table[FLOPPY_TIMER].fn = NULL; - timer_active &= ~(1 << FLOPPY_TIMER); - #if 0 /* Doesn't seem to be correct */ if (fd_def_df0==0) { if ((amiga.model == AMI_3000) || (amiga.model == AMI_3000T) || @@ -1925,14 +1872,10 @@ __initfunc(int amiga_floppy_init(void)) fd_def_df0 = FD_DD_3; #endif - probe_drives(); - - raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE); - for (i = 0; i < 128; i++) - mfmdecode[i]=255; + mfmdecode[i]=255; for (i = 0; i < 16; i++) - mfmdecode[mfmencode[i]]=i; + mfmdecode[mfmencode[i]]=i; /* make sure that disk DMA is enabled */ custom.dmacon = DMAF_SETCLR | DMAF_DISK; @@ -1940,9 +1883,7 @@ __initfunc(int amiga_floppy_init(void)) /* init ms timer */ ciaa.crb = 8; /* one-shot, stop */ - request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL); - request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL); - + (void)do_floppy; /* avoid warning about unused variable */ return 0; } @@ -1958,11 +1899,15 @@ int init_module(void) void cleanup_module(void) { +int i; + +for( i = 0; i < FD_MAX_UNITS; i++) + if (unit[i].type->code != FD_NODRIVE) + kfree(unit[i].trackbuf); free_irq(IRQ_AMIGA_CIAA_TB, NULL); free_irq(IRQ_FLOPPY, NULL); custom.dmacon = DMAF_DISK; /* disable DMA */ amiga_chip_free(raw_buf); -timer_active &= ~(1 << FLOPPY_TIMER); blk_size[MAJOR_NR] = NULL; blksize_size[MAJOR_NR] = NULL; blk_dev[MAJOR_NR].request_fn = NULL; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 9bb78d63e..900ed785a 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -19,7 +19,9 @@ #include <linux/module.h> #include <linux/config.h> +#include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/stat.h> #include <linux/errno.h> #include <linux/major.h> @@ -277,7 +279,7 @@ repeat: end_request(1); goto repeat; error_out: - current_request->next=CURRENT; + current_request->next=CURRENT; CURRENT=current_request; end_request(0); goto repeat; @@ -287,27 +289,36 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) { struct file *file; struct inode *inode; + int error; + + MOD_INC_USE_COUNT; + error = -EBADF; + file = fget(arg); + if (!file) + goto out; - if (arg >= NR_OPEN || !(file = current->files->fd[arg])) - return -EBADF; + error = -EBUSY; if (lo->lo_inode) - return -EBUSY; + goto out_putf; + + error = -EINVAL; inode = file->f_dentry->d_inode; if (!inode) { printk("loop_set_fd: NULL inode?!?\n"); - return -EINVAL; + goto out_putf; } + if (S_ISBLK(inode->i_mode)) { - int error = blkdev_open(inode, file); - if (error) - return error; + error = blkdev_open(inode, file); lo->lo_device = inode->i_rdev; lo->lo_flags = 0; } else if (S_ISREG(inode->i_mode)) { lo->lo_device = inode->i_dev; lo->lo_flags = LO_FLAGS_DO_BMAP; - } else - return -EINVAL; + error = 0; + } + if (error) + goto out_putf; if (IS_RDONLY (inode) || is_read_only(lo->lo_device)) { lo->lo_flags |= LO_FLAGS_READ_ONLY; @@ -317,25 +328,34 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) set_device_ro(dev, 0); } + /* N.B. Should keep the file or dentry ... */ + inode->i_count++; lo->lo_inode = inode; - lo->lo_inode->i_count++; lo->transfer = NULL; figure_loop_size(lo); - MOD_INC_USE_COUNT; - return 0; + +out_putf: + fput(file); +out: + if (error) + MOD_DEC_USE_COUNT; + return error; } static int loop_clr_fd(struct loop_device *lo, kdev_t dev) { - if (!lo->lo_inode) + struct inode *inode = lo->lo_inode; + + if (!inode) return -ENXIO; if (lo->lo_refcnt > 1) /* we needed one fd for the ioctl */ return -EBUSY; - if (S_ISBLK(lo->lo_inode->i_mode)) - blkdev_release (lo->lo_inode); - iput(lo->lo_inode); - lo->lo_device = 0; + + if (S_ISBLK(inode->i_mode)) + blkdev_release (inode); lo->lo_inode = NULL; + iput(inode); + lo->lo_device = 0; lo->lo_encrypt_type = 0; lo->lo_offset = 0; lo->lo_encrypt_key_size = 0; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 9917963a9..b9ebdc848 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -313,7 +313,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct nbd_device *lo; - int dev; + int dev, error; if (!suser()) return -EPERM; @@ -322,6 +322,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, dev = MINOR(inode->i_rdev); if (dev >= MAX_NBD) return -ENODEV; + lo = &nbd_dev[dev]; switch (cmd) { case NBD_CLEAR_SOCK: @@ -329,19 +330,26 @@ static int nbd_ioctl(struct inode *inode, struct file *file, printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n"); return -EBUSY; } - if (!lo->file) + file = lo->file; + if (!file) return -EINVAL; - lo->file->f_count--; lo->file = NULL; lo->sock = NULL; + fput(file); return 0; case NBD_SET_SOCK: - file = current->files->fd[arg]; - inode = file->f_dentry->d_inode; - file->f_count++; - lo->sock = &inode->u.socket_i; - lo->file = file; - return 0; + if (lo->file) + return -EBUSY; + error = -EINVAL; + file = fget(arg); + if (file) { + inode = file->f_dentry->d_inode; + /* N.B. Should verify that it's a socket */ + lo->file = file; + lo->sock = &inode->u.socket_i; + error = 0; + } + return error; case NBD_SET_BLKSIZE: if ((arg & 511) || (arg > PAGE_SIZE)) return -EINVAL; @@ -383,6 +391,7 @@ static int nbd_release(struct inode *inode, struct file *file) if (lo->refcnt <= 0) printk(KERN_ALERT "nbd_release: refcount(%d) <= 0\n", lo->refcnt); lo->refcnt--; + /* N.B. Doesn't lo->file need an fput?? */ MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/char/amigamouse.c b/drivers/char/amigamouse.c index 96a7e019d..a3cbd737e 100644 --- a/drivers/char/amigamouse.c +++ b/drivers/char/amigamouse.c @@ -267,7 +267,7 @@ static ssize_t read_mouse(struct file * file, char * buffer, mouse.ready = 0; AMI_MSE_INT_ON(); - if ((put_user(buttons | 0x80, buffer++)) || + if (put_user(buttons | 0x80, buffer++) || put_user((char)dx, buffer++) || put_user((char)dy, buffer++)) return -EINVAL; diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c new file mode 100644 index 000000000..9647caf90 --- /dev/null +++ b/drivers/char/amikeyb.c @@ -0,0 +1,343 @@ +/* + * linux/arch/m68k/amiga/amikeyb.c + * + * Amiga Keyboard driver for Linux/m68k + * + * 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. + */ + +/* + * Amiga support by Hamish Macdonald + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/keyboard.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/kd.h> +#include <linux/random.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/kbd_ll.h> + +#include <asm/amigaints.h> +#include <asm/amigahw.h> +#include <asm/irq.h> + +#define AMIKEY_CAPS (0x62) +#define BREAK_MASK (0x80) +#define RESET_WARNING (0xf0) /* before rotation */ + +static u_short amiplain_map[NR_KEYS] __initdata = { + 0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, + 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf05c, 0xf200, 0xf300, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf200, 0xf301, 0xf302, 0xf303, + 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, + 0xfb6c, 0xf03b, 0xf027, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, + 0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, + 0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, + 0xf108, 0xf109, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf30a, 0xf11b, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amishift_map[NR_KEYS] __initdata = { + 0xf07e, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, + 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07c, 0xf200, 0xf300, + 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, + 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf200, 0xf301, 0xf302, 0xf303, + 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, + 0xfb4c, 0xf03a, 0xf022, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, + 0xf03c, 0xf03e, 0xf03f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, + 0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111, + 0xf112, 0xf113, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf203, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amialtgr_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, + 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xf300, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf07e, 0xf200, 0xf301, 0xf302, 0xf303, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, + 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513, + 0xf514, 0xf515, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amictrl_map[NR_KEYS] __initdata = { + 0xf000, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, + 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf01c, 0xf200, 0xf300, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf200, 0xf301, 0xf302, 0xf303, + 0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, + 0xf00c, 0xf200, 0xf007, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d, + 0xf200, 0xf200, 0xf07f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, + 0xf000, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, + 0xf108, 0xf109, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf202, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amishift_ctrl_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, + 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amialt_map[NR_KEYS] __initdata = { + 0xf860, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, + 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf85c, 0xf200, 0xf900, + 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, + 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf200, 0xf901, 0xf902, 0xf903, + 0xf861, 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, + 0xf86c, 0xf83b, 0xf827, 0xf200, 0xf200, 0xf904, 0xf905, 0xf906, + 0xf200, 0xf87a, 0xf878, 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d, + 0xf82c, 0xf82e, 0xf82f, 0xf200, 0xf310, 0xf907, 0xf908, 0xf909, + 0xf820, 0xf87f, 0xf809, 0xf30e, 0xf80d, 0xf81b, 0xf87f, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507, + 0xf508, 0xf509, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short amictrl_alt_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c, 0xf307, 0xf308, 0xf309, + 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200, + 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +#define DEFAULT_KEYB_REP_DELAY (HZ/4) +#define DEFAULT_KEYB_REP_RATE (HZ/25) + +/* These could be settable by some ioctl() in future... */ +static unsigned int key_repeat_delay = DEFAULT_KEYB_REP_DELAY; +static unsigned int key_repeat_rate = DEFAULT_KEYB_REP_RATE; + +static unsigned char rep_scancode; +static void amikeyb_rep(unsigned long ignore); +static struct timer_list amikeyb_rep_timer = {NULL, NULL, 0, 0, amikeyb_rep}; + +static void amikeyb_rep(unsigned long ignore) +{ + unsigned long flags; + save_flags(flags); + cli(); + + kbd_pt_regs = NULL; + + amikeyb_rep_timer.expires = jiffies + key_repeat_rate; + amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; + add_timer(&amikeyb_rep_timer); + handle_scancode(rep_scancode); + + restore_flags(flags); +} + +static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) +{ + unsigned char scancode, break_flag, keycode; + static int reset_warning = 0; + + /* save frame for register dump */ + kbd_pt_regs = fp; + + /* get and invert scancode (keyboard is active low) */ + scancode = ~ciaa.sdr; + + /* switch SP pin to output for handshake */ + ciaa.cra |= 0x40; + +#if 0 /* No longer used */ + /* + * On receipt of the second RESET_WARNING, we must not pull KDAT high + * again to delay the hard reset as long as possible. + * + * Note that not all keyboards send reset warnings... + */ + if (reset_warning) + if (scancode == RESET_WARNING) { + printk(KERN_ALERT "amikeyb: Ctrl-Amiga-Amiga reset warning!!\n" + "The system will be reset within 10 seconds!!\n"); + /* Panic doesn't sync from within an interrupt, so we do nothing */ + return; + } else + /* Probably a mistake, cancel the alert */ + reset_warning = 0; +#endif + + /* wait until 85 us have expired */ + udelay(85); + /* switch CIA serial port to input mode */ + ciaa.cra &= ~0x40; + + mark_bh(KEYBOARD_BH); + + /* rotate scan code to get up/down bit in proper position */ + scancode = ((scancode >> 1) & 0x7f) | ((scancode << 7) & 0x80); + + /* + * Check make/break first + */ + break_flag = scancode & BREAK_MASK; + keycode = scancode & (unsigned char)~BREAK_MASK; + + if (keycode == AMIKEY_CAPS) { + /* if the key is CAPS, fake a press/release. */ + handle_scancode(AMIKEY_CAPS); + handle_scancode(BREAK_MASK | AMIKEY_CAPS); + } else if (keycode < 0x78) { + /* handle repeat */ + if (break_flag) { + del_timer(&amikeyb_rep_timer); + rep_scancode = 0; + } else { + del_timer(&amikeyb_rep_timer); + rep_scancode = keycode; + amikeyb_rep_timer.expires = jiffies + key_repeat_delay; + amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; + add_timer(&amikeyb_rep_timer); + } + handle_scancode(scancode); + } else + switch (keycode) { + case 0x78: + reset_warning = 1; + break; + case 0x79: + printk(KERN_WARNING "amikeyb: keyboard lost sync\n"); + break; + case 0x7a: + printk(KERN_WARNING "amikeyb: keyboard buffer overflow\n"); + break; +#if 0 /* obsolete according to the HRM */ + case 0x7b: + printk(KERN_WARNING "amikeyb: keyboard controller failure\n"); + break; +#endif + case 0x7c: + printk(KERN_ERR "amikeyb: keyboard selftest failure\n"); + break; + case 0x7d: + printk(KERN_INFO "amikeyb: initiate power-up key stream\n"); + break; + case 0x7e: + printk(KERN_INFO "amikeyb: terminate power-up key stream\n"); + break; +#if 0 /* obsolete according to the HRM */ + case 0x7f: + printk(KERN_WARNING "amikeyb: keyboard interrupt\n"); + break; +#endif + default: + printk(KERN_WARNING "amikeyb: unknown keyboard communication code 0x%02x\n", + scancode); + break; + } +} + +__initfunc(int amiga_keyb_init(void)) +{ + if (!AMIGAHW_PRESENT(AMI_KEYBOARD)) + return -EIO; + + /* setup key map */ + memcpy(plain_map, amiplain_map, sizeof(plain_map)); + memcpy(shift_map, amishift_map, sizeof(shift_map)); + memcpy(altgr_map, amialtgr_map, sizeof(altgr_map)); + memcpy(ctrl_map, amictrl_map, sizeof(ctrl_map)); + memcpy(shift_ctrl_map, amishift_ctrl_map, sizeof(shift_ctrl_map)); + memcpy(alt_map, amialt_map, sizeof(alt_map)); + memcpy(ctrl_alt_map, amictrl_alt_map, sizeof(ctrl_alt_map)); + + /* + * Initialize serial data direction. + */ + ciaa.cra &= ~0x41; /* serial data in, turn off TA */ + + /* + * arrange for processing of keyboard interrupt + */ + request_irq(IRQ_AMIGA_CIAA_SP, keyboard_interrupt, 0, "keyboard", NULL); + + return 0; +} + +int amiga_kbdrate( struct kbd_repeat *k ) +{ + if (k->delay > 0) { + /* convert from msec to jiffies */ + key_repeat_delay = (k->delay * HZ + 500) / 1000; + if (key_repeat_delay < 1) + key_repeat_delay = 1; + } + if (k->rate > 0) { + key_repeat_rate = (k->rate * HZ + 500) / 1000; + if (key_repeat_rate < 1) + key_repeat_rate = 1; + } + + k->delay = key_repeat_delay * 1000 / HZ; + k->rate = key_repeat_rate * 1000 / HZ; + + return( 0 ); +} diff --git a/drivers/char/dn_keyb.c b/drivers/char/dn_keyb.c new file mode 100644 index 000000000..3dd385df1 --- /dev/null +++ b/drivers/char/dn_keyb.c @@ -0,0 +1,614 @@ +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/keyboard.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/kd.h> +#include <linux/random.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/init.h> + +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/apollohw.h> +#include <asm/uaccess.h> + + +extern void handle_scancode(unsigned char); + +#define DNKEY_CAPS 0x7e +#define BREAK_FLAG 0x80 +#define DNKEY_REPEAT_DELAY 50 +#define DNKEY_CTRL 0x43 +#define DNKEY_LSHIFT 0x5e +#define DNKEY_RSHIFT 0x6a +#define DNKEY_REPT 0x5d +#define DNKEY_REPEAT 0x7f +#define DNKEY_LALT 0x75 +#define DNKEY_RALT 0x77 + +#define APOLLO_KEYB_CMD_ENTRIES 16 +#define APOLLO_KBD_MODE_KEYB 0x01 +#define APOLLO_KBD_MODE_MOUSE 0x02 +#define APOLLO_KBD_MODE_CHANGE 0xff + +#define MSE_UPDATE_ON() mouse_update_allowed=1 +#define MSE_UPDATE_OFF() mouse_update_allowed=0 + +static u_char keyb_cmds[APOLLO_KEYB_CMD_ENTRIES]; +static short keyb_cmd_read=0, keyb_cmd_write=0; +static int keyb_cmd_transmit=0; + +static unsigned int kbd_mode=APOLLO_KBD_MODE_KEYB; +static short mouse_dx,mouse_dy,mouse_buttons; +static int mouse_ready=0,mouse_update_allowed=0,mouse_active=0; +static struct wait_queue *mouse_wait=NULL; +static struct fasync_struct *mouse_fasyncptr=NULL; + +#if 0 +static void debug_keyb_timer_handler(unsigned long ignored); +static u_char debug_buf1[4096],debug_buf2[4096],*debug_buf=&debug_buf1[0]; +static u_char *shadow_buf=&debug_buf2[0]; +static short debug_buf_count=0; +static int debug_buf_overrun=0,debug_timer_running=0,debug_buffer_updated=0; +static struct timer_list debug_keyb_timer = { NULL, NULL, 0, 0, + debug_keyb_timer_handler }; +#endif + +static u_short dnplain_map[NR_KEYS] __initdata = { +/* ins del del F1 F2 F3 F4 + mark line char */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* F5 F6 F7 F8 F9 F0 Again Read */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* Edit Exit Hold Copy Paste Grow ESC */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b, +/* 1 2 3 4 5 6 7 8 */ + 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, +/* 9 0 - = ` Back |<-- + Space */ + 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf060, 0xf07f, 0xf200, 0xf200, +/* Shell -->| Tab q w e + Cmd */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xfb71, 0xfb77, 0xfb65, +/* r t y u i o p [ */ + 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 0xfb6f, 0xfb70, 0xf05b, +/* ] Del 7 8 9 + */ + 0xf05d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a, +/* [<--] Up [-->] Ctrl a s */ + 0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb61, 0xfb73, +/* d f g h j k l ; */ + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, +/* ' Return \ 4 5 6 */ + 0xf027, 0xf200, 0xf201, 0xf05c, 0xf200, 0xf304, 0xf305, 0xf306, +/* - <-- Next --> Rept Shift + Window */ + 0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf700, 0xf200, +/* z x c v b n m , */ + 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, +/* . / Shift Pop 1 2 */ + 0xf02e, 0xf02f, 0xf700, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, +/* 3 PgUp Down PgDn Alt Space Alt */ + 0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701, +/* 0 . Enter */ + 0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf700, 0xf200, +}; + +static u_short dnshift_map[NR_KEYS] __initdata = { +/* ins del del F1 F2 F3 F4 + mark line char */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* F5 F6 F7 F8 F9 F0 Again Read */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* Save Abort Help Cut Undo Grow ESC */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b, +/* ! @ # $ % ^ & * */ + 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, +/* ( ) _ + ~ Back |<-- + Space */ + 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07e, 0xf07f, 0xf200, 0xf200, +/* Shell -->| Tab Q W E + Cmd */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xfb51, 0xfb57, 0xfb45, +/* R T Y U I O P { */ + 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 0xfb4f, 0xfb50, 0xf07b, +/* } Del 7 8 9 + */ + 0xf07d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a, +/* [<--] Up [-->] Ctrl A S */ + 0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb41, 0xfb53, +/* D F G H J K L : */ + 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, +/* " Return | 4 5 6 */ + 0xf022, 0xf200, 0xf201, 0xf07c, 0xf200, 0xf304, 0xf305, 0xf306, +/* - <-- Next --> Rept Shift + Window */ + 0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf700, 0xf200, +/* Z X C V B N M < */ + 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, +/* > ? Shift Pop 1 2 */ + 0xf03e, 0xf03f, 0xf700, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, +/* 3 PgUp Down PgDn Alt Space Alt */ + 0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701, +/* 0 . Enter */ + 0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf708, 0xf200, +}; + +static u_short dnctrl_map[NR_KEYS] __initdata = { +/* ins del del F1 F2 F3 F4 + mark line char */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* F5 F6 F7 F8 F9 F0 Again Read */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +/* Save Abort Help Cut Undo Grow ESC */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b, +/* ! @ # $ % ^ & * */ + 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, +/* ( ) _ + ~ Back |<-- + Space */ + 0xf200, 0xf200, 0xf01f, 0xf200, 0xf01c, 0xf200, 0xf200, 0xf200, +/* Shell -->| Tab Q W E + Cmd */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xf011, 0xf017, 0xf005, +/* R T Y U I O P { */ + 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf01b, +/* } Del 7 8 9 + */ + 0xf01d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a, +/* [<--] Up [-->] Ctrl A S */ + 0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb01, 0xfb53, +/* D F G H J K L : */ + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, +/* " Return | 4 5 6 */ + 0xf200, 0xf200, 0xf201, 0xf01c, 0xf200, 0xf304, 0xf305, 0xf306, +/* - <-- Next --> Rept Shift + Window */ + 0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf704, 0xf200, +/* Z X C V B N M < */ + 0xf01a, 0xf018, 0xf003, 0xf016, 0xf002, 0xf00e, 0xf01d, 0xf03c, +/* > ? Shift Pop 1 2 */ + 0xf03e, 0xf03f, 0xf705, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, +/* 3 PgUp Down PgDn Alt Space Alt */ + 0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701, +/* 0 . Enter */ + 0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, +}; + +static u_short dnalt_map[NR_KEYS] __initdata = { +/* ins del del F1 F2 F3 F4 + mark line char */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf500, 0xf501, 0xf502, 0xf503, +/* F5 F6 F7 F8 F9 F0 Again Read */ + 0xf504, 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf200, 0xf200, +/* Edit Exit Hold Copy Paste Grow ESC */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b, +/* 1 2 3 4 5 6 7 8 */ + 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, +/* 9 0 - = ` Back |<-- + Space */ + 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf860, 0xf87f, 0xf200, 0xf200, +/* Shell -->| Tab q w e + Cmd */ + 0xf200, 0xf200, 0xf200, 0xf200, 0xf809, 0xf871, 0xf877, 0xf865, +/* r t y u i o p [ */ + 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 0xf86f, 0xf870, 0xf85b, +/* ] Del 7 8 9 + */ + 0xf05d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a, +/* [<--] Up [-->] Ctrl a s */ + 0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xf861, 0xf873, +/* d f g h j k l ; */ + 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf03b, +/* ' Return \ 4 5 6 */ + 0xf027, 0xf200, 0xf201, 0xf05c, 0xf200, 0xf304, 0xf305, 0xf306, +/* - <-- Next --> Rept Shift + Window */ + 0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf704, 0xf200, +/* z x c v b n m , */ + 0xf87a, 0xf878, 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d, 0xf82c, +/* . / Shift Pop 1 2 */ + 0xf82e, 0xf82f, 0xf705, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, +/* 3 PgUp Down PgDn Alt Space Alt */ + 0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf820, 0xf701, +/* 0 . Enter */ + 0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, +}; + +static u_short dnaltgr_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 +}; + +static u_short dnshift_ctrl_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 +}; + +static u_short dnctrl_alt_map[NR_KEYS] __initdata = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200 +}; + +#if 0 +static void debug_keyb_timer_handler(unsigned long ignored) { + + unsigned long flags; + u_char *swap; + short length,i; + + if((jiffies-debug_buffer_updated) > 100) { + save_flags(flags); + cli(); + length=debug_buf_count; + swap=debug_buf; + debug_buf=shadow_buf; + shadow_buf=swap; + debug_buf_count=0; + debug_timer_running=0; + restore_flags(flags); + for(i=1;length;length--,i++) + printk("%02x%c",*(swap++), (i % 25) ? ' ' : '\n'); + printk("\n"); + } + else { + debug_keyb_timer.expires=jiffies+10; + add_timer(&debug_keyb_timer); + } +} +#endif + +static unsigned int mouse_poll(struct file *file, poll_table * wait) +{ + poll_wait(&mouse_wait, wait); + if (mouse_ready) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t write_mouse(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t read_mouse(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + int dx,dy,r; + unsigned char buttons; + + if (count < 3) + return -EINVAL; + if ((r = verify_area(VERIFY_WRITE, buffer, count))) + return r; + if (!mouse_ready) + return -EAGAIN; + + MSE_UPDATE_OFF(); + dx=mouse_dx; + dy=mouse_dy; + if (dx < -127) + dx = -127; + else + if (dx > 127) + dx = 127; + if (dy < -127) + dy = -127; + else + if (dy > 127) + dy = 127; + buttons=(mouse_buttons & 1 ? 4 : 0) | + (mouse_buttons & 2 ? 1 : 0) | + (mouse_buttons & 4 ? 2 : 0); + + mouse_dx-=dx; + mouse_dy-=dy; + MSE_UPDATE_ON(); + + if (put_user(buttons | 0x80, buffer++) || + put_user((char)dx, buffer++) || + put_user((char)dy, buffer++)) + return -EINVAL; + + if (count > 3) + if (clear_user(buffer, count - 3)) + return -EFAULT; + return count; +} + +static int fasync_mouse(struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(filp, on, &mouse_fasyncptr); + if (retval < 0) + return retval; + return 0; +} + + +static int release_mouse(struct inode * inode, struct file * file) +{ + fasync_mouse(file, 0); + if (--mouse_active) + return 0; + MSE_UPDATE_OFF(); + MOD_DEC_USE_COUNT; + return 0; +} + +static int open_mouse(struct inode * inode, struct file * file) +{ + if (mouse_active++) + return 0; + /* + * use VBL to poll mouse deltas + */ + + mouse_dx = 0; + mouse_dy = 0; + mouse_buttons = 0; + mouse_active = 1; + MOD_INC_USE_COUNT; + MSE_UPDATE_ON(); + return 0; +} + +static void dn_keyb_process_key_event(unsigned char scancode) { + + static unsigned char lastscancode; + unsigned char prev_scancode=lastscancode; + static unsigned int lastkeypress; + + lastscancode=scancode; + + /* printk("scan: %02x, lastscan: %02X, prev_scancode: %02X\n",scancode,lastscancode,prev_scancode); */ + + if(prev_scancode==APOLLO_KBD_MODE_CHANGE) { + kbd_mode=scancode; +/* printk("modechange: %d\n",scancode);*/ + } + else if((scancode & (~BREAK_FLAG)) == DNKEY_CAPS) { + /* printk("handle_scancode: %02x\n",DNKEY_CAPS); */ + handle_scancode(DNKEY_CAPS); + /* printk("handle_scancode: %02x\n",BREAK_FLAG | DNKEY_CAPS); */ + handle_scancode(BREAK_FLAG | DNKEY_CAPS); + } + else if( (scancode == DNKEY_REPEAT) && (prev_scancode < 0x7e) && + !(prev_scancode==DNKEY_CTRL || prev_scancode==DNKEY_LSHIFT || + prev_scancode==DNKEY_RSHIFT || prev_scancode==DNKEY_REPT || + prev_scancode==DNKEY_LALT || prev_scancode==DNKEY_RALT)) { + if(jiffies-lastkeypress > DNKEY_REPEAT_DELAY) { + /* printk("handle_scancode: %02x\n",prev_scancode); */ + handle_scancode(prev_scancode); + } + lastscancode=prev_scancode; + } + else { + /* printk("handle_scancode: %02x\n",scancode); */ + handle_scancode(scancode); + lastkeypress=jiffies; + } +} + +static void dn_keyb_process_mouse_event(unsigned char mouse_data) { + + static short mouse_byte_count=0; + static u_char mouse_packet[3]; + + mouse_packet[mouse_byte_count++]=mouse_data; + + if(mouse_byte_count==3) { + if(mouse_packet[0]==APOLLO_KBD_MODE_CHANGE) { + kbd_mode=mouse_packet[1]; + mouse_byte_count=0; +/* printk("modechange: %d\n",mouse_packet[1]); */ + if(kbd_mode==APOLLO_KBD_MODE_KEYB) + dn_keyb_process_key_event(mouse_packet[2]); + } + if((mouse_packet[0] & 0x8f) == 0x80) { + if(mouse_update_allowed) { + mouse_ready=1; + mouse_buttons=(mouse_packet[0] >> 4) & 0x7; + mouse_dx+=mouse_packet[1] == 0xff ? 0 : (signed char)mouse_packet[1]; + mouse_dy+=mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2]; + wake_up_interruptible(&mouse_wait); + if (mouse_dx < -2048) + mouse_dx = -2048; + else + if (mouse_dx > 2048) + mouse_dx = 2048; + if (mouse_dy < -2048) + mouse_dy = -2048; + else + if (mouse_dy > 2048) + mouse_dy = 2048; + if (mouse_fasyncptr) + kill_fasync(mouse_fasyncptr, SIGIO); + } + mouse_byte_count=0; +/* printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */ + } + } +} + +static void dn_keyb_int(int irq, void *dummy, struct pt_regs *fp) { + + unsigned char data; + unsigned long flags; + int scn2681_ints; + + do { + scn2681_ints=sio01.isr_imr & 3; + if(scn2681_ints & 2) { + data=sio01.rhra_thra; +#if 0 + if(debug_buf_count<4096) { + debug_buf[debug_buf_count++]=data; + debug_buffer_updated=jiffies; + if(!debug_timer_running) { + add_timer(&debug_keyb_timer); + debug_keyb_timer.expires=jiffies+10; + debug_timer_running=1; + } + } + else + debug_buf_overrun=1; +#endif + if(sio01.sra_csra & 0x10) { + printk("whaa overrun !\n"); + continue; + } + + if(kbd_mode==APOLLO_KBD_MODE_KEYB) + dn_keyb_process_key_event(data); + else + dn_keyb_process_mouse_event(data); + } + + if(scn2681_ints & 1) { + save_flags(flags); + cli(); + if(keyb_cmd_write!=keyb_cmd_read) { + sio01.rhra_thra=keyb_cmds[keyb_cmd_read++]; + if(keyb_cmd_read==APOLLO_KEYB_CMD_ENTRIES) + keyb_cmd_read=0; + keyb_cmd_transmit=1; + } + else { + keyb_cmd_transmit=0; + sio01.BRGtest_cra=9; + } + restore_flags(flags); + } + } while(scn2681_ints) ; +} + +void write_keyb_cmd(u_short length, u_char *cmd) { + + unsigned long flags; + + if((keyb_cmd_write==keyb_cmd_read) && keyb_cmd_transmit) + return; + + save_flags(flags); + cli(); + for(;length;length--) { + keyb_cmds[keyb_cmd_write++]=*(cmd++); + if(keyb_cmd_write==keyb_cmd_read) + return; + if(keyb_cmd_write==APOLLO_KEYB_CMD_ENTRIES) + keyb_cmd_write=0; + } + if(!keyb_cmd_transmit) { + sio01.BRGtest_cra=5; + } + restore_flags(flags); + +} + +struct file_operations apollo_mouse_fops = { + NULL, /* mouse_seek */ + read_mouse, + write_mouse, + NULL, /* mouse_readdir */ + mouse_poll, /* mouse_poll */ + NULL, /* mouse_ioctl */ + NULL, /* mouse_mmap */ + open_mouse, + release_mouse, + NULL, + fasync_mouse, +}; + +static struct miscdevice apollo_mouse = { + APOLLO_MOUSE_MINOR, "apollomouse", &apollo_mouse_fops +}; + +__initfunc(int dn_keyb_init(void)) { + +/* printk("dn_keyb_init\n"); */ + + memcpy(plain_map, dnplain_map, sizeof(plain_map)); + memcpy(shift_map, dnshift_map, sizeof(shift_map)); + memcpy(altgr_map, dnaltgr_map, sizeof(altgr_map)); + memcpy(ctrl_map, dnctrl_map, sizeof(ctrl_map)); + memcpy(shift_ctrl_map, dnshift_ctrl_map, sizeof(shift_ctrl_map)); + memcpy(alt_map, dnalt_map, sizeof(alt_map)); + memcpy(ctrl_alt_map, dnctrl_alt_map, sizeof(ctrl_alt_map)); + + mouse_dx=0; + mouse_dy=0; + mouse_buttons=0; + mouse_wait=NULL; + + misc_register(&apollo_mouse); + + /* program UpDownMode */ + + while(!(sio01.sra_csra & 0x4)); + sio01.rhra_thra=0xff; + + while(!(sio01.sra_csra & 0x4)); + sio01.rhra_thra=0x1; + + request_irq(1, dn_keyb_int,0,NULL,NULL); + + /* enable receive int on DUART */ + sio01.isr_imr=3; + + return 0; + +} + +int dn_dummy_kbdrate(struct kbd_repeat *k) { + + printk("dn_dummy_kbdrate\n"); + + return 0; + +} diff --git a/drivers/char/hfmodem/gentbl.c b/drivers/char/hfmodem/gentbl.c index d60651b1b..c99b963d8 100644 --- a/drivers/char/hfmodem/gentbl.c +++ b/drivers/char/hfmodem/gentbl.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n", argv[0]); gensintbl(); - exit(0); + return(0); } /* --------------------------------------------------------------------- */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 0d7ea6da0..2e0dd3736 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -170,7 +170,7 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); */ static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; static char *stli_drvname = "istallion"; -static char *stli_drvversion = "5.4.3"; +static char *stli_drvversion = "5.4.5"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; diff --git a/drivers/char/macmouse.c b/drivers/char/macmouse.c new file mode 100644 index 000000000..0cf1fab20 --- /dev/null +++ b/drivers/char/macmouse.c @@ -0,0 +1,311 @@ +/* + * Macintosh ADB Mouse driver for Linux + * + * 27 Oct 1997 Michael Schmitz + * + * Apple mouse protocol according to: + * + * Device code shamelessly stolen from: + */ +/* + * Atari Mouse Driver for Linux + * by Robert de Vries (robert@and.nl) 19Jul93 + * + * 16 Nov 1994 Andreas Schwab + * Compatibility with busmouse + * Support for three button mouse (shamelessly stolen from MiNT) + * third button wired to one of the joystick directions on joystick 1 + * + * 1996/02/11 Andreas Schwab + * Module support + * Allow multiple open's + */ + +#include <linux/module.h> + +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/init.h> + +#include <asm/setup.h> +#include <asm/mac_mouse.h> +#include <asm/segment.h> +#include <asm/uaccess.h> + +static struct mouse_status mouse; +static int mac_mouse_x_threshold = 2, mac_mouse_y_threshold = 2; +static int mac_mouse_buttons = 0; + +extern void (*mac_mouse_interrupt_hook) (char *, int); +extern int mac_emulate_button2; +extern int mac_emulate_button3; + +extern int console_loglevel; + +/* + * XXX: need to figure out what ADB mouse packets mean ... + * This is the stuff stolen from the Atari driver ... + */ +static void mac_mouse_interrupt(char *buf, int nb) +{ + static int buttons = 7; /* all mouse buttons _up_ !! */ + + /* + Handler 1 -- 100cpi original Apple mouse protocol. + Handler 2 -- 200cpi original Apple mouse protocol. + + For Apple's standard one-button mouse protocol the data array will + contain the following values: + + BITS COMMENTS + data[0] = 0000 0000 ADB packet identifer. + data[1] = ???? ???? (?) + data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device. + data[3] = bxxx xxxx First button and x-axis motion. + data[4] = byyy yyyy Second button and y-axis motion. + + NOTE: data[0] is confirmed by the parent function and need not be + checked here. + */ + + /* + Handler 4 -- Apple Extended mouse protocol. + + For Apple's 3-button mouse protocol the data array will contain the + following values: + + BITS COMMENTS + data[0] = 0000 0000 ADB packet identifer. + data[1] = 0100 0000 Extended protocol register. + Bits 6-7 are the device id, which should be 1. + Bits 4-5 are resolution which is in "units/inch". + The Logitech MouseMan returns these bits clear but it has + 200/300cpi resolution. + Bits 0-3 are unique vendor id. + data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device. + Bits 2-3 should be 8 + 4. + Bits 4-7 should be 3 for a mouse device. + data[3] = bxxx xxxx Left button and x-axis motion. + data[4] = byyy yyyy Second button and y-axis motion. + data[5] = byyy bxxx Third button and fourth button. + Y is additiona. high bits of y-axis motion. + X is additional high bits of x-axis motion. + + NOTE: data[0] and data[2] are confirmed by the parent function and + need not be checked here. + */ + + /* + * 'buttons' here means 'button down' states! + * Button 1 (left) : bit 2, busmouse button 3 + * Button 2 (right) : bit 0, busmouse button 1 + * Button 3 (middle): bit 1, busmouse button 2 + */ + + /* x/y and buttons swapped */ + + if (buf[0] == 0) { /* real packet : use buttons? */ +#ifdef DEBUG_ADBMOUSE + if (console_loglevel >= 8) + printk("mac_mouse: real data; "); +#endif + /* button 1 (left, bit 2) : always significant ! */ + buttons = (buttons&3) | (buf[3] & 0x80 ? 4 : 0); /* 1+2 unchanged */ + /* button 2 (right, bit 0) present ? */ + if ( !mac_emulate_button2 ) + buttons = (buttons&6) | (buf[4] & 0x80 ? 1 : 0); /* 2+3 unchanged */ + /* button 2 (middle) present? */ + /* data valid only if extended mouse format ! (buf[3] = 0 else)*/ + if ( !mac_emulate_button3 && buf[1]&0x40 ) + buttons = (buttons&5) | (buf[5] & 0x80 ? 2 : 0); /* 1+3 unchanged */ + } else { /* fake packet : use 2+3 */ +#ifdef DEBUG_ADBMOUSE + if (console_loglevel >= 8) + printk("mac_mouse: fake data; "); +#endif + /* we only see state changes here, but the fake driver takes care + * to preserve state... button 1 state must stay unchanged! */ + buttons = (buttons&4) | ((buf[4] & 0x80 ? 1 : 0) | (buf[5] & 0x80 ? 2 : 0)); + } + + add_mouse_randomness(((~buttons & 7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f)); + mouse.buttons = buttons & 7; + mouse.dx += ((buf[4]&0x7f) < 64 ? (buf[4]&0x7f) : (buf[4]&0x7f)-128 ); + mouse.dy -= ((buf[3]&0x7f) < 64 ? (buf[3]&0x7f) : (buf[3]&0x7f)-128 ); + +#ifdef DEBUG_ADBMOUSE + if (console_loglevel >= 8) + printk(" %X %X %X buttons %x dx %d dy %d \n", + buf[3], buf[4], buf[5], mouse.buttons, mouse.dx, mouse.dy); +#endif + + mouse.ready = 1; + wake_up_interruptible(&mouse.wait); + if (mouse.fasyncptr) + kill_fasync(mouse.fasyncptr, SIGIO); + +} + +static int fasync_mouse(struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(filp, on, &mouse.fasyncptr); + if (retval < 0) + return retval; + return 0; +} + +static int release_mouse(struct inode *inode, struct file *file) +{ + fasync_mouse(file, 0); + if (--mouse.active) + return 0; + + mac_mouse_interrupt_hook = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static int open_mouse(struct inode *inode, struct file *file) +{ + if (mouse.active++) + return 0; + + mouse.ready = 0; + + mouse.dx = mouse.dy = 0; + mac_mouse_buttons = 0; + MOD_INC_USE_COUNT; + mac_mouse_interrupt_hook = mac_mouse_interrupt; + return 0; +} + +static ssize_t write_mouse(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t read_mouse(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + int dx, dy, buttons; + + if (count < 3) + return -EINVAL; + if (!mouse.ready) + return -EAGAIN; + dx = mouse.dx; + dy = mouse.dy; + buttons = mouse.buttons; + if (dx > 127) + dx = 127; + else if (dx < -128) + dx = -128; + if (dy > 127) + dy = 127; + else if (dy < -128) + dy = -128; + mouse.dx -= dx; + mouse.dy -= dy; + if (mouse.dx == 0 && mouse.dy == 0) + mouse.ready = 0; + if (put_user(buttons | 0x80, buffer++) || + put_user((char) dx, buffer++) || + put_user((char) dy, buffer++)) + return -EFAULT; + if (count > 3) + if (clear_user(buffer, count - 3)) + return -EFAULT; + return count; +} + +static unsigned int mouse_poll(struct file *file, poll_table *wait) +{ + poll_wait(&mouse.wait, wait); + if (mouse.ready) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations mac_mouse_fops = { + NULL, /* mouse_seek */ + read_mouse, + write_mouse, + NULL, /* mouse_readdir */ + mouse_poll, + NULL, /* mouse_ioctl */ + NULL, /* mouse_mmap */ + open_mouse, + release_mouse, + NULL, + fasync_mouse, +}; + +#define ADB_MOUSE_MINOR 10 + +static struct miscdevice mac_mouse = { + ADB_MOUSE_MINOR, "adbmouse", &mac_mouse_fops +}; + +__initfunc(int mac_mouse_init(void)) +{ + mouse.active = 0; + mouse.ready = 0; + mouse.wait = NULL; + + if (!MACH_IS_MAC) + return -ENODEV; + + printk(KERN_INFO "Macintosh ADB mouse installed.\n"); + misc_register(&mac_mouse); + return 0; +} + + +#define MIN_THRESHOLD 1 +#define MAX_THRESHOLD 20 /* more seems not reasonable... */ + +__initfunc(void mac_mouse_setup(char *str, int *ints)) +{ + if (ints[0] < 1) { + printk( "mac_mouse_setup: no arguments!\n" ); + return; + } + else if (ints[0] > 2) { + printk( "mac_mouse_setup: too many arguments\n" ); + } + + if (ints[1] < MIN_THRESHOLD || ints[1] > MAX_THRESHOLD) + printk( "mac_mouse_setup: bad threshold value (ignored)\n" ); + else { + mac_mouse_x_threshold = ints[1]; + mac_mouse_y_threshold = ints[1]; + if (ints[0] > 1) { + if (ints[2] < MIN_THRESHOLD || ints[2] > MAX_THRESHOLD) + printk("mac_mouse_setup: bad threshold value (ignored)\n" ); + else + mac_mouse_y_threshold = ints[2]; + } + } + +} + +#ifdef MODULE +#include <asm/setup.h> + +int init_module(void) +{ + return mac_mouse_init(); +} + +void cleanup_module(void) +{ + misc_deregister(&mac_mouse); +} +#endif diff --git a/drivers/char/mem.c b/drivers/char/mem.c index a5c10988e..1ca4412af 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -38,6 +38,9 @@ int pcwatchdog_init(void); #ifdef CONFIG_VIDEO_DEV extern int videodev_init(void); #endif +#if defined(CONFIG_FB) +extern void fbmem_init( void ); +#endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) @@ -522,6 +525,9 @@ __initfunc(int chr_dev_init(void)) if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); +#if defined (CONFIG_FB) + fbmem_init(); +#endif tty_init(); #ifdef CONFIG_PRINTER lp_init(); diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index c17ae5e14..5f6ed4b01 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -53,7 +53,7 @@ unsigned char pckbd_sysrq_xlate[128] = "\r\000/"; /* 0x60 - 0x6f */ #endif -__initfunc(static int kbd_wait_for_input(void)) +static int kbd_wait_for_input(void) { int n; int status, data; @@ -86,7 +86,7 @@ __initfunc(static int kbd_wait_for_input(void)) return -1; /* timed-out if fell through to here... */ } -__initfunc(static void init_write_command(int data)) +static void init_write_command(int data) { int status; @@ -96,7 +96,7 @@ __initfunc(static void init_write_command(int data)) kbd_write_command(data); } -__initfunc(static void init_write_output(int data)) +static void init_write_output(int data) { int status; @@ -106,7 +106,7 @@ __initfunc(static void init_write_output(int data)) kbd_write_output(data); } -__initfunc(static char *initialize_kbd2(void)) +static char *initialize_kbd2(void) { /* Flush any pending input. */ @@ -183,7 +183,7 @@ __initfunc(static char *initialize_kbd2(void)) return NULL; } -__initfunc(void initialize_kbd(void)) +void initialize_kbd(void) { char *msg; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 064031dd8..67aeafc47 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -143,7 +143,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); */ static char *stl_drvtitle = "Stallion Multiport Serial Driver"; static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.4.3"; +static char *stl_drvversion = "5.4.5"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -278,6 +278,7 @@ static char *stl_brdnames[] = { #define EIO_INTRPEND 0x08 #define EIO_INTEDGE 0x00 #define EIO_INTLEVEL 0x08 +#define EIO_0WS 0x10 #define ECH_ID 0xa0 #define ECH_IDBITMASK 0xe0 @@ -2153,6 +2154,10 @@ static inline int stl_initeio(stlbrd_t *brdp) brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 2; + status = inb(brdp->iostatus); + if ((status & EIO_IDBITMASK) == EIO_MK3) + brdp->ioctrl++; + /* * Handle board specific stuff now. The real difference is PCI * or not PCI. @@ -2171,7 +2176,7 @@ static inline int stl_initeio(stlbrd_t *brdp) brdp->irq, brdp->brdnr); return(-EINVAL); } - outb((stl_vecmap[brdp->irq] | + outb((stl_vecmap[brdp->irq] | EIO_0WS | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); } @@ -2194,7 +2199,6 @@ static inline int stl_initeio(stlbrd_t *brdp) brdp->clk = CD1400_CLK; brdp->isr = stl_eiointr; - status = inb(brdp->iostatus); switch (status & EIO_IDBITMASK) { case EIO_8PORTM: brdp->clk = CD1400_CLK8M; @@ -2220,7 +2224,6 @@ static inline int stl_initeio(stlbrd_t *brdp) default: return(-ENODEV); } - brdp->ioctrl++; break; default: return(-ENODEV); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 5d100bba4..364c5602e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -66,6 +66,7 @@ #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/file.h> #include <linux/console.h> #include <linux/timer.h> #include <linux/ctype.h> @@ -1734,8 +1735,8 @@ void do_SAK( struct tty_struct *tty) ((session > 0) && (p->session == session))) send_sig(SIGKILL, p, 1); else if (p->files) { - for (i=0; i < NR_OPEN; i++) { - filp = p->files->fd[i]; + for (i=0; i < p->files->max_fds; i++) { + filp = fcheck_task(p, i); if (filp && (filp->f_op == &tty_fops) && (filp->private_data == tty)) { send_sig(SIGKILL, p, 1); diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c index e5ba92c9e..bd542b4fd 100644 --- a/drivers/misc/parport_pc.c +++ b/drivers/misc/parport_pc.c @@ -134,12 +134,12 @@ void parport_pc_change_mode(struct parport *p, int m) void parport_pc_write_fifo(struct parport *p, unsigned char v) { - /* FIXME */ + outb (v, p->base+CONFIGA); } unsigned char parport_pc_read_fifo(struct parport *p) { - return 0; /* FIXME */ + return inb (p->base+CONFIGA); } void parport_pc_disable_irq(struct parport *p) @@ -186,22 +186,34 @@ void parport_pc_restore_state(struct parport *p, struct parport_state *s) size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length) { - return 0; /* FIXME */ + size_t got = 0; + for (; got < length; got++) { + *((char*)buf)++ = inb (p->base+EPPREG); + if (inb (p->base+STATUS) & 0x01) + break; + } + return got; } size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length) { - return 0; /* FIXME */ + size_t written = 0; + for (; written < length; written++) { + outb (*((char*)buf)++, p->base+EPPREG); + if (inb (p->base+STATUS) & 0x01) + break; + } + return written; } int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle) { - return 0; /* FIXME */ + return -ENOSYS; /* FIXME */ } int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle) { - return 0; /* FIXME */ + return -ENOSYS; /* FIXME */ } int parport_pc_examine_irq(struct parport *p) diff --git a/drivers/misc/parport_share.c b/drivers/misc/parport_share.c index bc39b351c..bd0ae3c8b 100644 --- a/drivers/misc/parport_share.c +++ b/drivers/misc/parport_share.c @@ -1,4 +1,4 @@ -/* $Id: parport_share.c,v 1.10 1998/03/18 06:32:19 ralf Exp $ +/* $Id: parport_share.c,v 1.11 1998/03/26 10:38:32 ralf Exp $ * Parallel-port resource manager code. * * Authors: David Campbell <campbell@tirian.che.curtin.edu.au> @@ -46,7 +46,9 @@ struct parport *parport_enumerate(void) { #ifdef CONFIG_KMOD if (portlist == NULL) { +#if defined(CONFIG_PARPORT_PC_MODULE) || defined(CONFIG_PARPORT_AX_MODULE) || defined(CONFIG_PARPORT_ARC_MODULE) request_module("parport_lowlevel"); +#endif /* CONFIG_PARPORT_LOWLEVEL_MODULE */ #ifdef CONFIG_PNP_PARPORT_MODULE request_module("parport_probe"); #endif /* CONFIG_PNP_PARPORT_MODULE */ diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index e0f47e712..9a818d4d4 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -383,8 +383,8 @@ alloc586( struct device *dev ) { } /*****************************************************************/ -__initfunc(static int -elmc_getinfo( char* buf, int slot, void* d )) { +static int +elmc_getinfo( char* buf, int slot, void* d ) { int len = 0; struct device* dev = (struct device*) d; int i; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 7b8f9ec10..2694dbb6c 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -83,7 +83,7 @@ static int max_interrupt_work = 20; #if LINUX_VERSION_CODE < 0x10300 #define RUN_AT(x) (x) /* What to put in timer->expires. */ #define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#if defined(__alpha) +#if defined(__alpha__) #error "The Alpha architecture is only support with kernel version 2.0." #endif #define virt_to_bus(addr) ((unsigned long)addr) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 8d3f1f40b..47738877f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -739,7 +739,21 @@ L_OBJS += mace.o endif ifeq ($(CONFIG_VENDOR_SANGOMA),y) - M_OBJS += sdladrv.o + L_OBJS += sdladrv.o + L_OBJS += sdlamain.o + ifeq ($(CONFIG_WANPIPE_X25),y) + L_OBJS += sdla_x25.o + endif + ifeq ($(CONFIG_WANPIPE_FR),y) + L_OBJS += sdla_fr.o + endif + ifeq ($(CONFIG_WANPIPE_PPP),y) + L_OBJS += sdla_ppp.o + endif +endif + +ifeq ($(CONFIG_VENDOR_SANGOMA),m) + MX_OBJS += sdladrv.o M_OBJS += wanpipe.o WANPIPE_OBJS = sdlamain.o ifeq ($(CONFIG_WANPIPE_X25),y) diff --git a/drivers/net/hamradio/soundmodem/gentbl.c b/drivers/net/hamradio/soundmodem/gentbl.c index 4750c18ee..3f507d248 100644 --- a/drivers/net/hamradio/soundmodem/gentbl.c +++ b/drivers/net/hamradio/soundmodem/gentbl.c @@ -631,7 +631,7 @@ static void gentbl_banner(FILE *f) /* -------------------------------------------------------------------- */ -void main(int argc, char *argv[]) +int main(int argc, char *argv[]) { FILE *f; @@ -681,7 +681,7 @@ void main(int argc, char *argv[]) gentbl_costab(f, 6); gentbl_afsk2400(f, 7372800); fclose(f); - exit(0); + return(0); } diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c index e30bede48..9c1c65af8 100644 --- a/drivers/net/sdla_fr.c +++ b/drivers/net/sdla_fr.c @@ -1,4 +1,4 @@ -/**************************************************************************** +/***************************************************************************** * sdla_fr.c WANPIPE(tm) Multiprotocol WAN Link Driver. Frame relay module. * * Author(s): Gene Kozin @@ -83,85 +83,83 @@ * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ -#if !defined(__KERNEL__) || !defined(MODULE) -#error This code MUST be compiled as a kernel module! -#endif - #include <linux/config.h> /* OS configuration options */ #include <linux/kernel.h> /* printk(), and other useful stuff */ #include <linux/stddef.h> /* offsetof(), etc. */ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/init.h> /* __initfunc */ #include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ #include <asm/byteorder.h> /* htons(), etc. */ #include <asm/io.h> /* for inb(), outb(), etc. */ -#include <asm/uaccess.h> -#include <linux/time.h> /* for do_gettimeofday */ - +#include <linux/time.h> /* for do_gettimeofday */ #define _GNUC_ #include <linux/sdla_fr.h> /* frame relay firmware API definitions */ -/****** Defines & Macros ****************************************************/ +#include <asm/uaccess.h> -#define MAX_CMD_RETRY 10 /* max number of firmware retries */ +/****** Defines & Macros ****************************************************/ -#define FR_HEADER_LEN 8 /* max encapsulation header size */ -#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */ +#define MAX_CMD_RETRY 10 /* max number of firmware retries */ +#define FR_HEADER_LEN 8 /* max encapsulation header size */ +#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */ /* Q.922 frame types */ -#define Q922_UI 0x03 /* Unnumbered Info frame */ -#define Q922_XID 0xAF /* ??? */ + +#define Q922_UI 0x03 /* Unnumbered Info frame */ +#define Q922_XID 0xAF /* ??? */ /* DLCI configured or not */ + #define DLCI_NOT_CONFIGURED 0x00 #define DLCI_CONFIG_PENDING 0x01 #define DLCI_CONFIGURED 0x02 /* CIR enabled or not */ + #define CIR_ENABLED 0x00 #define CIR_DISABLED 0x01 /* Interrupt mode for DLCI = 0 */ + #define BUFFER_INTR_MODE 0x00 #define DLCI_LIST_INTR_MODE 0x01 /* Transmit Interrupt Status */ + #define DISABLED 0x00 #define WAITING_TO_BE_ENABLED 0x01 /* For handle_IPXWAN() */ + #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) - + /****** Data Structures *****************************************************/ /* This is an extention of the 'struct device' we create for each network * interface to keep the rest of channel-specific data. */ -typedef struct fr_channel -{ - char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ - unsigned dlci_configured ; /* check whether configured or not */ - unsigned cir_status; /* check whether CIR enabled or not */ - unsigned dlci; /* logical channel number */ - unsigned cir; /* committed information rate */ - unsigned bc; /* committed burst size */ - unsigned be; /* excess burst size */ - unsigned mc; /* multicast support on or off */ - unsigned tx_int_status; /* Transmit Interrupt Status */ +typedef struct fr_channel { + char name[WAN_IFNAME_SZ + 1]; /* interface name, ASCIIZ */ + unsigned dlci_configured; /* check whether configured or not */ + unsigned cir_status; /* check whether CIR enabled or not */ + unsigned dlci; /* logical channel number */ + unsigned cir; /* committed information rate */ + unsigned bc; /* committed burst size */ + unsigned be; /* excess burst size */ + unsigned mc; /* multicast support on or off */ + unsigned tx_int_status; /* Transmit Interrupt Status */ unsigned short pkt_length; /* Packet Length */ - unsigned long router_start_time;/* Router start time in seconds */ + unsigned long router_start_time; /* Router start time in seconds */ unsigned long tick_counter; /* counter for transmit time out */ char dev_pending_devtint; /* interface pending dev_tint() */ - char state; /* channel state */ - void* dlci_int_interface; /* pointer to the DLCI Interface */ - unsigned long IB_addr; /* physical address of Interface Byte */ + char state; /* channel state */ + void *dlci_int_interface; /* pointer to the DLCI Interface */ + unsigned long IB_addr; /* physical address of Interface Byte */ unsigned long state_tick; /* time of the last state change */ - sdla_t* card; /* -> owner */ - struct enet_statistics ifstats; /* interface statistics */ - + sdla_t *card; /* -> owner */ + struct enet_statistics ifstats; /* interface statistics */ unsigned long if_send_entry; unsigned long if_send_skb_null; unsigned long if_send_broadcast; @@ -177,14 +175,12 @@ typedef struct fr_channel unsigned long if_send_no_bfrs; unsigned long if_send_adptr_bfrs_full; unsigned long if_send_bfrs_passed_to_adptr; - unsigned long rx_intr_no_socket; unsigned long rx_intr_dev_not_started; unsigned long rx_intr_FPIPE_request; unsigned long rx_intr_DRVSTATS_request; unsigned long rx_intr_bfr_not_passed_to_stack; unsigned long rx_intr_bfr_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_kmalloc_err; unsigned long UDP_FPIPE_mgmt_direction_err; unsigned long UDP_FPIPE_mgmt_adptr_type_err; @@ -206,37 +202,32 @@ typedef struct fr_channel unsigned long router_up_time; } fr_channel_t; -typedef struct dlci_status -{ - unsigned short dlci PACKED; - unsigned char state PACKED; +typedef struct dlci_status { + unsigned short dlci PACKED; + unsigned char state PACKED; } dlci_status_t; -typedef struct dlci_IB_mapping -{ - unsigned short dlci PACKED; - unsigned long addr_value PACKED; +typedef struct dlci_IB_mapping { + unsigned short dlci PACKED; + unsigned long addr_value PACKED; } dlci_IB_mapping_t; /* This structure is used for DLCI list Tx interrupt mode. It is used to enable interrupt bit and set the packet length for transmission */ -typedef struct fr_dlci_interface -{ - unsigned char gen_interrupt PACKED; - unsigned short packet_length PACKED; - unsigned char reserved PACKED; -} fr_dlci_interface_t; + +typedef struct fr_dlci_interface { + unsigned char gen_interrupt PACKED; + unsigned short packet_length PACKED; + unsigned char reserved PACKED; +} fr_dlci_interface_t; static unsigned short num_frames; static unsigned long curr_trace_addr; static unsigned long start_trace_addr; static unsigned short available_buffer_space; -static char TracingEnabled; - -/* variable for keeping track of enabling/disabling FT1 monitor status */ +static char TracingEnabled; /* variable for keeping track of enabling/disabling FT1 monitor status */ static int rCount = 0; - extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); @@ -248,73 +239,63 @@ static int Intr_test_counter; /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ -static int update (wan_device_t* wandev); -static int new_if (wan_device_t* wandev, struct device* dev, - wanif_conf_t* conf); -static int del_if (wan_device_t* wandev, struct device* dev); - +static int update(wan_device_t * wandev); +static int new_if(wan_device_t * wandev, struct device *dev, + wanif_conf_t * conf); +static int del_if(wan_device_t * wandev, struct device *dev); /* WANPIPE-specific entry points */ -static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data); - +static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data); /* Network device interface */ -static int if_init (struct device* dev); -static int if_open (struct device* dev); -static int if_close (struct device* dev); -static int if_header (struct sk_buff* skb, struct device* dev, - unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (struct sk_buff* skb); -static int if_send (struct sk_buff* skb, struct device* dev); -static struct enet_statistics* if_stats (struct device* dev); - +static int if_init(struct device *dev); +static int if_open(struct device *dev); +static int if_close(struct device *dev); +static int if_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len); +static int if_rebuild_hdr(struct sk_buff *skb); +static int if_send(struct sk_buff *skb, struct device *dev); +static struct enet_statistics *if_stats(struct device *dev); /* Interrupt handlers */ -static void fr502_isr (sdla_t* card); -static void fr508_isr (sdla_t* card); -static void fr502_rx_intr (sdla_t* card); -static void fr508_rx_intr (sdla_t* card); -static void tx_intr (sdla_t* card); -static void spur_intr (sdla_t* card); - +static void fr502_isr(sdla_t * card); +static void fr508_isr(sdla_t * card); +static void fr502_rx_intr(sdla_t * card); +static void fr508_rx_intr(sdla_t * card); +static void tx_intr(sdla_t * card); +static void spur_intr(sdla_t * card); /* Background polling routines */ -static void wpf_poll (sdla_t* card); - +static void wpf_poll(sdla_t * card); /* Frame relay firmware interface functions */ -static int fr_read_version (sdla_t* card, char* str); -static int fr_configure (sdla_t* card, fr_conf_t *conf); -static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci); -static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu); -static int fr_comm_enable (sdla_t* card); -static int fr_comm_disable (sdla_t* card); -static int fr_get_err_stats (sdla_t* card); -static int fr_get_stats (sdla_t* card); -static int fr_add_dlci (sdla_t* card, int dlci, int num); -static int fr_activate_dlci (sdla_t* card, int dlci, int num); -static int fr_issue_isf (sdla_t* card, int isf); -static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf); -static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf); - +static int fr_read_version(sdla_t * card, char *str); +static int fr_configure(sdla_t * card, fr_conf_t * conf); +static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci); +static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu); +static int fr_comm_enable(sdla_t * card); +static int fr_comm_disable(sdla_t * card); +static int fr_get_err_stats(sdla_t * card); +static int fr_get_stats(sdla_t * card); +static int fr_add_dlci(sdla_t * card, int dlci, int num); +static int fr_activate_dlci(sdla_t * card, int dlci, int num); +static int fr_issue_isf(sdla_t * card, int isf); +static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf); +static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf); /* Firmware asynchronous event handlers */ -static int fr_event (sdla_t* card, int event, fr_mbox_t* mbox); -static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox); -static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox); - +static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox); +static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox); +static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox); /* Miscellaneous functions */ -static int update_chan_state (struct device* dev); -static void set_chan_state (struct device* dev, int state); -static struct device* find_channel (sdla_t* card, unsigned dlci); -static int is_tx_ready (sdla_t* card, fr_channel_t* chan); -static unsigned int dec_to_uint (unsigned char* str, int len); -static int reply_udp( unsigned char *data, unsigned int mbox_len ); - -static int intr_test( sdla_t* card ); -static void init_chan_statistics( fr_channel_t* chan ); -static void init_global_statistics( sdla_t* card ); -static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ); - +static int update_chan_state(struct device *dev); +static void set_chan_state(struct device *dev, int state); +static struct device *find_channel(sdla_t * card, unsigned dlci); +static int is_tx_ready(sdla_t * card, fr_channel_t * chan); +static unsigned int dec_to_uint(unsigned char *str, int len); +static int reply_udp(unsigned char *data, unsigned int mbox_len); +static int intr_test(sdla_t * card); +static void init_chan_statistics(fr_channel_t * chan); +static void init_global_statistics(sdla_t * card); +static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan); /* Udp management functions */ -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan); -static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan); -static int udp_pkt_type( struct sk_buff *skb, sdla_t* card ); - +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan); +static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan); +static int udp_pkt_type(struct sk_buff *skb, sdla_t * card); /* IPX functions */ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number); @@ -333,15 +314,16 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char * Return: 0 o.k. * < 0 failure. */ -__initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) +int wpf_init(sdla_t * card, wandev_conf_t * conf) { union { char str[80]; fr_conf_t cfg; } u; - + int i; /* Verify configuration ID */ - if (conf->config_id != WANCONFIG_FR) { + if (conf->config_id != WANCONFIG_FR) + { printk(KERN_INFO "%s: invalid configuration ID %u!\n", card->devname, conf->config_id); return -EINVAL; @@ -355,45 +337,41 @@ __initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS); card->isr = &fr502_isr; break; - case SFID_FR508: card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS); card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS); card->isr = &fr508_isr; break; - default: return -EINVAL; } - /* Read firmware version. Note that when adapter initializes, it * clears the mailbox, so it may appear that the first command was * executed successfully when in fact it was merely erased. To work * around this, we execute the first command twice. */ if (fr_read_version(card, NULL) || fr_read_version(card, u.str)) - return -EIO - ; + return -EIO; printk(KERN_INFO "%s: running frame relay firmware v%s\n", card->devname, u.str); - /* Adjust configuration */ conf->mtu = max(min(conf->mtu, 4080), FR_CHANNEL_MTU + FR_HEADER_LEN); conf->bps = min(conf->bps, 2048000); - /* Configure adapter firmware */ memset(&u.cfg, 0, sizeof(u.cfg)); u.cfg.mtu = conf->mtu; - u.cfg.t391 = 10; - u.cfg.t392 = 15; - u.cfg.n391 = 6; - u.cfg.n392 = 3; - u.cfg.n393 = 4; u.cfg.kbps = conf->bps / 1000; - u.cfg.cir_fwd = 16; - u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd; - u.cfg.options = 0x0081; /* direct Rx, no CIR check */ - + u.cfg.cir_fwd = u.cfg.cir_bwd = 16; + u.cfg.bc_fwd = u.cfg.bc_bwd = 16; + if (conf->station == WANOPT_CPE) + { + u.cfg.options = 0x0080; + printk(KERN_INFO "%s: Global CIR enabled by Default\n", card->devname); + } + else + { + u.cfg.options = 0x0081; + } switch (conf->u.fr.signalling) { case WANOPT_FR_Q933: @@ -403,54 +381,59 @@ __initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) u.cfg.options |= 0x0400; break; } - if (conf->station == WANOPT_CPE) { u.cfg.options |= 0x8000; /* auto config DLCI */ + card->u.f.dlci_num = 0; } else { u.cfg.station = 1; /* switch emulation mode */ - card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16; + /* For switch emulation we have to create a list of dlci(s) + * that will be sent to be global SET_DLCI_CONFIGURATION + * command in fr_configure() routine. + */ card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); + for (i = 0; i < card->u.f.dlci_num; i++) + { + card->u.f.node_dlci[i] = (unsigned short) + conf->u.fr.dlci[i] ? conf->u.fr.dlci[i] : 16; + } } - if (conf->clocking == WANOPT_INTERNAL) - u.cfg.port |= 0x0001 - ; + u.cfg.port |= 0x0001; if (conf->interface == WANOPT_RS232) - u.cfg.port |= 0x0002 - ; + u.cfg.port |= 0x0002; if (conf->u.fr.t391) u.cfg.t391 = min(conf->u.fr.t391, 30); + else + u.cfg.t391 = 5; if (conf->u.fr.t392) u.cfg.t392 = min(conf->u.fr.t392, 30); + else + u.cfg.t392 = 15; if (conf->u.fr.n391) u.cfg.n391 = min(conf->u.fr.n391, 255); + else + u.cfg.n391 = 2; if (conf->u.fr.n392) u.cfg.n392 = min(conf->u.fr.n392, 10); + else + u.cfg.n392 = 3; if (conf->u.fr.n393) u.cfg.n393 = min(conf->u.fr.n393, 10); - + else + u.cfg.n393 = 4; if (fr_configure(card, &u.cfg)) - return -EIO - ; - + return -EIO; if (card->hw.fwid == SFID_FR508) { fr_buf_info_t *buf_info = (void *) (card->hw.dpmbase + FR508_RXBC_OFFS); - - card->rxmb = - (void *) (buf_info->rse_next - - FR_MB_VECTOR + card->hw.dpmbase); - card->u.f.rxmb_base = - (void *) (buf_info->rse_base - - FR_MB_VECTOR + card->hw.dpmbase); - card->u.f.rxmb_last = - (void *) (buf_info->rse_base + - (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) - - FR_MB_VECTOR + card->hw.dpmbase); + card->rxmb = (void *) (buf_info->rse_next - FR_MB_VECTOR + card->hw.dpmbase); + card->u.f.rxmb_base = (void *) (buf_info->rse_base - FR_MB_VECTOR + card->hw.dpmbase); + card->u.f.rxmb_last = (void *) (buf_info->rse_base + (buf_info->rse_num - 1) * + sizeof(fr_buf_ctl_t) - FR_MB_VECTOR + card->hw.dpmbase); card->u.f.rx_base = buf_info->buf_base; card->u.f.rx_top = buf_info->buf_top; } @@ -465,8 +448,19 @@ __initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; + card->wandev.ttl = conf->ttl; card->wandev.udp_port = conf->udp_port; - TracingEnabled = '0'; + card->wandev.enable_tx_int = 0; + card->irq_dis_if_send_count = 0; + card->irq_dis_poll_count = 0; + card->wandev.enable_IPX = conf->enable_IPX; + if (conf->network_number) + card->wandev.network_number = conf->network_number; + else + card->wandev.network_number = 0xDEADBEEF; + /* Intialize global statistics for a card */ + init_global_statistics(card); + TracingEnabled = 0; return 0; } @@ -475,20 +469,17 @@ __initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) /*============================================================================ * Update device status & statistics. */ + static int update(wan_device_t * wandev) { sdla_t *card; - /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) - return -EFAULT - ; + return -EFAULT; if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV - ; + return -ENODEV; if (test_and_set_bit(0, (void *) &wandev->critical)) - return -EAGAIN - ; + return -EAGAIN; card = wandev->private; fr_get_err_stats(card); fr_get_stats(card); @@ -508,13 +499,14 @@ static int update(wan_device_t * wandev) * Return: 0 o.k. * < 0 failure (channel will not be created) */ + static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf) { sdla_t *card = wandev->private; fr_channel_t *chan; int err = 0; - - if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) + { printk(KERN_INFO "%s: invalid interface name!\n", card->devname); return -EINVAL; @@ -522,73 +514,67 @@ static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf /* allocate and initialize private data */ chan = kmalloc(sizeof(fr_channel_t), GFP_KERNEL); if (chan == NULL) - return -ENOMEM - ; + return -ENOMEM; memset(chan, 0, sizeof(fr_channel_t)); strcpy(chan->name, conf->name); chan->card = card; - /* verify media address */ - if (is_digit(conf->addr[0])) { + if (is_digit(conf->addr[0])) + { int dlci = dec_to_uint(conf->addr, 0); - - if (dlci && (dlci <= 4095)) { + if (dlci && (dlci <= 4095)) + { chan->dlci = dlci; - } else { - printk(KERN_ERR - "%s: invalid DLCI %u on interface %s!\n", + } + else + { + printk(KERN_ERR "%s: invalid DLCI %u on interface %s!\n", wandev->name, dlci, chan->name); err = -EINVAL; } - } else { - printk(KERN_ERR - "%s: invalid media address on interface %s!\n", + } + else + { + printk(KERN_ERR "%s: invalid media address on interface %s!\n", wandev->name, chan->name); err = -EINVAL; } - if (err) { + if (err) + { kfree(chan); return err; } - /* place cir,be,bc and other channel specific information into the * chan structure - */ + */ if (conf->cir) { - chan->cir = max( 1, min( conf->cir, 512 ) ); - chan->cir_status = CIR_ENABLED; - + chan->cir = max(1, min(conf->cir, 512)); + chan->cir_status = CIR_ENABLED; if (conf->bc) - chan->bc = max( 1, min( conf->bc, 512 ) ); + chan->bc = max(1, min(conf->bc, 512)); if (conf->be) - chan->be = max( 0, min( conf->be, 511) ); - + chan->be = max(0, min(conf->be, 511)); } else chan->cir_status = CIR_DISABLED; - chan->mc = conf->mc; - - chan->dlci_configured = DLCI_NOT_CONFIGURED; - - chan->tx_int_status = DISABLED; - - init_chan_statistics( chan ); - + chan->dlci_configured = DLCI_NOT_CONFIGURED; + chan->tx_int_status = DISABLED; + init_chan_statistics(chan); /* prepare network device data space for registration */ dev->name = chan->name; dev->init = &if_init; dev->priv = chan; return 0; } - /*============================================================================ * Delete logical channel. */ static int del_if(wan_device_t * wandev, struct device *dev) { - if (dev->priv) { + if (dev->priv) + { kfree(dev->priv); dev->priv = NULL; } @@ -606,24 +592,27 @@ static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data) int retry = MAX_CMD_RETRY; int err, len; fr_cmd_t cmd; - - if (copy_from_user((void *) &cmd, u_cmd, sizeof(cmd))) - return -EFAULT; + if(copy_from_user((void *) &cmd, u_cmd, sizeof(cmd))) + return -EFAULT; /* execute command */ - do { + do + { memcpy(&mbox->cmd, &cmd, sizeof(cmd)); if (cmd.length) - if (copy_from_user((void *) &mbox->data, u_data, cmd.length)) + { + if(copy_from_user((void *) &mbox->data, u_data, cmd.length)) return -EFAULT; + } if (sdla_exec(mbox)) err = mbox->cmd.result; else return -EIO; - } + } while (err && retry-- && fr_event(card, err, mbox)); /* return result */ - if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t))) + + if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t))) return -EFAULT; len = mbox->cmd.length; if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) @@ -632,7 +621,6 @@ static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data) } /****** Network Device Interface ********************************************/ - /*============================================================================ * Initialize Linux network interface. * @@ -645,7 +633,6 @@ static int if_init(struct device *dev) fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; wan_device_t *wandev = &card->wandev; - int i; /* Initialize device driver entry points */ dev->open = &if_open; @@ -654,27 +641,22 @@ static int if_init(struct device *dev) dev->rebuild_header = &if_rebuild_hdr; dev->hard_start_xmit = &if_send; dev->get_stats = &if_stats; - /* Initialize media-specific parameters */ - dev->family = AF_INET; /* address family */ - dev->type = ARPHRD_DLCI; /* ARP h/w type */ + dev->type = ARPHRD_DLCI; /* ARP h/w type */ dev->mtu = FR_CHANNEL_MTU; - dev->hard_header_len = FR_HEADER_LEN; /* media header length */ - dev->addr_len = 2; /* hardware address length */ + dev->hard_header_len = FR_HEADER_LEN; /* media header length */ + dev->addr_len = 2; /* hardware address length */ *(unsigned short *) dev->dev_addr = htons(chan->dlci); - /* Initialize hardware parameters (just for reference) */ dev->irq = wandev->irq; dev->dma = wandev->dma; dev->base_addr = wandev->ioport; dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; - /* Set transmit buffer queue length */ - dev->tx_queue_len = 30; - + dev->tx_queue_len = 10; + /* Initialize socket buffers */ dev_init_buffers(dev); - set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -695,124 +677,93 @@ static int if_open(struct device *dev) int err = 0; fr508_flags_t *flags = card->flags; struct timeval tv; - if (dev->start) return -EBUSY; /* only one open is allowed */ - if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; - if (!card->open_cnt) { Intr_test_counter = 0; card->intr_mode = INTR_TEST_MODE; - err = intr_test( card ); - - if ((err) || (Intr_test_counter !=(MAX_INTR_TEST_COUNTER +1))) { - printk(KERN_INFO - "%s: Interrupt Test Failed, Counter: %i\n", - card->devname, Intr_test_counter); + err = intr_test(card); + if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { + printk(KERN_INFO + "%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); err = -EIO; card->wandev.critical = 0; return err; } - printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n" - ,card->devname, Intr_test_counter); - + ,card->devname, Intr_test_counter); /* The following allocates and intializes a circular * link list of interfaces per card. */ - card->devs_struct = kmalloc(sizeof(load_sharing_t), GFP_KERNEL); if (card->devs_struct == NULL) - return -ENOMEM; + return -ENOMEM; card->dev_to_devtint_next = card->devs_struct; - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { (card->devs_struct)->dev_ptr = dev2; - if(dev2->slave == NULL) + if (dev2->slave == NULL) (card->devs_struct)->next = card->dev_to_devtint_next; else { (card->devs_struct)->next = kmalloc( - sizeof(load_sharing_t), GFP_KERNEL); + sizeof(load_sharing_t), GFP_KERNEL); if ((card->devs_struct)->next == NULL) - return -ENOMEM; + return -ENOMEM; card->devs_struct = (card->devs_struct)->next; - } + } } - card->devs_struct = card->dev_to_devtint_next; - - card->intr_mode = BUFFER_INTR_MODE; - + card->intr_mode = BUFFER_INTR_MODE; /* - check all the interfaces for the device to see if CIR has - been enabled for any DLCI(s). If so then use the DLCI list - Interrupt mode for fr_set_intr_mode(), otherwise use the default global interrupt mode - */ - + check all the interfaces for the device to see if CIR has + been enabled for any DLCI(s). If so then use the DLCI list + Interrupt mode for fr_set_intr_mode(), otherwise use the default global interrupt mode + */ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { - - if( ((fr_channel_t *)dev2->priv)->cir_status - == CIR_ENABLED) { + if (((fr_channel_t *) dev2->priv)->cir_status + == CIR_ENABLED) { card->intr_mode = DLCI_LIST_INTR_MODE; break; } } - /* - * If you enable comms and then set ints, you get a Tx int as you - * perform the SET_INT_TRIGGERS command. So, we only set int - * triggers and then adjust the interrupt mask (to disable Tx ints) before enabling comms. + If you enable comms and then set ints, you get a Tx int as you + perform the SET_INT_TRIGGERS command. So, we only set int + triggers and then adjust the interrupt mask (to disable Tx ints) before enabling comms. */ - if (card->intr_mode == BUFFER_INTR_MODE) - { - if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) - { + if (card->intr_mode == BUFFER_INTR_MODE) { + if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) { err = -EIO; card->wandev.critical = 0; return err; } - - printk( KERN_INFO - "%s: Global Buffering Tx Interrupt Mode\n" - , card->devname); - - } - else if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) - { + printk(KERN_INFO + "%s: Global Buffering Tx Interrupt Mode\n" + ,card->devname); + } else if (card->intr_mode == DLCI_LIST_INTR_MODE) { + if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) { err = -EIO; card->wandev.critical = 0; return err; } - - printk( KERN_INFO "%s: DLCI list Tx Interrupt Mode\n", - card->devname); - + printk(KERN_INFO + "%s: DLCI list Tx Interrupt Mode\n", + card->devname); } - flags->imask &= ~0x02; - - if (fr_comm_enable(card)) - { + if (fr_comm_enable(card)) { err = -EIO; card->wandev.critical = 0; return err; - } - + } wanpipe_set_state(card, WAN_CONNECTED); - - if (card->wandev.station == WANOPT_CPE) - { + if (card->wandev.station == WANOPT_CPE) { /* CPE: issue full status enquiry */ fr_issue_isf(card, FR_ISF_FSE); - } - else - { /* FR switch: activate DLCI(s) */ - + } else { /* FR switch: activate DLCI(s) */ /* For Switch emulation we have to ADD and ACTIVATE * the DLCI(s) that were configured with the SET_DLCI_ * CONFIGURATION command. Add and Activate will fail if @@ -820,12 +771,13 @@ static int if_open(struct device *dev) * * Also If_open is called once for each interface. But * it does not get in here for all the interface. So - * we have to pass the entire list of DLCI(s) to add + * we have to pass the entire list of DLCI(s) to add * activate routines. - */ - - fr_add_dlci(card, card->u.f.node_dlci[0], card->u.f.dlci_num); - fr_activate_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num); + */ + fr_add_dlci(card, + card->u.f.node_dlci[0], card->u.f.dlci_num); + fr_activate_dlci(card, + card->u.f.node_dlci[0], card->u.f.dlci_num); } } dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN); @@ -834,8 +786,7 @@ static int if_open(struct device *dev) dev->start = 1; wanpipe_open(card); update_chan_state(dev); - - do_gettimeofday( &tv ); + do_gettimeofday(&tv); chan->router_start_time = tv.tv_sec; card->wandev.critical = 0; return err; @@ -846,17 +797,17 @@ static int if_open(struct device *dev) * o if this is the last open, then disable communications and interrupts. * o reset flags. */ + static int if_close(struct device *dev) { fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; - ; dev->start = 0; wanpipe_close(card); - if (!card->open_cnt) { + if (!card->open_cnt) + { wanpipe_set_state(card, WAN_DISCONNECTED); fr_set_intr_mode(card, 0, 0); fr_comm_disable(card); @@ -875,14 +826,15 @@ static int if_close(struct device *dev) * * Return: media header length. */ + static int if_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { int hdr_len = 0; - skb->protocol = type; hdr_len = wan_encapsulate(skb, dev); - if (hdr_len < 0) { + if (hdr_len < 0) + { hdr_len = 0; skb->protocol = 0; } @@ -898,13 +850,14 @@ static int if_header(struct sk_buff *skb, struct device *dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ + static int if_rebuild_hdr(struct sk_buff *skb) { - fr_channel_t *chan = skb->dev->priv; + struct device *dev=skb->dev; + fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; - printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, skb->dev->name); + card->devname, dev->name); return 1; } @@ -926,80 +879,101 @@ static int if_rebuild_hdr(struct sk_buff *skb) * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ + static int if_send(struct sk_buff *skb, struct device *dev) { fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; int retry = 0, err; + unsigned char *sendpacket; struct device *dev2; - fr508_flags_t* adptr_flags = card->flags; - fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface; + unsigned long check_braddr, check_mcaddr; + fr508_flags_t *adptr_flags = card->flags; + int udp_type, send_data; + fr_dlci_interface_t *dlci_interface = chan->dlci_int_interface; unsigned long host_cpu_flags; - int send_data = 0; - ++chan->if_send_entry; + ++chan->if_send_entry; - if (dev->tbusy) - { + if (dev->tbusy) + { /* If our device stays busy for at least 5 seconds then we will - * kick start the device by making dev->tbusy = 0. We expect - * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. - */ - + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. + */ ++chan->if_send_busy; ++chan->ifstats.collisions; - - if ((jiffies - chan->tick_counter) < (5*HZ)) + if ((jiffies - chan->tick_counter) < (5 * HZ)) return 1; printk(KERN_INFO "%s: Transmit timed out\n", chan->name); - ++chan->if_send_busy_timeout; - /* unbusy all the interfaces on the card */ - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) dev2->tbusy = 0; - } - - if (test_and_set_bit(0, (void *) &card->wandev.critical)) { -#ifdef _DEBUG_ - printk(KERN_INFO "%s: if_send() hit critical section!\n", - card->devname); -#endif + } + sendpacket = skb->data; + udp_type = udp_pkt_type(skb, card); + if (udp_type == UDP_DRVSTATS_TYPE) + { + ++chan->if_send_DRVSTATS_request; + process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev, 0, + chan); dev_kfree_skb(skb); return 0; } - disable_irq(card->hw.irq); + else if (udp_type == UDP_FPIPE_TYPE) + ++chan->if_send_FPIPE_request; + /* retreive source address in two forms: broadcast & multicast */ + check_braddr = sendpacket[17]; + check_mcaddr = sendpacket[14]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[16]; + check_mcaddr |= sendpacket[15]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[15]; + check_mcaddr |= sendpacket[16]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[14]; + check_mcaddr |= sendpacket[17]; + /* if the Source Address is a Multicast address */ + if ((chan->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001) && + (check_mcaddr <= 0xFFFFFFFE)) + { + printk(KERN_INFO "%s: Multicast Src. Addr. silently discarded\n" + ,card->devname); + dev_kfree_skb(skb); + ++chan->ifstats.tx_dropped; + ++chan->if_send_multicast; + return 0; + } + disable_irq(card->hw.irq); ++card->irq_dis_if_send_count; - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { if (card->wandev.critical == CRITICAL_IN_ISR) - { + { ++chan->if_send_critical_ISR; - - if (card->intr_mode == DLCI_LIST_INTR_MODE) + if (card->intr_mode == DLCI_LIST_INTR_MODE) { /* The enable_tx_int flag is set here so that if - * the critical flag is set due to an interrupt + * the critical flag is set due to an interrupt * then we want to enable transmit interrupts * again. - */ - + */ card->wandev.enable_tx_int = 1; - /* Setting this flag to WAITING_TO_BE_ENABLED * specifies that interrupt bit has to be * enabled for that particular interface. * (delayed interrupt) - */ - + */ chan->tx_int_status = WAITING_TO_BE_ENABLED; - /* This is used for enabling dynamic calculation * of CIRs relative to the packet length. - */ - - chan->pkt_length = skb->len; + */ + chan->pkt_length = skb->len; dev->tbusy = 1; chan->tick_counter = jiffies; } @@ -1011,34 +985,36 @@ static int if_send(struct sk_buff *skb, struct device *dev) } save_flags(host_cpu_flags); cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) enable_irq(card->hw.irq); restore_flags(host_cpu_flags); return 1; } - ++chan->if_send_critical_non_ISR; ++chan->ifstats.tx_dropped; dev_kfree_skb(skb); save_flags(host_cpu_flags); cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); restore_flags(host_cpu_flags); return 0; } - card->wandev.critical = 0x21; - - if (card->wandev.state != WAN_CONNECTED) + if (udp_type == UDP_FPIPE_TYPE) + { + err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, + dev, 0, chan); + } + else if (card->wandev.state != WAN_CONNECTED) { ++chan->if_send_wan_disconnected; ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; } - else if (chan->state != WAN_CONNECTED) + else if (chan->state != WAN_CONNECTED) { ++chan->if_send_dlci_disconnected; update_chan_state(dev); @@ -1047,313 +1023,361 @@ static int if_send(struct sk_buff *skb, struct device *dev) } else if (!is_tx_ready(card, chan)) { - if (card->intr_mode == DLCI_LIST_INTR_MODE ) + if (card->intr_mode == DLCI_LIST_INTR_MODE) { dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = skb->len; - } - dev->tbusy = 1; + dlci_interface->packet_length = skb->len; + } + dev->tbusy = 1; chan->tick_counter = jiffies; - adptr_flags->imask |= 0x02; - - ++ chan->if_send_no_bfrs; + ++chan->if_send_no_bfrs; retry = 1; } - else + else { send_data = 1; /* If it's an IPX packet */ - if( sendpacket[1] == 0x00 && + if (sendpacket[1] == 0x00 && sendpacket[2] == 0x80 && sendpacket[6] == 0x81 && sendpacket[7] == 0x37) { - if( card->wandev.enable_IPX ) + if (card->wandev.enable_IPX) { - switch_net_numbers(sendpacket, - card->wandev.network_number, 0); - } - else + switch_net_numbers(sendpacket, + card->wandev.network_number, 0); + } + else { /* increment some statistic here! */ send_data = 0; } } - - if (send_data) + if (send_data) { - err = (card->hw.fwid == SFID_FR508) ? - fr508_send(card, chan->dlci, 0, skb->len, skb->data) : - fr502_send(card, chan->dlci, 0, skb->len, skb->data); - + err = (card->hw.fwid == SFID_FR508) ? + fr508_send(card, chan->dlci, 0, skb->len, skb->data) : + fr502_send(card, chan->dlci, 0, skb->len, skb->data); if (err) { if (card->intr_mode == DLCI_LIST_INTR_MODE) { dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = skb->len; - } - dev->tbusy = 1; + dlci_interface->packet_length = skb->len; + } + dev->tbusy = 1; chan->tick_counter = jiffies; - adptr_flags->imask |= 0x02; + adptr_flags->imask |= 0x02; retry = 1; - ++ chan->if_send_adptr_bfrs_full; - ++ chan->ifstats.tx_errors; - ++ card->wandev.stats.tx_errors; - } - else + ++chan->if_send_adptr_bfrs_full; + ++chan->ifstats.tx_errors; + ++card->wandev.stats.tx_errors; + } + else { - ++ chan->if_send_bfrs_passed_to_adptr; + ++chan->if_send_bfrs_passed_to_adptr; ++chan->ifstats.tx_packets; ++card->wandev.stats.tx_packets; } } } - if (!retry) dev_kfree_skb(skb); card->wandev.critical = 0; save_flags(host_cpu_flags); cli(); - if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); + if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); restore_flags(host_cpu_flags); return retry; } +/*============================================================================ + * Reply to UDP Management system. + * Return nothing. + */ + +static int reply_udp(unsigned char *data, unsigned int mbox_len) +{ + unsigned short len, udp_length, temp, i, ip_length; + unsigned long sum; + /* Set length of packet */ + len = mbox_len + 62; + /* fill in UDP reply */ + data[38] = 0x02; + /* fill in UDP length */ + udp_length = mbox_len + 40; + /* put it on an even boundary */ + if (udp_length & 0x0001) + { + udp_length += 1; + len += 1; + } + temp = (udp_length << 8) | (udp_length >> 8); + memcpy(&data[26], &temp, 2); + /* swap UDP ports */ + memcpy(&temp, &data[22], 2); + memcpy(&data[22], &data[24], 2); + memcpy(&data[24], &temp, 2); + /* add UDP pseudo header */ + temp = 0x1100; + memcpy(&data[udp_length + 22], &temp, 2); + temp = (udp_length << 8) | (udp_length >> 8); + memcpy(&data[udp_length + 24], &temp, 2); + /* calculate UDP checksum */ + data[28] = data[29] = 0; + sum = 0; + for (i = 0; i < udp_length + 12; i += 2) + { + memcpy(&temp, &data[14 + i], 2); + sum += (unsigned long) temp; + } + while (sum >> 16) + sum = (sum & 0xffffUL) + (sum >> 16); + + temp = (unsigned short) sum; + temp = ~temp; + if (temp == 0) + temp = 0xffff; + memcpy(&data[28], &temp, 2); + /* fill in IP length */ + ip_length = udp_length + 20; + temp = (ip_length << 8) | (ip_length >> 8); + memcpy(&data[4], &temp, 2); + /* swap IP addresses */ + memcpy(&temp, &data[14], 2); + memcpy(&data[14], &data[18], 2); + memcpy(&data[18], &temp, 2); + memcpy(&temp, &data[16], 2); + memcpy(&data[16], &data[20], 2); + memcpy(&data[20], &temp, 2); + /* fill in IP checksum */ + data[12] = data[13] = 0; + sum = 0; + for (i = 0; i < 20; i += 2) + { + memcpy(&temp, &data[2 + i], 2); + sum += (unsigned long) temp; + } + while (sum >> 16) + sum = (sum & 0xffffUL) + (sum >> 16); + temp = (unsigned short) sum; + temp = ~temp; + if (temp == 0) + temp = 0xffff; + memcpy(&data[12], &temp, 2); + return len; +} /* reply_udp */ /* - * If incoming is 0 (outgoing)- if the net numbers is ours make it 0 - * if incoming is 1 - if the net number is 0 make it ours - * + If incoming is 0 (outgoing)- if the net numbers is ours make it 0 + if incoming is 1 - if the net number is 0 make it ours */ - + static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) { unsigned long pnetwork_number; - - pnetwork_number = (unsigned long)((sendpacket[14] << 24) + - (sendpacket[15] << 16) + (sendpacket[16] << 8) + - sendpacket[17]); - + pnetwork_number = (unsigned long) ((sendpacket[14] << 24) + + (sendpacket[15] << 16) + (sendpacket[16] << 8) + + sendpacket[17]); if (!incoming) { - if( pnetwork_number == network_number) { - sendpacket[14] = sendpacket[15] = sendpacket[16] = - sendpacket[17] = 0x00; + /* If the destination network number is ours, make it 0 */ + if (pnetwork_number == network_number) { + sendpacket[14] = sendpacket[15] = sendpacket[16] = + sendpacket[17] = 0x00; } } else { - if( pnetwork_number == 0) { - sendpacket[14] = (unsigned char)(network_number >> 24); - sendpacket[15] = (unsigned char)((network_number & - 0x00FF0000) >> 16); - sendpacket[16] = (unsigned char)((network_number & - 0x0000FF00) >> 8); - sendpacket[17] = (unsigned char)(network_number & - 0x000000FF); + /* If the incoming network is 0, make it ours */ + if (pnetwork_number == 0) + { + sendpacket[14] = (unsigned char) (network_number >> 24); + sendpacket[15] = (unsigned char) ((network_number & + 0x00FF0000) >> 16); + sendpacket[16] = (unsigned char) ((network_number & + 0x0000FF00) >> 8); + sendpacket[17] = (unsigned char) (network_number & + 0x000000FF); } } - - - pnetwork_number = (unsigned long)((sendpacket[26] << 24) + - (sendpacket[27] << 16) + (sendpacket[28] << 8) + - sendpacket[29]); - - if( !incoming ) { - if( pnetwork_number == network_number) { - sendpacket[26] = sendpacket[27] = sendpacket[28] = - sendpacket[29] = 0x00; + pnetwork_number = (unsigned long) ((sendpacket[26] << 24) + + (sendpacket[27] << 16) + (sendpacket[28] << 8) + + sendpacket[29]); + if (!incoming) { + /* If the source network is ours, make it 0 */ + if (pnetwork_number == network_number) + { + sendpacket[26] = sendpacket[27] = sendpacket[28] = + sendpacket[29] = 0x00; } } else { - if( pnetwork_number == 0 ) { - sendpacket[26] = (unsigned char)(network_number >> 24); - sendpacket[27] = (unsigned char)((network_number & - 0x00FF0000) >> 16); - sendpacket[28] = (unsigned char)((network_number & - 0x0000FF00) >> 8); - sendpacket[29] = (unsigned char)(network_number & - 0x000000FF); + /* If the source network is 0, make it ours */ + if (pnetwork_number == 0) { + sendpacket[26] = (unsigned char) (network_number >> 24); + sendpacket[27] = (unsigned char) ((network_number & + 0x00FF0000) >> 16); + sendpacket[28] = (unsigned char) ((network_number & + 0x0000FF00) >> 8); + sendpacket[29] = (unsigned char) (network_number & + 0x000000FF); } } -} /* switch_net_numbers */ - +} /* switch_net_numbers */ /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ + static struct net_device_stats *if_stats(struct device *dev) { fr_channel_t *chan = dev->priv; - return &chan->ifstats; } /****** Interrupt Handlers **************************************************/ - /*============================================================================ * S502 frame relay interrupt service routine. */ + static void fr502_isr(sdla_t * card) { fr502_flags_t *flags = card->flags; - switch (flags->iflag) { case 0x01: /* receive interrupt */ fr502_rx_intr(card); break; - case 0x02: /* transmit interrupt */ flags->imask &= ~0x02; tx_intr(card); break; - default: spur_intr(card); } flags->iflag = 0; } - /*============================================================================ * S508 frame relay interrupt service routine. */ + static void fr508_isr(sdla_t * card) { fr508_flags_t *flags = card->flags; fr_buf_ctl_t *bctl; char *ptr = &flags->iflag; - struct device* dev = card->wandev.dev; - struct device* dev2; + struct device *dev = card->wandev.dev; + struct device *dev2; int i; unsigned long host_cpu_flags; - unsigned disable_tx_intr =1; - fr_channel_t* chan; - fr_dlci_interface_t* dlci_interface; - + unsigned disable_tx_intr = 1; + fr_channel_t *chan; + fr_dlci_interface_t *dlci_interface; /* This flag prevents nesting of interrupts. See sdla_isr() routine - * in sdlamain.c. + * in sdlamain.c. */ card->in_isr = 1; - ++card->statistics.isr_entry; - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { - printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag); + printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag); ++card->statistics.isr_already_critical; card->in_isr = 0; return; } - int_occur = 1; - /* For all interrupts set the critical flag to CRITICAL_RX_INTR. - * If the if_send routine is called with this flag set it will set - * the enable transmit flag to 1. (for a delayed interrupt) - */ + * If the if_send routine is called with this flag set it will set + * the enable transmit flag to 1. (for a delayed interrupt) + */ card->wandev.critical = CRITICAL_IN_ISR; - card->dlci_int_mode_unbusy = 0; - card->buff_int_mode_unbusy = 0; - - switch (flags->iflag) { - case 0x01: /* receive interrupt */ - ++card->statistics.isr_rx; - fr508_rx_intr(card); - break; - - case 0x02: /* transmit interrupt */ - ++card->statistics.isr_tx; - bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + - card->hw.dpmbase); + card->buff_int_mode_unbusy = 0; + switch (flags->iflag) + { + case 0x01: /* receive interrupt */ + ++card->statistics.isr_rx; + fr508_rx_intr(card); + break; + case 0x02: /* transmit interrupt */ + ++card->statistics.isr_tx; + bctl = (void *) (flags->tse_offs - FR_MB_VECTOR + + card->hw.dpmbase); bctl->flag = 0xA0; - - if (card->intr_mode == DLCI_LIST_INTR_MODE ) + if (card->intr_mode == DLCI_LIST_INTR_MODE) { /* Find the structure and make it unbusy */ - dev = find_channel( card, flags->dlci); + dev = find_channel(card, flags->dlci); dev->tbusy = 0; - /* This is used to perform devtint at the * end of the isr */ card->dlci_int_mode_unbusy = 1; - /* check to see if any other interfaces are * busy. If so then do not disable the tx * interrupts - */ - for (dev2 = card->wandev.dev; dev2; - dev2 = dev2->slave) + */ + for (dev2 = card->wandev.dev; dev2; + dev2 = dev2->slave) { - if ( dev2->tbusy == 1) + if (dev2->tbusy == 1) { disable_tx_intr = 0; break; - } + } } if (disable_tx_intr) flags->imask &= ~0x02; - - } + } else if (card->intr_mode == BUFFER_INTR_MODE) { - for (dev2 = card->wandev.dev; dev2; - dev2 = dev2->slave) + for (dev2 = card->wandev.dev; dev2; + dev2 = dev2->slave) { - if ( !dev2 || !dev2->start ) + if (!dev2 || !dev2->start) { - ++card->statistics. - tx_intr_dev_not_started; + ++card->statistics.tx_intr_dev_not_started; continue; } - if(dev2->tbusy) + if (dev2->tbusy) { card->buff_int_mode_unbusy = 1; - ((fr_channel_t*)dev2->priv)->dev_pending_devtint = 1; + ((fr_channel_t *) dev2->priv)->dev_pending_devtint = 1; dev2->tbusy = 0; - } else - ((fr_channel_t*)dev2->priv)->dev_pending_devtint = 0; + } + else + ((fr_channel_t *) dev2->priv)->dev_pending_devtint = 0; } - flags->imask &= ~0x02; + flags->imask &= ~0x02; } - break; - + break; case 0x08: - Intr_test_counter++; + Intr_test_counter++; ++card->statistics.isr_intr_test; - break; - + break; default: - ++card->statistics.isr_spurious; - spur_intr(card); - printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", - card->devname, flags->iflag); - - printk(KERN_INFO "%s: ID Bytes = ",card->devname); - for(i = 0; i < 8; i ++) + ++card->statistics.isr_spurious; + spur_intr(card); + printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", + card->devname, flags->iflag); + printk(KERN_INFO "%s: ID Bytes = ", card->devname); + for (i = 0; i < 8; i++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); - + printk(KERN_INFO "\n"); break; - } - + } card->wandev.critical = CRITICAL_INTR_HANDLED; if (card->wandev.enable_tx_int) { - if( card->intr_mode == DLCI_LIST_INTR_MODE ) - { - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + if (card->intr_mode == DLCI_LIST_INTR_MODE) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { - chan = dev2->priv; - if ( chan->tx_int_status == - WAITING_TO_BE_ENABLED ) + chan = dev2->priv; + if (chan->tx_int_status == WAITING_TO_BE_ENABLED) { - dlci_interface = - chan->dlci_int_interface; + dlci_interface = chan->dlci_int_interface; dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = - chan->pkt_length; + dlci_interface->packet_length = chan->pkt_length; chan->tx_int_status = DISABLED; } } @@ -1361,7 +1385,7 @@ static void fr508_isr(sdla_t * card) card->wandev.enable_tx_int = 0; flags->imask |= 0x02; ++card->statistics.isr_enable_tx_int; - } + } save_flags(host_cpu_flags); cli(); card->in_isr = 0; @@ -1369,35 +1393,30 @@ static void fr508_isr(sdla_t * card) flags->iflag = 0; card->wandev.critical = 0; restore_flags(host_cpu_flags); - - /* - * Device is now ready to send. The instant this is executed the If_Send - * routine is called. That is why this is put at the bottom of the ISR - * to prevent a endless loop condition caused by repeated Interrupts and - * enable_tx_int flag. + /* Device is now ready to send. The instant this is executed the If_Send + routine is called. That is why this is put at the bottom of the ISR + to prevent a endless loop condition caused by repeated Interrupts and + enable_tx_int flag. */ - - if(card->dlci_int_mode_unbusy) - dev_tint(dev); - - if(card->buff_int_mode_unbusy) + if (card->dlci_int_mode_unbusy) + mark_bh(NET_BH); + if (card->buff_int_mode_unbusy) { for (;;) { - if (((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1){ - - ((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint=0; - dev_tint((card->devs_struct)->dev_ptr); + if (((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1) + { + ((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint = 0; + mark_bh(NET_BH); } if ((card->devs_struct)->next == card->dev_to_devtint_next) break; card->devs_struct = (card->devs_struct)->next; } card->devs_struct = (card->dev_to_devtint_next)->next; - card->dev_to_devtint_next = card->devs_struct; + card->dev_to_devtint_next = card->devs_struct; } } - /*============================================================================ * Receive interrupt handler. */ @@ -1410,28 +1429,31 @@ static void fr502_rx_intr(sdla_t * card) fr_channel_t *chan; unsigned dlci, len; void *buf; - + unsigned char *sendpacket; + unsigned char buf2[3]; + int udp_type; sdla_mapmem(&card->hw, FR502_RX_VECTOR); - dlci = mbox->cmd.dlci; len = mbox->cmd.length; - /* Find network interface for this packet */ dev = find_channel(card, dlci); - if (dev == NULL) { + if (dev == NULL) + { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n", card->devname, dlci); sdla_mapmem(&card->hw, FR_MB_VECTOR); } chan = dev->priv; - if (!dev->start) { + if (!dev->start) + { ++chan->ifstats.rx_dropped; sdla_mapmem(&card->hw, FR_MB_VECTOR); } /* Allocate socket buffer */ skb = dev_alloc_skb(len); - if (skb == NULL) { + if (skb == NULL) + { printk(KERN_INFO "%s: no socket buffers available!\n", card->devname); ++chan->ifstats.rx_dropped; @@ -1441,26 +1463,50 @@ static void fr502_rx_intr(sdla_t * card) buf = skb_put(skb, len); memcpy(buf, mbox->data, len); sdla_mapmem(&card->hw, FR_MB_VECTOR); - - /* Decapsulate packet and pass it up the protocol stack */ - skb->dev = dev; - buf = skb_pull(skb, 1); /* remove hardware header */ - if (!wan_type_trans(skb, dev)) { - /* can't decapsulate packet */ - dev_kfree_skb(skb); - ++chan->ifstats.rx_errors; - ++card->wandev.stats.rx_errors; - } else { - netif_rx(skb); - ++chan->ifstats.rx_packets; - ++card->wandev.stats.rx_packets; + /* Check if it's a UDP management packet */ + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + udp_type = udp_pkt_type(skb, card); + if ((udp_type == UDP_FPIPE_TYPE) || (udp_type == UDP_DRVSTATS_TYPE)) + { + if (udp_type == UDP_DRVSTATS_TYPE) + { + ++chan->rx_intr_DRVSTATS_request; + process_udp_driver_call(UDP_PKT_FRM_NETWORK, card, skb, + dev, dlci, chan); + } + else + { + ++chan->rx_intr_FPIPE_request; + process_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK, card, skb, + dev, dlci, chan); + } + } + else + { + /* Decapsulate packet and pass it up the protocol stack */ + skb->dev = dev; + buf = skb_pull(skb, 1); /* remove hardware header */ + if (!wan_type_trans(skb, dev)) + { + /* can't decapsulate packet */ + dev_kfree_skb(skb); + ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; + } + else + { + netif_rx(skb); + ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; + } } sdla_mapmem(&card->hw, FR_MB_VECTOR); } - /*============================================================================ * Receive interrupt handler. */ + static void fr508_rx_intr(sdla_t * card) { fr_buf_ctl_t *frbuf = card->rxmb; @@ -1470,134 +1516,127 @@ static void fr508_rx_intr(sdla_t * card) unsigned dlci, len, offs; void *buf; unsigned rx_count = 0; - fr508_flags_t* flags = card->flags; + fr508_flags_t *flags = card->flags; char *ptr = &flags->iflag; - int i, err; - - if (frbuf->flag != 0x01) { - printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", - card->devname, (unsigned) frbuf); - printk(KERN_INFO "%s: ID Bytes = ",card->devname); - for(i = 0; i < 8; i ++) + int i, err, udp_type; + if (frbuf->flag != 0x01) + { + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned) frbuf, frbuf->flag); + printk(KERN_INFO "%s: ID Bytes = ", card->devname); + for (i = 0; i < 8; i++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); printk(KERN_INFO "\n"); - ++card->statistics.rx_intr_corrupt_rx_bfr; return; } - - do + + do { - len = frbuf->length; + len = frbuf->length; dlci = frbuf->dlci; offs = frbuf->offset; - /* Find network interface for this packet */ dev = find_channel(card, dlci); chan = dev->priv; - if (dev == NULL) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n" - , card->devname, dlci); - ++card->statistics.rx_intr_on_orphaned_DLCI; - } + ,card->devname, dlci); + ++card->statistics.rx_intr_on_orphaned_DLCI; + } else { - skb = dev_alloc_skb(len); - if (!dev->start || (skb == NULL)) - { + skb = dev_alloc_skb(len); + if (!dev->start || (skb == NULL)) + { ++chan->ifstats.rx_dropped; - if(dev->start) + if (dev->start) { - printk(KERN_INFO - "%s: no socket buffers available!\n", - card->devname); - ++chan->rx_intr_no_socket; - - } - else - ++ chan->rx_intr_dev_not_started; - } + printk(KERN_INFO + "%s: no socket buffers available!\n", + card->devname); + ++chan->rx_intr_no_socket; + } else + ++chan->rx_intr_dev_not_started; + } else { /* Copy data to the socket buffer */ if ((offs + len) > card->u.f.rx_top + 1) { - unsigned tmp = card->u.f.rx_top - - offs + 1; - + unsigned tmp = card->u.f.rx_top - offs + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, offs, buf, tmp); offs = card->u.f.rx_base; - len -= tmp; - } - + len -= tmp; + } buf = skb_put(skb, len); sdla_peek(&card->hw, offs, buf, len); -#ifdef CONFIG_SANGOMA_MANAGER - if (management_check(skb,card)) + udp_type = udp_pkt_type(skb, card); + if (udp_type == UDP_DRVSTATS_TYPE) { ++chan->rx_intr_DRVSTATS_request; - } - else -#endif - if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number)) + process_udp_driver_call( + UDP_PKT_FRM_NETWORK, card, skb, + dev, dlci, chan); + } + else if (udp_type == UDP_FPIPE_TYPE) + { + ++chan->rx_intr_FPIPE_request; + err = process_udp_mgmt_pkt( + UDP_PKT_FRM_NETWORK, card, + skb, dev, dlci, chan); + } + else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number)) { if (card->wandev.enable_IPX) - { fr508_send(card, dlci, 0, skb->len, skb->data); - } else { - /* increment some statistic! */ - } - } - else + } + else { - /* Decapsulate packet and pass it up the - protocol stack */ + /* Decapsulate packet and pass it up the + protocol stack */ skb->dev = dev; - /* remove hardware header */ - buf = skb_pull(skb, 1); - + buf = skb_pull(skb, 1); if (!wan_type_trans(skb, dev)) { /* can't decapsulate packet */ dev_kfree_skb(skb); - ++chan->rx_intr_bfr_not_passed_to_stack; - ++chan->ifstats.rx_errors; - ++card->wandev.stats.rx_errors; + ++chan-> + rx_intr_bfr_not_passed_to_stack; + ++chan-> + ifstats.rx_errors; + ++card-> + wandev.stats.rx_errors; } else { netif_rx(skb); - ++ chan->rx_intr_bfr_passed_to_stack; - ++ chan->ifstats.rx_packets; - ++ card->wandev.stats.rx_packets; + ++chan->rx_intr_bfr_passed_to_stack; + ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; } - } - } - } - - /* Release buffer element and calculate a pointer to the next + } + } + } + /* Release buffer element and calculate a pointer to the next one */ - frbuf->flag = 0; + frbuf->flag = 0; card->rxmb = ++frbuf; - - if ((void*)frbuf > card->u.f.rxmb_last) + if ((void *) frbuf > card->u.f.rxmb_last) card->rxmb = card->u.f.rxmb_base; - /* The loop put in is temporary, that is why the break is - * placed here. (?????) + * placed here. (?????) */ break; - - frbuf = card->rxmb; - - } while (frbuf->flag && ((++ rx_count) < 4)); + frbuf = card->rxmb; + } + while (frbuf->flag && ((++rx_count) < 4)); } - /*============================================================================ * Transmit interrupt handler. * o print a warning @@ -1607,25 +1646,23 @@ static void fr508_rx_intr(sdla_t * card) static void tx_intr(sdla_t * card) { struct device *dev = card->wandev.dev; - if (card->intr_mode == BUFFER_INTR_MODE) - { + { for (; dev; dev = dev->slave) { - if ( !dev || !dev->start ) - { - ++ card->statistics.tx_intr_dev_not_started; + if (!dev || !dev->start) + { + ++card->statistics.tx_intr_dev_not_started; continue; } - dev->tbusy = 0; - dev_tint(dev); + mark_bh(NET_BH); } } else { dev->tbusy = 0; - dev_tint(dev); + mark_bh(NET_BH); } } @@ -1635,70 +1672,75 @@ static void tx_intr(sdla_t * card) * o * If number of spurious interrupts exceeded some limit, then ??? */ + static void spur_intr(sdla_t * card) { printk(KERN_INFO "%s: spurious interrupt!\n", card->devname); } /* - Return 0 for non-IPXWAN packet - 1 for IPXWAN packet or IPX is not enabled! - -*/ + Return 0 for non-IPXWAN packet + 1 for IPXWAN packet or IPX is not enabled! + */ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number) { int i; - - if( sendpacket[1] == 0x00 && + if (sendpacket[1] == 0x00 && sendpacket[2] == 0x80 && sendpacket[6] == 0x81 && - sendpacket[7] == 0x37) { - - if(!enable_IPX) { + sendpacket[7] == 0x37) + { + /* It's an IPX packet */ + if (!enable_IPX) { + /* Return 1 so we don't pass it up the stack. */ return 1; } - } else { + } + else + { + /* It's not IPX so return and pass it up the stack. */ return 0; } - - if( sendpacket[24] == 0x90 && - sendpacket[25] == 0x04) + if (sendpacket[24] == 0x90 && + sendpacket[25] == 0x04) { - if( sendpacket[10] == 0x02 && - sendpacket[42] == 0x00) + /* It's IPXWAN */ + if (sendpacket[10] == 0x02 && + sendpacket[42] == 0x00) { - printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); - - for(i = 49; sendpacket[i] == 0x00; i += 5) + /* It's a timer request packet */ + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname); + /* Go through the routing options and answer no to every */ + /* option except Unnumbered RIP/SAP */ + for (i = 49; sendpacket[i] == 0x00; i += 5) { - if( sendpacket[i + 4] != 0x02) + /* 0x02 is the option for Unnumbered RIP/SAP */ + if (sendpacket[i + 4] != 0x02) { sendpacket[i + 1] = 0; } } - - if( sendpacket[i] == 0x04 ) - { + /* Skip over the extended Node ID option */ + if (sendpacket[i] == 0x04) i += 8; - } - - for(; sendpacket[i] == 0x80 ;) + /* We also want to turn off all header compression opt. */ + for (; sendpacket[i] == 0x80;) { sendpacket[i + 1] = 0; i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; } - + /* Set the packet type to timer response */ sendpacket[42] = 0x01; - - printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname); } - else if( sendpacket[42] == 0x02 ) + else if (sendpacket[42] == 0x02) { - printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); - + /* This is an information request packet */ + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname); + /* Set the packet type to information response */ sendpacket[42] = 0x03; - + /* Set the router name */ sendpacket[59] = 'F'; sendpacket[60] = 'P'; sendpacket[61] = 'I'; @@ -1706,39 +1748,35 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char sendpacket[63] = 'E'; sendpacket[64] = '-'; sendpacket[65] = CVHexToAscii(network_number >> 28); - sendpacket[66] = CVHexToAscii((network_number & 0x0F000000)>> 24); - sendpacket[67] = CVHexToAscii((network_number & 0x00F00000)>> 20); - sendpacket[68] = CVHexToAscii((network_number & 0x000F0000)>> 16); - sendpacket[69] = CVHexToAscii((network_number & 0x0000F000)>> 12); - sendpacket[70] = CVHexToAscii((network_number & 0x00000F00)>> 8); - sendpacket[71] = CVHexToAscii((network_number & 0x000000F0)>> 4); + sendpacket[66] = CVHexToAscii((network_number & 0x0F000000) >> 24); + sendpacket[67] = CVHexToAscii((network_number & 0x00F00000) >> 20); + sendpacket[68] = CVHexToAscii((network_number & 0x000F0000) >> 16); + sendpacket[69] = CVHexToAscii((network_number & 0x0000F000) >> 12); + sendpacket[70] = CVHexToAscii((network_number & 0x00000F00) >> 8); + sendpacket[71] = CVHexToAscii((network_number & 0x000000F0) >> 4); sendpacket[72] = CVHexToAscii(network_number & 0x0000000F); - for(i = 73; i < 107; i+= 1) - { + for (i = 73; i < 107; i += 1) sendpacket[i] = 0; - } - - printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname); } else { - printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname); return 0; } - - sendpacket[43] = (unsigned char)(network_number >> 24); - sendpacket[44] = (unsigned char)((network_number & 0x00FF0000) >> 16); - sendpacket[45] = (unsigned char)((network_number & 0x0000FF00) >> 8); - sendpacket[46] = (unsigned char)(network_number & 0x000000FF); - + /* Set the WNodeID to our network address */ + sendpacket[43] = (unsigned char) (network_number >> 24); + sendpacket[44] = (unsigned char) ((network_number & 0x00FF0000) >> 16); + sendpacket[45] = (unsigned char) ((network_number & 0x0000FF00) >> 8); + sendpacket[46] = (unsigned char) (network_number & 0x000000FF); return 1; } - - switch_net_numbers(sendpacket, network_number ,1); + /* If we get here, its an IPX-data packet so it'll get passed up the stack. */ + /* switch the network numbers */ + switch_net_numbers(sendpacket, network_number, 1); return 0; } - /****** Background Polling Routines ****************************************/ /*============================================================================ @@ -1755,136 +1793,127 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char static void wpf_poll(sdla_t * card) { - fr508_flags_t *flags; +/* struct device* dev = card->wandev.dev; */ + fr508_flags_t *flags = card->flags; unsigned long host_cpu_flags; - ++card->statistics.poll_entry; - - if (((jiffies - card->state_tick) < HZ) || - (card->intr_mode == INTR_TEST_MODE)) + if (((jiffies - card->state_tick) < HZ) || + (card->intr_mode == INTR_TEST_MODE)) return; - disable_irq(card->hw.irq); ++card->irq_dis_poll_count; - - if (test_and_set_bit(0, (void *)&card->wandev.critical)) + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { - ++ card->statistics.poll_already_critical; + ++card->statistics.poll_already_critical; save_flags(host_cpu_flags); cli(); if ((!card->irq_dis_if_send_count) && - (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); + (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); restore_flags(host_cpu_flags); - return; } - card->wandev.critical = 0x11; - - ++ card->statistics.poll_processed; - - if (flags->event) { - fr_mbox_t* mbox = card->mbox; + ++card->statistics.poll_processed; + /* This is to be changed later ??? */ + /* + if( dev && dev->tbusy && !(flags->imask & 0x02) ) { + printk(KERN_INFO "%s: Wpf_Poll: tbusy = 0x01, imask = 0x%02X\n", card->devname, flags->imask); + } + */ + if (flags->event) + { + fr_mbox_t *mbox = card->mbox; int err; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_STATUS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; if (err) fr_event(card, err, mbox); } - card->wandev.critical = 0; - save_flags(host_cpu_flags); cli(); - if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) + if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) enable_irq(card->hw.irq); restore_flags(host_cpu_flags); - card->state_tick = jiffies; } - /****** Frame Relay Firmware-Specific Functions *****************************/ /*============================================================================ * Read firmware code version. * o fill string str with firmware version info. */ + static int fr_read_version(sdla_t * card, char *str) { fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_CODE_VERSION; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); - if (!err && str) { + } while (err && retry-- && fr_event(card, err, mbox)); + + if (!err && str) + { int len = mbox->cmd.length; - memcpy(str, mbox->data, len); str[len] = '\0'; } return err; } - /*============================================================================ * Set global configuration. */ + static int fr_configure(sdla_t * card, fr_conf_t * conf) { fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int dlci_num = card->u.f.dlci_num; int err, i; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, conf, sizeof(fr_conf_t)); - - if (dlci_num) for (i = 0; i < dlci_num; ++i) - ((fr_conf_t*)mbox->data)->dlci[i] = - card->u.f.node_dlci[i]; - + if (dlci_num) + for (i = 0; i < dlci_num; ++i) + ((fr_conf_t *) mbox->data)->dlci[i] = + card->u.f.node_dlci[i]; mbox->cmd.command = FR_SET_CONFIG; mbox->cmd.length = sizeof(fr_conf_t) + dlci_num * sizeof(short); err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Set DLCI configuration. */ -static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci) +static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, conf, sizeof(fr_dlc_conf_t)); - mbox->cmd.dlci = (unsigned short) dlci; + mbox->cmd.dlci = (unsigned short) dlci; mbox->cmd.command = FR_SET_CONFIG; mbox->cmd.length = 0x0E; - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - - } while (err && retry--); - + } + while (err && retry--); return err; } - /*============================================================================ * Set interrupt mode. */ @@ -1893,19 +1922,20 @@ static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - if (card->hw.fwid == SFID_FR502) { + if (card->hw.fwid == SFID_FR502) + { fr502_intr_ctl_t *ictl = (void *) mbox->data; - memset(ictl, 0, sizeof(fr502_intr_ctl_t)); ictl->mode = mode; ictl->tx_len = mtu; mbox->cmd.length = sizeof(fr502_intr_ctl_t); - } else { + } + else + { fr508_intr_ctl_t *ictl = (void *) mbox->data; - memset(ictl, 0, sizeof(fr508_intr_ctl_t)); ictl->mode = mode; ictl->tx_len = mtu; @@ -1916,9 +1946,9 @@ static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Enable communications. */ @@ -1927,16 +1957,16 @@ static int fr_comm_enable(sdla_t * card) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_ENABLE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Disable communications. */ @@ -1945,16 +1975,16 @@ static int fr_comm_disable(sdla_t * card) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_DISABLE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Get communications error statistics. */ @@ -1963,26 +1993,26 @@ static int fr_get_err_stats(sdla_t * card) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_ERROR_STATS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) { - fr_comm_stat_t* stats = (void*)mbox->data; - - card->wandev.stats.rx_over_errors = stats->rx_overruns; - card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; - card->wandev.stats.rx_missed_errors = stats->rx_aborts; - card->wandev.stats.rx_length_errors = stats->rx_too_long; + + if (!err) + { + fr_comm_stat_t *stats = (void *) mbox->data; + card->wandev.stats.rx_over_errors = stats->rx_overruns; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_aborts; + card->wandev.stats.rx_length_errors = stats->rx_too_long; card->wandev.stats.tx_aborted_errors = stats->tx_aborts; } return err; } - /*============================================================================ * Get statistics. */ @@ -1991,74 +2021,68 @@ static int fr_get_stats(sdla_t * card) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_STATISTICS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) { + + if (!err) + { fr_link_stat_t *stats = (void *) mbox->data; - card->wandev.stats.rx_frame_errors = stats->rx_bad_format; - card->wandev.stats.rx_dropped = - stats->rx_dropped + stats->rx_dropped2 - ; + card->wandev.stats.rx_dropped = stats->rx_dropped + stats->rx_dropped2; } return err; } - /*============================================================================ - * Add DLCI(s) (Access Node only!). + * Add DLCI(s) (Access Node only!). + * This routine will perform the ADD_DLCIs command for the specified DLCI. */ - static int fr_add_dlci(sdla_t * card, int dlci, int num) { fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, i; - - do { + do + { unsigned short *dlci_list = (void *) mbox->data; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); for (i = 0; i < num; ++i) dlci_list[i] = card->u.f.node_dlci[i]; - mbox->cmd.length = num * sizeof(short); mbox->cmd.command = FR_ADD_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Activate DLCI(s) (Access Node only!). + * This routine will perform the ACTIVATE_DLCIs command with a list of DLCIs. */ static int fr_activate_dlci(sdla_t * card, int dlci, int num) { fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, i; - - do { + do + { unsigned short *dlci_list = (void *) mbox->data; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); for (i = 0; i < num; ++i) dlci_list[i] = card->u.f.node_dlci[i]; - mbox->cmd.length = num * sizeof(short); mbox->cmd.command = FR_ACTIVATE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Issue in-channel signalling frame. */ @@ -2067,8 +2091,8 @@ static int fr_issue_isf(sdla_t * card, int isf) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->data[0] = isf; mbox->cmd.length = 1; @@ -2076,9 +2100,9 @@ static int fr_issue_isf(sdla_t * card, int isf) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Send a frame (S502 version). */ @@ -2087,8 +2111,9 @@ static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, buf, len); mbox->cmd.dlci = dlci; @@ -2098,9 +2123,9 @@ static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); + return err; } - /*============================================================================ * Send a frame (S508 version). */ @@ -2109,8 +2134,9 @@ static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf) fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do { + + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.dlci = dlci; mbox->cmd.attr = attr; @@ -2119,8 +2145,9 @@ static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) { + + if (!err) + { fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data - FR_MB_VECTOR + card->hw.dpmbase); sdla_poke(&card->hw, frbuf->offset, buf, len); @@ -2138,53 +2165,47 @@ static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf) * * Return zero if previous command has to be cancelled. */ + static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox) { - fr508_flags_t* flags = card->flags; + fr508_flags_t *flags = card->flags; char *ptr = &flags->iflag; int i; - switch (event) { case FRRES_MODEM_FAILURE: return fr_modem_failure(card, mbox); - case FRRES_CHANNEL_DOWN: wanpipe_set_state(card, WAN_DISCONNECTED); return 1; - case FRRES_CHANNEL_UP: wanpipe_set_state(card, WAN_CONNECTED); return 1; - case FRRES_DLCI_CHANGE: return fr_dlci_change(card, mbox); - case FRRES_DLCI_MISMATCH: - printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname); + printk(KERN_INFO "%s: DLCI list mismatch!\n", + card->devname); return 1; - case CMD_TIMEOUT: printk(KERN_ERR "%s: command 0x%02X timed out!\n", card->devname, mbox->cmd.command); - printk(KERN_INFO "%s: ID Bytes = ",card->devname); - for(i = 0; i < 8; i ++) + printk(KERN_INFO "%s: ID Bytes = ", card->devname); + for (i = 0; i < 8; i++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); - break; - + printk(KERN_INFO "\n"); + break; case FRRES_DLCI_INACTIVE: - printk(KERN_ERR "%s: DLCI %u is inactive!\n", - card->devname, mbox->cmd.dlci); + printk(KERN_ERR "%s: DLCI %u is inactive!\n", + card->devname, mbox->cmd.dlci); break; - case FRRES_CIR_OVERFLOW: break; case FRRES_BUFFER_OVERFLOW: - break; + break; default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", - card->devname, mbox->cmd.command, event); + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" + ,card->devname, mbox->cmd.command, event); } return 0; } @@ -2206,7 +2227,6 @@ static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox) } return 1; } - /*============================================================================ * Handle DLCI status change. * @@ -2218,18 +2238,18 @@ static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox) int cnt = mbox->cmd.length / sizeof(dlci_status_t); fr_dlc_conf_t cfg; fr_channel_t *chan; - struct device* dev2; - - for (; cnt; --cnt, ++status) { + struct device *dev2; + for (; cnt; --cnt, ++status) + { unsigned short dlci = status->dlci; struct device *dev = find_channel(card, dlci); - - if (dev == NULL) + if (dev == NULL) { - printk(KERN_INFO "%s: CPE contains unconfigured DLCI= %d\n", - card->devname, dlci); + printk(KERN_INFO + "%s: CPE contains unconfigured DLCI= %d\n", + card->devname, dlci); } - else + else { if (status->state & 0x01) { @@ -2242,68 +2262,56 @@ static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox) else if (status->state & 0x02) { printk(KERN_INFO - "%s: DLCI %u becomes active!\n", - card->devname, dlci); + "%s: DLCI %u becomes active!\n", + card->devname, dlci); chan = dev->priv; /* This flag is used for configuring specific DLCI(s) when they become active. - */ + */ chan->dlci_configured = DLCI_CONFIG_PENDING; - if (dev && dev->start) set_chan_state(dev, WAN_CONNECTED); } } } - - for (dev2 =card->wandev.dev; dev2; dev2 = dev2->slave) - { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { chan = dev2->priv; - - if (chan->dlci_configured == DLCI_CONFIG_PENDING) + if (chan->dlci_configured == DLCI_CONFIG_PENDING) { memset(&cfg, 0, sizeof(cfg)); - - if ( chan->cir_status == CIR_DISABLED) + if (chan->cir_status == CIR_DISABLED) { - cfg.cir_fwd = cfg.cir_bwd = 16; + cfg.cir_fwd = cfg.cir_bwd = 16; cfg.bc_fwd = cfg.bc_bwd = 16; - cfg.conf_flags = 0x0001; - printk(KERN_INFO "%s: CIR Disabled for %s\n", - card->devname, chan->name); - } - else if (chan->cir_status == CIR_ENABLED) - { + cfg.conf_flags = 0x0001; + printk(KERN_INFO "%s: CIR Disabled for %s\n", + card->devname, chan->name); + } else if (chan->cir_status == CIR_ENABLED) { cfg.cir_fwd = cfg.cir_bwd = chan->cir; - cfg.bc_fwd = cfg.bc_bwd = chan->bc; - cfg.be_fwd = cfg.be_bwd = chan->be; + cfg.bc_fwd = cfg.bc_bwd = chan->bc; + cfg.be_fwd = cfg.be_bwd = chan->be; cfg.conf_flags = 0x0000; printk(KERN_INFO "%s: CIR Enabled for %s\n", - card->devname, chan->name); - + card->devname, chan->name); } - - if (fr_dlci_configure( card, &cfg , chan->dlci)) + if (fr_dlci_configure(card, &cfg, chan->dlci)) { - printk(KERN_INFO - "%s: DLCI Configure failed for %d\n", - card->devname, chan->dlci); - return 1; + printk(KERN_INFO + "%s: DLCI Configure failed for %d\n", + card->devname, chan->dlci); + return 1; } - chan->dlci_configured = DLCI_CONFIGURED; - - /* - * Read the interface byte mapping into the channel - * structure. + /* Read the interface byte mapping into the channel + structure. */ if (card->intr_mode == DLCI_LIST_INTR_MODE) - read_DLCI_IB_mapping( card, chan ); - } + read_DLCI_IB_mapping(card, chan); + } } return 1; } - /******* Miscellaneous ******************************************************/ /*============================================================================ @@ -2318,17 +2326,18 @@ static int update_chan_state(struct device *dev) int err; int dlci_found = 0; - do { + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_LIST_ACTIVE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) { + if (!err) + { unsigned short *list = (void *) mbox->data; int cnt = mbox->cmd.length / sizeof(short); - for (; cnt; --cnt, ++list) { if (*list == chan->dlci) @@ -2338,14 +2347,13 @@ static int update_chan_state(struct device *dev) break; } } - - if(!dlci_found) - printk(KERN_INFO "%s: DLCI %u is inactive\n", - card->devname, chan->dlci); + if (!dlci_found) + printk(KERN_INFO "%s: DLCI %u is inactive\n", + card->devname, chan->dlci); } + return err; } - /*============================================================================ * Set channel state. */ @@ -2354,27 +2362,27 @@ static void set_chan_state(struct device *dev, int state) fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; unsigned long flags; - + save_flags(flags); cli(); - + if (chan->state != state) { switch (state) { case WAN_CONNECTED: printk(KERN_INFO "%s: interface %s connected!\n" - , card->devname, dev->name); + ,card->devname, dev->name); break; case WAN_CONNECTING: - printk(KERN_INFO - "%s: interface %s connecting...\n", - card->devname, dev->name); + printk(KERN_INFO + "%s: interface %s connecting...\n", + card->devname, dev->name); break; case WAN_DISCONNECTED: - printk (KERN_INFO - "%s: interface %s disconnected!\n", - card->devname, dev->name); + printk(KERN_INFO + "%s: interface %s disconnected!\n", + card->devname, dev->name); break; } chan->state = state; @@ -2389,14 +2397,11 @@ static void set_chan_state(struct device *dev, int state) static struct device *find_channel(sdla_t * card, unsigned dlci) { struct device *dev; - for (dev = card->wandev.dev; dev; dev = dev->slave) if (((fr_channel_t *) dev->priv)->dlci == dlci) - break - ; + break; return dev; } - /*============================================================================ * Check to see if a frame can be sent. If no transmit buffers available, * enable transmit interrupts. @@ -2405,18 +2410,18 @@ static struct device *find_channel(sdla_t * card, unsigned dlci) * 0 - no buffers available */ -static int is_tx_ready(sdla_t * card) +static int is_tx_ready(sdla_t * card, fr_channel_t * chan) { if (card->hw.fwid == SFID_FR508) { unsigned char sb = inb(card->hw.port); - if (sb & 0x02) return 1; - } else { - fr502_flags_t* flags = card->flags; - - if (flags->tx_ready) + } + else + { + fr502_flags_t *flags = card->flags; + if (flags->tx_ready) return 1; flags->imask |= 0x02; } @@ -2430,7 +2435,6 @@ static int is_tx_ready(sdla_t * card) static unsigned int dec_to_uint(unsigned char *str, int len) { unsigned val; - if (!len) len = strlen(str); for (val = 0; len && is_digit(*str); ++str, --len) @@ -2439,109 +2443,677 @@ static unsigned int dec_to_uint(unsigned char *str, int len) } /*============================================================================== + * Process UDP call of type FPIPE8ND + */ + +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan) +{ + int c_retry = MAX_CMD_RETRY; + unsigned char *data; + unsigned char *buf; + unsigned char buf2[5]; + unsigned int loops, frames, len; + unsigned long data_ptr; + unsigned short real_len, buffer_length; + struct sk_buff *new_skb; + unsigned char *sendpacket; + fr_mbox_t *mbox = card->mbox; + int err; + struct timeval tv; + int udp_mgmt_req_valid = 1; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) + { + printk(KERN_INFO + "%s: Error allocating memory for UDP management cmnd 0x%02X", + card->devname, data[47]); + ++chan->UDP_FPIPE_mgmt_kmalloc_err; + return 1; + } + memcpy(data, sendpacket, skb->len); + switch (data[47]) + { + /* FPIPE_ENABLE_TRACE */ + case 0x41: + /* FPIPE_DISABLE_TRACE */ + case 0x42: + /* FPIPE_GET_TRACE_INFO */ + case 0x43: + /* SET FT1 MODE */ + case 0x81: + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) + { + ++chan->UDP_FPIPE_mgmt_direction_err; + udp_mgmt_req_valid = 0; + break; + } + /* FPIPE_FT1_READ_STATUS */ + case 0x44: + /* FT1 MONITOR STATUS */ + case 0x80: + if (card->hw.fwid != SFID_FR508) + { + ++chan->UDP_FPIPE_mgmt_adptr_type_err; + udp_mgmt_req_valid = 0; + } + break; + default: + break; + } + if (!udp_mgmt_req_valid) + { + /* set length to 0 */ + data[48] = data[49] = 0; + /* set return code */ + data[50] = (card->hw.fwid != SFID_FR508) ? 0x1F : 0xCD; + } + else + { + switch (data[47]) + { + /* FPIPE_ENABLE_TRACE */ + case 0x41: + if (!TracingEnabled) + { + do + { + /* SET_TRACE_CONFIGURATION */ + mbox->cmd.command = 0x60; + mbox->cmd.length = 1; + mbox->cmd.dlci = 0x00; + mbox->data[0] = 0x37; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + } + while (err && c_retry-- && fr_event(card, err, mbox)); + + if (err) + { + TracingEnabled = 0; + /* set the return code */ + data[50] = mbox->cmd.result; + mbox->cmd.length = 0; + break; + } + /* get num_frames */ + sdla_peek(&card->hw, 0x9000, &num_frames, 2); + sdla_peek(&card->hw, 0x9002, &curr_trace_addr,4); + start_trace_addr = curr_trace_addr; + /* MAX_SEND_BUFFER_SIZE - + * sizeof(UDP_MGMT_PACKET) - 41 */ + available_buffer_space = 1926; + /* set return code */ + data[50] = 0; + } + else + { + /* set return code to line trace already + enabled */ + data[50] = 1; + } + mbox->cmd.length = 0; + TracingEnabled = 1; + break; + /* FPIPE_DISABLE_TRACE */ + case 0x42: + if (TracingEnabled) + { + do + { + /* SET_TRACE_CONFIGURATION */ + mbox->cmd.command = 0x60; + mbox->cmd.length = 1; + mbox->cmd.dlci = 0x00; + mbox->data[0] = 0x36; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && c_retry-- && fr_event(card, err, mbox)); + } + /* set return code */ + data[50] = 0; + mbox->cmd.length = 0; + TracingEnabled = 0; + break; + /* FPIPE_GET_TRACE_INFO */ + case 0x43: + /* Line trace cannot be performed on the 502 */ + if (!TracingEnabled) + { + /* set return code */ + data[50] = 1; + mbox->cmd.length = 0; + break; + } + buffer_length = 0; + loops = (num_frames < 20) ? num_frames : 20; + for (frames = 0; frames < loops; frames += 1) + { + sdla_peek(&card->hw, curr_trace_addr, &buf2, 1); + /* no data on board so exit */ + if (buf2[0] == 0x00) + break; + /* 1+sizeof(FRAME_DATA) = 9 */ + if ((available_buffer_space - buffer_length) < 9) + { + /* indicate we have more frames on board + and exit */ + data[62] |= 0x02; + break; + } + /* get frame status */ + sdla_peek(&card->hw, curr_trace_addr + 0x05, &data[62 + buffer_length], 1); + /* get time stamp */ + sdla_peek(&card->hw, curr_trace_addr + 0x06, &data[66 + buffer_length], 2); + /* get frame length */ + sdla_peek(&card->hw, curr_trace_addr + 0x01, &data[64 + buffer_length], 2); + /* get pointer to real data */ + sdla_peek(&card->hw, curr_trace_addr + 0x0C,&data_ptr, 4); + /* see if we can fit the frame into the user buffer */ + memcpy(&real_len, &data[64 + buffer_length], 2); + if (data_ptr == 0 || real_len + 8 > available_buffer_space) + { + data[63 + buffer_length] = 0x00; + } + else + { + /* we can take it next time */ + if (available_buffer_space - buffer_length < real_len + 8) + { + data[62] |= 0x02; + break; + } + /* ok, get the frame */ + data[63 + buffer_length] = 0x01; + /* get the data */ + sdla_peek(&card->hw, data_ptr, &data[68 + buffer_length], real_len); + /* zero the opp flag to show we got the frame */ + buf2[0] = 0x00; + sdla_poke(&card->hw, curr_trace_addr, &buf2, 1); + /* now move onto the next frame */ + curr_trace_addr += 16; + /* check if we passed the last address */ + if (curr_trace_addr >= (start_trace_addr + num_frames * 16)) + curr_trace_addr = start_trace_addr; + /* update buffer length and make sure + its even */ + if (data[63 + buffer_length] == 0x01) + buffer_length += real_len - 1; + /* for the header */ + buffer_length += 8; + if (buffer_length & 0x0001) + buffer_length += 1; + } + } + /* ok now set the total number of frames passed in the + high 5 bits */ + data[62] = (frames << 3) | data[62]; + /* set the data length */ + mbox->cmd.length = buffer_length; + memcpy(&data[48], &buffer_length, 2); + data[50] = 0; + break; + /* FPIPE_FT1_READ_STATUS */ + case 0x44: + sdla_peek(&card->hw, 0xF020, &data[62], 2); + data[48] = 2; + data[49] = 0; + data[50] = 0; + mbox->cmd.length = 2; + break; + /* FPIPE_FLUSH_DRIVER_STATS */ + case 0x48: + init_chan_statistics(chan); + init_global_statistics(card); + mbox->cmd.length = 0; + break; + case 0x49: + do_gettimeofday(&tv); + chan->router_up_time = tv.tv_sec - chan->router_start_time; + *(unsigned long *) &data[62] = chan->router_up_time; + mbox->cmd.length = 4; + break; + /* FPIPE_KILL_BOARD */ + case 0x50: + break; + /* FT1 MONITOR STATUS */ + case 0x80: + if (data[62] == 1) + { + if (rCount++ != 0) + { + data[50] = 0; + mbox->cmd.length = 1; + break; + } + } + /* Disable FT1 MONITOR STATUS */ + if (data[62] == 0) + { + if (--rCount != 0) + { + data[50] = 0; + mbox->cmd.length = 1; + break; + } + } + default: + do + { + memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t)); + if (mbox->cmd.length) + memcpy(&mbox->data, &sendpacket[62],mbox->cmd.length); + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && c_retry-- && fr_event(card, err, mbox)); + + if (!err) + { + ++chan->UDP_FPIPE_mgmt_adptr_cmnd_OK; + memcpy(data, sendpacket, skb->len); + memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t)); + if (mbox->cmd.length) + { + memcpy(&data[62], &mbox->data,mbox->cmd.length); + } + } + else + { + ++chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout; + } + } + } + /* Fill UDP TTL */ + data[10] = card->wandev.ttl; + len = reply_udp(data, mbox->cmd.length); + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) + { + err = fr508_send(card, dlci, 0, len, data); + if (err) + ++chan->UDP_FPIPE_mgmt_adptr_send_passed; + else + ++chan->UDP_FPIPE_mgmt_adptr_send_failed; + dev_kfree_skb(skb); + } + else + { + /* Allocate socket buffer */ + if ((new_skb = dev_alloc_skb(len)) != NULL) + { + /* copy data into new_skb */ + buf = skb_put(new_skb, len); + memcpy(buf, data, len); + /* Decapsulate packet and pass it up the protocol + stack */ + new_skb->dev = dev; + buf = skb_pull(new_skb, 1); /* remove hardware header */ + if (!wan_type_trans(new_skb, dev)) + { + ++chan->UDP_FPIPE_mgmt_not_passed_to_stack; + /* can't decapsulate packet */ + dev_kfree_skb(new_skb); + } + else + { + ++chan->UDP_FPIPE_mgmt_passed_to_stack; + netif_rx(new_skb); + } + } + else + { + ++chan->UDP_FPIPE_mgmt_no_socket; + printk(KERN_INFO + "%s: UDP mgmt cmnd, no socket buffers available!\n", + card->devname); + } + } + kfree(data); + return 0; +} +/*============================================================================== * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_ * TEST_COUNTER times. */ -static int intr_test( sdla_t* card ) + +static int intr_test(sdla_t * card) { - fr_mbox_t* mb = card->mbox; - int err,i; - - /* - * The critical flag is unset here because we want to get into the - * ISR without the flag already set. The If_open sets the flag. + fr_mbox_t *mb = card->mbox; + int err, i; + /* The critical flag is unset here because we want to get into the + ISR without the flag already set. The If_open sets the flag. */ - card->wandev.critical = 0; - - err = fr_set_intr_mode( card, 0x08, card->wandev.mtu ); - + err = fr_set_intr_mode(card, 0x08, card->wandev.mtu); if (err == CMD_OK) { - for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) + for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) { - /* Run command READ_CODE_VERSION */ + /* Run command READ_CODE_VERSION */ memset(&mb->cmd, 0, sizeof(fr_cmd_t)); - mb->cmd.length = 0; + mb->cmd.length = 0; mb->cmd.command = 0x40; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) fr_event(card, err, mb); } + } + else + { + return err; } - else - return err; - - err = fr_set_intr_mode( card, 0, card->wandev.mtu ); - - if( err != CMD_OK ) + err = fr_set_intr_mode(card, 0, card->wandev.mtu); + if (err != CMD_OK) return err; - card->wandev.critical = 1; return 0; } +/*============================================================================ + * Process UDP call of type DRVSTATS. + */ +static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan) +{ + int c_retry = MAX_CMD_RETRY; + unsigned char *sendpacket; + unsigned char buf2[5]; + unsigned char *data; + unsigned char *buf; + unsigned int len; + fr_mbox_t *mbox = card->mbox; + struct sk_buff *new_skb; + int err; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) + { + printk(KERN_INFO + "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X" + ,card->devname, data[45]); + ++chan->UDP_DRVSTATS_mgmt_kmalloc_err; + return 1; + } + memcpy(data, sendpacket, skb->len); + switch (data[47]) + { + case 0x45: + *(unsigned long *) &data[62] = chan->if_send_entry; + *(unsigned long *) &data[66] = chan->if_send_skb_null; + *(unsigned long *) &data[70] = chan->if_send_broadcast; + *(unsigned long *) &data[74] = chan->if_send_multicast; + *(unsigned long *) &data[78] = chan->if_send_critical_ISR; + *(unsigned long *) &data[82] = chan->if_send_critical_non_ISR; + *(unsigned long *) &data[86] = chan->if_send_busy; + *(unsigned long *) &data[90] = chan->if_send_busy_timeout; + *(unsigned long *) &data[94] = chan->if_send_DRVSTATS_request; + *(unsigned long *) &data[98] = chan->if_send_FPIPE_request; + *(unsigned long *) &data[102] = chan->if_send_wan_disconnected; + *(unsigned long *) &data[106] = chan->if_send_dlci_disconnected; + *(unsigned long *) &data[110] = chan->if_send_no_bfrs; + *(unsigned long *) &data[114] = chan->if_send_adptr_bfrs_full; + *(unsigned long *) &data[118] = chan->if_send_bfrs_passed_to_adptr; + *(unsigned long *) &data[120] = card->irq_dis_if_send_count; + mbox->cmd.length = 62; + break; + case 0x46: + *(unsigned long *) &data[62] = card->statistics.isr_entry; + *(unsigned long *) &data[66] = card->statistics.isr_already_critical; + *(unsigned long *) &data[70] = card->statistics.isr_rx; + *(unsigned long *) &data[74] = card->statistics.isr_tx; + *(unsigned long *) &data[78] = card->statistics.isr_intr_test; + *(unsigned long *) &data[82] = card->statistics.isr_spurious; + *(unsigned long *) &data[86] = card->statistics.isr_enable_tx_int; + *(unsigned long *) &data[90] = card->statistics.tx_intr_dev_not_started; + *(unsigned long *) &data[94] = card->statistics.rx_intr_corrupt_rx_bfr; + *(unsigned long *) &data[98] = card->statistics.rx_intr_on_orphaned_DLCI; + *(unsigned long *) &data[102] = chan->rx_intr_no_socket; + *(unsigned long *) &data[106] = chan->rx_intr_dev_not_started; + *(unsigned long *) &data[110] = chan->rx_intr_DRVSTATS_request; + *(unsigned long *) &data[114] = chan->rx_intr_FPIPE_request; + *(unsigned long *) &data[118] = chan->rx_intr_bfr_not_passed_to_stack; + *(unsigned long *) &data[122] = chan->rx_intr_bfr_passed_to_stack; + mbox->cmd.length = 64; + break; + case 0x47: + *(unsigned long *) &data[62] = chan->UDP_FPIPE_mgmt_kmalloc_err; + *(unsigned long *) &data[66] = chan->UDP_FPIPE_mgmt_adptr_type_err; + *(unsigned long *) &data[70] = chan->UDP_FPIPE_mgmt_direction_err; + *(unsigned long *) &data[74] = chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout; + *(unsigned long *) &data[78] = chan->UDP_FPIPE_mgmt_adptr_cmnd_OK; + *(unsigned long *) &data[82] = chan->UDP_FPIPE_mgmt_adptr_send_passed; + *(unsigned long *) &data[86] = chan->UDP_FPIPE_mgmt_adptr_send_failed; + *(unsigned long *) &data[90] = chan->UDP_FPIPE_mgmt_no_socket; + *(unsigned long *) &data[94] = chan->UDP_FPIPE_mgmt_not_passed_to_stack; + *(unsigned long *) &data[98] = chan->UDP_FPIPE_mgmt_passed_to_stack; + *(unsigned long *) &data[102] = chan->UDP_DRVSTATS_mgmt_kmalloc_err; + *(unsigned long *) &data[106] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + *(unsigned long *) &data[110] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + *(unsigned long *) &data[114] = chan->UDP_DRVSTATS_mgmt_adptr_send_passed; + *(unsigned long *) &data[118] = chan->UDP_DRVSTATS_mgmt_adptr_send_failed; + *(unsigned long *) &data[122] = chan->UDP_DRVSTATS_mgmt_no_socket; + *(unsigned long *) &data[126] = chan->UDP_DRVSTATS_mgmt_not_passed_to_stack; + *(unsigned long *) &data[130] = chan->UDP_DRVSTATS_mgmt_passed_to_stack; + *(unsigned long *) &data[134] = card->statistics.poll_entry; + *(unsigned long *) &data[138] = card->statistics.poll_already_critical; + *(unsigned long *) &data[142] = card->statistics.poll_processed; + *(unsigned long *) &data[144] = card->irq_dis_poll_count; + mbox->cmd.length = 86; + break; + default: + do + { + memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t)); + if (mbox->cmd.length) + memcpy(&mbox->data, &sendpacket[62], mbox->cmd.length); + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && c_retry-- && fr_event(card, err, mbox)); + + if (!err) + { + ++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + memcpy(data, sendpacket, skb->len); + memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t)); + if (mbox->cmd.length) + memcpy(&data[62], &mbox->data, mbox->cmd.length); + } + else + { + ++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + } + } + /* Fill UDP TTL */ + data[10] = card->wandev.ttl; + len = reply_udp(data, mbox->cmd.length); + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) + { + err = fr508_send(card, dlci, 0, len, data); + if (err) + ++chan->UDP_DRVSTATS_mgmt_adptr_send_failed; + else + ++chan->UDP_DRVSTATS_mgmt_adptr_send_passed; + dev_kfree_skb(skb); + } + else + { + /* Allocate socket buffer */ + if ((new_skb = dev_alloc_skb(len)) != NULL) + { + /* copy data into new_skb */ + buf = skb_put(new_skb, len); + memcpy(buf, data, len); + /* Decapsulate packet and pass it up the + protocol stack */ + new_skb->dev = dev; + /* remove hardware header */ + buf = skb_pull(new_skb, 1); + if (!wan_type_trans(new_skb, dev)) + { + /* can't decapsulate packet */ + ++chan->UDP_DRVSTATS_mgmt_not_passed_to_stack; + dev_kfree_skb(new_skb); + } + else + { + ++chan->UDP_DRVSTATS_mgmt_passed_to_stack; + netif_rx(new_skb); + } + } + else + { + ++chan->UDP_DRVSTATS_mgmt_no_socket; + printk(KERN_INFO "%s: UDP mgmt cmnd, no socket buffers available!\n", card->devname); + } + } + kfree(data); + return 0; +} /*============================================================================== + * Determine what type of UDP call it is. DRVSTATS or FPIPE8ND ? + */ + +static int udp_pkt_type(struct sk_buff *skb, sdla_t * card) +{ + unsigned char *sendpacket; + unsigned char buf2[5]; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if (sendpacket[2] == 0x45 && /* IP packet */ + sendpacket[11] == 0x11 && /* UDP packet */ + sendpacket[24] == buf2[1] && /* UDP Port */ + sendpacket[25] == buf2[0] && + sendpacket[38] == 0x01) + { + if (sendpacket[30] == 0x46 && /* FPIPE8ND: Signature */ + sendpacket[31] == 0x50 && + sendpacket[32] == 0x49 && + sendpacket[33] == 0x50 && + sendpacket[34] == 0x45 && + sendpacket[35] == 0x38 && + sendpacket[36] == 0x4E && + sendpacket[37] == 0x44) + { + return UDP_FPIPE_TYPE; + } else if (sendpacket[30] == 0x44 && /* DRVSTATS: Signature */ + sendpacket[31] == 0x52 && + sendpacket[32] == 0x56 && + sendpacket[33] == 0x53 && + sendpacket[34] == 0x54 && + sendpacket[35] == 0x41 && + sendpacket[36] == 0x54 && + sendpacket[37] == 0x53) + { + return UDP_DRVSTATS_TYPE; + } + else + return UDP_INVALID_TYPE; + } + else + return UDP_INVALID_TYPE; +} +/*============================================================================== + * Initializes the Statistics values in the fr_channel structure. + */ + +void init_chan_statistics(fr_channel_t * chan) +{ + chan->if_send_entry = 0; + chan->if_send_skb_null = 0; + chan->if_send_broadcast = 0; + chan->if_send_multicast = 0; + chan->if_send_critical_ISR = 0; + chan->if_send_critical_non_ISR = 0; + chan->if_send_busy = 0; + chan->if_send_busy_timeout = 0; + chan->if_send_FPIPE_request = 0; + chan->if_send_DRVSTATS_request = 0; + chan->if_send_wan_disconnected = 0; + chan->if_send_dlci_disconnected = 0; + chan->if_send_no_bfrs = 0; + chan->if_send_adptr_bfrs_full = 0; + chan->if_send_bfrs_passed_to_adptr = 0; + chan->rx_intr_no_socket = 0; + chan->rx_intr_dev_not_started = 0; + chan->rx_intr_FPIPE_request = 0; + chan->rx_intr_DRVSTATS_request = 0; + chan->rx_intr_bfr_not_passed_to_stack = 0; + chan->rx_intr_bfr_passed_to_stack = 0; + chan->UDP_FPIPE_mgmt_kmalloc_err = 0; + chan->UDP_FPIPE_mgmt_direction_err = 0; + chan->UDP_FPIPE_mgmt_adptr_type_err = 0; + chan->UDP_FPIPE_mgmt_adptr_cmnd_OK = 0; + chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout = 0; + chan->UDP_FPIPE_mgmt_adptr_send_passed = 0; + chan->UDP_FPIPE_mgmt_adptr_send_failed = 0; + chan->UDP_FPIPE_mgmt_not_passed_to_stack = 0; + chan->UDP_FPIPE_mgmt_passed_to_stack = 0; + chan->UDP_FPIPE_mgmt_no_socket = 0; + chan->UDP_DRVSTATS_mgmt_kmalloc_err = 0; + chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; + chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; + chan->UDP_DRVSTATS_mgmt_adptr_send_passed = 0; + chan->UDP_DRVSTATS_mgmt_adptr_send_failed = 0; + chan->UDP_DRVSTATS_mgmt_not_passed_to_stack = 0; + chan->UDP_DRVSTATS_mgmt_passed_to_stack = 0; + chan->UDP_DRVSTATS_mgmt_no_socket = 0; +} +/*============================================================================== * Initializes the Statistics values in the Sdla_t structure. */ -void init_global_statistics( sdla_t* card ) +void init_global_statistics(sdla_t * card) { /* Intialize global statistics for a card */ - card->statistics.isr_entry = 0; - card->statistics.isr_already_critical = 0; - card->statistics.isr_rx = 0; - card->statistics.isr_tx = 0; - card->statistics.isr_intr_test = 0; - card->statistics.isr_spurious = 0; - card->statistics.isr_enable_tx_int = 0; - card->statistics.rx_intr_corrupt_rx_bfr = 0; + card->statistics.isr_entry = 0; + card->statistics.isr_already_critical = 0; + card->statistics.isr_rx = 0; + card->statistics.isr_tx = 0; + card->statistics.isr_intr_test = 0; + card->statistics.isr_spurious = 0; + card->statistics.isr_enable_tx_int = 0; + card->statistics.rx_intr_corrupt_rx_bfr = 0; card->statistics.rx_intr_on_orphaned_DLCI = 0; - card->statistics.tx_intr_dev_not_started = 0; - card->statistics.poll_entry = 0; - card->statistics.poll_already_critical = 0; - card->statistics.poll_processed = 0; + card->statistics.tx_intr_dev_not_started = 0; + card->statistics.poll_entry = 0; + card->statistics.poll_already_critical = 0; + card->statistics.poll_processed = 0; } -static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ) +static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan) { - fr_mbox_t* mbox = card->mbox; - int retry = MAX_CMD_RETRY; - dlci_IB_mapping_t* result; - int err, counter, found; - - do { + fr_mbox_t *mbox = card->mbox; + int retry = MAX_CMD_RETRY; + dlci_IB_mapping_t *result; + int err, counter, found; + do + { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_DLCI_IB_MAPPING; - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && retry-- && fr_event(card, err, mbox)); - } while (err && retry-- && fr_event(card, err, mbox)); - - if( mbox->cmd.result != 0) - printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", - chan->name); + if (mbox->cmd.result != 0) + printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", chan->name); counter = mbox->cmd.length / sizeof(dlci_IB_mapping_t); - result = (void *)mbox->data; - + result = (void *) mbox->data; found = 0; - for (; counter; --counter, ++result) { - if ( result->dlci == chan->dlci ) { - printk( KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n" - ,card->devname,result->dlci, result->addr_value - ,chan->name); + for (; counter; --counter, ++result) + { + if (result->dlci == chan->dlci) + { + printk(KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n" + ,card->devname, result->dlci, result->addr_value ,chan->name); chan->IB_addr = result->addr_value; - chan->dlci_int_interface = (void*)(card->hw.dpmbase + - ( chan->IB_addr & 0x00001FFF)); + chan->dlci_int_interface = (void *) (card->hw.dpmbase + + (chan->IB_addr & 0x00001FFF)); found = 1; - break; - } + break; + } } if (!found) - printk( KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", - card->devname, chan->dlci); + printk(KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", + card->devname, chan->dlci); } /****** End *****************************************************************/ diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c index 9d3e52fb0..32675d355 100644 --- a/drivers/net/sdla_ppp.c +++ b/drivers/net/sdla_ppp.c @@ -10,6 +10,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Mar 15, 1998 Alan Cox o 2.1.8x basic port. * Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs * while they have been disabled. * Nov 24, 1997 Jaspreet Singh o Fixed another RACE condition caused by @@ -55,11 +56,7 @@ * Jan 06, 1997 Gene Kozin Initial version. *****************************************************************************/ -#if !defined(__KERNEL__) || !defined(MODULE) -#error This code MUST be compiled as a kernel module! -#endif - -#include <linux/config.h> /* CONFIG_SANGOMA_MANAGER */ +#include <linux/config.h> /* OS configuration options */ #include <linux/kernel.h> /* printk(), and other useful stuff */ #include <linux/stddef.h> /* offsetof(), etc. */ #include <linux/errno.h> /* return codes */ @@ -68,10 +65,8 @@ #include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ -#include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ -#include <asm/uaccess.h> - +#include <asm/uaccess.h> /* copyto/from user */ #define _GNUC_ #include <linux/sdla_ppp.h> /* PPP firmware API definitions */ @@ -82,30 +77,27 @@ #else #define STATIC static #endif - -#define PPP_DFLT_MTU 1500 /* default MTU */ -#define PPP_MAX_MTU 4000 /* maximum MTU */ +#define PPP_DFLT_MTU 1500 /* default MTU */ +#define PPP_MAX_MTU 4000 /* maximum MTU */ #define PPP_HDR_LEN 1 - -#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ -#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ +#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ +#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ /* For handle_IPXWAN() */ #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) - -/******Data Structures*****************************************************/ +/******Data Structures*****************************************************/ /* This structure is placed in the private data area of the device structure. * The card structure used to occupy the private area but now the following * structure will incorporate the card structure along with PPP specific data */ - -typedef struct ppp_private_area + +typedef struct ppp_private_area { - sdla_t* card; + sdla_t *card; unsigned long router_start_time; /*router start time in sec */ - unsigned long tick_counter; /*used for 5 second counter*/ - unsigned mc; /*multicast support on or off*/ + unsigned long tick_counter; /*used for 5 second counter */ + unsigned mc; /*multicast support on or off */ /* PPP specific statistics */ unsigned long if_send_entry; unsigned long if_send_skb_null; @@ -122,13 +114,11 @@ typedef struct ppp_private_area unsigned long if_send_protocol_error; unsigned long if_send_tx_int_enabled; unsigned long if_send_bfr_passed_to_adptr; - unsigned long rx_intr_no_socket; unsigned long rx_intr_DRVSTATS_request; unsigned long rx_intr_PTPIPE_request; unsigned long rx_intr_bfr_not_passed_to_stack; unsigned long rx_intr_bfr_passed_to_stack; - unsigned long UDP_PTPIPE_mgmt_kmalloc_err; unsigned long UDP_PTPIPE_mgmt_adptr_type_err; unsigned long UDP_PTPIPE_mgmt_direction_err; @@ -136,8 +126,7 @@ typedef struct ppp_private_area unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_OK; unsigned long UDP_PTPIPE_mgmt_passed_to_adptr; unsigned long UDP_PTPIPE_mgmt_passed_to_stack; - unsigned long UDP_PTPIPE_mgmt_no_socket; - + unsigned long UDP_PTPIPE_mgmt_no_socket; unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; unsigned long UDP_DRVSTATS_mgmt_adptr_type_err; unsigned long UDP_DRVSTATS_mgmt_direction_err; @@ -145,84 +134,74 @@ typedef struct ppp_private_area unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; unsigned long UDP_DRVSTATS_mgmt_passed_to_adptr; unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_no_socket; - - unsigned long router_up_time; - -}ppp_private_area_t; + unsigned long UDP_DRVSTATS_mgmt_no_socket; + unsigned long router_up_time; +} ppp_private_area_t; /* variable for keeping track of enabling/disabling FT1 monitor status */ -static int rCount = 0; +static int rCount = 0; extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ -static int update (wan_device_t* wandev); -static int new_if (wan_device_t* wandev, struct device* dev, - wanif_conf_t* conf); -static int del_if (wan_device_t* wandev, struct device* dev); - +static int update(wan_device_t * wandev); +static int new_if(wan_device_t * wandev, struct device *dev, + wanif_conf_t * conf); +static int del_if(wan_device_t * wandev, struct device *dev); /* WANPIPE-specific entry points */ -static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data); - +static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data); /* Network device interface */ -static int if_init (struct device* dev); -static int if_open (struct device* dev); -static int if_close (struct device* dev); -static int if_header (struct sk_buff* skb, struct device* dev, - unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (struct sk_buff* skb); -static int if_send (struct sk_buff* skb, struct device* dev); -static struct enet_statistics* if_stats (struct device* dev); - - +static int if_init(struct device *dev); +static int if_open(struct device *dev); +static int if_close(struct device *dev); +static int if_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len); +static int if_rebuild_hdr(struct sk_buff *skb); +static int if_send(struct sk_buff *skb, struct device *dev); +static struct enet_statistics *if_stats(struct device *dev); /* PPP firmware interface functions */ -static int ppp_read_version (sdla_t* card, char* str); -static int ppp_configure (sdla_t* card, void* data); -static int ppp_set_intr_mode (sdla_t* card, unsigned mode); -static int ppp_comm_enable (sdla_t* card); -static int ppp_comm_disable (sdla_t* card); -static int ppp_get_err_stats (sdla_t* card); -static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto); -static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb); - +static int ppp_read_version(sdla_t * card, char *str); +static int ppp_configure(sdla_t * card, void *data); +static int ppp_set_intr_mode(sdla_t * card, unsigned mode); +static int ppp_comm_enable(sdla_t * card); +static int ppp_comm_disable(sdla_t * card); +static int ppp_get_err_stats(sdla_t * card); +static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto); +static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb); /* Interrupt handlers */ -STATIC void wpp_isr (sdla_t* card); -static void rx_intr (sdla_t* card); -static void tx_intr (sdla_t* card); - +STATIC void wpp_isr(sdla_t * card); +static void rx_intr(sdla_t * card); +static void tx_intr(sdla_t * card); /* Background polling routines */ -static void wpp_poll (sdla_t* card); -static void poll_active (sdla_t* card); -static void poll_connecting (sdla_t* card); -static void poll_disconnected (sdla_t* card); - +static void wpp_poll(sdla_t * card); +static void poll_active(sdla_t * card); +static void poll_connecting(sdla_t * card); +static void poll_disconnected(sdla_t * card); /* Miscellaneous functions */ -static int config502 (sdla_t* card); -static int config508 (sdla_t* card); -static void show_disc_cause (sdla_t* card, unsigned cause); -static unsigned char bps_to_speed_code (unsigned long bps); -static int reply_udp( unsigned char *data, unsigned int mbox_len ); -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); -static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); -static void init_ppp_tx_rx_buff( sdla_t* card ); -static int intr_test( sdla_t* card ); -static int udp_pkt_type( struct sk_buff *skb , sdla_t* card); -static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area); -static void init_global_statistics( sdla_t* card ); - -static int Intr_test_counter; +static int config502(sdla_t * card); +static int config508(sdla_t * card); +static void show_disc_cause(sdla_t * card, unsigned cause); +static unsigned char bps_to_speed_code(unsigned long bps); +static int reply_udp(unsigned char *data, unsigned int mbox_len); +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area); +static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area); +static void init_ppp_tx_rx_buff(sdla_t * card); +static int intr_test(sdla_t * card); +static int udp_pkt_type(struct sk_buff *skb, sdla_t * card); +static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area); +static void init_global_statistics(sdla_t * card); +static int Intr_test_counter; static char TracingEnabled; static unsigned long curr_trace_addr; static unsigned long start_trace_addr; static unsigned short available_buffer_space; - /* IPX functions */ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto); + /****** Public Functions ****************************************************/ /*============================================================================ @@ -237,40 +216,30 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char * Return: 0 o.k. * < 0 failure. */ -int wpp_init (sdla_t* card, wandev_conf_t* conf) +int wpp_init(sdla_t * card, wandev_conf_t * conf) { - union - { + union { char str[80]; } u; - /* Verify configuration ID */ if (conf->config_id != WANCONFIG_PPP) { - printk(KERN_INFO "%s: invalid configuration ID %u!\n", - card->devname, conf->config_id); + card->devname, conf->config_id); return -EINVAL; - } - /* Initialize protocol-specific fields */ switch (card->hw.fwid) { - - case SFID_PPP502: - card->mbox =(void*)(card->hw.dpmbase + PPP502_MB_OFFS); - card->flags=(void*)(card->hw.dpmbase + PPP502_FLG_OFFS); - break; - - case SFID_PPP508: - card->mbox =(void*)(card->hw.dpmbase + PPP508_MB_OFFS); - card->flags=(void*)(card->hw.dpmbase + PPP508_FLG_OFFS); - break; - - default: - return -EINVAL; - + case SFID_PPP502: + card->mbox = (void *) (card->hw.dpmbase + PPP502_MB_OFFS); + card->flags = (void *) (card->hw.dpmbase + PPP502_FLG_OFFS); + break; + case SFID_PPP508: + card->mbox = (void *) (card->hw.dpmbase + PPP508_MB_OFFS); + card->flags = (void *) (card->hw.dpmbase + PPP508_FLG_OFFS); + break; + default: + return -EINVAL; } - /* Read firmware version. Note that when adapter initializes, it * clears the mailbox, so it may appear that the first command was * executed successfully when in fact it was merely erased. To work @@ -278,38 +247,33 @@ int wpp_init (sdla_t* card, wandev_conf_t* conf) */ if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str)) return -EIO; - - printk(KERN_INFO "%s: running PPP firmware v%s\n",card->devname, u.str); + printk(KERN_INFO "%s: running PPP firmware v%s\n", card->devname, u.str); /* Adjust configuration and set defaults */ card->wandev.mtu = (conf->mtu) ? - min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU; - - card->wandev.bps = conf->bps; - card->wandev.interface = conf->interface; - card->wandev.clocking = conf->clocking; - card->wandev.station = conf->station; - card->isr = &wpp_isr; - card->poll = &wpp_poll; - card->exec = &wpp_exec; - card->wandev.update = &update; - card->wandev.new_if = &new_if; - card->wandev.del_if = &del_if; - card->wandev.state = WAN_DISCONNECTED; - card->wandev.udp_port = conf->udp_port; - card->wandev.ttl = conf->ttl; + min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU; + card->wandev.bps = conf->bps; + card->wandev.interface = conf->interface; + card->wandev.clocking = conf->clocking; + card->wandev.station = conf->station; + card->isr = &wpp_isr; + card->poll = &wpp_poll; + card->exec = &wpp_exec; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; + card->wandev.state = WAN_DISCONNECTED; + card->wandev.udp_port = conf->udp_port; + card->wandev.ttl = conf->ttl; card->irq_dis_if_send_count = 0; - card->irq_dis_poll_count = 0; - TracingEnabled = 0; - + card->irq_dis_poll_count = 0; + TracingEnabled = 0; card->wandev.enable_IPX = conf->enable_IPX; if (conf->network_number) card->wandev.network_number = conf->network_number; else card->wandev.network_number = 0xDEADBEEF; - /* initialize global statistics */ - init_global_statistics( card ); - + init_global_statistics(card); return 0; } @@ -321,19 +285,14 @@ int wpp_init (sdla_t* card, wandev_conf_t* conf) static int update(wan_device_t * wandev) { sdla_t *card; - /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) - return -EFAULT - ; + return -EFAULT; if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV - ; + return -ENODEV; if (test_and_set_bit(0, (void *) &wandev->critical)) - return -EAGAIN - ; + return -EAGAIN; card = wandev->private; - ppp_get_err_stats(card); wandev->critical = 0; return 0; @@ -351,41 +310,29 @@ static int update(wan_device_t * wandev) * Return: 0 o.k. * < 0 failure (channel will not be created) */ -static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) -{ - sdla_t* card = wandev->private; - ppp_private_area_t* ppp_priv_area; +static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf) +{ + sdla_t *card = wandev->private; + ppp_private_area_t *ppp_priv_area; if (wandev->ndev) return -EEXIST; - if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { - printk(KERN_INFO "%s: invalid interface name!\n", - card->devname); + card->devname); return -EINVAL; - } - /* allocate and initialize private data */ ppp_priv_area = kmalloc(sizeof(ppp_private_area_t), GFP_KERNEL); - - if( ppp_priv_area == NULL ) - return -ENOMEM; - + if (ppp_priv_area == NULL) + return -ENOMEM; memset(ppp_priv_area, 0, sizeof(ppp_private_area_t)); - - ppp_priv_area->card = card; - + ppp_priv_area->card = card; /* initialize data */ strcpy(card->u.p.if_name, conf->name); - /* initialize data in ppp_private_area structure */ - - init_ppp_priv_struct( ppp_priv_area ); - + init_ppp_priv_struct(ppp_priv_area); ppp_priv_area->mc = conf->mc; - /* prepare network device data space for registration */ dev->name = card->u.p.if_name; dev->init = &if_init; @@ -396,14 +343,13 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) /*============================================================================ * Delete logical channel. */ -static int del_if (wan_device_t* wandev, struct device* dev) + +static int del_if(wan_device_t * wandev, struct device *dev) { if (dev->priv) { - - kfree(dev->priv); - dev->priv = NULL; - } - + kfree(dev->priv); + dev->priv = NULL; + } return 0; } @@ -412,24 +358,23 @@ static int del_if (wan_device_t* wandev, struct device* dev) /*============================================================================ * Execute adapter interface command. */ + static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data) { ppp_mbox_t *mbox = card->mbox; int len; - - if (copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) + if(copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) return -EFAULT; len = mbox->cmd.length; if (len) { - if (copy_from_user((void *) &mbox->data, u_data, len)) + if(copy_from_user((void *) &mbox->data, u_data, len)) return -EFAULT; } /* execute command */ if (!sdla_exec(mbox)) return -EIO; - /* return result */ - if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t))) + if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t))) return -EFAULT; len = mbox->cmd.length; if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) @@ -446,41 +391,34 @@ static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data) * interface registration. Returning anything but zero will fail interface * registration. */ -static int if_init (struct device* dev) + +static int if_init(struct device *dev) { - ppp_private_area_t* ppp_priv_area = dev->priv; - sdla_t* card = ppp_priv_area->card; - wan_device_t* wandev = &card->wandev; - int i; + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; + wan_device_t *wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_header = &if_header; - dev->rebuild_header = &if_rebuild_hdr; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; - - + dev->open = &if_open; + dev->stop = &if_close; + dev->hard_header = &if_header; + dev->rebuild_header = &if_rebuild_hdr; + dev->hard_start_xmit = &if_send; + dev->get_stats = &if_stats; /* Initialize media-specific parameters */ - dev->family = AF_INET; /* address family */ - dev->type = ARPHRD_PPP; /* ARP h/w type */ - dev->mtu = wandev->mtu; - dev->hard_header_len = PPP_HDR_LEN; /* media header length */ - + dev->type = ARPHRD_PPP; /* ARP h/w type */ + dev->mtu = wandev->mtu; + dev->hard_header_len = PPP_HDR_LEN; /* media header length */ /* Initialize hardware parameters (just for reference) */ - dev->irq = wandev->irq; - dev->dma = wandev->dma; - dev->base_addr = wandev->ioport; - dev->mem_start = wandev->maddr; - dev->mem_end = wandev->maddr + wandev->msize - 1; - - /* Set transmit buffer queue length */ - dev->tx_queue_len = 100; - + dev->irq = wandev->irq; + dev->dma = wandev->dma; + dev->base_addr = wandev->ioport; + dev->mem_start = wandev->maddr; + dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 100; /* Initialize socket buffers */ dev_init_buffers(dev); - return 0; } @@ -491,72 +429,54 @@ static int if_init (struct device* dev) * * Return 0 if O.k. or errno. */ -static int if_open (struct device* dev) + +static int if_open(struct device *dev) { - ppp_private_area_t* ppp_priv_area = dev->priv; - sdla_t* card = ppp_priv_area->card; - ppp_flags_t* flags = card->flags; + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; + ppp_flags_t *flags = card->flags; struct timeval tv; int err = 0; - if (dev->start) - return -EBUSY; /* only one open is allowed */ - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + return -EBUSY; /* only one open is allowed */ + if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; - - if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)){ - + if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) { err = -EIO; card->wandev.critical = 0; return err; - } - Intr_test_counter = 0; - err = intr_test( card ); - - if( (err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { - - printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", - card->devname, Intr_test_counter); + err = intr_test(card); + if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { + printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); err = -EIO; card->wandev.critical = 0; return err; - } - - printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", - card->devname, Intr_test_counter); - + printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", + card->devname, Intr_test_counter); /* Initialize Rx/Tx buffer control fields */ - init_ppp_tx_rx_buff( card ); - + init_ppp_tx_rx_buff(card); if (ppp_set_intr_mode(card, 0x03)) { - err = -EIO; card->wandev.critical = 0; return err; - } - flags->imask &= ~0x02; - if (ppp_comm_enable(card)) { - err = -EIO; card->wandev.critical = 0; return err; - } - wanpipe_set_state(card, WAN_CONNECTING); wanpipe_open(card); dev->mtu = min(dev->mtu, card->wandev.mtu); dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; - do_gettimeofday( &tv ); + do_gettimeofday(&tv); ppp_priv_area->router_start_time = tv.tv_sec; card->wandev.critical = 0; return err; @@ -567,14 +487,13 @@ static int if_open (struct device* dev) * o if this is the last open, then disable communications and interrupts. * o reset flags. */ -static int if_close (struct device* dev) -{ - ppp_private_area_t* ppp_priv_area = dev->priv; - sdla_t* card = ppp_priv_area->card; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) +static int if_close(struct device *dev) +{ + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; + if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; - dev->start = 0; wanpipe_close(card); wanpipe_set_state(card, WAN_DISCONNECTED); @@ -593,21 +512,19 @@ static int if_close (struct device* dev) * * Return: media header length. */ -static int if_header (struct sk_buff* skb, struct device* dev, - unsigned short type, void* daddr, void* saddr, unsigned len) + +static int if_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) { - switch (type) + switch (type) { case ETH_P_IP: - case ETH_P_IPX: skb->protocol = type; break; - default: skb->protocol = 0; } - return PPP_HDR_LEN; } @@ -617,13 +534,14 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (struct sk_buff* skb) -{ - ppp_private_area_t* ppp_priv_area = skb->dev->priv; - sdla_t* card = ppp_priv_area->card; +static int if_rebuild_hdr(struct sk_buff *skb) +{ + struct device *dev=skb->dev; + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name); + card->devname, dev->name); return 1; } @@ -644,133 +562,133 @@ static int if_rebuild_hdr (struct sk_buff* skb) * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ -static int if_send (struct sk_buff* skb, struct device* dev) + +static int if_send(struct sk_buff *skb, struct device *dev) { - ppp_private_area_t* ppp_priv_area = dev->priv; - sdla_t* card = ppp_priv_area->card; + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; unsigned char *sendpacket; unsigned long check_braddr, check_mcaddr; unsigned long host_cpu_flags; - ppp_flags_t* flags = card->flags; + ppp_flags_t *flags = card->flags; int retry = 0; int err, udp_type; - ++ppp_priv_area->if_send_entry; - if (skb == NULL) { - /* If we get here, some higher layer thinks we've missed an * tx-done interrupt. */ printk(KERN_INFO "%s: interface %s got kicked!\n", - card->devname, dev->name); - + card->devname, dev->name); ++ppp_priv_area->if_send_skb_null; - - dev_tint(dev); + mark_bh(NET_BH); return 0; - } - if (dev->tbusy) { - /* If our device stays busy for at least 5 seconds then we will * kick start the device by making dev->tbusy = 0. We expect * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. */ - ++ppp_priv_area->if_send_busy; - ++card->wandev.stats.collisions; - - if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) { + ++card->wandev.stats.collisions; + if ((jiffies - ppp_priv_area->tick_counter) < (5 * HZ)) { return 1; } - - printk (KERN_INFO "%s: Transmit times out\n",card->devname); - + printk(KERN_INFO "%s: Transmit times out\n", card->devname); ++ppp_priv_area->if_send_busy_timeout; - - /* unbusy the card (because only one interface per card)*/ + /* unbusy the card (because only one interface per card) */ dev->tbusy = 0; - } + } sendpacket = skb->data; -#ifdef CONFIG_SANGOMA_MANAGER - if(sangoma_ppp_manager(skb,card)) - { + udp_type = udp_pkt_type(skb, card); + if (udp_type == UDP_DRVSTATS_TYPE) { + ++ppp_priv_area->if_send_DRVSTATS_request; + process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev, + ppp_priv_area); dev_kfree_skb(skb); return 0; + } else if (udp_type == UDP_PTPIPE_TYPE) + ++ppp_priv_area->if_send_PTPIPE_request; + /* retreive source address in two forms: broadcast & multicast */ + check_braddr = sendpacket[15]; + check_mcaddr = sendpacket[12]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[14]; + check_mcaddr |= sendpacket[13]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[13]; + check_mcaddr |= sendpacket[14]; + check_braddr = check_braddr << 8; + check_mcaddr = check_mcaddr << 8; + check_braddr |= sendpacket[12]; + check_mcaddr |= sendpacket[15]; + /* if the Source Address is a Multicast address */ + if ((ppp_priv_area->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001) + && (check_mcaddr <= 0xFFFFFFFE)) { + printk(KERN_INFO "%s: Mutlicast Src. Addr. silently discarded\n" + ,card->devname); + dev_kfree_skb(skb); + ++ppp_priv_area->if_send_multicast; + ++card->wandev.stats.tx_dropped; + return 0; } -#endif disable_irq(card->hw.irq); ++card->irq_dis_if_send_count; - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) - { - if (card->wandev.critical == CRITICAL_IN_ISR) - { + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { + if (card->wandev.critical == CRITICAL_IN_ISR) { /* If the critical flag is set due to an Interrupt * then set enable transmit interrupt flag to enable * transmit interrupt. (delay interrupt) */ card->wandev.enable_tx_int = 1; - dev->tbusy = 1; - + dev->tbusy = 1; /* set the counter to see if we get the interrupt in * 5 seconds. */ ppp_priv_area->tick_counter = jiffies; - ++ppp_priv_area->if_send_critical_ISR; - + ++ppp_priv_area->if_send_critical_ISR; save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); return 1; - } - - dev_kfree_skb(skb); + dev_kfree_skb(skb); ++ppp_priv_area->if_send_critical_non_ISR; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); return 0; } - - - if (card->wandev.state != WAN_CONNECTED) { - + if (udp_type == UDP_PTPIPE_TYPE) { + err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, + dev, ppp_priv_area); + } else if (card->wandev.state != WAN_CONNECTED) { ++ppp_priv_area->if_send_wan_disconnected; - ++card->wandev.stats.tx_dropped; - - } else if (!skb->protocol) { + ++card->wandev.stats.tx_dropped; + } else if (!skb->protocol) { ++ppp_priv_area->if_send_protocol_error; - ++card->wandev.stats.tx_errors; - + ++card->wandev.stats.tx_errors; } else { - - /*If it's IPX change the network numbers to 0 if they're ours.*/ - if( skb->protocol == ETH_P_IPX ) { - if(card->wandev.enable_IPX) { - switch_net_numbers( skb->data, - card->wandev.network_number, 0); + /*If it's IPX change the network numbers to 0 if they're ours. */ + if (skb->protocol == ETH_P_IPX) { + if (card->wandev.enable_IPX) { + switch_net_numbers(skb->data, + card->wandev.network_number, 0); } else { ++card->wandev.stats.tx_dropped; goto tx_done; } } - if (ppp_send(card, skb->data, skb->len, skb->protocol)) { - retry = 1; dev->tbusy = 1; ++ppp_priv_area->if_send_adptr_bfrs_full; @@ -778,105 +696,160 @@ static int if_send (struct sk_buff* skb, struct device* dev) ppp_priv_area->tick_counter = jiffies; ++card->wandev.stats.tx_errors; flags->imask |= 0x02; /* unmask Tx interrupts */ - } else { ++ppp_priv_area->if_send_bfr_passed_to_adptr; ++card->wandev.stats.tx_packets; } - } - -tx_done: - if (!retry){ + } +tx_done: + if (!retry) { dev_kfree_skb(skb); } - card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - + cli(); + if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); return retry; } +/*============================================================================ + * Reply to UDP Management system. + * Return length of reply. + */ + +static int reply_udp(unsigned char *data, unsigned int mbox_len) +{ + unsigned short len, udp_length, temp, i, ip_length; + unsigned long sum; + /* Set length of packet */ + len = mbox_len + 60; + /* fill in UDP reply */ + data[36] = 0x02; + /* fill in UDP length */ + udp_length = mbox_len + 40; + /* put it on an even boundary */ + if (udp_length & 0x0001) { + udp_length += 1; + len += 1; + } + temp = (udp_length << 8) | (udp_length >> 8); + memcpy(&data[24], &temp, 2); + /* swap UDP ports */ + memcpy(&temp, &data[20], 2); + memcpy(&data[20], &data[22], 2); + memcpy(&data[22], &temp, 2); + /* add UDP pseudo header */ + temp = 0x1100; + memcpy(&data[udp_length + 20], &temp, 2); + temp = (udp_length << 8) | (udp_length >> 8); + memcpy(&data[udp_length + 22], &temp, 2); + /* calculate UDP checksum */ + data[26] = data[27] = 0; + sum = 0; + for (i = 0; i < udp_length + 12; i += 2) { + memcpy(&temp, &data[12 + i], 2); + sum += (unsigned long) temp; + } + while (sum >> 16) { + sum = (sum & 0xffffUL) + (sum >> 16); + } + temp = (unsigned short) sum; + temp = ~temp; + if (temp == 0) + temp = 0xffff; + memcpy(&data[26], &temp, 2); + /* fill in IP length */ + ip_length = udp_length + 20; + temp = (ip_length << 8) | (ip_length >> 8); + memcpy(&data[2], &temp, 2); + /* swap IP addresses */ + memcpy(&temp, &data[12], 2); + memcpy(&data[12], &data[16], 2); + memcpy(&data[16], &temp, 2); + memcpy(&temp, &data[14], 2); + memcpy(&data[14], &data[18], 2); + memcpy(&data[18], &temp, 2); + /* fill in IP checksum */ + data[10] = data[11] = 0; + sum = 0; + for (i = 0; i < 20; i += 2) { + memcpy(&temp, &data[i], 2); + sum += (unsigned long) temp; + } + while (sum >> 16) { + sum = (sum & 0xffffUL) + (sum >> 16); + } + temp = (unsigned short) sum; + temp = ~temp; + if (temp == 0) + temp = 0xffff; + memcpy(&data[10], &temp, 2); + return len; +} /* reply_udp */ + /* If incoming is 0 (outgoing)- if the net numbers is ours make it 0 if incoming is 1 - if the net number is 0 make it ours + */ -*/ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) { unsigned long pnetwork_number; - - pnetwork_number = (unsigned long)((sendpacket[6] << 24) + - (sendpacket[7] << 16) + (sendpacket[8] << 8) + - sendpacket[9]); - - if (!incoming) - { + pnetwork_number = (unsigned long) ((sendpacket[6] << 24) + + (sendpacket[7] << 16) + (sendpacket[8] << 8) + + sendpacket[9]); + if (!incoming) { /* If the destination network number is ours, make it 0 */ - if( pnetwork_number == network_number) - { - sendpacket[6] = sendpacket[7] = sendpacket[8] = - sendpacket[9] = 0x00; + if (pnetwork_number == network_number) { + sendpacket[6] = sendpacket[7] = sendpacket[8] = + sendpacket[9] = 0x00; } - } - else - { + } else { /* If the incoming network is 0, make it ours */ - if( pnetwork_number == 0) - { - sendpacket[6] = (unsigned char)(network_number >> 24); - sendpacket[7] = (unsigned char)((network_number & - 0x00FF0000) >> 16); - sendpacket[8] = (unsigned char)((network_number & - 0x0000FF00) >> 8); - sendpacket[9] = (unsigned char)(network_number & - 0x000000FF); + if (pnetwork_number == 0) { + sendpacket[6] = (unsigned char) (network_number >> 24); + sendpacket[7] = (unsigned char) ((network_number & + 0x00FF0000) >> 16); + sendpacket[8] = (unsigned char) ((network_number & + 0x0000FF00) >> 8); + sendpacket[9] = (unsigned char) (network_number & + 0x000000FF); } } - - - pnetwork_number = (unsigned long)((sendpacket[18] << 24) + - (sendpacket[19] << 16) + (sendpacket[20] << 8) + - sendpacket[21]); - - if( !incoming ) - { + pnetwork_number = (unsigned long) ((sendpacket[18] << 24) + + (sendpacket[19] << 16) + (sendpacket[20] << 8) + + sendpacket[21]); + if (!incoming) { /* If the source network is ours, make it 0 */ - if( pnetwork_number == network_number) - { - sendpacket[18] = sendpacket[19] = sendpacket[20] = - sendpacket[21] = 0x00; + if (pnetwork_number == network_number) { + sendpacket[18] = sendpacket[19] = sendpacket[20] = + sendpacket[21] = 0x00; } - } - else - { + } else { /* If the source network is 0, make it ours */ - if( pnetwork_number == 0 ) - { - sendpacket[18] = (unsigned char)(network_number >> 24); - sendpacket[19] = (unsigned char)((network_number & - 0x00FF0000) >> 16); - sendpacket[20] = (unsigned char)((network_number & - 0x0000FF00) >> 8); - sendpacket[21] = (unsigned char)(network_number & - 0x000000FF); + if (pnetwork_number == 0) { + sendpacket[18] = (unsigned char) (network_number >> 24); + sendpacket[19] = (unsigned char) ((network_number & + 0x00FF0000) >> 16); + sendpacket[20] = (unsigned char) ((network_number & + 0x0000FF00) >> 8); + sendpacket[21] = (unsigned char) (network_number & + 0x000000FF); } } -} /* switch_net_numbers */ +} /* switch_net_numbers */ /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ -static struct net_device_stats* if_stats (struct device* dev) -{ - ppp_private_area_t* ppp_priv_area = dev->priv; - sdla_t* card = ppp_priv_area->card; +static struct enet_statistics *if_stats(struct device *dev) +{ + ppp_private_area_t *ppp_priv_area = dev->priv; + sdla_t *card = ppp_priv_area->card; return &card->wandev.stats; } @@ -886,146 +859,122 @@ static struct net_device_stats* if_stats (struct device* dev) * Read firmware code version. * Put code version as ASCII string in str. */ -static int ppp_read_version (sdla_t* card, char* str) + +static int ppp_read_version(sdla_t * card, char *str) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_CODE_VERSION; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) - ppp_error(card, err, mb); - else if (str) { - int len = mb->cmd.length; - memcpy(str, mb->data, len); str[len] = '\0'; - } - return err; } /*============================================================================ * Configure PPP firmware. */ -static int ppp_configure (sdla_t* card, void* data) + +static int ppp_configure(sdla_t * card, void *data) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int data_len = (card->hw.fwid == SFID_PPP502) ? - sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t); + sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t); int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); memcpy(mb->data, data, data_len); - mb->cmd.length = data_len; + mb->cmd.length = data_len; mb->cmd.command = PPP_SET_CONFIG; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); - return err; } /*============================================================================ * Set interrupt mode. */ -static int ppp_set_intr_mode (sdla_t* card, unsigned mode) + +static int ppp_set_intr_mode(sdla_t * card, unsigned mode) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->data[0] = mode; - - switch (card->hw.fwid) - { - case SFID_PPP502: - mb->cmd.length = 1; - break; - - case SFID_PPP508: - - default: - mb->data[1] = card->hw.irq; - mb->cmd.length = 2; + switch (card->hw.fwid) { + case SFID_PPP502: + mb->cmd.length = 1; + break; + case SFID_PPP508: + default: + mb->data[1] = card->hw.irq; + mb->cmd.length = 2; } - mb->cmd.command = PPP_SET_INTR_FLAGS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); - return err; } /*============================================================================ * Enable communications. */ -static int ppp_comm_enable (sdla_t* card) + +static int ppp_comm_enable(sdla_t * card) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_ENABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); - return err; } /*============================================================================ * Disable communications. */ -static int ppp_comm_disable (sdla_t* card) + +static int ppp_comm_disable(sdla_t * card) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_DISABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); - return err; } /*============================================================================ * Get communications error statistics. */ -static int ppp_get_err_stats (sdla_t* card) + +static int ppp_get_err_stats(sdla_t * card) { - ppp_mbox_t* mb = card->mbox; + ppp_mbox_t *mb = card->mbox; int err; - memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_ERROR_STATS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err == CMD_OK) - { - ppp_err_stats_t* stats = (void*)mb->data; - card->wandev.stats.rx_over_errors = stats->rx_overrun; - card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; - card->wandev.stats.rx_missed_errors = stats->rx_abort; - card->wandev.stats.rx_length_errors = stats->rx_lost; + if (err == CMD_OK) { + ppp_err_stats_t *stats = (void *) mb->data; + card->wandev.stats.rx_over_errors = stats->rx_overrun; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_abort; + card->wandev.stats.rx_length_errors = stats->rx_lost; card->wandev.stats.tx_aborted_errors = stats->tx_abort; - - } else + } else ppp_error(card, err, mb); - return err; } @@ -1034,36 +983,27 @@ static int ppp_get_err_stats (sdla_t* card) * Return: 0 - o.k. * 1 - no transmit buffers available */ - -static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto) + +static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto) { - ppp_buf_ctl_t* txbuf = card->u.p.txbuf; + ppp_buf_ctl_t *txbuf = card->u.p.txbuf; unsigned long addr; - if (txbuf->flag) - return 1; - + return 1 + ; if (card->hw.fwid == SFID_PPP502) - addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; - else + addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; + else addr = txbuf->buf.ptr; - - sdla_poke(&card->hw, addr, data, len); - - txbuf->length = len; /* frame length */ - + txbuf->length = len; /* frame length */ if (proto == ETH_P_IPX) txbuf->proto = 0x01; /* protocol ID */ - - txbuf->flag = 1; /* start transmission */ - + txbuf->flag = 1; /* start transmission */ /* Update transmit buffer control fields */ card->u.p.txbuf = ++txbuf; - - if ((void*)txbuf > card->u.p.txbuf_last) + if ((void *) txbuf > card->u.p.txbuf_last) card->u.p.txbuf = card->u.p.txbuf_base; - return 0; } @@ -1076,23 +1016,19 @@ static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto) * * Return zero if previous command has to be cancelled. */ - -static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb) + +static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb) { unsigned cmd = mb->cmd.command; - - switch (err) - { - case CMD_TIMEOUT: - printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, cmd); - break; - - default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" - , card->devname, cmd, err); + switch (err) { + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, cmd); + break; + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" + ,card->devname, cmd, err); } - return 0; } @@ -1101,81 +1037,65 @@ static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb) /*============================================================================ * PPP interrupt service routine. */ -STATIC void wpp_isr (sdla_t* card) + +STATIC void wpp_isr(sdla_t * card) { - ppp_flags_t* flags = card->flags; + ppp_flags_t *flags = card->flags; char *ptr = &flags->iflag; unsigned long host_cpu_flags; - struct device* dev = card->wandev.dev; + struct device *dev = card->wandev.dev; int i; - card->in_isr = 1; - ++card->statistics.isr_entry; - - if (set_bit(0, (void*)&card->wandev.critical)) { - + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { ++card->statistics.isr_already_critical; - printk (KERN_INFO "%s: Critical while in ISR!\n",card->devname); + printk(KERN_INFO "%s: Critical while in ISR!\n", card->devname); card->in_isr = 0; return; - } - /* For all interrupts set the critical flag to CRITICAL_IN_ISR. * If the if_send routine is called with this flag set it will set * the enable transmit flag to 1. (for a delayed interrupt) */ card->wandev.critical = CRITICAL_IN_ISR; - card->buff_int_mode_unbusy = 0; - switch (flags->iflag) { - - case 0x01: /* receive interrupt */ - ++card->statistics.isr_rx; - rx_intr(card); - break; - - case 0x02: /* transmit interrupt */ - ++card->statistics.isr_tx; - flags->imask &= ~0x02; - dev->tbusy = 0; - card->buff_int_mode_unbusy = 1; - break; - - case 0x08: - ++Intr_test_counter; - ++card->statistics.isr_intr_test; - break; - - default: /* unexpected interrupt */ - ++card->statistics.isr_spurious; - printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", - card->devname, flags->iflag); - printk(KERN_INFO "%s: ID Bytes = ",card->devname); - for(i = 0; i < 8; i ++) - printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); + case 0x01: /* receive interrupt */ + ++card->statistics.isr_rx; + rx_intr(card); + break; + case 0x02: /* transmit interrupt */ + ++card->statistics.isr_tx; + flags->imask &= ~0x02; + dev->tbusy = 0; + card->buff_int_mode_unbusy = 1; + break; + case 0x08: + ++Intr_test_counter; + ++card->statistics.isr_intr_test; + break; + default: /* unexpected interrupt */ + ++card->statistics.isr_spurious; + printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", + card->devname, flags->iflag); + printk(KERN_INFO "%s: ID Bytes = ", card->devname); + for (i = 0; i < 8; i++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); } - /* The critical flag is set to CRITICAL_INTR_HANDLED to let the * if_send call know that the interrupt is handled so that * transmit interrupts are not enabled again. - */ - + */ card->wandev.critical = CRITICAL_INTR_HANDLED; - /* If the enable transmit interrupt flag is set then enable transmit * interrupt on the board. This only goes through if if_send is called * and the critical flag is set due to an Interrupt. */ - if(card->wandev.enable_tx_int) { - + if (card->wandev.enable_tx_int) { flags->imask |= 0x02; card->wandev.enable_tx_int = 0; ++card->statistics.isr_enable_tx_int; - } save_flags(host_cpu_flags); cli(); @@ -1183,236 +1103,174 @@ STATIC void wpp_isr (sdla_t* card) flags->iflag = 0; card->wandev.critical = 0; restore_flags(host_cpu_flags); - - if(card->buff_int_mode_unbusy) + if (card->buff_int_mode_unbusy) mark_bh(NET_BH); - } /*============================================================================ * Receive interrupt handler. */ -static void rx_intr (sdla_t* card) + +static void rx_intr(sdla_t * card) { - ppp_buf_ctl_t* rxbuf = card->rxmb; - struct device* dev = card->wandev.dev; - ppp_private_area_t* ppp_priv_area; - struct sk_buff* skb; + ppp_buf_ctl_t *rxbuf = card->rxmb; + struct device *dev = card->wandev.dev; + ppp_private_area_t *ppp_priv_area; + struct sk_buff *skb; unsigned len; - void* buf; + void *buf; int i, err; - ppp_flags_t* flags = card->flags; - char *ptr = &flags->iflag; + ppp_flags_t *flags = card->flags; + char *ptr = &flags->iflag; int udp_type; - - if (rxbuf->flag != 0x01) { - - - printk(KERN_INFO - "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", - card->devname, (unsigned)rxbuf, rxbuf->flag); - - printk(KERN_INFO "%s: ID Bytes = ",card->devname); - - for(i = 0; i < 8; i ++) + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned) rxbuf, rxbuf->flag); + printk(KERN_INFO "%s: ID Bytes = ", card->devname); + for (i = 0; i < 8; i++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); - + printk(KERN_INFO "\n"); ++card->statistics.rx_intr_corrupt_rx_bfr; return; - } - - if (dev && dev->start) { - - len = rxbuf->length; + len = rxbuf->length; ppp_priv_area = dev->priv; - /* Allocate socket buffer */ skb = dev_alloc_skb(len); - if (skb != NULL) { - /* Copy data to the socket buffer */ if (card->hw.fwid == SFID_PPP502) { - - unsigned addr = (rxbuf->buf.o_p[1] << 8) + - rxbuf->buf.o_p[0]; + unsigned addr = (rxbuf->buf.o_p[1] << 8) + + rxbuf->buf.o_p[0]; buf = skb_put(skb, len); sdla_peek(&card->hw, addr, buf, len); - } else { - unsigned addr = rxbuf->buf.ptr; - if ((addr + len) > card->u.p.rx_top + 1) { - - unsigned tmp = card->u.p.rx_top - addr - + 1; + unsigned tmp = card->u.p.rx_top - addr + + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, addr, buf, tmp); addr = card->u.p.rx_base; len -= tmp; - } - buf = skb_put(skb, len); sdla_peek(&card->hw, addr, buf, len); } - /* Decapsulate packet */ - switch (rxbuf->proto) { - - case 0x00: - skb->protocol = htons(ETH_P_IP); - break; - - case 0x01: - skb->protocol = htons(ETH_P_IPX); - break; + switch (rxbuf->proto) { + case 0x00: + skb->protocol = htons(ETH_P_IP); + break; + case 0x01: + skb->protocol = htons(ETH_P_IPX); + break; } -#ifdef CONFIG_SANGOMA_MANAGER - udp_type = udp_pkt_type( skb, card ); - - if (udp_type == UDP_DRVSTATS_TYPE){ - ++ppp_priv_area->rx_intr_DRVSTATS_request; + udp_type = udp_pkt_type(skb, card); + if (udp_type == UDP_DRVSTATS_TYPE) { + ++ppp_priv_area->rx_intr_DRVSTATS_request; process_udp_driver_call( - UDP_PKT_FRM_NETWORK, card, skb, - dev, ppp_priv_area); - dev_kfree_skb(skb); - - } else if (udp_type == UDP_PTPIPE_TYPE){ + UDP_PKT_FRM_NETWORK, card, skb, + dev, ppp_priv_area); + dev_kfree_skb(skb); + } else if (udp_type == UDP_PTPIPE_TYPE) { ++ppp_priv_area->rx_intr_PTPIPE_request; err = process_udp_mgmt_pkt( - UDP_PKT_FRM_NETWORK, card, - skb, dev, ppp_priv_area); + UDP_PKT_FRM_NETWORK, card, + skb, dev, ppp_priv_area); dev_kfree_skb(skb); - } else -#endif - if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) { - - if( card->wandev.enable_IPX) { + } else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) { + if (card->wandev.enable_IPX) { ppp_send(card, skb->data, skb->len, ETH_P_IPX); - dev_kfree_skb(skb); - + dev_kfree_skb(skb); } else { ++card->wandev.stats.rx_dropped; } } else { /* Pass it up the protocol stack */ - skb->dev = dev; - skb->mac.raw = skb->data; - netif_rx(skb); - ++card->wandev.stats.rx_packets; - ++ppp_priv_area->rx_intr_bfr_passed_to_stack; + skb->dev = dev; + skb->mac.raw = skb->data; + netif_rx(skb); + ++card->wandev.stats.rx_packets; + ++ppp_priv_area->rx_intr_bfr_passed_to_stack; } - } else { - printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname); + card->devname); ++card->wandev.stats.rx_dropped; ++ppp_priv_area->rx_intr_no_socket; - } - } else ++card->statistics.rx_intr_dev_not_started; - /* Release buffer element and calculate a pointer to the next one */ rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00; card->rxmb = ++rxbuf; - - if ((void*)rxbuf > card->u.p.rxbuf_last) + if ((void *) rxbuf > card->u.p.rxbuf_last) card->rxmb = card->u.p.rxbuf_base; } /*============================================================================ * Transmit interrupt handler. */ -static void tx_intr (sdla_t* card) -{ - struct device* dev = card->wandev.dev; - if (!dev || !dev->start) - { +static void tx_intr(sdla_t * card) +{ + struct device *dev = card->wandev.dev; + if (!dev || !dev->start) { ++card->statistics.tx_intr_dev_not_started; - return; - } - + return; + } dev->tbusy = 0; - dev_tint(dev); + mark_bh(NET_BH); } static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto) { int i; - - if( proto == htons(ETH_P_IPX) ) - { + if (proto == htons(ETH_P_IPX)) { /* It's an IPX packet */ - if(!enable_IPX) - { - //Return 1 so we don't pass it up the stack. + if (!enable_IPX) { + /* Return 1 so we don't pass it up the stack. */ return 1; } - } - else - { + } else { /* It's not IPX so pass it up the stack. */ return 0; } - - if( sendpacket[16] == 0x90 && - sendpacket[17] == 0x04) - { + if (sendpacket[16] == 0x90 && + sendpacket[17] == 0x04) { /* It's IPXWAN */ - - if( sendpacket[2] == 0x02 && - sendpacket[34] == 0x00) - { + if (sendpacket[2] == 0x02 && + sendpacket[34] == 0x00) { /* It's a timer request packet */ - printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); - + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname); /* Go through the routing options and answer no to every */ /* option except Unnumbered RIP/SAP */ - for(i = 41; sendpacket[i] == 0x00; i += 5) - { + for (i = 41; sendpacket[i] == 0x00; i += 5) { /* 0x02 is the option for Unnumbered RIP/SAP */ - if( sendpacket[i + 4] != 0x02) - { + if (sendpacket[i + 4] != 0x02) { sendpacket[i + 1] = 0; } } - /* Skip over the extended Node ID option */ - if( sendpacket[i] == 0x04 ) - { + if (sendpacket[i] == 0x04) { i += 8; } - /* We also want to turn off all header compression opt. */ - for(; sendpacket[i] == 0x80 ;) - { + for (; sendpacket[i] == 0x80;) { sendpacket[i + 1] = 0; i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; } - /* Set the packet type to timer response */ sendpacket[34] = 0x01; - - printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); - } - else if( sendpacket[34] == 0x02 ) - { + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname); + } else if (sendpacket[34] == 0x02) { /* This is an information request packet */ - printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); - + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname); /* Set the packet type to information response */ sendpacket[34] = 0x03; - /* Set the router name */ sendpacket[51] = 'P'; sendpacket[52] = 'T'; @@ -1422,38 +1280,30 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char sendpacket[56] = 'E'; sendpacket[57] = '-'; sendpacket[58] = CVHexToAscii(network_number >> 28); - sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24); - sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20); - sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16); - sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12); - sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8); - sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4); + sendpacket[59] = CVHexToAscii((network_number & 0x0F000000) >> 24); + sendpacket[60] = CVHexToAscii((network_number & 0x00F00000) >> 20); + sendpacket[61] = CVHexToAscii((network_number & 0x000F0000) >> 16); + sendpacket[62] = CVHexToAscii((network_number & 0x0000F000) >> 12); + sendpacket[63] = CVHexToAscii((network_number & 0x00000F00) >> 8); + sendpacket[64] = CVHexToAscii((network_number & 0x000000F0) >> 4); sendpacket[65] = CVHexToAscii(network_number & 0x0000000F); - for(i = 66; i < 99; i+= 1) - { + for (i = 66; i < 99; i += 1) sendpacket[i] = 0; - } - - printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); - } - else - { - printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname); + } else { + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname); return 0; } - /* Set the WNodeID to our network address */ - sendpacket[35] = (unsigned char)(network_number >> 24); - sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16); - sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8); - sendpacket[38] = (unsigned char)(network_number & 0x000000FF); - + sendpacket[35] = (unsigned char) (network_number >> 24); + sendpacket[36] = (unsigned char) ((network_number & 0x00FF0000) >> 16); + sendpacket[37] = (unsigned char) ((network_number & 0x0000FF00) >> 8); + sendpacket[38] = (unsigned char) (network_number & 0x000000FF); return 1; } else { /* If we get here's its an IPX-data packet, so it'll get passed up the stack. */ - /* switch the network numbers */ - switch_net_numbers(sendpacket, network_number, 1); + switch_net_numbers(sendpacket, network_number, 1); return 0; } } @@ -1469,100 +1319,80 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char * 1. This routine may be called on interrupt context with all interrupts * enabled. Beware! */ -static void wpp_poll (sdla_t* card) + +static void wpp_poll(sdla_t * card) { - struct device* dev = card->wandev.dev; - ppp_flags_t* adptr_flags = card->flags; + struct device *dev = card->wandev.dev; + ppp_flags_t *adptr_flags = card->flags; unsigned long host_cpu_flags; - ++card->statistics.poll_entry; - /* The wpp_poll is called continously by the WANPIPE thread to allow * for line state housekeeping. However if we are in a connected state * then we do not need to go through all the checks everytime. When in * connected state execute wpp_poll once every second. */ - - if (card->wandev.state == WAN_CONNECTED) - { - if ((jiffies - card->state_tick) < HZ ) - return; + if (card->wandev.state == WAN_CONNECTED) { + if ((jiffies - card->state_tick) < HZ) + return; } - - disable_irq(card->hw.irq); - ++card->irq_dis_poll_count; - - if (set_bit(0, (void *)&card->wandev.critical)) - { + disable_irq(card->hw.irq); + ++card->irq_dis_poll_count; + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { ++card->statistics.poll_already_critical; - printk(KERN_INFO "%s: critical inside wpp_poll\n", - card->devname); + printk(KERN_INFO "%s: critical inside wpp_poll\n", + card->devname); save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && - (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - + cli(); + if ((!card->irq_dis_if_send_count) && + (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); return; } - - ++card->statistics.poll_processed; - - if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) - { + ++card->statistics.poll_processed; + if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) { ++card->statistics.poll_tbusy_bad_status; printk(KERN_INFO "%s: Wpp_Poll: tbusy = 0x01, imask = 0x%02X\n" - , card->devname, adptr_flags->imask); - } - - switch(card->wandev.state) - { - case WAN_CONNECTED: - card->state_tick = jiffies; - poll_active(card); - break; - - case WAN_CONNECTING: - poll_connecting(card); - break; - - case WAN_DISCONNECTED: - poll_disconnected(card); - break; - - default: - printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", - card->devname, card->wandev.state); - break; + ,card->devname, adptr_flags->imask); + } + switch (card->wandev.state) { + case WAN_CONNECTED: + card->state_tick = jiffies; + poll_active(card); + break; + case WAN_CONNECTING: + poll_connecting(card); + break; + case WAN_DISCONNECTED: + poll_disconnected(card); + break; + default: + printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", + card->devname, card->wandev.state); + break; } - card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - + cli(); + if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); } /*============================================================================ * Monitor active link phase. */ -static void poll_active (sdla_t* card) -{ - ppp_flags_t* flags = card->flags; +static void poll_active(sdla_t * card) +{ + ppp_flags_t *flags = card->flags; /* We check the lcp_state to see if we are in DISCONNECTED state. * We are considered to be connected for lcp states 0x06, 0x07, 0x08 * and 0x09. */ if ((flags->lcp_state <= 0x05) || (flags->disc_cause & 0x03)) { - wanpipe_set_state(card, WAN_DISCONNECTED); show_disc_cause(card, flags->disc_cause); - } } @@ -1570,16 +1400,13 @@ static void poll_active (sdla_t* card) * Monitor link establishment phase. * o if connection timed out, disconnect the link. */ -static void poll_connecting (sdla_t* card) + +static void poll_connecting(sdla_t * card) { - ppp_flags_t* flags = card->flags; - - if (flags->lcp_state == 0x09) - { + ppp_flags_t *flags = card->flags; + if (flags->lcp_state == 0x09) { wanpipe_set_state(card, WAN_CONNECTED); - } - else if (flags->disc_cause & 0x03) - { + } else if (flags->disc_cause & 0x03) { wanpipe_set_state(card, WAN_DISCONNECTED); show_disc_cause(card, flags->disc_cause); } @@ -1590,16 +1417,15 @@ static void poll_connecting (sdla_t* card) * o if interface is up and the hold-down timeout has expired, then retry * connection. */ -static void poll_disconnected (sdla_t* card) -{ - struct device* dev = card->wandev.dev; +static void poll_disconnected(sdla_t * card) +{ + struct device *dev = card->wandev.dev; if (dev && dev->start && - ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) - { - wanpipe_set_state(card, WAN_CONNECTING); - if(ppp_comm_enable(card) == CMD_OK) - init_ppp_tx_rx_buff( card ); + ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) { + wanpipe_set_state(card, WAN_CONNECTING); + if (ppp_comm_enable(card) == CMD_OK) + init_ppp_tx_rx_buff(card); } } @@ -1608,319 +1434,818 @@ static void poll_disconnected (sdla_t* card) /*============================================================================ * Configure S502 adapter. */ -static int config502 (sdla_t* card) + +static int config502(sdla_t * card) { ppp502_conf_t cfg; - /* Prepare PPP configuration structure */ memset(&cfg, 0, sizeof(ppp502_conf_t)); - if (card->wandev.clocking) cfg.line_speed = bps_to_speed_code(card->wandev.bps); - - cfg.txbuf_num = 4; - cfg.mtu_local = card->wandev.mtu; - cfg.mtu_remote = card->wandev.mtu; - cfg.restart_tmr = 30; - cfg.auth_rsrt_tmr = 30; - cfg.auth_wait_tmr = 300; - cfg.mdm_fail_tmr = 5; - cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 0; /* changed it from 900 */ - cfg.conf_retry = 10; - cfg.term_retry = 2; - cfg.fail_retry = 5; - cfg.auth_retry = 10; - cfg.ip_options = 0x80; - cfg.ipx_options = 0xA0; - cfg.conf_flags |= 0x0E; + cfg.txbuf_num = 4; + cfg.mtu_local = card->wandev.mtu; + cfg.mtu_remote = card->wandev.mtu; + cfg.restart_tmr = 30; + cfg.auth_rsrt_tmr = 30; + cfg.auth_wait_tmr = 300; + cfg.mdm_fail_tmr = 5; + cfg.dtr_drop_tmr = 1; + cfg.connect_tmout = 0; /* changed it from 900 */ + cfg.conf_retry = 10; + cfg.term_retry = 2; + cfg.fail_retry = 5; + cfg.auth_retry = 10; + cfg.ip_options = 0x80; + cfg.ipx_options = 0xA0; + cfg.conf_flags |= 0x0E; /* - cfg.ip_local = dev->pa_addr; - cfg.ip_remote = dev->pa_dstaddr; -*/ + cfg.ip_local = dev->pa_addr; + cfg.ip_remote = dev->pa_dstaddr; + */ return ppp_configure(card, &cfg); } /*============================================================================ * Configure S508 adapter. */ -static int config508 (sdla_t* card) + +static int config508(sdla_t * card) { ppp508_conf_t cfg; - /* Prepare PPP configuration structure */ memset(&cfg, 0, sizeof(ppp508_conf_t)); - if (card->wandev.clocking) cfg.line_speed = card->wandev.bps; - if (card->wandev.interface == WANOPT_RS232) cfg.conf_flags |= 0x0020; - - cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/ - cfg.txbuf_percent = 60; /* % of Tx bufs */ - cfg.mtu_local = card->wandev.mtu; - cfg.mtu_remote = card->wandev.mtu; - cfg.restart_tmr = 30; - cfg.auth_rsrt_tmr = 30; - cfg.auth_wait_tmr = 300; - cfg.mdm_fail_tmr = 100; - cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 0; /* changed it from 900 */ - cfg.conf_retry = 10; - cfg.term_retry = 2; - cfg.fail_retry = 5; - cfg.auth_retry = 10; - cfg.ip_options = 0x80; - cfg.ipx_options = 0xA0; + cfg.conf_flags |= 0x300; /*send Configure-Request packets forever */ + cfg.txbuf_percent = 60; /* % of Tx bufs */ + cfg.mtu_local = card->wandev.mtu; + cfg.mtu_remote = card->wandev.mtu; + cfg.restart_tmr = 30; + cfg.auth_rsrt_tmr = 30; + cfg.auth_wait_tmr = 300; + cfg.mdm_fail_tmr = 100; + cfg.dtr_drop_tmr = 1; + cfg.connect_tmout = 0; /* changed it from 900 */ + cfg.conf_retry = 10; + cfg.term_retry = 2; + cfg.fail_retry = 5; + cfg.auth_retry = 10; + cfg.ip_options = 0x80; + cfg.ipx_options = 0xA0; /* - cfg.ip_local = dev->pa_addr; - cfg.ip_remote = dev->pa_dstaddr; -*/ + cfg.ip_local = dev->pa_addr; + cfg.ip_remote = dev->pa_dstaddr; + */ return ppp_configure(card, &cfg); } /*============================================================================ * Show disconnection cause. */ -static void show_disc_cause (sdla_t* card, unsigned cause) -{ - if (cause & 0x0002) - printk(KERN_INFO "%s: link terminated by peer\n", - card->devname); - else if (cause & 0x0004) - printk(KERN_INFO "%s: link terminated by user\n", - card->devname); - - else if (cause & 0x0008) +static void show_disc_cause(sdla_t * card, unsigned cause) +{ + if (cause & 0x0002) + printk(KERN_INFO "%s: link terminated by peer\n", + card->devname); + else if (cause & 0x0004) + printk(KERN_INFO "%s: link terminated by user\n", + card->devname); + else if (cause & 0x0008) printk(KERN_INFO "%s: authentication failed\n", card->devname); - else if (cause & 0x0010) - printk(KERN_INFO - "%s: authentication protocol negotiation failed\n", - card->devname); - - else if (cause & 0x0020) printk(KERN_INFO - "%s: peer's request for authentication rejected\n", - card->devname); - - else if (cause & 0x0040) - printk(KERN_INFO "%s: MRU option rejected by peer\n", - card->devname); - - else if (cause & 0x0080) - printk(KERN_INFO "%s: peer's MRU was too small\n", - card->devname); - - else if (cause & 0x0100) + "%s: authentication protocol negotiation failed\n", + card->devname); + else if (cause & 0x0020) + printk(KERN_INFO + "%s: peer's request for authentication rejected\n", + card->devname); + else if (cause & 0x0040) + printk(KERN_INFO "%s: MRU option rejected by peer\n", + card->devname); + else if (cause & 0x0080) + printk(KERN_INFO "%s: peer's MRU was too small\n", + card->devname); + else if (cause & 0x0100) printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n", - card->devname); - - else if (cause & 0x0200) + card->devname); + else if (cause & 0x0200) printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n" - , card->devname); - - else if (cause & 0x0400) - printk(KERN_INFO - "%s: failed to negotiate peer's IPXCP options\n", - card->devname); + ,card->devname); + else if (cause & 0x0400) + printk(KERN_INFO + "%s: failed to negotiate peer's IPXCP options\n", + card->devname); } /*============================================================================ * Convert line speed in bps to a number used by S502 code. */ -static unsigned char bps_to_speed_code (unsigned long bps) -{ - unsigned char number; - if (bps <= 1200) +static unsigned char bps_to_speed_code(unsigned long bps) +{ + unsigned char number; + if (bps <= 1200) number = 0x01; - else if (bps <= 2400) + else if (bps <= 2400) number = 0x02; - else if (bps <= 4800) + else if (bps <= 4800) number = 0x03; - else if (bps <= 9600) + else if (bps <= 9600) number = 0x04; - else if (bps <= 19200) + else if (bps <= 19200) number = 0x05; - else if (bps <= 38400) + else if (bps <= 38400) number = 0x06; - else if (bps <= 45000) + else if (bps <= 45000) number = 0x07; - else if (bps <= 56000) + else if (bps <= 56000) number = 0x08; - else if (bps <= 64000) + else if (bps <= 64000) number = 0x09; - else if (bps <= 74000) + else if (bps <= 74000) number = 0x0A; - else if (bps <= 112000) + else if (bps <= 112000) number = 0x0B; - else if (bps <= 128000) + else if (bps <= 128000) number = 0x0C; - else + else number = 0x0D; - return number; } +/*============================================================================ + * Process UDP call of type DRVSTATS. + */ + +static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area) +{ + unsigned char *sendpacket; + unsigned char buf2[5]; + unsigned char *data; + unsigned char *buf; + unsigned int len; + ppp_mbox_t *mbox = card->mbox; + struct sk_buff *new_skb; + int err; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) { + printk(KERN_INFO + "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X" + ,card->devname, data[45]); + ++ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err; + return 1; + } + memcpy(data, sendpacket, skb->len); + switch (data[45]) { + /* PPIPE_DRIVER_STATISTICS */ + case 0x26: + *(unsigned long *) &data[60] = + ppp_priv_area->if_send_entry; + *(unsigned long *) &data[64] = + ppp_priv_area->if_send_skb_null; + *(unsigned long *) &data[68] = + ppp_priv_area->if_send_broadcast; + *(unsigned long *) &data[72] = + ppp_priv_area->if_send_multicast; + *(unsigned long *) &data[76] = + ppp_priv_area->if_send_critical_ISR; + *(unsigned long *) &data[80] = + ppp_priv_area->if_send_critical_non_ISR; + *(unsigned long *) &data[84] = + ppp_priv_area->if_send_busy; + *(unsigned long *) &data[88] = + ppp_priv_area->if_send_busy_timeout; + *(unsigned long *) &data[92] = + ppp_priv_area->if_send_DRVSTATS_request; + *(unsigned long *) &data[96] = + ppp_priv_area->if_send_PTPIPE_request; + *(unsigned long *) &data[100] = + ppp_priv_area->if_send_wan_disconnected; + *(unsigned long *) &data[104] = + ppp_priv_area->if_send_adptr_bfrs_full; + *(unsigned long *) &data[108] = + ppp_priv_area->if_send_protocol_error; + *(unsigned long *) &data[112] = + ppp_priv_area->if_send_tx_int_enabled; + *(unsigned long *) &data[116] = + ppp_priv_area->if_send_bfr_passed_to_adptr; + *(unsigned long *) &data[118] = + card->irq_dis_if_send_count; + mbox->cmd.length = 62; + break; + case 0x27: + *(unsigned long *) &data[60] = card->statistics.isr_entry; + *(unsigned long *) &data[64] = + card->statistics.isr_already_critical; + *(unsigned long *) &data[68] = card->statistics.isr_rx; + *(unsigned long *) &data[72] = card->statistics.isr_tx; + *(unsigned long *) &data[76] = + card->statistics.isr_intr_test; + *(unsigned long *) &data[80] = + card->statistics.isr_spurious; + *(unsigned long *) &data[84] = + card->statistics.isr_enable_tx_int; + *(unsigned long *) &data[88] = + card->statistics.rx_intr_corrupt_rx_bfr; + *(unsigned long *) &data[92] = + ppp_priv_area->rx_intr_no_socket; + *(unsigned long *) &data[96] = + ppp_priv_area->rx_intr_DRVSTATS_request; + *(unsigned long *) &data[100] = + ppp_priv_area->rx_intr_PTPIPE_request; + *(unsigned long *) &data[104] = + ppp_priv_area->rx_intr_bfr_passed_to_stack; + *(unsigned long *) &data[108] = + card->statistics.rx_intr_dev_not_started; + *(unsigned long *) &data[112] = + card->statistics.tx_intr_dev_not_started; + mbox->cmd.length = 56; + break; + case 0x28: + *(unsigned long *) &data[60] = + ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err; + *(unsigned long *) &data[64] = + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err; + *(unsigned long *) &data[68] = + ppp_priv_area->UDP_PTPIPE_mgmt_direction_err; + *(unsigned long *) &data[72] = + ppp_priv_area-> + UDP_PTPIPE_mgmt_adptr_cmnd_timeout; + *(unsigned long *) &data[76] = + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK; + *(unsigned long *) &data[80] = + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr; + *(unsigned long *) &data[84] = + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack; + *(unsigned long *) &data[88] = + ppp_priv_area->UDP_PTPIPE_mgmt_no_socket; + *(unsigned long *) &data[92] = + ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err; + *(unsigned long *) &data[96] = + ppp_priv_area-> + UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + *(unsigned long *) &data[100] = + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + *(unsigned long *) &data[104] = + ppp_priv_area-> + UDP_DRVSTATS_mgmt_passed_to_adptr; + *(unsigned long *) &data[108] = + ppp_priv_area-> + UDP_DRVSTATS_mgmt_passed_to_stack; + *(unsigned long *) &data[112] = + ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket; + *(unsigned long *) &data[116] = + card->statistics.poll_entry; + *(unsigned long *) &data[120] = + card->statistics.poll_already_critical; + *(unsigned long *) &data[124] = + card->statistics.poll_processed; + *(unsigned long *) &data[126] = + card->irq_dis_poll_count; + mbox->cmd.length = 70; + break; + default: + /* it's a board command */ + memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t)); + if (mbox->cmd.length) { + memcpy(&mbox->data, &sendpacket[60], + mbox->cmd.length); + } + /* run the command on the board */ + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) { + ppp_error(card, err, mbox); + ++ppp_priv_area-> + UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + break; + } + ++ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + /* copy the result back to our buffer */ + memcpy(data, sendpacket, skb->len); + memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t)); + if (mbox->cmd.length) { + memcpy(&data[60], &mbox->data, mbox->cmd.length); + } + } + /* Fill UDP TTL */ + data[8] = card->wandev.ttl; + len = reply_udp(data, mbox->cmd.length); + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { + ++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr; + ppp_send(card, data, len, skb->protocol); + } else { + /* Pass it up the stack + Allocate socket buffer */ + if ((new_skb = dev_alloc_skb(len)) != NULL) { + /* copy data into new_skb */ + buf = skb_put(new_skb, len); + memcpy(buf, data, len); + ++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack; + /* Decapsulate packet and pass it up the protocol + stack */ + new_skb->protocol = htons(ETH_P_IP); + new_skb->dev = dev; + new_skb->mac.raw = new_skb->data; + netif_rx(new_skb); + } else { + ++ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket; + printk(KERN_INFO "no socket buffers available!\n"); + } + } + kfree(data); + return 0; +} + +/*============================================================================= + * Process UDP call of type PTPIPEAB. + */ + +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, + struct sk_buff *skb, struct device *dev, + ppp_private_area_t * ppp_priv_area) +{ + unsigned char *sendpacket; + unsigned char buf2[5]; + unsigned char *data; + unsigned char *buf; + unsigned int frames, len; + struct sk_buff *new_skb; + unsigned short buffer_length, real_len; + unsigned long data_ptr; + int udp_mgmt_req_valid = 1; + ppp_mbox_t *mbox = card->mbox; + struct timeval tv; + int err; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) { + printk(KERN_INFO + "%s: Error allocating memory for UDP management cmnd0x%02X" + ,card->devname, data[45]); + ++ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err; + return 1; + } + memcpy(data, sendpacket, skb->len); + switch (data[45]) { + /* FT1 MONITOR STATUS */ + case 0x80: + if (card->hw.fwid != SFID_PPP508) { + ++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err; + udp_mgmt_req_valid = 0; + break; + } + /* PPIPE_ENABLE_TRACING */ + case 0x20: + /* PPIPE_DISABLE_TRACING */ + case 0x21: + /* PPIPE_GET_TRACE_INFO */ + case 0x22: + /* SET FT1 MODE */ + case 0x81: + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { + ++ppp_priv_area->UDP_PTPIPE_mgmt_direction_err; + udp_mgmt_req_valid = 0; + } + break; + default: + break; + } + if (!udp_mgmt_req_valid) { + /* set length to 0 */ + data[46] = data[47] = 0; + /* set return code */ + data[48] = 0xCD; + } else { + switch (data[45]) { + /* PPIPE_ENABLE_TRACING */ + case 0x20: + if (!TracingEnabled) { + /* OPERATE_DATALINE_MONITOR */ + mbox->cmd.command = 0x33; + mbox->cmd.length = 1; + mbox->data[0] = 0x03; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) { + ppp_error(card, err, mbox); + TracingEnabled = 0; + /* set the return code */ + data[48] = mbox->cmd.result; + mbox->cmd.length = 0; + break; + } + if (card->hw.fwid == SFID_PPP502) { + sdla_peek(&card->hw, 0x9000, &buf2, 2); + } else { + sdla_peek(&card->hw, 0xC000, &buf2, 2); + } + curr_trace_addr = 0; + memcpy(&curr_trace_addr, &buf2, 2); + start_trace_addr = curr_trace_addr; + /* MAX_SEND_BUFFER_SIZE -sizeof(UDP_MGMT_PACKET) + - 41 */ + available_buffer_space = 1926; + } + data[48] = 0; + mbox->cmd.length = 0; + TracingEnabled = 1; + break; + /* PPIPE_DISABLE_TRACING */ + case 0x21: + if (TracingEnabled) { + /* OPERATE_DATALINE_MONITOR */ + mbox->cmd.command = 0x3; + mbox->cmd.length = 1; + mbox->data[0] = 0x00; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + } + /*set return code */ + data[48] = 0; + mbox->cmd.length = 0; + TracingEnabled = 0; + break; + /* PPIPE_GET_TRACE_INFO */ + case 0x22: + if (TracingEnabled) { + buffer_length = 0; + /* frames < NUM_TRACE_FRAMES */ + for (frames = 0; frames < 62; frames += 1) { + sdla_peek(&card->hw, curr_trace_addr, + &buf2, 1); + /* no data on board so exit */ + if (buf2[0] == 0x00) + break; + /*1+sizeof(FRAME_DATA) = 9 */ + if ((available_buffer_space - + buffer_length) < 9) { + /*indicate we have more frames + on board and exit */ + data[60] |= 0x02; + break; + } + /* get frame status */ + sdla_peek(&card->hw, curr_trace_addr + + 0x01, &data[60 + buffer_length], 1); + /* get time stamp */ + sdla_peek(&card->hw, curr_trace_addr + + 0x06, &data[64 + buffer_length], 2); + /* get frame length */ + sdla_peek(&card->hw, curr_trace_addr + + 0x02, &data[62 + buffer_length], 2); + /* get pointer to real data */ + sdla_peek(&card->hw, curr_trace_addr + + 0x04, &buf2, 2); + data_ptr = 0; + memcpy(&data_ptr, &buf2, 2); + /* see if we can fit the frame into the + user buffer */ + memcpy(&real_len, + &data[62 + buffer_length], 2); + if ((data_ptr == 0) || + ((real_len + 8) > + available_buffer_space)) { + data[61 + buffer_length] = 0x00; + } else { + /* we can take it next time */ + if ((available_buffer_space - + buffer_length) < + (real_len + 8)) { + data[60] |= 0x02; + break; + } + /* ok, get the frame */ + data[61 + buffer_length] = 0x01; + /* get the data */ + sdla_peek(&card->hw, data_ptr, + &data[66 + buffer_length], + real_len); + /* zero the opp flag to + show we got the frame */ + buf2[0] = 0x00; + sdla_poke(&card->hw, + curr_trace_addr, &buf2, 1); + /* now move onto the next + frame */ + curr_trace_addr += 8; + /* check if we passed the last + address */ + if (curr_trace_addr >= + start_trace_addr + 0x1F0) { + curr_trace_addr = + start_trace_addr; + } + /* update buffer length and make sure its even */ + if (data[61 + buffer_length] + == 0x01) { + buffer_length += + real_len - 1; + } + /* for the header */ + buffer_length += 8; + if (buffer_length & 0x0001) + buffer_length += 1; + } + } + /* ok now set the total number of frames passed + in the high 5 bits */ + data[60] = (frames << 2) | data[60]; + /* set the data length */ + mbox->cmd.length = buffer_length; + memcpy(&data[46], &buffer_length, 2); + /* set return code */ + data[48] = 0; + } else { + /* set return code */ + data[48] = 1; + mbox->cmd.length = 0; + } + break; + /* PPIPE_GET_IBA_DATA */ + case 0x23: + mbox->cmd.length = 0x09; + if (card->hw.fwid == SFID_PPP502) { + sdla_peek(&card->hw, 0xA003, &data[60], + mbox->cmd.length); + } else { + sdla_peek(&card->hw, 0xF003, &data[60], + mbox->cmd.length); + } + /* set the length of the data */ + data[46] = 0x09; + /* set return code */ + data[48] = 0x00; + break; + /* PPIPE_KILL_BOARD */ + case 0x24: + break; + /* PPIPE_FT1_READ_STATUS */ + case 0x25: + sdla_peek(&card->hw, 0xF020, &data[60], 2); + data[46] = 2; + data[47] = 0; + data[48] = 0; + mbox->cmd.length = 2; + break; + case 0x29: + init_ppp_priv_struct(ppp_priv_area); + init_global_statistics(card); + mbox->cmd.length = 0; + break; + case 0x30: + do_gettimeofday(&tv); + ppp_priv_area->router_up_time = tv.tv_sec - + ppp_priv_area->router_start_time; + *(unsigned long *) &data[60] = + ppp_priv_area->router_up_time; + mbox->cmd.length = 4; + break; + /* FT1 MONITOR STATUS */ + case 0x80: + /* Enable FT1 MONITOR STATUS */ + if (data[60] == 1) { + if (rCount++ != 0) { + data[48] = 0; + mbox->cmd.length = 1; + break; + } + } + /* Disable FT1 MONITOR STATUS */ + if (data[60] == 0) { + if (--rCount != 0) { + data[48] = 0; + mbox->cmd.length = 1; + break; + } + } + default: + /* it's a board command */ + memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t)); + if (mbox->cmd.length) { + memcpy(&mbox->data, &sendpacket[60], + mbox->cmd.length); + } + /* run the command on the board */ + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) { + ppp_error(card, err, mbox); + ++ppp_priv_area-> + UDP_PTPIPE_mgmt_adptr_cmnd_timeout; + break; + } + ++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK; + /* copy the result back to our buffer */ + memcpy(data, sendpacket, skb->len); + memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t)); + if (mbox->cmd.length) { + memcpy(&data[60], &mbox->data, mbox->cmd.length); + } + } /* end of switch */ + } /* end of else */ + /* Fill UDP TTL */ + data[8] = card->wandev.ttl; + len = reply_udp(data, mbox->cmd.length); + if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { + ++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr; + ppp_send(card, data, len, skb->protocol); + } else { + /* Pass it up the stack + Allocate socket buffer */ + if ((new_skb = dev_alloc_skb(len)) != NULL) { + /* copy data into new_skb */ + buf = skb_put(new_skb, len); + memcpy(buf, data, len); + ++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack; + /* Decapsulate packet and pass it up the protocol + stack */ + new_skb->protocol = htons(ETH_P_IP); + new_skb->dev = dev; + new_skb->mac.raw = new_skb->data; + netif_rx(new_skb); + } else { + ++ppp_priv_area->UDP_PTPIPE_mgmt_no_socket; + printk(KERN_INFO "no socket buffers available!\n"); + } + } + kfree(data); + return 0; +} + /*============================================================================= * Initial the ppp_private_area structure. */ -static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area ) +static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area) { - ppp_priv_area->if_send_entry = 0; - ppp_priv_area->if_send_skb_null = 0; - ppp_priv_area->if_send_broadcast = 0; - ppp_priv_area->if_send_multicast = 0; - ppp_priv_area->if_send_critical_ISR = 0; - ppp_priv_area->if_send_critical_non_ISR = 0; - ppp_priv_area->if_send_busy = 0; - ppp_priv_area->if_send_busy_timeout = 0; - ppp_priv_area->if_send_DRVSTATS_request = 0; - ppp_priv_area->if_send_PTPIPE_request = 0; - ppp_priv_area->if_send_wan_disconnected = 0; - ppp_priv_area->if_send_adptr_bfrs_full = 0; - ppp_priv_area->if_send_bfr_passed_to_adptr = 0; - - ppp_priv_area->rx_intr_no_socket = 0; - ppp_priv_area->rx_intr_DRVSTATS_request = 0; - ppp_priv_area->rx_intr_PTPIPE_request = 0; - ppp_priv_area->rx_intr_bfr_not_passed_to_stack = 0; - ppp_priv_area->rx_intr_bfr_passed_to_stack = 0; - - ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_direction_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_no_socket = 0; - - ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket = 0; + ppp_priv_area->if_send_entry = 0; + ppp_priv_area->if_send_skb_null = 0; + ppp_priv_area->if_send_broadcast = 0; + ppp_priv_area->if_send_multicast = 0; + ppp_priv_area->if_send_critical_ISR = 0; + ppp_priv_area->if_send_critical_non_ISR = 0; + ppp_priv_area->if_send_busy = 0; + ppp_priv_area->if_send_busy_timeout = 0; + ppp_priv_area->if_send_DRVSTATS_request = 0; + ppp_priv_area->if_send_PTPIPE_request = 0; + ppp_priv_area->if_send_wan_disconnected = 0; + ppp_priv_area->if_send_adptr_bfrs_full = 0; + ppp_priv_area->if_send_bfr_passed_to_adptr = 0; + ppp_priv_area->rx_intr_no_socket = 0; + ppp_priv_area->rx_intr_DRVSTATS_request = 0; + ppp_priv_area->rx_intr_PTPIPE_request = 0; + ppp_priv_area->rx_intr_bfr_not_passed_to_stack = 0; + ppp_priv_area->rx_intr_bfr_passed_to_stack = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_direction_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_no_socket = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket = 0; } /*============================================================================ * Initialize Global Statistics */ -static void init_global_statistics( sdla_t* card ) + +static void init_global_statistics(sdla_t * card) { - card->statistics.isr_entry = 0; - card->statistics.isr_already_critical = 0; - card->statistics.isr_tx = 0; - card->statistics.isr_rx = 0; - card->statistics.isr_intr_test = 0; - card->statistics.isr_spurious = 0; - card->statistics.isr_enable_tx_int = 0; + card->statistics.isr_entry = 0; + card->statistics.isr_already_critical = 0; + card->statistics.isr_tx = 0; + card->statistics.isr_rx = 0; + card->statistics.isr_intr_test = 0; + card->statistics.isr_spurious = 0; + card->statistics.isr_enable_tx_int = 0; card->statistics.rx_intr_corrupt_rx_bfr = 0; - card->statistics.rx_intr_dev_not_started= 0; - card->statistics.tx_intr_dev_not_started= 0; - card->statistics.poll_entry = 0; - card->statistics.poll_already_critical = 0; - card->statistics.poll_processed = 0; - card->statistics.poll_tbusy_bad_status = 0; - + card->statistics.rx_intr_dev_not_started = 0; + card->statistics.tx_intr_dev_not_started = 0; + card->statistics.poll_entry = 0; + card->statistics.poll_already_critical = 0; + card->statistics.poll_processed = 0; + card->statistics.poll_tbusy_bad_status = 0; } /*============================================================================ * Initialize Receive and Transmit Buffers. */ -static void init_ppp_tx_rx_buff( sdla_t* card ) -{ - - if (card->hw.fwid == SFID_PPP502) - { - ppp502_buf_info_t* info = - (void*)(card->hw.dpmbase + PPP502_BUF_OFFS); - - card->u.p.txbuf_base = - (void*)(card->hw.dpmbase + info->txb_offs); - - card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + - (info->txb_num - 1); - - card->u.p.rxbuf_base = - (void*)(card->hw.dpmbase + info->rxb_offs); - - card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + - (info->rxb_num - 1); - } - else - { - ppp508_buf_info_t* info = - (void*)(card->hw.dpmbase + PPP508_BUF_OFFS); - - card->u.p.txbuf_base = (void*)(card->hw.dpmbase + - (info->txb_ptr - PPP508_MB_VECT)); - - card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + - (info->txb_num - 1); - - card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + - (info->rxb_ptr - PPP508_MB_VECT)); - - card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + - (info->rxb_num - 1); +static void init_ppp_tx_rx_buff(sdla_t * card) +{ + if (card->hw.fwid == SFID_PPP502) { + ppp502_buf_info_t *info = + (void *) (card->hw.dpmbase + PPP502_BUF_OFFS); + card->u.p.txbuf_base = + (void *) (card->hw.dpmbase + info->txb_offs); + card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base + + (info->txb_num - 1); + card->u.p.rxbuf_base = + (void *) (card->hw.dpmbase + info->rxb_offs); + card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base + + (info->rxb_num - 1); + } else { + ppp508_buf_info_t *info = + (void *) (card->hw.dpmbase + PPP508_BUF_OFFS); + card->u.p.txbuf_base = (void *) (card->hw.dpmbase + + (info->txb_ptr - PPP508_MB_VECT)); + card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base + + (info->txb_num - 1); + card->u.p.rxbuf_base = (void *) (card->hw.dpmbase + + (info->rxb_ptr - PPP508_MB_VECT)); + card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base + + (info->rxb_num - 1); card->u.p.rx_base = info->rxb_base; - card->u.p.rx_top = info->rxb_end; + card->u.p.rx_top = info->rxb_end; } - card->u.p.txbuf = card->u.p.txbuf_base; card->rxmb = card->u.p.rxbuf_base; - } /*============================================================================= * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR * _TEST_COUNTER times. */ -static int intr_test( sdla_t* card ) -{ - ppp_mbox_t* mb = card->mbox; - int err,i; +static int intr_test(sdla_t * card) +{ + ppp_mbox_t *mb = card->mbox; + int err, i; /* The critical flag is unset because during intialization (if_open) * we want the interrupts to be enabled so that when the wpp_isr is * called it does not exit due to critical flag set. - */ - + */ card->wandev.critical = 0; - - err = ppp_set_intr_mode( card, 0x08 ); - - if ( err == CMD_OK ) - { - for (i=0; i<MAX_INTR_TEST_COUNTER; i++) - { + err = ppp_set_intr_mode(card, 0x08); + if (err == CMD_OK) { + for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) { /* Run command READ_CODE_VERSION */ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); - mb->cmd.length = 0; + mb->cmd.length = 0; mb->cmd.command = 0x10; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); } - } - else return err; - - err = ppp_set_intr_mode( card, 0 ); - if (err != CMD_OK) + } else + return err; + err = ppp_set_intr_mode(card, 0); + if (err != CMD_OK) return err; - card->wandev.critical = 1; return 0; } +/*============================================================================== + * Determine what type of UDP call it is. DRVSTATS or PTPIPEAB ? + */ + +static int udp_pkt_type(struct sk_buff *skb, sdla_t * card) +{ + unsigned char *sendpacket; + unsigned char buf2[5]; + sendpacket = skb->data; + memcpy(&buf2, &card->wandev.udp_port, 2); + if (sendpacket[0] == 0x45 && /* IP packet */ + sendpacket[9] == 0x11 && /* UDP packet */ + sendpacket[22] == buf2[1] && /* UDP Port */ + sendpacket[23] == buf2[0] && + sendpacket[36] == 0x01) { + if (sendpacket[28] == 0x50 && /* PTPIPEAB: Signature */ + sendpacket[29] == 0x54 && + sendpacket[30] == 0x50 && + sendpacket[31] == 0x49 && + sendpacket[32] == 0x50 && + sendpacket[33] == 0x45 && + sendpacket[34] == 0x41 && + sendpacket[35] == 0x42) { + return UDP_PTPIPE_TYPE; + } else if (sendpacket[28] == 0x44 && /* DRVSTATS: Signature */ + sendpacket[29] == 0x52 && + sendpacket[30] == 0x56 && + sendpacket[31] == 0x53 && + sendpacket[32] == 0x54 && + sendpacket[33] == 0x41 && + sendpacket[34] == 0x54 && + sendpacket[35] == 0x53) { + return UDP_DRVSTATS_TYPE; + } else + return UDP_INVALID_TYPE; + } else + return UDP_INVALID_TYPE; +} + /****** End *****************************************************************/ diff --git a/drivers/net/sdla_x25.c b/drivers/net/sdla_x25.c index 15844a3ab..6a649a2d2 100644 --- a/drivers/net/sdla_x25.c +++ b/drivers/net/sdla_x25.c @@ -10,6 +10,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Mar 15, 1998 Alan Cox o 2.1.x porting * Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs * when they are disabled. * Nov 17, 1997 Farhan Thawar o Added IPX support @@ -41,6 +42,7 @@ #error This code MUST be compiled as a kernel module! #endif +#include <linux/config.h> /* OS configuration options */ #include <linux/kernel.h> /* printk(), and other useful stuff */ #include <linux/stddef.h> /* offsetof(), etc. */ #include <linux/errno.h> /* return codes */ @@ -48,9 +50,8 @@ #include <linux/malloc.h> /* kmalloc(), kfree() */ #include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ -#include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ -#include <asm/uaccess.h> /* copy_from_user, etc */ +#include <asm/uaccess.h> #define _GNUC_ #include <linux/sdla_x25.h> /* X.25 firmware API definitions */ @@ -132,7 +133,7 @@ static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); -static struct enet_statistics* if_stats (struct device* dev); +static struct net_device_stats * if_stats (struct device* dev); /* Interrupt handlers */ static void wpx_isr (sdla_t* card); @@ -211,7 +212,7 @@ extern void enable_irq(unsigned int); * Return: 0 o.k. * < 0 failure. */ -__initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) +int wpx_init (sdla_t* card, wandev_conf_t* conf) { union { @@ -372,14 +373,11 @@ static int update (wan_device_t* wandev) /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) - return -EFAULT - ; + return -EFAULT; if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV - ; + return -ENODEV; if (test_and_set_bit(0, (void*)&wandev->critical)) - return -EAGAIN - ; + return -EAGAIN; card = wandev->private; x25_get_err_stats(card); @@ -476,7 +474,6 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) /*============================================================================ * Delete logical channel. */ - static int del_if (wan_device_t* wandev, struct device* dev) { if (dev->priv) @@ -502,18 +499,21 @@ static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) return -EFAULT; - + /* execute command */ + do { memcpy(&mbox->cmd, &cmd, sizeof(cmd)); if (cmd.length) + { if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) - return -EFAULT; + return-EFAULT; + } if (sdla_exec(mbox)) - err = mbox->cmd.result; - else - return -EIO; + err = mbox->cmd.result + ; + else return -EIO; } while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn)); @@ -521,9 +521,8 @@ static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd))) return -EFAULT; len = mbox->cmd.length; - if (len && u_data) - if(copy_to_user(u_data, (void*)&mbox->data, len)) - return -EFAULT; + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; return 0; } @@ -541,7 +540,6 @@ static int if_init (struct device* dev) x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; wan_device_t* wandev = &card->wandev; - int i; /* Initialize device driver entry points */ dev->open = &if_open; @@ -552,14 +550,12 @@ static int if_init (struct device* dev) dev->get_stats = &if_stats; /* Initialize media-specific parameters */ - dev->family = AF_INET; /* address family */ dev->type = 30; /* ARP h/w type */ dev->mtu = X25_CHAN_MTU; dev->hard_header_len = X25_HRDHDR_SZ; /* media header length */ dev->addr_len = 2; /* hardware address length */ if (!chan->svc) - *(unsigned short*)dev->dev_addr = htons(chan->lcn) - ; + *(unsigned short*)dev->dev_addr = htons(chan->lcn); /* Initialize hardware parameters (just for reference) */ dev->irq = wandev->irq; @@ -572,8 +568,8 @@ static int if_init (struct device* dev) dev->tx_queue_len = 10; /* Initialize socket buffers */ - dev_init_buffers(dev); + dev_init_buffers(dev); set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -591,11 +587,10 @@ static int if_open (struct device* dev) sdla_t* card = chan->card; if (dev->start) - return -EBUSY /* only one open is allowed */ - ; + return -EBUSY; /* only one open is allowed */ + if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; - ; dev->interrupt = 0; dev->tbusy = 0; @@ -604,8 +599,7 @@ static int if_open (struct device* dev) /* If this is the first open, initiate physical connection */ if (card->open_cnt == 1) - connect(card) - ; + connect(card); card->wandev.critical = 0; return 0; } @@ -622,17 +616,17 @@ static int if_close (struct device* dev) if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; - ; + dev->start = 0; if ((chan->state == WAN_CONNECTED) || (chan->state == WAN_CONNECTING)) - chan_disc(dev) - ; + chan_disc(dev); + wanpipe_close(card); /* If this is the last close, disconnect physical link */ if (!card->open_cnt) - disconnect(card) - ; + disconnect(card); + card->wandev.critical = 0; return 0; } @@ -672,14 +666,15 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ + static int if_rebuild_hdr (struct sk_buff* skb) { - x25_channel_t* chan = skb->dev->priv; + struct device *dev=skb->dev; + x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, skb->dev->name) - ; + card->devname, dev->name); return 1; } @@ -700,6 +695,7 @@ static int if_rebuild_hdr (struct sk_buff* skb) * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ + static int if_send (struct sk_buff* skb, struct device* dev) { x25_channel_t* chan = dev->priv; @@ -708,20 +704,6 @@ static int if_send (struct sk_buff* skb, struct device* dev) TX25Status* status = card->flags; unsigned long host_cpu_flags; - if (skb == NULL) - { - /* If we get here, some higher layer thinks we've missed a - * tx-done interrupt. - */ -#ifdef _DEBUG_ - printk(KERN_INFO "%s: interface %s got kicked!\n", - card->devname, dev->name) - ; -#endif - dev_tint(dev); - return 0; - } - if (dev->tbusy) { ++chan->ifstats.rx_dropped; @@ -742,7 +724,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) disable_irq(card->hw.irq); ++card->irq_dis_if_send_count; - if (set_bit(0, (void*)&card->wandev.critical)) + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { printk(KERN_INFO "Hit critical in if_send()!\n"); if (card->wandev.critical == CRITICAL_IN_ISR) @@ -773,85 +755,81 @@ static int if_send (struct sk_buff* skb, struct device* dev) /* Below is only until we have per-channel IPX going.... */ if(!(chan->svc)) - { chan->protocol = skb->protocol; - } if (card->wandev.state != WAN_CONNECTED) - { - ++chan->ifstats.tx_dropped - ; - } + ++chan->ifstats.tx_dropped; + /* Below is only until we have per-channel IPX going.... */ else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol))) { printk(KERN_INFO "%s: unsupported Ethertype 0x%04X on interface %s!\n", - card->devname, skb->protocol, dev->name) - ; + card->devname, skb->protocol, dev->name); ++chan->ifstats.tx_errors; } else switch (chan->state) { - case WAN_DISCONNECTED: - /* Try to establish connection. If succeded, then start - * transmission, else drop a packet. - */ - if (chan_connect(dev) != 0) - { - ++chan->ifstats.tx_dropped; - ++card->wandev.stats.tx_dropped; - break; - } - /* fall through */ - - case WAN_CONNECTED: - if( skb->protocol == ETH_P_IPX ) { - if(card->wandev.enable_IPX) { - switch_net_numbers( skb->data, - card->wandev.network_number, 0); - } else { - ++card->wandev.stats.tx_dropped; + case WAN_DISCONNECTED: + /* Try to establish connection. If succeded, then start + * transmission, else drop a packet. + */ + if (chan_connect(dev) != 0) + { ++chan->ifstats.tx_dropped; - goto tx_done; + ++card->wandev.stats.tx_dropped; + break; } - } - dev->trans_start = jiffies; - if(chan_send(dev, skb)) - { - dev->tbusy = 1; - status->imask |= 0x2; - } - break; + /* fall through */ - default: - ++chan->ifstats.tx_dropped; - ++card->wandev.stats.tx_dropped; - } + case WAN_CONNECTED: + if( skb->protocol == ETH_P_IPX ) + { + if(card->wandev.enable_IPX) + { + switch_net_numbers( skb->data, + card->wandev.network_number, 0); + } + else + { + ++card->wandev.stats.tx_dropped; + ++chan->ifstats.tx_dropped; + goto tx_done; + } + } + dev->trans_start = jiffies; + if(chan_send(dev, skb)) + { + dev->tbusy = 1; + status->imask |= 0x2; + } + break; + default: + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + } tx_done: if (!dev->tbusy) - { dev_kfree_skb(skb); - } + card->wandev.critical = 0; save_flags(host_cpu_flags); cli(); if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) enable_irq(card->hw.irq); restore_flags(host_cpu_flags); - return dev->tbusy; } /*============================================================================ * Get ethernet-style interface statistics. - * Return a pointer to struct enet_statistics. + * Return a pointer to struct net_device_stats */ -static struct enet_statistics* if_stats (struct device* dev) + +static struct net_device_stats* if_stats (struct device* dev) { x25_channel_t* chan = dev->priv; - return &chan->ifstats; } @@ -860,6 +838,7 @@ static struct enet_statistics* if_stats (struct device* dev) /*============================================================================ * X.25 Interrupt Service Routine. */ + static void wpx_isr (sdla_t* card) { TX25Status* status = card->flags; @@ -869,7 +848,8 @@ static void wpx_isr (sdla_t* card) card->in_isr = 1; card->buff_int_mode_unbusy = 0; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + if (test_and_set_bit(0, (void*)&card->wandev.critical)) + { printk(KERN_INFO "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags); card->in_isr = 0; @@ -884,28 +864,27 @@ static void wpx_isr (sdla_t* card) switch (status->iflags) { - case 0x01: /* receive interrupt */ - rx_intr(card); - break; + case 0x01: /* receive interrupt */ + rx_intr(card); + break; - case 0x02: /* transmit interrupt */ - tx_intr(card); - card->buff_int_mode_unbusy = 1; - status->imask &= ~0x2; - break; + case 0x02: /* transmit interrupt */ + tx_intr(card); + card->buff_int_mode_unbusy = 1; + status->imask &= ~0x2; + break; - case 0x04: /* modem status interrupt */ - status_intr(card); - break; + case 0x04: /* modem status interrupt */ + status_intr(card); + break; - case 0x10: /* network event interrupt */ - event_intr(card); - break; + case 0x10: /* network event interrupt */ + event_intr(card); + break; - default: /* unwanted interrupt */ - spur_intr(card); + default: /* unwanted interrupt */ + spur_intr(card); } - card->wandev.critical = CRITICAL_INTR_HANDLED; if( card->wandev.enable_tx_int) { @@ -925,7 +904,8 @@ static void wpx_isr (sdla_t* card) { if(((x25_channel_t*)dev->priv)->devtint) { - dev_tint(dev); + mark_bh(NET_BH); + return; } } } @@ -947,6 +927,7 @@ static void wpx_isr (sdla_t* card) * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no * socket buffers available) the whole packet sequence must be discarded. */ + static void rx_intr (sdla_t* card) { TX25Mbox* rxmb = card->rxmb; @@ -963,8 +944,7 @@ static void rx_intr (sdla_t* card) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n", - card->devname, lcn) - ; + card->devname, lcn); return; } @@ -986,8 +966,7 @@ static void rx_intr (sdla_t* card) if (skb == NULL) { printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname) - ; + card->devname); chan->drop_sequence = 1; /* set flag */ ++chan->ifstats.rx_dropped; return; @@ -1005,8 +984,7 @@ static void rx_intr (sdla_t* card) if (qdm & 0x01) chan->drop_sequence = 1; printk(KERN_INFO "%s: unexpectedly long packet sequence " - "on interface %s!\n", card->devname, dev->name) - ; + "on interface %s!\n", card->devname, dev->name); ++chan->ifstats.rx_length_errors; return; } @@ -1014,7 +992,9 @@ static void rx_intr (sdla_t* card) /* Append packet to the socket buffer */ bufptr = skb_put(skb, len); memcpy(bufptr, rxmb->data, len); - if (qdm & 0x01) return; /* more data is comming */ + + if (qdm & 0x01) + return; /* more data is comming */ dev->last_rx = jiffies; /* timestamp */ chan->rx_skb = NULL; /* dequeue packet */ @@ -1043,7 +1023,7 @@ static void rx_intr (sdla_t* card) } else { - /* increment IPX packet dropped statistic */ + /* FIXME: increment IPX packet dropped statistic */ } } else @@ -1059,11 +1039,11 @@ static void rx_intr (sdla_t* card) * o Release socket buffer * o Clear 'tbusy' flag */ + static void tx_intr (sdla_t* card) { struct device *dev; - /* unbusy all devices and then dev_tint(); */ for(dev = card->wandev.dev; dev; dev = dev->slave) { @@ -1102,13 +1082,14 @@ static void spur_intr (sdla_t* card) /*============================================================================ * Main polling routine. - * This routine is repeatedly called by the WANPIPE 'thead' to allow for + * This routine is repeatedly called by the WANPIPE 'thread' to allow for * time-dependent housekeeping work. * * Notes: * 1. This routine may be called on interrupt context with all interrupts * enabled. Beware! */ + static void wpx_poll (sdla_t* card) { unsigned long host_cpu_flags; @@ -1116,42 +1097,37 @@ static void wpx_poll (sdla_t* card) disable_irq(card->hw.irq); ++card->irq_dis_poll_count; - if (set_bit(0, (void*)&card->wandev.critical)) { - - printk(KERN_INFO "%s: critical in polling!\n",card->devname); - + if (test_and_set_bit(0, (void*)&card->wandev.critical)) + { + printk(KERN_INFO "%s: critical in polling!\n",card->devname); save_flags(host_cpu_flags); cli(); if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) enable_irq(card->hw.irq); restore_flags(host_cpu_flags); - return; } switch(card->wandev.state) { - case WAN_CONNECTED: - poll_active(card); - break; + case WAN_CONNECTED: + poll_active(card); + break; - case WAN_CONNECTING: - poll_connecting(card); - break; + case WAN_CONNECTING: + poll_connecting(card); + break; - case WAN_DISCONNECTED: - poll_disconnected(card); + case WAN_DISCONNECTED: + poll_disconnected(card); } - card->wandev.critical = 0; - save_flags(host_cpu_flags); cli(); if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) enable_irq(card->hw.irq); restore_flags(host_cpu_flags); - } /*============================================================================ @@ -1169,8 +1145,7 @@ static void poll_connecting (sdla_t* card) status->imask &= ~0x2; /* mask Tx interupts */ } else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT) - disconnect(card) - ; + disconnect(card); } /*============================================================================ @@ -1181,8 +1156,7 @@ static void poll_connecting (sdla_t* card) static void poll_disconnected (sdla_t* card) { if (card->open_cnt && ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) - connect(card) - ; + connect(card); } /*============================================================================ @@ -1220,7 +1194,7 @@ static void poll_active (sdla_t* card) { if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout ) { - //Close svc + /* Close svc */ printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn); chan->i_timeout_sofar = jiffies; chan_disc(dev); @@ -1255,13 +1229,11 @@ static int x25_get_version (sdla_t* card, char* str) mbox->cmd.command = X25_READ_CODE_VERSION; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && - x25_error(card, err, X25_READ_CODE_VERSION, 0)) - ; + x25_error(card, err, X25_READ_CODE_VERSION, 0)); if (!err && str) { int len = mbox->cmd.length; - memcpy(str, mbox->data, len); str[len] = '\0'; } @@ -1271,6 +1243,7 @@ static int x25_get_version (sdla_t* card, char* str) /*============================================================================ * Configure adapter. */ + static int x25_configure (sdla_t* card, TX25Config* conf) { TX25Mbox* mbox = card->mbox; @@ -1284,9 +1257,7 @@ static int x25_configure (sdla_t* card, TX25Config* conf) mbox->cmd.length = sizeof(TX25Config); mbox->cmd.command = X25_SET_CONFIGURATION; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_SET_CONFIGURATION, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0)); return err; } @@ -1304,9 +1275,8 @@ static int x25_get_err_stats (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_HDLC_READ_COMM_ERR; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0)); + if (!err) { THdlcCommErr* stats = (void*)mbox->data; @@ -1333,9 +1303,8 @@ static int x25_get_stats (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_READ_STATISTICS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_READ_STATISTICS, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0)); + if (!err) { TX25Stats* stats = (void*)mbox->data; @@ -1360,9 +1329,8 @@ static int x25_close_hdlc (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_HDLC_LINK_CLOSE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_HDLC_LINK_CLOSE, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_CLOSE, 0)); + return err; } @@ -1380,9 +1348,8 @@ static int x25_open_hdlc (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_HDLC_LINK_OPEN; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_HDLC_LINK_OPEN, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_OPEN, 0)); + return err; } @@ -1400,9 +1367,8 @@ static int x25_setup_hdlc (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_HDLC_LINK_SETUP; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_HDLC_LINK_SETUP, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_SETUP, 0)); + return err; } @@ -1424,9 +1390,8 @@ static int x25_set_dtr (sdla_t* card, int dtr) mbox->cmd.length = 3; mbox->cmd.command = X25_SET_GLOBAL_VARS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_SET_GLOBAL_VARS, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_SET_GLOBAL_VARS, 0)); + return err; } @@ -1451,9 +1416,7 @@ static int x25_set_intr_mode (sdla_t* card, int mode) else mbox->cmd.length = 1; mbox->cmd.command = X25_SET_INTERRUPT_MODE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_SET_INTERRUPT_MODE, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0)) ; return err; } @@ -1473,9 +1436,7 @@ static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan) mbox->cmd.lcn = lcn; mbox->cmd.command = X25_READ_CHANNEL_CONFIG; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn)) - ; + } while (err && retry-- && x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn)); if (!err) { @@ -1483,19 +1444,23 @@ static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan) /* calculate an offset into the array of status bytes */ if (card->u.x.hi_svc <= 255) - chan->ch_idx = lcn - 1 - ; + chan->ch_idx = lcn - 1; else { int offset; switch (mbox->data[0] && 0x1F) { - case 0x01: offset = status->pvc_map; break; - case 0x03: offset = status->icc_map; break; - case 0x07: offset = status->twc_map; break; - case 0x0B: offset = status->ogc_map; break; - default: offset = 0; + case 0x01: + offset = status->pvc_map; break; + case 0x03: + offset = status->icc_map; break; + case 0x07: + offset = status->twc_map; break; + case 0x0B: + offset = status->ogc_map; break; + default: + offset = 0; } chan->ch_idx = lcn - 1 - offset; } @@ -1503,17 +1468,30 @@ static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan) /* get actual transmit packet size on this channel */ switch(mbox->data[1] & 0x38) { - case 0x00: chan->tx_pkt_size = 16; break; - case 0x08: chan->tx_pkt_size = 32; break; - case 0x10: chan->tx_pkt_size = 64; break; - case 0x18: chan->tx_pkt_size = 128; break; - case 0x20: chan->tx_pkt_size = 256; break; - case 0x28: chan->tx_pkt_size = 512; break; - case 0x30: chan->tx_pkt_size = 1024; break; + case 0x00: + chan->tx_pkt_size = 16; + break; + case 0x08: + chan->tx_pkt_size = 32; + break; + case 0x10: + chan->tx_pkt_size = 64; + break; + case 0x18: + chan->tx_pkt_size = 128; + break; + case 0x20: + chan->tx_pkt_size = 256; + break; + case 0x28: + chan->tx_pkt_size = 512; + break; + case 0x30: + chan->tx_pkt_size = 1024; + break; } printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n", - card->devname, lcn, chan->tx_pkt_size) - ; + card->devname, lcn, chan->tx_pkt_size); } return err; } @@ -1521,6 +1499,7 @@ static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan) /*============================================================================ * Place X.25 call. */ + static int x25_place_call (sdla_t* card, x25_channel_t* chan) { TX25Mbox* mbox = card->mbox; @@ -1536,9 +1515,8 @@ static int x25_place_call (sdla_t* card, x25_channel_t* chan) mbox->cmd.length = strlen(str); mbox->cmd.command = X25_PLACE_CALL; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_PLACE_CALL, 0)) - ; + } while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0)); + if (!err) { chan->lcn = mbox->cmd.lcn; @@ -1550,6 +1528,7 @@ static int x25_place_call (sdla_t* card, x25_channel_t* chan) /*============================================================================ * Accept X.25 call. */ + static int x25_accept_call (sdla_t* card, int lcn, int qdm) { TX25Mbox* mbox = card->mbox; @@ -1563,9 +1542,8 @@ static int x25_accept_call (sdla_t* card, int lcn, int qdm) mbox->cmd.qdm = qdm; mbox->cmd.command = X25_ACCEPT_CALL; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_ACCEPT_CALL, lcn)) - ; + } while (err && retry-- && x25_error(card, err, X25_ACCEPT_CALL, lcn)); + return err; } @@ -1586,9 +1564,8 @@ static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn) mbox->cmd.diagn = diagn; mbox->cmd.command = X25_CLEAR_CALL; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } while (err && retry-- && - x25_error(card, err, X25_CLEAR_CALL, lcn)) - ; + } while (err && retry-- && x25_error(card, err, X25_CLEAR_CALL, lcn)); + return err; } @@ -1600,7 +1577,7 @@ static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf) TX25Mbox* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - + do { memset(&mbox->cmd, 0, sizeof(TX25Cmd)); @@ -1628,7 +1605,8 @@ static int x25_fetch_events (sdla_t* card) memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_IS_DATA_AVAILABLE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - if (err) x25_error(card, err, X25_IS_DATA_AVAILABLE, 0); + if (err) + x25_error(card, err, X25_IS_DATA_AVAILABLE, 0); } return err; } @@ -1656,99 +1634,90 @@ static int x25_error (sdla_t* card, int err, int cmd, int lcn) if (mb == NULL) { printk(KERN_ERR "%s: x25_error() out of memory!\n", - card->devname) - ; + card->devname); return 0; } memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen); switch (err) { - case 0x40: /* X.25 asynchronous packet was received */ - mb->data[dlen] = '\0'; - switch (mb->cmd.pktType & 0x7F) - { - case 0x30: /* incomming call */ - retry = incomming_call(card, cmd, lcn, mb); + case 0x40: /* X.25 asynchronous packet was received */ + mb->data[dlen] = '\0'; + switch (mb->cmd.pktType & 0x7F) + { + case 0x30: /* incomming call */ + retry = incomming_call(card, cmd, lcn, mb); + break; + + case 0x31: /* connected */ + retry = call_accepted(card, cmd, lcn, mb); + break; + + case 0x02: /* call clear request */ + retry = call_cleared(card, cmd, lcn, mb); + break; + + case 0x04: /* reset request */ + printk(KERN_INFO "%s: X.25 reset request on LCN %d! " + "Cause:0x%02X Diagn:0x%02X\n", + card->devname, mb->cmd.lcn, mb->cmd.cause, + mb->cmd.diagn); + break; + + case 0x08: /* restart request */ + retry = restart_event(card, cmd, lcn, mb); + break; + + default: + printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! " + "Cause:0x%02X Diagn:0x%02X\n", + card->devname, mb->cmd.pktType, + mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn); + } break; - case 0x31: /* connected */ - retry = call_accepted(card, cmd, lcn, mb); + case 0x41: /* X.25 protocol violation indication */ + printk(KERN_INFO + "%s: X.25 protocol violation on LCN %d! " + "Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n", + card->devname, mb->cmd.lcn, + mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn); break; - case 0x02: /* call clear request */ - retry = call_cleared(card, cmd, lcn, mb); + case 0x42: /* X.25 timeout */ + retry = timeout_event(card, cmd, lcn, mb); break; - case 0x04: /* reset request */ - printk(KERN_INFO "%s: X.25 reset request on LCN %d! " - "Cause:0x%02X Diagn:0x%02X\n", - card->devname, mb->cmd.lcn, mb->cmd.cause, - mb->cmd.diagn) - ; + case 0x43: /* X.25 retry limit exceeded */ + printk(KERN_INFO + "%s: exceeded X.25 retry limit on LCN %d! " + "Packet:0x%02X Diagn:0x%02X\n", card->devname, + mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn); break; - case 0x08: /* restart request */ - retry = restart_event(card, cmd, lcn, mb); + case 0x08: /* modem failure */ + printk(KERN_INFO "%s: modem failure!\n", card->devname); break; - default: - printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! " - "Cause:0x%02X Diagn:0x%02X\n", - card->devname, mb->cmd.pktType, - mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn) - ; - } - break; - - case 0x41: /* X.25 protocol violation indication */ - printk(KERN_INFO - "%s: X.25 protocol violation on LCN %d! " - "Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n", - card->devname, mb->cmd.lcn, - mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn) - ; - break; - - case 0x42: /* X.25 timeout */ - retry = timeout_event(card, cmd, lcn, mb); - break; - - case 0x43: /* X.25 retry limit exceeded */ - printk(KERN_INFO - "%s: exceeded X.25 retry limit on LCN %d! " - "Packet:0x%02X Diagn:0x%02X\n", card->devname, - mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn) - ; - break; - - case 0x08: /* modem failure */ - printk(KERN_INFO "%s: modem failure!\n", card->devname); - break; - - case 0x09: /* N2 retry limit */ - printk(KERN_INFO "%s: exceeded HDLC retry limit!\n", - card->devname) - ; - break; + case 0x09: /* N2 retry limit */ + printk(KERN_INFO "%s: exceeded HDLC retry limit!\n", + card->devname); + break; - case 0x06: /* unnumbered frame was received while in ABM */ - printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n", - card->devname, mb->data[0]) - ; - break; + case 0x06: /* unnumbered frame was received while in ABM */ + printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n", + card->devname, mb->data[0]); + break; - case CMD_TIMEOUT: - printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, cmd) - ; - retry = 0; /* abort command */ - break; + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, cmd); + retry = 0; /* abort command */ + break; - default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", - card->devname, cmd, err) - ; - retry = 0; /* abort command */ + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", + card->devname, cmd, err); + retry = 0; /* abort command */ } kfree(mb); return retry; @@ -1772,6 +1741,7 @@ static int x25_error (sdla_t* card, int err, int cmd, int lcn) * (i.e. call collision has occured), the incomming call shall be * rejected and call request shall be retried. */ + static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { wan_device_t* wandev = &card->wandev; @@ -1786,8 +1756,7 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { printk(KERN_INFO "%s: X.25 incomming call collision on LCN %d!\n", - card->devname, new_lcn) - ; + card->devname, new_lcn); x25_clear_call(card, new_lcn, 0, 0); return 1; } @@ -1797,8 +1766,7 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { printk(KERN_INFO "%s: X.25 incomming call on LCN %d with D-bit set!\n", - card->devname, new_lcn) - ; + card->devname, new_lcn); x25_clear_call(card, new_lcn, 0, 0); return 1; } @@ -1815,8 +1783,7 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) } parse_call_info(mb->data, info); printk(KERN_INFO "%s: X.25 incomming call on LCN %d! Call data: %s\n", - card->devname, new_lcn, mb->data) - ; + card->devname, new_lcn, mb->data); /* Find available channel */ for (dev = wandev->dev; dev; dev = dev->slave) @@ -1824,22 +1791,18 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) chan = dev->priv; if (!chan->svc || (chan->state != WAN_DISCONNECTED)) - continue - ; + continue; if (strcmp(info->src, chan->addr) == 0) - break - ; - // If just an '@' is specified, accept all incomming calls + break; + /* If just an '@' is specified, accept all incomming calls */ if (strcmp(chan->addr, "") == 0) - break - ; + break; } if (dev == NULL) { printk(KERN_INFO "%s: no channels available!\n", - card->devname) - ; + card->devname); x25_clear_call(card, new_lcn, 0, 0); } @@ -1848,41 +1811,39 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { printk(KERN_INFO "%s: no user data in incomming call on LCN %d!\n", - card->devname, new_lcn) - ; + card->devname, new_lcn); x25_clear_call(card, new_lcn, 0, 0); } else switch (info->user[0]) { - case 0: /* multiplexed */ - chan->protocol = 0; - accept = 1; - break; + case 0: /* multiplexed */ + chan->protocol = 0; + accept = 1; + break; - case NLPID_IP: /* IP datagrams */ - chan->protocol = ETH_P_IP; - accept = 1; - break; - - case NLPID_SNAP: /* IPX datagrams */ - chan->protocol = ETH_P_IPX; - accept = 1; - break; - default: - printk(KERN_INFO - "%s: unsupported NLPID 0x%02X in incomming call " - "on LCN %d!\n", card->devname, info->user[0], new_lcn) - ; - x25_clear_call(card, new_lcn, 0, 249); + case NLPID_IP: /* IP datagrams */ + chan->protocol = ETH_P_IP; + accept = 1; + break; + + case NLPID_SNAP: /* IPX datagrams */ + chan->protocol = ETH_P_IPX; + accept = 1; + break; + default: + printk(KERN_INFO + "%s: unsupported NLPID 0x%02X in incomming call " + "on LCN %d!\n", card->devname, info->user[0], new_lcn); + x25_clear_call(card, new_lcn, 0, 249); } if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK)) { chan->lcn = new_lcn; if (x25_get_chan_conf(card, chan) == CMD_OK) - set_chan_state(dev, WAN_CONNECTED) - ; - else x25_clear_call(card, new_lcn, 0, 0); + set_chan_state(dev, WAN_CONNECTED); + else + x25_clear_call(card, new_lcn, 0, 0); } kfree(info); return 1; @@ -1891,6 +1852,7 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) /*============================================================================ * Handle accepted call. */ + static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { unsigned new_lcn = mb->cmd.lcn; @@ -1898,14 +1860,12 @@ static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) x25_channel_t* chan; printk(KERN_INFO "%s: X.25 call accepted on LCN %d!\n", - card->devname, new_lcn) - ; + card->devname, new_lcn); if (dev == NULL) { printk(KERN_INFO "%s: clearing orphaned connection on LCN %d!\n", - card->devname, new_lcn) - ; + card->devname, new_lcn); x25_clear_call(card, new_lcn, 0, 0); return 1; } @@ -1924,6 +1884,7 @@ static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) /*============================================================================ * Handle cleared call. */ + static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { unsigned new_lcn = mb->cmd.lcn; @@ -1931,17 +1892,17 @@ static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X " "Diagn:0x%02X\n", - card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn) - ; - if (dev == NULL) return 1; + card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn); + if (dev == NULL) + return 1; set_chan_state(dev, WAN_DISCONNECTED); - return ((cmd == X25_WRITE) && (lcn == new_lcn)) ? 0 : 1; } /*============================================================================ * Handle X.25 restart event. */ + static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) { wan_device_t* wandev = &card->wandev; @@ -1949,13 +1910,11 @@ static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) printk(KERN_INFO "%s: X.25 restart request! Cause:0x%02X Diagn:0x%02X\n", - card->devname, mb->cmd.cause, mb->cmd.diagn) - ; + card->devname, mb->cmd.cause, mb->cmd.diagn); /* down all logical channels */ for (dev = wandev->dev; dev; dev = dev->slave) - set_chan_state(dev, WAN_DISCONNECTED) - ; + set_chan_state(dev, WAN_DISCONNECTED); return (cmd == X25_WRITE) ? 0 : 1; } @@ -1971,13 +1930,12 @@ static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) struct device* dev = get_dev_by_lcn(&card->wandev, new_lcn); printk(KERN_INFO "%s: X.25 call timed timeout on LCN %d!\n", - card->devname, new_lcn) - ; - if (dev) set_chan_state(dev, WAN_DISCONNECTED); + card->devname, new_lcn); + if (dev) + set_chan_state(dev, WAN_DISCONNECTED); } else printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n", - card->devname, mb->cmd.pktType, new_lcn) - ; + card->devname, mb->cmd.pktType, new_lcn); return 1; } @@ -1994,8 +1952,7 @@ static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) static int connect (sdla_t* card) { if (x25_open_hdlc(card) || x25_setup_hdlc(card)) - return -EIO - ; + return -EIO; wanpipe_set_state(card, WAN_CONNECTING); return 1; } @@ -2025,8 +1982,8 @@ static struct device* get_dev_by_lcn (wan_device_t* wandev, unsigned lcn) struct device* dev; for (dev = wandev->dev; dev; dev = dev->slave) - if (((x25_channel_t*)dev->priv)->lcn == lcn) break - ; + if (((x25_channel_t*)dev->priv)->lcn == lcn) + break; return dev; } @@ -2047,22 +2004,18 @@ static int chan_connect (struct device* dev) if (chan->svc) { if (!chan->addr[0]) - return -EINVAL /* no destination address */ - ; + return -EINVAL; /* no destination address */ printk(KERN_INFO "%s: placing X.25 call to %s ...\n", - card->devname, chan->addr) - ; + card->devname, chan->addr); if (x25_place_call(card, chan) != CMD_OK) - return -EIO - ; + return -EIO; set_chan_state(dev, WAN_CONNECTING); return 1; } else { if (x25_get_chan_conf(card, chan) != CMD_OK) - return -EIO - ; + return -EIO; set_chan_state(dev, WAN_CONNECTED); } return 0; @@ -2076,7 +2029,8 @@ static int chan_disc (struct device* dev) { x25_channel_t* chan = dev->priv; - if (chan->svc) x25_clear_call(chan->card, chan->lcn, 0, 0); + if (chan->svc) + x25_clear_call(chan->card, chan->lcn, 0, 0); set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -2096,29 +2050,27 @@ static void set_chan_state (struct device* dev, int state) { switch (state) { - case WAN_CONNECTED: - printk (KERN_INFO "%s: interface %s connected!\n", - card->devname, dev->name) - ; - *(unsigned short*)dev->dev_addr = htons(chan->lcn); - chan->i_timeout_sofar = jiffies; - break; + case WAN_CONNECTED: + printk (KERN_INFO "%s: interface %s connected!\n", + card->devname, dev->name); + *(unsigned short*)dev->dev_addr = htons(chan->lcn); + chan->i_timeout_sofar = jiffies; + break; - case WAN_CONNECTING: - printk (KERN_INFO "%s: interface %s connecting...\n", - card->devname, dev->name) - ; - break; + case WAN_CONNECTING: + printk (KERN_INFO "%s: interface %s connecting...\n", + card->devname, dev->name); + break; - case WAN_DISCONNECTED: - printk (KERN_INFO "%s: interface %s disconnected!\n", - card->devname, dev->name) - ; - if (chan->svc) { - *(unsigned short*)dev->dev_addr = 0; - chan->lcn = 0; - } - break; + case WAN_DISCONNECTED: + printk (KERN_INFO "%s: interface %s disconnected!\n", + card->devname, dev->name); + if (chan->svc) + { + *(unsigned short*)dev->dev_addr = 0; + chan->lcn = 0; + } + break; } chan->state = state; } @@ -2150,8 +2102,8 @@ static int chan_send (struct device* dev, struct sk_buff* skb) /* Check to see if channel is ready */ if (!(status->cflags[chan->ch_idx] & 0x40)) - return 1 - ; + return 1; + if (skb->len > chan->tx_pkt_size) { len = chan->tx_pkt_size; @@ -2164,22 +2116,22 @@ static int chan_send (struct device* dev, struct sk_buff* skb) } switch(x25_send(card, chan->lcn, qdm, len, skb->data)) { - case 0x00: /* success */ - chan->i_timeout_sofar = jiffies; - if (qdm) - { - skb_pull(skb, len); - return 1; - } - ++chan->ifstats.tx_packets; - break; + case 0x00: /* success */ + chan->i_timeout_sofar = jiffies; + if (qdm) + { + skb_pull(skb, len); + return 1; + } + ++chan->ifstats.tx_packets; + break; - case 0x33: /* Tx busy */ - return 1; + case 0x33: /* Tx busy */ + return 1; - default: /* failure */ - ++chan->ifstats.tx_errors; -/* return 1; */ + default: /* failure */ + ++chan->ifstats.tx_errors; +/* return 1; */ } return 0; } @@ -2187,6 +2139,7 @@ static int chan_send (struct device* dev, struct sk_buff* skb) /*============================================================================ * Parse X.25 call request data and fill x25_call_info_t structure. */ + static void parse_call_info (unsigned char* str, x25_call_info_t* info) { memset(info, 0, sizeof(x25_call_info_t)); @@ -2197,50 +2150,53 @@ static void parse_call_info (unsigned char* str, x25_call_info_t* info) if (*str == '-') switch (str[1]) { - case 'd': /* destination address */ - for (i = 0; i < 16; ++i) - { - ch = str[2+i]; - if (!is_digit(ch)) break; - info->dest[i] = ch; - } - break; - - case 's': /* source address */ - for (i = 0; i < 16; ++i) - { - ch = str[2+i]; - if (!is_digit(ch)) break; - info->src[i] = ch; - } - break; + case 'd': /* destination address */ + for (i = 0; i < 16; ++i) + { + ch = str[2+i]; + if (!is_digit(ch)) + break; + info->dest[i] = ch; + } + break; + + case 's': /* source address */ + for (i = 0; i < 16; ++i) + { + ch = str[2+i]; + if (!is_digit(ch)) + break; + info->src[i] = ch; + } + break; - case 'u': /* user data */ - for (i = 0; i < 127; ++i) - { - ch = str[2+2*i]; - if (!is_hex_digit(ch)) break; - info->user[i] = hex_to_uint(&str[2+2*i], 2); - } - info->nuser = i; - break; + case 'u': /* user data */ + for (i = 0; i < 127; ++i) + { + ch = str[2+2*i]; + if (!is_hex_digit(ch)) + break; + info->user[i] = hex_to_uint(&str[2+2*i], 2); + } + info->nuser = i; + break; - case 'f': /* facilities */ - for (i = 0; i < 64; ++i) - { - ch = str[2+4*i]; - if (!is_hex_digit(ch)) break; - info->facil[i].code = - hex_to_uint(&str[2+4*i], 2) - ; - ch = str[4+4*i]; - if (!is_hex_digit(ch)) break; - info->facil[i].parm = - hex_to_uint(&str[4+4*i], 2) - ; - } - info->nfacil = i; - break; + case 'f': /* facilities */ + for (i = 0; i < 64; ++i) + { + ch = str[2+4*i]; + if (!is_hex_digit(ch)) + break; + info->facil[i].code = + hex_to_uint(&str[2+4*i], 2); + ch = str[4+4*i]; + if (!is_hex_digit(ch)) + break; + info->facil[i].parm = + hex_to_uint(&str[4+4*i], 2); + } + info->nfacil = i; + break; } } } @@ -2279,8 +2235,7 @@ static unsigned int dec_to_uint (unsigned char* str, int len) if (!len) len = strlen(str); for (val = 0; len && is_digit(*str); ++str, --len) - val = (val * 10) + (*str - (unsigned)'0') - ; + val = (val * 10) + (*str - (unsigned)'0'); return val; } @@ -2297,12 +2252,11 @@ static unsigned int hex_to_uint (unsigned char* str, int len) { ch = *str; if (is_digit(ch)) - val = (val << 4) + (ch - (unsigned)'0') - ; + val = (val << 4) + (ch - (unsigned)'0'); else if (is_hex_digit(ch)) - val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10) - ; - else break; + val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10); + else + break; } return val; } @@ -2334,22 +2288,18 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char /* It's a timer request packet */ printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); - /* Go through the routing options and answer no to every */ - /* option except Unnumbered RIP/SAP */ + /* Go through the routing options and answer no to every + * option except Unnumbered RIP/SAP */ for(i = 41; sendpacket[i] == 0x00; i += 5) { /* 0x02 is the option for Unnumbered RIP/SAP */ if( sendpacket[i + 4] != 0x02) - { sendpacket[i + 1] = 0; - } } /* Skip over the extended Node ID option */ if( sendpacket[i] == 0x04 ) - { i += 8; - } /* We also want to turn off all header compression opt. */ for(; sendpacket[i] == 0x80 ;) @@ -2388,15 +2338,13 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4); sendpacket[65] = CVHexToAscii(network_number & 0x0000000F); for(i = 66; i < 99; i+= 1) - { sendpacket[i] = 0; - } - /* printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); */ + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); } else { - printk(KERN_WARNING "%s: Unknown IPXWAN packet!\n",devname); + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); return 0; } @@ -2408,9 +2356,8 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char return 1; } else { - /* If we get here its an IPX-data packet, so it'll get passed up the stack. */ - - /* switch the network numbers */ + /* If we get here its an IPX-data packet, so it'll get passed up the stack. + switch the network numbers */ switch_net_numbers(sendpacket, network_number, 1); return 0; } @@ -2421,6 +2368,7 @@ static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char if incoming is 1 - if the net number is 0 make it ours */ + static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) { unsigned long pnetwork_number; @@ -2429,15 +2377,20 @@ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_ (sendpacket[7] << 16) + (sendpacket[8] << 8) + sendpacket[9]); - if (!incoming) { + if (!incoming) + { /* If the destination network number is ours, make it 0 */ - if( pnetwork_number == network_number) { + if( pnetwork_number == network_number) + { sendpacket[6] = sendpacket[7] = sendpacket[8] = sendpacket[9] = 0x00; } - } else { + } + else + { /* If the incoming network is 0, make it ours */ - if( pnetwork_number == 0) { + if( pnetwork_number == 0) + { sendpacket[6] = (unsigned char)(network_number >> 24); sendpacket[7] = (unsigned char)((network_number & 0x00FF0000) >> 16); @@ -2453,15 +2406,20 @@ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_ (sendpacket[19] << 16) + (sendpacket[20] << 8) + sendpacket[21]); - if( !incoming ) { + if( !incoming ) + { /* If the source network is ours, make it 0 */ - if( pnetwork_number == network_number) { + if( pnetwork_number == network_number) + { sendpacket[18] = sendpacket[19] = sendpacket[20] = sendpacket[21] = 0x00; } - } else { + } + else + { /* If the source network is 0, make it ours */ - if( pnetwork_number == 0 ) { + if( pnetwork_number == 0 ) + { sendpacket[18] = (unsigned char)(network_number >> 24); sendpacket[19] = (unsigned char)((network_number & 0x00FF0000) >> 16); diff --git a/drivers/net/sdladrv.c b/drivers/net/sdladrv.c index e756d8fdc..3adc0ba8e 100644 --- a/drivers/net/sdladrv.c +++ b/drivers/net/sdladrv.c @@ -324,6 +324,9 @@ void cleanup_module (void) * Return: 0 ok. * < 0 error */ + +EXPORT_SYMBOL(sdla_setup); + int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len) { unsigned* irq_opt = NULL; /* IRQ options */ @@ -449,6 +452,9 @@ int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len) /*============================================================================ * Shut down SDLA: disable shared memory access and interrupts, stop CPU, etc. */ + +EXPORT_SYMBOL(sdla_down); + int sdla_down (sdlahw_t* hw) { unsigned port = hw->port; @@ -491,6 +497,9 @@ int sdla_down (sdlahw_t* hw) /*============================================================================ * Map shared memory window into SDLA adress space. */ + +EXPORT_SYMBOL(sdla_mapmem); + int sdla_mapmem (sdlahw_t* hw, unsigned long addr) { unsigned port = hw->port; @@ -552,6 +561,9 @@ int sdla_mapmem (sdlahw_t* hw, unsigned long addr) /*============================================================================ * Enable interrupt generation. */ + +EXPORT_SYMBOL(sdla_inten); + int sdla_inten (sdlahw_t* hw) { unsigned port = hw->port; @@ -606,6 +618,9 @@ int sdla_inten (sdlahw_t* hw) /*============================================================================ * Disable interrupt generation. */ + +EXPORT_SYMBOL(sdla_intde); + int sdla_intde (sdlahw_t* hw) { unsigned port = hw->port; @@ -662,6 +677,9 @@ int sdla_intde (sdlahw_t* hw) /*============================================================================ * Acknowledge SDLA hardware interrupt. */ + +EXPORT_SYMBOL(sdla_intack); + int sdla_intack (sdlahw_t* hw) { unsigned port = hw->port; @@ -711,6 +729,9 @@ int sdla_intack (sdlahw_t* hw) /*============================================================================ * Generate an interrupt to adapter's CPU. */ + +EXPORT_SYMBOL(sdla_intr); + int sdla_intr (sdlahw_t* hw) { unsigned port = hw->port; @@ -756,6 +777,9 @@ int sdla_intr (sdlahw_t* hw) * o Busy-wait until flag is reset. * o Return number of loops made, or 0 if command timed out. */ + +EXPORT_SYMBOL(sdla_exec); + int sdla_exec (void* opflag) { volatile unsigned char* flag = opflag; @@ -784,6 +808,9 @@ int sdla_exec (void* opflag) * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ + +EXPORT_SYMBOL(sdla_peek); + int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) { unsigned long oldvec = hw->vector; @@ -823,6 +850,9 @@ int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ + +EXPORT_SYMBOL(sdla_poke); + int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) { unsigned long oldvec = hw->vector; @@ -1827,4 +1857,5 @@ static unsigned short checksum (unsigned char* buf, unsigned len) return crc; } + /****** End *****************************************************************/ diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index c66f58e7c..82f75efbb 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -1,7 +1,9 @@ -/* $Id: sgiseeq.c,v 1.5 1997/12/06 23:53:49 ralf Exp $ +/* * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: sgiseeq.c,v 1.3 1997/11/16 13:57:45 alan Exp $ */ #include <linux/kernel.h> @@ -15,6 +17,7 @@ #include <linux/string.h> #include <linux/delay.h> +#include <asm/io.h> #include <asm/segment.h> #include <asm/system.h> #include <asm/bitops.h> @@ -173,7 +176,7 @@ static void seeq_init_ring(struct device *dev) buffer = (unsigned long) kmalloc(PKT_BUF_SZ, GFP_KERNEL); ib->tx_desc[i].buf_vaddr = KSEG1ADDR(buffer); ib->tx_desc[i].tdma.pbuf = PHYSADDR(buffer); - flush_cache_all(); +// flush_cache_all(); } ib->tx_desc[i].tdma.cntinfo = (TCNTINFO_INIT); } @@ -186,7 +189,7 @@ static void seeq_init_ring(struct device *dev) buffer = (unsigned long) kmalloc(PKT_BUF_SZ, GFP_KERNEL); ib->rx_desc[i].buf_vaddr = KSEG1ADDR(buffer); ib->rx_desc[i].rdma.pbuf = PHYSADDR(buffer); - flush_cache_all(); +// flush_cache_all(); } ib->rx_desc[i].rdma.cntinfo = (RCNTINFO_INIT); } @@ -662,15 +665,17 @@ int sgiseeq_init(struct device *dev, struct sgiseeq_regs *sregs, sp->name = sgiseeqstr; sp->srings.rx_desc = (struct sgiseeq_rx_desc *) - (KSEG1ADDR(ALIGNED(&sp->srings.rxvector[0]))); + (KSEG1ADDR(ALIGNED(&sp->srings.rxvector[0]))); + dma_cache_wback_inv((unsigned long)&sp->srings.rxvector, + sizeof(sp->srings.rxvector)); sp->srings.tx_desc = (struct sgiseeq_tx_desc *) - (KSEG1ADDR(ALIGNED(&sp->srings.txvector[0]))); - flush_cache_all(); + (KSEG1ADDR(ALIGNED(&sp->srings.txvector[0]))); + dma_cache_wback_inv((unsigned long)&sp->srings.txvector, + sizeof(sp->srings.txvector)); /* A couple calculations now, saves many cycles later. */ setup_rx_ring(sp->srings.rx_desc, SEEQ_RX_BUFFERS); setup_tx_ring(sp->srings.tx_desc, SEEQ_TX_BUFFERS); - flush_cache_all(); /* Reset the chip. */ hpc3_eth_reset((volatile struct hpc3_ethregs *) hregs); diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c index 6ef0a66f8..4dbaad977 100644 --- a/drivers/net/sk_g16.c +++ b/drivers/net/sk_g16.c @@ -762,17 +762,17 @@ __initfunc(int SK_probe(struct device *dev, short ioaddr)) dev->dev_addr[4], dev->dev_addr[5]); - /* Grab the I/O Port region */ - request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); - - /* Initialize device structure */ - /* Allocate memory for private structure */ p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL); - if (p == NULL) + if (p == NULL) { + printk("%s: ERROR - no memory for driver data!\n", dev->name); return -ENOMEM; + } memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */ + /* Grab the I/O Port region */ + request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); + /* Assign our Device Driver functions */ dev->open = &SK_open; diff --git a/drivers/nubus/Makefile b/drivers/nubus/Makefile new file mode 100644 index 000000000..781edd052 --- /dev/null +++ b/drivers/nubus/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the nubus specific 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. +# + +L_OBJS := nubus.o +L_TARGET := nubus.a + +include $(TOPDIR)/Rules.make diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c new file mode 100644 index 000000000..d266c64b7 --- /dev/null +++ b/drivers/nubus/nubus.c @@ -0,0 +1,629 @@ +/* + * Macintosh Nubus Interface Code + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/nubus.h> +#include <linux/errno.h> +#include <asm/setup.h> +#include <asm/system.h> +#include <asm/page.h> +/* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */ +#include <asm/macintosh.h> + +#undef LCIII_WEIRDNESS + +static struct nubus_slot nubus_slots[16]; + +/* + * Please skip to the bottom of this file if you ate lunch recently + * -- Alan + */ + + + +/* This function tests for the presence of an address, specially a + * hardware register address. It is called very early in the kernel + * initialization process, when the VBR register isn't set up yet. On + * an Atari, it still points to address 0, which is unmapped. So a bus + * error would cause another bus error while fetching the exception + * vector, and the CPU would do nothing at all. So we needed to set up + * a temporary VBR and a vector table for the duration of the test. + * + * See the atari/config.c code we nicked it from for more clues. + */ + +int nubus_hwreg_present( volatile void *regp ) +{ + int ret = 0; + long save_sp, save_vbr; + long tmp_vectors[3]; + unsigned long flags; + + save_flags(flags); + cli(); + + __asm__ __volatile__ + ( "movec %/vbr,%2\n\t" + "movel #Lberr1,%4@(8)\n\t" + "movec %4,%/vbr\n\t" + "movel %/sp,%1\n\t" + "moveq #0,%0\n\t" + "tstb %3@\n\t" + "nop\n\t" + "moveq #1,%0\n" + "Lberr1:\n\t" + "movel %1,%/sp\n\t" + "movec %2,%/vbr" + : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr) + : "a" (regp), "a" (tmp_vectors) + ); + restore_flags(flags); + return( ret ); +} + + + +/* + * Yes this sucks. The ROM can appear on arbitary bytes of the long + * word. We are not amused. + */ + +extern __inline__ int not_useful(void *p, int map) +{ + unsigned long pv=(unsigned long)p; + pv&=3; + if(map&(1<<pv)) + return 0; + return 1; +} + +static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map) +{ + unsigned long v=0; + unsigned char *p=*ptr; /* as v|=*((*ptr)++) upset someone */ + while(len) + { + v<<=8; + while(not_useful(p,map)) + p++; + v|=*p++; + len--; + } + *ptr=p; + return v; +} + +static void nubus_rewind(unsigned char **ptr, int len, int map) +{ + unsigned char *p=*ptr; + + if(len>8192) + printk("rewind of %d!\n", len); + while(len) + { + do + { + p--; + } + while(not_useful(p,map)); + len--; + } + *ptr=p; +} + +static void nubus_advance(unsigned char **ptr, int len, int map) +{ + unsigned char *p=*ptr; + if(len>8192) + printk("advance of %d!\n", len); + while(len) + { + while(not_useful(p,map)) + p++; + p++; + len--; + } + *ptr=p; +} + +/* + * 24bit signed offset to 32bit + */ + +static unsigned long nubus_expand32(unsigned long foo) +{ + if(foo&0x00800000) /* 24bit negative */ + foo|=0xFF000000; + return foo; +} + +static void nubus_move(unsigned char **ptr, int len, int map) +{ + if(len>0) + nubus_advance(ptr,len,map); + else if(len<0) + nubus_rewind(ptr,-len,map); +} + +static void *nubus_rom_addr(int slot) +{ + /* + * Returns the first byte after the card. We then walk + * backwards to get the lane register and the config + */ + return (void *)(0xF1000000+(slot<<24)); +} + +void nubus_memcpy(int slot, void *to, unsigned char *p, int len) +{ + unsigned char *t=(unsigned char *)to; + while(len) + { + *t++=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes); + len--; + } +} + +void nubus_strncpy(int slot, void *to, unsigned char *p, int len) +{ + unsigned char *t=(unsigned char *)to; + while(len) + { + *t=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes); + if(!*t++) + break; + len--; + } +} + + + + +unsigned char *nubus_dirptr(struct nubus_dirent *nd) +{ + unsigned char *p=(unsigned char *)(nd->base); + + nubus_move(&p, nubus_expand32(nd->value), nd->mask); + return p; +} + + +struct nubus_dir *nubus_openrootdir(int slot) +{ + static struct nubus_dir nbdir; + unsigned char *rp=nubus_rom_addr(slot); + + nubus_rewind(&rp,20, nubus_slots[slot].slot_lanes); + + nubus_move(&rp, nubus_expand32(nubus_slots[slot].slot_directory), + nubus_slots[slot].slot_lanes); + + nbdir.base=rp; + nbdir.length=nubus_slots[slot].slot_dlength; + nbdir.count=0; + nbdir.mask=nubus_slots[slot].slot_lanes; + return &nbdir; +} + +struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d) +{ + static struct nubus_dir nbdir; + unsigned char *rp=nubus_dirptr(d); + nbdir.base=rp; + nbdir.length=99999;/*slots[i].slot_dlength;*/ + nbdir.count=0; + nbdir.mask=d->mask; + return &nbdir; +} + +void nubus_closedir(struct nubus_dir *nd) +{ + ; +} + +struct nubus_dirent *nubus_readdir(struct nubus_dir *nd) +{ + u32 resid; + u8 rescode; + static struct nubus_dirent d; + + if(nd->count==nd->length) + return NULL; + + d.base=(unsigned long)nd->base; + + resid=nubus_get_rom(&nd->base, 4, nd->mask); + nd->count++; + rescode=resid>>24; + if(rescode==0xFF) + { + nd->count=nd->length; + return NULL; + } + d.type=rescode; + d.value=resid&0xFFFFFF; + d.mask=nd->mask; + return &d; +} + +/* + * MAC video handling irritations + */ + +static unsigned char nubus_vid_byte[16]; +static unsigned long nubus_vid_offset[16]; + +static void nubus_irqsplat(int slot, void *dev_id, struct pt_regs *regs) +{ + unsigned char *p=((unsigned char *)nubus_slot_addr(slot))+ + nubus_vid_offset[slot]; + *p=nubus_vid_byte[slot]; +} + +static int nubus_add_irqsplatter(int slot, unsigned long ptr, unsigned char v) +{ + nubus_vid_byte[slot]=v; + nubus_vid_offset[slot]=ptr; + nubus_request_irq(slot, NULL, nubus_irqsplat); + return 0; +} + +void nubus_video_shutup(int slot, struct nubus_type *nt) +{ + if(nt->category!=3 /* Display */ || nt->type!=1 /* Video */ + || nt->DrSW!=1 /* Quickdraw device */) + return; + switch(nt->DrHW) + { + /* + * Toby and MacII Hires cards. These behave in a MacII + * anyway but not on an RBV box + */ + case 0x0001: + case 0x0013: + nubus_add_irqsplatter(slot, 0xA0000, 0); + break; + /* + * Apple workstation video card. + */ + case 0x0006: + nubus_add_irqsplatter(slot, 0xA00000, 0); + break; + /* + * Futura cards + */ + case 0x0417: + case 0x042F: + nubus_add_irqsplatter(slot, 0xF05000, 0x80); + break; + + /* + * Fingers crossed 8) + * + * If you have another card and an RBV based mac you'll + * almost certainly have to add it here to make it work. + */ + + default: + break; + } +} + +/* + * Device list + */ + +static struct nubus_device_specifier *nubus_device_list=NULL; + +void register_nubus_device(struct nubus_device_specifier *d) +{ + d->next=nubus_device_list; + nubus_device_list=d; +} + +void unregister_nubus_device(struct nubus_device_specifier *nb) +{ + struct nubus_device_specifier **t=&nubus_device_list; + while(*t!=nb && *t) + t=&((*t)->next); + *t=nb->next; +} + +static struct nubus_device_specifier *find_nubus_device(int slot, struct nubus_type *nt) +{ + struct nubus_device_specifier *t=nubus_device_list; + while(t!=NULL) + { + if(t->setup(t,slot, nt)==0) + return t; + t=t->next; + } + printk("No driver for device [%d %d %d %d]\n", + nt->category, nt->type, nt->DrHW, nt->DrSW); + return NULL; +} + +/* + * Probe a nubus slot + */ + +void nubus_probe_slot(int slot, int mode) +{ + unsigned char *rp; + unsigned char dp; + int lanes; + int i; + unsigned long dpat; + struct nubus_dir *dir; + struct nubus_dirent *nd; + struct nubus_type type_info; + + /* + * Ok see whats cooking in the bytelanes + */ + + rp=nubus_rom_addr(slot); + + for(i=4;i;i--) + { + rp--; + + if(!nubus_hwreg_present(rp)) + continue; + + dp=*rp; + + if(dp==0) + continue; + + /* + * Valid ? + */ + + if((((dp>>4)^dp)&0x0F)!=0x0F) + continue; + + if((dp&0x0F) >= 1<<i) + continue; + + /* + * Looks promising + */ + + nubus_slots[slot].slot_flags|=NUBUS_DEVICE_PRESENT; + lanes=dp; + + if (mode==0) + printk("nubus%c: ", + "0123456789abcdef"[slot]); + + + /* + * Time to dig deeper. Find the ROM base + * and read it + */ + + rp=nubus_rom_addr(slot); + + /* + * Now to make this more fun the ROM is only visible + * on its bytelanes - that is smeared across the address + * space. + */ + + nubus_rewind(&rp,20,lanes); + + nubus_slots[slot].slot_directory= nubus_get_rom(&rp,4,lanes); + nubus_slots[slot].slot_dlength = nubus_get_rom(&rp,4,lanes); + nubus_slots[slot].slot_crc = nubus_get_rom(&rp,4,lanes); + nubus_slots[slot].slot_rev = nubus_get_rom(&rp,1,lanes); + nubus_slots[slot].slot_format = nubus_get_rom(&rp,1,lanes); + nubus_slots[slot].slot_lanes = lanes; + + dpat=nubus_get_rom(&rp,4,lanes); + + /* + * Ok now check what we got + */ + + if(!(nubus_slots[slot].slot_directory&0x00FF0000)) + printk("Dodgy doffset ??\n"); + if(dpat!=0x5A932BC7) + printk("Wrong test pattern %lx\n",dpat); + + /* + * I wonder how the CRC is meant to work - + * any takers ? + */ + + + /* + * Now parse the directories on the card + */ + + + dir=nubus_openrootdir(slot); + + /* + * Find the board resource + */ + + while((nd=nubus_readdir(dir))!=NULL) + { + /* + * This ought to be 1. 1 doesn't work, 0x80 + * does. Seems the Apple docs are wrong. + */ + if(nd->type==0x80/*RES_ID_BOARD_DIR*/) + break; + } + + nubus_closedir(dir); + + if(nd==NULL) + { + printk("board resource not found!\n"); + return; + } + + dir=nubus_opensubdir(nd); + + /* + * Walk the board resource + */ + + while((nd=nubus_readdir(dir))!=NULL) + { + switch(nd->type) + { + case RES_ID_TYPE: + { + unsigned short nbtdata[4]; + nubus_memcpy(slot, nbtdata, nubus_dirptr(nd), 8); + type_info.category=nbtdata[0]; + type_info.type=nbtdata[1]; + type_info.DrHW=nbtdata[2]; + type_info.DrSW=nbtdata[3]; + break; + } + case RES_ID_NAME: + nubus_strncpy(slot, nubus_slots[slot].slot_cardname,nubus_dirptr(nd),64); + break; + default: + ; + } + } + + nubus_closedir(dir); + + /* + * Attempt to bind a driver to this slot + */ + + if (mode==0) { + printk("%s\n", + nubus_slots[slot].slot_cardname); + find_nubus_device(slot,&type_info); + } + if (mode==1) + nubus_video_shutup(slot, &type_info); + + return; + } +} + + +void nubus_probe_bus(void) +{ + int i; + for(i=9;i<15;i++) + { + /* printk("nubus: probing slot %d !\n", i); */ + nubus_probe_slot(i, 0); + } +} + +/* + * RBV machines have level triggered video interrupts, and a VIA + * emulation that doesn't always seem to include being able to disable + * an interrupt. Totally lusing hardware. Before we can init irq's we + * have to install a handler to shut the bloody things up. + */ + +void nubus_sweep_video(void) +{ + int i; + return; /* XXX why ?? */ + for(i=9;i<15;i++) + { + nubus_probe_slot(i,1); + } +} + +/* + * Support functions + */ + +int nubus_ethernet_addr(int slot, unsigned char *addr) +{ + struct nubus_dir *nb; + struct nubus_dirent *d; + int ng=-ENOENT; + + nb=nubus_openrootdir(slot); + + if(nb==NULL) + return -ENOENT; + + while((d=nubus_readdir(nb))!=NULL) + { + if(d->type==0x80) /* First private resource */ + break; + } + if(d==NULL) + return -ENOENT; + + nb=nubus_opensubdir(d); + + while((d=nubus_readdir(nb))!=NULL) + { + if(d->type==0x80) /* First private field is the mac */ + { + int i; + nubus_memcpy(slot, addr, nubus_dirptr(d), 6); +/* printk("d.base=%lX, d.value=%lX\n", + d->base,d->value); + memcpy(addr,"\xC0\xC1\xC2\xC3\xC4\xC5",6);*/ + printk("MAC address: "); + for(i=0;i<6;i++) + { + printk("%s%02X", i?":":"", addr[i]); + } + ng=0; + break; + } + else + printk("ID=%d val=%x\n", + d->type, d->value); + } + return ng; +} + +void nubus_init(void) +{ + /* + * Register cards + */ +#ifdef CONFIG_DAYNAPORT + extern struct nubus_device_specifier nubus_8390; +#endif + + if (!MACH_IS_MAC) + return; + +#ifdef LCIII_WEIRDNESS + if (macintosh_config->ident == MAC_MODEL_LCIII) { + printk("nubus init: LCIII has no nubus!\n"); + return; + } +#endif + +#ifdef CONFIG_DAYNAPORT + register_nubus_device(&nubus_8390); +#endif + + /* + * And probe + */ + + nubus_init_via(); + printk("Scanning nubus slots.\n"); + nubus_probe_bus(); +} diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b12b2a454..de7b85cdc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -71,7 +71,7 @@ const char *pcibios_strerror(int error) unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp) { unsigned int devfn, l, max, class; - unsigned char cmd, irq, tmp, hdr_type = 0; + unsigned char cmd, irq, tmp, hdr_type, is_multi = 0; struct pci_dev *dev; struct pci_bus *child; int reg; @@ -82,12 +82,13 @@ unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp) max = bus->secondary; for (devfn = 0; devfn < 0xff; ++devfn) { - if (PCI_FUNC(devfn) == 0) { - pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type); - } else if (!(hdr_type & 0x80)) { + if (PCI_FUNC(devfn) && !is_multi) { /* not a multi-function device */ continue; } + pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type); + if (!PCI_FUNC(devfn)) + is_multi = hdr_type & 0x80; pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l); /* some broken boards return 0 if a slot is empty: */ diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c index 95400fe68..93a30f3e9 100644 --- a/drivers/scsi/53c7xx.c +++ b/drivers/scsi/53c7xx.c @@ -2,7 +2,7 @@ * 53c710 driver. Modified from Drew Eckhardts driver * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk] * Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the - * relevant machine specific file (eg. mvme166.[ch], amiga7xx.[ch]). + * relevant machine specific file (eg. mvme16x.[ch], amiga7xx.[ch]). * There are also currently some defines at the top of 53c7xx.scr. * The chip type is #defined in script_asm.pl, as well as the Makefile. * Host scsi ID expected to be 7 - see NCR53c7x0_init(). @@ -10,9 +10,9 @@ * I have removed the PCI code and some of the 53c8xx specific code - * simply to make this file smaller and easier to manage. * - * MVME166 issues: + * MVME16x issues: * Problems trying to read any chip registers in NCR53c7x0_init(), as they - * may never have been set by 166Bug (eg. If kernel has come in over tftp). + * may never have been set by 16xBug (eg. If kernel has come in over tftp). */ /* @@ -45,6 +45,9 @@ * validids:0x?? - Bitmask field that disallows certain ID's. * - e.g. 0x03 allows ID 0,1 * - 0x1F allows ID 0,1,2,3,4 + * opthi:n - replace top word of options with 'n' + * optlo:n - replace bottom word of options with 'n' + * - ALWAYS SPECIFY opthi THEN optlo <<<<<<<<<< */ /* @@ -60,7 +63,7 @@ * out brain damaged main boards. * * Other PERM_OPTIONS settings are listed below. Note the actual options - * required are set in the relevant file (mvme166.c, amiga7xx.c, etc): + * required are set in the relevant file (mvme16x.c, amiga7xx.c, etc): * * OPTION_NO_ASYNC * Don't negotiate for asynchronous transfers on the first command @@ -231,6 +234,9 @@ #endif #include <linux/config.h> + +#include <linux/types.h> +#include <asm/setup.h> #include <asm/dma.h> #include <asm/io.h> #include <asm/system.h> @@ -259,11 +265,13 @@ #define NO_IO_SPACE #endif -#ifdef CONFIG_MVME166 -#include <asm/mvme166hw.h> +#ifdef CONFIG_MVME16x +#include <asm/pgtable.h> +#include <asm/mvme16xhw.h> #define BIG_ENDIAN #define NO_IO_SPACE +#define VALID_IDS #endif #include "scsi.h" @@ -636,6 +644,7 @@ static const unsigned char wdtr_message[] = { EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */ }; +#if 0 /* * Function : struct Scsi_Host *find_host (int host) * @@ -734,6 +743,7 @@ request_disconnect (int host, int on_or_off) { hostdata->options &= ~OPTION_DISCONNECT; return 0; } +#endif /* * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host) @@ -751,10 +761,6 @@ NCR53c7x0_driver_init (struct Scsi_Host *host) { int i, j; u32 *ncrcurrent; -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - for (i = 0; i < 16; ++i) { hostdata->request_sense[i] = 0; for (j = 0; j < 8; ++j) @@ -783,9 +789,9 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); hostdata->initiate_sdtr = 0; hostdata->talked_to = 0; hostdata->idle = 1; -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); + + if (!MACH_IS_MVME16x) + cache_push(virt_to_bus(hostdata->script), flushsize); } /* @@ -877,18 +883,22 @@ NCR53c7x0_init (struct Scsi_Host *host) { setup_used[--i] = 1; } + if (check_setup_strings("opthi",&flags,&val,buf)) + hostdata->options = (long long)val << 32; + if (check_setup_strings("optlo",&flags,&val,buf)) + hostdata->options |= val; NCR53c7x0_local_setup(host); - switch (hostdata->chip) { case 710: + case 770: hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr; hostdata->init_save_regs = NULL; hostdata->dsa_fixup = NCR53c7xx_dsa_fixup; hostdata->init_fixup = NCR53c7x0_init_fixup; hostdata->soft_reset = NCR53c7x0_soft_reset; hostdata->run_tests = NCR53c7xx_run_tests; - expected_clock = hostdata->scsi_clock = 50000000; + expected_clock = hostdata->scsi_clock; expected_id = 7; break; default: @@ -904,7 +914,6 @@ NCR53c7x0_init (struct Scsi_Host *host) { hostdata->NCR53c7xx_msg_abort = ABORT; hostdata->NCR53c7xx_msg_nop = NOP; hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24; - if (expected_mapping == -1 || (hostdata->options & (OPTION_MEMORY_MAPPED)) != (expected_mapping & OPTION_MEMORY_MAPPED)) @@ -917,6 +926,12 @@ NCR53c7x0_init (struct Scsi_Host *host) { hostdata->istat = ((hostdata->chip / 100) == 8) ? ISTAT_REG_800 : ISTAT_REG_700; +/* We have to assume that this may be the first access to the chip, so + * we must set EA in DCNTL. */ + + NCR53c7x0_write8 (DCNTL_REG, DCNTL_10_EA|DCNTL_10_COM); + + /* Only the ISTAT register is readable when the NCR is running, so make sure it's halted. */ ncr_halt(host); @@ -990,7 +1005,7 @@ NCR53c7x0_init (struct Scsi_Host *host) { * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor, * on 800 series chips, it allows for a totem-pole IRQ driver. * NOTE saved_dcntl currently overwritten in init function. - * The value read here may be garbage anyway, MVME166 board at least + * The value read here may be garbage anyway, MVME16x board at least * does not initialise chip if kernel arrived via tftp. */ @@ -999,7 +1014,7 @@ NCR53c7x0_init (struct Scsi_Host *host) { /* * DMODE controls DMA burst length, and on 700 series chips, * 286 mode and bus width - * NOTE: On MVME166, chip may have been reset, so this could be a + * NOTE: On MVME16x, chip may have been reset, so this could be a * power-on/reset default value. */ hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode); @@ -1053,11 +1068,11 @@ NCR53c7x0_init (struct Scsi_Host *host) { * with another board. */ -#ifdef CONFIG_MVME166 - if (request_irq(IRQ_MVME166_SCSI, NCR53c7x0_intr, 0, "SCSI-script", NULL)) +#ifdef CONFIG_MVME16x + if (request_irq(IRQ_MVME16x_SCSI, NCR53c7x0_intr, 0, "SCSI-script", NULL)) panic ("Couldn't get SCSI IRQ"); -#ifdef MVME166_INTFLY - else if (request_irq(IRQ_MVME166_FLY, NCR53c7x0_intr, 0, "SCSI-intfly", NULL)) +#ifdef MVME16x_INTFLY + else if (request_irq(IRQ_MVME16x_FLY, NCR53c7x0_intr, 0, "SCSI-intfly", NULL)) panic ("Couldn't get INT_FLY IRQ"); #endif #else @@ -1105,10 +1120,9 @@ NCR53c7x0_init (struct Scsi_Host *host) { } /* - * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, - * int chip, u32 base, int io_port, int irq, int dma, int pcivalid, - * unsigned char pci_bus, unsigned char pci_device_fn, - * long long options); + * Function : static int ncr53c7xx_init(Scsi_Host_Template *tpnt, int board, + * int chip, u32 base, int io_port, int irq, int dma, + * long long options, int clock); * * Purpose : initializes a NCR53c7,8x0 based on base addresses, * IRQ, and DMA channel. @@ -1137,6 +1151,7 @@ ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, switch (chip) { case 710: + case 770: schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */; script_len = NCR53c7xx_script_len; dsa_len = NCR53c7xx_dsa_len; @@ -1214,7 +1229,7 @@ ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, /* FIXME : if we ever support an ISA NCR53c7xx based board, we need to check if the chip is running in a 16 bit mode, and if so unregister it if it is past the 16M (0x1000000) mark */ - + hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata; hostdata->size = size; @@ -1269,6 +1284,7 @@ ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, hostdata->dsa_len = dsa_len; hostdata->max_cmd_size = max_cmd_size; hostdata->num_cmds = 1; + hostdata->scsi_clock = clock; /* Initialize single command */ tmp = (hostdata->script + hostdata->script_count); #ifdef FORCE_DSA_ALIGNMENT @@ -1393,8 +1409,8 @@ NCR53c7x0_init_fixup (struct Scsi_Host *host) { * register. Make sure SCRIPTS start automagically. */ -#if defined(CONFIG_MVME166) - /* We know better what we want than 166Bug does! */ +#if defined(CONFIG_MVME16x) + /* We know better what we want than 16xBug does! */ tmp = DMODE_10_BL_8 | DMODE_10_FC2; #else tmp = NCR53c7x0_read8(DMODE_REG_10); @@ -1543,9 +1559,9 @@ NCR53c7x0_init_fixup (struct Scsi_Host *host) { printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no, virt_to_bus(hostdata->script), hostdata->script); -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); + + if (!MACH_IS_MVME16x) + cache_push(virt_to_bus(hostdata->script), flushsize); } /* @@ -1599,9 +1615,8 @@ NCR53c7xx_run_tests (struct Scsi_Host *host) { start = virt_to_bus (hostdata->script) + hostdata->E_test_1; hostdata->state = STATE_RUNNING; printk ("scsi%d : test 1", host->host_no); - flush_cache_all(); - cache_push(virt_to_bus(hostdata->script), flushsize); - cache_clear(virt_to_bus(hostdata->script), flushsize); + if (!MACH_IS_MVME16x) + cache_push(virt_to_bus(hostdata->script), flushsize); NCR53c7x0_write32 (DSP_REG, start); if (hostdata->options & OPTION_DEBUG_TRACE) NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM | @@ -1695,8 +1710,8 @@ NCR53c7xx_run_tests (struct Scsi_Host *host) { hostdata->test_completed = -1; start = virt_to_bus(hostdata->script) + hostdata->E_test_2; hostdata->state = STATE_RUNNING; - flush_cache_all(); - cache_clear(virt_to_bus(hostdata->script), flushsize); + if(!MACH_IS_MVME16x) + cache_clear(virt_to_bus(hostdata->script), flushsize); NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa)); NCR53c7x0_write32 (DSP_REG, start); if (hostdata->options & OPTION_DEBUG_TRACE) @@ -1803,9 +1818,10 @@ NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) { patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr)); -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); + if (!MACH_IS_MVME16x) { + cache_push(virt_to_bus(hostdata->script), flushsize); + cache_push(virt_to_bus(cmd->dsa), flushsize); + } } /* @@ -1988,8 +2004,6 @@ intr_break (struct Scsi_Host *host, struct */ save_flags(flags); cli(); - flush_cache_all(); - cache_push(virt_to_bus(hostdata->script), flushsize); dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); for (bp = hostdata->breakpoints; bp && bp->address != dsp; bp = bp->next); @@ -2258,14 +2272,20 @@ NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct host->hostdata; u32 dsps,*dsp; /* Argument of the INT instruction */ -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - NCR53c7x0_local_setup(host); dsps = NCR53c7x0_read32(DSPS_REG); dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); + /* RGH 150597: Frig. Commands which fail with Check Condition are + * Flagged as successful - hack dsps to indicate check condition */ +#if 0 + /* RGH 200597: Need to disable for BVME6000, as it gets Check Conditions + * and then dies. Seems to handle Check Condition at startup, but + * not mid kernel build. */ + if (dsps == A_int_norm_emulateintfly && c && c->result == 2) + dsps = A_int_err_check_condition; +#endif + if (hostdata->options & OPTION_DEBUG_INTR) printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps); @@ -2870,9 +2890,9 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); host->host_no, (unsigned) dsps); return SPECIFIC_INT_PANIC; } -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); + + if (!MACH_IS_MVME16x) + flush_cache_all(); } /* @@ -2910,7 +2930,7 @@ static void NCR53c7x0_soft_reset (struct Scsi_Host *host) { NCR53c7x0_local_declare(); unsigned long flags; -#ifdef CONFIG_MVME166 +#ifdef CONFIG_MVME16x volatile unsigned long v; #endif struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) @@ -2922,7 +2942,7 @@ NCR53c7x0_soft_reset (struct Scsi_Host *host) { /* Disable scsi chip and s/w level 7 ints */ -#ifdef CONFIG_MVME166 +#ifdef CONFIG_MVME16x v = *(volatile unsigned long *)0xfff4006c; v &= ~0x8000; *(volatile unsigned long *)0xfff4006c = v; @@ -2997,7 +3017,7 @@ NCR53c7x0_soft_reset (struct Scsi_Host *host) { SIEN_PAR : 0) | SIEN_700_STO | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA); -#ifdef CONFIG_MVME166 +#ifdef CONFIG_MVME16x /* Enable scsi chip and s/w level 7 ints */ v = *(volatile unsigned long *)0xfff40080; @@ -3285,10 +3305,6 @@ create_cmd (Scsi_Cmnd *cmd) { patch_dsa_32(tmp->dsa, dsa_next, 0, 0); patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd)); -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) { exp_select_indirect = ((1 << cmd->target) << 16) | @@ -3306,11 +3322,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].select_indirect); - -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - /* * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on * different commands; although it should be trivial to do them @@ -3339,10 +3350,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); else if (!(hostdata->talked_to & (1 << cmd->target)) && !(hostdata->options & OPTION_NO_ASYNC)) { -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - memcpy ((void *) (tmp->select + 1), (void *) async_message, sizeof(async_message)); patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message)); @@ -3352,10 +3359,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); else patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1); -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - hostdata->talked_to |= (1 << cmd->target); tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun); @@ -3429,11 +3432,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); * Not bad, not good. We'll see. */ -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - - for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, cmd_dataout += 4, ++i) { u32 buf = cmd->use_sg ? @@ -3476,11 +3474,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); } } -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - - /* * Install JUMP instructions after the data transfer routines to return * control to the do_other_transfer routines. @@ -3515,10 +3508,6 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); cmd_dataout += 2; } -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - return tmp; } @@ -3693,11 +3682,6 @@ to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, i > 0 && ncrcurrent[0] != hostdata->NOP_insn; --i, ncrcurrent += 2 /* JUMP instructions are two words */); - -flush_cache_all(); -cache_push(virt_to_bus(hostdata->script), flushsize); -cache_clear(virt_to_bus(hostdata->script), flushsize); - if (i > 0) { ++hostdata->busy[tmp->target][tmp->lun]; cmd->next = hostdata->running_list; @@ -3724,15 +3708,15 @@ cache_clear(virt_to_bus(hostdata->script), flushsize); return; } - cache_push(virt_to_bus(cmd->dsa), hostdata->dsa_len); - cache_push(virt_to_bus(ncrcurrent), sizeof(ncrcurrent)); - /* * If the NCR chip is in an idle state, start it running the scheduler * immediately. Otherwise, signal the chip to jump to schedule as * soon as it is idle. */ + if (!MACH_IS_MVME16x) + flush_cache_all(); + if (hostdata->idle) { hostdata->idle = 0; hostdata->state = STATE_RUNNING; @@ -4133,7 +4117,7 @@ NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { done = 1; for (host = first_host; host; host = host->next) if (host->hostt == the_template -#if defined(MVME166_INTFLY) +#if defined(MVME16x_INTFLY) /* We have two different interrupts pointing * at this routine, so remove this check */ #else @@ -4157,7 +4141,7 @@ NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { istat = NCR53c7x0_read8(hostdata->istat); if ((hostdata->options & OPTION_INTFLY) && -#ifdef MVME166_INTFLY +#ifdef MVME16x_INTFLY /* the bit is set which indicates an on-the-fly int */ (*(volatile unsigned long *)0xfff40068 & 0x8000)) #else @@ -4168,7 +4152,7 @@ NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { done = 0; interrupted = 1; -#ifdef MVME166_INTFLY +#ifdef MVME16x_INTFLY /* clear the INTFLY bit */ *(volatile unsigned long *)0xfff40074 = 0x8000; #endif @@ -4345,6 +4329,8 @@ restart: #endif hostdata->state = STATE_RUNNING; + if (!MACH_IS_MVME16x) + flush_cache_all(); NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); if (hostdata->options & OPTION_DEBUG_TRACE) { #ifdef CYCLIC_TRACE @@ -4669,7 +4655,9 @@ intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { where = "non-BMI dynamic DSA code"; action = ACTION_ABORT_PRINT; } - } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) { + } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4 + 2)) { + /* RGH 290697: Added +2 above, to compensate for the script + * instruction which disables the selection timer. */ /* Release ATN */ NCR53c7x0_write8 (SOCL_REG, 0); switch (sbcl) { @@ -4751,8 +4739,9 @@ intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { print_insn (host, hostdata->dsp, "", 1); } #endif - - cache_push(virt_to_bus(hostdata->script), flushsize); + + if (!MACH_IS_MVME16x) + cache_push(virt_to_bus(hostdata->script), flushsize); } /* @@ -4826,7 +4815,7 @@ intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { */ if (retry == NEVER) { - printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); + printk(KERN_ALERT " mail ricahrd@sleepie.demon.co.uk\n"); FATAL (host); } } @@ -5097,7 +5086,7 @@ print_insn (struct Scsi_Host *host, const u32 *insn, * FIXME : (void *) cast in virt_to_bus should be unnecessary, because * it should take const void * as argument. */ -#ifndef CONFIG_MVME166 +#ifndef CONFIG_MVME16x sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", (prefix ? prefix : ""), virt_to_bus((void *) insn), insn, insn[0], insn[1], bus_to_virt (insn[1])); @@ -5110,7 +5099,7 @@ print_insn (struct Scsi_Host *host, const u32 *insn, #endif tmp = buf + strlen(buf); if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) { -#ifndef CONFIG_MVME166 +#ifndef CONFIG_MVME16x sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], bus_to_virt(insn[2])); #else diff --git a/drivers/scsi/53c7xx.h b/drivers/scsi/53c7xx.h index 56271a104..a2a53f107 100644 --- a/drivers/scsi/53c7xx.h +++ b/drivers/scsi/53c7xx.h @@ -893,7 +893,7 @@ extern inline void * phys_to_virt(unsigned long address) * NCR53c710, this bit moved to CTEST8 */ #define DCNTL_10_COM 0x01 /* 700 software compatibility mode */ -#define DCNTL_10_EA 0x20 /* Enable Ack - needed for MVME166 */ +#define DCNTL_10_EA 0x20 /* Enable Ack - needed for MVME16x */ #define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16) diff --git a/drivers/scsi/53c7xx.scr b/drivers/scsi/53c7xx.scr index 2fc9db2dd..5f26cde9f 100644 --- a/drivers/scsi/53c7xx.scr +++ b/drivers/scsi/53c7xx.scr @@ -315,7 +315,7 @@ dsa_code_check_reselect: MOVE MEMORY 1, reselected_identify, addr_scratch DMODE_MEMORY_TO_MEMORY #ifdef BIG_ENDIAN - ; BIG ENDIAN ON MVME166 + ; BIG ENDIAN ON MVME16x MOVE SCRATCH3 TO SFBR #else MOVE SCRATCH0 TO SFBR @@ -1134,8 +1134,8 @@ command_complete_msgin: JUMP command_failed, IF 0x02 #endif #if (CHIP == 710) -#if defined(MVME166_INTFLY) -; For MVME166 (ie CHIP=710) we will force an INTFLY by triggering a software +#if defined(MVME16x_INTFLY) +; For MVME16x (ie CHIP=710) we will force an INTFLY by triggering a software ; interupt (SW7). We can use SCRATCH, as we are about to jump to ; schedule, which corrupts it anyway. Will probably remove this later, ; but want to check performance effects first. diff --git a/drivers/scsi/NCR53C9x.c b/drivers/scsi/NCR53C9x.c new file mode 100644 index 000000000..81a3fab54 --- /dev/null +++ b/drivers/scsi/NCR53C9x.c @@ -0,0 +1,3733 @@ +/* NCR53C9x.c: Generic SCSI driver code for NCR53C9x chips. + * + * Originally esp.c : EnhancedScsiProcessor Sun SCSI driver code. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * Most DMA dependencies put in driver specific files by + * Jesper Skov (jskov@cygnus.co.uk) + */ + +/* TODO: + * + * 1) Maybe disable parity checking in config register one for SCSI1 + * targets. (Gilmore says parity error on the SBus can lock up + * old sun4c's) + * 2) Add support for DMA2 pipelining. + * 3) Add tagged queueing. + * 4) Maybe change use of "esp" to something more "NCR"'ish. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/blk.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <linux/init.h> + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" + +#ifdef CONFIG_SCSI_SUNESP +#include "sparc_esp.h" +#include <asm/sbus.h> +#include <asm/dma.h> +#include <asm/machines.h> +#include <asm/oplib.h> +#include <asm/idprom.h> +#endif + +#if defined(CONFIG_BLZ1230_SCSI)||defined(CONFIG_BLZ2060_SCSI)||defined(CONFIG_CYBERSTORMII_SCSI) +#define SYMBIOS_HACK +#else +#undef SYMBIOS_HACK +#endif + +#include <asm/system.h> +#include <asm/ptrace.h> +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/irq.h> + +/* Command phase enumeration. */ +enum { + not_issued = 0x00, /* Still in the issue_SC queue. */ + + /* Various forms of selecting a target. */ +#define in_slct_mask 0x10 + in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */ + in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */ + in_slct_msg = 0x12, /* select, then send a message */ + in_slct_tag = 0x13, /* select and send tagged queue msg */ + in_slct_sneg = 0x14, /* select and acquire sync capabilities */ + + /* Any post selection activity. */ +#define in_phases_mask 0x20 + in_datain = 0x20, /* Data is transferring from the bus */ + in_dataout = 0x21, /* Data is transferring to the bus */ + in_data_done = 0x22, /* Last DMA data operation done (maybe) */ + in_msgin = 0x23, /* Eating message from target */ + in_msgincont = 0x24, /* Eating more msg bytes from target */ + in_msgindone = 0x25, /* Decide what to do with what we got */ + in_msgout = 0x26, /* Sending message to target */ + in_msgoutdone = 0x27, /* Done sending msg out */ + in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */ + in_cmdend = 0x29, /* Done sending slow cmd */ + in_status = 0x2a, /* Was in status phase, finishing cmd */ + in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */ + in_the_dark = 0x2c, /* Don't know what bus phase we are in */ + + /* Special states, ie. not normal bus transitions... */ +#define in_spec_mask 0x80 + in_abortone = 0x80, /* Aborting one command currently */ + in_abortall = 0x81, /* Blowing away all commands we have */ + in_resetdev = 0x82, /* SCSI target reset in progress */ + in_resetbus = 0x83, /* SCSI bus reset in progress */ + in_tgterror = 0x84, /* Target did something stupid */ +}; + +struct proc_dir_entry proc_scsi_esp = { + PROC_SCSI_ESP, 3, "esp", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +/* The master ring of all esp hosts we are managing in this driver. */ +struct NCR_ESP *espchain = 0; +int nesps = 0, esps_in_use = 0, esps_running = 0; + +void esp_intr(int irq, void *dev_id, struct pt_regs *pregs); + +/* Debugging routines */ +struct esp_cmdstrings { + unchar cmdchar; + char *text; +} esp_cmd_strings[] = { + /* Miscellaneous */ + { ESP_CMD_NULL, "ESP_NOP", }, + { ESP_CMD_FLUSH, "FIFO_FLUSH", }, + { ESP_CMD_RC, "RSTESP", }, + { ESP_CMD_RS, "RSTSCSI", }, + /* Disconnected State Group */ + { ESP_CMD_RSEL, "RESLCTSEQ", }, + { ESP_CMD_SEL, "SLCTNATN", }, + { ESP_CMD_SELA, "SLCTATN", }, + { ESP_CMD_SELAS, "SLCTATNSTOP", }, + { ESP_CMD_ESEL, "ENSLCTRESEL", }, + { ESP_CMD_DSEL, "DISSELRESEL", }, + { ESP_CMD_SA3, "SLCTATN3", }, + { ESP_CMD_RSEL3, "RESLCTSEQ", }, + /* Target State Group */ + { ESP_CMD_SMSG, "SNDMSG", }, + { ESP_CMD_SSTAT, "SNDSTATUS", }, + { ESP_CMD_SDATA, "SNDDATA", }, + { ESP_CMD_DSEQ, "DISCSEQ", }, + { ESP_CMD_TSEQ, "TERMSEQ", }, + { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", }, + { ESP_CMD_DCNCT, "DISC", }, + { ESP_CMD_RMSG, "RCVMSG", }, + { ESP_CMD_RCMD, "RCVCMD", }, + { ESP_CMD_RDATA, "RCVDATA", }, + { ESP_CMD_RCSEQ, "RCVCMDSEQ", }, + /* Initiator State Group */ + { ESP_CMD_TI, "TRANSINFO", }, + { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", }, + { ESP_CMD_MOK, "MSGACCEPTED", }, + { ESP_CMD_TPAD, "TPAD", }, + { ESP_CMD_SATN, "SATN", }, + { ESP_CMD_RATN, "RATN", }, +}; +#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings))) + +/* Print textual representation of an ESP command */ +static inline void esp_print_cmd(unchar espcmd) +{ + unchar dma_bit = espcmd & ESP_CMD_DMA; + int i; + + espcmd &= ~dma_bit; + for(i=0; i<NUM_ESP_COMMANDS; i++) + if(esp_cmd_strings[i].cmdchar == espcmd) + break; + if(i==NUM_ESP_COMMANDS) + printk("ESP_Unknown"); + else + printk("%s%s", esp_cmd_strings[i].text, + ((dma_bit) ? "+DMA" : "")); +} + +/* Print the status register's value */ +static inline void esp_print_statreg(unchar statreg) +{ + unchar phase; + + printk("STATUS<"); + phase = statreg & ESP_STAT_PMASK; + printk("%s,", (phase == ESP_DOP ? "DATA-OUT" : + (phase == ESP_DIP ? "DATA-IN" : + (phase == ESP_CMDP ? "COMMAND" : + (phase == ESP_STATP ? "STATUS" : + (phase == ESP_MOP ? "MSG-OUT" : + (phase == ESP_MIP ? "MSG_IN" : + "unknown"))))))); + if(statreg & ESP_STAT_TDONE) + printk("TRANS_DONE,"); + if(statreg & ESP_STAT_TCNT) + printk("TCOUNT_ZERO,"); + if(statreg & ESP_STAT_PERR) + printk("P_ERROR,"); + if(statreg & ESP_STAT_SPAM) + printk("SPAM,"); + if(statreg & ESP_STAT_INTR) + printk("IRQ,"); + printk(">"); +} + +/* Print the interrupt register's value */ +static inline void esp_print_ireg(unchar intreg) +{ + printk("INTREG< "); + if(intreg & ESP_INTR_S) + printk("SLCT_NATN "); + if(intreg & ESP_INTR_SATN) + printk("SLCT_ATN "); + if(intreg & ESP_INTR_RSEL) + printk("RSLCT "); + if(intreg & ESP_INTR_FDONE) + printk("FDONE "); + if(intreg & ESP_INTR_BSERV) + printk("BSERV "); + if(intreg & ESP_INTR_DC) + printk("DISCNCT "); + if(intreg & ESP_INTR_IC) + printk("ILL_CMD "); + if(intreg & ESP_INTR_SR) + printk("SCSI_BUS_RESET "); + printk(">"); +} + +/* Print the sequence step registers contents */ +static inline void esp_print_seqreg(unchar stepreg) +{ + stepreg &= ESP_STEP_VBITS; + printk("STEP<%s>", + (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" : + (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" : + (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" : + (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" : + (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" : + "UNKNOWN")))))); +} + +#if defined(DEBUG_STATE_MACHINE) || defined(DEBUG_ESP) +static char *phase_string(int phase) +{ + switch(phase) { + case not_issued: + return "UNISSUED"; + case in_slct_norm: + return "SLCTNORM"; + case in_slct_stop: + return "SLCTSTOP"; + case in_slct_msg: + return "SLCTMSG"; + case in_slct_tag: + return "SLCTTAG"; + case in_slct_sneg: + return "SLCTSNEG"; + case in_datain: + return "DATAIN"; + case in_dataout: + return "DATAOUT"; + case in_data_done: + return "DATADONE"; + case in_msgin: + return "MSGIN"; + case in_msgincont: + return "MSGINCONT"; + case in_msgindone: + return "MSGINDONE"; + case in_msgout: + return "MSGOUT"; + case in_msgoutdone: + return "MSGOUTDONE"; + case in_cmdbegin: + return "CMDBEGIN"; + case in_cmdend: + return "CMDEND"; + case in_status: + return "STATUS"; + case in_freeing: + return "FREEING"; + case in_the_dark: + return "CLUELESS"; + case in_abortone: + return "ABORTONE"; + case in_abortall: + return "ABORTALL"; + case in_resetdev: + return "RESETDEV"; + case in_resetbus: + return "RESETBUS"; + case in_tgterror: + return "TGTERROR"; + default: + return "UNKNOWN"; + }; +} +#endif + +static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase) +{ +#ifdef DEBUG_STATE_MACHINE + ESPLOG(("<%s>", phase_string(newphase))); +#endif + s->SCp.sent_command = s->SCp.phase; + s->SCp.phase = newphase; +} + +extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, + unchar cmd) +{ +#ifdef DEBUG_ESP_CMDS + esp->espcmdlog[esp->espcmdent] = cmd; + esp->espcmdent = (esp->espcmdent + 1) & 31; +#endif + eregs->esp_cmd = cmd; +} + +/* How we use the various Linux SCSI data structures for operation. + * + * struct scsi_cmnd: + * + * We keep track of the syncronous capabilities of a target + * in the device member, using sync_min_period and + * sync_max_offset. These are the values we directly write + * into the ESP registers while running a command. If offset + * is zero the ESP will use asynchronous transfers. + * If the borken flag is set we assume we shouldn't even bother + * trying to negotiate for synchronous transfer as this target + * is really stupid. If we notice the target is dropping the + * bus, and we have been allowing it to disconnect, we clear + * the disconnect flag. + */ + + +/* Manipulation of the ESP command queues. Thanks to the aha152x driver + * and its author, Juergen E. Fischer, for the methods used here. + * Note that these are per-ESP queues, not global queues like + * the aha152x driver uses. + */ +static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) +{ + Scsi_Cmnd *end; + unsigned long flags; + + save_flags(flags); cli(); + new_SC->host_scribble = (unsigned char *) NULL; + if(!*SC) + *SC = new_SC; + else { + for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble) + ; + end->host_scribble = (unsigned char *) new_SC; + } + restore_flags(flags); +} + +static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) +{ + unsigned long flags; + + save_flags(flags); cli(); + new_SC->host_scribble = (unsigned char *) *SC; + *SC = new_SC; + restore_flags(flags); +} + +static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC) +{ + Scsi_Cmnd *ptr; + unsigned long flags; + + save_flags(flags); cli(); + ptr = *SC; + if(ptr) + *SC = (Scsi_Cmnd *) (*SC)->host_scribble; + restore_flags(flags); + return ptr; +} + +static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun) +{ + Scsi_Cmnd *ptr, *prev; + unsigned long flags; + + save_flags(flags); cli(); + for(ptr = *SC, prev = NULL; + ptr && ((ptr->target != target) || (ptr->lun != lun)); + prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble) + ; + if(ptr) { + if(prev) + prev->host_scribble=ptr->host_scribble; + else + *SC=(Scsi_Cmnd *)ptr->host_scribble; + } + restore_flags(flags); + return ptr; +} + +/* Resetting various pieces of the ESP scsi driver chipset */ + +/* Reset the ESP chip, _not_ the SCSI bus. */ +static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + int family_code, version, i; + volatile int trash; + + /* Now reset the ESP chip */ + esp_cmd(esp, eregs, ESP_CMD_RC); + esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); + esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); + + /* Reload the configuration registers */ + eregs->esp_cfact = esp->cfact; + eregs->esp_stp = 0; + eregs->esp_soff = 0; + eregs->esp_timeo = esp->neg_defp; + + /* This is the only point at which it is reliable to read + * the ID-code for a fast ESP chip variant. + */ + esp->max_period = ((35 * esp->ccycle) / 1000); + if(esp->erev == fast) { + version = eregs->esp_uid; + family_code = (version & 0xf8) >> 3; +#ifdef SYMBIOS_HACK + if (version == 0 && family_code == 0) + { + printk ("Detected SymBIOS chip with no family code.\n"); + version = 3; + family_code = 2; + } +#endif + if(family_code == 0x02) + esp->erev = fas236; + else if(family_code == 0x0a) + esp->erev = fashme; /* Version is usually '5'. */ + else + esp->erev = fas100a; + printk("esp%d: FAST chip is %s (family=%d, version=%d)\n", + esp->esp_id, + (esp->erev == fas236) ? "fas236" : + ((esp->erev == fas100a) ? "fas100a" : + "fasHME"), family_code, (version & 7)); + + esp->min_period = ((4 * esp->ccycle) / 1000); + } else { + esp->min_period = ((5 * esp->ccycle) / 1000); + } + esp->max_period = (esp->max_period + 3)>>2; + esp->min_period = (esp->min_period + 3)>>2; + + eregs->esp_cfg1 = esp->config1; + switch(esp->erev) { + case esp100: + /* nothing to do */ + break; + case esp100a: + eregs->esp_cfg2 = esp->config2; + break; + case esp236: + /* Slow 236 */ + eregs->esp_cfg2 = esp->config2; + eregs->esp_cfg3 = esp->config3[0]; + break; + case fashme: + esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); + /* fallthrough... */ + case fas236: + /* Fast 236 or HME */ + eregs->esp_cfg2 = esp->config2; + for(i=0; i<8; i++) { + if(esp->erev == fashme) + esp->config3[i] |= + (ESP_CONFIG3_FCLOCK | ESP_CONFIG3_BIGID | ESP_CONFIG3_OBPUSH); + else + esp->config3[i] |= ESP_CONFIG3_FCLK; + } + eregs->esp_cfg3 = esp->config3[0]; + if(esp->erev == fashme) { + esp->radelay = 80; + } else { + if(esp->diff) + esp->radelay = 0; + else + esp->radelay = 96; + } + break; + case fas100a: + /* Fast 100a */ + eregs->esp_cfg2 = esp->config2; + for(i=0; i<8; i++) + esp->config3[i] |= ESP_CONFIG3_FCLOCK; + eregs->esp_cfg3 = esp->config3[0]; + esp->radelay = 32; + break; + default: + panic("esp: what could it be... I wonder..."); + break; + }; + + /* Eat any bitrot in the chip */ + trash = eregs->esp_intrpt; + udelay(100); +} + +/* This places the ESP into a known state at boot time. */ +inline void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + volatile unchar trash; + + /* Reset the DMA */ + if(esp->dma_reset) + esp->dma_reset(esp); + + /* Reset the ESP */ + esp_reset_esp(esp, eregs); + + /* Reset the SCSI bus, but tell ESP not to generate an irq */ + eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB; + esp_cmd(esp, eregs, ESP_CMD_RS); + udelay(400); + eregs->esp_cfg1 = esp->config1; + + /* Eat any bitrot in the chip and we are done... */ + trash = eregs->esp_intrpt; +} + +/* Allocate structure and insert basic data such as SCSI chip frequency + * data and a pointer to the device + */ +struct NCR_ESP* esp_allocate(Scsi_Host_Template *tpnt, void *esp_dev) +{ + struct NCR_ESP *esp, *elink; + struct Scsi_Host *esp_host; + + esp_host = scsi_register(tpnt, sizeof(struct NCR_ESP)); + if(!esp_host) + panic("Cannot register ESP SCSI host"); + esp = (struct NCR_ESP *) esp_host->hostdata; + if(!esp) + panic("No esp in hostdata"); + esp->ehost = esp_host; + esp->edev = esp_dev; + esp->esp_id = nesps++; + + /* Put into the chain of esp chips detected */ + if(espchain) { + elink = espchain; + while(elink->next) elink = elink->next; + elink->next = esp; + } else { + espchain = esp; + } + esp->next = 0; + + return esp; +} + +/* Complete initialization of ESP structure and device + * Caller must have initialized appropriate parts of the ESP structure + * between the call to esp_allocate and this function. + */ +void esp_initialize(struct NCR_ESP *esp) +{ + struct ESP_regs *eregs = esp->eregs; + unsigned int fmhz; + unchar ccf; + int i; + + /* Check out the clock properties of the chip. */ + + /* This is getting messy but it has to be done + * correctly or else you get weird behavior all + * over the place. We are trying to basically + * figure out three pieces of information. + * + * a) Clock Conversion Factor + * + * This is a representation of the input + * crystal clock frequency going into the + * ESP on this machine. Any operation whose + * timing is longer than 400ns depends on this + * value being correct. For example, you'll + * get blips for arbitration/selection during + * high load or with multiple targets if this + * is not set correctly. + * + * b) Selection Time-Out + * + * The ESP isn't very bright and will arbitrate + * for the bus and try to select a target + * forever if you let it. This value tells + * the ESP when it has taken too long to + * negotiate and that it should interrupt + * the CPU so we can see what happened. + * The value is computed as follows (from + * NCR/Symbios chip docs). + * + * (Time Out Period) * (Input Clock) + * STO = ---------------------------------- + * (8192) * (Clock Conversion Factor) + * + * You usually want the time out period to be + * around 250ms, I think we'll set it a little + * bit higher to account for fully loaded SCSI + * bus's and slow devices that don't respond so + * quickly to selection attempts. (yeah, I know + * this is out of spec. but there is a lot of + * buggy pieces of firmware out there so bite me) + * + * c) Imperical constants for synchronous offset + * and transfer period register values + * + * This entails the smallest and largest sync + * period we could ever handle on this ESP. + */ + + fmhz = esp->cfreq; + + if(fmhz <= (5000000)) + ccf = 0; + else + ccf = (((5000000 - 1) + (fmhz))/(5000000)); + if(!ccf || ccf > 8) { + /* If we can't find anything reasonable, + * just assume 20MHZ. This is the clock + * frequency of the older sun4c's where I've + * been unable to find the clock-frequency + * PROM property. All other machines provide + * useful values it seems. + */ + ccf = ESP_CCF_F4; + fmhz = (20000000); + } + if(ccf==(ESP_CCF_F7+1)) + esp->cfact = ESP_CCF_F0; + else if(ccf == ESP_CCF_NEVER) + esp->cfact = ESP_CCF_F2; + else + esp->cfact = ccf; + esp->cfreq = fmhz; + esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz); + esp->ctick = ESP_TICK(ccf, esp->ccycle); + esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); + esp->sync_defp = SYNC_DEFP_SLOW; + + + /* Fill in ehost data */ + esp->ehost->base = (unsigned char *) eregs; + esp->ehost->this_id = esp->scsi_id; + esp->ehost->irq = esp->irq; + + /* SCSI id mask */ + esp->scsi_id_mask = (1 << esp->scsi_id); + + /* Probe the revision of this esp */ + esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); + esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); + eregs->esp_cfg2 = esp->config2; +#ifndef SYMBIOS_HACK + if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) != + (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { + printk("NCR53C90(esp100) detected\n"); + esp->erev = esp100; + } else { +#endif + eregs->esp_cfg2 = esp->config2 = 0; + eregs->esp_cfg3 = 0; + eregs->esp_cfg3 = esp->config3[0] = 5; +#ifndef SYMBIOS_HACK + if(eregs->esp_cfg3 != 5) { + printk("NCR53C90A(esp100a) detected\n"); + esp->erev = esp100a; + } else { +#else + { +#endif + int target; + + for(target=0; target<8; target++) + esp->config3[target] = 0; + eregs->esp_cfg3 = 0; +#ifndef SYMBIOS_HACK + if(ccf > ESP_CCF_F5) { +#endif + printk("NCR53C9XF(espfast) detected\n"); + esp->erev = fast; + eregs->esp_cfg2 = esp->config2 = 0; + esp->sync_defp = SYNC_DEFP_FAST; +#ifndef SYMBIOS_HACK + } else { + printk("NCR53C9x(esp236) detected\n"); + esp->erev = esp236; + eregs->esp_cfg2 = esp->config2 = 0; + } + } +#endif + } + + /* Initialize the command queues */ + esp->current_SC = 0; + esp->disconnected_SC = 0; + esp->issue_SC = 0; + + /* Clear the state machines. */ + esp->targets_present = 0; + esp->resetting_bus = 0; + esp->snip = 0; + esp->targets_present = 0; + for(i = 0; i < 32; i++) + esp->espcmdlog[i] = 0; + esp->espcmdent = 0; + for(i = 0; i < 16; i++) { + esp->cur_msgout[i] = 0; + esp->cur_msgin[i] = 0; + } + esp->prevmsgout = esp->prevmsgin = 0; + esp->msgout_len = esp->msgin_len = 0; + + /* Reset the thing before we try anything... */ + esp_bootup_reset(esp, eregs); + + esps_in_use++; + + printk("SCSI ID %d Clock %d MHz CCF=%d Time-Out %d ", + esp->scsi_id, (esp->cfreq / 1000000), + esp->ccf, (int) esp->neg_defp); +} + +/* The info function will return whatever useful + * information the developer sees fit. If not provided, then + * the name field will be used instead. + */ +const char *esp_info(struct Scsi_Host *host) +{ + struct NCR_ESP *esp; + + esp = (struct NCR_ESP *) host->hostdata; + switch(esp->erev) { + case esp100: + return "Sparc ESP100 (NCR53C90)"; + case esp100a: + return "Sparc ESP100A (NCR53C90A)"; + case esp236: + return "Sparc ESP236"; + case fas236: + return "Sparc ESP236-FAST"; + case fashme: + return "Sparc ESP366-HME"; + case fas100a: + return "Sparc ESP100A-FAST"; + default: + panic("Bogon ESP revision"); + }; +} + +/* From Wolfgang Stanglmeier's NCR scsi driver. */ +struct info_str +{ + char *buffer; + int length; + int offset; + int pos; +}; + +static void copy_mem_info(struct info_str *info, char *data, int len) +{ + if (info->pos + len > info->length) + len = info->length - info->pos; + + if (info->pos + len < info->offset) { + info->pos += len; + return; + } + if (info->pos < info->offset) { + data += (info->offset - info->pos); + len -= (info->offset - info->pos); + } + + if (len > 0) { + memcpy(info->buffer + info->pos, data, len); + info->pos += len; + } +} + +static int copy_info(struct info_str *info, char *fmt, ...) +{ + va_list args; + char buf[81]; + int len; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + + copy_mem_info(info, buf, len); + return len; +} + +static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) +{ + struct info_str info; + int i; + + info.buffer = ptr; + info.length = len; + info.offset = offset; + info.pos = 0; + + copy_info(&info, "Sparc ESP Host Adapter:\n"); + copy_info(&info, "\tPROM node\t\t%08lx\n", (unsigned long) esp->prom_node); + copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name); + copy_info(&info, "\tESP Model\t\t"); + switch(esp->erev) { + case esp100: + copy_info(&info, "ESP100\n"); + break; + case esp100a: + copy_info(&info, "ESP100A\n"); + break; + case esp236: + copy_info(&info, "ESP236\n"); + break; + case fas236: + copy_info(&info, "FAS236\n"); + break; + case fas100a: + copy_info(&info, "FAS100A\n"); + break; + case fast: + copy_info(&info, "FAST\n"); + break; + case fashme: + copy_info(&info, "Happy Meal FAS\n"); + break; + case espunknown: + default: + copy_info(&info, "Unknown!\n"); + break; + }; +#ifdef CONFIG_SCSI_SUNESP + copy_info(&info, "\tDMA Revision\t\t"); + switch(((struct Linux_SBus_DMA*) (esp->dma))->revision) { + case dvmarev0: + copy_info(&info, "Rev 0\n"); + break; + case dvmaesc1: + copy_info(&info, "ESC Rev 1\n"); + break; + case dvmarev1: + copy_info(&info, "Rev 1\n"); + break; + case dvmarev2: + copy_info(&info, "Rev 2\n"); + break; + case dvmarev3: + copy_info(&info, "Rev 3\n"); + break; + case dvmarevplus: + copy_info(&info, "Rev 1+\n"); + break; + case dvmahme: + copy_info(&info, "Rev HME/FAS\n"); + break; + default: + copy_info(&info, "Unknown!\n"); + break; + }; +#endif + copy_info(&info, "\tLive Targets\t\t[ "); + for(i = 0; i < 15; i++) { + if(esp->targets_present & (1 << i)) + copy_info(&info, "%d ", i); + } + copy_info(&info, "]\n\n"); + + /* Now describe the state of each existing target. */ + copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); + for(i = 0; i < 15; i++) { + if(esp->targets_present & (1 << i)) { + Scsi_Device *SDptr = esp->ehost->host_queue; + + while((SDptr->host != esp->ehost) && + (SDptr->id != i) && + (SDptr->next)) + SDptr = SDptr->next; + + copy_info(&info, "%d\t\t", i); + copy_info(&info, "%08lx\t", esp->config3[i]); + copy_info(&info, "[%02lx,%02lx]\t\t\t", SDptr->sync_max_offset, + SDptr->sync_min_period); + copy_info(&info, "%s\t\t", SDptr->disconnect ? "yes" : "no"); + copy_info(&info, "%s\n", + (esp->config3[i] & ESP_CONFIG3_EWIDE) ? "yes" : "no"); + } + } + + return info.pos > info.offset? info.pos - info.offset : 0; +} + +/* ESP proc filesystem code. */ +int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + struct NCR_ESP *esp; + + if(inout) + return -EINVAL; /* not yet */ + + for_each_esp(esp) { + if(esp->ehost->host_no == hostno) + break; + } + if(!esp) + return -EINVAL; + + if(start) + *start = buffer; + + return esp_host_info(esp, buffer, offset, length); +} + +/* Some rules: + * + * 1) Never ever panic while something is live on the bus. + * If there is to be any chance of syncing the disks this + * rule is to be obeyed. + * + * 2) Any target that causes a foul condition will no longer + * have synchronous transfers done to it, no questions + * asked. + * + * 3) Keep register accesses to a minimum. Think about some + * day when we have Xbus machines this is running on and + * the ESP chip is on the other end of the machine on a + * different board from the cpu where this is running. + */ + +/* Fire off a command. We assume the bus is free and that the only + * case where we could see an interrupt is where we have disconnected + * commands active and they are trying to reselect us. + */ +static inline void esp_check_cmd(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + switch(sp->cmd_len) { + case 6: + case 10: + case 12: + esp->esp_slowcmd = 0; + break; + + default: + esp->esp_slowcmd = 1; + esp->esp_scmdleft = sp->cmd_len; + esp->esp_scmdp = &sp->cmnd[0]; + break; + }; +} + +static inline void build_sync_nego_msg(struct NCR_ESP *esp, int period, int offset) +{ + esp->cur_msgout[0] = EXTENDED_MESSAGE; + esp->cur_msgout[1] = 3; + esp->cur_msgout[2] = EXTENDED_SDTR; + esp->cur_msgout[3] = period; + esp->cur_msgout[4] = offset; + esp->msgout_len = 5; +} + +/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */ +static inline void build_wide_nego_msg(struct NCR_ESP *esp, int size) +{ + esp->cur_msgout[0] = EXTENDED_MESSAGE; + esp->cur_msgout[1] = 2; + esp->cur_msgout[2] = EXTENDED_WDTR; + switch(size) { + case 32: + esp->cur_msgout[3] = 2; + break; + case 16: + esp->cur_msgout[3] = 1; + break; + case 8: + default: + esp->cur_msgout[3] = 0; + break; + }; + + esp->msgout_len = 4; +} + +static inline void esp_exec_cmd(struct NCR_ESP *esp) +{ + struct ESP_regs *eregs = esp->eregs; + Scsi_Cmnd *SCptr; + Scsi_Device *SDptr; + volatile unchar *cmdp = esp->esp_command; + unsigned char the_esp_command; + int lun, target; + int i; + + /* Hold off if we've been reselected or an IRQ is showing... */ + if(esp->disconnected_SC || esp->dma_irq_p(esp)) + return; + + /* Grab first member of the issue queue. */ + SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); + + /* Safe to panic here because current_SC is null. */ + if(!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL"); + + SDptr = SCptr->device; + lun = SCptr->lun; + target = SCptr->target; + + esp->snip = 0; + esp->msgout_len = 0; + + /* Send it out whole, or piece by piece? The ESP + * only knows how to automatically send out 6, 10, + * and 12 byte commands. I used to think that the + * Linux SCSI code would never throw anything other + * than that to us, but then again there is the + * SCSI generic driver which can send us anything. + */ + esp_check_cmd(esp, SCptr); + + /* If arbitration/selection is successful, the ESP will leave + * ATN asserted, causing the target to go into message out + * phase. The ESP will feed the target the identify and then + * the target can only legally go to one of command, + * datain/out, status, or message in phase, or stay in message + * out phase (should we be trying to send a sync negotiation + * message after the identify). It is not allowed to drop + * BSY, but some buggy targets do and we check for this + * condition in the selection complete code. Most of the time + * we'll make the command bytes available to the ESP and it + * will not interrupt us until it finishes command phase, we + * cannot do this for command sizes the ESP does not + * understand and in this case we'll get interrupted right + * when the target goes into command phase. + * + * It is absolutely _illegal_ in the presence of SCSI-2 devices + * to use the ESP select w/o ATN command. When SCSI-2 devices are + * present on the bus we _must_ always go straight to message out + * phase with an identify message for the target. Being that + * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2 + * selections should not confuse SCSI-1 we hope. + */ + + if(SDptr->sync) { + /* this targets sync is known */ +#ifdef CONFIG_SCSI_SUNESP +do_sync_known: +#endif + if(SDptr->disconnect) + *cmdp++ = IDENTIFY(1, lun); + else + *cmdp++ = IDENTIFY(0, lun); + + if(esp->esp_slowcmd) { + the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); + esp_advance_phase(SCptr, in_slct_stop); + } else { + the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); + esp_advance_phase(SCptr, in_slct_norm); + } + } else if(!(esp->targets_present & (1<<target)) || !(SDptr->disconnect)) { + /* After the bootup SCSI code sends both the + * TEST_UNIT_READY and INQUIRY commands we want + * to at least attempt allowing the device to + * disconnect. + */ + ESPMISC(("esp: Selecting device for first time. target=%d " + "lun=%d\n", target, SCptr->lun)); + if(!SDptr->borken && !SDptr->disconnect) + SDptr->disconnect = 1; + + *cmdp++ = IDENTIFY(0, lun); + esp->prevmsgout = NOP; + esp_advance_phase(SCptr, in_slct_norm); + the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); + + /* Take no chances... */ + SDptr->sync_max_offset = 0; + SDptr->sync_min_period = 0; + } else { + int toshiba_cdrom_hwbug_wkaround = 0; + +#ifdef CONFIG_SCSI_SUNESP + /* Never allow disconnects or synchronous transfers on + * SparcStation1 and SparcStation1+. Allowing those + * to be enabled seems to lockup the machine completely. + */ + if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* But we are nice and allow tapes to disconnect. */ + if(SDptr->type == TYPE_TAPE) + SDptr->disconnect = 1; + else + SDptr->disconnect = 0; + SDptr->sync_max_offset = 0; + SDptr->sync_min_period = 0; + SDptr->sync = 1; + esp->snip = 0; + goto do_sync_known; + } +#endif + /* We've talked to this guy before, + * but never negotiated.. lets try, + * need to attempt WIDE first, before + * sync nego, as per SCSI 2 standard. + */ + if(esp->erev == fashme && !SDptr->wide) { + if(!SDptr->borken && + (SDptr->type != TYPE_ROM || + strncmp(SDptr->vendor, "TOSHIBA", 7))) { + build_wide_nego_msg(esp, 16); + esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE; + SDptr->wide = 1; + esp->wnip = 1; + goto after_nego_msg_built; + } else { + SDptr->wide = 1; + /* Fall through and try sync. */ + } + } + + if(!SDptr->borken) { + if((SDptr->type == TYPE_ROM) && + (!strncmp(SDptr->vendor, "TOSHIBA", 7))) { + /* Nice try sucker... */ + printk(KERN_INFO "esp%d: Disabling sync for buggy " + "Toshiba CDROM.\n", esp->esp_id); + toshiba_cdrom_hwbug_wkaround = 1; + build_sync_nego_msg(esp, 0, 0); + } else { + build_sync_nego_msg(esp, esp->sync_defp, 15); + } + } else { + build_sync_nego_msg(esp, 0, 0); + } + SDptr->sync = 1; + esp->snip = 1; + +after_nego_msg_built: + /* A fix for broken SCSI1 targets, when they disconnect + * they lock up the bus and confuse ESP. So disallow + * disconnects for SCSI1 targets for now until we + * find a better fix. + * + * Addendum: This is funny, I figured out what was going + * on. The blotzed SCSI1 target would disconnect, + * one of the other SCSI2 targets or both would be + * disconnected as well. The SCSI1 target would + * stay disconnected long enough that we start + * up a command on one of the SCSI2 targets. As + * the ESP is arbitrating for the bus the SCSI1 + * target begins to arbitrate as well to reselect + * the ESP. The SCSI1 target refuses to drop it's + * ID bit on the data bus even though the ESP is + * at ID 7 and is the obvious winner for any + * arbitration. The ESP is a poor sport and refuses + * to lose arbitration, it will continue indefinately + * trying to arbitrate for the bus and can only be + * stopped via a chip reset or SCSI bus reset. + * Therefore _no_ disconnects for SCSI1 targets + * thank you very much. ;-) + */ + if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) || +#if 1 /* Until I find out why HME barfs with disconnects enabled... */ + toshiba_cdrom_hwbug_wkaround || SDptr->borken || esp->erev == fashme) { +#else + toshiba_cdrom_hwbug_wkaround || SDptr->borken) { +#endif + printk(KERN_INFO "esp%d: Disabling DISCONNECT for target %d " + "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun); + SDptr->disconnect = 0; + *cmdp++ = IDENTIFY(0, lun); + } else { + *cmdp++ = IDENTIFY(1, lun); + } + + /* ESP fifo is only so big... + * Make this look like a slow command. + */ + esp->esp_slowcmd = 1; + esp->esp_scmdleft = SCptr->cmd_len; + esp->esp_scmdp = &SCptr->cmnd[0]; + + the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); + esp_advance_phase(SCptr, in_slct_msg); + } + + if(!esp->esp_slowcmd) + for(i = 0; i < SCptr->cmd_len; i++) + *cmdp++ = SCptr->cmnd[i]; + + /* HME sucks... */ + if(esp->erev == fashme) + eregs->esp_busid = (target & 0xf) | + (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT); + else + eregs->esp_busid = (target & 7); + eregs->esp_soff = SDptr->sync_max_offset; + eregs->esp_stp = SDptr->sync_min_period; + if(esp->erev > esp100a) + eregs->esp_cfg3 = esp->config3[target]; + + i = (cmdp - esp->esp_command); + + /* Set up the DMA and ESP counters */ + if(esp->do_pio_cmds){ + int j = 0; + + for(;j<i;j++) + eregs->esp_fdata = esp->esp_command[j]; + the_esp_command &= ~ESP_CMD_DMA; + + /* Tell ESP to "go". */ + esp_cmd(esp, eregs, the_esp_command); + } else { + if(esp->erev == fashme) { + esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */ + + /* Set up the HME counters */ + eregs->esp_tclow = i; + eregs->esp_tcmed = 0; + eregs->fas_rlo = 0; + eregs->fas_rhi = 0; + esp_cmd(esp, eregs, the_esp_command); + esp->dma_init_write(esp, esp->esp_command_dvma, 16); + } else { + /* Set up the ESP counters */ + eregs->esp_tclow = i; + eregs->esp_tcmed = 0; + esp->dma_init_write(esp, esp->esp_command_dvma, i); + + /* Tell ESP to "go". */ + esp_cmd(esp, eregs, the_esp_command); + } + } +} + +/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */ +int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + struct NCR_ESP *esp; + unsigned long flags; + + /* Set up func ptr and initial driver cmd-phase. */ + SCpnt->scsi_done = done; + SCpnt->SCp.phase = not_issued; + + esp = (struct NCR_ESP *) SCpnt->host->hostdata; + + if(esp->dma_led_on) + esp->dma_led_on(esp); + + /* We use the scratch area. */ + ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun)); + ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun)); + if(!SCpnt->use_sg) { + ESPQUEUE(("!use_sg\n")); + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + SCpnt->SCp.buffer = + (struct scatterlist *) SCpnt->request_buffer; + SCpnt->SCp.buffers_residual = 0; +#ifdef CONFIG_SCSI_SUNESP + /* Sneaky. */ + SCpnt->SCp.have_data_in = mmu_get_scsi_one((char *)SCpnt->SCp.buffer, + SCpnt->SCp.this_residual, + ((struct linux_sbus_device*) (esp->edev))->my_bus); + /* XXX The casts are extremely gross, but with 64-bit kernel + * XXX and 32-bit SBUS what am I to do? -DaveM + */ + SCpnt->SCp.ptr = (char *)((unsigned long)SCpnt->SCp.have_data_in); +#else + SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr = + (char *) VTOP((unsigned long) SCpnt->request_buffer); +#endif + + } else { + ESPQUEUE(("use_sg ")); +#ifdef DEBUG_ESP_SG + printk("esp%d: sglist at %p with %d buffers\n", + esp->esp_id, SCpnt->buffer, SCpnt->use_sg); +#endif + SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; +#ifdef CONFIG_SCSI_SUNESP + mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer, + SCpnt->SCp.buffers_residual, + ((struct linux_sbus_device *) (esp->edev))->my_bus); + /* XXX Again these casts are sick... -DaveM */ + SCpnt->SCp.ptr=(char *)((unsigned long)SCpnt->SCp.buffer->dvma_address); +#else + SCpnt->SCp.ptr = + (char *) VTOP((unsigned long) SCpnt->SCp.buffer->address); +#endif + } + SCpnt->SCp.Status = CHECK_CONDITION; + SCpnt->SCp.Message = 0xff; + SCpnt->SCp.sent_command = 0; + + /* Place into our queue. */ + if(SCpnt->cmnd[0] == REQUEST_SENSE) { + ESPQUEUE(("RQSENSE\n")); + prepend_SC(&esp->issue_SC, SCpnt); + } else { + ESPQUEUE(("\n")); + append_SC(&esp->issue_SC, SCpnt); + } + + save_and_cli(flags); + + /* Run it now if we can. */ + if(!esp->current_SC && !esp->resetting_bus) + esp_exec_cmd(esp); + + restore_flags(flags); + return 0; +} + +/* Only queuing supported in this ESP driver. */ +int esp_command(Scsi_Cmnd *SCpnt) +{ +#ifdef DEBUG_ESP + struct NCR_ESP *esp = (struct NCR_ESP *) SCpnt->host->hostdata; +#endif + + ESPLOG(("esp%d: esp_command() called...\n", esp->esp_id)); + return -1; +} + +/* Dump driver state. */ +static inline void esp_dump_cmd(Scsi_Cmnd *SCptr) +{ + ESPLOG(("[tgt<%02x> lun<%02x> " + "pphase<%s> cphase<%s>]", + SCptr->target, SCptr->lun, + phase_string(SCptr->SCp.sent_command), + phase_string(SCptr->SCp.phase))); +} + +static inline void esp_dump_state(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; +#ifdef DEBUG_ESP_CMDS + int i; +#endif + + ESPLOG(("esp%d: dumping state\n", esp->esp_id)); + + /* Print DMA status */ + esp->dma_dump_state(esp); + + ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n", + esp->esp_id, esp->sreg, esp->seqreg, esp->ireg)); + ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n", + esp->esp_id, eregs->esp_status, eregs->esp_sstep, eregs->esp_intrpt)); +#ifdef DEBUG_ESP_CMDS + printk("esp%d: last ESP cmds [", esp->esp_id); + i = (esp->espcmdent - 1) & 31; + printk("<"); + esp_print_cmd(esp->espcmdlog[i]); + printk(">"); + i = (i - 1) & 31; + printk("<"); + esp_print_cmd(esp->espcmdlog[i]); + printk(">"); + i = (i - 1) & 31; + printk("<"); + esp_print_cmd(esp->espcmdlog[i]); + printk(">"); + i = (i - 1) & 31; + printk("<"); + esp_print_cmd(esp->espcmdlog[i]); + printk(">"); + printk("]\n"); +#endif /* (DEBUG_ESP_CMDS) */ + + if(SCptr) { + ESPLOG(("esp%d: current command ", esp->esp_id)); + esp_dump_cmd(SCptr); + } + ESPLOG(("\n")); + SCptr = esp->disconnected_SC; + ESPLOG(("esp%d: disconnected ", esp->esp_id)); + while(SCptr) { + esp_dump_cmd(SCptr); + SCptr = (Scsi_Cmnd *) SCptr->host_scribble; + } + ESPLOG(("\n")); +} + +/* Abort a command. */ +int esp_abort(Scsi_Cmnd *SCptr) +{ + struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata; + struct ESP_regs *eregs = esp->eregs; + int don; + unsigned long flags; + + ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); + esp_dump_state(esp, eregs); + + /* Wheee, if this is the current command on the bus, the + * best we can do is assert ATN and wait for msgout phase. + * This should even fix a hung SCSI bus when we lose state + * in the driver and timeout because the eventual phase change + * will cause the ESP to (eventually) give an interrupt. + */ + save_and_cli(flags); + if(esp->current_SC == SCptr) { + esp->cur_msgout[0] = ABORT; + esp->msgout_len = 1; + esp->msgout_ctr = 0; + esp_cmd(esp, eregs, ESP_CMD_SATN); + restore_flags(flags); + return SCSI_ABORT_PENDING; + } + restore_flags(flags); + + /* If it is still in the issue queue then we can safely + * call the completion routine and report abort success. + */ + don = esp->dma_ports_p(esp); + if(don) { + esp->dma_ints_off(esp); + synchronize_irq(); + } + if(esp->issue_SC) { + Scsi_Cmnd **prev, *this; + for(prev = (&esp->issue_SC), this = esp->issue_SC; + this; + prev = (Scsi_Cmnd **) &(this->host_scribble), + this = (Scsi_Cmnd *) this->host_scribble) { + if(this == SCptr) { + *prev = (Scsi_Cmnd *) this->host_scribble; + this->host_scribble = NULL; + this->result = DID_ABORT << 16; + this->done(this); + if(don) + esp->dma_ints_on(esp); + return SCSI_ABORT_SUCCESS; + } + } + } + + /* Yuck, the command to abort is disconnected, it is not + * worth trying to abort it now if something else is live + * on the bus at this time. So, we let the SCSI code wait + * a little bit and try again later. + */ + if(esp->current_SC) + return SCSI_ABORT_BUSY; + + /* It's disconnected, we have to reconnect to re-establish + * the nexus and tell the device to abort. However, we really + * cannot 'reconnect' per se, therefore we tell the upper layer + * the safest thing we can. This is, wait a bit, if nothing + * happens, we are really hung so reset the bus. + */ + + return SCSI_ABORT_SNOOZE; +} + +/* Reset ESP chip, reset hanging bus, then kill active and + * disconnected commands for targets without soft reset. + */ +int esp_reset(Scsi_Cmnd *SCptr, unsigned int how) +{ + struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata; + struct ESP_regs *eregs = esp->eregs; + + ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); + esp->resetting_bus = 1; + esp_cmd(esp, eregs, ESP_CMD_RS); + return SCSI_RESET_PENDING; +} + +/* Internal ESP done function. */ +static void esp_done(struct NCR_ESP *esp, int error) +{ + Scsi_Cmnd *done_SC; + + if(esp->current_SC) { + unsigned long flags; + + done_SC = esp->current_SC; + esp->current_SC = NULL; + + /* Free dvma entry. */ + if(!done_SC->use_sg) { +#ifdef CONFIG_SCSI_SUNESP + /* Sneaky. */ + mmu_release_scsi_one(done_SC->SCp.have_data_in, + done_SC->request_bufflen, + ((struct linux_sbus_device *) (esp->edev))->my_bus); +#endif + } else { +#ifdef DEBUG_ESP_SG + printk("esp%d: unmapping sg ", esp->esp_id); +#endif +#ifdef CONFIG_SCSI_SUNESP + mmu_release_scsi_sgl((struct mmu_sglist *) done_SC->buffer, + done_SC->use_sg - 1, + ((struct linux_sbus_device *) (esp->edev))->my_bus); +#endif +#ifdef DEBUG_ESP_SG + printk("done.\n"); +#endif + } + + done_SC->result = error; + done_SC->scsi_done(done_SC); + + save_and_cli(flags); + + /* Bus is free, issue any commands in the queue. */ + if(esp->issue_SC && !esp->current_SC) + esp_exec_cmd(esp); + + restore_flags(flags); + } else { + /* Panic is safe as current_SC is null so we may still + * be able to accept more commands to sync disk buffers. + */ + ESPLOG(("panicing\n")); + panic("esp: done() called with NULL esp->current_SC"); + } +} + +/* Wheee, ESP interrupt engine. */ + +enum { + do_phase_determine, do_reset_bus, do_reset_complete, + do_work_bus, do_intr_end, +}; + +/* Forward declarations. */ +static int esp_do_phase_determine(struct NCR_ESP *esp, + struct ESP_regs *eregs); +static int esp_do_data_finale(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs); +static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs); + +static inline int sreg_datainp(unchar sreg) +{ + return (sreg & ESP_STAT_PMASK) == ESP_DIP; +} + +static inline int sreg_dataoutp(unchar sreg) +{ + return (sreg & ESP_STAT_PMASK) == ESP_DOP; +} + +/* Did they drop these fabs on the floor or what?!?!! */ +static inline void hme_fifo_hwbug_workaround(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + unchar status = esp->sreg; + + /* Cannot safely frob the fifo for these following cases. */ + if(sreg_datainp(status) || sreg_dataoutp(status) || + (esp->current_SC && esp->current_SC->SCp.phase == in_data_done)) { + ESPHME(("<wkaround_skipped>")); + return; + } else { + unsigned long count = 0; + unsigned long fcnt = eregs->esp_fflags & ESP_FF_FBYTES; + + /* The HME stores bytes in multiples of 2 in the fifo. */ + ESPHME(("hme_fifo[fcnt=%d", (int)fcnt)); + while(fcnt) { + esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata; + esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata; + ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1])); + fcnt--; + } + if(eregs->esp_status2 & ESP_STAT2_F1BYTE) { + ESPHME(("<poke_byte>")); + eregs->esp_fdata = 0; + esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata; + ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1])); + ESPHME(("CMD_FLUSH")); + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } else { + ESPHME(("no_xtra_byte")); + } + esp->hme_fifo_workaround_count = count; + ESPHME(("wkarnd_cnt=%d]", (int)count)); + } +} + +static inline void hme_fifo_push(struct NCR_ESP *esp, struct ESP_regs *eregs, + unchar *bytes, unchar count) +{ + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + while(count) { + eregs->esp_fdata = *bytes++; + eregs->esp_fdata = 0; + count--; + } +} + +/* We try to avoid some interrupts by jumping ahead and see if the ESP + * has gotten far enough yet. Hence the following. + */ +static inline int skipahead1(struct NCR_ESP *esp, struct ESP_regs *eregs, + Scsi_Cmnd *scp, int prev_phase, int new_phase) +{ + if(scp->SCp.sent_command != prev_phase) + return 0; + + if(esp->dma_irq_p(esp)) { + /* Yes, we are able to save an interrupt. */ + esp->sreg = eregs->esp_status; + if(esp->erev == fashme) { + /* This chip is really losing. */ + ESPHME(("HME[")); + /* Must latch fifo before reading the interrupt + * register else garbage ends up in the FIFO + * which confuses the driver utterly. + * Happy Meal indeed.... + */ + ESPHME(("fifo_workaround]")); + hme_fifo_hwbug_workaround(esp, eregs); + } + esp->ireg = eregs->esp_intrpt; + esp->sreg &= ~(ESP_STAT_INTR); + if(!(esp->ireg & ESP_INTR_SR)) + return 0; + else + return do_reset_complete; + } + /* Ho hum, target is taking forever... */ + scp->SCp.sent_command = new_phase; /* so we don't recurse... */ + return do_intr_end; +} + +static inline int skipahead2(struct NCR_ESP *esp, + struct ESP_regs *eregs, + Scsi_Cmnd *scp, int prev_phase1, int prev_phase2, + int new_phase) +{ + if(scp->SCp.sent_command != prev_phase1 && + scp->SCp.sent_command != prev_phase2) + return 0; + if(esp->dma_irq_p(esp)) { + /* Yes, we are able to save an interrupt. */ + esp->sreg = eregs->esp_status; + if(esp->erev == fashme) { + /* This chip is really losing. */ + ESPHME(("HME[")); + + /* Must latch fifo before reading the interrupt + * register else garbage ends up in the FIFO + * which confuses the driver utterly. + * Happy Meal indeed.... + */ + ESPHME(("fifo_workaround]")); + hme_fifo_hwbug_workaround(esp, eregs); + } + esp->ireg = eregs->esp_intrpt; + esp->sreg &= ~(ESP_STAT_INTR); + if(!(esp->ireg & ESP_INTR_SR)) + return 0; + else + return do_reset_complete; + } + /* Ho hum, target is taking forever... */ + scp->SCp.sent_command = new_phase; /* so we don't recurse... */ + return do_intr_end; +} + +/* Misc. esp helper routines. */ +static inline void esp_setcount(struct ESP_regs *eregs, int cnt, int hme) +{ + eregs->esp_tclow = (cnt & 0xff); + eregs->esp_tcmed = ((cnt >> 8) & 0xff); + if(hme) { + eregs->fas_rlo = 0; + eregs->fas_rhi = 0; + } +} + +static inline int esp_getcount(struct ESP_regs *eregs) +{ + return (((eregs->esp_tclow)&0xff) | + (((eregs->esp_tcmed)&0xff) << 8)); +} + +static inline int fcount(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + if(esp->erev == fashme) + return esp->hme_fifo_workaround_count; + else + return eregs->esp_fflags & ESP_FF_FBYTES; +} + +static inline int fnzero(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + if(esp->erev == fashme) + return 0; + else + return eregs->esp_fflags & ESP_FF_ONOTZERO; +} + +/* XXX speculative nops unnecessary when continuing amidst a data phase + * XXX even on esp100!!! another case of flooding the bus with I/O reg + * XXX writes... + */ +static inline void esp_maybe_nop(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + if(esp->erev == esp100) + esp_cmd(esp, eregs, ESP_CMD_NULL); +} + +static inline int sreg_to_dataphase(unchar sreg) +{ + if((sreg & ESP_STAT_PMASK) == ESP_DOP) + return in_dataout; + else + return in_datain; +} + +/* The ESP100 when in synchronous data phase, can mistake a long final + * REQ pulse from the target as an extra byte, it places whatever is on + * the data lines into the fifo. For now, we will assume when this + * happens that the target is a bit quirky and we don't want to + * be talking synchronously to it anyways. Regardless, we need to + * tell the ESP to eat the extraneous byte so that we can proceed + * to the next phase. + */ +static inline int esp100_sync_hwbug(struct NCR_ESP *esp, struct ESP_regs *eregs, + Scsi_Cmnd *sp, int fifocnt) +{ + /* Do not touch this piece of code. */ + if((!(esp->erev == esp100)) || + (!(sreg_datainp((esp->sreg = eregs->esp_status)) && !fifocnt) && + !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) { + if(sp->SCp.phase == in_dataout) + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + return 0; + } else { + /* Async mode for this guy. */ + build_sync_nego_msg(esp, 0, 0); + + /* Ack the bogus byte, but set ATN first. */ + esp_cmd(esp, eregs, ESP_CMD_SATN); + esp_cmd(esp, eregs, ESP_CMD_MOK); + return 1; + } +} + +/* This closes the window during a selection with a reselect pending, because + * we use DMA for the selection process the FIFO should hold the correct + * contents if we get reselected during this process. So we just need to + * ack the possible illegal cmd interrupt pending on the esp100. + */ +static inline int esp100_reconnect_hwbug(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + volatile unchar junk; + + if(esp->erev != esp100) + return 0; + junk = eregs->esp_intrpt; + + if(junk & ESP_INTR_SR) + return 1; + return 0; +} + +/* This verifies the BUSID bits during a reselection so that we know which + * target is talking to us. + */ +static inline int reconnect_target(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + int it, me = esp->scsi_id_mask, targ = 0; + + if(2 != fcount(esp, eregs)) + return -1; + if(esp->erev == fashme) { + /* HME does not latch it's own BUS ID bits during + * a reselection. Also the target number is given + * as an unsigned char, not as a sole bit number + * like the other ESP's do. + * Happy Meal indeed.... + */ + targ = esp->hme_fifo_workaround_buffer[0]; + } else { + it = eregs->esp_fdata; + if(!(it & me)) + return -1; + it &= ~me; + if(it & (it - 1)) + return -1; + while(!(it & 1)) + targ++, it >>= 1; + } + return targ; +} + +/* This verifies the identify from the target so that we know which lun is + * being reconnected. + */ +static inline int reconnect_lun(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + int lun; + + if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) + return -1; + if(esp->erev == fashme) + lun = esp->hme_fifo_workaround_buffer[1]; + else + lun = eregs->esp_fdata; + if(esp->sreg & ESP_STAT_PERR) + return 0; + if((lun & 0x40) || !(lun & 0x80)) + return -1; + return lun & 7; +} + +/* This puts the driver in a state where it can revitalize a command that + * is being continued due to reselection. + */ +static inline void esp_connect(struct NCR_ESP *esp, struct ESP_regs *eregs, + Scsi_Cmnd *sp) +{ + Scsi_Device *dp = sp->device; + eregs->esp_soff = dp->sync_max_offset; + eregs->esp_stp = dp->sync_min_period; + if(esp->erev > esp100a) + eregs->esp_cfg3 = esp->config3[sp->target]; + if(esp->erev == fashme) + eregs->esp_busid = (sp->target & 0xf) | + (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT); + esp->current_SC = sp; +} + +/* This will place the current working command back into the issue queue + * if we are to receive a reselection amidst a selection attempt. + */ +static inline void esp_reconnect(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + if(!esp->disconnected_SC) + printk("esp%d: Weird, being reselected but disconnected " + "command queue is empty.\n", esp->esp_id); + esp->snip = 0; + esp->current_SC = 0; + sp->SCp.phase = not_issued; + append_SC(&esp->issue_SC, sp); +} + +/* Begin message in phase. */ +static inline int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + /* Must be very careful with the fifo on the HME */ + if((esp->erev != fashme) || !(eregs->esp_status2 & ESP_STAT2_FEMPTY)) + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + esp_maybe_nop(esp, eregs); + esp_cmd(esp, eregs, ESP_CMD_TI); + esp->msgin_len = 1; + esp->msgin_ctr = 0; + esp_advance_phase(esp->current_SC, in_msgindone); + return do_work_bus; +} + +static inline void advance_sg(Scsi_Cmnd *sp) +{ + ++sp->SCp.buffer; + --sp->SCp.buffers_residual; + sp->SCp.this_residual = sp->SCp.buffer->length; +#ifdef CONFIG_SCSI_SUNESP + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address); +#else + sp->SCp.ptr = (char *)VTOP((unsigned long) sp->SCp.buffer->address); +#endif +} + +/* Please note that the way I've coded these routines is that I _always_ + * check for a disconnect during any and all information transfer + * phases. The SCSI standard states that the target _can_ cause a BUS + * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note + * that during information transfer phases the target controls every + * change in phase, the only thing the initiator can do is "ask" for + * a message out phase by driving ATN true. The target can, and sometimes + * will, completely ignore this request so we cannot assume anything when + * we try to force a message out phase to abort/reset a target. Most of + * the time the target will eventually be nice and go to message out, so + * we may have to hold on to our state about what we want to tell the target + * for some period of time. + */ + +/* I think I have things working here correctly. Even partial transfers + * within a buffer or sub-buffer should not upset us at all no matter + * how bad the target and/or ESP fucks things up. + */ + +static inline int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + int thisphase, hmuch; + + ESPDATA(("esp_do_data: ")); + esp_maybe_nop(esp, eregs); + thisphase = sreg_to_dataphase(esp->sreg); + esp_advance_phase(SCptr, thisphase); + ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT")); + hmuch = esp->dma_can_transfer(esp, SCptr); + ESPDATA(("hmuch<%d> ", hmuch)); + esp->current_transfer_size = hmuch; + if(esp->erev == fashme) { + /* Touchy chip, this stupid HME scsi adapter... */ + esp_setcount(eregs, hmuch, 1); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + + if(thisphase == in_datain) + esp->dma_init_read(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch); + else + esp->dma_init_write(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch); + } else { + esp_setcount(eregs, hmuch, 0); + esp->dma_setup(esp, + (__u32)((unsigned long)SCptr->SCp.ptr), + hmuch, (thisphase == in_datain)); + ESPDATA(("DMA|TI --> do_intr_end\n")); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + } + return do_intr_end; +} + +/* See how successful the data transfer was. */ +static inline int esp_do_data_finale(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; + + if(esp->dma_led_off) + esp->dma_led_off(esp); + + ESPDATA(("esp_do_data_finale: ")); + + if(SCptr->SCp.phase == in_datain) { + if(esp->sreg & ESP_STAT_PERR) { + /* Yuck, parity error. The ESP asserts ATN + * so that we can go to message out phase + * immediately and inform the target that + * something bad happened. + */ + ESPLOG(("esp%d: data bad parity detected.\n", + esp->esp_id)); + esp->cur_msgout[0] = INITIATOR_ERROR; + esp->msgout_len = 1; + } + if(esp->dma_drain) + esp->dma_drain(esp); + } + if(esp->dma_invalidate) + esp->dma_invalidate(esp); + + /* This could happen for the above parity error case. */ + if(!(esp->ireg == ESP_INTR_BSERV)) { + /* Please go to msgout phase, please please please... */ + ESPLOG(("esp%d: !BSERV after data, probably to msgout\n", + esp->esp_id)); + return esp_do_phase_determine(esp, eregs); + } + + /* Check for partial transfers and other horrible events. + * Note, here we read the real fifo flags register even + * on HME broken adapters because we skip the HME fifo + * workaround code in esp_handle() if we are doing data + * phase things. We don't want to fuck directly with + * the fifo like that, especially if doing syncronous + * transfers! Also, will need to double the count on + * HME if we are doing wide transfers, as the HME fifo + * will move and count 16-bit quantities during wide data. + * SMCC _and_ Qlogic can both bite me. + */ + fifocnt = eregs->esp_fflags & ESP_FF_FBYTES; + if(esp->erev != fashme) + ecount = esp_getcount(eregs); + bytes_sent = esp->current_transfer_size; + + /* Uhhh, might not want both of these conditionals to run + * at once on HME due to the fifo problems it has. Consider + * changing it to: + * + * if(!(esp->sreg & ESP_STAT_TCNT)) { + * bytes_sent -= ecount; + * } else if(SCptr->SCp.phase == in_dataout) { + * bytes_sent -= fifocnt; + * } + * + * But only for the HME case, leave the current code alone + * for all other ESP revisions as we know the existing code + * works just fine for them. + */ + ESPDATA(("trans_sz=%d, ", bytes_sent)); + if(esp->erev == fashme) { + if(!(esp->sreg & ESP_STAT_TCNT)) { + bytes_sent -= esp_getcount(eregs); + } else if(SCptr->SCp.phase == in_dataout) { + bytes_sent -= fifocnt; + } + } else { + if(!(esp->sreg & ESP_STAT_TCNT)) + bytes_sent -= ecount; + if(SCptr->SCp.phase == in_dataout) + bytes_sent -= fifocnt; + } + + ESPDATA(("bytes_sent=%d, ", bytes_sent)); + + /* If we were in synchronous mode, check for peculiarities. */ + if(esp->erev == fashme) { + if(SCptr->device->sync_max_offset) { + if(SCptr->SCp.phase == in_dataout) + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } else { + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } + } else { + if(SCptr->device->sync_max_offset) + bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt); + else + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } + + /* Until we are sure of what has happened, we are certainly + * in the dark. + */ + esp_advance_phase(SCptr, in_the_dark); + + if(bytes_sent < 0) { + /* I've seen this happen due to lost state in this + * driver. No idea why it happened, but allowing + * this value to be negative caused things to + * lock up. This allows greater chance of recovery. + */ + ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); + ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", + esp->esp_id, + esp->current_transfer_size, fifocnt, ecount)); + ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n", + esp->esp_id, + SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual)); + bytes_sent = 0; + } + + /* Update the state of our transfer. */ + SCptr->SCp.ptr += bytes_sent; + SCptr->SCp.this_residual -= bytes_sent; + if(SCptr->SCp.this_residual < 0) { + /* shit */ + printk("esp%d: Data transfer overrun.\n", esp->esp_id); + SCptr->SCp.this_residual = 0; + } + + /* Maybe continue. */ + if(!bogus_data) { + ESPDATA(("!bogus_data, ")); + /* NO MATTER WHAT, we advance the scatterlist, + * if the target should decide to disconnect + * in between scatter chunks (which is common) + * we could die horribly! I used to have the sg + * advance occur only if we are going back into + * (or are staying in) a data phase, you can + * imagine the hell I went through trying to + * figure this out. + */ + if(SCptr->use_sg && !SCptr->SCp.this_residual) + advance_sg(SCptr); + if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) { + ESPDATA(("to more data\n")); + return esp_do_data(esp, eregs); + } + ESPDATA(("to new phase\n")); + return esp_do_phase_determine(esp, eregs); + } + /* Bogus data, just wait for next interrupt. */ + ESPLOG(("esp%d: bogus_data during end of data phase\n", + esp->esp_id)); + return do_intr_end; +} + +/* Either a command is completing or a target is dropping off the bus + * to continue the command in the background so we can do other work. + */ +static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + int rval; + + rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing); + if(rval) + return rval; + + if(esp->ireg != ESP_INTR_DC) { + ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id)); + return do_reset_bus; /* target will not drop BSY... */ + } + esp->msgout_len = 0; + esp->prevmsgout = NOP; + if(esp->prevmsgin == COMMAND_COMPLETE) { + /* Normal end of nexus. */ + if(esp->disconnected_SC || (esp->erev == fashme)) + esp_cmd(esp, eregs, ESP_CMD_ESEL); + + if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD && + ((1<<SCptr->target) & esp->targets_present) && + SCptr->device->sync && SCptr->device->sync_max_offset) { + /* SCSI standard says that the synchronous capabilities + * should be renegotiated at this point. Most likely + * we are about to request sense from this target + * in which case we want to avoid using sync + * transfers until we are sure of the current target + * state. + */ + ESPMISC(("esp: Status <%d> for target %d lun %d\n", + SCptr->SCp.Status, SCptr->target, SCptr->lun)); + + /* But don't do this when spinning up a disk at + * boot time while we poll for completion as it + * fills up the console with messages. Also, tapes + * can report not ready many times right after + * loading up a tape. + */ + if(SCptr->cmnd[0] != START_STOP && + SCptr->data_cmnd[0] != START_STOP && + SCptr->cmnd[0] != TEST_UNIT_READY && + SCptr->data_cmnd[0] != TEST_UNIT_READY && + !(SCptr->device->type == TYPE_TAPE && + (SCptr->cmnd[0] == TEST_UNIT_READY || + SCptr->data_cmnd[0] == TEST_UNIT_READY || + SCptr->cmnd[0] == MODE_SENSE || + SCptr->data_cmnd[0] == MODE_SENSE))) + SCptr->device->sync = 0; + } + ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun)); + esp_done(esp, ((SCptr->SCp.Status & 0xff) | + ((SCptr->SCp.Message & 0xff)<<8) | + (DID_OK << 16))); + } else if(esp->prevmsgin == DISCONNECT) { + /* Normal disconnect. */ + esp_cmd(esp, eregs, ESP_CMD_ESEL); + ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun)); + append_SC(&esp->disconnected_SC, SCptr); + esp->current_SC = NULL; + if(esp->issue_SC) + esp_exec_cmd(esp); + } else { + /* Driver bug, we do not expect a disconnect here + * and should not have advanced the state engine + * to in_freeing. + */ + ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n", + esp->esp_id)); + return do_reset_bus; + } + return do_intr_end; +} + +/* Do the needy when a target tries to reconnect to us. */ +static inline int esp_do_reconnect(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + int lun, target; + Scsi_Cmnd *SCptr; + + /* Check for all bogus conditions first. */ + target = reconnect_target(esp, eregs); + if(target < 0) { + ESPDISC(("bad bus bits\n")); + return do_reset_bus; + } + lun = reconnect_lun(esp, eregs); + if(lun < 0) { + ESPDISC(("target=%2x, bad identify msg\n", target)); + return do_reset_bus; + } + + /* Things look ok... */ + ESPDISC(("R<%02x,%02x>", target, lun)); + + /* Must flush both FIFO and the DVMA on HME. */ + if(esp->erev == fashme) { + /* XXX this still doesn't fix the problem... */ + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + if(esp->dma_invalidate) + esp->dma_invalidate(esp); + } else { + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + if(esp100_reconnect_hwbug(esp, eregs)) + return do_reset_bus; + esp_cmd(esp, eregs, ESP_CMD_NULL); + } + + SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun); + if(!SCptr) { + Scsi_Cmnd *sp; + + ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", + esp->esp_id)); + ESPLOG(("QUEUE DUMP\n")); + sp = esp->issue_SC; + ESPLOG(("esp%d: issue_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->current_SC; + ESPLOG(("esp%d: current_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->disconnected_SC; + ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + return do_reset_bus; + } + esp_connect(esp, eregs, SCptr); + esp_cmd(esp, eregs, ESP_CMD_MOK); + + /* No need for explicit restore pointers operation. */ + esp->snip = 0; + esp_advance_phase(SCptr, in_the_dark); + return do_intr_end; +} + +/* End of NEXUS (hopefully), pick up status + message byte then leave if + * all goes well. + */ +static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + int intr, rval; + + rval = skipahead1(esp, eregs, SCptr, in_the_dark, in_status); + if(rval) + return rval; + + intr = esp->ireg; + ESPSTAT(("esp_do_status: ")); + if(intr != ESP_INTR_DC) { + int message_out = 0; /* for parity problems */ + + /* Ack the message. */ + ESPSTAT(("ack msg, ")); + esp_cmd(esp, eregs, ESP_CMD_MOK); + + if(esp->dma_poll) + esp->dma_poll(esp, (unsigned char *) esp->esp_command); + + ESPSTAT(("got something, ")); + /* ESP chimes in with one of + * + * 1) function done interrupt: + * both status and message in bytes + * are available + * + * 2) bus service interrupt: + * only status byte was acquired + * + * 3) Anything else: + * can't happen, but we test for it + * anyways + * + * ALSO: If bad parity was detected on either + * the status _or_ the message byte then + * the ESP has asserted ATN on the bus + * and we must therefore wait for the + * next phase change. + */ + if(intr & ESP_INTR_FDONE) { + /* We got it all, hallejulia. */ + ESPSTAT(("got both, ")); + SCptr->SCp.Status = esp->esp_command[0]; + SCptr->SCp.Message = esp->esp_command[1]; + esp->prevmsgin = SCptr->SCp.Message; + esp->cur_msgin[0] = SCptr->SCp.Message; + if(esp->sreg & ESP_STAT_PERR) { + /* There was bad parity for the + * message byte, the status byte + * was ok. + */ + message_out = MSG_PARITY_ERROR; + } + } else if(intr == ESP_INTR_BSERV) { + /* Only got status byte. */ + ESPLOG(("esp%d: got status only, ", esp->esp_id)); + if(!(esp->sreg & ESP_STAT_PERR)) { + SCptr->SCp.Status = esp->esp_command[0]; + SCptr->SCp.Message = 0xff; + } else { + /* The status byte had bad parity. + * we leave the scsi_pointer Status + * field alone as we set it to a default + * of CHECK_CONDITION in esp_queue. + */ + message_out = INITIATOR_ERROR; + } + } else { + /* This shouldn't happen ever. */ + ESPSTAT(("got bolixed\n")); + esp_advance_phase(SCptr, in_the_dark); + return esp_do_phase_determine(esp, eregs); + } + + if(!message_out) { + ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status, + SCptr->SCp.Message)); + if(SCptr->SCp.Message == COMMAND_COMPLETE) { + ESPSTAT(("and was COMMAND_COMPLETE\n")); + esp_advance_phase(SCptr, in_freeing); + return esp_do_freebus(esp, eregs); + } else { + ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n", + esp->esp_id)); + esp->msgin_len = esp->msgin_ctr = 1; + esp_advance_phase(SCptr, in_msgindone); + return esp_do_msgindone(esp, eregs); + } + } else { + /* With luck we'll be able to let the target + * know that bad parity happened, it will know + * which byte caused the problems and send it + * again. For the case where the status byte + * receives bad parity, I do not believe most + * targets recover very well. We'll see. + */ + ESPLOG(("esp%d: bad parity somewhere mout=%2x\n", + esp->esp_id, message_out)); + esp->cur_msgout[0] = message_out; + esp->msgout_len = esp->msgout_ctr = 1; + esp_advance_phase(SCptr, in_the_dark); + return esp_do_phase_determine(esp, eregs); + } + } else { + /* If we disconnect now, all hell breaks loose. */ + ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id)); + esp_advance_phase(SCptr, in_the_dark); + return esp_do_phase_determine(esp, eregs); + } +} + +/* The target has control of the bus and we have to see where it has + * taken us. + */ +static int esp_do_phase_determine(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + + ESPPHASE(("esp_do_phase_determine: ")); + if(!(esp->ireg & ESP_INTR_DC)) { + switch(esp->sreg & ESP_STAT_PMASK) { + case ESP_DOP: + case ESP_DIP: + ESPPHASE(("to data phase\n")); + return esp_do_data(esp, eregs); + + case ESP_STATP: + /* Whee, status phase, finish up the command. */ + ESPPHASE(("to status phase\n")); + + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + + if(esp->do_pio_cmds){ + esp_advance_phase(SCptr, in_status); + esp_cmd(esp, eregs, ESP_CMD_ICCSEQ); + while(!(esp->eregs->esp_status & ESP_STAT_INTR)); + esp->esp_command[0] = eregs->esp_fdata; + while(!(esp->eregs->esp_status & ESP_STAT_INTR)); + esp->esp_command[1] = eregs->esp_fdata; + } else { + if(esp->erev != fashme) { + esp->esp_command[0] = 0xff; + esp->esp_command[1] = 0xff; + eregs->esp_tclow = 2; + eregs->esp_tcmed = 0; + esp->dma_init_read(esp, esp->esp_command_dvma, 2); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ); + } else { + /* Using DVMA for status/message bytes is + * unreliable on HME, nice job QLogic. + * Happy Meal indeed.... + */ + esp_cmd(esp, eregs, ESP_CMD_ICCSEQ); + } + esp_advance_phase(SCptr, in_status); + } + return esp_do_status(esp, eregs); + + case ESP_MOP: + ESPPHASE(("to msgout phase\n")); + esp_advance_phase(SCptr, in_msgout); + return esp_do_msgout(esp, eregs); + + case ESP_MIP: + ESPPHASE(("to msgin phase\n")); + esp_advance_phase(SCptr, in_msgin); + return esp_do_msgin(esp, eregs); + + case ESP_CMDP: + /* Ugh, we're running a non-standard command the + * ESP doesn't understand, one byte at a time. + */ + ESPPHASE(("to cmd phase\n")); + esp_advance_phase(SCptr, in_cmdbegin); + return esp_do_cmdbegin(esp, eregs); + }; + } else { + Scsi_Device *dp = SCptr->device; + + /* This means real problems if we see this + * here. Unless we were actually trying + * to force the device to abort/reset. + */ + ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); + ESPLOG(("pphase<%s> cphase<%s>, ", + phase_string(SCptr->SCp.phase), + phase_string(SCptr->SCp.sent_command))); + if(esp->disconnected_SC || (esp->erev == fashme)) + esp_cmd(esp, eregs, ESP_CMD_ESEL); + + switch(esp->cur_msgout[0]) { + default: + /* We didn't expect this to happen at all. */ + ESPLOG(("device is bolixed\n")); + esp_advance_phase(SCptr, in_tgterror); + esp_done(esp, (DID_ERROR << 16)); + break; + + case BUS_DEVICE_RESET: + ESPLOG(("device reset successful\n")); + dp->sync_max_offset = 0; + dp->sync_min_period = 0; + dp->sync = 0; + esp_advance_phase(SCptr, in_resetdev); + esp_done(esp, (DID_RESET << 16)); + break; + + case ABORT: + ESPLOG(("device abort successful\n")); + esp_advance_phase(SCptr, in_abortone); + esp_done(esp, (DID_ABORT << 16)); + break; + + }; + return do_intr_end; + } + + ESPLOG(("esp%d: to unknown phase\n", esp->esp_id)); + printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, + esp->sreg & ESP_STAT_PMASK); + return do_reset_bus; +} + +/* First interrupt after exec'ing a cmd comes here. */ +static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + Scsi_Device *SDptr = SCptr->device; + int cmd_bytes_sent, fcnt; + + if(esp->erev != fashme) + esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS); + if(esp->erev == fashme) + fcnt = esp->hme_fifo_workaround_count; + else + fcnt = (eregs->esp_fflags & ESP_FF_FBYTES); + cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt); + if(esp->dma_invalidate) + esp->dma_invalidate(esp); + + /* Let's check to see if a reselect happened + * while we we're trying to select. This must + * be checked first. + */ + if(esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) { + esp_reconnect(esp, SCptr); + return esp_do_reconnect(esp, eregs); + } + + /* Looks like things worked, we should see a bus service & + * a function complete interrupt at this point. Note we + * are doing a direct comparison because we don't want to + * be fooled into thinking selection was successful if + * ESP_INTR_DC is set, see below. + */ + if(esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { + /* target speaks... */ + esp->targets_present |= (1<<SCptr->target); + + /* What if the target ignores the sdtr? */ + if(esp->snip) + SDptr->sync = 1; + + /* See how far, if at all, we got in getting + * the information out to the target. + */ + switch(esp->seqreg) { + default: + + case ESP_STEP_ASEL: + /* Arbitration won, target selected, but + * we are in some phase which is not command + * phase nor is it message out phase. + * + * XXX We've confused the target, obviously. + * XXX So clear it's state, but we also end + * XXX up clearing everyone elses. That isn't + * XXX so nice. I'd like to just reset this + * XXX target, but if I cannot even get it's + * XXX attention and finish selection to talk + * XXX to it, there is not much more I can do. + * XXX If we have a loaded bus we're going to + * XXX spend the next second or so renegotiating + * XXX for synchronous transfers. + */ + ESPLOG(("esp%d: STEP_ASEL for tgt %d\n", + esp->esp_id, SCptr->target)); + + case ESP_STEP_SID: + /* Arbitration won, target selected, went + * to message out phase, sent one message + * byte, then we stopped. ATN is asserted + * on the SCSI bus and the target is still + * there hanging on. This is a legal + * sequence step if we gave the ESP a select + * and stop command. + * + * XXX See above, I could set the borken flag + * XXX in the device struct and retry the + * XXX command. But would that help for + * XXX tagged capable targets? + */ + + case ESP_STEP_NCMD: + /* Arbitration won, target selected, maybe + * sent the one message byte in message out + * phase, but we did not go to command phase + * in the end. Actually, we could have sent + * only some of the message bytes if we tried + * to send out the entire identify and tag + * message using ESP_CMD_SA3. + */ + cmd_bytes_sent = 0; + break; + + case ESP_STEP_PPC: + /* No, not the powerPC pinhead. Arbitration + * won, all message bytes sent if we went to + * message out phase, went to command phase + * but only part of the command was sent. + * + * XXX I've seen this, but usually in conjunction + * XXX with a gross error which appears to have + * XXX occurred between the time I told the + * XXX ESP to arbitrate and when I got the + * XXX interrupt. Could I have misloaded the + * XXX command bytes into the fifo? Actually, + * XXX I most likely missed a phase, and therefore + * XXX went into never never land and didn't even + * XXX know it. That was the old driver though. + * XXX What is even more peculiar is that the ESP + * XXX showed the proper function complete and + * XXX bus service bits in the interrupt register. + */ + + case ESP_STEP_FINI4: + case ESP_STEP_FINI5: + case ESP_STEP_FINI6: + case ESP_STEP_FINI7: + /* Account for the identify message */ + if(SCptr->SCp.phase == in_slct_norm) + cmd_bytes_sent -= 1; + }; + if(esp->erev != fashme) + esp_cmd(esp, eregs, ESP_CMD_NULL); + + /* Be careful, we could really get fucked during synchronous + * data transfers if we try to flush the fifo now. + */ + if((esp->erev != fashme) && /* not a Happy Meal and... */ + !fcnt && /* Fifo is empty and... */ + /* either we are not doing synchronous transfers or... */ + (!SDptr->sync_max_offset || + /* We are not going into data in phase. */ + ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP))) + esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* flush is safe */ + + /* See how far we got if this is not a slow command. */ + if(!esp->esp_slowcmd) { + if(cmd_bytes_sent < 0) + cmd_bytes_sent = 0; + if(cmd_bytes_sent != SCptr->cmd_len) { + /* Crapola, mark it as a slowcmd + * so that we have some chance of + * keeping the command alive with + * good luck. + * + * XXX Actually, if we didn't send it all + * XXX this means either we didn't set things + * XXX up properly (driver bug) or the target + * XXX or the ESP detected parity on one of + * XXX the command bytes. This makes much + * XXX more sense, and therefore this code + * XXX should be changed to send out a + * XXX parity error message or if the status + * XXX register shows no parity error then + * XXX just expect the target to bring the + * XXX bus into message in phase so that it + * XXX can send us the parity error message. + * XXX SCSI sucks... + */ + esp->esp_slowcmd = 1; + esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]); + esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent); + } + } + + /* Now figure out where we went. */ + esp_advance_phase(SCptr, in_the_dark); + return esp_do_phase_determine(esp, eregs); + } + + /* Did the target even make it? */ + if(esp->ireg == ESP_INTR_DC) { + /* wheee... nobody there or they didn't like + * what we told it to do, clean up. + */ + + /* If anyone is off the bus, but working on + * a command in the background for us, tell + * the ESP to listen for them. + */ + if(esp->disconnected_SC) + esp_cmd(esp, eregs, ESP_CMD_ESEL); + + if(((1<<SCptr->target) & esp->targets_present) && + esp->seqreg && esp->cur_msgout[0] == EXTENDED_MESSAGE && + (SCptr->SCp.phase == in_slct_msg || + SCptr->SCp.phase == in_slct_stop)) { + /* shit */ + esp->snip = 0; + printk("esp%d: Failed synchronous negotiation for target %d " + "lun %d\n", + esp->esp_id, SCptr->target, SCptr->lun); + SDptr->sync_max_offset = 0; + SDptr->sync_min_period = 0; + SDptr->sync = 1; /* so we don't negotiate again */ + + /* Run the command again, this time though we + * won't try to negotiate for synchronous transfers. + * + * XXX I'd like to do something like send an + * XXX INITIATOR_ERROR or ABORT message to the + * XXX target to tell it, "Sorry I confused you, + * XXX please come back and I will be nicer next + * XXX time". But that requires having the target + * XXX on the bus, and it has dropped BSY on us. + */ + esp->current_SC = NULL; + esp_advance_phase(SCptr, not_issued); + prepend_SC(&esp->issue_SC, SCptr); + esp_exec_cmd(esp); + return do_intr_end; + } + + /* Ok, this is normal, this is what we see during boot + * or whenever when we are scanning the bus for targets. + * But first make sure that is really what is happening. + */ + if(((1<<SCptr->target) & esp->targets_present)) { + printk("esp%d: Warning, live target %d not responding to " + "selection.\n", esp->esp_id, SCptr->target); + + /* This _CAN_ happen. The SCSI standard states that + * the target is to _not_ respond to selection if + * _it_ detects bad parity on the bus for any reason. + * Therefore, we assume that if we've talked successfully + * to this target before, bad parity is the problem. + */ + esp_done(esp, (DID_PARITY << 16)); + } else { + /* Else, there really isn't anyone there. */ + ESPMISC(("esp: selection failure, maybe nobody there?\n")); + ESPMISC(("esp: target %d lun %d\n", + SCptr->target, SCptr->lun)); + esp_done(esp, (DID_BAD_TARGET << 16)); + } + return do_intr_end; + } + + + ESPLOG(("esp%d: Selection failure.\n", esp->esp_id)); + printk("esp%d: Currently -- ", esp->esp_id); + esp_print_ireg(esp->ireg); + printk(" "); + esp_print_statreg(esp->sreg); + printk(" "); + esp_print_seqreg(esp->seqreg); + printk("\n"); + printk("esp%d: New -- ", esp->esp_id); + esp->sreg = eregs->esp_status; + esp->seqreg = eregs->esp_sstep; + esp->ireg = eregs->esp_intrpt; + esp_print_ireg(esp->ireg); + printk(" "); + esp_print_statreg(esp->sreg); + printk(" "); + esp_print_seqreg(esp->seqreg); + printk("\n"); + ESPLOG(("esp%d: resetting bus\n", esp->esp_id)); + return do_reset_bus; /* ugh... */ +} + +/* Continue reading bytes for msgin phase. */ +static int esp_do_msgincont(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + if(esp->ireg & ESP_INTR_BSERV) { + /* in the right phase too? */ + if((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) { + /* phew... */ + esp_cmd(esp, eregs, ESP_CMD_TI); + esp_advance_phase(esp->current_SC, in_msgindone); + return do_intr_end; + } + + /* We changed phase but ESP shows bus service, + * in this case it is most likely that we, the + * hacker who has been up for 20hrs straight + * staring at the screen, drowned in coffee + * smelling like retched cigarette ashes + * have miscoded something..... so, try to + * recover as best we can. + */ + printk("esp%d: message in mis-carriage.\n", esp->esp_id); + } + esp_advance_phase(esp->current_SC, in_the_dark); + return do_phase_determine; +} + +static inline int check_singlebyte_msg(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + esp->prevmsgin = esp->cur_msgin[0]; + if(esp->cur_msgin[0] & 0x80) { + /* wheee... */ + ESPLOG(("esp%d: target sends identify amidst phases\n", + esp->esp_id)); + esp_advance_phase(esp->current_SC, in_the_dark); + return 0; + } else if(((esp->cur_msgin[0] & 0xf0) == 0x20) || + (esp->cur_msgin[0] == EXTENDED_MESSAGE)) { + esp->msgin_len = 2; + esp_advance_phase(esp->current_SC, in_msgincont); + return 0; + } + esp_advance_phase(esp->current_SC, in_the_dark); + switch(esp->cur_msgin[0]) { + default: + /* We don't want to hear about it. */ + ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id, + esp->cur_msgin[0])); + return MESSAGE_REJECT; + + case NOP: + ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id, + esp->current_SC->target)); + return 0; + + case RESTORE_POINTERS: + case SAVE_POINTERS: + /* We handle this all automatically. */ + return 0; + + case COMMAND_COMPLETE: + case DISCONNECT: + /* Freeing the bus, let it go. */ + esp->current_SC->SCp.phase = in_freeing; + return 0; + + case MESSAGE_REJECT: + ESPMISC(("msg reject, ")); + if(esp->prevmsgout == EXTENDED_MESSAGE) { + Scsi_Device *SDptr = esp->current_SC->device; + + /* Doesn't look like this target can + * do synchronous or WIDE transfers. + */ + ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n")); + SDptr->sync = 1; + SDptr->wide = 1; + SDptr->sync_min_period = 0; + SDptr->sync_max_offset = 0; + return 0; + } else { + ESPMISC(("not sync nego, sending ABORT\n")); + return ABORT; + } + }; +} + +/* Target negotiates for synchronous transfers before we do, this + * is legal although very strange. What is even funnier is that + * the SCSI2 standard specifically recommends against targets doing + * this because so many initiators cannot cope with this occuring. + */ +static inline int target_with_ants_in_pants(struct NCR_ESP *esp, + Scsi_Cmnd *SCptr, + Scsi_Device *SDptr) +{ + if(SDptr->sync || SDptr->borken) { + /* sorry, no can do */ + ESPSDTR(("forcing to async, ")); + build_sync_nego_msg(esp, 0, 0); + SDptr->sync = 1; + esp->snip = 1; + ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id)); + esp_advance_phase(SCptr, in_the_dark); + return EXTENDED_MESSAGE; + } + + /* Ok, we'll check them out... */ + return 0; +} + +static inline void sync_report(struct NCR_ESP *esp) +{ + int msg3, msg4; + char *type; + + msg3 = esp->cur_msgin[3]; + msg4 = esp->cur_msgin[4]; + if(msg4) { + int hz = 1000000000 / (msg3 * 4); + int integer = hz / 1000000; + int fraction = (hz - (integer * 1000000)) / 10000; + if((esp->erev == fashme) && + (esp->config3[esp->current_SC->target] & ESP_CONFIG3_EWIDE)) { + type = "FAST-WIDE"; + integer <<= 1; + fraction <<= 1; + } else if((msg3 * 4) < 200) { + type = "FAST"; + } else { + type = "synchronous"; + } + printk(KERN_INFO "esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n", + esp->esp_id, esp->current_SC->target, + (int) msg3 * 4, + (int) msg4, + integer, fraction, type, + (((msg3 * 4) < 200) ? "-II" : "")); + } else { + printk(KERN_INFO "esp%d: target %d asynchronous\n", + esp->esp_id, esp->current_SC->target); + } +} + +static inline int check_multibyte_msg(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + Scsi_Device *SDptr = SCptr->device; + unchar regval = 0; + int message_out = 0; + + ESPSDTR(("chk multibyte msg: ")); + if(esp->cur_msgin[2] == EXTENDED_SDTR) { + int period = esp->cur_msgin[3]; + int offset = esp->cur_msgin[4]; + + ESPSDTR(("is sync nego response, ")); + if(!esp->snip) { + int rval; + + /* Target negotiates first! */ + ESPSDTR(("target jumps the gun, ")); + message_out = EXTENDED_MESSAGE; /* we must respond */ + rval = target_with_ants_in_pants(esp, SCptr, SDptr); + if(rval) + return rval; + } + + ESPSDTR(("examining sdtr, ")); + + /* Offset cannot be larger than ESP fifo size. */ + if(offset > 15) { + ESPSDTR(("offset too big %2x, ", offset)); + offset = 15; + ESPSDTR(("sending back new offset\n")); + build_sync_nego_msg(esp, period, offset); + return EXTENDED_MESSAGE; + } + + if(offset && period > esp->max_period) { + /* Yeee, async for this slow device. */ + ESPSDTR(("period too long %2x, ", period)); + build_sync_nego_msg(esp, 0, 0); + ESPSDTR(("hoping for msgout\n")); + esp_advance_phase(esp->current_SC, in_the_dark); + return EXTENDED_MESSAGE; + } else if (offset && period < esp->min_period) { + ESPSDTR(("period too short %2x, ", period)); + period = esp->min_period; + if(esp->erev > esp236) + regval = 4; + else + regval = 5; + } else if(offset) { + int tmp; + + ESPSDTR(("period is ok, ")); + tmp = esp->ccycle / 1000; + regval = (((period << 2) + tmp - 1) / tmp); + if(regval && ((esp->erev == fas100a || + esp->erev == fas236 || + esp->erev == fashme))) { + if(period >= 50) + regval--; + } + } + + if(offset) { + unchar bit; + + SDptr->sync_min_period = (regval & 0x1f); + SDptr->sync_max_offset = (offset | esp->radelay); + if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) { + if((esp->erev == fas100a) || (esp->erev == fashme)) + bit = ESP_CONFIG3_FAST; + else + bit = ESP_CONFIG3_FSCSI; + if(period < 50) + esp->config3[SCptr->target] |= bit; + else + esp->config3[SCptr->target] &= ~bit; + eregs->esp_cfg3 = esp->config3[SCptr->target]; + } + eregs->esp_soff = SDptr->sync_min_period; + eregs->esp_stp = SDptr->sync_max_offset; + + ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n", + SDptr->sync_max_offset, + SDptr->sync_min_period, + esp->config3[SCptr->target])); + + esp->snip = 0; + } else if(SDptr->sync_max_offset) { + unchar bit; + + /* back to async mode */ + ESPSDTR(("unaccaptable sync nego, forcing async\n")); + SDptr->sync_max_offset = 0; + SDptr->sync_min_period = 0; + eregs->esp_soff = 0; + eregs->esp_stp = 0; + if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) { + if((esp->erev == fas100a) || (esp->erev == fashme)) + bit = ESP_CONFIG3_FAST; + else + bit = ESP_CONFIG3_FSCSI; + esp->config3[SCptr->target] &= ~bit; + eregs->esp_cfg3 = esp->config3[SCptr->target]; + } + } + + sync_report(esp); + + ESPSDTR(("chk multibyte msg: sync is known, ")); + SDptr->sync = 1; + + if(message_out) { + ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n", + esp->esp_id)); + build_sync_nego_msg(esp, period, offset); + esp_advance_phase(SCptr, in_the_dark); + return EXTENDED_MESSAGE; + } + + ESPSDTR(("returning zero\n")); + esp_advance_phase(SCptr, in_the_dark); /* ...or else! */ + return 0; + } else if(esp->cur_msgin[2] == EXTENDED_WDTR) { + int size = 8 << esp->cur_msgin[3]; + + esp->wnip = 0; + if(esp->erev != fashme) { + printk("esp%d: AIEEE wide msg received and not HME.\n", + esp->esp_id); + message_out = MESSAGE_REJECT; + } else if(size > 16) { + printk("esp%d: AIEEE wide transfer for %d size not supported.\n", + esp->esp_id, size); + message_out = MESSAGE_REJECT; + } else { + /* Things look good, lets see what we got. */ + if(size == 16) { + /* Set config 3 register for this target. */ + printk("esp%d: 16 byte WIDE transfers enabled for target %d.\n", + esp->esp_id, SCptr->target); + esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE; + } else { + /* Just make sure it was one byte sized. */ + if(size != 8) { + printk("esp%d: Aieee, wide nego of %d size.\n", + esp->esp_id, size); + message_out = MESSAGE_REJECT; + goto finish; + } + /* Pure paranoia. */ + esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE); + } + eregs->esp_cfg3 = esp->config3[SCptr->target]; + + /* Regardless, next try for sync transfers. */ + build_sync_nego_msg(esp, esp->sync_defp, 15); + SDptr->sync = 1; + esp->snip = 1; + message_out = EXTENDED_MESSAGE; + } + } else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) { + ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id)); + message_out = MESSAGE_REJECT; + } +finish: + esp_advance_phase(SCptr, in_the_dark); + return message_out; +} + +static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + int message_out = 0, it = 0, rval; + + rval = skipahead1(esp, eregs, SCptr, in_msgin, in_msgindone); + if(rval) + return rval; + if(SCptr->SCp.sent_command != in_status) { + if(!(esp->ireg & ESP_INTR_DC)) { + if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) { + message_out = MSG_PARITY_ERROR; + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } else if(esp->erev != fashme && + (it = (eregs->esp_fflags & ESP_FF_FBYTES))!=1) { + /* We certainly dropped the ball somewhere. */ + message_out = INITIATOR_ERROR; + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + } else if(!esp->msgin_len) { + if(esp->erev == fashme) + it = esp->hme_fifo_workaround_buffer[0]; + else + it = eregs->esp_fdata; + esp_advance_phase(SCptr, in_msgincont); + } else { + /* it is ok and we want it */ + if(esp->erev == fashme) + it = esp->cur_msgin[esp->msgin_ctr] = + esp->hme_fifo_workaround_buffer[0]; + else + it = esp->cur_msgin[esp->msgin_ctr] = + eregs->esp_fdata; + esp->msgin_ctr++; + } + } else { + esp_advance_phase(SCptr, in_the_dark); + return do_work_bus; + } + } else { + it = esp->cur_msgin[0]; + } + if(!message_out && esp->msgin_len) { + if(esp->msgin_ctr < esp->msgin_len) { + esp_advance_phase(SCptr, in_msgincont); + } else if(esp->msgin_len == 1) { + message_out = check_singlebyte_msg(esp, eregs); + } else if(esp->msgin_len == 2) { + if(esp->cur_msgin[0] == EXTENDED_MESSAGE) { + if((it+2) >= 15) { + message_out = MESSAGE_REJECT; + } else { + esp->msgin_len = (it + 2); + esp_advance_phase(SCptr, in_msgincont); + } + } else { + message_out = MESSAGE_REJECT; /* foo on you */ + } + } else { + message_out = check_multibyte_msg(esp, eregs); + } + } + if(message_out < 0) { + return -message_out; + } else if(message_out) { + if(((message_out != 1) && + ((message_out < 0x20) || (message_out & 0x80)))) + esp->msgout_len = 1; + esp->cur_msgout[0] = message_out; + esp_cmd(esp, eregs, ESP_CMD_SATN); + esp_advance_phase(SCptr, in_the_dark); + esp->msgin_len = 0; + } + esp->sreg = eregs->esp_status; + esp->sreg &= ~(ESP_STAT_INTR); + if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD)) + esp_cmd(esp, eregs, ESP_CMD_MOK); + if((SCptr->SCp.sent_command == in_msgindone) && + (SCptr->SCp.phase == in_freeing)) + return esp_do_freebus(esp, eregs); + return do_intr_end; +} + +static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + + esp_advance_phase(SCptr, in_cmdend); + if(esp->erev == fashme) { + int i; + + for(i = 0; i < esp->esp_scmdleft; i++) + esp->esp_command[i] = *esp->esp_scmdp++; + esp->esp_scmdleft = 0; + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + esp_setcount(eregs, i, 1); + esp_cmd(esp, eregs, (ESP_CMD_DMA | ESP_CMD_TI)); + esp->dma_init_write(esp, esp->esp_command_dvma, i); + } else { + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + eregs->esp_fdata = *esp->esp_scmdp++; + esp->esp_scmdleft--; + esp_cmd(esp, eregs, ESP_CMD_TI); + } + return do_intr_end; +} + +static inline int esp_do_cmddone(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + if(esp->erev == fashme){ + if(esp->dma_invalidate) + esp->dma_invalidate(esp); + } else + esp_cmd(esp, eregs, ESP_CMD_NULL); + if(esp->ireg & ESP_INTR_BSERV) { + esp_advance_phase(esp->current_SC, in_the_dark); + return esp_do_phase_determine(esp, eregs); + } + ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n", + esp->esp_id)); + return do_reset_bus; +} + +static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + switch(esp->msgout_len) { + case 1: + if(esp->erev == fashme) + hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1); + else + eregs->esp_fdata = esp->cur_msgout[0]; + esp_cmd(esp, eregs, ESP_CMD_TI); + break; + + case 2: + if(esp->do_pio_cmds){ + eregs->esp_fdata = esp->cur_msgout[0]; + eregs->esp_fdata = esp->cur_msgout[1]; + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->esp_command[0] = esp->cur_msgout[0]; + esp->esp_command[1] = esp->cur_msgout[1]; + if(esp->erev == fashme) { + hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 2); + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->dma_setup(esp, esp->esp_command_dvma, 2, 0); + esp_setcount(eregs, 2, 0); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + } + } + break; + + case 4: + esp->snip = 1; + if(esp->do_pio_cmds){ + eregs->esp_fdata = esp->cur_msgout[0]; + eregs->esp_fdata = esp->cur_msgout[1]; + eregs->esp_fdata = esp->cur_msgout[2]; + eregs->esp_fdata = esp->cur_msgout[3]; + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->esp_command[0] = esp->cur_msgout[0]; + esp->esp_command[1] = esp->cur_msgout[1]; + esp->esp_command[2] = esp->cur_msgout[2]; + esp->esp_command[3] = esp->cur_msgout[3]; + if(esp->erev == fashme) { + hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 4); + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->dma_setup(esp, esp->esp_command_dvma, 4, 0); + esp_setcount(eregs, 4, 0); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + } + } + break; + + case 5: + esp->snip = 1; + if(esp->do_pio_cmds){ + eregs->esp_fdata = esp->cur_msgout[0]; + eregs->esp_fdata = esp->cur_msgout[1]; + eregs->esp_fdata = esp->cur_msgout[2]; + eregs->esp_fdata = esp->cur_msgout[3]; + eregs->esp_fdata = esp->cur_msgout[4]; + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->esp_command[0] = esp->cur_msgout[0]; + esp->esp_command[1] = esp->cur_msgout[1]; + esp->esp_command[2] = esp->cur_msgout[2]; + esp->esp_command[3] = esp->cur_msgout[3]; + esp->esp_command[4] = esp->cur_msgout[4]; + if(esp->erev == fashme) { + hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 5); + esp_cmd(esp, eregs, ESP_CMD_TI); + } else { + esp->dma_setup(esp, esp->esp_command_dvma, 5, 0); + esp_setcount(eregs, 5, 0); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + } + } + break; + + default: + /* whoops */ + ESPMISC(("bogus msgout sending NOP\n")); + esp->cur_msgout[0] = NOP; + if(esp->erev == fashme) { + hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1); + } else { + eregs->esp_fdata = esp->cur_msgout[0]; + } + esp->msgout_len = 1; + esp_cmd(esp, eregs, ESP_CMD_TI); + break; + } + esp_advance_phase(esp->current_SC, in_msgoutdone); + return do_intr_end; +} + +static inline int esp_do_msgoutdone(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + if((esp->msgout_len > 1) && esp->dma_barrier) + esp->dma_barrier(esp); + + if(!(esp->ireg & ESP_INTR_DC)) { + if(esp->erev != fashme) + esp_cmd(esp, eregs, ESP_CMD_NULL); + switch(esp->sreg & ESP_STAT_PMASK) { + case ESP_MOP: + /* whoops, parity error */ + ESPLOG(("esp%d: still in msgout, parity error assumed\n", + esp->esp_id)); + if(esp->msgout_len > 1) + esp_cmd(esp, eregs, ESP_CMD_SATN); + esp_advance_phase(esp->current_SC, in_msgout); + return do_work_bus; + + case ESP_DIP: + break; + + default: + /* Happy Meal fifo is touchy... */ + if((esp->erev != fashme) && + !fcount(esp, eregs) && + !(esp->current_SC->device->sync_max_offset)) + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + break; + + }; + } else { + ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id)); + return do_reset_bus; + } + + /* If we sent out a synchronous negotiation message, update + * our state. + */ + if(esp->cur_msgout[2] == EXTENDED_MESSAGE && + esp->cur_msgout[4] == EXTENDED_SDTR) { + esp->snip = 1; /* anal retentiveness... */ + } + + esp->prevmsgout = esp->cur_msgout[0]; + esp->msgout_len = 0; + esp_advance_phase(esp->current_SC, in_the_dark); + return esp_do_phase_determine(esp, eregs); +} + +/* This is the second tier in our dual-level SCSI state machine. */ +static inline int esp_work_bus(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + Scsi_Cmnd *SCptr = esp->current_SC; + + ESPBUS(("esp_work_bus: ")); + if(!SCptr) { + ESPBUS(("reconnect\n")); + return esp_do_reconnect(esp, eregs); + } + + switch(SCptr->SCp.phase) { + case in_the_dark: + ESPBUS(("in the dark\n")); + return esp_do_phase_determine(esp, eregs); + + case in_slct_norm: + case in_slct_stop: + case in_slct_msg: + case in_slct_tag: + case in_slct_sneg: + ESPBUS(("finish selection\n")); + return esp_select_complete(esp, eregs); + + case in_datain: + case in_dataout: + ESPBUS(("finish data\n")); + return esp_do_data_finale(esp, eregs); + + case in_msgout: + ESPBUS(("message out ")); + return esp_do_msgout(esp, eregs); + + case in_msgoutdone: + ESPBUS(("finish message out ")); + return esp_do_msgoutdone(esp, eregs); + + case in_msgin: + ESPBUS(("message in ")); + return esp_do_msgin(esp, eregs); + + case in_msgincont: + ESPBUS(("continue message in ")); + return esp_do_msgincont(esp, eregs); + + case in_msgindone: + ESPBUS(("finish message in ")); + return esp_do_msgindone(esp, eregs); + + case in_status: + ESPBUS(("status phase ")); + return esp_do_status(esp, eregs); + + case in_freeing: + ESPBUS(("freeing the bus ")); + return esp_do_freebus(esp, eregs); + + case in_cmdbegin: + ESPBUS(("begin slow cmd ")); + return esp_do_cmdbegin(esp, eregs); + + case in_cmdend: + ESPBUS(("end slow cmd ")); + return esp_do_cmddone(esp, eregs); + + default: + printk("esp%d: command in weird state %2x\n", + esp->esp_id, esp->current_SC->SCp.phase); + return do_reset_bus; + }; +} + +/* Main interrupt handler for an esp adapter. */ +inline void esp_handle(struct NCR_ESP *esp) +{ + struct ESP_regs *eregs; + Scsi_Cmnd *SCptr; + int what_next = do_intr_end; +#ifdef CONFIG_SCSI_SUNESP + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers*) esp->dregs; +#endif + eregs = esp->eregs; + SCptr = esp->current_SC; + + if(esp->dma_irq_entry) + esp->dma_irq_entry(esp); + + /* Check for errors. */ + esp->sreg = eregs->esp_status; + esp->sreg &= (~ESP_STAT_INTR); + if(esp->erev == fashme) { + esp->sreg2 = eregs->esp_status2; + esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS); + } + if(esp->sreg & (ESP_STAT_SPAM)) { + /* Gross error, could be due to one of: + * + * - top of fifo overwritten, could be because + * we tried to do a synchronous transfer with + * an offset greater than ESP fifo size + * + * - top of command register overwritten + * + * - DMA setup to go in one direction, SCSI + * bus points in the other, whoops + * + * - weird phase change during asynchronous + * data phase while we are initiator + */ + ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg)); + + /* If a command is live on the bus we cannot safely + * reset the bus, so we'll just let the pieces fall + * where they may. Here we are hoping that the + * target will be able to cleanly go away soon + * so we can safely reset things. + */ + if(!SCptr) { + ESPLOG(("esp%d: No current cmd during gross error, " + "resetting bus\n", esp->esp_id)); + what_next = do_reset_bus; + goto again; + } + } + +#ifdef CONFIG_SCSI_SUNESP + if(dregs->cond_reg & DMA_HNDL_ERROR) { + /* A DMA gate array error. Here we must + * be seeing one of two things. Either the + * virtual to physical address translation + * on the SBUS could not occur, else the + * translation it did get pointed to a bogus + * page. Ho hum... + */ + ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id, + dregs->cond_reg)); + + /* DMA gate array itself must be reset to clear the + * error condition. + */ + if(esp->dma_reset) + esp->dma_reset(esp); + + what_next = do_reset_bus; + goto again; + } +#endif /* CONFIG_SCSI_SUNESP */ + + if(esp->erev == fashme) { + /* This chip is really losing. */ + ESPHME(("HME[")); + + ESPHME(("sreg2=%02x,", esp->sreg2)); + /* Must latch fifo before reading the interrupt + * register else garbage ends up in the FIFO + * which confuses the driver utterly. + */ + if(!(esp->sreg2 & ESP_STAT2_FEMPTY) || + (esp->sreg2 & ESP_STAT2_F1BYTE)) { + ESPHME(("fifo_workaround]")); + hme_fifo_hwbug_workaround(esp, eregs); + } else { + ESPHME(("no_fifo_workaround]")); + } + } + + esp->ireg = eregs->esp_intrpt; /* Unlatch intr and stat regs */ + + /* This cannot be done until this very moment. -DaveM */ + synchronize_irq(); + + /* No current cmd is only valid at this point when there are + * commands off the bus or we are trying a reset. + */ + if(!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) { + /* Panic is safe, since current_SC is null. */ + ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id)); + panic("esp_handle: current_SC == penguin within interrupt!"); + } + + if(esp->ireg & (ESP_INTR_IC)) { + /* Illegal command fed to ESP. Outside of obvious + * software bugs that could cause this, there is + * a condition with esp100 where we can confuse the + * ESP into an erroneous illegal command interrupt + * because it does not scrape the FIFO properly + * for reselection. See esp100_reconnect_hwbug() + * to see how we try very hard to avoid this. + */ + ESPLOG(("esp%d: illegal command\n", esp->esp_id)); + + esp_dump_state(esp, eregs); + + if(SCptr) { + /* Devices with very buggy firmware can drop BSY + * during a scatter list interrupt when using sync + * mode transfers. We continue the transfer as + * expected, the target drops the bus, the ESP + * gets confused, and we get a illegal command + * interrupt because the bus is in the disconnected + * state now and ESP_CMD_TI is only allowed when + * a nexus is alive on the bus. + */ + ESPLOG(("esp%d: Forcing async and disabling disconnect for " + "target %d\n", esp->esp_id, SCptr->target)); + SCptr->device->borken = 1; /* foo on you */ + } + + what_next = do_reset_bus; + goto again; + } + + if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { + int phase; + + if(SCptr) { + phase = SCptr->SCp.phase; + if(phase & in_phases_mask) { + what_next = esp_work_bus(esp, eregs); + } else if(phase & in_slct_mask) { + what_next = esp_select_complete(esp, eregs); + } else { + ESPLOG(("esp%d: interrupt for no good reason...\n", + esp->esp_id)); + goto esp_handle_done; + } + } else { + ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n", + esp->esp_id)); + what_next = do_reset_bus; + goto again; + } + } else if(esp->ireg & ESP_INTR_SR) { + ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id)); + what_next = do_reset_complete; + } else if(esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) { + ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n", + esp->esp_id)); + what_next = do_reset_bus; + goto again; + } else if(esp->ireg & ESP_INTR_RSEL) { + if(!SCptr) { + /* This is ok. */ + what_next = esp_do_reconnect(esp, eregs); + } else if(SCptr->SCp.phase & in_slct_mask) { + /* Only selection code knows how to clean + * up properly. + */ + ESPDISC(("Reselected during selection attempt\n")); + what_next = esp_select_complete(esp, eregs); + } else { + ESPLOG(("esp%d: Reselected while bus is busy\n", + esp->esp_id)); + what_next = do_reset_bus; + goto again; + } + } + + /* We're trying to fight stack problems, and inline as much as + * possible without making this driver a mess. hate hate hate + * This is tier-one in our dual level SCSI state machine. + */ +again: + switch(what_next) { + case do_intr_end: + goto esp_handle_done; + + case do_work_bus: + what_next = esp_work_bus(esp, eregs); + break; + + case do_phase_determine: + what_next = esp_do_phase_determine(esp, eregs); + break; + + case do_reset_bus: + ESPLOG(("esp%d: resetting bus...\n", esp->esp_id)); + esp->resetting_bus = 1; + esp_cmd(esp, eregs, ESP_CMD_RS); + goto esp_handle_done; + + case do_reset_complete: + /* Tricky, we don't want to cause any more commands to + * go out until we clear all the live cmds by hand. + */ + if(esp->current_SC) { + Scsi_Cmnd *SCptr = esp->current_SC; + +#ifdef CONFIG_SCSI_SUNESP + if(!SCptr->use_sg) + mmu_release_scsi_one(SCptr->SCp.have_data_in, + SCptr->request_bufflen, + ((struct linux_sbus_device *) (esp->edev))->my_bus); + else + mmu_release_scsi_sgl((struct mmu_sglist *) + SCptr->buffer, + SCptr->use_sg - 1, + ((struct linux_sbus_device *) (esp->edev))->my_bus); +#endif + SCptr->result = (DID_RESET << 16); + + SCptr->scsi_done(SCptr); + } + esp->current_SC = NULL; + if(esp->disconnected_SC) { + Scsi_Cmnd *SCptr; + while((SCptr = remove_first_SC(&esp->disconnected_SC))) { + if(!SCptr->use_sg) +#ifdef CONFIG_SCSI_SUNESP + mmu_release_scsi_one(SCptr->SCp.have_data_in, + SCptr->request_bufflen, + ((struct linux_sbus_device *) (esp->edev))->my_bus); + else + mmu_release_scsi_sgl((struct mmu_sglist *) + SCptr->buffer, + SCptr->use_sg - 1, + ((struct linux_sbus_device *) (esp->edev))->my_bus); +#endif + SCptr->result = (DID_RESET << 16); + + SCptr->scsi_done(SCptr); + } + } + esp->resetting_bus = 0; + + if(esp->current_SC) { + printk("esp%d: weird weird weird, current_SC not NULL after " + "SCSI bus reset.\n", esp->esp_id); + goto esp_handle_done; + } + + /* Now it is safe to execute more things. */ + if(esp->issue_SC) + esp_exec_cmd(esp); + goto esp_handle_done; + + default: + /* state is completely lost ;-( */ + ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", + esp->esp_id)); + what_next = do_reset_bus; + break; + + }; + goto again; + +esp_handle_done: + if(esp->dma_irq_exit) + esp->dma_irq_exit(esp); + return; +} + +#ifndef __sparc_v9__ + +#ifndef __SMP__ +void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) +{ + struct NCR_ESP *esp; + int again; + + /* Handle all ESP interrupts showing at this IRQ level. */ +repeat: + again = 0; + for_each_esp(esp) { + if(((esp)->irq & 0xf) == irq) { + if(esp->dma_irq_p(esp)) { + again = 1; + + esp->dma_ints_off(esp); + + ESPIRQ(("I%d(", esp->esp_id)); + esp_handle(esp); + ESPIRQ((")")); + + esp->dma_ints_on(esp); + } + } + } + if(again) + goto repeat; +} +#else +/* For SMP we only service one ESP on the list list at our IRQ level! */ +static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) +{ + struct NCR_ESP *esp; + + /* Handle all ESP interrupts showing at this IRQ level. */ + for_each_esp(esp) { + if(((esp)->irq & 0xf) == irq) { + if(esp->dma_irq_p(esp)) { + esp->dma_ints_off(esp); + + ESPIRQ(("I[%d:%d](", + smp_processor_id(), esp->esp_id)); + esp_handle(esp); + ESPIRQ((")")); + + esp->dma_ints_on(esp); + return; + } + } + } +} +#endif + +#else /* __sparc_v9__ */ + +static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) +{ + struct NCR_ESP *esp = dev_id; + + if(esp->dma_irq_p(esp)) { + esp->dma_ints_off(dregs); + + ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id)); + esp_handle(esp); + ESPIRQ((")")); + + esp->dma_ints_on(esp); + } +} + +#endif diff --git a/drivers/scsi/NCR53C9x.h b/drivers/scsi/NCR53C9x.h new file mode 100644 index 000000000..a7431ca87 --- /dev/null +++ b/drivers/scsi/NCR53C9x.h @@ -0,0 +1,529 @@ +/* NCR53C9x.c: Defines and structures for the NCR53C9x generic driver. + * + * Originaly esp.h: Defines and structures for the Sparc ESP + * (Enhanced SCSI Processor) driver under Linux. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * Generalization by Jesper Skov (jskov@cygnus.co.uk) + */ + +#ifndef NCR53C9X_H +#define NCR53C9X_H + +/* Macros for debugging messages */ + +/* #define DEBUG_ESP */ +/* #define DEBUG_ESP_HME */ +/* #define DEBUG_ESP_DATA */ +/* #define DEBUG_ESP_QUEUE */ +/* #define DEBUG_ESP_DISCONNECT */ +/* #define DEBUG_ESP_STATUS */ +/* #define DEBUG_ESP_PHASES */ +/* #define DEBUG_ESP_WORKBUS */ +/* #define DEBUG_STATE_MACHINE */ +/* #define DEBUG_ESP_CMDS */ +/* #define DEBUG_ESP_IRQS */ +/* #define DEBUG_SDTR */ +/* #define DEBUG_ESP_SG */ + +/* Use the following to sprinkle debugging messages in a way which + * suits you if combinations of the above become too verbose when + * trying to track down a specific problem. + */ +/* #define DEBUG_ESP_MISC */ + +#if defined(DEBUG_ESP) +#define ESPLOG(foo) printk foo +#else +#define ESPLOG(foo) +#endif /* (DEBUG_ESP) */ + +#if defined(DEBUG_ESP_HME) +#define ESPHME(foo) printk foo +#else +#define ESPHME(foo) +#endif + +#if defined(DEBUG_ESP_DATA) +#define ESPDATA(foo) printk foo +#else +#define ESPDATA(foo) +#endif + +#if defined(DEBUG_ESP_QUEUE) +#define ESPQUEUE(foo) printk foo +#else +#define ESPQUEUE(foo) +#endif + +#if defined(DEBUG_ESP_DISCONNECT) +#define ESPDISC(foo) printk foo +#else +#define ESPDISC(foo) +#endif + +#if defined(DEBUG_ESP_STATUS) +#define ESPSTAT(foo) printk foo +#else +#define ESPSTAT(foo) +#endif + +#if defined(DEBUG_ESP_PHASES) +#define ESPPHASE(foo) printk foo +#else +#define ESPPHASE(foo) +#endif + +#if defined(DEBUG_ESP_WORKBUS) +#define ESPBUS(foo) printk foo +#else +#define ESPBUS(foo) +#endif + +#if defined(DEBUG_ESP_IRQS) +#define ESPIRQ(foo) printk foo +#else +#define ESPIRQ(foo) +#endif + +#if defined(DEBUG_SDTR) +#define ESPSDTR(foo) printk foo +#else +#define ESPSDTR(foo) +#endif + +#if defined(DEBUG_ESP_MISC) +#define ESPMISC(foo) printk foo +#else +#define ESPMISC(foo) +#endif + +#define INTERNAL_ESP_ERROR \ + (panic ("Internal ESP driver error in file %s, line %d\n", \ + __FILE__, __LINE__)) + +#define INTERNAL_ESP_ERROR_NOPANIC \ + (printk ("Internal ESP driver error in file %s, line %d\n", \ + __FILE__, __LINE__)) + +/* The ESP SCSI controllers have their register sets in three + * "classes": + * + * 1) Registers which are both read and write. + * 2) Registers which are read only. + * 3) Registers which are write only. + * + * Yet, they all live within the same IO space. + */ + +/* All the ESP registers are one byte each and are accessed longwords + * apart with a big-endian ordering to the bytes. + */ + +struct ESP_regs { + /* Access Description Offset */ + volatile unchar esp_tclow; /* rw Low bits of the transfer count 0x00 */ + unchar tlpad1[3]; + volatile unchar esp_tcmed; /* rw Mid bits of the transfer count 0x04 */ + unchar fdpad[3]; + volatile unchar esp_fdata; /* rw FIFO data bits 0x08 */ + unchar cbpad[3]; + volatile unchar esp_cmd; /* rw SCSI command bits 0x0c */ + unchar stpad[3]; + volatile unchar esp_status; /* ro ESP status register 0x10 */ +#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */ + unchar irqpd[3]; + volatile unchar esp_intrpt; /* ro Kind of interrupt 0x14 */ +#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */ + unchar sspad[3]; + volatile unchar esp_sstep; /* ro Sequence step register 0x18 */ +#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */ + unchar ffpad[3]; + volatile unchar esp_fflags; /* ro Bits of current FIFO info 0x1c */ +#define esp_soff esp_fflags /* wo Sync offset 0x1c */ + unchar cf1pd[3]; + volatile unchar esp_cfg1; /* rw First configuration register 0x20 */ + unchar cfpad[3]; + volatile unchar esp_cfact; /* wo Clock conversion factor 0x24 */ +#define esp_status2 esp_cfact /* ro HME status2 register 0x24 */ + unchar ctpad[3]; + volatile unchar esp_ctest; /* wo Chip test register 0x28 */ + unchar cf2pd[3]; + volatile unchar esp_cfg2; /* rw Second configuration register 0x2c */ + unchar cf3pd[3]; + + /* The following is only found on the 53C9X series SCSI chips */ + volatile unchar esp_cfg3; /* rw Third configuration register 0x30 */ + unchar thpd[7]; + + /* The following is found on all chips except the NCR53C90 (ESP100) */ + volatile unchar esp_tchi; /* rw High bits of transfer count 0x38 */ +#define esp_uid esp_tchi /* ro Unique ID code 0x38 */ +#define fas_rlo esp_tchi /* rw HME extended counter 0x38 */ + unchar fgpad[3]; + volatile unchar esp_fgrnd; /* rw Data base for fifo 0x3c */ +#define fas_rhi esp_fgrnd /* rw HME extended counter 0x3c */ +}; + +/* Various revisions of the ESP board. */ +enum esp_rev { + esp100 = 0x00, /* NCR53C90 - very broken */ + esp100a = 0x01, /* NCR53C90A */ + esp236 = 0x02, + fas236 = 0x03, + fas100a = 0x04, + fast = 0x05, + fashme = 0x06, + espunknown = 0x07 +}; + +/* We get one of these for each ESP probed. */ +struct NCR_ESP { + struct NCR_ESP *next; /* Next ESP on probed or NULL */ + struct ESP_regs *eregs; /* All esp registers */ + struct Linux_DMA *dma; /* Who I do transfers with. */ + void *dregs; /* And his registers. */ + struct Scsi_Host *ehost; /* Backpointer to SCSI Host */ + + void *edev; /* Pointer to controller base/SBus */ + char prom_name[64]; /* Name of ESP device from prom */ + int prom_node; /* Prom node where ESP found */ + int esp_id; /* Unique per-ESP ID number */ + + /* ESP Configuration Registers */ + unsigned char config1; /* Copy of the 1st config register */ + unsigned char config2; /* Copy of the 2nd config register */ + unsigned char config3[16]; /* Copy of the 3rd config register */ + + /* The current command we are sending to the ESP chip. This esp_command + * ptr needs to be mapped in DVMA area so we can send commands and read + * from the ESP fifo without burning precious CPU cycles. Programmed I/O + * sucks when we have the DVMA to do it for us. The ESP is stupid and will + * only send out 6, 10, and 12 byte SCSI commands, others we need to send + * one byte at a time. esp_slowcmd being set says that we are doing one + * of the command types ESP doesn't understand, esp_scmdp keeps track of + * which byte we are sending, esp_scmdleft says how many bytes to go. + */ + volatile unchar *esp_command; /* Location of command (CPU view) */ + __u32 esp_command_dvma; /* Location of command (DVMA view) */ + unsigned char esp_clen; /* Length of this command */ + unsigned char esp_slowcmd; + unsigned char *esp_scmdp; + unsigned char esp_scmdleft; + + /* The following are used to determine the cause of an IRQ. Upon every + * IRQ entry we synchronize these with the hardware registers. + */ + unchar ireg; /* Copy of ESP interrupt register */ + unchar sreg; /* Same for ESP status register */ + unchar seqreg; /* The ESP sequence register */ + unchar sreg2; /* Copy of HME status2 register */ + + /* The HME is the biggest piece of shit I have ever seen. */ + unchar hme_fifo_workaround_buffer[16 * 2]; /* 16-bit/entry fifo for wide scsi */ + unchar hme_fifo_workaround_count; + + /* Clock periods, frequencies, synchronization, etc. */ + unsigned int cfreq; /* Clock frequency in HZ */ + unsigned int cfact; /* Clock conversion factor */ + unsigned int ccycle; /* One ESP clock cycle */ + unsigned int ctick; /* One ESP clock time */ + unsigned int radelay; /* FAST chip req/ack delay */ + unsigned int neg_defp; /* Default negotiation period */ + unsigned int sync_defp; /* Default sync transfer period */ + unsigned int max_period; /* longest our period can be */ + unsigned int min_period; /* shortest period we can withstand */ + unsigned char ccf; /* Clock conversion factor */ + /* For slow to medium speed input clock rates we shoot for 5mb/s, + * but for high input clock rates we try to do 10mb/s although I + * don't think a transfer can even run that fast with an ESP even + * with DMA2 scatter gather pipelining. + */ +#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */ +#define SYNC_DEFP_FAST 0x19 /* 10mb/s */ + + unsigned int snip; /* Sync. negotiation in progress */ + unsigned int wnip; /* WIDE negotiation in progress */ + unsigned int targets_present; /* targets spoken to before */ + + int current_transfer_size; /* Set at beginning of data dma */ + + unchar espcmdlog[32]; /* Log of current esp cmds sent. */ + unchar espcmdent; /* Current entry in esp cmd log. */ + + /* Misc. info about this ESP */ + enum esp_rev erev; /* ESP revision */ + int irq; /* SBus IRQ for this ESP */ + int scsi_id; /* Who am I as initiator? */ + int scsi_id_mask; /* Bitmask of 'me'. */ + int diff; /* Differential SCSI bus? */ + int bursts; /* Burst sizes our DVMA supports */ + + /* Our command queues, only one cmd lives in the current_SC queue. */ + Scsi_Cmnd *issue_SC; /* Commands to be issued */ + Scsi_Cmnd *current_SC; /* Who is currently working the bus */ + Scsi_Cmnd *disconnected_SC; /* Commands disconnected from the bus */ + + /* Message goo */ + unchar cur_msgout[16]; + unchar cur_msgin[16]; + unchar prevmsgout, prevmsgin; + unchar msgout_len, msgin_len; + unchar msgout_ctr, msgin_ctr; + + /* States that we cannot keep in the per cmd structure because they + * cannot be assosciated with any specific command. + */ + unchar resetting_bus; + + unchar do_pio_cmds; /* Do command transfer with pio */ + + /* Functions handling DMA + */ + /* Required functions */ + int (*dma_bytes_sent)(struct NCR_ESP *, int); + int (*dma_can_transfer)(struct NCR_ESP *, Scsi_Cmnd *); + void (*dma_dump_state)(struct NCR_ESP *); + void (*dma_init_read)(struct NCR_ESP *, __u32, int); + void (*dma_init_write)(struct NCR_ESP *, __u32, int); + void (*dma_ints_off)(struct NCR_ESP *); + void (*dma_ints_on)(struct NCR_ESP *); + int (*dma_irq_p)(struct NCR_ESP *); + int (*dma_ports_p)(struct NCR_ESP *); + void (*dma_setup)(struct NCR_ESP *, __u32, int, int); + + /* Optional functions (i.e. may be initialized to 0) */ + void (*dma_barrier)(struct NCR_ESP *); + void (*dma_drain)(struct NCR_ESP *); + void (*dma_invalidate)(struct NCR_ESP *); + void (*dma_irq_entry)(struct NCR_ESP *); + void (*dma_irq_exit)(struct NCR_ESP *); + void (*dma_led_off)(struct NCR_ESP *); + void (*dma_led_on)(struct NCR_ESP *); + void (*dma_poll)(struct NCR_ESP *, unsigned char *); + void (*dma_reset)(struct NCR_ESP *); +}; + +/* Bitfield meanings for the above registers. */ + +/* ESP config reg 1, read-write, found on all ESP chips */ +#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ +#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ +#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ +#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ +#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ +#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ + +/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ +#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ +#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ +#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ +#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */ +#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ +#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ +#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ +#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ +#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */ +#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */ +#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ +#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ +#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ + +/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ +#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ +#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ +#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ +#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ +#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ +#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ +#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ +#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ +#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ +#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ +#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ +#define ESP_CONFIG3_BIGID 0x20 /* SCSI-ID's are 4bits (hme) */ +#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ +#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ +#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ +#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ + +/* ESP command register read-write */ +/* Group 1 commands: These may be sent at any point in time to the ESP + * chip. None of them can generate interrupts 'cept + * the "SCSI bus reset" command if you have not disabled + * SCSI reset interrupts in the config1 ESP register. + */ +#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ +#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ +#define ESP_CMD_RC 0x02 /* Chip reset */ +#define ESP_CMD_RS 0x03 /* SCSI bus reset */ + +/* Group 2 commands: ESP must be an initiator and connected to a target + * for these commands to work. + */ +#define ESP_CMD_TI 0x10 /* Transfer Information */ +#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ +#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ +#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ +#define ESP_CMD_SATN 0x1a /* Set ATN */ +#define ESP_CMD_RATN 0x1b /* De-assert ATN */ + +/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected + * to a target as the initiator for these commands to work. + */ +#define ESP_CMD_SMSG 0x20 /* Send message */ +#define ESP_CMD_SSTAT 0x21 /* Send status */ +#define ESP_CMD_SDATA 0x22 /* Send data */ +#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ +#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ +#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ +#define ESP_CMD_DCNCT 0x27 /* Disconnect */ +#define ESP_CMD_RMSG 0x28 /* Receive Message */ +#define ESP_CMD_RCMD 0x29 /* Receive Command */ +#define ESP_CMD_RDATA 0x2a /* Receive Data */ +#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ + +/* Group 4 commands: The ESP must be in the disconnected state and must + * not be connected to any targets as initiator for + * these commands to work. + */ +#define ESP_CMD_RSEL 0x40 /* Reselect */ +#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ +#define ESP_CMD_SELA 0x42 /* Select w/ATN */ +#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ +#define ESP_CMD_ESEL 0x44 /* Enable selection */ +#define ESP_CMD_DSEL 0x45 /* Disable selections */ +#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ +#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ + +/* This bit enables the ESP's DMA on the SBus */ +#define ESP_CMD_DMA 0x80 /* Do DMA? */ + + +/* ESP status register read-only */ +#define ESP_STAT_PIO 0x01 /* IO phase bit */ +#define ESP_STAT_PCD 0x02 /* CD phase bit */ +#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ +#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ +#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ +#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ +#define ESP_STAT_PERR 0x20 /* Parity error */ +#define ESP_STAT_SPAM 0x40 /* Real bad error */ +/* This indicates the 'interrupt pending' condition on esp236, it is a reserved + * bit on other revs of the ESP. + */ +#define ESP_STAT_INTR 0x80 /* Interrupt */ + +/* HME only: status 2 register */ +#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ +#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ +#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ +#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ +#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ +#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ +#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ +#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ + +/* The status register can be masked with ESP_STAT_PMASK and compared + * with the following values to determine the current phase the ESP + * (at least thinks it) is in. For our purposes we also add our own + * software 'done' bit for our phase management engine. + */ +#define ESP_DOP (0) /* Data Out */ +#define ESP_DIP (ESP_STAT_PIO) /* Data In */ +#define ESP_CMDP (ESP_STAT_PCD) /* Command */ +#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ +#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ +#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ + +/* ESP interrupt register read-only */ +#define ESP_INTR_S 0x01 /* Select w/o ATN */ +#define ESP_INTR_SATN 0x02 /* Select w/ATN */ +#define ESP_INTR_RSEL 0x04 /* Reselected */ +#define ESP_INTR_FDONE 0x08 /* Function done */ +#define ESP_INTR_BSERV 0x10 /* Bus service */ +#define ESP_INTR_DC 0x20 /* Disconnect */ +#define ESP_INTR_IC 0x40 /* Illegal command given */ +#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ + +/* Interrupt status macros */ +#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR)) +#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC)) +#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN)) +#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S)) +#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \ + (ESP_SELECT_WITHOUT_ATN_IRQ(esp))) +#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL)) + +/* ESP sequence step register read-only */ +#define ESP_STEP_VBITS 0x07 /* Valid bits */ +#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ +#define ESP_STEP_SID 0x01 /* One msg byte sent */ +#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ +#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd + * bytes to be lost + */ +#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ + +/* Ho hum, some ESP's set the step register to this as well... */ +#define ESP_STEP_FINI5 0x05 +#define ESP_STEP_FINI6 0x06 +#define ESP_STEP_FINI7 0x07 + +/* ESP chip-test register read-write */ +#define ESP_TEST_TARG 0x01 /* Target test mode */ +#define ESP_TEST_INI 0x02 /* Initiator test mode */ +#define ESP_TEST_TS 0x04 /* Tristate test mode */ + +/* ESP unique ID register read-only, found on fas236+fas100a only */ +#define ESP_UID_F100A 0x00 /* ESP FAS100A */ +#define ESP_UID_F236 0x02 /* ESP FAS236 */ +#define ESP_UID_REV 0x07 /* ESP revision */ +#define ESP_UID_FAM 0xf8 /* ESP family */ + +/* ESP fifo flags register read-only */ +/* Note that the following implies a 16 byte FIFO on the ESP. */ +#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ +#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ +#define ESP_FF_SSTEP 0xe0 /* Sequence step */ + +/* ESP clock conversion factor register write-only */ +#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ +#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ +#define ESP_CCF_F2 0x02 /* 10MHz */ +#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ +#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ +#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ +#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ +#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ + +/* HME only... */ +#define ESP_BUSID_RESELID 0x10 +#define ESP_BUSID_CTR32BIT 0x40 + +#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ +#define ESP_TIMEO_CONST 8192 +#define ESP_NEG_DEFP(mhz, cfact) \ + ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) +#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) +#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) + + +extern struct proc_dir_entry proc_scsi_esp; + +/* UGLY, UGLY, UGLY! */ +extern int nesps, esps_in_use, esps_running; + +/* For our interrupt engine. */ +#define for_each_esp(esp) \ + for((esp) = espchain; (esp); (esp) = (esp)->next) + + +/* External functions */ +extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, + unchar cmd); +extern struct NCR_ESP *esp_allocate(Scsi_Host_Template *, void *); +extern void esp_initialize(struct NCR_ESP *); +extern void esp_intr(int, void *, struct pt_regs *); +#endif /* !(NCR53C9X_H) */ diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index 0290331ce..fedb3ed66 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -196,8 +196,8 @@ __initfunc(int a2091_detect(Scsi_Host_Template *tpnt)) static unsigned char called = 0; struct Scsi_Host *instance; caddr_t address; - int key; - struct ConfigDev *cd; + unsigned int key; + const struct ConfigDev *cd; if (!MACH_IS_AMIGA || called) return 0; @@ -206,8 +206,8 @@ __initfunc(int a2091_detect(Scsi_Host_Template *tpnt)) tpnt->proc_dir = &proc_scsi_a2091; tpnt->proc_info = &wd33c93_proc_info; - while ((key = zorro_find(MANUF_COMMODORE, PROD_A2091, 0, 0)) || - (key = zorro_find(MANUF_COMMODORE, PROD_A590, 0, 0))) { + while ((key = zorro_find(ZORRO_PROD_CBM_A590_A2091_1, 0, 0)) || + (key = zorro_find(ZORRO_PROD_CBM_A590_A2091_2, 0, 0))) { cd = zorro_get_board(key); address = cd->cd_BoardAddr; instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c index f07d5e2cd..7ec76b19a 100644 --- a/drivers/scsi/amiga7xx.c +++ b/drivers/scsi/amiga7xx.c @@ -33,14 +33,17 @@ struct proc_dir_entry proc_scsi_amiga7xx = { S_IFDIR | S_IRUGO | S_IXUGO, 2 }; +extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, + u32 base, int io_port, int irq, int dma, + long long options, int clock); + int amiga7xx_detect(Scsi_Host_Template *tpnt) { static unsigned char called = 0; - int key, clock; - int num = 0; - unsigned long address; + unsigned int key; + int num = 0, clock; long long options; - struct ConfigDev *cd; + const struct ConfigDev *cd; if (called || !MACH_IS_AMIGA) return 0; @@ -48,8 +51,9 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) tpnt->proc_dir = &proc_scsi_amiga7xx; #ifdef CONFIG_WARPENGINE_SCSI - if ((key = zorro_find(MANUF_MACROSYSTEMS, PROD_WARP_ENGINE, 0, 0))) + if ((key = zorro_find(ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, 0, 0))) { + unsigned long address; cd = zorro_get_board(key); address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); @@ -59,7 +63,8 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, + options, clock); zorro_config_board(key, 0); num++; @@ -73,17 +78,18 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, - (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), - 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); + ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), + 0, IRQ_AMIGA_PORTS, DMA_NONE, + options, clock); num++; } #endif #ifdef CONFIG_A4091_SCSI - while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) || - (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) + while ( (key = zorro_find(ZORRO_PROD_CBM_A4091_1, 0, 0)) || + (key = zorro_find(ZORRO_PROD_CBM_A4091_2, 0, 0)) ) { + unsigned long address; cd = zorro_get_board(key); address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); @@ -93,14 +99,34 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address+0x800000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; } #endif +#ifdef CONFIG_GVP_TURBO_SCSI + if((key = zorro_find(ZORRO_PROD_GVP_GFORCE_040_060, 0, 0))) + { + cd = zorro_get_board(key); + address = ZTWO_VADDR((unsigned long)cd->cd_BoardAddr); + + options = OPTION_MEMORY_MAPPED | OPTION_DEBUG_TEST1 | + OPTION_INTFLY | OPTION_SYNCHRONOUS | + OPTION_ALWAYS_SYNCHRONOUS | OPTION_DISCONNECT; + + clock = 50000000; /* 50MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, + (u32)(unsigned char *)(address + 0x40000), + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); + + zorro_config_board(key, 0); + num++; + } +#endif + called = 1; return num; } diff --git a/drivers/scsi/amiga7xx.h b/drivers/scsi/amiga7xx.h index 09317d376..d8f68c628 100644 --- a/drivers/scsi/amiga7xx.h +++ b/drivers/scsi/amiga7xx.h @@ -6,7 +6,7 @@ int amiga7xx_detect(Scsi_Host_Template *); const char *NCR53c7x0_info(void); int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int NCR53c7xx_abort(Scsi_Cmnd *); -int NCR53c7x0_release (Scsi_Host_Template *); +int NCR53c7x0_release (struct Scsi_Host *); int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int); void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c new file mode 100644 index 000000000..60d461182 --- /dev/null +++ b/drivers/scsi/atari_dma_emul.c @@ -0,0 +1,466 @@ +/* + * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades. + * + * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net> + * + * 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. + * + * This code was written using the Hades TOS source code as a + * reference. This source code can be found on the home page + * of Medusa Computer Systems. + * + * Version 0.1, 1997-09-24. + * + * This code should be considered experimental. It has only been + * tested on a Hades with a 68060. It might not work on a Hades + * with a 68040. Make backups of your hard drives before using + * this code. + */ + +#include <asm/uaccess.h> + +#define hades_dma_ctrl (*(unsigned char *) 0xffff8717) +#define hades_psdm_reg (*(unsigned char *) 0xffff8741) + +#define TRANSFER_SIZE 16 + +struct m68040_frame { + unsigned long effaddr; /* effective address */ + unsigned short ssw; /* special status word */ + unsigned short wb3s; /* write back 3 status */ + unsigned short wb2s; /* write back 2 status */ + unsigned short wb1s; /* write back 1 status */ + unsigned long faddr; /* fault address */ + unsigned long wb3a; /* write back 3 address */ + unsigned long wb3d; /* write back 3 data */ + unsigned long wb2a; /* write back 2 address */ + unsigned long wb2d; /* write back 2 data */ + unsigned long wb1a; /* write back 1 address */ + unsigned long wb1dpd0; /* write back 1 data/push data 0*/ + unsigned long pd1; /* push data 1*/ + unsigned long pd2; /* push data 2*/ + unsigned long pd3; /* push data 3*/ +}; + +static void writeback (unsigned short wbs, unsigned long wba, + unsigned long wbd, void *old_buserr) +{ + mm_segment_t fs = get_fs(); + static void *save_buserr; + + __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" + "move.l %0,8(%%a0)\n\t" + : + : "r" (&&bus_error) + : "a0" ); + + save_buserr = old_buserr; + + set_fs (MAKE_MM_SEG(wbs & WBTM_040)); + + switch (wbs & WBSIZ_040) { + case BA_SIZE_BYTE: + put_user (wbd & 0xff, (char *)wba); + break; + case BA_SIZE_WORD: + put_user (wbd & 0xffff, (short *)wba); + break; + case BA_SIZE_LONG: + put_user (wbd, (int *)wba); + break; + } + + set_fs (fs); + return; + +bus_error: + __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" + "bcs.s .jump_old\n\t" + "cmp.l %1,2(%%sp)\n\t" + "bls.s .restore_old\n" + ".jump_old:\n\t" + "move.l %2,-(%%sp)\n\t" + "rts\n" + ".restore_old:\n\t" + "move.l %%a0,-(%%sp)\n\t" + "movec.l %%vbr,%%a0\n\t" + "move.l %2,8(%%a0)\n\t" + "move.l (%%sp)+,%%a0\n\t" + "rte\n\t" + : + : "i" (writeback), "i" (&&bus_error), + "m" (save_buserr) ); +} + +/* + * static inline void set_restdata_reg(unsigned char *cur_addr) + * + * Set the rest data register if necessary. + */ + +static inline void set_restdata_reg(unsigned char *cur_addr) +{ + if (((long) cur_addr & ~3) != 0) + tt_scsi_dma.dma_restdata = + *((unsigned long *) ((long) cur_addr & ~3)); +} + +/* + * void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp) + * + * This code emulates TT SCSI DMA on the Hades. + * + * Note the following: + * + * 1. When there is no byte available to read from the SCSI bus, or + * when a byte cannot yet bet written to the SCSI bus, a bus + * error occurs when reading or writing the pseudo DMA data + * register (hades_psdm_reg). We have to catch this bus error + * and try again to read or write the byte. If after several tries + * we still get a bus error, the interrupt handler is left. When + * the byte can be read or written, the interrupt handler is + * called again. + * + * 2. The SCSI interrupt must be disabled in this interrupt handler. + * + * 3. If we set the EOP signal, the SCSI controller still expects one + * byte to be read or written. Therefore the last byte is transferred + * separately, after setting the EOP signal. + * + * 4. When this function is left, the address pointer (start_addr) is + * converted to a physical address. Because it points one byte + * further than the last transfered byte, it can point outside the + * current page. If virt_to_phys() is called with this address we + * might get an access error. Therefore virt_to_phys() is called with + * start_addr - 1 if the count has reached zero. The result is + * increased with one. + */ + +static void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp) +{ + unsigned long dma_base; + register unsigned long dma_cnt asm ("d3"); + static long save_buserr; + register unsigned long save_sp asm ("d4"); + register int tries asm ("d5"); + register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4"); + register unsigned char *eff_addr; + register unsigned char *psdm_reg; + unsigned long rem; + + atari_disable_irq(IRQ_TT_MFP_SCSI); + + /* + * Read the dma address and count registers. + */ + + dma_base = SCSI_DMA_READ_P(dma_addr); + dma_cnt = SCSI_DMA_READ_P(dma_cnt); + + /* + * Check if DMA is still enabled. + */ + + if ((tt_scsi_dma.dma_ctrl & 2) == 0) + { + atari_enable_irq(IRQ_TT_MFP_SCSI); + return; + } + + if (dma_cnt == 0) + { + printk(KERN_NOTICE "DMA emulation: count is zero.\n"); + tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ + atari_enable_irq(IRQ_TT_MFP_SCSI); + return; + } + + /* + * Install new bus error routine. + */ + + __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" + "move.l 8(%%a0),%0\n\t" + "move.l %1,8(%%a0)\n\t" + : "=&r" (save_buserr) + : "r" (&&scsi_bus_error) + : "a0" ); + + hades_dma_ctrl &= 0xfc; /* Bus error and EOP off. */ + + /* + * Save the stack pointer. + */ + + __asm__ __volatile__ ("move.l %%sp,%0\n\t" + : "=&r" (save_sp) ); + + tries = 100; /* Maximum number of bus errors. */ + start_addr = phys_to_virt(dma_base); + end_addr = start_addr + dma_cnt; + +scsi_loop: + dma_cnt--; + rem = dma_cnt & (TRANSFER_SIZE - 1); + dma_cnt &= ~(TRANSFER_SIZE - 1); + psdm_reg = &hades_psdm_reg; + + if (tt_scsi_dma.dma_ctrl & 1) /* Read or write? */ + { + /* + * SCSI write. Abort when count is zero. + */ + + switch (rem) + { + case 0: + while (dma_cnt > 0) + { + dma_cnt -= TRANSFER_SIZE; + + *psdm_reg = *start_addr++; + case 15: + *psdm_reg = *start_addr++; + case 14: + *psdm_reg = *start_addr++; + case 13: + *psdm_reg = *start_addr++; + case 12: + *psdm_reg = *start_addr++; + case 11: + *psdm_reg = *start_addr++; + case 10: + *psdm_reg = *start_addr++; + case 9: + *psdm_reg = *start_addr++; + case 8: + *psdm_reg = *start_addr++; + case 7: + *psdm_reg = *start_addr++; + case 6: + *psdm_reg = *start_addr++; + case 5: + *psdm_reg = *start_addr++; + case 4: + *psdm_reg = *start_addr++; + case 3: + *psdm_reg = *start_addr++; + case 2: + *psdm_reg = *start_addr++; + case 1: + *psdm_reg = *start_addr++; + } + } + + hades_dma_ctrl |= 1; /* Set EOP. */ + udelay(10); + *psdm_reg = *start_addr++; /* Dummy byte. */ + tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ + } + else + { + /* + * SCSI read. Abort when count is zero. + */ + + switch (rem) + { + case 0: + while (dma_cnt > 0) + { + dma_cnt -= TRANSFER_SIZE; + + *start_addr++ = *psdm_reg; + case 15: + *start_addr++ = *psdm_reg; + case 14: + *start_addr++ = *psdm_reg; + case 13: + *start_addr++ = *psdm_reg; + case 12: + *start_addr++ = *psdm_reg; + case 11: + *start_addr++ = *psdm_reg; + case 10: + *start_addr++ = *psdm_reg; + case 9: + *start_addr++ = *psdm_reg; + case 8: + *start_addr++ = *psdm_reg; + case 7: + *start_addr++ = *psdm_reg; + case 6: + *start_addr++ = *psdm_reg; + case 5: + *start_addr++ = *psdm_reg; + case 4: + *start_addr++ = *psdm_reg; + case 3: + *start_addr++ = *psdm_reg; + case 2: + *start_addr++ = *psdm_reg; + case 1: + *start_addr++ = *psdm_reg; + } + } + + hades_dma_ctrl |= 1; /* Set EOP. */ + udelay(10); + *start_addr++ = *psdm_reg; + tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ + + set_restdata_reg(start_addr); + } + + if (start_addr != end_addr) + printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n"); + + dma_cnt = end_addr - start_addr; + +scsi_end: + dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 : + virt_to_phys(start_addr); + + SCSI_DMA_WRITE_P(dma_addr, dma_base); + SCSI_DMA_WRITE_P(dma_cnt, dma_cnt); + + /* + * Restore old bus error routine. + */ + + __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" + "move.l %0,8(%%a0)\n\t" + : + : "r" (save_buserr) + : "a0" ); + + atari_enable_irq(IRQ_TT_MFP_SCSI); + + return; + +scsi_bus_error: + /* + * First check if the bus error is caused by our code. + * If not, call the original handler. + */ + + __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" + "bcs.s .old_vector\n\t" + "cmp.l %1,2(%%sp)\n\t" + "bls.s .scsi_buserr\n" + ".old_vector:\n\t" + "move.l %2,-(%%sp)\n\t" + "rts\n" + ".scsi_buserr:\n\t" + : + : "i" (&&scsi_loop), "i" (&&scsi_end), + "m" (save_buserr) ); + + if (CPU_IS_060) + { + /* + * Get effective address and restore the stack. + */ + + __asm__ __volatile__ ("move.l 8(%%sp),%0\n\t" + "move.l %1,%%sp\n\t" + : "=a&" (eff_addr) + : "r" (save_sp) ); + } + else + { + register struct m68040_frame *frame; + + __asm__ __volatile__ ("lea 8(%%sp),%0\n\t" + : "=a&" (frame) ); + + if (tt_scsi_dma.dma_ctrl & 1) + { + /* + * Bus error while writing. + */ + + if (frame->wb3s & WBV_040) + { + if (frame->wb3a == (long) &hades_psdm_reg) + start_addr--; + else + writeback(frame->wb3s, frame->wb3a, + frame->wb3d, &&scsi_bus_error); + } + + if (frame->wb2s & WBV_040) + { + if (frame->wb2a == (long) &hades_psdm_reg) + start_addr--; + else + writeback(frame->wb2s, frame->wb2a, + frame->wb2d, &&scsi_bus_error); + } + + if (frame->wb1s & WBV_040) + { + if (frame->wb1a == (long) &hades_psdm_reg) + start_addr--; + } + } + else + { + /* + * Bus error while reading. + */ + + if (frame->wb3s & WBV_040) + writeback(frame->wb3s, frame->wb3a, + frame->wb3d, &&scsi_bus_error); + } + + eff_addr = (unsigned char *) frame->faddr; + + __asm__ __volatile__ ("move.l %0,%%sp\n\t" + : + : "r" (save_sp) ); + } + + dma_cnt = end_addr - start_addr; + + if (eff_addr == &hades_psdm_reg) + { + /* + * Bus error occured while reading the pseudo + * DMA register. Time out. + */ + + tries--; + + if (tries <= 0) + { + if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ + set_restdata_reg(start_addr); + + if (dma_cnt <= 1) + printk(KERN_CRIT "DMA emulation: Fatal " + "error while %s the last byte.\n", + (tt_scsi_dma.dma_ctrl & 1) + ? "writing" : "reading"); + + goto scsi_end; + } + else + goto scsi_loop; + } + else + { + /* + * Bus error during pseudo DMA transfer. + * Terminate the DMA transfer. + */ + + hades_dma_ctrl |= 3; /* Set EOP and bus error. */ + if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ + set_restdata_reg(start_addr); + goto scsi_end; + } +} diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index 50ce159a2..8c43432a7 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -107,6 +107,7 @@ #include "NCR5380.h" #include "constants.h" #include <asm/atari_stdma.h> +#include <asm/atari_stram.h> #include <asm/io.h> #include <linux/stat.h> @@ -255,6 +256,10 @@ static int setup_hostid = -1; MODULE_PARM(setup_hostid, "i"); +#if defined(CONFIG_TT_DMA_EMUL) +#include "atari_dma_emul.c" +#endif + #if defined(REAL_DMA) static int scsi_dma_is_ignored_buserr( unsigned char dma_stat ) @@ -652,8 +657,12 @@ int atari_scsi_detect (Scsi_Host_Template *host) */ if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) { - atari_dma_buffer = scsi_init_malloc(STRAM_BUFFER_SIZE, - GFP_ATOMIC | GFP_DMA); + atari_dma_buffer = atari_stram_alloc( STRAM_BUFFER_SIZE, NULL, "SCSI" ); + if (!atari_dma_buffer) { + printk( KERN_ERR "atari_scsi_detect: can't allocate ST-RAM " + "double buffer\n" ); + return( 0 ); + } atari_dma_phys_buffer = VTOP( atari_dma_buffer ); atari_dma_orig_addr = 0; } @@ -682,7 +691,14 @@ int atari_scsi_detect (Scsi_Host_Template *host) atari_dma_residual = 0; #endif /* REAL_DMA */ #ifdef REAL_DMA - if (is_medusa || is_hades) { +#ifdef CONFIG_TT_DMA_EMUL + if (MACH_IS_HADES) { + request_irq(IRQ_AUTO_2, hades_dma_emulator, + IRQ_TYPE_PRIO, "Hades DMA emulator", + hades_dma_emulator); + } +#endif + if (MACH_IS_MEDUSA || MACH_IS_HADES) { /* While the read overruns (described by Drew Eckhardt in * NCR5380.c) never happened on TTs, they do in fact on the Medusa * (This was the cause why SCSI didn't work right for so long @@ -739,7 +755,7 @@ int atari_scsi_release (struct Scsi_Host *sh) if (IS_A_TT()) free_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr); if (atari_dma_buffer) - scsi_init_free (atari_dma_buffer, STRAM_BUFFER_SIZE); + atari_stram_free (atari_dma_buffer); return 1; } #endif @@ -1003,11 +1019,11 @@ static unsigned long atari_dma_xfer_len( unsigned long wanted_len, int write_flag ) { unsigned long possible_len, limit; - - if (is_hades) +#ifndef CONFIG_TT_DMA_EMUL + if (MACH_IS_HADES) /* Hades has no SCSI DMA at all :-( Always force use of PIO */ return( 0 ); - +#endif if (IS_A_TT()) /* TT SCSI DMA can transfer arbitrary #bytes */ return( wanted_len ); diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 165a68e60..b1fa60b11 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -199,14 +199,22 @@ static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, static int num_gvp11 = 0; +#define CHECK_WD33C93 + __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) { static unsigned char called = 0; struct Scsi_Host *instance; caddr_t address; - enum GVP_ident epc; - int key = 0; - struct ConfigDev *cd; + unsigned int epc; + unsigned int key = 0, skey; + const struct ConfigDev *cd; + unsigned int default_dma_xfer_mask; +#ifdef CHECK_WD33C93 + volatile unsigned char *sasr_3393, *scmd_3393; + unsigned char save_sasr; + unsigned char q, qq; +#endif if (!MACH_IS_AMIGA || called) return 0; @@ -215,7 +223,27 @@ __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) tpnt->proc_dir = &proc_scsi_gvp11; tpnt->proc_info = &wd33c93_proc_info; - while ((key = zorro_find(MANUF_GVP, PROD_GVPIISCSI, 0, key))) { + while (1) { + /* + * This should (hopefully) be the correct way to identify + * all the different GVP SCSI controllers (except for the + * SERIES I though). + */ + skey = key; + + if ((key = zorro_find(ZORRO_PROD_GVP_COMBO_030_R3_SCSI, 0, skey)) || + (key = zorro_find(ZORRO_PROD_GVP_SERIES_II, 0, skey))) + default_dma_xfer_mask = ~0x00ffffff; + else if ((key = zorro_find(ZORRO_PROD_GVP_GFORCE_030_SCSI, 0, skey)) || + (key = zorro_find(ZORRO_PROD_GVP_A530_SCSI, 0, skey)) || + (key = zorro_find(ZORRO_PROD_GVP_COMBO_030_R4_SCSI, 0, skey))) + default_dma_xfer_mask = ~0x01ffffff; + else if ((key = zorro_find(ZORRO_PROD_GVP_A1291, 0, skey)) || + (key = zorro_find(ZORRO_PROD_GVP_GFORCE_040_SCSI_1, 0, skey))) + default_dma_xfer_mask = ~0x07ffffff; + else + break; + cd = zorro_get_board(key); address = cd->cd_BoardAddr; @@ -227,23 +255,77 @@ __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) if (cd->cd_BoardSize != 0x10000) continue; - /* check extended product code */ - epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000); - epc = epc & GVP_PRODMASK; +#ifdef CHECK_WD33C93 - /* - * This should (hopefully) be the correct way to identify - * all the different GVP SCSI controllers (except for the - * SERIES I though). + /* + * These darn GVP boards are a problem - it can be tough to tell + * whether or not they include a SCSI controller. This is the + * ultimate Yet-Another-GVP-Detection-Hack in that it actually + * probes for a WD33c93 chip: If we find one, it's extremely + * likely that this card supports SCSI, regardless of Product_ + * Code, Board_Size, etc. */ - if (!((epc == GVP_A1291_SCSI) || - (epc == GVP_GFORCE_040_SCSI) || - (epc == GVP_GFORCE_030_SCSI) || - (epc == GVP_A530_SCSI) || - (epc == GVP_COMBO_R4_SCSI) || - (epc == GVP_COMBO_R3_SCSI) || - (epc == GVP_SERIESII))) - continue; + + /* Get pointers to the presumed register locations and save contents */ + + sasr_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SASR); + scmd_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SCMD); + save_sasr = *sasr_3393; + + /* First test the AuxStatus Reg */ + + q = *sasr_3393; /* read it */ + if (q & 0x08) /* bit 3 should always be clear */ + continue; + *sasr_3393 = WD_AUXILIARY_STATUS; /* setup indirect address */ + if (*sasr_3393 == WD_AUXILIARY_STATUS) { /* shouldn't retain the write */ + *sasr_3393 = save_sasr; /* Oops - restore this byte */ + continue; + } + if (*sasr_3393 != q) { /* should still read the same */ + *sasr_3393 = save_sasr; /* Oops - restore this byte */ + continue; + } + if (*scmd_3393 != q) /* and so should the image at 0x1f */ + continue; + + + /* Ok, we probably have a wd33c93, but let's check a few other places + * for good measure. Make sure that this works for both 'A and 'B + * chip versions. + */ + + *sasr_3393 = WD_SCSI_STATUS; + q = *scmd_3393; + *sasr_3393 = WD_SCSI_STATUS; + *scmd_3393 = ~q; + *sasr_3393 = WD_SCSI_STATUS; + qq = *scmd_3393; + *sasr_3393 = WD_SCSI_STATUS; + *scmd_3393 = q; + if (qq != q) /* should be read only */ + continue; + *sasr_3393 = 0x1e; /* this register is unimplemented */ + q = *scmd_3393; + *sasr_3393 = 0x1e; + *scmd_3393 = ~q; + *sasr_3393 = 0x1e; + qq = *scmd_3393; + *sasr_3393 = 0x1e; + *scmd_3393 = q; + if (qq != q || qq != 0xff) /* should be read only, all 1's */ + continue; + *sasr_3393 = WD_TIMEOUT_PERIOD; + q = *scmd_3393; + *sasr_3393 = WD_TIMEOUT_PERIOD; + *scmd_3393 = ~q; + *sasr_3393 = WD_TIMEOUT_PERIOD; + qq = *scmd_3393; + *sasr_3393 = WD_TIMEOUT_PERIOD; + *scmd_3393 = q; + if (qq != (~q & 0xff)) /* should be read/write */ + continue; +#endif instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); instance->base = (unsigned char *)ZTWO_VADDR(address); @@ -252,22 +334,9 @@ __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) if (gvp11_xfer_mask) HDATA(instance)->dma_xfer_mask = gvp11_xfer_mask; - else{ - switch (epc){ - case GVP_COMBO_R3_SCSI: - case GVP_SERIESII: - HDATA(instance)->dma_xfer_mask = ~0x00ffffff; - break; - case GVP_GFORCE_030_SCSI: - case GVP_A530_SCSI: - case GVP_COMBO_R4_SCSI: - HDATA(instance)->dma_xfer_mask = ~0x01ffffff; - break; - default: - HDATA(instance)->dma_xfer_mask = ~0x07ffffff; - break; - } - } + else + HDATA(instance)->dma_xfer_mask = default_dma_xfer_mask; + DMA(instance)->secret2 = 1; DMA(instance)->secret1 = 0; @@ -283,16 +352,17 @@ __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) * Check for 14MHz SCSI clock */ if (epc & GVP_SCSICLKMASK) - wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR), - dma_setup, dma_stop, WD33C93_FS_8_10); + wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR), + dma_setup, dma_stop, WD33C93_FS_8_10); else - wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR), - dma_setup, dma_stop, WD33C93_FS_12_15); + wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR), + dma_setup, dma_stop, WD33C93_FS_12_15); if (num_gvp11++ == 0) { - first_instance = instance; - gvp11_template = instance->hostt; - request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0, "GVP11 SCSI", gvp11_intr); + first_instance = instance; + gvp11_template = instance->hostt; + request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0, + "GVP11 SCSI", gvp11_intr); } DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; zorro_config_board(key, 0); diff --git a/drivers/scsi/mvme16x.c b/drivers/scsi/mvme16x.c new file mode 100644 index 000000000..c3ab67906 --- /dev/null +++ b/drivers/scsi/mvme16x.c @@ -0,0 +1,60 @@ +/* + * Detection routine for the NCR53c710 based MVME16x SCSI Controllers for Linux. + * + * Based on work by Alan Hourihane + */ +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/blk.h> +#include <linux/sched.h> +#include <linux/version.h> +#include <linux/config.h> +#include <linux/zorro.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/mvme16xhw.h> +#include <asm/irq.h> + +#include "scsi.h" +#include "hosts.h" +#include "53c7xx.h" +#include "mvme16x.h" +#include "asm/mvme16xhw.h" + +#include<linux/stat.h> + +struct proc_dir_entry proc_scsi_mvme16x = { + PROC_SCSI_MVME16x, 7, "MVME16x", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, + u32 base, int io_port, int irq, int dma, + long long options, int clock); + +int mvme16x_scsi_detect(Scsi_Host_Template *tpnt) +{ + static unsigned char called = 0; + int clock; + long long options; + + if (mvme16x_config & MVME16x_CONFIG_NO_SCSICHIP) { + printk ("SCSI detection disabled, SCSI chip not present\n"); + return 0; + } + if (called) + return 0; + + tpnt->proc_dir = &proc_scsi_mvme16x; + + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; + + clock = 66000000; /* 66MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, (u32)0xfff47000, + 0, 0x55, DMA_NONE, + options, clock); + called = 1; + return 1; +} diff --git a/drivers/scsi/mvme16x.h b/drivers/scsi/mvme16x.h new file mode 100644 index 000000000..1bebf337c --- /dev/null +++ b/drivers/scsi/mvme16x.h @@ -0,0 +1,53 @@ +#ifndef MVME16x_SCSI_H +#define MVME16x_SCSI_H + +#include <linux/types.h> + +int mvme16x_scsi_detect(Scsi_Host_Template *); +const char *NCR53c7x0_info(void); +int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int NCR53c7xx_abort(Scsi_Cmnd *); +int NCR53c7x0_release (Scsi_Host_Template *); +int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int); +void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef CMD_PER_LUN +#define CMD_PER_LUN 3 +#endif + +#ifndef CAN_QUEUE +#define CAN_QUEUE 24 +#endif + +#if defined(HOSTS_C) || defined(MODULE) +#include <scsi/scsicam.h> + +extern struct proc_dir_entry proc_scsi_mvme16x; + +#define MVME16x_SCSI {/* next */ NULL, \ + /* usage_count */ NULL, \ + /* proc_dir_entry */ NULL, \ + /* proc_info */ NULL, \ + /* name */ "MVME16x SCSI", \ + /* detect */ mvme16x_scsi_detect, \ + /* release */ NULL, \ + /* info */ NULL, \ + /* command */ NULL, \ + /* queuecommand */ NCR53c7xx_queue_command, \ + /* abort */ NCR53c7xx_abort, \ + /* reset */ NCR53c7xx_reset, \ + /* slave_attach */ NULL, \ + /* bios_param */ NULL /*scsicam_bios_param*/, \ + /* can_queue */ 24, \ + /* this_id */ 7, \ + /* sg_tablesize */ 127, \ + /* cmd_per_lun */ 3, \ + /* present */ 0, \ + /* unchecked_isa_dma */ 0, \ + /* use_clustering */ DISABLE_CLUSTERING } +#endif +#endif /* MVME16x_SCSI_H */ diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index fa0dc8f06..9826fb990 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -312,7 +312,14 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; #define ASKED_FOR_SENSE 0x20 +#ifdef __mc68000__ +#include <asm/pgtable.h> +#define CONTIGUOUS_BUFFERS(X,Y) \ + (virt_to_phys((X)->b_data+(X)->b_size-1)+1==virt_to_phys((Y)->b_data)) +#else #define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data) +#endif + /* * This is the crap from the old error handling code. We have it in a special @@ -455,14 +462,6 @@ struct scsi_device { unsigned device_blocked:1; /* Device returned QUEUE_FULL. */ }; -#ifdef __mc68000__ -#include <asm/pgtable.h> -#define CONTIGUOUS_BUFFERS(X,Y) \ - (virt_to_phys((X)->b_data+(X)->b_size-1)+1==virt_to_phys((Y)->b_data)) -#else -#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data) -#endif - /* * The Scsi_Cmnd structure is used by scsi.c internally, and for communication diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index ce448de91..afd9fd6a9 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -1,10 +1,13 @@ -/* $Id: sgiwd93.c,v 1.4 1998/03/04 10:49:47 ralf Exp $ +/* * sgiwd93.c: SGI WD93 scsi driver. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * * (In all truth, Jed Schimmel wrote all this code.) + * + * $Id: sgiwd93.c,v 1.7 1996/07/23 09:00:16 dm Exp $ */ +#include <linux/init.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/blk.h> @@ -214,7 +217,7 @@ static inline void init_hpc_chain(uchar *buf) hcp->desc.pnext = PHYSADDR(buf); } -int sgiwd93_detect(Scsi_Host_Template *HPsUX) +__initfunc(int sgiwd93_detect(Scsi_Host_Template *HPsUX)) { static unsigned char called = 0; struct hpc3_scsiregs *hregs = &hpc3c0->scsi_chan0; diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 6d60d12ae..6946cc788 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -1490,7 +1490,7 @@ int wd33c93_abort (Scsi_Cmnd *cmd) } #define MAX_WD33C93_HOSTS 4 -#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) +#define MAX_SETUP_ARGS ((int)(sizeof(setup_args) / sizeof(char *))) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; static char setup_used[MAX_SETUP_ARGS]; diff --git a/drivers/sgi/char/cons_newport.c b/drivers/sgi/char/cons_newport.c index b8a7f958d..b49ba172b 100644 --- a/drivers/sgi/char/cons_newport.c +++ b/drivers/sgi/char/cons_newport.c @@ -3,9 +3,9 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: cons_newport.c,v 1.7 1998/03/03 01:23:05 ralf Exp $ + * $Id: cons_newport.c,v 1.8 1998/03/03 16:57:28 ralf Exp $ */ - +#include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/sched.h> @@ -573,8 +573,7 @@ struct graphics_ops newport_graphic_ops = { newport_reset, newport_ioctl /* g_reset_console, g_ioctl */ }; -struct graphics_ops * -newport_probe (int slot, const char **name) +__initfunc(struct graphics_ops * newport_probe (int slot, const char **name)) { struct newport_regs *p; diff --git a/drivers/sgi/char/graphics.c b/drivers/sgi/char/graphics.c index 663d124fa..3f9a80d14 100644 --- a/drivers/sgi/char/graphics.c +++ b/drivers/sgi/char/graphics.c @@ -22,6 +22,7 @@ * the reasons behind them. */ #include <linux/fs.h> +#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/sched.h> #include <linux/mm.h> @@ -292,15 +293,13 @@ static struct miscdevice dev_opengl = { }; /* This is called later from the misc-init routine */ -void -gfx_register (void) +__initfunc(void gfx_register (void)) { misc_register (&dev_graphics); misc_register (&dev_opengl); } -void -gfx_init (const char **name) +__initfunc(void gfx_init (const char **name)) { struct console_ops *console; struct graphics_ops *g; diff --git a/drivers/sgi/char/sgiserial.c b/drivers/sgi/char/sgiserial.c index e377d3241..12176d6d2 100644 --- a/drivers/sgi/char/sgiserial.c +++ b/drivers/sgi/char/sgiserial.c @@ -1,6 +1,9 @@ -/* sgiserial.c: Serial port driver for SGI machines. +/* + * sgiserial.c: Serial port driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id$ */ #include <linux/config.h> /* for CONFIG_REMOTE_DEBUG */ diff --git a/drivers/sgi/char/streamable.c b/drivers/sgi/char/streamable.c index f26aad0e2..02129fde4 100644 --- a/drivers/sgi/char/streamable.c +++ b/drivers/sgi/char/streamable.c @@ -5,9 +5,10 @@ * Major 10 is the streams clone device. The IRIX Xsgi server just * opens /dev/gfx and closes it inmediately. * + * $Id$ */ - #include <linux/fs.h> +#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/sched.h> #include <linux/kbd_kern.h> @@ -351,8 +352,7 @@ static struct miscdevice dev_input_mouse = { SGI_STREAMS_KEYBOARD, "streams-mouse", &sgi_mouse_fops }; -void -streamable_init (void) +__initfunc(void streamable_init (void)) { printk ("streamable misc devices registered (keyb:%d, gfx:%d)\n", SGI_STREAMS_KEYBOARD, SGI_GFX_MINOR); diff --git a/drivers/sgi/char/usema.c b/drivers/sgi/char/usema.c index dfef772eb..83a26a717 100644 --- a/drivers/sgi/char/usema.c +++ b/drivers/sgi/char/usema.c @@ -20,9 +20,10 @@ * usema(7m), usinit(3p), usnewsema(3p) * /usr/include/sys/usioctl.h * -*/ - + * $Id$ + */ #include <linux/fs.h> +#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/sched.h> #include <linux/file.h> @@ -186,8 +187,7 @@ static struct miscdevice dev_usemaclone = { SGI_USEMACLONE, "usemaclone", &sgi_usemaclone_fops }; -void -usema_init(void) +__initfunc(void usema_init(void)) { printk("usemaclone misc device registered (minor: %d)\n", SGI_USEMACLONE); diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c index bf95b84d5..e00108b0b 100644 --- a/drivers/sound/pas2_card.c +++ b/drivers/sound/pas2_card.c @@ -164,7 +164,7 @@ static int config_pas_hw(struct address_info *hw_config) } else { - if (request_irq(pas_irq, pasintr, "PAS16", 0, NULL) < 0) + if (request_irq(pas_irq, pasintr, 0, "PAS16", NULL) < 0) ok = 0; } } diff --git a/drivers/video/Config.in b/drivers/video/Config.in index e39e33bfc..b4766876b 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -7,43 +7,70 @@ if [ "$CONFIG_FB" = "y" ]; then mainmenu_option next_comment comment 'Frame buffer devices' - if [ "$CONFIG_AMIGA" = "y" ]; then + if [ "$CONFIG_APOLLO" = "y" ]; then + define_bool CONFIG_FB_APOLLO y + fi + if [ "$CONFIG_AMIGA" = "y" ]; then bool 'Amiga native chipset support' CONFIG_FB_AMIGA if [ "$CONFIG_FB_AMIGA" != "n" ]; then bool 'Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS bool 'Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS bool 'Amiga AGA chipset support' CONFIG_FB_AMIGA_AGA fi - tristate 'Amiga Cybervision support' CONFIG_FB_CYBER + tristate 'Amiga CyberVision support' CONFIG_FB_CYBER if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Amiga CyberVision3D support (experimental)' CONFIG_FB_VIRGE tristate 'Amiga RetinaZ3 support' CONFIG_FB_RETINAZ3 fi fi if [ "$CONFIG_ATARI" = "y" ]; then bool 'Atari native chipset support' CONFIG_FB_ATARI -# tristate 'Mach64 Frame Buffer support' CONFIG_FB_MACH64 + bool 'ATI Mach64 display support' CONFIG_FB_ATY + fi + if [ "$CONFIG_PPC" = "y" ]; then + bool 'Open Firmware frame buffer device support' CONFIG_FB_OF + bool 'S3 Trio frame buffer device support' CONFIG_FB_S3TRIO + if [ "$CONFIG_FB_OF" = "y" ]; then +# bool 'Apple "control" display support' CONFIG_FB_CONTROL +# bool 'Apple "platinum" display support' CONFIG_FB_PLATINUM +# bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE + bool 'ATI Mach64 display support' CONFIG_FB_ATY +# bool 'IMS Twin Turbo display support' CONFIG_FB_IMSTT +# bool 'Chips 65550 display support' CONFIG_FB_CT65550 +# bool 'S3 Trio display support' CONFIG_FB_S3TRIO + fi + fi + if [ "$CONFIG_MAC" = "y" ]; then + define_bool CONFIG_FB_MAC y + fi + if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then + define_bool CONFIG_FB_TGA y fi - if [ "$CONFIG_CHRP" = "y" -o "$CONFIG_PMAC" = "y" ]; then - bool 'Open Firmware frame buffer device support' CONFIG_FB_OPEN_FIRMWARE - fi - tristate 'Virtual Frame Buffer support' CONFIG_FB_VIRTUAL + tristate 'Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL bool 'Advanced low level driver options' CONFIG_FBCON_ADVANCED if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then - tristate 'Monochrome support' CONFIG_FBCON_MFB - tristate 'Interleaved bitplanes support' CONFIG_FBCON_ILBM - tristate 'Normal bitplanes support' CONFIG_FBCON_AFB - tristate 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2 - tristate 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4 - tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8 - tristate '8 bpp packed pixel support' CONFIG_FBCON_CFB8 - tristate '16 bpp packed pixel support' CONFIG_FBCON_CFB16 - tristate 'Cybervision support (accelerated)' CONFIG_FBCON_CYBER - tristate 'RetinaZ3 support (accelerated)' CONFIG_FBCON_RETINAZ3 + bool 'Monochrome support' CONFIG_FBCON_MFB + bool '2 bpp packed pixels support' CONFIG_FBCON_CFB2 + bool '4 bpp packed pixels support' CONFIG_FBCON_CFB4 + bool '8 bpp packed pixels support' CONFIG_FBCON_CFB8 + bool '16 bpp packed pixels support' CONFIG_FBCON_CFB16 + bool '24 bpp packed pixels support' CONFIG_FBCON_CFB24 + bool '32 bpp packed pixels support' CONFIG_FBCON_CFB32 + bool 'Amiga bitplanes support' CONFIG_FBCON_AFB + bool 'Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM + bool 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2 + bool 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4 + bool 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8 + bool 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC else - if [ "$CONFIG_FB_AMIGA" != "n" -o "$CONFIG_FB_ATARI" != "n" -o \ - "$CONFIG_FB_CYBER" != "n" -o "$CONFIG_FB_RETINAZ3" != "n" -o \ - "$CONFIG_FB_VIRTUAL" != "n" ]; then + if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" -o \ + "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \ + "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" -o \ + "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" -o \ + "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRGE" = "m" -o \ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then define_bool CONFIG_FBCON_MFB y fi if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" ]; then @@ -55,20 +82,32 @@ if [ "$CONFIG_FB" = "y" ]; then define_bool CONFIG_FBCON_IPLAN2P4 y define_bool CONFIG_FBCON_IPLAN2P8 y fi + if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then + define_bool CONFIG_FBCON_MAC y + define_bool CONFIG_FBCON_CFB2 y + define_bool CONFIG_FBCON_CFB4 y + fi if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \ - "$CONFIG_FB_OPEN_FIRMWARE" = "y" -o \ + "$CONFIG_FB_OF" = "y" -o "$CONFIG_FB_OF" = "m" -o \ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_TGA" = "m" -o \ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then define_bool CONFIG_FBCON_CFB8 y fi if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \ + "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then define_bool CONFIG_FBCON_CFB16 y fi - if [ "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" ]; then - define_bool CONFIG_FBCON_CYBER y + if [ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then + define_bool CONFIG_FBCON_CFB24 y fi - if [ "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" ]; then - define_bool CONFIG_FBCON_RETINAZ3 y + if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \ + "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \ + "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then + define_bool CONFIG_FBCON_CFB32 y fi fi diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ebed69d68..2349e4720 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -22,15 +22,11 @@ MOD_LIST_NAME := VIDEO_MODULES # Frame Buffer Console ifeq ($(CONFIG_FB),y) - L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o - LX_OBJS += fbcon.o fbcmap.o + L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o font_6x11.o + LX_OBJS += fbcon.o fbcmap.o fbgen.o endif -# Frame buffer devices - -ifeq ($(CONFIG_APOLLO),y) -L_OBJS += dn_fb.o -endif +# Frame Buffer Devices ifeq ($(CONFIG_FB_AMIGA),y) L_OBJS += amifb.o @@ -40,6 +36,10 @@ else endif endif +ifeq ($(CONFIG_FB_APOLLO),y) +L_OBJS += dnfb.o +endif + ifeq ($(CONFIG_FB_ATARI),y) L_OBJS += atafb.o else @@ -48,134 +48,114 @@ else endif endif +ifeq ($(CONFIG_FB_ATY),y) +L_OBJS += atyfb.o +endif + ifeq ($(CONFIG_FB_CYBER),y) -LX_OBJS += cyberfb.o +L_OBJS += cyberfb.o else ifeq ($(CONFIG_FB_CYBER),m) - MX_OBJS += cyberfb.o + M_OBJS += cyberfb.o endif endif +ifeq ($(CONFIG_FB_MAC),y) +L_OBJS += macfb.o +endif + +ifeq ($(CONFIG_FB_OF),y) +L_OBJS += offb.o +endif + ifeq ($(CONFIG_FB_RETINAZ3),y) -LX_OBJS += retz3fb.o +L_OBJS += retz3fb.o else ifeq ($(CONFIG_FB_RETINAZ3),m) - MX_OBJS += retz3fb.o + M_OBJS += retz3fb.o endif endif -ifeq ($(CONFIG_FB_VIRTUAL),y) -L_OBJS += vfb.o +ifeq ($(CONFIG_FB_S3TRIO),y) +L_OBJS += S3triofb.o else - ifeq ($(CONFIG_FB_VIRTUAL),m) - M_OBJS += vfb.o + ifeq ($(CONFIG_FB_S3TRIO),m) + M_OBJS += S3triofb.o endif endif -ifeq ($(CONFIG_FB_OPEN_FIRMWARE),y) -L_OBJS += offb.o +ifeq ($(CONFIG_FB_TGA),y) +L_OBJS += tgafb.o endif -ifeq ($(CONFIG_FB_MACH64),y) -L_OBJS += mach64fb.o +ifeq ($(CONFIG_FB_VIRGE),y) +L_OBJS += virgefb.o else - ifeq ($(CONFIG_FB_MACH64),m) - M_OBJS += mach64fb.o + ifeq ($(CONFIG_FB_VIRGE),m) + M_OBJS += virgefb.o endif endif -ifeq ($(CONFIG_FB_TGA),y) -L_OBJS += tgafb.o +ifeq ($(CONFIG_FB_VIRTUAL),y) +L_OBJS += vfb.o +else + ifeq ($(CONFIG_FB_VIRTUAL),m) + M_OBJS += vfb.o + endif endif -# Low level drivers +# Generic Low Level Drivers -ifeq ($(CONFIG_FBCON_AFB),y) -L_OBJS += fbcon-afb.o -else - ifeq ($(CONFIG_FBCON_AFB),m) - M_OBJS += fbcon-afb.o - endif +ifdef CONFIG_FBCON_AFB +LX_OBJS += fbcon-afb.o endif -ifeq ($(CONFIG_FBCON_CFB8),y) -L_OBJS += fbcon-cfb8.o -else - ifeq ($(CONFIG_FBCON_CFB8),m) - M_OBJS += fbcon-cfb8.o - endif +ifdef CONFIG_FBCON_CFB2 +LX_OBJS += fbcon-cfb2.o endif -ifeq ($(CONFIG_FBCON_CFB16),y) +ifdef CONFIG_FBCON_CFB4 +LX_OBJS += fbcon-cfb4.o +endif + +ifdef CONFIG_FBCON_CFB8 +LX_OBJS += fbcon-cfb8.o +endif + +ifdef CONFIG_FBCON_CFB16 LX_OBJS += fbcon-cfb16.o -else - ifeq ($(CONFIG_FBCON_CFB16),m) - MX_OBJS += fbcon-cfb16.o - endif endif -ifeq ($(CONFIG_FBCON_ILBM),y) -L_OBJS += fbcon-ilbm.o -else - ifeq ($(CONFIG_FBCON_ILBM),m) - M_OBJS += fbcon-ilbm.o - endif +ifdef CONFIG_FBCON_CFB24 +LX_OBJS += fbcon-cfb24.o endif -ifeq ($(CONFIG_FBCON_IPLAN2P2),y) -L_OBJS += fbcon-iplan2p2.o -else - ifeq ($(CONFIG_FBCON_IPLAN2P2),m) - M_OBJS += fbcon-iplan2p2.o - endif +ifdef CONFIG_FBCON_CFB32 +LX_OBJS += fbcon-cfb32.o endif -ifeq ($(CONFIG_FBCON_IPLAN2P4),y) -L_OBJS += fbcon-iplan2p4.o -else - ifeq ($(CONFIG_FBCON_IPLAN2P4),m) - M_OBJS += fbcon-iplan2p4.o - endif +ifdef CONFIG_FBCON_ILBM +LX_OBJS += fbcon-ilbm.o endif -ifeq ($(CONFIG_FBCON_IPLAN2P8),y) -L_OBJS += fbcon-iplan2p8.o -else - ifeq ($(CONFIG_FBCON_IPLAN2P8),m) - M_OBJS += fbcon-iplan2p8.o - endif +ifdef CONFIG_FBCON_IPLAN2P2 +LX_OBJS += fbcon-iplan2p2.o endif -ifeq ($(CONFIG_FBCON_MFB),y) -L_OBJS += fbcon-mfb.o -else - ifeq ($(CONFIG_FBCON_MFB),m) - M_OBJS += fbcon-mfb.o - endif +ifdef CONFIG_FBCON_IPLAN2P4 +LX_OBJS += fbcon-iplan2p4.o endif -ifeq ($(CONFIG_FBCON_CYBER),y) -L_OBJS += fbcon-cyber.o -else - ifeq ($(CONFIG_FBCON_CYBER),m) - M_OBJS += fbcon-cyber.o - endif +ifdef CONFIG_FBCON_IPLAN2P8 +LX_OBJS += fbcon-iplan2p8.o endif -ifeq ($(CONFIG_FBCON_RETINAZ3),y) -L_OBJS += fbcon-retz3.o -else - ifeq ($(CONFIG_FBCON_RETINAZ3),m) - M_OBJS += fbcon-retz3.o - endif +ifdef CONFIG_FBCON_MAC +LX_OBJS += fbcon-mac.o endif -ifeq ($(CONFIG_FBCON_MACH64),y) -L_OBJS += fbcon-mach64.o -else - ifeq ($(CONFIG_FBCON_MACH64),m) - M_OBJS += fbcon-mach64.o - endif +ifdef CONFIG_FBCON_MFB +LX_OBJS += fbcon-mfb.o endif # GSP Console @@ -184,7 +164,7 @@ ifdef CONFIG_AMIGA_GSP L_OBJS := $(L_OBJS) gspcon.o gspcore.o endif -# VGA Console +# VGA Text Console ifdef CONFIG_ABSTRACT_CONSOLE ifdef CONFIG_VGA_CONSOLE diff --git a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c new file mode 100644 index 000000000..16c0b888a --- /dev/null +++ b/drivers/video/S3triofb.c @@ -0,0 +1,885 @@ +/* + * linux/drivers/video/S3Triofb.c -- Open Firmware based frame buffer device + * + * Copyright (C) 1997 Peter De Schrijver + * + * This driver is partly based on the PowerMac console driver: + * + * Copyright (C) 1996 Paul Mackerras + * + * and on the Open Firmware based frame buffer device: + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * 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. + */ + +/* + Bugs : + OF dependencies should be removed. + + This driver should be merged with the CyberVision driver. The + CyberVision is a Zorro III implementation of the S3Trio64 chip. + +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <linux/pci.h> + +#include "fbcon-cfb8.h" + + +#define mem_in8(addr) in_8((void *)(addr)) +#define mem_in16(addr) in_le16((void *)(addr)) +#define mem_in32(addr) in_le32((void *)(addr)) + +#define mem_out8(val, addr) out_8((void *)(addr), val) +#define mem_out16(val, addr) out_le16((void *)(addr), val) +#define mem_out32(val, addr) out_le32((void *)(addr), val) + +#define IO_OUT16VAL(v, r) (((v) << 8) | (r)) + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + +static int currcon = 0; +static struct display disp; +static struct fb_info fb_info; +static struct { u_char red, green, blue, pad; } palette[256]; +static char s3trio_name[16] = "S3Trio "; + + +static struct fb_fix_screeninfo fb_fix; +static struct fb_var_screeninfo fb_var = { 0, }; + + + /* + * Interface used by the world + */ + +void of_video_setup(char *options, int *ints); + +static int s3trio_open(struct fb_info *info); +static int s3trio_release(struct fb_info *info); +static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int s3trio_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int s3trio_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int s3trio_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + +#ifdef CONFIG_FB_COMPAT_XPMAC +extern struct vc_mode display_info; +extern struct fb_info *console_fb_info; +extern int (*console_setmode_ptr)(struct vc_mode *, int); +extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int, + struct fb_info *); +static int s3trio_console_setmode(struct vc_mode *mode, int doit); +#endif /* CONFIG_FB_COMPAT_XPMAC */ + + /* + * Interface to the low level console driver + */ + +unsigned long s3trio_fb_init(unsigned long mem_start); +static int s3triofbcon_switch(int con, struct fb_info *info); +static int s3triofbcon_updatevar(int con, struct fb_info *info); +static void s3triofbcon_blank(int blank, struct fb_info *info); +static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con); + + /* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static struct display_switch fbcon_trio8; +#endif + + /* + * Accelerated Functions used by the low level console driver + */ + +static void Trio_WaitQueue(u_short fifo); +static void Trio_WaitBlit(void); +static void Trio_BitBLT(u_short curx, u_short cury, u_short destx, + u_short desty, u_short width, u_short height, + u_short mode); +static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height, + u_short mode, u_short color); +static void Trio_MoveCursor(u_short x, u_short y); + + + /* + * Internal routines + */ + +static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); +static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static void do_install_cmap(int con); + + +static struct fb_ops s3trio_ops = { + s3trio_open, s3trio_release, s3trio_get_fix, s3trio_get_var, s3trio_set_var, + s3trio_get_cmap, s3trio_set_cmap, s3trio_pan_display, NULL, s3trio_ioctl +}; + + + /* + * Open/Release the frame buffer device + */ + +static int s3trio_open(struct fb_info *info) +{ + /* + * Nothing, only a usage count for the moment + */ + + MOD_INC_USE_COUNT; + return(0); +} + +static int s3trio_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + + + /* + * Get the Fixed Part of the Display + */ + +static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + memcpy(fix, &fb_fix, sizeof(fb_fix)); + return 0; +} + + + /* + * Get the User Defined Part of the Display + */ + +static int s3trio_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + memcpy(var, &fb_var, sizeof(fb_var)); + return 0; +} + + + /* + * Set the User Defined Part of the Display + */ + +static int s3trio_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (var->xres > fb_var.xres || var->yres > fb_var.yres || + var->xres_virtual > fb_var.xres_virtual || + var->yres_virtual > fb_var.yres_virtual || + var->bits_per_pixel > fb_var.bits_per_pixel || + var->nonstd || var->vmode != FB_VMODE_NONINTERLACED) + return -EINVAL; + memcpy(var, &fb_var, sizeof(fb_var)); + return 0; +} + + + /* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ + +static int s3trio_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (var->xoffset || var->yoffset) + return -EINVAL; + else + return 0; +} + + /* + * Get the Colormap + */ + +static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (con == currcon) /* current console? */ + return fb_get_cmap(cmap, &fb_display[con].var, kspc, s3trio_getcolreg, + info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return 0; +} + + /* + * Set the Colormap + */ + +static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1<<fb_display[con].var.bits_per_pixel, 0))) + return err; + } + if (con == currcon) /* current console? */ + return fb_set_cmap(cmap, &fb_display[con].var, kspc, s3trio_setcolreg, + info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + + +static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + +__initfunc(int s3trio_resetaccel(void)) { + + +#define EC01_ENH_ENB 0x0005 +#define EC01_LAW_ENB 0x0010 +#define EC01_MMIO_ENB 0x0020 + +#define EC00_RESET 0x8000 +#define EC00_ENABLE 0x4000 +#define MF_MULT_MISC 0xE000 +#define SRC_FOREGROUND 0x0020 +#define SRC_BACKGROUND 0x0000 +#define MIX_SRC 0x0007 +#define MF_T_CLIP 0x1000 +#define MF_L_CLIP 0x2000 +#define MF_B_CLIP 0x3000 +#define MF_R_CLIP 0x4000 +#define MF_PIX_CONTROL 0xA000 +#define MFA_SRC_FOREGR_MIX 0x0000 +#define MF_PIX_CONTROL 0xA000 + + outw(EC00_RESET, 0x42e8); + inw( 0x42e8); + outw(EC00_ENABLE, 0x42e8); + inw( 0x42e8); + outw(EC01_ENH_ENB | EC01_LAW_ENB, + 0x4ae8); + outw(MF_MULT_MISC, 0xbee8); /* 16 bit I/O registers */ + + /* Now set some basic accelerator registers */ + Trio_WaitQueue(0x0400); + outw(SRC_FOREGROUND | MIX_SRC, 0xbae8); + outw(SRC_BACKGROUND | MIX_SRC, 0xb6e8);/* direct color*/ + outw(MF_T_CLIP | 0, 0xbee8 ); /* clip virtual area */ + outw(MF_L_CLIP | 0, 0xbee8 ); + outw(MF_R_CLIP | (640 - 1), 0xbee8); + outw(MF_B_CLIP | (480 - 1), 0xbee8); + Trio_WaitQueue(0x0400); + outw(0xffff, 0xaae8); /* Enable all planes */ + outw(0xffff, 0xaae8); /* Enable all planes */ + outw( MF_PIX_CONTROL | MFA_SRC_FOREGR_MIX, 0xbee8); + +} + +__initfunc(int s3trio_init(void)) { + + u_char bus, dev; + unsigned int t32; + unsigned short cmd; + int i; + + bus=0; + dev=(3<<3); + pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); + if(t32 == (PCI_DEVICE_ID_S3_TRIO << 16) + PCI_VENDOR_ID_S3) { + pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); + pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32); + pcibios_read_config_word(bus, dev, PCI_COMMAND,&cmd); + + pcibios_write_config_word(bus, dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,0xffffffff); + pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); + +/* This is a gross hack as OF only maps enough memory for the framebuffer and + we want to use MMIO too. We should find out which chunk of address space + we can use here */ + pcibios_write_config_dword(bus,dev,PCI_BASE_ADDRESS_0,0xc6000000); + + /* unlock s3 */ + + outb(0x01, 0x3C3); + + outb(inb(0x03CC) | 1, 0x3c2); + + outw(IO_OUT16VAL(0x48, 0x38),0x03D4); + outw(IO_OUT16VAL(0xA0, 0x39),0x03D4); + outb(0x33,0x3d4); + outw(IO_OUT16VAL( inb(0x3d5) & ~(0x2 | + 0x10 | 0x40) , 0x33),0x3d4); + + outw(IO_OUT16VAL(0x6,0x8), 0x3c4); + + /* switch to MMIO only mode */ + + outb(0x58,0x3d4); + outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10,0x58),0x3d4); + outw(IO_OUT16VAL(8,0x53),0x3d4); + + /* switch off I/O accesses */ + +#if 0 + pcibios_write_config_word(bus, dev, PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); +#endif + } + + return 0; +} + + + /* + * Initialisation + * We heavily rely on OF for the moment. This needs fixing. + */ + +__initfunc(unsigned long s3trio_fb_init(unsigned long mem_start)) +{ + struct device_node *dp; + int i, err, *pp, len; + unsigned *up, address; + u_long *CursorBase; + + if (!prom_display_paths[0]) + return mem_start; + if (!(dp = find_path_device(prom_display_paths[0]))) + return mem_start; + + strncat(s3trio_name, dp->name, sizeof(s3trio_name)); + s3trio_name[sizeof(s3trio_name)-1] = '\0'; + strcpy(fb_fix.id, s3trio_name); + + if((pp = (int *)get_property(dp, "vendor-id", &len)) != NULL + && *pp!=PCI_VENDOR_ID_S3) { + printk("%s: can't find S3 Trio board\n", dp->full_name); + return mem_start; + } + + if((pp = (int *)get_property(dp, "device-id", &len)) != NULL + && *pp!=PCI_DEVICE_ID_S3_TRIO) { + printk("%s: can't find S3 Trio board\n", dp->full_name); + return mem_start; + } + + if ((pp = (int *)get_property(dp, "depth", &len)) != NULL + && len == sizeof(int) && *pp != 8) { + printk("%s: can't use depth = %d\n", dp->full_name, *pp); + return mem_start; + } + if ((pp = (int *)get_property(dp, "width", &len)) != NULL + && len == sizeof(int)) + fb_var.xres = fb_var.xres_virtual = *pp; + if ((pp = (int *)get_property(dp, "height", &len)) != NULL + && len == sizeof(int)) + fb_var.yres = fb_var.yres_virtual = *pp; + if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL + && len == sizeof(int)) + fb_fix.line_length = *pp; + else + fb_fix.line_length = fb_var.xres_virtual; + fb_fix.smem_len = fb_fix.line_length*fb_var.yres; + + s3trio_init(); + address=0xc6000000; + fb_fix.smem_start = ioremap(address,64*1024*1024); + fb_fix.type = FB_TYPE_PACKED_PIXELS; + fb_fix.type_aux = 0; + + + s3trio_resetaccel(); + + mem_out8(0x30,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0x2d,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0x2e,fb_fix.smem_start+0x1008000 + 0x03D4); + + mem_out8(0x50,fb_fix.smem_start+0x1008000 + 0x03D4); + + /* disable HW cursor */ + + mem_out8(0x39,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0xa0,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x4e,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x4f,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5); + + /* init HW cursor */ + + CursorBase=(u_long *)(fb_fix.smem_start + 2*1024*1024 - 0x400); + for (i=0; i < 8; i++) { + *(CursorBase +(i*4)) = 0xffffff00; + *(CursorBase+1+(i*4)) = 0xffff0000; + *(CursorBase+2+(i*4)) = 0xffff0000; + *(CursorBase+3+(i*4)) = 0xffff0000; + } + for (i=8; i < 64; i++) { + *(CursorBase +(i*4)) = 0xffff0000; + *(CursorBase+1+(i*4)) = 0xffff0000; + *(CursorBase+2+(i*4)) = 0xffff0000; + *(CursorBase+3+(i*4)) = 0xffff0000; + } + + + mem_out8(0x4c,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(((2*1024 - 1)&0xf00)>>8,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x4d,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8((2*1024 - 1) & 0xff,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_in8(fb_fix.smem_start+0x1008000 + 0x03D4); + + mem_out8(0x4a,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5); + mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5); + mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x4b,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5); + mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5); + mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5); + + mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4); + mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5); + + s3trio_setcolreg(255, 56, 100, 160, 0, NULL /* not used */); + s3trio_setcolreg(254, 0, 0, 0, 0, NULL /* not used */); + memset((char *)fb_fix.smem_start,0,640*480); + +#if 0 + Trio_RectFill(0,0,90,90,7,1); +#endif + + fb_fix.visual = FB_VISUAL_PSEUDOCOLOR ; + fb_var.xoffset = fb_var.yoffset = 0; + fb_var.bits_per_pixel = 8; + fb_var.grayscale = 0; + fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0; + fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8; + fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0; + fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0; + fb_var.nonstd = 0; + fb_var.activate = 0; + fb_var.height = fb_var.width = -1; + fb_var.accel = 5; + fb_var.pixclock = 1; + fb_var.left_margin = fb_var.right_margin = 0; + fb_var.upper_margin = fb_var.lower_margin = 0; + fb_var.hsync_len = fb_var.vsync_len = 0; + fb_var.sync = 0; + fb_var.vmode = FB_VMODE_NONINTERLACED; + + disp.var = fb_var; + disp.cmap.start = 0; + disp.cmap.len = 0; + disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL; + disp.screen_base = fb_fix.smem_start; + disp.visual = fb_fix.visual; + disp.type = fb_fix.type; + disp.type_aux = fb_fix.type_aux; + disp.ypanstep = 0; + disp.ywrapstep = 0; + disp.line_length = fb_fix.line_length; + disp.can_soft_blank = 1; + disp.inverse = 0; +#ifdef CONFIG_FBCON_CFB8 + disp.dispsw = &fbcon_trio8; +#else + disp.dispsw = NULL; +#endif + + strcpy(fb_info.modename, "Trio64 "); + strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename)); + fb_info.node = -1; + fb_info.fbops = &s3trio_ops; +#if 0 + fb_info.fbvar_num = 1; + fb_info.fbvar = &fb_var; +#endif + fb_info.disp = &disp; + fb_info.fontname[0] = '\0'; + fb_info.changevar = NULL; + fb_info.switch_con = &s3triofbcon_switch; + fb_info.updatevar = &s3triofbcon_updatevar; + fb_info.blank = &s3triofbcon_blank; +#if 0 + fb_info.setcmap = &s3triofbcon_setcmap; +#endif + +#ifdef CONFIG_FB_COMPAT_XPMAC + if (!console_fb_info) { + display_info.height = fb_var.yres; + display_info.width = fb_var.xres; + display_info.depth = 8; + display_info.pitch = fb_fix.line_length; + display_info.mode = 0; + strncpy(display_info.name, dp->name, sizeof(display_info.name)); + display_info.fb_address = (unsigned long)fb_fix.smem_start; + display_info.disp_reg_address = address + 0x1008000; + display_info.cmap_adr_address = address + 0x1008000 + 0x3c8; + display_info.cmap_data_address = address + 0x1008000 + 0x3c9; + console_fb_info = &fb_info; + console_setmode_ptr = s3trio_console_setmode; + console_set_cmap_ptr = s3trio_set_cmap; + } +#endif /* CONFIG_FB_COMPAT_XPMAC) */ + + err = register_framebuffer(&fb_info); + if (err < 0) + return mem_start; + + printk("fb%d: S3 Trio frame buffer device on %s\n", + GET_FB_IDX(fb_info.node), dp->full_name); + + return mem_start; +} + + +static int s3triofbcon_switch(int con, struct fb_info *info) +{ + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, + s3trio_getcolreg, info); + + currcon = con; + /* Install new colormap */ + do_install_cmap(con); + return 0; +} + + /* + * Update the `var' structure (called by fbcon.c) + */ + +static int s3triofbcon_updatevar(int con, struct fb_info *info) +{ + /* Nothing */ + return 0; +} + + /* + * Blank the display. + */ + +static void s3triofbcon_blank(int blank, struct fb_info *info) +{ + /* Nothing */ +} + + /* + * Set the colormap + */ + +static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con) +{ + return(s3trio_set_cmap(cmap, 1, con, &fb_info)); +} + + + /* + * Read a single color register and split it into + * colors/transparent. Return != 0 for invalid regno. + */ + +static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info) +{ + if (regno > 255) + return 1; + *red = palette[regno].red; + *green = palette[regno].green; + *blue = palette[regno].blue; + return 0; +} + + + /* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + */ + +static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + if (regno > 255) + return 1; + palette[regno].red = red; + palette[regno].green = green; + palette[regno].blue = blue; + + mem_out8(regno,fb_fix.smem_start+0x1008000 + 0x3c8); + mem_out8((red & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9); + mem_out8((green & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9); + mem_out8((blue & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9); + + return 0; +} + + +static void do_install_cmap(int con) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, + s3trio_setcolreg, &fb_info); + else + fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, s3trio_setcolreg, &fb_info); +} + +#ifdef CONFIG_FB_COMPAT_XPMAC + + /* + * Backward compatibility mode for Xpmac + */ + +static int s3trio_console_setmode(struct vc_mode *mode, int doit) +{ + int err; + struct fb_var_screeninfo var; + struct s3trio_par par; + + if (mode->mode <= 0 || mode->mode > VMODE_MAX ) + return -EINVAL; + par.video_mode = mode->mode; + + switch (mode->depth) { + case 24: + case 32: + par.color_mode = CMODE_32; + break; + case 16: + par.color_mode = CMODE_16; + break; + case 8: + case 0: /* (default) */ + par.color_mode = CMODE_8; + break; + default: + return -EINVAL; + } + encode_var(&var, &par); + if ((err = decode_var(&var, &par))) + return err; + if (doit) + s3trio_set_var(&var, currcon, 0); + return 0; +} + +#endif /* CONFIG_FB_COMPAT_XPMAC */ + +void s3trio_video_setup(char *options, int *ints) { + + return; + +} + +static void Trio_WaitQueue(u_short fifo) { + + u_short status; + + do + { + status = mem_in16(fb_fix.smem_start + 0x1000000 + 0x9AE8); + } while (!(status & fifo)); + +} + +static void Trio_WaitBlit(void) { + + u_short status; + + do + { + status = mem_in16(fb_fix.smem_start + 0x1000000 + 0x9AE8); + } while (status & 0x200); + +} + +static void Trio_BitBLT(u_short curx, u_short cury, u_short destx, + u_short desty, u_short width, u_short height, + u_short mode) { + + u_short blitcmd = 0xc011; + + /* Set drawing direction */ + /* -Y, X maj, -X (default) */ + + if (curx > destx) + blitcmd |= 0x0020; /* Drawing direction +X */ + else { + curx += (width - 1); + destx += (width - 1); + } + + if (cury > desty) + blitcmd |= 0x0080; /* Drawing direction +Y */ + else { + cury += (height - 1); + desty += (height - 1); + } + + Trio_WaitQueue(0x0400); + + outw(0xa000, 0xBEE8); + outw(0x60 | mode, 0xBAE8); + + outw(curx, 0x86E8); + outw(cury, 0x82E8); + + outw(destx, 0x8EE8); + outw(desty, 0x8AE8); + + outw(height - 1, 0xBEE8); + outw(width - 1, 0x96E8); + + outw(blitcmd, 0x9AE8); + +} + +static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height, + u_short mode, u_short color) { + + u_short blitcmd = 0x40b1; + + Trio_WaitQueue(0x0400); + + outw(0xa000, 0xBEE8); + outw((0x20 | mode), 0xBAE8); + outw(0xe000, 0xBEE8); + outw(color, 0xA6E8); + outw(x, 0x86E8); + outw(y, 0x82E8); + outw((height - 1), 0xBEE8); + outw((width - 1), 0x96E8); + outw(blitcmd, 0x9AE8); + +} + + +static void Trio_MoveCursor(u_short x, u_short y) { + + mem_out8(0x39, fb_fix.smem_start + 0x1008000 + 0x3d4); + mem_out8(0xa0, fb_fix.smem_start + 0x1008000 + 0x3d5); + + mem_out8(0x46, fb_fix.smem_start + 0x1008000 + 0x3d4); + mem_out8((x & 0x0700) >> 8, fb_fix.smem_start + 0x1008000 + 0x3d5); + mem_out8(0x47, fb_fix.smem_start + 0x1008000 + 0x3d4); + mem_out8(x & 0x00ff, fb_fix.smem_start + 0x1008000 + 0x3d5); + + mem_out8(0x48, fb_fix.smem_start + 0x1008000 + 0x3d4); + mem_out8((y & 0x0700) >> 8, fb_fix.smem_start + 0x1008000 + 0x3d5); + mem_out8(0x49, fb_fix.smem_start + 0x1008000 + 0x3d4); + mem_out8(y & 0x00ff, fb_fix.smem_start + 0x1008000 + 0x3d5); + +} + + + /* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static void fbcon_trio8_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width) +{ + sx *= 8; dx *= 8; width *= 8; + Trio_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx, + (u_short)(dy*p->fontheight), (u_short)width, + (u_short)(height*p->fontheight), (u_short)S3_NEW); +} + +static void fbcon_trio8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) +{ + unsigned char bg; + + sx *= 8; width *= 8; + bg = attr_bgcol_ec(p,conp); + Trio_RectFill((u_short)sx, + (u_short)(sy*p->fontheight), + (u_short)width, + (u_short)(height*p->fontheight), + (u_short)S3_NEW, + (u_short)bg); +} + +static void fbcon_trio8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) +{ + Trio_WaitBlit(); + fbcon_cfb8_putc(conp, p, c, yy, xx); +} + +static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) +{ + Trio_WaitBlit(); + fbcon_cfb8_putcs(conp, p, s, count, yy, xx); +} + +static void fbcon_trio8_revc(struct display *p, int xx, int yy) +{ + Trio_WaitBlit(); + fbcon_cfb8_revc(p, xx, yy); +} + +static struct display_switch fbcon_trio8 = { + fbcon_cfb8_setup, fbcon_trio8_bmove, fbcon_trio8_clear, fbcon_trio8_putc, + fbcon_trio8_putcs, fbcon_trio8_revc +}; +#endif diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 7aa9df61a..09ff067df 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -60,6 +60,11 @@ #include <asm/amigaints.h> #include <asm/setup.h> +#include "fbcon-afb.h" +#include "fbcon-ilbm.h" +#include "fbcon-mfb.h" + + #define DEBUG #if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA) @@ -535,15 +540,6 @@ static u_short maxfmode, chipset; /* - * Monitor Specifications - * - * These are typical for a `generic' Amiga monitor (e.g. A1960) - */ - -static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000; - - - /* * Various macros */ @@ -672,7 +668,7 @@ static u_short *lofsprite, *shfsprite, *dummysprite; * Current Video Mode */ -static struct amiga_fb_par { +static struct amifb_par { /* General Values */ @@ -740,6 +736,7 @@ static struct amiga_fb_par { static int currcon = 0; static struct display disp; + static struct fb_info fb_info; @@ -789,229 +786,199 @@ static u_short is_lace = 0; /* Screen is laced */ * The rest of the name is filled in during initialization */ -static char amiga_fb_name[16] = "Amiga "; +static char amifb_name[16] = "Amiga "; - /* - * Predefined Video Mode Names - * - * The a2024-?? modes don't work yet because there's no A2024 driver. - */ - -static char *amiga_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", /* - * AmigaOS Video Modes - */ - - "ntsc", /* 640x200, 15 kHz, 60 Hz (NTSC) */ - "ntsc-lace", /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */ - "pal", /* 640x256, 15 kHz, 50 Hz (PAL) */ - "pal-lace", /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */ - "multiscan", /* 640x480, 29 kHz, 57 Hz */ - "multiscan-lace", /* 640x960, 29 kHz, 57 Hz interlaced */ - "a2024-10", /* 1024x800, 10 Hz (Not yet supported) */ - "a2024-15", /* 1024x800, 15 Hz (Not yet supported) */ - "euro36", /* 640x200, 15 kHz, 72 Hz */ - "euro36-lace", /* 640x400, 15 kHz, 72 Hz interlaced */ - "euro72", /* 640x400, 29 kHz, 68 Hz */ - "euro72-lace", /* 640x800, 29 kHz, 68 Hz interlaced */ - "super72", /* 800x300, 23 kHz, 70 Hz */ - "super72-lace", /* 800x600, 23 kHz, 70 Hz interlaced */ - "dblntsc", /* 640x200, 27 kHz, 57 Hz doublescan */ - "dblntsc-ff", /* 640x400, 27 kHz, 57 Hz */ - "dblntsc-lace", /* 640x800, 27 kHz, 57 Hz interlaced */ - "dblpal", /* 640x256, 27 kHz, 47 Hz doublescan */ - "dblpal-ff", /* 640x512, 27 kHz, 47 Hz */ - "dblpal-lace", /* 640x1024, 27 kHz, 47 Hz interlaced */ - - /* - * VGA Video Modes - */ - - "vga", /* 640x480, 31 kHz, 60 Hz (VGA) */ - "vga70", /* 640x400, 31 kHz, 70 Hz (VGA) */ - - /* - * User Defined Video Modes: to be set after boot up using e.g. fbset - */ - - "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7" -}; - -static struct fb_var_screeninfo amiga_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode + * Predefined Video Modes + * */ - { 0, }, - - /* - * AmigaOS Video Modes - */ +static struct fb_videomode amifb_predefined[] __initdata = { - { - /* ntsc */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 44, 16, 76, 2, - FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* ntsc-lace */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 88, 33, 76, 4, - FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* pal */ - 640, 256, 640, 256, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 40, 14, 76, 2, - FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* pal-lace */ - 640, 512, 640, 512, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 80, 29, 76, 4, - FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* multiscan */ - 640, 480, 640, 480, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 29, 8, 72, 8, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - - }, { - /* multiscan-lace */ - 640, 960, 640, 960, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 58, 16, 72, 16, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* a2024-10 (Not yet supported) */ - 1024, 800, 1024, 800, 0, 0, 2, 0, - {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* a2024-15 (Not yet supported) */ - 1024, 800, 1024, 800, 0, 0, 2, 0, - {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro36 */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro36-lace */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* euro72 */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro72-lace */ - 640, 800, 640, 800, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* super72 */ - 800, 300, 800, 300, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* super72-lace */ - 800, 600, 800, 600, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* dblntsc */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4, - 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP - }, { - /* dblntsc-ff */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* dblntsc-lace */ - 640, 800, 640, 800, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* dblpal */ - 640, 256, 640, 256, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4, - 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP - }, { - /* dblpal-ff */ - 640, 512, 640, 512, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* dblpal-lace */ - 640, 1024, 640, 1024, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, - - /* - * VGA Video Modes - */ + /* + * AmigaOS Video Modes + */ - { - /* vga */ - 640, 480, 640, 480, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* vga70 */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2, - FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, + { + "ntsc", { /* 640x200, 15 kHz, 60 Hz (NTSC) */ + 640, 200, 640, 200, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 44, 16, 76, 2, + FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "ntsc-lace", { /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */ + 640, 400, 640, 400, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 88, 33, 76, 4, + FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "pal", { /* 640x256, 15 kHz, 50 Hz (PAL) */ + 640, 256, 640, 256, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 40, 14, 76, 2, + FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "pal-lace", { /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */ + 640, 512, 640, 512, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 80, 29, 76, 4, + FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "multiscan", { /* 640x480, 29 kHz, 57 Hz */ + 640, 480, 640, 480, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 96, 112, 29, 8, 72, 8, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "multiscan-lace", { /* 640x960, 29 kHz, 57 Hz interlaced */ + 640, 960, 640, 960, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 96, 112, 58, 16, 72, 16, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "euro36", { /* 640x200, 15 kHz, 72 Hz */ + 640, 200, 640, 200, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 92, 124, 6, 6, 52, 5, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "euro36-lace", { /* 640x400, 15 kHz, 72 Hz interlaced */ + 640, 400, 640, 400, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 92, 124, 12, 12, 52, 10, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "euro72", { /* 640x400, 29 kHz, 68 Hz */ + 640, 400, 640, 400, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 164, 92, 9, 9, 80, 8, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "euro72-lace", { /* 640x800, 29 kHz, 68 Hz interlaced */ + 640, 800, 640, 800, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 164, 92, 18, 18, 80, 16, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "super72", { /* 800x300, 23 kHz, 70 Hz */ + 800, 300, 800, 300, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 212, 140, 10, 11, 80, 7, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "super72-lace", { /* 800x600, 23 kHz, 70 Hz interlaced */ + 800, 600, 800, 600, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 212, 140, 20, 22, 80, 14, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "dblntsc", { /* 640x200, 27 kHz, 57 Hz doublescan */ + 640, 200, 640, 200, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 18, 17, 80, 4, + 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP + } + }, { + "dblntsc-ff", { /* 640x400, 27 kHz, 57 Hz */ + 640, 400, 640, 400, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 36, 35, 80, 7, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "dblntsc-lace", { /* 640x800, 27 kHz, 57 Hz interlaced */ + 640, 800, 640, 800, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 72, 70, 80, 14, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, { + "dblpal", { /* 640x256, 27 kHz, 47 Hz doublescan */ + 640, 256, 640, 256, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 14, 13, 80, 4, + 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP + } + }, { + "dblpal-ff", { /* 640x512, 27 kHz, 47 Hz */ + 640, 512, 640, 512, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 28, 27, 80, 7, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "dblpal-lace", { /* 640x1024, 27 kHz, 47 Hz interlaced */ + 640, 1024, 640, 1024, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 56, 54, 80, 14, + 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP + } + }, + + /* + * VGA Video Modes + */ + + { + "vga", { /* 640x480, 31 kHz, 60 Hz (VGA) */ + 640, 480, 640, 480, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 64, 96, 30, 9, 112, 2, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "vga70", { /* 640x400, 31 kHz, 70 Hz (VGA) */ + 640, 400, 640, 400, 0, 0, 4, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_SHRES, 64, 96, 35, 12, 112, 2, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, - /* - * User Defined Video Modes - */ +#if 0 - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } + /* + * A2024 video modes + * These modes don't work yet because there's no A2024 driver. + */ + + { + "a2024-10", { /* 1024x800, 10 Hz */ + 1024, 800, 1024, 800, 0, 0, 2, 0, + {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 0, 0, 0, 0, 0, 0, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + }, { + "a2024-15", { /* 1024x800, 15 Hz */ + 1024, 800, 1024, 800, 0, 0, 2, 0, + {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, TAG_HIRES, 0, 0, 0, 0, 0, 0, + 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP + } + } +#endif }; -#define NUM_USER_MODES (8) -#define NUM_TOTAL_MODES arraysize(amiga_fb_predefined) -#define NUM_PREDEF_MODES (NUM_TOTAL_MODES-NUM_USER_MODES) +#define NUM_TOTAL_MODES arraysize(amifb_predefined) static int amifb_ilbm = 0; /* interleaved or normal bitplanes */ - static int amifb_inverse = 0; -static int amifb_usermode = 0; +static int amifb_usermode __initdata = 0; +static int amifb_userdepth __initdata = -1; /* * Some default modes @@ -1023,6 +990,9 @@ static int amifb_usermode = 0; #define DEFMODE_AMBER_NTSC "ntsc-lace" /* for flicker fixed NTSC (A3000) */ #define DEFMODE_AGA "vga70" /* for AGA */ +static struct fb_var_screeninfo amifb_default; + + /* * Macros for the conversion from real world values to hardware register * values @@ -1181,40 +1151,47 @@ static u_short sprfetchmode[3] = { * Interface used by the world */ -void amiga_video_setup(char *options, int *ints); - -static int amiga_fb_open(int fbidx); -static int amiga_fb_release(int fbidx); -static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con); -static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con); -static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); - -static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con); -static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con); -static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con); +void amifb_setup(char *options, int *ints); + +static int amifb_open(struct fb_info *info); +static int amifb_release(struct fb_info *info); +static int amifb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int amifb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int amifb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int amifb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int amifb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + +static int amifb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con); +static int amifb_get_var_cursorinfo(struct fb_var_cursorinfo *var, + u_char *data, int con); +static int amifb_set_var_cursorinfo(struct fb_var_cursorinfo *var, + u_char *data, int con); +static int amifb_get_cursorstate(struct fb_cursorstate *state, int con); +static int amifb_set_cursorstate(struct fb_cursorstate *state, int con); /* * Interface to the low level console driver */ -unsigned long amiga_fb_init(unsigned long mem_start); -static int amifbcon_switch(int con); -static int amifbcon_updatevar(int con); -static void amifbcon_blank(int blank); -static int amifbcon_setcmap(struct fb_cmap *cmap, int con); +unsigned long amifb_init(unsigned long mem_start); +static int amifbcon_switch(int con, struct fb_info *info); +static int amifbcon_updatevar(int con, struct fb_info *info); +static void amifbcon_blank(int blank, struct fb_info *info); /* * Internal routines */ -static void do_install_cmap(int con); +static void do_install_cmap(int con, struct fb_info *info); static int flash_cursor(void); static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp); static void get_video_mode(const char *name); @@ -1227,22 +1204,22 @@ static char *strtoke(char *s,const char *ct); */ static int ami_encode_fix(struct fb_fix_screeninfo *fix, - struct amiga_fb_par *par); + struct amifb_par *par); static int ami_decode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par); + struct amifb_par *par); static int ami_encode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par); -static void ami_get_par(struct amiga_fb_par *par); + struct amifb_par *par); +static void ami_get_par(struct amifb_par *par); static void ami_set_var(struct fb_var_screeninfo *var); #ifdef DEBUG -static void ami_set_par(struct amiga_fb_par *par); +static void ami_set_par(struct amifb_par *par); #endif static void ami_pan_var(struct fb_var_screeninfo *var); static int ami_update_par(void); static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); + u_int transp, struct fb_info *info); static void ami_update_display(void); static void ami_init_display(void); static void ami_do_blank(void); @@ -1265,29 +1242,14 @@ static void ami_rebuild_copper(void); extern unsigned short ami_intena_vals[]; -static struct fb_ops amiga_fb_ops = { - amiga_fb_open, amiga_fb_release, amiga_fb_get_fix, amiga_fb_get_var, - amiga_fb_set_var, amiga_fb_get_cmap, amiga_fb_set_cmap, - amiga_fb_pan_display, amiga_fb_ioctl +static struct fb_ops amifb_ops = { + amifb_open, amifb_release, amifb_get_fix, amifb_get_var, + amifb_set_var, amifb_get_cmap, amifb_set_cmap, + amifb_pan_display, NULL, amifb_ioctl }; -struct useropts { - long xres; - long yres; - long xres_virtual; - long yres_virtual; - long bits_per_pixel; - long left_margin; - long right_margin; - long upper_margin; - long lower_margin; - long hsync_len; - long vsync_len; -} useropts __initdata = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; -__initfunc(void amiga_video_setup(char *options, int *ints)) +__initfunc(void amifb_setup(char *options, int *ints)) { char *this_opt; char mcap_spec[80]; @@ -1313,51 +1275,51 @@ __initfunc(void amiga_video_setup(char *options, int *ints)) else if (!strncmp(this_opt, "fstart:", 7)) min_fstrt = simple_strtoul(this_opt+7, NULL, 0); else if (!strncmp(this_opt, "depth:", 6)) - useropts.bits_per_pixel = simple_strtoul(this_opt+6, NULL, 0); + amifb_userdepth = simple_strtoul(this_opt+6, NULL, 0); else if (!strncmp(this_opt, "size:", 5)) { p = this_opt + 5; if (*p != ';') - useropts.xres = simple_strtoul(p, NULL, 0); + amifb_default.xres = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p != ';') - useropts.yres = simple_strtoul(p, NULL, 0); + amifb_default.yres = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p != ';') - useropts.xres_virtual = simple_strtoul(p, NULL, 0); + amifb_default.xres_virtual = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p != ';') - useropts.yres_virtual = simple_strtoul(p, NULL, 0); + amifb_default.yres_virtual = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p) - useropts.bits_per_pixel = simple_strtoul(p, NULL, 0); + amifb_default.bits_per_pixel = simple_strtoul(p, NULL, 0); } else if (!strncmp(this_opt, "timing:", 7)) { p = this_opt + 7; if (*p != ';') - useropts.left_margin = simple_strtoul(p, NULL, 0); + amifb_default.left_margin = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p != ';') - useropts.right_margin = simple_strtoul(p, NULL, 0); + amifb_default.right_margin = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p != ';') - useropts.upper_margin = simple_strtoul(p, NULL, 0); + amifb_default.upper_margin = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p) - useropts.lower_margin = simple_strtoul(p, NULL, 0); + amifb_default.lower_margin = simple_strtoul(p, NULL, 0); } else if (!strncmp(this_opt, "sync:", 5)) { p = this_opt + 5; if (*p != ';') - useropts.hsync_len = simple_strtoul(p, NULL, 0); + amifb_default.hsync_len = simple_strtoul(p, NULL, 0); if (!(p = strchr(p, ';'))) continue; if (*++p) - useropts.vsync_len = simple_strtoul(p, NULL, 0); + amifb_default.vsync_len = simple_strtoul(p, NULL, 0); } else get_video_mode(this_opt); } @@ -1395,10 +1357,10 @@ __initfunc(void amiga_video_setup(char *options, int *ints)) if (hmax <= 0 || hmax <= hmin) goto cap_invalid; - vfmin = vmin; - vfmax = vmax; - hfmin = hmin; - hfmax = hmax; + fb_info.monspecs.vfmin = vmin; + fb_info.monspecs.vfmax = vmax; + fb_info.monspecs.hfmin = hmin; + fb_info.monspecs.hfmax = hmax; cap_invalid: ; } @@ -1408,7 +1370,7 @@ cap_invalid: * Open/Release the frame buffer device */ -static int amiga_fb_open(int fbidx) +static int amifb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -1418,7 +1380,7 @@ static int amiga_fb_open(int fbidx) return(0); } -static int amiga_fb_release(int fbidx) +static int amifb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); @@ -1429,9 +1391,10 @@ static int amiga_fb_release(int fbidx) * Get the Fixed Part of the Display */ -static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int amifb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { - struct amiga_fb_par par; + struct amifb_par par; if (con == -1) ami_get_par(&par); @@ -1448,12 +1411,13 @@ static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con) +static int amifb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { int err = 0; if (con == -1) { - struct amiga_fb_par par; + struct amifb_par par; ami_get_par(&par); err = ami_encode_var(var, &par); @@ -1466,11 +1430,12 @@ static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con) * Set the User Defined Part of the Display */ -static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con) +static int amifb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { int err, activate = var->activate; int oldxres, oldyres, oldvxres, oldvyres, oldbpp; - struct amiga_fb_par par; + struct amifb_par par; struct display *display; if (con >= 0) @@ -1513,13 +1478,32 @@ static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con) display->line_length = fix.line_length; display->can_soft_blank = 1; display->inverse = amifb_inverse; + switch (fix.type) { +#ifdef CONFIG_FBCON_ILBM + case FB_TYPE_INTERLEAVED_PLANES: + display->dispsw = &fbcon_ilbm; + break; +#endif +#ifdef CONFIG_FBCON_AFB + case FB_TYPE_PLANES: + display->dispsw = &fbcon_afb; + break; +#endif +#ifdef CONFIG_FBCON_MFB + case FB_TYPE_PACKED_PIXELS: /* depth == 1 */ + display->dispsw = &fbcon_mfb; + break; +#endif + default: + display->dispsw = NULL; + } if (fb_info.changevar) (*fb_info.changevar)(con); } if (oldbpp != var->bits_per_pixel) { if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) return err; - do_install_cmap(con); + do_install_cmap(con, info); } if (con == currcon) ami_set_var(&display->var); @@ -1533,7 +1517,8 @@ static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con) +static int amifb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { if (var->vmode & FB_VMODE_YWRAP) { if (var->yoffset<0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset) @@ -1562,15 +1547,16 @@ static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ return fb_get_cmap(cmap, &fb_display[con].var, kspc, - ami_getcolreg); + ami_getcolreg, info); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } @@ -1579,7 +1565,8 @@ static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { int err; @@ -1591,7 +1578,7 @@ static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) } if (con == currcon) /* current console? */ return fb_set_cmap(cmap, &fb_display[con].var, kspc, - ami_setcolreg); + ami_setcolreg, info); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return 0; @@ -1601,8 +1588,8 @@ static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) * Amiga Frame Buffer Specific ioctls */ -static int amiga_fb_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg, int con) +static int amifb_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg, int con, struct fb_info *info) { int i; @@ -1612,7 +1599,7 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrfix)); if (!i) { - i = amiga_fb_get_fix_cursorinfo(&crsrfix, con); + i = amifb_get_fix_cursorinfo(&crsrfix, con); copy_to_user((void *)arg, &crsrfix, sizeof(crsrfix)); } return i; @@ -1622,7 +1609,7 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrvar)); if (!i) { - i = amiga_fb_get_var_cursorinfo(&crsrvar, + i = amifb_get_var_cursorinfo(&crsrvar, ((struct fb_var_cursorinfo *)arg)->data, con); copy_to_user((void *)arg, &crsrvar, sizeof(crsrvar)); } @@ -1634,7 +1621,7 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar)); if (!i) { copy_from_user(&crsrvar, (void *)arg, sizeof(crsrvar)); - i = amiga_fb_set_var_cursorinfo(&crsrvar, + i = amifb_set_var_cursorinfo(&crsrvar, ((struct fb_var_cursorinfo *)arg)->data, con); } return i; @@ -1644,7 +1631,7 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate)); if (!i) { - i = amiga_fb_get_cursorstate(&crsrstate, con); + i = amifb_get_cursorstate(&crsrstate, con); copy_to_user((void *)arg, &crsrstate, sizeof(crsrstate)); } return i; @@ -1655,27 +1642,27 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate)); if (!i) { copy_from_user(&crsrstate, (void *)arg, sizeof(crsrstate)); - i = amiga_fb_set_cursorstate(&crsrstate, con); + i = amifb_set_cursorstate(&crsrstate, con); } return i; } #ifdef DEBUG case FBCMD_GET_CURRENTPAR : { - struct amiga_fb_par par; + struct amifb_par par; - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amiga_fb_par)); + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amifb_par)); if (!i) { ami_get_par(&par); - copy_to_user((void *)arg, &par, sizeof(struct amiga_fb_par)); + copy_to_user((void *)arg, &par, sizeof(struct amifb_par)); } return i; } case FBCMD_SET_CURRENTPAR : { - struct amiga_fb_par par; + struct amifb_par par; - i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amiga_fb_par)); + i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amifb_par)); if (!i) { - copy_from_user(&par, (void *)arg, sizeof(struct amiga_fb_par)); + copy_from_user(&par, (void *)arg, sizeof(struct amifb_par)); ami_set_par(&par); } return i; @@ -1689,27 +1676,27 @@ static int amiga_fb_ioctl(struct inode *inode, struct file *file, * Hardware Cursor */ -static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) +static int amifb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) { return ami_get_fix_cursorinfo(fix, con); } -static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) +static int amifb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) { return ami_get_var_cursorinfo(var, data, con); } -static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) +static int amifb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) { return ami_set_var_cursorinfo(var, data, con); } -static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con) +static int amifb_get_cursorstate(struct fb_cursorstate *state, int con) { return ami_get_cursorstate(state, con); } -static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con) +static int amifb_set_cursorstate(struct fb_cursorstate *state, int con) { return ami_set_cursorstate(state, con); } @@ -1719,7 +1706,7 @@ static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con) * Initialisation */ -__initfunc(unsigned long amiga_fb_init(unsigned long mem_start)) +__initfunc(unsigned long amifb_init(unsigned long mem_start)) { int err, tag, i; u_long chipptr; @@ -1745,7 +1732,7 @@ __initfunc(unsigned long amiga_fb_init(unsigned long mem_start)) switch (amiga_chipset) { #ifdef CONFIG_FB_AMIGA_OCS case CS_OCS: - strcat(amiga_fb_name, "OCS"); + strcat(amifb_name, "OCS"); default_chipset: chipset = TAG_OCS; maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */ @@ -1761,7 +1748,7 @@ default_chipset: #ifdef CONFIG_FB_AMIGA_ECS case CS_ECS: - strcat(amiga_fb_name, "ECS"); + strcat(amifb_name, "ECS"); chipset = TAG_ECS; maxdepth[TAG_SHRES] = 2; maxdepth[TAG_HIRES] = 4; @@ -1785,7 +1772,7 @@ default_chipset: #ifdef CONFIG_FB_AMIGA_AGA case CS_AGA: - strcat(amiga_fb_name, "AGA"); + strcat(amifb_name, "AGA"); chipset = TAG_AGA; maxdepth[TAG_SHRES] = 8; maxdepth[TAG_HIRES] = 8; @@ -1804,7 +1791,7 @@ default_chipset: default: #ifdef CONFIG_FB_AMIGA_OCS printk("Unknown graphics chipset, defaulting to OCS\n"); - strcat(amiga_fb_name, "Unknown"); + strcat(amifb_name, "Unknown"); goto default_chipset; #else /* CONFIG_FB_AMIGA_OCS */ return mem_start; @@ -1824,26 +1811,42 @@ default_chipset: * Replace the Tag Values with the Real Pixel Clock Values */ - for (i = 0; i < NUM_PREDEF_MODES; i++) { - tag = amiga_fb_predefined[i].pixclock; + if (amifb_userdepth != -1) + amifb_default.bits_per_pixel = amifb_userdepth; + for (i = 0; i < NUM_TOTAL_MODES; i++) { + struct fb_var_screeninfo *var = &amifb_predefined[i].var; + tag = var->pixclock; if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) { - amiga_fb_predefined[i].pixclock = pixclock[tag]; - if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag]) - amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag]; + var->pixclock = pixclock[tag]; + if (var->bits_per_pixel > maxdepth[tag]) + var->bits_per_pixel = maxdepth[tag]; } } + tag = amifb_default.pixclock; + if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) { + amifb_default.pixclock = pixclock[tag]; + if (amifb_default.bits_per_pixel > maxdepth[tag]) + amifb_default.bits_per_pixel = maxdepth[tag]; + } + + /* + * These monitor specs are for a typical Amiga monitor (e.g. A1960) + */ + if (fb_info.monspecs.hfmin == 0) { + fb_info.monspecs.hfmin = 15000; + fb_info.monspecs.hfmax = 38000; + fb_info.monspecs.vfmin = 49; + fb_info.monspecs.vfmax = 90; + } - strcpy(fb_info.modename, amiga_fb_name); + strcpy(fb_info.modename, amifb_name); fb_info.changevar = NULL; fb_info.node = -1; - fb_info.fbops = &amiga_fb_ops; - fb_info.fbvar_num = NUM_TOTAL_MODES; - fb_info.fbvar = amiga_fb_predefined; + fb_info.fbops = &amifb_ops; fb_info.disp = &disp; fb_info.switch_con = &amifbcon_switch; fb_info.updatevar = &amifbcon_updatevar; fb_info.blank = &amifbcon_blank; - fb_info.setcmap = &amifbcon_setcmap; err = register_framebuffer(&fb_info); if (err < 0) @@ -1889,10 +1892,11 @@ default_chipset: custom.intena = IF_VERTB; custom.intena = IF_SETCLR | IF_COPER; - amiga_fb_set_var(&amiga_fb_predefined[0], -1); + amifb_set_var(&amifb_default, -1, &fb_info); - printk("%s frame buffer device, using %ldK of video memory\n", - fb_info.modename, videomemorysize>>10); + printk("fb%d: %s frame buffer device, using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, + videomemorysize>>10); /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; @@ -1900,17 +1904,17 @@ default_chipset: return mem_start; } -static int amifbcon_switch(int con) +static int amifbcon_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, - &fb_display[currcon].var, 1, ami_getcolreg); + &fb_display[currcon].var, 1, ami_getcolreg, info); currcon = con; ami_set_var(&fb_display[con].var); /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -1918,7 +1922,7 @@ static int amifbcon_switch(int con) * Update the `var' structure (called by fbcon.c) */ -static int amifbcon_updatevar(int con) +static int amifbcon_updatevar(int con, struct fb_info *info) { ami_pan_var(&fb_display[con].var); return 0; @@ -1928,7 +1932,7 @@ static int amifbcon_updatevar(int con) * Blank the display. */ -static void amifbcon_blank(int blank) +static void amifbcon_blank(int blank, struct fb_info *info) { do_blank = blank ? blank : -1; } @@ -1937,23 +1941,17 @@ static void amifbcon_blank(int blank) * Set the colormap */ -static int amifbcon_setcmap(struct fb_cmap *cmap, int con) -{ - return(amiga_fb_set_cmap(cmap, 1, con)); -} - - -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - ami_setcolreg); + ami_setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), &fb_display[con].var, 1, - ami_setcolreg); + ami_setcolreg, info); } static int flash_cursor(void) @@ -2039,33 +2037,9 @@ __initfunc(static void get_video_mode(const char *name)) { int i; - for (i = 1; i < NUM_PREDEF_MODES; i++) { - if (!strcmp(name, amiga_fb_modenames[i])) { - amiga_fb_predefined[0] = amiga_fb_predefined[i]; - - if (useropts.xres != -1) - amiga_fb_predefined[0].xres = useropts.xres; - if (useropts.yres != -1) - amiga_fb_predefined[0].yres = useropts.yres; - if (useropts.xres_virtual != -1) - amiga_fb_predefined[0].xres_virtual = useropts.xres_virtual; - if (useropts.yres_virtual != -1) - amiga_fb_predefined[0].yres_virtual = useropts.yres_virtual; - if (useropts.bits_per_pixel != -1) - amiga_fb_predefined[0].bits_per_pixel = useropts.bits_per_pixel; - if (useropts.left_margin != -1) - amiga_fb_predefined[0].left_margin = useropts.left_margin; - if (useropts.right_margin != -1) - amiga_fb_predefined[0].right_margin = useropts.right_margin; - if (useropts.upper_margin != -1) - amiga_fb_predefined[0].upper_margin = useropts.upper_margin; - if (useropts.lower_margin != -1) - amiga_fb_predefined[0].lower_margin = useropts.lower_margin; - if (useropts.hsync_len != -1) - amiga_fb_predefined[0].hsync_len = useropts.hsync_len; - if (useropts.vsync_len != -1) - amiga_fb_predefined[0].vsync_len = useropts.vsync_len; - + for (i = 0; i < NUM_TOTAL_MODES; i++) { + if (!strcmp(name, amifb_predefined[i].name)) { + amifb_default = amifb_predefined[i].var; amifb_usermode = i; return; } @@ -2073,23 +2047,22 @@ __initfunc(static void get_video_mode(const char *name)) } /* - * Probe the Video Modes + * Probe the Video Modes */ __initfunc(static void check_default_mode(void)) { - struct amiga_fb_par par; + struct amifb_par par; int mode; - for (mode = 0; mode < NUM_PREDEF_MODES; mode++) { - if (!ami_decode_var(&amiga_fb_predefined[mode], &par)) { - if (mode) - amiga_fb_predefined[0] = amiga_fb_predefined[mode]; + if (!ami_decode_var(&amifb_default, &par)) + return; + printk("Can't use default video mode. Probing video modes...\n"); + for (mode = 0; mode < NUM_TOTAL_MODES; mode++) + if (!ami_decode_var(&amifb_predefined[mode].var, &par)) { + amifb_default = amifb_predefined[mode].var; return; } - if (!mode) - printk("Can't use default video mode. Probing video modes...\n"); - } panic("Can't find any usable video mode"); } @@ -2141,13 +2114,19 @@ __initfunc(static char *strtoke(char *s,const char *ct)) */ static int ami_encode_fix(struct fb_fix_screeninfo *fix, - struct amiga_fb_par *par) + struct amifb_par *par) { memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, amiga_fb_name); + strcpy(fix->id, amifb_name); fix->smem_start = (char *)videomemory; fix->smem_len = videomemorysize; +#ifdef CONFIG_FBCON_MFB + if (par->bpp == 1) { + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + } else +#endif if (amifb_ilbm) { fix->type = FB_TYPE_INTERLEAVED_PLANES; fix->type_aux = par->next_line; @@ -2169,6 +2148,7 @@ static int ami_encode_fix(struct fb_fix_screeninfo *fix, fix->xpanstep = 16<<maxfmode; fix->ypanstep = 1; } + fix->accel = FB_ACCEL_AMIGABLITT; return 0; } @@ -2178,11 +2158,11 @@ static int ami_encode_fix(struct fb_fix_screeninfo *fix, */ static int ami_decode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par) + struct amifb_par *par) { u_short clk_shift, line_shift; u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n; - u_long hrate = 0, vrate = 0; + u_int htotal, vtotal; /* * Find a matching Pixel Clock @@ -2319,8 +2299,8 @@ static int ami_decode_var(struct fb_var_screeninfo *var, DPRINTK("diwstrt_v too low for pal\n"); return -EINVAL; } - hrate = 15625; - vrate = 50; + htotal = PAL_HTOTAL>>clk_shift; + vtotal = PAL_VTOTAL>>1; if (!IS_OCS) { par->beamcon0 = BMC0_PAL; par->bplcon3 |= BPC3_BRDRBLNK; @@ -2349,8 +2329,8 @@ static int ami_decode_var(struct fb_var_screeninfo *var, DPRINTK("diwstrt_v too low for ntsc\n"); return -EINVAL; } - hrate = 15750; - vrate = 60; + htotal = NTSC_HTOTAL>>clk_shift; + vtotal = NTSC_VTOTAL>>1; if (!IS_OCS) { par->beamcon0 = 0; par->bplcon3 |= BPC3_BRDRBLNK; @@ -2409,9 +2389,8 @@ static int ami_decode_var(struct fb_var_screeninfo *var, par->beamcon0 |= BMC0_VSYTRUE; if (var->sync & FB_SYNC_COMP_HIGH_ACT) par->beamcon0 |= BMC0_CSYTRUE; - hrate = (amiga_masterclock+par->htotal/2)/par->htotal; - vrate = div2(par->vtotal) * par->htotal; - vrate = (amiga_masterclock+vrate/2)/vrate; + htotal = par->htotal>>clk_shift; + vtotal = par->vtotal>>1; } else { DPRINTK("only broadcast modes possible for ocs\n"); return -EINVAL; @@ -2546,7 +2525,8 @@ static int ami_decode_var(struct fb_var_screeninfo *var, par->crsr.spot_x = par->crsr.spot_y = 0; par->crsr.height = par->crsr.width = 0; - if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax) { + if (!fbmon_valid_timings(pixclock[clk_shift], htotal, vtotal, + &fb_info)) { DPRINTK("mode doesn't fit for monitor\n"); return -EINVAL; } @@ -2560,7 +2540,7 @@ static int ami_decode_var(struct fb_var_screeninfo *var, */ static int ami_encode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par) + struct amifb_par *par) { u_short clk_shift, line_shift; int i; @@ -2606,7 +2586,6 @@ static int ami_encode_var(struct fb_var_screeninfo *var, var->height = -1; var->width = -1; - var->accel = 0; var->pixclock = pixclock[clk_shift]; @@ -2657,7 +2636,7 @@ static int ami_encode_var(struct fb_var_screeninfo *var, * Get current hardware setting */ -static void ami_get_par(struct amiga_fb_par *par) +static void ami_get_par(struct amifb_par *par) { *par = currentpar; } @@ -2676,7 +2655,7 @@ static void ami_set_var(struct fb_var_screeninfo *var) } #ifdef DEBUG -static void ami_set_par(struct amiga_fb_par *par) +static void ami_set_par(struct amifb_par *par) { do_vmode_pan = 0; do_vmode_full = 0; @@ -2695,7 +2674,7 @@ static void ami_set_par(struct amiga_fb_par *par) static void ami_pan_var(struct fb_var_screeninfo *var) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; par->xoffset = var->xoffset; par->yoffset = var->yoffset; @@ -2715,7 +2694,7 @@ static void ami_pan_var(struct fb_var_screeninfo *var) static int ami_update_par(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod; clk_shift = par->clk_shift; @@ -2780,7 +2759,7 @@ static int ami_update_par(void) */ static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) + u_int *transp, struct fb_info *info) { if (IS_AGA) { if (regno > 255) @@ -2804,7 +2783,7 @@ static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, */ static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) + u_int transp, struct fb_info *info) { #if defined(CONFIG_FB_AMIGA_AGA) u_short bplcon3 = currentpar.bplcon3; @@ -2867,7 +2846,7 @@ static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static void ami_update_display(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; custom.bplcon1 = par->bplcon1; custom.bpl1mod = par->bpl1mod; @@ -2882,7 +2861,7 @@ static void ami_update_display(void) static void ami_init_display(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; custom.bplcon0 = par->bplcon0 & ~BPC0_LACE; custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2; @@ -2938,7 +2917,7 @@ static void ami_init_display(void) static void ami_do_blank(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; #if defined(CONFIG_FB_AMIGA_AGA) u_short bplcon3 = par->bplcon3; #endif @@ -3023,7 +3002,7 @@ static void ami_do_blank(void) static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; fix->crsr_width = fix->crsr_xsize = par->crsr.width; fix->crsr_height = fix->crsr_ysize = par->crsr.height; @@ -3034,7 +3013,7 @@ static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; register u_short *lspr, *sspr; #ifdef __mc68000__ register u_long datawords asm ("d2"); @@ -3109,7 +3088,7 @@ static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, i static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; register u_short *lspr, *sspr; #ifdef __mc68000__ register u_long datawords asm ("d2"); @@ -3228,7 +3207,7 @@ static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, i static int ami_get_cursorstate(struct fb_cursorstate *state, int con) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; state->xoffset = par->crsr.crsr_x; state->yoffset = par->crsr.crsr_y; @@ -3238,7 +3217,7 @@ static int ami_get_cursorstate(struct fb_cursorstate *state, int con) static int ami_set_cursorstate(struct fb_cursorstate *state, int con) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; par->crsr.crsr_x = state->xoffset; par->crsr.crsr_y = state->yoffset; @@ -3250,7 +3229,7 @@ static int ami_set_cursorstate(struct fb_cursorstate *state, int con) static void ami_set_sprite(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; copins *copl, *cops; u_short hs, vs, ve; u_long pl, ps, pt; @@ -3333,7 +3312,7 @@ __initfunc(static void ami_init_copper(void)) static void ami_reinit_copper(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0; copdisplay.wait->l = CWAIT(32, par->diwstrt_v-4); @@ -3345,7 +3324,7 @@ static void ami_reinit_copper(void) static void ami_build_copper(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; copins *copl, *cops; u_long p; @@ -3422,7 +3401,7 @@ static void ami_build_copper(void) static void ami_rebuild_copper(void) { - struct amiga_fb_par *par = ¤tpar; + struct amifb_par *par = ¤tpar; copins *copl, *cops; u_short line, h_end1, h_end2; short i; @@ -3507,7 +3486,7 @@ static void ami_rebuild_copper(void) #ifdef MODULE int init_module(void) { - return(amiga_fb_init(NULL)); + return(amifb_init(NULL)); } void cleanup_module(void) diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index ada071f2b..ff7aedfe7 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -66,10 +66,31 @@ #include <asm/atarihw.h> #include <asm/atariints.h> +#include <asm/atari_stram.h> #include <linux/fb.h> #include <asm/atarikb.h> +#ifdef CONFIG_FBCON_CFB8 +#include "fbcon-cfb8.h" +#endif +#ifdef CONFIG_FBCON_CFB16 +#include "fbcon-cfb16.h" +#endif +#ifdef CONFIG_FBCON_IPLAN2P2 +#include "fbcon-iplan2p2.h" +#endif +#ifdef CONFIG_FBCON_IPLAN2P4 +#include "fbcon-iplan2p4.h" +#endif +#ifdef CONFIG_FBCON_IPLAN2P8 +#include "fbcon-iplan2p8.h" +#endif +#ifdef CONFIG_FBCON_MFB +#include "fbcon-mfb.h" +#endif + + #define SWITCH_ACIA 0x01 /* modes for switch on OverScan */ #define SWITCH_SND6 0x40 #define SWITCH_SND7 0x80 @@ -92,16 +113,18 @@ static int use_hwscroll = 1; static int sttt_xres=640,st_yres=400,tt_yres=480; static int sttt_xres_virtual=640,sttt_yres_virtual=400; static int ovsc_offset=0, ovsc_addlen=0; -int ovsc_switchmode=0; -static struct atari_fb_par { +static struct atafb_par { unsigned long screen_base; int yres_virtual; +#if defined ATAFB_TT || defined ATAFB_STE union { struct { int mode; int sync; } tt, st; +#endif +#ifdef ATAFB_FALCON struct falcon_hw { /* Here are fields for storing a video mode, as direct * parameters for the hardware. @@ -121,6 +144,7 @@ static struct atari_fb_par { short ste_mode; short bpp; } falcon; +#endif /* Nothing needed for external mode */ } hw; } current_par; @@ -130,6 +154,7 @@ static struct atari_fb_par { * hardware extensions (e.g. ScreenBlaster) */ static int DontCalcRes = 0; +#ifdef ATAFB_FALCON #define HHT hw.falcon.hht #define HBB hw.falcon.hbb #define HBE hw.falcon.hbe @@ -150,6 +175,7 @@ static int DontCalcRes = 0; #define VMO_DOUBLE 0x01 #define VMO_INTER 0x02 #define VMO_PREMASK 0x0c +#endif static struct fb_info fb_info; @@ -239,26 +265,22 @@ extern int fontheight_8x16; extern int fontwidth_8x16; extern unsigned char fontdata_8x16[]; -/* import first 16 colors from fbcon.c */ -extern unsigned short packed16_cmap[16]; - - /* ++roman: This structure abstracts from the underlying hardware (ST(e), * TT, or Falcon. * * int (*detect)( void ) * This function should detect the current video mode settings and - * store them in atari_fb_predefined[0] for later reference by the + * store them in atafb_predefined[0] for later reference by the * user. Return the index+1 of an equivalent predefined mode or 0 * if there is no such. * * int (*encode_fix)( struct fb_fix_screeninfo *fix, - * struct atari_fb_par *par ) + * struct atafb_par *par ) * This function should fill in the 'fix' structure based on the * values in the 'par' structure. * * int (*decode_var)( struct fb_var_screeninfo *var, - * struct atari_fb_par *par ) + * struct atafb_par *par ) * Get the video params out of 'var'. If a value doesn't fit, round * it up, if it's too big, return EINVAL. * Round up in the following order: bits_per_pixel, xres, yres, @@ -266,26 +288,26 @@ extern unsigned short packed16_cmap[16]; * horizontal timing, vertical timing. * * int (*encode_var)( struct fb_var_screeninfo *var, - * struct atari_fb_par *par ); + * struct atafb_par *par ); * Fill the 'var' structure based on the values in 'par' and maybe * other values read out of the hardware. * - * void (*get_par)( struct atari_fb_par *par ) + * void (*get_par)( struct atafb_par *par ) * Fill the hardware's 'par' structure. * - * void (*set_par)( struct atari_fb_par *par ) + * void (*set_par)( struct atafb_par *par ) * Set the hardware according to 'par'. * * int (*setcolreg)( unsigned regno, unsigned red, * unsigned green, unsigned blue, - * unsigned transp ) + * unsigned transp, struct fb_info *info ) * Set a single color register. The values supplied are already * rounded down to the hardware's capabilities (according to the * entries in the var structure). Return != 0 for invalid regno. * * int (*getcolreg)( unsigned regno, unsigned *red, * unsigned *green, unsigned *blue, - * unsigned *transp ) + * unsigned *transp, struct fb_info *info ) * Read a single color register and split it into * colors/transparent. Return != 0 for invalid regno. * @@ -305,23 +327,23 @@ extern unsigned short packed16_cmap[16]; static struct fb_hwswitch { int (*detect)( void ); int (*encode_fix)( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ); + struct atafb_par *par ); int (*decode_var)( struct fb_var_screeninfo *var, - struct atari_fb_par *par ); + struct atafb_par *par ); int (*encode_var)( struct fb_var_screeninfo *var, - struct atari_fb_par *par ); - void (*get_par)( struct atari_fb_par *par ); - void (*set_par)( struct atari_fb_par *par ); + struct atafb_par *par ); + void (*get_par)( struct atafb_par *par ); + void (*set_par)( struct atafb_par *par ); int (*getcolreg)( unsigned regno, unsigned *red, unsigned *green, unsigned *blue, - unsigned *transp ); + unsigned *transp, struct fb_info *info ); int (*setcolreg)( unsigned regno, unsigned red, unsigned green, unsigned blue, - unsigned transp ); + unsigned transp, struct fb_info *info ); void (*set_screen_base)( unsigned long s_base ); int (*blank)( int blank_mode ); int (*pan_display)( struct fb_var_screeninfo *var, - struct atari_fb_par *par); + struct atafb_par *par); } *fbhw; static char *autodetect_names[] = {"autodetect", NULL}; @@ -337,15 +359,6 @@ static char *vga16_names[] = {"vga16", "default3", NULL}; static char *vga256_names[] = {"vga256", NULL}; static char *falh2_names[] = {"falh2", NULL}; static char *falh16_names[] = {"falh16", NULL}; -static char *user0_names[] = {"user0", NULL}; -static char *user1_names[] = {"user1", NULL}; -static char *user2_names[] = {"user2", NULL}; -static char *user3_names[] = {"user3", NULL}; -static char *user4_names[] = {"user4", NULL}; -static char *user5_names[] = {"user5", NULL}; -static char *user6_names[] = {"user6", NULL}; -static char *user7_names[] = {"user7", NULL}; -static char *dummy_names[] = {"dummy", NULL}; static char **fb_var_names[] = { /* Writing the name arrays directly in this array (via "(char *[]){...}") @@ -365,22 +378,11 @@ static char **fb_var_names[] = { vga256_names, falh2_names, falh16_names, - dummy_names, dummy_names, dummy_names, dummy_names, - dummy_names, dummy_names, dummy_names, dummy_names, - dummy_names, dummy_names, - user0_names, - user1_names, - user2_names, - user3_names, - user4_names, - user5_names, - user6_names, - user7_names, NULL /* ,NULL */ /* this causes a sigsegv on my gcc-2.5.8 */ }; -static struct fb_var_screeninfo atari_fb_predefined[] = { +static struct fb_var_screeninfo atafb_predefined[] = { /* * yres_virtual==0 means use hw-scrolling if possible, else yres */ @@ -436,53 +438,9 @@ static struct fb_var_screeninfo atari_fb_predefined[] = { 896, 608, 896, 0, 0, 0, 4, 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* Minor 14..23 free for more standard video modes */ - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - /* Minor 24..31 reserved for user defined video modes */ - { /* user0, initialized to Rx;y;d from commandline, if supplied */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user1 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user2 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user3 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user4 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user5 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user6 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user7 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 } }; -static int num_atari_fb_predefined=arraysize(atari_fb_predefined); +static int num_atafb_predefined=arraysize(atafb_predefined); static int @@ -492,7 +450,7 @@ get_video_mode(char *vname) char **name; int i; name_list=fb_var_names; - for (i = 0 ; i < num_atari_fb_predefined ; i++) { + for (i = 0 ; i < num_atafb_predefined ; i++) { name=*(name_list++); if (! name || ! *name) break; @@ -512,7 +470,7 @@ get_video_mode(char *vname) #ifdef ATAFB_TT static int tt_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) + struct atafb_par *par ) { int mode; @@ -539,7 +497,7 @@ static int tt_encode_fix( struct fb_fix_screeninfo *fix, static int tt_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int xres=var->xres; int yres=var->yres; @@ -620,7 +578,7 @@ static int tt_decode_var( struct fb_var_screeninfo *var, } static int tt_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int linelen, i; var->red.offset=0; @@ -716,7 +674,7 @@ static int tt_encode_var( struct fb_var_screeninfo *var, } -static void tt_get_par( struct atari_fb_par *par ) +static void tt_get_par( struct atafb_par *par ) { unsigned long addr; par->hw.tt.mode=shifter_tt.tt_shiftmode; @@ -727,7 +685,7 @@ static void tt_get_par( struct atari_fb_par *par ) par->screen_base = PTOV(addr); } -static void tt_set_par( struct atari_fb_par *par ) +static void tt_set_par( struct atafb_par *par ) { shifter_tt.tt_shiftmode=par->hw.tt.mode; shifter.syncmode=par->hw.tt.sync; @@ -739,7 +697,7 @@ static void tt_set_par( struct atari_fb_par *par ) static int tt_getcolreg( unsigned regno, unsigned *red, unsigned *green, unsigned *blue, - unsigned *transp ) + unsigned *transp, struct fb_info *info ) { if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH) regno += 254; @@ -756,7 +714,7 @@ static int tt_getcolreg( unsigned regno, unsigned *red, static int tt_setcolreg( unsigned regno, unsigned red, unsigned green, unsigned blue, - unsigned transp ) + unsigned transp, struct fb_info *info ) { if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH) regno += 254; @@ -772,7 +730,7 @@ static int tt_setcolreg( unsigned regno, unsigned red, static int tt_detect( void ) -{ struct atari_fb_par par; +{ struct atafb_par par; /* Determine the connected monitor: The DMA sound must be * disabled before reading the MFP GPIP, because the Sound @@ -789,7 +747,7 @@ static int tt_detect( void ) mono_moni = (mfp.par_dt_reg & 0x80) == 0; tt_get_par(&par); - tt_encode_var(&atari_fb_predefined[0], &par); + tt_encode_var(&atafb_predefined[0], &par); return 1; } @@ -807,10 +765,6 @@ static int f030_bus_width; /* Falcon ram bus width (for vid_control) */ #define F_MON_VGA 2 #define F_MON_TV 3 -/* Multisync monitor capabilities */ -/* Atari-TOS defaults if no boot option present */ -static long vfmin=58, vfmax=62, hfmin=31000, hfmax=32000; - static struct pixel_clock { unsigned long f; /* f/[Hz] */ unsigned long t; /* t/[ps] (=1/f) */ @@ -837,7 +791,7 @@ static inline int hxx_prescale(struct falcon_hw *hw) } static int falcon_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) + struct atafb_par *par ) { strcpy(fix->id, "Atari Builtin"); fix->smem_start = (char *)real_screen_base; @@ -867,7 +821,7 @@ static int falcon_encode_fix( struct fb_fix_screeninfo *fix, static int falcon_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int bpp = var->bits_per_pixel; int xres = var->xres; @@ -943,7 +897,7 @@ static int falcon_decode_var( struct fb_var_screeninfo *var, if (mon_type == F_MON_SM || DontCalcRes) { /* Skip all calculations. VGA/TV/SC1224 only supported. */ - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; + struct fb_var_screeninfo *myvar = &atafb_predefined[0]; if (bpp > myvar->bits_per_pixel || var->xres > myvar->xres || @@ -1069,11 +1023,14 @@ static int falcon_decode_var( struct fb_var_screeninfo *var, /* Choose master pixelclock depending on hor. timing */ plen = 1 * xstretch; - if ((plen * xres + f25.right+f25.hsync+f25.left) * hfmin < f25.f) + if ((plen * xres + f25.right+f25.hsync+f25.left) * + fb_info.monspecs.hfmin < f25.f) pclock = &f25; - else if ((plen * xres + f32.right+f32.hsync+f32.left) * hfmin < f32.f) + else if ((plen * xres + f32.right+f32.hsync+f32.left) * + fb_info.monspecs.hfmin < f32.f) pclock = &f32; - else if ((plen * xres + fext.right+fext.hsync+fext.left) * hfmin < fext.f + else if ((plen * xres + fext.right+fext.hsync+fext.left) * + fb_info.monspecs.hfmin < fext.f && fext.f) pclock = &fext; else @@ -1246,14 +1203,14 @@ static int falcon_decode_var( struct fb_var_screeninfo *var, /* check hor. frequency */ hfreq = pclock->f / ((par->HHT+2)*prescale*2); - if (hfreq > hfmax && mon_type!=F_MON_VGA) { + if (hfreq > fb_info.monspecs.hfmax && mon_type!=F_MON_VGA) { /* ++guenther: ^^^^^^^^^^^^^^^^^^^ can't remember why I did this */ /* Too high -> enlarge margin */ left_margin += 1; right_margin += 1; goto again; } - if (hfreq > hfmax || hfreq < hfmin) + if (hfreq > fb_info.monspecs.hfmax || hfreq < fb_info.monspecs.hfmin) return -EINVAL; /* Vxx-registers */ @@ -1282,45 +1239,52 @@ static int falcon_decode_var( struct fb_var_screeninfo *var, /* V-frequency check, hope I didn't create any loop here. */ /* Interlace and doubleline are mutually exclusive. */ vfreq = (hfreq * 2) / (par->VFT + 1); - if (vfreq > vfmax && !doubleline && !interlace) { + if (vfreq > fb_info.monspecs.vfmax && !doubleline && !interlace) { /* Too high -> try again with doubleline */ doubleline = 1; goto again; } - else if (vfreq < vfmin && !interlace && !doubleline) { + else if (vfreq < fb_info.monspecs.vfmin && !interlace && !doubleline) { /* Too low -> try again with interlace */ interlace = 1; goto again; } - else if (vfreq < vfmin && doubleline) { + else if (vfreq < fb_info.monspecs.vfmin && doubleline) { /* Doubleline too low -> clear doubleline and enlarge margins */ int lines; doubleline = 0; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines-2*yres)>vfmax; lines++) + for (lines=0; + (hfreq*2)/(par->VFT+1+4*lines-2*yres)>fb_info.monspecs.vfmax; + lines++) ; upper_margin += lines; lower_margin += lines; goto again; } - else if (vfreq > vfmax && doubleline) { + else if (vfreq > fb_info.monspecs.vfmax && doubleline) { /* Doubleline too high -> enlarge margins */ int lines; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines+=2) + for (lines=0; + (hfreq*2)/(par->VFT+1+4*lines)>fb_info.monspecs.vfmax; + lines+=2) ; upper_margin += lines; lower_margin += lines; goto again; } - else if (vfreq > vfmax && interlace) { + else if (vfreq > fb_info.monspecs.vfmax && interlace) { /* Interlace, too high -> enlarge margins */ int lines; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines++) + for (lines=0; + (hfreq*2)/(par->VFT+1+4*lines)>fb_info.monspecs.vfmax; + lines++) ; upper_margin += lines; lower_margin += lines; goto again; } - else if (vfreq < vfmin || vfreq > vfmax) + else if (vfreq < fb_info.monspecs.vfmin || + vfreq > fb_info.monspecs.vfmax) return -EINVAL; set_screen_base: @@ -1339,7 +1303,7 @@ static int falcon_decode_var( struct fb_var_screeninfo *var, } static int falcon_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { /* !!! only for VGA !!! */ int linelen, i; @@ -1425,12 +1389,13 @@ static int falcon_encode_var( struct fb_var_screeninfo *var, var->transp.msb_right=0; linelen = var->xres_virtual * var->bits_per_pixel / 8; - if (screen_len) + if (screen_len) { if (par->yres_virtual) var->yres_virtual = par->yres_virtual; else /* yres_virtual==0 means use maximum */ var->yres_virtual = screen_len / linelen; + } else { if (hwscroll < 0) var->yres_virtual = 2 * var->yres; @@ -1506,7 +1471,7 @@ static int f_change_mode = 0; static struct falcon_hw f_new_mode; static int f_pan_display = 0; -static void falcon_get_par( struct atari_fb_par *par ) +static void falcon_get_par( struct atafb_par *par ) { unsigned long addr; struct falcon_hw *hw = &par->hw.falcon; @@ -1543,7 +1508,7 @@ static void falcon_get_par( struct atari_fb_par *par ) ((hw->f_shift & 0x510)==0 && hw->st_shift==0x200); } -static void falcon_set_par( struct atari_fb_par *par ) +static void falcon_set_par( struct atafb_par *par ) { f_change_mode = 0; @@ -1627,7 +1592,7 @@ static void falcon_vbl_switcher( int irq, void *dummy, struct pt_regs *fp ) static int falcon_pan_display( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int xoffset; int bpp = fb_display[currcon].var.bits_per_pixel; @@ -1659,7 +1624,7 @@ static int falcon_pan_display( struct fb_var_screeninfo *var, static int falcon_getcolreg( unsigned regno, unsigned *red, unsigned *green, unsigned *blue, - unsigned *transp ) + unsigned *transp, struct fb_info *info ) { unsigned long col; if (regno > 255) @@ -1679,7 +1644,7 @@ static int falcon_getcolreg( unsigned regno, unsigned *red, static int falcon_setcolreg( unsigned regno, unsigned red, unsigned green, unsigned blue, - unsigned transp ) + unsigned transp, struct fb_info *info ) { if (regno > 255) return 1; @@ -1690,7 +1655,7 @@ static int falcon_setcolreg( unsigned regno, unsigned red, (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) | ((blue & 0xe) >> 1) | ((blue & 1) << 3); #ifdef CONFIG_FBCON_CFB16 - packed16_cmap[regno] = (red << 11) | (green << 5) | blue; + fbcon_cfb16_cmap[regno] = (red << 11) | (green << 5) | blue; #endif } return 0; @@ -1738,7 +1703,7 @@ static int falcon_blank( int blank_mode ) static int falcon_detect( void ) { - struct atari_fb_par par; + struct atafb_par par; unsigned char fhw; /* Determine connected monitor and set monitor parameters */ @@ -1748,18 +1713,18 @@ static int falcon_detect( void ) f030_bus_width = fhw << 6 & 0x80; switch (mon_type) { case F_MON_SM: - vfmin = 70; - vfmax = 72; - hfmin = 35713; - hfmax = 35715; + fb_info.monspecs.vfmin = 70; + fb_info.monspecs.vfmax = 72; + fb_info.monspecs.hfmin = 35713; + fb_info.monspecs.hfmax = 35715; break; case F_MON_SC: case F_MON_TV: /* PAL...NTSC */ - vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */ - vfmax = 60; - hfmin = 15620; - hfmax = 15755; + fb_info.monspecs.vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */ + fb_info.monspecs.vfmax = 60; + fb_info.monspecs.hfmin = 15620; + fb_info.monspecs.hfmax = 15755; break; } /* initialize hsync-len */ @@ -1769,7 +1734,7 @@ static int falcon_detect( void ) fext.hsync = h_syncs[mon_type] / fext.t; falcon_get_par(&par); - falcon_encode_var(&atari_fb_predefined[0], &par); + falcon_encode_var(&atafb_predefined[0], &par); /* Detected mode is always the "autodetect" slot */ return 1; @@ -1782,7 +1747,7 @@ static int falcon_detect( void ) #ifdef ATAFB_STE static int stste_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) + struct atafb_par *par ) { int mode; @@ -1813,7 +1778,7 @@ static int stste_encode_fix( struct fb_fix_screeninfo *fix, static int stste_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int xres=var->xres; int yres=var->yres; @@ -1871,7 +1836,7 @@ static int stste_decode_var( struct fb_var_screeninfo *var, } static int stste_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int linelen, i; var->red.offset=0; @@ -1922,12 +1887,13 @@ static int stste_encode_var( struct fb_var_screeninfo *var, if (! use_hwscroll) var->yres_virtual=var->yres; - else if (screen_len) + else if (screen_len) { if (par->yres_virtual) var->yres_virtual = par->yres_virtual; else /* yres_virtual==0 means use maximum */ var->yres_virtual = screen_len / linelen; + } else { if (hwscroll < 0) var->yres_virtual = 2 * var->yres; @@ -1948,7 +1914,7 @@ static int stste_encode_var( struct fb_var_screeninfo *var, } -static void stste_get_par( struct atari_fb_par *par ) +static void stste_get_par( struct atafb_par *par ) { unsigned long addr; par->hw.st.mode=shifter_tt.st_shiftmode; @@ -1960,7 +1926,7 @@ static void stste_get_par( struct atari_fb_par *par ) par->screen_base = PTOV(addr); } -static void stste_set_par( struct atari_fb_par *par ) +static void stste_set_par( struct atafb_par *par ) { shifter_tt.st_shiftmode=par->hw.st.mode; shifter.syncmode=par->hw.st.sync; @@ -1972,7 +1938,7 @@ static void stste_set_par( struct atari_fb_par *par ) static int stste_getcolreg( unsigned regno, unsigned *red, unsigned *green, unsigned *blue, - unsigned *transp ) + unsigned *transp, struct fb_info *info ) { unsigned col; if (regno > 15) @@ -1995,7 +1961,7 @@ static int stste_getcolreg( unsigned regno, unsigned *red, static int stste_setcolreg( unsigned regno, unsigned red, unsigned green, unsigned blue, - unsigned transp ) + unsigned transp, struct fb_info *info ) { if (regno > 15) return 1; @@ -2015,7 +1981,7 @@ static int stste_setcolreg( unsigned regno, unsigned red, static int stste_detect( void ) -{ struct atari_fb_par par; +{ struct atafb_par par; /* Determine the connected monitor: The DMA sound must be * disabled before reading the MFP GPIP, because the Sound @@ -2028,7 +1994,7 @@ static int stste_detect( void ) mono_moni = (mfp.par_dt_reg & 0x80) == 0; stste_get_par(&par); - stste_encode_var(&atari_fb_predefined[0], &par); + stste_encode_var(&atafb_predefined[0], &par); if (!ATARIHW_PRESENT(EXTD_SHIFTER)) use_hwscroll = 0; @@ -2067,12 +2033,12 @@ static void stste_set_screen_base(unsigned long s_base) #define SYNC_DELAY (mono_moni ? 1500 : 2000) /* SWITCH_ACIA may be used for Falcon (ScreenBlaster III internal!) */ -static void st_ovsc_switch(int switchmode) +static void st_ovsc_switch(void) { unsigned long flags; register unsigned char old, new; - if ((switchmode & (SWITCH_ACIA | SWITCH_SND6 | SWITCH_SND7)) == 0) + if (!(atari_switches & ATARI_SWITCH_OVSC_MASK)) return; save_flags(flags); cli(); @@ -2093,11 +2059,15 @@ static void st_ovsc_switch(int switchmode) mfp.tim_ct_b = 0x10; udelay(SYNC_DELAY); - if (switchmode == SWITCH_ACIA) - acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE); - else { + if (atari_switches & ATARI_SWITCH_OVSC_IKBD) + acia.key_ctrl = ACIA_DIV64 | ACIA_D8N1S | ACIA_RHTID | ACIA_RIE; + if (atari_switches & ATARI_SWITCH_OVSC_MIDI) + acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RHTID; + if (atari_switches & (ATARI_SWITCH_OVSC_SND6|ATARI_SWITCH_OVSC_SND7)) { sound_ym.rd_data_reg_sel = 14; - sound_ym.wd_data = sound_ym.rd_data_reg_sel | switchmode; + sound_ym.wd_data = sound_ym.rd_data_reg_sel | + ((atari_switches&ATARI_SWITCH_OVSC_SND6) ? 0x40:0) | + ((atari_switches&ATARI_SWITCH_OVSC_SND7) ? 0x80:0); } restore_flags(flags); } @@ -2107,7 +2077,7 @@ static void st_ovsc_switch(int switchmode) #ifdef ATAFB_EXT static int ext_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) + struct atafb_par *par ) { strcpy(fix->id,"Unknown Extern"); @@ -2157,9 +2127,9 @@ static int ext_encode_fix( struct fb_fix_screeninfo *fix, static int ext_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; + struct fb_var_screeninfo *myvar = &atafb_predefined[0]; if (var->bits_per_pixel > myvar->bits_per_pixel || var->xres > myvar->xres || @@ -2173,7 +2143,7 @@ static int ext_decode_var( struct fb_var_screeninfo *var, static int ext_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { int i; @@ -2217,12 +2187,12 @@ static int ext_encode_var( struct fb_var_screeninfo *var, } -static void ext_get_par( struct atari_fb_par *par ) +static void ext_get_par( struct atafb_par *par ) { par->screen_base = external_addr; } -static void ext_set_par( struct atari_fb_par *par ) +static void ext_set_par( struct atafb_par *par ) { } @@ -2238,10 +2208,8 @@ static void ext_set_par( struct atari_fb_par *par ) static int ext_getcolreg( unsigned regno, unsigned *red, unsigned *green, unsigned *blue, - unsigned *transp ) - -{ unsigned char colmask = (1 << external_bitspercol) - 1; - + unsigned *transp, struct fb_info *info ) +{ if (! external_vgaiobase) return 1; @@ -2254,7 +2222,7 @@ static int ext_getcolreg( unsigned regno, unsigned *red, static int ext_setcolreg( unsigned regno, unsigned red, unsigned green, unsigned blue, - unsigned transp ) + unsigned transp, struct fb_info *info ) { unsigned char colmask = (1 << external_bitspercol) - 1; @@ -2292,8 +2260,8 @@ static int ext_setcolreg( unsigned regno, unsigned red, static int ext_detect( void ) { - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; - struct atari_fb_par dummy_par; + struct fb_var_screeninfo *myvar = &atafb_predefined[0]; + struct atafb_par dummy_par; myvar->xres = external_xres; myvar->xres_virtual = external_xres_virtual; @@ -2319,7 +2287,7 @@ static void set_screen_base(unsigned long s_base) static int pan_display( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) + struct atafb_par *par ) { if (!fbhw->set_screen_base || (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset)) @@ -2369,7 +2337,7 @@ static struct fb_hwswitch ext_switch = { -static void atari_fb_get_par( struct atari_fb_par *par ) +static void atafb_get_par( struct atafb_par *par ) { if (current_par_valid) { *par=current_par; @@ -2379,7 +2347,7 @@ static void atari_fb_get_par( struct atari_fb_par *par ) } -static void atari_fb_set_par( struct atari_fb_par *par ) +static void atafb_set_par( struct atafb_par *par ) { fbhw->set_par(par); current_par=*par; @@ -2396,7 +2364,7 @@ static void atari_fb_set_par( struct atari_fb_par *par ) /* used for hardware scrolling */ static int -fb_update_var(int con) +fb_update_var(int con, struct fb_info *info) { int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual* fb_display[con].var.bits_per_pixel>>3; @@ -2412,12 +2380,12 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) { int err,activate; - struct atari_fb_par par; + struct atafb_par par; if ((err=fbhw->decode_var(var, &par))) return err; activate=var->activate; if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) - atari_fb_set_par(&par); + atafb_set_par(&par); fbhw->encode_var(var, &par); var->activate=activate; return 0; @@ -2426,17 +2394,17 @@ do_fb_set_var(struct fb_var_screeninfo *var, int isactive) /* Functions for handling colormap */ static void -do_install_cmap(int con) +do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &(fb_display[con].var), 1, - fbhw->setcolreg); + fbhw->setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), &(fb_display[con].var), 1, - fbhw->setcolreg); + fbhw->setcolreg, info); } @@ -2444,7 +2412,7 @@ do_install_cmap(int con) * Open/Release the frame buffer device */ -static int atari_fb_open(int fbidx) +static int atafb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -2454,7 +2422,7 @@ static int atari_fb_open(int fbidx) return(0); } -static int atari_fb_release(int fbidx) +static int atafb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); @@ -2462,11 +2430,11 @@ static int atari_fb_release(int fbidx) static int -atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con) +atafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) { - struct atari_fb_par par; + struct atafb_par par; if (con == -1) - atari_fb_get_par(&par); + atafb_get_par(&par); else fbhw->decode_var(&fb_display[con].var,&par); memset(fix, 0, sizeof(struct fb_fix_screeninfo)); @@ -2474,11 +2442,11 @@ atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con) } static int -atari_fb_get_var(struct fb_var_screeninfo *var, int con) +atafb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) { - struct atari_fb_par par; + struct atafb_par par; if (con == -1) { - atari_fb_get_par(&par); + atafb_get_par(&par); fbhw->encode_var(var, &par); } else @@ -2487,9 +2455,10 @@ atari_fb_get_var(struct fb_var_screeninfo *var, int con) } static void -atari_fb_set_disp(int con) +atafb_set_disp(int con, struct fb_info *info) { struct fb_fix_screeninfo fix; + struct fb_var_screeninfo var; struct display *display; if (con >= 0) @@ -2497,7 +2466,8 @@ atari_fb_set_disp(int con) else display = &disp; /* used during initialization */ - atari_fb_get_fix(&fix, con); + atafb_get_fix(&fix, con, info); + atafb_get_var(&var, con, info); if (con == -1) con=0; display->screen_base = (u_char *)fix.smem_start; @@ -2514,10 +2484,50 @@ atari_fb_set_disp(int con) display->can_soft_blank = 1; display->inverse = (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse); + switch (fix.type) { + case FB_TYPE_INTERLEAVED_PLANES: + switch (var.bits_per_pixel) { +#ifdef CONFIG_FBCON_IPLAN2P2 + case 2: + display->dispsw = &fbcon_iplan2p2; + break; +#endif +#ifdef CONFIG_FBCON_IPLAN2P4 + case 4: + display->dispsw = &fbcon_iplan2p4; + break; +#endif +#ifdef CONFIG_FBCON_IPLAN2P8 + case 8: + display->dispsw = &fbcon_iplan2p8; + break; +#endif + } + break; + case FB_TYPE_PACKED_PIXELS: + switch (var.bits_per_pixel) { +#ifdef CONFIG_FBCON_MFB + case 1: + display->dispsw = &fbcon_mfb; + break; +#endif +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_cfb8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif + } + break; + } } static int -atari_fb_set_var(struct fb_var_screeninfo *var, int con) +atafb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) { int err,oldxres,oldyres,oldbpp,oldxres_virtual, oldyres_virtual,oldyoffset; @@ -2536,10 +2546,10 @@ atari_fb_set_var(struct fb_var_screeninfo *var, int con) || oldyres_virtual != var->yres_virtual || oldbpp != var->bits_per_pixel || oldyoffset != var->yoffset) { - atari_fb_set_disp(con); + atafb_set_disp(con, info); (*fb_info.changevar)(con); fb_alloc_cmap(&fb_display[con].cmap, 0, 0); - do_install_cmap(con); + do_install_cmap(con, info); } } var->activate=0; @@ -2549,22 +2559,22 @@ atari_fb_set_var(struct fb_var_screeninfo *var, int con) static int -atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +atafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { if (con == currcon) /* current console ? */ return fb_get_cmap(cmap, &(fb_display[con].var), kspc, - fbhw->getcolreg); + fbhw->getcolreg, info); else if (fb_display[con].cmap.len) /* non default colormap ? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } static int -atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +atafb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { int err; if (! fb_display[con].cmap.len) { /* no colormap allocated ? */ @@ -2575,14 +2585,14 @@ atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) } if (con == currcon) /* current console ? */ return fb_set_cmap(cmap, &(fb_display[con].var), kspc, - fbhw->setcolreg); + fbhw->setcolreg, info); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return 0; } static int -atari_fb_pan_display(struct fb_var_screeninfo *var, int con) +atafb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info) { int xoffset = var->xoffset; int yoffset = var->yoffset; @@ -2606,33 +2616,33 @@ atari_fb_pan_display(struct fb_var_screeninfo *var, int con) } static int -atari_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg, int con) +atafb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info) { switch (cmd) { #ifdef FBCMD_GET_CURRENTPAR case FBCMD_GET_CURRENTPAR: if (copy_to_user((void *)arg, (void *)¤t_par, - sizeof(struct atari_fb_par))) + sizeof(struct atafb_par))) return -EFAULT; return 0; #endif #ifdef FBCMD_SET_CURRENTPAR case FBCMD_SET_CURRENTPAR: if (copy_from_user((void *)¤t_par, (void *)arg, - sizeof(struct atari_fb_par))) + sizeof(struct atafb_par))) return -EFAULT; - atari_fb_set_par(¤t_par); + atafb_set_par(¤t_par); return 0; #endif } return -EINVAL; } -static struct fb_ops atari_fb_ops = { - atari_fb_open, atari_fb_release, atari_fb_get_fix, atari_fb_get_var, - atari_fb_set_var, atari_fb_get_cmap, atari_fb_set_cmap, - atari_fb_pan_display, atari_fb_ioctl +static struct fb_ops atafb_ops = { + atafb_open, atafb_release, atafb_get_fix, atafb_get_var, + atafb_set_var, atafb_get_cmap, atafb_set_cmap, + atafb_pan_display, NULL, atafb_ioctl }; static void @@ -2645,14 +2655,14 @@ check_default_par( int detected_mode ) /* First try the user supplied mode */ if (default_par) { - var=atari_fb_predefined[default_par-1]; + var=atafb_predefined[default_par-1]; var.activate = FB_ACTIVATE_TEST; if (do_fb_set_var(&var,1)) default_par=0; /* failed */ } /* Next is the autodetected one */ if (! default_par) { - var=atari_fb_predefined[detected_mode-1]; /* autodetect */ + var=atafb_predefined[detected_mode-1]; /* autodetect */ var.activate = FB_ACTIVATE_TEST; if (!do_fb_set_var(&var,1)) default_par=detected_mode; @@ -2665,7 +2675,7 @@ check_default_par( int detected_mode ) default_par=get_video_mode(default_name); if (! default_par) panic("can't set default video mode\n"); - var=atari_fb_predefined[default_par-1]; + var=atafb_predefined[default_par-1]; var.activate = FB_ACTIVATE_TEST; if (! do_fb_set_var(&var,1)) break; /* ok */ @@ -2677,16 +2687,17 @@ check_default_par( int detected_mode ) } static int -atafb_switch(int con) +atafb_switch(int con, struct fb_info *info) { /* Do we have to save the colormap ? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, - &(fb_display[currcon].var), 1, fbhw->getcolreg); + &(fb_display[currcon].var), 1, fbhw->getcolreg, + info); do_fb_set_var(&fb_display[con].var,1); currcon=con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -2698,7 +2709,7 @@ atafb_switch(int con) * 4 = off */ static void -atafb_blank(int blank) +atafb_blank(int blank, struct fb_info *info) { unsigned short black[16]; struct fb_cmap cmap; @@ -2713,19 +2724,13 @@ atafb_blank(int blank) cmap.start=0; cmap.len=16; fb_set_cmap(&cmap, &(fb_display[currcon].var), 1, - fbhw->setcolreg); + fbhw->setcolreg, info); } else - do_install_cmap(currcon); -} - -static int -atafb_setcmap(struct fb_cmap *cmap, int con) -{ - return(atari_fb_set_cmap(cmap, 1, con)); + do_install_cmap(currcon, info); } -__initfunc(unsigned long atari_fb_init(unsigned long mem_start)) +__initfunc(unsigned long atafb_init(unsigned long mem_start)) { int err; int pad; @@ -2770,6 +2775,16 @@ __initfunc(unsigned long atari_fb_init(unsigned long mem_start)) panic("Cannot initialize video hardware\n"); #endif } while (0); + + /* Multisync monitor capabilities */ + /* Atari-TOS defaults if no boot option present */ + if (fb_info.monspecs.hfmin == 0) { + fb_info.monspecs.hfmin = 31000; + fb_info.monspecs.hfmax = 32000; + fb_info.monspecs.vfmin = 58; + fb_info.monspecs.vfmax = 62; + } + detected_mode = fbhw->detect(); check_default_par(detected_mode); #ifdef ATAFB_EXT @@ -2778,13 +2793,14 @@ __initfunc(unsigned long atari_fb_init(unsigned long mem_start)) mem_req = default_mem_req + ovsc_offset + ovsc_addlen; mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE; - screen_base = (unsigned long) atari_stram_alloc(mem_req, &mem_start); + screen_base = (unsigned long)atari_stram_alloc(mem_req, &mem_start, + "atafb"); memset((char *) screen_base, 0, mem_req); pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base; screen_base+=pad; real_screen_base=screen_base+ovsc_offset; screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK; - st_ovsc_switch(ovsc_switchmode); + st_ovsc_switch(); if (CPU_IS_040_OR_060) { /* On a '040+, the cache mode of video RAM must be set to * write-through also for internal video hardware! */ @@ -2815,32 +2831,29 @@ __initfunc(unsigned long atari_fb_init(unsigned long mem_start)) strcpy(fb_info.modename, "Atari Builtin "); fb_info.changevar = NULL; fb_info.node = -1; - fb_info.fbops = &atari_fb_ops; - fb_info.fbvar_num = num_atari_fb_predefined; - fb_info.fbvar = atari_fb_predefined; + fb_info.fbops = &atafb_ops; fb_info.disp = &disp; fb_info.switch_con = &atafb_switch; fb_info.updatevar = &fb_update_var; fb_info.blank = &atafb_blank; - fb_info.setcmap = &atafb_setcmap; - do_fb_set_var(&atari_fb_predefined[default_par-1], 1); + do_fb_set_var(&atafb_predefined[default_par-1], 1); strcat(fb_info.modename, fb_var_names[default_par-1][0]); err=register_framebuffer(&fb_info); if (err < 0) return(err); - atari_fb_get_var(&disp.var, -1); - atari_fb_set_disp(-1); + atafb_get_var(&disp.var, -1, &fb_info); + atafb_set_disp(-1, &fb_info); printk("Determined %dx%d, depth %d\n", disp.var.xres, disp.var.yres, disp.var.bits_per_pixel); if ((disp.var.xres != disp.var.xres_virtual) || (disp.var.yres != disp.var.yres_virtual)) printk(" virtual %dx%d\n", disp.var.xres_virtual, disp.var.yres_virtual); - do_install_cmap(0); - printk("%s frame buffer device, using %dK of video memory\n", - fb_info.modename, screen_len>>10); + do_install_cmap(0, &fb_info); + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, screen_len>>10); /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; @@ -2870,7 +2883,7 @@ static char * strtoke(char * s,const char * ct) return sbegin; } -__initfunc(void atari_video_setup( char *options, int *ints )) +__initfunc(void atafb_setup( char *options, int *ints )) { char *this_opt; int temp; @@ -2902,15 +2915,6 @@ __initfunc(void atari_video_setup( char *options, int *ints )) if (hwscroll > 200) hwscroll = 200; } - else if (! strncmp(this_opt, "sw_",3)) { - if (! strcmp(this_opt+3, "acia")) - ovsc_switchmode = SWITCH_ACIA; - else if (! strcmp(this_opt+3, "snd6")) - ovsc_switchmode = SWITCH_SND6; - else if (! strcmp(this_opt+3, "snd7")) - ovsc_switchmode = SWITCH_SND7; - else ovsc_switchmode = SWITCH_NONE; - } #ifdef ATAFB_EXT else if (!strcmp(this_opt,"mv300")) { external_bitspercol = 8; @@ -2939,12 +2943,8 @@ __initfunc(void atari_video_setup( char *options, int *ints )) if (*int_str) { /* Format to config extended internal video hardware like OverScan: - "<switch-type>,internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>" + "internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>" Explanation: - <switch-type> type to switch on higher resolution - sw_acia : via keyboard ACIA - sw_snd6 : via bit 6 of the soundchip port - sw_snd7 : via bit 7 of the soundchip port <xres>: x-resolution <yres>: y-resolution The following are only needed if you have an overscan which @@ -2974,9 +2974,9 @@ __initfunc(void atari_video_setup( char *options, int *ints )) if (ovsc_offset || (sttt_yres_virtual != st_yres)) use_hwscroll=0; + int_invalid: + ; } - else - int_invalid: ovsc_switchmode = SWITCH_NONE; #ifdef ATAFB_EXT if (*ext_str) { @@ -3103,10 +3103,10 @@ __initfunc(void atari_video_setup( char *options, int *ints )) hmax = 1000 * simple_strtoul(p, NULL, 10); if (hmax <= 0 || hmax <= hmin) goto cap_invalid; - vfmin = vmin; - vfmax = vmax; - hfmin = hmin; - hfmax = hmax; + fb_info.monspecs.vfmin = vmin; + fb_info.monspecs.vfmax = vmax; + fb_info.monspecs.hfmin = hmin; + fb_info.monspecs.hfmax = hmax; cap_invalid: ; } @@ -3126,9 +3126,9 @@ __initfunc(void atari_video_setup( char *options, int *ints )) depth = simple_strtoul(p, NULL, 10); if ((temp=get_video_mode("user0"))) { default_par=temp; - atari_fb_predefined[default_par-1].xres = xres; - atari_fb_predefined[default_par-1].yres = yres; - atari_fb_predefined[default_par-1].bits_per_pixel = depth; + atafb_predefined[default_par-1].xres = xres; + atafb_predefined[default_par-1].yres = yres; + atafb_predefined[default_par-1].bits_per_pixel = depth; } user_invalid: @@ -3139,7 +3139,7 @@ __initfunc(void atari_video_setup( char *options, int *ints )) #ifdef MODULE int init_module(void) { - return(atari_fb_init(NULL)); + return(atafb_init(NULL)); } void cleanup_module(void) @@ -3147,6 +3147,7 @@ void cleanup_module(void) /* Not reached because the usecount will never be decremented to zero */ unregister_framebuffer(&fb_info); - /* TODO: clean up ... */ + /* atari_stram_free( screen_base ); */ + /* TODO: further clean up ... */ } #endif /* MODULE */ diff --git a/drivers/video/ati-gt.h b/drivers/video/ati-gt.h new file mode 100644 index 000000000..32dc792b5 --- /dev/null +++ b/drivers/video/ati-gt.h @@ -0,0 +1,203 @@ +/* the usage for the following structs vary from the gx and vt: +and sdram and sgram gt's + pll registers (sdram) 6,7,11; + crtc_h_sync_strt_wid[3]; + dsp1[3] (sdram,sgram,unused) + dsp2[3] (offset regbase+24, depends on colour mode); + crtc_h_tot_disp,crtc_v_tot_disp,crtc_v_sync_strt_wid,unused; + pll registers (sgram) 7,11; +*/ + +/* Register values for 1280x1024, 75Hz mode (20). no 16/32 */ +static struct aty_regvals aty_gt_reg_init_20 = { + { 0x41, 0xf9, 0x04 }, + { 0xe02a7, 0x1401a6, 0 }, + { 0x260957, 0x2806d6, 0 }, + { 0x10006b6, 0x20006b6, 0x30006b6 }, + + 0x9f00d2, 0x03ff0429, 0x30400, 0, + { 0xb5, 0x04 } +}; + +#if 0 +/* Register values for 1280x960, 75Hz mode (19) */ +static struct aty_regvals aty_gt_reg_init_19 = { +}; +#endif + +/* Register values for 1152x870, 75Hz mode (18) */ +static struct aty_regvals aty_gt_reg_init_18 = { + { 0x41, 0xe6, 0x04 }, + { 0x300295, 0x300194, 0x300593 }, + { 0x260a1c, 0x380561, 0}, + { 0x1000744, 0x2000744, 0x3000744 }, + + 0x8f00b5, 0x3650392, 0x230368, 0, + { 0xe6, 0x04 } +}; + +/* Register values for 1024x768, 75Hz mode (17), 32 bpp untested */ +static struct aty_regvals aty_gt_reg_init_17 = { + { 0x41, 0xb5, 0x04 }, + { 0xc0283, 0xc0182, 0xc0581 }, + { 0x36066d, 0x3806d6, 0}, + { 0xa0049e, 0x100049e, 0x200049e }, + + 0x7f00a3, 0x2ff031f, 0x30300, 0, + { 0xb8, 0x04 } +}; + +#if 0 +/* Register values for x, Hz mode (16) */ +static struct aty_regvals aty_gt_reg_init_16 = { +}; +#endif + +/* Register values for 1024x768, 70Hz mode (15) */ +static struct aty_regvals aty_gt_reg_init_15 = { + { 0x41, 0xad, 0x04 }, + { 0x310284, 0x310183, 0x310582 }, + { 0x0, 0x380727 }, + { 0x0 }, + 0x7f00a5, 0x2ff0325, 0x260302, +}; + +/* Register values for 1024x768, 60Hz mode (14) */ +static struct aty_regvals aty_gt_reg_init_14 = { + { 0x40, 0xe1, 0x14 }, + { 0x310284, 0x310183, 0x310582 }, + { 0x3607c0, 0x380840, 0x0 }, + { 0xa80592, 0x1000592, 0x0 }, + + 0x7f00a7, 0x2ff0325, 0x260302, 0, + { 0xe1, 0x14 } +}; + +/* Register values for 832x624, 75Hz mode (13) */ +static struct aty_regvals aty_gt_reg_init_13 = { + { 0x40, 0xc6, 0x14 }, + { 0x28026d, 0x28016c, 0x28056b }, + { 0x3608cf, 0x380960, 0 }, + { 0xb00655, 0x1000655, 0x2000655 }, + + 0x67008f, 0x26f029a, 0x230270, 0, + { 0xc6, 0x14 } +}; + +/* Register values for 800x600, 75Hz mode (12) */ +static struct aty_regvals aty_gt_reg_init_12 = { + { 0x42, 0xe4, 0x04 }, + { 0xa0267, 0xa0166, 0x0a0565}, + { 0x360a33, 0x48056d, 0}, + { 0xc00755, 0x1000755, 0x02000755}, + + 0x630083, 0x2570270, 0x30258, 0, + { 0xe4, 0x4 } +}; + +/* Register values for 800x600, 72Hz mode (11) */ +static struct aty_regvals aty_gt_reg_init_11 = { + { 0x42, 0xe6, 0x04 }, + { 0xf026c, 0xf016b, 0xf056a }, + { 0x360a1d, 0x480561, 0}, + { 0xc00745, 0x1000745, 0x2000745 }, + + 0x630081, 0x02570299, 0x6027c +}; + +/* Register values for 800x600, 60Hz mode (10) */ +static struct aty_regvals aty_gt_reg_init_10 = { + { 0x42, 0xb8, 0x04 }, + { 0x10026a, 0x100169, 0x100568 }, + { 0x460652, 0x4806ba, 0}, + { 0x68048b, 0xa0048b, 0x100048b }, + + 0x630083, 0x02570273, 0x40258, 0, + { 0xb8, 0x4 } +}; + +/* Register values for 800x600, 56Hz mode (9) */ +static struct aty_regvals aty_gt_reg_init_9 = { + { 0x42, 0xf9, 0x14 }, + { 0x90268, 0x90167, 0x090566 }, + { 0x460701, 0x480774, 0}, + { 0x700509, 0xa80509, 0x1000509 }, + + 0x63007f, 0x2570270, 0x20258 +}; + +#if 0 +/* Register values for 768x576, 50Hz mode (8) */ +static struct aty_regvals aty_gt_reg_init_8 = { +}; + +/* Register values for 640x870, 75Hz Full Page Display (7) */ +static struct aty_regvals aty_gt_reg_init_7 = { +}; +#endif + +/* Register values for 640x480, 67Hz mode (6) */ +static struct aty_regvals aty_gt_reg_init_6 = { + { 0x42, 0xd1, 0x14 }, + { 0x280259, 0x280158, 0x280557 }, + { 0x460858, 0x4808e2, 0}, + { 0x780600, 0xb00600, 0x1000600 }, + + 0x4f006b, 0x1df020c, 0x2301e2, 0, + { 0x8b, 0x4 } +}; + +/* Register values for 640x480, 60Hz mode (5) */ +static struct aty_regvals aty_gt_reg_init_5 = { + { 0x43, 0xe8, 0x04 }, + { 0x2c0253, 0x2c0152, 0x2c0551 }, + { 0x460a06, 0x580555, 0}, + { 0x880734, 0xc00734, 0x1000734 }, + + 0x4f0063, 0x1df020c, 0x2201e9, 0, + { 0xe8, 0x04 } +}; + +#if 0 +/* Register values for x, Hz mode (4) */ +static struct aty_regvals aty_gt_reg_init_4 = { +}; + +/* Register values for x, Hz mode (3) */ +static struct aty_regvals aty_gt_reg_init_3 = { +}; + +/* Register values for x, Hz mode (2) */ +static struct aty_regvals aty_gt_reg_init_2 = { +}; + +/* Register values for x, Hz mode (1) */ +static struct aty_regvals aty_gt_reg_init_1 = { +}; +#endif + +/* yikes, more data structures (dsp2) + * XXX kludge for sgram + */ +static int sgram_dsp[20][3] = { + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0x5203d7,0x7803d9,0xb803dd}, //5 + {0x940666,0xe0066a,0x1700672}, //6 + {0,0,0}, + {0,0,0}, + {0x88055f,0xd80563,0x170056b}, //9 + {0x8404d9,0xb804dd,0x17004e5}, //10 + {0x7803e2,0xb803e6,0x17003ee}, //11 + {0x7803eb,0xb803ef,0x17003f7}, //12 + {0xe806c5,0x17006cd,0x2e006dd}, //13 + {0xe005f6,0x17005fe,0x2e0060e}, //14 + {0xd8052c,0x1700534,0x2e00544}, //15 + {0,0,0}, + {0xb804f2,0x17004e5,0x2e0050a}, //17 + {0xb803e6,0x17003ee,0x2e003fe}, //18 + {0,0,0}, + {0,0,0}, +}; diff --git a/drivers/video/ati-gx.h b/drivers/video/ati-gx.h new file mode 100644 index 000000000..df48c1865 --- /dev/null +++ b/drivers/video/ati-gx.h @@ -0,0 +1,122 @@ +/* Register values for 1280x1024, 75Hz (WAS 60) mode (20) */ +static struct aty_regvals aty_gx_reg_init_20 = { + { 0x200, 0x200, 0x200 }, + + { 0x1200a5, 0x1200a3, 0x1200a3 }, + { 0x30c0200, 0x30e0300, 0x30e0300 }, + { 0x2, 0x3, 0x3 }, + + 0x9f00d2, 0x3ff0429, 0x30400, 0x28100040, + { 0xd4, 0x9 } +}; + +/* Register values for 1152x870, 75Hz mode (18) */ +static struct aty_regvals aty_gx_reg_init_18 = { + { 0x200, 0x200, 0x200 }, + + { 0x300097, 0x300095, 0x300094 }, + { 0x3090200, 0x30e0300, 0x30e0600 }, + { 0x2, 0x3, 0x6 }, + + 0x8f00b5, 0x3650392, 0x230368, 0x24100040, + { 0x53, 0x3 } +}; + +/* Register values for 1024x768, 75Hz mode (17) */ +static struct aty_regvals aty_gx_reg_init_17 = { + { 0x200, 0x200, 0x200 }, + + { 0x2c0087, 0x2c0085, 0x2c0084 }, + { 0x3070200, 0x30e0300, 0x30e0600 }, + { 0x2, 0x3, 0x6 }, + + 0x7f00a5, 0x2ff0323, 0x230302, 0x20100000, + { 0x42, 0x3 } +}; + +/* Register values for 1024x768, 72Hz mode (15) */ +static struct aty_regvals aty_gx_reg_init_15 = { + { 0, 0, 0 }, + + { 0x310086, 0x310084, 0x310084 }, + { 0x3070200, 0x30e0300, 0x30e0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000, + { 0x88, 0x7 } +}; + +/* Register values for 1024x768, 60Hz mode (14) */ +static struct aty_regvals aty_gx_reg_init_14 = { + { 0, 0, 0 }, + + { 0x310086, 0x310084, 0x310084 }, + { 0x3060200, 0x30d0300, 0x30d0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a7, 0x2ff0325, 0x260302, 0x20100000, + { 0x6c, 0x6 } +}; + +/* Register values for 832x624, 75Hz mode (13) */ +static struct aty_regvals aty_gx_reg_init_13 = { + { 0x200, 0x200, 0x200 }, + + { 0x28006f, 0x28006d, 0x28006c }, + { 0x3050200, 0x30b0300, 0x30e0600 }, + { 0x2, 0x3, 0x6 }, + + 0x67008f, 0x26f029a, 0x230270, 0x1a100040, + { 0x4f, 0x5 } +}; + +#if 0 /* not filled in yet */ +/* Register values for 800x600, 75Hz mode (12) */ +static struct aty_regvals aty_gx_reg_init_12 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 49.11MHz for V=74.40Hz */ +}; + +/* Register values for 800x600, 72Hz mode (11) */ +static struct aty_regvals aty_gx_reg_init_11 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 49.63MHz for V=71.66Hz */ +}; + +/* Register values for 800x600, 60Hz mode (10) */ +static struct aty_regvals aty_gx_reg_init_10 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 41.41MHz for V=59.78Hz */ +}; + +/* Register values for 640x870, 75Hz Full Page Display (7) */ +static struct aty_regvals aty_gx_reg_init_7 = { + { 0x10, 0x30, 0x68 }, + { }, + { } /* pixel clock = 57.29MHz for V=75.01Hz */ +}; +#endif + +/* Register values for 640x480, 67Hz mode (6) */ +static struct aty_regvals aty_gx_reg_init_6 = { + { 0x200, 0x200, 0x200 }, + + { 0x28005b, 0x280059, 0x280058 }, + { 0x3040200, 0x3060300, 0x30c0600 }, + { 0x2002312, 0x3002312, 0x6002312 }, + + 0x4f006b, 0x1df020c, 0x2301e2, 0x14100040, + { 0x35, 0x07 } +}; + +#if 0 /* not filled in yet */ +/* Register values for 640x480, 60Hz mode (5) */ +static struct aty_regvals aty_gx_reg_init_5 = { + { 0x200, 0x200, 0x200 }, + { }, + { 0x35, 0x07 } +}; +#endif diff --git a/drivers/video/ati-vt.h b/drivers/video/ati-vt.h new file mode 100644 index 000000000..3b25d6d5d --- /dev/null +++ b/drivers/video/ati-vt.h @@ -0,0 +1,147 @@ +/* Register values for 1280x1024, 60Hz mode (20) */ +static struct aty_regvals aty_vt_reg_init_20 = { + { 0, 0, 0 }, + + { 0x002e02a7, 0x002e02a7, 0 }, + { 0x03070200, 0x03070200, 0 }, + { 0x0a00cb22, 0x0b00cb23, 0 }, + + 0x009f00d2, 0x03ff0429, 0x00030400, 0x28000000, + { 0x00, 0xaa } +}; + +/* Register values for 1280x960, 75Hz mode (19) */ +static struct aty_regvals aty_vt_reg_init_19 = { + { 0, 0, 0 }, + { 0x003202a3, 0x003201a2, 0 }, + { 0x030b0200, 0x030b0300, 0 }, + { 0x0a00cb22, 0x0b00cb23, 0 }, + + 0x009f00d1, 0x03bf03e7, 0x000303c0, 0x28000000, + { 0x00, 0xc6 } +}; + +/* Register values for 1152x870, 75Hz mode (18) */ +static struct aty_regvals aty_vt_reg_init_18 = { + { 0, 0, 0 }, + + { 0x00300295, 0x00300194, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, + + 0x008f00b5, 0x03650392, 0x00230368, 0x24000000, + { 0x00, 0x9d } +}; + +/* Register values for 1024x768, 75Hz mode (17) */ +static struct aty_regvals aty_vt_reg_init_17 = { + { 0, 0, 0 }, + + { 0x002c0283, 0x002c0182, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, + + 0x007f00a3, 0x02ff031f, 0x00030300, 0x20000000, + { 0x01, 0xf7 } +}; + +/* Register values for 1024x768, 70Hz mode (15) */ +static struct aty_regvals aty_vt_reg_init_15 = { + { 0, 0, 0 }, + { 0x00310284, 0x00310183, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, + + 0x007f00a5, 0x02ff0325, 0x00260302, 0x20000000, + { 0x01, 0xeb } +}; + +/* Register values for 1024x768, 60Hz mode (14) */ +static struct aty_regvals aty_vt_reg_init_14 = { + { 0, 0, 0 }, + + { 0x00310284, 0x00310183, 0x00310582 }, /* 32 bit 0x00310582 */ + { 0x03080200, 0x03080300, 0x03070600 }, /* 32 bit 0x03070600 */ + { 0x0a00cb21, 0x0b00cb22, 0x0e00cb23 }, + + 0x007f00a7, 0x02ff0325, 0x00260302, 0x20000000, + { 0x01, 0xcc } +}; + +/* Register values for 832x624, 75Hz mode (13) */ +static struct aty_regvals aty_vt_reg_init_13 = { + { 0, 0, 0 }, + + { 0x0028026d, 0x0028016c, 0x0028056b }, + { 0x03080200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x0067008f, 0x026f029a, 0x00230270, 0x1a000000, + { 0x01, 0xb4 } +}; + +/* Register values for 800x600, 75Hz mode (12) */ +static struct aty_regvals aty_vt_reg_init_12 = { + { 0, 0, 0 }, + + { 0x002a0267, 0x002a0166, 0x002a0565 }, + { 0x03040200, 0x03060300, 0x03070600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630083, 0x02570270, 0x00030258, 0x19000000, + { 0x01, 0x9c } +}; + +/* Register values for 800x600, 72Hz mode (11) */ +static struct aty_regvals aty_vt_reg_init_11 = { + { 0, 0, 0 }, + + { 0x002f026c, 0x002f016b, 0x002f056a }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630081, 0x02570299, 0x0006027c, 0x19000000, + { 0x01, 0x9d } +}; + +/* Register values for 800x600, 60Hz mode (10) */ +static struct aty_regvals aty_vt_reg_init_10 = { + { 0, 0, 0 }, + + { 0x0030026a, 0x00300169, 0x00300568 }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630083, 0x02570273, 0x00040258, 0x19000000, + { 0x02, 0xfb } +}; + +/* Register values for 640x480, 67Hz mode (6) */ +static struct aty_regvals aty_vt_reg_init_6 = { + { 0, 0, 0 }, + + { 0x00280259, 0x00280158, 0x00280557 }, + { 0x03050200, 0x03070300, 0x030a0600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x004f006b, 0x01df020c, 0x002301e2, 0x14000000, + { 0x02, 0xbe } +}; + +/* Register values for 640x480, 60Hz mode (5) */ +static struct aty_regvals aty_vt_reg_init_5 = { + { 0, 0, 0 }, + + { 0x002c0253, 0x002c0152, 0x002c0551 }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x004f0063, 0x01df020c, 0x002201e9, 0x14000000, + { 0x02, 0x9e } +}; + /* 8 bit 15 bit 32 bit */ +static int vt_mem_cntl[3][3] = { { 0x0A00CB21, 0x0B00CB21, 0x0E00CB21 }, /* 1 MB VRAM */ + { 0x0A00CB22, 0x0B00CB22, 0x0E00CB22 }, /* 2 MB VRAM */ + { 0x0200053B, 0x0300053B, 0x0600053B } /* 4 M B VRAM */ + }; + diff --git a/drivers/video/aty.h b/drivers/video/aty.h new file mode 100644 index 000000000..7c9b00ad7 --- /dev/null +++ b/drivers/video/aty.h @@ -0,0 +1,923 @@ +/* + * Exported procedures for the ATI/mach64 display driver on PowerMacs. + * + * Copyright (C) 1997 Michael AK Tesch + * written with much help from Jon Howell + * + * Updated for 3D RAGE PRO by Geert Uytterhoeven + * + * 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. + */ + +/* + * most of the rest of this file comes from ATI sample code + */ +#ifndef REGMACH64_H +#define REGMACH64_H + +/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */ + +#define CRTC_H_TOTAL_DISP 0x0000 /* Dword offset 0_00 */ +#define CRTC_H_SYNC_STRT_WID 0x0004 /* Dword offset 0_01 */ +#define CRTC_H_SYNC_STRT 0x0004 +#define CRTC_H_SYNC_DLY 0x0005 +#define CRTC_H_SYNC_WID 0x0006 + +#define CRTC_V_TOTAL_DISP 0x0008 /* Dword offset 0_02 */ +#define CRTC_V_TOTAL 0x0008 +#define CRTC_V_DISP 0x000A +#define CRTC_V_SYNC_STRT_WID 0x000C /* Dword offset 0_03 */ +#define CRTC_V_SYNC_STRT 0x000C +#define CRTC_V_SYNC_WID 0x000E + +#define CRTC_VLINE_CRNT_VLINE 0x0010 /* Dword offset 0_04 */ +#define CRTC_OFF_PITCH 0x0014 /* Dword offset 0_05 */ +#define CRTC_OFFSET 0x0014 +#define CRTC_PITCH 0x0016 + +#define CRTC_INT_CNTL 0x0018 /* Dword offset 0_06 */ +#define CRTC_GEN_CNTL 0x001C /* Dword offset 0_07 */ +#define CRTC_PIX_WIDTH 0x001D +#define CRTC_FIFO 0x001E +#define CRTC_EXT_DISP 0x001F + +#define DSP_CONFIG 0x0020 /* Dword offset 0_08 */ +#define DSP_ON_OFF 0x0024 /* Dword offset 0_09 */ +#define TIMER_CONFIG 0x0028 /* Dword offset 0_0A */ +#define MEM_BUF_CNTL 0x002C /* Dword offset 0_0B */ +#define MEM_ADDR_CONFIG 0x0034 /* Dword offset 0_0D */ + +#define CRT_TRAP 0x0038 /* Dword offset 0_0E */ + +#define I2C_CNTL_0 0x003C /* Dword offset 0_0F */ + +#define OVR_CLR 0x0040 /* Dword offset 0_10 */ +#define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 0_11 */ +#define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 0_12 */ + +#define VGA_DSP_CONFIG 0x004C /* Dword offset 0_13 */ +#define VGA_DSP_ON_OFF 0x0050 /* Dword offset 0_14 */ + +#define CUR_CLR0 0x0060 /* Dword offset 0_18 */ +#define CUR_CLR1 0x0064 /* Dword offset 0_19 */ +#define CUR_OFFSET 0x0068 /* Dword offset 0_1A */ +#define CUR_HORZ_VERT_POSN 0x006C /* Dword offset 0_1B */ +#define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 0_1C */ + +#define GP_IO 0x0078 /* Dword offset 0_1E */ + +#define HW_DEBUG 0x007C /* Dword offset 0_1F */ + +#define SCRATCH_REG0 0x0080 /* Dword offset 0_20 */ +#define SCRATCH_REG1 0x0084 /* Dword offset 0_21 */ + +#define CLOCK_CNTL 0x0090 /* Dword offset 0_24 */ +#define CLOCK_SEL_CNTL 0x0090 /* Dword offset 0_24 */ + +#define CONFIG_STAT1 0x0094 /* Dword offset 0_25 */ +#define CONFIG_STAT2 0x0098 /* Dword offset 0_26 */ + +#define BUS_CNTL 0x00A0 /* Dword offset 0_28 */ + +#define EXT_MEM_CNTL 0x00AC /* Dword offset 0_2B */ +#define MEM_CNTL 0x00B0 /* Dword offset 0_2C */ + +#define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 0_2D */ +#define MEM_VGA_RP_SEL 0x00B8 /* Dword offset 0_2E */ + +#define DAC_REGS 0x00C0 /* Dword offset 0_30 */ +#define DAC_W_INDEX 0x00C0 /* Dword offset 0_30 */ +#define DAC_DATA 0x00C1 /* Dword offset 0_30 */ +#define DAC_MASK 0x00C2 /* Dword offset 0_30 */ +#define DAC_R_INDEX 0x00C3 /* Dword offset 0_30 */ +#define DAC_CNTL 0x00C4 /* Dword offset 0_31 */ + +#define EXT_DAC_REGS 0x00C8 /* Dword offset 0_32 */ + +#define GEN_TEST_CNTL 0x00D0 /* Dword offset 0_34 */ + +#define CUSTOM_MACRO_CNTL 0x00D4 /* Dword offset 0_35 */ + +#define CONFIG_CNTL 0x00DC /* Dword offset 0_37 (CT, ET, VT) */ +#define CONFIG_CHIP_ID 0x00E0 /* Dword offset 0_38 */ +#define CONFIG_STAT0 0x00E4 /* Dword offset 0_39 */ +#define CRC_SIG 0x00E8 /* Dword offset 0_3A */ + + +/* GUI MEMORY MAPPED Registers */ + +#define DST_OFF_PITCH 0x0100 /* Dword offset 0_40 */ +#define DST_X 0x0104 /* Dword offset 0_41 */ +#define DST_Y 0x0108 /* Dword offset 0_42 */ +#define DST_Y_X 0x010C /* Dword offset 0_43 */ +#define DST_WIDTH 0x0110 /* Dword offset 0_44 */ +#define DST_HEIGHT 0x0114 /* Dword offset 0_45 */ +#define DST_HEIGHT_WIDTH 0x0118 /* Dword offset 0_46 */ +#define DST_X_WIDTH 0x011C /* Dword offset 0_47 */ +#define DST_BRES_LNTH 0x0120 /* Dword offset 0_48 */ +#define DST_BRES_ERR 0x0124 /* Dword offset 0_49 */ +#define DST_BRES_INC 0x0128 /* Dword offset 0_4A */ +#define DST_BRES_DEC 0x012C /* Dword offset 0_4B */ +#define DST_CNTL 0x0130 /* Dword offset 0_4C */ +#define DST_Y_X__ALIAS__ 0x0134 /* Dword offset 0_4D */ +#define TRAIL_BRES_ERR 0x0138 /* Dword offset 0_4E */ +#define TRAIL_BRES_INC 0x013C /* Dword offset 0_4F */ +#define TRAIL_BRES_DEC 0x0140 /* Dword offset 0_50 */ +#define LEAD_BRES_LNTH 0x0144 /* Dword offset 0_51 */ +#define Z_OFF_PITCH 0x0148 /* Dword offset 0_52 */ +#define Z_CNTL 0x014C /* Dword offset 0_53 */ +#define ALPHA_TST_CNTL 0x0150 /* Dword offset 0_54 */ +#define SECONDARY_STW_EXP 0x0158 /* Dword offset 0_56 */ +#define SECONDARY_S_X_INC 0x015C /* Dword offset 0_57 */ +#define SECONDARY_S_Y_INC 0x0160 /* Dword offset 0_58 */ +#define SECONDARY_S_START 0x0164 /* Dword offset 0_59 */ +#define SECONDARY_W_X_INC 0x0168 /* Dword offset 0_5A */ +#define SECONDARY_W_Y_INC 0x016C /* Dword offset 0_5B */ +#define SECONDARY_W_START 0x0170 /* Dword offset 0_5C */ +#define SECONDARY_T_X_INC 0x0174 /* Dword offset 0_5D */ +#define SECONDARY_T_Y_INC 0x0178 /* Dword offset 0_5E */ +#define SECONDARY_T_START 0x017C /* Dword offset 0_5F */ + +#define SRC_OFF_PITCH 0x0180 /* Dword offset 0_60 */ +#define SRC_X 0x0184 /* Dword offset 0_61 */ +#define SRC_Y 0x0188 /* Dword offset 0_62 */ +#define SRC_Y_X 0x018C /* Dword offset 0_63 */ +#define SRC_WIDTH1 0x0190 /* Dword offset 0_64 */ +#define SRC_HEIGHT1 0x0194 /* Dword offset 0_65 */ +#define SRC_HEIGHT1_WIDTH1 0x0198 /* Dword offset 0_66 */ +#define SRC_X_START 0x019C /* Dword offset 0_67 */ +#define SRC_Y_START 0x01A0 /* Dword offset 0_68 */ +#define SRC_Y_X_START 0x01A4 /* Dword offset 0_69 */ +#define SRC_WIDTH2 0x01A8 /* Dword offset 0_6A */ +#define SRC_HEIGHT2 0x01AC /* Dword offset 0_6B */ +#define SRC_HEIGHT2_WIDTH2 0x01B0 /* Dword offset 0_6C */ +#define SRC_CNTL 0x01B4 /* Dword offset 0_6D */ + +#define SCALE_OFF 0x01C0 /* Dword offset 0_70 */ +#define SECONDARY_SCALE_OFF 0x01C4 /* Dword offset 0_71 */ + +#define TEX_0_OFF 0x01C0 /* Dword offset 0_70 */ +#define TEX_1_OFF 0x01C4 /* Dword offset 0_71 */ +#define TEX_2_OFF 0x01C8 /* Dword offset 0_72 */ +#define TEX_3_OFF 0x01CC /* Dword offset 0_73 */ +#define TEX_4_OFF 0x01D0 /* Dword offset 0_74 */ +#define TEX_5_OFF 0x01D4 /* Dword offset 0_75 */ +#define TEX_6_OFF 0x01D8 /* Dword offset 0_76 */ +#define TEX_7_OFF 0x01DC /* Dword offset 0_77 */ + +#define SCALE_WIDTH 0x01DC /* Dword offset 0_77 */ +#define SCALE_HEIGHT 0x01E0 /* Dword offset 0_78 */ + +#define TEX_8_OFF 0x01E0 /* Dword offset 0_78 */ +#define TEX_9_OFF 0x01E4 /* Dword offset 0_79 */ +#define TEX_10_OFF 0x01E8 /* Dword offset 0_7A */ +#define S_Y_INC 0x01EC /* Dword offset 0_7B */ + +#define SCALE_PITCH 0x01EC /* Dword offset 0_7B */ +#define SCALE_X_INC 0x01F0 /* Dword offset 0_7C */ + +#define RED_X_INC 0x01F0 /* Dword offset 0_7C */ +#define GREEN_X_INC 0x01F4 /* Dword offset 0_7D */ + +#define SCALE_Y_INC 0x01F4 /* Dword offset 0_7D */ +#define SCALE_VACC 0x01F8 /* Dword offset 0_7E */ +#define SCALE_3D_CNTL 0x01FC /* Dword offset 0_7F */ + +#define HOST_DATA0 0x0200 /* Dword offset 0_80 */ +#define HOST_DATA1 0x0204 /* Dword offset 0_81 */ +#define HOST_DATA2 0x0208 /* Dword offset 0_82 */ +#define HOST_DATA3 0x020C /* Dword offset 0_83 */ +#define HOST_DATA4 0x0210 /* Dword offset 0_84 */ +#define HOST_DATA5 0x0214 /* Dword offset 0_85 */ +#define HOST_DATA6 0x0218 /* Dword offset 0_86 */ +#define HOST_DATA7 0x021C /* Dword offset 0_87 */ +#define HOST_DATA8 0x0220 /* Dword offset 0_88 */ +#define HOST_DATA9 0x0224 /* Dword offset 0_89 */ +#define HOST_DATAA 0x0228 /* Dword offset 0_8A */ +#define HOST_DATAB 0x022C /* Dword offset 0_8B */ +#define HOST_DATAC 0x0230 /* Dword offset 0_8C */ +#define HOST_DATAD 0x0234 /* Dword offset 0_8D */ +#define HOST_DATAE 0x0238 /* Dword offset 0_8E */ +#define HOST_DATAF 0x023C /* Dword offset 0_8F */ +#define HOST_CNTL 0x0240 /* Dword offset 0_90 */ + +#define BM_HOSTDATA 0x0244 /* Dword offset 0_91 */ +#define BM_ADDR 0x0248 /* Dword offset 0_92 */ +#define BM_DATA 0x0248 /* Dword offset 0_92 */ +#define BM_GUI_TABLE_CMD 0x024C /* Dword offset 0_93 */ + +#define PAT_REG0 0x0280 /* Dword offset 0_A0 */ +#define PAT_REG1 0x0284 /* Dword offset 0_A1 */ +#define PAT_CNTL 0x0288 /* Dword offset 0_A2 */ + +#define SC_LEFT 0x02A0 /* Dword offset 0_A8 */ +#define SC_RIGHT 0x02A4 /* Dword offset 0_A9 */ +#define SC_LEFT_RIGHT 0x02A8 /* Dword offset 0_AA */ +#define SC_TOP 0x02AC /* Dword offset 0_AB */ +#define SC_BOTTOM 0x02B0 /* Dword offset 0_AC */ +#define SC_TOP_BOTTOM 0x02B4 /* Dword offset 0_AD */ + +#define DP_BKGD_CLR 0x02C0 /* Dword offset 0_B0 */ +#define DP_FOG_CLR 0x02C4 /* Dword offset 0_B1 */ +#define DP_FRGD_CLR 0x02C4 /* Dword offset 0_B1 */ +#define DP_WRITE_MSK 0x02C8 /* Dword offset 0_B2 */ +#define DP_CHAIN_MSK 0x02CC /* Dword offset 0_B3 */ +#define DP_PIX_WIDTH 0x02D0 /* Dword offset 0_B4 */ +#define DP_MIX 0x02D4 /* Dword offset 0_B5 */ +#define DP_SRC 0x02D8 /* Dword offset 0_B6 */ +#define DP_FRGD_CLR_MIX 0x02DC /* Dword offset 0_B7 */ +#define DP_FRGD_BLGD_CLR 0x02E0 /* Dword offset 0_B8 */ + +#define DST_X_Y 0x02E8 /* Dword offset 0_BA */ +#define DST_WIDTH_HEIGHT 0x02EC /* Dword offset 0_BB */ +#define USR_DST_PICTH 0x02F0 /* Dword offset 0_BC */ +#define DP_SET_GUI_ENGINE2 0x02F8 /* Dword offset 0_BE */ +#define DP_SET_GUI_ENGINE 0x02FC /* Dword offset 0_BF */ + +#define CLR_CMP_CLR 0x0300 /* Dword offset 0_C0 */ +#define CLR_CMP_MSK 0x0304 /* Dword offset 0_C1 */ +#define CLR_CMP_CNTL 0x0308 /* Dword offset 0_C2 */ + +#define FIFO_STAT 0x0310 /* Dword offset 0_C4 */ + +#define CONTEXT_MASK 0x0320 /* Dword offset 0_C8 */ +#define CONTEXT_LOAD_CNTL 0x032C /* Dword offset 0_CB */ + +#define GUI_TRAJ_CNTL 0x0330 /* Dword offset 0_CC */ +#define GUI_STAT 0x0338 /* Dword offset 0_CE */ + +#define TEX_PALETTE_INDEX 0x0340 /* Dword offset 0_D0 */ +#define STW_EXP 0x0344 /* Dword offset 0_D1 */ +#define LOG_MAX_INC 0x0348 /* Dword offset 0_D2 */ +#define S_X_INC 0x034C /* Dword offset 0_D3 */ +#define S_Y_INC__ALIAS__ 0x0350 /* Dword offset 0_D4 */ + +#define SCALE_PITCH__ALIAS__ 0x0350 /* Dword offset 0_D4 */ + +#define S_START 0x0354 /* Dword offset 0_D5 */ +#define W_X_INC 0x0358 /* Dword offset 0_D6 */ +#define W_Y_INC 0x035C /* Dword offset 0_D7 */ +#define W_START 0x0360 /* Dword offset 0_D8 */ +#define T_X_INC 0x0364 /* Dword offset 0_D9 */ +#define T_Y_INC 0x0368 /* Dword offset 0_DA */ + +#define SECONDARY_SCALE_PITCH 0x0368 /* Dword offset 0_DA */ + +#define T_START 0x036C /* Dword offset 0_DB */ +#define TEX_SIZE_PITCH 0x0370 /* Dword offset 0_DC */ +#define TEX_CNTL 0x0374 /* Dword offset 0_DD */ +#define SECONDARY_TEX_OFFSET 0x0378 /* Dword offset 0_DE */ +#define TEX_PALETTE 0x037C /* Dword offset 0_DF */ + +#define SCALE_PITCH_BOTH 0x0380 /* Dword offset 0_E0 */ +#define SECONDARY_SCALE_OFF_ACC 0x0384 /* Dword offset 0_E1 */ +#define SCALE_OFF_ACC 0x0388 /* Dword offset 0_E2 */ +#define SCALE_DST_Y_X 0x038C /* Dword offset 0_E3 */ + +#define COMPOSITE_SHADOW_ID 0x0398 /* Dword offset 0_E6 */ + +#define SECONDARY_SCALE_X_INC 0x039C /* Dword offset 0_E7 */ + +#define SPECULAR_RED_X_INC 0x039C /* Dword offset 0_E7 */ +#define SPECULAR_RED_Y_INC 0x03A0 /* Dword offset 0_E8 */ +#define SPECULAR_RED_START 0x03A4 /* Dword offset 0_E9 */ + +#define SECONDARY_SCALE_HACC 0x03A4 /* Dword offset 0_E9 */ + +#define SPECULAR_GREEN_X_INC 0x03A8 /* Dword offset 0_EA */ +#define SPECULAR_GREEN_Y_INC 0x03AC /* Dword offset 0_EB */ +#define SPECULAR_GREEN_START 0x03B0 /* Dword offset 0_EC */ +#define SPECULAR_BLUE_X_INC 0x03B4 /* Dword offset 0_ED */ +#define SPECULAR_BLUE_Y_INC 0x03B8 /* Dword offset 0_EE */ +#define SPECULAR_BLUE_START 0x03BC /* Dword offset 0_EF */ + +#define SCALE_X_INC__ALIAS__ 0x03C0 /* Dword offset 0_F0 */ + +#define RED_X_INC__ALIAS__ 0x03C0 /* Dword offset 0_F0 */ +#define RED_Y_INC 0x03C4 /* Dword offset 0_F1 */ +#define RED_START 0x03C8 /* Dword offset 0_F2 */ + +#define SCALE_HACC 0x03C8 /* Dword offset 0_F2 */ +#define SCALE_Y_INC__ALIAS__ 0x03CC /* Dword offset 0_F3 */ + +#define GREEN_X_INC__ALIAS__ 0x03CC /* Dword offset 0_F3 */ +#define GREEN_Y_INC 0x03D0 /* Dword offset 0_F4 */ + +#define SECONDARY_SCALE_Y_INC 0x03D0 /* Dword offset 0_F4 */ +#define SECONDARY_SCALE_VACC 0x03D4 /* Dword offset 0_F5 */ + +#define GREEN_START 0x03D4 /* Dword offset 0_F5 */ +#define BLUE_X_INC 0x03D8 /* Dword offset 0_F6 */ +#define BLUE_Y_INC 0x03DC /* Dword offset 0_F7 */ +#define BLUE_START 0x03E0 /* Dword offset 0_F8 */ +#define Z_X_INC 0x03E4 /* Dword offset 0_F9 */ +#define Z_Y_INC 0x03E8 /* Dword offset 0_FA */ +#define Z_START 0x03EC /* Dword offset 0_FB */ +#define ALPHA_X_INC 0x03F0 /* Dword offset 0_FC */ +#define FOG_X_INC 0x03F0 /* Dword offset 0_FC */ +#define ALPHA_Y_INC 0x03F4 /* Dword offset 0_FD */ +#define FOG_Y_INC 0x03F4 /* Dword offset 0_FD */ +#define ALPHA_START 0x03F8 /* Dword offset 0_FE */ +#define FOG_START 0x03F8 /* Dword offset 0_FE */ + +#define OVERLAY_Y_X_START 0x0400 /* Dword offset 1_00 */ +#define OVERLAY_Y_X_END 0x0404 /* Dword offset 1_01 */ +#define OVERLAY_VIDEO_KEY_CLR 0x0408 /* Dword offset 1_02 */ +#define OVERLAY_VIDEO_KEY_MSK 0x040C /* Dword offset 1_03 */ +#define OVERLAY_GRAPHICS_KEY_CLR 0x0410 /* Dword offset 1_04 */ +#define OVERLAY_GRAPHICS_KEY_MSK 0x0414 /* Dword offset 1_05 */ +#define OVERLAY_KEY_CNTL 0x0418 /* Dword offset 1_06 */ + +#define OVERLAY_SCALE_INC 0x0420 /* Dword offset 1_08 */ +#define OVERLAY_SCALE_CNTL 0x0424 /* Dword offset 1_09 */ +#define SCALER_HEIGHT_WIDTH 0x0428 /* Dword offset 1_0A */ +#define SCALER_TEST 0x042C /* Dword offset 1_0B */ +#define SCALER_BUF0_OFFSET 0x0434 /* Dword offset 1_0D */ +#define SCALER_BUF1_OFFSET 0x0438 /* Dword offset 1_0E */ +#define SCALE_BUF_PITCH 0x043C /* Dword offset 1_0F */ + +#define CAPTURE_START_END 0x0440 /* Dword offset 1_10 */ +#define CAPTURE_X_WIDTH 0x0444 /* Dword offset 1_11 */ +#define VIDEO_FORMAT 0x0448 /* Dword offset 1_12 */ +#define VBI_START_END 0x044C /* Dword offset 1_13 */ +#define CAPTURE_CONFIG 0x0450 /* Dword offset 1_14 */ +#define TRIG_CNTL 0x0454 /* Dword offset 1_15 */ + +#define OVERLAY_EXCLUSIVE_HORZ 0x0458 /* Dword offset 1_16 */ +#define OVERLAY_EXCLUSIVE_VERT 0x045C /* Dword offset 1_17 */ + +#define VAL_WIDTH 0x0460 /* Dword offset 1_18 */ +#define CAPTURE_DEBUG 0x0464 /* Dword offset 1_19 */ +#define VIDEO_SYNC_TEST 0x0468 /* Dword offset 1_1A */ + +#define SNAPSHOT_VH_COUNTS 0x0470 /* Dword offset 1_1C */ +#define SNAPSHOT_F_COUNT 0x0474 /* Dword offset 1_1D */ +#define N_VIF_COUNT 0x0478 /* Dword offset 1_1E */ +#define SNAPSHOT_VIF_COUNT 0x047C /* Dword offset 1_1F */ + +#define CAPTURE_BUF0_OFFSET 0x0480 /* Dword offset 1_20 */ +#define CAPTURE_BUF1_OFFSET 0x0484 /* Dword offset 1_21 */ +#define CAPTURE_BUF_PITCH 0x0488 /* Dword offset 1_22 */ + +#define MPP_CONFIG 0x04C0 /* Dword offset 1_30 */ +#define MPP_STROBE_SEQ 0x04C4 /* Dword offset 1_31 */ +#define MPP_ADDR 0x04C8 /* Dword offset 1_32 */ +#define MPP_DATA 0x04CC /* Dword offset 1_33 */ +#define TVO_CNTL 0x0500 /* Dword offset 1_40 */ + +#define CRT_HORZ_VERT_LOAD 0x0544 /* Dword offset 1_51 */ + +#define AGP_BASE 0x0548 /* Dword offset 1_52 */ +#define AGP_CNTL 0x054C /* Dword offset 1_53 */ + +#define SCALER_COLOUR_CNTL 0x0550 /* Dword offset 1_54 */ +#define SCALER_H_COEFF0 0x0554 /* Dword offset 1_55 */ +#define SCALER_H_COEFF1 0x0558 /* Dword offset 1_56 */ +#define SCALER_H_COEFF2 0x055C /* Dword offset 1_57 */ +#define SCALER_H_COEFF3 0x0560 /* Dword offset 1_58 */ +#define SCALER_H_COEFF4 0x0564 /* Dword offset 1_59 */ + +#define GUI_CNTL 0x0578 /* Dword offset 1_5E */ + +#define BM_FRAME_BUF_OFFSET 0x0580 /* Dword offset 1_60 */ +#define BM_SYSTEM_MEM_ADDR 0x0584 /* Dword offset 1_61 */ +#define BM_COMMAND 0x0588 /* Dword offset 1_62 */ +#define BM_STATUS 0x058C /* Dword offset 1_63 */ +#define BM_GUI_TABLE 0x05B8 /* Dword offset 1_6E */ +#define BM_SYSTEM_TABLE 0x05BC /* Dword offset 1_6F */ + +#define SCALER_BUF0_OFFSET_U 0x05D4 /* Dword offset 1_75 */ +#define SCALER_BUF0_OFFSET_V 0x05D8 /* Dword offset 1_76 */ +#define SCALER_BUF1_OFFSET_U 0x05DC /* Dword offset 1_77 */ +#define SCALER_BUF1_OFFSET_V 0x05E0 /* Dword offset 1_78 */ + +#define VERTEX_1_S 0x0640 /* Dword offset 1_90 */ +#define VERTEX_1_T 0x0644 /* Dword offset 1_91 */ +#define VERTEX_1_W 0x0648 /* Dword offset 1_92 */ +#define VERTEX_1_SPEC_ARGB 0x064C /* Dword offset 1_93 */ +#define VERTEX_1_Z 0x0650 /* Dword offset 1_94 */ +#define VERTEX_1_ARGB 0x0654 /* Dword offset 1_95 */ +#define VERTEX_1_X_Y 0x0658 /* Dword offset 1_96 */ +#define ONE_OVER_AREA 0x065C /* Dword offset 1_97 */ +#define VERTEX_2_S 0x0660 /* Dword offset 1_98 */ +#define VERTEX_2_T 0x0664 /* Dword offset 1_99 */ +#define VERTEX_2_W 0x0668 /* Dword offset 1_9A */ +#define VERTEX_2_SPEC_ARGB 0x066C /* Dword offset 1_9B */ +#define VERTEX_2_Z 0x0670 /* Dword offset 1_9C */ +#define VERTEX_2_ARGB 0x0674 /* Dword offset 1_9D */ +#define VERTEX_2_X_Y 0x0678 /* Dword offset 1_9E */ +#define ONE_OVER_AREA 0x065C /* Dword offset 1_9F */ +#define VERTEX_3_S 0x0680 /* Dword offset 1_A0 */ +#define VERTEX_3_T 0x0684 /* Dword offset 1_A1 */ +#define VERTEX_3_W 0x0688 /* Dword offset 1_A2 */ +#define VERTEX_3_SPEC_ARGB 0x068C /* Dword offset 1_A3 */ +#define VERTEX_3_Z 0x0690 /* Dword offset 1_A4 */ +#define VERTEX_3_ARGB 0x0694 /* Dword offset 1_A5 */ +#define VERTEX_3_X_Y 0x0698 /* Dword offset 1_A6 */ +#define ONE_OVER_AREA 0x065C /* Dword offset 1_A7 */ +#define VERTEX_1_S 0x0640 /* Dword offset 1_AB */ +#define VERTEX_1_T 0x0644 /* Dword offset 1_AC */ +#define VERTEX_1_W 0x0648 /* Dword offset 1_AD */ +#define VERTEX_2_S 0x0660 /* Dword offset 1_AE */ +#define VERTEX_2_T 0x0664 /* Dword offset 1_AF */ +#define VERTEX_2_W 0x0668 /* Dword offset 1_B0 */ +#define VERTEX_3_SECONDARY_S 0x06C0 /* Dword offset 1_B0 */ +#define VERTEX_3_S 0x0680 /* Dword offset 1_B1 */ +#define VERTEX_3_SECONDARY_T 0x06C4 /* Dword offset 1_B1 */ +#define VERTEX_3_T 0x0684 /* Dword offset 1_B2 */ +#define VERTEX_3_SECONDARY_W 0x06C8 /* Dword offset 1_B2 */ +#define VERTEX_3_W 0x0688 /* Dword offset 1_B3 */ +#define VERTEX_1_SPEC_ARGB 0x064C /* Dword offset 1_B4 */ +#define VERTEX_2_SPEC_ARGB 0x066C /* Dword offset 1_B5 */ +#define VERTEX_3_SPEC_ARGB 0x068C /* Dword offset 1_B6 */ +#define VERTEX_1_Z 0x0650 /* Dword offset 1_B7 */ +#define VERTEX_2_Z 0x0670 /* Dword offset 1_B8 */ +#define VERTEX_3_Z 0x0690 /* Dword offset 1_B9 */ +#define VERTEX_1_ARGB 0x0654 /* Dword offset 1_BA */ +#define VERTEX_2_ARGB 0x0674 /* Dword offset 1_BB */ +#define VERTEX_3_ARGB 0x0694 /* Dword offset 1_BC */ +#define VERTEX_1_X_Y 0x0658 /* Dword offset 1_BD */ +#define VERTEX_2_X_Y 0x0678 /* Dword offset 1_BE */ +#define VERTEX_3_X_Y 0x0698 /* Dword offset 1_BF */ +#define ONE_OVER_AREA_UC 0x0700 /* Dword offset 1_C0 */ +#define SETUP_CNTL 0x0704 /* Dword offset 1_C1 */ +#define VERTEX_1_SECONDARY_S 0x0728 /* Dword offset 1_CA */ +#define VERTEX_1_SECONDARY_T 0x072C /* Dword offset 1_CB */ +#define VERTEX_1_SECONDARY_W 0x0730 /* Dword offset 1_CC */ +#define VERTEX_2_SECONDARY_S 0x0734 /* Dword offset 1_CD */ +#define VERTEX_2_SECONDARY_T 0x0738 /* Dword offset 1_CE */ +#define VERTEX_2_SECONDARY_W 0x073C /* Dword offset 1_CF */ + + +/* CRTC control values (mostly CRTC_GEN_CNTL) */ + +#define CRTC_H_SYNC_NEG 0x00200000 +#define CRTC_V_SYNC_NEG 0x00200000 + +#define CRTC_DBL_SCAN_EN 0x00000001 +#define CRTC_INTERLACE_EN 0x00000002 +#define CRTC_HSYNC_DIS 0x00000004 +#define CRTC_VSYNC_DIS 0x00000008 +#define CRTC_CSYNC_EN 0x00000010 +#define CRTC_PIX_BY_2_EN 0x00000020 /* unused on RAGE */ +#define CRTC_DISPLAY_DIS 0x00000040 +#define CRTC_VGA_XOVERSCAN 0x00000040 + +#define CRTC_PIX_WIDTH_MASK 0x00000700 +#define CRTC_PIX_WIDTH_4BPP 0x00000100 +#define CRTC_PIX_WIDTH_8BPP 0x00000200 +#define CRTC_PIX_WIDTH_15BPP 0x00000300 +#define CRTC_PIX_WIDTH_16BPP 0x00000400 +#define CRTC_PIX_WIDTH_24BPP 0x00000500 +#define CRTC_PIX_WIDTH_32BPP 0x00000600 + +#define CRTC_BYTE_PIX_ORDER 0x00000800 +#define CRTC_PIX_ORDER_MSN_LSN 0x00000000 +#define CRTC_PIX_ORDER_LSN_MSN 0x00000800 + +#define CRTC_FIFO_LWM 0x000f0000 + +#define VGA_128KAP_PAGING 0x00100000 +#define VFC_SYNC_TRISTATE 0x00200000 +#define CRTC_LOCK_REGS 0x00400000 +#define CRTC_SYNC_TRISTATE 0x00800000 + +#define CRTC_EXT_DISP_EN 0x01000000 +#define CRTC_ENABLE 0x02000000 +#define CRTC_DISP_REQ_ENB 0x04000000 +#define VGA_ATI_LINEAR 0x08000000 +#define CRTC_VSYNC_FALL_EDGE 0x10000000 +#define VGA_TEXT_132 0x20000000 +#define VGA_XCRT_CNT_EN 0x40000000 +#define VGA_CUR_B_TEST 0x80000000 + +#define CRTC_CRNT_VLINE 0x07f00000 +#define CRTC_VBLANK 0x00000001 + + +/* DAC control values */ + +#define DAC_EXT_SEL_RS2 0x01 +#define DAC_EXT_SEL_RS3 0x02 +#define DAC_8BIT_EN 0x00000100 +#define DAC_PIX_DLY_MASK 0x00000600 +#define DAC_PIX_DLY_0NS 0x00000000 +#define DAC_PIX_DLY_2NS 0x00000200 +#define DAC_PIX_DLY_4NS 0x00000400 +#define DAC_BLANK_ADJ_MASK 0x00001800 +#define DAC_BLANK_ADJ_0 0x00000000 +#define DAC_BLANK_ADJ_1 0x00000800 +#define DAC_BLANK_ADJ_2 0x00001000 + + +/* Mix control values */ + +#define MIX_NOT_DST 0x0000 +#define MIX_0 0x0001 +#define MIX_1 0x0002 +#define MIX_DST 0x0003 +#define MIX_NOT_SRC 0x0004 +#define MIX_XOR 0x0005 +#define MIX_XNOR 0x0006 +#define MIX_SRC 0x0007 +#define MIX_NAND 0x0008 +#define MIX_NOT_SRC_OR_DST 0x0009 +#define MIX_SRC_OR_NOT_DST 0x000a +#define MIX_OR 0x000b +#define MIX_AND 0x000c +#define MIX_SRC_AND_NOT_DST 0x000d +#define MIX_NOT_SRC_AND_DST 0x000e +#define MIX_NOR 0x000f + +/* Maximum engine dimensions */ +#define ENGINE_MIN_X 0 +#define ENGINE_MIN_Y 0 +#define ENGINE_MAX_X 4095 +#define ENGINE_MAX_Y 16383 + +/* Mach64 engine bit constants - these are typically ORed together */ + +/* BUS_CNTL register constants */ +#define BUS_FIFO_ERR_ACK 0x00200000 +#define BUS_HOST_ERR_ACK 0x00800000 + +/* GEN_TEST_CNTL register constants */ +#define GEN_OVR_OUTPUT_EN 0x20 +#define HWCURSOR_ENABLE 0x80 +#define GUI_ENGINE_ENABLE 0x100 +#define BLOCK_WRITE_ENABLE 0x200 + +/* DSP_CONFIG register constants */ +#define DSP_XCLKS_PER_QW 0x00003fff +#define DSP_LOOP_LATENCY 0x000f0000 +#define DSP_PRECISION 0x00700000 + +/* DSP_ON_OFF register constants */ +#define DSP_OFF 0x000007ff +#define DSP_ON 0x07ff0000 + +/* CLOCK_CNTL register constants */ +#define CLOCK_SEL 0x0f +#define CLOCK_DIV 0x30 +#define CLOCK_DIV1 0x00 +#define CLOCK_DIV2 0x10 +#define CLOCK_DIV4 0x20 +#define CLOCK_STROBE 0x40 +#define PLL_WR_EN 0x02 + +/* PLL registers */ +#define PLL_MACRO_CNTL 0x01 +#define PLL_REF_DIV 0x02 +#define PLL_GEN_CNTL 0x03 +#define MCLK_FB_DIV 0x04 +#define PLL_VCLK_CNTL 0x05 +#define VCLK_POST_DIV 0x06 +#define VCLK0_FB_DIV 0x07 +#define VCLK1_FB_DIV 0x08 +#define VCLK2_FB_DIV 0x09 +#define VCLK3_FB_DIV 0x0A +#define PLL_XCLK_CNTL 0x0B +#define PLL_TEST_CTRL 0x0E +#define PLL_TEST_COUNT 0x0F + +/* Fields in PLL registers */ +#define PLL_PC_GAIN 0x07 +#define PLL_VC_GAIN 0x18 +#define PLL_DUTY_CYC 0xE0 +#define PLL_OVERRIDE 0x01 +#define PLL_MCLK_RST 0x02 +#define OSC_EN 0x04 +#define EXT_CLK_EN 0x08 +#define MCLK_SRC_SEL 0x70 +#define EXT_CLK_CNTL 0x80 +#define VCLK_SRC_SEL 0x03 +#define PLL_VCLK_RST 0x04 +#define VCLK_INVERT 0x08 +#define VCLK0_POST 0x03 +#define VCLK1_POST 0x0C +#define VCLK2_POST 0x30 +#define VCLK3_POST 0xC0 + +/* CONFIG_CNTL register constants */ +#define APERTURE_4M_ENABLE 1 +#define APERTURE_8M_ENABLE 2 +#define VGA_APERTURE_ENABLE 4 + +/* CONFIG_STAT0 register constants (GX, CX) */ +#define CFG_BUS_TYPE 0x00000007 +#define CFG_MEM_TYPE 0x00000038 +#define CFG_INIT_DAC_TYPE 0x00000e00 + +/* CONFIG_STAT0 register constants (CT, ET, VT) */ +#define CFG_MEM_TYPE_xT 0x00000007 + +#define ISA 0 +#define EISA 1 +#define LOCAL_BUS 6 +#define PCI 7 + +/* Memory types for GX, CX */ +#define DRAMx4 0 +#define VRAMx16 1 +#define VRAMx16ssr 2 +#define DRAMx16 3 +#define GraphicsDRAMx16 4 +#define EnhancedVRAMx16 5 +#define EnhancedVRAMx16ssr 6 + +/* Memory types for CT, ET, VT, GT */ +#define DRAM 1 +#define EDO 2 +#define PSEUDO_EDO 3 +#define SDRAM 4 +#define SGRAM 5 +#define WRAM 6 + +#define DAC_INTERNAL 0x00 +#define DAC_IBMRGB514 0x01 +#define DAC_ATI68875 0x02 +#define DAC_TVP3026_A 0x72 +#define DAC_BT476 0x03 +#define DAC_BT481 0x04 +#define DAC_ATT20C491 0x14 +#define DAC_SC15026 0x24 +#define DAC_MU9C1880 0x34 +#define DAC_IMSG174 0x44 +#define DAC_ATI68860_B 0x05 +#define DAC_ATI68860_C 0x15 +#define DAC_TVP3026_B 0x75 +#define DAC_STG1700 0x06 +#define DAC_ATT498 0x16 +#define DAC_STG1702 0x07 +#define DAC_SC15021 0x17 +#define DAC_ATT21C498 0x27 +#define DAC_STG1703 0x37 +#define DAC_CH8398 0x47 +#define DAC_ATT20C408 0x57 + +#define CLK_ATI18818_0 0 +#define CLK_ATI18818_1 1 +#define CLK_STG1703 2 +#define CLK_CH8398 3 +#define CLK_INTERNAL 4 +#define CLK_ATT20C408 5 +#define CLK_IBMRGB514 6 + +/* MEM_CNTL register constants */ +#define MEM_SIZE_ALIAS 0x00000007 +#define MEM_SIZE_512K 0x00000000 +#define MEM_SIZE_1M 0x00000001 +#define MEM_SIZE_2M 0x00000002 +#define MEM_SIZE_4M 0x00000003 +#define MEM_SIZE_6M 0x00000004 +#define MEM_SIZE_8M 0x00000005 +#define MEM_SIZE_ALIAS_GTB 0x0000000F +#define MEM_SIZE_2M_GTB 0x00000003 +#define MEM_SIZE_4M_GTB 0x00000007 +#define MEM_SIZE_6M_GTB 0x00000009 +#define MEM_SIZE_8M_GTB 0x0000000B +#define MEM_BNDRY 0x00030000 +#define MEM_BNDRY_0K 0x00000000 +#define MEM_BNDRY_256K 0x00010000 +#define MEM_BNDRY_512K 0x00020000 +#define MEM_BNDRY_1M 0x00030000 +#define MEM_BNDRY_EN 0x00040000 + +/* ATI PCI constants */ +#define PCI_ATI_VENDOR_ID 0x1002 +#define PCI_MACH64_GX 0x4758 +#define PCI_MACH64_CX 0x4358 +#define PCI_MACH64_CT 0x4354 +#define PCI_MACH64_ET 0x4554 +#define PCI_MACH64_VT 0x5654 +#define PCI_MACH64_GT 0x4754 + +/* CONFIG_CHIP_ID register constants */ +#define CFG_CHIP_TYPE 0x0000FFFF +#define CFG_CHIP_CLASS 0x00FF0000 +#define CFG_CHIP_REV 0xFF000000 +#define CFG_CHIP_VERSION 0x07000000 +#define CFG_CHIP_FOUNDRY 0x38000000 +#define CFG_CHIP_REVISION 0xC0000000 + +/* Chip IDs read from CONFIG_CHIP_ID */ +#define MACH64_GX_ID 0xD7 +#define MACH64_CX_ID 0x57 +#define MACH64_CT_ID 0x4354 +#define MACH64_ET_ID 0x4554 +#define MACH64_VT_ID 0x5654 +#define MACH64_GT_ID 0x4754 + +/* Mach64 chip types */ +#define MACH64_UNKNOWN 0 +#define MACH64_GX 1 +#define MACH64_CX 2 +#define MACH64_CT 3 +#define MACH64_ET 4 +#define MACH64_VT 5 +#define MACH64_GT 6 + +/* DST_CNTL register constants */ +#define DST_X_RIGHT_TO_LEFT 0 +#define DST_X_LEFT_TO_RIGHT 1 +#define DST_Y_BOTTOM_TO_TOP 0 +#define DST_Y_TOP_TO_BOTTOM 2 +#define DST_X_MAJOR 0 +#define DST_Y_MAJOR 4 +#define DST_X_TILE 8 +#define DST_Y_TILE 0x10 +#define DST_LAST_PEL 0x20 +#define DST_POLYGON_ENABLE 0x40 +#define DST_24_ROTATION_ENABLE 0x80 + +/* SRC_CNTL register constants */ +#define SRC_PATTERN_ENABLE 1 +#define SRC_ROTATION_ENABLE 2 +#define SRC_LINEAR_ENABLE 4 +#define SRC_BYTE_ALIGN 8 +#define SRC_LINE_X_RIGHT_TO_LEFT 0 +#define SRC_LINE_X_LEFT_TO_RIGHT 0x10 + +/* HOST_CNTL register constants */ +#define HOST_BYTE_ALIGN 1 + +/* GUI_TRAJ_CNTL register constants */ +#define PAT_MONO_8x8_ENABLE 0x01000000 +#define PAT_CLR_4x2_ENABLE 0x02000000 +#define PAT_CLR_8x1_ENABLE 0x04000000 + +/* DP_CHAIN_MASK register constants */ +#define DP_CHAIN_4BPP 0x8888 +#define DP_CHAIN_7BPP 0xD2D2 +#define DP_CHAIN_8BPP 0x8080 +#define DP_CHAIN_8BPP_RGB 0x9292 +#define DP_CHAIN_15BPP 0x4210 +#define DP_CHAIN_16BPP 0x8410 +#define DP_CHAIN_24BPP 0x8080 +#define DP_CHAIN_32BPP 0x8080 + +/* DP_PIX_WIDTH register constants */ +#define DST_1BPP 0 +#define DST_4BPP 1 +#define DST_8BPP 2 +#define DST_15BPP 3 +#define DST_16BPP 4 +#define DST_32BPP 6 +#define SRC_1BPP 0 +#define SRC_4BPP 0x100 +#define SRC_8BPP 0x200 +#define SRC_15BPP 0x300 +#define SRC_16BPP 0x400 +#define SRC_32BPP 0x600 +#define HOST_1BPP 0 +#define HOST_4BPP 0x10000 +#define HOST_8BPP 0x20000 +#define HOST_15BPP 0x30000 +#define HOST_16BPP 0x40000 +#define HOST_32BPP 0x60000 +#define BYTE_ORDER_MSB_TO_LSB 0 +#define BYTE_ORDER_LSB_TO_MSB 0x1000000 + +/* DP_MIX register constants */ +#define BKGD_MIX_NOT_D 0 +#define BKGD_MIX_ZERO 1 +#define BKGD_MIX_ONE 2 +#define BKGD_MIX_D 3 +#define BKGD_MIX_NOT_S 4 +#define BKGD_MIX_D_XOR_S 5 +#define BKGD_MIX_NOT_D_XOR_S 6 +#define BKGD_MIX_S 7 +#define BKGD_MIX_NOT_D_OR_NOT_S 8 +#define BKGD_MIX_D_OR_NOT_S 9 +#define BKGD_MIX_NOT_D_OR_S 10 +#define BKGD_MIX_D_OR_S 11 +#define BKGD_MIX_D_AND_S 12 +#define BKGD_MIX_NOT_D_AND_S 13 +#define BKGD_MIX_D_AND_NOT_S 14 +#define BKGD_MIX_NOT_D_AND_NOT_S 15 +#define BKGD_MIX_D_PLUS_S_DIV2 0x17 +#define FRGD_MIX_NOT_D 0 +#define FRGD_MIX_ZERO 0x10000 +#define FRGD_MIX_ONE 0x20000 +#define FRGD_MIX_D 0x30000 +#define FRGD_MIX_NOT_S 0x40000 +#define FRGD_MIX_D_XOR_S 0x50000 +#define FRGD_MIX_NOT_D_XOR_S 0x60000 +#define FRGD_MIX_S 0x70000 +#define FRGD_MIX_NOT_D_OR_NOT_S 0x80000 +#define FRGD_MIX_D_OR_NOT_S 0x90000 +#define FRGD_MIX_NOT_D_OR_S 0xa0000 +#define FRGD_MIX_D_OR_S 0xb0000 +#define FRGD_MIX_D_AND_S 0xc0000 +#define FRGD_MIX_NOT_D_AND_S 0xd0000 +#define FRGD_MIX_D_AND_NOT_S 0xe0000 +#define FRGD_MIX_NOT_D_AND_NOT_S 0xf0000 +#define FRGD_MIX_D_PLUS_S_DIV2 0x170000 + +/* DP_SRC register constants */ +#define BKGD_SRC_BKGD_CLR 0 +#define BKGD_SRC_FRGD_CLR 1 +#define BKGD_SRC_HOST 2 +#define BKGD_SRC_BLIT 3 +#define BKGD_SRC_PATTERN 4 +#define FRGD_SRC_BKGD_CLR 0 +#define FRGD_SRC_FRGD_CLR 0x100 +#define FRGD_SRC_HOST 0x200 +#define FRGD_SRC_BLIT 0x300 +#define FRGD_SRC_PATTERN 0x400 +#define MONO_SRC_ONE 0 +#define MONO_SRC_PATTERN 0x10000 +#define MONO_SRC_HOST 0x20000 +#define MONO_SRC_BLIT 0x30000 + +/* CLR_CMP_CNTL register constants */ +#define COMPARE_FALSE 0 +#define COMPARE_TRUE 1 +#define COMPARE_NOT_EQUAL 4 +#define COMPARE_EQUAL 5 +#define COMPARE_DESTINATION 0 +#define COMPARE_SOURCE 0x1000000 + +/* FIFO_STAT register constants */ +#define FIFO_ERR 0x80000000 + +/* CONTEXT_LOAD_CNTL constants */ +#define CONTEXT_NO_LOAD 0 +#define CONTEXT_LOAD 0x10000 +#define CONTEXT_LOAD_AND_DO_FILL 0x20000 +#define CONTEXT_LOAD_AND_DO_LINE 0x30000 +#define CONTEXT_EXECUTE 0 +#define CONTEXT_CMD_DISABLE 0x80000000 + +/* GUI_STAT register constants */ +#define ENGINE_IDLE 0 +#define ENGINE_BUSY 1 +#define SCISSOR_LEFT_FLAG 0x10 +#define SCISSOR_RIGHT_FLAG 0x20 +#define SCISSOR_TOP_FLAG 0x40 +#define SCISSOR_BOTTOM_FLAG 0x80 + +/* ATI VGA Extended Regsiters */ +#define sioATIEXT 0x1ce +#define bioATIEXT 0x3ce + +#define ATI2E 0xae +#define ATI32 0xb2 +#define ATI36 0xb6 + +/* VGA Graphics Controller Registers */ +#define VGAGRA 0x3ce +#define GRA06 0x06 + +/* VGA Seququencer Registers */ +#define VGASEQ 0x3c4 +#define SEQ02 0x02 +#define SEQ04 0x04 + +#define MACH64_MAX_X ENGINE_MAX_X +#define MACH64_MAX_Y ENGINE_MAX_Y + +#define INC_X 0x0020 +#define INC_Y 0x0080 + +#define RGB16_555 0x0000 +#define RGB16_565 0x0040 +#define RGB16_655 0x0080 +#define RGB16_664 0x00c0 + +#define POLY_TEXT_TYPE 0x0001 +#define IMAGE_TEXT_TYPE 0x0002 +#define TEXT_TYPE_8_BIT 0x0004 +#define TEXT_TYPE_16_BIT 0x0008 +#define POLY_TEXT_TYPE_8 (POLY_TEXT_TYPE | TEXT_TYPE_8_BIT) +#define IMAGE_TEXT_TYPE_8 (IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT) +#define POLY_TEXT_TYPE_16 (POLY_TEXT_TYPE | TEXT_TYPE_16_BIT) +#define IMAGE_TEXT_TYPE_16 (IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT) + +#define MACH64_NUM_CLOCKS 16 +#define MACH64_NUM_FREQS 50 + +/* Wait until "v" queue entries are free */ +#define aty_WaitQueue(v) { while ((aty_ld_le32(FIFO_STAT) & 0xffff) > \ + ((unsigned short)(0x8000 >> (v)))); } + +/* Wait until GP is idle and queue is empty */ +#define aty_WaitIdleEmpty() { aty_WaitQueue(16); \ + while ((aty_ld_le32(GUI_STAT) & 1) != 0); } + +#define SKIP_2(_v) ((((_v)<<1)&0xfff8)|((_v)&0x3)|(((_v)&0x80)>>5)) + +#define MACH64_BIT_BLT(_srcx, _srcy, _dstx, _dsty, _w, _h, _dir) \ +{ \ + aty_WaitQueue(5); \ + aty_st_le32(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \ + aty_st_le32(SRC_WIDTH1, (_w)); \ + aty_st_le32(DST_CNTL, (_dir)); \ + aty_st_le32(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \ + aty_st_le32(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \ +} +#endif /* REGMACH64_H */ + diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c new file mode 100644 index 000000000..e1f8b3ef4 --- /dev/null +++ b/drivers/video/atyfb.c @@ -0,0 +1,1685 @@ +/* + * linux/drivers/video/atyfb.c -- Frame buffer device for ATI/Open Firmware + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This driver is partly based on the PowerMac console driver: + * + * Copyright (C) 1996 Paul Mackerras + * + * and on the PowerMac ATI/mach64 display driver: + * + * Copyright (C) 1997 Michael AK Tesch + * + * with work by Jon Howell + * Harry AC Eaton + * Anthony Tong <atong@uiuc.edu> + * + * 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. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/selection.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/nvram.h> +#include <linux/vc_ioctl.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +#include "aty.h" +#include "fbcon.h" +#include "fbcon-cfb8.h" +#include "fbcon-cfb16.h" +#include "fbcon-cfb32.h" + + +static int currcon = 0; +static struct display fb_disp; +static struct fb_info fb_info; +static struct { u_char red, green, blue, pad; } palette[256]; + +static char atyfb_name[16] = "ATY Mach64"; + +struct atyfb_par { + int vmode; + int cmode; + u_int vxres; /* virtual screen size */ + u_int vyres; + int xoffset; /* virtual screen position */ + int yoffset; +}; + + +/* + * Video mode values. + * These are supposed to be the same as the values that + * Apple uses in MacOS. + */ +#define VMODE_NVRAM 0 /* use value stored in nvram */ +#define VMODE_512_384_60I 1 /* 512x384, 60Hz interlaced (NTSC) */ +#define VMODE_512_384_60 2 /* 512x384, 60Hz */ +#define VMODE_640_480_50I 3 /* 640x480, 50Hz interlaced (PAL) */ +#define VMODE_640_480_60I 4 /* 640x480, 60Hz interlaced (NTSC) */ +#define VMODE_640_480_60 5 /* 640x480, 60Hz (VGA) */ +#define VMODE_640_480_67 6 /* 640x480, 67Hz */ +#define VMODE_640_870_75P 7 /* 640x870, 75Hz (portrait) */ +#define VMODE_768_576_50I 8 /* 768x576, 50Hz (PAL full frame) */ +#define VMODE_800_600_56 9 /* 800x600, 56Hz */ +#define VMODE_800_600_60 10 /* 800x600, 60Hz */ +#define VMODE_800_600_72 11 /* 800x600, 72Hz */ +#define VMODE_800_600_75 12 /* 800x600, 75Hz */ +#define VMODE_832_624_75 13 /* 832x624, 75Hz */ +#define VMODE_1024_768_60 14 /* 1024x768, 60Hz */ +#define VMODE_1024_768_70 15 /* 1024x768, 70Hz (or 72Hz?) */ +#define VMODE_1024_768_75V 16 /* 1024x768, 75Hz (VESA) */ +#define VMODE_1024_768_75 17 /* 1024x768, 75Hz */ +#define VMODE_1152_870_75 18 /* 1152x870, 75Hz */ +#define VMODE_1280_960_75 19 /* 1280x960, 75Hz */ +#define VMODE_1280_1024_75 20 /* 1280x1024, 75Hz */ +#define VMODE_MAX 20 +#define VMODE_CHOOSE 99 /* choose based on monitor sense */ + +/* + * Color mode values, used to select number of bits/pixel. + */ +#define CMODE_NVRAM -1 /* use value stored in nvram */ +#define CMODE_8 0 /* 8 bits/pixel */ +#define CMODE_16 1 /* 16 (actually 15) bits/pixel */ +#define CMODE_32 2 /* 32 (actually 24) bits/pixel */ + + +static int default_video_mode = VMODE_NVRAM; +static int default_color_mode = CMODE_NVRAM; + +static struct atyfb_par default_par; +static struct atyfb_par current_par; + + +/* + * Addresses in NVRAM where video mode and pixel size are stored. + */ +#define NV_VMODE 0x140f +#define NV_CMODE 0x1410 + +/* + * Horizontal and vertical resolution information. + */ +extern struct vmode_attr { + int hres; + int vres; + int vfreq; + int interlaced; +} vmode_attrs[VMODE_MAX]; + + +/* + * Horizontal and vertical resolution for each mode. + */ +static struct vmode_attr vmode_attrs[VMODE_MAX] = { + {512, 384, 60, 1}, + {512, 384, 60}, + {640, 480, 50, 1}, + {640, 480, 60, 1}, + {640, 480, 60}, + {640, 480, 67}, + {640, 870, 75}, + {768, 576, 50, 1}, + {800, 600, 56}, + {800, 600, 60}, + {800, 600, 72}, + {800, 600, 75}, + {832, 624, 75}, + {1024, 768, 60}, + {1024, 768, 72}, + {1024, 768, 75}, + {1024, 768, 75}, + {1152, 870, 75}, + {1280, 960, 75}, + {1280, 1024, 75} +}; + + +/* + * We get a sense value from the monitor and use it to choose + * what resolution to use. This structure maps sense values + * to display mode values (which determine the resolution and + * frequencies). + */ +static struct mon_map { + int sense; + int vmode; +} monitor_map [] = { + {0x000, VMODE_1280_1024_75}, /* 21" RGB */ + {0x114, VMODE_640_870_75P}, /* Portrait Monochrome */ + {0x221, VMODE_512_384_60}, /* 12" RGB*/ + {0x331, VMODE_1280_1024_75}, /* 21" RGB (Radius) */ + {0x334, VMODE_1280_1024_75}, /* 21" mono (Radius) */ + {0x335, VMODE_1280_1024_75}, /* 21" mono */ + {0x40A, VMODE_640_480_60I}, /* NTSC */ + {0x51E, VMODE_640_870_75P}, /* Portrait RGB */ + {0x603, VMODE_832_624_75}, /* 12"-16" multiscan */ + {0x60b, VMODE_1024_768_70}, /* 13"-19" multiscan */ + {0x623, VMODE_1152_870_75}, /* 13"-21" multiscan */ + {0x62b, VMODE_640_480_67}, /* 13"/14" RGB */ + {0x700, VMODE_640_480_50I}, /* PAL */ + {0x714, VMODE_640_480_60I}, /* NTSC */ + {0x717, VMODE_800_600_75}, /* VGA */ + {0x72d, VMODE_832_624_75}, /* 16" RGB (Goldfish) */ + {0x730, VMODE_768_576_50I}, /* PAL (Alternate) */ + {0x73a, VMODE_1152_870_75}, /* 3rd party 19" */ + {-1, VMODE_640_480_60}, /* catch-all, must be last */ +}; + +static int map_monitor_sense(int sense) +{ + struct mon_map *map; + + for (map = monitor_map; map->sense >= 0; ++map) + if (map->sense == sense) + break; + return map->vmode; +} + +struct aty_cmap_regs { + unsigned char windex; + unsigned char lut; + unsigned char mask; + unsigned char rindex; + unsigned char cntl; +}; + +typedef struct aty_regvals { + int offset[3]; /* first pixel address */ + + int crtc_h_sync_strt_wid[3]; /* depth dependant */ + int crtc_gen_cntl[3]; + int mem_cntl[3]; + + int crtc_h_tot_disp; /* mode dependant */ + int crtc_v_tot_disp; + int crtc_v_sync_strt_wid; + int crtc_off_pitch; + + unsigned char clock_val[2]; /* vals for 20 and 21 */ +} aty_regvals; + +struct rage_regvals { + int h_total, h_sync_start, h_sync_width; + int v_total, v_sync_start, v_sync_width; + int h_sync_neg, v_sync_neg; +}; + +static int aty_vram_reqd(const struct atyfb_par *par); +static struct aty_regvals *get_aty_struct(int vmode); + +static unsigned long frame_buffer; + +static int total_vram; /* total amount of video memory, bytes */ +static int chip_type; /* what chip type was detected */ + +static unsigned long ati_regbase; +static struct aty_cmap_regs *aty_cmap_regs; + +#include "ati-gx.h" +#include "ati-gt.h" +#include "ati-vt.h" + +static struct aty_regvals *aty_gt_reg_init[20] = { + NULL, NULL, NULL, NULL, + &aty_gt_reg_init_5, + &aty_gt_reg_init_6, + NULL, NULL, + &aty_gt_reg_init_9, + &aty_gt_reg_init_10, + &aty_gt_reg_init_11, + &aty_gt_reg_init_12, + &aty_gt_reg_init_13, + &aty_gt_reg_init_14, + &aty_gt_reg_init_15, + NULL, + &aty_gt_reg_init_17, + &aty_gt_reg_init_18, + NULL, + &aty_gt_reg_init_20 +}; + +static struct aty_regvals *aty_gx_reg_init[20] = { + NULL, NULL, NULL, NULL, + &aty_gx_reg_init_6, + &aty_gx_reg_init_6, + NULL, NULL, NULL, NULL, NULL, NULL, + &aty_gx_reg_init_13, + &aty_gx_reg_init_14, + &aty_gx_reg_init_15, + NULL, + &aty_gx_reg_init_17, + &aty_gx_reg_init_18, + NULL, + &aty_gx_reg_init_20 +}; + +static struct aty_regvals *aty_vt_reg_init[21] = { + NULL, NULL, NULL, NULL, + &aty_vt_reg_init_5, + &aty_vt_reg_init_6, + NULL, NULL, NULL, + &aty_vt_reg_init_10, + &aty_vt_reg_init_11, + &aty_vt_reg_init_12, + &aty_vt_reg_init_13, + &aty_vt_reg_init_14, + &aty_vt_reg_init_15, + NULL, + &aty_vt_reg_init_17, + &aty_vt_reg_init_18, + &aty_vt_reg_init_19, + &aty_vt_reg_init_20 +}; + + /* + * Interface used by the world + */ + +unsigned long atyfb_init(unsigned long mem_start); +void atyfb_setup(char *options, int *ints); + +static int atyfb_open(struct fb_info *info); +static int atyfb_release(struct fb_info *info); +static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int atyfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int atyfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int atyfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + + + /* + * Interface to the low level console driver + */ + +static int atyfbcon_switch(int con, struct fb_info *info); +static int atyfbcon_updatevar(int con, struct fb_info *info); +static void atyfbcon_blank(int blank, struct fb_info *info); + + + /* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static struct display_switch fbcon_aty8; +#endif + + +#ifdef CONFIG_FB_COMPAT_XPMAC +extern struct vc_mode display_info; +extern struct fb_info *console_fb_info; +extern int (*console_setmode_ptr)(struct vc_mode *, int); +extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int, + struct fb_info *); +static int atyfb_console_setmode(struct vc_mode *, int); +#endif + + + /* + * Internal routines + */ + +static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); +static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static void do_install_cmap(int con, struct fb_info *info); + + +static struct fb_ops atyfb_ops = { + atyfb_open, atyfb_release, atyfb_get_fix, atyfb_get_var, atyfb_set_var, + atyfb_get_cmap, atyfb_set_cmap, atyfb_pan_display, NULL, atyfb_ioctl +}; + + +static inline int aty_vram_reqd(const struct atyfb_par *par) +{ + return (par->vxres*par->vyres) << par->cmode; +} + +extern inline unsigned aty_ld_le32(volatile unsigned long addr) +{ + register unsigned long temp = ati_regbase,val; + + asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp)); + return val; +} + +extern inline void aty_st_le32(volatile unsigned long addr, unsigned val) +{ + register unsigned long temp = ati_regbase; + + asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory"); +} + +extern inline unsigned char aty_ld_8(volatile unsigned long addr) +{ + return *(char *) ((long) addr + (long) ati_regbase); +} + +extern inline void aty_st_8(volatile unsigned long addr, unsigned char val) +{ + *(unsigned char *) (addr + (unsigned long) ati_regbase) = val; +} + +static void aty_st_514(int offset, char val) +{ + aty_WaitQueue(5); + aty_st_8(DAC_CNTL, 1); + aty_st_8(DAC_W_INDEX, offset & 0xff); /* right addr byte */ + aty_st_8(DAC_DATA, (offset >> 8) & 0xff); /* left addr byte */ + eieio(); + aty_st_8(DAC_MASK, val); + eieio(); + aty_st_8(DAC_CNTL, 0); +} + +static void aty_st_pll(int offset, char val) +{ + aty_WaitQueue(3); + aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN); /* write addr byte */ + eieio(); + aty_st_8(CLOCK_CNTL + 2, val); /* write the register value */ + eieio(); + aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN); +} + +static struct aty_regvals *get_aty_struct(int vmode) +{ + int v = vmode - 1; + + switch (chip_type) { + case MACH64_GT_ID: + return aty_gt_reg_init[v]; + break; + case MACH64_VT_ID: + return aty_vt_reg_init[v]; + break; + default: /* default to MACH64_GX_ID */ + return aty_gx_reg_init[v]; + break; + } +} + +static int read_aty_sense(void) +{ + int sense, i; + + aty_st_le32(GP_IO, 0x31003100); /* drive outputs high */ + __delay(200); + aty_st_le32(GP_IO, 0); /* turn off outputs */ + __delay(2000); + i = aty_ld_le32(GP_IO); /* get primary sense value */ + sense = ((i & 0x3000) >> 3) | (i & 0x100); + + /* drive each sense line low in turn and collect the other 2 */ + aty_st_le32(GP_IO, 0x20000000); /* drive A low */ + __delay(2000); + i = aty_ld_le32(GP_IO); + sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4); + aty_st_le32(GP_IO, 0x20002000); /* drive A high again */ + __delay(200); + + aty_st_le32(GP_IO, 0x10000000); /* drive B low */ + __delay(2000); + i = aty_ld_le32(GP_IO); + sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6); + aty_st_le32(GP_IO, 0x10001000); /* drive B high again */ + __delay(200); + + aty_st_le32(GP_IO, 0x01000000); /* drive C low */ + __delay(2000); + sense |= (aty_ld_le32(GP_IO) & 0x3000) >> 12; + aty_st_le32(GP_IO, 0); /* turn off outputs */ + + return sense; +} + +static void RGB514_Program(int cmode) +{ + typedef struct { + char pixel_dly; + char misc2_cntl; + char pixel_rep; + char pixel_cntl_index; + char pixel_cntl_v1; + } RGB514_DAC_Table; + + static RGB514_DAC_Table RGB514DAC_Tab[8] = { + {0, 0x41, 0x03, 0x71, 0x45}, // 8bpp + {0, 0x45, 0x04, 0x0c, 0x01}, // 555 + {0, 0x45, 0x06, 0x0e, 0x00}, // XRGB + }; + RGB514_DAC_Table *pDacProgTab; + + pDacProgTab = &RGB514DAC_Tab[cmode]; + + aty_st_514(0x90, 0x00); + aty_st_514(0x04, pDacProgTab->pixel_dly); + aty_st_514(0x05, 0x00); + + aty_st_514(0x2, 0x1); + aty_st_514(0x71, pDacProgTab->misc2_cntl); + aty_st_514(0x0a, pDacProgTab->pixel_rep); + + aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1); +} + +static void set_off_pitch(const struct atyfb_par *par) +{ + u32 pitch, offset; + + pitch = par->vxres>>3; + offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->cmode; + aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset); + if (chip_type == MACH64_GT_ID) { + /* Is this OK for other chips? */ + aty_st_le32(DST_OFF_PITCH, pitch<<22 | offset); + aty_st_le32(SRC_OFF_PITCH, pitch<<22 | offset); + } +} + +static void atyfb_set_par(struct atyfb_par *par) +{ + int i, hres; + struct aty_regvals *init = get_aty_struct(par->vmode); + int vram_type = aty_ld_le32(CONFIG_STAT0) & 7; + + if (init == 0) /* paranoia, shouldn't get here */ + panic("aty: display mode %d not supported", par->vmode); + + current_par = *par; + hres = vmode_attrs[par->vmode-1].hres; + + /* clear FIFO errors */ + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK + | BUS_FIFO_ERR_ACK); + + /* Reset engine */ + i = aty_ld_le32(GEN_TEST_CNTL); + aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE); + eieio(); + aty_WaitIdleEmpty(); + aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE); + aty_WaitIdleEmpty(); + + if ( chip_type != MACH64_GT_ID ) { + i = aty_ld_le32(CRTC_GEN_CNTL); + aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN); + } + + if ( chip_type == MACH64_GX_ID ) { + i = aty_ld_le32(GEN_TEST_CNTL); + aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN ); + } + + switch (chip_type) { + case MACH64_VT_ID: + aty_st_pll(PLL_MACRO_CNTL, 0xb5); + aty_st_pll(PLL_REF_DIV, 0x2d); + aty_st_pll(PLL_GEN_CNTL, 0x14); + aty_st_pll(MCLK_FB_DIV, 0xbd); + aty_st_pll(PLL_VCLK_CNTL, 0x0b); + aty_st_pll(VCLK_POST_DIV, init->clock_val[0]); + aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]); + aty_st_pll(VCLK1_FB_DIV, 0xd6); + aty_st_pll(VCLK2_FB_DIV, 0xee); + aty_st_pll(VCLK3_FB_DIV, 0xf8); + aty_st_pll(PLL_XCLK_CNTL, 0x0); + aty_st_pll(PLL_TEST_CTRL, 0x0); + aty_st_pll(PLL_TEST_COUNT, 0x0); + break; + case MACH64_GT_ID: + if (vram_type == 5) { + aty_st_pll(0, 0xcd); + aty_st_pll(PLL_MACRO_CNTL, + par->vmode >= VMODE_1024_768_60 ? 0xd3: 0xd5); + aty_st_pll(PLL_REF_DIV, 0x21); + aty_st_pll(PLL_GEN_CNTL, 0x44); + aty_st_pll(MCLK_FB_DIV, 0xe8); + aty_st_pll(PLL_VCLK_CNTL, 0x03); + aty_st_pll(VCLK_POST_DIV, init->offset[0]); + aty_st_pll(VCLK0_FB_DIV, init->offset[1]); + aty_st_pll(VCLK1_FB_DIV, 0x8e); + aty_st_pll(VCLK2_FB_DIV, 0x9e); + aty_st_pll(VCLK3_FB_DIV, 0xc6); + aty_st_pll(PLL_XCLK_CNTL, init->offset[2]); + aty_st_pll(12, 0xa6); + aty_st_pll(13, 0x1b); + } else { + aty_st_pll(PLL_MACRO_CNTL, 0xd5); + aty_st_pll(PLL_REF_DIV, 0x21); + aty_st_pll(PLL_GEN_CNTL, 0xc4); + aty_st_pll(MCLK_FB_DIV, 0xda); + aty_st_pll(PLL_VCLK_CNTL, 0x03); + /* offset actually holds clock values */ + aty_st_pll(VCLK_POST_DIV, init->offset[0]); + aty_st_pll(VCLK0_FB_DIV, init->offset[1]); + aty_st_pll(VCLK1_FB_DIV, 0x8e); + aty_st_pll(VCLK2_FB_DIV, 0x9e); + aty_st_pll(VCLK3_FB_DIV, 0xc6); + aty_st_pll(PLL_TEST_CTRL, 0x0); + aty_st_pll(PLL_XCLK_CNTL, init->offset[2]); + aty_st_pll(12, 0xa0); + aty_st_pll(13, 0x1b); + } + break; + default: + RGB514_Program(par->cmode); + aty_WaitIdleEmpty(); + aty_st_514(0x06, 0x02); + aty_st_514(0x10, 0x01); + aty_st_514(0x70, 0x01); + aty_st_514(0x8f, 0x1f); + aty_st_514(0x03, 0x00); + aty_st_514(0x05, 0x00); + aty_st_514(0x20, init->clock_val[0]); + aty_st_514(0x21, init->clock_val[1]); + break; + } + + aty_ld_8(DAC_REGS); /* clear counter */ + aty_WaitIdleEmpty(); + + aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp); + aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[par->cmode]); + aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp); + aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid); + + aty_st_8(CLOCK_CNTL, 0); + aty_st_8(CLOCK_CNTL, CLOCK_STROBE); + + aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0); + + set_off_pitch(par); + + if (chip_type == MACH64_GT_ID) { + aty_st_le32(BUS_CNTL, 0x7b23a040); + + /* need to set DSP values !! assume sdram */ + i = init->crtc_gen_cntl[0] - (0x100000 * par->cmode); + if ( vram_type == 5 ) + i = init->crtc_gen_cntl[1] - (0x100000 * par->cmode); + aty_st_le32(DSP_CONFIG, i); + + i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS; + if ( vram_type == 5 ) { + i |= ((1 * par->cmode) << 26) | 0x4215b0; + aty_st_le32(DSP_ON_OFF,sgram_dsp[par->vmode-1][par->cmode]); + + //aty_st_le32(CLOCK_CNTL,8192); + } else { + i |= ((1 * par->cmode) << 26) | 0x300090; + aty_st_le32(DSP_ON_OFF, init->mem_cntl[par->cmode]); + } + + aty_st_le32(MEM_CNTL, i); + aty_st_le32(EXT_MEM_CNTL, 0x5000001); + + /* if (total_vram > 0x400000) + i |= 0x538; this not been verified on > 4Megs!! */ + } else { + +/* The magic constant below translates into: +* 5 = No RDY delay, 1 wait st for mem write, increment during burst transfer +* 9 = DAC access delayed, 1 wait state for DAC +* 0 = Disables interupts for FIFO errors +* e = Allows FIFO to generate 14 wait states before generating error +* 1 = DAC snooping disabled, ROM disabled +* 0 = ROM page at 0 (disabled so doesn't matter) +* f = 15 ROM wait states (disabled so doesn't matter) +* f = 15 BUS wait states (I'm not sure this applies to PCI bus types) +* at some point it would be good to experiment with bench marks to see if +* we can gain some speed by fooling with the wait states etc. +*/ + if (chip_type == MACH64_VT_ID) + aty_st_le32(BUS_CNTL, 0x680000f9); + else + aty_st_le32(BUS_CNTL, 0x590e10ff); + + switch (total_vram) { + case 0x00100000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[0][par->cmode]); + break; + case 0x00200000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[1][par->cmode]); + break; + case 0x00400000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[2][par->cmode]); + break; + default: + i = aty_ld_le32(MEM_CNTL) & 0x000F; + aty_st_le32(MEM_CNTL, + (init->mem_cntl[par->cmode] & 0xFFFFFFF0) | i); + } + } +/* These magic constants are harder to figure out +* on the vt chipset bit 2 set makes the screen brighter +* and bit 15 makes the screen black! But nothing else +* seems to matter for the vt DAC_CNTL +*/ + switch (chip_type) { + case MACH64_GT_ID: + i = 0x86010102; + break; + case MACH64_VT_ID: + i = 0x87010184; + break; + default: + i = 0x47012100; + break; + } + + aty_st_le32(DAC_CNTL, i); + aty_st_8(DAC_MASK, 0xff); + + switch (par->cmode) { + case CMODE_16: + i = CRTC_PIX_WIDTH_15BPP; break; + /*case CMODE_24: */ + case CMODE_32: + i = CRTC_PIX_WIDTH_32BPP; break; + case CMODE_8: + default: + i = CRTC_PIX_WIDTH_8BPP; break; + } + + if (chip_type != MACH64_GT_ID) { + aty_st_le32(CRTC_INT_CNTL, 0x00000002); + aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE); /* gui_en block_en */ + i |= init->crtc_gen_cntl[par->cmode]; + } + /* Gentlemen, start your crtc engine */ + aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_ENABLE | i); + +#ifdef CONFIG_FB_COMPAT_XPMAC + display_info.height = vmode_attrs[par->vmode-1].vres; + display_info.width = vmode_attrs[par->vmode-1].hres; + display_info.depth = 8<<par->cmode; + display_info.pitch = par->vxres<<par->cmode; + display_info.mode = par->vmode; + strcpy(display_info.name, atyfb_name); + display_info.fb_address = + iopa(((chip_type != MACH64_GT_ID) ? + frame_buffer + init->offset[par->cmode] : frame_buffer)); + display_info.cmap_adr_address = iopa((unsigned long)&aty_cmap_regs->windex); + display_info.cmap_data_address = iopa((unsigned long)&aty_cmap_regs->lut); + display_info.disp_reg_address = iopa(ati_regbase); +#endif /* CONFIG_FB_COMPAT_XPMAC) */ +} + + + /* + * Open/Release the frame buffer device + */ + +static int atyfb_open(struct fb_info *info) + +{ + /* + * Nothing, only a usage count for the moment + */ + + MOD_INC_USE_COUNT; + return(0); +} + +static int atyfb_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + + +static int encode_fix(struct fb_fix_screeninfo *fix, + const struct atyfb_par *par) +{ + struct aty_regvals *init; + + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + + strcpy(fix->id, atyfb_name); + init = get_aty_struct(par->vmode); + /* + * FIXME: This will cause problems on non-GT chips, because the frame + * buffer must be aligned to a page + */ + fix->smem_start = (char *)((chip_type != MACH64_GT_ID) + ? frame_buffer + init->offset[par->cmode] : frame_buffer); + fix->smem_len = (u32)total_vram; + if (fix->smem_len > 0x7ff000) + fix->smem_len = 0x7ff000; /* last page is MMIO */ + fix->mmio_start = (char *)(ati_regbase & ~0xfff); + fix->mmio_len = 4096; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + fix->line_length = par->vxres<<par->cmode; + fix->visual = par->cmode == CMODE_8 ? FB_VISUAL_PSEUDOCOLOR + : FB_VISUAL_TRUECOLOR; + fix->ywrapstep = 0; + fix->xpanstep = 8; + fix->ypanstep = 1; + + return 0; +} + + +static int decode_var(struct fb_var_screeninfo *var, + struct atyfb_par *par) +{ + int xres = var->xres; + int yres = var->yres; + int bpp = var->bits_per_pixel; + struct aty_regvals *init; + + /* This should support more video modes */ + + if (xres <= 512 && yres <= 384) + par->vmode = VMODE_512_384_60; /* 512x384, 60Hz */ + else if (xres <= 640 && yres <= 480) + par->vmode = VMODE_640_480_67; /* 640x480, 67Hz */ + else if (xres <= 640 && yres <= 870) + par->vmode = VMODE_640_870_75P; /* 640x870, 75Hz (portrait) */ + else if (xres <= 768 && yres <= 576) + par->vmode = VMODE_768_576_50I; /* 768x576, 50Hz (PAL full frame) */ + else if (xres <= 800 && yres <= 600) + par->vmode = VMODE_800_600_75; /* 800x600, 75Hz */ + else if (xres <= 832 && yres <= 624) + par->vmode = VMODE_832_624_75; /* 832x624, 75Hz */ + else if (xres <= 1024 && yres <= 768) + par->vmode = VMODE_1024_768_75; /* 1024x768, 75Hz */ + else if (xres <= 1152 && yres <= 870) + par->vmode = VMODE_1152_870_75; /* 1152x870, 75Hz */ + else if (xres <= 1280 && yres <= 960) + par->vmode = VMODE_1280_960_75; /* 1280x960, 75Hz */ + else if (xres <= 1280 && yres <= 1024) + par->vmode = VMODE_1280_1024_75; /* 1280x1024, 75Hz */ + else + return -EINVAL; + + xres = vmode_attrs[par->vmode-1].hres; + yres = vmode_attrs[par->vmode-1].vres; + + if (var->xres_virtual <= xres) + par->vxres = xres; + else + par->vxres = (var->xres_virtual+7) & ~7; + if (var->yres_virtual <= yres) + par->vyres = yres; + else + par->vyres = var->yres_virtual; + + par->xoffset = (var->xoffset+7) & ~7; + par->yoffset = var->yoffset; + if (par->xoffset+xres > par->vxres || par->yoffset+yres > par->vyres) + return -EINVAL; + + if (bpp <= 8) + par->cmode = CMODE_8; + else if (bpp <= 16) + par->cmode = CMODE_16; + else if (bpp <= 32) + par->cmode = CMODE_32; + else + return -EINVAL; + + if (aty_vram_reqd(par) > total_vram) + return -EINVAL; + + /* Check if we know about the wanted video mode */ + init = get_aty_struct(par->vmode); + if (init == NULL || init->crtc_h_sync_strt_wid[par->cmode] == 0 || + (chip_type != MACH64_GT_ID && + init->crtc_gen_cntl[par->cmode] == 0) || + (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 && + init->crtc_gen_cntl[1] == 0)) + return -EINVAL; + +#if 0 + if (!fbmon_valid_timings(pixclock, htotal, vtotal, info)) + return -EINVAL; +#endif + + return 0; +} + +static int encode_var(struct fb_var_screeninfo *var, + const struct atyfb_par *par) +{ + memset(var, 0, sizeof(struct fb_var_screeninfo)); + + var->xres = vmode_attrs[par->vmode-1].hres; + var->yres = vmode_attrs[par->vmode-1].vres; + var->xres_virtual = par->vxres; + var->yres_virtual = par->vyres; + var->xoffset = par->xoffset; + var->yoffset = par->yoffset; + var->grayscale = 0; + switch (par->cmode) { + case CMODE_8: + var->bits_per_pixel = 8; + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CMODE_16: /* RGB 555 */ + var->bits_per_pixel = 16; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CMODE_32: /* RGB 888 */ + var->bits_per_pixel = 32; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + break; + } + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + var->nonstd = 0; + var->activate = 0; + var->height = -1; + var->width = -1; + var->accel = /* FB_ACCEL_ATY */ 0; + var->vmode = FB_VMODE_NONINTERLACED; + var->left_margin = var->right_margin = 64; /* guesses */ + var->upper_margin = var->lower_margin = 32; + var->hsync_len = 64; + var->vsync_len = 2; + + /* no long long support in the kernel :-( */ + /* this splittig trick will work if xres > 232 */ + var->pixclock = 1000000000/ + (var->left_margin+var->xres+var->right_margin+var->hsync_len); + var->pixclock *= 1000; + var->pixclock /= vmode_attrs[par->vmode-1].vfreq* + (var->upper_margin+var->yres+var->lower_margin+var->vsync_len); + var->sync = 0; + + return 0; +} + + +static void init_par(struct atyfb_par *par, int vmode, int cmode) +{ + par->vmode = vmode; + par->cmode = cmode; + par->vxres = vmode_attrs[vmode-1].hres; + par->vyres = vmode_attrs[vmode-1].vres; + par->xoffset = 0; + par->yoffset = 0; +} + + + /* + * Get the Fixed Part of the Display + */ + +static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + struct atyfb_par par; + + if (con == -1) + par = default_par; + else + decode_var(&fb_display[con].var, &par); + encode_fix(fix, &par); + return 0; +} + + + /* + * Get the User Defined Part of the Display + */ + +static int atyfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (con == -1) + encode_var(var, &default_par); + else + *var=fb_display[con].var; + return 0; +} + + + /* + * Set the User Defined Part of the Display + */ + +static int atyfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct atyfb_par par; + struct display *display; + int oldxres, oldyres, oldvxres, oldvyres, oldbpp; + int err; + int activate = var->activate; + + if (con >= 0) + display = &fb_display[con]; + else + display = &fb_disp; /* used during initialization */ + + if ((err = decode_var(var, &par))) + return err; + + encode_var(var, &par); + + if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + oldxres = display->var.xres; + oldyres = display->var.yres; + oldvxres = display->var.xres_virtual; + oldvyres = display->var.yres_virtual; + oldbpp = display->var.bits_per_pixel; + display->var = *var; + if (oldxres != var->xres || oldyres != var->yres || + oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || + oldbpp != var->bits_per_pixel) { + struct fb_fix_screeninfo fix; + + encode_fix(&fix, &par); + display->screen_base = (u_char *)fix.smem_start; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->can_soft_blank = 1; + display->inverse = 0; + switch (par.cmode) { + case CMODE_8: +#if 1 + display->dispsw = &fbcon_cfb8; +#else + display->dispsw = &fbcon_aty8; +#endif + break; + case CMODE_16: + display->dispsw = &fbcon_cfb16; + break; + case CMODE_32: + display->dispsw = &fbcon_cfb32; + break; + default: + display->dispsw = NULL; + break; + } + if (fb_info.changevar) + (*fb_info.changevar)(con); + } + if (con == currcon) + atyfb_set_par(&par); + if (oldbpp != var->bits_per_pixel) { + if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) + return err; + do_install_cmap(con, info); + } + } + + return 0; +} + + + /* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ + +static int atyfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + u32 xres, yres, xoffset, yoffset; + struct atyfb_par *par = ¤t_par; + + xres = vmode_attrs[par->vmode-1].hres; + yres = vmode_attrs[par->vmode-1].vres; + xoffset = (var->xoffset+7) & ~7; + yoffset = var->yoffset; + if (xoffset+xres > par->vxres || yoffset+yres > par->vyres) + return -EINVAL; + par->xoffset = xoffset; + par->yoffset = yoffset; + set_off_pitch(par); + return 0; +} + + /* + * Get the Colormap + */ + +static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (con == currcon) /* current console? */ + return fb_get_cmap(cmap, &fb_display[con].var, kspc, atyfb_getcolreg, + info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return 0; +} + + /* + * Set the Colormap + */ + +static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1<<fb_display[con].var.bits_per_pixel, 0))) + return err; + } + if (con == currcon) /* current console? */ + return fb_set_cmap(cmap, &fb_display[con].var, kspc, atyfb_setcolreg, + info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + + +static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + + + /* + * Initialisation + */ + +__initfunc(unsigned long atyfb_init(unsigned long mem_start)) +{ +#ifdef __powerpc__ + /* We don't want to be called like this. */ + /* We rely on Open Firmware (offb) instead. */ + return mem_start; +#else /* !__powerpc__ */ + /* To be merged with Bernd's mach64fb */ + return mem_start; +#endif /* !__powerpc__ */ +} + + +unsigned long atyfb_of_init(unsigned long mem_start, struct device_node *dp) +{ + int i, err, sense; + struct fb_var_screeninfo var; + struct aty_regvals *init; + unsigned long addr; + unsigned char bus, devfn; + unsigned short cmd; + + if (dp->next) + printk("Warning: only using first ATI card detected\n"); + if (dp->n_addrs != 1 && dp->n_addrs != 3) + printk("Warning: expecting 1 or 3 addresses for ATY (got %d)", + dp->n_addrs); + + ati_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000); + aty_cmap_regs = (struct aty_cmap_regs *)(ati_regbase + 0xC0); + + /* enable memory-space accesses using config-space command register */ + if (pci_device_loc(dp, &bus, &devfn) == 0) { + pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + if (cmd != 0xffff) { + cmd |= PCI_COMMAND_MEMORY; + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + } + chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE); + + i = aty_ld_le32(MEM_CNTL); + if (chip_type != MACH64_GT_ID) + switch (i & MEM_SIZE_ALIAS) { + case MEM_SIZE_512K: + total_vram = 0x80000; + break; + case MEM_SIZE_1M: + total_vram = 0x100000; + break; + case MEM_SIZE_2M: + total_vram = 0x200000; + break; + case MEM_SIZE_4M: + total_vram = 0x400000; + break; + case MEM_SIZE_6M: + total_vram = 0x600000; + break; + case MEM_SIZE_8M: + total_vram = 0x800000; + break; + default: + total_vram = 0x80000; + } + else + switch (i & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ + case MEM_SIZE_512K: + total_vram = 0x80000; + break; + case MEM_SIZE_1M: + total_vram = 0x100000; + break; + case MEM_SIZE_2M_GTB: + total_vram = 0x200000; + break; + case MEM_SIZE_4M_GTB: + total_vram = 0x400000; + break; + case MEM_SIZE_6M_GTB: + total_vram = 0x600000; + break; + case MEM_SIZE_8M_GTB: + total_vram = 0x800000; + break; + default: + total_vram = 0x80000; + } + +#if 1 + printk("aty_display_init: node = %p, addrs = ", dp->node); + printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size); + printk(", intrs ="); + for (i = 0; i < dp->n_intrs; ++i) + printk(" %x", dp->intrs[i].line); + printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %p\n", + (int)ati_regbase, bus, devfn, total_vram, aty_cmap_regs); +#endif + + /* Map in frame buffer */ + addr = dp->addrs[0].address; + + /* use the big-endian aperture (??) */ + addr += 0x800000; + frame_buffer = (unsigned long)__ioremap(addr, 0x800000, _PAGE_WRITETHRU); + + if (default_video_mode != -1) { + sense = read_aty_sense(); + printk("monitor sense = %x\n", sense); + if (default_video_mode == VMODE_NVRAM) { + default_video_mode = nvram_read_byte(NV_VMODE); + init = get_aty_struct(default_video_mode); + if (default_video_mode <= 0 || + default_video_mode > VMODE_MAX || init == 0) + default_video_mode = VMODE_CHOOSE; + } + if (default_video_mode == VMODE_CHOOSE) + default_video_mode = map_monitor_sense(sense); + + init = get_aty_struct(default_video_mode); + if (!init) + default_video_mode = VMODE_640_480_60; + } + + /* + * Reduce the pixel size if we don't have enough VRAM. + */ + + if (default_color_mode == CMODE_NVRAM) + default_color_mode = nvram_read_byte(NV_CMODE); + if (default_color_mode < CMODE_8 || + default_color_mode > CMODE_32) + default_color_mode = CMODE_8; + + init_par(&default_par, default_video_mode, default_color_mode); + while (aty_vram_reqd(&default_par) > total_vram) { + while (default_color_mode > CMODE_8 && + aty_vram_reqd(&default_par) > total_vram) { + --default_color_mode; + init_par(&default_par, default_video_mode, default_color_mode); + } + /* + * adjust the video mode smaller if there still is not enough VRAM + */ + if (aty_vram_reqd(&default_par) > total_vram) + do { + default_video_mode--; + init_par(&default_par, default_video_mode, default_color_mode); + init = get_aty_struct(default_video_mode); + } while ((init == 0) && + (default_video_mode > VMODE_640_480_60)); + } + + if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 + && init->crtc_gen_cntl[1] == 0) { + default_video_mode = VMODE_640_480_67; + default_color_mode = CMODE_8; + init_par(&default_par, default_video_mode, default_color_mode); + } + + switch (chip_type) { + case MACH64_GX_ID: + strcat(atyfb_name, "GX"); + break; + case MACH64_VT_ID: + strcat(atyfb_name, "VT"); + break; + case MACH64_GT_ID: + strcat(atyfb_name, "GT"); + break; + default: + break; + } + strcpy(fb_info.modename, atyfb_name); + fb_info.node = -1; + fb_info.fbops = &atyfb_ops; + fb_info.disp = &fb_disp; + fb_info.fontname[0] = '\0'; + fb_info.changevar = NULL; + fb_info.switch_con = &atyfbcon_switch; + fb_info.updatevar = &atyfbcon_updatevar; + fb_info.blank = &atyfbcon_blank; + + err = register_framebuffer(&fb_info); + if (err < 0) + return mem_start; + + for (i = 0; i < 16; i++) { + int j = color_table[i]; + palette[i].red = default_red[j]; + palette[i].green = default_grn[j]; + palette[i].blue = default_blu[j]; + } + atyfb_set_par(&default_par); + encode_var(&var, &default_par); + atyfb_set_var(&var, -1, &fb_info); + + printk("fb%d: %s frame buffer device on %s\n", GET_FB_IDX(fb_info.node), + atyfb_name, dp->full_name); + +#ifdef CONFIG_FB_COMPAT_XPMAC + if (!console_fb_info) { + console_fb_info = &fb_info; + console_setmode_ptr = atyfb_console_setmode; + console_set_cmap_ptr = atyfb_set_cmap; + } +#endif /* CONFIG_FB_COMPAT_XPMAC) */ + + return mem_start; +} + + +/* XXX: doesn't work yet */ +void atyfb_setup(char *options, int *ints) +{ + char *this_opt; + int vmode; + int depth; + + if (!options || !*options) + return; + + for (this_opt = strtok(options, ","); this_opt; + this_opt = strtok(NULL, ",")) { + if (!strncmp(this_opt, "vmode:", 6)) { + vmode = simple_strtoul(this_opt+6, NULL, 0); + if (vmode > 0 && vmode <= VMODE_MAX) + default_video_mode = vmode; + } else if (!strncmp(this_opt, "cmode:", 6)) { + depth = simple_strtoul(this_opt+6, NULL, 0); + switch (depth) { + case 8: + default_color_mode = CMODE_8; + break; + case 15: + case 16: + default_color_mode = CMODE_16; + break; + case 24: + case 32: + default_color_mode = CMODE_32; + break; + }; + } + } +} + + +static int atyfbcon_switch(int con, struct fb_info *info) +{ + struct atyfb_par par; + + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, + atyfb_getcolreg, info); + currcon = con; + decode_var(&fb_display[con].var, &par); + atyfb_set_par(&par); + /* Install new colormap */ + do_install_cmap(con, info); + return 0; +} + + /* + * Update the `var' structure (called by fbcon.c) + */ + +static int atyfbcon_updatevar(int con, struct fb_info *info) +{ + current_par.yoffset = fb_display[con].var.yoffset; + set_off_pitch(¤t_par); + return 0; +} + + /* + * Blank the display. + */ + +static void atyfbcon_blank(int blank, struct fb_info *info) +{ + char gen_cntl; + + gen_cntl = aty_ld_8(CRTC_GEN_CNTL); + if (blank & VESA_VSYNC_SUSPEND) + gen_cntl |= 0x8; + if (blank & VESA_HSYNC_SUSPEND) + gen_cntl |= 0x4; + if ((blank & VESA_POWERDOWN) == VESA_POWERDOWN) + gen_cntl |= 0x40; + if (blank == VESA_NO_BLANKING) + gen_cntl &= ~(0x4c); + aty_st_8(CRTC_GEN_CNTL, gen_cntl); +} + + + /* + * Read a single color register and split it into + * colors/transparent. Return != 0 for invalid regno. + */ + +static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info) +{ + if (regno > 255) + return 1; + *red = palette[regno].red; + *green = palette[regno].green; + *blue = palette[regno].blue; + return 0; +} + + + /* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + */ + +static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + int i, scale; + + if (regno > 255) + return 1; + palette[regno].red = red; + palette[regno].green = green; + palette[regno].blue = blue; + aty_WaitQueue(2); + i = aty_ld_8(DAC_CNTL) & 0xfc; + if (chip_type == MACH64_GT_ID) + i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt*/ + aty_st_8(DAC_CNTL, i); + aty_st_8(DAC_REGS + DAC_MASK, 0xff); + eieio(); + scale = ((chip_type != MACH64_GX_ID) && + (current_par.cmode == CMODE_16)) ? 3 : 0; + aty_WaitQueue(4); + aty_cmap_regs->windex = regno << scale; + eieio(); + aty_cmap_regs->lut = red << scale; + eieio(); + aty_cmap_regs->lut = green << scale; + eieio(); + aty_cmap_regs->lut = blue << scale; + eieio(); + if (regno < 16) { +#ifdef CONFIG_FBCON_CFB16 + fbcon_cfb16_cmap[regno] = (regno << 10) | (regno << 5) | regno; +#endif +#ifdef CONFIG_FBCON_CFB32 + fbcon_cfb32_cmap[regno] = (regno << 24) | (regno << 16) | + (regno << 8) | regno; +#endif + } + return 0; +} + + +static void do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, + atyfb_setcolreg, info); + else + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, atyfb_setcolreg, + info); +} + + + /* + * Accelerated functions + */ + +void aty_waitblit(void) +{ + aty_WaitIdleEmpty(); /* Make sure that all commands have finished */ +} + +void aty_rectcopy(int srcx, int srcy, int dstx, int dsty, u_int width, + u_int height) +{ + u_int direction = 0; + + if (srcy < dsty) { + dsty += height - 1; + srcy += height - 1; + } else + direction |= DST_Y_TOP_TO_BOTTOM; + + if (srcx < dstx) { + dstx += width - 1; + srcx += width - 1; + } else + direction |= DST_X_LEFT_TO_RIGHT; + + aty_WaitQueue(4); + aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ ); + aty_st_le32(DP_MIX, (MIX_SRC << 16) | MIX_DST); + aty_st_le32(DP_SRC, FRGD_SRC_BLIT); + + aty_WaitQueue(5); + aty_st_le32(SRC_Y_X, (srcx << 16) | (srcy & 0x0000ffff)); + aty_st_le32(SRC_WIDTH1, width); + aty_st_le32(DST_CNTL, direction); + aty_st_le32(DST_Y_X, (dstx << 16) | (dsty & 0x0000ffff)); + aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | (height & 0x0000ffff)); + + aty_WaitIdleEmpty(); /* Make sure that all commands have finished */ + + /* + * Make sure that the destination trajectory is correctly set + * for subsequent calls. MACH64_BIT_BLT is the only function that + * currently changes the destination trajectory from L->R and T->B. + */ + aty_st_le32(DST_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); +} + +void aty_rectfill(int dstx, int dsty, u_int width, u_int height, u_int color) +{ + if (!width || !height) + return; + + aty_WaitQueue(5); + aty_st_le32(DP_FRGD_CLR, color /* pGC->fgPixel */ ); + aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ ); + aty_st_le32(DP_MIX, (MIX_SRC << 16) | MIX_DST); + aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR); + + aty_st_le32(DST_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM); + + aty_WaitQueue(2); + aty_st_le32(DST_Y_X, (((u_int)dstx << 16) | ((u_int)dsty & 0x0000ffff))); + aty_st_le32(DST_HEIGHT_WIDTH, (((u_int)width << 16) | height)); + + aty_WaitIdleEmpty(); /* Make sure that all commands have finished */ +} + + + /* + * Text console acceleration + */ + +static void fbcon_aty8_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + sx *= p->fontwidth; + sy *= p->fontheight; + dx *= p->fontwidth; + dy *= p->fontheight; + width *= p->fontwidth; + height *= p->fontheight; + + aty_rectcopy(sx, sy, dx, dy, width, height); +} + +static void fbcon_aty8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) +{ + u32 bgx = attr_bgcol_ec(p, conp); + bgx |= (bgx << 8); + bgx |= (bgx << 16); + + sx *= p->fontwidth; + sy *= p->fontheight; + width *= p->fontwidth; + height *= p->fontheight; + + aty_rectfill(sx, sy, width, height, bgx); +} + +static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) +{ + aty_waitblit(); + fbcon_cfb8_putc(conp, p, c, yy, xx); +} + +static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) +{ + aty_waitblit(); + fbcon_cfb8_putcs(conp, p, s, count, yy, xx); +} + +static struct display_switch fbcon_aty8 = { + fbcon_cfb8_setup, fbcon_aty8_bmove, fbcon_aty8_clear, fbcon_aty8_putc, + fbcon_aty8_putcs, fbcon_cfb8_revc +}; + + +#ifdef CONFIG_FB_COMPAT_XPMAC + + /* + * Backward compatibility mode for Xpmac + */ + +static int atyfb_console_setmode(struct vc_mode *mode, int doit) +{ + int err; + struct fb_var_screeninfo var; + struct atyfb_par par; + int vmode, cmode; + + if (mode->mode <= 0 || mode->mode > VMODE_MAX ) + return -EINVAL; + vmode = mode->mode; + + switch (mode->depth) { + case 24: + case 32: + cmode = CMODE_32; + break; + case 16: + cmode = CMODE_16; + break; + case 8: + case 0: /* (default) */ + cmode = CMODE_8; + break; + default: + return -EINVAL; + } + init_par(&par, vmode, cmode); + encode_var(&var, &par); + if ((err = decode_var(&var, &par))) + return err; + if (doit) + atyfb_set_var(&var, currcon, 0); + return 0; +} + +#endif /* CONFIG_FB_COMPAT_XPMAC */ diff --git a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c index ccd83bb01..2eaacb57d 100644 --- a/drivers/video/cyberfb.c +++ b/drivers/video/cyberfb.c @@ -36,18 +36,34 @@ #include <asm/system.h> #include <asm/irq.h> #include <asm/pgtable.h> +#include <asm/amigahw.h> + #include "s3blit.h" +#include "fbcon.h" +#include "fbcon-cfb8.h" +#include "fbcon-cfb16.h" + +#ifdef CYBERFBDEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif #define arraysize(x) (sizeof(x)/sizeof(*(x))) -struct Cyber_fb_par { + +#define wb_64(reg,dat) (*((unsigned char volatile *)CyberRegs + reg) = dat) + + + +struct cyberfb_par { int xres; int yres; int bpp; }; -static struct Cyber_fb_par current_par; +static struct cyberfb_par current_par; static int current_par_valid = 0; static int currcon = 0; @@ -68,13 +84,13 @@ static struct fb_hwswitch { /* Display Control */ - int (*encode_fix)(struct fb_fix_screeninfo *fix, struct Cyber_fb_par *par); - int (*decode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par); - int (*encode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par); + int (*encode_fix)(struct fb_fix_screeninfo *fix, struct cyberfb_par *par); + int (*decode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par); + int (*encode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par); int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); + u_int transp, struct fb_info *info); void (*blank)(int blank); } *fbhw; @@ -83,7 +99,7 @@ static struct fb_hwswitch { * Frame Buffer Name */ -static char Cyber_fb_name[16] = "Cybervision"; +static char cyberfb_name[16] = "Cybervision"; /* @@ -106,105 +122,67 @@ static unsigned char Cyber_colour_table [256][4]; static unsigned long CyberMem; static unsigned long CyberSize; static volatile char *CyberRegs; - - -/* - * Predefined Video Mode Names - */ - -static char *Cyber_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", - - /* - * Predefined Video Modes - */ - - "cyber8", /* Cybervision 8 bpp */ - "cyber16", /* Cybervision 16 bpp */ - "800x600x8", - "640x480x8", - - /* - * Dummy Video Modes - */ - - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", - - /* - * User Defined Video Modes - * - * This doesn't work yet!! - */ - - "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7" -}; - + /* - * Predefined Video Mode Definitions + * Predefined Video Modes */ -static struct fb_var_screeninfo cyber_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { 0, }, - - /* - * Predefined Video Modes - */ - - { - /* Cybervision 8 bpp */ - CYBER8_WIDTH, CYBER8_HEIGHT, CYBER8_WIDTH, CYBER8_HEIGHT, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { - /* Cybervision 16 bpp */ - 800, 600, 800, 600, 0, 0, 16, 0, - {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { - /* Cybervision 8 bpp */ - 800, 600, 800, 600, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { - /* Cybervision 8 bpp */ - 640, 480, 640, 480, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - /* - * Dummy Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - - /* - * User Defined Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } +static struct fb_videomode cyberfb_predefined[] __initdata = { + { + "640x480-8", { /* Cybervision 8 bpp */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "800x600-8", { /* Cybervision 8 bpp */ + 800, 600, 800, 600, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1024x768-8", { /* Cybervision 8 bpp */ + 1024, 768, 1024, 768, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1152x886-8", { /* Cybervision 8 bpp */ + 1152, 886, 1152, 886, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1280x1024-8", { /* Cybervision 8 bpp */ + 1280, 1024, 1280, 1024, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1600x1200-8", { /* Cybervision 8 bpp */ + 1600, 1200, 1600, 1200, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "800x600-16", { /* Cybervision 16 bpp */ + 800, 600, 800, 600, 0, 0, 16, 0, + {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + } }; -#define NUM_TOTAL_MODES arraysize(cyber_fb_predefined) -#define NUM_PREDEF_MODES (5) +#define NUM_TOTAL_MODES arraysize(cyberfb_predefined) static int Cyberfb_inverse = 0; @@ -212,57 +190,72 @@ static int Cyberfb_inverse = 0; static int Cyberfb_Cyber8 = 0; /* Use Cybervision board */ static int Cyberfb_Cyber16 = 0; /* Use Cybervision board */ #endif -static int Cyberfb_mode = 0; - /* * Some default modes */ -#define CYBER8_DEFMODE (1) -#define CYBER16_DEFMODE (2) +#define CYBER8_DEFMODE (0) +#define CYBER16_DEFMODE (6) + +static struct fb_var_screeninfo cyberfb_default; /* * Interface used by the world */ -void Cyber_video_setup(char *options, int *ints); - -static int Cyber_fb_open(int fbidx); -static int Cyber_fb_release(int fbidx); -static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); +void cyberfb_setup(char *options, int *ints); + +static int cyberfb_open(struct fb_info *info); +static int cyberfb_release(struct fb_info *info); +static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct +fb_info *info); +static int cyberfb_get_var(struct fb_var_screeninfo *var, int con, struct +fb_info *info); +static int cyberfb_set_var(struct fb_var_screeninfo *var, int con, struct +fb_info *info); +static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int cyberfb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); /* * Interface to the low level console driver */ -unsigned long Cyber_fb_init(unsigned long mem_start); -static int Cyberfb_switch(int con); -static int Cyberfb_updatevar(int con); -static void Cyberfb_blank(int blank); -static int Cyberfb_setcmap(struct fb_cmap *cmap, int con); +unsigned long cyberfb_init(unsigned long mem_start); +static int Cyberfb_switch(int con, struct fb_info *info); +static int Cyberfb_updatevar(int con, struct fb_info *info); +static void Cyberfb_blank(int blank, struct fb_info *info); + + +/* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static struct display_switch fbcon_cyber8; +#endif /* * Accelerated Functions used by the low level console driver */ -void Cyber_WaitQueue(u_short fifo); -void Cyber_WaitBlit(void); -void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, u_short desty, - u_short width, u_short height, u_short mode); -void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short color); -void Cyber_MoveCursor(u_short x, u_short y); +static void Cyber_WaitQueue(u_short fifo); +static void Cyber_WaitBlit(void); +static void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, + u_short desty, u_short width, u_short height, + u_short mode); +static void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height, + u_short mode, u_short color); +static void Cyber_MoveCursor(u_short x, u_short y); /* @@ -271,15 +264,15 @@ void Cyber_MoveCursor(u_short x, u_short y); static int Cyber_init(void); static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, - struct Cyber_fb_par *par); + struct cyberfb_par *par); static int Cyber_decode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par); + struct cyberfb_par *par); static int Cyber_encode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par); + struct cyberfb_par *par); static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); + u_int transp, struct fb_info *info); static void Cyber_blank(int blank); @@ -287,11 +280,11 @@ static void Cyber_blank(int blank); * Internal routines */ -static void Cyber_fb_get_par(struct Cyber_fb_par *par); -static void Cyber_fb_set_par(struct Cyber_fb_par *par); +static void cyberfb_get_par(struct cyberfb_par *par); +static void cyberfb_set_par(struct cyberfb_par *par); static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); -static void do_install_cmap(int con); -static void Cyber_fb_set_disp(int con); +static void do_install_cmap(int con, struct fb_info *info); +static void cyberfb_set_disp(int con, struct fb_info *info); static int get_video_mode(const char *name); @@ -311,41 +304,32 @@ static int Cyber_init(void) char size; volatile u_long *CursorBase; -#if 0 - if (Cyberfb_mode == -1) + for (i = 0; i < 256; i++) { - if (Cyberfb_Cyber8) - Cyberfb_mode = CYBER8_DEFMODE; - else - Cyberfb_mode = CYBER16_DEFMODE; + Cyber_colour_table [i][0] = i; + Cyber_colour_table [i][1] = i; + Cyber_colour_table [i][2] = i; + Cyber_colour_table [i][3] = 0; } -#endif - - for (i = 0; i < 256; i++) - - for (i = 0; i < 256; i++) - { - Cyber_colour_table [i][0] = i; - Cyber_colour_table [i][1] = i; - Cyber_colour_table [i][2] = i; - Cyber_colour_table [i][3] = 0; - } /* * Just clear the thing for the biggest mode. + * + * ++Andre, TODO: determine size first, then clear all memory + * (the 3D penguin might need texture memory :-) ) */ - memset ((char*)CyberMem, 0, CYBER8_WIDTH * CYBER8_HEIGHT); + memset ((char*)CyberMem, 0, 1600 * 1200); /* Disable hardware cursor */ - *(CyberRegs + S3_CRTC_ADR) = S3_REG_LOCK2; - *(CyberRegs + S3_CRTC_DATA) = 0xa0; - *(CyberRegs + S3_CRTC_ADR) = S3_HGC_MODE; - *(CyberRegs + S3_CRTC_DATA) = 0x00; - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DX; - *(CyberRegs + S3_CRTC_DATA) = 0x00; - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DY; - *(CyberRegs + S3_CRTC_DATA) = 0x00; + wb_64(S3_CRTC_ADR, S3_REG_LOCK2); + wb_64(S3_CRTC_DATA, 0xa0); + wb_64(S3_CRTC_ADR, S3_HGC_MODE); + wb_64(S3_CRTC_DATA, 0x00); + wb_64(S3_CRTC_ADR, S3_HWGC_DX); + wb_64(S3_CRTC_DATA, 0x00); + wb_64(S3_CRTC_ADR, S3_HWGC_DY); + wb_64(S3_CRTC_DATA, 0x00); /* Get memory size (if not 2MB it is 4MB) */ *(CyberRegs + S3_CRTC_ADR) = S3_LAW_CTL; @@ -372,8 +356,8 @@ static int Cyber_init(void) *(CursorBase+3+(i*4)) = 0xffff0000; } - Cyber_setcolreg (255, 56, 100, 160, 0); - Cyber_setcolreg (254, 0, 0, 0, 0); + Cyber_setcolreg (255, 56, 100, 160, 0, NULL /* unused */); + Cyber_setcolreg (254, 0, 0, 0, 0, NULL /* unused */); return 0; } @@ -385,11 +369,11 @@ static int Cyber_init(void) */ static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, - struct Cyber_fb_par *par) + struct cyberfb_par *par) { int i; - strcpy(fix->id, Cyber_fb_name); + strcpy(fix->id, cyberfb_name); fix->smem_start = (caddr_t)CyberMem; fix->smem_len = CyberSize; fix->mmio_start = (unsigned char *)CyberRegs; @@ -420,7 +404,7 @@ static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, */ static int Cyber_decode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par) + struct cyberfb_par *par) { #if 1 par->xres = var->xres; @@ -447,7 +431,7 @@ static int Cyber_decode_var(struct fb_var_screeninfo *var, */ static int Cyber_encode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par) + struct cyberfb_par *par) { int i; @@ -486,7 +470,10 @@ static int Cyber_encode_var(struct fb_var_screeninfo *var, var->height = -1; var->width = -1; + var->accel = FB_ACCEL_CYBERVISION; + DPRINTK("accel CV64\n"); + var->vmode = FB_VMODE_NONINTERLACED; /* Dummy values */ @@ -517,20 +504,22 @@ static int Cyber_encode_var(struct fb_var_screeninfo *var, */ static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) + u_int transp, struct fb_info *info) { if (regno > 255) + { return (1); + } - *(CyberRegs + 0x3c8) = (char)regno; + wb_64(0x3c8, (unsigned char) regno); Cyber_colour_table [regno][0] = red & 0xff; Cyber_colour_table [regno][1] = green & 0xff; Cyber_colour_table [regno][2] = blue & 0xff; Cyber_colour_table [regno][3] = transp; - *(CyberRegs + 0x3c9) = (red & 0xff) >> 2; - *(CyberRegs + 0x3c9) = (green & 0xff) >> 2; - *(CyberRegs + 0x3c9) = (blue & 0xff) >> 2; + wb_64(0x3c9, (red & 0xff) >> 2); + wb_64(0x3c9, (green & 0xff) >> 2); + wb_64(0x3c9, (blue & 0xff) >> 2); return (0); } @@ -542,13 +531,13 @@ static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, */ static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) + u_int *transp, struct fb_info *info) { if (regno >= 256) return (1); - *red = Cyber_colour_table [regno][0]; - *green = Cyber_colour_table [regno][1]; - *blue = Cyber_colour_table [regno][2]; + *red = Cyber_colour_table [regno][0]; + *green = Cyber_colour_table [regno][1]; + *blue = Cyber_colour_table [regno][2]; *transp = Cyber_colour_table [regno][3]; return (0); } @@ -563,28 +552,32 @@ void Cyber_blank(int blank) int i; if (blank) + { for (i = 0; i < 256; i++) { - *(CyberRegs + 0x3c8) = i; - *(CyberRegs + 0x3c9) = 0; - *(CyberRegs + 0x3c9) = 0; - *(CyberRegs + 0x3c9) = 0; + wb_64(0x3c8, (unsigned char) i); + wb_64(0x3c9, 0); + wb_64(0x3c9, 0); + wb_64(0x3c9, 0); } + } else + { for (i = 0; i < 256; i++) { - *(CyberRegs + 0x3c8) = i; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][0] >> 2; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][1] >> 2; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][2] >> 2; + wb_64(0x3c8, (unsigned char) i); + wb_64(0x3c9, Cyber_colour_table[i][0] >> 2); + wb_64(0x3c9, Cyber_colour_table[i][1] >> 2); + wb_64(0x3c9, Cyber_colour_table[i][2] >> 2); } + } } /************************************************************** * We are waiting for "fifo" FIFO-slots empty */ -void Cyber_WaitQueue (u_short fifo) +static void Cyber_WaitQueue (u_short fifo) { u_short status; @@ -598,7 +591,7 @@ void Cyber_WaitQueue (u_short fifo) /************************************************************** * We are waiting for Hardware (Graphics Engine) not busy */ -void Cyber_WaitBlit (void) +static void Cyber_WaitBlit (void) { u_short status; @@ -612,8 +605,9 @@ void Cyber_WaitBlit (void) /************************************************************** * BitBLT - Through the Plane */ -void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty, - u_short width, u_short height, u_short mode) +static void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, + u_short desty, u_short width, u_short height, + u_short mode) { u_short blitcmd = S3_BITBLT; @@ -655,8 +649,8 @@ void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty, /************************************************************** * Rectangle Fill Solid */ -void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short color) +static void Cyber_RectFill (u_short x, u_short y, u_short width, + u_short height, u_short mode, u_short color) { u_short blitcmd = S3_FILLEDRECT; @@ -677,11 +671,10 @@ void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, *((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd; } - /************************************************************** * Move cursor to x, y */ -void Cyber_MoveCursor (u_short x, u_short y) +static void Cyber_MoveCursor (u_short x, u_short y) { *(CyberRegs + S3_CRTC_ADR) = 0x39; *(CyberRegs + S3_CRTC_DATA) = 0xa0; @@ -714,16 +707,20 @@ static struct fb_hwswitch Cyber_switch = { * Fill the hardware's `par' structure. */ -static void Cyber_fb_get_par(struct Cyber_fb_par *par) +static void cyberfb_get_par(struct cyberfb_par *par) { if (current_par_valid) + { *par = current_par; + } else - fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], par); + { + fbhw->decode_var(&cyberfb_default, par); + } } -static void Cyber_fb_set_par(struct Cyber_fb_par *par) +static void cyberfb_set_par(struct cyberfb_par *par) { current_par = *par; current_par_valid = 1; @@ -733,6 +730,7 @@ static void Cyber_fb_set_par(struct Cyber_fb_par *par) static void cyber_set_video(struct fb_var_screeninfo *var) { /* Set clipping rectangle to current screen size */ + *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x1000; *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x2000; @@ -744,13 +742,13 @@ static void cyber_set_video(struct fb_var_screeninfo *var) static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) { int err, activate; - struct Cyber_fb_par par; + struct cyberfb_par par; if ((err = fbhw->decode_var(var, &par))) return(err); activate = var->activate; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) - Cyber_fb_set_par(&par); + cyberfb_set_par(&par); fbhw->encode_var(var, &par); var->activate = activate; @@ -759,16 +757,16 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) } -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - fbhw->setcolreg); + fbhw->setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), - &fb_display[con].var, 1, fbhw->setcolreg); + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, fbhw->setcolreg, info); } @@ -776,7 +774,7 @@ static void do_install_cmap(int con) * Open/Release the frame buffer device */ -static int Cyber_fb_open(int fbidx) +static int cyberfb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -786,7 +784,7 @@ static int Cyber_fb_open(int fbidx) return(0); } -static int Cyber_fb_release(int fbidx) +static int cyberfb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); @@ -797,13 +795,14 @@ static int Cyber_fb_release(int fbidx) * Get the Fixed Part of the Display */ -static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { - struct Cyber_fb_par par; + struct cyberfb_par par; int error = 0; if (con == -1) - Cyber_fb_get_par(&par); + cyberfb_get_par(&par); else error = fbhw->decode_var(&fb_display[con].var, &par); return(error ? error : fbhw->encode_fix(fix, &par)); @@ -814,21 +813,28 @@ static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con) +static int cyberfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { - struct Cyber_fb_par par; + struct cyberfb_par par; int error = 0; - if (con == -1) { - Cyber_fb_get_par(&par); + if (con == -1) + { + cyberfb_get_par(&par); error = fbhw->encode_var(var, &par); - } else + disp.var = *var; /* ++Andre: don't know if this is the right place */ + } + else + { *var = fb_display[con].var; + } + return(error); } -static void Cyber_fb_set_disp(int con) +static void cyberfb_set_disp(int con, struct fb_info *info) { struct fb_fix_screeninfo fix; struct display *display; @@ -838,7 +844,7 @@ static void Cyber_fb_set_disp(int con) else display = &disp; /* used during initialization */ - Cyber_fb_get_fix(&fix, con); + cyberfb_get_fix(&fix, con, info); if (con == -1) con = 0; display->screen_base = (u_char *)fix.smem_start; @@ -849,6 +855,21 @@ static void Cyber_fb_set_disp(int con) display->ywrapstep = fix.ywrapstep; display->can_soft_blank = 1; display->inverse = Cyberfb_inverse; + switch (display->var.bits_per_pixel) { +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_cyber8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif + default: + display->dispsw = NULL; + break; + } } @@ -856,7 +877,8 @@ static void Cyber_fb_set_disp(int con) * Set the User Defined Part of the Display */ -static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con) +static int cyberfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp; @@ -873,10 +895,10 @@ static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con) oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || oldbpp != var->bits_per_pixel) { - Cyber_fb_set_disp(con); + cyberfb_set_disp(con, info); (*fb_info.changevar)(con); fb_alloc_cmap(&fb_display[con].cmap, 0, 0); - do_install_cmap(con); + do_install_cmap(con, info); } } var->activate = 0; @@ -888,15 +910,16 @@ static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ return(fb_get_cmap(cmap, &fb_display[con].var, - kspc, fbhw->getcolreg)); + kspc, fbhw->getcolreg, info)); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return(0); } @@ -906,7 +929,8 @@ static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { int err; @@ -915,9 +939,9 @@ static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) 1<<fb_display[con].var.bits_per_pixel, 0))) return(err); } - if (con == currcon) /* current console? */ + if (con == currcon) /* current console? */ return(fb_set_cmap(cmap, &fb_display[con].var, - kspc, fbhw->setcolreg)); + kspc, fbhw->setcolreg, info)); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return(0); @@ -930,31 +954,32 @@ static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con) +static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { return(-EINVAL); } /* - * Cybervision Frame Buffer Specific ioctls + * Cybervision Frame Buffer Specific ioctls */ -static int Cyber_fb_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg, int con) +static int cyberfb_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg, int con, struct fb_info *info) { return(-EINVAL); } -static struct fb_ops Cyber_fb_ops = { - Cyber_fb_open, Cyber_fb_release, Cyber_fb_get_fix, Cyber_fb_get_var, - Cyber_fb_set_var, Cyber_fb_get_cmap, Cyber_fb_set_cmap, - Cyber_fb_pan_display, Cyber_fb_ioctl +static struct fb_ops cyberfb_ops = { + cyberfb_open, cyberfb_release, cyberfb_get_fix, cyberfb_get_var, + cyberfb_set_var, cyberfb_get_cmap, cyberfb_set_cmap, + cyberfb_pan_display, NULL, cyberfb_ioctl }; -__initfunc(void Cyber_video_setup(char *options, int *ints)) +__initfunc(void cyberfb_setup(char *options, int *ints)) { char *this_opt; @@ -969,14 +994,18 @@ __initfunc(void Cyber_video_setup(char *options, int *ints)) fb_invert_cmaps(); } else if (!strncmp(this_opt, "font:", 5)) strcpy(fb_info.fontname, this_opt+5); -#if 0 - else if (!strcmp (this_opt, "cyber8")) - Cyberfb_Cyber8 = 1; - else if (!strcmp (this_opt, "cyber16")) - Cyberfb_Cyber16 = 1; -#endif + else if (!strcmp (this_opt, "cyber8")){ + cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var; + } + else if (!strcmp (this_opt, "cyber16")){ + cyberfb_default = cyberfb_predefined[CYBER16_DEFMODE].var; + } else - Cyberfb_mode = get_video_mode(this_opt); + get_video_mode(this_opt); + + DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",cyberfb_default.xres, + cyberfb_default.yres, + cyberfb_default.bits_per_pixel); } @@ -984,10 +1013,10 @@ __initfunc(void Cyber_video_setup(char *options, int *ints)) * Initialization */ -__initfunc(unsigned long Cyber_fb_init(unsigned long mem_start)) +__initfunc(unsigned long cyberfb_init(unsigned long mem_start)) { int err; - struct Cyber_fb_par par; + struct cyberfb_par par; unsigned long board_addr; const struct ConfigDev *cd; @@ -1005,33 +1034,30 @@ __initfunc(unsigned long Cyber_fb_init(unsigned long mem_start)) fbhw = &Cyber_switch; - strcpy(fb_info.modename, Cyber_fb_name); + strcpy(fb_info.modename, cyberfb_name); fb_info.changevar = NULL; fb_info.node = -1; - fb_info.fbops = &Cyber_fb_ops; - fb_info.fbvar_num = NUM_TOTAL_MODES; - fb_info.fbvar = cyber_fb_predefined; + fb_info.fbops = &cyberfb_ops; fb_info.disp = &disp; fb_info.switch_con = &Cyberfb_switch; fb_info.updatevar = &Cyberfb_updatevar; fb_info.blank = &Cyberfb_blank; - fb_info.setcmap = &Cyberfb_setcmap; err = register_framebuffer(&fb_info); if (err < 0) return mem_start; fbhw->init(); - fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], &par); - fbhw->encode_var(&cyber_fb_predefined[0], &par); + fbhw->decode_var(&cyberfb_default, &par); + fbhw->encode_var(&cyberfb_default, &par); - do_fb_set_var(&cyber_fb_predefined[0], 1); - Cyber_fb_get_var(&fb_display[0].var, -1); - Cyber_fb_set_disp(-1); - do_install_cmap(0); + do_fb_set_var(&cyberfb_default, 1); + cyberfb_get_var(&fb_display[0].var, -1, &fb_info); + cyberfb_set_disp(-1, &fb_info); + do_install_cmap(0, &fb_info); - printk("%s frame buffer device, using %ldK of video memory\n", - fb_info.modename, CyberSize>>10); + printk("fb%d: %s frame buffer device, using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10); /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; @@ -1040,17 +1066,17 @@ __initfunc(unsigned long Cyber_fb_init(unsigned long mem_start)) } -static int Cyberfb_switch(int con) +static int Cyberfb_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, - fbhw->getcolreg); + fbhw->getcolreg, info); do_fb_set_var(&fb_display[con].var, 1); currcon = con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return(0); } @@ -1062,7 +1088,7 @@ static int Cyberfb_switch(int con) * Since it's called by a kernel driver, no range checking is done. */ -static int Cyberfb_updatevar(int con) +static int Cyberfb_updatevar(int con, struct fb_info *info) { return(0); } @@ -1072,42 +1098,92 @@ static int Cyberfb_updatevar(int con) * Blank the display. */ -static void Cyberfb_blank(int blank) +static void Cyberfb_blank(int blank, struct fb_info *info) { fbhw->blank(blank); } /* - * Set the colormap + * Get a Video Mode */ -static int Cyberfb_setcmap(struct fb_cmap *cmap, int con) +__initfunc(static int get_video_mode(const char *name)) { - return(Cyber_fb_set_cmap(cmap, 1, con)); + int i; + + for (i = 0; i < NUM_TOTAL_MODES; i++) { + if (!strcmp(name, cyberfb_predefined[i].name)) { + cyberfb_default = cyberfb_predefined[i].var; + return(i); + } + } + /* ++Andre: set cyberfb default mode */ + cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var; + return(0); } /* - * Get a Video Mode + * Text console acceleration */ -static int get_video_mode(const char *name) +#ifdef CONFIG_FBCON_CFB8 +static void fbcon_cyber8_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width) { - int i; + sx *= 8; dx *= 8; width *= 8; + Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx, + (u_short)(dy*p->fontheight), (u_short)width, + (u_short)(height*p->fontheight), (u_short)S3_NEW); +} - for (i = 1; i < NUM_PREDEF_MODES; i++) - if (!strcmp(name, Cyber_fb_modenames[i])) - cyber_fb_predefined[0] = cyber_fb_predefined[i]; - return(i); - return(0); +static void fbcon_cyber8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) +{ + unsigned char bg; + + sx *= 8; width *= 8; + bg = attr_bgcol_ec(p,conp); + Cyber_RectFill((u_short)sx, + (u_short)(sy*p->fontheight), + (u_short)width, + (u_short)(height*p->fontheight), + (u_short)S3_NEW, + (u_short)bg); +} + +static void fbcon_cyber8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) +{ + Cyber_WaitBlit(); + fbcon_cfb8_putc(conp, p, c, yy, xx); +} + +static void fbcon_cyber8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) +{ + Cyber_WaitBlit(); + fbcon_cfb8_putcs(conp, p, s, count, yy, xx); } +static void fbcon_cyber8_revc(struct display *p, int xx, int yy) +{ + Cyber_WaitBlit(); + fbcon_cfb8_revc(p, xx, yy); +} + +static struct display_switch fbcon_cyber8 = { + fbcon_cfb8_setup, fbcon_cyber8_bmove, fbcon_cyber8_clear, fbcon_cyber8_putc, + fbcon_cyber8_putcs, fbcon_cyber8_revc +}; +#endif + #ifdef MODULE int init_module(void) { - return(Cyber_fb_init(NULL)); + return(cyberfb_init(NULL)); } void cleanup_module(void) @@ -1118,12 +1194,3 @@ void cleanup_module(void) /* TODO: clean up ... */ } #endif /* MODULE */ - - -/* - * Visible symbols for modules - */ - -EXPORT_SYMBOL(Cyber_BitBLT); -EXPORT_SYMBOL(Cyber_RectFill); -EXPORT_SYMBOL(Cyber_WaitBlit); diff --git a/drivers/video/dn_fb.c b/drivers/video/dnfb.c index 8c5aeb099..db0a6172f 100644 --- a/drivers/video/dn_fb.c +++ b/drivers/video/dnfb.c @@ -5,6 +5,7 @@ #include <linux/tty.h> #include <linux/malloc.h> #include <linux/delay.h> +#include <linux/config.h> #include <linux/interrupt.h> #include <asm/setup.h> #include <asm/segment.h> @@ -14,6 +15,9 @@ #include <linux/fb.h> #include <linux/module.h> +#include "fbcon-mfb.h" + + /* apollo video HW definitions */ /* @@ -108,46 +112,44 @@ #endif -void dn_video_setup(char *options, int *ints); - /* frame buffer operations */ -static int dn_fb_open(int fbidx); -static int dn_fb_release(int fbidx); -static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int dn_fb_get_var(struct fb_var_screeninfo *var, int con); -static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive); -static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con); -static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con); -static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg, int con); - -static int dnfbcon_switch(int con); -static int dnfbcon_updatevar(int con); -static void dnfbcon_blank(int blank); - -static void dn_fb_set_disp(int con); +static int dnfb_open(struct fb_info *info); +static int dnfb_release(struct fb_info *info); +static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int dnfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dnfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dnfb_get_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dnfb_set_cmap(struct fb_cmap *cmap,int kspc,int con, + struct fb_info *info); +static int dnfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int dnfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info); + +static int dnfbcon_switch(int con, struct fb_info *info); +static int dnfbcon_updatevar(int con, struct fb_info *info); +static void dnfbcon_blank(int blank, struct fb_info *info); + +static void dnfb_set_disp(int con, struct fb_info *info); static struct display disp[MAX_NR_CONSOLES]; static struct fb_info fb_info; -static struct fb_ops dn_fb_ops = { - dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var, - dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl +static struct fb_ops dnfb_ops = { + dnfb_open,dnfb_release, dnfb_get_fix, dnfb_get_var, dnfb_set_var, + dnfb_get_cmap, dnfb_set_cmap, dnfb_pan_display, NULL, dnfb_ioctl }; static int currcon=0; -#define NUM_TOTAL_MODES 1 -struct fb_var_screeninfo dn_fb_predefined[] = { - - { 0, }, +static char dnfb_name[]="Apollo"; -}; - -static char dn_fb_name[]="Apollo "; - -static int dn_fb_open(int fbidx) +static int dnfb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -157,14 +159,15 @@ static int dn_fb_open(int fbidx) return(0); } -static int dn_fb_release(int fbidx) +static int dnfb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); } -static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con) { - +static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ memset(fix, 0, sizeof(struct fb_fix_screeninfo)); strcpy(fix->id,"Apollo Mono"); fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE); @@ -181,8 +184,9 @@ static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con) { } -static int dn_fb_get_var(struct fb_var_screeninfo *var, int con) { - +static int dnfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ var->xres=1280; var->yres=1024; var->xres_virtual=2048; @@ -208,9 +212,9 @@ static int dn_fb_get_var(struct fb_var_screeninfo *var, int con) { } -static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive) { - - printk("fb_set_var\n"); +static int dnfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ if(var->xres!=1280) return -EINVAL; if(var->yres!=1024) @@ -252,48 +256,48 @@ static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive) { } -static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con) { - +static int dnfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ printk("get cmap not supported\n"); return -EINVAL; } -static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con) { - +static int dnfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ printk("set cmap not supported\n"); return -EINVAL; } -static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con) { - +static int dnfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ printk("panning not supported\n"); return -EINVAL; } -static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg, int con) { - - printk("no IOCTLs as of yet.\n"); - +static int dnfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info) +{ return -EINVAL; - } -static void dn_fb_set_disp(int con) { - +static void dnfb_set_disp(int con, struct fb_info *info) +{ struct fb_fix_screeninfo fix; - dn_fb_get_fix(&fix,con); + dnfb_get_fix(&fix, con, info); if(con==-1) con=0; disp[con].screen_base = (u_char *)fix.smem_start; -printk("screenbase: %p\n",fix.smem_start); disp[con].visual = fix.visual; disp[con].type = fix.type; disp[con].type_aux = fix.type_aux; @@ -302,27 +306,27 @@ printk("screenbase: %p\n",fix.smem_start); disp[con].can_soft_blank = 1; disp[con].inverse = 0; disp[con].line_length = fix.line_length; +#ifdef CONFIG_FBCON_MFB + disp[con].dispsw = &fbcon_mfb; +#else + disp[con].dispsw = NULL; +#endif } -unsigned long dn_fb_init(unsigned long mem_start) { - +unsigned long dnfb_init(unsigned long mem_start) +{ int err; -printk("dn_fb_init\n"); - fb_info.changevar=NULL; - strcpy(&fb_info.modename[0],dn_fb_name); + strcpy(&fb_info.modename[0],dnfb_name); fb_info.fontname[0]=0; fb_info.disp=disp; fb_info.switch_con=&dnfbcon_switch; fb_info.updatevar=&dnfbcon_updatevar; fb_info.blank=&dnfbcon_blank; fb_info.node = -1; - fb_info.fbops = &dn_fb_ops; - fb_info.fbvar = dn_fb_predefined; - fb_info.fbvar_num = NUM_TOTAL_MODES; + fb_info.fbops = &dnfb_ops; -printk("dn_fb_init: register\n"); err=register_framebuffer(&fb_info); if(err < 0) { panic("unable to register apollo frame buffer\n"); @@ -337,35 +341,34 @@ printk("dn_fb_init: register\n"); outb(S_DATA_PLN, AP_CONTROL_2); outw(SWAP(0x3),AP_ROP_1); - printk("apollo frame buffer alive and kicking !\n"); + printk("fb%d: apollo frame buffer alive and kicking !\n", + GET_FB_IDX(fb_info.node)); - dn_fb_get_var(&disp[0].var,0); + dnfb_get_var(&disp[0].var, 0, &fb_info); - dn_fb_set_disp(-1); + dnfb_set_disp(-1, &fb_info); return mem_start; } -static int dnfbcon_switch(int con) { - +static int dnfbcon_switch(int con, struct fb_info *info) +{ currcon=con; return 0; } -static int dnfbcon_updatevar(int con) { - +static int dnfbcon_updatevar(int con, struct fb_info *info) +{ return 0; - } -static void dnfbcon_blank(int blank) { - - printk("dnfbcon_blank: %d\n",blank); +static void dnfbcon_blank(int blank, struct fb_info *info) +{ if(blank) { outb(0, AP_CONTROL_3A); outb((AD_BLT | DST_EQ_SRC | NORM_CREG1) & ~ENAB_DISP, @@ -375,14 +378,4 @@ static void dnfbcon_blank(int blank) { outb(1, AP_CONTROL_3A); outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1); } - - return ; - } - -void dn_video_setup(char *options, int *ints) { - - return; - -} - diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index 9f3c586c7..bbf06d89b 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -35,45 +35,45 @@ static void memcpy_fs(int fsfromto, void *to, void *from, int len) #define CNVT_FROMHW(val,width) (((width) ? ((((val)<<16)-(val)) / \ ((1<<(width))-1)) : 0)) -static u_short red2[] = { +static u16 red2[] = { 0x0000, 0xaaaa }; -static u_short green2[] = { +static u16 green2[] = { 0x0000, 0xaaaa }; -static u_short blue2[] = { +static u16 blue2[] = { 0x0000, 0xaaaa -}; - -static u_short red4[] = { +}; + +static u16 red4[] = { 0x0000, 0xaaaa, 0x5555, 0xffff }; -static u_short green4[] = { +static u16 green4[] = { 0x0000, 0xaaaa, 0x5555, 0xffff }; -static u_short blue4[] = { +static u16 blue4[] = { 0x0000, 0xaaaa, 0x5555, 0xffff }; - -static u_short red8[] = { + +static u16 red8[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa }; -static u_short green8[] = { +static u16 green8[] = { 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa }; -static u_short blue8[] = { +static u16 blue8[] = { 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa }; - -static u_short red16[] = { + +static u16 red16[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa, 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff }; -static u_short green16[] = { +static u16 green16[] = { 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff }; -static u_short blue16[] = { +static u16 blue16[] = { 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff }; @@ -83,10 +83,10 @@ static struct fb_cmap default_2_colors = { }; static struct fb_cmap default_8_colors = { 0, 8, red8, green8, blue8, NULL -}; +}; static struct fb_cmap default_4_colors = { 0, 4, red4, green4, blue4, NULL -}; +}; static struct fb_cmap default_16_colors = { 0, 16, red16, green16, blue16, NULL }; @@ -98,32 +98,32 @@ static struct fb_cmap default_16_colors = { int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) { - int size = len*sizeof(u_short); - + int size = len*sizeof(u16); + if (cmap->len != len) { - if (cmap->red) + if (cmap->red) kfree(cmap->red); if (cmap->green) kfree(cmap->green); if (cmap->blue) kfree(cmap->blue); if (cmap->transp) - kfree(cmap->transp); - cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; - cmap->len = 0; - if (!len) - return 0; + kfree(cmap->transp); + cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; + cmap->len = 0; + if (!len) + return 0; if (!(cmap->red = kmalloc(size, GFP_ATOMIC))) return -1; - if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) + if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) return -1; if (!(cmap->blue = kmalloc(size, GFP_ATOMIC))) - return -1; - if (transp) { + return -1; + if (transp) { if (!(cmap->transp = kmalloc(size, GFP_ATOMIC))) - return -1; - } else - cmap->transp = NULL; + return -1; + } else + cmap->transp = NULL; } cmap->start = 0; cmap->len = len; @@ -150,12 +150,12 @@ void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) size = from->len-fromoff; if (size < 0) return; - size *= sizeof(u_short); + size *= sizeof(u16); memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size); memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size); memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size); if (from->transp && to->transp) - memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size); + memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size); } @@ -164,10 +164,11 @@ void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) */ int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, - int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *)) + int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *, + struct fb_info *), struct fb_info *info) { int i, start; - u_short *red, *green, *blue, *transp; + u16 *red, *green, *blue, *transp; u_int hred, hgreen, hblue, htransp; red = cmap->red; @@ -178,7 +179,7 @@ int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, if (start < 0) return -EINVAL; for (i = 0; i < cmap->len; i++) { - if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp)) + if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp, info)) return 0; hred = CNVT_FROMHW(hred, var->red.length); hgreen = CNVT_FROMHW(hgreen, var->green.length); @@ -212,10 +213,11 @@ int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, */ int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, - int (*setcolreg)(u_int, u_int, u_int, u_int, u_int)) + int (*setcolreg)(u_int, u_int, u_int, u_int, u_int, + struct fb_info *), struct fb_info *info) { int i, start; - u_short *red, *green, *blue, *transp; + u16 *red, *green, *blue, *transp; u_int hred, hgreen, hblue, htransp; red = cmap->red; @@ -250,7 +252,7 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, blue++; if (transp) transp++; - if (setcolreg(start++, hred, hgreen, hblue, htransp)) + if (setcolreg(start++, hred, hgreen, hblue, htransp, info)) return 0; } return 0; @@ -261,22 +263,15 @@ int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc, * Get the default colormap for a specific screen depth */ -struct fb_cmap *fb_default_cmap(int bpp) +struct fb_cmap *fb_default_cmap(int len) { - switch (bpp) { - case 1: - return &default_2_colors; - break; - case 2: - return &default_4_colors; - break; - case 3: - return &default_8_colors; - break; - default: - return &default_16_colors; - break; - } + if (len <= 2) + return &default_2_colors; + if (len <= 4) + return &default_4_colors; + if (len <= 8) + return &default_8_colors; + return &default_16_colors; } diff --git a/drivers/video/fbcon-afb.c b/drivers/video/fbcon-afb.c index a56323041..66132809a 100644 --- a/drivers/video/fbcon-afb.c +++ b/drivers/video/fbcon-afb.c @@ -13,132 +13,242 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> #include "fbcon.h" +#include "fbcon-afb.h" /* - * Prototypes - */ - -static int open_afb(struct display *p); -static void release_afb(void); -static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_afb(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_afb(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations + * Bitplanes à la Amiga */ -static struct display_switch dispsw_afb = { - open_afb, release_afb, bmove_afb, clear_afb, putc_afb, putcs_afb, - rev_char_afb +static u8 expand_table[1024] = { + /* bg = fg = 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* bg = 0, fg = 1 */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + /* bg = 1, fg = 0 */ + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, + 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, + 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, + 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, + 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, + 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, + 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, + 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + /* bg = fg = 1 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - /* - * Bitplanes à la Amiga - */ - -static int open_afb(struct display *p) +void fbcon_afb_setup(struct display *p) { - if (p->type != FB_TYPE_PLANES) - return -EINVAL; - if (p->line_length) p->next_line = p->line_length; else p->next_line = p->var.xres_virtual>>3; p->next_plane = p->var.yres_virtual*p->next_line; - MOD_INC_USE_COUNT; - return 0; } -static void release_afb(void) +void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { - MOD_DEC_USE_COUNT; -} - -static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - u_char *src, *dest, *src0, *dest0; - u_int i, rows; + u8 *src, *dest, *src0, *dest0; + u_short i, j; if (sx == 0 && dx == 0 && width == p->next_line) { src = p->screen_base+sy*p->fontheight*width; dest = p->screen_base+dy*p->fontheight*width; - for (i = p->var.bits_per_pixel; i--;) { + i = p->var.bits_per_pixel; + do { mymemmove(dest, src, height*p->fontheight*width); src += p->next_plane; dest += p->next_plane; - } + } while (--i); } else if (dy <= sy) { src0 = p->screen_base+sy*p->fontheight*p->next_line+sx; dest0 = p->screen_base+dy*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel; i--;) { + i = p->var.bits_per_pixel; + do { src = src0; dest = dest0; - for (rows = height*p->fontheight; rows--;) { + j = height*p->fontheight; + do { mymemmove(dest, src, width); src += p->next_line; dest += p->next_line; - } + } while (--j); src0 += p->next_plane; dest0 += p->next_plane; - } + } while (--i); } else { src0 = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx; dest0 = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel; i--;) { + i = p->var.bits_per_pixel; + do { src = src0; dest = dest0; - for (rows = height*p->fontheight; rows--;) { + j = height*p->fontheight; + do { src -= p->next_line; dest -= p->next_line; mymemmove(dest, src, width); - } + } while (--j); src0 += p->next_plane; dest0 += p->next_plane; - } + } while (--i); } } -static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) +void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) { - u_char *dest, *dest0; - u_int i, rows; + u8 *dest, *dest0; + u_short i, j; int bg; dest0 = p->screen_base+sy*p->fontheight*p->next_line+sx; bg = attr_bgcol_ec(p,conp); - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { + i = p->var.bits_per_pixel; + do { dest = dest0; - for (rows = height*p->fontheight; rows--; dest += p->next_line) + j = height*p->fontheight; + do { if (bg & 1) mymemset(dest, width); else mymemclear(dest, width); + dest += p->next_line; + } while (--j); bg >>= 1; - } + dest0 += p->next_plane; + } while (--i); } -static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy, - int xx) +void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) { - u_char *dest, *dest0, *cdat, *cdat0; - u_int rows, i; - u_char d; + u8 *dest, *dest0, *cdat, *cdat0, *expand; + u_short i, j; int fg, bg; c &= 0xff; @@ -148,25 +258,24 @@ static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy, fg = attr_fgcol(p,conp); bg = attr_bgcol(p,conp); - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { + i = p->var.bits_per_pixel; + do { dest = dest0; cdat = cdat0; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - } + expand = expand_table; + if (bg & 1) + expand += 512; + if (fg & 1) + expand += 256; + j = p->fontheight; + do { + *dest = expand[*cdat++]; + dest += p->next_line; + } while (--j); bg >>= 1; fg >>= 1; - } + dest0 += p->next_plane; + } while (--i); } /* @@ -174,14 +283,13 @@ static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy, * (cfr. fbcon_putcs_ilbm()) */ -static void putcs_afb(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) +void fbcon_afb_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) { - u_char *dest, *dest0, *dest1; - u_char *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40; - u_int rows, i; - u_char c1, c2, c3, c4; - u_long d; + u8 *dest, *dest0, *dest1, *expand; + u8 *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40; + u_short i, j; + u8 c1, c2, c3, c4; int fg0, bg0, fg, bg; dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx; @@ -198,26 +306,25 @@ static void putcs_afb(struct vc_data *conp, struct display *p, const char *s, fg = fg0; bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) { + i = p->var.bits_per_pixel; + do { dest = dest1; cdat1 = cdat10; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat1++; - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - } + expand = expand_table; + if (bg & 1) + expand += 512; + if (fg & 1) + expand += 256; + j = p->fontheight; + do { + *dest = expand[*cdat1++]; + dest += p->next_line; + } while (--j); bg >>= 1; fg >>= 1; - } - } else { /* Fast version */ + dest1 += p->next_plane; + } while (--i); + } else { /* Fast version */ c1 = s[0]; c2 = s[1]; c3 = s[2]; @@ -231,28 +338,39 @@ static void putcs_afb(struct vc_data *conp, struct display *p, const char *s, fg = fg0; bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) { + i = p->var.bits_per_pixel; + do { dest = dest1; cdat1 = cdat10; cdat2 = cdat20; cdat3 = cdat30; cdat4 = cdat40; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++; - if (bg & 1) - if (fg & 1) - *(u_long *)dest = 0xffffffff; - else - *(u_long *)dest = ~d; - else - if (fg & 1) - *(u_long *)dest = d; - else - *(u_long *)dest = 0x00000000; - } + expand = expand_table; + if (bg & 1) + expand += 512; + if (fg & 1) + expand += 256; + j = p->fontheight; + do { +#if defined(__BIG_ENDIAN) + *(u32 *)dest = expand[*cdat1++]<<24 | + expand[*cdat2++]<<16 | + expand[*cdat3++]<<8 | + expand[*cdat4++]; +#elif defined(__LITTLE_ENDIAN) + *(u32 *)dest = expand[*cdat1++] | + expand[*cdat2++]<<8 | + expand[*cdat3++]<<16 | + expand[*cdat4++]<<24; +#else +#error FIXME: No endianness?? +#endif + dest += p->next_line; + } while (--j); bg >>= 1; fg >>= 1; - } + dest1 += p->next_plane; + } while (--i); s += 4; dest0 += 4; xx += 4; @@ -260,10 +378,10 @@ static void putcs_afb(struct vc_data *conp, struct display *p, const char *s, } } -static void rev_char_afb(struct display *p, int xx, int yy) +void fbcon_afb_revc(struct display *p, int xx, int yy) { - u_char *dest, *dest0; - u_int rows, i; + u8 *dest, *dest0; + u_short i, j; int mask; dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx; @@ -275,29 +393,40 @@ static void rev_char_afb(struct display *p, int xx, int yy) * inverting. */ - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { + i = p->var.bits_per_pixel; + do { if (mask & 1) { dest = dest0; - for (rows = p->fontheight; rows--; dest += p->next_line) + j = p->fontheight; + do { *dest = ~*dest; + dest += p->next_line; + } while (--j); } mask >>= 1; - } + dest0 += p->next_plane; + } while (--i); } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_afb(void) -#endif -{ - return(fbcon_register_driver(&dispsw_afb, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_afb); -} -#endif /* MODULE */ +struct display_switch fbcon_afb = { + fbcon_afb_setup, fbcon_afb_bmove, fbcon_afb_clear, fbcon_afb_putc, + fbcon_afb_putcs, fbcon_afb_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_afb); +EXPORT_SYMBOL(fbcon_afb_setup); +EXPORT_SYMBOL(fbcon_afb_bmove); +EXPORT_SYMBOL(fbcon_afb_clear); +EXPORT_SYMBOL(fbcon_afb_putc); +EXPORT_SYMBOL(fbcon_afb_putcs); +EXPORT_SYMBOL(fbcon_afb_revc); diff --git a/drivers/video/fbcon-afb.h b/drivers/video/fbcon-afb.h new file mode 100644 index 000000000..537b3bdd7 --- /dev/null +++ b/drivers/video/fbcon-afb.h @@ -0,0 +1,15 @@ + /* + * Amiga bitplanes (afb) + */ + +extern struct display_switch fbcon_afb; +extern void fbcon_afb_setup(struct display *p); +extern void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_afb_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_afb_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb16.c b/drivers/video/fbcon-cfb16.c index 8bd58ce8a..b27683c95 100644 --- a/drivers/video/fbcon-cfb16.c +++ b/drivers/video/fbcon-cfb16.c @@ -1,6 +1,6 @@ /* * linux/drivers/video/cfb16.c -- Low level frame buffer operations for 16 bpp - * packed pixels + * truecolor packed pixels * * Created 5 Apr 1997 by Geert Uytterhoeven * @@ -13,69 +13,40 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> #include "fbcon.h" - - - /* - * Prototypes - */ - -static int open_cfb16(struct display *p); -static void release_cfb16(void); -static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_cfb16(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_cfb16(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_cfb16(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_cfb16(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_cfb16 = { - open_cfb16, release_cfb16, bmove_cfb16, clear_cfb16, putc_cfb16, - putcs_cfb16, rev_char_cfb16 -}; +#include "fbcon-cfb16.h" /* * 16 bpp packed pixels */ -u_short packed16_cmap[16]; +u16 fbcon_cfb16_cmap[16]; -static u_long tab_cfb16[] = { - 0x00000000,0x0000ffff,0xffff0000,0xffffffff +static u32 tab_cfb16[] = { +#if defined(__BIG_ENDIAN) + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff +#elif defined(__LITTLE_ENDIAN) + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff +#else +#error FIXME: No endianness?? +#endif }; -static int open_cfb16(struct display *p) +void fbcon_cfb16_setup(struct display *p) { - if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 16) - return -EINVAL; - p->next_line = p->var.xres_virtual<<1; p->next_plane = 0; - MOD_INC_USE_COUNT; - return 0; -} - -static void release_cfb16(void) -{ - MOD_DEC_USE_COUNT; } -static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { int bytes = p->next_line, linesize = bytes * p->fontheight, rows; - u_char *src,*dst; + u8 *src, *dst; if (sx == 0 && dx == 0 && width * 16 == bytes) mymemmove(p->screen_base + dy * linesize, @@ -100,82 +71,78 @@ static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_cfb16(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) +void fbcon_cfb16_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) { - u_char *dest0,*dest; - int bytes=p->next_line,lines=height * p->fontheight, rows, i; - u_long bgx; + u8 *dest0, *dest; + int bytes = p->next_line, lines = height * p->fontheight, rows, i; + u32 bgx; dest = p->screen_base + sy * p->fontheight * bytes + sx * 16; - bgx = attr_bgcol_ec(p,conp); - bgx = packed16_cmap[bgx]; + bgx = fbcon_cfb16_cmap[attr_bgcol_ec(p, conp)]; bgx |= (bgx << 16); if (sx == 0 && width * 16 == bytes) for (i = 0 ; i < lines * width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - ((u_long *)dest)[2]=bgx; - ((u_long *)dest)[3]=bgx; - dest+=16; + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + dest += 16; } else { - dest0=dest; + dest0 = dest; for (rows = lines; rows-- ; dest0 += bytes) { - dest=dest0; + dest = dest0; for (i = 0 ; i < width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - ((u_long *)dest)[2]=bgx; - ((u_long *)dest)[3]=bgx; - dest+=16; + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + dest += 16; } } } } -static void putc_cfb16(struct vc_data *conp, struct display *p, int c, int yy, - int xx) +void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) { - u_char *dest,*cdat; - int bytes=p->next_line,rows; - ulong eorx,fgx,bgx; + u8 *dest, *cdat; + int bytes = p->next_line, rows; + u32 eorx, fgx, bgx; c &= 0xff; dest = p->screen_base + yy * p->fontheight * bytes + xx * 16; cdat = p->fontdata + c * p->fontheight; - fgx = attr_fgcol(p,conp); - fgx = packed16_cmap[fgx]; - bgx = attr_bgcol(p,conp); - bgx = packed16_cmap[bgx]; + fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)]; fgx |= (fgx << 16); bgx |= (bgx << 16); eorx = fgx ^ bgx; for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx; - ((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx; + u8 bits = *cdat++; + ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; + ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; + ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; } } -static void putcs_cfb16(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) +void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) { - u_char *cdat, c, *dest, *dest0; - int rows,bytes=p->next_line; - u_long eorx, fgx, bgx; + u8 *cdat, c, *dest, *dest0; + int rows, bytes = p->next_line; + u32 eorx, fgx, bgx; dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 16; - fgx = attr_fgcol(p,conp); - fgx = packed16_cmap[fgx]; - bgx = attr_bgcol(p,conp); - bgx = packed16_cmap[bgx]; + fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)]; fgx |= (fgx << 16); bgx |= (bgx << 16); eorx = fgx ^ bgx; @@ -184,49 +151,50 @@ static void putcs_cfb16(struct vc_data *conp, struct display *p, const char *s, cdat = p->fontdata + c * p->fontheight; for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx; - ((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx; + u8 bits = *cdat++; + ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; + ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; + ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; } - dest0+=16; + dest0 += 16; } } -static void rev_char_cfb16(struct display *p, int xx, int yy) +void fbcon_cfb16_revc(struct display *p, int xx, int yy) { - u_char *dest; - int bytes=p->next_line, rows; + u8 *dest; + int bytes = p->next_line, rows; dest = p->screen_base + yy * p->fontheight * bytes + xx * 16; for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0] ^= 0xffffffff; - ((u_long *)dest)[1] ^= 0xffffffff; - ((u_long *)dest)[2] ^= 0xffffffff; - ((u_long *)dest)[3] ^= 0xffffffff; + ((u32 *)dest)[0] ^= 0xffffffff; + ((u32 *)dest)[1] ^= 0xffffffff; + ((u32 *)dest)[2] ^= 0xffffffff; + ((u32 *)dest)[3] ^= 0xffffffff; } } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_cfb16(void) -#endif -{ - return(fbcon_register_driver(&dispsw_cfb16, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_cfb16); -} -#endif /* MODULE */ +struct display_switch fbcon_cfb16 = { + fbcon_cfb16_setup, fbcon_cfb16_bmove, fbcon_cfb16_clear, fbcon_cfb16_putc, + fbcon_cfb16_putcs, fbcon_cfb16_revc +}; /* * Visible symbols for modules */ -EXPORT_SYMBOL(packed16_cmap); +EXPORT_SYMBOL(fbcon_cfb16); +EXPORT_SYMBOL(fbcon_cfb16_setup); +EXPORT_SYMBOL(fbcon_cfb16_bmove); +EXPORT_SYMBOL(fbcon_cfb16_clear); +EXPORT_SYMBOL(fbcon_cfb16_putc); +EXPORT_SYMBOL(fbcon_cfb16_putcs); +EXPORT_SYMBOL(fbcon_cfb16_revc); +EXPORT_SYMBOL(fbcon_cfb16_cmap); diff --git a/drivers/video/fbcon-cfb16.h b/drivers/video/fbcon-cfb16.h new file mode 100644 index 000000000..905d6329a --- /dev/null +++ b/drivers/video/fbcon-cfb16.h @@ -0,0 +1,16 @@ + /* + * 16 bpp packed pixel (cfb16) + */ + +extern struct display_switch fbcon_cfb16; +extern u16 fbcon_cfb16_cmap[16]; +extern void fbcon_cfb16_setup(struct display *p); +extern void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_cfb16_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb16_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb2.c b/drivers/video/fbcon-cfb2.c new file mode 100644 index 000000000..617c85235 --- /dev/null +++ b/drivers/video/fbcon-cfb2.c @@ -0,0 +1,205 @@ +/* + * linux/drivers/video/cfb2.c -- Low level frame buffer operations for 2 bpp + * packed pixels + * + * Created 26 Dec 1997 by Michael Schmitz + * Based on cfb4.c + * + * 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. + */ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/fb.h> + +#include "fbcon.h" +#include "fbcon-cfb2.h" + + + /* + * 2 bpp packed pixels + */ + + /* + * IFF the font is even pixel aligned (that is to say each + * character start is a byte start in the pixel pairs). That + * avoids us having to mask bytes and means we won't be here + * all week. On a MacII that matters _lots_ + */ + +static u_char nibbletab_cfb2[]={ + 0x00,0x03,0x0c,0x0f, + 0x30,0x33,0x3c,0x3f, + 0xc0,0xc3,0xcc,0xcf, + 0xf0,0xf3,0xfc,0xff +}; + + +void fbcon_cfb2_setup(struct display *p) +{ + p->next_line = p->var.xres_virtual>>2; + p->next_plane = 0; +} + +void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int bytes = p->next_line, linesize = bytes * p->fontheight, rows; + u8 *src,*dst; + + if (sx == 0 && dx == 0 && width * 2 == bytes) { + mymemmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, + height * linesize); + } + else { + if (dy < sy || (dy == sy && dx < sx)) { + src = p->screen_base + sy * linesize + sx * 2; + dst = p->screen_base + dy * linesize + dx * 2; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 2); + src += bytes; + dst += bytes; + } + } + else { + src = p->screen_base + (sy+height) * linesize + sx * 2 + - bytes; + dst = p->screen_base + (dy+height) * linesize + dx * 2 + - bytes; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 2); + src -= bytes; + dst -= bytes; + } + } + } +} + +void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + u8 *dest0,*dest; + int bytes=p->next_line,lines=height * p->fontheight, rows, i; + u32 bgx; + + dest = p->screen_base + sy * p->fontheight * bytes + sx * 2; + + bgx=attr_bgcol_ec(p,conp); + bgx |= (bgx << 2); /* expand the colour to 16 bits */ + bgx |= (bgx << 4); + bgx |= (bgx << 8); + + if (sx == 0 && width * 2 == bytes) { + for (i = 0 ; i < lines * width ; i++) { + ((u16 *)dest)[0]=bgx; + dest+=2; + } + } else { + dest0=dest; + for (rows = lines; rows-- ; dest0 += bytes) { + dest=dest0; + for (i = 0 ; i < width ; i++) { + /* memset ?? */ + ((u16 *)dest)[0]=bgx; + dest+=2; + } + } + } +} + +void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u8 *dest,*cdat; + int bytes=p->next_line,rows; + u32 eorx,fgx,bgx; + + c &= 0xff; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 2; + cdat = p->fontdata + c * p->fontheight; + + fgx=3;/*attr_fgcol(p,conp)&0x0F;*/ + bgx=attr_bgcol(p,conp)&0x0F; + fgx |= (fgx << 2); /* expand color to 8 bits */ + fgx |= (fgx << 4); + bgx |= (bgx << 2); + bgx |= (bgx << 4); + eorx = fgx ^ bgx; + + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u8 *)dest)[0]= + (nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx; + ((u8 *)dest)[1]= + (nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx; + } +} + +void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u8 *cdat, c, *dest, *dest0; + int rows,bytes=p->next_line; + u32 eorx, fgx, bgx; + + dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 2; + fgx=3/*attr_fgcol(p,conp)*/; + bgx=attr_bgcol(p,conp); + fgx |= (fgx << 2); + fgx |= (fgx << 4); + bgx |= (bgx << 2); + bgx |= (bgx << 4); + eorx = fgx ^ bgx; + while (count--) { + c = *s++; + cdat = p->fontdata + c * p->fontheight; + + for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { + ((u8 *)dest)[0]= + (nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx; + ((u8 *)dest)[1]= + (nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx; + } + dest0+=2; + } +} + +void fbcon_cfb2_revc(struct display *p, int xx, int yy) +{ + u8 *dest; + int bytes=p->next_line, rows; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 2; + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u16 *)dest)[0] ^= 0xffff; + } +} + + + /* + * `switch' for the low level operations + */ + +struct display_switch fbcon_cfb2 = { + fbcon_cfb2_setup, fbcon_cfb2_bmove, fbcon_cfb2_clear, fbcon_cfb2_putc, + fbcon_cfb2_putcs, fbcon_cfb2_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_cfb2); +EXPORT_SYMBOL(fbcon_cfb2_setup); +EXPORT_SYMBOL(fbcon_cfb2_bmove); +EXPORT_SYMBOL(fbcon_cfb2_clear); +EXPORT_SYMBOL(fbcon_cfb2_putc); +EXPORT_SYMBOL(fbcon_cfb2_putcs); +EXPORT_SYMBOL(fbcon_cfb2_revc); diff --git a/drivers/video/fbcon-cfb2.h b/drivers/video/fbcon-cfb2.h new file mode 100644 index 000000000..4fb3bb13a --- /dev/null +++ b/drivers/video/fbcon-cfb2.h @@ -0,0 +1,15 @@ + /* + * 2 bpp packed pixel (cfb2) + */ + +extern struct display_switch fbcon_cfb2; +extern void fbcon_cfb2_setup(struct display *p); +extern void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb2_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb24.c b/drivers/video/fbcon-cfb24.c new file mode 100644 index 000000000..65a4ad556 --- /dev/null +++ b/drivers/video/fbcon-cfb24.c @@ -0,0 +1,221 @@ +/* + * linux/drivers/video/cfb24.c -- Low level frame buffer operations for 24 bpp + * truecolor packed pixels + * + * Created 7 Mar 1998 by Geert Uytterhoeven + * + * 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. + */ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/fb.h> + +#include "fbcon.h" +#include "fbcon-cfb24.h" + + +#warning Remove this warning after the test cycle was finalized + + + /* + * 24 bpp packed pixels + */ + +u32 fbcon_cfb24_cmap[16]; + +void fbcon_cfb24_setup(struct display *p) +{ + p->next_line = p->line_length ? p->line_length : p->var.xres_virtual*3; + p->next_plane = 0; +} + +void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int bytes = p->next_line, linesize = bytes * p->fontheight, rows; + u8 *src, *dst; + + if (sx == 0 && dx == 0 && width * 24 == bytes) + mymemmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, + height * linesize); + else if (dy < sy || (dy == sy && dx < sx)) { + src = p->screen_base + sy * linesize + sx * 24; + dst = p->screen_base + dy * linesize + dx * 24; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 24); + src += bytes; + dst += bytes; + } + } else { + src = p->screen_base + (sy+height) * linesize + sx * 24 - bytes; + dst = p->screen_base + (dy+height) * linesize + dx * 24 - bytes; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 24); + src -= bytes; + dst -= bytes; + } + } +} + +void fbcon_cfb24_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + u8 *dest0, *dest; + int bytes = p->next_line, lines = height * p->fontheight, rows, i; + u32 bgx; + + dest = p->screen_base + sy * p->fontheight * bytes + sx * 24; + + bgx = fbcon_cfb24_cmap[attr_bgcol_ec(p, conp)]; + + if (sx == 0 && width * 24 == bytes) + for (i = 0 ; i < lines * width ; i++) { + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + ((u32 *)dest)[4] = bgx; + ((u32 *)dest)[5] = bgx; + dest += 24; + } + else { + dest0 = dest; + for (rows = lines; rows-- ; dest0 += bytes) { + dest = dest0; + for (i = 0 ; i < width ; i++) { + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + ((u32 *)dest)[4] = bgx; + ((u32 *)dest)[5] = bgx; + dest += 24; + } + } + } +} + +static inline void store4pixels(u32 d1, u32 d2, u32 d3, u32 d4, u32 *dest) +{ +#if defined(__BIG_ENDIAN) + *dest++ = (d1<<8) | (d2>>16); + *dest++ = (d2<<16) | (d3>>8); + *dest++ = (d3<<24) | d4; +#elif defined(__LITTLE_ENDIAN) +#error Please add support for little endian byteorder +#else +#error FIXME: No endianness?? +#endif +} + +void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u8 *dest, *cdat; + int bytes = p->next_line, rows; + u32 eorx, fgx, bgx; + + c &= 0xff; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 24; + cdat = p->fontdata + c * p->fontheight; + + fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)]; + eorx = fgx ^ bgx; + + for (rows = p->fontheight ; rows-- ; dest += bytes) { + u8 bits = *cdat++; + u32 d1, d2, d3, d4; + d1 = (-(bits >> 7) & eorx) ^ bgx; + d2 = (-(bits >> 6 & 1) & eorx) ^ bgx; + d3 = (-(bits >> 5 & 1) & eorx) ^ bgx; + d4 = (-(bits >> 4 & 1) & eorx) ^ bgx; + store4pixels(d1, d2, d3, d4, (u32 *)dest); + d1 = (-(bits >> 3 & 1) & eorx) ^ bgx; + d2 = (-(bits >> 2 & 1) & eorx) ^ bgx; + d3 = (-(bits >> 1 & 1) & eorx) ^ bgx; + d4 = (-(bits & 1) & eorx) ^ bgx; + store4pixels(d1, d2, d3, d4, (u32 *)(dest+12)); + } +} + +void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u8 *cdat, c, *dest, *dest0; + int rows, bytes = p->next_line; + u32 eorx, fgx, bgx; + + dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 24; + fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)]; + eorx = fgx ^ bgx; + while (count--) { + c = *s++; + cdat = p->fontdata + c * p->fontheight; + + for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { + u8 bits = *cdat++; + u32 d1, d2, d3, d4; + d1 = (-(bits >> 7) & eorx) ^ bgx; + d2 = (-(bits >> 6 & 1) & eorx) ^ bgx; + d3 = (-(bits >> 5 & 1) & eorx) ^ bgx; + d4 = (-(bits >> 4 & 1) & eorx) ^ bgx; + store4pixels(d1, d2, d3, d4, (u32 *)dest); + d1 = (-(bits >> 3 & 1) & eorx) ^ bgx; + d2 = (-(bits >> 2 & 1) & eorx) ^ bgx; + d3 = (-(bits >> 1 & 1) & eorx) ^ bgx; + d4 = (-(bits & 1) & eorx) ^ bgx; + store4pixels(d1, d2, d3, d4, (u32 *)(dest+12)); + } + dest0 += 24; + } +} + +void fbcon_cfb24_revc(struct display *p, int xx, int yy) +{ + u8 *dest; + int bytes = p->next_line, rows; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 24; + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u32 *)dest)[0] ^= 0xffffffff; + ((u32 *)dest)[1] ^= 0xffffffff; + ((u32 *)dest)[2] ^= 0xffffffff; + ((u32 *)dest)[3] ^= 0xffffffff; + ((u32 *)dest)[4] ^= 0xffffffff; + ((u32 *)dest)[5] ^= 0xffffffff; + } +} + + + /* + * `switch' for the low level operations + */ + +struct display_switch fbcon_cfb24 = { + fbcon_cfb24_setup, fbcon_cfb24_bmove, fbcon_cfb24_clear, fbcon_cfb24_putc, + fbcon_cfb24_putcs, fbcon_cfb24_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_cfb24); +EXPORT_SYMBOL(fbcon_cfb24_setup); +EXPORT_SYMBOL(fbcon_cfb24_bmove); +EXPORT_SYMBOL(fbcon_cfb24_clear); +EXPORT_SYMBOL(fbcon_cfb24_putc); +EXPORT_SYMBOL(fbcon_cfb24_putcs); +EXPORT_SYMBOL(fbcon_cfb24_revc); +EXPORT_SYMBOL(fbcon_cfb24_cmap); diff --git a/drivers/video/fbcon-cfb24.h b/drivers/video/fbcon-cfb24.h new file mode 100644 index 000000000..bd672ab20 --- /dev/null +++ b/drivers/video/fbcon-cfb24.h @@ -0,0 +1,16 @@ + /* + * 24 bpp packed pixel (cfb24) + */ + +extern struct display_switch fbcon_cfb24; +extern u32 fbcon_cfb24_cmap[16]; +extern void fbcon_cfb24_setup(struct display *p); +extern void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_cfb24_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb24_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb32.c b/drivers/video/fbcon-cfb32.c new file mode 100644 index 000000000..8c3306458 --- /dev/null +++ b/drivers/video/fbcon-cfb32.c @@ -0,0 +1,205 @@ +/* + * linux/drivers/video/cfb32.c -- Low level frame buffer operations for 32 bpp + * truecolor packed pixels + * + * Created 28 Dec 1997 by Geert Uytterhoeven + * + * 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. + */ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/fb.h> + +#include "fbcon.h" +#include "fbcon-cfb32.h" + + + /* + * 32 bpp packed pixels + */ + +u32 fbcon_cfb32_cmap[16]; + +void fbcon_cfb32_setup(struct display *p) +{ + p->next_line = p->line_length ? p->line_length : p->var.xres_virtual<<2; + p->next_plane = 0; +} + +void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int bytes = p->next_line, linesize = bytes * p->fontheight, rows; + u8 *src, *dst; + + if (sx == 0 && dx == 0 && width * 32 == bytes) + mymemmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, + height * linesize); + else if (dy < sy || (dy == sy && dx < sx)) { + src = p->screen_base + sy * linesize + sx * 32; + dst = p->screen_base + dy * linesize + dx * 32; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 32); + src += bytes; + dst += bytes; + } + } else { + src = p->screen_base + (sy+height) * linesize + sx * 32 - bytes; + dst = p->screen_base + (dy+height) * linesize + dx * 32 - bytes; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 32); + src -= bytes; + dst -= bytes; + } + } +} + +void fbcon_cfb32_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + u8 *dest0, *dest; + int bytes = p->next_line, lines = height * p->fontheight, rows, i; + u32 bgx; + + dest = p->screen_base + sy * p->fontheight * bytes + sx * 32; + + bgx = fbcon_cfb32_cmap[attr_bgcol_ec(p, conp)]; + + if (sx == 0 && width * 32 == bytes) + for (i = 0 ; i < lines * width ; i++) { + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + ((u32 *)dest)[4] = bgx; + ((u32 *)dest)[5] = bgx; + ((u32 *)dest)[6] = bgx; + ((u32 *)dest)[7] = bgx; + dest += 32; + } + else { + dest0 = dest; + for (rows = lines; rows-- ; dest0 += bytes) { + dest = dest0; + for (i = 0 ; i < width ; i++) { + ((u32 *)dest)[0] = bgx; + ((u32 *)dest)[1] = bgx; + ((u32 *)dest)[2] = bgx; + ((u32 *)dest)[3] = bgx; + ((u32 *)dest)[4] = bgx; + ((u32 *)dest)[5] = bgx; + ((u32 *)dest)[6] = bgx; + ((u32 *)dest)[7] = bgx; + dest += 32; + } + } + } +} + +void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u8 *dest, *cdat; + int bytes = p->next_line, rows; + u32 eorx, fgx, bgx; + + c &= 0xff; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 32; + cdat = p->fontdata + c * p->fontheight; + + fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)]; + eorx = fgx ^ bgx; + + for (rows = p->fontheight ; rows-- ; dest += bytes) { + u8 bits = *cdat++; + ((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx; + ((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx; + ((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx; + ((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx; + ((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx; + ((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx; + ((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx; + ((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx; + } +} + +void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u8 *cdat, c, *dest, *dest0; + int rows, bytes = p->next_line; + u32 eorx, fgx, bgx; + + dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 32; + fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)]; + bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)]; + eorx = fgx ^ bgx; + while (count--) { + c = *s++; + cdat = p->fontdata + c * p->fontheight; + + for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { + u8 bits = *cdat++; + ((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx; + ((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx; + ((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx; + ((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx; + ((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx; + ((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx; + ((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx; + ((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx; + } + dest0 += 32; + } +} + +void fbcon_cfb32_revc(struct display *p, int xx, int yy) +{ + u8 *dest; + int bytes = p->next_line, rows; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 32; + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u32 *)dest)[0] ^= 0xffffffff; + ((u32 *)dest)[1] ^= 0xffffffff; + ((u32 *)dest)[2] ^= 0xffffffff; + ((u32 *)dest)[3] ^= 0xffffffff; + ((u32 *)dest)[4] ^= 0xffffffff; + ((u32 *)dest)[5] ^= 0xffffffff; + ((u32 *)dest)[6] ^= 0xffffffff; + ((u32 *)dest)[7] ^= 0xffffffff; + } +} + + + /* + * `switch' for the low level operations + */ + +struct display_switch fbcon_cfb32 = { + fbcon_cfb32_setup, fbcon_cfb32_bmove, fbcon_cfb32_clear, fbcon_cfb32_putc, + fbcon_cfb32_putcs, fbcon_cfb32_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_cfb32); +EXPORT_SYMBOL(fbcon_cfb32_setup); +EXPORT_SYMBOL(fbcon_cfb32_bmove); +EXPORT_SYMBOL(fbcon_cfb32_clear); +EXPORT_SYMBOL(fbcon_cfb32_putc); +EXPORT_SYMBOL(fbcon_cfb32_putcs); +EXPORT_SYMBOL(fbcon_cfb32_revc); +EXPORT_SYMBOL(fbcon_cfb32_cmap); diff --git a/drivers/video/fbcon-cfb32.h b/drivers/video/fbcon-cfb32.h new file mode 100644 index 000000000..1f74141c2 --- /dev/null +++ b/drivers/video/fbcon-cfb32.h @@ -0,0 +1,16 @@ + /* + * 32 bpp packed pixel (cfb32) + */ + +extern struct display_switch fbcon_cfb32; +extern u32 fbcon_cfb32_cmap[16]; +extern void fbcon_cfb32_setup(struct display *p); +extern void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_cfb32_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb32_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb4.c b/drivers/video/fbcon-cfb4.c new file mode 100644 index 000000000..bcdfcc436 --- /dev/null +++ b/drivers/video/fbcon-cfb4.c @@ -0,0 +1,208 @@ +/* + * linux/drivers/video/cfb4.c -- Low level frame buffer operations for 4 bpp + * packed pixels + * + * Created 26 Dec 1997 by Michael Schmitz + * Based on the old macfb.c 4bpp code by Alan Cox + * + * 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. + */ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/fb.h> + +#include "fbcon.h" +#include "fbcon-cfb4.h" + + + /* + * 4 bpp packed pixels + */ + + /* + * IFF the font is even pixel aligned (that is to say each + * character start is a byte start in the pixel pairs). That + * avoids us having to mask bytes and means we won't be here + * all week. On a MacII that matters _lots_ + */ + +static u16 nibbletab_cfb4[] = { + 0x0000,0x000f,0x00f0,0x00ff, + 0x0f00,0x0f0f,0x0ff0,0x0fff, + 0xf000,0xf00f,0xf0f0,0xf0ff, + 0xff00,0xff0f,0xfff0,0xffff +}; + +void fbcon_cfb4_setup(struct display *p) +{ + p->next_line = p->var.xres_virtual>>1; + p->next_plane = 0; +} + +void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int bytes = p->next_line, linesize = bytes * p->fontheight, rows; + u8 *src,*dst; + + if (sx == 0 && dx == 0 && width * 4 == bytes) { + mymemmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, + height * linesize); + } + else { + if (dy < sy || (dy == sy && dx < sx)) { + src = p->screen_base + sy * linesize + sx * 4; + dst = p->screen_base + dy * linesize + dx * 4; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 4); + src += bytes; + dst += bytes; + } + } + else { + src = p->screen_base + (sy+height) * linesize + sx * 4 + - bytes; + dst = p->screen_base + (dy+height) * linesize + dx * 4 + - bytes; + for (rows = height * p->fontheight ; rows-- ;) { + mymemmove(dst, src, width * 4); + src -= bytes; + dst -= bytes; + } + } + } +} + +void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + u8 *dest0,*dest; + int bytes=p->next_line,lines=height * p->fontheight, rows, i; + u32 bgx; + +/* if(p->screen_base!=0xFDD00020) + mac_boom(1);*/ + dest = p->screen_base + sy * p->fontheight * bytes + sx * 4; + + bgx=attr_bgcol_ec(p,conp); + bgx |= (bgx << 4); /* expand the colour to 32bits */ + bgx |= (bgx << 8); + bgx |= (bgx << 16); + + if (sx == 0 && width * 4 == bytes) { + for (i = 0 ; i < lines * width ; i++) { + ((u32 *)dest)[0]=bgx; + dest+=4; + } + } else { + dest0=dest; + for (rows = lines; rows-- ; dest0 += bytes) { + dest=dest0; + for (i = 0 ; i < width ; i++) { + /* memset ?? */ + ((u32 *)dest)[0]=bgx; + dest+=4; + } + } + } +} + +void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u8 *dest,*cdat; + int bytes=p->next_line,rows; + u32 eorx,fgx,bgx; + + c &= 0xff; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 4; + cdat = p->fontdata + c * p->fontheight; + + fgx=15;/*attr_fgcol(p,conp)&0x0F;*/ + bgx=attr_bgcol(p,conp)&0x0F; + fgx |= (fgx << 4); + fgx |= (fgx << 8); + bgx |= (bgx << 4); + bgx |= (bgx << 8); + eorx = fgx ^ bgx; + + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u16 *)dest)[0]= + (nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx; + ((u16 *)dest)[1]= + (nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx; + } +} + +void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u8 *cdat, c, *dest, *dest0; + int rows,bytes=p->next_line; + u32 eorx, fgx, bgx; + + dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 4; + fgx=15/*attr_fgcol(p,conp)*/; + bgx=attr_bgcol(p,conp); + fgx |= (fgx << 4); + fgx |= (fgx << 8); + fgx |= (fgx << 16); + bgx |= (bgx << 4); + bgx |= (bgx << 8); + bgx |= (bgx << 16); + eorx = fgx ^ bgx; + while (count--) { + c = *s++; + cdat = p->fontdata + c * p->fontheight; + + for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { + ((u16 *)dest)[0]= + (nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx; + ((u16 *)dest)[1]= + (nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx; + } + dest0+=4; + } +} + +void fbcon_cfb4_revc(struct display *p, int xx, int yy) +{ + u8 *dest; + int bytes=p->next_line, rows; + + dest = p->screen_base + yy * p->fontheight * bytes + xx * 4; + for (rows = p->fontheight ; rows-- ; dest += bytes) { + ((u32 *)dest)[0] ^= 0x0f0f0f0f; + } +} + + + /* + * `switch' for the low level operations + */ + +struct display_switch fbcon_cfb4 = { + fbcon_cfb4_setup, fbcon_cfb4_bmove, fbcon_cfb4_clear, fbcon_cfb4_putc, + fbcon_cfb4_putcs, fbcon_cfb4_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_cfb4); +EXPORT_SYMBOL(fbcon_cfb4_setup); +EXPORT_SYMBOL(fbcon_cfb4_bmove); +EXPORT_SYMBOL(fbcon_cfb4_clear); +EXPORT_SYMBOL(fbcon_cfb4_putc); +EXPORT_SYMBOL(fbcon_cfb4_putcs); +EXPORT_SYMBOL(fbcon_cfb4_revc); diff --git a/drivers/video/fbcon-cfb4.h b/drivers/video/fbcon-cfb4.h new file mode 100644 index 000000000..6fe3bc5a4 --- /dev/null +++ b/drivers/video/fbcon-cfb4.h @@ -0,0 +1,15 @@ + /* + * 4 bpp packed pixel (cfb4) + */ + +extern struct display_switch fbcon_cfb4; +extern void fbcon_cfb4_setup(struct display *p); +extern void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb4_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cfb8.c b/drivers/video/fbcon-cfb8.c index 5fa339be1..c559f4025 100644 --- a/drivers/video/fbcon-cfb8.c +++ b/drivers/video/fbcon-cfb8.c @@ -13,70 +13,44 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> #include "fbcon.h" - - - /* - * Prototypes - */ - -static int open_cfb8(struct display *p); -static void release_cfb8(void); -static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_cfb8(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_cfb8(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_cfb8 = { - open_cfb8, release_cfb8, bmove_cfb8, clear_cfb8, putc_cfb8, putcs_cfb8, - rev_char_cfb8 -}; +#include "fbcon-cfb8.h" /* * 8 bpp packed pixels */ -static u_long nibbletab_cfb8[] = { +static u32 nibbletab_cfb8[] = { +#if defined(__BIG_ENDIAN) 0x00000000,0x000000ff,0x0000ff00,0x0000ffff, 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff, 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, 0xffff0000,0xffff00ff,0xffffff00,0xffffffff +#elif defined(__LITTLE_ENDIAN) + 0x00000000,0xff000000,0x00ff0000,0xffff0000, + 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00, + 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff, + 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff +#else +#error FIXME: No endianness?? +#endif }; -static int open_cfb8(struct display *p) +void fbcon_cfb8_setup(struct display *p) { - if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 8) - return -EINVAL; - p->next_line = p->var.xres_virtual; p->next_plane = 0; - MOD_INC_USE_COUNT; - return 0; } -static void release_cfb8(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { int bytes = p->next_line, linesize = bytes * p->fontheight, rows; - u_char *src,*dst; + u8 *src,*dst; if (sx == 0 && dx == 0 && width * 8 == bytes) mymemmove(p->screen_base + dy * linesize, @@ -101,12 +75,12 @@ static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) +void fbcon_cfb8_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) { - u_char *dest0,*dest; + u8 *dest0,*dest; int bytes=p->next_line,lines=height * p->fontheight, rows, i; - u_long bgx; + u32 bgx; dest = p->screen_base + sy * p->fontheight * bytes + sx * 8; @@ -116,8 +90,8 @@ static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, int sx, if (sx == 0 && width * 8 == bytes) for (i = 0 ; i < lines * width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; + ((u32 *)dest)[0]=bgx; + ((u32 *)dest)[1]=bgx; dest+=8; } else { @@ -125,20 +99,20 @@ static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, int sx, for (rows = lines; rows-- ; dest0 += bytes) { dest=dest0; for (i = 0 ; i < width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; + ((u32 *)dest)[0]=bgx; + ((u32 *)dest)[1]=bgx; dest+=8; } } } } -static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy, - int xx) +void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) { - u_char *dest,*cdat; + u8 *dest,*cdat; int bytes=p->next_line,rows; - ulong eorx,fgx,bgx; + u32 eorx,fgx,bgx; c &= 0xff; @@ -154,17 +128,17 @@ static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy, eorx = fgx ^ bgx; for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; + ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; + ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; } } -static void putcs_cfb8(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) +void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) { - u_char *cdat, c, *dest, *dest0; + u8 *cdat, c, *dest, *dest0; int rows,bytes=p->next_line; - u_long eorx, fgx, bgx; + u32 eorx, fgx, bgx; dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 8; fgx=attr_fgcol(p,conp); @@ -179,39 +153,44 @@ static void putcs_cfb8(struct vc_data *conp, struct display *p, const char *s, cdat = p->fontdata + c * p->fontheight; for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ - bgx; + ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; + ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; } dest0+=8; } } -static void rev_char_cfb8(struct display *p, int xx, int yy) +void fbcon_cfb8_revc(struct display *p, int xx, int yy) { - u_char *dest; + u8 *dest; int bytes=p->next_line, rows; dest = p->screen_base + yy * p->fontheight * bytes + xx * 8; for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0] ^= 0x0f0f0f0f; - ((u_long *)dest)[1] ^= 0x0f0f0f0f; + ((u32 *)dest)[0] ^= 0x0f0f0f0f; + ((u32 *)dest)[1] ^= 0x0f0f0f0f; } } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_cfb8(void) -#endif -{ - return(fbcon_register_driver(&dispsw_cfb8, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_cfb8); -} -#endif /* MODULE */ +struct display_switch fbcon_cfb8 = { + fbcon_cfb8_setup, fbcon_cfb8_bmove, fbcon_cfb8_clear, fbcon_cfb8_putc, + fbcon_cfb8_putcs, fbcon_cfb8_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_cfb8); +EXPORT_SYMBOL(fbcon_cfb8_setup); +EXPORT_SYMBOL(fbcon_cfb8_bmove); +EXPORT_SYMBOL(fbcon_cfb8_clear); +EXPORT_SYMBOL(fbcon_cfb8_putc); +EXPORT_SYMBOL(fbcon_cfb8_putcs); +EXPORT_SYMBOL(fbcon_cfb8_revc); diff --git a/drivers/video/fbcon-cfb8.h b/drivers/video/fbcon-cfb8.h new file mode 100644 index 000000000..4c0ffec99 --- /dev/null +++ b/drivers/video/fbcon-cfb8.h @@ -0,0 +1,15 @@ + /* + * 8 bpp packed pixel (cfb8) + */ + +extern struct display_switch fbcon_cfb8; +extern void fbcon_cfb8_setup(struct display *p); +extern void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_cfb8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_cfb8_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-cyber.c b/drivers/video/fbcon-cyber.c deleted file mode 100644 index ee740516d..000000000 --- a/drivers/video/fbcon-cyber.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * linux/drivers/video/cyber.c -- Low level frame buffer operations for the - * CyberVision64 (accelerated) - * - * Created 5 Apr 1997 by Geert Uytterhoeven - * - * 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. - */ - -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/console.h> -#include <linux/string.h> -#include <linux/fb.h> - -#include "fbcon.h" -#include "s3blit.h" - - - /* - * Prototypes - */ - -static int open_cyber(struct display *p); -static void release_cyber(void); -static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_cyber(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_cyber(struct display *p, int xx, int yy); - - - /* - * Acceleration functions in cyberfb.c - */ - -extern void Cyber_WaitQueue(unsigned short fifo); -extern void Cyber_WaitBlit(void); -extern void Cyber_BitBLT(unsigned short curx, unsigned short cury, - unsigned short destx, unsigned short desty, - unsigned short width, unsigned short height, - unsigned short mode); -extern void Cyber_RectFill(unsigned short xx, unsigned short yy, - unsigned short width, unsigned short height, - unsigned short mode, unsigned short fillcolor); -extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_cyber = { - open_cyber, release_cyber, bmove_cyber, clear_cyber, putc_cyber, - putcs_cyber, rev_char_cyber -}; - - - /* - * CyberVision64 (accelerated) - */ - -static int open_cyber(struct display *p) -{ - if (p->type != FB_TYPE_PACKED_PIXELS || - p->var.accel != FB_ACCEL_CYBERVISION) - return -EINVAL; - - p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3; - p->next_plane = 0; - MOD_INC_USE_COUNT; - return 0; -} - -static void release_cyber(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - sx *= 8; dx *= 8; width *= 8; - Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx, - (u_short)(dy*p->fontheight), (u_short)width, - (u_short)(height*p->fontheight), (u_short)S3_NEW); -} - -static void clear_cyber(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width) -{ - unsigned char bg; - - sx *= 8; width *= 8; - bg = attr_bgcol_ec(p,conp); - Cyber_RectFill((u_short)sx, - (u_short)(sy*p->fontheight), - (u_short)width, - (u_short)(height*p->fontheight), - (u_short)S3_NEW, - (u_short)bg); -} - -static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest, *cdat; - u_long tmp; - u_int rows, revs, underl; - u_char d; - u_char fg, bg; - - c &= 0xff; - - dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx; - cdat = p->fontdata+(c*p->fontheight); - fg = p->fgcol; - bg = p->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - Cyber_WaitBlit(); - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((u_long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((u_long*) dest + 1) = tmp; - } -} - -static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) -{ - u_char *dest, *dest0, *cdat; - u_long tmp; - u_int rows, underl; - u_char c, d; - u_char fg, bg; - - dest0 = p->screen_base+yy*p->fontheight*p->next_line+8*xx; - fg = p->fgcol; - bg = p->bgcol; - underl = conp->vc_underline; - - Cyber_WaitBlit(); - while (count--) { - c = *s++; - dest = dest0; - dest0 += 8; - cdat = p->fontdata+(c*p->fontheight); - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((u_long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((u_long*) dest + 1) = tmp; - } - } -} - - -static void rev_char_cyber(struct display *p, int xx, int yy) -{ - unsigned char *dest; - unsigned int rows; - unsigned char fg, bg; - - fg = p->fgcol; - bg = p->bgcol; - - dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx; - Cyber_WaitBlit(); - for (rows = p->fontheight; rows--; dest += p->next_line) { - *dest = (*dest == fg) ? bg : fg; - *(dest+1) = (*(dest + 1) == fg) ? bg : fg; - *(dest+2) = (*(dest + 2) == fg) ? bg : fg; - *(dest+3) = (*(dest + 3) == fg) ? bg : fg; - *(dest+4) = (*(dest + 4) == fg) ? bg : fg; - *(dest+5) = (*(dest + 5) == fg) ? bg : fg; - *(dest+6) = (*(dest + 6) == fg) ? bg : fg; - *(dest+7) = (*(dest + 7) == fg) ? bg : fg; - } -} - - -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_cyber(void) -#endif -{ - return(fbcon_register_driver(&dispsw_cyber, 1)); -} - -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_cyber); -} -#endif /* MODULE */ diff --git a/drivers/video/fbcon-ilbm.c b/drivers/video/fbcon-ilbm.c index 33be946e2..82f9982f6 100644 --- a/drivers/video/fbcon-ilbm.c +++ b/drivers/video/fbcon-ilbm.c @@ -16,33 +16,7 @@ #include <linux/fb.h> #include "fbcon.h" - - - /* - * Prototypes - */ - -static int open_ilbm(struct display *p); -static void release_ilbm(void); -static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_ilbm(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_ilbm = { - open_ilbm, release_ilbm, bmove_ilbm, clear_ilbm, putc_ilbm, putcs_ilbm, - rev_char_ilbm -}; +#include "fbcon-ilbm.h" /* @@ -56,11 +30,8 @@ static struct display_switch dispsw_ilbm = { * much performance loss? */ -static int open_ilbm(struct display *p) +void fbcon_ilbm_setup(struct display *p) { - if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux == 2) - return -EINVAL; - if (p->line_length) { p->next_line = p->line_length*p->var.bits_per_pixel; p->next_plane = p->line_length; @@ -68,24 +39,17 @@ static int open_ilbm(struct display *p) p->next_line = p->type_aux; p->next_plane = p->type_aux/p->var.bits_per_pixel; } - MOD_INC_USE_COUNT; - return 0; } -static void release_ilbm(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { if (sx == 0 && dx == 0 && width == p->next_plane) mymemmove(p->screen_base+dy*p->fontheight*p->next_line, p->screen_base+sy*p->fontheight*p->next_line, height*p->fontheight*p->next_line); else { - u_char *src, *dest; + u8 *src, *dest; u_int i; if (dy <= sy) { @@ -108,10 +72,10 @@ static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) +void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) { - u_char *dest; + u8 *dest; u_int i, rows; int bg, bg0; @@ -130,12 +94,12 @@ static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx, } } -static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, - int xx) +void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) { - u_char *dest, *cdat; + u8 *dest, *cdat; u_int rows, i; - u_char d; + u8 d; int fg0, bg0, fg, bg; c &= 0xff; @@ -150,16 +114,17 @@ static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, fg = fg0; bg = bg0; for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) + if (bg & 1){ if (fg & 1) *dest = 0xff; else *dest = ~d; - else + }else{ if (fg & 1) *dest = d; else *dest = 0x00; + } bg >>= 1; fg >>= 1; } @@ -181,13 +146,13 @@ static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, * -- Geert */ -static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) +void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) { - u_char *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4; + u8 *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4; u_int rows, i; - u_char c1, c2, c3, c4; - u_long d; + u8 c1, c2, c3, c4; + u32 d; int fg0, bg0, fg, bg; dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx; @@ -206,16 +171,17 @@ static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, fg = fg0; bg = bg0; for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) + if (bg & 1){ if (fg & 1) *dest = 0xff; else *dest = ~d; - else + }else{ if (fg & 1) *dest = d; else *dest = 0x00; + } bg >>= 1; fg >>= 1; } @@ -232,20 +198,27 @@ static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, cdat3 = p->fontdata+c3*p->fontheight; cdat4 = p->fontdata+c4*p->fontheight; for (rows = p->fontheight; rows--;) { +#if defined(__BIG_ENDIAN) d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++; +#elif defined(__LITTLE_ENDIAN) + d = *cdat1++ | *cdat2++<<8 | *cdat3++<<16 | *cdat4++<<32); +#else +#error FIXME: No endianness?? +#endif fg = fg0; bg = bg0; for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) + if (bg & 1){ if (fg & 1) - *(u_long *)dest = 0xffffffff; + *(u32 *)dest = 0xffffffff; else - *(u_long *)dest = ~d; - else + *(u32 *)dest = ~d; + }else{ if (fg & 1) - *(u_long *)dest = d; + *(u32 *)dest = d; else - *(u_long *)dest = 0x00000000; + *(u32 *)dest = 0x00000000; + } bg >>= 1; fg >>= 1; } @@ -257,9 +230,9 @@ static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, } } -static void rev_char_ilbm(struct display *p, int xx, int yy) +void fbcon_ilbm_revc(struct display *p, int xx, int yy) { - u_char *dest, *dest0; + u8 *dest, *dest0; u_int rows, i; int mask; @@ -283,18 +256,24 @@ static void rev_char_ilbm(struct display *p, int xx, int yy) } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_ilbm(void) -#endif -{ - return(fbcon_register_driver(&dispsw_ilbm, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_ilbm); -} -#endif /* MODULE */ +struct display_switch fbcon_ilbm = { + fbcon_ilbm_setup, fbcon_ilbm_bmove, fbcon_ilbm_clear, fbcon_ilbm_putc, + fbcon_ilbm_putcs, fbcon_ilbm_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_ilbm); +EXPORT_SYMBOL(fbcon_ilbm_setup); +EXPORT_SYMBOL(fbcon_ilbm_bmove); +EXPORT_SYMBOL(fbcon_ilbm_clear); +EXPORT_SYMBOL(fbcon_ilbm_putc); +EXPORT_SYMBOL(fbcon_ilbm_putcs); +EXPORT_SYMBOL(fbcon_ilbm_revc); diff --git a/drivers/video/fbcon-ilbm.h b/drivers/video/fbcon-ilbm.h new file mode 100644 index 000000000..e2434c7d1 --- /dev/null +++ b/drivers/video/fbcon-ilbm.h @@ -0,0 +1,15 @@ + /* + * Amiga interleaved bitplanes (ilbm) + */ + +extern struct display_switch fbcon_ilbm; +extern void fbcon_ilbm_setup(struct display *p); +extern void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_ilbm_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-iplan2p2.c b/drivers/video/fbcon-iplan2p2.c index 3973a0013..adef5819d 100644 --- a/drivers/video/fbcon-iplan2p2.c +++ b/drivers/video/fbcon-iplan2p2.c @@ -14,41 +14,13 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> -#include "fbcon.h" - - -#ifndef __mc68000__ -#error No support for non-m68k yet -#endif +#include <asm/byteorder.h> - - /* - * Prototypes - */ - -static int open_iplan2p2(struct display *p); -static void release_iplan2p2(void); -static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_iplan2p2(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_iplan2p2(struct display *display, int xx, int yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_iplan2p2 = { - open_iplan2p2, release_iplan2p2, bmove_iplan2p2, clear_iplan2p2, - putc_iplan2p2, putcs_iplan2p2, rev_char_iplan2p2 -}; +#include "fbcon.h" +#include "fbcon-iplan2p2.h" /* @@ -65,8 +37,19 @@ static struct display_switch dispsw_iplan2p2 = { * The intensity bit (b3) is shifted into b1. */ -#define COLOR_2P(c) (((c & 7) >= 3 && (c & 7) != 4) | (c & 8) >> 2) +static const u8 color_2p[] = { 0, 0, 0, 1, 0, 1, 1, 1, 2, 2, 2, 3, 2, 3, 3, 3 }; +#define COLOR_2P(c) color_2p[c] +/* Perform the m68k movepw operation. */ +static inline void movepw(u8 *d, u16 val) +{ +#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060 + asm volatile ("movepw %1,%0@(0)" : : "a" (d), "d" (val)); +#else + d[0] = (val >> 16) & 0xff; + d[2] = val & 0xff; +#endif +} /* Sets the bytes in the visible column at d, height h, to the value * val for a 2 plane screen. The the bis of the color in 'color' are @@ -77,16 +60,13 @@ static struct display_switch dispsw_iplan2p2 = { * *(d+2) = (color & 2) ? 0xff : 0; */ -static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, int bpr) +static __inline__ void memclear_2p_col(void *d, size_t h, u16 val, int bpr) { -#ifdef __mc68000__ - __asm__ __volatile__ ("1: movepw %4,%0@(0)\n\t" - "addal %5,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val), "r" (bpr)); -#else /* !m68k */ -#endif /* !m68k */ + u8 *dd = d; + do { + movepw(dd, val); + dd += bpr; + } while (--h); } /* Sets a 2 plane region from 'd', length 'count' bytes, to the color @@ -98,9 +78,9 @@ static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, int bpr) * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0; */ -static __inline__ void memset_even_2p(void *d, size_t count, u_long val) +static __inline__ void memset_even_2p(void *d, size_t count, u32 val) { - u_long *dd = d; + u32 *dd = d; count /= 4; while (count--) @@ -111,7 +91,7 @@ static __inline__ void memset_even_2p(void *d, size_t count, u_long val) static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr) { - u_char *dd = d, *ss = s; + u8 *dd = d, *ss = s; while (h--) { dd[0] = ss[0]; @@ -124,81 +104,54 @@ static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr) /* This expands a 2 bit color into a short for movepw (2 plane) operations. */ -static __inline__ u_short expand2w(u_char c) +static const u16 two2byte[] = { + 0x0000, 0xff00, 0x00ff, 0xffff +}; + +static __inline__ u16 expand2w(u8 c) { - u_short rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c)); -#endif /* !m68k */ - return(rv); + return two2byte[c]; } + /* This expands a 2 bit color into one long for a movel operation * (2 planes). */ -static __inline__ u_long expand2l(u_char c) +static const u32 two2word[] = { +#ifndef __LITTLE_ENDIAN + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff +#else + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff +#endif +}; + +static __inline__ u32 expand2l(u8 c) { - u_long rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("lsrb #1,%2\n\t" - "scs %0\n\t" - "extw %0\n\t" - "swap %0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "extw %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c)); -#endif /* !m68k */ - return rv; + return two2word[c]; } /* This duplicates a byte 2 times into a short. */ -static __inline__ u_short dup2w(u_char c) +static __inline__ u16 dup2w(u8 c) { - ushort rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("moveb %1,%0\n\t" - "lslw #8,%0\n\t" - "moveb %1,%0\n\t" - : "=&d" (rv) - : "d" (c)); -#endif /* !m68k */ - return( rv ); + u16 rv; + + rv = c; + rv |= c << 8; + return rv; } -static int open_iplan2p2(struct display *p) +void fbcon_iplan2p2_setup(struct display *p) { - if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 || - p->var.bits_per_pixel != 2) - return -EINVAL; - p->next_line = p->var.xres_virtual>>2; p->next_plane = 2; - MOD_INC_USE_COUNT; - return 0; } -static void release_iplan2p2(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { /* bmove() has to distinguish two major cases: If both, source and * destination, start at even addresses or both are at odd @@ -212,7 +165,7 @@ static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx, * all movements by memmove_col(). */ - if (sx == 0 && dx == 0 && width == p->next_line/2) { + if (sx == 0 && dx == 0 && width * 2 == p->next_line) { /* Special (but often used) case: Moving whole lines can be * done with memmove() */ @@ -221,8 +174,8 @@ static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx, p->next_line * height * p->fontheight); } else { int rows, cols; - u_char *src; - u_char *dst; + u8 *src; + u8 *dst; int bytes = p->next_line; int linesize = bytes * p->fontheight; u_int colsize = height * p->fontheight; @@ -298,21 +251,21 @@ static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) +void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) { - ulong offset; - u_char *start; + u32 offset; + u8 *start; int rows; int bytes = p->next_line; int lines = height * p->fontheight; - ulong size; - u_long cval; - u_short pcval; + u32 size; + u32 cval; + u16 pcval; cval = expand2l (COLOR_2P (attr_bgcol_ec(p,conp))); - if (sx == 0 && width == bytes/2) { + if (sx == 0 && width * 2 == bytes) { offset = sy * bytes * p->fontheight; size = lines * bytes; memset_even_2p(p->screen_base+offset, size, cval); @@ -344,14 +297,14 @@ static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy, } } -static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c, - int yy, int xx) +void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) { - u_char *dest; - u_char *cdat; + u8 *dest; + u8 *cdat; int rows; int bytes = p->next_line; - ulong eorx, fgx, bgx, fdx; + u16 eorx, fgx, bgx, fdx; c &= 0xff; @@ -364,22 +317,18 @@ static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c, for (rows = p->fontheight ; rows-- ; dest += bytes) { fdx = dup2w(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepw %1,%0@(0)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); -#endif /* !m68k */ + movepw(dest, (fdx & eorx) ^ bgx); } } -static void putcs_iplan2p2(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) +void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) { - u_char *dest, *dest0; - u_char *cdat, c; + u8 *dest, *dest0; + u8 *cdat, c; int rows; int bytes; - ulong eorx, fgx, bgx, fdx; + u16 eorx, fgx, bgx, fdx; bytes = p->next_line; dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1); @@ -393,19 +342,15 @@ static void putcs_iplan2p2(struct vc_data *conp, struct display *p, for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { fdx = dup2w(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepw %1,%0@(0)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); -#endif /* !m68k */ + movepw(dest, (fdx & eorx) ^ bgx); } INC_2P(dest0); } } -static void rev_char_iplan2p2(struct display *p, int xx, int yy) +void fbcon_iplan2p2_revc(struct display *p, int xx, int yy) { - u_char *dest; + u8 *dest; int j; int bytes; @@ -425,18 +370,24 @@ static void rev_char_iplan2p2(struct display *p, int xx, int yy) } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_iplan2p2(void) -#endif -{ - return(fbcon_register_driver(&dispsw_iplan2p2, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_iplan2p2); -} -#endif /* MODULE */ +struct display_switch fbcon_iplan2p2 = { + fbcon_iplan2p2_setup, fbcon_iplan2p2_bmove, fbcon_iplan2p2_clear, + fbcon_iplan2p2_putc, fbcon_iplan2p2_putcs, fbcon_iplan2p2_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_iplan2p2); +EXPORT_SYMBOL(fbcon_iplan2p2_setup); +EXPORT_SYMBOL(fbcon_iplan2p2_bmove); +EXPORT_SYMBOL(fbcon_iplan2p2_clear); +EXPORT_SYMBOL(fbcon_iplan2p2_putc); +EXPORT_SYMBOL(fbcon_iplan2p2_putcs); +EXPORT_SYMBOL(fbcon_iplan2p2_revc); diff --git a/drivers/video/fbcon-iplan2p2.h b/drivers/video/fbcon-iplan2p2.h new file mode 100644 index 000000000..ae18a1b32 --- /dev/null +++ b/drivers/video/fbcon-iplan2p2.h @@ -0,0 +1,15 @@ + /* + * Atari interleaved bitplanes (2 planes) (iplan2p2) + */ + +extern struct display_switch fbcon_iplan2p2; +extern void fbcon_iplan2p2_setup(struct display *p); +extern void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p, + int sy, int sx, int height, int width); +extern void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_iplan2p2_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-iplan2p4.c b/drivers/video/fbcon-iplan2p4.c index e3a10f0ea..b299701c4 100644 --- a/drivers/video/fbcon-iplan2p4.c +++ b/drivers/video/fbcon-iplan2p4.c @@ -14,41 +14,13 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> -#include "fbcon.h" - - -#ifndef __mc68000__ -#error No support for non-m68k yet -#endif - - - /* - * Prototypes - */ - -static int open_iplan2p4(struct display *p); -static void release_iplan2p4(void); -static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_iplan2p4(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_iplan2p4(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations - */ +#include <asm/byteorder.h> -static struct display_switch dispsw_iplan2p4 = { - open_iplan2p4, release_iplan2p4, bmove_iplan2p4, clear_iplan2p4, - putc_iplan2p4, putcs_iplan2p4, rev_char_iplan2p4 -}; +#include "fbcon.h" +#include "fbcon-iplan2p4.h" /* @@ -60,6 +32,18 @@ static struct display_switch dispsw_iplan2p4 = { #define INC_4P(p) do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0) #define DEC_4P(p) do { if ((long)(--(p)) & 1) (p) -= 6; } while(0) +/* Perform the m68k movepl operation. */ +static inline void movepl(u8 *d, u32 val) +{ +#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060 + asm volatile ("movepl %1,%0@(0)" : : "a" (d), "d" (val)); +#else + d[0] = (val >> 24) & 0xff; + d[2] = (val >> 16) & 0xff; + d[4] = (val >> 8) & 0xff; + d[6] = val & 0xff; +#endif +} /* Sets the bytes in the visible column at d, height h, to the value * val for a 4 plane screen. The the bis of the color in 'color' are @@ -72,15 +56,13 @@ static struct display_switch dispsw_iplan2p4 = { * *(d+6) = (color & 8) ? 0xff : 0; */ -static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr) +static __inline__ void memclear_4p_col(void *d, size_t h, u32 val, int bpr) { -#ifdef __mc68000__ - __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t" - "addal %5,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val), "r" (bpr)); -#endif /* !m68k */ + u8 *dd = d; + do { + movepl(dd, val); + dd += bpr; + } while (--h); } /* Sets a 4 plane region from 'd', length 'count' bytes, to the color @@ -94,10 +76,10 @@ static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr) * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0; */ -static __inline__ void memset_even_4p(void *d, size_t count, u_long val1, - u_long val2) +static __inline__ void memset_even_4p(void *d, size_t count, u32 val1, + u32 val2) { - u_long *dd = d; + u32 *dd = d; count /= 8; while (count--) { @@ -110,7 +92,7 @@ static __inline__ void memset_even_4p(void *d, size_t count, u_long val1, static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr) { - u_char *dd = d, *ss = s; + u8 *dd = d, *ss = s; while (h--) { dd[0] = ss[0]; @@ -125,99 +107,59 @@ static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr) /* This expands a 4 bit color into a long for movepl (4 plane) operations. */ -static __inline__ u_long expand4l(u_char c) +static const u32 four2byte[] = { + 0x00000000, 0xff000000, 0x00ff0000, 0xffff0000, + 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00, + 0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff, + 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff +}; + +static __inline__ u32 expand4l(u8 c) { - u_long rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c)); -#endif /* !m68k */ - return(rv); + return four2byte[c]; } + /* This expands a 4 bit color into two longs for two movel operations * (4 planes). */ -static __inline__ void expand4dl(u_char c, u_long *ret1, u_long *ret2) +static const u32 two2word[] = { +#ifndef __LITTLE_ENDIAN + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff, +#else + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff, +#endif +}; + +static __inline__ void expand4dl(u8 c, u32 *ret1, u32 *ret2) { - u_long rv1, rv2; - -#ifdef __mc68000__ - __asm__ __volatile__ ("lsrb #1,%3\n\t" - "scs %0\n\t" - "extw %0\n\t" - "swap %0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "extw %0\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "extw %1\n\t" - "swap %1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "extw %1" - : "=&d" (rv1), "=&d" (rv2), "=d" (c) - : "2" (c)); -#endif /* !m68k */ - *ret1 = rv1; - *ret2 = rv2; + *ret1 = two2word[c & 3]; + *ret2 = two2word[c >> 2]; } /* This duplicates a byte 4 times into a long. */ -static __inline__ u_long dup4l(u_char c) +static __inline__ u32 dup4l(u8 c) { - ushort tmp; - ulong rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("moveb %2,%0\n\t" - "lslw #8,%0\n\t" - "moveb %2,%0\n\t" - "movew %0,%1\n\t" - "swap %0\n\t" - "movew %1,%0" - : "=&d" (rv), "=d" (tmp) - : "d" (c)); -#endif /* !m68k */ - return(rv); + u32 rv; + + rv = c; + rv |= rv << 8; + rv |= rv << 16; + return rv; } -static int open_iplan2p4(struct display *p) +void fbcon_iplan2p4_setup(struct display *p) { - if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 || - p->var.bits_per_pixel != 4) - return -EINVAL; - p->next_line = p->var.xres_virtual>>1; p->next_plane = 2; - MOD_INC_USE_COUNT; - return 0; } -static void release_iplan2p4(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { /* bmove() has to distinguish two major cases: If both, source and * destination, start at even addresses or both are at odd @@ -231,7 +173,7 @@ static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx, * all movements by memmove_col(). */ - if (sx == 0 && dx == 0 && width == p->next_line/4) { + if (sx == 0 && dx == 0 && width * 4 == p->next_line) { /* Special (but often used) case: Moving whole lines can be *done with memmove() */ @@ -240,8 +182,8 @@ static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx, p->next_line * height * p->fontheight); } else { int rows, cols; - u_char *src; - u_char *dst; + u8 *src; + u8 *dst; int bytes = p->next_line; int linesize = bytes * p->fontheight; u_int colsize = height * p->fontheight; @@ -320,20 +262,20 @@ static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) +void fbcon_iplan2p4_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) { - ulong offset; - u_char *start; + u32 offset; + u8 *start; int rows; int bytes = p->next_line; int lines = height * p->fontheight; - ulong size; - u_long cval1, cval2, pcval; + u32 size; + u32 cval1, cval2, pcval; expand4dl(attr_bgcol_ec(p,conp), &cval1, &cval2); - if (sx == 0 && width == bytes/4) { + if (sx == 0 && width * 4 == bytes) { offset = sy * bytes * p->fontheight; size = lines * bytes; memset_even_4p(p->screen_base+offset, size, cval1, cval2); @@ -365,14 +307,14 @@ static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy, } } -static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c, - int yy, int xx) +void fbcon_iplan2p4_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) { - u_char *dest; - u_char *cdat; + u8 *dest; + u8 *cdat; int rows; int bytes = p->next_line; - ulong eorx, fgx, bgx, fdx; + u32 eorx, fgx, bgx, fdx; c &= 0xff; @@ -385,22 +327,18 @@ static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c, for(rows = p->fontheight ; rows-- ; dest += bytes) { fdx = dup4l(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepl %1,%0@(0)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); -#endif /* !m68k */ + movepl(dest, (fdx & eorx) ^ bgx); } } -static void putcs_iplan2p4(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) +void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) { - u_char *dest, *dest0; - u_char *cdat, c; + u8 *dest, *dest0; + u8 *cdat, c; int rows; int bytes; - ulong eorx, fgx, bgx, fdx; + u32 eorx, fgx, bgx, fdx; bytes = p->next_line; dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1); @@ -421,19 +359,15 @@ static void putcs_iplan2p4(struct vc_data *conp, struct display *p, for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { fdx = dup4l(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepl %1,%0@(0)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); -#endif /* !m68k */ + movepl(dest, (fdx & eorx) ^ bgx); } INC_4P(dest0); } } -static void rev_char_iplan2p4(struct display *p, int xx, int yy) +void fbcon_iplan2p4_revc(struct display *p, int xx, int yy) { - u_char *dest; + u8 *dest; int j; int bytes; @@ -456,18 +390,24 @@ static void rev_char_iplan2p4(struct display *p, int xx, int yy) } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_iplan2p4(void) -#endif -{ - return(fbcon_register_driver(&dispsw_iplan2p4, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_iplan2p4); -} -#endif /* MODULE */ +struct display_switch fbcon_iplan2p4 = { + fbcon_iplan2p4_setup, fbcon_iplan2p4_bmove, fbcon_iplan2p4_clear, + fbcon_iplan2p4_putc, fbcon_iplan2p4_putcs, fbcon_iplan2p4_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_iplan2p4); +EXPORT_SYMBOL(fbcon_iplan2p4_setup); +EXPORT_SYMBOL(fbcon_iplan2p4_bmove); +EXPORT_SYMBOL(fbcon_iplan2p4_clear); +EXPORT_SYMBOL(fbcon_iplan2p4_putc); +EXPORT_SYMBOL(fbcon_iplan2p4_putcs); +EXPORT_SYMBOL(fbcon_iplan2p4_revc); diff --git a/drivers/video/fbcon-iplan2p4.h b/drivers/video/fbcon-iplan2p4.h new file mode 100644 index 000000000..ae3b38494 --- /dev/null +++ b/drivers/video/fbcon-iplan2p4.h @@ -0,0 +1,15 @@ + /* + * Atari interleaved bitplanes (4 planes) (iplan2p4) + */ + +extern struct display_switch fbcon_iplan2p4; +extern void fbcon_iplan2p4_setup(struct display *p); +extern void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_iplan2p4_clear(struct vc_data *conp, struct display *p, + int sy, int sx, int height, int width); +extern void fbcon_iplan2p4_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_iplan2p4_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-iplan2p8.c b/drivers/video/fbcon-iplan2p8.c index 78f2f2e0e..5ea15c26b 100644 --- a/drivers/video/fbcon-iplan2p8.c +++ b/drivers/video/fbcon-iplan2p8.c @@ -14,41 +14,13 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> -#include "fbcon.h" - - -#ifndef __mc68000__ -#error No support for non-m68k yet -#endif - - - /* - * Prototypes - */ - -static int open_iplan2p8(struct display *p); -static void release_iplan2p8(void); -static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_iplan2p8(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_iplan2p8(struct display *display, int xx, int yy); - - - /* - * `switch' for the low level operations - */ +#include <asm/byteorder.h> -static struct display_switch dispsw_iplan2p8 = { - open_iplan2p8, release_iplan2p8, bmove_iplan2p8, clear_iplan2p8, - putc_iplan2p8, putcs_iplan2p8, rev_char_iplan2p8 -}; +#include "fbcon.h" +#include "fbcon-iplan2p8.h" /* @@ -65,9 +37,26 @@ static struct display_switch dispsw_iplan2p8 = { #define INC_8P(p) do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0) #define DEC_8P(p) do { if ((long)(--(p)) & 1) (p) -= 14; } while(0) +/* Perform the m68k movepl operation extended to 64 bits. */ +static inline void movepl2(u8 *d, u32 val1, u32 val2) +{ +#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060 + asm volatile ("movepl %1,%0@(0); movepl %2,%0@(8)" + : : "a" (d), "d" (val1), "d" (val2)); +#else + d[0] = (val1 >> 24) & 0xff; + d[2] = (val1 >> 16) & 0xff; + d[4] = (val1 >> 8) & 0xff; + d[6] = val1 & 0xff; + d[8] = (val2 >> 24) & 0xff; + d[10] = (val2 >> 16) & 0xff; + d[12] = (val2 >> 8) & 0xff; + d[14] = val2 & 0xff; +#endif +} /* Sets the bytes in the visible column at d, height h, to the value - * val1,val2 for a 8 plane screen. The the bis of the color in 'color' are + * val1,val2 for a 8 plane screen. The bits of the color in 'color' are * moved (8 times) to the respective bytes. This means: * * for(h times; d += bpr) @@ -81,18 +70,14 @@ static struct display_switch dispsw_iplan2p8 = { * *(d+14) = (color & 128) ? 0xff : 0; */ -static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1, - u_long val2, int bpr) +static __inline__ void memclear_8p_col(void *d, size_t h, u32 val1, + u32 val2, int bpr) { -#ifdef __mc68000__ - __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t" - "movepl %5,%0@(8)\n\t" - "addal %6,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val1), "d" (val2), - "r" (bpr)); -#endif /* !m68k */ + u8 *dd = d; + do { + movepl2(dd, val1, val2); + dd += bpr; + } while (--h); } /* Sets a 8 plane region from 'd', length 'count' bytes, to the color @@ -110,10 +95,10 @@ static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1, * *(d+14) = *(d+15) = (color & 128) ? 0xff : 0; */ -static __inline__ void memset_even_8p(void *d, size_t count, u_long val1, - u_long val2, u_long val3, u_long val4) +static __inline__ void memset_even_8p(void *d, size_t count, u32 val1, + u32 val2, u32 val3, u32 val4) { - u_long *dd = d; + u32 *dd = d; count /= 16; while (count--) { @@ -128,7 +113,7 @@ static __inline__ void memset_even_8p(void *d, size_t count, u_long val1, static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr) { - u_char *dd = d, *ss = s; + u8 *dd = d, *ss = s; while (h--) { dd[0] = ss[0]; @@ -149,124 +134,64 @@ static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr) * operations. */ -static __inline__ void expand8dl(u_char c, u_long *ret1, u_long *ret2) +static const u32 four2long[] = +{ + 0x00000000, 0xff000000, 0x00ff0000, 0xffff0000, + 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00, + 0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff, + 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff, +}; + +static __inline__ void expand8dl(u8 c, u32 *ret1, u32 *ret2) { - u_long rv1, rv2; - -#ifdef __mc68000__ - __asm__ __volatile__ ("lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1" - : "=&d" (rv1), "=&d" (rv2),"=d" (c) - : "2" (c)); -#endif /* !m68k */ - *ret1 = rv1; - *ret2 = rv2; + *ret1 = four2long[c & 15]; + *ret2 = four2long[c >> 4]; } + /* This expands a 8 bit color into four longs for four movel operations * (8 planes). */ -#ifdef __mc68000__ -/* ++andreas: use macro to avoid taking address of return values */ -#define expand8ql(c, rv1, rv2, rv3, rv4) \ - do { \ - u_char tmp = c; \ - __asm__ __volatile__ ("lsrb #1,%5\n\t" \ - "scs %0\n\t" \ - "extw %0\n\t" \ - "swap %0\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %0\n\t" \ - "extw %0\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %1\n\t" \ - "extw %1\n\t" \ - "swap %1\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %1\n\t" \ - "extw %1\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %2\n\t" \ - "extw %2\n\t" \ - "swap %2\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %2\n\t" \ - "extw %2\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %3\n\t" \ - "extw %3\n\t" \ - "swap %3\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %3\n\t" \ - "extw %3" \ - : "=&d" (rv1), "=&d" (rv2), "=&d" (rv3), \ - "=&d" (rv4), "=d" (tmp) \ - : "4" (tmp)); \ - } while (0) -#endif /* !m68k */ +static const u32 two2word[] = +{ +#ifndef __LITTLE_ENDIAN + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff +#else + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff +#endif +}; + +static inline void expand8ql(u8 c, u32 *rv1, u32 *rv2, u32 *rv3, u32 *rv4) +{ + *rv1 = two2word[c & 4]; + *rv2 = two2word[(c >> 2) & 4]; + *rv3 = two2word[(c >> 4) & 4]; + *rv4 = two2word[c >> 6]; +} /* This duplicates a byte 4 times into a long. */ -static __inline__ u_long dup4l(u_char c) +static __inline__ u32 dup4l(u8 c) { - ushort tmp; - ulong rv; - -#ifdef __mc68000__ - __asm__ __volatile__ ("moveb %2,%0\n\t" - "lslw #8,%0\n\t" - "moveb %2,%0\n\t" - "movew %0,%1\n\t" - "swap %0\n\t" - "movew %1,%0" - : "=&d" (rv), "=d" (tmp) - : "d" (c)); -#endif /* !m68k */ - return(rv); + u32 rv; + + rv = c; + rv |= rv << 8; + rv |= rv << 16; + return rv; } -static int open_iplan2p8(struct display *p) +void fbcon_iplan2p8_setup(struct display *p) { - if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 || - p->var.bits_per_pixel != 8) - return -EINVAL; - p->next_line = p->var.xres_virtual; p->next_plane = 2; - MOD_INC_USE_COUNT; - return 0; -} - -static void release_iplan2p8(void) -{ - MOD_DEC_USE_COUNT; } -static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) +void fbcon_iplan2p8_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { /* bmove() has to distinguish two major cases: If both, source and * destination, start at even addresses or both are at odd @@ -280,7 +205,7 @@ static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx, * all movements by memmove_col(). */ - if (sx == 0 && dx == 0 && width == p->next_line/8) { + if (sx == 0 && dx == 0 && width * 8 == p->next_line) { /* Special (but often used) case: Moving whole lines can be * done with memmove() */ @@ -289,8 +214,8 @@ static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx, p->next_line * height * p->fontheight); } else { int rows, cols; - u_char *src; - u_char *dst; + u8 *src; + u8 *dst; int bytes = p->next_line; int linesize = bytes * p->fontheight; u_int colsize = height * p->fontheight; @@ -369,20 +294,20 @@ static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) +void fbcon_iplan2p8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) { - ulong offset; - u_char *start; + u32 offset; + u8 *start; int rows; int bytes = p->next_line; int lines = height * p->fontheight; - ulong size; - u_long cval1, cval2, cval3, cval4, pcval1, pcval2; + u32 size; + u32 cval1, cval2, cval3, cval4, pcval1, pcval2; - expand8ql(attr_bgcol_ec(p,conp), cval1, cval2, cval3, cval4); + expand8ql(attr_bgcol_ec(p,conp), &cval1, &cval2, &cval3, &cval4); - if (sx == 0 && width == bytes/8) { + if (sx == 0 && width * 8 == bytes) { offset = sy * bytes * p->fontheight; size = lines * bytes; memset_even_8p(p->screen_base+offset, size, cval1, cval2, cval3, cval4); @@ -414,14 +339,14 @@ static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy, } } -static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c, - int yy, int xx) +void fbcon_iplan2p8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx) { - u_char *dest; - u_char *cdat; + u8 *dest; + u8 *cdat; int rows; int bytes = p->next_line; - ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; + u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; c &= 0xff; @@ -434,24 +359,18 @@ static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c, for(rows = p->fontheight ; rows-- ; dest += bytes) { fdx = dup4l(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepl %1,%0@(0)\n\t" - "movepl %2,%0@(8)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx1) ^ bgx1), - "d" ((fdx & eorx2) ^ bgx2) ); -#endif /* !m68k */ + movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2); } } -static void putcs_iplan2p8(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) +void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx) { - u_char *dest, *dest0; - u_char *cdat, c; + u8 *dest, *dest0; + u8 *cdat, c; int rows; int bytes; - ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; + u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; bytes = p->next_line; dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 + @@ -475,21 +394,15 @@ static void putcs_iplan2p8(struct vc_data *conp, struct display *p, for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { fdx = dup4l(*cdat++); -#ifdef __mc68000__ - __asm__ __volatile__ ("movepl %1,%0@(0)\n\t" - "movepl %2,%0@(8)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx1) ^ bgx1), - "d" ((fdx & eorx2) ^ bgx2)); -#endif /* !m68k */ + movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2); } INC_8P(dest0); } } -static void rev_char_iplan2p8(struct display *p, int xx, int yy) +void fbcon_iplan2p8_revc(struct display *p, int xx, int yy) { - u_char *dest; + u8 *dest; int j; int bytes; @@ -502,7 +415,7 @@ static void rev_char_iplan2p8(struct display *p, int xx, int yy) /* This should really obey the individual character's * background and foreground colors instead of simply * inverting. For 8 plane mode, only the lower 4 bits of the - * color are inverted, because only that color registers have + * color are inverted, because only these color registers have * been set up. */ dest[0] = ~dest[0]; @@ -514,18 +427,24 @@ static void rev_char_iplan2p8(struct display *p, int xx, int yy) } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_iplan2p8(void) -#endif -{ - return(fbcon_register_driver(&dispsw_iplan2p8, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_iplan2p8); -} -#endif /* MODULE */ +struct display_switch fbcon_iplan2p8 = { + fbcon_iplan2p8_setup, fbcon_iplan2p8_bmove, fbcon_iplan2p8_clear, + fbcon_iplan2p8_putc, fbcon_iplan2p8_putcs, fbcon_iplan2p8_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_iplan2p8); +EXPORT_SYMBOL(fbcon_iplan2p8_setup); +EXPORT_SYMBOL(fbcon_iplan2p8_bmove); +EXPORT_SYMBOL(fbcon_iplan2p8_clear); +EXPORT_SYMBOL(fbcon_iplan2p8_putc); +EXPORT_SYMBOL(fbcon_iplan2p8_putcs); +EXPORT_SYMBOL(fbcon_iplan2p8_revc); diff --git a/drivers/video/fbcon-iplan2p8.h b/drivers/video/fbcon-iplan2p8.h new file mode 100644 index 000000000..f2c46e229 --- /dev/null +++ b/drivers/video/fbcon-iplan2p8.h @@ -0,0 +1,15 @@ + /* + * Atari interleaved bitplanes (8 planes) (iplan2p8) + */ + +extern struct display_switch fbcon_iplan2p8; +extern void fbcon_iplan2p8_setup(struct display *p); +extern void fbcon_iplan2p8_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width); +extern void fbcon_iplan2p8_clear(struct vc_data *conp, struct display *p, + int sy, int sx, int height, int width); +extern void fbcon_iplan2p8_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_iplan2p8_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-mac.c b/drivers/video/fbcon-mac.c new file mode 100644 index 000000000..1dd5f1988 --- /dev/null +++ b/drivers/video/fbcon-mac.c @@ -0,0 +1,513 @@ +/* + * linux/drivers/video/fbcon-mac.c -- Low level frame buffer operations for + * x bpp packed pixels, font width != 8 + * + * Created 26 Dec 1997 by Michael Schmitz + * Based on the old macfb.c 6x11 code by Randy Thelen + * + * This driver is significantly slower than the 8bit font drivers + * and would probably benefit from splitting into drivers for each depth. + * + * 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 + */ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/config.h> +#include <linux/fb.h> +#include <linux/delay.h> + +#include "fbcon.h" +#include "fbcon-mac.h" + + + /* + * variable bpp packed pixels + */ + +static void plot_pixel_mac(struct display *p, int bw, int pixel_x, + int pixel_y); +static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y); + +void fbcon_mac_setup(struct display *p) +{ + if (p->line_length) + p->next_line = p->line_length; + else + p->next_line = p->var.xres_virtual>>3; + p->next_plane = 0; +} + + + /* + * Macintosh + */ +#define PIXEL_BLACK_MAC 0 +#define PIXEL_WHITE_MAC 1 +#define PIXEL_INVERT_MAC 2 + +void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int i, j; + u8 *dest, *src; + int l,r,t,b,w,lo,s; + int dl,dr,dt,db,dw,dlo; + int move_up; + + src = (u8 *) (p->screen_base + sy * p->fontheight * p->next_line); + dest = (u8 *) (p->screen_base + dy * p->fontheight * p->next_line); + + if( sx == 0 && width == p->conp->vc_cols) { + s = height * p->fontheight * p->next_line; + mymemmove(dest, src, s); + return; + } + + l = sx * p->fontwidth; + r = l + width * p->fontwidth; + t = sy * p->fontheight; + b = t + height * p->fontheight; + + dl = dx * p->fontwidth; + dr = dl + width * p->fontwidth; + dt = dy * p->fontheight; + db = dt + height * p->fontheight; + + /* w is the # pixels between two long-aligned points, left and right */ + w = (r&~31) - ((l+31)&~31); + dw = (dr&~31) - ((dl+31)&~31); + /* lo is the # pixels between the left edge and a long-aligned left pixel */ + lo = ((l+31)&~31) - l; + dlo = ((dl+31)&~31) - dl; + + /* if dx != sx then, logic has to align the left and right edges for fast moves */ + if (lo != dlo) { + lo = ((l+7)&~7) - l; + dlo = ((dl+7)&~7) - dl; + w = (r&~7) - ((l+7)&~7); + dw = (dr&~7) - ((dl+7)&~7); + if (lo != dlo) { + char err_str[256]; + unsigned long cnt; + sprintf( err_str, "ERROR: Shift algorithm: sx=%d,sy=%d,dx=%d,dy=%d,w=%d,h=%d,bpp=%d", + sx,sy,dx,dy,width,height,p->var.bits_per_pixel); + fbcon_mac_putcs(p->conp, p, err_str, strlen(err_str), 0, 0); + /* pause for the user */ + for(cnt = 0; cnt < 50000; cnt++) + udelay(100); + return; + } + } + + s = 0; + switch (p->var.bits_per_pixel) { + case 1: + s = w >> 3; + src += lo >> 3; + dest += lo >> 3; + break; + case 2: + s = w >> 2; + src += lo >> 2; + dest += lo >> 2; + break; + case 4: + s = w >> 1; + src += lo >> 1; + dest += lo >> 1; + break; + case 8: + s = w; + src += lo; + dest += lo; + break; + case 16: + s = w << 1; + src += lo << 1; + dest += lo << 1; + break; + case 32: + s = w << 2; + src += lo << 2; + dest += lo << 2; + break; + } + + if (sy <= sx) { + i = b; + move_up = 0; + src += height * p->fontheight; + dest += height * p->fontheight; + } else { + i = t; + move_up = 1; + } + + while (1) { + for (i = t; i < b; i++) { + j = l; + + for (; j & 31 && j < r; j++) + plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i); + + if (j < r) { + mymemmove(dest, src, s); + if (move_up) { + dest += p->next_line; + src += p->next_line; + } else { + dest -= p->next_line; + src -= p->next_line; + } + j += w; + } + + for (; j < r; j++) + plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i); + } + + if (move_up) { + i++; + if (i >= b) + break; + } else { + i--; + if (i < t) + break; + } + } +} + + +void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + int pixel; + int i, j; + int inverse; + u8 *dest; + int l,r,t,b,w,lo,s; + + inverse = attr_reverse(p,conp); + pixel = inverse ? PIXEL_WHITE_MAC : PIXEL_BLACK_MAC; + dest = (u8 *) (p->screen_base + sy * p->fontheight * p->next_line); + + if( sx == 0 && width == p->conp->vc_cols) { + s = height * p->fontheight * p->next_line; + if (inverse) + mymemclear(dest, s); + else + mymemset(dest, s); + } + + l = sx * p->fontwidth; + r = l + width * p->fontwidth; + t = sy * p->fontheight; + b = t + height * p->fontheight; + /* w is the # pixels between two long-aligned points, left and right */ + w = (r&~31) - ((l+31)&~31); + /* lo is the # pixels between the left edge and a long-aligned left pixel */ + lo = ((l+31)&~31) - l; + s = 0; + switch (p->var.bits_per_pixel) { + case 1: + s = w >> 3; + dest += lo >> 3; + break; + case 2: + s = w >> 2; + dest += lo >> 2; + break; + case 4: + s = w >> 1; + dest += lo >> 1; + break; + case 8: + s = w; + dest += lo; + break; + case 16: + s = w << 1; + dest += lo << 1; + break; + case 32: + s = w << 2; + dest += lo << 2; + break; + } + + for (i = t; i < b; i++) { + j = l; + + for (; j & 31 && j < r; j++) + plot_pixel_mac(p, pixel, j, i); + + if (j < r) { + if (PIXEL_WHITE_MAC == pixel) + mymemclear(dest, s); + else + mymemset(dest, s); + dest += p->next_line; + j += w; + } + + for (; j < r; j++) + plot_pixel_mac(p, pixel, j, i); + } +} + + +void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) +{ + u8 *cdat; + u_int rows, bold, ch_reverse, ch_underline; + u8 d; + int j; + + c &= 0xff; + + cdat = p->fontdata+c*p->fontheight; + bold = attr_bold(p,conp); + ch_reverse = attr_reverse(p,conp); + ch_underline = attr_underline(p,conp); + + for (rows = 0; rows < p->fontheight; rows++) { + d = *cdat++; + if (!conp->vc_can_do_color) { + if (ch_underline && rows == (p->fontheight-2)) + d = 0xff; + else if (bold) + d |= d>>1; + if (ch_reverse) + d = ~d; + } + for (j = 0; j < p->fontwidth; j++) { + plot_pixel_mac(p, (d & 0x80) >> 7, (xx*p->fontwidth) + j, (yy*p->fontheight) + rows); + d <<= 1; + } + } +} + + +void fbcon_mac_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) +{ + u8 c; + + while (count--) { + c = *s++; + fbcon_mac_putc(conp, p, c, yy, xx++); + } +} + + +void fbcon_mac_revc(struct display *p, int xx, int yy) +{ + u_int rows, j; + + for (rows = 0; rows < p->fontheight; rows++) { + for (j = 0; j < p->fontwidth; j++) { + plot_pixel_mac (p, PIXEL_INVERT_MAC, (xx*p->fontwidth)+j, (yy*p->fontheight)+rows); + } + } +} + +/* + * plot_pixel_mac + * + * bw == 0 = black + * 1 = white + * 2 = invert + */ +static void plot_pixel_mac(struct display *p, int bw, int pixel_x, int pixel_y) +{ + u8 *dest, bit; + u16 *dest16, pix16; + u32 *dest32, pix32; + + if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) { + int cnt; + printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y); + for(cnt = 0; cnt < 100000; cnt++) + udelay(100); + return; + } + + switch (p->var.bits_per_pixel) { + case 1: + dest = (u8 *) ((pixel_x >> 3) + p->screen_base + pixel_y * p->next_line); + bit = 0x80 >> (pixel_x & 7); + switch (bw) { + case PIXEL_BLACK_MAC: + *dest |= bit; + break; + case PIXEL_WHITE_MAC: + *dest &= ~bit; + break; + case PIXEL_INVERT_MAC: + *dest ^= bit; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + + case 2: + dest = (u8 *) ((pixel_x >> 2) + p->screen_base + pixel_y * p->next_line); + bit = 0xC0 >> ((pixel_x & 3) << 1); + switch (bw) { + case PIXEL_BLACK_MAC: + *dest |= bit; + break; + case PIXEL_WHITE_MAC: + *dest &= ~bit; + break; + case PIXEL_INVERT_MAC: + *dest ^= bit; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + + case 4: + dest = (u8 *) ((pixel_x / 2) + p->screen_base + pixel_y * p->next_line); + bit = 0xF0 >> ((pixel_x & 1) << 2); + switch (bw) { + case PIXEL_BLACK_MAC: + *dest |= bit; + break; + case PIXEL_WHITE_MAC: + *dest &= ~bit; + break; + case PIXEL_INVERT_MAC: + *dest ^= bit; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + + case 8: + dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line); + bit = 0xFF; + switch (bw) { + case PIXEL_BLACK_MAC: + *dest |= bit; + break; + case PIXEL_WHITE_MAC: + *dest &= ~bit; + break; + case PIXEL_INVERT_MAC: + *dest ^= bit; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + + case 16: + dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line); + pix16 = 0xFFFF; + switch (bw) { + case PIXEL_BLACK_MAC: + *dest16 = ~pix16; + break; + case PIXEL_WHITE_MAC: + *dest16 = pix16; + break; + case PIXEL_INVERT_MAC: + *dest16 ^= pix16; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + + case 32: + dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line); + pix32 = 0xFFFFFFFF; + switch (bw) { + case PIXEL_BLACK_MAC: + *dest32 = ~pix32; + break; + case PIXEL_WHITE_MAC: + *dest32 = pix32; + break; + case PIXEL_INVERT_MAC: + *dest32 ^= pix32; + break; + default: + printk( "ERROR: Unknown pixel value in plot_pixel_mac\n"); + } + break; + } +} + +static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y) +{ + u8 *dest, bit; + u16 *dest16; + u32 *dest32; + u8 pixel; + + switch (p->var.bits_per_pixel) { + case 1: + dest = (u8 *) ((pixel_x / 8) + p->screen_base + pixel_y * p->next_line); + bit = 0x80 >> (pixel_x & 7); + pixel = *dest & bit; + break; + case 2: + dest = (u8 *) ((pixel_x / 4) + p->screen_base + pixel_y * p->next_line); + bit = 0xC0 >> (pixel_x & 3); + pixel = *dest & bit; + break; + case 4: + dest = (u8 *) ((pixel_x / 2) + p->screen_base + pixel_y * p->next_line); + bit = 0xF0 >> (pixel_x & 1); + pixel = *dest & bit; + break; + case 8: + dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line); + pixel = *dest; + break; + case 16: + dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line); + pixel = *dest16 ? 1 : 0; + break; + case 32: + dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line); + pixel = *dest32 ? 1 : 0; + break; + } + + return pixel ? PIXEL_BLACK_MAC : PIXEL_WHITE_MAC; +} + + + /* + * `switch' for the low level operations + */ + +struct display_switch fbcon_mac = { + fbcon_mac_setup, fbcon_mac_bmove, fbcon_mac_clear, fbcon_mac_putc, + fbcon_mac_putcs, fbcon_mac_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_mac); +EXPORT_SYMBOL(fbcon_mac_setup); +EXPORT_SYMBOL(fbcon_mac_bmove); +EXPORT_SYMBOL(fbcon_mac_clear); +EXPORT_SYMBOL(fbcon_mac_putc); +EXPORT_SYMBOL(fbcon_mac_putcs); +EXPORT_SYMBOL(fbcon_mac_revc); diff --git a/drivers/video/fbcon-mac.h b/drivers/video/fbcon-mac.h new file mode 100644 index 000000000..7e807cce9 --- /dev/null +++ b/drivers/video/fbcon-mac.h @@ -0,0 +1,15 @@ + /* + * Mac variable bpp packed pixels (mac) + */ + +extern struct display_switch fbcon_mac; +extern void fbcon_mac_setup(struct display *p); +extern void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_mac_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_mac_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-mfb.c b/drivers/video/fbcon-mfb.c index 7ac001ab4..3484cef2a 100644 --- a/drivers/video/fbcon-mfb.c +++ b/drivers/video/fbcon-mfb.c @@ -13,65 +13,30 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> +#include <linux/config.h> #include <linux/fb.h> #include "fbcon.h" - - - /* - * Prototypes - */ - -static int open_mfb(struct display *p); -static void release_mfb(void); -static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_mfb(struct display *p, int xx, int yy); - - - /* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_mfb = { - open_mfb, release_mfb, bmove_mfb, clear_mfb, putc_mfb, putcs_mfb, - rev_char_mfb -}; +#include "fbcon-mfb.h" /* * Monochrome */ -static int open_mfb(struct display *p) +void fbcon_mfb_setup(struct display *p) { - if (p->var.bits_per_pixel != 1) - return -EINVAL; - if (p->line_length) p->next_line = p->line_length; else p->next_line = p->var.xres_virtual>>3; p->next_plane = 0; - MOD_INC_USE_COUNT; - return 0; } -static void release_mfb(void) +void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { - MOD_DEC_USE_COUNT; -} - -static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - u_char *src, *dest; + u8 *src, *dest; u_int rows; if (sx == 0 && dx == 0 && width == p->next_line) { @@ -97,10 +62,10 @@ static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx, } } -static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) +void fbcon_mfb_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) { - u_char *dest; + u8 *dest; u_int rows; dest = p->screen_base+sy*p->fontheight*p->next_line+sx; @@ -118,12 +83,12 @@ static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx, mymemclear_small(dest, width); } -static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy, - int xx) +void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c, int yy, + int xx) { - u_char *dest, *cdat; + u8 *dest, *cdat; u_int rows, bold, revs, underl; - u_char d; + u8 d; c &= 0xff; @@ -145,12 +110,12 @@ static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy, } } -static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) +void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, const char *s, + int count, int yy, int xx) { - u_char *dest, *dest0, *cdat; + u8 *dest, *dest0, *cdat; u_int rows, bold, revs, underl; - u_char c, d; + u8 c, d; dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx; bold = attr_bold(p,conp); @@ -174,9 +139,9 @@ static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s, } } -static void rev_char_mfb(struct display *p, int xx, int yy) +void fbcon_mfb_revc(struct display *p, int xx, int yy) { - u_char *dest; + u8 *dest; u_int rows; dest = p->screen_base+yy*p->fontheight*p->next_line+xx; @@ -185,18 +150,24 @@ static void rev_char_mfb(struct display *p, int xx, int yy) } -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_mfb(void) -#endif -{ - return(fbcon_register_driver(&dispsw_mfb, 0)); -} + /* + * `switch' for the low level operations + */ -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_mfb); -} -#endif /* MODULE */ +struct display_switch fbcon_mfb = { + fbcon_mfb_setup, fbcon_mfb_bmove, fbcon_mfb_clear, fbcon_mfb_putc, + fbcon_mfb_putcs, fbcon_mfb_revc +}; + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(fbcon_mfb); +EXPORT_SYMBOL(fbcon_mfb_setup); +EXPORT_SYMBOL(fbcon_mfb_bmove); +EXPORT_SYMBOL(fbcon_mfb_clear); +EXPORT_SYMBOL(fbcon_mfb_putc); +EXPORT_SYMBOL(fbcon_mfb_putcs); +EXPORT_SYMBOL(fbcon_mfb_revc); diff --git a/drivers/video/fbcon-mfb.h b/drivers/video/fbcon-mfb.h new file mode 100644 index 000000000..b67d9f70e --- /dev/null +++ b/drivers/video/fbcon-mfb.h @@ -0,0 +1,15 @@ + /* + * Monochrome (mfb) + */ + +extern struct display_switch fbcon_mfb; +extern void fbcon_mfb_setup(struct display *p); +extern void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width); +extern void fbcon_mfb_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width); +extern void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c, + int yy, int xx); +extern void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, + const char *s, int count, int yy, int xx); +extern void fbcon_mfb_revc(struct display *p, int xx, int yy); diff --git a/drivers/video/fbcon-retz3.c b/drivers/video/fbcon-retz3.c deleted file mode 100644 index 71095a084..000000000 --- a/drivers/video/fbcon-retz3.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * linux/drivers/video/retz3.c -- Low level frame buffer operations for the - * RetinaZ3 (accelerated) - * - * Created 5 Apr 1997 by Geert Uytterhoeven - * - * 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. - */ - -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/console.h> -#include <linux/string.h> -#include <linux/fb.h> - -#include "fbcon.h" - - -/* - * Prototypes - */ - -static int open_retz3(struct display *p); -static void release_retz3(void); -static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_retz3(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width); -static void putc_retz3(struct vc_data *conp, struct display *p, int c, - int ypos, int xpos); -static void putcs_retz3(struct vc_data *conp, struct display *p, const - char *s, int count, int ypos, int xpos); -static void rev_char_retz3(struct display *p, int xpos, int ypos); - - - /* - * Acceleration functions in retz3fb.c - */ - -extern void retz3_bitblt(struct fb_var_screeninfo *scr, - unsigned short srcx, unsigned short srcy, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask); - -#define Z3BLTcopy 0xc0 -#define Z3BLTset 0xf0 - - -/* - * `switch' for the low level operations - */ - -static struct display_switch dispsw_retz3 = { - open_retz3, release_retz3, bmove_retz3, clear_retz3, putc_retz3, - putcs_retz3, rev_char_retz3 -}; - - -/* - * RetinaZ3 (accelerated) - */ - -static int open_retz3(struct display *p) -{ - if (p->type != FB_TYPE_PACKED_PIXELS || - p->var.accel != FB_ACCEL_RETINAZ3) - return -EINVAL; - - p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3; - p->next_plane = 0; - MOD_INC_USE_COUNT; - return 0; -} - -static void release_retz3(void) -{ - MOD_DEC_USE_COUNT; -} - -static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - int fontwidth = p->fontwidth; - - sx *= fontwidth; - dx *= fontwidth; - width *= fontwidth; - - retz3_bitblt(&p->var, - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)dx, - (unsigned short)(dy*p->fontheight), - (unsigned short)width, - (unsigned short)(height*p->fontheight), - Z3BLTcopy, - 0xffff); -} - -static void clear_retz3(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width) -{ - unsigned short col; - int fontwidth = p->fontwidth; - - sx *= fontwidth; - width *= fontwidth; - - col = attr_bgcol_ec(p, conp); - col &= 0xff; - col |= (col << 8); - - retz3_bitblt(&p->var, - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)width, - (unsigned short)(height*p->fontheight), - Z3BLTset, - col); -} - -static void putc_retz3(struct vc_data *conp, struct display *p, - int c, int ypos, int xpos) -{ - unsigned char *dest, *cdat; - unsigned long tmp; - unsigned int rows, revs, underl; - unsigned char d; - unsigned char fg, bg; - - c &= 0xff; - - dest = p->screen_base + ypos * p->fontheight * p->next_line + - xpos*p->fontwidth; - cdat = p->fontdata + c * p->fontheight; - - fg = p->fgcol; - bg = p->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((unsigned long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((unsigned long*) dest + 1) = tmp; - } -} - -static void putcs_retz3(struct vc_data *conp, struct display *p, - const char *s, int count, int ypos, int xpos) -{ - unsigned char *dest, *dest0, *cdat; - unsigned long tmp; - unsigned int rows, revs, underl; - unsigned char c, d; - unsigned char fg, bg; - - dest0 = p->screen_base + ypos * p->fontheight * p->next_line - + xpos * p->fontwidth; - - fg = p->fgcol; - bg = p->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - while (count--) { - c = *s++; - dest = dest0; - dest0 += 8; - - cdat = p->fontdata + c * p->fontheight; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((unsigned long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((unsigned long*) dest + 1) = tmp; - } - } -} - -static void rev_char_retz3(struct display *p, int xpos, int ypos) -{ - unsigned char *dest; - int bytes=p->next_line, rows; - unsigned int bpp, mask; - - bpp = p->var.bits_per_pixel; - - switch (bpp){ - case 8: - mask = 0x0f0f0f0f; - break; - case 16: - mask = 0xffffffff; - break; - case 24: - mask = 0xffffffff; /* ??? */ - break; - default: - printk("illegal depth for rev_char_retz3(), bpp = %i\n", bpp); - return; - } - - dest = p->screen_base + ypos * p->fontheight * bytes + - xpos * p->fontwidth; - - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((unsigned long *)dest)[0] ^= mask; - ((unsigned long *)dest)[1] ^= mask; - } -} - - -#ifdef MODULE -int init_module(void) -#else -int fbcon_init_retz3(void) -#endif -{ - return(fbcon_register_driver(&dispsw_retz3, 1)); -} - -#ifdef MODULE -void cleanup_module(void) -{ - fbcon_unregister_driver(&dispsw_retz3); -} -#endif /* MODULE */ diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c index 872d77928..a41bf081d 100644 --- a/drivers/video/fbcon.c +++ b/drivers/video/fbcon.c @@ -28,26 +28,17 @@ * The low level operations for the various display memory organizations are * now in separate source files. * - * Currently only the following organizations are supported: + * Currently the following organizations are supported: * - * - non-accelerated: - * - * o mfb Monochrome - * o ilbm Interleaved bitplanes à la Amiga - * o afb Bitplanes à la Amiga - * o iplan2p[248] Interleaved bitplanes à la Atari (2, 4 and 8 planes) - * o cfb{8,16} Packed pixels (8 and 16 bpp) - * - * - accelerated: - * - * o cyber CyberVision64 packed pixels (accelerated) - * o retz3 Retina Z3 packed pixels (accelerated) - * o mach64 ATI Mach 64 packed pixels (accelerated) + * o afb Amiga bitplanes + * o cfb{2,4,8,16,24,32} Packed pixels + * o ilbm Amiga interleaved bitplanes + * o iplan2p[248] Atari interleaved bitplanes + * o mfb Monochrome * * To do: * * - Implement 16 plane mode (iplan2p16) - * - Add support for 24/32 bit packed pixels (cfb{24,32}) * - Hardware cursor * * @@ -56,6 +47,8 @@ * more details. */ +#undef FBCONDEBUG + #define SUPPORT_SCROLLBACK 0 #define FLASHING_CURSOR 1 @@ -65,6 +58,7 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/kernel.h> +#include <linux/delay.h> /* MSch: for IRQ probe */ #include <linux/tty.h> #include <linux/console.h> #include <linux/string.h> @@ -74,9 +68,6 @@ #include <linux/vt_kern.h> #include <linux/selection.h> #include <linux/init.h> -#ifdef CONFIG_KMOD -#include <linux/kmod.h> -#endif #include <asm/irq.h> #include <asm/system.h> @@ -88,6 +79,9 @@ #ifdef CONFIG_ATARI #include <asm/atariints.h> #endif +#ifdef CONFIG_MAC +#include <asm/macints.h> +#endif #ifdef __mc68000__ #include <asm/machdep.h> #include <asm/setup.h> @@ -95,8 +89,14 @@ #include <asm/linux_logo.h> #include "fbcon.h" +#include "fbcon-mac.h" /* for 6x11 font on mac */ #include "font.h" +#ifdef FBCONDEBUG +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif struct display fb_display[MAX_NR_CONSOLES]; @@ -112,6 +112,7 @@ static int cursor_drawn = 0; /* # VBL ints between cursor state changes */ #define AMIGA_CURSOR_BLINK_RATE (20) #define ATARI_CURSOR_BLINK_RATE (42) +#define MAC_CURSOR_BLINK_RATE (32) #define DEFAULT_CURSOR_BLINK_RATE (20) static int vbl_cursor_cnt = 0; @@ -146,26 +147,25 @@ static __inline__ int CURSOR_UNDRAWN(void) static unsigned long fbcon_startup(unsigned long kmem_start, const char **display_desc); static void fbcon_init(struct vc_data *conp); -static int fbcon_deinit(struct vc_data *conp); +static void fbcon_deinit(struct vc_data *conp); static int fbcon_changevar(int con); -static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height, +static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height, int width); -static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos); -static int fbcon_putcs(struct vc_data *conp, const char *s, int count, - int ypos, int xpos); -static int fbcon_cursor(struct vc_data *conp, int mode); -static int fbcon_scroll(struct vc_data *conp, int t, int b, - int dir, int count); -static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width); +static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos); +static void fbcon_putcs(struct vc_data *conp, const char *s, int count, + int ypos, int xpos); +static void fbcon_cursor(struct vc_data *conp, int mode); +static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir, + int count); +static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, + int height, int width); static int fbcon_switch(struct vc_data *conp); static int fbcon_blank(int blank); static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data); static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data); static int fbcon_set_palette(struct vc_data *conp, unsigned char *table); static int fbcon_scrolldelta(int lines); -int fbcon_register_driver(struct display_switch *dispsw, int is_accel); -int fbcon_unregister_driver(struct display_switch *dispsw); +static int fbcon_set_mode(struct vc_data *conp, int mode); /* @@ -193,16 +193,23 @@ static __inline__ void ypan_down(int unit, struct vc_data *conp, struct display *p, int count); static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break); -static struct display_switch *probe_list(struct display_switch *dispsw, - struct display *disp); -#ifdef CONFIG_KMOD -static void request_driver(struct display *disp, int is_accel); -#endif -static struct display_switch *fbcon_get_driver(struct display *disp); static int fbcon_show_logo(void); #if FLASHING_CURSOR + +#ifdef CONFIG_MAC +/* + * On the Macintoy, there may or may not be a working VBL int. We need to prob + */ +static int vbl_detected = 0; + +static void fbcon_vbl_detect(int irq, void *dummy, struct pt_regs *fp) +{ + vbl_detected++; +} +#endif + static void cursor_timer_handler(unsigned long dev_addr); static struct timer_list cursor_timer = { @@ -212,7 +219,7 @@ static struct timer_list cursor_timer = { static void cursor_timer_handler(unsigned long dev_addr) { fbcon_vbl_handler(0, NULL, NULL); - cursor_timer.expires = jiffies+2; + cursor_timer.expires = jiffies+HZ/50; cursor_timer.data = 0; cursor_timer.next = cursor_timer.next = NULL; add_timer(&cursor_timer); @@ -223,49 +230,8 @@ static void cursor_timer_handler(unsigned long dev_addr) * Low Level Operations */ -static struct display_switch dispsw_dummy; +static struct display_switch fbcon_dummy; -#ifdef CONFIG_FBCON_MFB -extern int fbcon_init_mfb(void); -#endif -#ifdef CONFIG_FBCON_ILBM -extern int fbcon_init_ilbm(void); -#endif -#ifdef CONFIG_FBCON_AFB -extern int fbcon_init_afb(void); -#endif -#ifdef CONFIG_FBCON_IPLAN2P2 -extern int fbcon_init_iplan2p2(void); -#endif -#ifdef CONFIG_FBCON_IPLAN2P4 -extern int fbcon_init_iplan2p4(void); -#endif -#ifdef CONFIG_FBCON_IPLAN2P8 -extern int fbcon_init_iplan2p8(void); -#endif -#ifdef CONFIG_FBCON_CFB8 -extern int fbcon_init_cfb8(void); -#endif -#ifdef CONFIG_FBCON_CFB16 -extern int fbcon_init_cfb16(void); -#endif -#ifdef CONFIG_FBCON_CFB24 -extern int fbcon_init_cfb24(void); -#endif -#ifdef CONFIG_FBCON_CFB32 -extern int fbcon_init_cfb32(void); -#endif -#ifdef CONFIG_FBCON_CYBER -extern int fbcon_init_cyber(void); -#endif -#ifdef CONFIG_FBCON_RETINAZ3 -extern int fbcon_init_retz3(void); -#endif -#ifdef CONFIG_FBCON_MACH64 -extern int fbcon_init_mach64(void); -#endif - -extern int num_registered_fb; __initfunc(static unsigned long fbcon_startup(unsigned long kmem_start, const char **display_desc)) @@ -275,49 +241,10 @@ __initfunc(static unsigned long fbcon_startup(unsigned long kmem_start, /* Probe all frame buffer devices */ kmem_start = probe_framebuffers(kmem_start); - if (!num_registered_fb) + if (!num_registered_fb) { + DPRINTK("no framebuffer registered\n"); return kmem_start; - - /* Initialize all built-in low level drivers */ -#ifdef CONFIG_FBCON_RETINAZ3 - fbcon_init_retz3(); -#endif -#ifdef CONFIG_FBCON_MFB - fbcon_init_mfb(); -#endif -#ifdef CONFIG_FBCON_IPLAN2P2 - fbcon_init_iplan2p2(); -#endif -#ifdef CONFIG_FBCON_IPLAN2P4 - fbcon_init_iplan2p4(); -#endif -#ifdef CONFIG_FBCON_IPLAN2P8 - fbcon_init_iplan2p8(); -#endif -#ifdef CONFIG_FBCON_ILBM - fbcon_init_ilbm(); -#endif -#ifdef CONFIG_FBCON_AFB - fbcon_init_afb(); -#endif -#ifdef CONFIG_FBCON_CFB8 - fbcon_init_cfb8(); -#endif -#ifdef CONFIG_FBCON_CFB16 - fbcon_init_cfb16(); -#endif -#ifdef CONFIG_FBCON_CFB24 - fbcon_init_cfb24(); -#endif -#ifdef CONFIG_FBCON_CFB32 - fbcon_init_cfb32(); -#endif -#ifdef CONFIG_FBCON_CYBER - fbcon_init_cyber(); -#endif -#ifdef CONFIG_FBCON_MACH64 - fbcon_init_mach64(); -#endif + } *display_desc = "frame buffer device"; @@ -335,9 +262,49 @@ __initfunc(static unsigned long fbcon_startup(unsigned long kmem_start, "console/cursor", fbcon_vbl_handler); } #endif /* CONFIG_ATARI */ + +#ifdef CONFIG_MAC + /* + * On a Macintoy, the VBL interrupt may or may not be active. + * As interrupt based cursor is more reliable and race free, we + * probe for VBL interrupts. + */ + if (MACH_IS_MAC) { + int ct = 0; + /* + * Probe for VBL: set temp. handler ... + */ + irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_detect, 0, + "console/cursor", fbcon_vbl_detect); + /* + * ... and spin for 20 ms ... + */ + while (!vbl_detected && ++ct<1000) + udelay(20); + + if(ct==1000) + printk("fbcon_startup: No VBL detected, using timer based cursor.\n"); + + if (vbl_detected) { + /* + * interrupt based cursor ok + */ + cursor_blink_rate = MAC_CURSOR_BLINK_RATE; + irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_handler, 0, + "console/cursor", fbcon_vbl_handler); + } else { + /* + * VBL not detected: fall through, use timer based cursor + */ + irqres = 1; + /* free interrupt here ?? */ + } + } +#endif /* CONFIG_MAC */ + if (irqres) { cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE; - cursor_timer.expires = jiffies+2; + cursor_timer.expires = jiffies+HZ/50; cursor_timer.data = 0; cursor_timer.next = cursor_timer.prev = NULL; add_timer(&cursor_timer); @@ -360,22 +327,24 @@ static void fbcon_init(struct vc_data *conp) info->changevar = &fbcon_changevar; fb_display[unit] = *(info->disp); /* copy from default */ + DPRINTK("mode: %s\n",info->modename); + DPRINTK("visual: %d\n",fb_display[unit].visual); + DPRINTK("res: %dx%d-%d\n",fb_display[unit].var.xres, + fb_display[unit].var.yres, + fb_display[unit].var.bits_per_pixel); fb_display[unit].conp = conp; fb_display[unit].fb_info = info; fbcon_setup(unit, 1, 1); } -static int fbcon_deinit(struct vc_data *conp) +static void fbcon_deinit(struct vc_data *conp) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; - if (p->dispsw) - p->dispsw->release(); - p->dispsw = 0; + p->dispsw = NULL; p->conp = 0; - return(0); } @@ -405,7 +374,6 @@ static void fbcon_setup(int con, int setcol, int init) struct display *p = &fb_display[con]; struct vc_data *conp = p->conp; int nr_rows, nr_cols; - struct display_switch *old_dispsw, *new_dispsw; p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ @@ -415,9 +383,17 @@ static void fbcon_setup(int con, int setcol, int init) getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth, &p->fontheight, &p->fontdata); if (p->fontwidth != 8) { - /* ++Geert: changed from panic() to `correct and continue' */ - printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8"); - p->fontwidth = 8; +#ifdef CONFIG_MAC + if (MACH_IS_MAC) + /* ++Geert: hack to make 6x11 fonts work on mac */ + p->dispsw = &fbcon_mac; + else +#endif + { + /* ++Geert: changed from panic() to `correct and continue' */ + printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8"); + p->dispsw = &fbcon_dummy; + } } updatescrollmode(p); @@ -435,18 +411,12 @@ static void fbcon_setup(int con, int setcol, int init) p->vrows = p->var.yres_virtual/p->fontheight; conp->vc_can_do_color = p->var.bits_per_pixel != 1; - new_dispsw = fbcon_get_driver(p); - if (!new_dispsw) { + if (!p->dispsw) { printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not " "supported\n", p->type, p->type_aux, p->var.bits_per_pixel); - dispsw_dummy.open(p); - new_dispsw = &dispsw_dummy; + p->dispsw = &fbcon_dummy; } - /* Be careful when changing dispsw, it might be the current console. */ - old_dispsw = p->dispsw; - p->dispsw = new_dispsw; - if (old_dispsw) - old_dispsw->release(); + p->dispsw->setup(p); if (setcol) { p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1; @@ -492,15 +462,18 @@ static __inline__ int real_y(struct display *p, int ypos) } -static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) +static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height, + int width) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; u_int y_break; if (!p->can_soft_blank && console_blanked) - return(0); + return; + + if (!height || !width) + return; if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) && (sx <= p->cursor_x) && (p->cursor_x < sx+width)) @@ -515,47 +488,41 @@ static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height, p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width); } else p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width); - - return(0); } -static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos) +static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; if (!p->can_soft_blank && console_blanked) - return 0; + return; if ((p->cursor_x == xpos) && (p->cursor_y == ypos)) CURSOR_UNDRAWN(); p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos); - - return 0; } -static int fbcon_putcs(struct vc_data *conp, const char *s, int count, +static void fbcon_putcs(struct vc_data *conp, const char *s, int count, int ypos, int xpos) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; if (!p->can_soft_blank && console_blanked) - return 0; + return; if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) && (p->cursor_x < (xpos + count))) CURSOR_UNDRAWN(); p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos); - - return(0); } -static int fbcon_cursor(struct vc_data *conp, int mode) +static void fbcon_cursor(struct vc_data *conp, int mode) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; @@ -563,9 +530,9 @@ static int fbcon_cursor(struct vc_data *conp, int mode) /* Avoid flickering if there's no real change. */ if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y && (mode == CM_ERASE) == !cursor_on) - return 0; + return; if (CURSOR_UNDRAWN ()) - p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y)); + p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y)); p->cursor_x = conp->vc_x; p->cursor_y = conp->vc_y; @@ -580,8 +547,6 @@ static int fbcon_cursor(struct vc_data *conp, int mode) cursor_on = 1; break; } - - return(0); } @@ -598,7 +563,7 @@ static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp) * switching code should set vbl_cursor_cnt to an appropriate value. */ p = &fb_display[fg_console]; - p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y)); + p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y)); cursor_drawn ^= 1; vbl_cursor_cnt = cursor_blink_rate; } @@ -623,7 +588,7 @@ static __inline__ void ywrap_up(int unit, struct display *p, int count) p->var.xoffset = 0; p->var.yoffset = p->yscroll*p->fontheight; p->var.vmode |= FB_VMODE_YWRAP; - p->fb_info->updatevar(unit); + p->fb_info->updatevar(unit, p->fb_info); #if SUPPORT_SCROLLBACK scrollback_max += count; if (scrollback_max > p->vrows-conp->vc_rows) @@ -646,7 +611,7 @@ static __inline__ void ywrap_down(int unit, struct display *p, int count) p->var.xoffset = 0; p->var.yoffset = p->yscroll*p->fontheight; p->var.vmode |= FB_VMODE_YWRAP; - p->fb_info->updatevar(unit); + p->fb_info->updatevar(unit, p->fb_info); #if SUPPORT_SCROLLBACK scrollback_max -= count; if (scrollback_max < 0) @@ -668,7 +633,7 @@ static __inline__ void ypan_up(int unit, struct vc_data *conp, p->var.xoffset = 0; p->var.yoffset = p->yscroll*p->fontheight; p->var.vmode &= ~FB_VMODE_YWRAP; - p->fb_info->updatevar(unit); + p->fb_info->updatevar(unit, p->fb_info); } @@ -684,17 +649,21 @@ static __inline__ void ypan_down(int unit, struct vc_data *conp, p->var.xoffset = 0; p->var.yoffset = p->yscroll*p->fontheight; p->var.vmode &= ~FB_VMODE_YWRAP; - p->fb_info->updatevar(unit); + p->fb_info->updatevar(unit, p->fb_info); } -static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) +static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir, + int count) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; if (!p->can_soft_blank && console_blanked) - return(0); + return; + + if (!count) + return; fbcon_cursor(conp, CM_ERASE); @@ -822,19 +791,20 @@ static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) fbcon_clear(conp, 0, t, conp->vc_rows, count); break; } - - return(0); } -static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) +static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, + int height, int width) { int unit = conp->vc_num; struct display *p = &fb_display[unit]; if (!p->can_soft_blank && console_blanked) - return(0); + return; + + if (!width || !height) + return; if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) && (sx <= p->cursor_x) && (p->cursor_x < sx+width)) || @@ -850,8 +820,6 @@ static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, * over again, so we use fbcon_bmove_rec() */ fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll); - - return(0); } @@ -894,7 +862,7 @@ static int fbcon_switch(struct vc_data *conp) struct fb_info *info = p->fb_info; if (info && info->switch_con) - (*info->switch_con)(conp->vc_num); + (*info->switch_con)(conp->vc_num, info); #if SUPPORT_SCROLLBACK scrollback_max = 0; scrollback_current = 0; @@ -906,11 +874,19 @@ static int fbcon_switch(struct vc_data *conp) static int fbcon_blank(int blank) { struct display *p = &fb_display[fg_console]; + struct fb_info *info = p->fb_info; fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW); if (!p->can_soft_blank) { if (blank) { +#ifdef CONFIG_MAC + if (MACH_IS_MAC) + mymemset(p->screen_base, + p->var.xres_virtual*p->var.yres_virtual* + p->var.bits_per_pixel>>3); + else +#endif if (p->visual == FB_VISUAL_MONO01) mymemset(p->screen_base, p->var.xres_virtual*p->var.yres_virtual* @@ -925,7 +901,7 @@ static int fbcon_blank(int blank) return(1); } } - (*p->fb_info->blank)(blank); + (*info->blank)(blank, info); return(0); } @@ -975,7 +951,7 @@ static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data) copy_from_user( name, data, MAX_FONT_NAME ); name[sizeof(name)-1] = 0; - if (!findsoftfont( name, &w, &h, (u_char **)&data )) + if (!findsoftfont( name, &w, &h, (u8 **)&data )) return( -ENOENT ); userspace = 0; } else if (w == 1) { @@ -1049,9 +1025,9 @@ activate: return( 0 ); } -static unsigned short palette_red[16]; -static unsigned short palette_green[16]; -static unsigned short palette_blue[16]; +static u16 palette_red[16]; +static u16 palette_green[16]; +static u16 palette_blue[16]; static struct fb_cmap palette_cmap = { 0, 16, palette_red, palette_green, palette_blue, NULL @@ -1062,7 +1038,7 @@ static int fbcon_set_palette(struct vc_data *conp, unsigned char *table) int unit = conp->vc_num; struct display *p = &fb_display[unit]; int i, j, k; - u_char val; + u8 val; if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked)) return(-EINVAL); @@ -1078,7 +1054,7 @@ static int fbcon_set_palette(struct vc_data *conp, unsigned char *table) palette_cmap.len = 1<<p->var.bits_per_pixel; if (palette_cmap.len > 16) palette_cmap.len = 16; - return(p->fb_info->setcmap(&palette_cmap, unit)); + return p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, unit, p->fb_info); } static int fbcon_scrolldelta(int lines) @@ -1109,13 +1085,27 @@ static int fbcon_scrolldelta(int lines) p->var.vmode |= FB_VMODE_YWRAP; p->var.xoffset = 0; p->var.yoffset = offset*p->fontheight; - p->fb_info->updatevar(unit); + p->fb_info->updatevar(unit, p->fb_info); #else return -ENOSYS; #endif } + /* + * Switch between `text' (emulated and accelerated) and `graphics' + * (unaccelerated text) mode + */ + +static int fbcon_set_mode(struct vc_data *conp, int mode) +{ + struct display *p = &fb_display[conp->vc_num]; + struct fb_ops *ops = p->fb_info->fbops; + + return ops->fb_set_mode ? ops->fb_set_mode(mode, p->fb_info) : 0; +} + + #define LOGO_H 80 #define LOGO_W 80 #define LOGO_LINE (LOGO_W/8) @@ -1166,7 +1156,8 @@ __initfunc(static int fbcon_show_logo( void )) palette_cmap.green[j] = (green[i+j] << 8) | green[i+j]; palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j]; } - p->fb_info->setcmap( &palette_cmap, fg_console ); + p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, fg_console, + p->fb_info); } fb_display[fg_console].cmap.len = old_cmap_len; } @@ -1184,12 +1175,65 @@ __initfunc(static int fbcon_show_logo( void )) logo_depth = 1; } -#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CYBER) || \ - defined(CONFIG_FBCON_RETINAZ3) - if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR || - p->visual == FB_VISUAL_DIRECTCOLOR)) { +#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \ + defined(CONFIG_FBCON_CFB32) + if (p->visual == FB_VISUAL_TRUECOLOR) { + unsigned int val; /* max. depth 32! */ + int bdepth; + int redshift, greenshift, blueshift; + + /* Bug: Doesn't obey msb_right ... (who needs that?) */ + redshift = p->var.red.offset; + greenshift = p->var.green.offset; + blueshift = p->var.blue.offset; + + if (depth >= 24 && (depth % 8) == 0) { + /* have at least 8 bits per color */ + src = logo; + bdepth = depth/8; + for( y1 = 0; y1 < LOGO_H; y1++ ) { + dst = fb + y1*line; + for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { + val = ((linux_logo_red[*src] & redmask) << redshift) | + ((linux_logo_green[*src] & greenmask) << greenshift) | + ((linux_logo_blue[*src] & bluemask) << blueshift); + for( i = bdepth-1; i >= 0; --i ) + *dst++ = val >> (i*8); + } + } + } + else if (depth >= 15 && depth <= 23) { + /* have 5..7 bits per color, using 16 color image */ + unsigned int pix; + src = linux_logo16; + bdepth = (depth+7)/8; + for( y1 = 0; y1 < LOGO_H; y1++ ) { + dst = fb + y1*line; + for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) { + pix = (*src >> 4) | 0x10; /* upper nibble */ + val = (pix << redshift) | + (pix << greenshift) | + (pix << blueshift); + for( i = 0; i < bdepth; ++i ) + *dst++ = val >> (i*8); + pix = (*src & 0x0f) | 0x10; /* lower nibble */ + val = (pix << redshift) | + (pix << greenshift) | + (pix << blueshift); + for( i = bdepth-1; i >= 0; --i ) + *dst++ = val >> (i*8); + } + } + } + + done = 1; + } +#endif +#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \ + defined(CONFIG_FBCON_CFB32) + if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) { /* Modes without color mapping, needs special data transformation... */ - unsigned long val; /* max. depth 32! */ + unsigned int val; /* max. depth 32! */ int bdepth = depth/8; unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; unsigned char redmask, greenmask, bluemask; @@ -1218,8 +1262,7 @@ __initfunc(static int fbcon_show_logo( void )) done = 1; } #endif -#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FBCON_CYBER) || \ - defined(CONFIG_FBCON_RETINAZ3) +#if defined(CONFIG_FBCON_CFB8) if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) { /* depth 8 or more, packed, with color registers */ @@ -1316,153 +1359,23 @@ struct consw fb_con = { fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc, fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch, fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette, - fbcon_scrolldelta + fbcon_scrolldelta, fbcon_set_mode }; /* - * Driver registration - */ - -static struct display_switch *drivers = NULL, *accel_drivers = NULL; - -int fbcon_register_driver(struct display_switch *dispsw, int is_accel) -{ - struct display_switch **list; - - list = is_accel ? &accel_drivers : &drivers; - dispsw->next = *list; - *list = dispsw; - return 0; -} - -int fbcon_unregister_driver(struct display_switch *dispsw) -{ - struct display_switch **list; - - for (list = &accel_drivers; *list; list = &(*list)->next) - if (*list == dispsw) { - *list = dispsw->next; - dispsw->next = NULL; - return 0; - } - for (list = &drivers; *list; list = &(*list)->next) - if (*list == dispsw) { - *list = dispsw->next; - dispsw->next = NULL; - return 0; - } - return -EINVAL; -} - - -static struct display_switch *probe_list(struct display_switch *dispsw, - struct display *disp) -{ - while (dispsw) { - if (!dispsw->open(disp)) - return(dispsw); - dispsw = dispsw->next; - } - return(NULL); -} - - -#ifdef CONFIG_KMOD -static void request_driver(struct display *disp, int is_accel) -{ - char modname[30]; - int len; - const char *type; - - if (disp->var.bits_per_pixel == 1) - type = "mfb"; - else - switch (disp->type) { - case FB_TYPE_INTERLEAVED_PLANES: - if (disp->type_aux == 2) - type = "iplan2p%d"; - else - type = "ilbm"; - break; - case FB_TYPE_PLANES: - type = "afb"; - break; - case FB_TYPE_PACKED_PIXELS: - type = "cfb%d"; - break; - default: - return; - } - len = sprintf(modname, "fbcon-"); - len += sprintf(modname+len, type, disp->var.bits_per_pixel); - if (is_accel) - len += sprintf(modname+len, "-%d", disp->var.accel); - request_module(modname); -} -#endif /* CONFIG_KMOD */ - - -static struct display_switch *fbcon_get_driver(struct display *disp) -{ - struct display_switch *dispsw; - - if (disp->var.accel != FB_ACCEL_NONE) { - /* First try an accelerated driver */ - dispsw = probe_list(accel_drivers, disp); -#ifdef CONFIG_KMOD - if (!dispsw) { - request_driver(disp, 1); - dispsw = probe_list(accel_drivers, disp); - } -#endif - if (dispsw) - return(dispsw); - } - - /* Then try an unaccelerated driver */ - dispsw = probe_list(drivers, disp); -#ifdef CONFIG_KMOD - if (!dispsw) { - request_driver(disp, 0); - dispsw = probe_list(drivers, disp); - } -#endif - return(dispsw); -} - - -/* * Dummy Low Level Operations */ -static int open_dummy(struct display *p) -{ - if (p->line_length) - p->next_line = p->line_length; - else - p->next_line = p->var.xres_virtual>>3; - p->next_plane = 0; - p->var.bits_per_pixel = 1; - return 0; -} +static void fbcon_dummy_op(void) {} -static void misc_dummy(void) {} - -static struct display_switch dispsw_dummy = { - open_dummy, - /* release_dummy */ - misc_dummy, - /* bmove_dummy */ - (void (*)(struct display *, int, int, int, int, int, int))misc_dummy, - /* clear_dummy */ - (void (*)(struct vc_data *, struct display *, int, int, int, int))misc_dummy, - /* putc_dummy */ - (void (*)(struct vc_data *, struct display *, int, int, int))misc_dummy, - /* putcs_dummy */ - (void (*)(struct vc_data *, struct display *, const char *, int, int, int))misc_dummy, - /* rev_char_dummy */ - (void (*)(struct display *, int, int))misc_dummy, +static struct display_switch fbcon_dummy = { + (void *)fbcon_dummy_op, /* fbcon_dummy_setup */ + (void *)fbcon_dummy_op, /* fbcon_dummy_bmove */ + (void *)fbcon_dummy_op, /* fbcon_dummy_clear */ + (void *)fbcon_dummy_op, /* fbcon_dummy_putc */ + (void *)fbcon_dummy_op, /* fbcon_dummy_putcs */ + (void *)fbcon_dummy_op, /* fbcon_dummy_revc */ }; @@ -1471,5 +1384,3 @@ static struct display_switch dispsw_dummy = { */ EXPORT_SYMBOL(fb_display); -EXPORT_SYMBOL(fbcon_register_driver); -EXPORT_SYMBOL(fbcon_unregister_driver); diff --git a/drivers/video/fbcon.h b/drivers/video/fbcon.h index b83376d9e..4868b7730 100644 --- a/drivers/video/fbcon.h +++ b/drivers/video/fbcon.h @@ -8,6 +8,9 @@ * for more details. */ +#ifndef __VIDEO_FBCON_H +#define __VIDEO_FBCON_H + #include <linux/console_struct.h> @@ -16,8 +19,7 @@ */ struct display_switch { - int (*open)(struct display *p); - void (*release)(void); + void (*setup)(struct display *p); void (*bmove)(struct display *p, int sy, int sx, int dy, int dx, int height, int width); void (*clear)(struct vc_data *conp, struct display *p, int sy, int sx, @@ -26,20 +28,11 @@ struct display_switch { int xx); void (*putcs)(struct vc_data *conp, struct display *p, const char *s, int count, int yy, int xx); - void (*rev_char)(struct display *p, int xx, int yy); - struct display_switch *next; + void (*revc)(struct display *p, int xx, int yy); }; /* - * Driver registration - */ - -extern int fbcon_register_driver(struct display_switch *dispsw, int is_accel); -int fbcon_unregister_driver(struct display_switch *dispsw); - - - /* * Attribute Decoding */ @@ -335,3 +328,5 @@ static __inline__ void fast_memmove(char *dst, const char *src, size_t size) } #endif /* !m68k */ + +#endif /* __VIDEO_FBCON_H */ diff --git a/drivers/video/fbgen.c b/drivers/video/fbgen.c new file mode 100644 index 000000000..730438d16 --- /dev/null +++ b/drivers/video/fbgen.c @@ -0,0 +1,386 @@ +/* + * linux/drivers/video/fbgen.c -- Generic routines for frame buffer devices + * + * Created 3 Jan 1998 by Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#include <linux/module.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/fb.h> +#include <linux/slab.h> + +#include <asm/uaccess.h> + + + +static int currcon = 0; + +static struct display disp; + + + /* + * `Generic' versions of the frame buffer device operations + */ + +extern int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +extern int fbgen_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +extern int fbgen_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +extern int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +extern int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +extern int fbgen_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +extern int fbgen_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info); + + + /* + * Helper functions + */ + +int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info_gen *info); +void fbgen_set_disp(int con, struct fb_info_gen *info); +void fbgen_install_cmap(int con, struct fb_info_gen *info); +int fbgen_update_var(int con, struct fb_info *info); +int fbgen_switch(int con, struct fb_info *info); +void fbgen_blank(int blank, struct fb_info *info); + + +/* ---- `Generic' versions of the frame buffer device operations ----------- */ + + + /* + * Get the Fixed Part of the Display + */ + +int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + char par[info2->parsize]; + + if (con == -1) + fbhw->get_par(&par, info2); + else { + int err; + + if ((err = fbhw->decode_var(&fb_display[con].var, &par, info2))) + return err; + } + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + return fbhw->encode_fix(fix, &par, info2); +} + + + /* + * Get the User Defined Part of the Display + */ + +int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + char par[info2->parsize]; + + if (con == -1) { + fbhw->get_par(&par, info2); + fbhw->encode_var(var, &par, info2); + } else + *var = fb_display[con].var; + return 0; +} + + + /* + * Set the User Defined Part of the Display + */ + +int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + int err; + int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset; + + if ((err = fbgen_do_set_var(var, con == currcon, info2))) + return err; + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + oldxres = fb_display[con].var.xres; + oldyres = fb_display[con].var.yres; + oldxres_virtual = fb_display[con].var.xres_virtual; + oldyres_virtual = fb_display[con].var.yres_virtual; + oldbpp = fb_display[con].var.bits_per_pixel; + oldyoffset = fb_display[con].var.yoffset; + fb_display[con].var = *var; + if (oldxres != var->xres || oldyres != var->yres || + oldxres_virtual != var->xres_virtual || + oldyres_virtual != var->yres_virtual || + oldbpp != var->bits_per_pixel || + oldyoffset != var->yoffset) { + fbgen_set_disp(con, info2); + if (info->changevar) + (*info->changevar)(con); + if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0))) + return err; + fbgen_install_cmap(con, info2); + } + } + var->activate = 0; + return 0; +} + + + /* + * Get the Colormap + */ + +int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + + if (con == currcon) /* current console ? */ + return fb_get_cmap(cmap, &fb_display[con].var, kspc, fbhw->getcolreg, + info); + else + if (fb_display[con].cmap.len) /* non default colormap ? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return 0; +} + + + /* + * Set the Colormap + */ + +int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated ? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1 << fb_display[con].var.bits_per_pixel, 0))) + return err; + } + if (con == currcon) /* current console ? */ + return fb_set_cmap(cmap, &fb_display[con].var, kspc, fbhw->setcolreg, + info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + + + /* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ + +int fbgen_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + int xoffset = var->xoffset; + int yoffset = var->yoffset; + int err; + + if (xoffset < 0 || + xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual || + yoffset < 0 || + yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual) + return -EINVAL; + if (con == currcon) { + if (fbhw->pan_display) { + if ((err = fbhw->pan_display(var, info2))) + return err; + } else + return -EINVAL; + } + fb_display[con].var.xoffset = var->xoffset; + fb_display[con].var.yoffset = var->yoffset; + if (var->vmode & FB_VMODE_YWRAP) + fb_display[con].var.vmode |= FB_VMODE_YWRAP; + else + fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; + + return 0; +} + + + /* + * Frame Buffer Specific ioctls + */ + +int fbgen_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + + +/* ---- Helper functions --------------------------------------------------- */ + + + /* + * Change the video mode + */ + +int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info_gen *info) +{ + struct fbgen_hwswitch *fbhw = info->fbhw; + int err, activate; + char par[info->parsize]; + + if ((err = fbhw->decode_var(var, &par, info))) + return err; + activate = var->activate; + if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) + fbhw->set_par(&par, info); + fbhw->encode_var(var, &par, info); + var->activate = activate; + return 0; +} + + +void fbgen_set_disp(int con, struct fb_info_gen *info) +{ + struct fbgen_hwswitch *fbhw = info->fbhw; + struct fb_fix_screeninfo fix; + char par[info->parsize]; + struct display *display; + + if (con >= 0) + display = &fb_display[con]; + else + display = &disp; /* used during initialization */ + + if (con == -1) + fbhw->get_par(&par, info); + else + fbhw->decode_var(&fb_display[con].var, &par, info); + memset(&fix, 0, sizeof(struct fb_fix_screeninfo)); + fbhw->encode_fix(&fix, &par, info); + + display->screen_base = fix.smem_start; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + if (info->fbhw->blank || fix.visual == FB_VISUAL_PSEUDOCOLOR || + fix.visual == FB_VISUAL_DIRECTCOLOR) + display->can_soft_blank = 1; + else + display->can_soft_blank = 0; + display->dispsw = fbhw->get_dispsw(&par, info); +#if 0 /* FIXME: generic inverse is not supported yet */ + display->inverse = (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse); +#else + display->inverse = fix.visual == FB_VISUAL_MONO01; +#endif +} + + + /* + * Install the current colormap + */ + +void fbgen_install_cmap(int con, struct fb_info_gen *info) +{ + struct fbgen_hwswitch *fbhw = info->fbhw; + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, + fbhw->setcolreg, &info->info); + else + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, fbhw->setcolreg, &info->info); +} + + + /* + * Update the `var' structure (called by fbcon.c) + */ + +int fbgen_update_var(int con, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + int err; + + if (fbhw->pan_display) { + if ((err = fbhw->pan_display(&fb_display[con].var, info2))) + return err; + } + return 0; +} + + + /* + * Switch to a different virtual console + */ + +int fbgen_switch(int con, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + + /* Do we have to save the colormap ? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, + fbhw->getcolreg, &info2->info); + fbgen_do_set_var(&fb_display[con].var, 1, info2); + currcon = con; + /* Install new colormap */ + fbgen_install_cmap(con, info2); + return 0; +} + + + /* + * Blank the screen + */ + +void fbgen_blank(int blank, struct fb_info *info) +{ + struct fb_info_gen *info2 = (struct fb_info_gen *)info; + struct fbgen_hwswitch *fbhw = info2->fbhw; + u16 black[16]; + struct fb_cmap cmap; + + if (fbhw->blank && !fbhw->blank(blank, info2)) + return; + if (blank) { + memset(black, 0, 16*sizeof(u16)); + cmap.red = black; + cmap.green = black; + cmap.blue = black; + cmap.transp = NULL; + cmap.start = 0; + cmap.len = 16; + fb_set_cmap(&cmap, &fb_display[currcon].var, 1, fbhw->setcolreg, info); + } else + fbgen_install_cmap(currcon, info2); +} diff --git a/drivers/video/font_6x11.c b/drivers/video/font_6x11.c new file mode 100644 index 000000000..ebfe24258 --- /dev/null +++ b/drivers/video/font_6x11.c @@ -0,0 +1,3345 @@ +/**********************************************/ +/* */ +/* Font file generated by rthelen */ +/* */ +/**********************************************/ + +#define FONTDATAMAX (11*256) + +char fontname_6x11[] = "ProFont6x11"; + +int fontheight_6x11 = 11; +int fontwidth_6x11 = 6; + +unsigned char fontdata_6x11[FONTDATAMAX] = { + + /* 0 0x00 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^G' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^H' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 9 0x09 '^J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^K' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 11 0x0b '^L' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^M' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^O' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^P' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^Q' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^S' */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^U' */ + 0x18, /* 000 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x78, /* 0 000 */ + 0x78, /* 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^V' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^W' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^X' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^Y' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Z' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^[' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^\' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^]' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^^' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^_' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^`' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x38, /* 00 000 */ + 0x14, /* 000 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x58, /* 0 0 000 */ + 0x28, /* 00 0 000 */ + 0x34, /* 00 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x54, /* 0 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x18, /* 000 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x28, /* 00 0 000 */ + 0x48, /* 0 00 000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x74, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x60, /* 0 00000 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x6c, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x02, /* 000000 0 */ + 0x02, /* 000000 0 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x70, /* 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '^?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '\200' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '\201' */ + 0x28, /* 00 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '\202' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '\203' */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '\204' */ + 0x58, /* 0 0 000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '\205' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '\206' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '\207' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 '\210' */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '\211' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '\212' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '\213' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '\214' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '\215' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '\216' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '\217' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '\220' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '\221' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '\222' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '\223' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '\224' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '\225' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '\226' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '\227' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '\230' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '\231' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '\232' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '\233' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c '\234' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '\235' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e '\236' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f '\237' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '\240' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '\241' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '\242' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '\243' */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x40, /* 0 000000 */ + 0x70, /* 0 0000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '\244' */ + 0x44, /* 0 000 00 */ + 0x24, /* 00 00 00 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x14, /* 000 0 00 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '\245' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '\246' */ + 0x3c, /* 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x3c, /* 00 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '\247' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x58, /* 0 0 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '\250' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x08, /* 0000 000 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '\251' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x08, /* 0000 000 */ + 0x34, /* 00 0 00 */ + 0x44, /* 0 000 00 */ + 0x34, /* 00 0 00 */ + 0x08, /* 0000 000 */ + 0x70, /* 0 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '\252' */ + 0x00, /* 00000000 */ + 0x7a, /* 0 0 0 */ + 0x2e, /* 00 0 0 */ + 0x2e, /* 00 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '\253' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '\254' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '\255' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '\256' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x78, /* 0 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x5c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '\257' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '\260' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 177 0xb1 '\261' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 178 0xb2 '\262' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 179 0xb3 '\263' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 180 0xb4 '\264' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 181 0xb5 '\265' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x74, /* 0 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 182 0xb6 '\266' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x14, /* 000 0 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 183 0xb7 '\267' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x24, /* 00 00 00 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x24, /* 00 00 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 184 0xb8 '\270' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 185 0xb9 '\271' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 186 0xba '\272' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 187 0xbb '\273' */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 188 0xbc '\274' */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '\275' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x6c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '\276' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x5c, /* 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '\277' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 192 0xc0 '\300' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '\301' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '\302' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 195 0xc3 '\303' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 196 0xc4 '\304' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '\305' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 198 0xc6 '\306' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 199 0xc7 '\307' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 200 0xc8 '\310' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '\311' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x54, /* 0 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 202 0xca '\312' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '\313' */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 204 0xcc '\314' */ + 0x58, /* 0 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 205 0xcd '\315' */ + 0x58, /* 0 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '\316' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x58, /* 0 0 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 207 0xcf '\317' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x5c, /* 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x2c, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '\320' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '\321' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 210 0xd2 '\322' */ + 0x00, /* 00000000 */ + 0x14, /* 000 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 211 0xd3 '\323' */ + 0x00, /* 00000000 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '\324' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '\325' */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 214 0xd6 '\326' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 215 0xd7 '\327' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 216 0xd8 '\330' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 217 0xd9 '\331' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '\332' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 219 0xdb '\333' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 220 0xdc '\334' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 221 0xdd '\335' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 222 0xde '\336' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 223 0xdf '\337' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '\340' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '\341' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '\342' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '\343' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '\344' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '\345' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '\346' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 '\347' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '\350' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 '\351' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '\352' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '\353' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec '\354' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '\355' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee '\356' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef '\357' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '\360' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '\361' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '\362' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '\363' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '\364' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 245 0xf5 '\365' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 '\366' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '\367' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '\370' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '\371' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '\372' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '\373' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc '\374' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '\375' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '\376' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '\377' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + diff --git a/drivers/video/fonts.c b/drivers/video/fonts.c index 8e89805bb..56a3a33b2 100644 --- a/drivers/video/fonts.c +++ b/drivers/video/fonts.c @@ -9,7 +9,7 @@ */ -#include <linux/config.h> /* for CONFIG_AMIGA */ +#include <linux/config.h> #include <linux/types.h> #include <linux/string.h> #ifdef __mc68000__ @@ -25,17 +25,22 @@ /* VGA8x8 */ extern char fontname_8x8[]; extern int fontwidth_8x8, fontheight_8x8; -extern u_char fontdata_8x8[]; +extern u8 fontdata_8x8[]; /* VGA8x16 */ extern char fontname_8x16[]; extern int fontwidth_8x16, fontheight_8x16; -extern u_char fontdata_8x16[]; +extern u8 fontdata_8x16[]; /* PEARL8x8 */ extern char fontname_pearl8x8[]; extern int fontwidth_pearl8x8, fontheight_pearl8x8; -extern u_char fontdata_pearl8x8[]; +extern u8 fontdata_pearl8x8[]; + +/* VGA6x11 */ +extern char fontname_6x11[]; +extern int fontwidth_6x11, fontheight_6x11; +extern u8 fontdata_6x11[]; /* @@ -46,18 +51,20 @@ struct softfontdesc { char *name; int *width; int *height; - u_char *data; + u8 *data; }; #define VGA8x8_IDX 0 #define VGA8x16_IDX 1 #define PEARL8x8_IDX 2 +#define VGA6x11_IDX 3 static struct softfontdesc softfonts[] = { { fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 }, { fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 }, { fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8, fontdata_pearl8x8 }, + { fontname_6x11, &fontwidth_6x11, &fontheight_6x11, fontdata_6x11 }, }; static unsigned int numsoftfonts = sizeof(softfonts)/sizeof(*softfonts); @@ -67,7 +74,7 @@ static unsigned int numsoftfonts = sizeof(softfonts)/sizeof(*softfonts); * Find a font with a specific name */ -int findsoftfont(char *name, int *width, int *height, u_char *data[]) +int findsoftfont(char *name, int *width, int *height, u8 *data[]) { unsigned int i; @@ -90,7 +97,7 @@ int findsoftfont(char *name, int *width, int *height, u_char *data[]) */ void getdefaultfont(int xres, int yres, char *name[], int *width, int *height, - u_char *data[]) + u8 *data[]) { int i; @@ -103,6 +110,16 @@ void getdefaultfont(int xres, int yres, char *name[], int *width, int *height, } else i = VGA8x16_IDX; +#if defined(CONFIG_MAC) + if (MACH_IS_MAC) { +#if 0 /* MSch: removed until 6x11 is debugged */ + i = VGA6x11_IDX; /* I added this for fun ... I like 6x11 */ +#endif + if (xres < 640) + i = VGA6x11_IDX; + } +#endif + if (name) *name = softfonts[i].name; if (width) diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c new file mode 100644 index 000000000..43932a080 --- /dev/null +++ b/drivers/video/macfb.c @@ -0,0 +1,459 @@ +/* + * We've been given MAC frame buffer info by the booter. Now go set it up + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/nubus.h> +#include <linux/init.h> + +#include <asm/setup.h> +#include <asm/bootinfo.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/irq.h> +#include <asm/macintosh.h> +#include <linux/fb.h> + + +/* conditionalize these ?? */ +#include "fbcon-mfb.h" +#include "fbcon-cfb2.h" +#include "fbcon-cfb4.h" +#include "fbcon-cfb8.h" + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + +static struct fb_var_screeninfo macfb_defined={ + 0,0,0,0, /* W,H, W, H (virtual) load xres,xres_virtual*/ + 0,0, /* virtual -> visible no offset */ + 8, /* depth -> load bits_per_pixel */ + 0, /* greyscale ? */ + {0,0,0}, /* R */ + {0,0,0}, /* G */ + {0,0,0}, /* B */ + {0,0,0}, /* transparency */ + 0, /* standard pixel format */ + FB_ACTIVATE_NOW, + 274,195, /* 14" monitor *Mikael Nykvist's anyway* */ + FB_ACCEL_NONE, /* The only way to accelerate a mac is .. */ + 0L,0L,0L,0L,0L, + 0L,0L,0, /* No sync info */ + FB_VMODE_NONINTERLACED, + {0,0,0,0,0,0} +}; + +#define NUM_TOTAL_MODES 1 +#define NUM_PREDEF_MODES 1 + +static struct display disp; +static struct fb_info fb_info; + +static int inverse = 0; + +struct macfb_par +{ + void *unused; +}; + +static int currcon = 0; +static int current_par_valid = 0; +struct macfb_par current_par; + +static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres; +static unsigned long mac_videobase; +static unsigned long mac_videosize; + + /* + * Open/Release the frame buffer device + */ + +static int macfb_open(struct fb_info *info) +{ + /* + * Nothing, only a usage count for the moment + */ + MOD_INC_USE_COUNT; + return(0); +} + +static int macfb_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +static void macfb_encode_var(struct fb_var_screeninfo *var, + struct macfb_par *par) +{ + int i=0; + var->xres=mac_xres; + var->yres=mac_yres; + var->xres_virtual=mac_vxres; + var->yres_virtual=var->yres; + var->xoffset=0; + var->yoffset=0; + var->bits_per_pixel = mac_depth; + var->grayscale=0; + var->transp.offset=0; + var->transp.length=0; + var->transp.msb_right=0; + var->nonstd=0; + var->activate=0; + var->height= -1; + var->width= -1; + var->accel=0; + var->vmode=FB_VMODE_NONINTERLACED; + var->pixclock=0; + var->sync=0; + var->left_margin=0; + var->right_margin=0; + var->upper_margin=0; + var->lower_margin=0; + var->hsync_len=0; + var->vsync_len=0; + for(i=0;i<arraysize(var->reserved);i++) + var->reserved[i]=0; + return; +} + + +static void macfb_get_par(struct macfb_par *par) +{ + *par=current_par; +} + +static void macfb_set_par(struct macfb_par *par) +{ + current_par_valid=1; +} + +static int fb_update_var(int con, struct fb_info *info) +{ + return 0; +} + +static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) +{ + struct macfb_par par; + + macfb_get_par(&par); + macfb_encode_var(var, &par); + return 0; +} + +extern int console_loglevel; + +static void macfb_encode_fix(struct fb_fix_screeninfo *fix, + struct macfb_par *par) +{ + int i; + + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id,"Macintosh"); + + /* + * X works, but screen wraps ... + */ + fix->smem_start=(char *)(mac_videobase&PAGE_MASK); + fix->smem_offset=(mac_videobase&~PAGE_MASK); + fix->smem_len=PAGE_ALIGN(mac_videosize); + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = FB_VISUAL_PSEUDOCOLOR; + fix->xpanstep=0; + fix->ypanstep=0; + fix->ywrapstep=0; + fix->line_length=mac_xbytes; + return; +} + +static int macfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + struct macfb_par par; + macfb_get_par(&par); + macfb_encode_fix(fix, &par); + return 0; +} + +static int macfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct macfb_par par; + if(con==-1) + { + macfb_get_par(&par); + macfb_encode_var(var, &par); + } + else + *var=fb_display[con].var; + return 0; +} + +static void macfb_set_disp(int con) +{ + struct fb_fix_screeninfo fix; + struct display *display; + + if (con >= 0) + display = &fb_display[con]; + else + display = &disp; /* used during initialization */ + + macfb_get_fix(&fix, con, 0); + + display->screen_base = (u_char *)(fix.smem_start+fix.smem_offset); + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->next_line = fix.line_length; + display->can_soft_blank = 0; + display->inverse = inverse; + + switch (mac_depth) { +#ifdef CONFIG_FBCON_MFB + case 1: + display->dispsw = &fbcon_mfb; + break; +#endif +#ifdef CONFIG_FBCON_CFB2 + case 2: + display->dispsw = &fbcon_cfb2; + break; +#endif +#ifdef CONFIG_FBCON_CFB4 + case 4: + display->dispsw = &fbcon_cfb4; + break; +#endif +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_cfb8; + break; +#endif + default: + display->dispsw = NULL; + break; + } +} + +static int macfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + int err; + + if ((err=do_fb_set_var(var, 1))) + return err; + return 0; +} + +static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ +#if 0 + printk("macfb_get_cmap: not supported!\n"); + /* interferes with X11 */ + if (console_loglevel < 7) + return -EINVAL; + if (con == currcon) /* current console? */ + return fb_get_cmap(cmap, &fb_display[con].var, kspc, 0 /*offb_getcolreg*/, info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); +#endif + return 0; + +} + +static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ +#if 0 + printk("macfb_set_cmap: not supported!\n"); + if (console_loglevel < 7) + return -EINVAL; + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1<<fb_display[con].var.bits_per_pixel, 0))) + return err; + } + if (con == currcon) /* current console? */ + return fb_set_cmap(cmap, &fb_display[con].var, kspc, 1 /*offb_setcolreg*/, info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); +#endif + return 0; +} + +static int macfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + /* no panning */ + printk("macfb_pan: not supported!\n"); + return -EINVAL; +} + +static int macfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info) +{ + printk("macfb_ioctl: not supported!\n"); + return -EINVAL; +} + +static struct fb_ops macfb_ops = { + macfb_open, + macfb_release, + macfb_get_fix, + macfb_get_var, + macfb_set_var, + macfb_get_cmap, + macfb_set_cmap, + macfb_pan_display, + NULL, + macfb_ioctl +}; + +void macfb_setup(char *options, int *ints) +{ + char *this_opt; + int temp; + + fb_info.fontname[0] = '\0'; + + if (!options || !*options) + return; + + for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) { + if (!*this_opt) continue; + + if (! strcmp(this_opt, "inverse")) + inverse=1; + else if (!strncmp(this_opt, "font:", 5)) { + strcpy(fb_info.fontname, this_opt+5); + printk("macfb_setup: option %s\n", this_opt); + } + } +} + +static int macfb_switch(int con, struct fb_info *info) +{ + do_fb_set_var(&fb_display[con].var,1); + currcon=con; + return 0; +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ + +static void macfb_blank(int blank, struct fb_info *info) +{ + /* Not supported */ +} + +/* + * Nubus call back. This will give us our board identity and also + * other useful info we need later + */ + +static int nubus_video_card(struct nubus_device_specifier *ns, int slot, struct nubus_type *nt) +{ + if(nt->category==NUBUS_CAT_DISPLAY) + return 0; + /* Claim all video cards. We dont yet do driver specifics tho. */ + return -ENODEV; +} + +static struct nubus_device_specifier nb_video={ + nubus_video_card, + NULL +}; + +__initfunc(unsigned long macfb_init(unsigned long mem_start)) +{ + /* nubus_remap the video .. */ + int err; + + if (!MACH_IS_MAC) + return mem_start; + + mac_xres=mac_bi_data.dimensions&0xFFFF; + mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16; + mac_depth=mac_bi_data.videodepth; + mac_xbytes=mac_bi_data.videorow; + mac_vxres = (mac_xbytes/mac_depth)*8; + mac_videosize=mac_xbytes*mac_yres; + mac_videobase=mac_bi_data.videoaddr; + + printk("macfb_init: xres %d yres %d bpp %d addr %x size %d \n", + mac_xres, mac_yres, mac_depth, mac_videobase, mac_videosize); + + mac_debugging_penguin(4); + + /* + * Fill in the available video resolution + */ + + macfb_defined.xres=mac_xres; + macfb_defined.yres=mac_yres; + macfb_defined.xres_virtual=mac_vxres; + macfb_defined.yres_virtual=mac_yres; + macfb_defined.bits_per_pixel=mac_depth; + + + /* + * Let there be consoles.. + */ + strcpy(fb_info.modename, "Macintosh Builtin "); + fb_info.changevar = NULL; + fb_info.node = -1; + fb_info.fbops = &macfb_ops; + fb_info.disp=&disp; + fb_info.switch_con=&macfb_switch; + fb_info.updatevar=&fb_update_var; + fb_info.blank=&macfb_blank; + do_fb_set_var(&macfb_defined,1); + + err=register_framebuffer(&fb_info); + if(err<0) + { + mac_boom(6); + return NULL; + } + + macfb_get_var(&disp.var, -1, &fb_info); + macfb_set_disp(-1); + + /* + * Register the nubus hook + */ + + register_nubus_device(&nb_video); + + printk("fb%d: %s frame buffer device using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10); + + return mem_start; +} + +#if 0 +/* + * These two auxiliary debug functions should go away ASAP. Only usage: + * before the console output is up (after head.S come some other crucial + * setup routines :-) + * + * Now in debug.c ... + */ +#endif diff --git a/drivers/video/offb.c b/drivers/video/offb.c index cc521899a..26ab95dfb 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -12,6 +12,7 @@ * more details. */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -23,53 +24,83 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/fb.h> +#include <linux/selection.h> #include <linux/init.h> +#ifdef CONFIG_FB_COMPAT_XPMAC +#include <linux/vc_ioctl.h> +#endif #include <asm/io.h> #include <asm/prom.h> +#include "fbcon-cfb8.h" -#define arraysize(x) (sizeof(x)/sizeof(*(x))) static int currcon = 0; -static struct display disp; -static struct fb_info fb_info; -static struct { u_char red, green, blue, pad; } palette[256]; -static char offb_name[16] = "OFfb "; -static volatile unsigned char *unknown_cmap_adr = NULL; -static volatile unsigned char *unknown_cmap_data = NULL; +struct fb_info_offb { + struct fb_info info; + struct fb_fix_screeninfo fix; + struct fb_var_screeninfo var; + struct display disp; + struct { u_char red, green, blue, pad; } palette[256]; + volatile unsigned char *cmap_adr; + volatile unsigned char *cmap_data; +}; + +static struct fb_info_offb fb_info[FB_MAX]; + +#ifdef __powerpc__ +#define mach_eieio() eieio() +#else +#define mach_eieio() do {} while (0) +#endif -static struct fb_fix_screeninfo fb_fix = { 0, }; -static struct fb_var_screeninfo fb_var = { 0, }; +static int ofonly = 0; /* * Interface used by the world */ -void offb_video_setup(char *options, int *ints); - -static int offb_open(int fbidx); -static int offb_release(int fbidx); -static int offb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int offb_get_var(struct fb_var_screeninfo *var, int con); -static int offb_set_var(struct fb_var_screeninfo *var, int con); -static int offb_pan_display(struct fb_var_screeninfo *var, int con); -static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con); +unsigned long offb_init(unsigned long mem_start); +void offb_setup(char *options, int *ints); + +static int offb_open(struct fb_info *info); +static int offb_release(struct fb_info *info); +static int offb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int offb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int offb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int offb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); + u_long arg, int con, struct fb_info *info); + +#ifdef CONFIG_FB_COMPAT_XPMAC +int console_getmode(struct vc_mode *); +int console_setmode(struct vc_mode *, int); +int console_powermode(int); +struct fb_info *console_fb_info = NULL; +int (*console_setmode_ptr)(struct vc_mode *, int) = NULL; +int (*console_set_cmap_ptr)(struct fb_cmap *, int, int, struct fb_info *) + = NULL; +struct vc_mode display_info; +#endif /* CONFIG_FB_COMPAT_XPMAC */ /* * Interface to the low level console driver */ -unsigned long offb_init(unsigned long mem_start); -static int offbcon_switch(int con); -static int offbcon_updatevar(int con); -static void offbcon_blank(int blank); -static int offbcon_setcmap(struct fb_cmap *cmap, int con); +static int offbcon_switch(int con, struct fb_info *info); +static int offbcon_updatevar(int con, struct fb_info *info); +static void offbcon_blank(int blank, struct fb_info *info); /* @@ -77,15 +108,15 @@ static int offbcon_setcmap(struct fb_cmap *cmap, int con); */ static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); -static void do_install_cmap(int con); + u_int transp, struct fb_info *info); +static void do_install_cmap(int con, struct fb_info *info); static struct fb_ops offb_ops = { offb_open, offb_release, offb_get_fix, offb_get_var, offb_set_var, - offb_get_cmap, offb_set_cmap, offb_pan_display, offb_ioctl + offb_get_cmap, offb_set_cmap, offb_pan_display, NULL, offb_ioctl }; @@ -93,20 +124,20 @@ static struct fb_ops offb_ops = { * Open/Release the frame buffer device */ -static int offb_open(int fbidx) +static int offb_open(struct fb_info *info) { - /* - * Nothing, only a usage count for the moment - */ + /* + * Nothing, only a usage count for the moment + */ MOD_INC_USE_COUNT; - return(0); + return(0); } - -static int offb_release(int fbidx) + +static int offb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; - return(0); + return(0); } @@ -114,9 +145,12 @@ static int offb_release(int fbidx) * Get the Fixed Part of the Display */ -static int offb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int offb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { - memcpy(fix, &fb_fix, sizeof(fb_fix)); + struct fb_info_offb *info2 = (struct fb_info_offb *)info; + + memcpy(fix, &info2->fix, sizeof(struct fb_fix_screeninfo)); return 0; } @@ -125,9 +159,12 @@ static int offb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int offb_get_var(struct fb_var_screeninfo *var, int con) +static int offb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { - memcpy(var, &fb_var, sizeof(fb_var)); + struct fb_info_offb *info2 = (struct fb_info_offb *)info; + + memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo)); return 0; } @@ -136,33 +173,36 @@ static int offb_get_var(struct fb_var_screeninfo *var, int con) * Set the User Defined Part of the Display */ -static int offb_set_var(struct fb_var_screeninfo *var, int con) +static int offb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { struct display *display; int oldbpp = -1, err; + int activate = var->activate; + struct fb_info_offb *info2 = (struct fb_info_offb *)info; if (con >= 0) display = &fb_display[con]; else - display = &disp; /* used during initialization */ + display = &info2->disp; /* used during initialization */ - if (var->xres > fb_var.xres || var->yres > fb_var.yres || - var->xres_virtual > fb_var.xres_virtual || - var->yres_virtual > fb_var.yres_virtual || - var->bits_per_pixel > fb_var.bits_per_pixel || + if (var->xres > info2->var.xres || var->yres > info2->var.yres || + var->xres_virtual > info2->var.xres_virtual || + var->yres_virtual > info2->var.yres_virtual || + var->bits_per_pixel > info2->var.bits_per_pixel || var->nonstd || (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) return -EINVAL; - memcpy(var, &fb_var, sizeof(fb_var)); + memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo)); - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { oldbpp = display->var.bits_per_pixel; display->var = *var; } if (oldbpp != var->bits_per_pixel) { if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) return err; - do_install_cmap(con); + do_install_cmap(con, info); } return 0; } @@ -174,7 +214,8 @@ static int offb_set_var(struct fb_var_screeninfo *var, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int offb_pan_display(struct fb_var_screeninfo *var, int con) +static int offb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { if (var->xoffset || var->yoffset) return -EINVAL; @@ -186,14 +227,16 @@ static int offb_pan_display(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ - return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg); + return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg, + info); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } @@ -202,11 +245,13 @@ static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { + struct fb_info_offb *info2 = (struct fb_info_offb *)info; int err; - if (!unknown_cmap_adr) + if (!info2->cmap_adr) return -ENOSYS; if (!fb_display[con].cmap.len) { /* no colormap allocated? */ @@ -215,7 +260,8 @@ static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con) return err; } if (con == currcon) /* current console? */ - return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg); + return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg, + info); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return 0; @@ -223,12 +269,24 @@ static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con) static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con) + u_long arg, int con, struct fb_info *info) { return -EINVAL; } +#ifdef CONFIG_FB_ATY +extern unsigned long atyfb_of_init(unsigned long mem_start, + struct device_node *dp); + +static const char *aty_names[] = { + "ATY,mach64", "ATY,XCLAIM", "ATY,264VT", "ATY,mach64ii", "ATY,264GT-B", + "ATY,mach64_3D_pcc", "ATY,XCLAIM3D", "ATY,XCLAIMVR", "ATY,RAGEII_M", + "ATY,XCLAIMVRPro", "ATY,mach64_3DU" +}; +#endif /* CONFIG_FB_ATY */ + + /* * Initialisation */ @@ -236,128 +294,196 @@ static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd, __initfunc(unsigned long offb_init(unsigned long mem_start)) { struct device_node *dp; - int i, err, *pp, len; + int dpy, i, err, *pp, len; unsigned *up, address; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + struct display *disp; + struct fb_info_offb *info; + + for (dpy = 0; dpy < prom_num_displays; dpy++) { + if (!(dp = find_path_device(prom_display_paths[dpy]))) + continue; + + info = &fb_info[dpy]; + fix = &info->fix; + var = &info->var; + disp = &info->disp; + + if (!ofonly) { +#ifdef CONFIG_FB_ATY + for (i = 0; i < sizeof(aty_names)/sizeof(*aty_names); i++) + if (!strcmp(dp->name, aty_names[i])) + break; + if (i < sizeof(aty_names)/sizeof(*aty_names)) { + mem_start = atyfb_of_init(mem_start, dp); + continue; + } +#endif /* CONFIG_FB_ATY */ + } - if (!prom_display_path[0]) - return mem_start; - if (!(dp = find_path_device(prom_display_path))) - return mem_start; - - strncat(offb_name, dp->name, sizeof(offb_name)); - offb_name[sizeof(offb_name)-1] = '\0'; - strcpy(fb_fix.id, offb_name); + strcpy(fix->id, "OFfb "); + strncat(fix->id, dp->name, sizeof(fix->id)); + fix->id[sizeof(fix->id)-1] = '\0'; - if ((pp = (int *)get_property(dp, "depth", &len)) != NULL - && len == sizeof(int) && *pp != 8) { - printk("%s: can't use depth = %d\n", dp->full_name, *pp); - return mem_start; - } - if ((pp = (int *)get_property(dp, "width", &len)) != NULL - && len == sizeof(int)) - fb_var.xres = fb_var.xres_virtual = *pp; - if ((pp = (int *)get_property(dp, "height", &len)) != NULL - && len == sizeof(int)) - fb_var.yres = fb_var.yres_virtual = *pp; - if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL - && len == sizeof(int)) - fb_fix.line_length = *pp; - else - fb_fix.line_length = fb_var.xres_virtual; - fb_fix.smem_len = fb_fix.line_length*fb_var.yres; - if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL - && len == sizeof(unsigned)) - address = (u_long)*up; - else { - for (i = 0; i < dp->n_addrs; ++i) - if (dp->addrs[i].size >= len) - break; - if (i >= dp->n_addrs) { - printk("no framebuffer address found for %s\n", dp->full_name); - return mem_start; + if ((pp = (int *)get_property(dp, "depth", &len)) != NULL + && len == sizeof(int) && *pp != 8) { + printk("%s: can't use depth = %d\n", dp->full_name, *pp); + continue; + } + if ((pp = (int *)get_property(dp, "width", &len)) != NULL + && len == sizeof(int)) + var->xres = var->xres_virtual = *pp; + if ((pp = (int *)get_property(dp, "height", &len)) != NULL + && len == sizeof(int)) + var->yres = var->yres_virtual = *pp; + if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL + && len == sizeof(int)) + fix->line_length = *pp; + else + fix->line_length = var->xres_virtual; + fix->smem_len = fix->line_length*var->yres; + if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL + && len == sizeof(unsigned)) + address = (u_long)*up; + else { + for (i = 0; i < dp->n_addrs; ++i) + if (dp->addrs[i].size >= len) + break; + if (i >= dp->n_addrs) { + printk("no framebuffer address found for %s\n", dp->full_name); + continue; + } + address = (u_long)dp->addrs[i].address; + } + fix->smem_start = ioremap(address, fix->smem_len); + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + + /* XXX kludge for ati */ + if (strncmp(dp->name, "ATY,", 4) == 0) { + info->cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0; + info->cmap_data = info->cmap_adr + 1; } - address = (u_long)dp->addrs[i].address; - } - fb_fix.smem_start = ioremap(address, fb_fix.smem_len); - fb_fix.type = FB_TYPE_PACKED_PIXELS; - fb_fix.type_aux = 0; - - /* XXX kludge for ati */ - if (strncmp(dp->name, "ATY,", 4) == 0) { - unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0; - unknown_cmap_data = unknown_cmap_adr + 1; - } - fb_fix.visual = unknown_cmap_adr ? FB_VISUAL_PSEUDOCOLOR : + fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; - fb_var.xoffset = fb_var.yoffset = 0; - fb_var.bits_per_pixel = 8; - fb_var.grayscale = 0; - fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0; - fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8; - fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0; - fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0; - fb_var.nonstd = 0; - fb_var.activate = 0; - fb_var.height = fb_var.width = -1; - fb_var.accel = FB_ACCEL_NONE; - fb_var.pixclock = 10000; - fb_var.left_margin = fb_var.right_margin = 16; - fb_var.upper_margin = fb_var.lower_margin = 16; - fb_var.hsync_len = fb_var.vsync_len = 8; - fb_var.sync = 0; - fb_var.vmode = FB_VMODE_NONINTERLACED; - - disp.var = fb_var; - disp.cmap.start = 0; - disp.cmap.len = 0; - disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL; - disp.screen_base = fb_fix.smem_start; - disp.visual = fb_fix.visual; - disp.type = fb_fix.type; - disp.type_aux = fb_fix.type_aux; - disp.ypanstep = 0; - disp.ywrapstep = 0; - disp.line_length = fb_fix.line_length; - disp.can_soft_blank = 1; - disp.inverse = 0; - - strcpy(fb_info.modename, "OFfb "); - strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename)); - fb_info.node = -1; - fb_info.fbops = &offb_ops; - fb_info.fbvar_num = 1; - fb_info.fbvar = &fb_var; - fb_info.disp = &disp; - fb_info.fontname[0] = '\0'; - fb_info.changevar = NULL; - fb_info.switch_con = &offbcon_switch; - fb_info.updatevar = &offbcon_updatevar; - fb_info.blank = &offbcon_blank; - fb_info.setcmap = &offbcon_setcmap; - - err = register_framebuffer(&fb_info); - if (err < 0) - return mem_start; - - offb_set_var(&fb_var, -1); - - printk("Open Firmware frame buffer device on %s\n", dp->full_name); + var->xoffset = var->yoffset = 0; + var->bits_per_pixel = 8; + var->grayscale = 0; + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.offset = var->transp.length = var->transp.msb_right = 0; + var->nonstd = 0; + var->activate = 0; + var->height = var->width = -1; + var->accel = FB_ACCEL_NONE; + var->pixclock = 10000; + var->left_margin = var->right_margin = 16; + var->upper_margin = var->lower_margin = 16; + var->hsync_len = var->vsync_len = 8; + var->sync = 0; + var->vmode = FB_VMODE_NONINTERLACED; + + disp->var = *var; + disp->cmap.start = 0; + disp->cmap.len = 0; + disp->cmap.red = disp->cmap.green = disp->cmap.blue = disp->cmap.transp = NULL; + disp->screen_base = fix->smem_start; + disp->visual = fix->visual; + disp->type = fix->type; + disp->type_aux = fix->type_aux; + disp->ypanstep = 0; + disp->ywrapstep = 0; + disp->line_length = fix->line_length; + disp->can_soft_blank = info->cmap_adr ? 1 : 0; + disp->inverse = 0; +#ifdef CONFIG_FBCON_CFB8 + disp->dispsw = &fbcon_cfb8; +#else + disp->dispsw = NULL; +#endif + + strcpy(info->info.modename, "OFfb "); + strncat(info->info.modename, dp->full_name, + sizeof(info->info.modename)); + info->info.node = -1; + info->info.fbops = &offb_ops; + info->info.disp = disp; + info->info.fontname[0] = '\0'; + info->info.changevar = NULL; + info->info.switch_con = &offbcon_switch; + info->info.updatevar = &offbcon_updatevar; + info->info.blank = &offbcon_blank; + + err = register_framebuffer(&info->info); + if (err < 0) + continue; + + for (i = 0; i < 16; i++) { + int j = color_table[i]; + info->palette[i].red = default_red[j]; + info->palette[i].green = default_grn[j]; + info->palette[i].blue = default_blu[j]; + } + offb_set_var(var, -1, &info->info); + + printk("fb%d: Open Firmware frame buffer device on %s\n", + GET_FB_IDX(info->info.node), dp->full_name); + +#ifdef CONFIG_FB_COMPAT_XPMAC + if (!console_fb_info) { + display_info.height = var->yres; + display_info.width = var->xres; + display_info.depth = 8; + display_info.pitch = fix->line_length; + display_info.mode = 0; + strncpy(display_info.name, dp->name, sizeof(display_info.name)); + display_info.fb_address = iopa((unsigned long)fix->smem_start); + display_info.cmap_adr_address = 0; + display_info.cmap_data_address = 0; + display_info.disp_reg_address = 0; + /* XXX kludge for ati */ + if (strncmp(dp->name, "ATY,", 4) == 0) { + display_info.disp_reg_address = iopa(address + 0x7ffc00); + display_info.cmap_adr_address = iopa(address + 0x7ffcc0); + display_info.cmap_data_address = iopa(address + 0x7ffcc1); + } + console_fb_info = &info->info; + console_set_cmap_ptr = offb_set_cmap; + } +#endif /* CONFIG_FB_COMPAT_XPMAC) */ + } return mem_start; } -static int offbcon_switch(int con) + /* + * Setup: parse used options + */ + +void offb_setup(char *options, int *ints) +{ + if (!options || !*options) + return; + + if (!strcmp(options, "ofonly")) + ofonly = 1; +} + + +static int offbcon_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, - offb_getcolreg); + offb_getcolreg, info); currcon = con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -365,7 +491,7 @@ static int offbcon_switch(int con) * Update the `var' structure (called by fbcon.c) */ -static int offbcon_updatevar(int con) +static int offbcon_updatevar(int con, struct fb_info *info) { /* Nothing */ return 0; @@ -375,34 +501,42 @@ static int offbcon_updatevar(int con) * Blank the display. */ -static void offbcon_blank(int blank) +static void offbcon_blank(int blank, struct fb_info *info) { - /* Nothing */ -} + struct fb_info_offb *info2 = (struct fb_info_offb *)info; + int i, j; - /* - * Set the colormap - */ + if (!info2->cmap_adr) + return; -static int offbcon_setcmap(struct fb_cmap *cmap, int con) -{ - return(offb_set_cmap(cmap, 1, con)); + if (blank) + for (i = 0; i < 256; i++) { + *info2->cmap_adr = i; + mach_eieio(); + for (j = 0; j < 3; j++) { + *info2->cmap_data = 0; + mach_eieio(); + } + } + else + do_install_cmap(currcon, info); } - /* * Read a single color register and split it into * colors/transparent. Return != 0 for invalid regno. */ static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) + u_int *transp, struct fb_info *info) { - if (!unknown_cmap_adr || regno > 255) + struct fb_info_offb *info2 = (struct fb_info_offb *)info; + + if (!info2->cmap_adr || regno > 255) return 1; - *red = palette[regno].red; - *green = palette[regno].green; - *blue = palette[regno].blue; + *red = info2->palette[regno].red; + *green = info2->palette[regno].green; + *blue = info2->palette[regno].blue; return 0; } @@ -414,41 +548,113 @@ static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, */ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) + u_int transp, struct fb_info *info) { - if (!unknown_cmap_adr || regno > 255) + struct fb_info_offb *info2 = (struct fb_info_offb *)info; + + if (!info2->cmap_adr || regno > 255) return 1; - palette[regno].red = red; - palette[regno].green = green; - palette[regno].blue = blue; - *unknown_cmap_adr = regno; -#ifdef __powerpc__ - eieio(); -#endif - *unknown_cmap_data = red; -#ifdef __powerpc__ - eieio(); -#endif - *unknown_cmap_data = green; -#ifdef __powerpc__ - eieio(); -#endif - *unknown_cmap_data = blue; -#ifdef __powerpc__ - eieio(); -#endif + info2->palette[regno].red = red; + info2->palette[regno].green = green; + info2->palette[regno].blue = blue; + *info2->cmap_adr = regno; + mach_eieio(); + *info2->cmap_data = red; + mach_eieio(); + *info2->cmap_data = green; + mach_eieio(); + *info2->cmap_data = blue; + mach_eieio(); return 0; } -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - offb_setcolreg); + offb_setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), - &fb_display[con].var, 1, offb_setcolreg); + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, offb_setcolreg, + info); } + + +#ifdef CONFIG_FB_COMPAT_XPMAC + + /* + * Backward compatibility mode for Xpmac + * + * To do: + * + * - console_setmode() should fill in a struct fb_var_screeninfo (using + * the MacOS video mode database) and simply call a decode_var() + * function, so console_setmode_ptr is no longer needed. + * + * - instead of using the console_* stuff (filled in by the frame + * buffer), we should use the correct struct fb_info for the + * foreground virtual console. + */ + +int console_getmode(struct vc_mode *mode) +{ + *mode = display_info; + return 0; +} + +int console_setmode(struct vc_mode *mode, int doit) +{ + int err; + + if (console_setmode_ptr == NULL) + return -EINVAL; + + err = (*console_setmode_ptr)(mode, doit); + return err; +} + +static u16 palette_red[16]; +static u16 palette_green[16]; +static u16 palette_blue[16]; + +static struct fb_cmap palette_cmap = { + 0, 16, palette_red, palette_green, palette_blue, NULL +}; + +int console_setcmap(int n_entries, unsigned char *red, unsigned char *green, + unsigned char *blue) +{ + int i, j, n; + + if (console_set_cmap_ptr == NULL) + return -EOPNOTSUPP; + for (i = 0; i < n_entries; i += n) { + n = n_entries-i; + if (n > 16) + n = 16; + palette_cmap.start = i; + palette_cmap.len = n; + for (j = 0; j < n; j++) { + palette_cmap.red[j] = (red[i+j] << 8) | red[i+j]; + palette_cmap.green[j] = (green[i+j] << 8) | green[i+j]; + palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j]; + } + (*console_set_cmap_ptr)(&palette_cmap, 1, fg_console, console_fb_info); + } + return 0; +} + +int console_powermode(int mode) +{ + if (mode == VC_POWERMODE_INQUIRY) + return 0; + if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN) + return -EINVAL; + /* Not supported */ + return -ENXIO; +} + +#endif /* CONFIG_FB_COMPAT_XPMAC */ diff --git a/drivers/video/retz3fb.c b/drivers/video/retz3fb.c index 18a410614..82dcc2877 100644 --- a/drivers/video/retz3fb.c +++ b/drivers/video/retz3fb.c @@ -21,6 +21,7 @@ */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -39,6 +40,10 @@ #include <asm/pgtable.h> #include "retz3fb.h" +#include "fbcon.h" +#include "fbcon-cfb8.h" +#include "fbcon-cfb16.h" + /* #define DEBUG if(1) */ #define DEBUG if(0) @@ -54,7 +59,7 @@ #define arraysize(x) (sizeof(x)/sizeof(*(x))) -struct retz3_fb_par { +struct retz3fb_par { int xres; int yres; int xres_vir; @@ -93,7 +98,7 @@ struct display_data { long v_dispend; /* Horizontal Display End */ }; -static struct retz3_fb_par current_par; +static struct retz3fb_par current_par; static int current_par_valid = 0; static int currcon = 0; @@ -114,13 +119,15 @@ static struct fb_hwswitch { /* Display Control */ - int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3_fb_par *par); - int (*decode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par); - int (*encode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par); + int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3fb_par *par); + int (*decode_var)(struct fb_var_screeninfo *var, struct retz3fb_par *par); + int (*encode_var)(struct fb_var_screeninfo *var, struct retz3fb_par *par); int (*getcolreg)(unsigned int regno, unsigned int *red, unsigned - int *green, unsigned int *blue, unsigned int *transp); + int *green, unsigned int *blue, unsigned int *transp, + struct fb_info *info); int (*setcolreg)(unsigned int regno, unsigned int red, unsigned int - green, unsigned int blue, unsigned int transp); + green, unsigned int blue, unsigned int transp, + struct fb_info *info); void (*blank)(int blank); } *fbhw; @@ -129,7 +136,7 @@ static struct fb_hwswitch { * Frame Buffer Name */ -static char retz3_fb_name[16] = "RetinaZ3"; +static char retz3fb_name[16] = "RetinaZ3"; static unsigned char retz3_color_table [256][4]; @@ -140,46 +147,6 @@ static volatile unsigned char *z3_regs; /* - * Predefined Video Mode Names - */ - -static char *retz3_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", - - /* - * Predefined Video Modes - */ - - "640x480", /* RetinaZ3 8 bpp */ - "800x600", /* RetinaZ3 8 bpp */ - "1024x768i", - "640x480-16", /* RetinaZ3 16 bpp */ - "640x480-24", /* RetinaZ3 24 bpp */ - - /* - * Dummy Video Modes - */ - - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - - /* - * User Defined Video Modes - * - * This doesn't work yet!! - */ - - "user0", "user1", "user2", "user3", - "user4", "user5", "user6", "user7" -}; - -/* * A small info on how to convert XFree86 timing values into fb * timings - by Frank Neumann: * @@ -217,131 +184,124 @@ under "programs/Xserver/hw/xfree86/doc/modeDB.txt". */ /* - * Predefined Video Mode Definitions + * Predefined Video Modes */ -static struct fb_var_screeninfo retz3_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { 0, }, - - /* - * Predefined Video Modes - */ - - /* - * NB: it is very important to adjust the pixel-clock to the color-depth. - */ - - { - 640, 480, 640, 480, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - /* - ModeLine "800x600" 36 800 824 896 1024 600 601 603 625 - < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL - */ - { - /* 800 x 600, 8 bpp */ - 800, 600, 800, 600, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 27778, 64, 24, 22, 1, 120, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - /* - ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace - < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL - */ - { - /* 1024 x 768, 8 bpp, interlaced */ - 1024, 768, 1024, 768, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 22222, 40, 40, 32, 9, 160, 8, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED - }, - { - 640, 480, 640, 480, 0, 0, 16, 0, - {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/2, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - { - 640, 480, 640, 480, 0, 0, 24, 0, - {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/3, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - - /* - * Dummy Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, - - /* - * User Defined Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } +static struct fb_videomode retz3fb_predefined[] __initdata = { + /* + * NB: it is very important to adjust the pixel-clock to the color-depth. + */ + + { + "640x480", { /* 640x480, 8 bpp */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461, 28, 32, 12, 10, 96, 2, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, + /* + ModeLine "800x600" 36 800 824 896 1024 600 601 603 625 + < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL + */ + { + "800x600", { /* 800x600, 8 bpp */ + 800, 600, 800, 600, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 27778, 64, 24, 22, 1, 120, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, + /* + ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace + < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL + */ + { + "1024x768i", { /* 1024x768, 8 bpp, interlaced */ + 1024, 768, 1024, 768, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 22222, 40, 40, 32, 9, 160, 8, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED + } + }, { + "640x480-16", { /* 640x480, 16 bpp */ + 640, 480, 640, 480, 0, 0, 16, 0, + {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461/2, 28, 32, 12, 10, 96, 2, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "640x480-24", { /* 640x480, 24 bpp */ + 640, 480, 640, 480, 0, 0, 24, 0, + {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461/3, 28, 32, 12, 10, 96, 2, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, }; -#define NUM_TOTAL_MODES arraysize(retz3_fb_predefined) -#define NUM_PREDEF_MODES 5 +#define NUM_TOTAL_MODES arraysize(retz3fb_predefined) +static struct fb_var_screeninfo retz3fb_default; static int z3fb_inverse = 0; -static int z3fb_mode = 0; +static int z3fb_mode __initdata = 0; /* * Interface used by the world */ -void retz3_video_setup(char *options, int *ints); - -static int retz3_fb_open(int fbidx); -static int retz3_fb_release(int fbidx); -static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con); -static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con); -static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int retz3_fb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, int con); +void retz3fb_setup(char *options, int *ints); + +static int retz3fb_open(struct fb_info *info); +static int retz3fb_release(struct fb_info *info); +static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int retz3fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int retz3fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int retz3fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int retz3fb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info); /* * Interface to the low level console driver */ -unsigned long retz3_fb_init(unsigned long mem_start); -static int z3fb_switch(int con); -static int z3fb_updatevar(int con); -static void z3fb_blank(int blank); -static int z3fb_setcmap(struct fb_cmap *cmap, int con); +unsigned long retz3fb_init(unsigned long mem_start); +static int z3fb_switch(int con, struct fb_info *info); +static int z3fb_updatevar(int con, struct fb_info *info); +static void z3fb_blank(int blank, struct fb_info *info); + + +/* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static struct display_switch fbcon_retz3_8; +#endif /* * Accelerated Functions used by the low level console driver */ -void retz3_bitblt(struct fb_var_screeninfo *scr, - unsigned short curx, unsigned short cury, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask); -void retz3_fill(unsigned short x, unsigned short y, unsigned short - width, unsigned short height, unsigned short mode, - unsigned short color); +static void retz3_bitblt(struct fb_var_screeninfo *scr, + unsigned short curx, unsigned short cury, unsigned + short destx, unsigned short desty, unsigned short + width, unsigned short height, unsigned short cmd, + unsigned short mask); /* * Hardware Specific Routines @@ -349,17 +309,17 @@ void retz3_fill(unsigned short x, unsigned short y, unsigned short static int retz3_init(void); static int retz3_encode_fix(struct fb_fix_screeninfo *fix, - struct retz3_fb_par *par); + struct retz3fb_par *par); static int retz3_decode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par); + struct retz3fb_par *par); static int retz3_encode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par); + struct retz3fb_par *par); static int retz3_getcolreg(unsigned int regno, unsigned int *red, unsigned int *green, unsigned int *blue, - unsigned int *transp); + unsigned int *transp, struct fb_info *info); static int retz3_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, - unsigned int transp); + unsigned int transp, struct fb_info *info); static void retz3_blank(int blank); @@ -367,13 +327,11 @@ static void retz3_blank(int blank); * Internal routines */ -static void retz3_fb_get_par(struct retz3_fb_par *par); -static void retz3_fb_set_par(struct retz3_fb_par *par); +static void retz3fb_get_par(struct retz3fb_par *par); +static void retz3fb_set_par(struct retz3fb_par *par); static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); -static void do_install_cmap(int con); -/* -static void retz3_fb_set_disp(int con); -*/ +static void do_install_cmap(int con, struct fb_info *info); +static void retz3fb_set_disp(int con, struct fb_info *info); static int get_video_mode(const char *name); @@ -397,7 +355,7 @@ static unsigned short find_fq(unsigned int freq) else if (freq <= 250000000) n2 = 0; else - return(0); + return 0; do { @@ -424,10 +382,12 @@ static unsigned short find_fq(unsigned int freq) static int retz3_set_video(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) + struct retz3fb_par *par) { +#if 0 float freq_f; - long freq; +#endif + unsigned int freq; int xres, hfront, hsync, hback; int yres, vfront, vsync, vback; @@ -478,7 +438,7 @@ static int retz3_set_video(struct fb_var_screeninfo *var, vback = var->upper_margin; } - data.h_total = (hback / 8) + (xres / 8) + data.h_total = (hback / 8) + (xres / 8) + (hfront / 8) + (hsync / 8) - 1 /* + 1 */; data.h_dispend = ((xres + bpp - 1)/ 8) - 1; data.h_bstart = xres / 8 /* + 1 */; @@ -736,8 +696,13 @@ static int retz3_set_video(struct fb_var_screeninfo *var, /* * Convert from ps to Hz. */ +#if 0 freq_f = (1.0/(float)var->pixclock) * 1000000000; - freq = ((long)freq_f) * 1000; + freq = ((unsigned int)freq_f) * 1000; +#else + freq = 2000000000 / var->pixclock; + freq = freq * 500; +#endif best_freq = find_fq(freq); pll_w(0x02, best_freq); @@ -791,7 +756,7 @@ static int retz3_set_video(struct fb_var_screeninfo *var, */ switch (bpp){ case 8: - reg_w(0x83c6, 0x00); + reg_w(0x83c6, 0x00); break; case 16: reg_w(0x83c6, 0x60); @@ -805,7 +770,7 @@ static int retz3_set_video(struct fb_var_screeninfo *var, reg_w(VDAC_ADDRESS, 0x00); - seq_w(SEQ_MAP_MASK, 0x0f ); + seq_w(SEQ_MAP_MASK, 0x0f ); return 0; } @@ -854,8 +819,8 @@ static int retz3_init(void) } #endif - retz3_setcolreg (255, 56, 100, 160, 0); - retz3_setcolreg (254, 0, 0, 0, 0); + retz3_setcolreg (255, 56, 100, 160, 0, NULL /* unused */); + retz3_setcolreg (254, 0, 0, 0, 0, NULL /* unused */); return 0; } @@ -867,11 +832,11 @@ static int retz3_init(void) */ static int retz3_encode_fix(struct fb_fix_screeninfo *fix, - struct retz3_fb_par *par) + struct retz3fb_par *par) { short i; - strcpy(fix->id, retz3_fb_name); + strcpy(fix->id, retz3fb_name); fix->smem_start = (char *)z3_fbmem; fix->smem_len = z3_size; fix->mmio_start = (unsigned char *)z3_regs; @@ -889,6 +854,8 @@ static int retz3_encode_fix(struct fb_fix_screeninfo *fix, fix->ywrapstep = 0; fix->line_length = 0; + fix->accel = FB_ACCEL_NCR77C32BLT; + for (i = 0; i < arraysize(fix->reserved); i++) fix->reserved[i] = 0; @@ -902,7 +869,7 @@ static int retz3_encode_fix(struct fb_fix_screeninfo *fix, */ static int retz3_decode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) + struct retz3fb_par *par) { par->xres = var->xres; par->yres = var->yres; @@ -934,7 +901,7 @@ static int retz3_decode_var(struct fb_var_screeninfo *var, */ static int retz3_encode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) + struct retz3fb_par *par) { short i; @@ -959,7 +926,7 @@ static int retz3_encode_var(struct fb_var_screeninfo *var, var->height = -1; var->width = -1; - var->accel = FB_ACCEL_RETINAZ3; + var->accel = FB_ACCEL_NCR77C32BLT; var->pixclock = par->pixclock; @@ -987,7 +954,7 @@ static int retz3_encode_var(struct fb_var_screeninfo *var, static int retz3_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, - unsigned int transp) + unsigned int transp, struct fb_info *info) { /* We'll get to this */ @@ -1015,7 +982,7 @@ static int retz3_setcolreg(unsigned int regno, unsigned int red, static int retz3_getcolreg(unsigned int regno, unsigned int *red, unsigned int *green, unsigned int *blue, - unsigned int *transp) + unsigned int *transp, struct fb_info *info) { if (regno > 255) return 1; @@ -1052,11 +1019,11 @@ void retz3_blank(int blank) } -void retz3_bitblt (struct fb_var_screeninfo *var, - unsigned short srcx, unsigned short srcy, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask) +static void retz3_bitblt (struct fb_var_screeninfo *var, + unsigned short srcx, unsigned short srcy, + unsigned short destx, unsigned short desty, + unsigned short width, unsigned short height, + unsigned short cmd, unsigned short mask) { volatile unsigned long *acm = (unsigned long *) (z3_mem + ACM_OFFSET); @@ -1129,7 +1096,7 @@ void retz3_bitblt (struct fb_var_screeninfo *var, *(acm + ACM_CONTROL/4) = tmp; tmp = width | (height << 16); - + *(acm + ACM_BITMAP_DIMENSION/4) = cpu_to_le32(tmp); *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00; @@ -1154,20 +1121,10 @@ void retz3_bitblt (struct fb_var_screeninfo *var, } #if 0 -void retz3_fill (unsigned short x, unsigned short y, unsigned - short width, unsigned short height, - unsigned short mode, unsigned short color) -{ - -} -#endif - - -#if 0 /* * Move cursor to x, y */ -void retz3_MoveCursor (unsigned short x, unsigned short y) +static void retz3_MoveCursor (unsigned short x, unsigned short y) { /* Guess we gotta deal with the cursor at some point */ } @@ -1189,16 +1146,16 @@ static struct fb_hwswitch retz3_switch = { * Fill the hardware's `par' structure. */ -static void retz3_fb_get_par(struct retz3_fb_par *par) +static void retz3fb_get_par(struct retz3fb_par *par) { if (current_par_valid) *par = current_par; else - fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], par); + fbhw->decode_var(&retz3fb_default, par); } -static void retz3_fb_set_par(struct retz3_fb_par *par) +static void retz3fb_set_par(struct retz3fb_par *par) { current_par = *par; current_par_valid = 1; @@ -1208,7 +1165,7 @@ static void retz3_fb_set_par(struct retz3_fb_par *par) static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) { int err, activate; - struct retz3_fb_par par; + struct retz3fb_par par; if ((err = fbhw->decode_var(var, &par))) return err; @@ -1217,7 +1174,7 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) /* XXX ... what to do about isactive ? */ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) - retz3_fb_set_par(&par); + retz3fb_set_par(&par); fbhw->encode_var(var, &par); var->activate = activate; @@ -1227,17 +1184,17 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) } -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - fbhw->setcolreg); + fbhw->setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), &fb_display[con].var, 1, - fbhw->setcolreg); + fbhw->setcolreg, info); } @@ -1245,7 +1202,7 @@ static void do_install_cmap(int con) * Open/Release the frame buffer device */ -static int retz3_fb_open(int fbidx) +static int retz3fb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -1255,7 +1212,7 @@ static int retz3_fb_open(int fbidx) return 0; } -static int retz3_fb_release(int fbidx) +static int retz3fb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return 0; @@ -1266,13 +1223,14 @@ static int retz3_fb_release(int fbidx) * Get the Fixed Part of the Display */ -static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { - struct retz3_fb_par par; + struct retz3fb_par par; int error = 0; if (con == -1) - retz3_fb_get_par(&par); + retz3fb_get_par(&par); else error = fbhw->decode_var(&fb_display[con].var, &par); return(error ? error : fbhw->encode_fix(fix, &par)); @@ -1283,13 +1241,14 @@ static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con) +static int retz3fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { - struct retz3_fb_par par; + struct retz3fb_par par; int error = 0; if (con == -1) { - retz3_fb_get_par(&par); + retz3fb_get_par(&par); error = fbhw->encode_var(var, &par); } else *var = fb_display[con].var; @@ -1298,7 +1257,7 @@ static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con) #if 1 -static void retz3_fb_set_disp(int con) +static void retz3fb_set_disp(int con, struct fb_info *info) { struct fb_fix_screeninfo fix; struct display *display; @@ -1308,7 +1267,7 @@ static void retz3_fb_set_disp(int con) else display = &disp; /* used during initialization */ - retz3_fb_get_fix(&fix, con); + retz3fb_get_fix(&fix, con, info); if (con == -1) con = 0; @@ -1321,6 +1280,21 @@ static void retz3_fb_set_disp(int con) display->ywrapstep = fix.ywrapstep; display->can_soft_blank = 1; display->inverse = z3fb_inverse; + switch (display->var.bits_per_pixel) { +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_retz3_8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif + default: + display->dispsw = NULL; + break; + } } #endif @@ -1328,7 +1302,8 @@ static void retz3_fb_set_disp(int con) * Set the User Defined Part of the Display */ -static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) +static int retz3fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp; struct display *display; @@ -1359,7 +1334,7 @@ static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) oldbpp != var->bits_per_pixel) { struct fb_fix_screeninfo fix; - retz3_fb_get_fix(&fix, con); + retz3fb_get_fix(&fix, con, info); display->screen_base = fix.smem_start; display->visual = fix.visual; @@ -1370,8 +1345,23 @@ static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) display->line_length = fix.line_length; display->can_soft_blank = 1; display->inverse = z3fb_inverse; + switch (display->var.bits_per_pixel) { +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_retz3_8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif + default: + display->dispsw = NULL; + break; + } /* - retz3_fb_set_disp(con); + retz3fb_set_disp(con, info); */ if (fb_info.changevar) (*fb_info.changevar)(con); @@ -1380,7 +1370,7 @@ static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) if (oldbpp != var->bits_per_pixel) { if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) return err; - do_install_cmap(con); + do_install_cmap(con, info); } } return 0; @@ -1391,15 +1381,16 @@ static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ return(fb_get_cmap(cmap, &fb_display[con].var, kspc, - fbhw->getcolreg)); + fbhw->getcolreg, info)); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } @@ -1409,7 +1400,8 @@ static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { int err; @@ -1421,7 +1413,7 @@ static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) } if (con == currcon) /* current console? */ return(fb_set_cmap(cmap, &fb_display[con].var, kspc, - fbhw->setcolreg)); + fbhw->setcolreg, info)); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return 0; @@ -1434,7 +1426,8 @@ static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con) +static int retz3fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { return -EINVAL; } @@ -1444,21 +1437,22 @@ static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con) * RetinaZ3 Frame Buffer Specific ioctls */ -static int retz3_fb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, int con) +static int retz3fb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info) { return -EINVAL; } -static struct fb_ops retz3_fb_ops = { - retz3_fb_open, retz3_fb_release, retz3_fb_get_fix, retz3_fb_get_var, - retz3_fb_set_var, retz3_fb_get_cmap, retz3_fb_set_cmap, - retz3_fb_pan_display, retz3_fb_ioctl +static struct fb_ops retz3fb_ops = { + retz3fb_open, retz3fb_release, retz3fb_get_fix, retz3fb_get_var, + retz3fb_set_var, retz3fb_get_cmap, retz3fb_set_cmap, + retz3fb_pan_display, NULL, retz3fb_ioctl }; -__initfunc(void retz3_video_setup(char *options, int *ints)) +__initfunc(void retz3fb_setup(char *options, int *ints)) { char *this_opt; @@ -1467,7 +1461,7 @@ __initfunc(void retz3_video_setup(char *options, int *ints)) if (!options || !*options) return; - for (this_opt = strtok(options, ","); this_opt; + for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")){ if (!strcmp(this_opt, "inverse")) { z3fb_inverse = 1; @@ -1484,14 +1478,14 @@ __initfunc(void retz3_video_setup(char *options, int *ints)) * Initialization */ -__initfunc(unsigned long retz3_fb_init(unsigned long mem_start)) +__initfunc(unsigned long retz3fb_init(unsigned long mem_start)) { int err; unsigned long board_addr, board_size; unsigned int key; const struct ConfigDev *cd; - struct retz3_fb_par par; + struct retz3fb_par par; if (!(key = zorro_find(ZORRO_PROD_MACROSYSTEMS_RETINA_Z3, 0, 0))) return mem_start; @@ -1511,43 +1505,38 @@ __initfunc(unsigned long retz3_fb_init(unsigned long mem_start)) z3_size = 0x00400000; /* 4 MB */ - memset ((char*)z3_fbmem, 0, z3_size); - fbhw = &retz3_switch; fbhw->init(); - strcpy(fb_info.modename, retz3_fb_name); + strcpy(fb_info.modename, retz3fb_name); fb_info.changevar = NULL; fb_info.node = -1; - fb_info.fbops = &retz3_fb_ops; - fb_info.fbvar_num = NUM_TOTAL_MODES; - fb_info.fbvar = retz3_fb_predefined; + fb_info.fbops = &retz3fb_ops; fb_info.disp = &disp; fb_info.switch_con = &z3fb_switch; fb_info.updatevar = &z3fb_updatevar; fb_info.blank = &z3fb_blank; - fb_info.setcmap = &z3fb_setcmap; err = register_framebuffer(&fb_info); if (err < 0) return mem_start; if (z3fb_mode == -1) - z3fb_mode = 1; + retz3fb_default = retz3fb_predefined[0].var; - fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], &par); - fbhw->encode_var(&retz3_fb_predefined[0], &par); + fbhw->decode_var(&retz3fb_default, &par); + fbhw->encode_var(&retz3fb_default, &par); - do_fb_set_var(&retz3_fb_predefined[0], 0); - retz3_fb_get_var(&disp.var, -1); + do_fb_set_var(&retz3fb_default, 0); + retz3fb_get_var(&disp.var, -1, &fb_info); - retz3_fb_set_disp(-1); + retz3fb_set_disp(-1, &fb_info); - do_install_cmap(0); + do_install_cmap(0, &fb_info); - printk("%s frame buffer device, using %ldK of video memory\n", - fb_info.modename, z3_size>>10); + printk("fb%d: %s frame buffer device, using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, z3_size>>10); /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; @@ -1556,17 +1545,18 @@ __initfunc(unsigned long retz3_fb_init(unsigned long mem_start)) } -static int z3fb_switch(int con) +static int z3fb_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, - &fb_display[currcon].var, 1, fbhw->getcolreg); + &fb_display[currcon].var, 1, fbhw->getcolreg, + info); do_fb_set_var(&fb_display[con].var, 1); currcon = con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -1578,7 +1568,7 @@ static int z3fb_switch(int con) * Since it's called by a kernel driver, no range checking is done. */ -static int z3fb_updatevar(int con) +static int z3fb_updatevar(int con, struct fb_info *info) { return 0; } @@ -1588,33 +1578,23 @@ static int z3fb_updatevar(int con) * Blank the display. */ -static void z3fb_blank(int blank) +static void z3fb_blank(int blank, struct fb_info *info) { fbhw->blank(blank); } /* - * Set the colormap - */ - -static int z3fb_setcmap(struct fb_cmap *cmap, int con) -{ - return(retz3_fb_set_cmap(cmap, 1, con)); -} - - -/* * Get a Video Mode */ -static int get_video_mode(const char *name) +__initfunc(static int get_video_mode(const char *name)) { short i; - for (i = 1; i <= NUM_PREDEF_MODES; i++) - if (!strcmp(name, retz3_fb_modenames[i])){ - retz3_fb_predefined[0] = retz3_fb_predefined[i]; + for (i = 0; i <= NUM_TOTAL_MODES; i++) + if (!strcmp(name, retz3fb_predefined[i].name)){ + retz3fb_default = retz3fb_predefined[i].var; return i; } return -1; @@ -1624,7 +1604,7 @@ static int get_video_mode(const char *name) #ifdef MODULE int init_module(void) { - return(retz3_fb_init(NULL)); + return(retz3fb_init(NULL)); } void cleanup_module(void) @@ -1636,11 +1616,60 @@ void cleanup_module(void) unregister_framebuffer(&fb_info); /* TODO: clean up ... */ } -#endif /* MODULE */ +#endif /* - * Visible symbols for modules + * Text console acceleration */ -EXPORT_SYMBOL(retz3_bitblt); +#ifdef CONFIG_FBCON_CFB8 +static void fbcon_retz3_8_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + int fontwidth = p->fontwidth; + + sx *= fontwidth; + dx *= fontwidth; + width *= fontwidth; + + retz3_bitblt(&p->var, + (unsigned short)sx, + (unsigned short)(sy*p->fontheight), + (unsigned short)dx, + (unsigned short)(dy*p->fontheight), + (unsigned short)width, + (unsigned short)(height*p->fontheight), + Z3BLTcopy, + 0xffff); +} + +static void fbcon_retz3_8_clear(struct vc_data *conp, struct display *p, int + sy, int sx, int height, int width) +{ + unsigned short col; + int fontwidth = p->fontwidth; + + sx *= fontwidth; + width *= fontwidth; + + col = attr_bgcol_ec(p, conp); + col &= 0xff; + col |= (col << 8); + + retz3_bitblt(&p->var, + (unsigned short)sx, + (unsigned short)(sy*p->fontheight), + (unsigned short)sx, + (unsigned short)(sy*p->fontheight), + (unsigned short)width, + (unsigned short)(height*p->fontheight), + Z3BLTset, + col); +} + +static struct display_switch fbcon_retz3_8 = { + fbcon_cfb8_setup, fbcon_retz3_8_bmove, fbcon_retz3_8_clear, + fbcon_cfb8_putc, fbcon_cfb8_putcs, fbcon_cfb8_revc +}; +#endif diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c new file mode 100644 index 000000000..3f795cd63 --- /dev/null +++ b/drivers/video/skeletonfb.c @@ -0,0 +1,388 @@ +/* + * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device + * + * Created 28 Dec 1997 by Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/fb.h> + +#include "fbcon.h" + + + /* + * This is just simple sample code. + * + * No warranty that it actually compiles. + * Even less warranty that it actually works :-) + */ + + +struct xxxfb_info { + /* + * Choose _one_ of the two alternatives: + * + * 1. Use the generic frame buffer operations (fbgen_*). + */ + struct fb_info_gen gen; + /* + * 2. Provide your own frame buffer operations. + */ + struct fb_info info; + + /* Here starts the frame buffer device dependent part */ + /* You can use this to store e.g. the board number if you support */ + /* multiple boards */ +}; + + +struct xxxfb_par { + /* + * The hardware specific data in this structure uniquely defines a video + * mode. + * + * If your hardware supports only one video mode, you can leave it empty. + */ +}; + + + /* + * If your driver supports multiple boards, you should make these arrays, + * or allocate them dynamically (using mem_start for builtin drivers, and + * kmalloc() for loaded modules). + */ + +static struct xxxfb_info fb_info; +static struct xxxfb_par current_par; +static int current_par_valid = 0; +static struct display disp; + +static struct fb_var_screeninfo default_var; + +static int currcon = 0; +static int inverse = 0; + + +/* ------------------- chipset specific functions -------------------------- */ + + +static void xxx_detect(void) +{ + /* + * This function should detect the current video mode settings and store + * it as the default video mode + */ + + /* ... */ + xxx_get_par(&par); + xxx_encode_var(&default_var, &par); +} + +static int xxx_encode_fix(struct fb_fix_screeninfo *fix, struct xxxfb_par *par, + const struct fb_info *fb_info) +{ + /* + * This function should fill in the 'fix' structure based on the values + * in the `par' structure. + */ + + /* ... */ + return 0; +} + +static int xxx_decode_var(struct fb_var_screeninfo *var, struct xxxfb_par *par, + const struct fb_info *fb_info) +{ + /* + * Get the video params out of 'var'. If a value doesn't fit, round it up, + * if it's too big, return -EINVAL. + * + * Suggestion: Round up in the following order: bits_per_pixel, xres, + * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, + * bitfields, horizontal timing, vertical timing. + */ + + /* ... */ + + /* pixclock in picos, htotal in pixels, vtotal in scanlines */ + if (!fbmon_valid_timings(pixclock, htotal, vtotal, info)) + return -EINVAL; + + return 0; +} + +static int xxx_encode_var(struct fb_var_screeninfo *var, struct xxxfb_par *par, + const struct fb_info *fb_info) +{ + /* + * Fill the 'var' structure based on the values in 'par' and maybe other + * values read out of the hardware. + */ + + /* ... */ + return 0; +} + +static void xxx_get_par(struct xxxfb_par *par, const struct fb_info *fb_info) +{ + /* + * Fill the hardware's 'par' structure. + */ + + if (current_par_valid) + *par = current_par; + else { + /* ... */ + } +} + +static void xxx_set_par(struct xxxfb_par *par, const struct fb_info *fb_info) +{ + /* + * Set the hardware according to 'par'. + */ + + current_par = *par; + current_par_valid = 1; + /* ... */ +} + +static int xxx_getcolreg(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, + const struct fb_info *fb_info) +{ + /* + * Read a single color register and split it into colors/transparent. + * Return != 0 for invalid regno. + */ + + /* ... */ + return 0; +} + +static int xxx_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + const struct fb_info *fb_info) +{ + /* + * Set a single color register. The values supplied are already rounded + * down to the hardware's capabilities (according to the entries in the + * `var' structure). Return != 0 for invalid regno. + */ + + if (regno < 16) { + /* + * Make the first 16 colors of the palette available to fbcon + */ + if (is_cfb15) /* RGB 555 */ + fbcon_cfb15_cmap[regno] = be16_to_cpu((red << 10) | (green << 5) | + blue); + if (is_cfb16) /* RGB 565 */ + fbcon_cfb16_cmap[regno] = be16_to_cpu((red << 11) | (green << 5) | + blue); + if (is_cfb24) /* RGB 888 */ + fbcon_cfb24_cmap[regno] = be32_to_cpu((red << 16) | (green << 8) | + blue); + if (is_cfb32) /* RGBA 8888 */ + fbcon_cfb32_cmap[regno] = be32_to_cpu((red << 24) | (green << 16) | + (blue << 8) | transp); + } + /* ... */ + return 0; +} + +static int xxx_pan_display(struct fb_var_screeninfo *var, + struct xxxfb_par *par, + const struct fb_info *fb_info) +{ + /* + * Pan (or wrap, depending on the `vmode' field) the display using the + * `xoffset' and `yoffset' fields of the `var' structure. + * If the values don't fit, return -EINVAL. + */ + + /* ... */ + return 0; +} + +static int xxx_blank(int blank_mode, const struct fb_info *fb_info) +{ + /* + * Blank the screen if blank_mode != 0, else unblank. If blank == NULL + * then the caller blanks by setting the CLUT (Color Look Up Table) to all + * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due + * to e.g. a video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown + */ + + /* ... */ + return 0; +} + +static struct display_switch *xxx_get_dispsw(const void *par, + struct fb_info_gen *info) +{ + /* + * Return a pointer to appropriate low level text console operations for + * the video mode `par' of your video hardware. These can be generic + * software routines, or hardware accelerated routines specifically + * tailored for your hardware. + * If you don't have any appropriate operations, simple fill in the NULL + * pointer, and there will be no text output. + */ +#ifdef CONFIG_FBCON_CFB8 + if (is_cfb8) + return &fbcon_cfb8; +#endif +#ifdef CONFIG_FBCON_CFB16 + if (is_cfb16) + return &fbcon_cfb16; +#endif +#ifdef CONFIG_FBCON_CFB32 + if (is_cfb32) + return &fbcon_cfb32; +#endif + return NULL; +} + + +/* ------------ Interfaces to hardware functions ------------ */ + + +struct fbgen_hwswitch xxx_switch = { + xxx_detect, xxx_encode_fix, xxx_decode_var, xxx_encode_var, xxx_get_par, + xxx_set_par, xxx_getcolreg, xxx_setcolreg, xxx_blank, xxx_dispsw +}; + + + +/* ------------ Hardware Independant Functions ------------ */ + + + /* + * Initialization + */ + +__initfunc(unsigned long xxxfb_init(unsigned long mem_start)) +{ + int err; + struct fb_var_screeninfo var; + + fb_info.fbhw = &xxx_switch; + fbhw->detect(); + strcpy(fb_info.modename, "XXX"); + fb_info.changevar = NULL; + fb_info.node = -1; + fb_info.fbops = &xxxfb_ops; + fb_info.disp = disp; + fb_info.switch_con = &xxxfb_switch; + fb_info.updatevar = &xxxfb_update_var; + fb_info.blank = &xxxfb_blank; + /* This should give a reasonable default video mode */ + fbgen_get_var(&disp.var, -1, &fb_info.gen); + fbgen_do_set_var(var, 1, &fbinfo.gen); + err = register_framebuffer(&fb_info.gen.info); + if (err < 0) + return err; + fbgen_set_disp(-1, &fb_info.gen.info); + fbgen_install_cmap(0, &fb_info.gen); + printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node), + fb_info.modename); + + /* uncomment this if your driver cannot be unloaded */ + /* MOD_INC_USE_COUNT; */ + + return mem_start; +} + + + /* + * Cleanup + */ + +void xxxfb_cleanup(struct fb_info *info) +{ + /* + * If your driver supports multiple boards, you should unregister and + * clean up all instances. + */ + + unregister_framebuffer(&fb_info); + /* ... */ +} + + + /* + * Setup + */ + +__initfunc(void xxxfb_setup(char *options, int *ints)) +{ + /* Parse user speficied options (`video=xxxfb:') */ +} + + +/* ------------------------------------------------------------------------- */ + + + /* + * Frame buffer operations + */ + +static int xxxfb_open(const struct fb_info *info) +{ + /* Nothing, only a usage count for the moment */ + MOD_INC_USE_COUNT; + return 0; +} + +static int xxxfb_release(const struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + + /* + * In most cases the `generic' routines (fbgen_*) should be satisfactory. + * However, you're free to fill in your own replacements. + */ + +static struct fb_ops xxxfb_ops = { + xxxfb_open, xxxfb_release, fbgen_get_fix, fbgen_get_var, fbgen_set_var, + fbgen_get_cmap, fbgen_set_cmap, fbgen_pan_display, NULL, fbgen_ioctl +}; + + +/* ------------------------------------------------------------------------- */ + + + /* + * Modularization + */ + +#ifdef MODULE +int init_module(void) +{ + return xxxfb_init(NULL); +} + +void cleanup_module(void) +{ + xxxfb_cleanup(void); +} +#endif /* MODULE */ diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index ac3746109..7e74f7449 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -17,13 +17,12 @@ * * - How to set a single color register? * - * - We don't have support for CFB32 yet (fbcon-cfb32.c) - * * - Hardware cursor (useful for other graphics boards too) * * KNOWN PROBLEMS/TO DO ==================================================== */ +#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -37,11 +36,13 @@ #include <linux/interrupt.h> #include <linux/fb.h> #include <linux/init.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/selection.h> #include <asm/io.h> +#include "fbcon-cfb8.h" +#include "fbcon-cfb32.h" + /* TGA hardware description (minimal) */ /* @@ -188,10 +189,17 @@ static unsigned int base_addr_presets[4] __initdata = { unsigned char PLLbits[7] __initdata = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 }; const unsigned long bt485_cursor_source[64] __initdata = { +#if 1 + 0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000, + 0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000, + 0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000, + 0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000, +#else 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, +#endif 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @@ -235,9 +243,8 @@ static int currcon = 0; static struct display disp; static struct fb_info fb_info; static struct { u_char red, green, blue, pad; } palette[256]; -static char tgafb_name[16] = "DEC TGA "; -static struct fb_fix_screeninfo fb_fix; +static struct fb_fix_screeninfo fb_fix = { { "DEC TGA ", } }; static struct fb_var_screeninfo fb_var = { 0, }; @@ -245,18 +252,22 @@ static struct fb_var_screeninfo fb_var = { 0, }; * Interface used by the world */ -void tgafb_video_setup(char *options, int *ints); - -static int tgafb_open(int fbidx); -static int tgafb_release(int fbidx); -static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int tgafb_get_var(struct fb_var_screeninfo *var, int con); -static int tgafb_set_var(struct fb_var_screeninfo *var, int con); -static int tgafb_pan_display(struct fb_var_screeninfo *var, int con); -static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con); +static int tgafb_open(struct fb_info *info); +static int tgafb_release(struct fb_info *info); +static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int tgafb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int tgafb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int tgafb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); + u_long arg, int con, struct fb_info *info); /* @@ -264,10 +275,9 @@ static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd, */ unsigned long tgafb_init(unsigned long mem_start); -static int tgafbcon_switch(int con); -static int tgafbcon_updatevar(int con); -static void tgafbcon_blank(int blank); -static int tgafbcon_setcmap(struct fb_cmap *cmap, int con); +static int tgafbcon_switch(int con, struct fb_info *info); +static int tgafbcon_updatevar(int con, struct fb_info *info); +static void tgafbcon_blank(int blank, struct fb_info *info); /* @@ -275,18 +285,18 @@ static int tgafbcon_setcmap(struct fb_cmap *cmap, int con); */ static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); + u_int transp, struct fb_info *info); #if 1 static void tga_update_palette(void); #endif -static void do_install_cmap(int con); +static void do_install_cmap(int con, struct fb_info *info); static struct fb_ops tgafb_ops = { tgafb_open, tgafb_release, tgafb_get_fix, tgafb_get_var, tgafb_set_var, - tgafb_get_cmap, tgafb_set_cmap, tgafb_pan_display, tgafb_ioctl + tgafb_get_cmap, tgafb_set_cmap, tgafb_pan_display, NULL, tgafb_ioctl }; @@ -294,7 +304,7 @@ static struct fb_ops tgafb_ops = { * Open/Release the frame buffer device */ -static int tgafb_open(int fbidx) +static int tgafb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -304,7 +314,7 @@ static int tgafb_open(int fbidx) return(0); } -static int tgafb_release(int fbidx) +static int tgafb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); @@ -315,7 +325,8 @@ static int tgafb_release(int fbidx) * Get the Fixed Part of the Display */ -static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { memcpy(fix, &fb_fix, sizeof(fb_fix)); return 0; @@ -326,7 +337,8 @@ static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int tgafb_get_var(struct fb_var_screeninfo *var, int con) +static int tgafb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { memcpy(var, &fb_var, sizeof(fb_var)); return 0; @@ -337,7 +349,8 @@ static int tgafb_get_var(struct fb_var_screeninfo *var, int con) * Set the User Defined Part of the Display */ -static int tgafb_set_var(struct fb_var_screeninfo *var, int con) +static int tgafb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { struct display *display; int oldbpp = -1, err; @@ -363,7 +376,7 @@ static int tgafb_set_var(struct fb_var_screeninfo *var, int con) if (oldbpp != var->bits_per_pixel) { if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) return err; - do_install_cmap(con); + do_install_cmap(con, info); } return 0; } @@ -375,7 +388,8 @@ static int tgafb_set_var(struct fb_var_screeninfo *var, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int tgafb_pan_display(struct fb_var_screeninfo *var, int con) +static int tgafb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { if (var->xoffset || var->yoffset) return -EINVAL; @@ -387,14 +401,16 @@ static int tgafb_pan_display(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ - return fb_get_cmap(cmap, &fb_display[con].var, kspc, tgafb_getcolreg); + return fb_get_cmap(cmap, &fb_display[con].var, kspc, tgafb_getcolreg, + info); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } @@ -403,7 +419,8 @@ static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { int err; @@ -413,7 +430,8 @@ static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con) return err; } if (con == currcon) { /* current console? */ - err = fb_set_cmap(cmap, &fb_display[con].var, kspc, tgafb_setcolreg); + err = fb_set_cmap(cmap, &fb_display[con].var, kspc, tgafb_setcolreg, + info); #if 1 tga_update_palette(); #endif @@ -425,7 +443,7 @@ static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con) static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con) + u_long arg, int con, struct fb_info *info) { return -EINVAL; } @@ -437,22 +455,14 @@ static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd, __initfunc(unsigned long tgafb_init(unsigned long mem_start)) { - unsigned char pci_bus, pci_devfn; - int status; int i, j, temp, err; unsigned char *cbp; + struct pci_dev *pdev; - status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, - 0, &pci_bus, &pci_devfn); - if (status == PCIBIOS_DEVICE_NOT_FOUND) + pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL); + if (!pdev) return mem_start; - - /* - * read BASE_REG_0 for memory address - */ - pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0, - &tga_mem_base); - tga_mem_base &= ~15; + tga_mem_base = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; #ifdef DEBUG printk("tgafb_init: mem_base 0x%x\n", tga_mem_base); #endif /* DEBUG */ @@ -460,19 +470,18 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) tga_type = (readl((unsigned long)tga_mem_base) >> 12) & 0x0f; switch (tga_type) { case 0: - strcat(tgafb_name, "8plane"); + strcat(fb_fix.id, "8plane"); break; case 1: - strcat(tgafb_name, "24plane"); + strcat(fb_fix.id, "24plane"); break; case 3: - strcat(tgafb_name, "24plusZ"); + strcat(fb_fix.id, "24plusZ"); break; default: printk("TGA type (0x%x) unrecognized!\n", tga_type); return mem_start; } - strcpy(fb_fix.id, tgafb_name); tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET); tga_fb_base = ((unsigned long)tga_mem_base + fb_offset_presets[tga_type]); @@ -537,6 +546,9 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) TGA_WRITE_REG(default_red[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); TGA_WRITE_REG(default_grn[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); TGA_WRITE_REG(default_blu[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); + palette[i].red=default_red[j]; + palette[i].green=default_grn[j]; + palette[i].blue=default_blu[j]; } for (i = 0; i < 240*3; i += 4) { TGA_WRITE_REG(0x55|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); @@ -548,9 +560,9 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) /* initialize RAMDAC cursor colors */ BT485_WRITE(0, BT485_ADDR_CUR_WRITE); - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ + BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */ + BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */ + BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */ @@ -677,7 +689,7 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) fb_var.xres = fb_var.xres_virtual = 640; fb_var.yres = fb_var.yres_virtual = 480; fb_fix.line_length = 80*fb_var.bits_per_pixel; - fb_fix.smem_start = (char *)tga_fb_base; + fb_fix.smem_start = (char *)(tga_fb_base + LCA_DENSE_MEM); fb_fix.smem_len = fb_fix.line_length*fb_var.yres; fb_fix.type = FB_TYPE_PACKED_PIXELS; fb_fix.type_aux = 0; @@ -686,7 +698,16 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) fb_var.xoffset = fb_var.yoffset = 0; fb_var.grayscale = 0; - fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0; + if (tga_type == 0) { /* 8-plane */ + fb_var.red.offset = 0; + fb_var.green.offset = 0; + fb_var.blue.offset = 0; + } else { /* 24-plane or 24plusZ */ + /* XXX: is this correct?? */ + fb_var.red.offset = 16; + fb_var.green.offset = 8; + fb_var.blue.offset = 0; + } fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8; fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0; fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0; @@ -717,41 +738,54 @@ __initfunc(unsigned long tgafb_init(unsigned long mem_start)) disp.line_length = fb_fix.line_length; disp.can_soft_blank = 1; disp.inverse = 0; + switch (tga_type) { +#ifdef CONFIG_FBCON_CFB8 + case 0: /* 8-plane */ + disp.dispsw = &fbcon_cfb8; + break; +#endif +#ifdef CONFIG_FBCON_CFB32 + case 1: /* 24-plane */ + case 3: /* 24plusZ */ + disp.dispsw = &fbcon_cfb32; + break; +#endif + default: + disp.dispsw = NULL; + } - strcpy(fb_info.modename, tgafb_name); + strcpy(fb_info.modename, fb_fix.id); fb_info.node = -1; fb_info.fbops = &tgafb_ops; - fb_info.fbvar_num = 1; - fb_info.fbvar = &fb_var; fb_info.disp = &disp; fb_info.fontname[0] = '\0'; fb_info.changevar = NULL; fb_info.switch_con = &tgafbcon_switch; fb_info.updatevar = &tgafbcon_updatevar; fb_info.blank = &tgafbcon_blank; - fb_info.setcmap = &tgafbcon_setcmap; err = register_framebuffer(&fb_info); if (err < 0) return mem_start; - tgafb_set_var(&fb_var, -1); + tgafb_set_var(&fb_var, -1, &fb_info); - printk("%s frame buffer device\n", tgafb_name); + printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node), + fb_fix.id); return mem_start; } -static int tgafbcon_switch(int con) +static int tgafbcon_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, - tgafb_getcolreg); + tgafb_getcolreg, info); currcon = con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -759,7 +793,7 @@ static int tgafbcon_switch(int con) * Update the `var' structure (called by fbcon.c) */ -static int tgafbcon_updatevar(int con) +static int tgafbcon_updatevar(int con, struct fb_info *info) { /* Nothing */ return 0; @@ -769,28 +803,18 @@ static int tgafbcon_updatevar(int con) * Blank the display. */ -static void tgafbcon_blank(int blank) +static void tgafbcon_blank(int blank, struct fb_info *info) { /* Nothing */ } /* - * Set the colormap - */ - -static int tgafbcon_setcmap(struct fb_cmap *cmap, int con) -{ - return(tgafb_set_cmap(cmap, 1, con)); -} - - - /* * Read a single color register and split it into * colors/transparent. Return != 0 for invalid regno. */ static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) + u_int *transp, struct fb_info *info) { if (regno > 255) return 1; @@ -808,7 +832,7 @@ static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, */ static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) + u_int transp, struct fb_info *info) { if (regno > 255) return 1; @@ -816,6 +840,11 @@ static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, palette[regno].green = green; palette[regno].blue = blue; +#ifdef CONFIG_FBCON_CFB32 + if (regno < 16 && tga_type != 0) + fbcon_cfb32_cmap[regno] = (red << 16) | (green << 8) | blue; +#endif /* CONFIG_FBCON_CFB32 */ + /* How to set a single color register?? */ return 0; @@ -853,22 +882,22 @@ static void tga_update_palette(void) } #endif -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - tgafb_setcolreg); + tgafb_setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), - &fb_display[con].var, 1, tgafb_setcolreg); + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, tgafb_setcolreg, + info); #if 1 tga_update_palette(); #endif } - #if 0 /* No cursor stuff yet */ /* diff --git a/drivers/video/txtcon.c b/drivers/video/txtcon.c index cdecc08b0..83165b49b 100644 --- a/drivers/video/txtcon.c +++ b/drivers/video/txtcon.c @@ -14,48 +14,55 @@ */ +#include <linux/errno.h> #include <linux/types.h> +#include <linux/kdev_t.h> #include <linux/console.h> - /* - * Interface used by the world - */ + /* + * Interface used by the world + */ -static int txtcon_startup(u_long *kmem_start, const char **display_desc); +static unsigned long txtcon_startup(unsigned long kmem_start, + const char **display_desc); static void txtcon_init(struct vc_data *conp); -static int txtcon_deinit(struct vc_data *conp); -static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width); -static int txtcon_putc(struct vc_data *conp, int c, int y, int x); -static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, - int x); -static int txtcon_cursor(struct vc_data *conp, int mode); -static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count); -static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width); +static void txtcon_deinit(struct vc_data *conp); +static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height, + int width); +static void txtcon_putc(struct vc_data *conp, int c, int y, int x); +static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, + int x); +static void txtcon_cursor(struct vc_data *conp, int mode); +static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir, + int count); +static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, + int height, int width); static int txtcon_switch(struct vc_data *conp); static int txtcon_blank(int blank); static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data); static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data); static int txtcon_set_palette(struct vc_data *conp, unsigned char *table); static int txtcon_scrolldelta(int lines); +static int txtcon_set_mode(struct vc_data *conp, int mode); -static int txtcon_startup(u_long *kmem_start, const char **display_desc) +static unsigned long txtcon_startup(unsigned long kmem_start, + const char **display_desc) { - return -ENODEV; + return kmem_start; } static void txtcon_init(struct vc_data *conp) { + /* ... */ } -static int txtcon_deinit(struct vc_data *conp) +static void txtcon_deinit(struct vc_data *conp) { - return 0; + /* ... */ } @@ -64,90 +71,108 @@ static int txtcon_deinit(struct vc_data *conp) /* txtcon_XXX routines - interface used by the world */ -static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) +static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height, + int width) { - return -ENOSYS; + /* ... */ } -static int txtcon_putc(struct vc_data *conp, int c, int y, int x) +static void txtcon_putc(struct vc_data *conp, int c, int y, int x) { - return -ENOSYS; + /* ... */ } -static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, - int x) +static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, + int x) { - return -ENOSYS; + /* ... */ } -static int txtcon_cursor(struct vc_data *conp, int mode) +static void txtcon_cursor(struct vc_data *conp, int mode) { - return -ENOSYS; + /* ... */ } -static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) +static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir, + int count) { - return -ENOSYS; + /* ... */ } -static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) +static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, + int height, int width) { - return -ENOSYS; + /* ... */ } static int txtcon_switch(struct vc_data *conp) { - return -ENOSYS; + return -ENOSYS; } static int txtcon_blank(int blank) { - return -ENOSYS; + return -ENOSYS; } static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data) { - return -ENOSYS; + return -ENOSYS; } static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data) { - return -ENOSYS; + return -ENOSYS; } static int txtcon_set_palette(struct vc_data *conp, unsigned char *table) { - return -ENOSYS; + return -ENOSYS; } static int txtcon_scrolldelta(int lines) { - return -ENOSYS; + return -ENOSYS; +} + +static int txtcon_set_mode(struct vc_data *conp, int mode) +{ + return -ENOSYS; } /* ====================================================================== */ - /* - * The console `switch' structure for the text mode based console - */ + /* + * The console `switch' structure for the text mode based console + */ struct consw txt_con = { - txtcon_startup, txtcon_init, txtcon_deinit, txtcon_clear, txtcon_putc, - txtcon_putcs, txtcon_cursor, txtcon_scroll, txtcon_bmove, txtcon_switch, - txtcon_blank, txtcon_get_font, txtcon_set_font, txtcon_set_palette, - txtcon_scrolldelta + txtcon_startup, + txtcon_init, + txtcon_deinit, + txtcon_clear, + txtcon_putc, + txtcon_putcs, + txtcon_cursor, + txtcon_scroll, + txtcon_bmove, + txtcon_switch, + txtcon_blank, + txtcon_get_font, + txtcon_set_font, + txtcon_set_palette, + txtcon_scrolldelta, + txtcon_set_mode }; diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index 63e7f7ca2..eea430ffa 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -8,6 +8,7 @@ * more details. */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -22,6 +23,14 @@ #include <linux/fb.h> #include <linux/init.h> +#include "fbcon-mfb.h" +#include "fbcon-cfb2.h" +#include "fbcon-cfb4.h" +#include "fbcon-cfb8.h" +#include "fbcon-cfb16.h" +#include "fbcon-cfb24.h" +#include "fbcon-cfb32.h" + #define arraysize(x) (sizeof(x)/sizeof(*(x))) @@ -41,61 +50,51 @@ static int currcon = 0; static struct display disp; static struct fb_info fb_info; static struct { u_char red, green, blue, pad; } palette[256]; -static char virtual_fb_name[16] = "Virtual FB"; - -static struct fb_var_screeninfo virtual_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { - /* 640x480, 8 bpp */ - 640, 480, 640, 480, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2, - 0, FB_VMODE_NONINTERLACED - }, - - /* - * User Defined Video Modes (8) - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } +static char vfb_name[16] = "Virtual FB"; + +static struct fb_var_screeninfo vfb_default = { + /* 640x480, 8 bpp */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2, + 0, FB_VMODE_NONINTERLACED }; -#define NUM_USER_MODES (8) -#define NUM_TOTAL_MODES arraysize(virtual_fb_predefined) -#define NUM_PREDEF_MODES (1) +static int vfb_enable = 0; /* disabled by default */ /* * Interface used by the world */ -void vfb_video_setup(char *options, int *ints); - -static int virtual_fb_open(int fbidx); -static int virtual_fb_release(int fbidx); -static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con); -static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con); -static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); +void vfb_setup(char *options, int *ints); + +static int vfb_open(struct fb_info *info); +static int vfb_release(struct fb_info *info); +static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int vfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int vfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int vfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int vfb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); /* * Interface to the low level console driver */ -unsigned long virtual_fb_init(unsigned long mem_start); -static int vfbcon_switch(int con); -static int vfbcon_updatevar(int con); -static void vfbcon_blank(int blank); -static int vfbcon_setcmap(struct fb_cmap *cmap, int con); +unsigned long vfb_init(unsigned long mem_start); +static int vfbcon_switch(int con, struct fb_info *info); +static int vfbcon_updatevar(int con, struct fb_info *info); +static void vfbcon_blank(int blank, struct fb_info *info); /* @@ -107,16 +106,15 @@ static void vfb_encode_fix(struct fb_fix_screeninfo *fix, struct fb_var_screeninfo *var); static void set_color_bitfields(struct fb_var_screeninfo *var); static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); + u_int *transp, struct fb_info *info); static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); -static void do_install_cmap(int con); + u_int transp, struct fb_info *info); +static void do_install_cmap(int con, struct fb_info *info); -static struct fb_ops virtual_fb_ops = { - virtual_fb_open, virtual_fb_release, virtual_fb_get_fix, - virtual_fb_get_var, virtual_fb_set_var, virtual_fb_get_cmap, - virtual_fb_set_cmap, virtual_fb_pan_display, virtual_fb_ioctl +static struct fb_ops vfb_ops = { + vfb_open, vfb_release, vfb_get_fix, vfb_get_var, vfb_set_var, vfb_get_cmap, + vfb_set_cmap, vfb_pan_display, NULL, vfb_ioctl }; @@ -124,7 +122,7 @@ static struct fb_ops virtual_fb_ops = { * Open/Release the frame buffer device */ -static int virtual_fb_open(int fbidx) +static int vfb_open(struct fb_info *info) { /* * Nothing, only a usage count for the moment @@ -134,7 +132,7 @@ static int virtual_fb_open(int fbidx) return(0); } -static int virtual_fb_release(int fbidx) +static int vfb_release(struct fb_info *info) { MOD_DEC_USE_COUNT; return(0); @@ -145,12 +143,13 @@ static int virtual_fb_release(int fbidx) * Get the Fixed Part of the Display */ -static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con) +static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) { struct fb_var_screeninfo *var; if (con == -1) - var = &virtual_fb_predefined[0]; + var = &vfb_default; else var = &fb_display[con].var; vfb_encode_fix(fix, var); @@ -162,10 +161,11 @@ static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con) * Get the User Defined Part of the Display */ -static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con) +static int vfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { if (con == -1) - *var = virtual_fb_predefined[0]; + *var = vfb_default; else *var = fb_display[con].var; set_color_bitfields(var); @@ -177,7 +177,8 @@ static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con) * Set the User Defined Part of the Display */ -static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con) +static int vfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { int err, activate = var->activate; int oldxres, oldyres, oldvxres, oldvyres, oldbpp; @@ -258,13 +259,53 @@ static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con) display->line_length = fix.line_length; display->can_soft_blank = 1; display->inverse = 0; + switch (var->bits_per_pixel) { +#ifdef CONFIG_FBCON_MFB + case 1: + display->dispsw = &fbcon_mfb; + break; +#endif +#ifdef CONFIG_FBCON_CFB2 + case 2: + display->dispsw = &fbcon_cfb2; + break; +#endif +#ifdef CONFIG_FBCON_CFB4 + case 4: + display->dispsw = &fbcon_cfb4; + break; +#endif +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_cfb8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif +#ifdef CONFIG_FBCON_CFB24 + case 24: + display->dispsw = &fbcon_cfb24; + break; +#endif +#ifdef CONFIG_FBCON_CFB32 + case 32: + display->dispsw = &fbcon_cfb32; + break; +#endif + default: + display->dispsw = NULL; + break; + } if (fb_info.changevar) (*fb_info.changevar)(con); } if (oldbpp != var->bits_per_pixel) { if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) return err; - do_install_cmap(con); + do_install_cmap(con, info); } } return 0; @@ -277,7 +318,8 @@ static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con) * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con) +static int vfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) { if (var->vmode & FB_VMODE_YWRAP) { if (var->yoffset < 0 || @@ -304,14 +346,16 @@ static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con) * Get the Colormap */ -static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) +static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { if (con == currcon) /* current console? */ - return fb_get_cmap(cmap, &fb_display[con].var, kspc, vfb_getcolreg); + return fb_get_cmap(cmap, &fb_display[con].var, kspc, vfb_getcolreg, + info); else if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); else - fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); return 0; } @@ -320,7 +364,8 @@ static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) * Set the Colormap */ -static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) +static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) { int err; @@ -330,7 +375,8 @@ static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) return err; } if (con == currcon) /* current console? */ - return fb_set_cmap(cmap, &fb_display[con].var, kspc, vfb_setcolreg); + return fb_set_cmap(cmap, &fb_display[con].var, kspc, vfb_setcolreg, + info); else fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); return 0; @@ -341,19 +387,21 @@ static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) * Virtual Frame Buffer Specific ioctls */ -static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con) +static int vfb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info) { return -EINVAL; } -__initfunc(void vfb_video_setup(char *options, int *ints)) +__initfunc(void vfb_setup(char *options, int *ints)) { char *this_opt; fb_info.fontname[0] = '\0'; + vfb_enable = 1; + if (!options || !*options) return; @@ -369,10 +417,13 @@ __initfunc(void vfb_video_setup(char *options, int *ints)) * Initialisation */ -__initfunc(unsigned long virtual_fb_init(unsigned long mem_start)) +__initfunc(unsigned long vfb_init(unsigned long mem_start)) { int err; + if (!vfb_enable) + return mem_start; + if (mem_start) { videomemory = mem_start; mem_start += videomemorysize; @@ -382,40 +433,37 @@ __initfunc(unsigned long virtual_fb_init(unsigned long mem_start)) if (!videomemory) return mem_start; - strcpy(fb_info.modename, virtual_fb_name); + strcpy(fb_info.modename, vfb_name); fb_info.changevar = NULL; fb_info.node = -1; - fb_info.fbops = &virtual_fb_ops; - fb_info.fbvar_num = NUM_TOTAL_MODES; - fb_info.fbvar = virtual_fb_predefined; + fb_info.fbops = &vfb_ops; fb_info.disp = &disp; fb_info.switch_con = &vfbcon_switch; fb_info.updatevar = &vfbcon_updatevar; fb_info.blank = &vfbcon_blank; - fb_info.setcmap = &vfbcon_setcmap; err = register_framebuffer(&fb_info); if (err < 0) return mem_start; - virtual_fb_set_var(&virtual_fb_predefined[0], -1); + vfb_set_var(&vfb_default, -1, &fb_info); - printk("Virtual frame buffer device, using %ldK of video memory\n", - videomemorysize>>10); + printk("fb%d: Virtual frame buffer device, using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), videomemorysize>>10); return mem_start; } -static int vfbcon_switch(int con) +static int vfbcon_switch(int con, struct fb_info *info) { /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, - vfb_getcolreg); + vfb_getcolreg, info); currcon = con; /* Install new colormap */ - do_install_cmap(con); + do_install_cmap(con, info); return 0; } @@ -423,7 +471,7 @@ static int vfbcon_switch(int con) * Update the `var' structure (called by fbcon.c) */ -static int vfbcon_updatevar(int con) +static int vfbcon_updatevar(int con, struct fb_info *info) { /* Nothing */ return 0; @@ -433,21 +481,11 @@ static int vfbcon_updatevar(int con) * Blank the display. */ -static void vfbcon_blank(int blank) +static void vfbcon_blank(int blank, struct fb_info *info) { /* Nothing */ } - /* - * Set the colormap - */ - -static int vfbcon_setcmap(struct fb_cmap *cmap, int con) -{ - return(virtual_fb_set_cmap(cmap, 1, con)); -} - - static u_long get_line_length(int xres_virtual, int bpp) { u_long length; @@ -462,7 +500,7 @@ static void vfb_encode_fix(struct fb_fix_screeninfo *fix, struct fb_var_screeninfo *var) { memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, virtual_fb_name); + strcpy(fix->id, vfb_name); fix->smem_start = (caddr_t)videomemory; fix->smem_len = videomemorysize; fix->type = FB_TYPE_PACKED_PIXELS; @@ -471,6 +509,8 @@ static void vfb_encode_fix(struct fb_fix_screeninfo *fix, case 1: fix->visual = FB_VISUAL_MONO01; break; + case 2: + case 4: case 8: fix->visual = FB_VISUAL_PSEUDOCOLOR; break; @@ -544,7 +584,7 @@ static void set_color_bitfields(struct fb_var_screeninfo *var) */ static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) + u_int *transp, struct fb_info *info) { if (regno > 255) return 1; @@ -562,7 +602,7 @@ static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, */ static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) + u_int transp, struct fb_info *info) { if (regno > 255) return 1; @@ -573,23 +613,23 @@ static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, } -static void do_install_cmap(int con) +static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) return; if (fb_display[con].cmap.len) fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, - vfb_setcolreg); + vfb_setcolreg, info); else - fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), - &fb_display[con].var, 1, vfb_setcolreg); + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, vfb_setcolreg, info); } #ifdef MODULE int init_module(void) { - return(virtual_fb_init(NULL)); + return(vfb_init(NULL)); } void cleanup_module(void) diff --git a/drivers/video/vgacon.c b/drivers/video/vgacon.c index 83a908a09..623ea799d 100644 --- a/drivers/video/vgacon.c +++ b/drivers/video/vgacon.c @@ -87,16 +87,16 @@ static unsigned long vgacon_startup(unsigned long kmem_start, const char **display_desc); static void vgacon_init(struct vc_data *conp); -static int vgacon_deinit(struct vc_data *conp); -static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height, - int width); -static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos); -static int vgacon_putcs(struct vc_data *conp, const char *s, int count, - int ypos, int xpos); -static int vgacon_cursor(struct vc_data *conp, int mode); -static int vgacon_scroll(struct vc_data *conp, int t, int b, - int dir, int count); -static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, +static void vgacon_deinit(struct vc_data *conp); +static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height, + int width); +static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos); +static void vgacon_putcs(struct vc_data *conp, const char *s, int count, + int ypos, int xpos); +static void vgacon_cursor(struct vc_data *conp, int mode); +static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir, + int count); +static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, int height, int width); static int vgacon_switch(struct vc_data *conp); static int vgacon_blank(int blank); @@ -104,6 +104,7 @@ static int vgacon_get_font(struct vc_data *conp, int *w, int *h, char *data); static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data); static int vgacon_set_palette(struct vc_data *conp, unsigned char *table); static int vgacon_scrolldelta(int lines); +static int vgacon_set_mode(struct vc_data *conp, int mode); /* @@ -215,25 +216,6 @@ __initfunc(static unsigned long vgacon_startup(unsigned long kmem_start, unsigned short saved; unsigned short *p; - /* - * Find out if there is a graphics card present. - * Are there smarter methods around? - */ - p = (unsigned short *)(((ORIG_VIDEO_MODE == 7) ? 0xb0000 : 0xb8000) + - + VGA_OFFSET); - saved = vga_readw(p); - vga_writew(0xAA55, p); - if (vga_readw(p) != 0xAA55) { - vga_writew(saved, p); - return kmem_start; - } - vga_writew(0x55AA, p); - if (vga_readw(p) != 0x55AA) { - vga_writew(saved, p); - return kmem_start; - } - vga_writew(saved, p); - vga_video_num_lines = ORIG_VIDEO_LINES; vga_video_num_columns = ORIG_VIDEO_COLS; vga_video_size_row = 2 * ORIG_VIDEO_COLS; @@ -327,6 +309,24 @@ __initfunc(static unsigned long vgacon_startup(unsigned long kmem_start, } } + /* + * Find out if there is a graphics card present. + * Are there smarter methods around? + */ + p = (unsigned short *)vga_video_mem_base; + saved = vga_readw(p); + vga_writew(0xAA55, p); + if (vga_readw(p) != 0xAA55) { + vga_writew(saved, p); + return kmem_start; + } + vga_writew(0x55AA, p); + if (vga_readw(p) != 0x55AA) { + vga_writew(saved, p); + return kmem_start; + } + vga_writew(saved, p); + vga_hardscroll_enabled = (vga_hardscroll_disabled_by_init ? 0 : (vga_video_type == VIDEO_TYPE_EGAC || vga_video_type == VIDEO_TYPE_VGAC @@ -358,22 +358,21 @@ static void vgacon_init(struct vc_data *conp) conp->vc_can_do_color = vga_can_do_color; } -static int vgacon_deinit(struct vc_data *conp) +static void vgacon_deinit(struct vc_data *conp) { - return 0; } /* ====================================================================== */ -static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) +static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height, + int width) { int rows; unsigned long dest; if (console_blanked) - return 0; + return; dest = vga_video_mem_base + sy*vga_video_size_row + sx*2; if (sx == 0 && width == vga_video_num_columns) @@ -381,41 +380,38 @@ static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height, else for (rows = height; rows-- ; dest += vga_video_size_row) vga_memsetw((void *)dest, conp->vc_video_erase_char, width); - return 0; } -static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos) +static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos) { - u_short *p; + u16 *p; if (console_blanked) - return 0; + return; - p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2); + p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2); vga_writew(conp->vc_attr << 8 | c, p); - return 0; } -static int vgacon_putcs(struct vc_data *conp, const char *s, int count, - int ypos, int xpos) +static void vgacon_putcs(struct vc_data *conp, const char *s, int count, + int ypos, int xpos) { - u_short *p; - u_short sattr; + u16 *p; + u16 sattr; if (console_blanked) - return 0; + return; - p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2); + p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2); sattr = conp->vc_attr << 8; while (count--) vga_writew(sattr | *s++, p++); - return 0; } -static int vgacon_cursor(struct vc_data *conp, int mode) +static void vgacon_cursor(struct vc_data *conp, int mode) { switch (mode) { case CM_ERASE: @@ -427,14 +423,14 @@ static int vgacon_cursor(struct vc_data *conp, int mode) write_vga(14, conp->vc_y*vga_video_num_columns+conp->vc_x); break; } - return 0; } -static int vgacon_scroll(struct vc_data *conp, int t, int b, int dir, int count) +static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir, + int count) { if (console_blanked) - return 0; + return; vgacon_cursor(conp, CM_ERASE); @@ -469,19 +465,17 @@ static int vgacon_scroll(struct vc_data *conp, int t, int b, int dir, int count) vgacon_clear(conp, 0, t, conp->vc_rows, count); break; } - - return 0; } -static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) +static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, + int height, int width) { unsigned long src, dst; int rows; if (console_blanked) - return 0; + return; if (sx == 0 && dx == 0 && width == vga_video_num_columns) { src = vga_video_mem_base + sy * vga_video_size_row; @@ -505,7 +499,6 @@ static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, dst -= vga_video_size_row; } } - return 0; } @@ -522,7 +515,7 @@ static int vgacon_blank(int blank) return 0; } else { /* Tell console.c that it has to restore the screen itself */ - return(1); + return 1; } return 0; } @@ -564,6 +557,11 @@ static int vgacon_scrolldelta(int lines) return -ENOSYS; } +static int vgacon_set_mode(struct vc_data *conp, int mode) +{ + return -ENOSYS; +} + __initfunc(static int vgacon_show_logo( void )) { @@ -587,5 +585,5 @@ struct consw vga_con = { vgacon_startup, vgacon_init, vgacon_deinit, vgacon_clear, vgacon_putc, vgacon_putcs, vgacon_cursor, vgacon_scroll, vgacon_bmove, vgacon_switch, vgacon_blank, vgacon_get_font, vgacon_set_font, vgacon_set_palette, - vgacon_scrolldelta + vgacon_scrolldelta, vgacon_set_mode }; diff --git a/drivers/video/virgefb.c b/drivers/video/virgefb.c new file mode 100644 index 000000000..d08e4a722 --- /dev/null +++ b/drivers/video/virgefb.c @@ -0,0 +1,1190 @@ +/* + * linux/drivers/video/virgefb.c -- CyberVision64/3D frame buffer device + * + * Copyright (C) 1997 André Heynatz + * + * + * This file is based on the CyberVision frame buffer device (cyberfb.c): + * + * Copyright (C) 1996 Martin Apel + * Geert Uytterhoeven + * + * 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. + */ + +#undef VIRGEFBDEBUG + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/zorro.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/pgtable.h> +#include <asm/amigahw.h> + +#include "s3blit.h" +#include "fbcon.h" +#include "fbcon-cfb8.h" +#include "fbcon-cfb16.h" + + +#ifdef VIRGEFBDEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + +#if 1 +#define vgawb_3d(reg,dat) \ + (*((unsigned char *)(CyberVGARegs + (reg ^ 3))) = dat) +#define vgaww_3d(reg,dat) \ + (*((unsigned word *)(CyberVGARegs + (reg ^ 2))) = swab16(dat)) +#define vgawl_3d(reg,dat) \ + (*((unsigned long *)(CyberVGARegs + reg)) = swab32(dat)) +#else + /* + * Dunno why this doesn't work at the moment - we'll have to look at + * it later. + */ +#define vgawb_3d(reg,dat) \ + (*((unsigned char *)(CyberRegs + 0x8000 + reg)) = dat) +#define vgaww_3d(reg,dat) \ + (*((unsigned word *)(CyberRegs + 0x8000 + reg)) = dat) +#define vgawl_3d(reg,dat) \ + (*((unsigned long *)(CyberRegs + 0x8000 + reg)) = dat) +#endif + + /* + * We asume P5 mapped the big-endian version of these registers. + */ +#define wb_3d(reg,dat) \ + (*((unsigned char volatile *)(CyberRegs + reg)) = dat) +#define ww_3d(reg,dat) \ + (*((unsigned word volatile *)(CyberRegs + reg)) = dat) +#define wl_3d(reg,dat) \ + (*((unsigned long volatile *)(CyberRegs + reg)) = dat) + +#define rl_3d(reg) \ + (*((unsigned long volatile *)(CyberRegs + reg))) + + + + + + +struct virgefb_par { + int xres; + int yres; + int bpp; +}; + +static struct virgefb_par current_par; + +static int current_par_valid = 0; +static int currcon = 0; + +static struct display disp; +static struct fb_info fb_info; + + +/* + * Switch for Chipset Independency + */ + +static struct fb_hwswitch { + + /* Initialisation */ + + int (*init)(void); + + /* Display Control */ + + int (*encode_fix)(struct fb_fix_screeninfo *fix, struct virgefb_par *par); + int (*decode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par); + int (*encode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par); + int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); + int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); + void (*blank)(int blank); +} *fbhw; + + +/* + * Frame Buffer Name + */ + +static char virgefb_name[16] = "Cybervision/3D"; + + +/* + * Cybervision Graphics Board + */ + +#define VIRGE8_WIDTH 1152 +#define VIRGE8_HEIGHT 886 +#define VIRGE8_PIXCLOCK 12500 /* ++Geert: Just a guess */ + +#if 0 +#define VIRGE16_WIDTH 800 +#define VIRGE16_HEIGHT 600 +#endif +#define VIRGE16_PIXCLOCK 25000 /* ++Geert: Just a guess */ + + +static unsigned int CyberKey = 0; +static unsigned char Cyber_colour_table [256][4]; +static unsigned long CyberMem; +static unsigned long CyberSize; +static volatile char *CyberRegs; +static volatile unsigned long CyberVGARegs; /* ++Andre: for CV64/3D, see macros at the beginning */ + + +/* + * Predefined Video Modes + */ + +static struct fb_videomode virgefb_predefined[] __initdata = { + { + "640x480-8", { /* Cybervision 8 bpp */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "800x600-8", { /* Cybervision 8 bpp */ + 800, 600, 800, 600, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1024x768-8", { /* Cybervision 8 bpp */ + 1024, 768, 1024, 768, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1152x886-8", { /* Cybervision 8 bpp */ + 1152, 886, 1152, 886, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1280x1024-8", { /* Cybervision 8 bpp */ + 1280, 1024, 1280, 1024, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "1600x1200-8", { /* Cybervision 8 bpp */ + 1600, 1200, 1600, 1200, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + }, { + "800x600-16", { /* Cybervision 16 bpp */ + 800, 600, 800, 600, 0, 0, 16, 0, + {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, + 0, 0, -1, -1, FB_ACCEL_NONE, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2, + FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + } + } +}; + + +#define NUM_TOTAL_MODES arraysize(virgefb_predefined) + + +static int Cyberfb_inverse = 0; +#if 0 +static int Cyberfb_Cyber8 = 0; /* Use Cybervision board */ +static int Cyberfb_Cyber16 = 0; /* Use Cybervision board */ +#endif + +/* + * Some default modes + */ + +#define VIRGE8_DEFMODE (0) +#define VIRGE16_DEFMODE (6) + +static struct fb_var_screeninfo virgefb_default; + + +/* + * Interface used by the world + */ + +void virgefb_setup(char *options, int *ints); + +static int virgefb_open(struct fb_info *info); +static int virgefb_release(struct fb_info *info); +static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, struct +fb_info *info); +static int virgefb_get_var(struct fb_var_screeninfo *var, int con, struct +fb_info *info); +static int virgefb_set_var(struct fb_var_screeninfo *var, int con, struct +fb_info *info); +static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int virgefb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int virgefb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + + +/* + * Interface to the low level console driver + */ + +unsigned long virgefb_init(unsigned long mem_start); +static int Cyberfb_switch(int con, struct fb_info *info); +static int Cyberfb_updatevar(int con, struct fb_info *info); +static void Cyberfb_blank(int blank, struct fb_info *info); + + +/* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static struct display_switch fbcon_virge8; +#endif + + +/* + * Hardware Specific Routines + */ + +static int Cyber_init(void); +static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, + struct virgefb_par *par); +static int Cyber_decode_var(struct fb_var_screeninfo *var, + struct virgefb_par *par); +static int Cyber_encode_var(struct fb_var_screeninfo *var, + struct virgefb_par *par); +static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); +static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static void Cyber_blank(int blank); + + +/* + * Internal routines + */ + +static void virgefb_get_par(struct virgefb_par *par); +static void virgefb_set_par(struct virgefb_par *par); +static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); +static void do_install_cmap(int con, struct fb_info *info); +static void virgefb_set_disp(int con, struct fb_info *info); +static int get_video_mode(const char *name); + + +/* -------------------- Hardware specific routines ------------------------- */ + + +/* + * Initialization + * + * Set the default video mode for this chipset. If a video mode was + * specified on the command line, it will override the default mode. + */ + +static int Cyber_init(void) +{ + int i; + + for (i = 0; i < 256; i++) + { + Cyber_colour_table [i][0] = i; + Cyber_colour_table [i][1] = i; + Cyber_colour_table [i][2] = i; + Cyber_colour_table [i][3] = 0; + } + + /* + * Just clear the thing for the biggest mode. + * + * ++Andre, TODO: determine size first, then clear all memory + * (the 3D penguin might need texture memory :-) ) + */ + + memset ((char*)CyberMem, 0, 1600 * 1200); + + /* Disable hardware cursor */ + CyberSize = 0x00400000; /* 4 MB */ + + vgawb_3d(0x3c8, 255); + vgawb_3d(0x3c9, 56); + vgawb_3d(0x3c9, 100); + vgawb_3d(0x3c9, 160); + + vgawb_3d(0x3c8, 254); + vgawb_3d(0x3c9, 0); + vgawb_3d(0x3c9, 0); + vgawb_3d(0x3c9, 0); + + /* Disable hardware cursor */ + vgawb_3d(S3_CRTC_ADR, S3_REG_LOCK2); + vgawb_3d(S3_CRTC_DATA, 0xa0); + vgawb_3d(S3_CRTC_ADR, S3_HGC_MODE); + vgawb_3d(S3_CRTC_DATA, 0x00); + vgawb_3d(S3_CRTC_ADR, S3_HWGC_DX); + vgawb_3d(S3_CRTC_DATA, 0x00); + vgawb_3d(S3_CRTC_ADR, S3_HWGC_DY); + vgawb_3d(S3_CRTC_DATA, 0x00); + + return 0; /* TODO: hardware cursor for CV64/3D */ +} + + +/* + * This function should fill in the `fix' structure based on the + * values in the `par' structure. + */ + +static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, + struct virgefb_par *par) +{ + int i; + + strcpy(fix->id, virgefb_name); + fix->smem_start = (caddr_t)CyberMem; + fix->smem_len = CyberSize; + fix->mmio_start = (unsigned char *)CyberRegs; + fix->mmio_len = 0x10000; /* TODO: verify this for the CV64/3D */ + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + if (par->bpp == 8) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else + fix->visual = FB_VISUAL_DIRECTCOLOR; + + fix->xpanstep = 0; + fix->ypanstep = 0; + fix->ywrapstep = 0; + fix->line_length = 0; + + for (i = 0; i < arraysize(fix->reserved); i++) + fix->reserved[i] = 0; + + return(0); +} + + +/* + * Get the video params out of `var'. If a value doesn't fit, round + * it up, if it's too big, return -EINVAL. + */ + +static int Cyber_decode_var(struct fb_var_screeninfo *var, + struct virgefb_par *par) +{ +#if 1 + par->xres = var->xres; + par->yres = var->yres; + par->bpp = var->bits_per_pixel; +#else + if (Cyberfb_Cyber8) { + par->xres = VIRGE8_WIDTH; + par->yres = VIRGE8_HEIGHT; + par->bpp = 8; + } else { + par->xres = VIRGE16_WIDTH; + par->yres = VIRGE16_HEIGHT; + par->bpp = 16; + } +#endif + return(0); +} + + +/* + * Fill the `var' structure based on the values in `par' and maybe + * other values read out of the hardware. + */ + +static int Cyber_encode_var(struct fb_var_screeninfo *var, + struct virgefb_par *par) +{ + int i; + + var->xres = par->xres; + var->yres = par->yres; + var->xres_virtual = par->xres; + var->yres_virtual = par->yres; + var->xoffset = 0; + var->yoffset = 0; + + var->bits_per_pixel = par->bpp; + var->grayscale = 0; + + if (par->bpp == 8) { + var->red.offset = 0; + var->red.length = 8; + var->red.msb_right = 0; + var->blue = var->green = var->red; + } else { + var->red.offset = 11; + var->red.length = 5; + var->red.msb_right = 0; + var->green.offset = 5; + var->green.length = 6; + var->green.msb_right = 0; + var->blue.offset = 0; + var->blue.length = 5; + var->blue.msb_right = 0; + } + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + + var->nonstd = 0; + var->activate = 0; + + var->height = -1; + var->width = -1; + + var->accel = FB_ACCEL_S3VIRGE; + DPRINTK("accel CV64/3D\n"); + + var->vmode = FB_VMODE_NONINTERLACED; + + /* Dummy values */ + + if (par->bpp == 8) + var->pixclock = VIRGE8_PIXCLOCK; + else + var->pixclock = VIRGE16_PIXCLOCK; + var->sync = 0; + var->left_margin = 64; + var->right_margin = 96; + var->upper_margin = 35; + var->lower_margin = 12; + var->hsync_len = 112; + var->vsync_len = 2; + + for (i = 0; i < arraysize(var->reserved); i++) + var->reserved[i] = 0; + + return(0); +} + + +/* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + */ + +static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + if (regno > 255) + { + return (1); + } + + /* + * No colors on the CV3D yet. + */ + + vgawb_3d(0x3c8, (unsigned char) regno); + Cyber_colour_table [regno][0] = red & 0xff; + Cyber_colour_table [regno][1] = green & 0xff; + Cyber_colour_table [regno][2] = blue & 0xff; + Cyber_colour_table [regno][3] = transp; + + vgawb_3d(0x3c9, ((red & 0xff) >> 2)); + vgawb_3d(0x3c9, ((green & 0xff) >> 2)); + vgawb_3d(0x3c9, ((blue & 0xff) >> 2)); + + return (0); +} + + +/* + * Read a single color register and split it into + * colors/transparent. Return != 0 for invalid regno. + */ + +static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info) +{ + if (regno >= 256) + return (1); + *red = Cyber_colour_table [regno][0]; + *green = Cyber_colour_table [regno][1]; + *blue = Cyber_colour_table [regno][2]; + *transp = Cyber_colour_table [regno][3]; + return (0); +} + + +/* + * (Un)Blank the screen + */ + +void Cyber_blank(int blank) +{ + int i; + + if (blank) + { + for (i = 0; i < 256; i++) + { + vgawb_3d(0x3c8, (unsigned char) i); + vgawb_3d(0x3c9, 0); + vgawb_3d(0x3c9, 0); + vgawb_3d(0x3c9, 0); + } + } + else + { + for (i = 0; i < 256; i++) + { + vgawb_3d(0x3c8, (unsigned char) i); + vgawb_3d(0x3c9, Cyber_colour_table[i][0] >> 2); + vgawb_3d(0x3c9, Cyber_colour_table[i][1] >> 2); + vgawb_3d(0x3c9, Cyber_colour_table[i][2] >> 2); + } + } +} + +/* + * CV3D low-level support + */ + +#define Cyber3D_WaitQueue(v) { do { while ((rl_3d(0x8504) & 0x1f00) < (((v)+2) << 8)); } while (0); } + +static inline void Cyber3D_WaitBusy(void) +{ +unsigned long status; + + do { + status = rl_3d(0x8504); + } while (!(status & (1 << 13))); +} + +#define S3V_BITBLT (0x0 << 27) +#define S3V_RECTFILL (0x2 << 27) +#define S3V_AUTOEXE 0x01 +#define S3V_HWCLIP 0x02 +#define S3V_DRAW 0x20 +#define S3V_DST_8BPP 0x00 +#define S3V_DST_16BPP 0x04 +#define S3V_DST_24BPP 0x08 +#define S3V_MONO_PAT 0x100 + +#define S3V_BLT_COPY (0xcc<<17) +#define S3V_BLT_CLEAR (0x00<<17) +#define S3V_BLT_SET (0xff<<17) + + /* + * BitBLT - Through the Plane + */ + +static void Cyber3D_BitBLT(u_short curx, u_short cury, u_short destx, + u_short desty, u_short width, u_short height) +{ + unsigned int blitcmd = S3V_BITBLT | S3V_DRAW | S3V_DST_8BPP; + + blitcmd |= S3V_BLT_COPY; + + /* Set drawing direction */ + /* -Y, X maj, -X (default) */ + if (curx > destx) + { + blitcmd |= (1 << 25); /* Drawing direction +X */ + } + else + { + curx += (width - 1); + destx += (width - 1); + } + + if (cury > desty) + { + blitcmd |= (1 << 26); /* Drawing direction +Y */ + } + else + { + cury += (height - 1); + desty += (height - 1); + } + + wl_3d(0xa4f4, 1); /* pattern fb color */ + + wl_3d(0xa4e8, ~0); /* mono pat 0 */ + wl_3d(0xa4ec, ~0); /* mono pat 1 */ + + wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */ + wl_3d(0xa508, ((curx << 16) | cury)); /* rsrc_xy */ + wl_3d(0xa50c, ((destx << 16) | desty)); /* rdest_xy */ + + wl_3d(0xa500, blitcmd); /* GO! */ + + Cyber3D_WaitBusy(); +} + +/* + * Rectangle Fill Solid + */ + +static void Cyber3D_RectFill(u_short x, u_short y, u_short width, + u_short height, u_short color) +{ + unsigned int tmp; + unsigned int blitcmd = S3V_RECTFILL | S3V_DRAW | S3V_DST_8BPP | + S3V_BLT_CLEAR | S3V_MONO_PAT | (1 << 26) | (1 << 25); + + tmp = color & 0xff; + wl_3d(0xa4f4, tmp); + + wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */ + wl_3d(0xa50c, ((x << 16) | y)); /* rdest_xy */ + + wl_3d(0xa500, blitcmd); /* GO! */ + Cyber3D_WaitBusy(); +} + + +/************************************************************** + * Move cursor to x, y + */ +static void Cyber_MoveCursor (u_short x, u_short y) +{ + printk("Yuck .... MoveCursor on a 3D\n"); + return; +} + + +/* -------------------- Interfaces to hardware functions -------------------- */ + + +static struct fb_hwswitch Cyber_switch = { + Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var, + Cyber_getcolreg, Cyber_setcolreg, Cyber_blank +}; + + +/* -------------------- Generic routines ------------------------------------ */ + + +/* + * Fill the hardware's `par' structure. + */ + +static void virgefb_get_par(struct virgefb_par *par) +{ + if (current_par_valid) + { + *par = current_par; + } + else + { + fbhw->decode_var(&virgefb_default, par); + } +} + + +static void virgefb_set_par(struct virgefb_par *par) +{ + current_par = *par; + current_par_valid = 1; +} + + +static void virge_set_video(struct fb_var_screeninfo *var) +{ + /* Set clipping rectangle to current screen size */ + + unsigned int clip; + + clip = ((0 << 16) | (var->xres - 1)); + wl_3d(0xa4dc, clip); + clip = ((0 << 16) | (var->yres - 1)); + wl_3d(0xa4e0, clip); +} + + +static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) +{ + int err, activate; + struct virgefb_par par; + + if ((err = fbhw->decode_var(var, &par))) + return(err); + activate = var->activate; + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) + virgefb_set_par(&par); + fbhw->encode_var(var, &par); + var->activate = activate; + + virge_set_video(var); + return 0; +} + + +static void do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, + fbhw->setcolreg, info); + else + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, fbhw->setcolreg, info); +} + + +/* + * Open/Release the frame buffer device + */ + +static int virgefb_open(struct fb_info *info) +{ + /* + * Nothing, only a usage count for the moment + */ + + MOD_INC_USE_COUNT; + return(0); +} + +static int virgefb_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + + +/* + * Get the Fixed Part of the Display + */ + +static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + struct virgefb_par par; + int error = 0; + + if (con == -1) + virgefb_get_par(&par); + else + error = fbhw->decode_var(&fb_display[con].var, &par); + return(error ? error : fbhw->encode_fix(fix, &par)); +} + + +/* + * Get the User Defined Part of the Display + */ + +static int virgefb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct virgefb_par par; + int error = 0; + + if (con == -1) + { + virgefb_get_par(&par); + error = fbhw->encode_var(var, &par); + disp.var = *var; /* ++Andre: don't know if this is the right place */ + } + else + { + *var = fb_display[con].var; + } + + return(error); +} + + +static void virgefb_set_disp(int con, struct fb_info *info) +{ + struct fb_fix_screeninfo fix; + struct display *display; + + if (con >= 0) + display = &fb_display[con]; + else + display = &disp; /* used during initialization */ + + virgefb_get_fix(&fix, con, info); + if (con == -1) + con = 0; + display->screen_base = (u_char *)fix.smem_start; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->can_soft_blank = 1; + display->inverse = Cyberfb_inverse; + switch (display->var.bits_per_pixel) { +#ifdef CONFIG_FBCON_CFB8 + case 8: + display->dispsw = &fbcon_virge8; + break; +#endif +#ifdef CONFIG_FBCON_CFB16 + case 16: + display->dispsw = &fbcon_cfb16; + break; +#endif + default: + display->dispsw = NULL; + break; + } +} + + +/* + * Set the User Defined Part of the Display + */ + +static int virgefb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp; + + if ((err = do_fb_set_var(var, con == currcon))) + return(err); + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + oldxres = fb_display[con].var.xres; + oldyres = fb_display[con].var.yres; + oldvxres = fb_display[con].var.xres_virtual; + oldvyres = fb_display[con].var.yres_virtual; + oldbpp = fb_display[con].var.bits_per_pixel; + fb_display[con].var = *var; + if (oldxres != var->xres || oldyres != var->yres || + oldvxres != var->xres_virtual || + oldvyres != var->yres_virtual || + oldbpp != var->bits_per_pixel) { + virgefb_set_disp(con, info); + (*fb_info.changevar)(con); + fb_alloc_cmap(&fb_display[con].cmap, 0, 0); + do_install_cmap(con, info); + } + } + var->activate = 0; + return(0); +} + + +/* + * Get the Colormap + */ + +static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (con == currcon) /* current console? */ + return(fb_get_cmap(cmap, &fb_display[con].var, + kspc, fbhw->getcolreg, info)); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return(0); +} + + +/* + * Set the Colormap + */ + +static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1<<fb_display[con].var.bits_per_pixel, 0))) + return(err); + } + if (con == currcon) /* current console? */ + return(fb_set_cmap(cmap, &fb_display[con].var, + kspc, fbhw->setcolreg, info)); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return(0); +} + + +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ + +static int virgefb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + return(-EINVAL); +} + + +/* + * Cybervision Frame Buffer Specific ioctls + */ + +static int virgefb_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg, int con, struct fb_info *info) +{ + return(-EINVAL); +} + + +static struct fb_ops virgefb_ops = { + virgefb_open, virgefb_release, virgefb_get_fix, virgefb_get_var, + virgefb_set_var, virgefb_get_cmap, virgefb_set_cmap, + virgefb_pan_display, NULL, virgefb_ioctl +}; + + +__initfunc(void virgefb_setup(char *options, int *ints)) +{ + char *this_opt; + + fb_info.fontname[0] = '\0'; + + if (!options || !*options) + return; + + for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) + if (!strcmp(this_opt, "inverse")) { + Cyberfb_inverse = 1; + fb_invert_cmaps(); + } else if (!strncmp(this_opt, "font:", 5)) + strcpy(fb_info.fontname, this_opt+5); + else if (!strcmp (this_opt, "virge8")){ + virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var; + } + else if (!strcmp (this_opt, "virge16")){ + virgefb_default = virgefb_predefined[VIRGE16_DEFMODE].var; + } + else + get_video_mode(this_opt); + + DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",virgefb_default.xres, + virgefb_default.yres, + virgefb_default.bits_per_pixel); +} + + +/* + * Initialization + */ + +__initfunc(unsigned long virgefb_init(unsigned long mem_start)) +{ + int err; + struct virgefb_par par; + unsigned long board_addr; + const struct ConfigDev *cd; + + if (!(CyberKey = zorro_find(ZORRO_PROD_PHASE5_CYBERVISION64_3D, 0, 0))) + return mem_start; + + cd = zorro_get_board (CyberKey); + zorro_config_board (CyberKey, 0); + board_addr = (unsigned long)cd->cd_BoardAddr; + + /* This includes the video memory as well as the S3 register set */ + if ((unsigned long)cd->cd_BoardAddr < 0x01000000) + { + /* + * Ok we got the board running in Z2 space. + */ + + CyberMem = ZTWO_VADDR(board_addr); + printk("CV3D detected running in Z2 mode ... not yet supported!\n"); + return -ENODEV; + } + else + { + CyberVGARegs = kernel_map(board_addr +0x0c000000, + 0x00010000, + KERNELMAP_NOCACHE_SER, + &mem_start); + CyberRegs = (char *)kernel_map(board_addr +0x05000000, + 0x00010000, + KERNELMAP_NOCACHE_SER, + &mem_start); + CyberMem = kernel_map(board_addr + 0x04800000, + 0x00400000, + KERNELMAP_NOCACHE_SER, + &mem_start); + printk("CV3D detected running in Z3 mode\n"); + } + + fbhw = &Cyber_switch; + + strcpy(fb_info.modename, virgefb_name); + fb_info.changevar = NULL; + fb_info.node = -1; + fb_info.fbops = &virgefb_ops; + fb_info.disp = &disp; + fb_info.switch_con = &Cyberfb_switch; + fb_info.updatevar = &Cyberfb_updatevar; + fb_info.blank = &Cyberfb_blank; + + err = register_framebuffer(&fb_info); + if (err < 0) + return mem_start; + + fbhw->init(); + fbhw->decode_var(&virgefb_default, &par); + fbhw->encode_var(&virgefb_default, &par); + + do_fb_set_var(&virgefb_default, 1); + virgefb_get_var(&fb_display[0].var, -1, &fb_info); + virgefb_set_disp(-1, &fb_info); + do_install_cmap(0, &fb_info); + + printk("fb%d: %s frame buffer device, using %ldK of video memory\n", + GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10); + + /* TODO: This driver cannot be unloaded yet */ + MOD_INC_USE_COUNT; + + return mem_start; +} + + +static int Cyberfb_switch(int con, struct fb_info *info) +{ + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, + fbhw->getcolreg, info); + + do_fb_set_var(&fb_display[con].var, 1); + currcon = con; + /* Install new colormap */ + do_install_cmap(con, info); + return(0); +} + + +/* + * Update the `var' structure (called by fbcon.c) + * + * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'. + * Since it's called by a kernel driver, no range checking is done. + */ + +static int Cyberfb_updatevar(int con, struct fb_info *info) +{ + return(0); +} + + +/* + * Blank the display. + */ + +static void Cyberfb_blank(int blank, struct fb_info *info) +{ + fbhw->blank(blank); +} + + +/* + * Get a Video Mode + */ + +__initfunc(static int get_video_mode(const char *name)) +{ + int i; + + for (i = 0; i < NUM_TOTAL_MODES; i++) { + if (!strcmp(name, virgefb_predefined[i].name)) { + virgefb_default = virgefb_predefined[i].var; + return(i); + } + } + /* ++Andre: set virgefb default mode */ + virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var; + return(0); +} + + +/* + * Text console acceleration + */ + +#ifdef CONFIG_FBCON_CFB8 +static void fbcon_virge8_bmove(struct display *p, int sy, int sx, int dy, + int dx, int height, int width) +{ + sx *= 8; dx *= 8; width *= 8; + Cyber3D_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx, + (u_short)(dy*p->fontheight), (u_short)width, + (u_short)(height*p->fontheight)); +} + +static void fbcon_virge8_clear(struct vc_data *conp, struct display *p, int sy, + int sx, int height, int width) +{ + unsigned char bg; + + sx *= 8; width *= 8; + bg = attr_bgcol_ec(p,conp); + Cyber3D_RectFill((u_short)sx, (u_short)(sy*p->fontheight), + (u_short)width, (u_short)(height*p->fontheight), + (u_short)bg); +} + +static struct display_switch fbcon_virge8 = { + fbcon_cfb8_setup, fbcon_virge8_bmove, fbcon_virge8_clear, fbcon_cfb8_putc, + fbcon_cfb8_putcs, fbcon_cfb8_revc +}; +#endif + + +#ifdef MODULE +int init_module(void) +{ + return(virgefb_init(NULL)); +} + +void cleanup_module(void) +{ + /* Not reached because the usecount will never be + decremented to zero */ + unregister_framebuffer(&fb_info); + /* TODO: clean up ... */ +} +#endif /* MODULE */ diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 9a4a460e8..be58c11ed 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -6,7 +6,6 @@ #include <linux/module.h> -#include <linux/fs.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -15,6 +14,8 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/string.h> +#include <linux/fs.h> +#include <linux/file.h> #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/ptrace.h> @@ -401,10 +402,10 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs printk(KERN_NOTICE "executable not page aligned\n"); fd = open_dentry(bprm->dentry, O_RDONLY); - if (fd < 0) return fd; - file = current->files->fd[fd]; + file = fcheck(fd); + if (!file->f_op || !file->f_op->mmap) { sys_close(fd); do_mmap(NULL, 0, ex.a_text+ex.a_data, @@ -479,48 +480,44 @@ static inline int do_load_aout_library(int fd) { struct file * file; - struct exec ex; - struct dentry * dentry; struct inode * inode; - unsigned int len; - unsigned int bss; - unsigned int start_addr; + unsigned long bss, start_addr, len; unsigned long error; + int retval; + loff_t offset = 0; + struct exec ex; - file = current->files->fd[fd]; - - if (!file || !file->f_op) - return -EACCES; - - dentry = file->f_dentry; - inode = dentry->d_inode; - - /* Seek into the file */ - if (file->f_op->llseek) { - if ((error = file->f_op->llseek(file, 0, 0)) != 0) - return -ENOEXEC; - } else - file->f_pos = 0; + retval = -EACCES; + file = fget(fd); + if (!file) + goto out; + if (!file->f_op) + goto out_putf; + inode = file->f_dentry->d_inode; + retval = -ENOEXEC; + /* N.B. Save current fs? */ set_fs(KERNEL_DS); - error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos); + error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset); set_fs(USER_DS); if (error != sizeof(ex)) - return -ENOEXEC; + goto out_putf; /* We come in here for the regular a.out style of shared libraries */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { - return -ENOEXEC; + goto out_putf; } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); - return -ENOEXEC; + goto out_putf; } - if (N_FLAGS(ex)) return -ENOEXEC; + if (N_FLAGS(ex)) + goto out_putf; /* For QMAGIC, the starting address is 0x20 into the page. We mask this off to get the starting address for the page */ @@ -532,18 +529,26 @@ do_load_aout_library(int fd) PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, N_TXTOFF(ex)); + retval = error; if (error != start_addr) - return error; + goto out_putf; + len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - error = do_mmap(NULL, start_addr + len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_FIXED, 0); + error = do_mmap(NULL, start_addr + len, bss - len, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_FIXED, 0); + retval = error; if (error != start_addr + len) - return error; + goto out_putf; } - return 0; + retval = 0; + +out_putf: + fput(file); +out: + return retval; } static int diff --git a/fs/buffer.c b/fs/buffer.c index ec844de9f..1d58146ae 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -55,12 +55,9 @@ static char buffersize_index[17] = number of unused buffer heads */ /* - * How large a hash table do we need? + * Hash table mask.. */ -#define HASH_PAGES_ORDER 4 -#define HASH_PAGES (1UL << HASH_PAGES_ORDER) -#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *)) -#define HASH_MASK (NR_HASH-1) +static unsigned long bh_hash_mask = 0; static int grow_buffers(int pri, int size); @@ -421,7 +418,7 @@ void invalidate_buffers(kdev_t dev) } } -#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block))&HASH_MASK) +#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) #define hash(dev,block) hash_table[_hashfn(dev,block)] static inline void remove_from_hash_queue(struct buffer_head * bh) @@ -732,7 +729,7 @@ static void refill_freelist(int size) needed = bdf_prm.b_un.nrefill * size; while ((nr_free_pages > freepages.min*2) && - BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) && + (buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.max_percent * num_physpages) && grow_buffers(GFP_BUFFER, size)) { obtained += PAGE_SIZE; if (obtained >= needed) @@ -817,7 +814,6 @@ repeat: */ while (obtained < (needed >> 1) && nr_free_pages > freepages.min + 5 && - BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) && grow_buffers(GFP_BUFFER, size)) obtained += PAGE_SIZE; @@ -1707,11 +1703,16 @@ void show_buffers(void) */ void buffer_init(void) { - hash_table = (struct buffer_head **) - __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER); + int order = 5; /* Currently maximum order.. */ + unsigned int nr_hash; + + nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct buffer_head *); + hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); + if (!hash_table) panic("Failed to allocate buffer hash table\n"); - memset(hash_table,0,NR_HASH*sizeof(struct buffer_head *)); + memset(hash_table, 0, nr_hash * sizeof(struct buffer_head *)); + bh_hash_mask = nr_hash-1; bh_cachep = kmem_cache_create("buffer_head", sizeof(struct buffer_head), @@ -142,10 +142,9 @@ int unregister_binfmt(struct linux_binfmt * fmt) /* N.B. Error returns must be < 0 */ int open_dentry(struct dentry * dentry, int mode) { - int fd; struct inode * inode = dentry->d_inode; struct file * f; - int error; + int fd, error; error = -EINVAL; if (!inode->i_op || !inode->i_op->default_file_ops) @@ -167,7 +166,7 @@ int open_dentry(struct dentry * dentry, int mode) if (error) goto out_filp; } - current->files->fd[fd] = f; + fd_install(fd, f); dget(dentry); } return fd; @@ -199,18 +198,20 @@ asmlinkage int sys_uselib(const char * library) retval = fd; if (fd < 0) goto out; - file = current->files->fd[fd]; + file = fget(fd); retval = -ENOEXEC; if (file && file->f_dentry && file->f_op && file->f_op->read) { for (fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(int) = fmt->load_shlib; if (!fn) continue; + /* N.B. Should use file instead of fd */ retval = fn(fd); if (retval != -ENOEXEC) break; } } + fput(file); sys_close(fd); out: unlock_kernel(); @@ -495,7 +496,7 @@ static inline void flush_old_files(struct files_struct * files) unsigned long set, i; i = j * __NFDBITS; - if (i >= NR_OPEN) + if (i >= files->max_fds) break; set = files->close_on_exec.fds_bits[j]; files->close_on_exec.fds_bits[j] = 0; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index 3b168cd75..442518af3 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -181,7 +181,7 @@ repeat: for (i = 0; i < addr_per_block; i++) if (le32_to_cpu(*(ind++))) break; - if (i >= addr_per_block) + if (i >= addr_per_block) { if (ind_bh->b_count != 1) retry = 1; else { @@ -193,6 +193,7 @@ repeat: bforget(ind_bh); ind_bh = NULL; } + } if (IS_SYNC(inode) && ind_bh && buffer_dirty(ind_bh)) { ll_rw_block (WRITE, 1, &ind_bh); wait_on_buffer (ind_bh); @@ -243,7 +244,7 @@ repeat: for (i = 0; i < addr_per_block; i++) if (le32_to_cpu(*(dind++))) break; - if (i >= addr_per_block) + if (i >= addr_per_block) { if (dind_bh->b_count != 1) retry = 1; else { @@ -255,6 +256,7 @@ repeat: bforget(dind_bh); dind_bh = 0; } + } if (IS_SYNC(inode) && dind_bh && buffer_dirty(dind_bh)) { ll_rw_block (WRITE, 1, &dind_bh); wait_on_buffer (dind_bh); @@ -304,7 +306,7 @@ repeat: for (i = 0; i < addr_per_block; i++) if (le32_to_cpu(*(tind++))) break; - if (i >= addr_per_block) + if (i >= addr_per_block) { if (tind_bh->b_count != 1) retry = 1; else { @@ -316,6 +318,7 @@ repeat: bforget(tind_bh); tind_bh = 0; } + } if (IS_SYNC(inode) && tind_bh && buffer_dirty(tind_bh)) { ll_rw_block (WRITE, 1, &tind_bh); wait_on_buffer (tind_bh); diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 298d6c155..56b490841 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -117,7 +117,7 @@ repeat: for (i = 0; i < 512; i++) if (*(ind++)) break; - if (i >= 512) + if (i >= 512) { if (ind_bh->b_count != 1) retry = 1; else { @@ -125,6 +125,7 @@ repeat: *p = 0; minix_free_block(inode->i_sb,tmp); } + } brelse(ind_bh); return retry; } @@ -161,7 +162,7 @@ repeat: for (i = 0; i < 512; i++) if (*(dind++)) break; - if (i >= 512) + if (i >= 512) { if (dind_bh->b_count != 1) retry = 1; else { @@ -170,6 +171,7 @@ repeat: mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } + } brelse(dind_bh); return retry; } @@ -279,7 +281,7 @@ repeat: for (i = 0; i < 256; i++) if (*(ind++)) break; - if (i >= 256) + if (i >= 256) { if (ind_bh->b_count != 1) retry = 1; else { @@ -287,6 +289,7 @@ repeat: *p = 0; minix_free_block(inode->i_sb,tmp); } + } brelse(ind_bh); return retry; } @@ -323,7 +326,7 @@ repeat: for (i = 0; i < 256; i++) if (*(dind++)) break; - if (i >= 256) + if (i >= 256) { if (dind_bh->b_count != 1) retry = 1; else { @@ -332,6 +335,7 @@ repeat: mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } + } brelse(dind_bh); return retry; } @@ -368,7 +372,7 @@ repeat: for (i = 0; i < 256; i++) if (*(tind++)) break; - if (i >= 256) + if (i >= 256) { if (tind_bh->b_count != 1) retry = 1; else { @@ -377,6 +381,7 @@ repeat: mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } + } brelse(tind_bh); return retry; } @@ -46,11 +46,12 @@ static ssize_t pipe_read(struct file * filp, char * buf, if (filp->f_flags & O_NONBLOCK) { if (PIPE_LOCK(*inode)) return -EAGAIN; - if (PIPE_EMPTY(*inode)) + if (PIPE_EMPTY(*inode)) { if (PIPE_WRITERS(*inode)) return -EAGAIN; else return 0; + } } else while (PIPE_EMPTY(*inode) || PIPE_LOCK(*inode)) { if (PIPE_EMPTY(*inode)) { if (!PIPE_WRITERS(*inode)) @@ -485,8 +486,9 @@ int do_pipe(int *fd) f2->f_flags = O_WRONLY; f2->f_op = &write_pipe_fops; f2->f_mode = 2; - current->files->fd[i] = f1; - current->files->fd[j] = f2; + + fd_install(i, f1); + fd_install(j, f2); fd[0] = i; fd[1] = j; return 0; diff --git a/fs/proc/array.c b/fs/proc/array.c index 33df2c56a..26621a6e5 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -597,8 +597,9 @@ static unsigned long get_wchan(struct task_struct *p) #elif defined(__mips__) # define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg \ - sizeof(struct pt_regs)) -# define KSTK_EIP(tsk) (*(unsigned long *)((tsk)->tss.ksp + PT_REG(cp0_epc))) -# define KSTK_ESP(tsk) (*(unsigned long *)((tsk)->tss.ksp + PT_REG(regs[29]))) +#define KSTK_TOS(tsk) ((unsigned long)(tsk) + KERNEL_STACK_SIZE - 32) +# define KSTK_EIP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(cp0_epc))) +# define KSTK_ESP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(regs[29]))) #endif /* Gcc optimizes away "strlen(x)" for constant x */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 8897578d6..ab9948a62 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -6,13 +6,14 @@ * proc fd directory handling functions */ -#include <asm/uaccess.h> - #include <linux/errno.h> #include <linux/sched.h> +#include <linux/file.h> #include <linux/proc_fs.h> #include <linux/stat.h> +#include <asm/uaccess.h> + static int proc_readfd(struct file *, void *, filldir_t); static int proc_lookupfd(struct inode *, struct dentry *); @@ -65,19 +66,20 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) { unsigned int ino, pid, fd, c; struct task_struct * p; - struct super_block * sb; + struct file * file; struct inode *inode; const char *name; - int len; + int len, err; + err = -ENOENT; + if (!dir) + goto out; ino = dir->i_ino; pid = ino >> 16; ino &= 0x0000ffff; - if (!dir) - return -ENOENT; - sb = dir->i_sb; + if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) - return -ENOENT; + goto out; fd = 0; len = dentry->d_name.len; @@ -85,22 +87,20 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) while (len-- > 0) { c = *name - '0'; name++; - if (c > 9) { - fd = 0xfffff; - break; - } + if (c > 9) + goto out; fd *= 10; fd += c; - if (fd & 0xffff0000) { - fd = 0xfffff; - break; - } + if (fd & 0xffff0000) + goto out; } + read_lock(&tasklist_lock); + file = NULL; p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done only after not using 'p' any more */ - if (!pid || !p) - return -ENOENT; + if (p) + file = fcheck_task(p, fd); + read_unlock(&tasklist_lock); /* * File handle is invalid if it is out of range, if the process @@ -108,60 +108,61 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) * is NULL */ - if (fd >= NR_OPEN || - !p->files || - !p->files->fd[fd] || - !p->files->fd[fd]->f_dentry) - return -ENOENT; + if (!file || !file->f_dentry) + goto out; + /* N.B. What happens if fd > 255?? */ ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; - - inode = proc_get_inode(sb, ino, NULL); - if (!inode) - return -ENOENT; - - d_add(dentry, inode); - return 0; + inode = proc_get_inode(dir->i_sb, ino, NULL); + if (inode) { + d_add(dentry, inode); + err = 0; + } +out: + return err; } #define NUMBUF 10 -static int proc_readfd(struct file * filp, - void * dirent, filldir_t filldir) +static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) { - char buf[NUMBUF]; + struct inode *inode = filp->f_dentry->d_inode; struct task_struct * p, **tarrayp; unsigned int fd, pid, ino; - unsigned long i,j; - struct inode *inode = filp->f_dentry->d_inode; + int retval; + char buf[NUMBUF]; + retval = -EBADF; if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; + goto out; + + retval = 0; ino = inode->i_ino; pid = ino >> 16; ino &= 0x0000ffff; if (ino != PROC_PID_FD) - return 0; + goto out; for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) { unsigned long ino = inode->i_ino; if (fd) ino = (ino & 0xffff0000) | PROC_PID_INO; if (filldir(dirent, "..", fd+1, fd, ino) < 0) - return 0; + goto out; } read_lock(&tasklist_lock); p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done only after not using 'p' any more */ - if(!p) - return 0; + if (!p) + goto out_unlock; tarrayp = p->tarray_ptr; - for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) { - if (!p->files) - break; - if (!p->files->fd[fd] || !p->files->fd[fd]->f_dentry) + for (fd -= 2 ; p->files && fd < p->files->max_fds; fd++, filp->f_pos++) + { + struct file * file = fcheck_task(p, fd); + unsigned int i,j; + + if (!file || !file->f_dentry) continue; j = NUMBUF; @@ -172,13 +173,21 @@ static int proc_readfd(struct file * filp, i /= 10; } while (i); + /* Drop the task lock, as the filldir function may block */ + read_unlock(&tasklist_lock); + ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) - break; + goto out; + read_lock(&tasklist_lock); /* filldir() might have slept, so we must re-validate "p" */ if (p != *tarrayp || p->pid != pid) break; } - return 0; +out_unlock: + read_unlock(&tasklist_lock); + +out: + return retval; } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 9a0e29a84..16ee84225 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/stat.h> +#include <linux/file.h> #include <linux/locks.h> #include <linux/limits.h> #include <linux/config.h> @@ -250,24 +251,24 @@ void proc_read_inode(struct inode * inode) inode->i_blocks = 0; inode->i_blksize = 1024; ino = inode->i_ino; - if (ino >= PROC_OPENPROM_FIRST && ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) - return; + if (ino >= PROC_OPENPROM_FIRST && + ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) + goto out; inode->i_op = NULL; inode->i_mode = 0; inode->i_uid = 0; inode->i_gid = 0; inode->i_nlink = 1; inode->i_size = 0; - pid = ino >> 16; + pid = ino >> 16; if (!pid) - return; + goto out; + read_lock(&tasklist_lock); p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done only after we have stopped using 'p' */ - if (!p) - return; + goto out_unlock; ino &= 0x0000ffff; if (ino == PROC_PID_INO || p->dumpable) { @@ -275,19 +276,27 @@ void proc_read_inode(struct inode * inode) inode->i_gid = p->egid; } switch (ino >> 8) { - case PROC_PID_FD_DIR: - ino &= 0xff; - if (ino >= NR_OPEN || !p->files->fd[ino]) - return; - inode->i_op = &proc_link_inode_operations; - inode->i_size = 64; - inode->i_mode = S_IFLNK; - if (p->files->fd[ino]->f_mode & 1) - inode->i_mode |= S_IRUSR | S_IXUSR; - if (p->files->fd[ino]->f_mode & 2) - inode->i_mode |= S_IWUSR | S_IXUSR; - return; + struct file * file; + case PROC_PID_FD_DIR: + ino &= 0xff; + file = fcheck_task(p, ino); + if (!file) + goto out_unlock; + + inode->i_op = &proc_link_inode_operations; + inode->i_size = 64; + inode->i_mode = S_IFLNK; + if (file->f_mode & 1) + inode->i_mode |= S_IRUSR | S_IXUSR; + if (file->f_mode & 2) + inode->i_mode |= S_IWUSR | S_IXUSR; } +out_unlock: + /* Defer unlocking until we're done with the task */ + read_unlock(&tasklist_lock); + +out: + return; } void proc_write_inode(struct inode * inode) diff --git a/fs/proc/link.c b/fs/proc/link.c index 66a01e1af..2f4abc945 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -10,8 +10,9 @@ #include <linux/errno.h> #include <linux/sched.h> -#include <linux/fs.h> #include <linux/mm.h> +#include <linux/fs.h> +#include <linux/file.h> #include <linux/proc_fs.h> #include <linux/stat.h> @@ -81,53 +82,58 @@ static struct dentry * proc_follow_link(struct dentry *dentry, pid = ino >> 16; ino &= 0x0000ffff; - p = find_task_by_pid(pid); result = ERR_PTR(-ENOENT); + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); if (!p) - goto out; + goto out_unlock; switch (ino) { case PROC_PID_CWD: if (!p->fs || !p->fs->pwd) - break; - result = dget(p->fs->pwd); - break; + goto out_unlock; + result = p->fs->pwd; + goto out_dget; case PROC_PID_ROOT: if (!p->fs || !p->fs->root) - break; - result = dget(p->fs->root); - break; + goto out_unlock; + result = p->fs->root; + goto out_dget; case PROC_PID_EXE: { struct vm_area_struct * vma; if (!p->mm) - break; + goto out_unlock; vma = p->mm->mmap; while (vma) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) - return dget(vma->vm_file->f_dentry); - + if ((vma->vm_flags & VM_EXECUTABLE) && + vma->vm_file) { + result = vma->vm_file->f_dentry; + goto out_dget; + } vma = vma->vm_next; } - break; + goto out_unlock; } default: switch (ino >> 8) { + struct file * file; case PROC_PID_FD_DIR: - if (!p->files) - break; ino &= 0xff; - if (ino >= NR_OPEN) - break; - if (!p->files->fd[ino]) - break; - if (!p->files->fd[ino]->f_dentry) - break; - result = dget(p->files->fd[ino]->f_dentry); - break; + file = fcheck_task(p, ino); + if (!file || !file->f_dentry) + goto out_unlock; + result = file->f_dentry; + goto out_dget; } } +out_dget: + result = dget(result); + +out_unlock: + read_unlock(&tasklist_lock); + out: return result; } diff --git a/fs/select.c b/fs/select.c index 1328660b0..a4e847e64 100644 --- a/fs/select.c +++ b/fs/select.c @@ -41,7 +41,7 @@ * sleep/wakeup mechanism works. * * Two very simple procedures, poll_wait() and free_wait() make all the - * work. poll_wait() is an inline-function defined in <linux/sched.h>, + * work. poll_wait() is an inline-function defined in <linux/poll.h>, * as all select/poll functions have to call it to add an entry to the * poll table. */ @@ -152,9 +152,8 @@ int do_select(int n, fd_set_buffer *fds, unsigned long timeout) n = retval; retval = 0; for (;;) { - struct file ** fd = current->files->fd; current->state = TASK_INTERRUPTIBLE; - for (i = 0 ; i < n ; i++, fd++) { + for (i = 0 ; i < n; i++) { unsigned long bit = BIT(i); unsigned long *in = MEM(i,fds->in); unsigned long mask; @@ -162,8 +161,12 @@ int do_select(int n, fd_set_buffer *fds, unsigned long timeout) if (!(bit & BITS(in))) continue; - - file = *fd; + /* + * The poll_wait routine will increment f_count if + * the file is added to the wait table, so we don't + * need to increment it now. + */ + file = fcheck(i); mask = POLLNVAL; if (file) { mask = DEFAULT_POLLMASK; @@ -286,23 +289,21 @@ out_nofds: static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) { - int count; - struct file ** fd = current->files->fd; + int count = 0; - count = 0; for (;;) { unsigned int j; struct pollfd * fdpnt; current->state = TASK_INTERRUPTIBLE; for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) { - unsigned int i; unsigned int mask; struct file * file; mask = POLLNVAL; - i = fdpnt->fd; - if (i < NR_OPEN && (file = fd[i]) != NULL) { + /* poll_wait increments f_count if needed */ + file = fcheck(fdpnt->fd); + if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) mask = file->f_op->poll(file, wait); @@ -326,18 +327,22 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) { - int i, count, fdcount, err; + int i, fdcount, err, size; struct pollfd * fds, *fds1; - poll_table wait_table, *wait; + poll_table wait_table, *wait = NULL; lock_kernel(); + /* Do a sanity check on nfds ... */ + err = -EINVAL; + if (nfds > NR_OPEN) + goto out; + if (timeout < 0) timeout = 0x7fffffff; else if (timeout) timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1; - err = -ENOMEM; - wait = NULL; + err = -ENOMEM; if (timeout) { struct poll_table_entry *entry; entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL); @@ -347,34 +352,32 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) wait_table.entry = entry; wait = &wait_table; } - fds = (struct pollfd *) kmalloc(nfds*sizeof(struct pollfd), GFP_KERNEL); - if (!fds) { + + size = nfds * sizeof(struct pollfd); + fds = (struct pollfd *) kmalloc(size, GFP_KERNEL); + if (!fds) goto out; - } err = -EFAULT; - if (copy_from_user(fds, ufds, nfds*sizeof(struct pollfd))) { - kfree(fds); - goto out; - } + if (copy_from_user(fds, ufds, size)) + goto out_fds; current->timeout = timeout; - - count = 0; - fdcount = do_poll(nfds, fds, wait); current->timeout = 0; /* OK, now copy the revents fields back to user space. */ fds1 = fds; - for(i=0; i < (int)nfds; i++, ufds++, fds++) { - __put_user(fds->revents, &ufds->revents); + for(i=0; i < (int)nfds; i++, ufds++, fds1++) { + __put_user(fds1->revents, &ufds->revents); } - kfree(fds1); + + err = fdcount; if (!fdcount && signal_pending(current)) err = -EINTR; - else - err = fdcount; + +out_fds: + kfree(fds); out: if (wait) { free_wait(&wait_table); diff --git a/include/asm-alpha/init.h b/include/asm-alpha/init.h index 7d769dfcd..9ce618965 100644 --- a/include/asm-alpha/init.h +++ b/include/asm-alpha/init.h @@ -7,6 +7,12 @@ __arginit __init; \ __arginit +#if __GNUC__ >= 2 && __GNUC_MINOR__ >= 8 +#define __initlocaldata __initdata +#else +#define __initlocaldata +#endif + /* For assembly routines */ #define __INIT .section .text.init,"ax" #define __FINIT .previous diff --git a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h index cef856f24..64361c234 100644 --- a/include/asm-alpha/unistd.h +++ b/include/asm-alpha/unistd.h @@ -421,6 +421,12 @@ static inline int read(int fd, char * buf, int nr) return sys_read(fd, buf, nr); } +extern int sys_fork(void); +static inline int fork(void) +{ + return sys_fork(); +} + extern int __kernel_execve(char *, char **, char **, struct pt_regs *); static inline int execve(char * file, char ** argvp, char ** envp) { @@ -452,6 +458,12 @@ static inline pid_t wait(int * wait_stat) return waitpid(-1,wait_stat,0); } +extern int sys_delete_module(const char *name); +static inline int delete_module(const char *name) +{ + return sys_delete_module(name); +} + #endif #endif /* _ALPHA_UNISTD_H */ diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index 31b407dbb..9bcf62793 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -325,6 +325,7 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode); static inline _syscall1(int,close,int,fd); static inline _syscall1(int,_exit,int,exitcode); static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options); +static inline _syscall1(int,delete_module,const char *,name) static inline pid_t wait(int * wait_stat) { diff --git a/include/asm-i386/string.h b/include/asm-i386/string.h index a2947ce3e..5370dbb6a 100644 --- a/include/asm-i386/string.h +++ b/include/asm-i386/string.h @@ -114,7 +114,7 @@ __asm__ __volatile__( "xorl %%eax,%%eax\n\t" "jmp 3f\n" "2:\tsbbl %%eax,%%eax\n\t" - "orb $1,%%eax\n" + "orb $1,%%al\n" "3:" :"=a" (__res):"S" (cs),"D" (ct):"si","di"); return __res; diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 53766f701..068be0f29 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -296,6 +296,7 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall1(int,delete_module,const char *,name) static inline pid_t wait(int * wait_stat) { diff --git a/include/asm-m68k/amifd.h b/include/asm-m68k/amifd.h index 0c13747c3..ea21e69fd 100644 --- a/include/asm-m68k/amifd.h +++ b/include/asm-m68k/amifd.h @@ -5,21 +5,23 @@ #include <linux/fd.h> -#define FD_MAX_UNITS 4 +#define FD_MAX_UNITS 4 /* Max. Number of drives */ +#define FLOPPY_MAX_SECTORS 22 /* Max. Number of sectors per track */ + +#ifndef ASSEMBLER struct fd_data_type { char *name; /* description of data type */ int sects; /* sectors per track */ #ifdef __STDC__ - int (*read_fkt)(int, unsigned char *, unsigned long, int); - void (*write_fkt)(int, unsigned long, unsigned char *, int); + int (*read_fkt)(int); + void (*write_fkt)(int); #else int (*read_fkt)(); /* read whole track */ void (*write_fkt)(); /* write whole track */ #endif }; -#ifndef ASSEMBLER /* ** Floppy type descriptions */ @@ -43,13 +45,15 @@ struct amiga_floppy_struct { struct fd_drive_type *type; /* type of floppy for this unit */ struct fd_data_type *dtype; /* type of floppy for this unit */ int track; /* current track (-1 == unknown) */ + unsigned char *trackbuf; /* current track (kmaloc()'d */ int blocks; /* total # blocks on disk */ - int sects; /* number of sectors per track */ + int changed; /* true when not known */ int disk; /* disk in drive (-1 == unknown) */ int motor; /* true when motor is at speed */ int busy; /* true when drive is active */ + int dirty; /* true when trackbuf is not on disk */ int status; /* current error code for unit */ }; #endif diff --git a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h index bf155740f..0a0222ac5 100644 --- a/include/asm-m68k/ide.h +++ b/include/asm-m68k/ide.h @@ -406,7 +406,7 @@ static __inline__ void ide_get_lock (int *ide_lock, void (*handler)(int, void *, #endif /* CONFIG_ATARI */ } -#define ide_ack_intr(hwif) (hwif)->ack_intr((hwif)) +#define ide_ack_intr(hwif) ((hwif)->ack_intr ? (hwif)->ack_intr(hwif) : 1) /* * On the Atari, we sometimes can't enable interrupts: diff --git a/include/asm-m68k/setup.h b/include/asm-m68k/setup.h index 0255ca97f..16cd23c12 100644 --- a/include/asm-m68k/setup.h +++ b/include/asm-m68k/setup.h @@ -131,7 +131,7 @@ extern u_long m68k_machtype; * * CPU_68020 == MMU_68851 * CPU_68030 == MMU_68030 - * CPU_68040 == FPU_68040 == MMU_68040 (not strictly, think of 68LC040!) + * CPU_68040 == FPU_68040 == MMU_68040 * CPU_68060 == FPU_68060 == MMU_68060 */ diff --git a/include/asm-m68k/signal.h b/include/asm-m68k/signal.h index 1f3466aff..5fa3fa67e 100644 --- a/include/asm-m68k/signal.h +++ b/include/asm-m68k/signal.h @@ -128,6 +128,13 @@ struct old_sigaction { void (*sa_restorer)(void); }; +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + struct k_sigaction { struct sigaction sa; }; @@ -173,9 +180,9 @@ extern __inline__ int __const_sigismember(sigset_t *set, int _sig) extern __inline__ int __gen_sigismember(sigset_t *set, int _sig) { - char ret; - __asm__("bftst %1{%2,#1}\n\t sne %0" - : "=rm"(ret) : "m"(*set), "id"((_sig-1) ^ 31) : "cc"); + int ret; + __asm__("bfextu %1{%2,#1},%0" + : "=d"(ret) : "m"(*set), "id"((_sig-1) ^ 31)); return ret; } diff --git a/include/asm-m68k/unistd.h b/include/asm-m68k/unistd.h index 7c5226b95..f82743219 100644 --- a/include/asm-m68k/unistd.h +++ b/include/asm-m68k/unistd.h @@ -316,6 +316,7 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall1(int,delete_module,const char *,name) /* * This is the mechanism for creating a new kernel thread. diff --git a/include/asm-mips/init.h b/include/asm-mips/init.h index cd963cb98..3dd2f614d 100644 --- a/include/asm-mips/init.h +++ b/include/asm-mips/init.h @@ -1,14 +1,30 @@ -#ifndef __ASM_MIPS_INIT_H -#define __ASM_MIPS_INIT_H +/* + * include/asm-mips/init.h + * + * 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. + * + * $Id: init.h,v 1.2 1998/03/27 08:54:04 ralf Exp $ + */ +#ifndef __MIPS_INIT_H +#define __MIPS_INIT_H -/* Throwing the initialization code and data out is not supported yet... */ +#define __init __attribute__ ((__section__ (".text.init"))) +#define __initdata __attribute__ ((__section__ (".data.init"))) +#define __initfunc(__arginit) \ + __arginit __init; \ + __arginit + +#if __GNUC__ >= 2 && __GNUC_MINOR__ >= 8 +#define __initlocaldata __initdata +#else +#define __initlocaldata +#endif -#define __init -#define __initdata -#define __initfunc(__arginit) __arginit /* For assembly routines */ -#define __INIT -#define __FINIT -#define __INITDATA +#define __INIT .section .text.init,"ax" +#define __FINIT .previous +#define __INITDATA .section .data.init,"a" -#endif /* __ASM_MIPS_INIT_H */ +#endif /* __MIPS_INIT_H */ diff --git a/include/asm-mips/offset.h b/include/asm-mips/offset.h index 3c6d274ce..e211de3cc 100644 --- a/include/asm-mips/offset.h +++ b/include/asm-mips/offset.h @@ -50,7 +50,7 @@ #define TASK_PRIORITY 56 #define TASK_FLAGS 4 #define TASK_SIGPENDING 8 -#define TASK_MM 928 +#define TASK_MM 920 /* MIPS specific thread_struct offsets. */ #define THREAD_REG16 568 @@ -69,12 +69,11 @@ #define THREAD_BVADDR 880 #define THREAD_ECODE 884 #define THREAD_TRAPNO 888 -#define THREAD_KSP 892 -#define THREAD_PGDIR 896 -#define THREAD_MFLAGS 900 -#define THREAD_CURDS 904 -#define THREAD_TRAMP 908 -#define THREAD_OLDCTX 912 +#define THREAD_PGDIR 892 +#define THREAD_MFLAGS 896 +#define THREAD_CURDS 900 +#define THREAD_TRAMP 904 +#define THREAD_OLDCTX 908 /* Linux mm_struct offsets. */ #define MM_COUNT 12 diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 84d6830a7..765208d3e 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -5,7 +5,7 @@ * written by Ralf Baechle * Modified further for R[236]000 compatibility by Paul M. Antoine * - * $Id: processor.h,v 1.11 1998/03/22 20:43:52 ralf Exp $ + * $Id: processor.h,v 1.8 1998/03/27 04:47:59 ralf Exp $ */ #ifndef __ASM_MIPS_PROCESSOR_H #define __ASM_MIPS_PROCESSOR_H @@ -104,7 +104,6 @@ struct thread_struct { unsigned long cp0_badvaddr; unsigned long error_code; unsigned long trap_no; - unsigned long ksp; /* Top of kernel stack */ unsigned long pg_dir; /* used in tlb refill */ #define MF_FIXADE 1 /* Fix address errors in software */ #define MF_LOGADE 2 /* Log address errors to syslog */ @@ -136,8 +135,7 @@ struct thread_struct { /* \ * Other stuff associated with the process \ */ \ - 0, 0, 0, (unsigned long)&init_task_union + KERNEL_STACK_SIZE - 32, \ - (unsigned long) swapper_pg_dir, \ + 0, 0, 0, (unsigned long) swapper_pg_dir, \ /* \ * For now the default is to fix address errors \ */ \ diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index 91202c955..5cc743dd9 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -1427,6 +1427,7 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall1(int,delete_module,const char *,name) static inline pid_t wait(int * wait_stat) { diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h index df519f28f..f5425f51b 100644 --- a/include/asm-sparc/unistd.h +++ b/include/asm-sparc/unistd.h @@ -438,6 +438,7 @@ static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) static __inline__ _syscall1(int,close,int,fd) static __inline__ _syscall1(int,_exit,int,exitcode) static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static __inline__ _syscall1(int,delete_module,const char *,name) static __inline__ pid_t wait(int * wait_stat) { diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index 1983c79d9..67a3c4108 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -426,6 +426,7 @@ static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) static __inline__ _syscall1(int,close,int,fd) static __inline__ _syscall1(int,_exit,int,exitcode) static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static __inline__ _syscall1(int,delete_module,const char *,name) static __inline__ pid_t wait(int * wait_stat) { diff --git a/include/linux/fb.h b/include/linux/fb.h index 9ed7546d5..60b6b7727 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1,16 +1,31 @@ #ifndef _LINUX_FB_H #define _LINUX_FB_H +#include <asm/types.h> + /* Definitions of frame buffers */ +#define FB_MAJOR 29 + +#define FB_MODES_SHIFT 5 /* 32 modes per framebuffer */ +#define FB_NUM_MINORS 256 /* 256 Minors */ +#define FB_MAX (FB_NUM_MINORS / (1 << FB_MODES_SHIFT)) +#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT) + /* ioctls 0x46 is 'F' */ -#define FBIOGET_VSCREENINFO 0x4600 -#define FBIOPUT_VSCREENINFO 0x4601 -#define FBIOGET_FSCREENINFO 0x4602 +#define FBIOGET_VSCREENINFO 0x4600 +#define FBIOPUT_VSCREENINFO 0x4601 +#define FBIOGET_FSCREENINFO 0x4602 #define FBIOGETCMAP 0x4604 #define FBIOPUTCMAP 0x4605 -#define FBIOPAN_DISPLAY 0x4606 +#define FBIOPAN_DISPLAY 0x4606 +/* 0x4607-0x460B are defined below */ +/* #define FBIOGET_MONITORSPEC 0x460C */ +/* #define FBIOPUT_MONITORSPEC 0x460D */ +/* #define FBIOSWITCH_MONIBIT 0x460E */ +#define FBIOGET_CON2FBMAP 0x460F +#define FBIOPUT_CON2FBMAP 0x4610 #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ #define FB_TYPE_PLANES 1 /* Non interleaved planes */ @@ -26,22 +41,25 @@ struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ - unsigned long smem_start; /* Start of frame buffer mem */ - unsigned long smem_len; /* Length of frame buffer mem */ - int type; /* see FB_TYPE_* */ - int type_aux; /* Interleave for interleaved Planes */ - int visual; /* see FB_VISUAL_* */ - u_short xpanstep; /* zero if no hardware panning */ - u_short ypanstep; /* zero if no hardware panning */ - u_short ywrapstep; /* zero if no hardware ywrap */ - u_long line_length; /* length of a line in bytes */ - short reserved[9]; /* Reserved for future compatibility */ + char *smem_start; /* Start of frame buffer mem */ + __u32 smem_len; /* Length of frame buffer mem */ + __u32 type; /* see FB_TYPE_* */ + __u32 type_aux; /* Interleave for interleaved Planes */ + __u32 visual; /* see FB_VISUAL_* */ + __u16 xpanstep; /* zero if no hardware panning */ + __u16 ypanstep; /* zero if no hardware panning */ + __u16 ywrapstep; /* zero if no hardware ywrap */ + __u32 line_length; /* length of a line in bytes */ + unsigned char *mmio_start; /* Start of Memory Mapped I/O */ + __u32 mmio_len; /* Length of Memory Mapped I/O */ + __u32 accel; /* Type of acceleration available */ + __u16 reserved[3]; /* Reserved for future compatibility */ }; struct fb_bitfield { - int offset; /* beginning of bitfield */ - int length; /* length of bitfield */ - int msb_right; /* != 0 : Most significant bit is */ + __u32 offset; /* beginning of bitfield */ + __u32 length; /* length of bitfield */ + __u32 msb_right; /* != 0 : Most significant bit is */ /* right */ }; @@ -58,8 +76,12 @@ struct fb_bitfield { #define FB_ACCEL_NONE 0 /* no hardware accelerator */ #define FB_ACCEL_ATARIBLITT 1 /* Atari Blitter */ #define FB_ACCEL_AMIGABLITT 2 /* Amiga Blitter */ -#define FB_ACCEL_CYBERVISION 3 /* Cybervision64 (S3 Trio64) */ -#define FB_ACCEL_RETINAZ3 4 /* RetinaZ3 (NCR77C32BLT) */ +#define FB_ACCEL_S3TRIO64 3 /* Cybervision64 (S3 Trio64) */ +#define FB_ACCEL_NCR77C32BLT 4 /* RetinaZ3 (NCR77C32BLT) */ +#define FB_ACCEL_S3VIRGE 5 /* Cybervision64/3D (S3 ViRGE) */ +#define FB_ACCEL_MACH64 6 /* ATI Mach 64 */ +#define FB_ACCEL_TGA 7 /* DEC 21030 TGA */ +#define FB_ACCEL_ATY 8 /* atyfb (ATI Mach64) */ #define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ #define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ @@ -68,9 +90,10 @@ struct fb_bitfield { #define FB_SYNC_BROADCAST 16 /* broadcast video timings */ /* vtotal = 144d/288n/576i => PAL */ /* vtotal = 121d/242n/484i => NTSC */ +#define FB_SYNC_ON_GREEN 32 /* sync on green */ #define FB_VMODE_NONINTERLACED 0 /* non interlaced */ -#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ #define FB_VMODE_DOUBLE 2 /* double scan */ #define FB_VMODE_MASK 255 @@ -79,77 +102,107 @@ struct fb_bitfield { #define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ struct fb_var_screeninfo { - int xres; /* visible resolution */ - int yres; - int xres_virtual; /* virtual resolution */ - int yres_virtual; - int xoffset; /* offset from virtual to visible */ - int yoffset; /* resolution */ + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 xres_virtual; /* virtual resolution */ + __u32 yres_virtual; + __u32 xoffset; /* offset from virtual to visible */ + __u32 yoffset; /* resolution */ - int bits_per_pixel; /* guess what */ - int grayscale; /* != 0 Graylevels instead of colors */ + __u32 bits_per_pixel; /* guess what */ + __u32 grayscale; /* != 0 Graylevels instead of colors */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ - int nonstd; /* != 0 Non standard pixel format */ + __u32 nonstd; /* != 0 Non standard pixel format */ - int activate; /* see FB_ACTIVATE_* */ + __u32 activate; /* see FB_ACTIVATE_* */ - int height; /* height of picture in mm */ - int width; /* width of picture in mm */ + __u32 height; /* height of picture in mm */ + __u32 width; /* width of picture in mm */ - int accel; /* see FB_ACCEL_* */ + __u32 accel; /* see FB_ACCEL_* */ /* Timing: All values in pixclocks, except pixclock (of course) */ - unsigned long pixclock; /* pixel clock in ps (pico seconds) */ - unsigned long left_margin; /* time from sync to picture */ - unsigned long right_margin; /* time from picture to sync */ - unsigned long upper_margin; /* time from sync to picture */ - unsigned long lower_margin; - unsigned long hsync_len; /* length of horizontal sync */ - unsigned long vsync_len; /* length of vertical sync */ - int sync; /* see FB_SYNC_* */ - int vmode; /* see FB_VMODE_* */ - int reserved[6]; /* Reserved for future compatibility */ + __u32 pixclock; /* pixel clock in ps (pico seconds) */ + __u32 left_margin; /* time from sync to picture */ + __u32 right_margin; /* time from picture to sync */ + __u32 upper_margin; /* time from sync to picture */ + __u32 lower_margin; + __u32 hsync_len; /* length of horizontal sync */ + __u32 vsync_len; /* length of vertical sync */ + __u32 sync; /* see FB_SYNC_* */ + __u32 vmode; /* see FB_VMODE_* */ + __u32 reserved[6]; /* Reserved for future compatibility */ }; struct fb_cmap { - int start; /* First entry */ - int len; /* Number of entries */ - unsigned short *red; /* Red values */ - unsigned short *green; - unsigned short *blue; - unsigned short *transp; /* transparency, can be NULL */ + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ + __u16 *red; /* Red values */ + __u16 *green; + __u16 *blue; + __u16 *transp; /* transparency, can be NULL */ +}; + +struct fb_con2fbmap { + __u32 console; + __u32 framebuffer; +}; + +struct fb_monspecs { + unsigned hfmin : 20; /* hfreq lower limit (Hz) */ + unsigned hfmax : 20; /* hfreq upper limit (Hz) */ + unsigned vfmin : 10; /* vfreq lower limit (Hz) */ + unsigned vfmax : 10; /* vfreq upper limit (Hz) */ + unsigned dpms : 1; /* supports DPMS */ }; #ifdef __KERNEL__ #include <linux/fs.h> + +struct fb_info; +struct fb_info_gen; + + + /* + * Frame buffer operations + */ + struct fb_ops { - /* get non settable parameters */ - int (*fb_get_fix) (struct fb_fix_screeninfo *, int); - /* get settable parameters */ - int (*fb_get_var) (struct fb_var_screeninfo *, int); - /* set settable parameters */ - int (*fb_set_var) (struct fb_var_screeninfo *, int); - /* get colormap */ - int (*fb_get_cmap) (struct fb_cmap *, int, int); - /* set colormap */ - int (*fb_set_cmap) (struct fb_cmap *, int, int); - /* pan display */ - int (*fb_pan_display) (struct fb_var_screeninfo *, int); - /* perform fb specific ioctl */ - int (*fb_ioctl)(struct inode *, struct file *, unsigned int, - unsigned long, int); + /* open/release and usage marking */ + int (*fb_open)(struct fb_info *info); + int (*fb_release)(struct fb_info *info); + /* get non settable parameters */ + int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); + /* get settable parameters */ + int (*fb_get_var)(struct fb_var_screeninfo *var, int con, + struct fb_info *info); + /* set settable parameters */ + int (*fb_set_var)(struct fb_var_screeninfo *var, int con, + struct fb_info *info); + /* get colormap */ + int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); + /* set colormap */ + int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); + /* pan display */ + int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, + struct fb_info *info); + /* switch between text and graphics mode */ + int (*fb_set_mode)(int mode, struct fb_info *info); + /* perform fb specific ioctl */ + int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, int con, struct fb_info *info); }; -int register_framebuffer(char *, int *, struct fb_ops *, int, - struct fb_var_screeninfo *); -int unregister_framebuffer(int); /* * This is the interface between the low-level console driver and the @@ -162,7 +215,7 @@ struct display { struct fb_var_screeninfo var; /* variable infos. yoffset and vmode */ /* are updated by fbcon.c */ struct fb_cmap cmap; /* colormap */ - u_char *screen_base; /* pointer to top of virtual screen */ + char *screen_base; /* pointer to top of virtual screen */ int visual; int type; /* see FB_TYPE_* */ int type_aux; /* Interleave for interleaved Planes */ @@ -181,6 +234,7 @@ struct display { /* Filled in by the low-level console driver */ struct vc_data *conp; /* pointer to console data */ + struct fb_info *fb_info; /* frame buffer for this console */ int vrows; /* number of virtual rows */ int cursor_x; /* current cursor position */ int cursor_y; @@ -199,22 +253,109 @@ struct display { struct fb_info { - char modename[40]; /* at boottime detected video mode */ - struct display *disp; /* pointer to display variables */ - char fontname[40]; /* default font name */ - int (*changevar)(int); /* tell console var has changed */ - int (*switch_con)(int); /* tell fb to switch consoles */ - int (*updatevar)(int); /* tell fb to update the vars */ - void (*blank)(int); /* tell fb to (un)blank the screen */ - int (*setcmap)(struct fb_cmap *, int); /* tell fb to set the colormap */ + char modename[40]; /* default video mode */ + int node; + struct fb_ops *fbops; + struct fb_monspecs monspecs; + struct display *disp; /* initial display variable */ + char fontname[40]; /* default font name */ + int (*changevar)(int); /* tell console var has changed */ + int (*switch_con)(int, struct fb_info*); + /* tell fb to switch consoles */ + int (*updatevar)(int, struct fb_info*); + /* tell fb to update the vars */ + void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */ + + /* From here on everything is device dependent */ +}; + + + /* + * This structure abstracts from the underlying hardware. It is not + * mandatory but used by the `generic' frame buffer operations. + * Read drivers/video/skeletonfb.c for more information. + */ + +struct fbgen_hwswitch { + void (*detect)(void); + int (*encode_fix)(struct fb_fix_screeninfo *fix, const void *par, + struct fb_info_gen *info); + int (*decode_var)(const struct fb_var_screeninfo *var, void *par, + struct fb_info_gen *info); + int (*encode_var)(struct fb_var_screeninfo *var, const void *par, + struct fb_info_gen *info); + void (*get_par)(void *par, struct fb_info_gen *info); + void (*set_par)(const void *par, struct fb_info_gen *info); + int (*getcolreg)(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, struct fb_info *info); + int (*setcolreg)(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info); + int (*pan_display)(const struct fb_var_screeninfo *var, + struct fb_info_gen *info); + int (*blank)(int blank_mode, struct fb_info_gen *info); + struct display_switch *(*get_dispsw)(const void *par, + struct fb_info_gen *info); +}; + +struct fb_info_gen { + struct fb_info info; + + /* Entries for a generic frame buffer device */ + /* Yes, this starts looking like C++ */ + u_int parsize; + struct fbgen_hwswitch *fbhw; + + /* From here on everything is device dependent */ }; + +struct fb_videomode { + const char *name; + struct fb_var_screeninfo var; +}; + + +/* prototypes */ +typedef unsigned long fb_init_func(unsigned long); + +/* drivers/char/fbmem.c */ +extern int register_framebuffer(struct fb_info *fb_info); +extern int unregister_framebuffer(const struct fb_info *fb_info); +extern unsigned long probe_framebuffers(unsigned long kmem_start); +extern int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal, + const struct fb_info *fb_info); +extern int fbmon_dpms(const struct fb_info *fb_info); + + +extern int num_registered_fb; +extern struct fb_info *registered_fb[FB_MAX]; +extern char con2fb_map[MAX_NR_CONSOLES]; + +/* drivers/video/fbcon.c */ +extern struct display fb_display[MAX_NR_CONSOLES]; + +/* drivers/video/fbcmap.c */ +extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); +extern void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, + int fsfromto); +extern int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, + int kspc, int (*getcolreg)(u_int, u_int *, u_int *, + u_int *, u_int *, + struct fb_info *), + struct fb_info *fb_info); +extern int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, + int kspc, int (*setcolreg)(u_int, u_int, u_int, u_int, + u_int, struct fb_info *), + struct fb_info *fb_info); +extern struct fb_cmap *fb_default_cmap(int len); +extern void fb_invert_cmaps(void); + #endif /* __KERNEL__ */ #if 1 -#define FBCMD_GET_CURRENTPAR 0xDEAD0005 -#define FBCMD_SET_CURRENTPAR 0xDEAD8005 +#define FBCMD_GET_CURRENTPAR 0xDEAD0005 +#define FBCMD_SET_CURRENTPAR 0xDEAD8005 #endif @@ -233,26 +374,26 @@ struct fb_info { struct fb_fix_cursorinfo { - u_short crsr_width; /* width and height of the cursor in */ - u_short crsr_height; /* pixels (zero if no cursor) */ - u_short crsr_xsize; /* cursor size in display pixels */ - u_short crsr_ysize; - u_short crsr_color1; /* colormap entry for cursor color1 */ - u_short crsr_color2; /* colormap entry for cursor color2 */ + __u16 crsr_width; /* width and height of the cursor in */ + __u16 crsr_height; /* pixels (zero if no cursor) */ + __u16 crsr_xsize; /* cursor size in display pixels */ + __u16 crsr_ysize; + __u16 crsr_color1; /* colormap entry for cursor color1 */ + __u16 crsr_color2; /* colormap entry for cursor color2 */ }; struct fb_var_cursorinfo { - u_short width; - u_short height; - u_short xspot; - u_short yspot; - u_char data[1]; /* field with [height][width] */ + __u16 width; + __u16 height; + __u16 xspot; + __u16 yspot; + __u8 data[1]; /* field with [height][width] */ }; struct fb_cursorstate { - short xoffset; - short yoffset; - u_short mode; + __s16 xoffset; + __s16 yoffset; + __u16 mode; }; #define FB_CURSOR_OFF 0 @@ -267,24 +408,23 @@ struct fb_cursorstate { #define FB_LINE_FILLED 4 struct fb_line { - int start_x; - int start_y; - int end_x; - int end_y; - int color; - int option; + __s32 start_x; + __s32 start_y; + __s32 end_x; + __s32 end_y; + __u32 color; + __u32 option; }; struct fb_move { - int src_x; - int src_y; - int dest_x; - int dest_y; - int height; - int width; + __s32 src_x; + __s32 src_y; + __s32 dest_x; + __s32 dest_y; + __u32 height; + __u32 width; }; #endif /* Preliminary */ - #endif /* _LINUX_FB_H */ diff --git a/include/linux/file.h b/include/linux/file.h index 240a5039c..fe7ca60c9 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -9,13 +9,26 @@ extern int __fput(struct file *); extern void insert_file_free(struct file *file); /* + * Check whether the specified task has the fd open. Since the task + * may not have a files_struct, we must test for p->files != NULL. + */ +extern inline struct file * fcheck_task(struct task_struct *p, unsigned int fd) +{ + struct file * file = NULL; + + if (p->files && fd < p->files->max_fds) + file = p->files->fd[fd]; + return file; +} + +/* * Check whether the specified fd has an open file. */ extern inline struct file * fcheck(unsigned int fd) { struct file * file = NULL; - if (fd < NR_OPEN) + if (fd < current->files->max_fds) file = current->files->fd[fd]; return file; } diff --git a/include/linux/ip_fw.h b/include/linux/ip_fw.h index 6f9bc510a..0334cb4af 100644 --- a/include/linux/ip_fw.h +++ b/include/linux/ip_fw.h @@ -52,11 +52,13 @@ #ifndef _IP_FW_H #define _IP_FW_H +#ifdef __KERNEL__ #include <linux/icmp.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> +#endif struct ip_fw { diff --git a/include/linux/ipv6_route.h b/include/linux/ipv6_route.h index 41591e876..7fc11c7ce 100644 --- a/include/linux/ipv6_route.h +++ b/include/linux/ipv6_route.h @@ -28,6 +28,7 @@ enum #define RTF_LINKRT 0x00100000 /* link specific - device match */ #define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */ +#define RTF_EXPIRES 0x00400000 #define RTF_CACHE 0x01000000 /* cache entry */ #define RTF_FLOW 0x02000000 /* flow significant route */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index c6646ddd1..e3d5f237b 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -8,7 +8,9 @@ #define AMIGAMOUSE_MINOR 4 #define ATARIMOUSE_MINOR 5 #define SUN_MOUSE_MINOR 6 +#define APOLLO_MOUSE_MINOR 7 #define PC110PAD_MINOR 9 +#define MAC_MOUSE_MINOR 10 #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ #define RTC_MINOR 135 diff --git a/include/linux/mm.h b/include/linux/mm.h index 11d04d453..2140eac53 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -251,7 +251,23 @@ extern inline unsigned long get_free_page(int gfp_mask) } /* memory.c & swap.c*/ -extern int free_memory_available(void); + +/* + * This traverses "nr" memory size lists, + * and returns true if there is enough memory. + * + * For example, we want to keep on waking up + * kswapd every once in a while until the highest + * memory order has an entry (ie nr == 0), but + * we want to do it in the background. + * + * We want to do it in the foreground only if + * none of the three highest lists have enough + * memory. Random number. + */ +extern int free_memory_available(int nr); +#define kswapd_continue() (!free_memory_available(3)) +#define kswapd_wakeup() (!free_memory_available(0)) #define free_page(addr) free_pages((addr),0) extern void FASTCALL(free_pages(unsigned long addr, unsigned long order)); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d1c005c70..e795ccb49 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -355,7 +355,6 @@ extern __inline__ int unregister_gifconf(unsigned int family) #define HAVE_NETIF_RX 1 extern void netif_rx(struct sk_buff *skb); extern void net_bh(void); -extern void dev_tint(struct device *dev); extern int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy); extern int dev_ioctl(unsigned int cmd, void *); extern int dev_change_flags(struct device *, unsigned); diff --git a/include/linux/nubus.h b/include/linux/nubus.h new file mode 100644 index 000000000..c09b95a93 --- /dev/null +++ b/include/linux/nubus.h @@ -0,0 +1,96 @@ + +struct nubus_slot +{ + int slot_flags; +#define NUBUS_DEVICE_PRESENT 1 +#define NUBUS_DEVICE_ACTIVE 2 +#define NUBUS_DEVICE_IRQ 4 + __u32 slot_directory; + __u32 slot_dlength; + __u32 slot_crc; + __u8 slot_rev; + __u8 slot_format; + __u8 slot_lanes; + /* + * Stuff we pulled from the directory + */ + __u32 slot_dirbase; + __u32 slot_thisdir; + char slot_vendor[64]; + char slot_cardname[64]; +}; + +struct nbnamevec +{ + char *name; + int id; +}; + +struct nubus_dir +{ + unsigned char *base; + int length; + int count; + int mask; +}; + +struct nubus_dirent +{ + unsigned char type; + int value; /* Actually 24bits used */ + int mask; + int base; /* For dirptr function */ +}; + +struct nubus_type +{ + __u16 category; + __u16 type; + __u16 DrHW; + __u16 DrSW; +}; + +#define NUBUS_CAT_BOARD 0x0001 +#define NUBUS_CAT_DISPLAY 0x0003 +#define NUBUS_CAT_NETWORK 0x0004 +#define NUBUS_CAT_COMMUNICATIONS 0x0006 +#define NUBUS_CAT_FONT 0x0009 +#define NUBUS_CAT_CPU 0x000A + +#define RES_ID_TYPE 0x0001 +#define RES_ID_NAME 0x0002 +#define RES_ID_BOARD_DIR 0x0001 +#define RES_ID_FLAGS 0x0007 + +struct nubus_device_specifier +{ + int (*setup)(struct nubus_device_specifier *, int slot, struct nubus_type *); + struct nubus_device_specifier *next; +}; + + +extern void register_nubus_device(struct nubus_device_specifier *nb); +extern void unregister_nubus_device(struct nubus_device_specifier *nb); + +extern struct nubus_dir *nubus_openrootdir(int slot); +extern struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d); +extern void nubus_closedir(struct nubus_dir *); +extern struct nubus_dirent *nubus_readdir(struct nubus_dir *); +extern unsigned char *nubus_dirptr(struct nubus_dirent *d); +extern void nubus_strncpy(int slot, void *to, unsigned char *p, int len); +extern void nubus_memcpy(int slot, void *to, unsigned char *p, int len); +extern void nubus_init(void); +extern void nubus_sweep_video(void); +extern int nubus_ethernet_addr(int slot, unsigned char *addr); + +extern __inline void *nubus_slot_addr(int slot) +{ + return (void *)(0xF0000000|(slot<<24)); +} + +extern int nubus_hwreg_present(volatile void *ptr); + +extern void nubus_init_via(void); +extern int nubus_free_irq(int slot); +extern int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *)); + diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 8c6467010..7b6b3af7e 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -5,7 +5,7 @@ #include <linux/netlink.h> #define RTNL_DEBUG 1 -#define CONFIG_RTNL_OLD_IFINFO 1 +/* #define CONFIG_RTNL_OLD_IFINFO 1 */ /**** diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e19a95fec..d94b40bcc 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -19,9 +19,11 @@ #include <asm/atomic.h> #include <asm/types.h> +#include <asm/spinlock.h> #define HAVE_ALLOC_SKB /* For the drivers to know */ #define HAVE_ALIGNABLE_SKB /* Ditto 8) */ +#define SLAB_SKB /* Slabified skbuffs */ #define CHECKSUM_NONE 0 #define CHECKSUM_HW 1 @@ -88,27 +90,27 @@ struct sk_buff unsigned int len; /* Length of actual data */ unsigned int csum; /* Checksum */ - volatile char used; - unsigned char tries, /* Times tried */ - inclone, /* Inline clone */ + volatile char used; /* Data moved to user and not MSG_PEEK */ + unsigned char is_clone, /* We are a clone */ + cloned, /* head may be cloned (check refcnt to be sure). */ pkt_type, /* Packet class */ pkt_bridged, /* Tracker for bridging */ ip_summed; /* Driver fed us an IP checksum */ - __u32 priority; + __u32 priority; /* Packet queueing priority */ atomic_t users; /* User count - see datagram.c,tcp.c */ unsigned short protocol; /* Packet protocol from driver. */ unsigned short security; /* Security level of packet */ unsigned int truesize; /* Buffer size */ +#ifndef SLAB_SKB atomic_t count; /* reference count */ struct sk_buff *data_skb; /* Link to the actual data skb */ +#endif unsigned char *head; /* Head of buffer */ unsigned char *data; /* Data head pointer */ unsigned char *tail; /* Tail pointer */ unsigned char *end; /* End pointer */ void (*destructor)(struct sk_buff *); /* Destruct function */ -#define SKB_CLONE_ORIG 1 -#define SKB_CLONE_INLINE 2 #if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) __u32 shapelatency; /* Latency on frame */ @@ -163,6 +165,12 @@ extern int skb_tailroom(struct sk_buff *skb); extern void skb_reserve(struct sk_buff *skb, unsigned int len); extern void skb_trim(struct sk_buff *skb, unsigned int len); +/* Internal */ +extern __inline__ atomic_t *skb_datarefp(struct sk_buff *skb) +{ + return (atomic_t *)(skb->end); +} + extern __inline__ int skb_queue_empty(struct sk_buff_head *list) { return (list->next == (struct sk_buff *) list); @@ -174,9 +182,16 @@ extern __inline__ void kfree_skb(struct sk_buff *skb) __kfree_skb(skb); } +/* Use this if you didn't touch the skb state [for fast switching] */ +extern __inline__ void kfree_skb_fast(struct sk_buff *skb) +{ + if (atomic_dec_and_test(&skb->users)) + kfree_skbmem(skb); +} + extern __inline__ int skb_cloned(struct sk_buff *skb) { - return (atomic_read(&skb->data_skb->count) != 1); + return skb->cloned && atomic_read(skb_datarefp(skb)) != 1; } extern __inline__ int skb_shared(struct sk_buff *skb) @@ -261,14 +276,15 @@ extern __inline__ void __skb_queue_head(struct sk_buff_head *list, struct sk_buf prev->next = newsk; } +extern spinlock_t skb_queue_lock; + extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); __skb_queue_head(list, newsk); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); } /* @@ -293,10 +309,9 @@ extern __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); __skb_queue_tail(list, newsk); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); } /* @@ -328,10 +343,9 @@ extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list) long flags; struct sk_buff *result; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); result = __skb_dequeue(list); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); return result; } @@ -358,10 +372,9 @@ extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); __skb_insert(newsk, old->prev, old, old->list); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); } /* @@ -372,10 +385,9 @@ extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); __skb_insert(newsk, old, old->next, old->list); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); } /* @@ -407,11 +419,10 @@ extern __inline__ void skb_unlink(struct sk_buff *skb) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); if(skb->list) __skb_unlink(skb, skb->list); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); } /* XXX: more streamlined implementation */ @@ -428,10 +439,9 @@ extern __inline__ struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list) long flags; struct sk_buff *result; - save_flags(flags); - cli(); + spin_lock_irqsave(&skb_queue_lock, flags); result = __skb_dequeue_tail(list); - restore_flags(flags); + spin_unlock_irqrestore(&skb_queue_lock, flags); return result; } @@ -451,7 +461,12 @@ extern __inline__ unsigned char *skb_put(struct sk_buff *skb, unsigned int len) if(skb->tail>skb->end) { __label__ here; +#if 1 + printk(KERN_DEBUG "skbput: over: %p:tail=%p:end=%p:len=%u\n", + &&here, skb->tail, skb->end, len); +#else panic(skb_put_errstr,&&here,len); +#endif here: ; } return tmp; @@ -543,5 +558,8 @@ extern int skb_copy_datagram(struct sk_buff *from, int offset, char *to,int si extern int skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size); extern void skb_free_datagram(struct sock * sk, struct sk_buff *skb); +extern void skb_init(void); +extern void skb_add_mtu(int mtu); + #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/include/linux/socket.h b/include/linux/socket.h index afff2fd5c..eccaa2f57 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -185,6 +185,8 @@ struct ucred { #define MSG_SYN 0x400 #define MSG_URG 0x800 #define MSG_RST 0x1000 +#define MSG_ERRQUEUE 0x2000 +#define MSG_NOSIGNAL 0x4000 #define MSG_CTLIGNORE 0x80000000 diff --git a/include/linux/swap.h b/include/linux/swap.h index 494490c32..48a0ca02c 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -39,7 +39,6 @@ extern atomic_t nr_async_pages; extern struct inode swapper_inode; extern unsigned long page_cache_size; extern int buffermem; -#define BUFFER_MEM ((buffermem >> PAGE_SHIFT) + page_cache_size) /* Incomplete types for prototype declarations: */ struct task_struct; @@ -123,6 +122,21 @@ static inline int is_page_shared(struct page *page) } /* + * When we're freeing pages from a user application, we want + * to cluster swapouts too. -- Rik. + * linux/mm/page_alloc.c + */ +static inline int try_to_free_pages(int gfp_mask, int count) +{ + int retval = 0; + while (count--) { + if (try_to_free_page(gfp_mask)) + retval = 1; + } + return retval; +} + +/* * Make these inline later once they are working properly. */ extern long find_in_swap_cache(struct page *page); diff --git a/include/linux/swapctl.h b/include/linux/swapctl.h index cc169d2da..bf787614b 100644 --- a/include/linux/swapctl.h +++ b/include/linux/swapctl.h @@ -39,6 +39,7 @@ typedef struct buffer_mem_v1 } buffer_mem_v1; typedef buffer_mem_v1 buffer_mem_t; extern buffer_mem_t buffer_mem; +extern buffer_mem_t page_cache; typedef struct freepages_v1 { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index b7550ba2c..3a8f31e77 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -84,7 +84,8 @@ enum VM_FREEPG, /* struct: Set free page thresholds */ VM_BDFLUSH, /* struct: Control buffer cache flushing */ VM_OVERCOMMIT_MEMORY, /* Turn off the virtual memory safety limit */ - VM_BUFFERMEM /* struct: Set cache memory thresholds */ + VM_BUFFERMEM, /* struct: Set buffer memory thresholds */ + VM_PAGECACHE /* struct: Set cache memory thresholds */ }; @@ -151,6 +152,7 @@ enum NET_IPV4_TCP_HOE_RETRANSMITS=32, NET_IPV4_TCP_TIMESTAMPS, NET_IPV4_TCP_WINDOW_SCALING, + NET_IPV4_TCP_SACK, NET_IPV4_TCP_VEGAS_CONG_AVOID, NET_IPV4_DEFAULT_TTL, NET_IPV4_AUTOCONFIG, @@ -196,6 +198,7 @@ enum { NET_IPV4_ROUTE_REDIRECT_SILENCE, NET_IPV4_ROUTE_ERROR_COST, NET_IPV4_ROUTE_ERROR_BURST, + NET_IPV4_ROUTE_GC_ELASTICITY, }; enum @@ -235,6 +238,7 @@ enum { NET_IPV6_ROUTE_GC_MIN_INTERVAL, NET_IPV6_ROUTE_GC_TIMEOUT, NET_IPV6_ROUTE_GC_INTERVAL, + NET_IPV6_ROUTE_GC_ELASTICITY, }; enum { diff --git a/include/linux/timer.h b/include/linux/timer.h index 7d11fd246..2649014a1 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -46,7 +46,7 @@ extern struct timer_struct timer_table[32]; * to distinguish between the different invocations. */ struct timer_list { - struct timer_list *next; + struct timer_list *next; /* MUST be first element */ struct timer_list *prev; unsigned long expires; unsigned long data; @@ -56,6 +56,13 @@ struct timer_list { extern void add_timer(struct timer_list * timer); extern int del_timer(struct timer_list * timer); +/* + * mod_timer is a more efficient way to update the expire field of an + * active timer (if the timer is inactive it will be activated) + * mod_timer(a,b) is equivalent to del_timer(a); a->expires = b; add_timer(a) + */ +void mod_timer(struct timer_list *timer, unsigned long expires); + extern void it_real_fn(unsigned long); extern inline void init_timer(struct timer_list * timer) diff --git a/include/linux/zorro.h b/include/linux/zorro.h index 95148b35d..05e62462a 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -1,503 +1,651 @@ /* - * linux/zorro.h -- Amiga AutoConfig (Zorro) Expansion Device Definitions + * linux/zorro.h -- Amiga AutoConfig (Zorro) Expansion Device Definitions * - * Copyright (C) 1995 Geert Uytterhoeven + * Copyright (C) 1995 Geert Uytterhoeven * - * 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. - */ - -#ifndef __ZORRO_H -#define __ZORRO_H - -#ifndef __ASSEMBLY__ - -/* - * Defined Board Manufacturers + * Please update arch/m68k/amiga/zorro.c if you make changes here! * - * Please update arch/m68k/amiga/zorro.c if you make changes here - * Many IDs were obtained from ExpName/Identify ((C) Richard Körber) - * and by looking at the NetBSD-Amiga kernel sources + * Many IDs were obtained from ExpName/Identify ((C) Richard Körber) + * and by looking at the NetBSD-Amiga kernel sources + * + * 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. */ -#define MANUF_PACIFIC (0x00D3) /* Pacific Peripherals */ -#define PROD_SE_2000_A500 (0x00) /* SE 2000 A500 */ -#define PROD_PACIFIC_HD (0x0A) /* HD Controller */ - -#define MANUF_KUPKE (0x00DD) /* Kupke */ -#define PROD_GOLEM_BOX_2 (0x00) /* Golem RAM Box 2MB */ - -#define MANUF_MEMPHIS (0x0100) /* Memphis */ -#define PROD_STORMBRINGER (0x00) /* Stormbringer */ - -#define MANUF_3_STATE (0x0200) /* 3-State */ -#define PROD_MEGAMIX_2000 (0x02) /* Megamix 2000 RAM */ - -#define MANUF_COMMODORE2 (0x0201) /* Commodore Braunschweig */ -#define PROD_A2088 (0x01) /* CBM A2088 XT Bridgeboard */ -#define PROD_A2286 (0x02) /* CBM A2286 AT Bridgeboard */ -#define PROD_A4091_2 (0x54) /* CBM A4091 SCSI Controller */ -#define PROD_A2386SX (0x67) /* CBM A2386-SX Bridgeboard */ - -#define MANUF_COMMODORE (0x0202) /* Commodore West Chester */ -#define PROD_A2090A (0x01) /* CBM A2090/A2090A HD Controller */ -#define PROD_A590 (0x02) /* CBM A590 SCSI Controller */ -#define PROD_A2091 (0x03) /* CBM A2091 SCSI Controller */ -#define PROD_A2090B (0x04) /* CBM A2090B 2090 Autoboot Card */ -#define PROD_ARCNET (0x09) /* CBM A2060 Arcnet Card */ -#define PROD_CBMRAM (0x0A) /* CBM A2052/58.RAM | 590/2091.RAM */ -#define PROD_A560RAM (0x20) /* CBM A560 Memory Module */ -#define PROD_A2232PROTO (0x45) /* CBM A2232 Serial Prototype */ -#define PROD_A2232 (0x46) /* CBM A2232 Serial Production */ -#define PROD_A2620 (0x50) /* CBM A2620 68020/RAM Card */ -#define PROD_A2630 (0x51) /* CBM A2630 68030/RAM Card */ -#define PROD_A4091 (0x54) /* CBM A4091 SCSI Controller */ -#define PROD_A2065_2 (0x5A) /* A2065 Ethernet Card */ -#define PROD_ROMULATOR (0x60) /* CBM Romulator Card */ -#define PROD_A3000TESTFIX (0x61) /* CBM A3000 Test Fixture */ -#define PROD_A2386SX_2 (0x67) /* A2386-SX Bridgeboard */ -#define PROD_A2065 (0x70) /* CBM A2065 Ethernet Card */ - -#define MANUF_COMMODORE3 (0x0203) /* Commodore West Chester */ -#define PROD_A2090A_CM (0x03) /* A2090A Combitec/MacroSystem */ - -#define MANUF_KCS (0x02FF) /* Kolff Computer Supplies */ -#define PROD_POWER_BOARD (0x00) /* KCS Power PC Board */ - -#define MANUF_CARDCO (0x03EC) /* Cardco */ -#define PROD_KRONOS_2000_SCSI (0x04) /* Kronos 2000 SCSI Controller */ -#define PROD_A1000_SCSI (0x0C) /* A1000 SCSI Controller */ -#define PROD_ESCORT_SCSI (0x0E) /* Escort SCSI Controller */ -#define PROD_CC_A2410 (0xF5) /* Cardco A2410 Hires Graphics Card */ - -#define MANUF_A_SQUARED (0x03ED) /* A-Squared */ -#define PROD_LIVE_2000 (0x01) /* Live! 2000 */ - -#define MANUF_COMSPEC (0x03EE) /* ComSpec Communications */ -#define PROD_AX2000 (0x01) /* AX2000 */ - -#define MANUF_ANAKIN (0x03F1) /* Anakin */ -#define PROD_EASYL (0x01) /* Easyl Tablet */ - -#define MANUF_MICROBOTICS (0x03F2) /* MicroBotics */ -#define PROD_STARBOARD_II (0x00) /* StarBoard II */ -#define PROD_STARDRIVE (0x02) /* StarDrive */ -#define PROD_8_UP_A (0x03) /* 8-Up (Rev A) */ -#define PROD_8_UP_Z (0x04) /* 8-Up (Rev Z) */ -#define PROD_DELTA_RAM (0x20) /* Delta Card RAM */ -#define PROD_8_STAR_RAM (0x40) /* 8-Star RAM */ -#define PROD_8_STAR (0x41) /* 8-Star */ -#define PROD_VXL_RAM (0x44) /* VXL RAM */ -#define PROD_VXL_30 (0x45) /* VXL-30 Turbo Board */ -#define PROD_DELTA (0x60) /* Delta Card */ -#define PROD_MBX_1200 (0x81) /* MBX 1200 */ -#define PROD_HARDFRAME_2000 (0x9E) /* Hardframe 2000 */ -#define PROD_MBX_1200_2 (0xC1) /* MBX 1200 */ - -#define MANUF_ACCESS (0x03F4) /* Access Associates */ - -#define MANUF_EXPANSION_TECH (0x03F6) /* Expansion Technologies */ - -#define MANUF_ASDG (0x03FF) /* ASDG */ -#define PROD_ASDG_MEMORY (0x01) /* Memory Expansion */ -#define PROD_ASDG_MEMORY_2 (0x02) /* Memory Expansion */ -#define PROD_LAN_ROVER (0xFE) /* Lan Rover Ethernet */ -#define PROD_TWIN_X (0xFF) /* Twin-X Serial Card */ - -#define MANUF_IMTRONICS (0x0404) /* Imtronics */ -#define PROD_HURRICANE_2800 (0x39) /* Hurricane 2800 68030 */ -#define PROD_HURRICANE_2800_2 (0x57) /* Hurricane 2800 68030 */ - -#define MANUF_UNIV_OF_LOWELL (0x0406) /* University of Lowell */ -#define PROD_A2410 (0x00) /* CBM A2410 Hires Graphics Card */ - -#define MANUF_AMERISTAR (0x041D) /* Ameristar */ -#define PROD_AMERISTAR2065 (0x01) /* A2065 Ethernet Card */ -#define PROD_A560 (0x09) /* Arcnet Card */ -#define PROD_A4066 (0x0A) /* A4066 Ethernet Card */ - -#define MANUF_SUPRA (0x0420) /* Supra */ -#define PROD_SUPRADRIVE_4x4 (0x01) /* SupraDrive 4x4 SCSI Controller */ -#define PROD_SUPRA_2000 (0x03) /* 2000 DMA HD */ -#define PROD_SUPRA_500 (0x05) /* 500 HD/RAM */ -#define PROD_SUPRA_500XP (0x09) /* 500XP/2000 RAM */ -#define PROD_SUPRA_500RX (0x0A) /* 500RX/2000 RAM */ -#define PROD_SUPRA_2400ZI (0x0B) /* 2400zi Modem */ -#define PROD_WORDSYNC (0x0C) /* Supra Wordsync SCSI Controller */ -#define PROD_WORDSYNC_II (0x0D) /* Supra Wordsync II SCSI Controller */ -#define PROD_SUPRA_2400ZIPLUS (0x10) /* 2400zi+ Modem */ - -#define MANUF_CSA (0x0422) /* Computer Systems Ass. */ -#define PROD_MAGNUM (0x11) /* Magnum 40 SCSI Controller */ -#define PROD_12GAUGE (0x15) /* 12 Gauge SCSI Controller */ - -#define MANUF_MTEC2 (0x0502) /* M-Tech */ -#define PROD_AT500_2 (0x03) /* AT500 RAM */ - -#define MANUF_GVP3 (0x06E1) /* Great Valley Products */ -#define PROD_IMPACT (0x08) /* Impact SCSI/Memory */ - -#define MANUF_BYTEBOX (0x07DA) /* ByteBox */ -#define PROD_BYTEBOX_A500 (0x00) /* A500 */ - -#define MANUF_HACKER (0x07DB) /* Test only: no product definitions */ - -#define MANUF_POWER_COMPUTING (0x07DC) /* Power Computing (DKB) */ -#define PROD_DKB_3128 (0x0E) /* DKB 3128 RAM */ -#define PROD_RAPID_FIRE (0x0F) /* Rapid Fire SCSI Controller */ -#define PROD_DKB_1202 (0x10) /* DKB 1202 RAM */ -#define PROD_VIPER_II_COBRA (0x12) /* Viper II Turbo Board (DKB Cobra) */ -#define PROD_WILDFIRE_060 (0x17) /* WildFire 060 Turbo Board */ -#define PROD_WILDFIRE_060_2 (0xFF) /* WildFire 060 Turbo Board */ - -#define MANUF_GVP (0x07E1) /* Great Valley Products */ -#define PROD_IMPACT_I_4K (0x01) /* Impact Series-I SCSI 4K */ -#define PROD_IMPACT_I_16K_2 (0x02) /* Impact Series-I SCSI 16K/2 */ -#define PROD_IMPACT_I_16K_3 (0x03) /* Impact Series-I SCSI 16K/3 */ -#define PROD_IMPACT_3001_IDE (0x08) /* Impact 3001 IDE */ -#define PROD_IMPACT_3001_RAM (0x09) /* Impact 3001 RAM */ -#define PROD_GVPIISCSI (0x0B) /* GVP Series II SCSI Controller */ -#define PROD_GVPIISCSI_2 (0x09) /* evidence that the driver works - for this product code also */ -#define PROD_GVPIIRAM (0x0A) /* GVP Series II RAM */ -#define PROD_GVP (0x0B) /* This code is used by a wide range of - GVP products - use the epc to - identify it correctly */ -#define PROD_GVP_A2000_030 (0x0D) /* GVP A2000 68030 Turbo Board */ -#define PROD_IMPACT_3001_IDE_2 (0x0D) /* Impact 3001 IDE */ -#define PROD_GFORCE_040_SCSI (0x16) /* GForce 040 with SCSI (new) */ -#define PROD_GVPIV_24 (0x20) /* GVP IV-24 Graphics Board */ -#define PROD_GFORCE_040 (0xFF) /* GForce 040 Turbo Board */ -/* #define PROD_GVPIO_EXT (0xFF)*/ /* GVP I/O Extender */ - -#define MANUF_SYNERGY (0x07E5) /* Synergy */ - -#define MANUF_XETEC (0x07E6) /* Xetec */ -#define PROD_FASTCARD_SCSI (0x01) /* FastCard SCSI Controller */ -#define PROD_FASTCARD_RAM (0x02) /* FastCard RAM */ - -#define MANUF_PPI (0x07EA) /* Progressive Peripherals Inc. */ -#define PROD_MERCURY (0x00) /* Mercury Turbo Board */ -#define PROD_PPS_A3000_040 (0x01) /* PP&S A3000 68040 Turbo Board */ -#define PROD_PPS_A2000_040 (0x69) /* PP&S A2000 68040 Turbo Board */ -#define PROD_ZEUS (0x96) /* Zeus SCSI Controller */ -#define PROD_PPS_A500_040 (0xBB) /* PP&S A500 68040 Turbo Board */ - -#define MANUF_XEBEC (0x07EC) /* Xebec */ - -#define MANUF_SPIRIT (0x07F2) /* Spirit */ -#define PROD_HDA_506 (0x04) /* HDA 506 Harddisk */ -#define PROD_OCTABYTE_RAM (0x06) /* OctaByte RAM */ - -#define MANUF_BSC (0x07FE) /* BSC */ -#define PROD_ALF_3_SCSI (0x03) /* BSC ALF 3 SCSI Controller */ - -#define MANUF_BSC3 (0x0801) /* BSC */ -#define PROD_ALF_2_SCSI (0x01) /* ALF 2 SCSI Controller */ -#define PROD_ALF_2_SCSI_2 (0x02) /* ALF 2 SCSI Controller */ -#define PROD_ALF_3_SCSI_2 (0x03) /* ALF 3 SCSI Controller */ - -#define MANUF_C_LTD (0x0802) /* C Ltd. */ -#define PROD_KRONOS_SCSI (0x04) /* Kronos SCSI Controller */ -#define PROD_A1000_SCSI_2 (0x0C) /* A1000 SCSI Controller */ - -#define MANUF_JOCHHEIM (0x0804) /* Jochheim */ -#define PROD_JOCHHEIM_RAM (0x01) /* Jochheim RAM */ - -#define MANUF_CHECKPOINT (0x0807) /* Checkpoint Technologies */ -#define PROD_SERIAL_SOLUTION (0x00) /* Serial Solution */ - -#define MANUF_ICD (0x0817) /* ICD */ -#define PROD_ADVANTAGE_2000 (0x01) /* Advantage 2000 SCSI Controller */ - -#define MANUF_KUPKE2 (0x0819) /* Kupke */ -#define PROD_KUPKE_SCSI_II (0x02) /* Golem SCSI-II Controller */ -#define PROD_GOLEM_BOX (0x03) /* Golem Box */ -#define PROD_KUPKE_TURBO (0x04) /* 030/882 Turbo Board */ -#define PROD_KUPKE_SCSI_AT (0x05) /* SCSI/AT Controller */ - -#define MANUF_GVP4 (0x081D) /* Great Valley Products */ -#define PROD_A2000_RAM8 (0x09) /* A2000-RAM8/2 */ - -#define MANUF_INTERWORKS_NET (0x081E) /* Interworks Network */ - -#define MANUF_HARDITAL (0x0820) /* Hardital Synthesis */ -#define PROD_TQM (0x14) /* TQM 68030+68882 Turbo Board */ - -#define MANUF_BSC2 (0x082C) /* BSC */ -#define PROD_OKTAGON_SCSI (0x05) /* BSC Oktagon 2008 SCSI Controller */ -#define PROD_TANDEM (0x06) /* BSC Tandem AT-2008/508 IDE */ -#define PROD_ALPHA_RAM_1200 (0x07) /* Alpha RAM 1200 */ -#define PROD_OKTAGON_RAM (0x08) /* BSC Oktagon 2008 RAM */ -#define PROD_MULTIFACE_I (0x10) /* Alfa Data MultiFace I */ -#define PROD_MULTIFACE_II (0x11) /* Alfa Data MultiFace II */ -#define PROD_MULTIFACE_III (0x12) /* Alfa Data MultiFace III */ -#define PROD_BSC_FRAEMBUFFER (0x20) /* Framebuffer */ -#define PROD_GRAFFITI_RAM (0x21) /* Graffiti Graphics Board */ -#define PROD_GRAFFITI_REG (0x22) -#define PROD_ISDN_MASTERCARD (0x40) /* BSC ISDN MasterCard */ -#define PROD_ISDN_MASTERCARD_2 (0x41) /* BSC ISDN MasterCard II */ - -#define MANUF_ADV_SYS_SOFT (0x0836) /* Advanced Systems & Software */ -#define PROD_NEXUS_SCSI (0x01) /* Nexus SCSI Controller */ -#define PROD_NEXUS_RAM (0x08) /* Nexus RAM */ - -#define MANUF_IMPULSE (0x0838) /* Impulse */ -#define PROD_FIRECRACKER_24 (0x00) /* FireCracker 24 */ - -#define MANUF_IVS (0x0840) /* IVS */ -#define PROD_GRANDSLAM_PIC_2 (0x02) /* GrandSlam PIC 2 RAM */ -#define PROD_GRANDSLAM_PIC_1 (0x04) /* GrandSlam PIC 1 RAM */ -#define PROD_IVS_OVERDRIVE (0x10) /* OverDrive HD */ -#define PROD_TRUMPCARD_CLASSIC (0x30) /* Trumpcard Classic SCSI Controller */ -#define PROD_TRUMPCARD_PRO (0x34) /* Trumpcard Pro SCSI Controller */ -#define PROD_META_4 (0x40) /* Meta-4 RAM */ -#define PROD_WAVETOOLS (0xBF) /* Wavetools Sound Board */ -#define PROD_VECTOR (0xF3) /* Vector SCSI Controller */ -#define PROD_VECTOR_2 (0xF4) /* Vector SCSI Controller */ - -#define MANUF_VECTOR (0x0841) /* Vector */ -#define PROD_CONNECTION (0xE3) /* Connection Serial IO */ - -#define MANUF_XPERT_PRODEV (0x0845) /* XPert/ProDev */ -#define PROD_VISIONA_RAM (0x01) /* Visiona Graphics Board */ -#define PROD_VISIONA_REG (0x02) -#define PROD_MERLIN_RAM (0x03) /* Merlin Graphics Board */ -#define PROD_MERLIN_REG (0x04) -#define PROD_MERLIN_REG_2 (0xC9) - -#define MANUF_HYDRA_SYSTEMS (0x0849) /* Hydra Systems */ -#define PROD_AMIGANET (0x01) /* Amiganet Board */ - -#define MANUF_SUNRIZE (0x084F) /* Sunrize Industries */ -#define PROD_AD1012 (0x01) /* AD1012 Sound Board */ -#define PROD_AD516 (0x02) /* AD516 Sound Board */ -#define PROD_DD512 (0x03) /* DD512 Sound Board */ - -#define MANUF_TRICERATOPS (0x0850) /* Triceratops */ -#define PROD_TRICERATOPS (0x01) /* Triceratops Multi I/O Board */ - -#define MANUF_APPLIED_MAGIC (0x0851) /* Applied Magic Inc */ -#define PROD_DMI_RESOLVER (0x01) /* DMI Resolver Graphics Board */ -#define PROD_DIGITAL_BCASTER (0x06) /* Digital Broadcaster */ - -#define MANUF_GFX_BASE (0x085E) /* GFX-Base */ -#define PROD_GDA_1_RAM (0x00) /* GDA-1 Graphics Board */ -#define PROD_GDA_1_REG (0x01) - -#define MANUF_ROCTEC (0x0860) /* RocTec */ -#define PROD_RH_800C (0x01) /* RH 800C Hard Disk Controller */ -#define PROD_RH_800C_RAM (0x01) /* RH 800C RAM */ - -#define MANUF_HELFRICH1 (0x0861) /* Helfrich */ -#define PROD_RAINBOW3 (0x21) /* Rainbow3 Graphics Board */ - -#define MANUF_SW_RESULT_ENTS (0x0866) /* Software Result Enterprises */ -#define PROD_GG2PLUS (0x01) /* GG2+ Bus Converter */ - -#define MANUF_MASOBOSHI (0x086D) /* Masoboshi */ -#define PROD_MASTER_CARD_RAM (0x03) /* Master Card RAM */ -#define PROD_MASTER_CARD_SCSI (0x04) /* Master Card SCSI Controller */ -#define PROD_MVD_819 (0x07) /* MVD 819 */ - -#define MANUF_VILLAGE_TRONIC (0x0877) /* Village Tronic */ -#define PROD_DOMINO_RAM (0x01) /* Domino Graphics Board */ -#define PROD_DOMINO_REG (0x02) -#define PROD_PICASSO_II_RAM (0x0B) /* Picasso II/II+ Graphics Board */ -#define PROD_PICASSO_II_REG (0x0C) -#define PROD_PICASSO_II_SEGM (0x0D) /* Picasso II/II+ (Segmented Mode) */ -#define PROD_PICASSO_IV (0x15) /* Picassio IV Graphics Board */ -#define PROD_PICASSO_IV_2 (0x16) -#define PROD_PICASSO_IV_3 (0x17) -#define PROD_PICASSO_IV_4 (0x18) -#define PROD_ARIADNE (0xC9) /* Ariadne Ethernet */ - -#define MANUF_UTILITIES_ULTD (0x087B) /* Utilities Unlimited */ -#define PROD_EMPLANT_DELUXE (0x15) /* Emplant Deluxe SCSI Controller */ -#define PROD_EMPLANT_DELUXE2 (0x20) /* Emplant Deluxe SCSI Controller */ - -#define MANUF_AMITRIX (0x0880) /* Amitrix */ -#define PROD_AMITRIX_MULTI_IO (0x01) /* Multi-IO */ -#define PROD_AMITRIX_CD_RAM (0x02) /* CD-RAM Memory */ - -#define MANUF_ARMAX (0x0885) /* ArMax */ -#define PROD_OMNIBUS (0x00) /* OmniBus Graphics Board */ - -#define MANUF_NEWTEK (0x088F) /* NewTek */ -#define PROD_VIDEOTOASTER (0x00) /* VideoToaster */ - -#define MANUF_MTEC (0x0890) /* M-Tech Germany */ -#define PROD_AT500 (0x01) /* AT500 IDE Controller */ -#define PROD_MTEC_68030 (0x03) /* 68030 Turbo Board */ -#define PROD_MTEC_68020I (0x06) /* 68020i Turbo Board */ -#define PROD_MTEC_T1230 (0x20) /* A1200 T68030/42 RTC Turbo Board */ -#define PROD_MTEC_RAM (0x22) /* MTEC 8MB RAM */ - -#define MANUF_GVP2 (0x0891) /* Great Valley Products */ -#define PROD_SPECTRUM_RAM (0x01) /* EGS 28/24 Spectrum Graphics Board */ -#define PROD_SPECTRUM_REG (0x02) - -#define MANUF_HELFRICH2 (0x0893) /* Helfrich */ -#define PROD_PICCOLO_RAM (0x05) /* Piccolo Graphics Board */ -#define PROD_PICCOLO_REG (0x06) -#define PROD_PEGGY_PLUS (0x07) /* PeggyPlus MPEG Decoder Board */ -#define PROD_VIDEOCRUNCHER (0x08) /* VideoCruncher */ -#define PROD_SD64_RAM (0x0A) /* SD64 Graphics Board */ -#define PROD_SD64_REG (0x0B) - -#define MANUF_MACROSYSTEMS (0x089B) /* MacroSystems USA */ -#define PROD_WARP_ENGINE (0x13) /* Warp Engine 40xx SCSI Controller */ - -#define MANUF_ELBOX (0x089E) /* ElBox Computer */ -#define PROD_ELBOX_1200 (0x06) /* Elbox 1200/4 RAM */ - -#define MANUF_HARMS_PROF (0x0A00) /* Harms Professional */ -#define PROD_HARMS_030_PLUS (0x10) /* 030 plus */ -#define PROD_3500_TURBO (0xD0) /* 3500 Turbo board */ - -#define MANUF_MICRONIK (0x0A50) /* Micronik */ -#define PROD_RCA_120 (0x0A) /* RCA 120 RAM */ - -#define MANUF_MEGA_MICRO (0x1000) /* MegaMicro */ -#define PROD_SCRAM_500_SCSI (0x03) /* SCRAM 500 SCSI Controller */ -#define PROD_SCRAM_500_RAM (0x04) /* SCRAM 500 RAM */ - -#define MANUF_IMTRONICS2 (0x1028) /* Imtronics */ -#define PROD_HURRICANE_2800_3 (0x39) /* Hurricane 2800 68030 */ -#define PROD_HURRICANE_2800_4 (0x57) /* Hurricane 2800 68030 */ - -#define MANUF_KUPKE3 (0x1248) /* Kupke */ -#define PROD_GOLEM_3000 (0x01) /* Golem HD 3000 */ - -#define MANUF_ITH (0x1388) /* ITH */ -#define PROD_ISDN_MASTER_II (0x01) /* ISDN-Master II */ - -#define MANUF_VMC (0x1389) /* VMC */ -#define PROD_ISDN_BLASTER_Z2 (0x01) /* ISDN Blaster Z2 */ -#define PROD_HYPERCOM_4 (0x02) /* HyperCom 4 */ - -#define MANUF_INFORMATION (0x157C) /* Information */ -#define PROD_ISDN_ENGINE_I (0x64) /* ISDN Engine I */ - -#define MANUF_VORTEX (0x2017) /* Vortex */ -#define PROD_GOLDEN_GATE_386SX (0x07) /* Golden Gate 80386SX Board */ -#define PROD_GOLDEN_GATE_RAM (0x08) /* Golden Gate RAM */ -#define PROD_GOLDEN_GATE_486 (0x09) /* Golden Gate 80486 Board */ - -#define MANUF_DATAFLYER (0x2062) /* DataFlyer */ -#define PROD_DATAFLYER_4000SXS (0x01) /* DataFlyer 4000SX SCSI Controller */ -#define PROD_DATAFLYER_4000SXR (0x02) /* DataFlyer 4000SX RAM */ - -#define MANUF_READYSOFT (0x2100) /* ReadySoft */ -#define PROD_AMAX (0x01) /* AMax II/IV */ - -#define MANUF_PHASE5 (0x2140) /* Phase5 */ -#define PROD_BLIZZARD_RAM (0x01) /* Blizzard RAM */ -#define PROD_BLIZZARD (0x02) /* Blizzard */ -#define PROD_BLIZZARD_1220_IV (0x06) /* Blizzard 1220-IV Turbo Board */ -#define PROD_FASTLANE_RAM (0x0A) /* FastLane RAM */ -#define PROD_FASTLANE_SCSI (0x0B) /* FastLane/Blizzard 1230-II SCSI/CyberSCSI */ -#define PROD_CYBERSTORM_SCSI (0x0C) /* Blizzard 1220/CyberStorm */ -#define PROD_BLIZZARD_1230_III (0x0D) /* Blizzard 1230-III Turbo Board */ -#define PROD_BLIZZARD_1230_IV (0x11) /* Blizzard 1230-IV/1260 Turbo Board */ -#define PROD_BLIZZARD_2060SCSI (0x18) /* Blizzard 2060 SCSI Controller */ -#define PROD_CYBERSTORM_II (0x19) /* CyberStorm Mk II */ -#define PROD_CYBERVISION (0x22) /* CyberVision64 Graphics Board */ -#define PROD_CYBERVISION3D_PRT (0x32) /* CyberVision64-3D Prototype */ -#define PROD_CYBERVISION3D (0x43) /* CyberVision64-3D Graphics Board */ - -#define MANUF_DPS (0x2169) /* DPS */ -#define PROD_DPS_PAR (0x01) /* Personal Animation Recorder */ - -#define MANUF_APOLLO2 (0x2200) /* Apollo */ -#define PROD_A620 (0x00) /* A620 68020 Accelerator */ -#define PROD_A620_2 (0x01) /* A620 68020 Accelerator */ - -#define MANUF_APOLLO (0x2222) /* Apollo */ -#define PROD_AT_APOLLO (0x22) /* AT-Apollo */ -#define PROD_APOLLO_TURBO (0x23) /* Apollo Turbo Board */ - -#define MANUF_PETSOFF (0x38A5) /* Petsoff LP */ -#define PROD_DELFINA (0x00) /* Delfina DSP */ - -#define MANUF_UWE_GERLACH (0x3FF7) /* Uwe Gerlach */ -#define PROD_UG_RAM_ROM (0xd4) /* RAM/ROM */ - -#define MANUF_MACROSYSTEMS2 (0x4754) /* MacroSystems Germany */ -#define PROD_MAESTRO (0x03) /* Maestro */ -#define PROD_VLAB (0x04) /* VLab */ -#define PROD_MAESTRO_PRO (0x05) /* Maestro Pro */ -#define PROD_RETINA_Z2 (0x06) /* Retina Z2 Graphics Board */ -#define PROD_MULTI_EVOLUTION (0x08) /* MultiEvolution */ -#define PROD_TOCCATA (0x0C) /* Toccata Sound Board */ -#define PROD_RETINA_Z3 (0x10) /* Retina Z3 Graphics Board */ -#define PROD_VLAB_MOTION (0x12) /* VLab Motion */ -#define PROD_ALTAIS (0x13) /* Altais Graphics Board */ -#define PROD_FALCON_040 (0xFD) /* Falcon '040 Turbo Board */ - -#define MANUF_COMBITEC (0x6766) /* Combitec */ - -#define MANUF_SKI (0x8000) /* SKI Peripherals */ -#define PROD_MAST_FIREBALL (0x08) /* M.A.S.T. Fireball SCSI Controller */ -#define PROD_SKI_SCSI_SERIAL (0x80) /* SCSI / Dual Serial */ - -#define MANUF_CAMERON (0xAA01) /* Cameron */ -#define PROD_PERSONAL_A4 (0x10) /* Personal A4 */ - -#define MANUF_REIS_WARE (0xAA11) /* Reis-Ware */ -#define PROD_RW_HANDYSCANNER (0x11) /* Handyscanner */ +#ifndef _LINUX_ZORRO_H +#define _LINUX_ZORRO_H +#ifndef __ASSEMBLY__ -/* Illegal Manufacturer IDs. These do NOT appear in arch/m68k/amiga/zorro.c! */ - -#define MANUF_HACKER_INC (0x07DB) /* Hacker Inc. */ -#define PROD_HACKER_SCSI (0x01) /* Hacker Inc. SCSI Controller */ - -#define MANUF_RES_MNGT_FORCE (0x07DB) /* Resource Management Force */ -#define PROD_QUICKNET (0x02) /* QuickNet Ethernet */ - -#define MANUF_VECTOR2 (0x07DB) /* Vector */ -#define PROD_CONNECTION_2 (0xE0) /* Vector Connection */ -#define PROD_CONNECTION_3 (0xE1) /* Vector Connection */ -#define PROD_CONNECTION_4 (0xE2) /* Vector Connection */ -#define PROD_CONNECTION_5 (0xE3) /* Vector Connection */ - + /* + * Zorro Product Classes + * + * Make sure to keep these in sync with arch/m68k/amiga/zorro.c! + */ + +enum Zorro_Classes { + ZORRO_CLASS_UNKNOWN = 0x00, + ZORRO_CLASS_ARCNET, + ZORRO_CLASS_AUDIO, + ZORRO_CLASS_BRIDGE, + ZORRO_CLASS_DSP, + ZORRO_CLASS_ETHERNET, + ZORRO_CLASS_ETHERNET_PARALLEL, + ZORRO_CLASS_FLASHROM, + ZORRO_CLASS_FPU_RAM, + ZORRO_CLASS_GFX, + ZORRO_CLASS_GFXRAM, + ZORRO_CLASS_HD, + ZORRO_CLASS_HD_RAM, + ZORRO_CLASS_IDE, + ZORRO_CLASS_IDE_RAM, + ZORRO_CLASS_IDE_FLOPPY, + ZORRO_CLASS_ISDN, + ZORRO_CLASS_MACEMU, + ZORRO_CLASS_MISC, + ZORRO_CLASS_MODEM, + ZORRO_CLASS_MULTIIO, + ZORRO_CLASS_RAM, + ZORRO_CLASS_SCANNER, + ZORRO_CLASS_SCSI, + ZORRO_CLASS_SCSI_IDE, + ZORRO_CLASS_SCSI_RAM, + ZORRO_CLASS_SCSI_SERIAL, + ZORRO_CLASS_SERIAL, + ZORRO_CLASS_TABLET, + ZORRO_CLASS_TURBO, + ZORRO_CLASS_TURBO_RAM, + ZORRO_CLASS_TURBO_HD, + ZORRO_CLASS_TURBO_IDE, + ZORRO_CLASS_TURBO_SCSI, + ZORRO_CLASS_TURBO_SCSI_RAM, + ZORRO_CLASS_VIDEO, +}; -/* - * GVP's identifies most of their product through the 'extended - * product code' (epc). The epc has to be and'ed with the GVP_PRODMASK - * before the identification. - */ -#define GVP_PRODMASK (0xf8) -#define GVP_SCSICLKMASK (0x01) - -enum GVP_ident { - GVP_GFORCE_040 = 0x20, - GVP_GFORCE_040_SCSI = 0x30, - GVP_A1291_SCSI = 0x40, - GVP_COMBO_R4 = 0x60, - GVP_COMBO_R4_SCSI = 0x70, - GVP_PHONEPAK = 0x78, - GVP_IOEXT = 0x98, - GVP_GFORCE_030 = 0xa0, - GVP_GFORCE_030_SCSI = 0xb0, - GVP_A530 = 0xc0, - GVP_A530_SCSI = 0xd0, - GVP_COMBO_R3 = 0xe0, - GVP_COMBO_R3_SCSI = 0xf0, - GVP_SERIESII = 0xf8, -}; + /* + * Known Zorro Boards + * + * Each Zorro board has a 32-bit ID of the form + * + * mmmmmmmmmmmmmmmmppppppppeeeeeeee + * + * with + * + * mmmmmmmmmmmmmmmm 16-bit Manufacturer ID (assigned by CBM (sigh)) + * pppppppp 8-bit Product ID (assigned by manufacturer) + * eeeeeeee 8-bit Extended Product ID (currently only used + * for some GVP boards) + */ + + +#define ZORRO_MANUF(id) ((id) >> 16) +#define ZORRO_PROD(id) (((id) >> 8) & 0xff) +#define ZORRO_EPC(id) ((id) & 0xff) + +#define ZORRO_ID(manuf, prod, epc) \ + ((ZORRO_MANUF_##manuf << 16) | ((prod) << 8) | (epc)) + +typedef u32 zorro_id; + + +#define ZORRO_MANUF_PACIFIC_PERIPHERALS 0x00D3 +#define ZORRO_PROD_PACIFIC_PERIPHERALS_SE_2000_A500 ZORRO_ID(PACIFIC_PERIPHERALS, 0x00, 0) +#define ZORRO_PROD_PACIFIC_PERIPHERALS_SCSI ZORRO_ID(PACIFIC_PERIPHERALS, 0x0A, 0) + +#define ZORRO_MANUF_MACROSYSTEMS_USA_2 0x0100 +#define ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE ZORRO_ID(MACROSYSTEMS_USA_2, 0x13, 0) + +#define ZORRO_MANUF_KUPKE_1 0x00DD +#define ZORRO_PROD_KUPKE_GOLEM_RAM_BOX_2MB ZORRO_ID(KUPKE_1, 0x00, 0) + +#define ZORRO_MANUF_MEMPHIS 0x0100 +#define ZORRO_PROD_MEMPHIS_STORMBRINGER ZORRO_ID(MEMPHIS, 0x00, 0) + +#define ZORRO_MANUF_3_STATE 0x0200 +#define ZORRO_PROD_3_STATE_MEGAMIX_2000 ZORRO_ID(3_STATE, 0x02, 0) + +#define ZORRO_MANUF_COMMODORE_BRAUNSCHWEIG 0x0201 +#define ZORRO_PROD_CBM_A2088_A2286 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x01, 0) +#define ZORRO_PROD_CBM_A2286 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x02, 0) +#define ZORRO_PROD_CBM_A4091_1 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x54, 0) +#define ZORRO_PROD_CBM_A2386SX_1 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x67, 0) + +#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_1 0x0202 +#define ZORRO_PROD_CBM_A2090A ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x01, 0) +#define ZORRO_PROD_CBM_A590_A2091_1 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x02, 0) +#define ZORRO_PROD_CBM_A590_A2091_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x03, 0) +#define ZORRO_PROD_CBM_A2090B ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x04, 0) +#define ZORRO_PROD_CBM_A2060 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x09, 0) +#define ZORRO_PROD_CBM_A590_A2052_A2058_A2091 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x0A, 0) +#define ZORRO_PROD_CBM_A560_RAM ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x20, 0) +#define ZORRO_PROD_CBM_A2232_PROTOTYPE ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x45, 0) +#define ZORRO_PROD_CBM_A2232 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x46, 0) +#define ZORRO_PROD_CBM_A2620 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x50, 0) +#define ZORRO_PROD_CBM_A2630 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x51, 0) +#define ZORRO_PROD_CBM_A4091_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x54, 0) +#define ZORRO_PROD_CBM_A2065_1 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x5A, 0) +#define ZORRO_PROD_CBM_ROMULATOR ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x60, 0) +#define ZORRO_PROD_CBM_A3000_TEST_FIXTURE ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x61, 0) +#define ZORRO_PROD_CBM_A2386SX_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x67, 0) +#define ZORRO_PROD_CBM_A2065_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x70, 0) + +#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_2 0x0203 +#define ZORRO_PROD_CBM_A2090A_CM ZORRO_ID(COMMODORE_WEST_CHESTER_2, 0x03, 0) + +#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2 0x02F4 +#define ZORRO_PROD_PPS_EXP8000 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2, 0x02, 0) + +#define ZORRO_MANUF_KOLFF_COMPUTER_SUPPLIES 0x02FF +#define ZORRO_PROD_KCS_POWER_PC_BOARD ZORRO_ID(KOLFF_COMPUTER_SUPPLIES, 0x00, 0) + +#define ZORRO_MANUF_CARDCO_1 0x03EC +#define ZORRO_PROD_CARDCO_KRONOS_2000_1 ZORRO_ID(CARDCO_1, 0x04, 0) +#define ZORRO_PROD_CARDCO_A1000_1 ZORRO_ID(CARDCO_1, 0x0C, 0) +#define ZORRO_PROD_CARDCO_ESCORT ZORRO_ID(CARDCO_1, 0x0E, 0) +#define ZORRO_PROD_CARDCO_A2410 ZORRO_ID(CARDCO_1, 0xF5, 0) + +#define ZORRO_MANUF_A_SQUARED 0x03ED +#define ZORRO_PROD_A_SQUARED_LIVE_2000 ZORRO_ID(A_SQUARED, 0x01, 0) + +#define ZORRO_MANUF_COMSPEC_COMMUNICATIONS 0x03EE +#define ZORRO_PROD_COMSPEC_COMMUNICATIONS_AX2000 ZORRO_ID(COMSPEC_COMMUNICATIONS, 0x01, 0) + +#define ZORRO_MANUF_ANAKIN_RESEARCH 0x03F1 +#define ZORRO_PROD_ANAKIN_RESEARCH_EASYL ZORRO_ID(ANAKIN_RESEARCH, 0x01, 0) + +#define ZORRO_MANUF_MICROBOTICS 0x03F2 +#define ZORRO_PROD_MICROBOTICS_STARBOARD_II ZORRO_ID(MICROBOTICS, 0x00, 0) +#define ZORRO_PROD_MICROBOTICS_STARDRIVE ZORRO_ID(MICROBOTICS, 0x02, 0) +#define ZORRO_PROD_MICROBOTICS_8_UP_A ZORRO_ID(MICROBOTICS, 0x03, 0) +#define ZORRO_PROD_MICROBOTICS_8_UP_Z ZORRO_ID(MICROBOTICS, 0x04, 0) +#define ZORRO_PROD_MICROBOTICS_DELTA_RAM ZORRO_ID(MICROBOTICS, 0x20, 0) +#define ZORRO_PROD_MICROBOTICS_8_STAR_RAM ZORRO_ID(MICROBOTICS, 0x40, 0) +#define ZORRO_PROD_MICROBOTICS_8_STAR ZORRO_ID(MICROBOTICS, 0x41, 0) +#define ZORRO_PROD_MICROBOTICS_VXL_RAM_32 ZORRO_ID(MICROBOTICS, 0x44, 0) +#define ZORRO_PROD_MICROBOTICS_VXL_68030 ZORRO_ID(MICROBOTICS, 0x45, 0) +#define ZORRO_PROD_MICROBOTICS_DELTA ZORRO_ID(MICROBOTICS, 0x60, 0) +#define ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z_RAM ZORRO_ID(MICROBOTICS, 0x81, 0) +#define ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_1 ZORRO_ID(MICROBOTICS, 0x96, 0) +#define ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_2 ZORRO_ID(MICROBOTICS, 0x9E, 0) +#define ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z ZORRO_ID(MICROBOTICS, 0xC1, 0) + +#define ZORRO_MANUF_ACCESS_ASSOCIATES_ALEGRA 0x03F4 + +#define ZORRO_MANUF_EXPANSION_TECHNOLOGIES 0x03F6 + +#define ZORRO_MANUF_ASDG 0x03FF +#define ZORRO_PROD_ASDG_MEMORY_1 ZORRO_ID(ASDG, 0x01, 0) +#define ZORRO_PROD_ASDG_MEMORY_2 ZORRO_ID(ASDG, 0x02, 0) +#define ZORRO_PROD_ASDG_EB920_LAN_ROVER ZORRO_ID(ASDG, 0xFE, 0) +#define ZORRO_PROD_ASDG_GPIB_DUALIEEE488_TWIN_X ZORRO_ID(ASDG, 0xFF, 0) + +#define ZORRO_MANUF_IMTRONICS_1 0x0404 +#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_1 ZORRO_ID(IMTRONICS_1, 0x39, 0) +#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_2 ZORRO_ID(IMTRONICS_1, 0x57, 0) + +#define ZORRO_MANUF_CBM_UNIVERSITY_OF_LOWELL 0x0406 +#define ZORRO_PROD_CBM_A2410 ZORRO_ID(CBM_UNIVERSITY_OF_LOWELL, 0x00, 0) + +#define ZORRO_MANUF_AMERISTAR 0x041D +#define ZORRO_PROD_AMERISTAR_A2065 ZORRO_ID(AMERISTAR, 0x01, 0) +#define ZORRO_PROD_AMERISTAR_A560 ZORRO_ID(AMERISTAR, 0x09, 0) +#define ZORRO_PROD_AMERISTAR_A4066 ZORRO_ID(AMERISTAR, 0x0A, 0) + +#define ZORRO_MANUF_SUPRA 0x0420 +#define ZORRO_PROD_SUPRA_SUPRADRIVE_4x4 ZORRO_ID(SUPRA, 0x01, 0) +#define ZORRO_PROD_SUPRA_1000_RAM ZORRO_ID(SUPRA, 0x02, 0) +#define ZORRO_PROD_SUPRA_2000_DMA ZORRO_ID(SUPRA, 0x03, 0) +#define ZORRO_PROD_SUPRA_500 ZORRO_ID(SUPRA, 0x05, 0) +#define ZORRO_PROD_SUPRA_500_SCSI ZORRO_ID(SUPRA, 0x08, 0) +#define ZORRO_PROD_SUPRA_500XP_2000_RAM ZORRO_ID(SUPRA, 0x09, 0) +#define ZORRO_PROD_SUPRA_500RX_2000_RAM ZORRO_ID(SUPRA, 0x0A, 0) +#define ZORRO_PROD_SUPRA_2400ZI ZORRO_ID(SUPRA, 0x0B, 0) +#define ZORRO_PROD_SUPRA_500XP_SUPRADRIVE_WORDSYNC ZORRO_ID(SUPRA, 0x0C, 0) +#define ZORRO_PROD_SUPRA_SUPRADRIVE_WORDSYNC_II ZORRO_ID(SUPRA, 0x0D, 0) +#define ZORRO_PROD_SUPRA_2400ZIPLUS ZORRO_ID(SUPRA, 0x10, 0) + +#define ZORRO_MANUF_COMPUTER_SYSTEMS_ASSOCIATES 0x0422 +#define ZORRO_PROD_CSA_MAGNUM ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x11, 0) +#define ZORRO_PROD_CSA_12_GAUGE ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x15, 0) + +#define ZORRO_MANUF_MARC_MICHAEL_GROTH 0x0439 + +#define ZORRO_MANUF_M_TECH 0x0502 +#define ZORRO_PROD_MTEC_AT500_1 ZORRO_ID(M_TECH, 0x03, 0) + +#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_1 0x06E1 +#define ZORRO_PROD_GVP_IMPACT_SERIES_I ZORRO_ID(GREAT_VALLEY_PRODUCTS_1, 0x08, 0) + +#define ZORRO_MANUF_BYTEBOX 0x07DA +#define ZORRO_PROD_BYTEBOX_A500 ZORRO_ID(BYTEBOX, 0x00, 0) + +#define ZORRO_MANUF_DKB_POWER_COMPUTING 0x07DC +#define ZORRO_PROD_DKB_POWER_COMPUTING_SECUREKEY ZORRO_ID(DKB_POWER_COMPUTING, 0x09, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_DKM_3128 ZORRO_ID(DKB_POWER_COMPUTING, 0x0E, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_RAPID_FIRE ZORRO_ID(DKB_POWER_COMPUTING, 0x0F, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_DKM_1202 ZORRO_ID(DKB_POWER_COMPUTING, 0x10, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_COBRA_VIPER_II_68EC030 ZORRO_ID(DKB_POWER_COMPUTING, 0x12, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_1 ZORRO_ID(DKB_POWER_COMPUTING, 0x17, 0) +#define ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_2 ZORRO_ID(DKB_POWER_COMPUTING, 0xFF, 0) + +#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_2 0x07E1 +#define ZORRO_PROD_GVP_IMPACT_SERIES_I_4K ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x01, 0) +#define ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x02, 0) +#define ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_3 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x03, 0) +#define ZORRO_PROD_GVP_IMPACT_3001_IDE_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x08, 0) +#define ZORRO_PROD_GVP_IMPACT_3001_RAM ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x09, 0) +#define ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0A, 0) +#define ZORRO_PROD_GVP_EPC_BASE ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0) +#define ZORRO_PROD_GVP_GFORCE_040_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x20) +#define ZORRO_PROD_GVP_GFORCE_040_SCSI_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x30) +#define ZORRO_PROD_GVP_A1291 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x40) +#define ZORRO_PROD_GVP_COMBO_030_R4 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x60) +#define ZORRO_PROD_GVP_COMBO_030_R4_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x70) +#define ZORRO_PROD_GVP_PHONEPAK ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x78) +#define ZORRO_PROD_GVP_IO_EXTENDER ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x98) +#define ZORRO_PROD_GVP_GFORCE_030 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xa0) +#define ZORRO_PROD_GVP_GFORCE_030_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xb0) +#define ZORRO_PROD_GVP_A530 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xc0) +#define ZORRO_PROD_GVP_A530_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xd0) +#define ZORRO_PROD_GVP_COMBO_030_R3 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xe0) +#define ZORRO_PROD_GVP_COMBO_030_R3_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf0) +#define ZORRO_PROD_GVP_SERIES_II ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf8) +#define ZORRO_PROD_GVP_IMPACT_3001_IDE_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0) +/*#define ZORRO_PROD_GVP_A2000_030 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/ +/*#define ZORRO_PROD_GVP_GFORCE_040_SCSI_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/ +#define ZORRO_PROD_GVP_GFORCE_040_060 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x16, 0) +#define ZORRO_PROD_GVP_IMPACT_VISION_24 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x20, 0) +#define ZORRO_PROD_GVP_GFORCE_040_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0xFF, 0) + +#define ZORRO_MANUF_CALIFORNIA_ACCESS_SYNERGY 0x07E5 +#define ZORRO_PROD_CALIFORNIA_ACCESS_SYNERGY_MALIBU ZORRO_ID(CALIFORNIA_ACCESS_SYNERGY, 0x01, 0) + +#define ZORRO_MANUF_XETEC 0x07E6 +#define ZORRO_PROD_XETEC_FASTCARD ZORRO_ID(XETEC, 0x01, 0) +#define ZORRO_PROD_XETEC_FASTCARD_RAM ZORRO_ID(XETEC, 0x02, 0) +#define ZORRO_PROD_XETEC_FASTCARD_PLUS ZORRO_ID(XETEC, 0x03, 0) + +#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS 0x07EA +#define ZORRO_PROD_PPS_MERCURY ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x00, 0) +#define ZORRO_PROD_PPS_A3000_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x01, 0) +#define ZORRO_PROD_PPS_A2000_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x69, 0) +#define ZORRO_PROD_PPS_ZEUS ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x96, 0) +#define ZORRO_PROD_PPS_A500_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0xBB, 0) + +#define ZORRO_MANUF_XEBEC 0x07EC + +#define ZORRO_MANUF_SPIRIT_TECHNOLOGY 0x07F2 +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN1000 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x01, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN500 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x02, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_SIN500 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x03, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_HDA_506 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x04, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_AX_S ZORRO_ID(SPIRIT_TECHNOLOGY, 0x05, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_OCTABYTE ZORRO_ID(SPIRIT_TECHNOLOGY, 0x06, 0) +#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INMATE ZORRO_ID(SPIRIT_TECHNOLOGY, 0x08, 0) + +#define ZORRO_MANUF_SPIRIT_TECHNOLOGY_2 0x07F3 + +#define ZORRO_MANUF_BSC_ALFADATA_1 0x07FE +#define ZORRO_PROD_BSC_ALF_3_1 ZORRO_ID(BSC_ALFADATA_1, 0x03, 0) + +#define ZORRO_MANUF_BSC_ALFADATA_2 0x0801 +#define ZORRO_PROD_BSC_ALF_2_1 ZORRO_ID(BSC_ALFADATA_2, 0x01, 0) +#define ZORRO_PROD_BSC_ALF_2_2 ZORRO_ID(BSC_ALFADATA_2, 0x02, 0) +#define ZORRO_PROD_BSC_ALF_3_2 ZORRO_ID(BSC_ALFADATA_2, 0x03, 0) + +#define ZORRO_MANUF_CARDCO_2 0x0802 +#define ZORRO_PROD_CARDCO_KRONOS_2000_2 ZORRO_ID(CARDCO_2, 0x04, 0) +#define ZORRO_PROD_CARDCO_A1000_2 ZORRO_ID(CARDCO_2, 0x0C, 0) + +#define ZORRO_MANUF_JOCHHEIM 0x0804 +#define ZORRO_PROD_JOCHHEIM_RAM ZORRO_ID(JOCHHEIM, 0x01, 0) + +#define ZORRO_MANUF_CHECKPOINT_TECHNOLOGIES 0x0807 +#define ZORRO_PROD_CHECKPOINT_TECHNOLOGIES_SERIAL_SOLUTION ZORRO_ID(CHECKPOINT_TECHNOLOGIES, 0x00, 0) + +#define ZORRO_MANUF_EDOTRONIK 0x0810 +#define ZORRO_PROD_EDOTRONIK_IEEE_488 ZORRO_ID(EDOTRONIK, 0x01, 0) +#define ZORRO_PROD_EDOTRONIK_8032 ZORRO_ID(EDOTRONIK, 0x02, 0) +#define ZORRO_PROD_EDOTRONIK_MULTISERIAL ZORRO_ID(EDOTRONIK, 0x03, 0) +#define ZORRO_PROD_EDOTRONIK_VIDEODIGITIZER ZORRO_ID(EDOTRONIK, 0x04, 0) +#define ZORRO_PROD_EDOTRONIK_PARALLEL_IO ZORRO_ID(EDOTRONIK, 0x05, 0) +#define ZORRO_PROD_EDOTRONIK_PIC_PROTOYPING ZORRO_ID(EDOTRONIK, 0x06, 0) +#define ZORRO_PROD_EDOTRONIK_ADC ZORRO_ID(EDOTRONIK, 0x07, 0) +#define ZORRO_PROD_EDOTRONIK_VME ZORRO_ID(EDOTRONIK, 0x08, 0) +#define ZORRO_PROD_EDOTRONIK_DSP96000 ZORRO_ID(EDOTRONIK, 0x09, 0) + +#define ZORRO_MANUF_NES_INC 0x0813 +#define ZORRO_PROD_NES_INC_RAM ZORRO_ID(NES_INC, 0x00, 0) + +#define ZORRO_MANUF_ICD 0x0817 +#define ZORRO_PROD_ICD_ADVANTAGE_2000_SCSI ZORRO_ID(ICD, 0x01, 0) +#define ZORRO_PROD_ICD_ADVANTAGE_IDE ZORRO_ID(ICD, 0x03, 0) +#define ZORRO_PROD_ICD_ADVANTAGE_2080_RAM ZORRO_ID(ICD, 0x04, 0) + +#define ZORRO_MANUF_KUPKE_2 0x0819 +#define ZORRO_PROD_KUPKE_OMTI ZORRO_ID(KUPKE_2, 0x01, 0) +#define ZORRO_PROD_KUPKE_SCSI_II ZORRO_ID(KUPKE_2, 0x02, 0) +#define ZORRO_PROD_KUPKE_GOLEM_BOX ZORRO_ID(KUPKE_2, 0x03, 0) +#define ZORRO_PROD_KUPKE_030_882 ZORRO_ID(KUPKE_2, 0x04, 0) +#define ZORRO_PROD_KUPKE_SCSI_AT ZORRO_ID(KUPKE_2, 0x05, 0) + +#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_3 0x081D +#define ZORRO_PROD_GVP_A2000_RAM8 ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x09, 0) +#define ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x0A, 0) + +#define ZORRO_MANUF_INTERWORKS_NETWORK 0x081E + +#define ZORRO_MANUF_HARDITAL_SYNTHESIS 0x0820 +#define ZORRO_PROD_HARDITAL_SYNTHESIS_TQM_68030_68882 ZORRO_ID(HARDITAL_SYNTHESIS, 0x14, 0) + +#define ZORRO_MANUF_APPLIED_ENGINEERING 0x0828 +#define ZORRO_PROD_APPLIED_ENGINEERING_DL2000 ZORRO_ID(APPLIED_ENGINEERING, 0x10, 0) +#define ZORRO_PROD_APPLIED_ENGINEERING_RAM_WORKS ZORRO_ID(APPLIED_ENGINEERING, 0xE0, 0) + +#define ZORRO_MANUF_BSC_ALFADATA_3 0x082C +#define ZORRO_PROD_BSC_OKTAGON_2008 ZORRO_ID(BSC_ALFADATA_3, 0x05, 0) +#define ZORRO_PROD_BSC_TANDEM_AT_2008_508 ZORRO_ID(BSC_ALFADATA_3, 0x06, 0) +#define ZORRO_PROD_BSC_ALFA_RAM_1200 ZORRO_ID(BSC_ALFADATA_3, 0x07, 0) +#define ZORRO_PROD_BSC_OKTAGON_2008_RAM ZORRO_ID(BSC_ALFADATA_3, 0x08, 0) +#define ZORRO_PROD_BSC_MULTIFACE_I ZORRO_ID(BSC_ALFADATA_3, 0x10, 0) +#define ZORRO_PROD_BSC_MULTIFACE_II ZORRO_ID(BSC_ALFADATA_3, 0x11, 0) +#define ZORRO_PROD_BSC_MULTIFACE_III ZORRO_ID(BSC_ALFADATA_3, 0x12, 0) +#define ZORRO_PROD_BSC_FRAMEBUFFER ZORRO_ID(BSC_ALFADATA_3, 0x20, 0) +#define ZORRO_PROD_BSC_GRAFFITI_RAM ZORRO_ID(BSC_ALFADATA_3, 0x21, 0) +#define ZORRO_PROD_BSC_GRAFFITI_REG ZORRO_ID(BSC_ALFADATA_3, 0x22, 0) +#define ZORRO_PROD_BSC_ISDN_MASTERCARD ZORRO_ID(BSC_ALFADATA_3, 0x40, 0) +#define ZORRO_PROD_BSC_ISDN_MASTERCARD_II ZORRO_ID(BSC_ALFADATA_3, 0x41, 0) + +#define ZORRO_MANUF_PHOENIX 0x0835 +#define ZORRO_PROD_PHOENIX_ST506 ZORRO_ID(PHOENIX, 0x21, 0) +#define ZORRO_PROD_PHOENIX_SCSI ZORRO_ID(PHOENIX, 0x22, 0) +#define ZORRO_PROD_PHOENIX_RAM ZORRO_ID(PHOENIX, 0xBE, 0) + +#define ZORRO_MANUF_ADVANCED_STORAGE_SYSTEMS 0x0836 +#define ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x01, 0) +#define ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS_RAM ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x08, 0) + +#define ZORRO_MANUF_IMPULSE 0x0838 +#define ZORRO_PROD_IMPULSE_FIRECRACKER_24 ZORRO_ID(IMPULSE, 0x00, 0) + +#define ZORRO_MANUF_IVS 0x0840 +#define ZORRO_PROD_IVS_GRANDSLAM_PIC_2 ZORRO_ID(IVS, 0x02, 0) +#define ZORRO_PROD_IVS_GRANDSLAM_PIC_1 ZORRO_ID(IVS, 0x04, 0) +#define ZORRO_PROD_IVS_OVERDRIVE ZORRO_ID(IVS, 0x10, 0) +#define ZORRO_PROD_IVS_TRUMPCARD_CLASSIC ZORRO_ID(IVS, 0x30, 0) +#define ZORRO_PROD_IVS_TRUMPCARD_PRO_GRANDSLAM ZORRO_ID(IVS, 0x34, 0) +#define ZORRO_PROD_IVS_META_4 ZORRO_ID(IVS, 0x40, 0) +#define ZORRO_PROD_IVS_WAVETOOLS ZORRO_ID(IVS, 0xBF, 0) +#define ZORRO_PROD_IVS_VECTOR_1 ZORRO_ID(IVS, 0xF3, 0) +#define ZORRO_PROD_IVS_VECTOR_2 ZORRO_ID(IVS, 0xF4, 0) + +#define ZORRO_MANUF_VECTOR_1 0x0841 +#define ZORRO_PROD_VECTOR_CONNECTION_1 ZORRO_ID(VECTOR_1, 0xE3, 0) + +#define ZORRO_MANUF_XPERT_PRODEV 0x0845 +#define ZORRO_PROD_XPERT_PRODEV_VISIONA_RAM ZORRO_ID(XPERT_PRODEV, 0x01, 0) +#define ZORRO_PROD_XPERT_PRODEV_VISIONA_REG ZORRO_ID(XPERT_PRODEV, 0x02, 0) +#define ZORRO_PROD_XPERT_PRODEV_MERLIN_RAM ZORRO_ID(XPERT_PRODEV, 0x03, 0) +#define ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_1 ZORRO_ID(XPERT_PRODEV, 0x04, 0) +#define ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_2 ZORRO_ID(XPERT_PRODEV, 0xC9, 0) + +#define ZORRO_MANUF_HYDRA_SYSTEMS 0x0849 +#define ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET ZORRO_ID(HYDRA_SYSTEMS, 0x01, 0) + +#define ZORRO_MANUF_SUNRIZE_INDUSTRIES 0x084F +#define ZORRO_PROD_SUNRIZE_INDUSTRIES_AD1012 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x01, 0) +#define ZORRO_PROD_SUNRIZE_INDUSTRIES_AD516 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x02, 0) +#define ZORRO_PROD_SUNRIZE_INDUSTRIES_DD512 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x03, 0) + +#define ZORRO_MANUF_TRICERATOPS 0x0850 +#define ZORRO_PROD_TRICERATOPS_MULTI_IO ZORRO_ID(TRICERATOPS, 0x01, 0) + +#define ZORRO_MANUF_APPLIED_MAGIC 0x0851 +#define ZORRO_PROD_APPLIED_MAGIC_DMI_RESOLVER ZORRO_ID(APPLIED_MAGIC, 0x01, 0) +#define ZORRO_PROD_APPLIED_MAGIC_DIGITAL_BROADCASTER ZORRO_ID(APPLIED_MAGIC, 0x06, 0) + +#define ZORRO_MANUF_GFX_BASE 0x085E +#define ZORRO_PROD_GFX_BASE_GDA_1_VRAM ZORRO_ID(GFX_BASE, 0x00, 0) +#define ZORRO_PROD_GFX_BASE_GDA_1 ZORRO_ID(GFX_BASE, 0x01, 0) + +#define ZORRO_MANUF_ROCTEC 0x0860 +#define ZORRO_PROD_ROCTEC_RH_800C ZORRO_ID(ROCTEC, 0x01, 0) +#define ZORRO_PROD_ROCTEC_RH_800C_RAM ZORRO_ID(ROCTEC, 0x01, 0) + +#define ZORRO_MANUF_KATO 0x0861 +#define ZORRO_PROD_KATO_MELODY ZORRO_ID(KATO, 0x80, 0) +/* ID clash!! */ +#define ZORRO_MANUF_HELFRICH_1 0x0861 +#define ZORRO_PROD_HELFRICH_RAINBOW_II ZORRO_ID(HELFRICH_1, 0x20, 0) +#define ZORRO_PROD_HELFRICH_RAINBOW_III ZORRO_ID(HELFRICH_1, 0x21, 0) + +#define ZORRO_MANUF_ATLANTIS 0x0862 + +#define ZORRO_MANUF_PROTAR 0x0864 + +#define ZORRO_MANUF_ACS 0x0865 + +#define ZORRO_MANUF_SOFTWARE_RESULTS_ENTERPRISES 0x0866 +#define ZORRO_PROD_SOFTWARE_RESULTS_ENTERPRISES_GOLDEN_GATE_2_BUS_PLUS ZORRO_ID(SOFTWARE_RESULTS_ENTERPRISES, 0x01, 0) + +#define ZORRO_MANUF_MASOBOSHI 0x086D +#define ZORRO_PROD_MASOBOSHI_MASTER_CARD_SC201 ZORRO_ID(MASOBOSHI, 0x03, 0) +#define ZORRO_PROD_MASOBOSHI_MASTER_CARD_MC702 ZORRO_ID(MASOBOSHI, 0x04, 0) +#define ZORRO_PROD_MASOBOSHI_MVD_819 ZORRO_ID(MASOBOSHI, 0x07, 0) + +#define ZORRO_MANUF_MAINHATTAN_DATA 0x086F +#define ZORRO_PROD_MAINHATTAN_DATA_IDE ZORRO_ID(MAINHATTAN_DATA, 0x01, 0) + +#define ZORRO_MANUF_VILLAGE_TRONIC 0x0877 +#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_RAM ZORRO_ID(VILLAGE_TRONIC, 0x01, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_REG ZORRO_ID(VILLAGE_TRONIC, 0x02, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_16M_PROTOTYPE ZORRO_ID(VILLAGE_TRONIC, 0x03, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM ZORRO_ID(VILLAGE_TRONIC, 0x0B, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG ZORRO_ID(VILLAGE_TRONIC, 0x0C, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_SEGMENTED_MODE ZORRO_ID(VILLAGE_TRONIC, 0x0D, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM1 ZORRO_ID(VILLAGE_TRONIC, 0x15, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM2 ZORRO_ID(VILLAGE_TRONIC, 0x16, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG ZORRO_ID(VILLAGE_TRONIC, 0x17, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3 ZORRO_ID(VILLAGE_TRONIC, 0x18, 0) +#define ZORRO_PROD_VILLAGE_TRONIC_ARIADNE ZORRO_ID(VILLAGE_TRONIC, 0xC9, 0) + +#define ZORRO_MANUF_UTILITIES_UNLIMITED 0x087B +#define ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE ZORRO_ID(UTILITIES_UNLIMITED, 0x15, 0) +#define ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE2 ZORRO_ID(UTILITIES_UNLIMITED, 0x20, 0) + +#define ZORRO_MANUF_AMITRIX 0x0880 +#define ZORRO_PROD_AMITRIX_MULTI_IO ZORRO_ID(AMITRIX, 0x01, 0) +#define ZORRO_PROD_AMITRIX_CD_RAM ZORRO_ID(AMITRIX, 0x02, 0) + +#define ZORRO_MANUF_ARMAX 0x0885 +#define ZORRO_PROD_ARMAX_OMNIBUS ZORRO_ID(ARMAX, 0x00, 0) + +#define ZORRO_MANUF_NEWTEK 0x088F +#define ZORRO_PROD_NEWTEK_VIDEOTOASTER ZORRO_ID(NEWTEK, 0x00, 0) + +#define ZORRO_MANUF_M_TECH_GERMANY 0x0890 +#define ZORRO_PROD_MTEC_AT500_2 ZORRO_ID(M_TECH_GERMANY, 0x01, 0) +#define ZORRO_PROD_MTEC_68030 ZORRO_ID(M_TECH_GERMANY, 0x03, 0) +#define ZORRO_PROD_MTEC_68020I ZORRO_ID(M_TECH_GERMANY, 0x06, 0) +#define ZORRO_PROD_MTEC_A1200_T68030_RTC ZORRO_ID(M_TECH_GERMANY, 0x20, 0) +#define ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530 ZORRO_ID(M_TECH_GERMANY, 0x21, 0) +#define ZORRO_PROD_MTEC_8_MB_RAM ZORRO_ID(M_TECH_GERMANY, 0x22, 0) +#define ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530_SCSI_IDE ZORRO_ID(M_TECH_GERMANY, 0x24, 0) + +#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_4 0x0891 +#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x01, 0) +#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x02, 0) + +#define ZORRO_MANUF_APOLLO_1 0x0892 +#define ZORRO_PROD_APOLLO_A1200 ZORRO_ID(APOLLO_1, 0x01, 0) + +#define ZORRO_MANUF_HELFRICH_2 0x0893 +#define ZORRO_PROD_HELFRICH_PICCOLO_RAM ZORRO_ID(HELFRICH_2, 0x05, 0) +#define ZORRO_PROD_HELFRICH_PICCOLO_REG ZORRO_ID(HELFRICH_2, 0x06, 0) +#define ZORRO_PROD_HELFRICH_PEGGY_PLUS_MPEG ZORRO_ID(HELFRICH_2, 0x07, 0) +#define ZORRO_PROD_HELFRICH_VIDEOCRUNCHER ZORRO_ID(HELFRICH_2, 0x08, 0) +#define ZORRO_PROD_HELFRICH_SD64_RAM ZORRO_ID(HELFRICH_2, 0x0A, 0) +#define ZORRO_PROD_HELFRICH_SD64_REG ZORRO_ID(HELFRICH_2, 0x0B, 0) + +#define ZORRO_MANUF_MACROSYSTEMS_USA 0x089B +#define ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx ZORRO_ID(MACROSYSTEMS_USA, 0x13, 0) + +#define ZORRO_MANUF_ELBOX_COMPUTER 0x089E +#define ZORRO_PROD_ELBOX_COMPUTER_1200_4 ZORRO_ID(ELBOX_COMPUTER, 0x06, 0) + +#define ZORRO_MANUF_HARMS_PROFESSIONAL 0x0A00 +#define ZORRO_PROD_HARMS_PROFESSIONAL_030_PLUS ZORRO_ID(HARMS_PROFESSIONAL, 0x10, 0) +#define ZORRO_PROD_HARMS_PROFESSIONAL_3500 ZORRO_ID(HARMS_PROFESSIONAL, 0xD0, 0) + +#define ZORRO_MANUF_MICRONIK 0x0A50 +#define ZORRO_PROD_MICRONIK_RCA_120 ZORRO_ID(MICRONIK, 0x0A, 0) + +#define ZORRO_MANUF_MICRONIK2 0x0F0F +#define ZORRO_PROD_MICRONIK2_Z3I ZORRO_ID(MICRONIK2, 0x01, 0) + +#define ZORRO_MANUF_MEGAMICRO 0x1000 +#define ZORRO_PROD_MEGAMICRO_SCRAM_500 ZORRO_ID(MEGAMICRO, 0x03, 0) +#define ZORRO_PROD_MEGAMICRO_SCRAM_500_RAM ZORRO_ID(MEGAMICRO, 0x04, 0) + +#define ZORRO_MANUF_IMTRONICS_2 0x1028 +#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_3 ZORRO_ID(IMTRONICS_2, 0x39, 0) +#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_4 ZORRO_ID(IMTRONICS_2, 0x57, 0) + +/* unofficial ID */ +#define ZORRO_MANUF_INDIVIDUAL_COMPUTERS 0x1212 +#define ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x00, 0) +#define ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x2A, 0) + +#define ZORRO_MANUF_KUPKE_3 0x1248 +#define ZORRO_PROD_KUPKE_GOLEM_HD_3000 ZORRO_ID(KUPKE_3, 0x01, 0) + +#define ZORRO_MANUF_ITH 0x1388 +#define ZORRO_PROD_ITH_ISDN_MASTER_II ZORRO_ID(ITH, 0x01, 0) + +#define ZORRO_MANUF_VMC 0x1389 +#define ZORRO_PROD_VMC_ISDN_BLASTER_Z2 ZORRO_ID(VMC, 0x01, 0) +#define ZORRO_PROD_VMC_HYPERCOM_4 ZORRO_ID(VMC, 0x02, 0) + +#define ZORRO_MANUF_INFORMATION 0x157C +#define ZORRO_PROD_INFORMATION_ISDN_ENGINE_I ZORRO_ID(INFORMATION, 0x64, 0) + +#define ZORRO_MANUF_VORTEX 0x2017 +#define ZORRO_PROD_VORTEX_GOLDEN_GATE_80386SX ZORRO_ID(VORTEX, 0x07, 0) +#define ZORRO_PROD_VORTEX_GOLDEN_GATE_RAM ZORRO_ID(VORTEX, 0x08, 0) +#define ZORRO_PROD_VORTEX_GOLDEN_GATE_80486 ZORRO_ID(VORTEX, 0x09, 0) + +#define ZORRO_MANUF_EXPANSION_SYSTEMS 0x2062 +#define ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX ZORRO_ID(EXPANSION_SYSTEMS, 0x01, 0) +#define ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX_RAM ZORRO_ID(EXPANSION_SYSTEMS, 0x02, 0) + +#define ZORRO_MANUF_READYSOFT 0x2100 +#define ZORRO_PROD_READYSOFT_AMAX_II_IV ZORRO_ID(READYSOFT, 0x01, 0) + +#define ZORRO_MANUF_PHASE5 0x2140 +#define ZORRO_PROD_PHASE5_BLIZZARD_RAM ZORRO_ID(PHASE5, 0x01, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD ZORRO_ID(PHASE5, 0x02, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_1220_IV ZORRO_ID(PHASE5, 0x06, 0) +#define ZORRO_PROD_PHASE5_FASTLANE_Z3_RAM ZORRO_ID(PHASE5, 0x0A, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060 ZORRO_ID(PHASE5, 0x0B, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_1220_CYBERSTORM ZORRO_ID(PHASE5, 0x0C, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_1230 ZORRO_ID(PHASE5, 0x0D, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_1230_IV_1260 ZORRO_ID(PHASE5, 0x11, 0) +#define ZORRO_PROD_PHASE5_BLIZZARD_2060 ZORRO_ID(PHASE5, 0x18, 0) +#define ZORRO_PROD_PHASE5_CYBERSTORM_MK_II ZORRO_ID(PHASE5, 0x19, 0) +#define ZORRO_PROD_PHASE5_CYBERVISION64 ZORRO_ID(PHASE5, 0x22, 0) +#define ZORRO_PROD_PHASE5_CYBERVISION64_3D_PROTOTYPE ZORRO_ID(PHASE5, 0x32, 0) +#define ZORRO_PROD_PHASE5_CYBERVISION64_3D ZORRO_ID(PHASE5, 0x43, 0) +#define ZORRO_PROD_PHASE5_CYBERSTORM_MK_III ZORRO_ID(PHASE5, 0x64, 0) + +#define ZORRO_MANUF_DPS 0x2169 +#define ZORRO_PROD_DPS_PERSONAL_ANIMATION_RECORDER ZORRO_ID(DPS, 0x01, 0) + +#define ZORRO_MANUF_APOLLO_2 0x2200 +#define ZORRO_PROD_APOLLO_A620_68020_1 ZORRO_ID(APOLLO_2, 0x00, 0) +#define ZORRO_PROD_APOLLO_A620_68020_2 ZORRO_ID(APOLLO_2, 0x01, 0) + +#define ZORRO_MANUF_APOLLO_3 0x2222 +#define ZORRO_PROD_APOLLO_AT_APOLLO ZORRO_ID(APOLLO_3, 0x22, 0) +#define ZORRO_PROD_APOLLO_1230_1240_1260_2030_4040_4060 ZORRO_ID(APOLLO_3, 0x23, 0) + +#define ZORRO_MANUF_PETSOFF_LP 0x38A5 +#define ZORRO_PROD_PETSOFF_LP_DELFINA ZORRO_ID(PETSOFF_LP, 0x00, 0) +#define ZORRO_PROD_PETSOFF_LP_DELFINA_LITE ZORRO_ID(PETSOFF_LP, 0x01, 0) + +#define ZORRO_MANUF_UWE_GERLACH 0x3FF7 +#define ZORRO_PROD_UWE_GERLACH_RAM_ROM ZORRO_ID(UWE_GERLACH, 0xd4, 0) + +#define ZORRO_MANUF_MACROSYSTEMS_GERMANY 0x4754 +#define ZORRO_PROD_MACROSYSTEMS_MAESTRO ZORRO_ID(MACROSYSTEMS_GERMANY, 0x03, 0) +#define ZORRO_PROD_MACROSYSTEMS_VLAB ZORRO_ID(MACROSYSTEMS_GERMANY, 0x04, 0) +#define ZORRO_PROD_MACROSYSTEMS_MAESTRO_PRO ZORRO_ID(MACROSYSTEMS_GERMANY, 0x05, 0) +#define ZORRO_PROD_MACROSYSTEMS_RETINA ZORRO_ID(MACROSYSTEMS_GERMANY, 0x06, 0) +#define ZORRO_PROD_MACROSYSTEMS_MULTI_EVOLUTION ZORRO_ID(MACROSYSTEMS_GERMANY, 0x08, 0) +#define ZORRO_PROD_MACROSYSTEMS_TOCCATA ZORRO_ID(MACROSYSTEMS_GERMANY, 0x0C, 0) +#define ZORRO_PROD_MACROSYSTEMS_RETINA_Z3 ZORRO_ID(MACROSYSTEMS_GERMANY, 0x10, 0) +#define ZORRO_PROD_MACROSYSTEMS_VLAB_MOTION ZORRO_ID(MACROSYSTEMS_GERMANY, 0x12, 0) +#define ZORRO_PROD_MACROSYSTEMS_ALTAIS ZORRO_ID(MACROSYSTEMS_GERMANY, 0x13, 0) +#define ZORRO_PROD_MACROSYSTEMS_FALCON_040 ZORRO_ID(MACROSYSTEMS_GERMANY, 0xFD, 0) + +#define ZORRO_MANUF_COMBITEC 0x6766 + +#define ZORRO_MANUF_SKI_PERIPHERALS 0x8000 +#define ZORRO_PROD_SKI_PERIPHERALS_MAST_FIREBALL ZORRO_ID(SKI_PERIPHERALS, 0x08, 0) +#define ZORRO_PROD_SKI_PERIPHERALS_SCSI_DUAL_SERIAL ZORRO_ID(SKI_PERIPHERALS, 0x80, 0) + +#define ZORRO_MANUF_REIS_WARE_2 0xA9AD +#define ZORRO_PROD_REIS_WARE_SCAN_KING ZORRO_ID(REIS_WARE_2, 0x11, 0) + +#define ZORRO_MANUF_CAMERON 0xAA01 +#define ZORRO_PROD_CAMERON_PERSONAL_A4 ZORRO_ID(CAMERON, 0x10, 0) + +#define ZORRO_MANUF_REIS_WARE 0xAA11 +#define ZORRO_PROD_REIS_WARE_HANDYSCANNER ZORRO_ID(REIS_WARE, 0x11, 0) + +#define ZORRO_MANUF_PHOENIX_2 0xB5A8 +#define ZORRO_PROD_PHOENIX_ST506_2 ZORRO_ID(PHOENIX_2, 0x21, 0) +#define ZORRO_PROD_PHOENIX_SCSI_2 ZORRO_ID(PHOENIX_2, 0x22, 0) +#define ZORRO_PROD_PHOENIX_RAM_2 ZORRO_ID(PHOENIX_2, 0xBE, 0) + +#define ZORRO_MANUF_COMBITEC_2 0xC008 +#define ZORRO_PROD_COMBITEC_HD ZORRO_ID(COMBITEC_2, 0x2A, 0) +#define ZORRO_PROD_COMBITEC_SRAM ZORRO_ID(COMBITEC_2, 0x2B, 0) + + + /* + * Test and illegal Manufacturer IDs. + * These do NOT appear in arch/m68k/amiga/zorro.c! + */ + +#define ZORRO_MANUF_HACKER 0x07DB +#define ZORRO_PROD_GENERAL_PROTOTYPE ZORRO_ID(HACKER, 0x00, 0) +#define ZORRO_PROD_HACKER_SCSI ZORRO_ID(HACKER, 0x01, 0) +#define ZORRO_PROD_RESOURCE_MANAGEMENT_FORCE_QUICKNET_QN2000 ZORRO_ID(HACKER, 0x02, 0) +#define ZORRO_PROD_VECTOR_CONNECTION_2 ZORRO_ID(HACKER, 0xE0, 0) +#define ZORRO_PROD_VECTOR_CONNECTION_3 ZORRO_ID(HACKER, 0xE1, 0) +#define ZORRO_PROD_VECTOR_CONNECTION_4 ZORRO_ID(HACKER, 0xE2, 0) +#define ZORRO_PROD_VECTOR_CONNECTION_5 ZORRO_ID(HACKER, 0xE3, 0) + + + /* + * GVP identifies most of its products through the 'extended product code' + * (epc). The epc has to be and'ed with the GVP_PRODMASK before the + * identification. + */ + +#define GVP_PRODMASK (0xf8) +#define GVP_SCSICLKMASK (0x01) enum GVP_flags { - GVP_IO = 0x01, - GVP_ACCEL = 0x02, - GVP_SCSI = 0x04, - GVP_24BITDMA = 0x08, - GVP_25BITDMA = 0x10, - GVP_NOBANK = 0x20, - GVP_14MHZ = 0x40, + GVP_IO = 0x01, + GVP_ACCEL = 0x02, + GVP_SCSI = 0x04, + GVP_24BITDMA = 0x08, + GVP_25BITDMA = 0x10, + GVP_NOBANK = 0x20, + GVP_14MHZ = 0x40, }; @@ -515,8 +663,8 @@ struct ExpansionRom { u_char er_Product; /* Product number, assigned by manufacturer */ u_char er_Flags; /* Flags */ u_char er_Reserved03; /* Must be zero ($ff inverted) */ - u_short er_Manufacturer; /* Unique ID,ASSIGNED BY COMMODORE-AMIGA! */ - u_long er_SerialNumber; /* Available for use by manufacturer */ + u_short er_Manufacturer;/* Unique ID,ASSIGNED BY COMMODORE-AMIGA! */ + u_long er_SerialNumber;/* Available for use by manufacturer */ u_short er_InitDiagVec; /* Offset to optional "DiagArea" structure */ u_char er_Reserved0c; u_char er_Reserved0d; @@ -547,7 +695,7 @@ struct ConfigDev { u_long cd_Unused[4]; /* for whatever the driver wants */ }; -#else /* __ASSEMBLY__ */ +#else /* __ASSEMBLY__ */ LN_Succ = 0 LN_Pred = LN_Succ+4 @@ -582,7 +730,7 @@ CD_NextCD = CD_Driver+4 CD_Unused = CD_NextCD+4 CD_sizeof = CD_Unused+(4*4) -#endif /* __ASSEMBLY__ */ +#endif /* __ASSEMBLY__ */ #ifndef __ASSEMBLY__ @@ -590,30 +738,30 @@ CD_sizeof = CD_Unused+(4*4) #ifdef __KERNEL__ -extern int zorro_num_autocon; /* # of autoconfig devices found */ +extern unsigned int zorro_num_autocon; /* # of autoconfig devices found */ extern struct ConfigDev zorro_autocon[ZORRO_NUM_AUTO]; -/* - * Zorro Functions - */ + /* + * Zorro Functions + */ -extern int zorro_find(int manuf, int prod, int part, int index); -extern struct ConfigDev *zorro_get_board(int key); -extern void zorro_config_board(int key, int part); -extern void zorro_unconfig_board(int key, int part); +extern unsigned int zorro_find(zorro_id id, unsigned int part, unsigned int index); +extern const struct ConfigDev *zorro_get_board(unsigned int key); +extern void zorro_config_board(unsigned int key, unsigned int part); +extern void zorro_unconfig_board(unsigned int key, unsigned int part); -/* - * Bitmask indicating portions of available Zorro II RAM that are unused - * by the system. Every bit represents a 64K chunk, for a maximum of 8MB - * (128 chunks, physical 0x00200000-0x009fffff). - * - * If you want to use (= allocate) portions of this RAM, you should clear - * the corresponding bits. - */ + /* + * Bitmask indicating portions of available Zorro II RAM that are unused + * by the system. Every bit represents a 64K chunk, for a maximum of 8MB + * (128 chunks, physical 0x00200000-0x009fffff). + * + * If you want to use (= allocate) portions of this RAM, you should clear + * the corresponding bits. + */ -extern u_long zorro_unused_z2ram[4]; +extern u32 zorro_unused_z2ram[4]; #define Z2RAM_START (0x00200000) #define Z2RAM_END (0x00a00000) @@ -623,14 +771,14 @@ extern u_long zorro_unused_z2ram[4]; #define Z2RAM_CHUNKSHIFT (16) -/* - * Verbose Board Identification - */ + /* + * Verbose Board Identification + */ extern void zorro_identify(void); extern int zorro_get_list(char *buffer); -#endif /* !__ASSEMBLY__ */ -#endif /* __KERNEL__ */ +#endif /* !__ASSEMBLY__ */ +#endif /* __KERNEL__ */ -#endif /* __ZORRO_H */ +#endif /* _LINUX_ZORRO_H */ diff --git a/include/net/flow.h b/include/net/flow.h index dff77fc6a..7759e506c 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -22,7 +22,7 @@ struct flowi { } ip6_u; } nl_u; - struct device *dev; + int oif; union { struct { diff --git a/include/net/ip.h b/include/net/ip.h index 70d22dcc5..9b536ddf7 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -85,10 +85,9 @@ extern int ip_mc_procinfo(char *, char **, off_t, int, int); */ extern int ip_ioctl(struct sock *sk, int cmd, unsigned long arg); -extern int ip_build_pkt(struct sk_buff *skb, struct sock *sk, - u32 saddr, u32 daddr, - struct ip_options *opt); -extern int ip_build_header(struct sk_buff *skb, struct sock *sk); +extern void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, + u32 saddr, u32 daddr, + struct ip_options *opt); extern int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); extern int ip_local_deliver(struct sk_buff *skb); diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 863037b23..8fb0fbed7 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -72,7 +72,7 @@ extern void rt6_sndmsg(int type, struct in6_addr *dst, extern struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, - struct device *dev, int flags); + int oif, int flags); /* * support functions for ND diff --git a/include/net/protocol.h b/include/net/protocol.h index 3c00907ea..63d562a98 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -55,7 +55,7 @@ struct inet6_protocol unsigned short len, int redo, struct inet6_protocol *protocol); - void (*err_handler)(int type, int code, unsigned char *buff, + void (*err_handler)(struct sk_buff *skb, int type, int code, unsigned char *buff, __u32 info, struct in6_addr *saddr, struct in6_addr *daddr, struct inet6_protocol *protocol); diff --git a/include/net/sock.h b/include/net/sock.h index 589f58c7c..f06f94ea9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -150,6 +150,7 @@ struct ipv6_pinfo __u32 flow_lbl; int hop_limit; int mcast_hops; + int mcast_oif; __u8 priority; @@ -163,10 +164,6 @@ struct ipv6_pinfo mc_loop:1, unused:2; - /* device for outgoing packets */ - - struct device *oif; - struct ipv6_mc_socklist *ipv6_mc_list; __u32 dst_cookie; @@ -188,6 +185,11 @@ struct raw_opt { }; #endif +/* This defines a selective acknowledgement block. */ +struct tcp_sack_block { + __u32 start_seq; + __u32 end_seq; +}; struct tcp_opt { @@ -259,7 +261,8 @@ struct tcp_opt * Options received (usually on last packet, some only on SYN packets). */ char tstamp_ok, /* TIMESTAMP seen on SYN packet */ - wscale_ok; /* Wscale seen on SYN packet */ + wscale_ok, /* Wscale seen on SYN packet */ + sack_ok; /* SACK seen on SYN packet */ char saw_tstamp; /* Saw TIMESTAMP on last packet */ __u16 in_mss; /* MSS option received from sender */ __u8 snd_wscale; /* Window scaling received from sender */ @@ -268,6 +271,8 @@ struct tcp_opt __u32 rcv_tsecr; /* Time stamp echo reply */ __u32 ts_recent; /* Time stamp to echo next */ __u32 ts_recent_stamp;/* Time we stored ts_recent (for aging) */ + int num_sacks; /* Number of SACK blocks */ + struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ struct timer_list probe_timer; /* Probes */ __u32 basertt; /* Vegas baseRTT */ @@ -355,7 +360,12 @@ struct sock unsigned short num; /* Local port */ volatile unsigned char state, /* Connection state */ zapped; /* In ax25 & ipx means not linked */ - struct tcphdr dummy_th; /* TCP header template */ + __u16 sport; /* Source port */ + __u16 dport; /* Destination port */ + + unsigned short family; + unsigned char reuse, + nonagle; int sock_readers; /* user count */ int rcvbuf; @@ -379,13 +389,11 @@ struct sock volatile char dead, done, urginline, - reuse, keepopen, linger, destroy, no_check, broadcast, - nonagle, bsdism; unsigned char debug; int proc; @@ -398,7 +406,6 @@ struct sock struct sk_buff_head back_log, error_queue; - unsigned short family; struct proto *prot; /* diff --git a/include/net/tcp.h b/include/net/tcp.h index cec01dfe6..84bf7f55e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -97,7 +97,7 @@ extern struct sock *tcp_regs[TCP_NUM_REGS]; #define TCP_RHASH_FN(__fport) \ ((((__fport) >> 7) ^ (__fport)) & (TCP_NUM_REGS - 1)) #define TCP_RHASH(__fport) tcp_regs[TCP_RHASH_FN((__fport))] -#define TCP_SK_RHASH_FN(__sock) TCP_RHASH_FN((__sock)->dummy_th.dest) +#define TCP_SK_RHASH_FN(__sock) TCP_RHASH_FN((__sock)->dport) #define TCP_SK_RHASH(__sock) tcp_regs[TCP_SK_RHASH_FN((__sock))] static __inline__ void tcp_reg_zap(struct sock *sk) @@ -161,9 +161,12 @@ struct tcp_tw_bucket { int bound_dev_if; unsigned short num; unsigned char state, - family; /* sk->zapped */ - __u16 source; /* sk->dummy_th.source */ - __u16 dest; /* sk->dummy_th.dest */ + zapped; + __u16 sport; + __u16 dport; + unsigned short family; + unsigned char reuse, + nonagle; /* And these are ours. */ __u32 rcv_nxt; @@ -187,6 +190,7 @@ extern kmem_cache_t *tcp_timewait_cachep; */ extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; +extern int sysctl_tcp_sack; /* These can have wildcards, don't try too hard. */ static __inline__ int tcp_lhashfn(unsigned short num) @@ -294,15 +298,12 @@ static __inline__ int tcp_sk_listen_hashfn(struct sock *sk) #define TCPOLEN_TIMESTAMP 10 /* But this is what stacks really send out. */ -#define TCPOLEN_TSTAMP_ALIGNED 12 -#define TCPOLEN_WSCALE_ALIGNED 4 - -/* - * TCP option flags for parsed options. - */ - -#define TCPOPTF_SACK_PERM 1 -#define TCPOPTF_TIMESTAMP 2 +#define TCPOLEN_TSTAMP_ALIGNED 12 +#define TCPOLEN_WSCALE_ALIGNED 4 +#define TCPOLEN_SACKPERM_ALIGNED 4 +#define TCPOLEN_SACK_BASE 2 +#define TCPOLEN_SACK_BASE_ALIGNED 4 +#define TCPOLEN_SACK_PERBLOCK 8 /* * TCP Vegas constants @@ -331,7 +332,7 @@ struct tcp_v6_open_req { struct in6_addr loc_addr; struct in6_addr rmt_addr; struct ipv6_options *opt; - struct device *dev; + int iif; }; #endif @@ -347,6 +348,7 @@ struct open_request { unsigned snd_wscale : 4, rcv_wscale : 4, tstamp_ok : 1, + sack_ok : 1, wscale_ok : 1; /* The following two fields can be easily recomputed I think -AK */ __u32 window_clamp; /* window clamp at creation time */ @@ -378,9 +380,6 @@ extern kmem_cache_t *tcp_openreq_cachep; */ struct tcp_func { - int (*build_net_header) (struct sock *sk, - struct sk_buff *skb); - void (*queue_xmit) (struct sk_buff *skb); void (*send_check) (struct sock *sk, @@ -388,8 +387,7 @@ struct tcp_func { int len, struct sk_buff *skb); - int (*rebuild_header) (struct sock *sk, - struct sk_buff *skb); + int (*rebuild_header) (struct sock *sk); int (*conn_request) (struct sock *sk, struct sk_buff *skb, @@ -497,15 +495,14 @@ extern int tcp_recvmsg(struct sock *sk, int len, int nonblock, int flags, int *addr_len); -extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, - int no_fancy); +extern void tcp_parse_options(struct sock *sk, struct tcphdr *th, + struct tcp_opt *tp, int no_fancy); /* * TCP v4 functions exported for the inet6 API */ -extern int tcp_v4_rebuild_header(struct sock *sk, - struct sk_buff *skb); +extern int tcp_v4_rebuild_header(struct sock *sk); extern int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb); @@ -520,7 +517,8 @@ extern int tcp_v4_conn_request(struct sock *sk, extern struct sock * tcp_create_openreq_child(struct sock *sk, struct open_request *req, - struct sk_buff *skb); + struct sk_buff *skb, + int mss); extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, @@ -534,6 +532,16 @@ extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); +extern void tcp_connect(struct sock *sk, + struct sk_buff *skb, + int est_mss); + +extern struct sk_buff * tcp_make_synack(struct sock *sk, + struct dst_entry *dst, + struct open_request *req, + int mss); + + /* From syncookies.c */ extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt); @@ -543,7 +551,8 @@ extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, extern void tcp_read_wakeup(struct sock *); extern void tcp_write_xmit(struct sock *); extern void tcp_time_wait(struct sock *); -extern void tcp_do_retransmit(struct sock *, int); +extern int tcp_retransmit_skb(struct sock *, struct sk_buff *); +extern void tcp_xmit_retransmit_queue(struct sock *); extern void tcp_simple_retransmit(struct sock *); /* tcp_output.c */ @@ -554,6 +563,7 @@ extern void tcp_write_wakeup(struct sock *); extern void tcp_send_fin(struct sock *sk); extern void tcp_send_active_reset(struct sock *sk); extern int tcp_send_synack(struct sock *); +extern void tcp_transmit_skb(struct sock *, struct sk_buff *); extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue); extern void tcp_send_ack(struct sock *sk); extern void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout); @@ -639,6 +649,32 @@ extern __inline__ int tcp_raise_window(struct sock *sk) return (new_win && (new_win > (cur_win << 1))); } +/* This is what the send packet queueing engine uses to pass + * TCP per-packet control information to the transmission + * code. + */ +struct tcp_skb_cb { + __u8 flags; /* TCP header flags. */ + + /* NOTE: These must match up to the flags byte in a + * real TCP header. + */ +#define TCPCB_FLAG_FIN 0x01 +#define TCPCB_FLAG_SYN 0x02 +#define TCPCB_FLAG_RST 0x04 +#define TCPCB_FLAG_PSH 0x08 +#define TCPCB_FLAG_ACK 0x10 +#define TCPCB_FLAG_URG 0x20 + + __u8 sacked; /* State flags for SACK/FACK. */ +#define TCPCB_SACKED_ACKED 0x01 /* SKB ACK'd by a SACK block */ +#define TCPCB_SACKED_RETRANS 0x02 /* SKB retransmitted */ + + __u16 urg_ptr; /* Valid w/URG flags is set. */ +}; + +#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0])) + /* This checks if the data bearing packet SKB (usually tp->send_head) * should be put on the wire right now. */ @@ -663,7 +699,7 @@ static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb) */ len = skb->end_seq - skb->seq; if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && - !skb->h.th->urg) + !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG)) nagle_check = 0; return (nagle_check && tp->packets_out < tp->snd_cwnd && @@ -739,72 +775,39 @@ static __inline__ void tcp_set_state(struct sock *sk, int state) } } -static __inline__ void tcp_build_options(__u32 *ptr, struct tcp_opt *tp) -{ - if (tp->tstamp_ok) { - *ptr = __constant_htonl((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_TIMESTAMP << 8) | - TCPOLEN_TIMESTAMP); - /* rest filled in by tcp_update_options */ - } -} - -static __inline__ void tcp_update_options(__u32 *ptr, struct tcp_opt *tp) -{ - if (tp->tstamp_ok) { - *++ptr = htonl(jiffies); - *++ptr = htonl(tp->ts_recent); - } -} - -static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp) +static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp, __u32 tstamp) { if (tp->tstamp_ok) { *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - *ptr++ = htonl(jiffies); - *ptr = htonl(tp->ts_recent); + *ptr++ = htonl(tstamp); + *ptr++ = htonl(tp->ts_recent); } -} - -/* - * This routines builds a generic TCP header. - * They also build the RFC1323 Timestamp, but don't fill the - * actual timestamp in (you need to call tcp_update_options for this). - * XXX: pass tp instead of sk here. - */ - -static inline void tcp_build_header_data(struct tcphdr *th, struct sock *sk, int push) -{ - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + if(tp->sack_ok && tp->num_sacks) { + int this_sack; - memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(tp->write_seq); - if (!push) - th->psh = 1; - tcp_build_options((__u32*)(th+1), tp); + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_SACK << 8) | + (TCPOLEN_SACK_BASE + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK))); + for(this_sack = 0; this_sack < tp->num_sacks; this_sack++) { + *ptr++ = htonl(tp->selective_acks[this_sack].start_seq); + *ptr++ = htonl(tp->selective_acks[this_sack].end_seq); + } + } } -/* - * Construct a tcp options header for a SYN or SYN_ACK packet. +/* Construct a tcp options header for a SYN or SYN_ACK packet. * If this is every changed make sure to change the definition of * MAX_SYN_SIZE to match the new maximum number of options that you * can generate. - * FIXME: This is completely disgusting. - * This is probably a good candidate for a bit of assembly magic. - * It would be especially magical to compute the checksum for this - * stuff on the fly here. */ -extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int ts, int offer_wscale, int wscale) +extern __inline__ void tcp_syn_build_options(__u32 *ptr, int mss, int ts, int sack, + int offer_wscale, int wscale, __u32 tstamp) { - int count = 4 + (offer_wscale ? TCPOLEN_WSCALE_ALIGNED : 0) + - ((ts) ? TCPOLEN_TSTAMP_ALIGNED : 0); - unsigned char *optr = skb_put(skb,count); - __u32 *ptr = (__u32 *)optr; - /* We always get an MSS option. * The option bytes which will be seen in normal data * packets should timestamps be used, must be in the MSS @@ -815,20 +818,26 @@ extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int ts * recognize data packets as being full sized when we * should, and thus we won't abide by the delayed ACK * rules correctly. + * SACKs don't matter, we never delay an ACK when we + * have any of those going out. */ if(ts) mss += TCPOLEN_TSTAMP_ALIGNED; *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss); if (ts) { - *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | - (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - *ptr++ = htonl(jiffies); /* TSVAL */ + if(sack) + *ptr++ = __constant_htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) | + (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); + else + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); + *ptr++ = htonl(tstamp); /* TSVAL */ *ptr++ = __constant_htonl(0); /* TSECR */ - } + } else if(sack) + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | + (TCPOPT_SACK_PERM << 8) | TCPOLEN_SACK_PERM); if (offer_wscale) - *ptr++ = htonl((TCPOPT_WINDOW << 24) | (TCPOLEN_WINDOW << 16) | (wscale << 8)); - skb->csum = csum_partial(optr, count, 0); - return count; + *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale)); } /* Determine a window scaling and initial window to offer. diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 23ed805a0..45bdcc59d 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -26,7 +26,7 @@ extern int datagram_recv_ctl(struct sock *sk, struct sk_buff *skb); extern int datagram_send_ctl(struct msghdr *msg, - struct device **src_dev, + int *oif, struct in6_addr **src_addr, struct ipv6_options *opt, int *hlimit); diff --git a/kernel/kmod.c b/kernel/kmod.c index a0f58d485..379c27695 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -9,24 +9,22 @@ #include <linux/types.h> #include <linux/unistd.h> -static inline _syscall1(int,delete_module,const char *,name_user) - /* kmod_unload_delay and modprobe_path are set via /proc/sys. */ int kmod_unload_delay = 60; char modprobe_path[256] = "/sbin/modprobe"; -char module_name[64] = ""; -char * argv[] = { "modprobe", "-k", NULL, NULL, }; -char * envp[] = { "HOME=/", "TERM=linux", NULL, }; +static char module_name[64] = ""; +static char * argv[] = { "modprobe", "-k", module_name, NULL, }; +static char * envp[] = { "HOME=/", "TERM=linux", NULL, }; /* kmod_queue synchronizes the kmod thread and the rest of the system kmod_unload_timer is what we use to unload modules after kmod_unload_delay seconds */ -struct wait_queue * kmod_queue = NULL; -struct timer_list kmod_unload_timer; +static struct wait_queue * kmod_queue = NULL; +static struct timer_list kmod_unload_timer; /* kmod_thread is the thread that does most of the work. kmod_unload and @@ -74,7 +72,6 @@ int kmod_thread(void * data) Call modprobe with module_name. If execve returns, print out an error. */ - argv[2] = module_name; execve(modprobe_path, argv, envp); printk("kmod: failed to load module %s\n", module_name); @@ -136,7 +133,8 @@ int request_module(const char * name) the module into module_name. Once that is done, wake up kmod_thread. */ - strcpy(module_name, name); + strncpy(module_name, name, sizeof(module_name)); + module_name[sizeof(module_name)-1] = '\0'; wake_up(&kmod_queue); /* diff --git a/kernel/sched.c b/kernel/sched.c index a86cb0413..8d8576808 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -333,20 +333,27 @@ void add_timer(struct timer_list *timer) static inline int detach_timer(struct timer_list *timer) { - int ret = 0; - struct timer_list *next, *prev; - next = timer->next; - prev = timer->prev; - if (next) { - next->prev = prev; - } + struct timer_list *prev = timer->prev; if (prev) { - ret = 1; + struct timer_list *next = timer->next; prev->next = next; + if (next) + next->prev = prev; + return 1; } - return ret; + return 0; } +void mod_timer(struct timer_list *timer, unsigned long expires) +{ + unsigned long flags; + + spin_lock_irqsave(&timerlist_lock, flags); + timer->expires = expires; + detach_timer(timer); + internal_add_timer(timer); + spin_unlock_irqrestore(&timerlist_lock, flags); +} int del_timer(struct timer_list * timer) { diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e6864541f..47bb36171 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -191,7 +191,7 @@ static ctl_table vm_table[] = { {VM_SWAPCTL, "swapctl", &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec}, {VM_SWAPOUT, "swapout_interval", - &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec_jiffies}, + &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec}, {VM_FREEPG, "freepages", &freepages, sizeof(freepages_t), 0600, NULL, &proc_dointvec}, {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL, @@ -201,6 +201,8 @@ static ctl_table vm_table[] = { sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec}, {VM_BUFFERMEM, "buffermem", &buffer_mem, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec}, + {VM_PAGECACHE, "pagecache", + &page_cache, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec}, {0} }; diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 8c3daf203..b2e72d584 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -112,13 +112,14 @@ static char * number(char * str, long num, int base, int size, int precision *str++ = ' '; if (sign) *str++ = sign; - if (type & SPECIAL) + if (type & SPECIAL) { if (base==8) *str++ = '0'; else if (base==16) { *str++ = '0'; *str++ = digits[33]; } + } if (!(type & LEFT)) while (size-- > 0) *str++ = c; @@ -281,12 +282,12 @@ int vsprintf(char *buf, const char *fmt, va_list args) } if (qualifier == 'l') num = va_arg(args, unsigned long); - else if (qualifier == 'h') + else if (qualifier == 'h') { if (flags & SIGN) num = va_arg(args, short); else num = va_arg(args, unsigned short); - else if (flags & SIGN) + } else if (flags & SIGN) num = va_arg(args, int); else num = va_arg(args, unsigned int); diff --git a/mm/filemap.c b/mm/filemap.c index 7a4e20e21..0971c63b7 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -150,6 +150,10 @@ int shrink_mmap(int priority, int gfp_mask) } tmp = tmp->b_this_page; } while (tmp != bh); + + /* Refuse to swap out all buffer pages */ + if ((buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.min_percent * num_physpages)) + goto next; } /* We can't throw away shared pages, but we do mark @@ -167,7 +171,7 @@ int shrink_mmap(int priority, int gfp_mask) break; } age_page(page); - if (page->age) + if (page->age || page_cache_size * 100 < (page_cache.min_percent * num_physpages)) break; if (PageSwapCache(page)) { delete_from_swap_cache(page); diff --git a/mm/mprotect.c b/mm/mprotect.c index a34225d83..0c5dac4cd 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -185,12 +185,12 @@ static int mprotect_fixup(struct vm_area_struct * vma, if (newflags == vma->vm_flags) return 0; newprot = protection_map[newflags & 0xf]; - if (start == vma->vm_start) + if (start == vma->vm_start) { if (end == vma->vm_end) error = mprotect_fixup_all(vma, newflags, newprot); else error = mprotect_fixup_start(vma, end, newflags, newprot); - else if (end == vma->vm_end) + } else if (end == vma->vm_end) error = mprotect_fixup_end(vma, start, newflags, newprot); else error = mprotect_fixup_middle(vma, start, end, newflags, newprot); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a3b1c0e8c..cb181e437 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -118,26 +118,33 @@ static spinlock_t page_alloc_lock; * * [previously, there had to be two entries of the highest memory * order, but this lead to problems on large-memory machines.] + * + * This will return zero if no list was found, non-zero + * if there was memory (the bigger, the better). */ -int free_memory_available(void) +int free_memory_available(int nr) { - int i, retval = 0; + int retval = 0; unsigned long flags; struct free_area_struct * list = NULL; + list = free_area + NR_MEM_LISTS; spin_lock_irqsave(&page_alloc_lock, flags); /* We fall through the loop if the list contains one * item. -- thanks to Colin Plumb <colin@nyx.net> */ - for (i = 1; i < 4; ++i) { - list = free_area + NR_MEM_LISTS - i; + do { + list--; + /* Empty list? Bad - we need more memory */ if (list->next == memory_head(list)) break; + /* One item on the list? Look further */ if (list->next->next == memory_head(list)) continue; - retval = 1; + /* More than one item? We're ok */ + retval = nr + 1; break; - } + } while (--nr >= 0); spin_unlock_irqrestore(&page_alloc_lock, flags); return retval; } @@ -275,7 +282,7 @@ repeat: spin_lock_irqsave(&page_alloc_lock, flags); RMQUEUE(order, maxorder, (gfp_mask & GFP_DMA)); spin_unlock_irqrestore(&page_alloc_lock, flags); - if ((gfp_mask & __GFP_WAIT) && try_to_free_page(gfp_mask)) + if ((gfp_mask & __GFP_WAIT) && try_to_free_pages(gfp_mask,SWAP_CLUSTER_MAX)) goto repeat; nopage: @@ -67,8 +67,13 @@ swap_control_t swap_control = { swapstat_t swapstats = {0}; buffer_mem_t buffer_mem = { - 6, /* minimum percent buffer + cache memory */ - 20, /* borrow percent buffer + cache memory */ - 90 /* maximum percent buffer + cache memory */ + 3, /* minimum percent buffer */ + 10, /* borrow percent buffer */ + 30 /* maximum percent buffer */ }; +buffer_mem_t page_cache = { + 10, /* minimum percent page cache */ + 30, /* borrow percent page cache */ + 75 /* maximum */ +}; diff --git a/mm/vmscan.c b/mm/vmscan.c index 5d4188ae5..0ad129a6b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -31,7 +31,7 @@ /* * When are we next due for a page scan? */ -static int next_swap_jiffies = 0; +static unsigned long next_swap_jiffies = 0; /* * How often do we do a pageout scan during normal conditions? @@ -44,11 +44,6 @@ int swapout_interval = HZ / 4; */ static struct wait_queue * kswapd_wait = NULL; -/* - * We avoid doing a reschedule if the pageout daemon is already awake; - */ -static int kswapd_awake = 0; - static void init_swap_timer(void); /* @@ -456,14 +451,14 @@ static inline int do_try_to_free_page(int gfp_mask) stop = 3; if (gfp_mask & __GFP_WAIT) stop = 0; - if (BUFFER_MEM > buffer_mem.borrow_percent * num_physpages / 100) + if (((buffermem >> PAGE_SHIFT) * 100 > buffer_mem.borrow_percent * num_physpages) + || (page_cache_size * 100 > page_cache.borrow_percent * num_physpages)) state = 0; switch (state) { do { case 0: - if (BUFFER_MEM > (buffer_mem.min_percent * num_physpages /100) && - shrink_mmap(i, gfp_mask)) + if (shrink_mmap(i, gfp_mask)) return 1; state = 1; case 1: @@ -545,30 +540,41 @@ int kswapd(void *unused) add_wait_queue(&kswapd_wait, &wait); while (1) { int tries; + int tried = 0; current->state = TASK_INTERRUPTIBLE; - kswapd_awake = 0; flush_signals(current); run_task_queue(&tq_disk); schedule(); - kswapd_awake = 1; swapstats.wakeups++; - /* Do the background pageout: - * When we've got loads of memory, we try - * (freepages.high - nr_free_pages) times to - * free memory. As memory gets tighter, kswapd - * gets more and more agressive. -- Rik. + + /* + * Do the background pageout: be + * more aggressive if we're really + * low on free memory. + * + * Normally this is called 4 times + * a second if we need more memory, + * so this has a normal rate of + * X*4 pages of memory free'd per + * second. That rate goes up when + * + * - we're really low on memory (we get woken + * up a lot more) + * - other processes fail to allocate memory, + * at which time they try to do their own + * freeing. + * + * A "tries" value of 50 means up to 200 pages + * per second (1.6MB/s). This should be a /proc + * thing. */ - tries = freepages.high - nr_free_pages; - if (tries < freepages.min) { - tries = freepages.min; - } - if (nr_free_pages < freepages.high + freepages.low) - tries <<= 1; + tries = (50 << 2) >> free_memory_available(3); + while (tries--) { int gfp_mask; - if (free_memory_available()) + if (++tried > SWAP_CLUSTER_MAX && free_memory_available(0)) break; gfp_mask = __GFP_IO; try_to_free_page(gfp_mask); @@ -589,27 +595,38 @@ int kswapd(void *unused) /* * The swap_tick function gets called on every clock tick. */ - void swap_tick(void) { - int want_wakeup = 0, memory_low = 0; - int pages = nr_free_pages + atomic_read(&nr_async_pages); + unsigned long now, want; + int want_wakeup = 0; - if (pages < freepages.low) - memory_low = want_wakeup = 1; - else if ((pages < freepages.high || BUFFER_MEM > (num_physpages * buffer_mem.max_percent / 100)) - && jiffies >= next_swap_jiffies) - want_wakeup = 1; + want = next_swap_jiffies; + now = jiffies; - if (want_wakeup) { - if (!kswapd_awake) { + /* + * Examine the memory queues. Mark memory low + * if there is nothing available in the three + * highest queues. + * + * Schedule for wakeup if there isn't lots + * of free memory. + */ + switch (free_memory_available(3)) { + case 0: + want = now; + /* Fall through */ + case 1 ... 3: + want_wakeup = 1; + default: + } + + if ((long) (now - want) >= 0) { + if (want_wakeup || (num_physpages * buffer_mem.max_percent) < (buffermem >> PAGE_SHIFT) * 100 + || (num_physpages * page_cache.max_percent < page_cache_size)) { + /* Set the next wake-up time */ + next_swap_jiffies = now + swapout_interval; wake_up(&kswapd_wait); - need_resched = 1; } - /* Set the next wake-up time */ - next_swap_jiffies = jiffies; - if (!memory_low) - next_swap_jiffies += swapout_interval; } timer_active |= (1<<SWAP_TIMER); } diff --git a/net/core/datagram.c b/net/core/datagram.c index cdab70aba..186ccf81b 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -132,15 +132,13 @@ restart: unsigned long flags; save_flags(flags); cli(); - skb=skb_peek(&sk->receive_queue); + skb = skb_peek(&sk->receive_queue); if(skb!=NULL) atomic_inc(&skb->users); restore_flags(flags); - if(skb==NULL) /* shouldn't happen but .. */ - goto restart; - return skb; - } - skb = skb_dequeue(&sk->receive_queue); + } else + skb = skb_dequeue(&sk->receive_queue); + if (!skb) /* Avoid race if someone beats us to the data */ goto restart; return skb; @@ -163,30 +161,23 @@ void skb_free_datagram(struct sock * sk, struct sk_buff *skb) int skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) { - int err; - err = copy_to_user(to, skb->h.raw+offset, size); - if (err) - { - err = -EFAULT; - } + int err = -EFAULT; + + if (!copy_to_user(to, skb->h.raw + offset, size)) + err = 0; return err; } /* * Copy a datagram to an iovec. + * Note: the iovec is modified during the copy. */ int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to, int size) { - int err; - err = memcpy_toiovec(to, skb->h.raw+offset, size); - if (err) - { - err = -EFAULT; - } - return err; + return memcpy_toiovec(to, skb->h.raw + offset, size); } /* diff --git a/net/core/dst.c b/net/core/dst.c index 4cad680c2..9007dde66 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -101,6 +101,14 @@ void * dst_alloc(int size, struct dst_ops * ops) void __dst_free(struct dst_entry * dst) { start_bh_atomic(); + /* The first case (dev==NULL) is required, when + protocol module is unloaded. + */ + if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) { + dst->input = dst_discard; + dst->output = dst_blackhole; + dst->dev = &loopback_dev; + } dst->obsolete = 2; dst->next = dst_garbage_list; dst_garbage_list = dst; diff --git a/net/core/iovec.c b/net/core/iovec.c index 9e8873646..5b684a48f 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -30,7 +30,6 @@ /* * Verify iovec - * verify area does a simple check for completly bogus addresses * * Save time not doing verify_area. copy_*_user will make this work * in any case. @@ -79,22 +78,21 @@ out_free: } /* - * Copy kernel to iovec. + * Copy kernel to iovec. Returns -EFAULT on error. * * Note: this modifies the original iovec. */ int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) { - int err; + int err = -EFAULT; while(len>0) { if(iov->iov_len) { int copy = min(iov->iov_len, len); - err = copy_to_user(iov->iov_base, kdata, copy); - if (err) + if (copy_to_user(iov->iov_base, kdata, copy)) goto out; kdata+=copy; len-=copy; @@ -109,7 +107,7 @@ out: } /* - * Copy iovec to kernel. + * Copy iovec to kernel. Returns -EFAULT on error. * * Note: this modifies the original iovec. */ @@ -147,35 +145,23 @@ int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, { int err = -EFAULT; - while(offset>0) + /* Skip over the finished iovecs */ + while(offset >= iov->iov_len) { - if (offset > iov->iov_len) - { - offset -= iov->iov_len; - } - else - { - u8 *base = iov->iov_base + offset; - int copy = min(len, iov->iov_len - offset); - - offset = 0; - - if (copy_from_user(kdata, base, copy)) - goto out; - len-=copy; - kdata+=copy; - } + offset -= iov->iov_len; iov++; } - while (len>0) + while (len > 0) { - int copy = min(len, iov->iov_len); + u8 *base = iov->iov_base + offset; + int copy = min(len, iov->iov_len - offset); - if (copy_from_user(kdata, iov->iov_base, copy)) + offset = 0; + if (copy_from_user(kdata, base, copy)) goto out; - len-=copy; - kdata+=copy; + len -= copy; + kdata += copy; iov++; } err = 0; @@ -195,51 +181,22 @@ out: int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, unsigned int len, int *csump) { - int partial_cnt = 0; - int err = 0; - int csum; + int csum = *csump; + int partial_cnt = 0, err = 0; - do { - int copy = iov->iov_len - offset; - - if (copy > 0) { - u8 *base = iov->iov_base + offset; - - /* Normal case (single iov component) is fastly detected */ - if (len <= copy) { - *csump = csum_and_copy_from_user(base, kdata, - len, *csump, &err); - goto out; - } - - partial_cnt = copy % 4; - if (partial_cnt) { - copy -= partial_cnt; - if (copy_from_user(kdata + copy, base + copy, - partial_cnt)) - goto out_fault; - } - - *csump = csum_and_copy_from_user(base, kdata, copy, - *csump, &err); - if (err) - goto out; - len -= copy + partial_cnt; - kdata += copy + partial_cnt; - iov++; - break; - } + /* Skip over the finished iovecs */ + while (offset >= iov->iov_len) + { + offset -= iov->iov_len; iov++; - offset = -copy; - } while (offset > 0); - - csum = *csump; + } while (len > 0) { - u8 *base = iov->iov_base; - unsigned int copy = min(len, iov->iov_len); + u8 *base = iov->iov_base + offset; + unsigned int copy = min(len, iov->iov_len - offset); + offset = 0; /* There is a remnant from previous iov. */ if (partial_cnt) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9180b8b54..57e58f85a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -16,6 +16,7 @@ * only put in the headers * Ray VanTassle : Fixed --skb->lock in free * Alan Cox : skb_copy copy arp field + * Andi Kleen : slabified it. * * NOTE: * The __skb_ routines should be called with interrupts @@ -45,6 +46,8 @@ #include <linux/netdevice.h> #include <linux/string.h> #include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/init.h> #include <net/ip.h> #include <net/protocol.h> @@ -57,6 +60,11 @@ #include <asm/system.h> /* + * Skb list spinlock + */ +spinlock_t skb_queue_lock = SPIN_LOCK_UNLOCKED; + +/* * Resource tracking variables */ @@ -66,6 +74,8 @@ static atomic_t net_fails = ATOMIC_INIT(0); extern atomic_t ip_frag_mem; +static kmem_cache_t *skbuff_head_cache; + /* * Strings we don't want inline's duplicating */ @@ -87,138 +97,119 @@ void show_net_buffers(void) #endif } -/* - * Free an sk_buff. Release anything attached to the buffer. - */ - -void __kfree_skb(struct sk_buff *skb) -{ - if (skb->list) - printk(KERN_WARNING "Warning: kfree_skb passed an skb still " - "on a list (from %p).\n", __builtin_return_address(0)); - - dst_release(skb->dst); - if(skb->destructor) - skb->destructor(skb); - kfree_skbmem(skb); -} - -/* - * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private' - * fields and also do memory statistics to find all the [BEEP] leaks. - * - * Note: For now we put the header after the data to get better cache - * usage. Once we have a good cache aware kmalloc this will cease - * to be a good idea. +/* Allocate a new skbuff. We do this ourselves so we can fill in a few + * 'private' fields and also do memory statistics to find all the + * [BEEP] leaks. + * */ struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) { struct sk_buff *skb; - unsigned char *bptr; - int len; + u8 *data; if (in_interrupt() && (gfp_mask & __GFP_WAIT)) { static int count = 0; if (++count < 5) { printk(KERN_ERR "alloc_skb called nonatomically " "from interrupt %p\n", __builtin_return_address(0)); - gfp_mask &= ~__GFP_WAIT; } + gfp_mask &= ~__GFP_WAIT; } - /* - * FIXME: We could do with an architecture dependent - * 'alignment mask'. - */ - - /* Allow for alignments. Make a multiple of 16 bytes */ - size = (size + 15) & ~15; - len = size; - - /* And stick the control itself on the end */ - size += sizeof(struct sk_buff); - - /* - * Allocate some space - */ - - bptr = kmalloc(size,gfp_mask); - if (bptr == NULL) { - atomic_inc(&net_fails); - return NULL; - } + /* Get the HEAD */ + skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask); + if (skb == NULL) + goto nohead; - /* - * Now we play a little game with the caches. Linux kmalloc is - * a bit cache dumb, in fact its just about maximally non - * optimal for typical kernel buffers. We actually run faster - * by doing the following. Which is to deliberately put the - * skb at the _end_ not the start of the memory block. + /* Get the DATA. Size must match skb_add_mtu(). */ + size = ((size + 15) & ~15); + data = kmalloc(size + sizeof(atomic_t), gfp_mask); + if (data == NULL) + goto nodata; + + /* Note that this counter is useless now - you can just look in the + * skbuff_head entry in /proc/slabinfo. We keep it only for emergency + * cases. */ atomic_inc(&net_allocs); - - skb = (struct sk_buff *)(bptr + size) - 1; - atomic_set(&skb->count, 1); /* only one reference to this */ - skb->data_skb = skb; /* and we're our own data skb */ + skb->truesize = size; + + atomic_inc(&net_skbcount); + + /* Load the data pointers. */ + skb->head = data; + skb->data = data; + skb->tail = data; + skb->end = data + size; + + /* Set up other state */ + skb->len = 0; + skb->is_clone = 0; + skb->cloned = 0; + atomic_set(&skb->users, 1); + atomic_set(skb_datarefp(skb), 1); + return skb; + +nodata: + kmem_cache_free(skbuff_head_cache, skb); +nohead: + atomic_inc(&net_fails); + return NULL; +} + + +/* + * Slab constructor for a skb head. + */ +static inline void skb_headerinit(void *p, kmem_cache_t *cache, + unsigned long flags) +{ + struct sk_buff *skb = p; + + skb->destructor = NULL; skb->pkt_type = PACKET_HOST; /* Default type */ skb->pkt_bridged = 0; /* Not bridged */ skb->prev = skb->next = NULL; skb->list = NULL; skb->sk = NULL; - skb->truesize=size; skb->stamp.tv_sec=0; /* No idea about time */ skb->ip_summed = 0; skb->security = 0; /* By default packets are insecure */ skb->dst = NULL; - skb->destructor = NULL; memset(skb->cb, 0, sizeof(skb->cb)); skb->priority = 0; - atomic_inc(&net_skbcount); - atomic_set(&skb->users, 1); - - /* Load the data pointers. */ - skb->head = bptr; - skb->data = bptr; - skb->tail = bptr; - skb->end = bptr + len; - skb->len = 0; - skb->inclone = 0; - return skb; } /* - * Free an skbuff by memory + * Free an skbuff by memory without cleaning the state. */ - -extern inline void __kfree_skbmem(struct sk_buff *skb) +void kfree_skbmem(struct sk_buff *skb) { - /* don't do anything if somebody still uses us */ - if (atomic_dec_and_test(&skb->count)) { + if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb))) kfree(skb->head); - atomic_dec(&net_skbcount); - } + + kmem_cache_free(skbuff_head_cache, skb); + atomic_dec(&net_skbcount); } -void kfree_skbmem(struct sk_buff *skb) -{ - void * addr = skb->head; +/* + * Free an sk_buff. Release anything attached to the buffer. Clean the state. + */ - /* don't do anything if somebody still uses us */ - if (atomic_dec_and_test(&skb->count)) { - int free_head = (skb->inclone != SKB_CLONE_INLINE); +void __kfree_skb(struct sk_buff *skb) +{ + if (skb->list) + printk(KERN_WARNING "Warning: kfree_skb passed an skb still " + "on a list (from %p).\n", __builtin_return_address(0)); - /* free the skb that contains the actual data if we've clone()'d */ - if (skb->data_skb != skb) { - addr = skb; - __kfree_skbmem(skb->data_skb); - } - if (free_head) - kfree(addr); - atomic_dec(&net_skbcount); - } + dst_release(skb->dst); + if(skb->destructor) + skb->destructor(skb); + skb_headerinit(skb, NULL, 0); /* clean state */ + kfree_skbmem(skb); } /* @@ -228,32 +219,24 @@ void kfree_skbmem(struct sk_buff *skb) struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) { struct sk_buff *n; - int inbuff = 0; - if (!skb->inclone && skb_tailroom(skb) >= sizeof(struct sk_buff)) { - n = ((struct sk_buff *) skb->end) - 1; - skb->end -= sizeof(struct sk_buff); - skb->inclone = SKB_CLONE_ORIG; - inbuff = SKB_CLONE_INLINE; - } else { - n = kmalloc(sizeof(*n), gfp_mask); - if (!n) - return NULL; - } + n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); + if (!n) + return NULL; + memcpy(n, skb, sizeof(*n)); - atomic_set(&n->count, 1); - skb = skb->data_skb; - atomic_inc(&skb->count); + atomic_inc(skb_datarefp(skb)); + skb->cloned = 1; + atomic_inc(&net_allocs); atomic_inc(&net_skbcount); dst_clone(n->dst); - n->data_skb = skb; + n->cloned = 1; n->next = n->prev = NULL; n->list = NULL; n->sk = NULL; - n->tries = 0; + n->is_clone = 1; atomic_set(&n->users, 1); - n->inclone = inbuff; n->destructor = NULL; return n; } @@ -287,6 +270,7 @@ struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask) skb_put(n,skb->len); /* Copy the bytes */ memcpy(n->head,skb->head,skb->end-skb->head); + n->csum = skb->csum; n->list=NULL; n->sk=NULL; n->when=skb->when; @@ -302,7 +286,7 @@ struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask) n->ack_seq=skb->ack_seq; memcpy(n->cb, skb->cb, sizeof(skb->cb)); n->used=skb->used; - n->tries=0; + n->is_clone=0; atomic_set(&n->users, 1); n->pkt_type=skb->pkt_type; n->stamp=skb->stamp; @@ -321,7 +305,7 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom) * Allocate the copy buffer */ - n=alloc_skb(skb->truesize+newheadroom-headroom-sizeof(struct sk_buff), GFP_ATOMIC); + n=alloc_skb(skb->truesize+newheadroom-headroom, GFP_ATOMIC); if(n==NULL) return NULL; @@ -352,7 +336,7 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom) n->end_seq=skb->end_seq; n->ack_seq=skb->ack_seq; n->used=skb->used; - n->tries=0; + n->is_clone=0; atomic_set(&n->users, 1); n->pkt_type=skb->pkt_type; n->stamp=skb->stamp; @@ -361,3 +345,27 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom) return n; } + +#if 0 +/* + * Tune the memory allocator for a new MTU size. + */ +void skb_add_mtu(int mtu) +{ + /* Must match allocation in alloc_skb */ + mtu = ((mtu + 15) & ~15) + sizeof(atomic_t); + + kmem_add_cache_size(mtu); +} +#endif + +__initfunc(void skb_init(void)) +{ + skbuff_head_cache = kmem_cache_create("skbuff_head_cache", + sizeof(struct sk_buff), + 0, + SLAB_HWCACHE_ALIGN, + skb_headerinit, NULL); + if (!skbuff_head_cache) + panic("cannot create skbuff cache"); +} diff --git a/net/core/sock.c b/net/core/sock.c index f940e5a80..7707c70d0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -663,31 +663,13 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne goto failure; /* - * FIXME: Check 1003.1g should we deliver - * a signal here ??? + * We should send SIGPIPE in these cases according to + * 1003.1g draft 6.4. If we (the user) did a shutdown() + * call however we should not. * - * Alan, could we solve this question once and forever? - * - * I believe, datagram sockets should never - * generate SIGPIPE. Moreover, I DO think that - * TCP is allowed to generate it only on write() - * call, but never on send/sendto/sendmsg. - * (btw, Solaris generates it even on read() :-)) - * - * The reason is that SIGPIPE is global flag, - * so that library function using sockets (f.e. syslog()), - * must save/disable it on entry and restore on exit. - * As result, signal arriving for another thread will - * be lost. Generation it on write() is still necessary - * because a lot of stupid programs never check write() - * return value. - * - * Seems, SIGPIPE is very bad idea, sort of gets(). - * At least, we could have an option disabling - * this behaviour on per-socket and/or per-message base. - * BTW it is very easy - MSG_SIGPIPE flag, which - * always set by read/write and checked here. - * --ANK + * Note: This routine isnt just used for datagrams and + * anyway some datagram protocols have a notion of + * close down. */ err = -EPIPE; @@ -699,7 +681,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne else { /* The buffer get won't block, or use the atomic queue. It does produce annoying no free page messages still.... */ - skb = sock_wmalloc(sk, size, 0 , GFP_BUFFER); + skb = sock_wmalloc(sk, size, 0, GFP_BUFFER); if (!skb) skb=sock_wmalloc(sk, fallback, 0, sk->allocation); } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ef1c44620..6667b8d72 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -274,7 +274,7 @@ static int inet_autobind(struct sock *sk) sk->num = sk->prot->good_socknum(); if (sk->num == 0) return(-EAGAIN); - sk->dummy_th.source = htons(sk->num); + sk->sport = htons(sk->num); sk->prot->hash(sk); add_to_prot_sklist(sk); } @@ -304,6 +304,7 @@ int inet_listen(struct socket *sock, int backlog) if (sk->state != TCP_LISTEN) { sk->ack_backlog = 0; sk->state = TCP_LISTEN; + dst_release(xchg(&sk->dst_cache, NULL)); sk->prot->rehash(sk); add_to_prot_sklist(sk); } @@ -348,7 +349,6 @@ static int inet_create(struct socket *sock, int protocol) switch (sock->type) { case SOCK_STREAM: - /* Note for tcp that also wiped the dummy_th block for us. */ if (protocol && protocol != IPPROTO_TCP) goto free_and_noproto; protocol = IPPROTO_TCP; @@ -412,17 +412,13 @@ static int inet_create(struct socket *sock, int protocol) sk->ip_mc_index=0; sk->ip_mc_list=NULL; - /* Speed up by setting some standard state for the dummy_th - * if TCP uses it (maybe move to tcp_init later) - */ - if (sk->num) { /* It assumes that any protocol which allows * the user to assign a number at socket * creation time automatically * shares. */ - sk->dummy_th.source = htons(sk->num); + sk->sport = htons(sk->num); /* Add to protocol hash chains. */ sk->prot->hash(sk); @@ -552,9 +548,9 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EADDRINUSE; sk->num = snum; - sk->dummy_th.source = htons(snum); + sk->sport = htons(snum); sk->daddr = 0; - sk->dummy_th.dest = 0; + sk->dport = 0; sk->prot->rehash(sk); add_to_prot_sklist(sk); dst_release(sk->dst_cache); @@ -753,13 +749,13 @@ static int inet_getname(struct socket *sock, struct sockaddr *uaddr, if (peer) { if (!tcp_connected(sk->state)) return(-ENOTCONN); - sin->sin_port = sk->dummy_th.dest; + sin->sin_port = sk->dport; sin->sin_addr.s_addr = sk->daddr; } else { __u32 addr = sk->rcv_saddr; if (!addr) addr = sk->saddr; - sin->sin_port = sk->dummy_th.source; + sin->sin_port = sk->sport; sin->sin_addr.s_addr = addr; } *uaddr_len = sizeof(*sin); @@ -798,7 +794,8 @@ int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size, struct sock *sk = sock->sk; if (sk->shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 1); + if (!(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 1); return(-EPIPE); } if (sk->prot->sendmsg == NULL) diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 7ec60a5be..cd9b5ba21 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: policy rules. * - * Version: $Id: fib_rules.c,v 1.3 1998/03/08 05:56:17 davem Exp $ + * Version: $Id: fib_rules.c,v 1.4 1998/03/21 07:27:58 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -86,7 +86,7 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) rtm->rtm_dst_len == r->r_dst_len && (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) && rtm->rtm_tos == r->r_tos && - rtm->rtm_type == r->r_action && + (!rtm->rtm_type || rtm->rtm_type == r->r_action) && (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) && (!rta[RTA_IFNAME-1] || strcmp(RTA_DATA(rta[RTA_IFNAME-1]), r->r_ifname) == 0) && (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index e6831adb8..21205362f 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.32 1998/03/08 05:56:21 davem Exp $ + * Version: $Id: ip_fragment.c,v 1.33 1998/03/19 08:34:08 davem Exp $ * * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> * Alan Cox <Alan.Cox@linux.org> @@ -430,11 +430,8 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) qp->ihlen = ihl; memcpy(qp->iph, iph, ihl+8); } - del_timer(&qp->timer); - qp->timer.expires = jiffies + sysctl_ipfrag_time; /* about 30 seconds */ - qp->timer.data = (unsigned long) qp; /* pointer to queue */ - qp->timer.function = ip_expire; /* expire function */ - add_timer(&qp->timer); + /* about 30 seconds */ + mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time); } else { /* If we failed to create it, then discard the frame. */ if ((qp = ip_create(skb, iph)) == NULL) { diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c index 4eb41c325..b364f66de 100644 --- a/net/ipv4/ip_fw.c +++ b/net/ipv4/ip_fw.c @@ -683,11 +683,6 @@ static int insert_in_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl, if ((ftmp->fw_vianame)[0]) { if (!(ftmp->fw_viadev = dev_get(ftmp->fw_vianame))) ftmp->fw_viadev = (struct device *) -1; - } else if (ftmp->fw_via.s_addr) { - if (!(ftmp->fw_viadev = ip_dev_find(ftmp->fw_via.s_addr))) - ftmp->fw_viadev = (struct device *) -1; - else - memcpy(ftmp->fw_vianame, ftmp->fw_viadev->name, IFNAMSIZ); } else ftmp->fw_viadev = NULL; @@ -732,11 +727,6 @@ static int append_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl, if ((ftmp->fw_vianame)[0]) { if (!(ftmp->fw_viadev = dev_get(ftmp->fw_vianame))) ftmp->fw_viadev = (struct device *) -1; - } else if (ftmp->fw_via.s_addr) { - if (!(ftmp->fw_viadev = ip_dev_find(ftmp->fw_via.s_addr))) - ftmp->fw_viadev = (struct device *) -1; - else - memcpy(ftmp->fw_vianame, ftmp->fw_viadev->name, IFNAMSIZ); } else ftmp->fw_viadev = NULL; diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c index dc367a289..cf92b1638 100644 --- a/net/ipv4/ip_masq.c +++ b/net/ipv4/ip_masq.c @@ -1819,13 +1819,9 @@ int ip_masq_ctl(int optname, void *arg, int arglen) struct ip_fw_masqctl *mctl = arg; int ret = EINVAL; - ip_masq_lockz(&__ip_masq_lock, &masq_wait, 0); - if (1) /* (mctl->mctl_action == IP_MASQ_MOD_CTL) */ ret = ip_masq_mod_ctl(optname, mctl, arglen); - ip_masq_unlockz(&__ip_masq_lock, &masq_wait, 0); - return ret; } diff --git a/net/ipv4/ip_masq_autofw.c b/net/ipv4/ip_masq_autofw.c index 30493d4cd..27b98bb03 100644 --- a/net/ipv4/ip_masq_autofw.c +++ b/net/ipv4/ip_masq_autofw.c @@ -119,10 +119,8 @@ static __inline__ void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, { if (af->flags & IP_AUTOFW_USETIME) { - if (af->timer.expires) - del_timer(&af->timer); - af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; - add_timer(&af->timer); + mod_timer(&af->timer, + jiffies+IP_AUTOFW_EXPIRE); } af->flags|=IP_AUTOFW_ACTIVE; af->lastcontact=where; @@ -139,9 +137,7 @@ static __inline__ void ip_autofw_update_in (__u32 where, __u16 port, __u16 proto af=ip_autofw_check_range(where, port,protocol); if (af) { - del_timer(&af->timer); - af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; - add_timer(&af->timer); + mod_timer(&af->timer, jiffies+IP_AUTOFW_EXPIRE); } } #endif diff --git a/net/ipv4/ip_masq_mod.c b/net/ipv4/ip_masq_mod.c index 2265161f3..f6a50dfc6 100644 --- a/net/ipv4/ip_masq_mod.c +++ b/net/ipv4/ip_masq_mod.c @@ -275,7 +275,7 @@ struct ip_masq_mod * ip_masq_mod_getbyname(const char *mmod_name) IP_MASQ_DEBUG(1, "searching mmod_name \"%s\"\n", mmod_name); - for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next) { + for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next_reg) { if (mmod->mmod_ctl && *(mmod_name) && (strcmp(mmod_name, mmod->mmod_name)==0)) { /* HIT */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 63fbbfe1e..69179738e 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -81,46 +81,24 @@ int sysctl_ip_dynaddr = 0; int ip_id_count = 0; -int ip_build_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr, - struct ip_options *opt) +/* Generate a checksum for an outgoing IP datagram. */ +__inline__ void ip_send_check(struct iphdr *iph) { - struct rtable *rt; - u32 final_daddr = daddr; + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); +} + +void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, + u32 saddr, u32 daddr, struct ip_options *opt) +{ + struct rtable *rt = (struct rtable *)skb->dst; struct iphdr *iph; - int err; - if (opt && opt->srr) - daddr = opt->faddr; - - err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) | - RTO_CONN | sk->localroute, sk->bound_dev_if); - if (err) - { - ip_statistics.IpOutNoRoutes++; - return err; - } - - if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) { - ip_rt_put(rt); - ip_statistics.IpOutNoRoutes++; - return -ENETUNREACH; - } - - skb->dst = dst_clone(&rt->u.dst); - skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15); - - /* - * Now build the IP header. - */ - - /* - * Build the IP addresses - */ - + /* Build the IP header. */ if (opt) - iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen); + iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen); else - iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr)); + iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr)); iph->version = 4; iph->ihl = 5; @@ -133,92 +111,19 @@ int ip_build_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr, iph->daddr = rt->rt_dst; iph->saddr = rt->rt_src; iph->protocol = sk->protocol; + iph->tot_len = htons(skb->len); + iph->id = htons(ip_id_count++); skb->nh.iph = iph; - skb->h.raw = (unsigned char*)(iph+1); - if (opt && opt->optlen) - { + if (opt && opt->optlen) { iph->ihl += opt->optlen>>2; - skb->h.raw += opt->optlen; - ip_options_build(skb, opt, final_daddr, rt, 0); + ip_options_build(skb, opt, daddr, rt, 0); } - - ip_rt_put(rt); - return 0; -} -/* - * This routine builds the appropriate hardware/IP headers for - * the routine. - */ -int ip_build_header(struct sk_buff *skb, struct sock *sk) -{ - struct rtable *rt; - struct ip_options *opt = sk->opt; - u32 daddr = sk->daddr; - u32 final_daddr = daddr; - struct iphdr *iph; - int err; - - if (opt && opt->srr) - daddr = opt->faddr; - - rt = (struct rtable*)sk->dst_cache; - - if (!rt || rt->u.dst.obsolete) { - sk->dst_cache = NULL; - ip_rt_put(rt); - err = ip_route_output(&rt, daddr, sk->saddr, RT_TOS(sk->ip_tos) | - RTO_CONN | sk->localroute, sk->bound_dev_if); - if (err) - return err; - sk->dst_cache = &rt->u.dst; - } - - if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) { - sk->dst_cache = NULL; - ip_rt_put(rt); - ip_statistics.IpOutNoRoutes++; - return -ENETUNREACH; - } - - skb->dst = dst_clone(sk->dst_cache); - skb_reserve(skb, MAX_HEADER); - - /* - * Now build the IP header. - */ - - /* - * Build the IP addresses - */ - - if (opt) - iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen); - else - iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr)); - - iph->version = 4; - iph->ihl = 5; - iph->tos = sk->ip_tos; - iph->frag_off = 0; - if (sk->ip_pmtudisc == IP_PMTUDISC_WANT && - !(rt->u.dst.mxlock&(1<<RTAX_MTU))) - iph->frag_off |= htons(IP_DF); - iph->ttl = sk->ip_ttl; - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; - iph->protocol = sk->protocol; - skb->nh.iph = iph; - skb->h.raw = (unsigned char*)(iph+1); - - if (!opt || !opt->optlen) - return 0; - iph->ihl += opt->optlen>>2; - skb->h.raw += opt->optlen; - ip_options_build(skb, opt, final_daddr, rt, 0); + ip_send_check(iph); - return 0; + /* Send it out. */ + skb->dst->output(skb); } int __ip_finish_output(struct sk_buff *skb) @@ -322,78 +227,101 @@ int ip_acct_output(struct sk_buff *skb) } #endif -/* - * Generate a checksum for an outgoing IP datagram. - */ - -void ip_send_check(struct iphdr *iph) -{ - iph->check = 0; - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); -} - - - -/* - * Queues a packet to be sent, and starts the transmitter if necessary. +/* Queues a packet to be sent, and starts the transmitter if necessary. * This routine also needs to put in the total length and compute the - * checksum + * checksum. We use to do this in two stages, ip_build_header() then + * this, but that scheme created a mess when routes disappeared etc. + * So we do it all here, and the TCP send engine has been changed to + * match. (No more unroutable FIN disasters, etc. wheee...) This will + * most likely make other reliable transport layers above IP easier + * to implement under Linux. */ - void ip_queue_xmit(struct sk_buff *skb) { struct sock *sk = skb->sk; - struct rtable *rt = (struct rtable*)skb->dst; + struct ip_options *opt = sk->opt; + struct rtable *rt; struct device *dev; + struct iphdr *iph; unsigned int tot_len; - struct iphdr *iph = skb->nh.iph; - tot_len = skb->len; - iph->tot_len = htons(tot_len); - iph->id = htons(ip_id_count++); + /* Make sure we can route this packet. */ + rt = (struct rtable *) sk->dst_cache; + if(rt == NULL || rt->u.dst.obsolete) { + u32 daddr; - if (rt->u.dst.obsolete) { - /* Ugly... ugly... but what can I do? - Essentially it is "ip_reroute_output" function. --ANK - */ - struct rtable *nrt; - if (ip_route_output(&nrt, rt->key.dst, rt->key.src, - rt->key.tos | RTO_CONN, - sk?sk->bound_dev_if:0)) - goto drop; - skb->dst = &nrt->u.dst; + sk->dst_cache = NULL; ip_rt_put(rt); - rt = nrt; + + /* Use correct destination address if we have options. */ + daddr = sk->daddr; + if(opt && opt->srr) + daddr = opt->faddr; + + /* If this fails, retransmit mechanism of transport layer will + * keep trying until route appears or the connection times itself + * out. + */ + if(ip_route_output(&rt, daddr, sk->saddr, + RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute, + sk->bound_dev_if)) + goto drop; + sk->dst_cache = &rt->u.dst; + } + if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) + goto no_route; + + /* We have a route, so grab a reference. */ + skb->dst = dst_clone(sk->dst_cache); + + /* OK, we know where to send it, allocate and build IP header. */ + iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + iph->version = 4; + iph->ihl = 5; + iph->tos = sk->ip_tos; + iph->frag_off = 0; + if(sk->ip_pmtudisc == IP_PMTUDISC_WANT && !(rt->u.dst.mxlock & (1 << RTAX_MTU))) + iph->frag_off |= __constant_htons(IP_DF); + iph->ttl = sk->ip_ttl; + iph->daddr = rt->rt_dst; + iph->saddr = rt->rt_src; + iph->protocol = sk->protocol; + skb->nh.iph = iph; + /* Transport layer set skb->h.foo itself. */ + + if(opt && opt->optlen) { + iph->ihl += opt->optlen >> 2; + ip_options_build(skb, opt, sk->daddr, rt, 0); } + tot_len = skb->len; + iph->tot_len = htons(tot_len); + iph->id = htons(ip_id_count++); + dev = rt->u.dst.dev; - if (call_out_firewall(PF_INET, dev, iph, NULL,&skb) < FW_ACCEPT) + if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT) goto drop; #ifdef CONFIG_NET_SECURITY - /* - * Add an IP checksum (must do this before SECurity because - * of possible tunneling) + /* Add an IP checksum (must do this before SECurity because + * of possible tunneling). */ - ip_send_check(iph); - - if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb)<FW_ACCEPT) + if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb) < FW_ACCEPT) goto drop; - iph = skb->nh.iph; - /* don't update tot_len, as the dev->mtu is already decreased */ + /* Don't update tot_len, as the dev->mtu is already decreased. */ #endif - + /* This can happen when the transport layer has segments queued + * with a cached route, and by the time we get here things are + * re-routed to a device with a different MTU than the original + * device. Sick, but we must cover it. + */ if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) { struct sk_buff *skb2; - /* ANK: It is almost impossible, but - * if you loaded module device with hh_len > MAX_HEADER, - * and if a route changed to this device, - * and if (uh...) TCP had segments queued on this route... - */ - skb2 = skb_realloc_headroom(skb, (dev->hard_header_len+15)&~15); + + skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15); kfree_skb(skb); if (skb2 == NULL) return; @@ -401,40 +329,35 @@ void ip_queue_xmit(struct sk_buff *skb) iph = skb->nh.iph; } - /* - * Do we need to fragment. Again this is inefficient. - * We need to somehow lock the original buffer and use - * bits of it. + /* Do we need to fragment. Again this is inefficient. We + * need to somehow lock the original buffer and use bits of it. */ - if (tot_len > rt->u.dst.pmtu) goto fragment; #ifndef CONFIG_NET_SECURITY - /* - * Add an IP checksum - */ - + /* Add an IP checksum. */ ip_send_check(iph); #endif - - if (sk) - skb->priority = sk->priority; + skb->priority = sk->priority; skb->dst->output(skb); return; fragment: - if ((iph->frag_off & htons(IP_DF))) - { + if ((iph->frag_off & htons(IP_DF)) != 0) { printk(KERN_DEBUG "sending pkt_too_big to self\n"); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(rt->u.dst.pmtu)); goto drop; } - ip_fragment(skb, skb->dst->output); return; +no_route: + sk->dst_cache = NULL; + ip_rt_put(rt); + ip_statistics.IpOutNoRoutes++; + /* Fall through... */ drop: kfree_skb(skb); } @@ -948,14 +871,7 @@ struct sk_buff * ip_reply(struct sk_buff *skb, int payload) reply->dst = &rt->u.dst; skb_reserve(reply, (rt->u.dst.dev->hard_header_len+15)&~15); - /* - * Now build the IP header. - */ - - /* - * Build the IP addresses - */ - + /* Now build the IP header. */ reply->nh.iph = iph = (struct iphdr *)skb_put(reply, iphlen); iph->version = 4; @@ -966,6 +882,7 @@ struct sk_buff * ip_reply(struct sk_buff *skb, int payload) iph->daddr = rt->rt_dst; iph->saddr = rt->rt_src; iph->protocol = skb->nh.iph->protocol; + iph->id = htons(ip_id_count++); ip_options_build(reply, &replyopts.opt, daddr, rt, 0); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 221207205..0ea231adf 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -59,7 +59,7 @@ static inline void get__openreq(struct sock *sk, struct open_request *req, " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu", i, (long unsigned int)req->af.v4_req.loc_addr, - ntohs(sk->dummy_th.source), + ntohs(sk->sport), (long unsigned int)req->af.v4_req.rmt_addr, req->rmt_port, TCP_SYN_RECV, @@ -83,8 +83,8 @@ static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format) dest = sp->daddr; src = sp->rcv_saddr; - destp = sp->dummy_th.dest; - srcp = sp->dummy_th.source; + destp = sp->dport; + srcp = sp->sport; /* FIXME: The fact that retransmit_timer occurs as a field * in two different parts of the socket structure is, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 8ce4a95f4..464090776 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -104,6 +104,7 @@ int ip_rt_redirect_load = HZ/50; int ip_rt_redirect_silence = ((HZ/50) << (9+1)); int ip_rt_error_cost = HZ; int ip_rt_error_burst = 5*HZ; +int ip_rt_gc_elasticity = 8; static unsigned long rt_deadline = 0; @@ -398,10 +399,10 @@ static int rt_garbage_collect(void) last_gc = now; if (atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh) - expire = ip_rt_gc_timeout; + expire = ip_rt_gc_timeout>>1; out: - expire >>= 1; + expire -= expire>>ip_rt_gc_elasticity; end_bh_atomic(); return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size); } @@ -1740,6 +1741,9 @@ ctl_table ipv4_route_table[] = { {NET_IPV4_ROUTE_ERROR_BURST, "error_burst", &ip_rt_error_burst, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_ROUTE_GC_ELASTICITY, "gc_elasticity", + &ip_rt_gc_elasticity, sizeof(int), 0644, NULL, + &proc_dointvec}, {0} }; #endif diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 767c5d00b..da64fc186 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -47,6 +47,7 @@ extern int sysctl_tcp_cong_avoidance; extern int sysctl_tcp_hoe_retransmits; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; +extern int sysctl_tcp_sack; extern int sysctl_tcp_keepalive_time; extern int sysctl_tcp_keepalive_probes; extern int sysctl_tcp_max_ka_probes; @@ -104,6 +105,9 @@ ctl_table ipv4_table[] = { {NET_IPV4_TCP_WINDOW_SCALING, "tcp_window_scaling", &sysctl_tcp_window_scaling, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_TCP_SACK, "tcp_sack", + &sysctl_tcp_sack, sizeof(int), 0644, NULL, + &proc_dointvec}, {NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid", &sysctl_tcp_cong_avoidance, sizeof(int), 0644, NULL, &tcp_sysctl_congavoid }, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b20df83d2..d57b7e3ef 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.96 1998/03/16 02:25:55 davem Exp $ + * Version: $Id: tcp.c,v 1.104 1998/03/22 22:10:30 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -668,7 +668,7 @@ static int wait_for_tcp_connect(struct sock * sk, int flags) return sock_error(sk); if((1 << sk->state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) { - if(sk->keepopen) + if(sk->keepopen && !(flags&MSG_NOSIGNAL)) send_sig(SIGPIPE, tsk, 0); return -EPIPE; } @@ -733,15 +733,25 @@ static void wait_for_tcp_memory(struct sock * sk) int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + int mss_now = sk->mss; int err = 0; int copied = 0; - struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); /* Wait for a connection to finish. */ if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) if((err = wait_for_tcp_connect(sk, flags)) != 0) return err; + /* The socket is locked, nothing can change the state of pending + * SACKs or IP options. + */ + if(tp->sack_ok && tp->num_sacks) + mss_now -= (TCPOLEN_SACK_BASE_ALIGNED + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)); + if(sk->opt && sk->opt->optlen) + mss_now -= (sk->opt->optlen); + /* Ok commence sending. */ while(--iovlen >= 0) { int seglen=iov->iov_len; @@ -769,22 +779,19 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) */ if (tp->send_head && !(flags & MSG_OOB)) { skb = sk->write_queue.prev; - copy = skb->tail - - ((unsigned char *)(skb->h.th) + - tp->tcp_header_len); - /* This window_seq test is somewhat dangerous - * If the remote does SWS avoidance we should + copy = skb->len; + /* If the remote does SWS avoidance we should * queue the best we can if not we should in * fact send multiple packets... - * a method for detecting this would be most - * welcome + * A method for detecting this would be most + * welcome. */ if (skb_tailroom(skb) > 0 && - (sk->mss - copy) > 0 && + (mss_now - copy) > 0 && tp->snd_nxt < skb->end_seq) { - int last_byte_was_odd = (copy & 1); + int last_byte_was_odd = (copy % 4); - copy = sk->mss - copy; + copy = mss_now - copy; if(copy > skb_tailroom(skb)) copy = skb_tailroom(skb); if(copy > seglen) @@ -793,12 +800,8 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) if(copy_from_user(skb_put(skb, copy), from, copy)) err = -EFAULT; - skb->csum = csum_partial( - (((unsigned char *)skb->h.th) + - tp->tcp_header_len), - (skb->tail - - (((unsigned char *)skb->h.th) + - tp->tcp_header_len)), 0); + skb->csum = csum_partial(skb->data, + skb->len, 0); } else { skb->csum = csum_and_copy_from_user( @@ -810,6 +813,8 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) from += copy; copied += copy; seglen -= copy; + if(!seglen && !iovlen) + TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH; continue; } } @@ -828,18 +833,17 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) */ copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); if(copy >= (tp->max_window >> 1)) - copy = min(copy, sk->mss); + copy = min(copy, mss_now); else - copy = sk->mss; + copy = mss_now; if(copy > seglen) copy = seglen; - tmp = MAX_HEADER + sk->prot->max_header + - sizeof(struct sk_buff) + 15; + tmp = MAX_HEADER + sk->prot->max_header + 15; queue_it = 0; - if (copy < min(sk->mss, tp->max_window >> 1) && + if (copy < min(mss_now, tp->max_window >> 1) && !(flags & MSG_OOB)) { - tmp += min(sk->mss, tp->max_window); + tmp += min(mss_now, tp->max_window); /* What is happening here is that we want to * tack on later members of the users iovec @@ -869,35 +873,34 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) continue; } - /* FIXME: we need to optimize this. - * Perhaps some hints here would be good. - */ - tmp = tp->af_specific->build_net_header(sk, skb); - if (tmp < 0) { - kfree_skb(skb); - err = tmp; - goto do_interrupted; - } - - skb->h.th =(struct tcphdr *) - skb_put(skb,tp->tcp_header_len); - seglen -= copy; - tcp_build_header_data(skb->h.th, sk, seglen || iovlen); + /* Prepare control bits for TCP header creation engine. */ + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | + ((!seglen && !iovlen) ? + TCPCB_FLAG_PSH : 0)); + TCP_SKB_CB(skb)->sacked = 0; if (flags & MSG_OOB) { - skb->h.th->urg = 1; - skb->h.th->urg_ptr = ntohs(copy); - } - + TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_URG; + TCP_SKB_CB(skb)->urg_ptr = copy; + } else + TCP_SKB_CB(skb)->urg_ptr = 0; + + /* TCP data bytes are SKB_PUT() on top, later + * TCP+IP+DEV headers are SKB_PUSH()'d beneath. + * Reserve header space and checksum the data. + */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); skb->csum = csum_and_copy_from_user(from, skb_put(skb, copy), copy, 0, &err); from += copy; copied += copy; - tp->write_seq += copy; + skb->seq = tp->write_seq; + skb->end_seq = skb->seq + copy; + /* This advances tp->write_seq for us. */ tcp_send_skb(sk, skb, queue_it); } } @@ -913,7 +916,8 @@ do_sock_err: do_shutdown: if(copied) return copied; - send_sig(SIGPIPE, current, 0); + if (!(flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 0); return -EPIPE; do_interrupted: if(copied) @@ -1044,9 +1048,20 @@ static void cleanup_rbuf(struct sock *sk, int copied) /* We send an ACK if we can now advertise a non-zero window * which has been raised "significantly". */ - if((copied > 0) && - (copied >= tcp_receive_window(&sk->tp_pinfo.af_tcp))) - tcp_read_wakeup(sk); + if(copied > 0) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + __u32 rcv_window_now = tcp_receive_window(tp); + + /* We won't be raising the window any further than + * the window-clamp allows. Our window selection + * also keeps things a nice multiple of MSS. These + * checks are necessary to prevent spurious ACKs + * which don't advertize a larger window. + */ + if((copied >= rcv_window_now) && + ((rcv_window_now + sk->mss) <= tp->window_clamp)) + tcp_read_wakeup(sk); + } } @@ -1319,12 +1334,8 @@ static int tcp_close_state(struct sock *sk, int dead) * that we won't make the old 4*rto = almost no time - whoops * reset mistake. */ - if(dead && ns==TCP_FIN_WAIT2) { - if(sk->timer.prev && del_timer(&sk->timer)) - add_timer(&sk->timer); - else - tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); - } + if(dead && ns == TCP_FIN_WAIT2 && !sk->timer.prev) + tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); return send_fin; } @@ -1448,12 +1459,8 @@ void tcp_close(struct sock *sk, unsigned long timeout) /* Now that the socket is dead, if we are in the FIN_WAIT2 state * we may need to set up a timer. */ - if (sk->state==TCP_FIN_WAIT2) { - if(sk->timer.prev && del_timer(&sk->timer)) - add_timer(&sk->timer); - else - tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); - } + if (sk->state == TCP_FIN_WAIT2 && !sk->timer.prev) + tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); sk->dead = 1; release_sock(sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4b7dcc9e9..1c34e6693 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.84 1998/03/15 03:23:20 davem Exp $ + * Version: $Id: tcp_input.c,v 1.98 1998/03/23 22:54:48 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -72,9 +72,10 @@ extern int sysctl_tcp_fin_timeout; */ int sysctl_tcp_timestamps = 1; int sysctl_tcp_window_scaling = 1; +int sysctl_tcp_sack = 1; +int sysctl_tcp_hoe_retransmits = 1; int sysctl_tcp_cong_avoidance; -int sysctl_tcp_hoe_retransmits; int sysctl_tcp_syncookies = SYNC_INIT; int sysctl_tcp_stdurg; int sysctl_tcp_rfc1337; @@ -177,7 +178,6 @@ static __inline__ void tcp_set_rto(struct tcp_opt *tp) * some modification to the RTO calculation that takes delayed * ack bais into account? This needs serious thought. -- erics */ - static __inline__ void tcp_bound_rto(struct tcp_opt *tp) { if (tp->rto > 120*HZ) @@ -187,7 +187,6 @@ static __inline__ void tcp_bound_rto(struct tcp_opt *tp) } /* WARNING: this must not be called if tp->saw_timestamp was false. */ - extern __inline__ void tcp_replace_ts_recent(struct tcp_opt *tp, __u32 end_seq) { /* From draft-ietf-tcplw-high-performance: the correct @@ -226,10 +225,7 @@ static int __tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq) return 0; } -/* - * This functions checks to see if the tcp header is actually acceptable. - */ - +/* This functions checks to see if the tcp header is actually acceptable. */ extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq) { if (seq == tp->rcv_nxt) @@ -238,11 +234,7 @@ extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq) return __tcp_sequence(tp, seq, end_seq); } -/* - * When we get a reset we do this. This probably is a tcp_output routine - * really. - */ - +/* When we get a reset we do this. */ static void tcp_reset(struct sock *sk, struct sk_buff *skb) { sk->zapped = 1; @@ -264,14 +256,36 @@ static void tcp_reset(struct sock *sk, struct sk_buff *skb) sk->state_change(sk); } -/* - * Look for tcp options. Normally only called on SYN and SYNACK packets. - * But, this can also be called on packets in the established flow when - * the fast version below fails. - * FIXME: surely this can be more efficient. -- erics +/* This tags the retransmission queue when SACKs arrive. */ +static void tcp_sacktag_write_queue(struct sock *sk, struct tcp_sack_block *sp, int nsacks) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + int i = nsacks; + + while(i--) { + struct sk_buff *skb = skb_peek(&sk->write_queue); + __u32 start_seq = ntohl(sp->start_seq); + __u32 end_seq = ntohl(sp->end_seq); + + while((skb != NULL) && + (skb != tp->send_head) && + (skb != (struct sk_buff *)&sk->write_queue)) { + /* We play conservative, we don't allow SACKS to partially + * tag a sequence space. + */ + if(!after(start_seq, skb->seq) && !before(end_seq, skb->end_seq)) + TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED; + skb = skb->next; + } + sp++; /* Move on to the next SACK block. */ + } +} + +/* Look for tcp options. Normally only called on SYN and SYNACK packets. + * But, this can also be called on packets in the established flow when + * the fast version below fails. */ - -void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) +void tcp_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp, int no_fancy) { unsigned char *ptr; int length=(th->doff*4)-sizeof(struct tcphdr); @@ -281,49 +295,68 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) while(length>0) { int opcode=*ptr++; - int opsize=*ptr++; - if (length - opsize < 0) /* Don't parse partial options */ - break; - switch(opcode) { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ - length--; - ptr--; /* the opsize=*ptr++ above was a mistake */ - continue; - - default: - if(opsize<=2) /* Avoid silly options looping forever */ - return; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize=*ptr++; + if (opsize < 2) /* "silly options" */ + return; + if (opsize > length) + break; /* don't parse partial options */ switch(opcode) { - case TCPOPT_MSS: - if(opsize==TCPOLEN_MSS && th->syn) { - tp->in_mss = ntohs(*(__u16 *)ptr); - if (tp->in_mss == 0) - tp->in_mss = 536; + case TCPOPT_MSS: + if(opsize==TCPOLEN_MSS && th->syn) { + tp->in_mss = ntohs(*(__u16 *)ptr); + if (tp->in_mss == 0) + tp->in_mss = 536; + } + break; + case TCPOPT_WINDOW: + if(opsize==TCPOLEN_WINDOW && th->syn) + if (!no_fancy && sysctl_tcp_window_scaling) { + tp->wscale_ok = 1; + tp->snd_wscale = *(__u8 *)ptr; } - break; - case TCPOPT_WINDOW: - if(opsize==TCPOLEN_WINDOW && th->syn) - if (!no_fancy && sysctl_tcp_window_scaling) { - tp->wscale_ok = 1; - tp->snd_wscale = *(__u8 *)ptr; - } - break; - case TCPOPT_TIMESTAMP: - if(opsize==TCPOLEN_TIMESTAMP) { - /* Cheaper to set again then to - * test syn. Optimize this? - */ - if (sysctl_tcp_timestamps && !no_fancy) { - tp->tstamp_ok = 1; - tp->saw_tstamp = 1; - tp->rcv_tsval = ntohl(*(__u32 *)ptr); - tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4)); - } + break; + case TCPOPT_TIMESTAMP: + if(opsize==TCPOLEN_TIMESTAMP) { + if (sysctl_tcp_timestamps && !no_fancy) { + tp->tstamp_ok = 1; + tp->saw_tstamp = 1; + tp->rcv_tsval = ntohl(*(__u32 *)ptr); + tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4)); + } + } + break; + case TCPOPT_SACK_PERM: + if(opsize==TCPOLEN_SACK_PERM && th->syn) { + if (sysctl_tcp_sack && !no_fancy) { + tp->sack_ok = 1; + tp->num_sacks = 0; + } + } + break; + + case TCPOPT_SACK: + if((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) && + sysctl_tcp_sack && (sk != NULL) && !th->syn) { + int sack_bytes = opsize - TCPOLEN_SACK_BASE; + + if(!(sack_bytes % TCPOLEN_SACK_PERBLOCK)) { + int num_sacks = sack_bytes >> 3; + struct tcp_sack_block *sackp; + + sackp = (struct tcp_sack_block *)ptr; + tcp_sacktag_write_queue(sk, sackp, num_sacks); } - break; - } + } + }; ptr+=opsize-2; length-=opsize; }; @@ -331,13 +364,11 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) } /* Fast parse options. This hopes to only see timestamps. - * If it is wrong it falls back on tcp_parse_option(). - * This should probably get extended for timestamps as well. - * Assembly code anyone? -- erics + * If it is wrong it falls back on tcp_parse_options(). */ -static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *tp) +static __inline__ int tcp_fast_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp) { - /* If we didn't send out any options ignore them all */ + /* If we didn't send out any options ignore them all. */ if (tp->tcp_header_len == sizeof(struct tcphdr)) return 0; if (th->doff == sizeof(struct tcphdr)>>2) { @@ -353,13 +384,14 @@ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt * return 1; } } - tcp_parse_options(th,tp,0); + tcp_parse_options(sk, th, tp, 0); return 1; } -#define FLAG_DATA 0x01 -#define FLAG_WIN_UPDATE 0x02 -#define FLAG_DATA_ACKED 0x04 +#define FLAG_DATA 0x01 /* Incoming frame contained data. */ +#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ +#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ +#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ static __inline__ void clear_fast_retransmit(struct sock *sk) { @@ -372,11 +404,9 @@ static __inline__ void clear_fast_retransmit(struct sock *sk) tp->dup_acks = 0; } -/* - * NOTE: This code assumes that tp->dup_acks gets cleared when a +/* NOTE: This code assumes that tp->dup_acks gets cleared when a * retransmit timer fires. */ - static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) { struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); @@ -407,7 +437,7 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); tp->snd_cwnd = tp->snd_ssthresh + 3; tp->high_seq = tp->snd_nxt; - tcp_do_retransmit(sk, 0); + tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } } @@ -425,7 +455,7 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) * block on duplicate fast retransmits, and if requested * we do Hoe style secondary fast retransmits. */ - if (!before(ack,tp->high_seq) || (not_dup&FLAG_DATA) != 0) { + if (!before(ack, tp->high_seq) || (not_dup & FLAG_DATA) != 0) { /* Once we have acked all the packets up to high_seq * we are done this fast retransmit phase. * Alternatively data arrived. In this case we @@ -438,7 +468,7 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) /* After we have cleared up to high_seq we can * clear the Floyd style block. */ - if (after(ack,tp->high_seq)) + if (after(ack, tp->high_seq)) tp->high_seq = 0; } else if (tp->dup_acks >= 3) { if (sysctl_tcp_hoe_retransmits) { @@ -455,10 +485,9 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) * the only way to get here without advancing * from snd_una is if this was a window update. */ - if (ack != tp->snd_una && before(ack,tp->high_seq)) { - tcp_do_retransmit(sk, 0); - tcp_reset_xmit_timer(sk, TIME_RETRANS, - tp->rto); + if (ack != tp->snd_una && before(ack, tp->high_seq)) { + tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); + tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } } else { /* Reno style. We didn't ack the whole @@ -589,9 +618,9 @@ static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt) } } - -static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, - __u32 *seq_rtt) +/* Remove acknowledged frames from the retransmission queue. */ +static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, + __u32 *seq, __u32 *seq_rtt) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *skb; @@ -600,8 +629,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) { /* If our packet is before the ack sequence we can - * discard it as it's confirmed to have arrived the - * other end. + * discard it as it's confirmed to have arrived at + * the other end. */ if (after(skb->end_seq, ack)) break; @@ -613,26 +642,22 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, * connection startup slow start one packet too * quickly. This is severely frowned upon behavior. */ - if(!skb->h.th->syn) - acked = FLAG_DATA_ACKED; - - /* FIXME: packet counting may break if we have to - * do packet "repackaging" for stacks that don't - * like overlapping packets. - */ + if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)) { + acked |= FLAG_DATA_ACKED; + if(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) + acked |= FLAG_RETRANS_DATA_ACKED; + } else { + tp->retrans_head = NULL; + } tp->packets_out--; - *seq = skb->seq; *seq_rtt = now - skb->when; - skb_unlink(skb); - kfree_skb(skb); } if (acked) tp->retrans_head = NULL; - return acked; } @@ -686,41 +711,23 @@ static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp, static void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) { - struct sk_buff *skb; - long when; - - skb = skb_peek(&sk->write_queue); - when = tp->rto - (jiffies - skb->when); - - /* FIXME: This assumes that when we are retransmitting - * we should only ever respond with one packet. - * This means congestion windows should not grow - * during recovery. In 2.0.X we allow the congestion - * window to grow. It is not clear to me which - * decision is correct. The RFCs should be double - * checked as should the behavior of other stacks. - * Also note that if we do want to allow the - * congestion window to grow during retransmits - * we have to fix the call to congestion window - * updates so that it works during retransmission. + struct sk_buff *skb = skb_peek(&sk->write_queue); + long when = tp->rto - (jiffies - skb->when); + + /* Some data was ACK'd, if still retransmitting (due to a + * timeout), resend more of the retransmit queue. The + * congestion window is handled properly by that code. */ if (tp->retransmits) { tp->retrans_head = NULL; - - /* This is tricky. We are retransmiting a - * segment of a window when congestion occured. - */ - tcp_do_retransmit(sk, 0); + tcp_xmit_retransmit_queue(sk); tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } else { tcp_reset_xmit_timer(sk, TIME_RETRANS, when); } } -/* - * This routine deals with incoming acks, but not outgoing ones. - */ - +/* This routine deals with incoming acks, but not outgoing ones. */ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack_seq, u32 ack, int len) { @@ -805,7 +812,8 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, * where the network delay has increased suddenly. * I.e. Karn's algorithm. (SIGCOMM '87, p5.) */ - if (flag & FLAG_DATA_ACKED) { + if ((flag & FLAG_DATA_ACKED) && + !(flag & FLAG_RETRANS_DATA_ACKED)) { tp->backoff = 0; tcp_rtt_estimator(tp, seq_rtt); tcp_set_rto(tp); @@ -923,9 +931,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, } else { if(th->ack) { /* In this case we must reset the TIMEWAIT timer. */ - del_timer(&tw->timer); - tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN; - add_timer(&tw->timer); + mod_timer(&tw->timer, jiffies + TCP_TIMEWAIT_LEN); } } return 0; /* Discard the frame. */ @@ -981,9 +987,10 @@ void tcp_time_wait(struct sock *sk) tw->bound_dev_if= sk->bound_dev_if; tw->num = sk->num; tw->state = TCP_TIME_WAIT; + tw->sport = sk->sport; + tw->dport = sk->dport; tw->family = sk->family; - tw->source = sk->dummy_th.source; - tw->dest = sk->dummy_th.dest; + tw->reuse = sk->reuse; tw->rcv_nxt = sk->tp_pinfo.af_tcp.rcv_nxt; tw->af_specific = sk->tp_pinfo.af_tcp.af_specific; @@ -1098,6 +1105,175 @@ static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) }; } +/* These routines update the SACK block as out-of-order packets arrive or + * in-order packets close up the sequence space. + */ +static void tcp_sack_maybe_coalesce(struct tcp_opt *tp, struct tcp_sack_block *sp) +{ + int this_sack, num_sacks = tp->num_sacks; + struct tcp_sack_block *swalk = &tp->selective_acks[0]; + + /* If more than one SACK block, see if the recent change to SP eats into + * or hits the sequence space of other SACK blocks, if so coalesce. + */ + if(num_sacks != 1) { + for(this_sack = 0; this_sack < num_sacks; this_sack++, swalk++) { + if(swalk == sp) + continue; + + /* First case, bottom of SP moves into top of the + * sequence space of SWALK. + */ + if(between(sp->start_seq, swalk->start_seq, swalk->end_seq)) { + sp->start_seq = swalk->start_seq; + goto coalesce; + } + /* Second case, top of SP moves into bottom of the + * sequence space of SWALK. + */ + if(between(sp->end_seq, swalk->start_seq, swalk->end_seq)) { + sp->end_seq = swalk->end_seq; + goto coalesce; + } + } + } + /* SP is the only SACK, or no coalescing cases found. */ + return; + +coalesce: + /* Zap SWALK, by moving every further SACK up by one slot. + * Decrease num_sacks. + */ + for(this_sack += 1; this_sack < num_sacks; this_sack++, swalk++) { + struct tcp_sack_block *next = (swalk + 1); + swalk->start_seq = next->start_seq; + swalk->end_seq = next->end_seq; + } + tp->num_sacks--; +} + +static __inline__ void tcp_sack_swap(struct tcp_sack_block *sack1, struct tcp_sack_block *sack2) +{ + __u32 tmp; + + tmp = sack1->start_seq; + sack1->start_seq = sack2->start_seq; + sack2->start_seq = tmp; + + tmp = sack1->end_seq; + sack1->end_seq = sack2->end_seq; + sack2->end_seq = tmp; +} + +static void tcp_sack_new_ofo_skb(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_sack_block *sp = &tp->selective_acks[0]; + + /* Optimize for the common case, new ofo frames arrive + * "in order". ;-) This also satisfies the requirements + * of RFC2018 about ordering of SACKs. + */ + if(sp->end_seq == skb->seq) { + sp->end_seq = skb->end_seq; + tcp_sack_maybe_coalesce(tp, sp); + } else if(sp->start_seq == skb->end_seq) { + /* Re-ordered arrival, in this case, can be optimized + * as well. + */ + sp->start_seq = skb->seq; + tcp_sack_maybe_coalesce(tp, sp); + } else { + int cur_sacks = tp->num_sacks; + int max_sacks = (tp->tstamp_ok ? 3 : 4); + + /* Oh well, we have to move things around. + * Try to find a SACK we can tack this onto. + */ + if(cur_sacks > 1) { + struct tcp_sack_block *swap = sp + 1; + int this_sack; + + for(this_sack = 1; this_sack < cur_sacks; this_sack++, swap++) { + if((swap->end_seq == skb->seq) || + (swap->start_seq == skb->end_seq)) { + if(swap->end_seq == skb->seq) + swap->end_seq = skb->end_seq; + else + swap->start_seq = skb->seq; + tcp_sack_swap(sp, swap); + tcp_sack_maybe_coalesce(tp, sp); + return; + } + } + } + + /* Could not find an adjacent existing SACK, build a new one, + * put it at the front, and shift everyone else down. We + * always know there is at least one SACK present already here. + */ + while(cur_sacks >= 1) { + struct tcp_sack_block *this = &tp->selective_acks[cur_sacks]; + struct tcp_sack_block *prev = (this - 1); + this->start_seq = prev->start_seq; + this->end_seq = prev->end_seq; + cur_sacks--; + } + + /* Build head SACK, and we're done. */ + sp->start_seq = skb->seq; + sp->end_seq = skb->end_seq; + if(tp->num_sacks < max_sacks) + tp->num_sacks++; + } +} + +static void tcp_sack_remove_skb(struct tcp_opt *tp, struct sk_buff *skb) +{ + struct tcp_sack_block *sp = &tp->selective_acks[0]; + int num_sacks = tp->num_sacks; + int this_sack; + + /* We know this removed SKB will eat from the front of a SACK. */ + for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) { + if(sp->start_seq == skb->seq) + break; + } + + /* This should only happen if so many SACKs get built that some get + * pushed out before we get here, or we eat some in sequence packets + * which are before the first SACK block. + */ + if(this_sack >= num_sacks) + return; + + sp->start_seq = skb->end_seq; + if(!before(sp->start_seq, sp->end_seq)) { + /* Zap this SACK, by moving forward any other SACKS. */ + for(this_sack += 1; this_sack < num_sacks; this_sack++, sp++) { + struct tcp_sack_block *next = (sp + 1); + sp->start_seq = next->start_seq; + sp->end_seq = next->end_seq; + } + tp->num_sacks--; + } +} + +static void tcp_sack_extend(struct tcp_opt *tp, struct sk_buff *old_skb, struct sk_buff *new_skb) +{ + struct tcp_sack_block *sp = &tp->selective_acks[0]; + int num_sacks = tp->num_sacks; + int this_sack; + + for(this_sack = 0; this_sack < num_sacks; this_sack++, tp++) { + if(sp->end_seq == old_skb->end_seq) + break; + } + if(this_sack >= num_sacks) + return; + sp->end_seq = new_skb->end_seq; +} + /* This one checks to see if we can put data from the * out_of_order queue into the receive_queue. */ @@ -1119,6 +1295,8 @@ static void tcp_ofo_queue(struct sock *sk) SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n", tp->rcv_nxt, skb->seq, skb->end_seq); + if(tp->sack_ok) + tcp_sack_remove_skb(tp, skb); skb_unlink(skb); skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = skb->end_seq; @@ -1142,13 +1320,23 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) dst_confirm(sk->dst_cache); skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = skb->end_seq; - if(skb->h.th->fin) + if(skb->h.th->fin) { tcp_fin(skb, sk, skb->h.th); - else + } else { tp->delayed_acks++; + + /* Tiny-grams with PSH set make us ACK quickly. */ + if(skb->h.th->psh && (skb->len < (sk->mss >> 1))) + tp->ato = HZ/50; + } + /* This may have eaten into a SACK block. */ + if(tp->sack_ok && tp->num_sacks) + tcp_sack_remove_skb(tp, skb); tcp_ofo_queue(sk); if (skb_queue_len(&tp->out_of_order_queue) == 0) - tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd); + tp->pred_flags = htonl(((tp->tcp_header_len >> 2) << 28) | + (0x10 << 16) | + tp->snd_wnd); return; } @@ -1180,25 +1368,44 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) tp->rcv_nxt, skb->seq, skb->end_seq); if (skb_peek(&tp->out_of_order_queue) == NULL) { + /* Initial out of order segment, build 1 SACK. */ + if(tp->sack_ok) { + tp->num_sacks = 1; + tp->selective_acks[0].start_seq = skb->seq; + tp->selective_acks[0].end_seq = skb->end_seq; + } skb_queue_head(&tp->out_of_order_queue,skb); } else { for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) { /* Already there. */ - if (skb->seq == skb1->seq && skb->len >= skb1->len) { - skb_append(skb1, skb); - skb_unlink(skb1); - kfree_skb(skb1); + if (skb->seq == skb1->seq) { + if (skb->len >= skb1->len) { + if(tp->sack_ok) + tcp_sack_extend(tp, skb1, skb); + skb_append(skb1, skb); + skb_unlink(skb1); + kfree_skb(skb1); + } else { + /* A duplicate, smaller than what is in the + * out-of-order queue right now, toss it. + */ + kfree_skb(skb); + } break; } if (after(skb->seq, skb1->seq)) { skb_append(skb1,skb); + if(tp->sack_ok) + tcp_sack_new_ofo_skb(sk, skb); break; } /* See if we've hit the start. If so insert. */ if (skb1 == skb_peek(&tp->out_of_order_queue)) { skb_queue_head(&tp->out_of_order_queue,skb); + if(tp->sack_ok) + tcp_sack_new_ofo_skb(sk, skb); break; } } @@ -1244,8 +1451,8 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len) static void tcp_data_snd_check(struct sock *sk) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *skb; - struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); if ((skb = tp->send_head)) { if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) && @@ -1273,6 +1480,7 @@ static __inline__ void __tcp_ack_snd_check(struct sock *sk) * - delay time <= 0.5 HZ * - we don't have a window update to send * - must send at least every 2 full sized packets + * - must send an ACK if we have any SACKs * * With an extra heuristic to handle loss of packet * situations and also helping the sender leave slow @@ -1283,8 +1491,10 @@ static __inline__ void __tcp_ack_snd_check(struct sock *sk) if (((tp->rcv_nxt - tp->rcv_wup) >= (sk->mss << 1)) || /* We will update the window "significantly" or... */ tcp_raise_window(sk) || - /* We entered "quick ACK" mode */ - tcp_in_quickack_mode(tp)) { + /* We entered "quick ACK" mode or... */ + tcp_in_quickack_mode(tp) || + /* We have pending SACKs */ + (tp->sack_ok && tp->num_sacks)) { /* Then ack it now */ tcp_send_ack(sk); } else { @@ -1446,7 +1656,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, /* * RFC1323: H1. Apply PAWS check first. */ - if (tcp_fast_parse_options(th,tp)) { + if (tcp_fast_parse_options(sk, th, tp)) { if (tp->saw_tstamp) { if (tcp_paws_discard(tp)) { if (!th->rst) { @@ -1460,10 +1670,10 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, flg = *(((u32 *)th) + 3); - /* - * pred_flags is 0x5?10 << 16 + snd_wnd + /* pred_flags is 0xS?10 << 16 + snd_wnd * if header_predition is to be made - * ? will be 0 else it will be !0 + * 'S' will always be tp->tcp_header_len >> 2 + * '?' will be 0 else it will be !0 * (when there are holes in the receive * space for instance) */ @@ -1498,6 +1708,11 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, */ sk->data_ready(sk, 0); tcp_delack_estimator(tp); + + /* Tiny-grams with PSH set make us ACK quickly. */ + if(th->psh && (skb->len < (sk->mss >> 1))) + tp->ato = HZ/50; + tp->delayed_acks++; __tcp_ack_snd_check(sk); return 0; @@ -1703,7 +1918,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->fin_seq = skb->seq; tcp_set_state(sk, TCP_ESTABLISHED); - tcp_parse_options(th,tp,0); + tcp_parse_options(sk, th, tp, 0); if (tp->wscale_ok == 0) { tp->snd_wscale = tp->rcv_wscale = 0; @@ -1712,7 +1927,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (tp->tstamp_ok) { tp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; - sk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); } else tp->tcp_header_len = sizeof(struct tcphdr); if (tp->saw_tstamp) { @@ -1745,7 +1959,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, sk->mss = min(sk->mss, real_mss); } - sk->dummy_th.dest = th->source; + sk->dport = th->source; tp->copied_seq = tp->rcv_nxt; if(!sk->dead) { @@ -1763,7 +1977,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * tcp_connect. */ tcp_set_state(sk, TCP_SYN_RECV); - tcp_parse_options(th,tp,0); + tcp_parse_options(sk, th, tp, 0); if (tp->saw_tstamp) { tp->ts_recent = tp->rcv_tsval; tp->ts_recent_stamp = jiffies; @@ -1788,7 +2002,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * Note that this really has to be here and not later for PAWS * (RFC1323) to work. */ - if (tcp_fast_parse_options(th,tp)) { + if (tcp_fast_parse_options(sk, th, tp)) { /* NOTE: assumes saw_tstamp is never set if we didn't * negotiate the option. tcp_fast_parse_options() must * guarantee this. @@ -1849,7 +2063,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, case TCP_SYN_RECV: if (acceptable) { tcp_set_state(sk, TCP_ESTABLISHED); - sk->dummy_th.dest=th->source; + sk->dport = th->source; tp->copied_seq = tp->rcv_nxt; if(!sk->dead) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 91f21ff75..ee53f47d6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.109 1998/03/15 07:24:15 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.119 1998/03/22 19:14:47 davem Exp $ * * IPv4 specific functions * @@ -62,16 +62,12 @@ extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; +extern int sysctl_tcp_sack; extern int sysctl_tcp_syncookies; extern int sysctl_ip_dynaddr; /* Check TCP sequence numbers in ICMP packets. */ -#define ICMP_PARANOIA 1 -#ifndef ICMP_PARANOIA -#define ICMP_MIN_LENGTH 4 -#else #define ICMP_MIN_LENGTH 8 -#endif static void tcp_v4_send_reset(struct sk_buff *skb); @@ -120,7 +116,7 @@ static __inline__ int tcp_sk_hashfn(struct sock *sk) __u32 laddr = sk->rcv_saddr; __u16 lport = sk->num; __u32 faddr = sk->daddr; - __u16 fport = sk->dummy_th.dest; + __u16 fport = sk->dport; return tcp_hashfn(laddr, lport, faddr, fport); } @@ -365,7 +361,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, sk = TCP_RHASH(sport); if(sk && sk->daddr == saddr && /* remote address */ - sk->dummy_th.dest == sport && /* remote port */ + sk->dport == sport && /* remote port */ sk->num == hnum && /* local port */ sk->rcv_saddr == daddr && /* local address */ (!sk->bound_dev_if || sk->bound_dev_if == dif)) @@ -377,7 +373,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, hash = tcp_hashfn(daddr, hnum, saddr, sport); for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { if(sk->daddr == saddr && /* remote address */ - sk->dummy_th.dest == sport && /* remote port */ + sk->dport == sport && /* remote port */ sk->num == hnum && /* local port */ sk->rcv_saddr == daddr && /* local address */ (!sk->bound_dev_if || sk->bound_dev_if == dif)) { @@ -389,7 +385,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, /* Must check for a TIME_WAIT'er before going to listener hash. */ for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { if(sk->daddr == saddr && /* remote address */ - sk->dummy_th.dest == sport && /* remote port */ + sk->dport == sport && /* remote port */ sk->num == hnum && /* local port */ sk->rcv_saddr == daddr && /* local address */ (!sk->bound_dev_if || sk->bound_dev_if == dif)) @@ -456,8 +452,8 @@ pass2: continue; score++; } - if(s->dummy_th.dest) { - if(s->dummy_th.dest != rnum) + if(s->dport) { + if(s->dport != rnum) continue; score++; } @@ -496,12 +492,7 @@ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) skb->h.th->source); } -/* - * From tcp.c - */ - -/* - * Check that a TCP address is unique, don't allow multiple +/* Check that a TCP address is unique, don't allow multiple * connects to/from the same address. Actually we can optimize * quite a bit, since the socket about to connect is still * in TCP_CLOSE, a tcp_bind_bucket for the local port he will @@ -509,8 +500,7 @@ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) * The good_socknum and verify_bind scheme we use makes this * work. */ - -static int tcp_unique_address(struct sock *sk) +static int tcp_v4_unique_address(struct sock *sk) { struct tcp_bind_bucket *tb; unsigned short snum = sk->num; @@ -524,7 +514,7 @@ static int tcp_unique_address(struct sock *sk) /* Almost certainly the re-use port case, search the real hashes * so it actually scales. */ - sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dummy_th.dest, + sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport, sk->rcv_saddr, snum, sk->bound_dev_if); if((sk != NULL) && (sk->state != TCP_LISTEN)) retval = 0; @@ -535,19 +525,15 @@ static int tcp_unique_address(struct sock *sk) return retval; } - -/* - * This will initiate an outgoing connection. - */ - +/* This will initiate an outgoing connection. */ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { - struct sk_buff *buff; - int tmp; - struct tcphdr *th; - struct rtable *rt; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; + struct sk_buff *buff; + struct rtable *rt; + int tmp; + int mss; if (sk->state != TCP_CLOSE) return(-EISCONN); @@ -567,8 +553,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm); } - dst_release(xchg(&sk->dst_cache, NULL)); - tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr, RT_TOS(sk->ip_tos)|sk->localroute, sk->bound_dev_if); if (tmp < 0) @@ -579,143 +563,52 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -ENETUNREACH; } - if (!tcp_unique_address(sk)) { - ip_rt_put(rt); - return -EADDRNOTAVAIL; - } - - lock_sock(sk); + dst_release(xchg(&sk->dst_cache, rt)); - /* Do this early, so there is less state to unwind on failure. */ - buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)), + buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header), 0, GFP_KERNEL); - if (buff == NULL) { - release_sock(sk); - ip_rt_put(rt); - return(-ENOBUFS); - } - sk->dst_cache = &rt->u.dst; + if (buff == NULL) + return -ENOBUFS; + + /* Socket has no identity, so lock_sock() is useless. Also + * since state==TCP_CLOSE (checked above) the socket cannot + * possibly be in the hashes. TCP hash locking is only + * needed while checking quickly for a unique address. + * However, the socket does need to be (and is) locked + * in tcp_connect(). + * Perhaps this addresses all of ANK's concerns. 8-) -DaveM + */ + sk->dport = usin->sin_port; sk->daddr = rt->rt_dst; if (!sk->saddr) sk->saddr = rt->rt_src; sk->rcv_saddr = sk->saddr; - if (sk->priority == 0) - sk->priority = rt->u.dst.priority; - - sk->dummy_th.dest = usin->sin_port; - - tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr, - sk->dummy_th.source, - usin->sin_port); - tp->snd_wnd = 0; - tp->snd_wl1 = 0; - tp->snd_wl2 = tp->write_seq; - tp->snd_una = tp->write_seq; - tp->rcv_nxt = 0; - - sk->err = 0; - - /* Put in the IP header and routing stuff. */ - tmp = ip_build_header(buff, sk); - if (tmp < 0) { - /* Caller has done ip_rt_put(rt) and set sk->dst_cache - * to NULL. We must unwind the half built TCP socket - * state so that this failure does not create a "stillborn" - * sock (ie. future re-tries of connect() would fail). - */ - sk->daddr = 0; - sk->saddr = sk->rcv_saddr = 0; + if (!tcp_v4_unique_address(sk)) { kfree_skb(buff); - release_sock(sk); - return(-ENETUNREACH); + return -EADDRNOTAVAIL; } - /* No failure conditions can result past this point. */ - - /* We'll fix this up when we get a response from the other end. - * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT. - */ - tp->tcp_header_len = sizeof(struct tcphdr) + - (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0); - - th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); - buff->h.th = th; - - memcpy(th,(void *)&(sk->dummy_th), sizeof(*th)); - /* th->doff gets fixed up below if we tack on options. */ - - buff->seq = tp->write_seq++; - th->seq = htonl(buff->seq); - tp->snd_nxt = tp->write_seq; - buff->end_seq = tp->write_seq; - th->ack = 0; - th->syn = 1; - sk->mtu = rt->u.dst.pmtu; if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT || (sk->ip_pmtudisc == IP_PMTUDISC_WANT && (rt->u.dst.mxlock&(1<<RTAX_MTU)))) && - rt->u.dst.pmtu > 576) + rt->u.dst.pmtu > 576 && rt->rt_dst != rt->rt_gateway) sk->mtu = 576; - if(sk->mtu < 64) + if (sk->mtu < 64) sk->mtu = 64; /* Sanity limit */ - sk->mss = (sk->mtu - sizeof(struct iphdr) - tp->tcp_header_len); - if(sk->user_mss) - sk->mss = min(sk->mss, sk->user_mss); - - if (sk->mss < 1) { - printk(KERN_DEBUG "intial sk->mss below 1\n"); - sk->mss = 1; /* Sanity limit */ - } - - tp->window_clamp = rt->u.dst.window; - tcp_select_initial_window(sock_rspace(sk)/2,sk->mss, - &tp->rcv_wnd, - &tp->window_clamp, - sysctl_tcp_window_scaling, - &tp->rcv_wscale); - th->window = htons(tp->rcv_wnd); - - tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps, - sysctl_tcp_window_scaling, tp->rcv_wscale); - buff->csum = 0; - th->doff = (sizeof(*th)+ tmp)>>2; - - tcp_v4_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff); - - tcp_set_state(sk,TCP_SYN_SENT); + mss = sk->mtu - sizeof(struct iphdr); + if (sk->opt) + mss -= sk->opt->optlen; - /* Socket identity change complete, no longer - * in TCP_CLOSE, so enter ourselves into the - * hash tables. - */ - tcp_v4_hash(sk); - - tp->rto = rt->u.dst.rtt; - - tcp_init_xmit_timers(sk); - - /* Now works the right way instead of a hacked initial setting. */ - tp->retransmits = 0; - - skb_queue_tail(&sk->write_queue, buff); - - tp->packets_out++; - buff->when = jiffies; - - ip_queue_xmit(skb_clone(buff, GFP_KERNEL)); + tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr, + sk->sport, usin->sin_port); - /* Timer for repeating the SYN until an answer. */ - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - tcp_statistics.TcpActiveOpens++; - tcp_statistics.TcpOutSegs++; - - release_sock(sk); - return(0); + tcp_connect(sk, buff, mss); + return 0; } static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len) @@ -724,7 +617,7 @@ static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len) int retval = -EINVAL; /* Do sanity checking for sendmsg/sendto/send. */ - if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT)) + if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL)) goto out; if (msg->msg_name) { struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name; @@ -737,7 +630,7 @@ static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len) if(sk->state == TCP_CLOSE) goto out; retval = -EISCONN; - if (addr->sin_port != sk->dummy_th.dest) + if (addr->sin_port != sk->dport) goto out; if (addr->sin_addr.s_addr != sk->daddr) goto out; @@ -851,9 +744,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) int code = skb->h.icmph->code; struct sock *sk; int opening; -#ifdef ICMP_PARANOIA __u32 seq; -#endif if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) { icmp_statistics.IcmpInErrors++; @@ -869,7 +760,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) } tp = &sk->tp_pinfo.af_tcp; -#ifdef ICMP_PARANOIA seq = ntohl(th->seq); if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, max(tp->snd_una+32768,tp->snd_nxt))) { @@ -879,7 +769,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) (int)sk->state, seq, tp->snd_una, tp->snd_nxt); return; } -#endif switch (type) { case ICMP_SOURCE_QUENCH: @@ -927,7 +816,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) req = tcp_v4_search_req(tp, iph, th, &prev); if (!req) return; -#ifdef ICMP_PARANOIA if (seq != req->snt_isn) { if (net_ratelimit()) printk(KERN_DEBUG "icmp packet for openreq " @@ -935,7 +823,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) seq, req->snt_isn); return; } -#endif if (req->sk) { /* not yet accept()ed */ sk = req->sk; /* report error in accept */ } else { @@ -987,44 +874,50 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, static void tcp_v4_send_reset(struct sk_buff *skb) { - struct tcphdr *th = skb->h.th; - struct sk_buff *skb1; - struct tcphdr *th1; + struct tcphdr *th = skb->h.th; - if (th->rst) - return; + /* Never send a reset in response to a reset. */ + if (th->rst == 0) { + struct tcphdr *th = skb->h.th; + struct sk_buff *skb1 = ip_reply(skb, sizeof(struct tcphdr)); + struct tcphdr *th1; - skb1 = ip_reply(skb, sizeof(struct tcphdr)); - if (skb1 == NULL) - return; + if (skb1 == NULL) + return; - skb1->h.th = th1 = (struct tcphdr *)skb_put(skb1, sizeof(struct tcphdr)); - memset(th1, 0, sizeof(*th1)); - - /* Swap the send and the receive. */ - th1->dest = th->source; - th1->source = th->dest; - th1->doff = sizeof(*th1)/4; - th1->rst = 1; - - if (th->ack) - th1->seq = th->ack_seq; - else { - th1->ack = 1; - if (!th->syn) - th1->ack_seq = th->seq; - else - th1->ack_seq = htonl(ntohl(th->seq)+1); - } + skb1->h.th = th1 = (struct tcphdr *) + skb_put(skb1, sizeof(struct tcphdr)); + + /* Swap the send and the receive. */ + memset(th1, 0, sizeof(*th1)); + th1->dest = th->source; + th1->source = th->dest; + th1->doff = sizeof(*th1)/4; + th1->rst = 1; + + if (th->ack) { + th1->seq = th->ack_seq; + } else { + th1->ack = 1; + if (!th->syn) + th1->ack_seq = th->seq; + else + th1->ack_seq = htonl(ntohl(th->seq)+1); + } + skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0); + th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr, + skb1->nh.iph->daddr, skb1->csum); - skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0); - th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr, - skb1->nh.iph->daddr, skb1->csum); + /* Finish up some IP bits. */ + skb1->nh.iph->tot_len = htons(skb1->len); + ip_send_check(skb1->nh.iph); - /* Do not place TCP options in a reset. */ - ip_queue_xmit(skb1); - tcp_statistics.TcpOutSegs++; - tcp_statistics.TcpOutRsts++; + /* All the other work was done by ip_reply(). */ + skb1->dst->output(skb1); + + tcp_statistics.TcpOutSegs++; + tcp_statistics.TcpOutRsts++; + } } #ifdef CONFIG_IP_TRANSPARENT_PROXY @@ -1055,82 +948,48 @@ int tcp_chkaddr(struct sk_buff *skb) static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) { + struct rtable *rt; + struct ip_options *opt; struct sk_buff * skb; - struct tcphdr *th; - int tmp; int mss; - skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC); - if (skb == NULL) + /* First, grab a route. */ + opt = req->af.v4_req.opt; + if(ip_route_output(&rt, ((opt && opt->srr) ? + opt->faddr : + req->af.v4_req.rmt_addr), + req->af.v4_req.loc_addr, + RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute, + sk->bound_dev_if)) { + ip_statistics.IpOutNoRoutes++; return; - - if(ip_build_pkt(skb, sk, req->af.v4_req.loc_addr, - req->af.v4_req.rmt_addr, req->af.v4_req.opt) < 0) { - kfree_skb(skb); + } + if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) { + ip_rt_put(rt); + ip_statistics.IpOutNoRoutes++; return; } - - mss = (skb->dst->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); - if (sk->user_mss) - mss = min(mss, sk->user_mss); - if(req->tstamp_ok) - mss -= TCPOLEN_TSTAMP_ALIGNED; - else - req->mss += TCPOLEN_TSTAMP_ALIGNED; - /* tcp_syn_build_options will do an skb_put() to obtain the TCP - * options bytes below. - */ - skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr)); + mss = (rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); + if (opt) + mss -= opt->optlen; - /* Don't offer more than they did. - * This way we don't have to memorize who said what. - * FIXME: maybe this should be changed for better performance - * with syncookies. - */ - req->mss = min(mss, req->mss); + skb = tcp_make_synack(sk, &rt->u.dst, req, mss); + if (skb) { + struct tcphdr *th = skb->h.th; - if (req->mss < 1) { - printk(KERN_DEBUG "initial req->mss below 1\n"); - req->mss = 1; - } - - /* Yuck, make this header setup more efficient... -DaveM */ - memset(th, 0, sizeof(struct tcphdr)); - th->syn = 1; - th->ack = 1; #ifdef CONFIG_IP_TRANSPARENT_PROXY - th->source = req->lcl_port; /* LVE */ -#else - th->source = sk->dummy_th.source; + th->source = req->lcl_port; /* LVE */ #endif - th->dest = req->rmt_port; - skb->seq = req->snt_isn; - skb->end_seq = skb->seq + 1; - th->seq = htonl(skb->seq); - th->ack_seq = htonl(req->rcv_isn + 1); - if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ - __u8 rcv_wscale; - /* Set this up on the first call only */ - req->window_clamp = skb->dst->window; - tcp_select_initial_window(sock_rspace(sk)/2,req->mss, - &req->rcv_wnd, - &req->window_clamp, - req->wscale_ok, - &rcv_wscale); - req->rcv_wscale = rcv_wscale; + + th->check = tcp_v4_check(th, skb->len, + req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr, + csum_partial((char *)th, skb->len, skb->csum)); + + ip_build_and_send_pkt(skb, sk, req->af.v4_req.loc_addr, + req->af.v4_req.rmt_addr, req->af.v4_req.opt); } - th->window = htons(req->rcv_wnd); - tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok, - req->wscale_ok,req->rcv_wscale); - skb->csum = 0; - th->doff = (sizeof(*th) + tmp)>>2; - th->check = tcp_v4_check(th, sizeof(*th) + tmp, - req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr, - csum_partial((char *)th, sizeof(*th)+tmp, skb->csum)); - - ip_queue_xmit(skb); - tcp_statistics.TcpOutSegs++; + ip_rt_put(rt); } static void tcp_v4_or_free(struct open_request *req) @@ -1240,15 +1099,16 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_isn = skb->seq; - tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(th,&tp,want_cookie); + tcp_parse_options(NULL, th, &tp, want_cookie); req->mss = tp.in_mss; if (tp.saw_tstamp) { req->mss -= TCPOLEN_TSTAMP_ALIGNED; req->ts_recent = tp.rcv_tsval; } req->tstamp_ok = tp.tstamp_ok; + req->sack_ok = tp.sack_ok; req->snd_wscale = tp.snd_wscale; req->wscale_ok = tp.wscale_ok; req->rmt_port = th->source; @@ -1300,8 +1160,11 @@ error: /* This is not only more efficient than what we used to do, it eliminates * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM + * + * This function wants to be moved to a common for IPv[46] file. --ANK */ -struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb) +struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb, + int snd_mss) { struct sock *newsk = sk_alloc(AF_INET, GFP_ATOMIC, 0); @@ -1310,27 +1173,16 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, memcpy(newsk, sk, sizeof(*newsk)); newsk->sklist_next = NULL; - newsk->daddr = req->af.v4_req.rmt_addr; - newsk->rcv_saddr = req->af.v4_req.loc_addr; -#ifdef CONFIG_IP_TRANSPARENT_PROXY - newsk->num = ntohs(skb->h.th->dest); -#endif newsk->state = TCP_SYN_RECV; /* Clone the TCP header template */ -#ifdef CONFIG_IP_TRANSPARENT_PROXY - newsk->dummy_th.source = req->lcl_port; -#endif - newsk->dummy_th.dest = req->rmt_port; - newsk->dummy_th.ack = 1; - newsk->dummy_th.doff = sizeof(struct tcphdr)>>2; + newsk->dport = req->rmt_port; newsk->sock_readers = 0; atomic_set(&newsk->rmem_alloc, 0); skb_queue_head_init(&newsk->receive_queue); atomic_set(&newsk->wmem_alloc, 0); skb_queue_head_init(&newsk->write_queue); - newsk->saddr = req->af.v4_req.loc_addr; newsk->done = 0; newsk->proc = 0; @@ -1395,12 +1247,40 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, newsk->priority = 1; /* IP layer stuff */ - newsk->opt = req->af.v4_req.opt; newsk->timeout = 0; init_timer(&newsk->timer); newsk->timer.function = &net_timer; newsk->timer.data = (unsigned long) newsk; newsk->socket = NULL; + + newtp->tstamp_ok = req->tstamp_ok; + if((newtp->sack_ok = req->sack_ok) != 0) + newtp->num_sacks = 0; + newtp->window_clamp = req->window_clamp; + newtp->rcv_wnd = req->rcv_wnd; + newtp->wscale_ok = req->wscale_ok; + if (newtp->wscale_ok) { + newtp->snd_wscale = req->snd_wscale; + newtp->rcv_wscale = req->rcv_wscale; + } else { + newtp->snd_wscale = newtp->rcv_wscale = 0; + newtp->window_clamp = min(newtp->window_clamp,65535); + } + if (newtp->tstamp_ok) { + newtp->ts_recent = req->ts_recent; + newtp->ts_recent_stamp = jiffies; + newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; + } else { + newtp->tcp_header_len = sizeof(struct tcphdr); + } + + snd_mss -= newtp->tcp_header_len; + + if (sk->user_mss) + snd_mss = min(snd_mss, sk->user_mss); + + newsk->mss = min(req->mss, snd_mss); + } return newsk; } @@ -1409,77 +1289,58 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, struct dst_entry *dst) { + struct ip_options *opt = req->af.v4_req.opt; struct tcp_opt *newtp; struct sock *newsk; int snd_mss; + int mtu; #ifdef NEW_LISTEN if (sk->ack_backlog > sk->max_ack_backlog) goto exit; /* head drop */ #endif - newsk = tcp_create_openreq_child(sk, req, skb); - if (!newsk) - goto exit; -#ifdef NEW_LISTEN - sk->ack_backlog++; -#endif - - newtp = &(newsk->tp_pinfo.af_tcp); - - /* options / mss / route_cache */ if (dst == NULL) { struct rtable *rt; if (ip_route_output(&rt, - newsk->opt && newsk->opt->srr ? - newsk->opt->faddr : newsk->daddr, - newsk->saddr, newsk->ip_tos|RTO_CONN, 0)) { - sk_free(newsk); + opt && opt->srr ? opt->faddr : req->af.v4_req.rmt_addr, + req->af.v4_req.loc_addr, sk->ip_tos|RTO_CONN, 0)) return NULL; - } dst = &rt->u.dst; - } - newsk->dst_cache = dst; - - snd_mss = dst->pmtu; - - /* FIXME: is mtu really the same as snd_mss? */ - newsk->mtu = snd_mss; - /* FIXME: where does mtu get used after this? */ - /* sanity check */ - if (newsk->mtu < 64) - newsk->mtu = 64; - - newtp->tstamp_ok = req->tstamp_ok; - newtp->window_clamp = req->window_clamp; - newtp->rcv_wnd = req->rcv_wnd; - newtp->wscale_ok = req->wscale_ok; - if (newtp->wscale_ok) { - newtp->snd_wscale = req->snd_wscale; - newtp->rcv_wscale = req->rcv_wscale; - } else { - newtp->snd_wscale = newtp->rcv_wscale = 0; - newtp->window_clamp = min(newtp->window_clamp,65535); - } - if (newtp->tstamp_ok) { - newtp->ts_recent = req->ts_recent; - newtp->ts_recent_stamp = jiffies; - newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; - newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); - } else { - newtp->tcp_header_len = sizeof(struct tcphdr); } - snd_mss -= sizeof(struct iphdr) + sizeof(struct tcphdr); - if (sk->user_mss) - snd_mss = min(snd_mss, sk->user_mss); +#ifdef NEW_LISTEN + sk->ack_backlog++; +#endif + + mtu = dst->pmtu; + if (mtu < 68) + mtu = 68; + snd_mss = mtu - sizeof(struct iphdr); + if (opt) + snd_mss -= opt->optlen; - /* Make sure our mtu is adjusted for headers. */ - newsk->mss = min(req->mss, snd_mss) + sizeof(struct tcphdr) - newtp->tcp_header_len; + newsk = tcp_create_openreq_child(sk, req, skb, snd_mss); + if (!newsk) + goto exit; + + newsk->dst_cache = dst; + + newtp = &(newsk->tp_pinfo.af_tcp); + newsk->daddr = req->af.v4_req.rmt_addr; + newsk->saddr = req->af.v4_req.loc_addr; + newsk->rcv_saddr = req->af.v4_req.loc_addr; +#ifdef CONFIG_IP_TRANSPARENT_PROXY + newsk->num = ntohs(skb->h.th->dest); + newsk->sport = req->lcl_port; +#endif + newsk->opt = req->af.v4_req.opt; + newsk->mtu = mtu; /* Must use the af_specific ops here for the case of IPv6 mapped. */ newsk->prot->hash(newsk); add_to_prot_sklist(newsk); + return newsk; exit: @@ -1677,106 +1538,82 @@ do_time_wait: goto discard_it; } -int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb) -{ - return ip_build_header(skb, sk); -} - -int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb) +int tcp_v4_rebuild_header(struct sock *sk) { - struct rtable *rt; - struct iphdr *iph; - struct tcphdr *th; - int size; + struct rtable *rt = (struct rtable *)sk->dst_cache; + __u32 new_saddr; int want_rewrite = sysctl_ip_dynaddr && sk->state == TCP_SYN_SENT; - /* Check route */ - - rt = (struct rtable*)skb->dst; + if(rt == NULL) + return 0; - /* Force route checking if want_rewrite */ - /* The idea is good, the implementation is disguisting. - Well, if I made bind on this socket, you cannot randomly ovewrite - its source address. --ANK + /* Force route checking if want_rewrite. + * The idea is good, the implementation is disguisting. + * Well, if I made bind on this socket, you cannot randomly ovewrite + * its source address. --ANK */ if (want_rewrite) { int tmp; + struct rtable *new_rt; __u32 old_saddr = rt->rt_src; - /* Query new route */ - tmp = ip_route_connect(&rt, rt->rt_dst, 0, + /* Query new route using another rt buffer */ + tmp = ip_route_connect(&new_rt, rt->rt_dst, 0, RT_TOS(sk->ip_tos)|sk->localroute, sk->bound_dev_if); /* Only useful if different source addrs */ - if (tmp == 0 || rt->rt_src != old_saddr ) { - dst_release(skb->dst); - skb->dst = &rt->u.dst; - } else { - want_rewrite = 0; - dst_release(&rt->u.dst); + if (tmp == 0) { + /* + * Only useful if different source addrs + */ + if (new_rt->rt_src != old_saddr ) { + dst_release(sk->dst_cache); + sk->dst_cache = &new_rt->u.dst; + rt = new_rt; + goto do_rewrite; + } + dst_release(&new_rt->u.dst); } - } else + } if (rt->u.dst.obsolete) { int err; err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos|RTO_CONN, rt->key.oif); if (err) { sk->err_soft=-err; - sk->error_report(skb->sk); + sk->error_report(sk); return -1; } - dst_release(skb->dst); - skb->dst = &rt->u.dst; + dst_release(xchg(&sk->dst_cache, &rt->u.dst)); } - iph = skb->nh.iph; - th = skb->h.th; - size = skb->tail - skb->h.raw; + return 0; - if (want_rewrite) { - __u32 new_saddr = rt->rt_src; +do_rewrite: + new_saddr = rt->rt_src; - /* - * Ouch!, this should not happen. - */ - if (!sk->saddr || !sk->rcv_saddr) { - printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX\n", - ntohl(sk->saddr), - ntohl(sk->rcv_saddr)); - return 0; - } - - /* - * Maybe whe are in a skb chain loop and socket address has - * yet been 'damaged'. - */ - - if (new_saddr != sk->saddr) { - if (sysctl_ip_dynaddr > 1) { - printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n", - NIPQUAD(sk->saddr), - NIPQUAD(new_saddr)); - } + /* Ouch!, this should not happen. */ + if (!sk->saddr || !sk->rcv_saddr) { + printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: " + "saddr=%08lX rcv_saddr=%08lX\n", + ntohl(sk->saddr), + ntohl(sk->rcv_saddr)); + return 0; + } - sk->saddr = new_saddr; - sk->rcv_saddr = new_saddr; - /* sk->prot->rehash(sk); */ - tcp_v4_rehash(sk); - } - - if (new_saddr != iph->saddr) { - if (sysctl_ip_dynaddr > 1) { - printk(KERN_INFO "tcp_v4_rebuild_header(): shifting iph->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n", - NIPQUAD(iph->saddr), - NIPQUAD(new_saddr)); - } + if (new_saddr != sk->saddr) { + if (sysctl_ip_dynaddr > 1) { + printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr " + "from %d.%d.%d.%d to %d.%d.%d.%d\n", + NIPQUAD(sk->saddr), + NIPQUAD(new_saddr)); + } - iph->saddr = new_saddr; - ip_send_check(iph); - } + sk->saddr = new_saddr; + sk->rcv_saddr = new_saddr; + tcp_v4_rehash(sk); + } - } - return 0; } @@ -1792,11 +1629,10 @@ static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) sin->sin_family = AF_INET; sin->sin_addr.s_addr = sk->daddr; - sin->sin_port = sk->dummy_th.dest; + sin->sin_port = sk->dport; } struct tcp_func ipv4_specific = { - tcp_v4_build_header, ip_queue_xmit, tcp_v4_send_check, tcp_v4_rebuild_header, @@ -1835,10 +1671,6 @@ static int tcp_v4_init_sock(struct sock *sk) sk->mtu = 576; sk->mss = 536; - /* Speed up by setting some standard state for the dummy_th. */ - sk->dummy_th.ack=1; - sk->dummy_th.doff=sizeof(struct tcphdr)>>2; - /* Init SYN queue. */ tcp_synq_init(tp); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d8c3c6480..465ee3fdc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.65 1998/03/15 12:07:03 davem Exp $ + * Version: $Id: tcp_output.c,v 1.76 1998/03/22 22:10:24 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -29,6 +29,7 @@ * Linus Torvalds : send_delayed_ack * David S. Miller : Charge memory using the right skb * during syn/ack processing. + * David S. Miller : Output engine completely rewritten. * */ @@ -57,278 +58,227 @@ static __inline__ void update_send_head(struct sock *sk) tp->send_head = NULL; } -/* - * This is the main buffer sending routine. We queue the buffer - * having checked it is sane seeming. +/* This routine actually transmits TCP packets queued in by + * tcp_do_sendmsg(). This is used by both the initial + * transmission and possible later retransmissions. + * All SKB's seen here are completely headerless. It is our + * job to build the TCP header, and pass the packet down to + * IP so it can do the same plus pass the packet off to the + * device. + * + * We are working here with either a clone of the original + * SKB, or a fresh unique copy made by the retransmit engine. */ - -void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue) +void tcp_transmit_skb(struct sock *sk, struct sk_buff *skb) { - struct tcphdr *th = skb->h.th; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int size; + if(skb != NULL) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + int tcp_header_size = tp->tcp_header_len; + struct tcphdr *th; - /* Length of packet (not counting length of pre-tcp headers). */ - size = skb->len - ((unsigned char *) th - skb->data); + if(tcb->flags & TCPCB_FLAG_SYN) { + tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS; + if(sysctl_tcp_timestamps) + tcp_header_size += TCPOLEN_TSTAMP_ALIGNED; + if(sysctl_tcp_window_scaling) + tcp_header_size += TCPOLEN_WSCALE_ALIGNED; + if(sysctl_tcp_sack && !sysctl_tcp_timestamps) + tcp_header_size += TCPOLEN_SACKPERM_ALIGNED; + } else if(tp->sack_ok && tp->num_sacks) { + /* A SACK is 2 pad bytes, a 2 byte header, plus + * 2 32-bit sequence numbers for each SACK block. + */ + tcp_header_size += (TCPOLEN_SACK_BASE_ALIGNED + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)); + } + th = (struct tcphdr *) skb_push(skb, tcp_header_size); + skb->h.th = th; + skb_set_owner_w(skb, sk); + + /* Build TCP header and checksum it. */ + th->source = sk->sport; + th->dest = sk->dport; + th->seq = htonl(skb->seq); + th->ack_seq = htonl(tp->rcv_nxt); + th->doff = (tcp_header_size >> 2); + th->res1 = 0; + *(((__u8 *)th) + 13) = tcb->flags; + th->window = htons(tcp_select_window(sk)); + th->check = 0; + th->urg_ptr = ntohs(tcb->urg_ptr); + if(tcb->flags & TCPCB_FLAG_SYN) { + th->window = htons(tp->rcv_wnd); + tcp_syn_build_options((__u32 *)(th + 1), sk->mss, + sysctl_tcp_timestamps, + sysctl_tcp_sack, + sysctl_tcp_window_scaling, + tp->rcv_wscale, + skb->when); + } else { + tcp_build_and_update_options((__u32 *)(th + 1), + tp, skb->when); + } + tp->af_specific->send_check(sk, th, skb->len, skb); - /* If there is a FIN or a SYN we add it onto the size. */ - if (th->fin || th->syn) { - if(th->syn) - size++; - if(th->fin) - size++; + clear_delayed_acks(sk); + tp->last_ack_sent = tp->rcv_nxt; + tcp_statistics.TcpOutSegs++; + tp->af_specific->queue_xmit(skb); } +} - /* Actual processing. */ - skb->seq = ntohl(th->seq); - skb->end_seq = skb->seq + size - 4*th->doff; +/* This is the main buffer sending routine. We queue the buffer + * and decide whether to queue or transmit now. + */ +void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + /* Advance write_seq and place onto the write_queue. */ + tp->write_seq += (skb->end_seq - skb->seq); skb_queue_tail(&sk->write_queue, skb); if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) { - struct sk_buff * buff; - - /* This is going straight out. */ - tp->last_ack_sent = tp->rcv_nxt; - th->ack_seq = htonl(tp->rcv_nxt); - th->window = htons(tcp_select_window(sk)); - tcp_update_options((__u32 *)(th + 1),tp); - - tp->af_specific->send_check(sk, th, size, skb); - - buff = skb_clone(skb, GFP_KERNEL); - if (buff == NULL) - goto queue; - - clear_delayed_acks(sk); - skb_set_owner_w(buff, sk); - + /* Send it out now. */ + skb->when = jiffies; tp->snd_nxt = skb->end_seq; tp->packets_out++; - - skb->when = jiffies; - - tcp_statistics.TcpOutSegs++; - tp->af_specific->queue_xmit(buff); - - if (!tcp_timer_is_set(sk, TIME_RETRANS)) + tcp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL)); + if(!tcp_timer_is_set(sk, TIME_RETRANS)) tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - - return; - } - -queue: - /* Remember where we must start sending. */ - if (tp->send_head == NULL) - tp->send_head = skb; - if (!force_queue && tp->packets_out == 0 && !tp->pending) { - tp->pending = TIME_PROBE0; - tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto); + } else { + /* Queue it, remembering where we must start sending. */ + if (tp->send_head == NULL) + tp->send_head = skb; + if (!force_queue && tp->packets_out == 0 && !tp->pending) { + tp->pending = TIME_PROBE0; + tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto); + } } } -/* - * Function to create two new tcp segments. - * Shrinks the given segment to the specified size and appends a new - * segment with the rest of the packet to the list. - * This won't be called frenquently, I hope... +/* Function to create two new tcp segments. Shrinks the given segment + * to the specified size and appends a new segment with the rest of the + * packet to the list. This won't be called frenquently, I hope... + * Remember, these are still header-less SKB's at this point. */ - static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *buff; - struct tcphdr *th, *nth; - int nsize; - int tmp; - - th = skb->h.th; - - /* Size of new segment. */ - nsize = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len) - len; - if (nsize <= 0) { - printk(KERN_DEBUG "tcp_fragment: bug size <= 0\n"); - return -1; - } + int nsize = skb->len - len; + u16 flags; /* Get a new skb... force flag on. */ - buff = sock_wmalloc(sk, nsize + 128 + sk->prot->max_header + 15, 1, - GFP_ATOMIC); + buff = sock_wmalloc(sk, + (nsize + + MAX_HEADER + + sk->prot->max_header + 15), + 1, GFP_ATOMIC); if (buff == NULL) - return -1; + return -1; /* We'll just try again later. */ - /* Put headers on the new packet. */ - tmp = tp->af_specific->build_net_header(sk, buff); - if (tmp < 0) { - kfree_skb(buff); - return -1; - } + /* Reserve space for headers. */ + skb_reserve(buff, MAX_HEADER + sk->prot->max_header); - /* Move the TCP header over. */ - nth = (struct tcphdr *) skb_put(buff, tp->tcp_header_len); - buff->h.th = nth; - memcpy(nth, th, tp->tcp_header_len); - - /* Correct the new header. */ + /* Correct the sequence numbers. */ buff->seq = skb->seq + len; buff->end_seq = skb->end_seq; - nth->seq = htonl(buff->seq); - nth->check = 0; - nth->doff = th->doff; - /* urg data is always an headache */ - if (th->urg) { - if (th->urg_ptr > len) { - th->urg = 0; - nth->urg_ptr -= len; + /* PSH and FIN should only be set in the second packet. */ + flags = TCP_SKB_CB(skb)->flags; + TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN | TCPCB_FLAG_PSH); + if(flags & TCPCB_FLAG_URG) { + u16 old_urg_ptr = TCP_SKB_CB(skb)->urg_ptr; + + /* Urgent data is always a pain in the ass. */ + if(old_urg_ptr > len) { + TCP_SKB_CB(skb)->flags &= ~(TCPCB_FLAG_URG); + TCP_SKB_CB(skb)->urg_ptr = 0; + TCP_SKB_CB(buff)->urg_ptr = old_urg_ptr - len; } else { - nth->urg = 0; + flags &= ~(TCPCB_FLAG_URG); } } + if(!(flags & TCPCB_FLAG_URG)) + TCP_SKB_CB(buff)->urg_ptr = 0; + TCP_SKB_CB(buff)->flags = flags; + TCP_SKB_CB(buff)->sacked = 0; - /* Copy data tail to our new buffer. */ - buff->csum = csum_partial_copy(((u8 *)(th)+tp->tcp_header_len) + len, - skb_put(buff, nsize), + /* Copy and checksum data tail into the new buffer. */ + buff->csum = csum_partial_copy(skb->data + len, skb_put(buff, nsize), nsize, 0); skb->end_seq -= nsize; skb_trim(skb, skb->len - nsize); - /* Remember to checksum this packet afterwards. */ - th->check = 0; - skb->csum = csum_partial((u8*)(th) + tp->tcp_header_len, skb->tail - ((u8 *) (th)+tp->tcp_header_len), - 0); + /* Rechecksum original buffer. */ + skb->csum = csum_partial(skb->data, skb->len, 0); + /* Link BUFF into the send queue. */ skb_append(skb, buff); return 0; } -static void tcp_wrxmit_prob(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - - /* This is acked data. We can discard it. This cannot currently occur. */ - tp->retransmits = 0; - - printk(KERN_DEBUG "tcp_write_xmit: bug skb in write queue\n"); - - update_send_head(sk); - - skb_unlink(skb); - kfree_skb(skb); - - if (!sk->dead) - sk->write_space(sk); -} - -static int tcp_wrxmit_frag(struct sock *sk, struct sk_buff *skb, int size) -{ - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - - SOCK_DEBUG(sk, "tcp_write_xmit: frag needed size=%d mss=%d\n", - size, sk->mss); - - if (tcp_fragment(sk, skb, sk->mss)) { - /* !tcp_frament Failed! */ - tp->send_head = skb; - tp->packets_out--; - return -1; - } - return 0; -} - -/* - * This routine writes packets to the network. - * It advances the send_head. - * This happens as incoming acks open up the remote window for us. +/* This routine writes packets to the network. It advances the + * send_head. This happens as incoming acks open up the remote + * window for us. */ - void tcp_write_xmit(struct sock *sk) { - struct sk_buff *skb; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - u16 rcv_wnd; - int sent_pkts = 0; + int mss_now = sk->mss; - /* The bytes will have to remain here. In time closedown will - * empty the write queue and all will be happy. + /* Account for SACKS, we may need to fragment due to this. + * It is just like the real MSS changing on us midstream. + * We also handle things correctly when the user adds some + * IP options mid-stream. Silly to do, but cover it. */ - if(sk->zapped) - return; - - /* Anything on the transmit queue that fits the window can - * be added providing we are: - * - * a) following SWS avoidance [and Nagle algorithm] - * b) not exceeding our congestion window. - * c) not retransmiting [Nagle] + if(tp->sack_ok && tp->num_sacks) + mss_now -= (TCPOLEN_SACK_BASE_ALIGNED + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)); + if(sk->opt && sk->opt->optlen) + mss_now -= sk->opt->optlen; + + /* If we are zapped, the bytes will have to remain here. + * In time closedown will empty the write queue and all + * will be happy. */ - rcv_wnd = htons(tcp_select_window(sk)); - while((skb = tp->send_head) && tcp_snd_test(sk, skb)) { - struct tcphdr *th; - struct sk_buff *buff; - int size; + if(!sk->zapped) { + struct sk_buff *skb; + int sent_pkts = 0; - /* See if we really need to send the packet. (debugging code) */ - if (!after(skb->end_seq, tp->snd_una)) { - tcp_wrxmit_prob(sk, skb); - continue; - } - - /* Put in the ack seq and window at this point rather - * than earlier, in order to keep them monotonic. - * We really want to avoid taking back window allocations. - * That's legal, but RFC1122 says it's frowned on. - * Ack and window will in general have changed since - * this packet was put on the write queue. + /* Anything on the transmit queue that fits the window can + * be added providing we are: + * + * a) following SWS avoidance [and Nagle algorithm] + * b) not exceeding our congestion window. + * c) not retransmiting [Nagle] */ - th = skb->h.th; - size = skb->len - (((unsigned char *) th) - skb->data); - if (size - (th->doff << 2) > sk->mss) { - if (tcp_wrxmit_frag(sk, skb, size)) - break; - size = skb->len - (((unsigned char*)th) - skb->data); - } - - tp->last_ack_sent = tp->rcv_nxt; - th->ack_seq = htonl(tp->rcv_nxt); - th->window = rcv_wnd; - tcp_update_options((__u32 *)(th + 1),tp); - - tp->af_specific->send_check(sk, th, size, skb); - -#ifdef TCP_DEBUG - if (before(skb->end_seq, tp->snd_nxt)) - printk(KERN_DEBUG "tcp_write_xmit:" - " sending already sent seq\n"); -#endif - - buff = skb_clone(skb, GFP_ATOMIC); - if (buff == NULL) - break; - - /* Advance the send_head. This one is going out. */ - update_send_head(sk); - clear_delayed_acks(sk); - - tp->packets_out++; - skb_set_owner_w(buff, sk); - - tp->snd_nxt = skb->end_seq; + while((skb = tp->send_head) && tcp_snd_test(sk, skb)) { + if (skb->len > mss_now) { + if (tcp_fragment(sk, skb, mss_now)) + break; + } - skb->when = jiffies; + /* Advance the send_head. This one is going out. */ + update_send_head(sk); + skb->when = jiffies; + tp->snd_nxt = skb->end_seq; + tp->packets_out++; + tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)); + sent_pkts = 1; + } - sent_pkts = 1; - tp->af_specific->queue_xmit(buff); + /* If we sent anything, make sure the retransmit + * timer is active. + */ + if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS)) + tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } - - if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS)) - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } - - /* This function returns the amount that we can raise the * usable window based on the following constraints * @@ -377,11 +327,7 @@ void tcp_write_xmit(struct sock *sk) * Below we obtain similar behavior by forcing the offered window to * a multiple of the mss when it is feasible to do so. * - * FIXME: In our current implementation the value returned by sock_rpsace(sk) - * is the total space we have allocated to the socket to store skbuf's. - * The current design assumes that up to half of that space will be - * taken by headers, and the remaining space will be available for TCP data. - * This should be accounted for correctly instead. + * Note, we don't "adjust" for TIMESTAMP or SACK option bytes. */ u32 __tcp_select_window(struct sock *sk) { @@ -422,57 +368,72 @@ u32 __tcp_select_window(struct sock *sk) return window; } -static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb) +/* Attempt to collapse two adjacent SKB's during retransmission. */ +static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - struct tcphdr *th1, *th2; - int size1, size2, avail; - struct sk_buff *buff = skb->next; - - th1 = skb->h.th; - - if (th1->urg) - return -1; + struct sk_buff *next_skb = skb->next; - avail = skb_tailroom(skb); + /* The first test we must make is that neither of these two + * SKB's are still referenced by someone else. + */ + if(!skb_cloned(skb) && !skb_cloned(next_skb)) { + int skb_size = skb->len, next_skb_size = next_skb->len; + u16 flags = TCP_SKB_CB(skb)->flags; - /* Size of TCP payload. */ - size1 = skb->tail - ((u8 *) (th1)+(th1->doff<<2)); + /* Punt if the first SKB has URG set. */ + if(flags & TCPCB_FLAG_URG) + return; - th2 = buff->h.th; - size2 = buff->tail - ((u8 *) (th2)+(th2->doff<<2)); + /* Also punt if next skb has been SACK'd. */ + if(TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED) + return; - if (size2 > avail || size1 + size2 > sk->mss ) - return -1; + /* Punt if not enough space exists in the first SKB for + * the data in the second, or the total combined payload + * would exceed the MSS. + */ + if ((next_skb_size > skb_tailroom(skb)) || + ((skb_size + next_skb_size) > mss_now)) + return; - /* Ok. We will be able to collapse the packet. */ - skb_unlink(buff); - memcpy(skb_put(skb, size2), ((char *) th2) + (th2->doff << 2), size2); - - /* Update sizes on original skb, both TCP and IP. */ - skb->end_seq += buff->end_seq - buff->seq; - if (th2->urg) { - th1->urg = 1; - th1->urg_ptr = th2->urg_ptr + size1; - } - if (th2->fin) - th1->fin = 1; + /* Ok. We will be able to collapse the packet. */ + skb_unlink(next_skb); - /* ... and off you go. */ - kfree_skb(buff); - tp->packets_out--; + if(skb->len % 4) { + /* Must copy and rechecksum all data. */ + memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size); + skb->csum = csum_partial(skb->data, skb->len, 0); + } else { + /* Optimize, actually we could also combine next_skb->csum + * to skb->csum using a single add w/carry operation too. + */ + skb->csum = csum_partial_copy(next_skb->data, + skb_put(skb, next_skb_size), + next_skb_size, skb->csum); + } + + /* Update sequence range on original skb. */ + skb->end_seq += next_skb->end_seq - next_skb->seq; + + /* Merge over control information. */ + flags |= TCP_SKB_CB(next_skb)->flags; /* This moves PSH/FIN etc. over */ + if(flags & TCPCB_FLAG_URG) { + u16 urgptr = TCP_SKB_CB(next_skb)->urg_ptr; + TCP_SKB_CB(skb)->urg_ptr = urgptr + skb_size; + } + TCP_SKB_CB(skb)->flags = flags; - /* Header checksum will be set by the retransmit procedure - * after calling rebuild header. - */ - th1->check = 0; - skb->csum = csum_partial((u8*)(th1)+(th1->doff<<2), size1 + size2, 0); - return 0; + /* All done, get rid of second SKB and account for it so + * packet counting does not break. + */ + kfree_skb(next_skb); + sk->tp_pinfo.af_tcp.packets_out--; + } } /* Do a simple retransmit without using the backoff mechanisms in * tcp_timer. This is used to speed up path mtu recovery. Note that - * these simple retransmit aren't counted in the usual tcp retransmit + * these simple retransmits aren't counted in the usual tcp retransmit * backoff counters. * The socket is already locked here. */ @@ -480,114 +441,114 @@ void tcp_simple_retransmit(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - /* Clear delay ack timer. */ - tcp_clear_xmit_timer(sk, TIME_DACK); - - tp->retrans_head = NULL; /* Don't muck with the congestion window here. */ tp->dup_acks = 0; tp->high_seq = tp->snd_nxt; + /* FIXME: make the current rtt sample invalid */ - tcp_do_retransmit(sk, 0); + tp->retrans_head = NULL; + tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); } -/* - * A socket has timed out on its send queue and wants to do a - * little retransmitting. - * retrans_head can be different from the head of the write_queue - * if we are doing fast retransmit. - */ +static __inline__ void update_retrans_head(struct sock *sk) +{ + struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + + tp->retrans_head = tp->retrans_head->next; + if((tp->retrans_head == tp->send_head) || + (tp->retrans_head == (struct sk_buff *) &sk->write_queue)) + tp->retrans_head = NULL; +} -void tcp_do_retransmit(struct sock *sk, int all) +/* This retransmits one SKB. Policy decisions and retransmit queue + * state updates are done by the caller. Returns non-zero if an + * error occured which prevented the send. + */ +int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) { - struct sk_buff * skb; - int ct=0; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + int current_mss = sk->mss; - if (tp->retrans_head == NULL) - tp->retrans_head = skb_peek(&sk->write_queue); - - if (tp->retrans_head == tp->send_head) - tp->retrans_head = NULL; - - while ((skb = tp->retrans_head) != NULL) { - struct sk_buff *buff; - struct tcphdr *th; - int tcp_size; - int size; - - /* In general it's OK just to use the old packet. However we - * need to use the current ack and window fields. Urg and - * urg_ptr could possibly stand to be updated as well, but we - * don't keep the necessary data. That shouldn't be a problem, - * if the other end is doing the right thing. Since we're - * changing the packet, we have to issue a new IP identifier. - */ + /* Account for outgoing SACKS and IP options, if any. */ + if(tp->sack_ok && tp->num_sacks) + current_mss -= (TCPOLEN_SACK_BASE_ALIGNED + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)); + if(sk->opt && sk->opt->optlen) + current_mss -= sk->opt->optlen; - th = skb->h.th; + if(skb->len > current_mss) { + if(tcp_fragment(sk, skb, current_mss)) + return 1; /* We'll try again later. */ - tcp_size = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len); + /* New SKB created, account for it. */ + tp->packets_out++; + } - if (tcp_size > sk->mss) { - if (tcp_fragment(sk, skb, sk->mss)) { - printk(KERN_DEBUG "tcp_fragment failed\n"); - return; - } - tp->packets_out++; - } + /* Collapse two adjacent packets if worthwhile and we can. */ + if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) && + (skb->len < (current_mss >> 1)) && + (skb->next != tp->send_head) && + (skb->next != (struct sk_buff *)&sk->write_queue)) + tcp_retrans_try_collapse(sk, skb, current_mss); - if (!th->syn && - tcp_size < (sk->mss >> 1) && - skb->next != tp->send_head && - skb->next != (struct sk_buff *)&sk->write_queue) - tcp_retrans_try_collapse(sk, skb); - - if (tp->af_specific->rebuild_header(sk, skb)) { -#ifdef TCP_DEBUG - printk(KERN_DEBUG "tcp_do_rebuild_header failed\n"); -#endif - break; - } + if(tp->af_specific->rebuild_header(sk)) + return 1; /* Routing failure or similar. */ - SOCK_DEBUG(sk, "retransmit sending seq=%x\n", skb->seq); + /* Ok, we're gonna send it out, update state. */ + TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_RETRANS; - /* Update ack and window. */ - tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt); - th->window = ntohs(tcp_select_window(sk)); - tcp_update_options((__u32 *)(th+1),tp); + /* Make a copy, if the first transmission SKB clone we made + * is still in somebodies hands, else make a clone. + */ + skb->when = jiffies; + if(skb_cloned(skb)) + skb = skb_copy(skb, GFP_ATOMIC); + else + skb = skb_clone(skb, GFP_ATOMIC); + tcp_transmit_skb(sk, skb); - size = skb->tail - (unsigned char *) th; - tp->af_specific->send_check(sk, th, size, skb); + /* Update global TCP statistics and return success. */ + sk->prot->retransmits++; + tcp_statistics.TcpRetransSegs++; - skb->when = jiffies; + return 0; +} - buff = skb_clone(skb, GFP_ATOMIC); - if (buff == NULL) - break; +/* This gets called after a retransmit timeout, and the initially + * retransmitted data is acknowledged. It tries to continue + * resending the rest of the retransmit queue, until either + * we've sent it all or the congestion window limit is reached. + */ +void tcp_xmit_retransmit_queue(struct sock *sk) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff *skb; + int ct = 0; - skb_set_owner_w(buff, sk); + if (tp->retrans_head == NULL) + tp->retrans_head = skb_peek(&sk->write_queue); + if (tp->retrans_head == tp->send_head) + tp->retrans_head = NULL; - clear_delayed_acks(sk); - tp->af_specific->queue_xmit(buff); + while ((skb = tp->retrans_head) != NULL) { + /* If it has been ack'd by a SACK block, we don't + * retransmit it. + */ + if(!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) { + /* Send it out, punt if error occurred. */ + if(tcp_retransmit_skb(sk, skb)) + break; - /* Count retransmissions. */ - ct++; - sk->prot->retransmits++; - tcp_statistics.TcpRetransSegs++; - - /* Only one retransmit requested. */ - if (!all) - break; - - /* This should cut it off before we send too many packets. */ - if (ct >= tp->snd_cwnd) - break; - - /* Advance the pointer. */ - tp->retrans_head = skb->next; - if ((tp->retrans_head == tp->send_head) || - (tp->retrans_head == (struct sk_buff *) &sk->write_queue)) - tp->retrans_head = NULL; + /* Count retransmissions locally. */ + ct++; + + /* Stop retransmitting if we've hit the congestion + * window limit. + */ + if (ct >= tp->snd_cwnd) + break; + } + update_retrans_head(sk); } } @@ -597,83 +558,44 @@ void tcp_do_retransmit(struct sock *sk, int all) void tcp_send_fin(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff *skb = skb_peek_tail(&sk->write_queue); + int mss_now = sk->mss; /* Optimization, tack on the FIN if we have a queue of - * unsent frames. + * unsent frames. But be careful about outgoing SACKS + * and IP options. */ - if(tp->send_head != NULL) { - struct sk_buff *tail = skb_peek_tail(&sk->write_queue); - struct tcphdr *th = tail->h.th; - int data_len; - - /* Unfortunately tcp_write_xmit won't check for going over - * the MSS due to the FIN sequence number, so we have to - * watch out for it here. - */ - data_len = (tail->tail - (((unsigned char *)th)+tp->tcp_header_len)); - if(data_len >= sk->mss) - goto build_new_frame; /* ho hum... */ - - /* tcp_write_xmit() will checksum the header etc. for us. */ - th->fin = 1; - tail->end_seq++; + if(tp->sack_ok && tp->num_sacks) + mss_now -= (TCPOLEN_SACK_BASE_ALIGNED + + (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)); + if(sk->opt && sk->opt->optlen) + mss_now -= sk->opt->optlen; + if((tp->send_head != NULL) && (skb->len < mss_now)) { + /* tcp_write_xmit() takes care of the rest. */ + TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN; + skb->end_seq++; + tp->write_seq++; } else { - struct sk_buff *buff; - struct tcphdr *th; - -build_new_frame: - buff = sock_wmalloc(sk, - (BASE_ACK_SIZE + tp->tcp_header_len + - sizeof(struct sk_buff)), - 1, GFP_KERNEL); - if (buff == NULL) { - /* We can only fail due to low memory situations, not - * due to going over our sndbuf limits (due to the - * force flag passed to sock_wmalloc). So just keep - * trying. We cannot allow this fail. The socket is - * still locked, so we need not check if the connection - * was reset in the meantime etc. - */ - goto build_new_frame; - } - - /* Administrivia. */ - buff->csum = 0; - - /* Put in the IP header and routing stuff. - * - * FIXME: - * We can fail if the interface for the route - * this socket takes goes down right before - * we get here. ANK is there a way to point - * this into a "black hole" route in such a - * case? Ideally, we should still be able to - * queue this and let the retransmit timer - * keep trying until the destination becomes - * reachable once more. -DaveM - */ - if(tp->af_specific->build_net_header(sk, buff) < 0) { - kfree_skb(buff); - goto update_write_seq; - } - th = (struct tcphdr *) skb_put(buff, tp->tcp_header_len); - buff->h.th = th; - - memcpy(th, (void *) &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(tp->write_seq); - th->fin = 1; - tcp_build_options((__u32 *)(th + 1), tp); - - /* This makes sure we do things like abide by the congestion - * window and other constraints which prevent us from sending. - */ - tcp_send_skb(sk, buff, 0); + /* Socket is locked, keep trying until memory is available. */ + do { + skb = sock_wmalloc(sk, + (MAX_HEADER + + sk->prot->max_header), + 1, GFP_KERNEL); + } while (skb == NULL); + + /* Reserve space for headers and prepare control bits. */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); + skb->csum = 0; + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN); + TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->urg_ptr = 0; + + /* FIN eats a sequence byte, write_seq advanced by tcp_send_skb(). */ + skb->seq = tp->write_seq; + skb->end_seq = skb->seq + 1; + tcp_send_skb(sk, skb, 0); } -update_write_seq: - /* So that we recognize the ACK coming back for - * this FIN as being legitimate. - */ - tp->write_seq++; } /* We get here when a process closes a file descriptor (either due to @@ -685,109 +607,218 @@ void tcp_send_active_reset(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *skb; - struct tcphdr *th; -again: /* NOTE: No TCP options attached and we never retransmit this. */ - skb = sock_wmalloc(sk, (BASE_ACK_SIZE + sizeof(*th)), 1, GFP_KERNEL); - if(skb == NULL) - goto again; + do { + skb = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_KERNEL); + } while(skb == NULL); + + /* Reserve space for headers and prepare control bits. */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); skb->csum = 0; - if(tp->af_specific->build_net_header(sk, skb) < 0) { - kfree_skb(skb); - } else { - th = (struct tcphdr *) skb_put(skb, sizeof(*th)); - memcpy(th, &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(tp->write_seq); - th->rst = 1; - th->doff = sizeof(*th) / 4; - tp->last_ack_sent = tp->rcv_nxt; - th->ack_seq = htonl(tp->rcv_nxt); - th->window = htons(tcp_select_window(sk)); - tp->af_specific->send_check(sk, th, sizeof(*th), skb); - tp->af_specific->queue_xmit(skb); - tcp_statistics.TcpOutSegs++; - tcp_statistics.TcpOutRsts++; - } + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_RST); + TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->urg_ptr = 0; + + /* Send it off. */ + skb->seq = tp->write_seq; + skb->end_seq = skb->seq; + skb->when = jiffies; + tcp_transmit_skb(sk, skb); } /* WARNING: This routine must only be called when we have already sent * a SYN packet that crossed the incoming SYN that caused this routine * to get called. If this assumption fails then the initial rcv_wnd * and rcv_wscale values will not be correct. - * - * XXX When you have time Dave, redo this to use tcp_send_skb() just - * XXX like tcp_send_fin() above now does.... -DaveM */ int tcp_send_synack(struct sock *sk) { - struct tcp_opt * tp = &(sk->tp_pinfo.af_tcp); - struct sk_buff * skb; - struct sk_buff * buff; - struct tcphdr *th; - int tmp; + struct tcp_opt* tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff* skb; - skb = sock_wmalloc(sk, MAX_SYN_SIZE + sizeof(struct sk_buff), 1, GFP_ATOMIC); + skb = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header), + 1, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; - tmp = tp->af_specific->build_net_header(sk, skb); - if (tmp < 0) { - kfree_skb(skb); - return tmp; + /* Reserve space for headers and prepare control bits. */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); + skb->csum = 0; + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_SYN); + TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->urg_ptr = 0; + + /* SYN eats a sequence byte. */ + skb->seq = tp->snd_una; + skb->end_seq = skb->seq + 1; + skb_queue_tail(&sk->write_queue, skb); + skb->when = jiffies; + tp->packets_out++; + tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)); + return 0; +} + +struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, + struct open_request *req, int mss) +{ + struct tcphdr *th; + int tcp_header_size; + struct sk_buff *skb; + + skb = sock_wmalloc(sk, MAX_HEADER + sk->prot->max_header, 1, GFP_ATOMIC); + if (skb == NULL) + return NULL; + + /* Reserve space for headers. */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); + + skb->dst = dst_clone(dst); + + if (sk->user_mss) + mss = min(mss, sk->user_mss); + if (req->tstamp_ok) + mss -= TCPOLEN_TSTAMP_ALIGNED; + else + req->mss += TCPOLEN_TSTAMP_ALIGNED; + + /* Don't offer more than they did. + * This way we don't have to memorize who said what. + * FIXME: maybe this should be changed for better performance + * with syncookies. + */ + req->mss = min(mss, req->mss); + if (req->mss < 1) { + printk(KERN_DEBUG "initial req->mss below 1\n"); + req->mss = 1; } - th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr)); - skb->h.th = th; - memset(th, 0, sizeof(struct tcphdr)); + tcp_header_size = (sizeof(struct tcphdr) + TCPOLEN_MSS + + (req->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) + + (req->wscale_ok ? TCPOLEN_WSCALE_ALIGNED : 0) + + /* SACK_PERM is in the place of NOP NOP of TS */ + ((req->sack_ok && !req->tstamp_ok) ? TCPOLEN_SACKPERM_ALIGNED : 0)); + skb->h.th = th = (struct tcphdr *) skb_push(skb, tcp_header_size); + memset(th, 0, sizeof(struct tcphdr)); th->syn = 1; th->ack = 1; + th->source = sk->sport; + th->dest = req->rmt_port; + skb->seq = req->snt_isn; + skb->end_seq = skb->seq + 1; + th->seq = htonl(skb->seq); + th->ack_seq = htonl(req->rcv_isn + 1); + if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ + __u8 rcv_wscale; + /* Set this up on the first call only */ + req->window_clamp = skb->dst->window; + tcp_select_initial_window(sock_rspace(sk)/2,req->mss, + &req->rcv_wnd, + &req->window_clamp, + req->wscale_ok, + &rcv_wscale); + req->rcv_wscale = rcv_wscale; + } + th->window = htons(req->rcv_wnd); - th->source = sk->dummy_th.source; - th->dest = sk->dummy_th.dest; - - skb->seq = tp->snd_una; - skb->end_seq = skb->seq + 1 /* th->syn */ ; - th->seq = ntohl(skb->seq); + skb->when = jiffies; + tcp_syn_build_options((__u32 *)(th + 1), req->mss, req->tstamp_ok, + req->sack_ok, req->wscale_ok, req->rcv_wscale, + skb->when); - /* This is a resend of a previous SYN, now with an ACK. - * we must reuse the previously offered window. - */ - th->window = htons(tp->rcv_wnd); + skb->csum = 0; + th->doff = (tcp_header_size >> 2); + tcp_statistics.TcpOutSegs++; + return skb; +} - tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt); +void tcp_connect(struct sock *sk, struct sk_buff *buff, int mss) +{ + struct dst_entry *dst = sk->dst_cache; + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - tmp = tcp_syn_build_options(skb, sk->mss, - tp->tstamp_ok, tp->wscale_ok, tp->rcv_wscale); - skb->csum = 0; - th->doff = (sizeof(*th) + tmp)>>2; + /* Reserve space for headers. */ + skb_reserve(buff, MAX_HEADER + sk->prot->max_header); - tp->af_specific->send_check(sk, th, sizeof(*th)+tmp, skb); + if (sk->priority == 0) + sk->priority = dst->priority; - skb_queue_tail(&sk->write_queue, skb); + tp->snd_wnd = 0; + tp->snd_wl1 = 0; + tp->snd_wl2 = tp->write_seq; + tp->snd_una = tp->write_seq; + tp->rcv_nxt = 0; + + sk->err = 0; - buff = skb_clone(skb, GFP_ATOMIC); - if (buff) { - skb_set_owner_w(buff, sk); + /* We'll fix this up when we get a response from the other end. + * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT. + */ + tp->tcp_header_len = sizeof(struct tcphdr) + + (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0); - tp->packets_out++; - skb->when = jiffies; + mss -= tp->tcp_header_len; - tp->af_specific->queue_xmit(buff); - tcp_statistics.TcpOutSegs++; + if (sk->user_mss) + mss = min(mss, sk->user_mss); - tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT); + if (mss < 1) { + printk(KERN_DEBUG "intial sk->mss below 1\n"); + mss = 1; /* Sanity limit */ } - return 0; + + sk->mss = mss; + + TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN; + TCP_SKB_CB(buff)->sacked = 0; + TCP_SKB_CB(buff)->urg_ptr = 0; + buff->csum = 0; + buff->seq = tp->write_seq++; + buff->end_seq = tp->write_seq; + tp->snd_nxt = buff->end_seq; + + tp->window_clamp = dst->window; + tcp_select_initial_window(sock_rspace(sk)/2,sk->mss, + &tp->rcv_wnd, + &tp->window_clamp, + sysctl_tcp_window_scaling, + &tp->rcv_wscale); + + /* Ok, now lock the socket before we make it visible to + * the incoming packet engine. + */ + lock_sock(sk); + + /* Socket identity change complete, no longer + * in TCP_CLOSE, so enter ourselves into the + * hash tables. + */ + tcp_set_state(sk,TCP_SYN_SENT); + sk->prot->hash(sk); + + tp->rto = dst->rtt; + tcp_init_xmit_timers(sk); + tp->retransmits = 0; + + /* Send it off. */ + skb_queue_tail(&sk->write_queue, buff); + buff->when = jiffies; + tp->packets_out++; + tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL)); + tcp_statistics.TcpActiveOpens++; + + /* Timer for repeating the SYN until an answer. */ + tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); + + /* Now, it is safe to release the socket. */ + release_sock(sk); } -/* - * Send out a delayed ack, the caller does the policy checking +/* Send out a delayed ack, the caller does the policy checking * to see if we should even be here. See tcp_input.c:tcp_ack_snd_check() * for details. */ - void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout) { unsigned long timeout; @@ -799,169 +830,120 @@ void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout) timeout += jiffies; /* Use new timeout only if there wasn't a older one earlier. */ - if ((!tp->delack_timer.prev || !del_timer(&tp->delack_timer)) || - (timeout < tp->delack_timer.expires)) + if (!tp->delack_timer.prev) { tp->delack_timer.expires = timeout; - - add_timer(&tp->delack_timer); + add_timer(&tp->delack_timer); + } else { + if (timeout < tp->delack_timer.expires) + mod_timer(&tp->delack_timer, timeout); + } } - - -/* - * This routine sends an ack and also updates the window. - */ - +/* This routine sends an ack and also updates the window. */ void tcp_send_ack(struct sock *sk) { - struct sk_buff *buff; - struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); - struct tcphdr *th; - int tmp; - - if(sk->zapped) - return; /* We have been reset, we may not send again. */ + /* If we have been reset, we may not send again. */ + if(!sk->zapped) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff *buff; - /* We need to grab some memory, and put together an ack, - * and then put it into the queue to be sent. - */ - buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_ATOMIC); - if (buff == NULL) { - /* Force it to send an ack. We don't have to do this - * (ACK is unreliable) but it's much better use of - * bandwidth on slow links to send a spare ack than - * resend packets. + /* We are not putting this on the write queue, so + * tcp_transmit_skb() will set the ownership to this + * sock. */ - tcp_send_delayed_ack(tp, HZ/2); - return; - } - - clear_delayed_acks(sk); - - /* Assemble a suitable TCP frame. */ - buff->csum = 0; + buff = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_ATOMIC); + if (buff == NULL) { + /* Force it to send an ack. We don't have to do this + * (ACK is unreliable) but it's much better use of + * bandwidth on slow links to send a spare ack than + * resend packets. + */ + tcp_send_delayed_ack(tp, HZ/2); + return; + } - /* Put in the IP header and routing stuff. */ - tmp = tp->af_specific->build_net_header(sk, buff); - if (tmp < 0) { - kfree_skb(buff); - return; + /* Reserve space for headers and prepare control bits. */ + skb_reserve(buff, MAX_HEADER + sk->prot->max_header); + buff->csum = 0; + TCP_SKB_CB(buff)->flags = TCPCB_FLAG_ACK; + TCP_SKB_CB(buff)->sacked = 0; + TCP_SKB_CB(buff)->urg_ptr = 0; + + /* Send it off, this clears delayed acks for us. */ + buff->seq = buff->end_seq = tp->snd_nxt; + buff->when = jiffies; + tcp_transmit_skb(sk, buff); } - - th = (struct tcphdr *)skb_put(buff,tp->tcp_header_len); - memcpy(th, &sk->dummy_th, sizeof(struct tcphdr)); - - /* Swap the send and the receive. */ - th->window = ntohs(tcp_select_window(sk)); - th->seq = ntohl(tp->snd_nxt); - tp->last_ack_sent = tp->rcv_nxt; - th->ack_seq = htonl(tp->rcv_nxt); - tcp_build_and_update_options((__u32 *)(th + 1), tp); - - /* Fill in the packet and send it. */ - tp->af_specific->send_check(sk, th, tp->tcp_header_len, buff); - tp->af_specific->queue_xmit(buff); - tcp_statistics.TcpOutSegs++; } -/* - * This routine sends a packet with an out of date sequence - * number. It assumes the other end will try to ack it. +/* This routine sends a packet with an out of date sequence + * number. It assumes the other end will try to ack it. */ - void tcp_write_wakeup(struct sock *sk) { - struct sk_buff *buff, *skb; - struct tcphdr *t1; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int tmp; - - if (sk->zapped) - return; /* After a valid reset we can send no more. */ - - /* Write data can still be transmitted/retransmitted in the - * following states. If any other state is encountered, return. - * [listen/close will never occur here anyway] - */ - if ((1 << sk->state) & - ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1|TCPF_LAST_ACK|TCPF_CLOSING)) - return; - - if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) && (skb=tp->send_head)) { - struct tcphdr *th; - unsigned long win_size; - - /* We are probing the opening of a window - * but the window size is != 0 - * must have been a result SWS avoidance ( sender ) - */ - win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); - if (win_size < skb->end_seq - skb->seq) { - if (tcp_fragment(sk, skb, win_size)) { - printk(KERN_DEBUG "tcp_write_wakeup: " - "fragment failed\n"); - return; - } - } - - th = skb->h.th; - tcp_update_options((__u32 *)(th + 1), tp); - tp->af_specific->send_check(sk, th, th->doff * 4 + win_size, skb); - buff = skb_clone(skb, GFP_ATOMIC); - if (buff == NULL) + /* After a valid reset we can send no more. */ + if (!sk->zapped) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff *skb; + + /* Write data can still be transmitted/retransmitted in the + * following states. If any other state is encountered, return. + * [listen/close will never occur here anyway] + */ + if ((1 << sk->state) & + ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1| + TCPF_LAST_ACK|TCPF_CLOSING)) return; - skb_set_owner_w(buff, sk); - tp->packets_out++; - - clear_delayed_acks(sk); + if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) && + ((skb = tp->send_head) != NULL)) { + unsigned long win_size; - if (!tcp_timer_is_set(sk, TIME_RETRANS)) - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - - skb->when = jiffies; - update_send_head(sk); - tp->snd_nxt = skb->end_seq; - } else { - buff = sock_wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) - return; + /* We are probing the opening of a window + * but the window size is != 0 + * must have been a result SWS avoidance ( sender ) + */ + win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); + if (win_size < skb->end_seq - skb->seq) { + if (tcp_fragment(sk, skb, win_size)) + return; /* Let a retransmit get it. */ + } + update_send_head(sk); + skb->when = jiffies; + tp->snd_nxt = skb->end_seq; + tp->packets_out++; + tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)); + if (!tcp_timer_is_set(sk, TIME_RETRANS)) + tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); + } else { + /* We don't queue it, tcp_transmit_skb() sets ownership. */ + skb = alloc_skb(MAX_HEADER + sk->prot->max_header, + GFP_ATOMIC); + if (skb == NULL) + return; - buff->csum = 0; + /* Reserve space for headers and set control bits. */ + skb_reserve(skb, MAX_HEADER + sk->prot->max_header); + skb->csum = 0; + TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK; + TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->urg_ptr = 0; - /* Put in the IP header and routing stuff. */ - tmp = tp->af_specific->build_net_header(sk, buff); - if (tmp < 0) { - kfree_skb(buff); - return; + /* Use a previous sequence. This should cause the other + * end to send an ack. Don't queue or clone SKB, just + * send it. + */ + skb->seq = tp->snd_nxt - 1; + skb->end_seq = skb->seq; + skb->when = jiffies; + tcp_transmit_skb(sk, skb); } - - t1 = (struct tcphdr *) skb_put(buff, tp->tcp_header_len); - memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); - - /* Use a previous sequence. - * This should cause the other end to send an ack. - */ - - t1->seq = htonl(tp->snd_nxt-1); - t1->ack_seq = htonl(tp->rcv_nxt); - t1->window = htons(tcp_select_window(sk)); - tcp_build_and_update_options((__u32 *)(t1 + 1), tp); - - tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff); } - - /* Send it. */ - tp->af_specific->queue_xmit(buff); - tcp_statistics.TcpOutSegs++; } -/* - * A window probe timeout has occurred. - * If window is not closed send a partial packet - * else a zero probe. +/* A window probe timeout has occurred. If window is not closed send + * a partial packet else a zero probe. */ - void tcp_send_probe0(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index fdf8f50ec..54380b07d 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -77,11 +77,6 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - if((long)when <= 0) { - printk(KERN_DEBUG "xmit_timer <= 0 - timer:%d when:%lx\n", what, when); - when=HZ/50; - } - switch (what) { case TIME_RETRANS: /* When seting the transmit timer the probe timer @@ -91,24 +86,15 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when) */ if(tp->probe_timer.prev) del_timer(&tp->probe_timer); - if(tp->retransmit_timer.prev) - del_timer(&tp->retransmit_timer); - tp->retransmit_timer.expires=jiffies+when; - add_timer(&tp->retransmit_timer); + mod_timer(&tp->retransmit_timer, jiffies+when); break; case TIME_DACK: - if(tp->delack_timer.prev) - del_timer(&tp->delack_timer); - tp->delack_timer.expires=jiffies+when; - add_timer(&tp->delack_timer); + mod_timer(&tp->delack_timer, jiffies+when); break; case TIME_PROBE0: - if(tp->probe_timer.prev) - del_timer(&tp->probe_timer); - tp->probe_timer.expires=jiffies+when; - add_timer(&tp->probe_timer); + mod_timer(&tp->probe_timer, jiffies+when); break; case TIME_WRITE: @@ -150,17 +136,12 @@ static int tcp_write_err(struct sock *sk, int force) return 1; } -/* - * A write timeout has occurred. Process the after effects. BROKEN (badly) - */ - +/* A write timeout has occurred. Process the after effects. */ static int tcp_write_timeout(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - /* - * Look for a 'soft' timeout. - */ + /* Look for a 'soft' timeout. */ if ((sk->state == TCP_ESTABLISHED && tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) || (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) { @@ -206,11 +187,10 @@ void tcp_probe_timer(unsigned long data) return; } - /* - * *WARNING* RFC 1122 forbids this - * It doesn't AFAIK, because we kill the retransmit timer -AK - * FIXME: We ought not to do it, Solaris 2.5 actually has fixing - * this behaviour in Solaris down as a bug fix. [AC] + /* *WARNING* RFC 1122 forbids this + * It doesn't AFAIK, because we kill the retransmit timer -AK + * FIXME: We ought not to do it, Solaris 2.5 actually has fixing + * this behaviour in Solaris down as a bug fix. [AC] */ if (tp->probes_out > sysctl_tcp_retries2) { if(sk->err_soft) @@ -226,9 +206,10 @@ void tcp_probe_timer(unsigned long data) /* Clean up time. */ tcp_set_state(sk, TCP_CLOSE); } + } else { + /* Only send another probe if we didn't close things up. */ + tcp_send_probe0(sk); } - - tcp_send_probe0(sk); } static __inline__ int tcp_keepopen_proc(struct sock *sk) @@ -375,6 +356,21 @@ void tcp_retransmit_timer(unsigned long data) /* Clear delay ack timer. */ tcp_clear_xmit_timer(sk, TIME_DACK); + /* RFC 2018, clear all 'sacked' flags in retransmission queue, + * the sender may have dropped out of order frames and we must + * send them out should this timer fire on us. + */ + if(tp->sack_ok) { + struct sk_buff *skb = skb_peek(&sk->write_queue); + + while((skb != NULL) && + (skb != tp->send_head) && + (skb != (struct sk_buff *)&sk->write_queue)) { + TCP_SKB_CB(skb)->sacked = 0; + skb = skb->next; + } + } + /* Retransmission. */ tp->retrans_head = NULL; if (tp->retransmits == 0) { @@ -390,7 +386,7 @@ void tcp_retransmit_timer(unsigned long data) tp->dup_acks = 0; tp->high_seq = tp->snd_nxt; - tcp_do_retransmit(sk, 0); + tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); /* Increase the timeout each time we retransmit. Note that * we do not increase the rtt estimate. rto is initialized @@ -407,7 +403,7 @@ void tcp_retransmit_timer(unsigned long data) * implemented ftp to mars will work nicely. We will have to fix * the 120 second clamps though! */ - tp->backoff++; /* FIXME: always same as retransmits? -- erics */ + tp->backoff++; tp->rto = min(tp->rto << 1, 120*HZ); tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); @@ -523,18 +519,18 @@ void tcp_sltimer_handler(unsigned long data) void __tcp_inc_slow_timer(struct tcp_sl_timer *slt) { unsigned long now = jiffies; - unsigned long next = 0; unsigned long when; slt->last = now; - + when = now + slt->period; - if (del_timer(&tcp_slow_timer)) - next = tcp_slow_timer.expires; - - if (next && ((long)(next - when) < 0)) - when = next; - - tcp_slow_timer.expires = when; - add_timer(&tcp_slow_timer); + + if (tcp_slow_timer.prev) { + if ((long)(tcp_slow_timer.expires - when) >= 0) { + mod_timer(&tcp_slow_timer, when); + } + } else { + tcp_slow_timer.expires = when; + add_timer(&tcp_slow_timer); + } } diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c index 79ae3309e..5c5e5eeb3 100644 --- a/net/ipv4/timer.c +++ b/net/ipv4/timer.c @@ -59,10 +59,8 @@ void net_delete_timer (struct sock *t) void net_reset_timer (struct sock *t, int timeout, unsigned long len) { - net_delete_timer (t); t->timeout = timeout; - t->timer.expires = jiffies+len; - add_timer (&t->timer); + mod_timer(&t->timer, jiffies+len); } /* Now we will only be called whenever we need to do diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6ba50b280..a580b0010 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -315,8 +315,8 @@ struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, i continue; score++; } - if(sk->dummy_th.dest) { - if(sk->dummy_th.dest != sport) + if(sk->dport) { + if(sk->dport != sport) continue; score++; } @@ -412,8 +412,8 @@ static struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, continue; score++; } - if(s->dummy_th.dest) { - if(s->dummy_th.dest != rnum) + if(s->dport) { + if(s->dport != rnum) continue; score++; } @@ -453,7 +453,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk, if ((s->num != hnum) || (s->dead && (s->state == TCP_CLOSE)) || (s->daddr && s->daddr!=raddr) || - (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) || + (s->dport != rnum && s->dport != 0) || (s->rcv_saddr && s->rcv_saddr != laddr)) continue; break; @@ -644,12 +644,12 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) return -EOPNOTSUPP; #ifdef CONFIG_IP_TRANSPARENT_PROXY - if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY)) + if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY|MSG_NOSIGNAL)) return -EINVAL; if ((msg->msg_flags&MSG_PROXY) && !suser() ) return -EPERM; #else - if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT)) + if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL)) return -EINVAL; #endif @@ -686,7 +686,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) if (sk->state != TCP_ESTABLISHED) return -EINVAL; ufh.daddr = sk->daddr; - ufh.uh.dest = sk->dummy_th.dest; + ufh.uh.dest = sk->dport; /* BUGGG Khm... And who will validate it? Fixing it fastly... @@ -712,7 +712,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) #endif { ipc.addr = sk->saddr; - ufh.uh.source = sk->dummy_th.source; + ufh.uh.source = sk->sport; } ipc.opt = NULL; @@ -971,7 +971,7 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if(!sk->rcv_saddr) sk->rcv_saddr = rt->rt_src; sk->daddr = rt->rt_dst; - sk->dummy_th.dest = usin->sin_port; + sk->dport = usin->sin_port; sk->state = TCP_ESTABLISHED; if(uh_cache_sk == sk) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4a4060601..0241e0459 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: addrconf.c,v 1.37 1998/03/08 20:52:46 davem Exp $ + * $Id: addrconf.c,v 1.38 1998/03/20 09:12:14 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -298,10 +298,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) struct inet6_ifaddr *iter, **back; int hash; - ipv6_ifa_notify(RTM_DELADDR, ifp); - if (atomic_read(&addr_list_lock)) { ifp->flags |= ADDR_INVALID; + ipv6_ifa_notify(RTM_DELADDR, ifp); return; } @@ -330,6 +329,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) } back = &(iter->if_next); } + + ipv6_ifa_notify(RTM_DELADDR, ifp); kfree(ifp); } @@ -543,7 +544,7 @@ static int ipv6_generate_eui64(u8 *eui, struct device *dev) static void addrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev, - unsigned long info) + unsigned long expires, unsigned flags) { struct in6_rtmsg rtmsg; int err; @@ -553,8 +554,8 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev, rtmsg.rtmsg_dst_len = plen; rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; rtmsg.rtmsg_ifindex = dev->ifindex; - rtmsg.rtmsg_info = info; - rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF; + rtmsg.rtmsg_info = expires; + rtmsg.rtmsg_flags = RTF_UP|flags; rtmsg.rtmsg_type = RTMSG_NEWROUTE; /* Prevent useless cloning on PtP SIT. @@ -608,7 +609,7 @@ static void addrconf_add_lroute(struct device *dev) struct in6_addr addr; ipv6_addr_set(&addr, __constant_htonl(0xFE800000), 0, 0, 0); - addrconf_prefix_route(&addr, 10, dev, 0); + addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF); } static struct inet6_dev *addrconf_add_dev(struct device *dev) @@ -688,18 +689,20 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len) else rt_expires = jiffies + valid_lft * HZ; - rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT); + rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, RTF_LINKRT); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { - if (pinfo->onlink == 0 || valid_lft == 0) { - ip6_del_rt(rt); - rt = NULL; - } else { - rt->rt6i_expires = rt_expires; + if (rt->rt6i_flags&RTF_EXPIRES) { + if (pinfo->onlink == 0 || valid_lft == 0) { + ip6_del_rt(rt); + rt = NULL; + } else { + rt->rt6i_expires = rt_expires; + } } } else if (pinfo->onlink && valid_lft) { addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, - dev, rt_expires); + dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES); } /* Try to figure out our local address for this prefix */ @@ -1265,8 +1268,8 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp) addrconf_join_solict(dev, &ifp->addr); - if (ifp->prefix_len != 128) - addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0); + if (ifp->prefix_len != 128 && (ifp->flags&ADDR_PERMANENT)) + addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0, RTF_ADDRCONF); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { start_bh_atomic(); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index bc5ba892a..6a24bea8b 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.28 1998/03/08 05:56:49 davem Exp $ + * $Id: af_inet6.c,v 1.29 1998/03/18 07:52:11 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -75,7 +75,6 @@ static int inet6_create(struct socket *sock, int protocol) if (sk == NULL) goto do_oom; - /* Note for tcp that also wiped the dummy_th block for us. */ if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) { if (protocol && protocol != IPPROTO_TCP) goto free_and_noproto; @@ -138,7 +137,7 @@ static int inet6_create(struct socket *sock, int protocol) * the user to assign a number at socket * creation time automatically shares. */ - sk->dummy_th.source = ntohs(sk->num); + sk->sport = ntohs(sk->num); sk->prot->hash(sk); add_to_prot_sklist(sk); } @@ -229,8 +228,8 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EADDRINUSE; sk->num = snum; - sk->dummy_th.source = ntohs(sk->num); - sk->dummy_th.dest = 0; + sk->sport = ntohs(sk->num); + sk->dport = 0; sk->daddr = 0; sk->prot->rehash(sk); add_to_prot_sklist(sk); @@ -259,7 +258,7 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, if (peer) { if (!tcp_connected(sk->state)) return(-ENOTCONN); - sin->sin6_port = sk->dummy_th.dest; + sin->sin6_port = sk->dport; memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr, sizeof(struct in6_addr)); } else { @@ -272,7 +271,7 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, &sk->net_pinfo.af_inet6.rcv_saddr, sizeof(struct in6_addr)); - sin->sin6_port = sk->dummy_th.source; + sin->sin6_port = sk->sport; } *uaddr_len = sizeof(*sin); return(0); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 875e0f2ed..b87f31b06 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: datagram.c,v 1.13 1997/12/13 21:53:09 kuznet Exp $ + * $Id: datagram.c,v 1.14 1998/03/20 09:12:15 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) return 0; } -int datagram_send_ctl(struct msghdr *msg, struct device **src_dev, +int datagram_send_ctl(struct msghdr *msg, int *oif, struct in6_addr **src_addr, struct ipv6_options *opt, int *hlimit) { @@ -81,15 +81,15 @@ int datagram_send_ctl(struct msghdr *msg, struct device **src_dev, src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); if (src_info->ipi6_ifindex) { - int index = src_info->ipi6_ifindex; - - *src_dev = dev_get_by_index(index); + if (*oif && src_info->ipi6_ifindex != *oif) + return -EINVAL; + *oif = src_info->ipi6_ifindex; } - + if (!ipv6_addr_any(&src_info->ipi6_addr)) { struct inet6_ifaddr *ifp; - ifp = ipv6_chk_addr(&src_info->ipi6_addr, *src_dev, 0); + ifp = ipv6_chk_addr(&src_info->ipi6_addr, NULL, 0); if (ifp == NULL) { err = -EINVAL; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 96867403b..f181aec52 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.13 1998/02/12 07:43:41 davem Exp $ + * $Id: icmp.c,v 1.15 1998/03/21 07:28:03 davem Exp $ * * Based on net/ipv4/icmp.c * @@ -153,7 +153,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, struct ipv6hdr *hdr = skb->nh.ipv6h; struct sock *sk = icmpv6_socket->sk; struct in6_addr *saddr = NULL; - struct device *src_dev = NULL; + int iif = 0; struct icmpv6_msg msg; struct flowi fl; int addr_type = 0; @@ -203,7 +203,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, */ if (addr_type & IPV6_ADDR_LINKLOCAL) - src_dev = skb->dev; + iif = skb->dev->ifindex; /* * Must not send if we know that source is Anycast also. @@ -251,12 +251,17 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, fl.proto = IPPROTO_ICMPV6; fl.nl_u.ip6_u.daddr = &hdr->saddr; fl.nl_u.ip6_u.saddr = saddr; - fl.dev = src_dev; + fl.oif = iif; fl.uli_u.icmpt.type = type; fl.uli_u.icmpt.code = code; ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, MSG_DONTWAIT); + + /* Oops! We must purge cached dst, otherwise + all the following ICMP messages will go there :) --ANK + */ + dst_release(xchg(&sk->dst_cache, NULL)); } static void icmpv6_echo_reply(struct sk_buff *skb) @@ -294,12 +299,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl.proto = IPPROTO_ICMPV6; fl.nl_u.ip6_u.daddr = &hdr->saddr; fl.nl_u.ip6_u.saddr = saddr; - fl.dev = skb->dev; + fl.oif = skb->dev->ifindex; fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY; fl.uli_u.icmpt.code = 0; ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, MSG_DONTWAIT); + + /* Oops! We must purge cached dst, otherwise + all the following ICMP messages will go there :) --ANK + */ + dst_release(xchg(&sk->dst_cache, NULL)); } static __inline__ int ipv6_ext_hdr(u8 nexthdr) @@ -317,7 +327,8 @@ static __inline__ int ipv6_ext_hdr(u8 nexthdr) } -static void icmpv6_notify(int type, int code, unsigned char *buff, int len, +static void icmpv6_notify(struct sk_buff *skb, + int type, int code, unsigned char *buff, int len, struct in6_addr *saddr, struct in6_addr *daddr, struct inet6_protocol *protocol) { @@ -367,7 +378,7 @@ static void icmpv6_notify(int type, int code, unsigned char *buff, int len, continue; if (ipprot->err_handler) - ipprot->err_handler(type, code, pbuff, info, + ipprot->err_handler(skb, type, code, pbuff, info, saddr, daddr, ipprot); return; } @@ -457,7 +468,7 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev, case ICMPV6_TIME_EXCEED: case ICMPV6_PARAMPROB: - icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code, + icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code, (char *) (hdr + 1), ulen, saddr, daddr, protocol); break; @@ -493,7 +504,7 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev, * must pass to upper level */ - icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code, + icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code, (char *) (hdr + 1), ulen, saddr, daddr, protocol); }; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 9fce1acca..735ceeb5f 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_fib.c,v 1.11 1998/03/08 05:56:50 davem Exp $ + * $Id: ip6_fib.c,v 1.12 1998/03/20 09:12:16 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -418,9 +418,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt) (iter->rt6i_flowr == rt->rt6i_flowr) && (ipv6_addr_cmp(&iter->rt6i_gateway, &rt->rt6i_gateway) == 0)) { - if (rt->rt6i_expires == 0 || - (long)(rt->rt6i_expires - iter->rt6i_expires) > 0) - rt->rt6i_expires = iter->rt6i_expires; + if (!(iter->rt6i_flags&RTF_EXPIRES)) + return -EEXIST; + iter->rt6i_expires = rt->rt6i_expires; + if (!(rt->rt6i_flags&RTF_EXPIRES)) { + iter->rt6i_flags &= ~RTF_EXPIRES; + iter->rt6i_expires = rt->rt6i_expires; + } return -EEXIST; } } @@ -931,7 +935,8 @@ static int fib6_gc_node(struct fib6_node *fn, int timeout) * Seems, radix tree walking is absolutely broken, * but we will try in any case --ANK */ - if (rt->rt6i_expires && (long)(now - rt->rt6i_expires) < 0) { + if ((rt->rt6i_flags&RTF_EXPIRES) && rt->rt6i_expires + && (long)(now - rt->rt6i_expires) > 0) { struct rt6_info *old; old = rt; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 13029e175..0f1c710d3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.9 1998/03/08 05:56:50 davem Exp $ + * $Id: ip6_output.c,v 1.10 1998/03/20 09:12:17 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -82,64 +82,43 @@ int ip6_output(struct sk_buff *skb) /* * xmit an sk_buff (used by TCP) - * sk can be NULL (for sending RESETs) */ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, struct ipv6_options *opt) { - struct ipv6_pinfo *np = NULL; - struct dst_entry *dst = NULL; + struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL; + struct dst_entry *dst = skb->dst; struct ipv6hdr *hdr; int seg_len; + int hlimit; - hdr = skb->nh.ipv6h; - - if (sk) { - np = &sk->net_pinfo.af_inet6; - - if (sk->dst_cache) { - /* - * dst_check returns NULL if route is no longer valid - */ - dst = dst_check(&sk->dst_cache, np->dst_cookie); - } - } - - if (dst == NULL) { - dst = ip6_route_output(sk, fl); + /* Do something with IPv6 options headers here. */ - if (dst->error) { - /* - * NETUNREACH usually - */ - dst_release(dst); - return dst->error; - } - } + seg_len = skb->len; - skb->dst = dst_clone(dst); - seg_len = skb->tail - ((unsigned char *) hdr); - hdr = skb->nh.ipv6h; + hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr)); /* * Fill in the IPv6 header */ hdr->version = 6; - hdr->priority = np ? np->priority : 0; - - if (np) + if (np) { + hdr->priority = np->priority; memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3); - else + hlimit = np->hop_limit; + } else { + hdr->priority = 0; memset(hdr->flow_lbl, 0, 3); + hlimit = -1; + } + if (hlimit < 0) + hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit; - hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr)); + hdr->payload_len = htons(seg_len); hdr->nexthdr = fl->proto; - if (np == NULL || np->hop_limit < 0) - hdr->hop_limit = ((struct rt6_info*)dst)->rt6i_hoplimit; - else - hdr->hop_limit = np->hop_limit; + hdr->hop_limit = hlimit; ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr); ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr); @@ -147,12 +126,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, ipv6_statistics.Ip6OutRequests++; dst->output(skb); - if (sk) { - if (sk->dst_cache == NULL) - ip6_dst_store(sk, dst); - } else - dst_release(dst); - return 0; } @@ -412,6 +385,9 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, } dst = NULL; + + if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr)) + fl->oif = np->mcast_oif; if (sk->dst_cache) dst = dst_check(&sk->dst_cache, np->dst_cookie); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index c6714eea3..9bb2d4d3c 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.17 1998/03/08 05:56:51 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.18 1998/03/20 09:12:18 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -157,15 +157,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, case IPV6_MULTICAST_IF: { + int oif = 0; struct in6_addr addr; - err = copy_from_user(&addr, optval, sizeof(struct in6_addr)); - if(err) + if (copy_from_user(&addr, optval, sizeof(struct in6_addr))) return -EFAULT; - if (ipv6_addr_any(&addr)) { - np->oif = NULL; - } else { + if (!ipv6_addr_any(&addr)) { struct inet6_ifaddr *ifp; ifp = ipv6_chk_addr(&addr, NULL, 0); @@ -175,8 +173,13 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, break; } - np->oif = ifp->idev->dev; + oif = ifp->idev->dev->ifindex; + } + if (sk->bound_dev_if && sk->bound_dev_if != oif) { + retv = -EINVAL; + break; } + np->mcast_oif = oif; retv = 0; break; } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 3f881673c..407698eb1 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: mcast.c,v 1.13 1998/01/04 15:28:31 mj Exp $ + * $Id: mcast.c,v 1.14 1998/03/20 09:12:18 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -91,7 +91,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr) if (ifindex == 0) { struct rt6_info *rt; - rt = rt6_lookup(addr, NULL, NULL, 0); + rt = rt6_lookup(addr, NULL, 0, 0); if (rt) dev = rt->rt6i_dev; } else diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ce37117a3..2e437f2de 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -774,7 +774,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, int hlen; dev = skb->dev; - rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0); + rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 0); if (rt == NULL || rt->u.dst.error) { ND_PRINTK1("ndisc_send_redirect: hostunreach\n"); diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index b87d4696b..c010b0964 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -7,7 +7,7 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.6 1998/03/13 08:02:19 davem Exp $ + * Version: $Id: proc.c,v 1.7 1998/03/18 07:52:13 davem Exp $ * * Authors: David S. Miller (davem@caip.rutgers.edu) * @@ -68,8 +68,8 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta dest = &sp->net_pinfo.af_inet6.daddr; src = &sp->net_pinfo.af_inet6.rcv_saddr; } - destp = ntohs(sp->dummy_th.dest); - srcp = ntohs(sp->dummy_th.source); + destp = ntohs(sp->dport); + srcp = ntohs(sp->sport); if((format == 0) && (sp->state == TCP_TIME_WAIT)) { timer_active1 = timer_active2 = 0; timer_active = 3; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 5b182b7ef..7429a9210 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.18 1998/03/08 05:56:54 davem Exp $ + * $Id: raw.c,v 1.19 1998/03/20 09:12:20 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -349,7 +349,6 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct ipv6_options *opt = NULL; - struct device *dev = NULL; struct in6_addr *saddr = NULL; struct flowi fl; int addr_len = msg->msg_namelen; @@ -419,15 +418,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) return(-EINVAL); } + fl.oif = sk->bound_dev_if; + if (msg->msg_controllen) { opt = &opt_space; memset(opt, 0, sizeof(struct ipv6_options)); - err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit); - if (err < 0) { - printk(KERN_DEBUG "invalid msg_control\n"); + err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit); + if (err < 0) return err; - } } raw_opt = &sk->tp_pinfo.tp_raw; @@ -435,7 +434,6 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) fl.proto = proto; fl.nl_u.ip6_u.daddr = daddr; fl.nl_u.ip6_u.saddr = saddr; - fl.dev = dev; fl.uli_u.icmpt.type = 0; fl.uli_u.icmpt.code = 0; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5188de864..3015d254b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $ + * $Id: route.c,v 1.27 1998/03/21 07:28:04 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,6 +36,7 @@ #include <net/ip6_route.h> #include <net/ndisc.h> #include <net/addrconf.h> +#include <net/tcp.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> @@ -60,6 +61,7 @@ int ip6_rt_max_size = 4096; int ip6_rt_gc_min_interval = 5*HZ; int ip6_rt_gc_timeout = 60*HZ; int ip6_rt_gc_interval = 30*HZ; +int ip6_rt_gc_elasticity = 9; static struct rt6_info * ip6_rt_copy(struct rt6_info *ort); static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); @@ -205,21 +207,20 @@ static __inline__ void rt6_unlock(void) */ static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt, - struct device *dev, + int oif, int strict) { struct rt6_info *local = NULL; struct rt6_info *sprt; - RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict)); - if (dev) { + if (oif) { for (sprt = rt; sprt; sprt = sprt->u.next) { - if (sprt->rt6i_dev == dev) { - RDBG(("match --> %p\n", sprt)); - return sprt; + if (sprt->rt6i_dev) { + if (sprt->rt6i_dev->ifindex == oif) + return sprt; + if (sprt->rt6i_dev->flags&IFF_LOOPBACK) + local = sprt; } - if (sprt->rt6i_dev && (sprt->rt6i_dev->flags&IFF_LOOPBACK)) - local = sprt; } if (local) @@ -239,13 +240,12 @@ static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt, */ static struct rt6_info *rt6_dflt_pointer = NULL; -static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev) +static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif) { struct rt6_info *match = NULL; struct rt6_info *sprt; int mpri = 0; - RDBG(("rt6_best_dflt(%p,%p): ", rt, dev)); for (sprt = rt; sprt; sprt = sprt->u.next) { struct neighbour *neigh; @@ -278,8 +278,7 @@ static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev) break; }; - if (dev && sprt->rt6i_dev == dev) { - RDBG(("dev&&sprt->rt6i_dev==dev(%p), m+=2, ", dev)); + if (oif && sprt->rt6i_dev && sprt->rt6i_dev->ifindex == oif) { m += 2; } @@ -316,17 +315,14 @@ out: } struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, - struct device *dev, int flags) + int oif, int flags) { struct fib6_node *fn; struct rt6_info *rt; - RDBG(("rt6_lookup(%p,%p,%p,%x) from %p\n", - daddr, saddr, dev, flags, __builtin_return_address(0))); rt6_lock(); fn = fib6_lookup(&ip6_routing_table, daddr, saddr); - - rt = rt6_device_match(fn->leaf, dev, flags&RTF_LINKRT); + rt = rt6_device_match(fn->leaf, oif, flags&RTF_LINKRT); rt6_unlock(); return rt; } @@ -414,7 +410,7 @@ void ip6_route_input(struct sk_buff *skb) if ((rt->rt6i_flags & RTF_CACHE)) { if (ip6_rt_policy == 0) { - rt = rt6_device_match(rt, skb->dev, 0); + rt = rt6_device_match(rt, skb->dev->ifindex, 0); goto out; } @@ -432,7 +428,7 @@ void ip6_route_input(struct sk_buff *skb) #endif } - rt = rt6_device_match(rt, skb->dev, 0); + rt = rt6_device_match(rt, skb->dev->ifindex, 0); if (ip6_rt_policy == 0) { if (!rt->rt6i_nexthop && rt->rt6i_dev && @@ -462,44 +458,19 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl) struct dst_entry *dst; int strict; - RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl, - __builtin_return_address(0))); strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); rt6_lock(); -#if RT6_DEBUG >= 3 - RDBG(("lkup(")); - if(fl->nl_u.ip6_u.daddr) { - struct in6_addr *addr = fl->nl_u.ip6_u.daddr; - int i; - RDBG(("daddr[")); - for(i = 0; i < 8; i++) { - RDBG(("%04x%c", addr->s6_addr16[i], - i == 7 ? ']' : ':')); - } - } - if(fl->nl_u.ip6_u.saddr) { - struct in6_addr *addr = fl->nl_u.ip6_u.saddr; - int i; - RDBG(("saddr[")); - for(i = 0; i < 8; i++) { - RDBG(("%04x%c", addr->s6_addr16[i], - i == 7 ? ']' : ':')); - } - } -#endif fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr); - RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT")); - restart: rt = fn->leaf; if ((rt->rt6i_flags & RTF_CACHE)) { RDBG(("RTF_CACHE ")); if (ip6_rt_policy == 0) { - rt = rt6_device_match(rt, fl->dev, strict); + rt = rt6_device_match(rt, fl->oif, strict); /* BUGGGG! It is capital bug, that was hidden by not-cloning multicast routes. However, @@ -536,11 +507,11 @@ restart: if (rt->rt6i_flags & RTF_DEFAULT) { RDBG(("RTF_DEFAULT ")); if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) { - rt = rt6_best_dflt(rt, fl->dev); + rt = rt6_best_dflt(rt, fl->oif); RDBG(("best_dflt(%p) ", rt)); } } else { - rt = rt6_device_match(rt, fl->dev, strict); + rt = rt6_device_match(rt, fl->oif, strict); RDBG(("!RTF_DEFAULT devmatch(%p) ", rt)); } @@ -638,10 +609,10 @@ static int ip6_dst_gc() fib6_run_gc(expire); last_gc = now; if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh) - expire = ip6_rt_gc_timeout; + expire = ip6_rt_gc_timeout>>1; out: - expire >>= 1; + expire -= expire>>ip6_rt_gc_elasticity; end_bh_atomic(); return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size); } @@ -780,7 +751,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) goto out; } - grt = rt6_lookup(gw_addr, NULL, dev, RTF_LINKRT); + grt = rt6_lookup(gw_addr, NULL, dev->ifindex, RTF_LINKRT); if (grt == NULL || (grt->rt6i_flags&RTF_GATEWAY)) { *err = -EHOSTUNREACH; @@ -814,6 +785,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) rt->rt6i_dev = dev; rt->u.dst.pmtu = ipv6_get_mtu(dev); + rt->u.dst.rtt = TCP_TIMEOUT_INIT; if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; else @@ -1078,7 +1050,7 @@ struct rt6_info *rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr, struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ - rt = rt6_lookup(dest, NULL, dev, 0); + rt = rt6_lookup(dest, NULL, dev->ifindex, 0); if (rt == NULL || rt->u.dst.error) return NULL; @@ -1200,7 +1172,7 @@ void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu) return; } - rt = rt6_lookup(addr, NULL, dev, 0); + rt = rt6_lookup(addr, NULL, dev->ifindex, 0); if (rt == NULL || rt->u.dst.error) { #if RT6_DEBUG >= 2 @@ -1268,6 +1240,9 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) rt->u.dst.output = ort->u.dst.output; rt->u.dst.pmtu = ort->u.dst.pmtu; + rt->u.dst.rtt = ort->u.dst.rtt; + rt->u.dst.window = ort->u.dst.window; + rt->u.dst.mxlock = ort->u.dst.mxlock; rt->rt6i_hoplimit = ort->rt6i_hoplimit; rt->rt6i_dev = ort->rt6i_dev; @@ -1472,6 +1447,7 @@ int ip6_rt_addr_add(struct in6_addr *addr, struct device *dev) rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = dev_get("lo"); + rt->u.dst.rtt = TCP_TIMEOUT_INIT; rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev); rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev); rt->u.dst.obsolete = -1; @@ -1501,7 +1477,7 @@ int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev) { struct rt6_info *rt; - rt = rt6_lookup(addr, NULL, dev_get("lo"), RTF_LINKRT); + rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, RTF_LINKRT); if (rt && rt->rt6i_dst.plen == 128) return ip6_del_rt(rt); @@ -1811,6 +1787,8 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, #else mx = (struct rtattr*)skb->tail; RTA_PUT(skb, RTA_METRICS, 0, NULL); + if (rt->u.dst.mxlock) + RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock); if (rt->u.dst.pmtu) RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu); if (rt->u.dst.window) @@ -2158,6 +2136,9 @@ ctl_table ipv6_route_table[] = { {NET_IPV6_ROUTE_GC_INTERVAL, "gc_interval", &ip6_rt_gc_interval, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, + {NET_IPV6_ROUTE_GC_ELASTICITY, "gc_elasticity", + &ip6_rt_gc_elasticity, sizeof(int), 0644, NULL, + &proc_dointvec_jiffies}, {0} }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1d082c195..4f176cd60 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.60 1998/03/15 02:59:32 davem Exp $ + * $Id: tcp_ipv6.c,v 1.68 1998/03/22 19:14:50 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -42,8 +42,6 @@ #include <asm/uaccess.h> -#define ICMP_PARANOIA - extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; @@ -52,7 +50,6 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, struct sk_buff *skb); static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb); -static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb); static void tcp_v6_xmit(struct sk_buff *skb); static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, struct ipv6hdr *ip6h, @@ -79,7 +76,7 @@ static __inline__ int tcp_v6_sk_hashfn(struct sock *sk) struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr; struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr; __u16 lport = sk->num; - __u16 fport = sk->dummy_th.dest; + __u16 fport = sk->dport; return tcp_v6_hashfn(laddr, lport, faddr, fport); } @@ -113,12 +110,14 @@ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum) /* We must walk the whole port owner list in this case. -DaveM */ for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) { - if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) { - if(addr_type == IPV6_ADDR_ANY || - !sk2->rcv_saddr || - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, - &sk2->net_pinfo.af_inet6.rcv_saddr)) - break; + if(sk->bound_dev_if == sk2->bound_dev_if) { + if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) { + if(addr_type == IPV6_ADDR_ANY || + !sk2->rcv_saddr || + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, + &sk2->net_pinfo.af_inet6.rcv_saddr)) + break; + } } } if(sk2 != NULL) @@ -195,20 +194,35 @@ static void tcp_v6_rehash(struct sock *sk) SOCKHASH_UNLOCK(); } -static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum) +static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif) { struct sock *sk; struct sock *result = NULL; + int score, hiscore; + hiscore=0; sk = tcp_listening_hash[tcp_lhashfn(hnum)]; for(; sk; sk = sk->next) { if((sk->num == hnum) && (sk->family == AF_INET6)) { struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + + score = 1; if(!ipv6_addr_any(&np->rcv_saddr)) { - if(!ipv6_addr_cmp(&np->rcv_saddr, daddr)) - return sk; /* Best possible match. */ - } else if(!result) + if(ipv6_addr_cmp(&np->rcv_saddr, daddr)) + continue; + score++; + } + if (sk->bound_dev_if) { + if (sk->bound_dev_if != dif) + continue; + score++; + } + if (score == 3) + return sk; + if (score > hiscore) { + hiscore = score; result = sk; + } } } return result; @@ -223,7 +237,8 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor */ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, struct in6_addr *saddr, u16 sport, - struct in6_addr *daddr, u16 dport) + struct in6_addr *daddr, u16 dport, + int dif) { unsigned short hnum = ntohs(dport); struct sock *sk; @@ -240,9 +255,10 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, if(sk && sk->num == hnum && /* local port */ sk->family == AF_INET6 && /* address family */ - sk->dummy_th.dest == sport && /* remote port */ + sk->dport == sport && /* remote port */ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) && + (!sk->bound_dev_if || sk->bound_dev_if == dif)) goto hit; /* Optimize here for direct hit, only listening connections can @@ -253,9 +269,10 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, /* For IPV6 do the cheaper port and family tests first. */ if(sk->num == hnum && /* local port */ sk->family == AF_INET6 && /* address family */ - sk->dummy_th.dest == sport && /* remote port */ + sk->dport == sport && /* remote port */ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) { + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) && + (!sk->bound_dev_if || sk->bound_dev_if == dif)) { if (sk->state == TCP_ESTABLISHED) TCP_RHASH(sport) = sk; goto hit; /* You sunk my battleship! */ @@ -265,21 +282,22 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) if(sk->num == hnum && /* local port */ sk->family == AF_INET6 && /* address family */ - sk->dummy_th.dest == sport) { /* remote port */ + sk->dport == sport) { /* remote port */ struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) && - !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr)) + !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) && + (!sk->bound_dev_if || sk->bound_dev_if == dif)) goto hit; } #ifdef USE_QUICKSYNS listener_shortcut: #endif - sk = tcp_v6_lookup_listener(daddr, hnum); + sk = tcp_v6_lookup_listener(daddr, hnum, dif); hit: return sk; } -#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp)) +#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif)) static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len, struct in6_addr *saddr, @@ -323,8 +341,9 @@ static int tcp_v6_unique_address(struct sock *sk) * use passive ftp, I just cover this case for completeness) */ sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr, - sk->dummy_th.dest, - &sk->net_pinfo.af_inet6.rcv_saddr, snum); + sk->dport, + &sk->net_pinfo.af_inet6.rcv_saddr, snum, + sk->bound_dev_if); if((sk != NULL) && (sk->state != TCP_LISTEN)) retval = 0; break; @@ -344,11 +363,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, struct in6_addr *saddr = NULL; struct flowi fl; struct dst_entry *dst; - struct tcphdr *th; struct sk_buff *buff; - struct sk_buff *skb1; - int tmp; int addr_type; + int mss; if (sk->state != TCP_CLOSE) return(-EISCONN); @@ -383,7 +400,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, */ if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 && - usin->sin6_port == sk->dummy_th.source) + usin->sin6_port == sk->sport) return (-EINVAL); memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr)); @@ -421,9 +438,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &np->daddr; fl.nl_u.ip6_u.saddr = saddr; - fl.dev = NULL; + fl.oif = sk->bound_dev_if; fl.uli_u.ports.dport = usin->sin6_port; - fl.uli_u.ports.sport = sk->dummy_th.source; + fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); @@ -431,11 +448,23 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, dst_release(dst); return dst->error; } - + + if (dst->pmtu < 576) { + dst_release(dst); + return -EINVAL; + } + + if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) { + /* Ough! This guy tries to connect to link local + * address and did not specify interface. + * Actually we should kick him out, but + * we will be patient :) --ANK + */ + sk->bound_dev_if = dst->dev->ifindex; + } + ip6_dst_store(sk, dst); - np->oif = dst->dev; - if (saddr == NULL) { ifa = ipv6_get_saddr(dst, &np->daddr); @@ -449,117 +478,38 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, ipv6_addr_copy(&np->saddr, saddr); } - sk->dummy_th.dest = usin->sin6_port; - if (!tcp_v6_unique_address(sk)) + buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header), + 0, GFP_KERNEL); + + if (buff == NULL) + return -ENOBUFS; + + sk->dport = usin->sin6_port; + + if (!tcp_v6_unique_address(sk)) { + kfree_skb(buff); return -EADDRNOTAVAIL; + } /* * Init variables */ - lock_sock(sk); - tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3], np->daddr.s6_addr32[3], - sk->dummy_th.source, - sk->dummy_th.dest); - - tp->snd_wnd = 0; - tp->snd_wl1 = 0; - tp->snd_wl2 = tp->write_seq; - tp->snd_una = tp->write_seq; - - tp->rcv_nxt = 0; - - sk->err = 0; - - release_sock(sk); - - buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)), - 0, GFP_KERNEL); - if (buff == NULL) { - /* FIXME: Free route references etc??? */ - return(-ENOMEM); - } - - lock_sock(sk); - - tcp_v6_build_header(sk, buff); - - tp->tcp_header_len = sizeof(struct tcphdr) + - (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0); - - /* build the tcp header */ - th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); - buff->h.th = th; - - memcpy(th, (void *) &(sk->dummy_th), sizeof(*th)); - buff->seq = tp->write_seq++; - th->seq = htonl(buff->seq); - tp->snd_nxt = tp->write_seq; - buff->end_seq = tp->write_seq; - th->ack = 0; - th->syn = 1; - + sk->sport, sk->dport); sk->mtu = dst->pmtu; - sk->mss = (sk->mtu - sizeof(struct ipv6hdr) - tp->tcp_header_len); - - if (sk->mss < 1) { - printk(KERN_DEBUG "intial ipv6 sk->mss below 1\n"); - sk->mss = 1; /* Sanity limit */ - } - - tp->window_clamp = 0; /* FIXME: shouldn't ipv6 dst cache have this? */ - tcp_select_initial_window(sock_rspace(sk)/2,sk->mss, - &tp->rcv_wnd, - &tp->window_clamp, - sysctl_tcp_window_scaling, - &tp->rcv_wscale); - th->window = htons(tp->rcv_wnd); - - /* - * Put in the TCP options to say MTU. - */ - - tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps, - sysctl_tcp_window_scaling,tp->rcv_wscale); - th->doff = sizeof(*th)/4 + (tmp>>2); - buff->csum = 0; - tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff); - - tcp_set_state(sk, TCP_SYN_SENT); - - /* Socket identity change complete, no longer - * in TCP_CLOSE, so enter ourselves into the - * hash tables. - */ - sk->prot->hash(sk); - - /* FIXME: should use dcache->rtt if availiable */ - tp->rto = TCP_TIMEOUT_INIT; - - tcp_init_xmit_timers(sk); - - tp->retransmits = 0; - - skb_queue_tail(&sk->write_queue, buff); - tp->packets_out++; - buff->when = jiffies; - skb1 = skb_clone(buff, GFP_KERNEL); - if(skb1 != NULL) { - skb_set_owner_w(skb1, sk); - tcp_v6_xmit(skb1); + mss = sk->mtu - sizeof(struct ipv6hdr); +#if 0 + if (np->opt) { + /* Adjust mss */ } +#endif - /* Timer for repeating the SYN until an answer */ - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - tcp_statistics.TcpActiveOpens++; - tcp_statistics.TcpOutSegs++; + tcp_connect(sk, buff, mss); - release_sock(sk); - - return(0); + return 0; } static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) @@ -572,7 +522,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) * Do sanity checking for sendmsg/sendto/send */ - if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT)) + if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL)) goto out; if (msg->msg_name) { struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name; @@ -587,7 +537,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) if(sk->state == TCP_CLOSE) goto out; retval = -EISCONN; - if (addr->sin6_port != sk->dummy_th.dest) + if (addr->sin6_port != sk->dport) goto out; if (ipv6_addr_cmp(&addr->sin6_addr, &np->daddr)) goto out; @@ -606,7 +556,7 @@ out: return retval; } -void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, +void tcp_v6_err(struct sk_buff *skb, int type, int code, unsigned char *header, __u32 info, struct in6_addr *saddr, struct in6_addr *daddr, struct inet6_protocol *protocol) { @@ -616,13 +566,11 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, int err; int opening; struct tcp_opt *tp; -#ifdef ICMP_PARANOIA __u32 seq; -#endif /* XXX: length check for tcphdr missing here */ - sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source); + sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex); if (sk == NULL || sk->state == TCP_TIME_WAIT) { /* XXX: Update ICMP error count */ @@ -630,7 +578,6 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, } tp = &sk->tp_pinfo.af_tcp; -#ifdef ICMP_PARANOIA seq = ntohl(th->seq); if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) { if (net_ratelimit()) @@ -639,8 +586,6 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, (int)sk->state, seq, tp->snd_una, tp->snd_nxt); return; } -#endif - np = &sk->net_pinfo.af_inet6; if (type == ICMPV6_PKT_TOOBIG && sk->state != TCP_LISTEN) { @@ -656,9 +601,9 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &np->daddr; fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.dport = sk->dummy_th.dest; - fl.uli_u.ports.sport = sk->dummy_th.source; + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.dport = sk->dport; + fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); @@ -696,7 +641,6 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, req = tcp_v6_search_req(tp, &hd,th, &prev); if (!req) return; -#ifdef ICMP_PARANOIA if (seq != req->snt_isn) { if (net_ratelimit()) printk(KERN_DEBUG "icmp packet for openreq " @@ -704,7 +648,6 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, seq, req->snt_isn); return; } -#endif if (req->sk) { sk = req->sk; /* report error in accept */ } else { @@ -739,87 +682,42 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) { struct sk_buff * skb; - struct tcphdr *th; struct dst_entry *dst; struct flowi fl; - int tmp; - - skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC); - if (skb == NULL) - return; + int mss; fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr; fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr; - fl.dev = req->af.v6_req.dev; + fl.oif = req->af.v6_req.iif; fl.uli_u.ports.dport = req->rmt_port; - fl.uli_u.ports.sport = sk->dummy_th.source; + fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); if (dst->error) { - kfree_skb(skb); dst_release(dst); return; } - skb->dev = dst->dev; - skb_reserve(skb, (skb->dev->hard_header_len + 15) & ~15); - skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb,sizeof(struct ipv6hdr)); - - skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr)); - - /* Yuck, make this header setup more efficient... -DaveM */ - memset(th, 0, sizeof(struct tcphdr)); - th->syn = 1; - th->ack = 1; - th->source = sk->dummy_th.source; - th->dest = req->rmt_port; - skb->seq = req->snt_isn; - skb->end_seq = skb->seq + 1; - th->seq = ntohl(skb->seq); - th->ack_seq = htonl(req->rcv_isn + 1); - - /* Don't offer more than they did. - * This way we don't have to memorize who said what. - * FIXME: the selection of initial mss here doesn't quite - * match what happens under IPV4. Figure out the right thing to do. - */ - req->mss = min(sk->mss, req->mss); - if(sk->user_mss) - req->mss = min(req->mss, sk->user_mss); - if(req->tstamp_ok == 0) - req->mss += TCPOLEN_TSTAMP_ALIGNED; - - if (req->rcv_wnd == 0) { - __u8 rcv_wscale; - /* Set this up on the first call only */ - req->window_clamp = 0; /* FIXME: should be in dst cache */ - tcp_select_initial_window(sock_rspace(sk)/2,req->mss, - &req->rcv_wnd, - &req->window_clamp, - req->wscale_ok, - &rcv_wscale); - req->rcv_wscale = rcv_wscale; + mss = dst->pmtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr); +#if 0 + /* Subtract option length... */ + if (opt) { + mss -= opt->optlen; } - th->window = htons(req->rcv_wnd); - - tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok, - req->wscale_ok,req->rcv_wscale); - skb->csum = 0; - th->doff = (sizeof(*th) + tmp)>>2; - th->check = tcp_v6_check(th, sizeof(*th) + tmp, - &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr, - csum_partial((char *)th, sizeof(*th)+tmp, skb->csum)); - - /* Actually we should not attach dst to socket in state LISTEN, - it results in stale destination per listen socket and - overflow of routing cache. - (IPv4 has the same flaw with more unpleasant consequences.) - */ - ip6_dst_store(sk, dst); - ip6_xmit(sk, skb, &fl, req->af.v6_req.opt); +#endif + + skb = tcp_make_synack(sk, dst, req, mss); + if (skb) { + struct tcphdr *th = skb->h.th; - tcp_statistics.TcpOutSegs++; + th->check = tcp_v6_check(th, skb->len, + &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr, + csum_partial((char *)th, skb->len, skb->csum)); + + ip6_xmit(sk, skb, &fl, req->af.v6_req.opt); + } + dst_release(dst); } static void tcp_v6_or_free(struct open_request *req) @@ -866,8 +764,6 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req = tcp_openreq_alloc(); if (req == NULL) { - tcp_statistics.TcpAttemptFails++; - goto exit; } sk->ack_backlog++; @@ -876,22 +772,27 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req->rcv_isn = skb->seq; req->snt_isn = isn; - tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(skb->h.th,&tp,0); + tcp_parse_options(NULL, skb->h.th, &tp, 0); req->mss = tp.in_mss; if (tp.saw_tstamp) { req->mss -= TCPOLEN_TSTAMP_ALIGNED; req->ts_recent = tp.rcv_tsval; } req->tstamp_ok = tp.tstamp_ok; + req->sack_ok = tp.sack_ok; req->snd_wscale = tp.snd_wscale; req->wscale_ok = tp.wscale_ok; req->rmt_port = skb->h.th->source; ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr); ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr); req->af.v6_req.opt = NULL; /* FIXME: options */ - req->af.v6_req.dev = skb->dev; /* So that link locals have meaning */ + req->af.v6_req.iif = sk->bound_dev_if; + + /* So that link locals have meaning */ + if (!sk->bound_dev_if && ipv6_addr_type(&req->af.v6_req.rmt_addr)&IPV6_ADDR_LINKLOCAL) + req->af.v6_req.iif = skb->dev->ifindex; req->class = &or_ipv6; req->retrans = 0; @@ -928,6 +829,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct flowi fl; struct tcp_opt *newtp; struct sock *newsk; + int mss; if (skb->protocol == __constant_htons(ETH_P_IP)) { /* @@ -955,13 +857,36 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, return newsk; } - newsk = tcp_create_openreq_child(sk, req, skb); - if (newsk == NULL) { - dst_release(dst); - return NULL; + + if (dst == NULL) { + /* + * options / mss / route cache + */ + + fl.proto = IPPROTO_TCP; + fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr; + fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr; + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.dport = req->rmt_port; + fl.uli_u.ports.sport = sk->sport; + + dst = ip6_route_output(sk, &fl); } - newsk->dst_cache = NULL; + if (dst->error || dst->pmtu < 576) + goto out; + + + mss = dst->pmtu - sizeof(struct ipv6hdr); +#if 0 + /* Adjust mss by option size */ +#endif + + newsk = tcp_create_openreq_child(sk, req, skb, mss); + if (newsk == NULL) + goto out; + + ip6_dst_store(newsk, dst); newtp = &(newsk->tp_pinfo.af_tcp); @@ -969,52 +894,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr); ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr); ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); - np->oif = req->af.v6_req.dev; - - if (dst == NULL) { - /* - * options / mss / route cache - */ - - fl.proto = IPPROTO_TCP; - fl.nl_u.ip6_u.daddr = &np->daddr; - fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.dport = newsk->dummy_th.dest; - fl.uli_u.ports.sport = newsk->dummy_th.source; - - dst = ip6_route_output(newsk, &fl); - } - - ip6_dst_store(newsk, dst); - - newtp->tstamp_ok = req->tstamp_ok; - newtp->window_clamp = req->window_clamp; - newtp->rcv_wnd = req->rcv_wnd; - newtp->wscale_ok = req->wscale_ok; - if (newtp->wscale_ok) { - newtp->snd_wscale = req->snd_wscale; - newtp->rcv_wscale = req->rcv_wscale; - } else { - newtp->snd_wscale = newtp->rcv_wscale = 0; - newtp->window_clamp = min(newtp->window_clamp,65535); - } - if (newtp->tstamp_ok) { - newtp->ts_recent = req->ts_recent; - newtp->ts_recent_stamp = jiffies; - newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; - newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); - } else { - newtp->tcp_header_len = sizeof(struct tcphdr); - } - - if (dst->error) - newsk->mtu = req->af.v6_req.dev->mtu; - else - newsk->mtu = dst->pmtu; - - newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len, - (newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len)); + newsk->bound_dev_if = req->af.v6_req.iif; + newsk->mtu = dst->pmtu; + newsk->opt = NULL; newsk->daddr = LOOPBACK4_IPV6; newsk->saddr = LOOPBACK4_IPV6; @@ -1023,6 +905,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newsk->prot->hash(newsk); add_to_prot_sklist(newsk); return newsk; + +out: + dst_release(dst); + return NULL; } static void tcp_v6_send_reset(struct sk_buff *skb) @@ -1031,7 +917,7 @@ static void tcp_v6_send_reset(struct sk_buff *skb) struct sk_buff *buff; struct flowi fl; - if(th->rst) + if (th->rst) return; /* @@ -1039,21 +925,16 @@ static void tcp_v6_send_reset(struct sk_buff *skb) * and then put it into the queue to be sent. */ - buff = alloc_skb(MAX_RESET_SIZE, GFP_ATOMIC); + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); if (buff == NULL) return; - buff->dev = skb->dev; + skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); - tcp_v6_build_header(NULL, buff); + t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr)); - t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); + /* Swap the send and the receive. */ memset(t1, 0, sizeof(*t1)); - - /* - * Swap the send and the receive. - */ - t1->dest = th->source; t1->source = th->dest; t1->doff = sizeof(*t1)/4; @@ -1080,13 +961,21 @@ static void tcp_v6_send_reset(struct sk_buff *skb) buff->csum); fl.proto = IPPROTO_TCP; - fl.dev = skb->dev; + fl.oif = skb->dev->ifindex; fl.uli_u.ports.dport = t1->dest; fl.uli_u.ports.sport = t1->source; - ip6_xmit(NULL, buff, &fl, NULL); - tcp_statistics.TcpOutSegs++; - tcp_statistics.TcpOutRsts++; + /* sk = NULL, but it is safe for now. RST socket required. */ + buff->dst = ip6_route_output(NULL, &fl); + + if (buff->dst->error == 0) { + ip6_xmit(NULL, buff, &fl, NULL); + tcp_statistics.TcpOutSegs++; + tcp_statistics.TcpOutRsts++; + return; + } + + kfree_skb(buff); } static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, @@ -1182,7 +1071,7 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev, /* CHECKSUM_UNNECESSARY */ }; - sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest); + sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, dev->ifindex); if (!sk) { printk(KERN_DEBUG "socket not found\n"); @@ -1267,37 +1156,35 @@ do_time_wait: goto discard_it; } -static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb) +static int tcp_v6_rebuild_header(struct sock *sk) { + struct dst_entry *dst = NULL; struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; if (sk->dst_cache) - dst_check(&sk->dst_cache, np->dst_cookie); + dst = dst_check(&sk->dst_cache, np->dst_cookie); - if (sk->dst_cache == NULL) { + if (dst == NULL) { struct flowi fl; - struct dst_entry *dst; fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &np->daddr; fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.dport = sk->dummy_th.dest; - fl.uli_u.ports.sport = sk->dummy_th.source; + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.dport = sk->dport; + fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); - ip6_dst_store(sk, dst); - } - if (sk->dst_cache->error) { - /* - * lost route to destination - */ - return -EHOSTUNREACH; + if (dst->error) { + dst_release(dst); + return dst->error; + } + + ip6_dst_store(sk, dst); } - skb_pull(skb, skb->nh.raw - skb->data); - return 0; + return dst->error; } static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb) @@ -1319,20 +1206,7 @@ static struct sock * tcp_v6_get_sock(struct sk_buff *skb, struct tcphdr *th) saddr = &skb->nh.ipv6h->saddr; daddr = &skb->nh.ipv6h->daddr; - return tcp_v6_lookup(saddr, th->source, daddr, th->dest); -} - -static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb) -{ - skb_reserve(skb, (MAX_HEADER + 15) & ~15); - skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr)); - - /* - * FIXME: reserve space for option headers - * length member of np->opt - */ - - return 0; + return tcp_v6_lookup(saddr, th->source, daddr, th->dest, skb->dev->ifindex); } static void tcp_v6_xmit(struct sk_buff *skb) @@ -1340,22 +1214,33 @@ static void tcp_v6_xmit(struct sk_buff *skb) struct sock *sk = skb->sk; struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6; struct flowi fl; - int err; + struct dst_entry *dst = sk->dst_cache; fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &np->daddr; fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.sport = sk->dummy_th.source; - fl.uli_u.ports.dport = sk->dummy_th.dest; + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.sport = sk->sport; + fl.uli_u.ports.dport = sk->dport; - err = ip6_xmit(sk, skb, &fl, np->opt); + if (sk->dst_cache) + dst = dst_check(&sk->dst_cache, np->dst_cookie); - /* - * FIXME: check error handling. - */ + if (dst == NULL) { + dst = ip6_route_output(sk, &fl); - sk->err_soft = err; + if (dst->error) { + sk->err_soft = dst->error; + dst_release(dst); + return; + } + + ip6_dst_store(sk, dst); + } + + skb->dst = dst_clone(dst); + + ip6_xmit(sk, skb, &fl, np->opt); } static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) @@ -1365,11 +1250,10 @@ static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr)); - sin6->sin6_port = sk->dummy_th.dest; + sin6->sin6_port = sk->dport; } static struct tcp_func ipv6_specific = { - tcp_v6_build_header, tcp_v6_xmit, tcp_v6_send_check, tcp_v6_rebuild_header, @@ -1387,7 +1271,6 @@ static struct tcp_func ipv6_specific = { */ static struct tcp_func ipv6_mapped = { - tcp_v4_build_header, ip_queue_xmit, tcp_v4_send_check, tcp_v4_rebuild_header, @@ -1425,11 +1308,6 @@ static int tcp_v6_init_sock(struct sock *sk) sk->max_ack_backlog = SOMAXCONN; sk->mtu = 576; sk->mss = 536; - sk->dummy_th.doff = sizeof(sk->dummy_th)/4; - - /* Speed up by setting some standard state for the dummy_th. */ - sk->dummy_th.ack=1; - sk->dummy_th.doff=sizeof(struct tcphdr)>>2; /* Init SYN queue. */ tcp_synq_init(tp); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 40e9b0233..6078ab679 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $ + * $Id: udp.c,v 1.27 1998/03/21 07:28:06 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -151,8 +151,8 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport, !(sk->dead && (sk->state == TCP_CLOSE))) { struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; int score = 0; - if(sk->dummy_th.dest) { - if(sk->dummy_th.dest != sport) + if(sk->dport) { + if(sk->dport != sport) continue; score++; } @@ -241,7 +241,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ipv6_addr_copy(&np->daddr, daddr); - sk->dummy_th.dest = usin->sin6_port; + sk->dport = usin->sin6_port; /* * Check for a route to destination an obtain the @@ -251,9 +251,9 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) fl.proto = IPPROTO_UDP; fl.nl_u.ip6_u.daddr = daddr; fl.nl_u.ip6_u.saddr = NULL; - fl.dev = NULL; - fl.uli_u.ports.dport = sk->dummy_th.dest; - fl.uli_u.ports.sport = sk->dummy_th.source; + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.dport = sk->dport; + fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); @@ -363,7 +363,7 @@ out: return err; } -void udpv6_err(int type, int code, unsigned char *buff, __u32 info, +void udpv6_err(struct sk_buff *skb, int type, int code, unsigned char *buff, __u32 info, struct in6_addr *saddr, struct in6_addr *daddr, struct inet6_protocol *protocol) { @@ -428,8 +428,8 @@ static struct sock *udp_v6_mcast_next(struct sock *sk, if((s->num == num) && !(s->dead && (s->state == TCP_CLOSE))) { struct ipv6_pinfo *np = &s->net_pinfo.af_inet6; - if(s->dummy_th.dest) { - if(s->dummy_th.dest != rmt_port) + if(s->dport) { + if(s->dport != rmt_port) continue; } if(!ipv6_addr_any(&np->daddr) && @@ -644,7 +644,6 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name; struct ipv6_options *opt = NULL; - struct device *dev = NULL; struct flowi fl; int addr_len = msg->msg_namelen; struct in6_addr *daddr; @@ -692,7 +691,7 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) if (sk->state != TCP_ESTABLISHED) return(-EINVAL); - udh.uh.dest = sk->dummy_th.dest; + udh.uh.dest = sk->dport; daddr = &sk->net_pinfo.af_inet6.daddr; } @@ -708,22 +707,21 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) } udh.daddr = NULL; + fl.oif = sk->bound_dev_if; if (msg->msg_controllen) { opt = &opt_space; memset(opt, 0, sizeof(struct ipv6_options)); - err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit); - if (err < 0) { - printk(KERN_DEBUG "invalid msg_control\n"); + err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit); + if (err < 0) return err; - } if (opt->srcrt) udh.daddr = daddr; } - udh.uh.source = sk->dummy_th.source; + udh.uh.source = sk->sport; udh.uh.len = htons(len); udh.uh.check = 0; udh.iov = msg->msg_iov; @@ -733,7 +731,6 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) fl.proto = IPPROTO_UDP; fl.nl_u.ip6_u.daddr = daddr; fl.nl_u.ip6_u.saddr = saddr; - fl.dev = dev; fl.uli_u.ports.dport = udh.uh.dest; fl.uli_u.ports.sport = udh.uh.source; diff --git a/net/netbeui/README b/net/netbeui/README deleted file mode 100644 index 02e270b5f..000000000 --- a/net/netbeui/README +++ /dev/null @@ -1,19 +0,0 @@ - -NetBEUI is a rather weird protocol. There are about three different set -of connection and name spaces here. - -Firstly we have an array of 802.2 LLC links acting as reliable inter node -links for the nodes we are talking to do. We create and tear these down as -needed. In effect it goes around pretending ethernet is a set of bits of -wire and running pseudo X.25 over it. The LLC code is elsewhere (net/802). - -Secondly we have the netbios name space. When we sit on multiple networks -we have fun. Netbios isnt routable, so we have to arse around looking on -all our devices for names. - -Thirdly we have logical netbeui sessions on top of the whole heap. - - *Don't blame us* - -We didn't design the protocol. - diff --git a/net/netbeui/af_netbeui.c b/net/netbeui/af_netbeui.c deleted file mode 100644 index 6769edde5..000000000 --- a/net/netbeui/af_netbeui.c +++ /dev/null @@ -1,659 +0,0 @@ - -#include <linux/config.h> -#include <linux/module.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <asm/bitops.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/socket.h> -#include <linux/sockios.h> -#include <linux/in.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/if_ether.h> -#include <linux/route.h> -#include <linux/inet.h> -#include <linux/notifier.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <linux/termios.h> /* For TIOCOUTQ/INQ */ -#include <linux/poll.h> -#include <net/datalink.h> -#include <net/p8022.h> -#include <net/psnap.h> -#include <net/sock.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/firewall.h> -#include <linux/init.h> - - -#undef NETBEUI_DEBUG - - -#ifdef NETBEUI_DEBUG -#define DPRINT(x) print(x) -#else -#define DPRINT(x) -#endif - -#define min(a,b) (((a)<(b))?(a):(b)) - -/***********************************************************************************************************************\ -* * -* Handlers for the socket list. * -* * -\***********************************************************************************************************************/ - -static netbeui_socket *netbeui_socket_list=NULL; - -/* - * Note: Sockets may not be removed _during_ an interrupt or inet_bh - * handler using this technique. They can be added although we do not - * use this facility. - */ - -extern inline void netbeui_remove_socket(netbeui_socket *sk) -{ - sklist_remove_socket(&netbeui_socket_list,sk); -} - -extenr inline void netbeui_insert_socket(netbeui_socket *sk) -{ - sklist_insert_socket(&netbeui_socket_list,sk); - netbeui_socket_list=sk; - restore_flags(flags); -} - -static void netbeui_destroy_socket(netbeui_socket *sk) -{ - /* - * Release netbios logical channels first - */ - if(sk->af_nb.nb_link) - { - netbeui_delete_channel(sk->af_nb.nb_link); - sk->af_nb.nb_link=NULL; - } - if(sk->af_nb.src_name) - { - netbeui_release_name(sk->af_nb.src_name); - sk->af_nb.src_name=NULL; - } - if(sk->af_nb.dst_name) - { - netbeui_release_name(sk->af_nb.dst_name); - sk->af_nb.dst_name=NULL; - } - netbeui_remove_listener(sk); - sklist_destroy_socket(&netbeui_socket,sk); -} - -/* - * Called from proc fs - */ - -int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - return 0; -} - -/* - * A device event has occurred. Watch for devices going down and - * delete our use of them (iface and route). - */ - -static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - if(event==NETDEV_DOWN) - { - /* Discard any use of this */ - netbeui_drop_device((struct device *)ptr); - } - return NOTIFY_DONE; -} - -/*******************************************************************************************************************\ -* * -* Handling for system calls applied via the various interfaces to a netbeui socket object * -* * -\*******************************************************************************************************************/ - -static int netbeui_listen(struct socket *sock, int backlog) -{ - struct sock *sk=(netbeui_socket *)sock->data; - if(sk->state!=TCP_CLOSED) - return -EINVAL; - if(backlog<0) - return -EINVAL; - if(backlog<128) - sk->backlog=backlog; - else - sk->backlog=128; - sk->state=TCP_LISTEN; - sk->state_change(sk); - netbeui_add_listener(sk); - return 0; -} - -/* - * Create a socket. Initialise the socket, blank the addresses - * set the state. - */ - -static int netbeui_create(struct socket *sock, int protocol) -{ - netbeui_socket *sk; - sk=(netbeui_socket *)sk_alloc(GFP_KERNEL, 1); - if(sk==NULL) - return(-ENOBUFS); - switch(sock->type) - { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free((void *)sk); - return(-ESOCKTNOSUPPORT); - } - - MOD_INC_USE_COUNT; - - sock_init_data(sock,sk); - sk->mtu=1500; - return(0); -} - -/* - * Copy a socket. No work needed. - */ - -static int netbeui_dup(struct socket *newsock,struct socket *oldsock) -{ - return(netbeui_create(newsock,oldsock->type)); -} - -/* - * Free a socket. No work needed - */ - -static int netbeui_release(struct socket *sock, struct socket *peer) -{ - netbeui_socket *sk=(netbeui_socket *)sock->data; - if(sk==NULL) - return(0); - if(!sk->dead) - sk->state_change(sk); - sk->dead=1; - sock->data=NULL; - netbeui_destroy_socket(sk); - return(0); -} - -/* - * Set the address 'our end' of the connection. - */ - -static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len) -{ - netbeui_socket *sk; - struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr; - int err; - - sk=(netbeui_socket *)sock->data; - - if(sk->zapped==0) - return(-EINVAL); - - if(addr_len!=sizeof(struct sockaddr_netbeui)) - return -EINVAL; - - if(addr->snb_family!=AF_NETBEUI) - return -EAFNOSUPPORT; - - /* - * This will sleep. To meet POSIX it is non interruptible. - * Someone should give the 1003.1g authors an injection of - * imagination... - */ - - if(sk->af_nb.src_name!=NULL) - return -EINVAL; - - /* - * Try and get the name. It may return various 'invalid' name - * problem reports or EADDRINUSE if we or another node holds - * the desired name. - */ - - sk->af_nb.src_name=netbeui_alloc_name(addr, &err); - if(sk->af_nb.src_name==NULL) - return err; - /* - * Add us to the active socket list - */ - netbeui_insert_socket(sk); - sk->zapped=0; - return(0); -} - -/* - * Set the address we talk to. - */ - -static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr, - size_t addr_len, int flags) -{ - netbeui_socket *sk=(netbeui_socket *)sock->data; - struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr; - - /* - * Check pending operations - */ - - if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) - { - sock->state==SS_CONNECTED; - return 0; - } - - if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING) - { - sock->state==SS_UNCONNECTED; - return -ECONNREFUSED; - } - - if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK)) - return -EINPROGRESS; - - if(sk->state==TCP_ESTABLISHED) - return -EISCONN; - - /* - * If this is new it must really be new... - */ - - if(sk->af_nb.dst_name==NULL) - { - if(addr_len != sizeof(struct sockaddr_nb)) - return -EINVAL; - if(addr->snb_family!=AF_NETBEUI) - return -EAFNOSUPPORT; - /* - * Try and find the name - */ - } -} - -/* - * Not relevant - */ - -static int netbeui_socketpair(struct socket *sock1, struct socket *sock2) -{ - return(-EOPNOTSUPP); -} - -/* - * WRITE ME - */ - -static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags) -{ - if(newsock->data) - sk_free(newsock->data); - return -EOPNOTSUPP; -} - -/* - * Find the name of a netbeui socket. Just copy the right - * fields into the sockaddr. - */ - -static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr, - size_t *uaddr_len, int peer) -{ - struct sockaddr_netbeui snb; - netbeui_socket *sk; - - sk=(netbeui_socket *)sock->data; - if(sk->zapped) - { - return -EINVAL; - } - - *uaddr_len = sizeof(struct sockaddr_netbeui); - - if(peer) - { - if(sk->state!=TCP_ESTABLISHED) - return -ENOTCONN; - } - else - { - } - snb.snb_family = AF_NETBEUI; - memcpy(uaddr,&snb,sizeof(snb)); - return(0); -} - -/* - * Receive a packet (in skb) from device dev. - */ - -static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) -{ - return nb_llc_rcv(skb); -} - -static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) -{ - netbeui_socket *sk=(netbeui_socket *)sock->data; - struct sockaddr_nb *usnb=(struct sockaddr_nb *)msg->msg_name; - struct sk_buff *skb; - struct device *dev; - struct nbhdr *nbp; - int size; - struct netbeui_route *rt; - int loopback=0; - int err; - - if(flags) - return -EINVAL; - - if(len>1500) /* - headers!! */ - return -EMSGSIZE; - - if(usnb) - { - if(sk->zapped) - { - if(netbeui_autobind(sk)<0) - return -EBUSY; - } - - if(msg->msg_namelen <sizeof(*usnb)) - return(-EINVAL); - if(usnb->snb_family != AF_NETBEUI) - return -EINVAL; - /* Check broadcast */ - } - else - { - if(sk->state!=TCP_ESTABLISHED) - return -ENOTCONN; - /* Connected .. */ - } - - /* Build a packet */ - SOCK_DEBUG(sk, "SK %p: Got address.\n",sk); - size=sizeof(struct nbhdr)+len+nb_dl->header_length; /* For headers */ - - SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name); - size += dev->hard_header_len; - skb = sock_alloc_send_skb(sk, size, 0, 0 , &err); - if(skb==NULL) - return err; - - skb->sk=sk; - skb->free=1; - skb->arp=1; - skb_reserve(skb,nb_dl->header_length); - skb_reserve(skb,dev->hard_header_len); - skb->dev=dev; - SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); - nbp=(struct nbhdr *)skb_put(skb,sizeof(struct nbhdr)); - SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len); - err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len); - if (err) - { - kfree_skb(skb); - return -EFAULT; - } - -#ifdef CONFIG_FIREWALL - - if(call_out_firewall(AF_NETBEUI, skb->dev, nbp, NULL)!=FW_ACCEPT) - { - kfree_skb(skb); - return -EPERM; - } - -#endif - - if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1) - kfree_skb(skb); - SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len); - return len; -} - - -static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) -{ - netbeui_socket *sk=(netbeui_socket *)sock->data; - struct sockaddr_nb *snb=(struct sockaddr_nb *)msg->msg_name; - struct nbphdr *nbp = NULL; - int copied = 0; - struct sk_buff *skb; - int er = 0; - - if(addr_len) - *addr_len=sizeof(*snb); - - skb=skb_recv_datagram(sk,flags,noblock,&er); - if(skb==NULL) - return er; - - snb = (struct nbphdr *)(skb->h.raw); - if(sk->type==SOCK_RAW) - { - copied=skb->len - if(copied > size) - { - copied=size; - msg->msg_flags|=MSG_TRUNC; - } - er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied); - if (er) - goto out; - } - else - { - copied=skb->len - sizeof(*nbp); - if (copied > size) - { - copied = size; - msg->msg_flags|=MSG_TRUNC; - } - er = skb_copy_datagram_iovec(skb,sizeof(*nbp),msg->msg_iov,copied); - if (er) - goto out; - } - if(snb) - { - sat->sat_family=AF_NETBEUI; - /* Copy name over */ - } -out: - skb_free_datagram(sk, skb); - return er ? er : (copied); -} - - -static int netbeui_shutdown(struct socket *sk,int how) -{ - return -EOPNOTSUPP; -} - -static int netbeui_poll(struct socket *sock, poll_table *wait) -{ - netbeui_socket *sk=(netbeui_socket *)sock->data; - - return datagram_poll(sk,wait); -} - -/* - * Netbeui ioctl calls. - */ - -static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg) -{ - long amount=0; - netbeui_socket *sk=(netbeui_socket *)sock->data; - - switch(cmd) - { - /* - * Protocol layer - */ - case TIOCOUTQ: - amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); - if(amount<0) - amount=0; - break; - case TIOCINQ: - { - struct sk_buff *skb; - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if((skb=skb_peek(&sk->receive_queue))!=NULL) - amount=skb->len-sizeof(struct ddpehdr); - break; - } - case SIOCGSTAMP: - if (sk) - { - if(sk->stamp.tv_sec==0) - return -ENOENT; - return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0; - } - return -EINVAL; - /* - * Routing - */ - case SIOCADDRT: - case SIOCDELRT: - if(!suser()) - return -EPERM; - return(nbrtr_ioctl(cmd,(void *)arg)); - /* - * Interface - */ - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - return nbif_ioctl(cmd,(void *)arg); - /* - * Physical layer ioctl calls - */ - case SIOCSIFLINK: - case SIOCGIFHWADDR: - case SIOCSIFHWADDR: - case SIOCGIFFLAGS: - case SIOCSIFFLAGS: - case SIOCGIFMTU: - case SIOCGIFCONF: - case SIOCADDMULTI: - case SIOCDELMULTI: - - return(dev_ioctl(cmd,(void *) arg)); - - case SIOCSIFMETRIC: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMEM: - case SIOCSIFMEM: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - return -EINVAL; - - default: - return -EINVAL; - } - return put_user(amount, (int *)arg); -} - -static struct proto_ops netbeui_proto_ops = { - AF_NETBEUI, - - netbeui_create, - netbeui_dup, - netbeui_release, - netbeui_bind, - netbeui_connect, - netbeui_socketpair, - netbeui_accept, - netbeui_getname, - netbeui_poll, - netbeui_ioctl, - netbeui_listen, - netbeui_shutdown, - sock_no_setsockopt, - sock_no_getsockopt, - sock_no_fcntl, - netbeui_sendmsg, - netbeui_recvmsg -}; - -static struct notifier_block nb_notifier={ - nb_device_event, - NULL, - 0 -}; - -static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B}; - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry proc_netbeui = { - PROC_NET_NETBEUI, 9, "netbeui", - S_IFREG | S_IRUGO, 1, 0, 0 - 0, &proc_net_inode_operations, - netbeui_get_info -}; -#endif - -/* Called by proto.c on kernel start up */ - -__initfunc(void netbeui_proto_init(struct net_proto *pro)) -{ - (void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops); - if ((nb_dl = register_8022_client(nb_8022_id, netbeui_rcv)) == NULL) - printk(KERN_CRIT "Unable to register Netbeui with 802.2.\n"); - - register_netdevice_notifier(&nb_notifier); - -#ifdef CONFIG_PROC_FS - proc_net_register(&proc_netbeui); -#endif - - printk(KERN_INFO "NetBEUI 0.03 for Linux NET3.037\n"); -} - -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -int init_module(void) -{ - netbeui_proto_init(NULL); - return 0; -} - -void cleanup_module(void) -{ - unsigned long flags; -#ifdef CONFIG_PROC_FS - proc_net_unregister(PROC_NET_NETBEUI); -#endif - unregister_netdevice_notifier(&nb_notifier); - unregister_snap_client(nb_snap_id); - sock_unregister(netbeui_proto_ops.family); -} - -#endif /* MODULE */ diff --git a/net/netbeui/netbeui_llc.c b/net/netbeui/netbeui_llc.c deleted file mode 100644 index 29edc5acf..000000000 --- a/net/netbeui/netbeui_llc.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * NET3: 802.2 LLC supervisor for the netbeui protocols. - * - * The basic aim is to provide a self managing link layer supervisor - * for netbeui. It creates and destroys the 802.2 virtual connections - * as needed, and copes with the various races when a link goes down - * just as its requested etc. - * - * The upper layers are presented with the notion of an nb_link which - * is a potentially shared object that represents a logical path - * between two hosts. Each nb_link has usage counts and users can - * treat it as if its their own. - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/socket.h> -#include <linux/sockios.h> -#include <linux/notifier.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/datalink.h> -#include <net/p8022.h> -#include <net/psnap.h> -#include <net/sock.h> -#include <net/llc.h> -#include <net/netbeui.h> - - -/* - * When this routine is called the netbeui layer has decided to - * drop the link. There is a tiny risk that we might reuse the - * link after we decide. Thus before we blast the link into little - * tiny pieces we must check.... - */ - -static void netbeui_do_destroy(struct nb_link *nb) -{ - /* - * Are we wanted again. Bring it back. Sigh, wish people - * would make up their minds 8) - */ - if(nb->users>0) - { - nb->state=NETBEUI_CONNWAIT; - llc_connect_request(&nb->llc); - return; - } - /* - * Blam.... into oblivion it goes - */ - - llc_unregister(&nb->llc); - netbeui_free_link(nb); -} - -/* - * Handle netbeui events. Basically that means keep it up when it - * should be up, down when it should be down and handle all the data. - */ - -static void netbeui_event(llcptr llc) -{ - struct nb_link *nb=(struct nb_link *)llc; - - /* - * See what has occured - */ - - - /* - * Connect completion confirmation - */ - - if(llc->llc_callbacks&LLC_CONN_CONFIRM) - { - /* - * Link up if desired. Otherwise try frantically - * to close it. - */ - if(nb->state!=NETBEUI_DEADWAIT) - { - /* - * Wake pending writers - */ - nb->state=NETBEUI_OPEN; - netbeui_wakeup(nb); - } - else - llc_disconnect_request(llc); - } - - /* - * Data is passed to the upper netbeui layer - */ - - if(llc->llc_callbacks&LLC_DATA_INDIC) - { - netbeu_rcv_stream(llc,llc->inc_skb); - /* - * Frame free is controlled by our stream processor - */ - return; - } - - /* - * We got disconnected - */ - - if(llc->llc_callbacks&LLC_DISC_INDICATION) - { - if(nb->state==NETBEUI_DEADWAIT) - { - netbeui_do_destroy(nb); - return; - } - if(nb->state==NETBEUI_DISCWAIT) - { - llc_connect_request(llc); - nb->state=NETBEUI_CONNWAIT; - } - } - - /* - * Miscellaneous burps - */ - - if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM| - LLC_RST_CONFIRM)) - { - /* - * Reset. - * Q: Is tearing the link down the right answer ? - * - * For now we just carry on - */ - } - - /* - * Track link busy status - */ - - if(llc->llc_callbacks&LLC_REMOTE_BUSY) - nb->busy=1; /* Send no more for a bit */ - if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY) - { - /* Coming unbusy may wake sending threads */ - nb->busy=0; - netbeui_wakeup(nb); - } - /* - * UI frames are passed to the upper netbeui layer. - */ - if(llc->llc_callbacks&LLC_UI_DATA) - { - netbeui_rcv_dgram(llc,llc->inc_skb); - return; - } - - /* We ignore TST, XID, FRMR stuff */ - /* FIXME: We need to free frames here once I fix the callback! */ - if(llc->inc_skb) - kfree_skb(skb); -} - -/* - * Netbeui has created a new logical link. As a result we will - * need to find or create a suitable 802.2 LLC session and join - * it. - */ - -struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri) -{ - struct nb_link *nb=netbeui_find_channel(dev,remote_mac); - if(nb) - { - if(nb->state==NETBEUI_DEADWAIT) - { - /* - * We had commenced a final shutdown. We - * cannot abort that (we sent the packet) but - * we can shift the mode to DISCWAIT. That will - * cause the disconnect event to bounce us - * back into connected state. - */ - nb->state==NETBEUI_DISCWAIT; - } - nb->users++; - return nb; - } - nb=netbeui_alloc_link(pri); - if(nb==NULL) - return NULL; - - /* - * Internal book keeping - */ - - nb->dev=dev; - nb->users=1; - nb->busy=0; - nb->wakeup=NULL; - nb->state=NETBEUI_CONNWAIT; - memcpy(nb->remote_mac, remote_mac, ETH_ALEN); - - /* - * Now try and attach an LLC. - */ - - if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event, - remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0) - { - netbeui_free_link(nb); - return NULL; - } - - /* - * Commence connection establishment. - */ - - llc_connect_request(&nb->llc); - - /* - * Done - */ - - nb->next=nb_link_list; - nb_link_list=nb; - - return nb; -} - -/* - * A logical netbeui channel has died. If the channel has no - * further users we commence shutdown. - */ - -int netbeui_delete_channel(struct nb_link *nb) -{ - nb->users--; - - /* - * FIXME: Must remove ourselves from the nb_link chain when - * we add that bit - */ - - if(nb->users) - return 0; - - /* - * Ensure we drop soon. The disconnect confirm will let - * us fix the deletion. If someone wants the link at - * the wrong moment nothing bad will occur. The create - * or the do_destroy will sort it. - */ - - nb->state = NETBEUI_DEADWAIT; - llc_disconnect_request(lp); - return 0; -} - - diff --git a/net/netbeui/netbeui_name.c b/net/netbeui/netbeui_name.c deleted file mode 100644 index c5a579597..000000000 --- a/net/netbeui/netbeui_name.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * NetBIOS name handler - */ - -/* - * You must hold the netbios name lock before using these. - */ - -struct nb_name *nb_name_find(struct device *dev,const char * name) -{ - struct nb_name *nb=nb_name_list; - while(nb!=NULL) - { - if((dev==NULL || dev==nb->dev) && - strncmp(name,nb->name, NB_NAME_LEN)==0) - return nb; - nb=nb->next; - } - return NULL; -} - -int nb_name_add(struct device *dev, const char *name, int ours, int pri) -{ - struct nb_name *nb=kmalloc(sizeof(*nb), pri); - if(nb==NULL) - return NULL; - nb->dev=dev; - strncpy(nb->name,name,NB_NAME_LEN); - nb->name[NB_NAME_LEN-1]=0; - nb->next=nb_name_list; - nb->ours=ours; - nb_name_list=nb; -} - -void nb_name_delete(struct nb_name *nb) -{ - struct nb_name *i=&nb_name_list; - while((*i)!=NULL) - { - if(*i==nb) - { - *i=nb->next; - kfree_s(nb,sizeof(*nb)); - return; - } - i=&((*i)->next); - } - printk(KERN_ERR "nb_name_delete: bad name pointer!\n"); -} - -/* - * NETBIOS name handlers - */ - -static void nb_defend(struct device *dev, const char *name) -{ - struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC); - if(nskb==NULL) - return; - /* Build a name defence packet */ - nskb->dev = dev; - nskb->priority = TC_PRIO_CONTROL; - dev_queue_xmit(nskb); -} - -void netbeui_heard_name(struct device *dev, struct sk_buff *skb) -{ - struct nb_name *nb; - name=... - - if((nb=nb_name_find(dev,name))!=NULL) - { - /* - * If we own the name then defend it - */ - if(nb->our && !nb->state==NB_ACQUIRE) - nb_defend(dev,name); - /* - * A name has been resolved. Wake up pending - * connectors. - */ - if(nb->state==NB_QUERY) - { - nb->state=NB_OTHER; - nb_complete(nb,skb); - } - } - kfree_skb(skb); - return 0; -} - -/* - * Handle incoming name defences - */ - -void netbeui_name_defence(struct dev *dev, struct sk_buff *skb) -{ - struct nb_name *name; - name= - - if((nb=nb_name_find(dev,name))!=NULL) - { - if(nb->ours) - { - /* - * We wanted it, we got told its used - */ - if(nb->state==NB_ACQUIRE) - { - /* - * Fill in the record for its true - * owner. Set the state first as - * nb_complete may well delete the - * record. - */ - nb->state=NB_OTHER; - nb_complete(nb,skb); - nb_wakeup(); - } - /* - * We own it we got told its used. This is - * a deep cack even that can only occur when - * a bridge comes back and the net was split. - * Make sure both sides lose. - */ - if(nb->state==NB_OURS || nb->state==NB_COLLIDE) - { - nb->state=NR_COLLIDE; - nb_wakeup(); - /* - * Kill the other copy too - */ - nb_defend(dev,name); - /* - * Timer expiry will delete our - * record. - */ - nb_start_timer(nb, NB_TIME_COLLIDED); - } - } - } - kfree_skb(skb); -} - -void netbeui_name_query(struct dev *dev, struct sk_buff *skb) -{ - char *name=... - struct nb_name *nb=nb_find_name(dev,name); - - if(nb!=NULL && nb->ours) - { - struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC); - if(nskb!=NULL) - { - /* Build a name reply packet */ - nskb->dev = dev; - nskb->priority = TC_PRIO_CONTROL; - dev_queue_xmit(nskb); - } - } - kfree_skb(skb); -} - diff --git a/net/netsyms.c b/net/netsyms.c index ad51e9a3e..9ce58d285 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -273,7 +273,6 @@ EXPORT_SYMBOL(tcp_statistics); EXPORT_SYMBOL(tcp_rcv_state_process); EXPORT_SYMBOL(tcp_timewait_state_process); EXPORT_SYMBOL(tcp_do_sendmsg); -EXPORT_SYMBOL(tcp_v4_build_header); EXPORT_SYMBOL(tcp_v4_rebuild_header); EXPORT_SYMBOL(tcp_v4_send_check); EXPORT_SYMBOL(tcp_v4_conn_request); @@ -291,6 +290,9 @@ EXPORT_SYMBOL(tcp_prot); EXPORT_SYMBOL(tcp_openreq_cachep); EXPORT_SYMBOL(ipv4_specific); EXPORT_SYMBOL(tcp_simple_retransmit); +EXPORT_SYMBOL(tcp_transmit_skb); +EXPORT_SYMBOL(tcp_connect); +EXPORT_SYMBOL(tcp_make_synack); EXPORT_SYMBOL(xrlim_allow); @@ -374,6 +376,7 @@ EXPORT_SYMBOL(unregister_netdevice_notifier); /* support for loadable net drivers */ #ifdef CONFIG_NET +EXPORT_SYMBOL(loopback_dev); EXPORT_SYMBOL(register_netdevice); EXPORT_SYMBOL(unregister_netdevice); EXPORT_SYMBOL(register_netdev); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 74fc7af82..858ea0e73 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -262,9 +262,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name; struct sk_buff *skb; struct device *dev; - struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name; unsigned short proto=0; int err; @@ -309,6 +309,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, return -EMSGSIZE; dev_lock_list(); + err = -ENOBUFS; skb = sock_wmalloc(sk, len+dev->hard_header_len+15, 0, GFP_KERNEL); /* @@ -318,10 +319,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, */ if (skb == NULL) - { - dev_unlock_list(); - return(-ENOBUFS); - } + goto out_unlock; /* * Fill it in @@ -339,36 +337,32 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, skb->data -= dev->hard_header_len; skb->tail -= dev->hard_header_len; } + + /* Returns -EFAULT on error */ err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); skb->protocol = proto; skb->dev = dev; skb->priority = sk->priority; - dev_unlock_list(); + if (err) + goto out_free; + + err = -ENETDOWN; + if (!(dev->flags & IFF_UP)) + goto out_free; /* * Now send it */ - if (err) - { - err = -EFAULT; - } - else - { - if (!(dev->flags & IFF_UP)) - { - err = -ENETDOWN; - } - } - - if (err) - { - kfree_skb(skb); - return err; - } - + dev_unlock_list(); dev_queue_xmit(skb); return(len); + +out_free: + kfree_skb(skb); +out_unlock: + dev_unlock_list(); + return err; } #endif @@ -434,13 +428,12 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name; struct sk_buff *skb; struct device *dev; - struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name; unsigned short proto; - int ifindex; - int err; - int reserve = 0; + unsigned char *addr; + int ifindex, err, reserve = 0; /* * Check the flags. @@ -454,13 +447,15 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len, */ if (saddr == NULL) { - ifindex = sk->protinfo.af_packet->ifindex; - proto = sk->num; + ifindex = sk->protinfo.af_packet->ifindex; + proto = sk->num; + addr = NULL; } else { if (msg->msg_namelen < sizeof(struct sockaddr_ll)) return -EINVAL; - ifindex = saddr->sll_ifindex; - proto = saddr->sll_protocol; + ifindex = saddr->sll_ifindex; + proto = saddr->sll_protocol; + addr = saddr->sll_addr; } dev = dev_get_by_index(ifindex); @@ -474,55 +469,50 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len, dev_lock_list(); - skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, msg->msg_flags&MSG_DONTWAIT, &err); - - if (skb==NULL) { - dev_unlock_list(); - return err; - } + skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, + msg->msg_flags & MSG_DONTWAIT, &err); + if (skb==NULL) + goto out_unlock; skb_reserve(skb, (dev->hard_header_len+15)&~15); skb->nh.raw = skb->data; if (dev->hard_header) { - if (dev->hard_header(skb, dev, ntohs(proto), - saddr ? saddr->sll_addr : NULL, - NULL, len) < 0 - && sock->type == SOCK_DGRAM) { - kfree_skb(skb); - dev_unlock_list(); - return -EINVAL; - } + int res; + err = -EINVAL; + res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len); if (sock->type != SOCK_DGRAM) { skb->tail = skb->data; skb->len = 0; - } + } else if (res < 0) + goto out_free; } + /* Returns -EFAULT on error */ err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); skb->protocol = proto; skb->dev = dev; skb->priority = sk->priority; - dev_unlock_list(); + if (err) + goto out_free; + + err = -ENETDOWN; + if (!(dev->flags & IFF_UP)) + goto out_free; /* * Now send it */ - if (err) { - err = -EFAULT; - } else { - if (!(dev->flags & IFF_UP)) - err = -ENETDOWN; - } - - if (err) { - kfree_skb(skb); - return err; - } - + dev_unlock_list(); dev_queue_xmit(skb); return(len); + +out_free: + kfree_skb(skb); +out_unlock: + dev_unlock_list(); + return err; } static void packet_destroy_timer(unsigned long data) @@ -699,6 +689,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len static int packet_create(struct socket *sock, int protocol) { struct sock *sk; + int err; if (!suser()) return -EPERM; @@ -711,27 +702,23 @@ static int packet_create(struct socket *sock, int protocol) sock->state = SS_UNCONNECTED; MOD_INC_USE_COUNT; + + err = -ENOBUFS; sk = sk_alloc(AF_PACKET, GFP_KERNEL, 1); - if (sk == NULL) { - MOD_DEC_USE_COUNT; - return -ENOBUFS; - } + if (sk == NULL) + goto out; sk->reuse = 1; + sock->ops = &packet_ops; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) sock->ops = &packet_ops_spkt; - else #endif - sock->ops = &packet_ops; sock_init_data(sock,sk); sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt), GFP_KERNEL); - if (sk->protinfo.af_packet == NULL) { - sk_free(sk); - MOD_DEC_USE_COUNT; - return -ENOBUFS; - } + if (sk->protinfo.af_packet == NULL) + goto out_free; memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt)); sk->zapped=0; sk->family = AF_PACKET; @@ -741,13 +728,11 @@ static int packet_create(struct socket *sock, int protocol) * Attach a protocol block */ + sk->protinfo.af_packet->prot_hook.func = packet_rcv; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt; - else #endif - sk->protinfo.af_packet->prot_hook.func = packet_rcv; - sk->protinfo.af_packet->prot_hook.data = (void *)sk; if (protocol) { @@ -758,6 +743,12 @@ static int packet_create(struct socket *sock, int protocol) sklist_insert_socket(&packet_sklist, sk); return(0); + +out_free: + sk_free(sk); +out: + MOD_DEC_USE_COUNT; + return err; } /* @@ -832,10 +823,8 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, int len, /* We can't use skb_copy_datagram here */ err = memcpy_toiovec(msg->msg_iov, skb->data, copied); - if (err) { - err = -EFAULT; + if (err) goto out_free; - } sk->stamp=skb->stamp; if (msg->msg_name) @@ -932,37 +921,39 @@ static void packet_dev_mclist(struct device *dev, struct packet_mclist *i, int w static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq) { - int err; struct packet_mclist *ml, *i; struct device *dev; + int err; rtnl_shlock(); - dev = dev_get_by_index(mreq->mr_ifindex); - - i = NULL; err = -ENODEV; + dev = dev_get_by_index(mreq->mr_ifindex); if (!dev) goto done; + err = -EINVAL; if (mreq->mr_alen > dev->addr_len) goto done; + err = -ENOBUFS; i = (struct packet_mclist *)kmalloc(sizeof(*i), GFP_KERNEL); + if (i == NULL) + goto done; + err = 0; for (ml=sk->protinfo.af_packet->mclist; ml; ml=ml->next) { if (ml->ifindex == mreq->mr_ifindex && ml->type == mreq->mr_type && ml->alen == mreq->mr_alen && memcmp(ml->addr, mreq->mr_address, ml->alen) == 0) { ml->count++; - err = 0; + /* Free the new element ... */ + kfree(i); goto done; } } - err = -ENOBUFS; - if (i == NULL) - goto done; + i->type = mreq->mr_type; i->ifindex = mreq->mr_ifindex; i->alen = mreq->mr_alen; @@ -971,13 +962,9 @@ static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq) i->next = sk->protinfo.af_packet->mclist; sk->protinfo.af_packet->mclist = i; packet_dev_mc(dev, i, +1); - i = NULL; - err = 0; done: rtnl_shunlock(); - if (i) - kfree(i); return err; } @@ -1109,13 +1096,12 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg case FIOGETOWN: case SIOCGPGRP: return put_user(sk->proc, (int *)arg); - return(0); case SIOCGSTAMP: if(sk->stamp.tv_sec==0) return -ENOENT; - err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)); - if (err) - err = -EFAULT; + err = -EFAULT; + if (!copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + err = 0; return err; case SIOCGIFFLAGS: #ifndef CONFIG_INET diff --git a/net/socket.c b/net/socket.c index dc77ef3e8..6220cff45 100644 --- a/net/socket.c +++ b/net/socket.c @@ -646,15 +646,17 @@ asmlinkage int sys_socket(int family, int type, int protocol) goto out; retval = get_fd(sock->inode); - if (retval < 0) { - sock_release(sock); - goto out; - } + if (retval < 0) + goto out_release; + sock->file = fcheck(retval); - sock->file = current->files->fd[retval]; out: unlock_kernel(); return retval; + +out_release: + sock_release(sock); + goto out; } /* @@ -787,9 +789,8 @@ asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_ad { struct inode *inode; struct socket *sock, *newsock; - int err; + int err, len; char address[MAX_SOCK_ADDR]; - int len; lock_kernel(); sock = sockfd_lookup(fd, &err); @@ -815,7 +816,7 @@ restart: if ((err = get_fd(inode)) < 0) goto out_release; - newsock->file = current->files->fd[err]; + newsock->file = fcheck(err); if (upeer_sockaddr) { @@ -1141,19 +1142,21 @@ asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags) char address[MAX_SOCK_ADDR]; struct iovec iov[UIO_FASTIOV]; unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */ - struct msghdr msg_sys; - int err= -EINVAL; - int total_len; unsigned char *ctl_buf = ctl; + struct msghdr msg_sys; + int err, total_len; lock_kernel(); - err=-EFAULT; + err = -EFAULT; if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr))) goto out; + /* do not move before msg_sys is valid */ - if (msg_sys.msg_iovlen>UIO_MAXIOV) + err = -EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) goto out; + /* This will also move the address data into kernel space */ err = verify_iovec(&msg_sys, iov, address, VERIFY_READ); if (err < 0) @@ -1163,7 +1166,7 @@ asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags) sock = sockfd_lookup(fd, &err); if (!sock) - goto out; + goto out_freeiov; if (msg_sys.msg_controllen) { @@ -1197,9 +1200,10 @@ failed: if (ctl_buf != ctl) sock_kfree_s(sock->sk, ctl_buf, msg_sys.msg_controllen); failed2: + sockfd_put(sock); +out_freeiov: if (msg_sys.msg_iov != iov) kfree(msg_sys.msg_iov); - sockfd_put(sock); out: unlock_kernel(); return err; @@ -1228,16 +1232,13 @@ asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags) int *uaddr_len; lock_kernel(); + err=-EFAULT; if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr))) - { - err=-EFAULT; goto out; - } - if (msg_sys.msg_iovlen>UIO_MAXIOV) - { - err=-EINVAL; + + err=-EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) goto out; - } /* * Save the user-mode address (verify_iovec will change the diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 761bfd242..0c4cc7f5a 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -14,11 +14,12 @@ #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/auth.h> +#define NFS_NGROUPS 16 struct unx_cred { struct rpc_cred uc_base; uid_t uc_fsuid; gid_t uc_gid, uc_fsgid; - gid_t uc_gids[16]; + gid_t uc_gids[NFS_NGROUPS]; }; #define uc_uid uc_base.cr_uid #define uc_count uc_base.cr_count @@ -84,12 +85,18 @@ unx_create_cred(struct rpc_task *task) cred->uc_gid = cred->uc_fsgid = 0; cred->uc_gids[0] = NOGROUP; } else { + int groups = current->ngroups; + if (groups > NFS_NGROUPS) + groups = NFS_NGROUPS; + cred->uc_uid = current->uid; cred->uc_gid = current->gid; cred->uc_fsuid = current->fsuid; cred->uc_fsgid = current->fsgid; - for (i = 0; i < 16 && i < NGROUPS; i++) + for (i = 0; i < groups; i++) cred->uc_gids[i] = (gid_t) current->groups[i]; + if (i < NFS_NGROUPS) + cred->uc_gids[i] = NOGROUP; } return (struct rpc_cred *) cred; @@ -135,13 +142,18 @@ unx_match(struct rpc_task * task, struct rpc_cred *rcred) int i; if (!RPC_DO_ROOTOVERRIDE(task)) { + int groups; + if (cred->uc_uid != current->uid || cred->uc_gid != current->gid || cred->uc_fsuid != current->fsuid || cred->uc_fsgid != current->fsgid) return 0; - for (i = 0; i < 16 && i < NGROUPS; i++) + groups = current->ngroups; + if (groups > NFS_NGROUPS) + groups = NFS_NGROUPS; + for (i = 0; i < groups ; i++) if (cred->uc_gids[i] != (gid_t) current->groups[i]) return 0; return 1; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index cec276857..47d1104dc 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -866,8 +866,7 @@ if (svsk->sk_sk == NULL) /* Register socket with portmapper */ if (*errp >= 0 && pmap_register) - *errp = svc_register(serv, inet->protocol, - ntohs(inet->dummy_th.source)); + *errp = svc_register(serv, inet->protocol, ntohs(inet->sport)); if (*errp < 0) { inet->user_data = NULL; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b04072d80..624cbb8d8 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -687,17 +687,19 @@ static int unix_stream_connect1(struct socket *sock, struct msghdr *msg, skb=sock_alloc_send_skb(sk, len, 0, nonblock, &err); /* Marker object */ if(skb==NULL) - return err; + goto out; memcpy(&UNIXCB(skb), cmsg, sizeof(*cmsg)); - if (len) - memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + if (len) { + err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, + len); + if (err) + goto out_free; + } + sk->state=TCP_CLOSE; other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err); if(other==NULL) - { - kfree_skb(skb); - return err; - } + goto out_free; other->ack_backlog++; unix_peer(sk)=other; skb_queue_tail(&other->receive_queue,skb); @@ -738,6 +740,11 @@ static int unix_stream_connect1(struct socket *sock, struct msghdr *msg, if (!sk->protinfo.af_unix.addr) unix_autobind(sock); return 0; + +out_free: + kfree_skb(skb); +out: + return err; } @@ -908,8 +915,8 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; - unix_socket *other; struct sockaddr_un *sunaddr=msg->msg_name; + unix_socket *other; int namelen = 0; /* fake GCC */ int err; unsigned hash; @@ -918,7 +925,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, if (msg->msg_flags&MSG_OOB) return -EOPNOTSUPP; - if (msg->msg_flags&~MSG_DONTWAIT) + if (msg->msg_flags&~(MSG_DONTWAIT|MSG_NOSIGNAL)) return -EINVAL; if (msg->msg_namelen) { @@ -935,9 +942,8 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, unix_autobind(sock); skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err); - if (skb==NULL) - return err; + goto out; memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred)); UNIXCB(skb).attr = msg->msg_flags; @@ -945,7 +951,9 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, unix_attach_fds(scm, skb); skb->h.raw = skb->data; - memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + if (err) + goto out_free; other = unix_peer(sk); if (other && other->dead) @@ -957,26 +965,18 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, unix_unlock(other); unix_peer(sk)=NULL; other = NULL; - if (sunaddr == NULL) { - kfree_skb(skb); - return -ECONNRESET; - } + err = -ECONNRESET; + if (sunaddr == NULL) + goto out_free; } if (!other) { other = unix_find_other(sunaddr, namelen, sk->type, hash, &err); - if (other==NULL) - { - kfree_skb(skb); - return err; - } + goto out_free; + err = -EINVAL; if (!unix_may_send(sk, other)) - { - unix_unlock(other); - kfree_skb(skb); - return -EINVAL; - } + goto out_unlock; } skb_queue_tail(&other->receive_queue, skb); @@ -985,6 +985,13 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, if (!unix_peer(sk)) unix_unlock(other); return len; + +out_unlock: + unix_unlock(other); +out_free: + kfree_skb(skb); +out: + return err; } @@ -1005,7 +1012,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, if (msg->msg_flags&MSG_OOB) return -EOPNOTSUPP; - if (msg->msg_flags&~MSG_DONTWAIT) + if (msg->msg_flags&~(MSG_DONTWAIT|MSG_NOSIGNAL)) return -EINVAL; if (msg->msg_namelen) { @@ -1020,7 +1027,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, } if (sk->shutdown&SEND_SHUTDOWN) { - send_sig(SIGPIPE,current,0); + if (!(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE,current,0); return -EPIPE; } @@ -1085,7 +1093,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, kfree_skb(skb); if(sent) goto out; - send_sig(SIGPIPE,current,0); + if (!(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE,current,0); return -EPIPE; } @@ -1265,9 +1274,7 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size } chunk = min(skb->len, size); - /* N.B. This could fail with a non-zero value (which means -EFAULT - * and the non-zero value is the number of bytes not copied). - */ + /* N.B. This could fail with -EFAULT */ memcpy_toiovec(msg->msg_iov, skb->data, chunk); copied += chunk; size -= chunk; |