users should especially try to use the 2.9.1.0.x releases, as they
resolve known issues with glibc2 and binutils-2.8.x releases.
- libbfd, libiberty, and /usr/include/bfd.h, which are part of recent
-binutils packages, are also required to compile ksymoops. Depending
-upon your distribution, this may require you to install both binutils
-and binutils-development packages (Debian puts bfd.h in binutils-dev,
-for example).
-
Gnu C
=====
People with SCSI-only systems should say N here; if unsure say Y.
Generic PCI bus-master DMA support
-CONFIG_BLK_DEV_IDEDMA
+CONFIG_BLK_DEV_IDEDMA_PCI
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. You can then use
It is safe to say Y to this question.
+Good-Bad DMA Model-Firmware (EXPERIMENTAL)
+IDEDMA_NEW_DRIVE_LISTINGS
+ This test compares both the model and firmware revision for buggy drives
+ that claim to (U)DMA capable. This is a blanket on/off test with no speed
+ limit options. Straight GNU GCC 2.7.3/2.8.X compilers are known to be safe;
+ whereas, many versions of EGCS have a problem and miscompile.
+
+ If in doubt, say N.
+
+Generic ATA-66 support (DANGEROUS)
+CONFIG_IDEDMA_ULTRA_66
+ This allows for your Generic IDE control to attempt support for
+ using ATA-66 or UDMA-66 transfer modes 3/4. If you are not sure what you
+ are attempting, "DO NOT" even think about this option, unless your
+ mainboard's chipset is verified. Do not complain to anyone if you
+ do not know what you are doing and are just playing around.
+ This option has no known success cases to date.
+
+ Say N, or beware.........
+
Winbond SL82c105 support
CONFIG_BLK_DEV_SL82C105
If you have a Winbond SL82c105 IDE controller, say Y here to enable
improve the usability of some boot managers such as LILO when
booting from a drive on an off-board controller.
+ Requires that all onboard ide controllers be disabled or calling
+ "pci=reverse" to invert the device scan order.
+
Note that, if you say Y here, the order of the hd* devices will be
rearranged which may require modification of fstab and other files.
If in doubt, say N.
Use DMA by default when available
-CONFIG_IDEDMA_AUTO
+CONFIG_IDEDMA_PCI_AUTO
Prior to kernel version 2.1.112, Linux used to automatically use
DMA for IDE drives and chipsets which support it. Due to concerns
about a couple of cases where buggy hardware may have caused damage,
This driver adds detection and support for the CY82C693 chipset
used on Digital's PC-Alpha 164SX boards.
- This requires CONFIG_IDEDMA_AUTO to be enabled.
+ This requires CONFIG_IDEDMA_PCI_AUTO to be enabled.
Please read the comments at the top of drivers/block/cy82c693.c
(while running a "cat") provided you enabled "proc" support and
set DISPLAY_APOLLO_TIMINGS in via82c586.c
- This requires CONFIG_IDEDMA_AUTO to be enabled.
+ This requires CONFIG_IDEDMA_PCI_AUTO to be enabled.
If unsure, say N.
onboard chipsets. It also tests for Simplex mode and enables
normal dual channel support.
- This requires CONFIG_IDEDMA_AUTO to be enabled.
+ This requires CONFIG_IDEDMA_PCI_AUTO to be enabled.
Please read the comments at the top of drivers/block/alim15x3.c
If unsure, say N.
-PROMISE PDC20246 support (EXPERIMENTAL)
-CONFIG_BLK_DEV_PDC20246
+PROMISE PDC20246/PDC20262 support
+CONFIG_BLK_DEV_PDC202XX
+ Promise Ultra33 or PDC20246.
This driver adds up to 4 more eide devices sharing a single interrupt.
This add-on card is a bootable PCI UDMA controller.
Since multiple cards can be installed and there are BIOS ROM problems
do not match. Should you be unable to make new BIOS chips with a burner,
the driver attempts to dynamic tuning of the chipset at boot-time
for max-speed. Ultra33 BIOS 1.25 or new required for more than one card.
+ This card may require "PDC202XX Special UDMA Feature (EXPERIMENTAL)".
- This requires CONFIG_IDEDMA_AUTO to be enabled.
-
- Please read the comments at the top of drivers/block/pdc202xx.c
-
- If unsure, say N.
-
-PROMISE PDC20262 support (EXPERIMENTAL)
-CONFIG_BLK_DEV_PDC20262
+ Promise Ultra66 or PDC20262.
This driver adds up to 4 more eide devices sharing a single interrupt.
This add-on card is a bootable PCI UDMA ATA-66 controller.
The driver attempts to dynamic tuning of the chipset at boot-time
for max-speed. Note tested limits are UDMA-2.
Ultra66 BIOS 1.11 or newer required.
- This requires CONFIG_IDEDMA_AUTO to be enabled.
+ This requires CONFIG_IDEDMA_PCI_AUTO to be enabled.
+
+ Please read the comments at the top of drivers/block/pdc202xx.c
+
+ If unsure, say N.
+
+Special UDMA Feature (EXPERIMENTAL)
+PDC202XX_FORCE_BURST_BIT
+ For PDC20246 and PDC20262 Ultra DMA chipsets.
+ Designed originally for PDC20246/Ultra33 that has BIOS setup failures
+ when using 3 or more cards.
Please read the comments at the top of drivers/block/pdc202xx.c
If unsure, say N.
+Special Mode Feature (DANGEROUS)
+PDC202XX_FORCE_MASTER_MODE
+ For PDC20246 and PDC20262 Ultra DMA chipsets.
+ This is reserved for possible Hardware RAID 0,1 for the FastTrak Series.
+
+ Say N.
+
AEC6210 chipset support
CONFIG_BLK_DEV_AEC6210
This driver adds up to 4 more eide devices sharing a single interrupt.
This add-on card is a bootable PCI UDMA controller. In order to get this
card to initialize correctly in some cases, you should include this driver.
- This prefers CONFIG_IDEDMA_AUTO to be enabled, regardless.
+ This prefers CONFIG_IDEDMA_PCI_AUTO to be enabled, regardless.
Please read the comments at the top of drivers/block/aec6210.c
-Intel PIIXn chipsets support (EXPERIMENTAL)
+Intel PIIXn chipsets support
CONFIG_BLK_DEV_PIIX
This driver adds PIO mode setting and tuning for all PIIX IDE
controllers by Intel. Since the BIOS can sometimes improperly tune
If unsure, say N.
-HPT343 chipset support (EXPERIMENTAL)
-CONFIG_BLK_DEV_HPT343
+PIIXn Tuning support (EXPERIMENTAL)
+CONFIG_BLK_DEV_PIIX_TUNING
+ This driver extension adds DMA mode setting and tuning for all PIIX IDE
+ controllers by Intel. Since the BIOS can sometimes improperly setup
+ the device/adapter combination and speed limits, It has become a necessity
+ to back/forward speed devices as needed.
+
+ Case 430HX/440FX PIIX3 need speed limits to reduce UDMA to DMA mode 2
+ if the BIOS can to perform this task at INIT.
+
+ If unsure, say N.
+
+HPT34X chipset support
+CONFIG_BLK_DEV_HPT34X
This driver adds up to 4 more EIDE devices sharing a single
- interrupt. The HPT343 chipset in its current form is a non-bootable
- PCI UDMA controller. This driver requires dynamic tuning of the
- chipset during the ide-probe at boot. It is reported to support DVD
- II drives, by the manufacturer.
+ interrupt. The HPT343 chipset in its current form is a non-bootable or
+ HPT345/HPT363 chipset is bootable (needs BIOS FIX) PCI UDMA controllers.
+ This driver requires dynamic tuning of the chipset during the ide-probe
+ at boot. It is reported to support DVD II drives, by the manufacturer.
- This requires CONFIG_IDEDMA_AUTO to be enabled.
+ Please read the comments at the top of drivers/block/hpt343.c
+
+HPT34X DMA support (DANGEROUS)
+CONFIG_BLK_DEV_HPT34X_DMA
+ This requires CONFIG_IDEDMA_PCI_AUTO to be enabled.
Please read the comments at the top of drivers/block/hpt343.c
Say Y if you have an IDE doubler. The driver is enabled at kernel
runtime using the "ide=doubler" kernel boot parameter.
- Support for PowerMac IDE devices (must also enable IDE)
- CONFIG_BLK_DEV_IDE_PMAC
- No help for CONFIG_BLK_DEV_IDE_PMAC
+Support for PowerMac IDE devices (must also enable IDE)
+CONFIG_BLK_DEV_IDE_PMAC
+ No help for CONFIG_BLK_DEV_IDE_PMAC
- PowerMac IDE DMA support
- CONFIG_BLK_DEV_IDEDMA_PMAC
- No help for CONFIG_BLK_DEV_IDEDMA_PMAC
+PowerMac IDE DMA support
+CONFIG_BLK_DEV_IDEDMA_PMAC
+ No help for CONFIG_BLK_DEV_IDEDMA_PMAC
- Use DMA by default
- CONFIG_PMAC_IDEDMA_AUTO
- No help for CONFIG_PMAC_IDEDMA_AUTO
+Use DMA by default
+CONFIG_IDEDMA_PMAC_AUTO
+ No help for CONFIG_IDEDMA_PMAC_AUTO
Macintosh Quadra/Powerbook IDE interface support
CONFIG_BLK_DEV_MAC_IDE
(hard disks, CD-ROM drives, etc.) that are connected to the builtin
IDE interface.
- RapIDE interface support
- CONFIG_BLK_DEV_IDE_RAPIDE
- No help for CONFIG_BLK_DEV_IDE_RAPIDE
+ICS IDE interface support
+CONFIG_BLK_DEV_IDE_ICSIDE
+ No help for CONFIG_BLK_DEV_IDE_ICSIDE
+
+ICS DMA support
+CONFIG_BLK_DEV_IDEDMA_ICS
+ No help for CONFIG_BLK_DEV_IDEDMA_ICS
+
+Use ICS DMA by default
+CONFIG_IDEDMA_ICS_AUTO
+ No help for CONFIG_IDEDMA_ICS_AUTO
+
+RapIDE interface support
+CONFIG_BLK_DEV_IDE_RAPIDE
+ No help for CONFIG_BLK_DEV_IDE_RAPIDE
XT hard disk support
CONFIG_BLK_DEV_XD
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 be used by
- this driver at module load time, read
- Documentation/networking/net-modules.txt.
+ this driver at module load time, take a look at
+ Documentation/networking/parport.txt.
If unsure, say Y.
-PC-style hardware
+PC-style hardware
CONFIG_PARPORT_PC
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
If unsure, say Y.
+Use FIFO/DMA if available
+CONFIG_PARPORT_PC_FIFO
+ Many parallel port chipsets provide hardware that can speed up
+ printing. Say Y here if you want to take advantage of that.
+
+ As well as actually having a FIFO, or DMA capability, the kernel
+ will need to know which IRQ the parallel port has. By default,
+ parallel port interrupts will not be used, and so neither will the
+ FIFO. See Documentation/parport.txt to find out how to specify
+ which IRQ/DMA to use.
+
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
+Sun Ultra/AX-style hardware
CONFIG_PARPORT_AX
Say Y here if you need support for the parallel port hardware on Sun
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.
-Plug and Play support
-CONFIG_PNP
- Plug and Play support allows the kernel to automatically configure
- some peripheral devices. Say Y to enable PnP.
-
-Auto-probe for parallel devices
-CONFIG_PNP_PARPORT
- 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_probe.o). If in doubt, say N.
+IEEE1284 transfer modes
+CONFIG_PARPORT_1284
+ If you have a printer that supports status readback or device ID, or
+ want to use a device that uses enhanced parallel port transfer modes
+ such as EPP and ECP, say Y here to enable advanced IEEE 1284
+ transfer modes. Also say Y if you want device ID information to
+ appear in /proc/parport/*/autoprobe*. It is safe to say N.
Enable loadable module support
CONFIG_MODULES
corresponding drivers into the kernel. If 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.
+ read Documentation/modules.txt and Documentation/parport.txt. The
+ module will be called lp.o.
If you have several parallel ports, you can specify which ports to
use with the "lp" kernel command line option. (Try "man bootparam"
If you have more than 3 printers, you need to increase the LP_NO
variable in lp.c.
-Support IEEE1284 status readback
-CONFIG_PRINTER_READBACK
- If your printer conforms to IEEE 1284, it may be able to provide a
- status indication when you read from it (for example, with `cat
- /dev/lp1'). To use this feature, say Y here.
+Support for console on line printer
+CONFIG_LP_CONSOLE
+ If you want kernel messages to be printed out as they occur, you
+ can have a console on the printer. This option adds support for
+ doing that; to actually get it to happen you need to pass the
+ option "console=lp" to the kernel at boot time.
+
+ Note that kernel messages can get lost if the printer is out of
+ paper (or off, or unplugged, or too busy..), but this behaviour
+ can be changed. See drivers/char/lp.c (do this at your own risk).
+
+ If unsure, say N.
Mouse Support (not serial mice)
CONFIG_MOUSE
SMP on x86/Linux is now an official feature and is not experimental.
Experimental SMP support for other architectures is underway.
-Please view linux/Documentation/smp for more information about enabling SMP.
+Please view linux/Documentation/smp.txt for more information about enabling SMP.
SMP support for Linux with up to 16 processors using the Intel MP
specification.
--- /dev/null
+[Also cloned from vesafb.txt, thanks to Gerd]
+
+What is tgafb?
+===============
+
+This is a driver for DECChip 21030 based graphics framebuffers, a.k.a. TGA
+cards, specifically the following models
+
+ZLxP-E1 (8bpp, 4 MB VRAM)
+ZLxP-E2 (32bpp, 8 MB VRAM)
+ZLxP-E3 (32bpp, 16 MB VRAM, Zbuffer)
+
+This version, tgafb-1.12, is almost a complete rewrite of the code written
+by Geert Uytterhoeven, which was based on the original TGA console code
+written by Jay Estabrook.
+
+Major new features:
+
+ * Support for multiple resolutions, including setting the resolution at
+ boot time, allowing the use of a fixed-frequency monitor.
+ * Complete code rewrite to follow Geert's skeletonfb spec which will allow
+ future implementation of hardware acceleration and other features.
+
+
+Configuration
+=============
+
+You can pass kernel command line options to tgafb with
+`video=tga:option1,option2:value2,option3' (multiple options should be
+separated by comma, values are separated from options by `:').
+Accepted options:
+
+font:X - default font to use. All fonts are supported, including the
+ SUN12x22 font which is very nice at high resolutions.
+mode:X - default video mode. See drivers/video/tgafb.c for a list.
+
+X11
+===
+
+XF68_FBDev should work just fine, but I haven't tested it. Running
+the XF86_TGA server (reasonably recent versions of which support all TGA
+cards) works fine for me.
+
+One minor problem with XF86_TGA is when running tgafb in resolutions higher
+than 640x480, on switching VCs from tgafb to X, the entire screen is not
+re-drawn and must be manually refreshed. This is an X server problem, not a
+tgafb problem.
+
+Enjoy!
+
+Martin Lucina <mato@kotelna.sk>
2) You have to compile BINFMT_MISC either as a module or into
the kernel (CONFIG_BINFMT_MISC) and set it up properly.
If you choose to compile it as a module, you will have
- to insert it manually with modprobe/insmod, as kerneld
+ to insert it manually with modprobe/insmod, as kmod
can not easy be supported with binfmt_misc.
Read the file 'binfmt_misc.txt' in this directory to know
more about the configuration process.
3) Add the following configuration items to binfmt_misc
(you should really have read binfmt_misc.txt now):
support for Java applications:
- ':Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/javawrapper:'
+ ':Java:M::\xca\xfe\xba\xbe::/usr/local/bin/javawrapper:'
support for Java Applets:
- ':Applet:E::html::/usr/local/java/bin/appletviewer:'
+ ':Applet:E::html::/usr/bin/appletviewer:'
or the following, if you want to be more selective:
- ':Applet:M::<!--applet::/usr/local/java/bin/appletviewer:'
+ ':Applet:M::<!--applet::/usr/bin/appletviewer:'
- Of cause you have to fix the path names, if you installed the JDK
- at another place than /usr/local/java.
+ Of cause you have to fix the path names. Given path/file names in this
+ document match the Debian 2.1 system. (i.e. jdk installed in /usr,
+ custom wrappers from this document in /usr/local)
Note, that for the more selective applet support you have to modify
existing html-files to contain <!--applet--> in the first line
For the compiled Java programs you need a wrapper script like the
following (this is because Java is broken in case of the filename
handling), again fix the path names, both in the script and in the
- above given configuration string:
+ above given configuration string.
+
+ You, too, need the little program after the script. Compile like
+ gcc -O2 -o javaclassname javaclassname.c
+ and stick it to /usr/local/bin.
+
+ Both the javawrapper shellscript and the javaclassname program
+ were supplied by Colin J. Watson <cjw44@cam.ac.uk>.
====================== Cut here ===================
#!/bin/bash
-# /usr/local/java/bin/javawrapper - the wrapper for binfmt_misc/java
+# /usr/local/bin/javawrapper - the wrapper for binfmt_misc/java
+
+if [ -z "$1" ]; then
+ exec 1>&2
+ echo Usage: $0 class-file
+ exit 1
+fi
+
CLASS=$1
+FQCLASS=`/usr/local/bin/javaclassname $1`
+FQCLASSN=`echo $FQCLASS | sed -e 's/^.*\.\([^.]*\)$/\1/'`
+FQCLASSP=`echo $FQCLASS | sed -e 's-\.-/-g' -e 's-^[^/]*$--' -e 's-/[^/]*$--'`
-# if classname is a link, we follow it (this could be done easier - how?)
-if [ -L "$1" ] ; then
- CLASS=`ls --color=no -l $1 | tr -s '\t ' ' ' | cut -d ' ' -f 11`
+# for example:
+# CLASS=Test.class
+# FQCLASS=foo.bar.Test
+# FQCLASSN=Test
+# FQCLASSP=foo/bar
+
+unset CLASSBASE
+
+declare -i LINKLEVEL=0
+
+while :; do
+ if [ "`basename $CLASS .class`" == "$FQCLASSN" ]; then
+ # See if this directory works straight off
+ cd -L `dirname $CLASS`
+ CLASSDIR=$PWD
+ cd $OLDPWD
+ if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
+ CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
+ break;
+ fi
+ # Try dereferencing the directory name
+ cd -P `dirname $CLASS`
+ CLASSDIR=$PWD
+ cd $OLDPWD
+ if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
+ CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
+ break;
+ fi
+ # If no other possible filename exists
+ if [ ! -L $CLASS ]; then
+ exec 1>&2
+ echo $0:
+ echo " $CLASS should be in a" \
+ "directory tree called $FQCLASSP"
+ exit 1
+ fi
+ fi
+ if [ ! -L $CLASS ]; then break; fi
+ # Go down one more level of symbolic links
+ let LINKLEVEL+=1
+ if [ $LINKLEVEL -gt 5 ]; then
+ exec 1>&2
+ echo $0:
+ echo " Too many symbolic links encountered"
+ exit 1
+ fi
+ CLASS=`ls --color=no -l $CLASS | sed -e 's/^.* \([^ ]*\)$/\1/'`
+done
+
+if [ -z "$CLASSBASE" ]; then
+ if [ -z "$FQCLASSP" ]; then
+ GOODNAME=$FQCLASSN.class
+ else
+ GOODNAME=$FQCLASSP/$FQCLASSN.class
+ fi
+ exec 1>&2
+ echo $0:
+ echo " $FQCLASS should be in a file called $GOODNAME"
+ exit 1
fi
-CLASSN=`basename $CLASS .class`
-CLASSP=`dirname $CLASS`
-
-FOO=$PATH
-PATH=$CLASSPATH
-if [ -z "`type -p -a $CLASSN.class`" ] ; then
- # class is not in CLASSPATH
- if [ -e "$CLASSP/$CLASSN.class" ] ; then
- # append dir of class to CLASSPATH
- if [ -z "${CLASSPATH}" ] ; then
- export CLASSPATH=$CLASSP
- else
- export CLASSPATH=$CLASSP:$CLASSPATH
- fi
- else
- # uh! now we would have to create a symbolic link - really
- # ugly, i.e. print a message that one has to change the setup
- echo "Hey! This is not a good setup to run $1 !"
- exit 1
- fi
+
+if ! echo $CLASSPATH | grep -q "^\(.*:\)*$CLASSBASE\(:.*\)*"; then
+ # class is not in CLASSPATH, so prepend dir of class to CLASSPATH
+ if [ -z "${CLASSPATH}" ] ; then
+ export CLASSPATH=$CLASSBASE
+ else
+ export CLASSPATH=$CLASSBASE:$CLASSPATH
+ fi
fi
-PATH=$FOO
shift
-/usr/local/java/bin/java $CLASSN "$@"
+/usr/bin/java $FQCLASS "$@"
+====================== Cut here ===================
+
+
+====================== Cut here ===================
+/* javaclassname.c
+ *
+ * Extracts the class name from a Java class file; intended for use in a Java
+ * wrapper of the type supported by the binfmt_misc option in the Linux kernel.
+ *
+ * Copyright (C) 1999 Colin J. Watson <cjw44@cam.ac.uk>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+/* From Sun's Java VM Specification, as tag entries in the constant pool. */
+
+#define CP_UTF8 1
+#define CP_INTEGER 3
+#define CP_FLOAT 4
+#define CP_LONG 5
+#define CP_DOUBLE 6
+#define CP_CLASS 7
+#define CP_STRING 8
+#define CP_FIELDREF 9
+#define CP_METHODREF 10
+#define CP_INTERFACEMETHODREF 11
+#define CP_NAMEANDTYPE 12
+
+/* Define some commonly used error messages */
+
+#define seek_error() error("%s: Cannot seek\n", program)
+#define corrupt_error() error("%s: Class file corrupt\n", program)
+#define eof_error() error("%s: Unexpected end of file\n", program)
+#define utf8_error() error("%s: Only ASCII 1-255 supported\n", program);
+
+char *program;
+
+long *pool;
+
+u_int8_t read_8(FILE *classfile);
+u_int16_t read_16(FILE *classfile);
+void skip_constant(FILE *classfile, u_int16_t *cur);
+void error(const char *format, ...);
+int main(int argc, char **argv);
+
+/* Reads in an unsigned 8-bit integer. */
+u_int8_t read_8(FILE *classfile)
+{
+ int b = fgetc(classfile);
+ if(b == EOF)
+ eof_error();
+ return (u_int8_t)b;
+}
+
+/* Reads in an unsigned 16-bit integer. */
+u_int16_t read_16(FILE *classfile)
+{
+ int b1, b2;
+ b1 = fgetc(classfile);
+ if(b1 == EOF)
+ eof_error();
+ b2 = fgetc(classfile);
+ if(b2 == EOF)
+ eof_error();
+ return (u_int16_t)((b1 << 8) | b2);
+}
+
+/* Reads in a value from the constant pool. */
+void skip_constant(FILE *classfile, u_int16_t *cur)
+{
+ u_int16_t len;
+ int seekerr = 1;
+ pool[*cur] = ftell(classfile);
+ switch(read_8(classfile))
+ {
+ case CP_UTF8:
+ len = read_16(classfile);
+ seekerr = fseek(classfile, len, SEEK_CUR);
+ break;
+ case CP_CLASS:
+ case CP_STRING:
+ seekerr = fseek(classfile, 2, SEEK_CUR);
+ break;
+ case CP_INTEGER:
+ case CP_FLOAT:
+ case CP_FIELDREF:
+ case CP_METHODREF:
+ case CP_INTERFACEMETHODREF:
+ case CP_NAMEANDTYPE:
+ seekerr = fseek(classfile, 4, SEEK_CUR);
+ break;
+ case CP_LONG:
+ case CP_DOUBLE:
+ seekerr = fseek(classfile, 8, SEEK_CUR);
+ ++(*cur);
+ break;
+ default:
+ corrupt_error();
+ }
+ if(seekerr)
+ seek_error();
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *classfile;
+ u_int16_t cp_count, i, this_class, classinfo_ptr;
+ u_int8_t length;
+
+ program = argv[0];
+
+ if(!argv[1])
+ error("%s: Missing input file\n", program);
+ classfile = fopen(argv[1], "rb");
+ if(!classfile)
+ error("%s: Error opening %s\n", program, argv[1]);
+
+ if(fseek(classfile, 8, SEEK_SET)) /* skip magic and version numbers */
+ seek_error();
+ cp_count = read_16(classfile);
+ pool = calloc(cp_count, sizeof(long));
+ if(!pool)
+ error("%s: Out of memory for constant pool\n", program);
+
+ for(i = 1; i < cp_count; ++i)
+ skip_constant(classfile, &i);
+ if(fseek(classfile, 2, SEEK_CUR)) /* skip access flags */
+ seek_error();
+
+ this_class = read_16(classfile);
+ if(this_class < 1 || this_class >= cp_count)
+ corrupt_error();
+ if(!pool[this_class] || pool[this_class] == -1)
+ corrupt_error();
+ if(fseek(classfile, pool[this_class] + 1, SEEK_SET))
+ seek_error();
+
+ classinfo_ptr = read_16(classfile);
+ if(classinfo_ptr < 1 || classinfo_ptr >= cp_count)
+ corrupt_error();
+ if(!pool[classinfo_ptr] || pool[classinfo_ptr] == -1)
+ corrupt_error();
+ if(fseek(classfile, pool[classinfo_ptr] + 1, SEEK_SET))
+ seek_error();
+
+ length = read_16(classfile);
+ for(i = 0; i < length; ++i)
+ {
+ u_int8_t x = read_8(classfile);
+ if((x & 0x80) || !x)
+ {
+ if((x & 0xE0) == 0xC0)
+ {
+ u_int8_t y = read_8(classfile);
+ if((y & 0xC0) == 0x80)
+ {
+ int c = ((x & 0x1f) << 6) + (y & 0x3f);
+ if(c) putchar(c);
+ else utf8_error();
+ }
+ else utf8_error();
+ }
+ else utf8_error();
+ }
+ else if(x == '/') putchar('.');
+ else putchar(x);
+ }
+ putchar('\n');
+ free(pool);
+ fclose(classfile);
+ return 0;
+}
====================== Cut here ===================
originally by Brian A. Lantz, brian@lantz.com
-heavily edited for binfmt_misc by Richard Günther.
+heavily edited for binfmt_misc by Richard Günther
+new scripts by Colin J. Watson <cjw44@cam.ac.uk>.
+
Quick Summary
-------------
-cd /usr/src/linux/scripts/ksymoops
-make ksymoops
-./ksymoops < the_oops.txt
+Install ksymoops from ftp://ftp.ocs.com.au/pub/ksymoops
+Read the ksymoops man page.
+ksymoops < the_oops.txt
and send the output the maintainer of the kernel area that seems to be
-involved with the problem. Don't worry too much about getting the wrong
-person. If you are unsure send it to the person responsible for the code
-relevant to what you were doing. If it occurs repeatably try and describe
-how to recreate it. Thats worth even more than the oops
+involved with the problem, not to the ksymoops maintainer. Don't worry
+too much about getting the wrong person. If you are unsure send it to
+the person responsible for the code relevant to what you were doing.
+If it occurs repeatably try and describe how to recreate it. Thats
+worth even more than the oops
If you are totally stumped as to whom to send the report, send it to
linux-kernel@vger.rutgers.edu. Thanks for your help in making Linux as
same compiler and similar setups.
The other thing to do is disassemble the "Code:" part of the bug report:
-ksymoops will do this too with the correct tools (and new version of
-ksymoops), but if you don't have the tools you can just do a silly
-program:
+ksymoops will do this too with the correct tools, but if you don't have
+the tools you can just do a silly program:
char str[] = "\xXX\xXX\xXX...";
main(){}
to tell the parport code that you want three PC-style ports, one at
0x3bc with no IRQ, one at 0x378 using IRQ 7, and one at 0x278 with an
-auto-detected IRQ. Currently, PC-style (parport_pc) and Sun Ultra/AX
-(parport_ax) hardware is supported; more is in the works.
+auto-detected IRQ. Currently, PC-style (parport_pc), Sun Ultra/AX
+(parport_ax), Amiga, Atari, and MFC3 hardware is supported.
KMod
----
If you use kmod, you will find it useful to edit /etc/conf.modules.
-Here is an example of the lines that need to be added:
+When a driver needs to use the parport subsystem for the first time, a
+module by the name of parport_lowlevel will be automatically loaded
+via kmod. No module actually exists with that name; it's just an
+alias. Here is an example of the lines that need to be added:
- alias parport_lowlevel parport_pc
+ post-install parport modprobe -k parport_pc
options parport_pc io=0x378,0x278 irq=7,auto
KMod will then automatically load parport_pc (with the options
Parport probe [optional]
-------------
-Once the architecture-dependent part of the parport code is loaded
-into the kernel, you can insert the parport_probe module with:
-
- # insmod parport_probe.o
-
-This will perform an IEEE1284 probe of any attached devices and log a
-message similar to:
+In 2.2 kernels there was a module called parport_probe, which was used
+for collecting IEEE 1284 device ID information. This has now been
+enhanced and now lives with the IEEE 1284 support. When a parallel
+port is detected, the devices that are connected to it are analysed,
+and information is logged like this:
parport0: Printer, BJC-210 (Canon)
-(If you are using kmod and have configured parport_probe as a module,
-this will just happen.)
-
-The probe information is available in /proc/parport/?/autoprobe.
+The probe information is available from files in /proc/sys/dev/parport/.
Parport linked into the kernel statically
==============
If you have configured the /proc filesystem into your kernel, you will
-see a new directory entry: /proc/parport. In there will be a
+see a new directory entry: /proc/sys/dev/parport. In there will be a
directory entry for each parallel port for which parport is
-configured. In each of those directories are four files describing
-that parallel port. For example:
-
-File: Contents:
-
-/proc/parport/0/devices A list of the device drivers using
- that port. A "+" will appear by the
- name of the device currently using the
- port (it might not appear against any).
-
-/proc/parport/0/hardware Parallel port's base address, IRQ line
- and DMA channel.
-
-/proc/parport/0/irq The IRQ that parport is using for that
- port. This is in a separate file to
- allow you to alter it by writing a new
- value in (IRQ number or "none").
-
-/proc/parport/0/autoprobe Any IEEE-1284 device ID information
- that has been acquired.
-
+configured. In each of those directories are a collection of files
+describing that parallel port.
+
+The /proc/sys/dev/parport directory tree looks like:
+
+parport
+|-- default
+| |-- spintime
+| `-- timeslice
+|-- parport0
+| |-- autoprobe
+| |-- autoprobe0
+| |-- autoprobe1
+| |-- autoprobe2
+| |-- autoprobe3
+| |-- devices
+| | |-- active
+| | `-- lp
+| | `-- timeslice
+| |-- hardware
+| `-- spintime
+`-- parport1
+ |-- autoprobe
+ |-- autoprobe0
+ |-- autoprobe1
+ |-- autoprobe2
+ |-- autoprobe3
+ |-- devices
+ | |-- active
+ | `-- ppa
+ | `-- timeslice
+ |-- hardware
+ `-- spintime
+
+
+File: Contents:
+
+devices/active A list of the device drivers using that port. A "+"
+ will appear by the name of the device currently using
+ the port (it might not appear against any). The
+ string "none" means that there are no device drivers
+ using that port.
+
+hardware Parallel port's base address, IRQ line and DMA channel.
+
+autoprobe Any IEEE-1284 device ID information that has been
+ acquired from the (non-IEEE 1284.3) device.
+
+autoprobe[0-3] IEEE 1284 device ID information retrieved from
+ daisy-chain devices that conform to IEEE 1284.3.
+
+spintime The number of microseconds to busy-loop while waiting
+ for the peripheral to respond. You might find that
+ adjusting this improves performance, depending on your
+ peripherals. This is a port-wide setting, i.e. it
+ applies to all devices on a particular port.
+
+timeslice The number of jiffies (FIXME: this should be in
+ milliseconds or something) that a device driver is
+ allowed to keep a port claimed for. This is advisory,
+ and driver can ignore it if it must.
+
+default/* The defaults for spintime and timeslice. When a new
+ port is registered, it picks up the default spintime.
+ When a new device is registered, it picks up the
+ default timeslice.
Device drivers
==============
Also:
- * If you selected the IEEE-1284 autoprobe at compile time, you can say
+ * If you selected the IEEE 1284 support at compile time, you can say
`lp=auto' on the kernel command line, and lp will create devices
only for those ports that seem to have printers attached.
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 9
+SUBLEVEL = 10
EXTRAVERSION =
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
clean: archclean
rm -f kernel/ksyms.lst include/linux/compile.h
- rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' \
- ! -regex '.*ksymoops/.*' -print`
+ rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print`
rm -f core `find . -type f -name 'core' -print`
rm -f core `find . -name '.*.flags' -print`
rm -f vmlinux System.map
rm -f .version .config* config.in config.old
rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
- rm -f scripts/ksymoops/*.o scripts/ksymoops/ksymoops
rm -f .menuconfig.log
rm -f include/asm
rm -rf include/config
bugs. It is *strongly* recommended that you back up the previous kernel
before installing any new 2.3.xx release.
-If you need to use a proven and stable Linux kernel, please use 1.2.13,
-2.0.36 or 2.2.xx. All features which will be in the 2.3.xx releases will
-be contained in 2.4.xx when the code base has stabilized again.
+If you need to use a proven and stable Linux kernel, please use 2.0.37
+or 2.2.xx. All features which will be in the 2.3.xx releases will be
+contained in 2.4.xx when the code base has stabilized again.
If you decide to use 2.3, it is recommended that you join the kernel mailing
list. To do this, e-mail majordomo@vger.rutgers.edu, and put in the body
SOFTWARE REQUIREMENTS
- Compiling and running the 2.3.x kernels requires up-to-date
+ Compiling and running the 2.3.xx kernels requires up-to-date
versions of various software packages. Consult
./Documentation/Changes for the minimum version numbers required
and how to get updates for these packages. Beware that using
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
-tristate 'Parallel port support' CONFIG_PARPORT
-if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
- if [ "$CONFIG_PARPORT_PC" != "n" ]; then
- bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
- fi
-fi
+source drivers/misc/Config.in
endmenu
source drivers/pnp/Config.in
update_one_process(current, 1, user, !user, cpu);
if (current->pid) {
- if (--current->counter < 0) {
+ if (--current->counter <= 0) {
current->counter = 0;
current->need_resched = 1;
}
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
-tristate 'Parallel port support' CONFIG_PARPORT
-if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
- if [ "$CONFIG_PARPORT_PC" != "n" ]; then
- bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
- fi
-fi
+source drivers/misc/Config.in
bool 'Advanced Power Management BIOS support' CONFIG_APM
if [ "$CONFIG_APM" = "y" ]; then
# CONFIG_I2O_SCSI is not set
# CONFIG_I2O_PROC is not set
-#
-# Plug and Play support
-#
-# CONFIG_PNP is not set
-
#
# Block devices
#
# CONFIG_BLK_DEV_IDEDMA_PCI is not set
# CONFIG_BLK_DEV_OFFBOARD is not set
# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+CONFIG_BLK_DEV_PIIX=y
# CONFIG_IDE_CHIPSETS is not set
#
*/
static inline void x86_do_profile (unsigned long eip)
{
- if (prof_buffer && current->pid) {
+ if (prof_buffer) {
eip -= (unsigned long) &_stext;
eip >>= prof_shift;
/*
#include <linux/config.h>
#include <linux/unistd.h>
#include <linux/delay.h>
-#include <linux/smp.h>
#include <linux/reboot.h>
#include <linux/init.h>
#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
* and that it is in the task area before calling this: this routine does
* no checking.
*/
-static unsigned long get_long(struct task_struct * tsk,
- struct vm_area_struct * vma, unsigned long addr)
+static unsigned long get_long(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr)
{
pgd_t * pgdir;
pmd_t * pgmiddle;
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir)) {
- handle_mm_fault(tsk, vma, addr, 0);
+ handle_mm_fault(mm, vma, addr, 0);
goto repeat;
}
if (pgd_bad(*pgdir)) {
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
- handle_mm_fault(tsk, vma, addr, 0);
+ handle_mm_fault(mm, vma, addr, 0);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 0);
+ handle_mm_fault(mm, vma, addr, 0);
goto repeat;
}
page = pte_page(*pgtable);
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
-static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
+static void put_long(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long addr,
unsigned long data)
{
pgd_t *pgdir;
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (!pgd_present(*pgdir)) {
- handle_mm_fault(tsk, vma, addr, 1);
+ handle_mm_fault(mm, vma, addr, 1);
goto repeat;
}
if (pgd_bad(*pgdir)) {
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
- handle_mm_fault(tsk, vma, addr, 1);
+ handle_mm_fault(mm, vma, addr, 1);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 1);
+ handle_mm_fault(mm, vma, addr, 1);
goto repeat;
}
page = pte_page(*pgtable);
if (!pte_write(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 1);
+ handle_mm_fault(mm, vma, addr, 1);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
*/
-static int read_long(struct task_struct * tsk, unsigned long addr,
+static int read_long(struct mm_struct * mm, unsigned long addr,
unsigned long * result)
{
- struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+ struct vm_area_struct * vma = find_extend_vma(mm, addr);
if (!vma)
return -EIO;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
- low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
- high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+ low = get_long(mm, vma, addr & ~(sizeof(long)-1));
+ high = get_long(mm, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 1:
low >>= 8;
}
*result = low;
} else
- *result = get_long(tsk, vma, addr);
+ *result = get_long(mm, vma, addr);
return 0;
}
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls put_long() to write a long.
*/
-static int write_long(struct task_struct * tsk, unsigned long addr,
+static int write_long(struct mm_struct * mm, unsigned long addr,
unsigned long data)
{
- struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+ struct vm_area_struct * vma = find_extend_vma(mm, addr);
if (!vma)
return -EIO;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
- low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
- high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+ low = get_long(mm, vma, addr & ~(sizeof(long)-1));
+ high = get_long(mm, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
low = data;
high |= data >> 8;
break;
}
- put_long(tsk, vma, addr & ~(sizeof(long)-1),low);
- put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
+ put_long(mm, vma, addr & ~(sizeof(long)-1),low);
+ put_long(mm, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
- put_long(tsk, vma, addr, data);
+ put_long(mm, vma, addr, data);
return 0;
}
unsigned long tmp;
down(&child->mm->mmap_sem);
- ret = read_long(child, addr, &tmp);
+ ret = read_long(child->mm, addr, &tmp);
up(&child->mm->mmap_sem);
if (ret >= 0)
ret = put_user(tmp,(unsigned long *) data);
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
down(&child->mm->mmap_sem);
- ret = write_long(child,addr,data);
+ ret = write_long(child->mm,addr,data);
up(&child->mm->mmap_sem);
goto out;
update_one_process(p, 1, user, system, cpu);
if (p->pid) {
p->counter -= 1;
- if (p->counter < 0) {
+ if (p->counter <= 0) {
p->counter = 0;
p->need_resched = 1;
}
start &= PAGE_MASK;
for (;;) {
- handle_mm_fault(current,vma, start, 1);
+ handle_mm_fault(current->mm, vma, start, 1);
if (!size)
break;
size--;
* make sure we exit gracefully rather than endlessly redo
* the fault.
*/
- if (!handle_mm_fault(tsk, vma, address, write))
+ if (!handle_mm_fault(mm, vma, address, write))
goto do_sigbus;
/*
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/autoconf.h>
#include <asm/bcache.h>
#include <asm/sgi.h>
define_bool CONFIG_KERNEL_ELF y
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
-tristate 'Parallel port support' CONFIG_PARPORT
-if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
- if [ "$CONFIG_PARPORT_PC" != "n" ]; then
- bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
- fi
-fi
+source drivers/misc/Config.in
bool 'Support for VGA Console' CONFIG_VGA_CONSOLE
bool 'Support for frame buffer devices' CONFIG_FB
#include <linux/malloc.h>
#include <linux/user.h>
#include <linux/elf.h>
-#include <linux/elf.h>
#include <linux/init.h>
#include <asm/pgtable.h>
update_one_process(p, 1, user, system, cpu);
p->counter -= 1;
- if (p->counter < 0) {
+ if (p->counter <= 0) {
p->counter = 0;
current->need_resched = 1;
}
if(current->pid) {
update_one_process(current, 1, user, !user, cpu);
- if(--current->counter < 0) {
+ if(--current->counter <= 0) {
current->counter = 0;
current->need_resched = 1;
}
if(current->pid) {
update_one_process(current, 1, user, !user, cpu);
- if(--current->counter < 0) {
+ if(--current->counter <= 0) {
current->counter = 0;
current->need_resched = 1;
}
fi
if [ "$CONFIG_PCI" = "y" ]; then
- tristate 'Parallel port support' CONFIG_PARPORT
- if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' Ultra/AX-style hardware' CONFIG_PARPORT_AX $CONFIG_PARPORT
- if [ "$CONFIG_PARPORT_AX" = "m" ]; then
- define_bool CONFIG_PARPORT_LOWLEVEL_MODULE y
- fi
- if [ "$CONFIG_PARPORT_AX" != "n" ]; then
- bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
- fi
- dep_tristate ' Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
- if [ "$CONFIG_PRINTER" != "n" ]; then
- bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
- fi
- fi
+ source drivers/misc/Config.in
+ dep_tristate ' Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
tristate 'SUNW,envctrl support' CONFIG_ENVCTRL
fi
endmenu
fd = open_dentry(bprm->dentry, O_RDONLY);
if (fd < 0)
return fd;
- file = fcheck(fd);
+ file = fget(fd);
if (!file->f_op || !file->f_op->mmap) {
+ fput(fd);
sys_close(fd);
do_brk(0, ex.a_text+ex.a_data);
read_exec(bprm->dentry, fd_offset,
fd_offset);
if (error != N_TXTADDR(ex)) {
+ fput(file);
sys_close(fd);
send_sig(SIGKILL, current, 0);
return error;
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
fd_offset + ex.a_text);
+ fput(file);
sys_close(fd);
if (error != N_DATADDR(ex)) {
send_sig(SIGKILL, current, 0);
unsigned int *inc, *inc2;
update_one_process(current, 1, user, !user, cpu);
- if(--current->counter < 0) {
+ if(--current->counter <= 0) {
current->counter = 0;
current->need_resched = 1;
}
bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' Good-Bad DMA Model-Firmware (EXPERIMENTAL)' IDEDMA_NEW_DRIVE_LISTINGS
+ bool ' Generic ATA-66 support (DANGEROUS)' CONFIG_IDEDMA_ULTRA_66
+ define_bool IDEDMA_PCI_EXPERIMENTAL y
+ else
+ define_bool IDEDMA_PCI_EXPERIMENTAL n
+ fi
fi
bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
+ if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
+ bool ' ALI M15x3 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_ALI15X3
+ bool ' CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646
+ bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693
+ fi
+ bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X
+ if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" -a \
+ "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then
+ bool ' HPT34X DMA support (DANGEROUS)' CONFIG_BLK_DEV_HPT34X_DMA
+ fi
+ bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX
+ if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" -a \
+ "$CONFIG_BLK_DEV_PIIX" = "y" ]; then
+ bool ' PIIXn Tuning support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX_TUNING
+ fi
+ if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
+ bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
+ fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
- bool ' Intel PIIXn chipsets support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
- bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
- bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
- bool ' CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646
- bool ' ALI M15x3 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_ALI15X3
- bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693
- bool ' PDC20246 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC20246
- bool ' PDC20262 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC20262
- if [ "$CONFIG_BLK_DEV_PDC20246" = "y" -o \
- "$CONFIG_BLK_DEV_PDC20262" = "y" ]; then
- define_bool CONFIG_BLK_DEV_PDC202XX y
- else
- define_bool CONFIG_BLK_DEV_PDC202XX n
- fi
- bool ' HPT343 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_HPT343
+ fi
+ if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
+ bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX
+ if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
+ "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then
+ bool ' Special UDMA Feature (EXPERIMENTAL)' PDC202XX_FORCE_BURST_BIT
+ bool ' Special Mode Feature (DANGEROUS)' PDC202XX_FORCE_MASTER_MODE
fi
fi
+ if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
+ bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
+ bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
+ fi
fi
if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a \
+ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+ fi
bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" ]; then
- bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
- fi
- fi
fi
if [ "$CONFIG_AMIGA" = "y" ]; then
bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE
source drivers/block/paride/Config.in
fi
-
-if [ "$CONFIG_BLK_DEV_CMD640" = "y" -o \
- "$CONFIG_IDE_CHIPSETS" = "y" -o \
- "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
- "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
+ "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD640" = "y" -o \
"$CONFIG_BLK_DEV_CY82C693" = "y" -o \
- "$CONFIG_BLK_DEV_HPT343" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
+ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+ "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
"$CONFIG_BLK_DEV_PIIX" = "y" -o \
"$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
define_bool CONFIG_BLK_DEV_IDE_MODES y
IDE_OBJS += aec6210.o
endif
-ifeq ($(CONFIG_BLK_DEV_HPT343),y)
-IDE_OBJS += hpt343.o
+ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
+IDE_OBJS += hpt34x.o
endif
### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
/*
- * linux/drivers/block/alim15x3.c Version 0.04 Feb. 8, 1999
+ * linux/drivers/block/alim15x3.c Version 0.05 Jun. 29, 1999
*
* Copyright (C) 1998-99 Michel Aubry, Maintainer
* Copyright (C) 1998-99 Andrzej Krzysztofowicz, Maintainer
#include <asm/io.h>
+#include "ide_modes.h"
+
#define DISPLAY_ALI_TIMINGS
#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
};
#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+static void ali15x3_tune_drive (ide_drive_t *drive, byte pio)
+{
+ ide_pio_data_t d;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ int s_time, a_time, c_time;
+ byte s_clc, a_clc, r_clc;
+ unsigned long flags;
+ int bus_speed = ide_system_bus_speed();
+ int port = hwif->index ? 0x5c : 0x58;
+
+ pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+ s_time = ide_pio_timings[pio].setup_time;
+ a_time = ide_pio_timings[pio].active_time;
+ if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
+ s_clc = 0;
+ if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
+ a_clc = 0;
+ c_time = ide_pio_timings[pio].cycle_time;
+
+#if 0
+ if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
+ r_clc = 0;
+#endif
+
+ if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
+ r_clc = 1;
+ } else {
+ if (r_clc >= 16)
+ r_clc = 0;
+ }
+ save_flags(flags);
+ cli();
+ pci_write_config_byte(dev, port, s_clc);
+ pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
+ restore_flags(flags);
+
+ /*
+ * setup active rec
+ * { 70, 165, 365 }, PIO Mode 0
+ * { 50, 125, 208 }, PIO Mode 1
+ * { 30, 100, 110 }, PIO Mode 2
+ * { 30, 80, 70 }, PIO Mode 3 with IORDY
+ * { 25, 70, 25 }, PIO Mode 4 with IORDY ns
+ * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard)
+ */
+
+}
+
__initfunc(unsigned int pci_init_ali15x3 (struct pci_dev *dev, const char *name))
{
byte confreg0 = 0, confreg1 =0, progif = 0;
__initfunc(void ide_init_ali15x3 (ide_hwif_t *hwif))
{
struct pci_dev *dev;
- byte ideic, inmir;
+ byte ideic, inmir, iderev;
byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6,
1, 11, 0, 12, 0, 14, 0, 15 };
+
+ pci_read_config_byte(hwif->pci_dev, PCI_REVISION_ID, &iderev);
+
hwif->irq = hwif->channel ? 15 : 14;
for (dev = pci_devices; dev; dev=dev->next) /* look for ISA bridge */
if (dev->vendor==PCI_VENDOR_ID_AL &&
- dev->device==PCI_DEVICE_ID_AL_M1533)
+ dev->device==PCI_DEVICE_ID_AL_M1533)
break;
if (dev) {
pci_read_config_byte(dev, 0x58, &ideic);
ideic = ideic & 0x03;
if ((hwif->channel && ideic == 0x03) ||
- (!hwif->channel && !ideic)) {
+ (!hwif->channel && !ideic)) {
pci_read_config_byte(dev, 0x44, &inmir);
inmir = inmir & 0x0f;
hwif->irq = irq_routing_table[inmir];
ali_display_info = &ali_get_info;
#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
- if (hwif->dma_base)
+ hwif->tuneproc = &ali15x3_tune_drive;
+ if ((hwif->dma_base) && (iderev >= 0xC1)) {
+ /* M1543C or newer for DMAing */
hwif->dmaproc = &ali15x3_dmaproc;
+ } else {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
return;
}
static void cmd646_do_setfeature(ide_drive_t *drive, byte command)
{
+#if 0
+ (void) ide_config_drive_speed(drive, command);
+#else
unsigned long flags;
byte old_select;
out:
OUT_BYTE(old_select, IDE_SELECT_REG);
restore_flags(flags);
+#endif
}
static void cmd646_dma2_enable(ide_drive_t *drive, unsigned long dma_base)
+++ /dev/null
-/*
- * linux/drivers/block/hpt343.c Version 0.23 May 12, 1999
- *
- * Copyright (C) 1998-99 Andre Hedrick
- * (hedrick@astro.dyer.vanderbilt.edu)
- *
- * 00:12.0 Unknown mass storage controller:
- * Triones Technologies, Inc.
- * Unknown device 0003 (rev 01)
- *
- * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
- * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
- * hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010)
- * hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030)
- * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070)
- * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0)
- *
- * drive_number
- * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#ifndef SPLIT_BYTE
-#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
-#endif
-
-#define HPT343_DEBUG_DRIVE_INFO 0
-#define HPT343_DISABLE_ALL_DMAING 0
-#define HPT343_DMA_DISK_ONLY 0
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-static void hpt343_clear_chipset (ide_drive_t *drive)
-{
- int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- unsigned int reg1 = 0, tmp1 = 0;
- unsigned int reg2 = 0, tmp2 = 0;
-
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1);
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2);
- tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
- tmp2 = ((0x00 << drive_number) | reg2);
- pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
- pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
-}
-
-static int hpt343_tune_chipset (ide_drive_t *drive, byte speed)
-{
- int err;
- byte hi_speed, lo_speed;
- int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- unsigned int reg1 = 0, tmp1 = 0;
- unsigned int reg2 = 0, tmp2 = 0;
-
- SPLIT_BYTE(speed, hi_speed, lo_speed);
-
- if (hi_speed & 7) {
- hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
- } else {
- lo_speed <<= 5;
- lo_speed >>= 5;
- }
-
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1);
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2);
- tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
- tmp2 = ((hi_speed << drive_number) | reg2);
- err = ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
- pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
- pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
-
-#if HPT343_DEBUG_DRIVE_INFO
- printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
- " (0x%02x 0x%02x) 0x%04x\n",
- drive->name, ide_xfer_verbose(speed),
- drive_number, reg1, tmp1, reg2, tmp2,
- hi_speed, lo_speed, err);
-#endif /* HPT343_DEBUG_DRIVE_INFO */
-
- return(err);
-}
-
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS. Initally for designed for
- * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
- */
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
- struct hd_driveid *id = drive->id;
- byte speed = 0x00;
-
-#if HPT343_DISABLE_ALL_DMAING
- return ((int) ide_dma_off);
-#elif HPT343_DMA_DISK_ONLY
- if (drive->media != ide_disk)
- return ((int) ide_dma_off_quietly);
-#endif /* HPT343_DISABLE_ALL_DMAING */
-
- if (id->dma_ultra & 0x0004) {
- if (!((id->dma_ultra >> 8) & 4)) {
- drive->id->dma_ultra &= ~0x0F00;
- drive->id->dma_ultra |= 0x0404;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_UDMA_2;
- } else if (id->dma_ultra & 0x0002) {
- if (!((id->dma_ultra >> 8) & 2)) {
- drive->id->dma_ultra &= ~0x0F00;
- drive->id->dma_ultra |= 0x0202;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_UDMA_1;
- } else if (id->dma_ultra & 0x0001) {
- if (!((id->dma_ultra >> 8) & 1)) {
- drive->id->dma_ultra &= ~0x0F00;
- drive->id->dma_ultra |= 0x0101;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_UDMA_0;
- } else if (id->dma_mword & 0x0004) {
- if (!((id->dma_mword >> 8) & 4)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0404;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_MW_DMA_2;
- } else if (id->dma_mword & 0x0002) {
- if (!((id->dma_mword >> 8) & 2)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0202;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_MW_DMA_1;
- } else if (id->dma_mword & 0x0001) {
- if (!((id->dma_mword >> 8) & 1)) {
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_mword |= 0x0101;
- drive->id->dma_1word &= ~0x0F00;
- }
- speed = XFER_MW_DMA_0;
- } else if (id->dma_1word & 0x0004) {
- if (!((id->dma_1word >> 8) & 4)) {
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0404;
- drive->id->dma_mword &= ~0x0F00;
- }
- speed = XFER_SW_DMA_2;
- } else if (id->dma_1word & 0x0002) {
- if (!((id->dma_1word >> 8) & 2)) {
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0202;
- drive->id->dma_mword &= ~0x0F00;
- }
- speed = XFER_SW_DMA_1;
- } else if (id->dma_1word & 0x0001) {
- if (!((id->dma_1word >> 8) & 1)) {
- drive->id->dma_1word &= ~0x0F00;
- drive->id->dma_1word |= 0x0101;
- drive->id->dma_mword &= ~0x0F00;
- }
- speed = XFER_SW_DMA_0;
- } else {
- return ((int) ide_dma_off_quietly);
- }
-
- (void) hpt343_tune_chipset(drive, speed);
-
- return ((int) ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
- ((id->dma_mword >> 8) & 7) ? ide_dma_on :
- ((id->dma_1word >> 8) & 7) ? ide_dma_on :
- ide_dma_off_quietly);
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
- unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
- unsigned short xfer_pio = drive->id->eide_pio_modes;
-
- byte timing, speed, pio;
-
- pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
- if (xfer_pio> 4)
- xfer_pio = 0;
-
- if (drive->id->eide_pio_iordy > 0) {
- for (xfer_pio = 5;
- xfer_pio>0 &&
- drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
- xfer_pio--);
- } else {
- xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
- (drive->id->eide_pio_modes & 2) ? 0x04 :
- (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
- }
-
- timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
- switch(timing) {
- case 4: speed = XFER_PIO_4;break;
- case 3: speed = XFER_PIO_3;break;
- case 2: speed = XFER_PIO_2;break;
- case 1: speed = XFER_PIO_1;break;
- default:
- speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
- break;
- }
-
- (void) hpt343_tune_chipset(drive, speed);
-}
-
-#if 0
-static void hpt343_tune_drive (ide_drive_t *drive, byte pio)
-{
-}
-#endif
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
- struct hd_driveid *id = drive->id;
- ide_dma_action_t dma_func = ide_dma_on;
-
- if (id && (id->capability & 1) && HWIF(drive)->autodma) {
- /* Consult the list of known "bad" drives */
- if (ide_dmaproc(ide_dma_bad_drive, drive)) {
- dma_func = ide_dma_off;
- goto fast_ata_pio;
- }
- dma_func = ide_dma_off_quietly;
- if (id->field_valid & 4) {
- if (id->dma_ultra & 0x0007) {
- /* Force if Capable UltraDMA */
- dma_func = config_chipset_for_dma(drive);
- if ((id->field_valid & 2) &&
- (dma_func != ide_dma_on))
- goto try_dma_modes;
- }
- } else if (id->field_valid & 2) {
-try_dma_modes:
- if ((id->dma_mword & 0x0007) ||
- (id->dma_1word & 0x0007)) {
- /* Force if Capable regular DMA modes */
- dma_func = config_chipset_for_dma(drive);
- if (dma_func != ide_dma_on)
- goto no_dma_set;
- }
- } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
- if (id->eide_dma_time > 150) {
- goto no_dma_set;
- }
- /* Consult the list of known "good" drives */
- dma_func = config_chipset_for_dma(drive);
- if (dma_func != ide_dma_on)
- goto no_dma_set;
- } else {
- goto fast_ata_pio;
- }
- } else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
- dma_func = ide_dma_off_quietly;
-no_dma_set:
-
- config_chipset_for_pio(drive);
- }
- return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * hpt343_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- *
- * This is specific to the HPT343 UDMA bios-less chipset
- * and HPT345 UDMA bios chipset (stamped HPT363)
- * by HighPoint|Triones Technologies, Inc.
- */
-
-int hpt343_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
- switch (func) {
- case ide_dma_check:
- hpt343_clear_chipset(drive);
- return config_drive_xfer_rate(drive);
-#if 0
- case ide_dma_off:
- case ide_dma_off_quietly:
- case ide_dma_on:
- case ide_dma_check:
- return config_drive_xfer_rate(drive);
- case ide_dma_read:
- case ide_dma_write:
- case ide_dma_begin:
- case ide_dma_end:
- case ide_dma_test_irq:
-#endif
- default:
- break;
- }
- return ide_dmaproc(func, drive); /* use standard DMA stuff */
-}
-
-/*
- * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
- */
-#define HPT343_PCI_INIT_REG 0x80
-
-__initfunc(unsigned int pci_init_hpt343 (struct pci_dev *dev, const char *name))
-{
- int i;
- unsigned short cmd;
- unsigned long hpt343IoBase = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
-#if 0
- unsigned char misc10 = inb(hpt343IoBase + 0x0010);
- unsigned char misc11 = inb(hpt343IoBase + 0x0011);
-#endif
-
- pci_write_config_byte(dev, HPT343_PCI_INIT_REG, 0x00);
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
-
- dev->base_address[0] = (hpt343IoBase + 0x20);
- dev->base_address[1] = (hpt343IoBase + 0x34);
- dev->base_address[2] = (hpt343IoBase + 0x28);
- dev->base_address[3] = (hpt343IoBase + 0x3c);
-
- for(i=0; i<4; i++)
- dev->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO;
-
- /*
- * Since 20-23 can be assigned and are R/W, we correct them.
- */
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->base_address[0]);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->base_address[1]);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->base_address[2]);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->base_address[3]);
- pci_write_config_word(dev, PCI_COMMAND, cmd);
-
-#if 0
- outb(misc10|0x78, (hpt343IoBase + 0x0010));
- outb(misc11, (hpt343IoBase + 0x0011));
-#endif
-
-#ifdef DEBUG
- printk("%s: 0x%02x 0x%02x\n",
- (pcicmd & PCI_COMMAND_MEMORY) ? "HPT345" : name,
- inb(hpt343IoBase + 0x0010),
- inb(hpt343IoBase + 0x0011));
-#endif
-
- if (cmd & PCI_COMMAND_MEMORY) {
- if (dev->rom_address) {
- pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
- printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->rom_address);
- }
- } else {
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
- }
- return dev->irq;
-}
-
-__initfunc(void ide_init_hpt343 (ide_hwif_t *hwif))
-{
- if (hwif->dma_base) {
- unsigned short pcicmd = 0;
-
- pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
- hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
- hwif->dmaproc = &hpt343_dmaproc;
- }
-}
--- /dev/null
+/*
+ * linux/drivers/block/hpt343.c Version 0.23 May 12, 1999
+ *
+ * Copyright (C) 1998-99 Andre Hedrick
+ * (hedrick@astro.dyer.vanderbilt.edu)
+ *
+ * 00:12.0 Unknown mass storage controller:
+ * Triones Technologies, Inc.
+ * Unknown device 0003 (rev 01)
+ *
+ * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070)
+ * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0)
+ *
+ * drive_number
+ * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define HPT343_DEBUG_DRIVE_INFO 0
+#define HPT343_DISABLE_ALL_DMAING 0
+#define HPT343_DMA_DISK_ONLY 0
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+static void hpt34x_clear_chipset (ide_drive_t *drive)
+{
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2);
+ tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = ((0x00 << drive_number) | reg2);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+}
+
+static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ int err;
+ byte hi_speed, lo_speed;
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ SPLIT_BYTE(speed, hi_speed, lo_speed);
+
+ if (hi_speed & 7) {
+ hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
+ } else {
+ lo_speed <<= 5;
+ lo_speed >>= 5;
+ }
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2);
+ tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = ((hi_speed << drive_number) | reg2);
+ err = ide_config_drive_speed(drive, speed);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+
+#if HPT343_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
+ " (0x%02x 0x%02x) 0x%04x\n",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, reg1, tmp1, reg2, tmp2,
+ hi_speed, lo_speed, err);
+#endif /* HPT343_DEBUG_DRIVE_INFO */
+
+ return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS. Initally for designed for
+ * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+
+#if HPT343_DISABLE_ALL_DMAING
+ return ((int) ide_dma_off);
+#elif HPT343_DMA_DISK_ONLY
+ if (drive->media != ide_disk)
+ return ((int) ide_dma_off_quietly);
+#endif /* HPT343_DISABLE_ALL_DMAING */
+
+ if (id->dma_ultra & 0x0004) {
+ if (!((id->dma_ultra >> 8) & 4)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0404;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ if (!((id->dma_ultra >> 8) & 2)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0202;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ if (!((id->dma_ultra >> 8) & 1)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0101;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ if (!((id->dma_mword >> 8) & 4)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0404;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ if (!((id->dma_mword >> 8) & 2)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0202;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ if (!((id->dma_mword >> 8) & 1)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0101;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ if (!((id->dma_1word >> 8) & 4)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0404;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ if (!((id->dma_1word >> 8) & 2)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0202;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ if (!((id->dma_1word >> 8) & 1)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0101;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) hpt34x_tune_chipset(drive, speed);
+
+ return ((int) ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+ break;
+ }
+ (void) hpt34x_tune_chipset(drive, speed);
+}
+
+static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+
+ hpt34x_clear_chipset(drive);
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ (void) hpt34x_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x0007) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT343 UDMA bios-less chipset
+ * and HPT345 UDMA bios chipset (stamped HPT363)
+ * by HighPoint|Triones Technologies, Inc.
+ */
+
+int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ hpt34x_clear_chipset(drive);
+ return config_drive_xfer_rate(drive);
+#if 0
+ case ide_dma_off:
+ case ide_dma_off_quietly:
+ case ide_dma_on:
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ case ide_dma_read:
+ case ide_dma_write:
+ case ide_dma_begin:
+ case ide_dma_end:
+ case ide_dma_test_irq:
+#endif
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+/*
+ * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
+ */
+#define HPT34X_PCI_INIT_REG 0x80
+
+__initfunc(unsigned int pci_init_hpt34x (struct pci_dev *dev, const char *name))
+{
+ int i;
+ unsigned short cmd;
+ unsigned long hpt34xIoBase = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
+#if 0
+ unsigned char misc10 = inb(hpt34xIoBase + 0x0010);
+ unsigned char misc11 = inb(hpt34xIoBase + 0x0011);
+#endif
+
+ pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+
+ dev->base_address[0] = (hpt34xIoBase + 0x20);
+ dev->base_address[1] = (hpt34xIoBase + 0x34);
+ dev->base_address[2] = (hpt34xIoBase + 0x28);
+ dev->base_address[3] = (hpt34xIoBase + 0x3c);
+
+ for(i=0; i<4; i++)
+ dev->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO;
+
+ /*
+ * Since 20-23 can be assigned and are R/W, we correct them.
+ */
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->base_address[0]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->base_address[1]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->base_address[2]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->base_address[3]);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+#if 0
+ outb(misc10|0x78, (hpt34xIoBase + 0x0010));
+ outb(misc11, (hpt34xIoBase + 0x0011));
+#endif
+
+#ifdef DEBUG
+ printk("%s: 0x%02x 0x%02x\n",
+ (pcicmd & PCI_COMMAND_MEMORY) ? "HPT345" : name,
+ inb(hpt34xIoBase + 0x0010),
+ inb(hpt34xIoBase + 0x0011));
+#endif
+
+ if (cmd & PCI_COMMAND_MEMORY) {
+ if (dev->rom_address) {
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
+ printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->rom_address);
+ }
+ } else {
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
+ }
+ return dev->irq;
+}
+
+__initfunc(void ide_init_hpt34x (ide_hwif_t *hwif))
+{
+ hwif->tuneproc = &hpt34x_tune_drive;
+ if (hwif->dma_base) {
+ unsigned short pcicmd = 0;
+
+ pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
+#ifdef CONFIG_BLK_DEV_HPT34X_DMA
+ hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
+#endif /* CONFIG_BLK_DEV_HPT34X_DMA */
+ hwif->dmaproc = &hpt34x_dmaproc;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
drive->drive_data = 250;
}
+#if 1
+ err = ide_config_drive_speed(drive, (byte) speed);
+#else
/*
* Don't use ide_wait_cmd here - it will
* attempt to set_geometry and recalibrate,
err = ide_wait_stat(drive, DRIVE_READY,
BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD);
+#endif
if (err == 0) {
drive->id->dma_mword &= 0x00ff;
#include <asm/io.h>
#include <asm/irq.h>
-#define IDE_DMA_NEW_LISTINGS 0
+#ifdef IDEDMA_NEW_DRIVE_LISTINGS
-#if IDE_DMA_NEW_LISTINGS
struct drive_list_entry {
char * id_model;
char * id_firmware;
return 1;
return 0;
}
-#else /* !IDE_DMA_NEW_LISTINGS */
+
+#else /* !IDEDMA_NEW_DRIVE_LISTINGS */
/*
* good_dma_drives() lists the model names (from "hdparm -i")
"WDC AC31600H",
NULL};
-#endif /* IDE_DMA_NEW_LISTINGS */
+#endif /* IDEDMA_NEW_DRIVE_LISTINGS */
/*
* Our Physical Region Descriptor (PRD) table should be large enough
{
struct hd_driveid *id = drive->id;
-#if IDE_DMA_NEW_LISTINGS
-
+#ifdef IDEDMA_NEW_DRIVE_LISTINGS
if (good_bad) {
return in_drive_list(id, drive_whitelist);
} else {
printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
return(blacklist);
}
-#else /* !IDE_DMA_NEW_LISTINGS */
-
+#else /* !IDEDMA_NEW_DRIVE_LISTINGS */
const char **list;
if (good_bad) {
}
}
}
-#endif /* IDE_DMA_NEW_LISTINGS */
+#endif /* IDEDMA_NEW_DRIVE_LISTINGS */
return 0;
}
/* Consult the list of known "bad" drives */
if (ide_dmaproc(ide_dma_bad_drive, drive))
return hwif->dmaproc(ide_dma_off, drive);
-#if 0
+#ifdef CONFIG_IDEDMA_ULTRA_66
/* Enable DMA on any drive that has UltraDMA (mode 3/4) enabled */
if ((id->field_valid & 4) && (id->word93 & 0x2000))
if ((id->dma_ultra & (id->dma_ultra >> 11) & 3))
return hwif->dmaproc(ide_dma_on, drive);
-#endif
+#endif /* CONFIG_IDEDMA_ULTRA_66 */
/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
if (id->field_valid & 4) /* UltraDMA */
if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
}
}
if (dma_base) {
- if (extra) /* PDC20246 & HPT343 */
+ if (extra) /* PDC20246, PDC20262, & HPT343 */
request_region(dma_base+16, extra, name);
dma_base += hwif->channel ? 8 : 0;
hwif->dma_extra = extra;
* Lets attempt to use the same Ali tricks
* to fix CMD643.....
*/
+#ifdef CONFIG_BLK_DEV_ALI15X3
case PCI_DEVICE_ID_AL_M5219:
case PCI_DEVICE_ID_AL_M5229:
- outb(inb(dma_base+2) & 0x60, dma_base+2);
/*
* Ali 15x3 chipsets know as ALI IV and V report
* this as simplex, skip this test for them.
*/
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+ outb(inb(dma_base+2) & 0x60, dma_base+2);
if (inb(dma_base+2) & 0x80) {
printk("%s: simplex device: DMA forced\n", name);
}
#define DEVID_W82C105 ((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
#define DEVID_UM8886A ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A})
#define DEVID_UM8886BF ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF})
-#define DEVID_HPT343 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343})
+#define DEVID_HPT34X ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343})
#define DEVID_ALI15X3 ((ide_pci_devid_t){PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229})
#define DEVID_CY82C693 ((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693})
#define DEVID_HINT ((ide_pci_devid_t){0x3388, 0x8013})
#define PCI_AEC6210 NULL
#endif
-#ifdef CONFIG_BLK_DEV_HPT343
-extern unsigned int pci_init_hpt343(struct pci_dev *, const char *);
-extern void ide_init_hpt343(ide_hwif_t *);
-#define PCI_HPT343 &pci_init_hpt343
-#define INIT_HPT343 &ide_init_hpt343
+#ifdef CONFIG_BLK_DEV_HPT34X
+extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *);
+extern void ide_init_hpt34x(ide_hwif_t *);
+#define PCI_HPT34X &pci_init_hpt34x
+#define INIT_HPT34X &ide_init_hpt34x
#else
-#define PCI_HPT343 NULL
-#define INIT_HPT343 NULL
+#define PCI_HPT34X NULL
+#define INIT_HPT34X NULL
#endif
#define INIT_SAMURAI NULL
{DEVID_W82C105, "W82C105", NULL, INIT_W82C105, NULL, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0 },
{DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
{DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
- {DEVID_HPT343, "HPT343", PCI_HPT343, INIT_HPT343, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 },
+ {DEVID_HPT34X, "HPT34X", PCI_HPT34X, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 },
{DEVID_ALI15X3, "ALI15X3", PCI_ALI15X3, INIT_ALI15X3, NULL, {{0x09,0x20,0x20}, {0x09,0x10,0x10}}, ON_BOARD, 0 },
{DEVID_CY82C693,"CY82C693", NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
{DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
{
int i;
unsigned short pcicmd = 0;
- unsigned long hpt343IoBase = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
+ unsigned long hpt34xIoBase = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
pci_write_config_byte(dev, 0x80, 0x00);
- dev->base_address[0] = (hpt343IoBase + 0x20);
- dev->base_address[1] = (hpt343IoBase + 0x34);
- dev->base_address[2] = (hpt343IoBase + 0x28);
- dev->base_address[3] = (hpt343IoBase + 0x3c);
+ dev->base_address[0] = (hpt34xIoBase + 0x20);
+ dev->base_address[1] = (hpt34xIoBase + 0x34);
+ dev->base_address[2] = (hpt34xIoBase + 0x28);
+ dev->base_address[3] = (hpt34xIoBase + 0x3c);
for(i=0; i<4; i++)
dev->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO;
printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
#endif
}
- if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT343)) {
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) {
/*
* Since there are two cards that report almost identically,
* the only discernable difference is the values
if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) ||
IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
-#ifdef CONFIG_BLK_DEV_HPT343
- IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT343) ||
-#endif
+#ifdef CONFIG_BLK_DEV_HPT34X
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) ||
+#endif /* CONFIG_BLK_DEV_HPT34X */
IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) ||
((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
while (0 < (signed long)(timeout - jiffies));
}
+int ide_config_drive_speed (ide_drive_t *drive, byte speed)
+{
+ int err;
+
+ /*
+ * Don't use ide_wait_cmd here - it will
+ * attempt to set_geometry and recalibrate,
+ * but for some reason these don't work at
+ * this point (lost interrupt).
+ */
+ SELECT_DRIVE(HWIF(drive), drive);
+ OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+ OUT_BYTE(speed, IDE_NSECTOR_REG);
+ OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+ OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+
+ err = ide_wait_stat(drive, DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD);
+
+ return(err);
+}
+
static int ide_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
EXPORT_SYMBOL(ide_cmd);
EXPORT_SYMBOL(ide_wait_cmd);
EXPORT_SYMBOL(ide_delay_50ms);
+EXPORT_SYMBOL(ide_config_drive_speed);
EXPORT_SYMBOL(ide_stall_queue);
#ifdef CONFIG_PROC_FS
EXPORT_SYMBOL(ide_add_proc_entries);
#define PDC202XX_DEBUG_DRIVE_INFO 0
#define PDC202XX_DECODE_REGISTER_INFO 0
-#define PDC202XX_FORCE_BURST_BIT 0
-#define PDC202XX_FORCE_MASTER_MODE 0
extern char *ide_xfer_verbose (byte xfer_rate);
switch(drive_number) {
case 0: drive_pci = 0x60;
pci_read_config_dword(dev, drive_pci, &drive_conf);
- if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
- goto chipset_is_set;
pci_read_config_byte(dev, (drive_pci), &test1);
if (!(test1 & SYNC_ERRDY_EN))
pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
break;
case 1: drive_pci = 0x64;
pci_read_config_dword(dev, drive_pci, &drive_conf);
- if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
- goto chipset_is_set;
pci_read_config_byte(dev, 0x60, &test1);
pci_read_config_byte(dev, (drive_pci), &test2);
if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
break;
case 2: drive_pci = 0x68;
pci_read_config_dword(dev, drive_pci, &drive_conf);
- if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
- goto chipset_is_set;
pci_read_config_byte(dev, (drive_pci), &test1);
if (!(test1 & SYNC_ERRDY_EN))
pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
break;
case 3: drive_pci = 0x6c;
pci_read_config_dword(dev, drive_pci, &drive_conf);
- if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
- goto chipset_is_set;
pci_read_config_byte(dev, 0x68, &test1);
pci_read_config_byte(dev, (drive_pci), &test2);
if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
return ide_dma_off_quietly;
}
- err = ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
+ err = ide_config_drive_speed(drive, speed);
#if PDC202XX_DECODE_REGISTER_INFO
pci_read_config_byte(dev, (drive_pci), &AP);
printk("0x%08x\n", drive_conf);
#endif /* PDC202XX_DEBUG_DRIVE_INFO */
-chipset_is_set:
-
return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
((id->dma_ultra >> 8) & 7) ? ide_dma_on :
((id->dma_mword >> 8) & 7) ? ide_dma_on :
(primary_mode & 1) ? "MASTER" : "PCI",
(secondary_mode & 1) ? "MASTER" : "PCI" );
-#if PDC202XX_FORCE_BURST_BIT
+#ifdef PDC202XX_FORCE_BURST_BIT
if (!(udma_speed_flag & 1)) {
printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1));
outb(udma_speed_flag|1, high_16 + 0x001f);
}
#endif /* PDC202XX_FORCE_BURST_BIT */
-#if PDC202XX_FORCE_MASTER_MODE
+#ifdef PDC202XX_FORCE_MASTER_MODE
if (!(primary_mode & 1)) {
printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
name, primary_mode, (primary_mode|1));
/*
- * linux/drivers/block/piix.c Version 0.23 May 29, 1999
+ * linux/drivers/block/piix.c Version 0.24 June 28, 1999
*
* Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
* Copyright (C) 1998-1999 Andre Hedrick, Author and Maintainer
* pci_read_config_word(HWIF(drive)->pci_dev, 0x48, ®48);
* pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, ®4a);
*
+ * #if 0
+ * int err;
+ * err = ide_config_drive_speed(drive, speed);
+ * (void) ide_config_drive_speed(drive, speed);
+ * #else
+ * #endif
*/
#include <linux/types.h>
extern char *ide_xfer_verbose (byte xfer_rate);
+#ifdef CONFIG_BLK_DEV_PIIX_TUNING
/*
*
*/
return 0;
}
}
+#endif /* CONFIG_BLK_DEV_PIIX_TUNING */
/*
* Based on settings done by AMI BIOS
{ 2, 1 },
{ 2, 3 }, };
-#if 1
pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
-#else
- pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
-#endif
pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
if (is_slave) {
master_data = master_data | 0x4000;
restore_flags(flags);
}
+#ifdef CONFIG_BLK_DEV_PIIX_TUNING
+
static int piix_config_drive_for_dma(ide_drive_t *drive, int ultra)
{
struct hd_driveid *id = drive->id;
}
speed = XFER_SW_DMA_2;
} else {
-#if 0
- speed = XFER_PIO_0;
-#else
speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
-#endif
}
restore_flags(flags);
piix_tune_drive(drive, piix_dma_2_pio(speed));
- (void) ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
+ (void) ide_config_drive_speed(drive, speed);
#if PIIX_DEBUG_DRIVE_INFO
printk("%s: %s drive%d ",
/* Other cases are done by generic IDE-DMA code. */
return ide_dmaproc(func, drive);
}
+#endif /* CONFIG_BLK_DEV_PIIX_TUNING */
void ide_init_piix (ide_hwif_t *hwif)
{
hwif->tuneproc = &piix_tune_drive;
+#ifdef CONFIG_BLK_DEV_PIIX_TUNING
if (hwif->dma_base) {
hwif->dmaproc = &piix_dmaproc;
+ } else
+#endif /* CONFIG_BLK_DEV_PIIX_TUNING */
+ {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
}
+
}
if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
if [ "$CONFIG_PRINTER" != "n" ]; then
- bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
+ bool ' Support for console on line printer' CONFIG_LP_CONSOLE
fi
fi
/*
* Video4Linux Colour QuickCam driver
- * Copyright 1997-1998 Philip Blundell <philb@gnu.org>
+ * Copyright 1997-1999 Philip Blundell <philb@gnu.org>
*
*/
if (is_bi_dir)
{
/* Turn the port around */
- parport_frob_control(q->pport, 0x20, 0x20);
+ parport_data_reverse(q->pport);
mdelay(3);
qcam_set_ack(q, 0);
if (qcam_await_ready1(q, 1)) {
{
printk("qcam: short read.\n");
if (is_bi_dir)
- parport_frob_control(q->pport, 0x20, 0);
+ parport_data_forward(q->pport);
qc_setup(q);
return len;
}
if (qcam_await_ready1(q, 1))
{
printk("qcam: no ack after EOF\n");
- parport_frob_control(q->pport, 0x20, 0);
+ parport_data_forward(q->pport);
qc_setup(q);
return len;
}
- parport_frob_control(q->pport, 0x20, 0);
+ parport_data_forward(q->pport);
mdelay(3);
qcam_set_ack(q, 1);
if (qcam_await_ready1(q, 0))
q->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
NULL, 0, NULL);
- q->bidirectional = (q->pport->modes & PARPORT_MODE_PCPS2)?1:0;
+ q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0;
if (q->pdev == NULL)
{
struct qcam_device *qcam;
if (num_cams == MAX_CAMS)
- {
- printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
return -ENOSPC;
- }
qcam = qcam_init(port);
if (qcam==NULL)
kfree(qcam);
}
-#define BANNER "Connectix Colour Quickcam driver v0.02\n"
+#define BANNER "Connectix Colour Quickcam driver v0.03"
-#ifdef MODULE
-int init_module(void)
+static void cq_attach(struct parport *port)
{
- struct parport *port;
+ init_cqcam(port);
+}
- printk(BANNER);
+static void cq_detach(struct parport *port)
+{
+ /* Write this some day. */
+}
- for (port = parport_enumerate(); port; port=port->next)
- init_cqcam(port);
+static struct parport_driver cqcam_driver = {
+ "cqcam",
+ cq_attach,
+ cq_detach,
+ NULL
+};
- return (num_cams)?0:-ENODEV;
+static void cqcam_init(void)
+{
+ printk(BANNER "\n");
+ parport_register_driver(&cqcam_driver);
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
+MODULE_DESCRIPTION(BANNER);
+
+int init_module(void)
+{
+ cqcam_init();
+ return 0;
}
void cleanup_module(void)
#else
__initfunc(int init_colour_qcams(struct video_init *unused))
{
- struct parport *port;
-
- printk(BANNER);
-
- for (port = parport_enumerate(); port; port=port->next)
- init_cqcam(port);
+ cqcam_init();
return 0;
}
#endif
printk("Not clean (jiff=%lu)...", jiffies);
#endif
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time);
if (signal_pending(current))
break;
}
/* Run one more char cycle */
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time * 5);
current->state = TASK_RUNNING;
#ifdef CY_DEBUG_WAIT_UNTIL_SENT
while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
(serial_in(info, UART_ESI_STAT2) != 0xff)) {
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0;
schedule_timeout(char_time);
if (signal_pending(current))
static struct parport_i2c_bus *bus_list;
-#ifdef __SMP__
static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
-#endif
/* software I2C functions */
return port;
}
- if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) {
+ if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
printk(KERN_ERR "js-db9: specified parport is not bidirectional\n");
return port;
}
* Obsoleted the CAREFUL flag since a printer that doesn' t work with
* CAREFUL will block a bit after in lp_check_status().
* Andrea Arcangeli, 15 Oct 1998
+ * Obsoleted and removed all the lowlevel stuff implemented in the last
+ * month to use the IEEE1284 functions (that handle the _new_ compatibilty
+ * mode fine).
*/
/* This driver should, in theory, work with any parallel port that has an
* # insmod lp.o reset=1
*/
-/*
- * LP OPTIMIZATIONS
- *
- * - TRUST_IRQ flag
- *
- * Epson Stylus Color, HP and many other new printers want the TRUST_IRQ flag
- * set when printing with interrupts. This is a long story. Such printers
- * use a broken handshake (see the timing graph below) when printing with
- * interrupts. The lp driver as default is just able to handle such bogus
- * handshake, but setting such flag cause lp to go faster and probably do
- * what such printers want (even if not documented).
- *
- * NOTE that setting the TRUST_IRQ flag in some printer can cause the irq
- * printing to fail completly. You must try, to know if your printer
- * will handle it. I suggest a graphics printing to force a major flow of
- * characters to the printer for do the test. NOTE also that the TRUST_IRQ
- * flag _should_ be fine everywhere but there is a lot of buggy hardware out
- * there, so I am forced to implement it as a not-default thing.
- * WARNING: before to do the test, be sure to have not played with the
- * `-w' parameter of tunelp!
- *
- * Note also that lp automagically warn you (with a KERN_WARNING) if it
- * detects that you could _try_ to set the TRUST_IRQ flag to speed up the
- * printing and decrease the CPU load.
- *
- * To set the TRUST_IRQ flag you can use this command:
- *
- * tunelp /dev/lp? -T on
- *
- * If you have an old tunelp executable you can (hack and) use this simple
- * C lazy proggy to set the flag in the lp driver:
-
--------------------------- cut here -------------------------------------
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#define LPTRUSTIRQ 0x060f
-
-int main(int argc, char **argv)
-{
- int fd = open("/dev/lp0", O_RDONLY);
- ioctl(fd, LPTRUSTIRQ, argc - 1);
- if (argc - 1)
- printf("trusting the irq\n");
- else
- printf("untrusting the irq\n");
- return 0;
-}
--------------------------- cut here -------------------------------------
-
- * - LP_WAIT time
- *
- * You can use this setting if your printer is fast enough and/or your
- * machine is slow enough ;-).
- *
- * tunelp /dev/lp? -w 0
- *
- * - LP_CHAR tries
- *
- * If you print with irqs probably you can decrease the CPU load a lot using
- * this setting. This is not the default because the printing is reported to
- * be jerky somewhere...
- *
- * tunelp /dev/lp? -c 1
- *
- * 11 Nov 1998, Andrea Arcangeli
- */
-
/* COMPATIBILITY WITH OLD KERNELS
*
* Under Linux 2.0 and previous versions, lp devices were bound to ports at
* this case fine too.
*
* 15 Oct 1998, Andrea Arcangeli
+ *
+ * The so called `buggy' handshake is really the well documented
+ * compatibility mode IEEE1284 handshake. They changed the well known
+ * Centronics handshake acking in the middle of busy expecting to not
+ * break drivers or legacy application, while they broken linux lp
+ * until I fixed it reverse engineering the protocol by hand some
+ * month ago...
+ *
+ * 14 Dec 1998, Andrea Arcangeli
*/
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/console.h>
#include <linux/parport.h>
#undef LP_STATS
struct lp_struct lp_table[LP_NO];
+static unsigned int lp_count = 0;
+
/* Test if printer is ready */
#define LP_READY(status) ((status) & LP_PBUSY)
/* Test if the printer is not acking the strobe */
#define LP_NO_ERROR(status) ((status) & LP_PERRORP)
#undef LP_DEBUG
-#undef LP_READ_DEBUG
+
+/* If you want to see if you can get lp_poll working, define this. */
+#undef SUPPORT_POLL
/* --- parport support ----------------------------------------- */
{
struct lp_struct *lps = (struct lp_struct *)handle;
- if (waitqueue_active (&lps->wait_q))
- wake_up_interruptible(&lps->wait_q);
+ if (!(lps->flags & LP_PORT_BUSY)) {
+ /* Let the port go. */
+ clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
+ return 0;
+ }
+
+ if (!(lps->flags & LP_PORT_BUSY)) {
+ /* Let the port go. */
+ clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
+ return 0;
+ }
/* Don't actually release the port now */
return 1;
}
-#define lp_parport_release(x) do { parport_release(lp_table[(x)].dev); } while (0);
-#define lp_parport_claim(x) do { parport_claim_or_block(lp_table[(x)].dev); } while (0);
+static void lp_check_data (struct lp_struct *lp)
+{
+#if !defined(CONFIG_PARPORT_1284) || !defined (SUPPORT_POLL)
+ return;
+#else
+ struct pardevice *dev = lp->dev;
+ if (!(lp->flags & LP_NO_REVERSE)) {
+ int err = parport_negotiate (dev->port, IEEE1284_MODE_NIBBLE);
+ if (err)
+ lp->flags |= LP_NO_REVERSE;
+ else {
+ unsigned char s = parport_read_status (dev->port);
+ if (s & PARPORT_STATUS_ERROR)
+ lp->flags &= ~LP_DATA_AVAIL;
+ else {
+ lp->flags |= LP_DATA_AVAIL;
+ if (waitqueue_active (&lp->dataq))
+ wake_up_interruptible (&lp->dataq);
+ }
+ }
+ }
+#endif /* IEEE 1284 support */
+}
+
+static void lp_parport_release (int minor)
+{
+ lp_check_data (&lp_table[minor]);
+ if (test_and_clear_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
+ parport_release (lp_table[minor].dev);
+
+ lp_table[minor].flags &= ~LP_PORT_BUSY;
+}
+
+static void lp_parport_claim (int minor)
+{
+ if (!test_and_set_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
+ parport_claim_or_block (lp_table[minor].dev);
+
+ lp_table[minor].flags |= LP_PORT_BUSY;
+}
/* --- low-level port access ----------------------------------- */
#define w_ctr(x,y) do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
#define w_dtr(x,y) do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
-static __inline__ void lp_yield (int minor)
-{
- if (!parport_yield_blocking (lp_table[minor].dev))
- {
- if (current->need_resched)
- schedule ();
- } else
- lp_table[minor].irq_missed = 1;
-}
-
-static __inline__ void lp_schedule(int minor, long timeout)
-{
- struct pardevice *dev = lp_table[minor].dev;
- register unsigned long int timeslip = (jiffies - dev->time);
- if ((timeslip > dev->timeslice) && (dev->port->waithead != NULL)) {
- lp_parport_release(minor);
- lp_table[minor].irq_missed = 1;
- schedule_timeout(timeout);
- lp_parport_claim(minor);
- } else
- schedule_timeout(timeout);
-}
-
static int lp_reset(int minor)
{
int retval;
return retval;
}
-#define lp_wait(minor) udelay(LP_WAIT(minor))
-
-static inline int lp_char(char lpchar, int minor)
+static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- unsigned long count = 0;
-#ifdef LP_STATS
- struct lp_stats *stats;
-#endif
-
- if (signal_pending(current))
- return 0;
-
- for (;;)
- {
- unsigned char status;
- int irq_ok = 0;
-
- /*
- * Give a chance to other pardevice to run in the meantime.
- */
- lp_yield(minor);
-
- status = r_str(minor);
- if (LP_NO_ERROR(status))
- {
- if (LP_READY(status))
- break;
-
- /*
- * This is a crude hack that should be well known
- * at least by Epson device driver developers. -arca
- */
- irq_ok = (!LP_POLLED(minor) &&
- LP_NO_ACKING(status) &&
- lp_table[minor].irq_detected);
- if ((LP_F(minor) & LP_TRUST_IRQ) && irq_ok)
- break;
- }
- /*
- * NOTE: if you run with irqs you _must_ use
- * `tunelp /dev/lp? -c 1' to be rasonable efficient!
- */
- if (++count == LP_CHAR(minor))
- {
- if (irq_ok)
- {
- static int first_time = 1;
- /*
- * The printer is using a buggy handshake, so
- * revert to polling to not overload the
- * machine and warn the user that its printer
- * could get optimized trusting the irq. -arca
- */
- lp_table[minor].irq_missed = 1;
- if (first_time)
- {
- first_time = 0;
- printk(KERN_WARNING "lp%d: the "
- "printing could be optimized "
- "using the TRUST_IRQ flag, "
- "see the top of "
- "linux/drivers/char/lp.c\n",
- minor);
- }
- }
- return 0;
- }
- }
-
- w_dtr(minor, lpchar);
-
-#ifdef LP_STATS
- stats = &LP_STAT(minor);
- stats->chars++;
-#endif
-
- /* must wait before taking strobe high, and after taking strobe
- low, according spec. Some printers need it, others don't. */
- lp_wait(minor);
+ struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
+ if (!(lp_dev->flags & LP_PORT_BUSY))
+ /* We must have the port since we got an interrupt. */
+ lp_check_data (lp_dev);
+ if (waitqueue_active (&lp_dev->waitq))
+ wake_up_interruptible (&lp_dev->waitq);
+}
- /* control port takes strobe high */
- if (LP_POLLED(minor))
- {
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
- lp_wait(minor);
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- } else {
- /*
- * Epson Stylus Color generate the IRQ on the rising edge of
- * strobe so clean the irq's information before playing with
- * the strobe. -arca
- */
- lp_table[minor].irq_detected = 0;
- lp_table[minor].irq_missed = 0;
- /*
- * Be sure that the CPU doesn' t reorder instructions. -arca
- */
- mb();
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE | LP_PINTEN);
- lp_wait(minor);
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
- }
+static void lp_wakeup (void *handle)
+{
+ struct lp_struct *lp_dev = handle;
- /*
- * Give to the printer a chance to put BUSY low. Really we could
- * remove this because we could _guess_ that we are slower to reach
- * again lp_char() than the printer to put BUSY low, but I' d like
- * to remove this variable from the function I go solve
- * when I read bug reports ;-). -arca
- */
- lp_wait(minor);
+ if (lp_dev->flags & LP_PORT_BUSY)
+ return;
-#ifdef LP_STATS
- /* update waittime statistics */
- if (count > stats->maxwait) {
-#ifdef LP_DEBUG
- printk(KERN_DEBUG "lp%d success after %d counts.\n",
- minor, count);
-#endif
- stats->maxwait = count;
+ /* Grab the port if it can help (i.e. reverse mode is possible). */
+ if (!(lp_dev->flags & LP_NO_REVERSE)) {
+ parport_claim (lp_dev->dev);
+ set_bit (LP_HAVE_PORT_BIT, &lp_dev->flags);
+ lp_check_data (lp_dev);
+ if (waitqueue_active (&lp_dev->waitq))
+ wake_up_interruptible (&lp_dev->waitq);
}
- count *= 256;
- wait = (count > stats->meanwait) ? count - stats->meanwait :
- stats->meanwait - count;
- stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
- stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
-#endif
-
- return 1;
}
-static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void lp_error (int minor)
{
- struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
+ int polling;
- if (waitqueue_active (&lp_dev->wait_q))
- wake_up_interruptible(&lp_dev->wait_q);
+ if (LP_F(minor) & LP_ABORT)
+ return;
- lp_dev->irq_detected = 1;
- lp_dev->irq_missed = 0;
-}
-
-static void lp_error(int minor)
-{
- if (LP_POLLED(minor) || LP_PREEMPTED(minor)) {
- current->state = TASK_INTERRUPTIBLE;
- lp_parport_release(minor);
- schedule_timeout(LP_TIMEOUT_POLLED);
- lp_parport_claim(minor);
- lp_table[minor].irq_missed = 1;
- }
+ polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
+ if (polling) lp_parport_release (minor);
+ interruptible_sleep_on_timeout (&lp_table[minor].waitq,
+ LP_TIMEOUT_POLLED);
+ if (polling) lp_parport_claim (minor);
+ else parport_yield_blocking (lp_table[minor].dev);
}
static int lp_check_status(int minor)
{
+ int error = 0;
unsigned int last = lp_table[minor].last_error;
unsigned char status = r_str(minor);
if (status & LP_PERRORP)
last = LP_POUTPA;
printk(KERN_INFO "lp%d out of paper\n", minor);
}
+ error = -ENOSPC;
} else if (!(status & LP_PSELECD)) {
if (last != LP_PSELECD) {
last = LP_PSELECD;
printk(KERN_INFO "lp%d off-line\n", minor);
}
+ error = -EIO;
} else {
if (last != LP_PERRORP) {
last = LP_PERRORP;
printk(KERN_INFO "lp%d on fire\n", minor);
}
+ error = -EIO;
}
lp_table[minor].last_error = last;
- if (last != 0) {
- if (LP_F(minor) & LP_ABORT)
- return 1;
+ if (last != 0)
lp_error(minor);
- }
- return 0;
+ return error;
}
-static int lp_write_buf(unsigned int minor, const char *buf, int count)
+static ssize_t lp_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long copy_size;
- unsigned long total_bytes_written = 0;
- unsigned long bytes_written;
- struct lp_struct *lp = &lp_table[minor];
-
- if (minor >= LP_NO)
- return -ENXIO;
- if (lp->dev == NULL)
- return -ENXIO;
-
- lp_table[minor].last_error = 0;
- lp_table[minor].irq_detected = 0;
- lp_table[minor].irq_missed = 1;
-
- if (LP_POLLED(minor))
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- else
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
-
- do {
- bytes_written = 0;
- copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
-
- if (copy_from_user(lp->lp_buffer, buf, copy_size))
- {
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- return -EFAULT;
- }
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct parport *port = lp_table[minor].dev->port;
+ char *kbuf = lp_table[minor].lp_buffer;
+ ssize_t retv = 0;
+ ssize_t written;
+ size_t copy_size = count;
- while (copy_size) {
- if (lp_char(lp->lp_buffer[bytes_written], minor)) {
- --copy_size;
- ++bytes_written;
#ifdef LP_STATS
- lp->runchars++;
-#endif
- } else {
- int rc = total_bytes_written + bytes_written;
+ if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
+ lp_table[minor].runchars = 0;
-#ifdef LP_STATS
- if (lp->runchars > LP_STAT(minor).maxrun)
- LP_STAT(minor).maxrun = lp->runchars;
- LP_STAT(minor).sleeps++;
+ lp_table[minor].lastcall = jiffies;
#endif
- if (signal_pending(current))
- {
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- if (total_bytes_written + bytes_written)
- return total_bytes_written + bytes_written;
- else
- return -EINTR;
- }
+ /* Need to copy the data from user-space. */
+ if (copy_size > LP_BUFFER_SIZE)
+ copy_size = LP_BUFFER_SIZE;
-#ifdef LP_STATS
- lp->runchars = 0;
-#endif
+ if (copy_from_user (kbuf, buf, copy_size))
+ return -EFAULT;
- if (lp_check_status(minor))
- {
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- return rc ? rc : -EIO;
- }
+ /* Claim Parport or sleep until it becomes available
+ */
+ lp_parport_claim (minor);
- if (LP_POLLED(minor) ||
- lp_table[minor].irq_missed)
- {
- lp_polling:
-#if defined(LP_DEBUG) && defined(LP_STATS)
- printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp->runchars, LP_TIME(minor));
-#endif
- current->state = TASK_INTERRUPTIBLE;
- lp_schedule(minor, LP_TIME(minor));
- } else {
- cli();
- if (LP_PREEMPTED(minor))
- {
- /*
- * We can' t sleep on the interrupt
- * since another pardevice need the port.
- * We must check this in a cli() protected
- * envinroment to avoid parport sharing
- * starvation.
- */
- sti();
- goto lp_polling;
- }
- if (!lp_table[minor].irq_detected)
- interruptible_sleep_on_timeout(&lp->wait_q, LP_TIMEOUT_INTERRUPT);
- sti();
- }
- }
+ /* Go to compatibility mode. */
+ parport_negotiate (port, IEEE1284_MODE_COMPAT);
+
+ do {
+ /* Wait until lp_read has finished. */
+ if (down_interruptible (&lp_table[minor].port_mutex))
+ break;
+
+ /* Write the data. */
+ written = parport_write (port, kbuf, copy_size);
+ if (written >= 0) {
+ copy_size -= written;
+ count -= written;
+ buf += written;
+ retv += written;
}
- total_bytes_written += bytes_written;
- buf += bytes_written;
- count -= bytes_written;
+ up (&lp_table[minor].port_mutex);
- } while (count > 0);
+ if (signal_pending (current)) {
+ if (retv == 0)
+ retv = -EINTR;
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- return total_bytes_written;
-}
+ break;
+ }
-static ssize_t lp_write(struct file * file, const char * buf,
- size_t count, loff_t *ppos)
-{
- unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
- ssize_t retv;
+ if (copy_size > 0) {
+ /* incomplete write -> check error ! */
+ int error = lp_check_status (minor);
-#ifdef LP_STATS
- if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
- lp_table[minor].runchars = 0;
+ if (LP_F(minor) & LP_ABORT) {
+ if (retv == 0)
+ retv = error;
+ break;
+ }
- lp_table[minor].lastcall = jiffies;
-#endif
+ parport_yield_blocking (lp_table[minor].dev);
+ } else if (current->need_resched)
+ schedule ();
- /* Claim Parport or sleep until it becomes available
- */
- lp_parport_claim (minor);
+ if (count) {
+ copy_size = count;
+ if (copy_size > LP_BUFFER_SIZE)
+ copy_size = LP_BUFFER_SIZE;
- retv = lp_write_buf(minor, buf, count);
+ if (copy_from_user(kbuf, buf, copy_size)) {
+ if (retv == 0)
+ retv = -EFAULT;
+ break;
+ }
+ }
+ } while (count > 0);
lp_parport_release (minor);
+
return retv;
}
return -ESPIPE;
}
-#ifdef CONFIG_PRINTER_READBACK
-
-static int lp_read_nibble(int minor)
-{
- unsigned char i;
- i = r_str(minor)>>3;
- i &= ~8;
- if ((i & 0x10) == 0) i |= 8;
- return (i & 0x0f);
-}
+#ifdef CONFIG_PARPORT_1284
-static void lp_read_terminate(struct parport *port) {
- parport_write_control(port, (parport_read_control(port) & ~2) | 8);
- /* SelectIN high, AutoFeed low */
- if (parport_wait_peripheral(port, 0x80, 0))
- /* timeout, SelectIN high, Autofeed low */
- return;
- parport_write_control(port, parport_read_control(port) | 2);
- /* AutoFeed high */
- parport_wait_peripheral(port, 0x80, 0x80);
- /* no timeout possible, Autofeed low, SelectIN high */
- parport_write_control(port, (parport_read_control(port) & ~2) | 8);
-}
-
-/* Status readback confirming to ieee1284 */
+/* Status readback conforming to ieee1284 */
static ssize_t lp_read(struct file * file, char * buf,
- size_t length, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
- int i;
unsigned int minor=MINOR(file->f_dentry->d_inode->i_rdev);
- char *temp = buf;
- ssize_t count = 0;
- unsigned char z = 0;
- unsigned char Byte = 0;
struct parport *port = lp_table[minor].dev->port;
+ ssize_t retval = 0;
+ char *kbuf = lp_table[minor].lp_buffer;
+
+ if (count > LP_BUFFER_SIZE)
+ count = LP_BUFFER_SIZE;
lp_parport_claim (minor);
- switch (parport_ieee1284_nibble_mode_ok(port, 0))
- {
- case 0:
- /* Handshake failed. */
- lp_read_terminate(port);
- lp_parport_release (minor);
- return -EIO;
- case 1:
- /* No data. */
- lp_read_terminate(port);
- lp_parport_release (minor);
- return 0;
- default:
- /* Data available. */
+ if (!down_interruptible (&lp_table[minor].port_mutex)) {
+ for (;;) {
+ retval = parport_read (port, kbuf, count);
- /* Hack: Wait 10ms (between events 6 and 7) */
- schedule_timeout((HZ+99)/100);
- break;
- }
+ if (retval)
+ break;
- for (i=0; ; i++) {
- parport_frob_control(port, 2, 2); /* AutoFeed high */
- if (parport_wait_peripheral(port, 0x40, 0)) {
-#ifdef LP_READ_DEBUG
- /* Some peripherals just time out when they've sent
- all their data. */
- printk("%s: read1 timeout.\n", port->name);
-#endif
- parport_frob_control(port, 2, 0); /* AutoFeed low */
- break;
- }
- z = lp_read_nibble(minor);
- parport_frob_control(port, 2, 0); /* AutoFeed low */
- if (parport_wait_peripheral(port, 0x40, 0x40)) {
- printk("%s: read2 timeout.\n", port->name);
- break;
- }
- if ((i & 1) != 0) {
- Byte |= (z<<4);
- if (__put_user (Byte, temp))
- {
- count = -EFAULT;
+ if (file->f_flags & O_NONBLOCK)
break;
- } else {
- temp++;
- if (++count == length)
- break;
- }
- /* Does the error line indicate end of data? */
- if ((parport_read_status(port) & LP_PERRORP) ==
- LP_PERRORP)
+ /* Wait for an interrupt. */
+ interruptible_sleep_on_timeout (&lp_table[minor].waitq,
+ LP_TIMEOUT_POLLED);
+
+ if (signal_pending (current)) {
+ retval = -EINTR;
break;
- } else
- Byte=z;
- }
+ }
+ }
- lp_read_terminate(port);
+ up (&lp_table[minor].port_mutex);
+ }
lp_parport_release (minor);
- return count;
+ if (retval > 0 && copy_to_user (buf, kbuf, retval))
+ retval = -EFAULT;
+
+ return retval;
}
-#endif
+#endif /* IEEE 1284 support */
static int lp_open(struct inode * inode, struct file * file)
{
LP_F(minor) &= ~LP_BUSY;
return -ENOMEM;
}
- init_waitqueue_head(&(lp_table[minor].wait_q));
return 0;
}
int retval = 0;
#ifdef LP_DEBUG
- printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+ printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
#endif
if (minor >= LP_NO)
return -ENODEV;
LP_F(minor) &= ~LP_CAREFUL;
break;
#endif
- case LPTRUSTIRQ:
- if (arg)
- LP_F(minor) |= LP_TRUST_IRQ;
- else
- LP_F(minor) &= ~LP_TRUST_IRQ;
- break;
case LPWAIT:
LP_WAIT(minor) = arg;
break;
return retval;
}
+#ifdef CONFIG_PARPORT_1284
+static unsigned int lp_poll (struct file *filp, struct poll_table_struct *wait)
+{
+ unsigned int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
+ unsigned int mask = POLLOUT | POLLWRNORM; /* always writable */
+
+ poll_wait (filp, &lp_table[minor].dataq, wait);
+
+ if (lp_table[minor].flags & LP_DATA_AVAIL)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+#endif /* IEEE 1284 support */
+
static struct file_operations lp_fops = {
lp_lseek,
-#ifdef CONFIG_PRINTER_READBACK
+#ifdef CONFIG_PARPORT_1284
lp_read,
#else
NULL,
#endif
lp_write,
NULL, /* lp_readdir */
- NULL, /* lp_poll */
+#ifdef CONFIG_PARPORT_1284
+ lp_poll,
+#else
+ NULL,
+#endif
lp_ioctl,
NULL, /* lp_mmap */
lp_open,
lp_release
};
+/* --- support for console on the line printer ----------------- */
+
+#ifdef CONFIG_LP_CONSOLE
+
+#define CONSOLE_LP 0
+
+/* If the printer is out of paper, we can either lose the messages or
+ * stall until the printer is happy again. Define CONSOLE_LP_STRICT
+ * non-zero to get the latter behaviour. */
+#define CONSOLE_LP_STRICT 1
+
+static void lp_console_write (struct console *co, const char *s,
+ unsigned count)
+{
+ struct pardevice *dev = lp_table[CONSOLE_LP].dev;
+ struct parport *port = dev->port;
+ ssize_t written;
+ signed long old_to;
+
+ if (!(lp_table[CONSOLE_LP].flags & (1<<LP_HAVE_PORT_BIT))) {
+ if (parport_claim (dev))
+ /* Nothing we can do. */
+ return;
+ set_bit (LP_HAVE_PORT_BIT, &lp_table[CONSOLE_LP].flags);
+ }
+
+ old_to = parport_set_timeout (dev, 0);
+
+ /* Go to compatibility mode. */
+ parport_negotiate (port, IEEE1284_MODE_COMPAT);
+
+ do {
+ /* Write the data. */
+ written = parport_write (port, s, count);
+ if (written > 0) {
+ s += written;
+ count -= written;
+ }
+ } while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
+
+ parport_set_timeout (dev, old_to);
+}
+
+static kdev_t lp_console_device (struct console *c)
+{
+ return MKDEV(LP_MAJOR, CONSOLE_LP);
+}
+
+static struct console lpcons = {
+ "lp",
+ lp_console_write,
+ NULL,
+ lp_console_device,
+ NULL,
+ NULL,
+ NULL,
+ CON_PRINTBUFFER,
+ -1,
+ 0,
+ NULL
+};
+
+#endif /* console on line printer */
+
/* --- initialisation code ------------------------------------- */
#ifdef MODULE
#else
-static int parport_nr[LP_NO] __initdata = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
-static int reset __initdata = 0;
+static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static int reset = 0;
static int parport_ptr = 0;
#endif
-int lp_register(int nr, struct parport *port)
+static int lp_register(int nr, struct parport *port)
{
lp_table[nr].dev = parport_register_device(port, "lp",
- lp_preempt, NULL,
+ lp_preempt, lp_wakeup,
lp_interrupt,
0,
(void *) &lp_table[nr]);
return 0;
}
-int lp_init(void)
+static void lp_attach (struct parport *port)
{
- unsigned int count = 0;
unsigned int i;
- struct parport *port;
- for(i = 0; i < LP_NO; i++) {
+ switch (parport_nr[0])
+ {
+ case LP_PARPORT_UNSPEC:
+ case LP_PARPORT_AUTO:
+ if (parport_nr[0] == LP_PARPORT_AUTO &&
+ port->probe_info[0].class != PARPORT_CLASS_PRINTER)
+ return;
+
+ if (!lp_register(lp_count, port))
+ if (++lp_count == LP_NO)
+ break;
+
+ break;
+
+ default:
+ for (i = 0; i < LP_NO; i++) {
+ if (port->number == parport_nr[i]) {
+ if (!lp_register(i, port))
+ lp_count++;
+ break;
+ }
+ }
+ break;
+ }
+}
+
+static void lp_detach (struct parport *port)
+{
+ /* Write this some day. */
+}
+
+static struct parport_driver lp_driver = {
+ "lp",
+ lp_attach,
+ lp_detach,
+ NULL
+};
+
+int __init lp_init (void)
+{
+ int i;
+
+ if (parport_nr[0] == LP_PARPORT_OFF)
+ return 0;
+
+ for (i = 0; i < LP_NO; i++) {
lp_table[i].dev = NULL;
lp_table[i].flags = 0;
lp_table[i].chars = LP_INIT_CHAR;
#ifdef LP_STATS
lp_table[i].lastcall = 0;
lp_table[i].runchars = 0;
- memset(&lp_table[i].stats, 0, sizeof(struct lp_stats));
+ memset (&lp_table[i].stats, 0, sizeof (struct lp_stats));
#endif
- init_waitqueue_head(&lp_table[i].wait_q);
lp_table[i].last_error = 0;
- lp_table[i].irq_detected = 0;
- lp_table[i].irq_missed = 0;
+ init_waitqueue_head (&lp_table[i].waitq);
+ init_waitqueue_head (&lp_table[i].dataq);
+ init_MUTEX (&lp_table[i].port_mutex);
}
- switch (parport_nr[0])
- {
- case LP_PARPORT_OFF:
- return 0;
-
- case LP_PARPORT_UNSPEC:
- case LP_PARPORT_AUTO:
- for (port = parport_enumerate(); port; port = port->next) {
-
- if (parport_nr[0] == LP_PARPORT_AUTO &&
- port->probe_info.class != PARPORT_CLASS_PRINTER)
- continue;
-
- if (!lp_register(count, port))
- if (++count == LP_NO)
- break;
- }
- break;
+ if (register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
+ printk ("lp: unable to get major %d\n", LP_MAJOR);
+ return -EIO;
+ }
- default:
- for (i = 0; i < LP_NO; i++) {
- for (port = parport_enumerate(); port;
- port = port->next) {
- if (port->number == parport_nr[i]) {
- if (!lp_register(i, port))
- count++;
- break;
- }
- }
- }
- break;
+ if (parport_register_driver (&lp_driver)) {
+ printk ("lp: unable to register with parport\n");
+ return -EIO;
}
- if (count) {
- if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
- printk("lp: unable to get major %d\n", LP_MAJOR);
- return -EIO;
- }
- } else {
- printk(KERN_INFO "lp: driver loaded but no devices found\n");
+ if (!lp_count) {
+ printk (KERN_INFO "lp: driver loaded but no devices found\n");
+#ifndef CONFIG_PARPORT_12843
+ if (parport_nr[0] == LP_PARPORT_AUTO)
+ printk (KERN_INFO "lp: (is IEEE 1284.3 support enabled?)\n");
+#endif
+ }
+#ifdef CONFIG_LP_CONSOLE
+ else {
+ register_console (&lpcons);
+ printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP);
}
+#endif
return 0;
}
{
unsigned int offset;
+ parport_unregister_driver (&lp_driver);
+
+#ifdef CONFIG_LP_CONSOLE
+ unregister_console (&lpcons);
+#endif
+
unregister_chrdev(LP_MAJOR, "lp");
for (offset = 0; offset < LP_NO; offset++) {
if (lp_table[offset].dev == NULL)
continue;
+ if (lp_table[offset].flags & (1<<LP_HAVE_PORT_BIT))
+ parport_release (lp_table[offset].dev);
parport_unregister_device(lp_table[offset].dev);
}
}
jiffies, check_time);
#endif
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(check_time);
if (signal_pending(current))
break;
printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time);
if (signal_pending(current))
break;
char_time = MIN(char_time, timeout);
while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time);
if (signal_pending(current))
break;
--- /dev/null
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+# Parport configuration.
+#
+
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+ dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+ if [ "$CONFIG_PARPORT_PC" != "n" ]; then
+ bool ' Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+ fi
+ if [ "$CONFIG_ARM" = "y" ]; then
+ dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+ fi
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_tristate ' Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT
+ if [ "$CONFIG_ZORRO" != "n" ]; then
+ dep_tristate ' Multiface III parallel port' CONFIG_PARPORT_MFC3 $CONFIG_PARPORT
+ fi
+ else
+ define_bool CONFIG_PARPORT_AMIGA n
+ define_bool CONFIG_PARPORT_MFC3 n
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ dep_tristate ' Atari hardware' CONFIG_PARPORT_ATARI $CONFIG_PARPORT
+ else
+ define_bool CONFIG_PARPORT_ATARI n
+ fi
+
+ # If exactly one hardware type is selected then parport will optimise away
+ # support for loading any others. Defeat this if the user is keen.
+ bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
+
+ bool ' IEEE 1284 transfer modes' CONFIG_PARPORT_1284
+fi
MIX_OBJS :=
ifeq ($(CONFIG_PARPORT),y)
- L_OBJS += parport_share.o parport_ieee1284.o parport_procfs.o
+ L_OBJS += parport_share.o parport_ieee1284.o parport_ieee1284_ops.o \
+ parport_procfs.o
+
+ ifeq ($(CONFIG_PARPORT_1284),y)
+ L_OBJS += parport_daisy.o parport_probe.o
+ endif
+
ifeq ($(CONFIG_PARPORT_PC),y)
LX_OBJS += parport_pc.o
else
LX_OBJS += parport_init.o
else
ifeq ($(CONFIG_PARPORT),m)
- MI_OBJS += parport_share.o parport_ieee1284.o
+ MI_OBJS += parport_share.o parport_ieee1284.o parport_ieee1284_ops.o
+ ifeq ($(CONFIG_PARPORT_1284),y)
+ MI_OBJS += parport_daisy.o parport_probe.o
+ endif
ifneq ($(CONFIG_PROC_FS),n)
MI_OBJS += parport_procfs.o
endif
arc_read_control,
arc_frob_control,
- NULL, /* write_econtrol */
- NULL, /* read_econtrol */
- NULL, /* frob_econtrol */
-
- arc_write_status,
arc_read_status,
- NULL, /* write_fifo */
- NULL, /* read_fifo */
-
- NULL, /* change_mode */
-
- NULL, /* epp_write_data */
- NULL, /* epp_read_data */
- NULL, /* epp_write_addr */
- NULL, /* epp_read_addr */
- NULL, /* epp_check_timeout */
+ arc_enable_irq,
+ arc_disable_irq,
- NULL, /* epp_write_block */
- NULL, /* epp_read_block */
+ arc_data_forward,
+ arc_data_reverse,
+
+ arc_interrupt,
- NULL, /* ecp_write_block */
- NULL, /* epp_write_block */
-
arc_init_state,
arc_save_state,
arc_restore_state,
- arc_enable_irq,
- arc_disable_irq,
- arc_interrupt,
-
arc_inc_use_count,
arc_dec_use_count,
- arc_fill_inode
+ arc_fill_inode,
+
+ parport_ieee1284_epp_write_data,
+ parport_ieee1284_epp_read_data,
+ parport_ieee1284_epp_write_addr,
+ parport_ieee1284_epp_read_addr,
+
+ parport_ieee1284_ecp_write_data,
+ parport_ieee1284_ecp_read_data,
+ parport_ieee1284_ecp_write_addr,
+
+ parport_ieee1284_write_compat,
+ parport_ieee1284_read_nibble,
+ parport_ieee1284_read_byte,
};
/* --- Initialisation code -------------------------------- */
/* Archimedes hardware provides only one port, at a fixed address */
struct parport *p;
- if (check_region(PORT_BASE, 4))
+ if (check_region(PORT_BASE, 1))
return 0;
-
- p = parport_register_port(base, IRQ_PRINTERACK,
- PARPORT_DMA_NONE, &parport_arc_ops);
+
+ p = parport_register_port (PORT_BASE, IRQ_PRINTERACK,
+ PARPORT_DMA_NONE, &parport_arc_ops);
if (!p)
return 0;
p->irq);
parport_proc_register(p);
- if (parport_probe_hook)
- (*parport_probe_hook)(p);
-
/* Tell the high-level drivers about the port. */
parport_announce_port (p);
printk(KERN_INFO "%s: Atari built-in port using irq\n", p->name);
parport_proc_register(p);
- if (parport_probe_hook)
- (*parport_probe_hook)(p);
-
parport_announce_port (p);
return 1;
void
parport_ax_change_mode(struct parport *p, int m)
{
+ /* FIXME */
parport_ax_frob_econtrol(p, 0xe0, m << 5);
}
writel(dcsr, (unsigned long)&dma->dcsr);
}
-int
-parport_ax_claim_resources(struct parport *p)
-{
-}
-
void
-parport_ax_init_state(struct parport_state *s)
+parport_ax_init_state(struct pardevice *dev, struct parport_state *s)
{
- s->u.pc.ctr = 0xc;
- s->u.pc.ecr = 0x0;
+ struct linux_ebus_dma *dma = dev->port->private_data;
+
+ s->u.ax.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+ s->u.ax.ecr = 0x0;
+
+ if (dev->irq_func)
+ s->u.ax.dcsr = (readl((unsigned long)&dma->dcsr)
+ | EBUS_DCSR_INT_EN);
+ else
+ s->u.ax.dcsr = (readl((unsigned long)&dma->dcsr)
+ & ~EBUS_DCSR_INT_EN);
}
void
parport_ax_save_state(struct parport *p, struct parport_state *s)
{
- s->u.pc.ctr = parport_ax_read_control(p);
- s->u.pc.ecr = parport_ax_read_econtrol(p);
+ struct linux_ebus_dma *dma = p->private_data;
+
+ s->u.ax.ctr = parport_ax_read_control(p);
+ s->u.ax.ecr = parport_ax_read_econtrol(p);
+ s->u.ax.dcsr = readl((unsigned long)&dma->dcsr);
}
void
parport_ax_restore_state(struct parport *p, struct parport_state *s)
{
- parport_ax_write_control(p, s->u.pc.ctr);
- parport_ax_write_econtrol(p, s->u.pc.ecr);
-}
-
-size_t
-parport_ax_epp_read_block(struct parport *p, void *buf, size_t length)
-{
- return 0; /* FIXME */
-}
-
-size_t
-parport_ax_epp_write_block(struct parport *p, void *buf, size_t length)
-{
- return 0; /* FIXME */
-}
-
-int
-parport_ax_ecp_read_block(struct parport *p, void *buf, size_t length,
- void (*fn)(struct parport *, void *, size_t),
- void *handle)
-{
- return 0; /* FIXME */
-}
+ struct linux_ebus_dma *dma = p->private_data;
-int
-parport_ax_ecp_write_block(struct parport *p, void *buf, size_t length,
- void (*fn)(struct parport *, void *, size_t),
- void *handle)
-{
- return 0; /* FIXME */
+ parport_ax_write_control(p, s->u.ax.ctr);
+ parport_ax_write_econtrol(p, s->u.ax.ecr);
+ writel(s->u.ax.dcsr, (unsigned long)&dma->dcsr);
}
void
parport_ax_read_control,
parport_ax_frob_control,
- parport_ax_write_econtrol,
- parport_ax_read_econtrol,
- parport_ax_frob_econtrol,
-
- parport_ax_write_status,
parport_ax_read_status,
- parport_ax_write_fifo,
- parport_ax_read_fifo,
-
- parport_ax_change_mode,
-
- parport_ax_write_epp,
- parport_ax_read_epp,
- parport_ax_write_epp_addr,
- parport_ax_read_epp_addr,
- parport_ax_check_epp_timeout,
+ parport_ax_enable_irq,
+ parport_ax_disable_irq,
- parport_ax_epp_write_block,
- parport_ax_epp_read_block,
+ parport_ax_data_forward,
+ parport_ax_data_reverse,
+
+ parport_ax_interrupt,
- parport_ax_ecp_write_block,
- parport_ax_ecp_read_block,
-
parport_ax_init_state,
parport_ax_save_state,
parport_ax_restore_state,
- parport_ax_enable_irq,
- parport_ax_disable_irq,
- parport_ax_interrupt,
-
parport_ax_inc_use_count,
parport_ax_dec_use_count,
- parport_ax_fill_inode
+ parport_ax_fill_inode,
+
+ parport_ieee1284_epp_write_data,
+ parport_ieee1284_epp_read_data,
+ parport_ieee1284_epp_write_addr,
+ parport_ieee1284_epp_read_addr,
+
+ parport_ieee1284_ecp_write_data,
+ parport_ieee1284_ecp_read_data,
+ parport_ieee1284_ecp_write_addr,
+
+ parport_ieee1284_write_compat,
+ parport_ieee1284_read_nibble,
+ parport_ieee1284_read_byte,
};
if (p->dma == PARPORT_DMA_AUTO)
p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE;
- if (p->irq != PARPORT_IRQ_NONE) {
- int err;
- if ((err = request_irq(p->irq, parport_ax_interrupt,
- 0, p->name, p)) != 0)
- return err;
- else
- parport_ax_enable_irq(p);
- }
- request_region(p->base, p->size, p->name);
- if (p->modes & PARPORT_MODE_PCECR)
- request_region(p->base+0x400, 3, p->name);
- request_region((unsigned long)p->private_data,
- sizeof(struct linux_ebus_dma), p->name);
-
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
if (p->irq != PARPORT_IRQ_NONE)
printk(", irq %s", __irq_itoa(p->irq));
printk("]\n");
parport_proc_register(p);
+ if (p->irq != PARPORT_IRQ_NONE)
+ if ((err = request_irq(p->irq, parport_ax_interrupt,
+ 0, p->name, p)) != 0)
+ return 0; /* @@@ FIXME */
+
+ request_region(p->base, p->size, p->name);
+ if (p->modes & PARPORT_MODE_PCECR)
+ request_region(p->base+0x400, 3, p->name);
+ request_region((unsigned long)p->private_data,
+ sizeof(struct linux_ebus_dma), p->name);
+
p->ops->write_control(p, 0x0c);
p->ops->write_data(p, 0);
- if (parport_probe_hook)
- (*parport_probe_hook)(p);
-
+ /* Tell the high-level drivers about the port. */
parport_announce_port (p);
return 1;
--- /dev/null
+/*
+ * IEEE 1284.3 Parallel port daisy chain and multiplexor code
+ *
+ * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ *
+ * 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.
+ *
+ * ??-12-1998: Initial implementation.
+ * 31-01-1999: Make port-cloning transparent.
+ * 13-02-1999: Move DeviceID technique from parport_probe.
+ * 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#define DEBUG /* undef me for production */
+
+#ifdef DEBUG
+#define DPRINTK(stuff...) printk (stuff)
+#else
+#define DPRINTK(stuff...)
+#endif
+
+static struct daisydev {
+ struct daisydev *next;
+ struct parport *port;
+ int daisy;
+ int devnum;
+} *topology = NULL;
+
+static int numdevs = 0;
+
+/* Forward-declaration of lower-level functions. */
+static int mux_present (struct parport *port);
+static int num_mux_ports (struct parport *port);
+static int select_port (struct parport *port);
+static int assign_addrs (struct parport *port);
+
+/* Add a device to the discovered topology. */
+static void add_dev (int devnum, struct parport *port, int daisy)
+{
+ struct daisydev *newdev;
+ newdev = kmalloc (GFP_KERNEL, sizeof (struct daisydev));
+ if (newdev) {
+ newdev->port = port;
+ newdev->daisy = daisy;
+ newdev->devnum = devnum;
+ newdev->next = topology;
+ if (!topology || topology->devnum >= devnum)
+ topology = newdev;
+ else {
+ struct daisydev *prev = topology;
+ while (prev->next && prev->next->devnum < devnum)
+ prev = prev->next;
+ newdev->next = prev->next;
+ prev->next = newdev;
+ }
+ }
+}
+
+/* Clone a parport (actually, make an alias). */
+static struct parport *clone_parport (struct parport *real, int muxport)
+{
+ struct parport *extra = parport_register_port (real->base,
+ real->irq,
+ real->dma,
+ real->ops);
+ if (extra) {
+ extra->portnum = real->portnum;
+ extra->physport = real;
+ extra->muxport = muxport;
+ }
+
+ return extra;
+}
+
+/* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains. */
+int parport_daisy_init (struct parport *port)
+{
+ char *deviceid;
+ static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" };
+ int num_ports;
+ int i;
+
+ /* Because this is called before any other devices exist,
+ * we don't have to claim exclusive access. */
+
+ /* If mux present on normal port, need to create new
+ * parports for each extra port. */
+ if (port->muxport < 0 && mux_present (port) &&
+ /* don't be fooled: a mux must have 2 or 4 ports. */
+ ((num_ports = num_mux_ports (port)) == 2 || num_ports == 4)) {
+ /* Leave original as port zero. */
+ port->muxport = 0;
+ printk (KERN_INFO
+ "%s: 1st (default) port of %d-way multiplexor\n",
+ port->name, num_ports);
+ for (i = 1; i < num_ports; i++) {
+ /* Clone the port. */
+ struct parport *extra = clone_parport (port, i);
+ if (!extra) {
+ if (signal_pending (current))
+ break;
+
+ schedule ();
+ continue;
+ }
+
+ printk (KERN_INFO
+ "%s: %d%s port of %d-way multiplexor on %s\n",
+ extra->name, i + 1, th[i + 1], num_ports,
+ port->name);
+
+ /* Analyse that port too. We won't recurse
+ forever because of the 'port->muxport < 0'
+ test above. */
+ parport_announce_port (extra);
+ }
+ }
+
+ if (port->muxport >= 0)
+ select_port (port);
+
+ parport_daisy_deselect_all (port);
+ assign_addrs (port);
+
+ /* Count the potential legacy device at the end. */
+ add_dev (numdevs++, port, -1);
+
+ /* Find out the legacy device's IEEE 1284 device ID. */
+ deviceid = kmalloc (1000, GFP_KERNEL);
+ if (deviceid) {
+ parport_device_id (numdevs - 1, deviceid, 1000);
+ kfree (deviceid);
+ }
+
+ return 0;
+}
+
+/* Forget about devices on a physical port. */
+void parport_daisy_fini (struct parport *port)
+{
+ struct daisydev *dev, *prev = topology;
+ while (prev && prev->port == port)
+ prev = topology = topology->next;
+
+ while (prev) {
+ dev = prev->next;
+ if (dev && dev->port == port)
+ prev->next = dev->next;
+
+ prev = prev->next;
+ }
+
+ /* Gaps in the numbering could be handled better. How should
+ someone enumerate through all IEEE1284.3 devices in the
+ topology?. */
+ if (!topology) numdevs = 0;
+ return; }
+
+/* Find a device by canonical device number. */
+struct pardevice *parport_open (int devnum, const char *name,
+ int (*pf) (void *), void (*kf) (void *),
+ void (*irqf) (int, void *, struct pt_regs *),
+ int flags, void *handle)
+{
+ struct parport *port = parport_enumerate ();
+ struct pardevice *dev;
+ int portnum;
+ int muxnum;
+ int daisynum;
+
+ if (parport_device_coords (devnum, &portnum, &muxnum, &daisynum))
+ return NULL;
+
+ while (port && ((port->portnum != portnum) ||
+ (port->muxport != muxnum)))
+ port = port->next;
+
+ if (!port)
+ /* No corresponding parport. */
+ return NULL;
+
+ dev = parport_register_device (port, name, pf, kf,
+ irqf, flags, handle);
+ if (dev)
+ dev->daisy = daisynum;
+
+ /* Check that there really is a device to select. */
+ if (daisynum >= 0) {
+ int selected;
+ parport_claim_or_block (dev);
+ selected = port->daisy;
+ parport_release (dev);
+
+ if (selected != port->daisy) {
+ /* No corresponding device. */
+ parport_unregister_device (dev);
+ return NULL;
+ }
+ }
+
+ return dev;
+}
+
+/* The converse of parport_open. */
+void parport_close (struct pardevice *dev)
+{
+ parport_unregister_device (dev);
+}
+
+/* Convert device coordinates into a canonical device number. */
+int parport_device_num (int parport, int mux, int daisy)
+{
+ struct daisydev *dev = topology;
+
+ while (dev && dev->port->portnum != parport &&
+ dev->port->muxport != mux && dev->daisy != daisy)
+ dev = dev->next;
+
+ if (!dev)
+ return -ENXIO;
+
+ return dev->devnum;
+}
+
+/* Convert a canonical device number into device coordinates. */
+int parport_device_coords (int devnum, int *parport, int *mux, int *daisy)
+{
+ struct daisydev *dev = topology;
+
+ while (dev && dev->devnum != devnum)
+ dev = dev->next;
+
+ if (!dev)
+ return -ENXIO;
+
+ if (parport) *parport = dev->port->portnum;
+ if (mux) *mux = dev->port->muxport;
+ if (daisy) *daisy = dev->daisy;
+ return 0;
+}
+
+/* Send a daisy-chain-style CPP command packet. */
+static int cpp_daisy (struct parport *port, int cmd)
+{
+ unsigned char s;
+
+ parport_write_data (port, 0xaa); udelay (2);
+ parport_write_data (port, 0x55); udelay (2);
+ parport_write_data (port, 0x00); udelay (2);
+ parport_write_data (port, 0xff); udelay (2);
+ s = parport_read_status (port) & (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR);
+ if (s != (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR)) {
+ DPRINTK (KERN_DEBUG "%s: cpp_daisy: aa5500ff(%02x)\n",
+ port->name, s);
+ return -ENXIO;
+ }
+
+ parport_write_data (port, 0x87); udelay (2);
+ s = parport_read_status (port) & (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR);
+ if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
+ DPRINTK (KERN_DEBUG "%s: cpp_daisy: aa5500ff87(%02x)\n",
+ port->name, s);
+ return -ENXIO;
+ }
+
+ parport_write_data (port, 0x78); udelay (2);
+ parport_write_data (port, cmd); udelay (2);
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ udelay (1);
+ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+ udelay (1);
+ s = parport_read_status (port);
+ parport_write_data (port, 0xff); udelay (2);
+
+ return s;
+}
+
+/* Send a mux-style CPP command packet. */
+static int cpp_mux (struct parport *port, int cmd)
+{
+ unsigned char s;
+ int rc;
+
+ parport_write_data (port, 0xaa); udelay (2);
+ parport_write_data (port, 0x55); udelay (2);
+ parport_write_data (port, 0xf0); udelay (2);
+ parport_write_data (port, 0x0f); udelay (2);
+ parport_write_data (port, 0x52); udelay (2);
+ parport_write_data (port, 0xad); udelay (2);
+ parport_write_data (port, cmd); udelay (2);
+
+ s = parport_read_status (port);
+ if (!(s & PARPORT_STATUS_ACK)) {
+ DPRINTK (KERN_DEBUG "%s: cpp_mux: aa55f00f52ad%02x(%02x)\n",
+ port->name, cmd, s);
+ return -EIO;
+ }
+
+ rc = (((s & PARPORT_STATUS_SELECT ? 1 : 0) << 0) |
+ ((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) |
+ ((s & PARPORT_STATUS_BUSY ? 0 : 1) << 2) |
+ ((s & PARPORT_STATUS_ERROR ? 0 : 1) << 3));
+
+ return rc;
+}
+
+void parport_daisy_deselect_all (struct parport *port)
+{
+ cpp_daisy (port, 0x30);
+}
+
+int parport_daisy_select (struct parport *port, int daisy, int mode)
+{
+ /* mode is currently ignored. FIXME? */
+ return cpp_daisy (port, 0xe0 + daisy) & PARPORT_STATUS_ERROR;
+}
+
+static int mux_present (struct parport *port)
+{
+ return cpp_mux (port, 0x51) == 3;
+}
+
+static int num_mux_ports (struct parport *port)
+{
+ return cpp_mux (port, 0x58);
+}
+
+static int select_port (struct parport *port)
+{
+ int muxport = port->muxport;
+ return cpp_mux (port, 0x60 + muxport) == muxport;
+}
+
+static int assign_addrs (struct parport *port)
+{
+ unsigned char s, last_dev;
+ unsigned char daisy;
+ int thisdev = numdevs;
+ char *deviceid;
+
+ parport_write_data (port, 0xaa); udelay (2);
+ parport_write_data (port, 0x55); udelay (2);
+ parport_write_data (port, 0x00); udelay (2);
+ parport_write_data (port, 0xff); udelay (2);
+ s = parport_read_status (port) & (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR);
+ if (s != (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR)) {
+ DPRINTK (KERN_DEBUG "%s: assign_addrs: aa5500ff(%02x)\n",
+ port->name, s);
+ return -ENXIO;
+ }
+
+ parport_write_data (port, 0x87); udelay (2);
+ s = parport_read_status (port) & (PARPORT_STATUS_BUSY
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_ERROR);
+ if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
+ DPRINTK (KERN_DEBUG "%s: assign_addrs: aa5500ff87(%02x)\n",
+ port->name, s);
+ return -ENXIO;
+ }
+
+ parport_write_data (port, 0x78); udelay (2);
+ last_dev = 0; /* We've just been speaking to a device, so we
+ know there must be at least _one_ out there. */
+
+ for (daisy = 0; daisy < 4; daisy++) {
+ parport_write_data (port, daisy);
+ udelay (2);
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ udelay (1);
+ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+ udelay (1);
+
+ if (last_dev)
+ /* No more devices. */
+ break;
+
+ last_dev = !(parport_read_status (port)
+ & PARPORT_STATUS_BUSY);
+
+ add_dev (numdevs++, port, daisy);
+ }
+
+ parport_write_data (port, 0xff); udelay (2);
+ DPRINTK (KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name,
+ numdevs - thisdev);
+
+ /* Ask the new devices to introduce themselves. */
+ deviceid = kmalloc (1000, GFP_KERNEL);
+ if (!deviceid) return 0;
+
+ for (daisy = 0; thisdev < numdevs; thisdev++, daisy++)
+ parport_device_id (thisdev, deviceid, 1000);
+
+ kfree (deviceid);
+ return 0;
+}
+
+/* Find a device with a particular manufacturer and model string,
+ starting from a given device number. Like the PCI equivalent,
+ 'from' itself is skipped. */
+int parport_find_device (const char *mfg, const char *mdl, int from)
+{
+ struct daisydev *d = topology; /* sorted by devnum */
+
+ /* Find where to start. */
+ while (d && d->devnum <= from)
+ d = d->next;
+
+ /* Search. */
+ while (d) {
+ struct parport_device_info *info;
+ info = &d->port->probe_info[1 + d->daisy];
+ if ((!mfg || !strcmp (mfg, info->mfr)) &&
+ (!mdl || !strcmp (mdl, info->model)))
+ break;
+
+ d = d->next;
+ }
+
+ if (d)
+ return d->devnum;
+
+ return -1;
+}
+
+/* Find a device in a particular class. Like the PCI equivalent,
+ 'from' itself is skipped. */
+int parport_find_class (parport_device_class cls, int from)
+{
+ struct daisydev *d = topology; /* sorted by devnum */
+
+ /* Find where to start. */
+ while (d && d->devnum <= from)
+ d = d->next;
+
+ /* Search. */
+ while (d && d->port->probe_info[1 + d->daisy].class != cls)
+ d = d->next;
+
+ if (d)
+ return d->devnum;
+
+ return -1;
+}
* Authors: Phil Blundell <Philip.Blundell@pobox.com>
* Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
* Jose Renau <renau@acm.org>
+ * Tim Waugh <tim@cyberelk.demon.co.uk> (largely rewritten)
+ *
+ * This file is responsible for IEEE 1284 negotiation, and for handing
+ * read/write requests to low-level drivers.
*/
+#include <linux/config.h>
#include <linux/tasks.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#define DEBUG /* undef me for production */
+
+#ifdef CONFIG_LP_CONSOLE
+#undef DEBUG /* Don't want a garbled console */
+#endif
+
+#ifdef DEBUG
+#define DPRINTK(stuff...) printk (stuff)
+#else
+#define DPRINTK(stuff...)
+#endif
+
+/* Make parport_wait_peripheral wake up.
+ * It will be useful to call this from an interrupt handler. */
+void parport_ieee1284_wakeup (struct parport *port)
+{
+ up (&port->physport->ieee1284.irq);
+}
+
+static struct parport *port_from_cookie[PARPORT_MAX];
+static void timeout_waiting_on_port (unsigned long cookie)
+{
+ parport_ieee1284_wakeup (port_from_cookie[cookie % PARPORT_MAX]);
+}
+
+/* Wait for a parport_ieee1284_wakeup.
+ * 0: success
+ * <0: error (exit as soon as possible)
+ * >0: timed out
+ */
+int parport_wait_event (struct parport *port, signed long timeout)
+{
+ int ret;
+ struct timer_list timer;
+
+ if (!port->physport->cad->timeout)
+ /* Zero timeout is special, and we can't down() the
+ semaphore. */
+ return 1;
+
+ init_timer (&timer);
+ timer.expires = jiffies + timeout;
+ timer.function = timeout_waiting_on_port;
+ port_from_cookie[port->number % PARPORT_MAX] = port;
+ timer.data = port->number;
+
+ add_timer (&timer);
+ ret = down_interruptible (&port->physport->ieee1284.irq);
+ if (!del_timer (&timer) && !ret)
+ /* Timed out. */
+ ret = 1;
+
+ return ret;
+}
/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
* 25 for this. After this time we can create a timeout because the
* are able to eat the time up to 40ms.
*/
-int parport_wait_peripheral(struct parport *port, unsigned char mask,
- unsigned char result)
+int parport_wait_peripheral(struct parport *port,
+ unsigned char mask,
+ unsigned char result)
{
int counter;
- unsigned char status;
-
- for (counter = 0; counter < 20; counter++) {
- status = parport_read_status(port);
+ long deadline;
+ unsigned char status;
+
+ counter = port->physport->spintime; /* usecs of fast polling */
+ if (!port->physport->cad->timeout)
+ /* A zero timeout is "special": busy wait for the
+ entire 35ms. */
+ counter = 35000;
+
+ /* Fast polling.
+ *
+ * This should be adjustable.
+ * How about making a note (in the device structure) of how long
+ * it takes, so we know for next time?
+ */
+ for (counter /= 5; counter > 0; counter--) {
+ status = parport_read_status (port);
if ((status & mask) == result)
return 0;
- udelay(25);
+ if (signal_pending (current))
+ return -EINTR;
if (current->need_resched)
- schedule();
+ break;
+ udelay(5);
+ }
+
+ if (!port->physport->cad->timeout)
+ /* We may be in an interrupt handler, so we can't poll
+ * slowly anyway. */
+ return 1;
+
+ /* 40ms of slow polling. */
+ deadline = jiffies + (HZ + 24) / 25;
+ while (time_before (jiffies, deadline)) {
+ int ret;
+
+ if (signal_pending (current))
+ return -EINTR;
+
+ /* Wait for 10ms (or until an interrupt occurs if
+ * the handler is set) */
+ if ((ret = parport_wait_event (port, (HZ + 99) / 100)) < 0)
+ return ret;
+
+ status = parport_read_status (port);
+ if ((status & mask) == result)
+ return 0;
+
+ if (!ret) {
+ /* parport_wait_event didn't time out, but the
+ * peripheral wasn't actually ready either.
+ * Wait for another 10ms. */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout ((HZ+ 99) / 100);
+ }
+ }
+
+ return 1;
+}
+
+#ifdef CONFIG_PARPORT_1284
+/* Terminate a negotiated mode. */
+static void parport_ieee1284_terminate (struct parport *port)
+{
+ port = port->physport;
+
+ port->ieee1284.phase = IEEE1284_PH_TERMINATE;
+
+ /* EPP terminates differently. */
+ switch (port->ieee1284.mode) {
+ case IEEE1284_MODE_EPP:
+ case IEEE1284_MODE_EPPSL:
+ case IEEE1284_MODE_EPPSWE:
+ /* Terminate from EPP mode. */
+
+ /* Event 68: Set nInit low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+ udelay (50);
+
+ /* Event 69: Set nInit high, nSelectIn low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ break;
+
+ default:
+ /* Terminate from all other modes. */
+
+ /* Event 22: Set nSelectIn low, nAutoFd high */
+ parport_frob_control (port,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_SELECT);
+
+ /* Event 24: nAck goes low */
+ parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
+
+ /* Event 25: Set nAutoFd low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 27: nAck goes high */
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK);
+
+ /* Event 29: Set nAutoFd high */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
}
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/25); /* wait for 40ms */
- status = parport_read_status(port);
- return ((status & mask) == result)?0:1;
+
+ port->ieee1284.mode = IEEE1284_MODE_COMPAT;
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+ DPRINTK (KERN_DEBUG "%s: In compatibility (forward idle) mode\n",
+ port->name);
}
+#endif /* IEEE1284 support */
-/* Test if the peripheral is IEEE 1284 compliant.
+/* Negotiate an IEEE 1284 mode.
* return values are:
- * 0 - handshake failed; peripheral is not compliant (or none present)
- * 1 - handshake OK; IEEE1284 peripheral present but no data available
- * 2 - handshake OK; IEEE1284 peripheral and data available
+ * 0 - handshake OK; IEEE1284 peripheral and mode available
+ * -1 - handshake failed; peripheral is not compliant (or none present)
+ * 1 - handshake OK; IEEE1284 peripheral present but mode not available
*/
-int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode)
+int parport_negotiate (struct parport *port, int mode)
{
- /* make sure it's a valid state, set nStrobe & nAutoFeed high */
- parport_frob_control (port, (1|2), 0);
- udelay(1);
- parport_write_data(port, mode);
- udelay(400);
- /* nSelectIn high, nAutoFd low */
- parport_frob_control(port, (2|8), 2);
- if (parport_wait_peripheral(port, 0x78, 0x38)) {
- parport_frob_control(port, (2|8), 8);
+#ifndef CONFIG_PARPORT_1284
+ if (mode == IEEE1284_MODE_COMPAT)
+ return 0;
+ printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
+ return -1;
+#else
+ int m = mode;
+ unsigned char xflag;
+
+ port = port->physport;
+
+ /* Is there anything to do? */
+ if (port->ieee1284.mode == mode)
+ return 0;
+
+ /* Go to compability forward idle mode */
+ if (port->ieee1284.mode != IEEE1284_MODE_COMPAT)
+ parport_ieee1284_terminate (port);
+
+ if (mode == IEEE1284_MODE_COMPAT)
+ /* Compatibility mode: no negotiation. */
return 0;
+
+ switch (mode) {
+ case IEEE1284_MODE_ECPSWE:
+ m = IEEE1284_MODE_ECP;
+ break;
+ case IEEE1284_MODE_EPPSL:
+ case IEEE1284_MODE_EPPSWE:
+ m = IEEE1284_MODE_EPP;
+ break;
+ case IEEE1284_MODE_BECP:
+ return -ENOSYS; /* FIXME (implement BECP) */
}
- /* nStrobe low */
- parport_frob_control (port, 1, 1);
- udelay(1); /* Strobe wait */
- /* nStrobe high, nAutoFeed low, last step before transferring
- * reverse data */
- parport_frob_control (port, (1|2), 0);
+
+ port->ieee1284.phase = IEEE1284_PH_NEGOTIATION;
+
+ /* Start off with nStrobe and nAutoFd high, and nSelectIn low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE
+ | PARPORT_CONTROL_AUTOFD
+ | PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
udelay(1);
- /* Data available? */
- parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK);
- return (parport_read_status(port) & PARPORT_STATUS_ERROR)?1:2;
+
+ /* Event 0: Set data */
+ parport_write_data (port, m);
+ udelay (400); /* Shouldn't need to wait this long. */
+
+ /* Event 1: Set nSelectIn high, nAutoFd low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 2: PError, Select, nFault go high, nAck goes low */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_PAPEROUT
+ | PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_SELECT
+ | PARPORT_STATUS_PAPEROUT)) {
+ /* Timeout */
+ parport_frob_control (port,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_SELECT);
+ DPRINTK (KERN_DEBUG
+ "%s: Peripheral not IEEE1284 compliant (0x%02X)\n",
+ port->name, parport_read_status (port));
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+ return -1; /* Not IEEE1284 compliant */
+ }
+
+ /* Event 3: Set nStrobe low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ /* Event 4: Set nStrobe and nAutoFd high */
+ udelay (5);
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE
+ | PARPORT_CONTROL_AUTOFD,
+ 0);
+
+ /* Event 6: nAck goes high */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK
+ | PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_ACK)) {
+ if (parport_read_status (port) & PARPORT_STATUS_ACK)
+ printk (KERN_DEBUG
+ "%s: working around buggy peripheral: tell "
+ "Tim what make it is\n", port->name);
+ DPRINTK (KERN_DEBUG
+ "%s: Mode 0x%02x not supported? (0x%02x)\n",
+ port->name, mode, port->ops->read_status (port));
+ parport_ieee1284_terminate (port);
+ return 1;
+ }
+
+ xflag = parport_read_status (port) & PARPORT_STATUS_SELECT;
+
+ /* xflag should be high for all modes other than nibble (0). */
+ if (mode && !xflag) {
+ /* Mode not supported. */
+ DPRINTK (KERN_DEBUG "%s: Mode 0x%02x not supported\n",
+ port->name, mode);
+ parport_ieee1284_terminate (port);
+ return 1;
+ }
+
+ /* Mode is supported */
+ DPRINTK (KERN_DEBUG "%s: In mode 0x%02x\n", port->name, mode);
+ port->ieee1284.mode = mode;
+
+ /* But ECP is special */
+ if (mode & IEEE1284_MODE_ECP) {
+ port->ieee1284.phase = IEEE1284_PH_ECP_SETUP;
+
+ /* Event 30: Set nAutoFd low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 31: PError goes high. */
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_PAPEROUT);
+ /* (Should check that this works..) */
+
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+ DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
+ port->name);
+ } else switch (mode) {
+ case IEEE1284_MODE_NIBBLE:
+ case IEEE1284_MODE_BYTE:
+ port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+ break;
+ default:
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+ }
+
+
+ return 0;
+#endif /* IEEE1284 support */
+}
+
+/* Acknowledge that the peripheral has data available.
+ * Events 18-20, in order to get from Reverse Idle phase
+ * to Host Busy Data Available.
+ * This will most likely be called from an interrupt.
+ * Returns zero if data was available.
+ */
+#ifdef CONFIG_PARPORT_1284
+static int parport_ieee1284_ack_data_avail (struct parport *port)
+{
+ if (parport_read_status (port) & PARPORT_STATUS_ERROR)
+ /* Event 18 didn't happen. */
+ return -1;
+
+ /* Event 20: nAutoFd goes high. */
+ port->ops->frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+ port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
+ return 0;
+}
+#endif /* IEEE1284 support */
+
+/* Handle an interrupt. */
+void parport_ieee1284_interrupt (int which, void *handle, struct pt_regs *regs)
+{
+ struct parport *port = handle;
+ parport_ieee1284_wakeup (port);
+
+#ifdef CONFIG_PARPORT_1284
+ if (port->ieee1284.phase == IEEE1284_PH_REV_IDLE) {
+ /* An interrupt in this phase means that data
+ * is now available. */
+ DPRINTK (KERN_DEBUG "%s: Data available\n", port->name);
+ parport_ieee1284_ack_data_avail (port);
+ }
+#endif /* IEEE1284 support */
+}
+
+/* Write a block of data. */
+ssize_t parport_write (struct parport *port, const void *buffer, size_t len)
+{
+#ifndef CONFIG_PARPORT_1284
+ return port->ops->compat_write_data (port, buffer, len, 0);
+#else
+ ssize_t retval;
+ int mode = port->ieee1284.mode;
+ size_t (*fn) (struct parport *, const void *, size_t, int);
+
+ /* Ignore the device-ID-request bit. */
+ mode &= ~IEEE1284_DEVICEID;
+
+ /* Use the mode we're in. */
+ switch (mode) {
+ case IEEE1284_MODE_NIBBLE:
+ parport_negotiate (port, IEEE1284_MODE_COMPAT);
+ case IEEE1284_MODE_COMPAT:
+ DPRINTK (KERN_DEBUG "%s: Using compatibility mode\n",
+ port->name);
+ fn = port->ops->compat_write_data;
+ break;
+
+ case IEEE1284_MODE_EPP:
+ DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
+ fn = port->ops->epp_write_data;
+ break;
+
+ case IEEE1284_MODE_ECP:
+ case IEEE1284_MODE_ECPRLE:
+ DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
+ fn = port->ops->ecp_write_data;
+ break;
+
+ case IEEE1284_MODE_ECPSWE:
+ DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
+ port->name);
+ /* The caller has specified that it must be emulated,
+ * even if we have ECP hardware! */
+ fn = parport_ieee1284_ecp_write_data;
+ break;
+
+ default:
+ DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
+ port->ieee1284.mode);
+ return -ENOSYS;
+ }
+
+ retval = (*fn) (port, buffer, len, 0);
+ DPRINTK (KERN_DEBUG "%s: wrote %d/%d bytes\n", port->name, retval,
+ len);
+ return retval;
+#endif /* IEEE1284 support */
+}
+
+/* Read a block of data. */
+ssize_t parport_read (struct parport *port, void *buffer, size_t len)
+{
+#ifndef CONFIG_PARPORT_1284
+ printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
+ return -ENODEV;
+#else
+ int mode = port->physport->ieee1284.mode;
+ size_t (*fn) (struct parport *, void *, size_t, int);
+
+ /* Ignore the device-ID-request bit. */
+ mode &= ~IEEE1284_DEVICEID;
+
+ /* Use the mode we're in. */
+ switch (mode) {
+ case IEEE1284_MODE_COMPAT:
+ if (parport_negotiate (port, IEEE1284_MODE_NIBBLE))
+ return -EIO;
+ case IEEE1284_MODE_NIBBLE:
+ DPRINTK (KERN_DEBUG "%s: Using nibble mode\n", port->name);
+ fn = port->ops->nibble_read_data;
+ break;
+
+ case IEEE1284_MODE_BYTE:
+ DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
+ fn = port->ops->byte_read_data;
+ break;
+
+ case IEEE1284_MODE_EPP:
+ DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
+ fn = port->ops->epp_read_data;
+ break;
+
+ case IEEE1284_MODE_ECP:
+ case IEEE1284_MODE_ECPRLE:
+ DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
+ fn = port->ops->ecp_read_data;
+ break;
+
+ case IEEE1284_MODE_ECPSWE:
+ DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
+ port->name);
+ fn = parport_ieee1284_ecp_read_data;
+ break;
+
+ default:
+ DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
+ port->physport->ieee1284.mode);
+ return -ENOSYS;
+ }
+
+ return (*fn) (port, buffer, len, 0);
+#endif /* IEEE1284 support */
+}
+
+/* Set the amount of time we wait while nothing's happening. */
+long parport_set_timeout (struct pardevice *dev, long inactivity)
+{
+ long int old = dev->timeout;
+
+ dev->timeout = inactivity;
+
+ if (dev->port->physport->cad == dev)
+ parport_ieee1284_wakeup (dev->port);
+
+ return old;
}
--- /dev/null
+/* IEEE-1284 operations for parport.
+ *
+ * This file is for generic IEEE 1284 operations. The idea is that
+ * they are used by the low-level drivers. If they have a special way
+ * of doing something, they can provide their own routines (and put
+ * the function pointers in port->ops); if not, they can just use these
+ * as a fallback.
+ *
+ * Note: Make no assumptions about hardware or architecture in this file!
+ *
+ * Author: Tim Waugh <tim@cyberelk.demon.co.uk>
+ */
+
+#include <linux/config.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#define DEBUG /* undef me for production */
+
+#ifdef CONFIG_LP_CONSOLE
+#undef DEBUG /* Don't want a garbled console */
+#endif
+
+#ifdef DEBUG
+#define DPRINTK(stuff...) printk (stuff)
+#else
+#define DPRINTK(stuff...)
+#endif
+
+/*** *
+ * One-way data transfer functions. *
+ * ***/
+
+static inline
+int polling (struct pardevice *dev)
+{
+ return dev->port->irq == PARPORT_IRQ_NONE;
+}
+
+/* Compatibility mode. */
+size_t parport_ieee1284_write_compat (struct parport *port,
+ const void *buffer, size_t len,
+ int flags)
+{
+ ssize_t count = 0;
+ const unsigned char *addr = buffer;
+ unsigned char byte;
+ struct pardevice *dev = port->physport->cad;
+ unsigned char ctl = (PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_INIT);
+
+ if (port->irq != PARPORT_IRQ_NONE)
+ parport_enable_irq (port);
+
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+ while (count < len) {
+ long expire = jiffies + dev->timeout;
+ long wait = (HZ + 99) / 100;
+ unsigned char mask = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ unsigned char val = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ int i;
+
+ /* Write the character to the data lines. */
+ byte = *addr++;
+ parport_write_data (port, byte);
+ udelay (1);
+
+ /* Wait until the peripheral's ready */
+ do {
+ /* Is the peripheral ready yet? */
+ if (!parport_wait_peripheral (port, mask, val))
+ /* Skip the loop */
+ goto ready;
+
+ /* Is the peripheral upset? */
+ if ((parport_read_status (port) &
+ (PARPORT_STATUS_PAPEROUT |
+ PARPORT_STATUS_SELECT |
+ PARPORT_STATUS_ERROR))
+ != (PARPORT_STATUS_SELECT |
+ PARPORT_STATUS_ERROR))
+ /* If nFault is asserted (i.e. no
+ * error) and PAPEROUT and SELECT are
+ * just red herrings, give the driver
+ * a chance to check it's happy with
+ * that before continuing. */
+ goto stop;
+
+ /* Have we run out of time? */
+ if (!time_before (jiffies, expire))
+ break;
+
+ /* Yield the port for a while. If this is the
+ first time around the loop, don't let go of
+ the port. This way, we find out if we have
+ our interrupt handler called. */
+ if (count && polling (dev)) {
+ parport_release (dev);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (wait);
+ parport_claim_or_block (dev);
+ }
+ else
+ /* We must have the device claimed here */
+ parport_wait_event (port, wait);
+
+ /* Is there a signal pending? */
+ if (signal_pending (current))
+ goto stop;
+
+ /* Wait longer next time. */
+ wait *= 2;
+ } while (time_before (jiffies, expire));
+
+ DPRINTK (KERN_DEBUG "%s: Timed out\n", port->name);
+ break;
+
+ ready:
+ /* Clear out previous irqs. */
+ while (!down_trylock (&port->physport->ieee1284.irq));
+
+ /* Pulse strobe. */
+ parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
+ udelay (1); /* strobe */
+
+ parport_write_control (port, ctl);
+ udelay (1); /* hold */
+
+ /* Wait until it's received (up to 20us). */
+ for (i = 0; i < 20; i++) {
+ if (!down_trylock (&port->physport->ieee1284.irq) ||
+ !(parport_read_status (port) & PARPORT_STATUS_ACK))
+ break;
+ udelay (1);
+ }
+
+ count++;
+
+ /* Let another process run if it needs to. */
+ if (time_before (jiffies, expire))
+ if (!parport_yield_blocking (dev)
+ && current->need_resched)
+ schedule ();
+ }
+ stop:
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+ return count;
+}
+
+/* Nibble mode. */
+size_t parport_ieee1284_read_nibble (struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+#ifndef CONFIG_PARPORT_1284
+ return 0;
+#else
+ unsigned char *buf = buffer;
+ int i;
+ unsigned char byte = 0;
+
+ len *= 2; /* in nibbles */
+ for (i=0; i < len; i++) {
+ unsigned char nibble;
+
+ /* Does the error line indicate end of data? */
+ if (((i & 1) == 0) &&
+ (parport_read_status(port) & PARPORT_STATUS_ERROR)) {
+ port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DNA;
+ DPRINTK (KERN_DEBUG
+ "%s: No more nibble data (%d bytes)\n",
+ port->name, i/2);
+
+ /* Go to reverse idle phase. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+ break;
+ }
+
+ /* Event 7: Set nAutoFd low. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 9: nAck goes low. */
+ port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK, 0)) {
+ /* Timeout -- no more data? */
+ DPRINTK (KERN_DEBUG
+ "%s: Nibble timeout at event 9 (%d bytes)\n",
+ port->name, i/2);
+ break;
+ }
+
+
+ /* Read a nibble. */
+ nibble = parport_read_status (port) >> 3;
+ nibble &= ~8;
+ if ((nibble & 0x10) == 0)
+ nibble |= 8;
+ nibble &= 0xf;
+
+ /* Event 10: Set nAutoFd high. */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+ /* Event 11: nAck goes high. */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK)) {
+ /* Timeout -- no more data? */
+ DPRINTK (KERN_DEBUG
+ "%s: Nibble timeout at event 11\n",
+ port->name);
+ break;
+ }
+
+ if (i & 1) {
+ /* Second nibble */
+ byte |= nibble << 4;
+ *buf++ = byte;
+ } else
+ byte = nibble;
+ }
+
+ i /= 2; /* i is now in bytes */
+
+ if (i == len) {
+ /* Read the last nibble without checking data avail. */
+ port = port->physport;
+ if (parport_read_status (port) & PARPORT_STATUS_ERROR)
+ port->ieee1284.phase = IEEE1284_PH_HBUSY_DNA;
+ else
+ port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
+ }
+
+ return i;
+#endif /* IEEE1284 support */
+}
+
+/* Byte mode. */
+size_t parport_ieee1284_read_byte (struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+#ifndef CONFIG_PARPORT_1284
+ return 0;
+#else
+ unsigned char *buf = buffer;
+ ssize_t count = 0;
+
+ for (count = 0; count < len; count++) {
+ unsigned char byte;
+
+ /* Data available? */
+ if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
+ port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DNA;
+ DPRINTK (KERN_DEBUG
+ "%s: No more byte data (%d bytes)\n",
+ port->name, count);
+
+ /* Go to reverse idle phase. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+ break;
+ }
+
+ /* Event 7: Set nAutoFd low. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 9: nAck goes low. */
+ port->physport->ieee1284.phase = IEEE1284_PH_REV_DATA;
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ 0)) {
+ /* Timeout -- no more data? */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
+ 0);
+ DPRINTK (KERN_DEBUG "%s: Byte timeout at event 9\n",
+ port->name);
+ break;
+ }
+
+ byte = parport_read_data (port);
+ *buf++ = byte;
+
+ /* Event 10: Set nAutoFd high */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+ /* Event 11: nAck goes high. */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK)) {
+ /* Timeout -- no more data? */
+ DPRINTK (KERN_DEBUG "%s: Byte timeout at event 11\n",
+ port->name);
+ break;
+ }
+
+ /* Event 16: Set nStrobe low. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ udelay (5);
+
+ /* Event 17: Set nStrobe high. */
+ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+ }
+
+ if (count == len) {
+ /* Read the last byte without checking data avail. */
+ port = port->physport;
+ if (parport_read_status (port) & PARPORT_STATUS_ERROR)
+ port->ieee1284.phase = IEEE1284_PH_HBUSY_DNA;
+ else
+ port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
+ }
+
+ return count;
+#endif /* IEEE1284 support */
+}
+
+/*** *
+ * ECP Functions. *
+ * ***/
+
+#ifdef CONFIG_PARPORT_1284
+
+static inline
+int ecp_forward_to_reverse (struct parport *port)
+{
+ int retval;
+
+ /* Event 38: Set nAutoFd low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ parport_data_reverse (port);
+ udelay (5);
+
+ /* Event 39: Set nInit low to initiate bus reversal */
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+
+ /* Event 40: PError goes low */
+ retval = parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT, 0);
+
+ if (!retval) {
+ DPRINTK (KERN_DEBUG "%s: ECP direction: reverse\n",
+ port->name);
+ port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+ }
+
+ return retval;
+}
+
+static inline
+int ecp_reverse_to_forward (struct parport *port)
+{
+ int retval;
+
+ /* Event 47: Set nInit high */
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+ parport_data_reverse (port);
+
+ /* Event 49: PError goes high */
+ retval = parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_PAPEROUT);
+
+ if (!retval) {
+ parport_data_forward (port);
+ DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
+ port->name);
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+ }
+
+ return retval;
+}
+
+#endif /* IEEE1284 support */
+
+/* ECP mode, forward channel, data. */
+size_t parport_ieee1284_ecp_write_data (struct parport *port,
+ const void *buffer, size_t len,
+ int flags)
+{
+#ifndef CONFIG_PARPORT_1284
+ return 0;
+#else
+ const unsigned char *buf = buffer;
+ size_t written;
+ int ctl = parport_read_control (port) & ~PARPORT_CONTROL_AUTOFD;
+ int retry;
+
+ port = port->physport;
+
+ if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
+ if (ecp_reverse_to_forward (port))
+ return 0;
+
+ port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+ /* HostAck high (data, not command) */
+ parport_write_control (port, ctl);
+ for (written = 0; written < len; written++, buf++) {
+ long expire = jiffies + port->cad->timeout;
+ unsigned char byte;
+
+ byte = *buf;
+ try_again:
+ parport_write_data (port, byte);
+ parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
+ udelay (5);
+ for (retry = 0; retry < 100; retry++) {
+ if (!parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY, 0))
+ goto success;
+
+ if (signal_pending (current)) {
+ parport_write_control (port, ctl);
+ break;
+ }
+ }
+
+ /* Time for Host Transfer Recovery (page 41 of IEEE1284) */
+ DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
+
+ parport_write_control (port, ctl | PARPORT_CONTROL_INIT);
+ udelay (50);
+ if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
+ /* It's buggered. */
+ parport_write_control (port, ctl);
+ break;
+ }
+
+ parport_write_control (port, ctl);
+ udelay (50);
+ if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
+ break;
+
+ DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
+ port->name);
+
+ if (time_after_eq (jiffies, expire)) break;
+ goto try_again;
+ success:
+ parport_write_control (port, ctl);
+ udelay (5);
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY,
+ PARPORT_STATUS_BUSY))
+ /* Peripheral hasn't accepted the data. */
+ break;
+ }
+
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+ return written;
+#endif /* IEEE1284 support */
+}
+
+/* ECP mode, reverse channel, data. */
+size_t parport_ieee1284_ecp_read_data (struct parport *port,
+ void *buffer, size_t len, int flags)
+{
+#ifndef CONFIG_PARPORT_1284
+ return 0;
+#else
+ struct pardevice *dev = port->cad;
+ unsigned char *buf = buffer;
+ int rle_count = 0; /* shut gcc up */
+ int rle = 0;
+ ssize_t count = 0;
+
+ port = port->physport;
+
+ if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE)
+ if (ecp_forward_to_reverse (port))
+ return 0;
+
+ port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+
+ /* Set HostAck low to start accepting data. */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ while (count < len) {
+ long expire = jiffies + dev->timeout;
+ unsigned char byte;
+ int command;
+
+ /* Event 43: Peripheral sets nAck low. It can take as
+ long as it wants. */
+ while (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK)) {
+ /* The peripheral hasn't given us data in
+ 35ms. If we have data to give back to the
+ caller, do it now. */
+ if (count)
+ goto out;
+
+ /* If we've used up all the time we were allowed,
+ give up altogether. */
+ if (!time_before (jiffies, expire))
+ goto out;
+
+ /* Yield the port for a while. */
+ if (count && polling (dev)) {
+ parport_release (dev);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout ((HZ + 99) / 25);
+ parport_claim_or_block (dev);
+ }
+ else
+ /* We must have the device claimed here. */
+ parport_wait_event (port, (HZ + 99) / 25);
+
+ /* Is there a signal pending? */
+ if (signal_pending (current))
+ goto out;
+ }
+
+ /* Is this a command? */
+ if (rle)
+ /* The last byte was a run-length count, so
+ this can't be as well. */
+ command = 0;
+ else
+ command = (parport_read_status (port) &
+ PARPORT_STATUS_BUSY) ? 1 : 0;
+
+ /* Read the data. */
+ byte = parport_read_data (port);
+
+ /* If this is a channel command, rather than an RLE
+ command or a normal data byte, don't accept it. */
+ if (command) {
+ if (byte & 0x80) {
+ DPRINTK (KERN_DEBUG "%s: stopping short at "
+ "channel command (%02x)\n",
+ port->name, byte);
+ goto out;
+ }
+ else if (port->ieee1284.mode != IEEE1284_MODE_ECPRLE)
+ DPRINTK (KERN_DEBUG "%s: device illegally "
+ "using RLE; accepting anyway\n",
+ port->name);
+
+ rle_count = byte + 1;
+
+ /* Are we allowed to read that many bytes? */
+ if (rle_count > (len - count)) {
+ DPRINTK (KERN_DEBUG "%s: leaving %d RLE bytes "
+ "for next time\n", port->name,
+ rle_count);
+ break;
+ }
+
+ rle = 1;
+ }
+
+ /* Event 44: Set HostAck high, acknowledging handshake. */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+ /* Event 45: The peripheral has 35ms to set nAck high. */
+ if (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
+ /* It's gone wrong. Return what data we have
+ to the caller. */
+ DPRINTK (KERN_DEBUG "ECP read timed out at 45\n");
+
+ if (command)
+ printk (KERN_WARNING
+ "%s: command ignored (%02x)\n",
+ port->name, byte);
+
+ break;
+ }
+
+ /* Event 46: Set HostAck low and accept the data. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* If we just read a run-length count, fetch the data. */
+ if (command)
+ continue;
+
+ /* If this is the byte after a run-length count, decompress. */
+ if (rle) {
+ rle = 0;
+ memset (buf, byte, rle_count);
+ buf += rle_count;
+ count += rle_count;
+ DPRINTK (KERN_DEBUG "%s: decompressed to %d bytes\n",
+ port->name, rle_count);
+ }
+ else
+ /* Normal data byte. */
+ *buf++ = byte, count++;
+ }
+
+ out:
+ return count;
+#endif /* IEEE1284 support */
+}
+
+/* ECP mode, forward channel, commands. */
+size_t parport_ieee1284_ecp_write_addr (struct parport *port,
+ const void *buffer, size_t len,
+ int flags)
+{
+#ifndef CONFIG_PARPORT_1284
+ return 0;
+#else
+ const unsigned char *buf = buffer;
+ size_t written;
+ int ctl = parport_read_control (port) | PARPORT_CONTROL_AUTOFD;
+ int retry;
+
+ port = port->physport;
+
+ if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
+ if (ecp_reverse_to_forward (port))
+ return 0;
+
+ port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+ /* HostAck low (command, not data) */
+ parport_write_control (port, ctl);
+ for (written = 0; written < len; written++, buf++) {
+ long expire = jiffies + port->cad->timeout;
+ unsigned char byte;
+
+ byte = *buf;
+ try_again:
+ parport_write_data (port, byte);
+ parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
+ udelay (5);
+ for (retry = 0; retry < 100; retry++) {
+ if (!parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY, 0))
+ goto success;
+
+ if (signal_pending (current)) {
+ parport_write_control (port, ctl);
+ break;
+ }
+ }
+
+ /* Time for Host Transfer Recovery (page 41 of IEEE1284) */
+ DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
+
+ parport_write_control (port, ctl | PARPORT_CONTROL_INIT);
+ udelay (50);
+ if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
+ /* It's buggered. */
+ parport_write_control (port, ctl);
+ break;
+ }
+
+ parport_write_control (port, ctl);
+ udelay (50);
+ if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
+ break;
+
+ DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
+ port->name);
+
+ if (time_after_eq (jiffies, expire)) break;
+ goto try_again;
+ success:
+ parport_write_control (port, ctl);
+ udelay (5);
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY,
+ PARPORT_STATUS_BUSY))
+ /* Peripheral hasn't accepted the data. */
+ break;
+ }
+
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+ return written;
+#endif /* IEEE1284 support */
+}
+
+/*** *
+ * EPP functions. *
+ * ***/
+
+/* EPP mode, forward channel, data. */
+size_t parport_ieee1284_epp_write_data (struct parport *port,
+ const void *buffer, size_t len,
+ int flags)
+{
+ return 0; /* FIXME */
+}
+
+/* EPP mode, reverse channel, data. */
+size_t parport_ieee1284_epp_read_data (struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+ return 0; /* FIXME */
+}
+
+/* EPP mode, forward channel, addresses. */
+size_t parport_ieee1284_epp_write_addr (struct parport *port,
+ const void *buffer, size_t len,
+ int flags)
+{
+ return 0; /* FIXME */
+}
+
+/* EPP mode, reverse channel, addresses. */
+size_t parport_ieee1284_epp_read_addr (struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+ return 0; /* FIXME */
+}
static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int io_hi[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int irq[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_IRQ_PROBEONLY };
-static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_AUTO };
+static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_NONE };
extern int parport_pc_init(int *io, int *io_hi, int *irq, int *dma);
extern int parport_ax_init(void);
if (io[0] == PARPORT_DISABLE)
return 1;
-#ifdef CONFIG_PNP_PARPORT
- parport_probe_hook = &parport_probe_one;
-#endif
-#ifdef CONFIG_SYSCTL
+#ifdef CONFIG_SYSCTL
parport_default_proc_register ();
#endif
EXPORT_SYMBOL(parport_register_device);
EXPORT_SYMBOL(parport_unregister_device);
EXPORT_SYMBOL(parport_enumerate);
-EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+EXPORT_SYMBOL(parport_negotiate);
+EXPORT_SYMBOL(parport_write);
+EXPORT_SYMBOL(parport_read);
+EXPORT_SYMBOL(parport_ieee1284_wakeup);
EXPORT_SYMBOL(parport_wait_peripheral);
+EXPORT_SYMBOL(parport_wait_event);
+EXPORT_SYMBOL(parport_set_timeout);
+EXPORT_SYMBOL(parport_ieee1284_interrupt);
+EXPORT_SYMBOL(parport_ieee1284_ecp_write_data);
+EXPORT_SYMBOL(parport_ieee1284_ecp_read_data);
+EXPORT_SYMBOL(parport_ieee1284_ecp_write_addr);
+EXPORT_SYMBOL(parport_ieee1284_write_compat);
+EXPORT_SYMBOL(parport_ieee1284_read_nibble);
+EXPORT_SYMBOL(parport_ieee1284_read_byte);
+EXPORT_SYMBOL(parport_ieee1284_epp_write_data);
+EXPORT_SYMBOL(parport_ieee1284_epp_read_data);
+EXPORT_SYMBOL(parport_ieee1284_epp_write_addr);
+EXPORT_SYMBOL(parport_ieee1284_epp_read_addr);
EXPORT_SYMBOL(parport_proc_register);
EXPORT_SYMBOL(parport_proc_unregister);
-EXPORT_SYMBOL(parport_probe_hook);
+EXPORT_SYMBOL(parport_device_proc_register);
+EXPORT_SYMBOL(parport_device_proc_unregister);
+EXPORT_SYMBOL(parport_default_proc_register);
+EXPORT_SYMBOL(parport_default_proc_unregister);
EXPORT_SYMBOL(parport_parse_irqs);
EXPORT_SYMBOL(parport_parse_dmas);
+#ifdef CONFIG_PARPORT_12843
+EXPORT_SYMBOL(parport_open);
+EXPORT_SYMBOL(parport_close);
+EXPORT_SYMBOL(parport_device_id);
+EXPORT_SYMBOL(parport_device_num);
+EXPORT_SYMBOL(parport_device_coords);
+EXPORT_SYMBOL(parport_daisy_deselect_all);
+EXPORT_SYMBOL(parport_daisy_select);
+EXPORT_SYMBOL(parport_daisy_init);
+#endif
void inc_parport_count(void)
{
* based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
*
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
+ * DMA support - Bert De Jonghe <bert@sophis.be>
* Better EPP probing - Carlos Henrique Bauer <chbauer@acm.org>
*/
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/pci.h>
+#include <linux/sysctl.h>
#include <asm/io.h>
#include <asm/dma.h>
+#include <asm/uaccess.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
than PARPORT_MAX (in <linux/parport.h>). */
#define PARPORT_PC_MAX_PORTS 8
+/* ECR modes */
+#define ECR_SPP 00
+#define ECR_PS2 01
+#define ECR_PPF 02
+#define ECR_ECP 03
+#define ECR_EPP 04
+#define ECR_VND 05
+#define ECR_TST 06
+#define ECR_CNF 07
+
static int user_specified __initdata = 0;
+/* frob_control, but for ECR */
+static void frob_econtrol (struct parport *pb, unsigned char m,
+ unsigned char v)
+{
+ outb ((inb (ECONTROL (pb)) & ~m) ^ v, ECONTROL (pb));
+}
+
+#ifdef CONFIG_PARPORT_1284
+/* Safely change the mode bits in the ECR */
+static int change_mode(struct parport *p, int m)
+{
+ const struct parport_pc_private *priv = p->physport->private_data;
+ int ecr = ECONTROL(p);
+ unsigned char oecr;
+ int mode;
+
+ if (!priv->ecr) {
+ printk (KERN_DEBUG "change_mode: but there's no ECR!\n");
+ return 0;
+ }
+
+ /* Bits <7:5> contain the mode. */
+ oecr = inb (ecr);
+ mode = (oecr >> 5) & 0x7;
+ if (mode == m) return 0;
+ if (mode && m)
+ /* We have to go through mode 000 */
+ change_mode (p, ECR_SPP);
+
+ if (m < 2 && !(parport_read_control (p) & 0x20)) {
+ /* This mode resets the FIFO, so we may
+ * have to wait for it to drain first. */
+ long expire = jiffies + p->physport->cad->timeout;
+ int counter;
+ switch (mode) {
+ case ECR_PPF: /* Parallel Port FIFO mode */
+ case ECR_ECP: /* ECP Parallel Port mode */
+ /* Busy wait for 200us */
+ for (counter = 0; counter < 40; counter++) {
+ if (inb (ECONTROL (p)) & 0x01)
+ break;
+ if (signal_pending (current)) break;
+ udelay (5);
+ }
+
+ /* Poll slowly. */
+ while (!(inb (ECONTROL (p)) & 0x01)) {
+ if (time_after_eq (jiffies, expire))
+ /* The FIFO is stuck. */
+ return -EBUSY;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout ((HZ + 99) / 100);
+ if (signal_pending (current))
+ break;
+ }
+ }
+ }
+
+ /* Set the mode. */
+ oecr &= ~(7 << 5);
+ oecr |= m << 5;
+ outb (oecr, ecr);
+ return 0;
+}
+
+/* Find FIFO lossage; FIFO is reset */
+static int get_fifo_residue (struct parport *p)
+{
+ int residue;
+ int cnfga;
+ const struct parport_pc_private *priv = p->physport->private_data;
+
+ /* Prevent further data transfer. */
+ parport_frob_control (p,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ /* Adjust for the contents of the FIFO. */
+ for (residue = priv->fifo_depth; ; residue--) {
+ if (inb (ECONTROL (p)) & 0x2)
+ /* Full up. */
+ break;
+
+ outb (0, FIFO (p));
+ }
+
+ printk (KERN_DEBUG "%s: %d PWords were left in FIFO\n", p->name,
+ residue);
+
+ /* Reset the FIFO. */
+ frob_econtrol (p, 0xe0, 0x20);
+ parport_frob_control (p, PARPORT_CONTROL_STROBE, 0);
+
+ /* Now change to config mode and clean up. FIXME */
+ frob_econtrol (p, 0xe0, 0xe0);
+ cnfga = inb (CONFIGA (p));
+ printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
+
+ if (!(cnfga & (1<<2))) {
+ printk (KERN_DEBUG "%s: Accounting for extra byte\n", p->name);
+ residue++;
+ }
+
+ /* Don't care about partial PWords until support is added for
+ * PWord != 1 byte. */
+
+ /* Back to PS2 mode. */
+ frob_econtrol (p, 0xe0, 0x20);
+
+ return residue;
+}
+
+#endif /* IEEE 1284 support */
+
/*
* Clear TIMEOUT BIT in EPP MODE
+ *
+ * This is also used in SPP detection.
*/
-int parport_pc_epp_clear_timeout(struct parport *pb)
+static int clear_epp_timeout(struct parport *pb)
{
unsigned char r;
/* To clear timeout some chips require double read */
parport_pc_read_status(pb);
r = parport_pc_read_status(pb);
- parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
- parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+ outb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
+ outb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
r = parport_pc_read_status(pb);
return !(r & 0x01);
}
+/*
+ * Access functions.
+ *
+ * These aren't static because they may be used by the parport_xxx_yyy
+ * macros. extern __inline__ versions of several of these are in
+ * parport_pc.h.
+ */
+
static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
parport_generic_irq(irq, (struct parport *) dev_id, regs);
}
-void parport_pc_write_epp(struct parport *p, unsigned char d)
+void parport_pc_write_data(struct parport *p, unsigned char d)
{
- outb(d, EPPDATA(p));
+ outb (d, DATA (p));
}
-unsigned char parport_pc_read_epp(struct parport *p)
+unsigned char parport_pc_read_data(struct parport *p)
{
- return inb(EPPDATA(p));
+ return inb (DATA (p));
}
-void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
+unsigned char __frob_control (struct parport *p, unsigned char mask,
+ unsigned char val)
{
- outb(d, EPPADDR(p));
+ struct parport_pc_private *priv = p->physport->private_data;
+ unsigned char ctr = priv->ctr;
+ ctr = (ctr & ~mask) ^ val;
+ ctr &= priv->ctr_writable; /* only write writable bits. */
+ outb (ctr, CONTROL (p));
+ return priv->ctr = ctr; /* update soft copy */
}
-unsigned char parport_pc_read_epp_addr(struct parport *p)
+void parport_pc_write_control(struct parport *p, unsigned char d)
{
- return inb(EPPADDR(p));
-}
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ /* Take this out when drivers have adapted to the newer interface. */
+ if (d & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
+ }
-int parport_pc_check_epp_timeout(struct parport *p)
-{
- if (!(inb(STATUS(p)) & 1))
- return 0;
- parport_pc_epp_clear_timeout(p);
- return 1;
+ __frob_control (p, wm, d & wm);
}
-unsigned char parport_pc_read_configb(struct parport *p)
+unsigned char parport_pc_read_control(struct parport *p)
{
- return inb(CONFIGB(p));
+ const struct parport_pc_private *priv = p->physport->private_data;
+ return priv->ctr; /* Use soft copy */
}
-void parport_pc_write_data(struct parport *p, unsigned char d)
+unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask,
+ unsigned char val)
{
- outb(d, DATA(p));
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ /* Take this out when drivers have adapted to the newer interface. */
+ if (mask & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
+ }
+
+ /* Restrict mask and val to control lines. */
+ mask &= wm;
+ val &= wm;
+
+ return __frob_control (p, mask, val);
}
-unsigned char parport_pc_read_data(struct parport *p)
+unsigned char parport_pc_read_status(struct parport *p)
{
- return inb(DATA(p));
+ return inb (STATUS (p));
}
-void parport_pc_write_control(struct parport *p, unsigned char d)
+void parport_pc_disable_irq(struct parport *p)
{
- struct parport_pc_private *priv = p->private_data;
- priv->ctr = d;/* update soft copy */
- outb(d, CONTROL(p));
+ __frob_control (p, 0x10, 0);
}
-unsigned char parport_pc_read_control(struct parport *p)
+void parport_pc_enable_irq(struct parport *p)
{
- struct parport_pc_private *priv = p->private_data;
- return priv->ctr;
+ __frob_control (p, 0x10, 0x10);
}
-unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask, unsigned char val)
+void parport_pc_data_forward (struct parport *p)
{
- struct parport_pc_private *priv = p->private_data;
- unsigned char ctr = priv->ctr;
- ctr = (ctr & ~mask) ^ val;
- outb (ctr, CONTROL(p));
- return priv->ctr = ctr; /* update soft copy */
+ __frob_control (p, 0x20, 0);
}
-void parport_pc_write_status(struct parport *p, unsigned char d)
+void parport_pc_data_reverse (struct parport *p)
{
- outb(d, STATUS(p));
+ __frob_control (p, 0x20, 0x20);
}
-unsigned char parport_pc_read_status(struct parport *p)
+void parport_pc_init_state(struct pardevice *dev, struct parport_state *s)
{
- return inb(STATUS(p));
+ struct parport_pc_private *priv = dev->port->physport->private_data;
+ priv->ctr = s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+ s->u.pc.ecr = 0x24;
}
-void parport_pc_write_econtrol(struct parport *p, unsigned char d)
+void parport_pc_save_state(struct parport *p, struct parport_state *s)
{
- outb(d, ECONTROL(p));
+ const struct parport_pc_private *priv = p->physport->private_data;
+ s->u.pc.ctr = inb (CONTROL (p));
+ if (priv->ecr)
+ s->u.pc.ecr = inb (ECONTROL (p));
}
-unsigned char parport_pc_read_econtrol(struct parport *p)
+void parport_pc_restore_state(struct parport *p, struct parport_state *s)
{
- return inb(ECONTROL(p));
+ const struct parport_pc_private *priv = p->physport->private_data;
+ outb (s->u.pc.ctr, CONTROL (p));
+ if (priv->ecr)
+ outb (s->u.pc.ecr, ECONTROL (p));
}
-unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val)
+#ifdef CONFIG_PARPORT_1284
+static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
+ size_t length, int flags)
{
- unsigned char old = inb(ECONTROL(p));
- outb(((old & ~mask) ^ val), ECONTROL(p));
- return old;
+ size_t got = 0;
+ for (; got < length; got++) {
+ *((char*)buf)++ = inb (EPPDATA(port));
+ if (inb (STATUS(port)) & 0x01) {
+ clear_epp_timeout (port);
+ break;
+ }
+ }
+
+ return got;
}
-void parport_pc_change_mode(struct parport *p, int m)
+static size_t parport_pc_epp_write_data (struct parport *port, const void *buf,
+ size_t length, int flags)
{
- /* FIXME */
+ size_t written = 0;
+ for (; written < length; written++) {
+ outb (*((char*)buf)++, EPPDATA(port));
+ if (inb (STATUS(port)) & 0x01) {
+ clear_epp_timeout (port);
+ break;
+ }
+ }
+
+ return written;
}
-void parport_pc_write_fifo(struct parport *p, unsigned char v)
+static size_t parport_pc_epp_read_addr (struct parport *port, void *buf,
+ size_t length, int flags)
{
- outb (v, CONFIGA(p));
+ size_t got = 0;
+ for (; got < length; got++) {
+ *((char*)buf)++ = inb (EPPADDR (port));
+ if (inb (STATUS (port)) & 0x01) {
+ clear_epp_timeout (port);
+ break;
+ }
+ }
+
+ return got;
}
-unsigned char parport_pc_read_fifo(struct parport *p)
+static size_t parport_pc_epp_write_addr (struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
- return inb (CONFIGA(p));
+ size_t written = 0;
+ for (; written < length; written++) {
+ outb (*((char*)buf)++, EPPADDR (port));
+ if (inb (STATUS (port)) & 0x01) {
+ clear_epp_timeout (port);
+ break;
+ }
+ }
+
+ return written;
}
-void parport_pc_disable_irq(struct parport *p)
+static size_t parport_pc_ecpepp_read_data (struct parport *port, void *buf,
+ size_t length, int flags)
{
- parport_pc_frob_control(p, 0x10, 0);
+ size_t got;
+
+ frob_econtrol (port, 0xe0, ECR_EPP << 5);
+ got = parport_pc_epp_read_data (port, buf, length, flags);
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+ return got;
}
-void parport_pc_enable_irq(struct parport *p)
+static size_t parport_pc_ecpepp_write_data (struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
- parport_pc_frob_control(p, 0x10, 0x10);
+ size_t written;
+
+ frob_econtrol (port, 0xe0, ECR_EPP << 5);
+ written = parport_pc_epp_write_data (port, buf, length, flags);
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+ return written;
}
-void parport_pc_init_state(struct parport_state *s)
+static size_t parport_pc_ecpepp_read_addr (struct parport *port, void *buf,
+ size_t length, int flags)
{
- s->u.pc.ctr = 0xc;
- s->u.pc.ecr = 0x0;
+ size_t got;
+
+ frob_econtrol (port, 0xe0, ECR_EPP << 5);
+ got = parport_pc_epp_read_addr (port, buf, length, flags);
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+ return got;
}
-void parport_pc_save_state(struct parport *p, struct parport_state *s)
+static size_t parport_pc_ecpepp_write_addr (struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
- s->u.pc.ctr = parport_pc_read_control(p);
- if (p->modes & PARPORT_MODE_PCECR)
- s->u.pc.ecr = parport_pc_read_econtrol(p);
+ size_t written;
+
+ frob_econtrol (port, 0xe0, ECR_EPP << 5);
+ written = parport_pc_epp_write_addr (port, buf, length, flags);
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+ return written;
}
+#endif /* IEEE 1284 support */
-void parport_pc_restore_state(struct parport *p, struct parport_state *s)
+#ifdef CONFIG_PARPORT_PC_FIFO
+static size_t parport_pc_fifo_write_block_pio (struct parport *port,
+ const void *buf, size_t length)
{
- parport_pc_write_control(p, s->u.pc.ctr);
- if (p->modes & PARPORT_MODE_PCECR)
- parport_pc_write_econtrol(p, s->u.pc.ecr);
+ int ret = 0;
+ const unsigned char *bufp = buf;
+ size_t left = length;
+ long expire = jiffies + port->physport->cad->timeout;
+ const int fifo = FIFO (port);
+ int poll_for = 8; /* 80 usecs */
+ const struct parport_pc_private *priv = port->physport->private_data;
+ const int fifo_depth = priv->fifo_depth;
+
+ port = port->physport;
+
+ /* We don't want to be interrupted every character. */
+ parport_pc_disable_irq (port);
+ frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
+
+ /* Forward mode. */
+ parport_pc_data_forward (port);
+
+ while (left) {
+ unsigned char byte;
+ unsigned char ecrval = inb (ECONTROL (port));
+ int i = 0;
+
+ if (current->need_resched && time_before (jiffies, expire))
+ /* Can't yield the port. */
+ schedule ();
+
+ /* Anyone else waiting for the port? */
+ if (port->waithead) {
+ printk (KERN_DEBUG "Somebody wants the port\n");
+ break;
+ }
+
+ if (ecrval & 0x02) {
+ /* FIFO is full. Wait for interrupt. */
+
+ /* Clear serviceIntr */
+ outb (ecrval & ~(1<<2), ECONTROL (port));
+ false_alarm:
+ ret = parport_wait_event (port, HZ);
+ if (ret < 0) break;
+ ret = 0;
+ if (!time_before (jiffies, expire)) {
+ /* Timed out. */
+ printk (KERN_DEBUG "Timed out\n");
+ break;
+ }
+ ecrval = inb (ECONTROL (port));
+ if (!(ecrval & (1<<2))) {
+ if (current->need_resched &&
+ time_before (jiffies, expire))
+ schedule ();
+
+ goto false_alarm;
+ }
+
+ continue;
+ }
+
+ /* Can't fail now. */
+ expire = jiffies + port->cad->timeout;
+
+ poll:
+ if (signal_pending (current))
+ break;
+
+ if (ecrval & 0x01) {
+ /* FIFO is empty. Blast it full. */
+ const int n = left < fifo_depth ? left : fifo_depth;
+ outsb (fifo, bufp, n);
+ bufp += n;
+ left -= n;
+
+ /* Adjust the poll time. */
+ if (i < (poll_for - 2)) poll_for--;
+ continue;
+ } else if (i++ < poll_for) {
+ udelay (10);
+ ecrval = inb (ECONTROL (port));
+ goto poll;
+ }
+
+ /* Half-full (call me an optimist) */
+ byte = *bufp++;
+ outb (byte, fifo);
+ left--;
+ }
+
+ return length - left;
}
-size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
+static size_t parport_pc_fifo_write_block_dma (struct parport *port,
+ const void *buf, size_t length)
{
- size_t got = 0;
- for (; got < length; got++) {
- *((char*)buf)++ = inb (EPPDATA(p));
- if (inb (STATUS(p)) & 0x01)
+ int ret = 0;
+ unsigned long dmaflag;
+ size_t left = length;
+ const struct parport_pc_private *priv = port->physport->private_data;
+
+ port = port->physport;
+
+ /* We don't want to be interrupted every character. */
+ parport_pc_disable_irq (port);
+ frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
+
+ /* Forward mode. */
+ parport_pc_data_forward (port);
+
+ while (left) {
+ long expire = jiffies + port->physport->cad->timeout;
+
+ size_t count = left;
+
+ if (count > PAGE_SIZE)
+ count = PAGE_SIZE;
+
+ memcpy(priv->dma_buf, buf, count);
+
+ dmaflag = claim_dma_lock();
+ disable_dma(port->dma);
+ clear_dma_ff(port->dma);
+ set_dma_mode(port->dma, DMA_MODE_WRITE);
+ set_dma_addr(port->dma, virt_to_bus((volatile char *) priv->dma_buf));
+ set_dma_count(port->dma, count);
+
+ /* Set DMA mode */
+ frob_econtrol (port, 1<<3, 1<<3);
+
+ /* Clear serviceIntr */
+ frob_econtrol (port, 1<<2, 0);
+
+ enable_dma(port->dma);
+ release_dma_lock(dmaflag);
+
+ /* assume DMA will be successful */
+ left -= count;
+ buf += count;
+
+ /* Wait for interrupt. */
+ false_alarm:
+ ret = parport_wait_event (port, HZ);
+ if (ret < 0) break;
+ ret = 0;
+ if (!time_before (jiffies, expire)) {
+ /* Timed out. */
+ printk (KERN_DEBUG "Timed out\n");
+ break;
+ }
+ /* Is serviceIntr set? */
+ if (!(inb (ECONTROL (port)) & (1<<2))) {
+ if (current->need_resched)
+ schedule ();
+
+ goto false_alarm;
+ }
+
+ dmaflag = claim_dma_lock();
+ disable_dma(port->dma);
+ clear_dma_ff(port->dma);
+ count = get_dma_residue(port->dma);
+ release_dma_lock(dmaflag);
+
+ if (current->need_resched)
+ /* Can't yield the port. */
+ schedule ();
+
+ /* Anyone else waiting for the port? */
+ if (port->waithead) {
+ printk (KERN_DEBUG "Somebody wants the port\n");
break;
+ }
+
+ /* update for possible DMA residue ! */
+ buf -= count;
+ left += count;
}
- return got;
+
+ /* Maybe got here through break, so adjust for DMA residue! */
+ dmaflag = claim_dma_lock();
+ disable_dma(port->dma);
+ clear_dma_ff(port->dma);
+ left += get_dma_residue(port->dma);
+ release_dma_lock(dmaflag);
+
+ /* Turn off DMA mode */
+ frob_econtrol (port, 1<<3, 0);
+
+ return length - left;
}
-size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length)
+/* Parallel Port FIFO mode (ECP chipsets) */
+size_t parport_pc_compat_write_block_pio (struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
- size_t written = 0;
- for (; written < length; written++) {
- outb (*((char*)buf)++, EPPDATA(p));
- if (inb (STATUS(p)) & 0x01)
- break;
+ size_t written;
+
+ /* Special case: a timeout of zero means we cannot call schedule(). */
+ if (!port->physport->cad->timeout)
+ return parport_ieee1284_write_compat (port, buf,
+ length, flags);
+
+ /* Set up parallel port FIFO mode.*/
+ change_mode (port, ECR_PPF); /* Parallel port FIFO */
+ parport_pc_data_forward (port);
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+ /* Write the data to the FIFO. */
+ if (port->dma != PARPORT_DMA_NONE)
+ written = parport_pc_fifo_write_block_dma (port, buf, length);
+ else
+ written = parport_pc_fifo_write_block_pio (port, buf, length);
+
+ /* Finish up. */
+ if (change_mode (port, ECR_PS2) == -EBUSY) {
+ const struct parport_pc_private *priv =
+ port->physport->private_data;
+
+ printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+
+ /* Prevent further data transfer. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ /* Adjust for the contents of the FIFO. */
+ for (written -= priv->fifo_depth; ; written++) {
+ if (inb (ECONTROL (port)) & 0x2)
+ /* Full up. */
+ break;
+
+ outb (0, FIFO (port));
+ }
+
+ /* Reset the FIFO. */
+ frob_econtrol (port, 0xe0, 0);
+
+ /* De-assert strobe. */
+ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
}
+
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY,
+ PARPORT_STATUS_BUSY);
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
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)
+/* ECP */
+#ifdef CONFIG_PARPORT_1284
+size_t parport_pc_ecp_write_block_pio (struct parport *port,
+ const void *buf, size_t length,
+ int flags)
{
- return -ENOSYS; /* FIXME */
+ size_t written;
+
+ /* Special case: a timeout of zero means we cannot call schedule(). */
+ if (!port->physport->cad->timeout)
+ return parport_ieee1284_ecp_write_data (port, buf,
+ length, flags);
+
+ /* Switch to forward mode if necessary. */
+ if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
+ /* Event 47: Set nInit high. */
+ parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+
+ /* Event 40: PError goes high. */
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_PAPEROUT);
+ }
+
+ /* Set up ECP parallel port mode.*/
+ change_mode (port, ECR_ECP); /* ECP FIFO */
+ parport_pc_data_forward (port);
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+ /* Write the data to the FIFO. */
+ if (port->dma != PARPORT_DMA_NONE)
+ written = parport_pc_fifo_write_block_dma (port, buf, length);
+ else
+ written = parport_pc_fifo_write_block_pio (port, buf, length);
+
+ /* Finish up. */
+ if (change_mode (port, ECR_PS2) == -EBUSY) {
+ const struct parport_pc_private *priv =
+ port->physport->private_data;
+
+ printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+
+ /* Prevent further data transfer. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ /* Adjust for the contents of the FIFO. */
+ for (written -= priv->fifo_depth; ; written++) {
+ if (inb (ECONTROL (port)) & 0x2)
+ /* Full up. */
+ break;
+
+ outb (0, FIFO (port));
+ }
+
+ /* Reset the FIFO. */
+ frob_econtrol (port, 0xe0, 0);
+ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+
+ /* Host transfer recovery. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+ parport_pc_data_reverse (port);
+ parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
+ parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_PAPEROUT);
+ }
+
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_BUSY,
+ PARPORT_STATUS_BUSY);
+ port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+ return written;
}
-int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
+size_t parport_pc_ecp_read_block_pio (struct parport *port,
+ void *buf, size_t length, int flags)
{
- return -ENOSYS; /* FIXME */
+ size_t left = length;
+ size_t fifofull;
+ const int fifo = FIFO(port);
+ const struct parport_pc_private *priv = port->physport->private_data;
+ const int fifo_depth = priv->fifo_depth;
+ char *bufp = buf;
+
+ port = port->physport;
+
+ /* Special case: a timeout of zero means we cannot call schedule(). */
+ if (!port->cad->timeout)
+ return parport_ieee1284_ecp_read_data (port, buf,
+ length, flags);
+
+ fifofull = fifo_depth;
+ if (port->ieee1284.mode == IEEE1284_MODE_ECPRLE)
+ /* If the peripheral is allowed to send RLE compressed
+ * data, it is possible for a byte to expand to 128
+ * bytes in the FIFO. */
+ fifofull = 128;
+
+ /* If the caller wants less than a full FIFO's worth of data,
+ * go through software emulation. Otherwise we may have to through
+ * away data. */
+ if (length < fifofull)
+ return parport_ieee1284_ecp_read_data (port, buf,
+ length, flags);
+
+ /* Switch to reverse mode if necessary. */
+ if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) {
+ /* Event 38: Set nAutoFd low */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ parport_pc_data_reverse (port);
+ udelay (5);
+
+ /* Event 39: Set nInit low to initiate bus reversal */
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+
+ /* Event 40: PError goes low */
+ parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
+ }
+
+ /* Set up ECP parallel port mode.*/
+ change_mode (port, ECR_ECP); /* ECP FIFO */
+ parport_pc_data_reverse (port);
+ port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+
+ /* Do the transfer. */
+ while (left > fifofull) {
+ int ret;
+ long int expire = jiffies + port->cad->timeout;
+ unsigned char ecrval = inb (ECONTROL (port));
+
+ if (current->need_resched && time_before (jiffies, expire))
+ /* Can't yield the port. */
+ schedule ();
+
+ /* At this point, the FIFO may already be full.
+ * Ideally, we'd be able to tell the port to hold on
+ * for a second while we empty the FIFO, and we'd be
+ * able to ensure that no data is lost. I'm not sure
+ * that's the case. :-( It might be that you can play
+ * games with STB, as in the forward case; someone should
+ * look at a datasheet. */
+
+ if (ecrval & 0x01) {
+ /* FIFO is empty. Wait for interrupt. */
+
+ /* Anyone else waiting for the port? */
+ if (port->waithead) {
+ printk (KERN_DEBUG
+ "Somebody wants the port\n");
+ break;
+ }
+
+ /* Clear serviceIntr */
+ outb (ecrval & ~(1<<2), ECONTROL (port));
+ false_alarm:
+ ret = parport_wait_event (port, HZ);
+ if (ret < 0) break;
+ ret = 0;
+ if (!time_before (jiffies, expire)) {
+ /* Timed out. */
+ printk (KERN_DEBUG "Timed out\n");
+ break;
+ }
+ ecrval = inb (ECONTROL (port));
+ if (!(ecrval & (1<<2))) {
+ if (current->need_resched &&
+ time_before (jiffies, expire))
+ schedule ();
+
+ goto false_alarm;
+ }
+
+ continue;
+ }
+
+ if (ecrval & 0x02) {
+ /* FIFO is full. */
+ insb (fifo, bufp, fifo_depth);
+ bufp += fifo_depth;
+ left -= fifo_depth;
+ continue;
+ }
+
+ *bufp++ = inb (fifo);
+ left--;
+ }
+
+ /* Finish up. */
+ if (change_mode (port, ECR_PS2) == -EBUSY) {
+ int lost = get_fifo_residue (port);
+ printk (KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n", port->name,
+ lost);
+ }
+
+ port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+
+ return length - left;
}
+#endif /* IEEE 1284 support */
+
+#endif /* Allowed to use FIFO/DMA */
+
void parport_pc_inc_use_count(void)
{
#ifdef MODULE
static void parport_pc_fill_inode(struct inode *inode, int fill)
{
+ /* Is this still needed? -tim */
#ifdef MODULE
if (fill)
MOD_INC_USE_COUNT;
parport_pc_read_control,
parport_pc_frob_control,
- parport_pc_write_econtrol,
- parport_pc_read_econtrol,
- parport_pc_frob_econtrol,
-
- parport_pc_write_status,
parport_pc_read_status,
- parport_pc_write_fifo,
- parport_pc_read_fifo,
-
- parport_pc_change_mode,
-
- parport_pc_write_epp,
- parport_pc_read_epp,
- parport_pc_write_epp_addr,
- parport_pc_read_epp_addr,
- parport_pc_check_epp_timeout,
+ parport_pc_enable_irq,
+ parport_pc_disable_irq,
- parport_pc_epp_write_block,
- parport_pc_epp_read_block,
+ parport_pc_data_forward,
+ parport_pc_data_reverse,
- parport_pc_ecp_write_block,
- parport_pc_ecp_read_block,
-
+ parport_pc_interrupt,
parport_pc_init_state,
parport_pc_save_state,
parport_pc_restore_state,
- parport_pc_enable_irq,
- parport_pc_disable_irq,
- parport_pc_interrupt,
-
parport_pc_inc_use_count,
parport_pc_dec_use_count,
- parport_pc_fill_inode
+ parport_pc_fill_inode,
+
+ parport_ieee1284_epp_write_data,
+ parport_ieee1284_epp_read_data,
+ parport_ieee1284_epp_write_addr,
+ parport_ieee1284_epp_read_addr,
+
+ parport_ieee1284_ecp_write_data,
+ parport_ieee1284_ecp_read_data,
+ parport_ieee1284_ecp_write_addr,
+
+ parport_ieee1284_write_compat,
+ parport_ieee1284_read_nibble,
+ parport_ieee1284_read_byte,
};
/* --- Mode detection ------------------------------------- */
-
/*
* Checks for port existence, all ports support SPP MODE
*/
* that does not even respond to SPP cycles if an EPP
* timeout is pending
*/
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
/* Do a simple read-write test to make sure the port exists. */
w = 0xc;
- parport_pc_write_control(pb, w);
+ outb (w, CONTROL (pb));
- /* Can we read from the control register? Some ports don't
- * allow reads, so read_control just returns a software
- * copy. Some ports _do_ allow reads, so bypass the software
- * copy here. In addition, some bits aren't writable. */
+ /* Is there a control register that we can read from? Some
+ * ports don't allow reads, so read_control just returns a
+ * software copy. Some ports _do_ allow reads, so bypass the
+ * software copy here. In addition, some bits aren't
+ * writable. */
r = inb (CONTROL (pb));
if ((r & 0xf) == w) {
w = 0xe;
- parport_pc_write_control (pb, w);
- r = inb (CONTROL(pb));
- parport_pc_write_control (pb, 0xc);
+ outb (w, CONTROL (pb));
+ r = inb (CONTROL (pb));
+ outb (0xc, CONTROL (pb));
if ((r & 0xf) == w)
return PARPORT_MODE_PCSPP;
}
}
if (user_specified)
- /* Didn't work with 0xaa, but the user is convinced
- * this is the place. */
+ /* Didn't work, but the user is convinced this is the
+ * place. */
printk (KERN_DEBUG "0x%lx: DATA: wrote 0x%02x, read 0x%02x\n",
pb->base, w, r);
/* It's possible that we can't read the control register or
- the data register. In that case just believe the user. */
+ * the data register. In that case just believe the user. */
if (user_specified)
return PARPORT_MODE_PCSPP;
return 0;
}
-/* Check for ECP
+/* Check for ECR
*
* Old style XT ports alias io ports every 0x400, hence accessing ECR
* on these cards actually accesses the CTR.
* regardless of what is written here if the card does NOT support
* ECP.
*
- * We will write 0x2c to ECR and 0xcc to CTR since both of these
- * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ * We first check to see if ECR is the same as CTR. If not, the low
+ * two bits of ECR aren't writable, so we check by writing ECR and
+ * reading it back to see if it's what we expect.
*/
static int __init parport_ECR_present(struct parport *pb)
{
- unsigned char r;
+ struct parport_pc_private *priv = pb->private_data;
+ unsigned char r = 0xc;
- parport_pc_write_control (pb, 0xc);
- r = parport_pc_read_control(pb);
- if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) {
- parport_pc_write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
+ priv->ecr = 0;
+ outb (r, CONTROL (pb));
+ if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) {
+ outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */
- r = parport_pc_read_control(pb);
- if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2))
+ r = inb (CONTROL (pb));
+ if ((inb (ECONTROL (pb)) & 0x2) == (r & 0x2))
goto no_reg; /* Sure that no ECR register exists */
}
- if ((parport_pc_read_econtrol(pb) & 0x3 ) != 0x1)
+ if ((inb (ECONTROL (pb)) & 0x3 ) != 0x1)
goto no_reg;
- parport_pc_write_econtrol(pb, 0x34);
- if (parport_pc_read_econtrol(pb) != 0x35)
+ outb (0x34, ECONTROL (pb));
+ if (inb (ECONTROL (pb)) != 0x35)
goto no_reg;
- parport_pc_write_control(pb, 0xc);
-
- /* Go to mode 000; SPP, reset FIFO */
- parport_pc_frob_econtrol (pb, 0xe0, 0x00);
+ priv->ecr = 1;
+ outb (0xc, CONTROL (pb));
- return PARPORT_MODE_PCECR;
+ /* Go to mode 000 */
+ frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+
+ return 1;
no_reg:
- parport_pc_write_control (pb, 0xc);
- return 0;
+ outb (0xc, CONTROL (pb));
+ return 0;
+}
+
+#ifdef CONFIG_PARPORT_1284
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines. In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero. So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present.
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal. Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic.
+ */
+
+static int __init parport_PS2_supported(struct parport *pb)
+{
+ int ok = 0;
+
+ clear_epp_timeout(pb);
+
+ /* try to tri-state the buffer */
+ parport_pc_data_reverse (pb);
+
+ parport_pc_write_data(pb, 0x55);
+ if (parport_pc_read_data(pb) != 0x55) ok++;
+
+ parport_pc_write_data(pb, 0xaa);
+ if (parport_pc_read_data(pb) != 0xaa) ok++;
+
+ /* cancel input mode */
+ parport_pc_data_forward (pb);
+
+ if (ok)
+ pb->modes |= PARPORT_MODE_TRISTATE;
+ else {
+ struct parport_pc_private *priv = pb->private_data;
+ priv->ctr_writable &= ~0x20;
+ }
+
+ return ok;
}
static int __init parport_ECP_supported(struct parport *pb)
{
int i;
- unsigned char oecr;
-
+ int config;
+ int pword;
+ struct parport_pc_private *priv = pb->private_data;
+
/* If there is no ECR, we have no hope of supporting ECP. */
- if (!(pb->modes & PARPORT_MODE_PCECR))
+ if (!priv->ecr)
return 0;
- oecr = parport_pc_read_econtrol(pb);
+ /* Find out FIFO depth */
+ outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+ outb (ECR_TST << 5, ECONTROL (pb)); /* TEST FIFO */
+ for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
+ outb (0xaa, FIFO (pb));
+
/*
* Using LGS chipset it uses ECR register, but
* it doesn't support ECP or FIFO MODE
*/
+ if (i == 1024) {
+ outb (ECR_SPP << 5, ECONTROL (pb));
+ return 0;
+ }
+
+ priv->fifo_depth = i;
+ printk (KERN_INFO "0x%lx: FIFO is %d bytes\n", pb->base, i);
+
+ /* Find out writeIntrThreshold */
+ frob_econtrol (pb, 1<<2, 1<<2);
+ frob_econtrol (pb, 1<<2, 0);
+ for (i = 1; i <= priv->fifo_depth; i++) {
+ inb (FIFO (pb));
+ udelay (50);
+ if (inb (ECONTROL (pb)) & (1<<2))
+ break;
+ }
+
+ if (i <= priv->fifo_depth)
+ printk (KERN_INFO "0x%lx: writeIntrThreshold is %d\n",
+ pb->base, i);
+ else
+ /* Number of bytes we know we can write if we get an
+ interrupt. */
+ i = 0;
+
+ priv->writeIntrThreshold = i;
+
+ /* Find out readIntrThreshold */
+ frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO */
+ parport_pc_data_reverse (pb);
+ frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */
+ frob_econtrol (pb, 1<<2, 1<<2);
+ frob_econtrol (pb, 1<<2, 0);
+ for (i = 1; i <= priv->fifo_depth; i++) {
+ outb (0xaa, FIFO (pb));
+ if (inb (ECONTROL (pb)) & (1<<2))
+ break;
+ }
+
+ if (i <= priv->fifo_depth)
+ printk (KERN_INFO "0x%lx: readIntrThreshold is %d\n",
+ pb->base, i);
+ else
+ /* Number of bytes we can read if we get an interrupt. */
+ i = 0;
+
+ priv->readIntrThreshold = i;
+
+ outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+ outb (0xf4, ECONTROL (pb)); /* Configuration mode */
+ config = inb (FIFO (pb));
+ pword = (config >> 4) & 0x7;
+ switch (pword) {
+ case 0:
+ pword = 2;
+ printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+ pb->base);
+ break;
+ case 2:
+ pword = 4;
+ printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+ pb->base);
+ break;
+ default:
+ printk (KERN_WARNING "0x%lx: Unknown implementation ID\n",
+ pb->base);
+ /* Assume 1 */
+ case 1:
+ pword = 1;
+ }
+ priv->pword = pword;
+ printk (KERN_DEBUG "0x%lx: PWord is %d bits\n", pb->base, 8 * pword);
+
+ config = inb (CONFIGB (pb));
+ printk (KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
+ config & 0x80 ? "Level" : "Pulses");
+
+ if (!(config & 0x40)) {
+ printk (KERN_WARNING "0x%lx: IRQ conflict!\n", pb->base);
+ pb->irq = PARPORT_IRQ_NONE;
+ }
+
+ /* Go back to mode 000 */
+ frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+ pb->modes |= PARPORT_MODE_ECP;
+
+ return 1;
+}
+
+static int __init parport_ECPPS2_supported(struct parport *pb)
+{
+ const struct parport_pc_private *priv = pb->private_data;
+ int result;
+ unsigned char oecr;
+
+ if (!priv->ecr)
+ return 0;
+
+ oecr = inb (ECONTROL (pb));
+ outb (ECR_PS2 << 5, ECONTROL (pb));
- parport_pc_write_econtrol(pb, 0xc0); /* TEST FIFO */
- for (i=0; i < 1024 && (parport_pc_read_econtrol(pb) & 0x01); i++)
- parport_pc_write_fifo(pb, 0xaa);
+ result = parport_PS2_supported(pb);
- parport_pc_write_econtrol(pb, oecr);
- return (i==1024)?0:PARPORT_MODE_PCECP;
+ outb (oecr, ECONTROL (pb));
+ return result;
}
-/* EPP mode detection
- * Theory:
- * Bit 0 of STR is the EPP timeout bit, this bit is 0
- * when EPP is possible and is set high when an EPP timeout
- * occurs (EPP uses the HALT line to stop the CPU while it does
- * the byte transfer, an EPP timeout occurs if the attached
- * device fails to respond after 10 micro seconds).
- *
- * This bit is cleared by either reading it (National Semi)
- * or writing a 1 to the bit (SMC, UMC, WinBond), others ???
- * This bit is always high in non EPP modes.
- */
+/* EPP mode detection */
+
static int __init parport_EPP_supported(struct parport *pb)
{
/* If EPP timeout bit clear then EPP available */
- if (!parport_pc_epp_clear_timeout(pb))
+ if (!clear_epp_timeout(pb))
return 0; /* No way to clear timeout */
/*
* This bit is always high in non EPP modes.
*/
- parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x20);
- parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x10);
- parport_pc_epp_clear_timeout(pb);
+ parport_pc_data_reverse (pb);
+ parport_pc_enable_irq (pb);
+ clear_epp_timeout(pb);
- parport_pc_read_epp(pb);
+ inb (EPPDATA (pb));
udelay(30); /* Wait for possible EPP timeout */
- if (parport_pc_read_status(pb) & 0x01) {
- parport_pc_epp_clear_timeout(pb);
- return PARPORT_MODE_PCEPP;
- }
+ if (parport_pc_read_status(pb) & 0x01)
+ goto supported;
/*
* Theory:
* in others (EPP 1.7, ECPEPP?) it is possible to read back
* its value.
*/
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
udelay(30); /* Wait for possible EPP timeout */
- /* Previous test left outputs disabled. */
+ /* Enable outputs. */
+ parport_pc_data_forward (pb);
outb (0x55, EPPADDR (pb));
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
udelay(30); /* Wait for possible EPP timeout */
/* We must enable the outputs to be able to read the address
register. */
- parport_pc_frob_control (pb, 0x20, 0x00);
+ parport_pc_data_forward (pb);
if (inb (EPPADDR (pb)) == 0x55) {
-
- /* wash ... */
- parport_pc_frob_control (pb, 0x20, 0x20);
- outb (0xaa, EPPADDR (pb));
-
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
udelay(30); /* Wait for possible EPP timeout */
+ outb (0xaa, EPPADDR (pb));
- /* ... and repeat */
- parport_pc_frob_control (pb, 0x20, 0x00);
-
- if (inb (EPPADDR (pb)) == 0xaa) {
- parport_pc_epp_clear_timeout (pb);
- return PARPORT_MODE_PCEPP;
- }
+ if (inb (EPPADDR (pb)) == 0xaa)
+ goto supported;
}
return 0;
+
+ supported:
+ clear_epp_timeout(pb);
+ pb->modes |= PARPORT_MODE_EPP;
+
+ /* Set up access functions to use EPP hardware. */
+ parport_pc_ops.epp_read_data = parport_pc_epp_read_data;
+ parport_pc_ops.epp_write_data = parport_pc_epp_write_data;
+ parport_pc_ops.epp_read_addr = parport_pc_epp_read_addr;
+ parport_pc_ops.epp_write_addr = parport_pc_epp_write_addr;
+
+ return 1;
}
static int __init parport_ECPEPP_supported(struct parport *pb)
{
- int mode;
+ struct parport_pc_private *priv = pb->private_data;
+ int result;
unsigned char oecr;
- if (!(pb->modes & PARPORT_MODE_PCECR))
+ if (!priv->ecr)
return 0;
- oecr = parport_pc_read_econtrol(pb);
+ oecr = inb (ECONTROL (pb));
/* Search for SMC style EPP+ECP mode */
- parport_pc_write_econtrol(pb, 0x80);
-
- mode = parport_EPP_supported(pb);
-
- parport_pc_write_econtrol(pb, oecr);
+ outb (0x80, ECONTROL (pb));
- return mode?PARPORT_MODE_PCECPEPP:0;
-}
-
-/* Detect PS/2 support.
- *
- * Bit 5 (0x20) sets the PS/2 data direction; setting this high
- * allows us to read data from the data lines. In theory we would get back
- * 0xff but any peripheral attached to the port may drag some or all of the
- * lines down to zero. So if we get back anything that isn't the contents
- * of the data register we deem PS/2 support to be present.
- *
- * Some SPP ports have "half PS/2" ability - you can't turn off the line
- * drivers, but an external peripheral with sufficiently beefy drivers of
- * its own can overpower them and assert its own levels onto the bus, from
- * where they can then be read back as normal. Ports with this property
- * and the right type of device attached are likely to fail the SPP test,
- * (as they will appear to have stuck bits) and so the fact that they might
- * be misdetected here is rather academic.
- */
+ result = parport_EPP_supported(pb);
-static int __init parport_PS2_supported(struct parport *pb)
-{
- int ok = 0;
- unsigned char octr = parport_pc_read_control(pb);
-
- parport_pc_epp_clear_timeout(pb);
+ outb (oecr, ECONTROL (pb));
- parport_pc_write_control(pb, octr | 0x20); /* try to tri-state the buffer */
-
- parport_pc_write_data(pb, 0x55);
- if (parport_pc_read_data(pb) != 0x55) ok++;
-
- parport_pc_write_data(pb, 0xaa);
- if (parport_pc_read_data(pb) != 0xaa) ok++;
-
- parport_pc_write_control(pb, octr); /* cancel input mode */
+ if (result) {
+ /* Set up access functions to use ECP+EPP hardware. */
+ parport_pc_ops.epp_read_data = parport_pc_ecpepp_read_data;
+ parport_pc_ops.epp_write_data = parport_pc_ecpepp_write_data;
+ parport_pc_ops.epp_read_addr = parport_pc_ecpepp_read_addr;
+ parport_pc_ops.epp_write_addr = parport_pc_ecpepp_write_addr;
+ }
- return ok?PARPORT_MODE_PCPS2:0;
+ return result;
}
-static int __init parport_ECPPS2_supported(struct parport *pb)
-{
- int mode;
- unsigned char oecr;
-
- if (!(pb->modes & PARPORT_MODE_PCECR))
- return 0;
+#else /* No IEEE 1284 support */
- oecr = parport_pc_read_econtrol(pb);
- parport_pc_write_econtrol(pb, 0x20);
-
- mode = parport_PS2_supported(pb);
+/* Don't bother probing for modes we know we won't use. */
+static int __init parport_PS2_supported(struct parport *pb) { return 0; }
+static int __init parport_ECP_supported(struct parport *pb) { return 0; }
+static int __init parport_EPP_supported(struct parport *pb) { return 0; }
+static int __init parport_ECPEPP_supported(struct parport *pb) { return 0; }
+static int __init parport_ECPPS2_supported(struct parport *pb) { return 0; }
- parport_pc_write_econtrol(pb, oecr);
- return mode?PARPORT_MODE_PCECPPS2:0;
-}
+#endif /* No IEEE 1284 support */
/* --- IRQ detection -------------------------------------- */
static int __init programmable_irq_support(struct parport *pb)
{
int irq, intrLine;
- unsigned char oecr = parport_pc_read_econtrol(pb);
+ unsigned char oecr = inb (ECONTROL (pb));
static const int lookup[8] = {
PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
};
- parport_pc_write_econtrol(pb,0xE0); /* Configuration MODE */
-
- intrLine = (parport_pc_read_configb(pb) >> 3) & 0x07;
+ outb (ECR_CNF << 5, ECONTROL (pb)); /* Configuration MODE */
+
+ intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
irq = lookup[intrLine];
- parport_pc_write_econtrol(pb, oecr);
+ outb (oecr, ECONTROL (pb));
return irq;
}
sti();
irqs = probe_irq_on();
- parport_pc_write_econtrol(pb, 0x00); /* Reset FIFO */
- parport_pc_write_econtrol(pb, 0xd0); /* TEST FIFO + nErrIntrEn */
+ outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+ outb ((ECR_TST << 5) | 0x04, ECONTROL (pb));
+ outb (ECR_TST << 5, ECONTROL (pb));
- /* If Full FIFO sure that WriteIntrThresold is generated */
- for (i=0; i < 1024 && !(parport_pc_read_econtrol(pb) & 0x02) ; i++)
- parport_pc_write_fifo(pb, 0xaa);
+ /* If Full FIFO sure that writeIntrThreshold is generated */
+ for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02) ; i++)
+ outb (0xaa, FIFO (pb));
pb->irq = probe_irq_off(irqs);
- parport_pc_write_econtrol(pb, 0x00);
+ outb (ECR_SPP << 5, ECONTROL (pb));
if (pb->irq <= 0)
pb->irq = PARPORT_IRQ_NONE;
return PARPORT_IRQ_NONE;
#else
int irqs;
- unsigned char octr = parport_pc_read_control(pb);
unsigned char oecr;
if (pb->modes & PARPORT_MODE_PCECR)
- oecr = parport_pc_read_econtrol(pb);
+ oecr = inb (ECONTROL (pb));
sti();
irqs = probe_irq_on();
if (pb->modes & PARPORT_MODE_PCECR)
- parport_pc_frob_econtrol (pb, 0x10, 0x10);
+ frob_econtrol (pb, 0x10, 0x10);
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
parport_pc_frob_control (pb, 0x20, 0x20);
parport_pc_frob_control (pb, 0x10, 0x10);
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
/* Device isn't expecting an EPP read
* and generates an IRQ.
pb->irq = probe_irq_off (irqs);
if (pb->modes & PARPORT_MODE_PCECR)
- parport_pc_write_econtrol(pb, oecr);
- parport_pc_write_control(pb, octr);
+ outb (oecr, ECONTROL (pb));
+ parport_pc_write_control(pb, 0xc);
if (pb->irq <= 0)
pb->irq = PARPORT_IRQ_NONE;
return pb->irq;
-#endif /* Advanced detection. */
+#endif /* Advanced detection */
}
static int __init irq_probe_SPP(struct parport *pb)
{
-#ifndef ADVANCED_DETECT
/* Don't even try to do this. */
return PARPORT_IRQ_NONE;
-#else
- int irqs;
- unsigned char octr = parport_pc_read_control(pb);
- unsigned char oecr;
-
- if (pb->modes & PARPORT_MODE_PCECR)
- oecr = parport_pc_read_econtrol(pb);
- probe_irq_off(probe_irq_on()); /* Clear any interrupts */
- irqs = probe_irq_on();
-
- if (pb->modes & PARPORT_MODE_PCECR)
- parport_pc_write_econtrol(pb, 0x10);
-
- parport_pc_write_data(pb,0x00);
- parport_pc_write_control(pb,0x00);
- parport_pc_write_control(pb,0x0c);
- udelay(5);
- parport_pc_write_control(pb,0x0d);
- udelay(5);
- parport_pc_write_control(pb,0x0c);
- udelay(25);
- parport_pc_write_control(pb,0x08);
- udelay(25);
- parport_pc_write_control(pb,0x0c);
- udelay(50);
-
- pb->irq = probe_irq_off(irqs);
- if (pb->irq <= 0)
- pb->irq = PARPORT_IRQ_NONE; /* No interrupt detected */
-
- if (pb->modes & PARPORT_MODE_PCECR)
- parport_pc_write_econtrol(pb, oecr);
- parport_pc_write_control(pb, octr);
- return pb->irq;
-#endif /* Advanced detection. */
}
/* We will attempt to share interrupt requests since other devices
*/
static int __init parport_irq_probe(struct parport *pb)
{
- if (pb->modes & PARPORT_MODE_PCECR) {
+ const struct parport_pc_private *priv = pb->private_data;
+
+ if (priv->ecr) {
pb->irq = programmable_irq_support(pb);
if (pb->irq != PARPORT_IRQ_NONE)
goto out;
}
- if (pb->modes & PARPORT_MODE_PCECP)
+ if (pb->modes & PARPORT_MODE_ECP)
pb->irq = irq_probe_ECP(pb);
- if (pb->irq == PARPORT_IRQ_NONE &&
- (pb->modes & PARPORT_MODE_PCECPEPP))
+ if (pb->irq == PARPORT_IRQ_NONE && priv->ecr &&
+ (pb->modes & PARPORT_MODE_EPP))
pb->irq = irq_probe_EPP(pb);
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
- if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_PCEPP))
+ if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
pb->irq = irq_probe_EPP(pb);
- parport_pc_epp_clear_timeout(pb);
+ clear_epp_timeout(pb);
if (pb->irq == PARPORT_IRQ_NONE)
pb->irq = irq_probe_SPP(pb);
return pb->irq;
}
+/* --- DMA detection -------------------------------------- */
+
+/* Only if supports ECP mode */
+static int __init programmable_dma_support (struct parport *p)
+{
+ unsigned char oecr = inb (ECONTROL (p));
+ int dma;
+
+ frob_econtrol (p, 0xe0, ECR_CNF << 5);
+
+ dma = inb (CONFIGB(p)) & 0x03;
+ if (!dma)
+ dma = PARPORT_DMA_NONE;
+
+ outb (oecr, ECONTROL (p));
+ return dma;
+}
+
+static int __init parport_dma_probe (struct parport *p)
+{
+ const struct parport_pc_private *priv = p->private_data;
+ if (priv->ecr)
+ p->dma = programmable_dma_support(p);
+
+ return p->dma;
+}
+
/* --- Initialisation code -------------------------------- */
static int __init probe_one_port(unsigned long int base,
return 0;
}
priv->ctr = 0xc;
+ priv->ctr_writable = 0xff;
+ priv->ecr = 0;
+ priv->fifo_depth = 0;
+ priv->dma_buf = 0;
p->base = base;
p->base_hi = base_hi;
p->irq = irq;
p->modes = PARPORT_MODE_PCSPP;
p->ops = &parport_pc_ops;
p->private_data = priv;
- if (base_hi && !check_region (base_hi, 3)) {
- p->modes |= parport_ECR_present (p);
- p->modes |= parport_ECP_supported (p);
- p->modes |= parport_ECPPS2_supported (p);
+ p->physport = p;
+ if (base_hi && !check_region(base_hi,3)) {
+ parport_ECR_present(p);
+ parport_ECP_supported(p);
+ parport_ECPPS2_supported(p);
}
- if (p->base != 0x3bc) {
+ if (base != 0x3bc) {
if (!check_region(base+0x3, 5)) {
- p->modes |= parport_EPP_supported (p);
- p->modes |= parport_ECPEPP_supported (p);
+ parport_ECPEPP_supported(p);
+ parport_EPP_supported(p);
}
}
- if (!parport_SPP_supported(p)) {
+ if (!parport_SPP_supported (p)) {
/* No port. */
kfree (priv);
return 0;
}
- p->modes |= parport_PS2_supported(p);
+ parport_PS2_supported (p);
- if (!(p = parport_register_port (base, PARPORT_IRQ_NONE,
- PARPORT_DMA_NONE, &parport_pc_ops))) {
+ if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
+ PARPORT_DMA_NONE, &parport_pc_ops))) {
kfree (priv);
return 0;
}
p->base_hi = base_hi;
p->modes = tmp.modes;
- p->size = (p->modes & PARPORT_MODE_PCEPP) ? 8 : 3;
+ p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
p->private_data = priv;
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
- if (p->base_hi && (p->modes & PARPORT_MODE_PCECR))
- printk (" (0x%lx)", p->base_hi);
+ if (p->base_hi && (p->modes & PARPORT_MODE_ECP))
+ printk(" (0x%lx)", p->base_hi);
p->irq = irq;
p->dma = dma;
if (p->irq == PARPORT_IRQ_AUTO) {
probedirq = p->irq;
p->irq = PARPORT_IRQ_NONE;
}
- if (p->irq != PARPORT_IRQ_NONE)
+ if (p->irq != PARPORT_IRQ_NONE) {
printk(", irq %d", p->irq);
+
+ if (p->dma == PARPORT_DMA_AUTO) {
+ p->dma = PARPORT_DMA_NONE;
+ parport_dma_probe(p);
+ }
+ }
if (p->dma == PARPORT_DMA_AUTO)
p->dma = PARPORT_DMA_NONE;
- if (p->dma != PARPORT_DMA_NONE)
+ if (p->dma != PARPORT_DMA_NONE)
printk(", dma %d", p->dma);
+
+#ifdef CONFIG_PARPORT_PC_FIFO
+ if (priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
+ parport_pc_ops.compat_write_data =
+ parport_pc_compat_write_block_pio;
+#ifdef CONFIG_PARPORT_1284
+ parport_pc_ops.ecp_write_data =
+ parport_pc_ecp_write_block_pio;
+#endif /* IEEE 1284 support */
+ if (p->dma != PARPORT_DMA_NONE)
+ p->modes |= PARPORT_MODE_DMA;
+ printk(", using FIFO");
+ }
+#endif /* Allowed to use FIFO/DMA */
+
printk(" [");
-#define printmode(x) {if(p->modes&PARPORT_MODE_PC##x){printk("%s%s",f?",":"",#x);f++;}}
+#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
{
int f = 0;
- printmode(SPP);
- printmode(PS2);
+ printmode(PCSPP);
+ printmode(TRISTATE);
+ printmode(COMPAT)
printmode(EPP);
printmode(ECP);
- printmode(ECPEPP);
- printmode(ECPPS2);
+ printmode(DMA);
}
#undef printmode
printk("]\n");
-#ifdef CONFIG_PROC_FS
if (probedirq != PARPORT_IRQ_NONE)
- printk("%s: detected irq %d; use procfs to enable interrupt-driven operation.\n", p->name, probedirq);
-#endif
+ printk("%s: irq %d detected\n", p->name, probedirq);
parport_proc_register(p);
request_region (p->base, p->size, p->name);
- if (p->modes & PARPORT_MODE_PCECR)
+ if (p->modes & PARPORT_MODE_ECP)
request_region (p->base_hi, 3, p->name);
if (p->irq != PARPORT_IRQ_NONE) {
"resorting to PIO operation\n",
p->name, p->dma);
p->dma = PARPORT_DMA_NONE;
+ } else {
+ priv->dma_buf = (char *) __get_dma_pages(GFP_KERNEL, 1);
+ if (! priv->dma_buf) {
+ printk (KERN_WARNING "%s: "
+ "cannot get buffer for DMA, "
+ "resorting to PIO operation\n",
+ p->name);
+ free_dma(p->dma);
+ p->dma = PARPORT_DMA_NONE;
+ }
}
}
}
- /* Done probing. Now put the port into a sensible start-up state. */
- if (p->modes & PARPORT_MODE_PCECR)
+ /* Done probing. Now put the port into a sensible start-up state.
+ * SELECT | INIT also puts IEEE1284-compliant devices into
+ * compatibility mode. */
+ if (p->modes & PARPORT_MODE_ECP)
/*
* Put the ECP detected port in PS2 mode.
*/
- parport_pc_write_econtrol(p, 0x24);
+ outb (0x24, ECONTROL (p));
+
parport_pc_write_data(p, 0);
- parport_pc_write_control(p, 0x8);
+ parport_pc_write_control(p, PARPORT_CONTROL_SELECT);
udelay (50);
- parport_pc_write_control(p, 0xc);
+ parport_pc_write_control(p,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_INIT);
udelay (50);
- if (parport_probe_hook)
- (*parport_probe_hook)(p);
-
/* Now that we've told the sharing engine about the port, and
found out its characteristics, let the high-level drivers
know about it. */
#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000
#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8001 /* The Lava Dual Parallel is */
#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8002 /* two PCI devices on a card */
-#endif /* IDs not defined */
+#endif
- int count = 0;
-#ifdef CONFIG_PCI
- int i;
struct {
unsigned int vendor;
unsigned int device;
{ 0, }
};
+ int count = 0;
+ int i;
+
if (!pci_present ())
return 0;
}
}
}
-#endif /* CONFIG_PCI */
return count;
}
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-
-MODULE_PARM_DESC(io, "base address");
MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
-
-MODULE_PARM_DESC(io_hi, "base address for ECR");
MODULE_PARM(io_hi, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
-
-MODULE_PARM_DESC(irq, "irq line to use (or 'auto' or 'none')");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-
-MODULE_PARM_DESC(dma, "dma channel to use (or 'auto' or 'none')");
MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
int init_module(void)
struct parport *p = parport_enumerate(), *tmp;
while (p) {
tmp = p->next;
- if (p->modes & PARPORT_MODE_PCSPP) {
+ if (p->modes & PARPORT_MODE_PCSPP) {
+ struct parport_pc_private *priv = p->private_data;
if (p->dma != PARPORT_DMA_NONE)
- free_dma (p->dma);
+ free_dma(p->dma);
if (p->irq != PARPORT_IRQ_NONE)
- free_irq (p->irq, p);
- release_region (p->base, p->size);
- if (p->modes & PARPORT_MODE_PCECP)
- release_region (p->base_hi, 3);
+ free_irq(p->irq, p);
+ release_region(p->base, p->size);
+ if (p->modes & PARPORT_MODE_ECP)
+ release_region(p->base_hi, 3);
parport_proc_unregister(p);
+ if (priv->dma_buf)
+ free_page((unsigned long) priv->dma_buf);
kfree (p->private_data);
parport_unregister_port(p);
}
--- /dev/null
+/* $Id: parport_probe.c,v 1.3 1997/10/19 18:18:46 phil Exp $
+ * Parallel port device probing code
+ *
+ * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/parport.h>
+#include <linux/ctype.h>
+#include <asm/uaccess.h>
+
+static struct {
+ char *token;
+ char *descr;
+} classes[] = {
+ { "", "Legacy device" },
+ { "PRINTER", "Printer" },
+ { "MODEM", "Modem" },
+ { "NET", "Network device" },
+ { "HDC", "Hard disk" },
+ { "PCMCIA", "PCMCIA" },
+ { "MEDIA", "Multimedia device" },
+ { "FDC", "Floppy disk" },
+ { "PORTS", "Ports" },
+ { "SCANNER", "Scanner" },
+ { "DIGICAM", "Digital camera" },
+ { "", "Unknown device" },
+ { "", "Unspecified" },
+ { "SCSIADAPTER", "SCSI adapter" },
+ { NULL, NULL }
+};
+
+static void pretty_print(struct parport *port, int device)
+{
+ struct parport_device_info *info = &port->probe_info[device + 1];
+
+ printk(KERN_INFO "%s", port->name);
+
+ if (device >= 0)
+ printk (" (addr %d)", device);
+
+ printk (": %s", classes[info->class].descr);
+ if (info->class)
+ printk(", %s %s", info->mfr, info->model);
+
+ printk("\n");
+}
+
+static char *strdup(char *str)
+{
+ int n = strlen(str)+1;
+ char *s = kmalloc(n, GFP_KERNEL);
+ if (!s) return NULL;
+ return strcpy(s, str);
+}
+
+static void parse_data(struct parport *port, int device, char *str)
+{
+ char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+ char *p = txt, *q;
+ int guessed_class = PARPORT_CLASS_UNSPEC;
+ struct parport_device_info *info = &port->probe_info[device + 1];
+
+ if (!txt) {
+ printk("%s probe: memory squeeze\n", port->name);
+ return;
+ }
+ strcpy(txt, str);
+ while (p) {
+ char *sep;
+ q = strchr(p, ';');
+ if (q) *q = 0;
+ sep = strchr(p, ':');
+ if (sep) {
+ char *u = p;
+ *(sep++) = 0;
+ while (*u) {
+ *u = toupper(*u);
+ u++;
+ }
+ if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+ if (info->mfr)
+ kfree (info->mfr);
+ info->mfr = strdup(sep);
+ } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+ if (info->model)
+ kfree (info->model);
+ info->model = strdup(sep);
+ } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+ int i;
+ if (info->class_name)
+ kfree (info->class_name);
+ info->class_name = strdup(sep);
+ for (u = sep; *u; u++)
+ *u = toupper(*u);
+ for (i = 0; classes[i].token; i++) {
+ if (!strcmp(classes[i].token, sep)) {
+ info->class = i;
+ goto rock_on;
+ }
+ }
+ printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+ info->class = PARPORT_CLASS_OTHER;
+ } else if (!strcmp(p, "CMD") ||
+ !strcmp(p, "COMMAND SET")) {
+ if (info->cmdset)
+ kfree (info->cmdset);
+ info->cmdset = strdup(sep);
+ /* if it speaks printer language, it's
+ probably a printer */
+ if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+ guessed_class = PARPORT_CLASS_PRINTER;
+ } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+ if (info->description)
+ kfree (info->description);
+ info->description = strdup(sep);
+ }
+ }
+ rock_on:
+ if (q) p = q+1; else p=NULL;
+ }
+
+ /* If the device didn't tell us its class, maybe we have managed to
+ guess one from the things it did say. */
+ if (info->class == PARPORT_CLASS_UNSPEC)
+ info->class = guessed_class;
+
+ pretty_print (port, device);
+
+ kfree(txt);
+}
+
+/* Get Std 1284 Device ID. */
+ssize_t parport_device_id (int devnum, char *buffer, size_t len)
+{
+ ssize_t retval = -ENXIO;
+ struct pardevice *dev = parport_open (devnum, "Device ID probe",
+ NULL, NULL, NULL, 0, NULL);
+ if (!dev)
+ return -ENXIO;
+
+ parport_claim_or_block (dev);
+
+ /* Negotiate to compatibility mode, and then to device ID mode.
+ * (This is in case we are already in device ID mode.) */
+ parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
+ retval = parport_negotiate (dev->port,
+ IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
+
+ if (!retval) {
+ int idlen;
+ unsigned char length[2];
+ mm_segment_t oldfs = get_fs ();
+ set_fs (get_ds ());
+
+ /* First two bytes are MSB,LSB of inclusive length. */
+ retval = parport_read (dev->port, length, 2);
+
+ if (retval != 2) goto restore_fs;
+
+ idlen = (length[0] << 8) + length[1] - 2;
+ if (idlen < len)
+ len = idlen;
+ retval = parport_read (dev->port, buffer, len);
+
+ if (retval != len) {
+ printk (KERN_DEBUG "%s: only read %d of %d ID bytes\n",
+ dev->port->name, retval, len);
+ goto restore_fs;
+ }
+
+ /* Some printer manufacturers mistakenly believe that
+ the length field is supposed to be _exclusive_. */
+ /* In addition, there are broken devices out there
+ that don't even finish off with a semi-colon. */
+ if (idlen == len && buffer[len - 1] != ';') {
+ ssize_t diff;
+ diff = parport_read (dev->port, buffer + len, 2);
+ retval += diff;
+
+ if (diff)
+ printk (KERN_DEBUG
+ "%s: device reported incorrect "
+ "length field (%d, should be %d)\n",
+ dev->port->name, idlen, retval);
+ else {
+ /* One semi-colon short of a device ID. */
+ buffer[len++] = ';';
+ buffer[len] = '\0';
+ printk (KERN_DEBUG "%s: faking semi-colon\n",
+ dev->port->name);
+
+ /* If we get here, I don't think we
+ need to worry about the possible
+ standard violation of having read
+ more than we were told to. The
+ device is non-compliant anyhow. */
+ }
+ }
+
+ restore_fs:
+ set_fs (oldfs);
+ parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
+ }
+ parport_release (dev);
+
+ if (retval > 0)
+ parse_data (dev->port, dev->daisy, buffer);
+
+ parport_close (dev);
+ return retval;
+}
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
-#if 0 && defined (CONFIG_PARPORT_1284)
+#ifdef CONFIG_PARPORT_1284
static int do_autoprobe(ctl_table *table, int write, struct file *filp,
void *result, size_t *lenp)
{
#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
int f = 0;
printmode(PCSPP);
- printmode(PCPS2);
- printmode(PCEPP);
- printmode(PCECP);
+ printmode(TRISTATE);
+ printmode(COMPAT);
+ printmode(EPP);
+ printmode(ECP);
+ printmode(DMA);
#undef printmode
}
buffer[len++] = '\n';
NULL, 0, 0444, NULL,
&do_hardware },
PARPORT_DEVICES_ROOT_DIR,
-#if 0 && defined(CONFIG_PARPORT_1284)
+#ifdef CONFIG_PARPORT_1284
{ DEV_PARPORT_AUTOPROBE, "autoprobe",
NULL, 0, 0444, NULL,
&do_autoprobe },
for (i = 0; i < 8; i++)
t->vars[i].extra1 = port;
-#if 0 /* Wait for IEEE 1284 support */
t->vars[0].data = &port->spintime;
-#endif
t->vars[2].child = t->device_dir;
for (i = 0; i < 5; i++)
-#if 0
t->vars[3 + i].extra2 = &port->probe_info[i];
-#else
- t->vars[3 + i].extra2 = &port->probe_info;
-#endif
t->port_dir[0].procname = port->name;
t->port_dir[0].ctl_name = port->number + 1; /* nb 0 isn't legal here */
t->port_dir[0].child = t->devices_root_dir;
t->devices_root_dir[0].child = t->device_dir;
-#if 0 && defined(CONFIG_PARPORT_1284)
+#ifdef CONFIG_PARPORT_1284
t->device_dir[0].ctl_name =
parport_device_num(port->number, port->muxport,
#define PARPORT_DEFAULT_TIMESLICE (HZ/5)
unsigned long parport_default_timeslice = PARPORT_DEFAULT_TIMESLICE;
-
-/* This doesn't do anything yet. */
-int parport_default_spintime;
+int parport_default_spintime = DEFAULT_SPIN_TIME;
static struct parport *portlist = NULL, *portlist_tail = NULL;
spinlock_t parportlist_lock = SPIN_LOCK_UNLOCKED;
static struct parport_driver *driver_chain = NULL;
spinlock_t driverlist_lock = SPIN_LOCK_UNLOCKED;
-static void call_driver_chain (int attach, struct parport *port)
+static void call_driver_chain(int attach, struct parport *port)
{
struct parport_driver *drv;
}
}
-void (*parport_probe_hook)(struct parport *port) = NULL;
-
/* Return a list of all the ports we know about. */
struct parport *parport_enumerate(void)
{
#ifdef CONFIG_KMOD
if (portlist == NULL) {
request_module("parport_lowlevel");
-#ifdef CONFIG_PNP_PARPORT_MODULE
- request_module("parport_probe");
-#endif /* CONFIG_PNP_PARPORT_MODULE */
}
#endif /* CONFIG_KMOD */
return portlist;
{
struct parport *tmp;
int portnum;
+ int device;
char *name;
- /* Check for a previously registered port.
- NOTE: we will ignore irq and dma if we find a previously
- registered device. */
- for (tmp = portlist; tmp; tmp = tmp->next) {
- if (tmp->base == base)
- return tmp;
- }
-
tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
if (!tmp) {
printk(KERN_WARNING "parport: memory squeeze\n");
tmp->base = base;
tmp->irq = irq;
tmp->dma = dma;
+ tmp->muxport = tmp->daisy = tmp->muxsel = -1;
tmp->modes = 0;
tmp->next = NULL;
tmp->devices = tmp->cad = NULL;
tmp->flags = 0;
tmp->ops = ops;
- tmp->number = portnum;
- memset (&tmp->probe_info, 0, sizeof (struct parport_device_info));
+ tmp->portnum = tmp->number = portnum;
+ tmp->physport = tmp;
+ memset (tmp->probe_info, 0, 5 * sizeof (struct parport_device_info));
tmp->cad_lock = RW_LOCK_UNLOCKED;
spin_lock_init(&tmp->waitlist_lock);
spin_lock_init(&tmp->pardevice_lock);
+ tmp->ieee1284.mode = IEEE1284_MODE_COMPAT;
+ tmp->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+ init_MUTEX_LOCKED (&tmp->ieee1284.irq); /* actually a semaphore at 0 */
+ tmp->spintime = parport_default_spintime;
name = kmalloc(15, GFP_KERNEL);
if (!name) {
portlist = tmp;
spin_unlock(&parportlist_lock);
- tmp->probe_info.class = PARPORT_CLASS_LEGACY; /* assume the worst */
+ for (device = 0; device < 5; device++)
+ /* assume the worst */
+ tmp->probe_info[device].class = PARPORT_CLASS_LEGACY;
+
tmp->waithead = tmp->waittail = NULL;
return tmp;
void parport_announce_port (struct parport *port)
{
+#ifdef CONFIG_PARPORT_1284
+ /* Analyse the IEEE1284.3 topology of the port. */
+ parport_daisy_init (port);
+#endif
+
/* Let drivers know that a new port has arrived. */
call_driver_chain (1, port);
}
void parport_unregister_port(struct parport *port)
{
struct parport *p;
+ int d;
/* Spread the word. */
call_driver_chain (0, port);
+#ifdef CONFIG_PARPORT_1284
+ /* Forget the IEEE1284.3 topology of the port. */
+ parport_daisy_fini (port);
+#endif
+
spin_lock(&parportlist_lock);
if (portlist == port) {
if ((portlist = port->next) == NULL)
"%s not found in port list!\n", port->name);
}
spin_unlock(&parportlist_lock);
- if (port->probe_info.class_name)
- kfree (port->probe_info.class_name);
- if (port->probe_info.mfr)
- kfree (port->probe_info.mfr);
- if (port->probe_info.model)
- kfree (port->probe_info.model);
- if (port->probe_info.cmdset)
- kfree (port->probe_info.cmdset);
- if (port->probe_info.description)
- kfree (port->probe_info.description);
+
+ for (d = 0; d < 5; d++) {
+ if (port->probe_info[d].class_name)
+ kfree (port->probe_info[d].class_name);
+ if (port->probe_info[d].mfr)
+ kfree (port->probe_info[d].mfr);
+ if (port->probe_info[d].model)
+ kfree (port->probe_info[d].model);
+ if (port->probe_info[d].cmdset)
+ kfree (port->probe_info[d].cmdset);
+ if (port->probe_info[d].description)
+ kfree (port->probe_info[d].description);
+ }
+
kfree(port->name);
kfree(port);
}
{
struct pardevice *tmp;
- if (port->flags & PARPORT_FLAG_EXCL) {
+ if (port->physport->flags & PARPORT_FLAG_EXCL) {
/* An exclusive device is registered. */
printk (KERN_DEBUG "%s: no more devices allowed\n",
port->name);
tmp->name = name;
tmp->port = port;
+ tmp->daisy = -1;
tmp->preempt = pf;
tmp->wakeup = kf;
tmp->private = handle;
tmp->flags = flags;
tmp->irq_func = irq_func;
- port->ops->init_state(tmp->state);
tmp->waiting = 0;
+ tmp->timeout = 5 * HZ;
/* Chain this onto the list */
tmp->prev = NULL;
* This function must not run from an irq handler so we don' t need
* to clear irq on the local CPU. -arca
*/
- spin_lock(&port->pardevice_lock);
+ spin_lock(&port->physport->pardevice_lock);
if (flags & PARPORT_DEV_EXCL) {
- if (port->devices) {
- spin_unlock (&port->pardevice_lock);
+ if (port->physport->devices) {
+ spin_unlock (&port->physport->pardevice_lock);
kfree (tmp->state);
kfree (tmp);
printk (KERN_DEBUG
port->flags |= PARPORT_FLAG_EXCL;
}
- tmp->next = port->devices;
- if (port->devices)
- port->devices->prev = tmp;
- port->devices = tmp;
- spin_unlock(&port->pardevice_lock);
+ tmp->next = port->physport->devices;
+ if (port->physport->devices)
+ port->physport->devices->prev = tmp;
+ port->physport->devices = tmp;
+ spin_unlock(&port->physport->pardevice_lock);
inc_parport_count();
port->ops->inc_use_count();
tmp->timeslice = parport_default_timeslice;
tmp->waitnext = tmp->waitprev = NULL;
+ /*
+ * This has to be run as last thing since init_state may need other
+ * pardevice fields. -arca
+ */
+ port->ops->init_state(tmp, tmp->state);
+ parport_device_proc_register(tmp);
return tmp;
}
}
#endif
- port = dev->port;
+ parport_device_proc_unregister(dev);
+
+ port = dev->port->physport;
if (port->cad == dev) {
printk(KERN_DEBUG "%s: %s forgot to release port\n",
dec_parport_count();
port->ops->dec_use_count();
-
- return;
}
int parport_claim(struct pardevice *dev)
{
struct pardevice *oldcad;
- struct parport *port = dev->port;
+ struct parport *port = dev->port->physport;
unsigned long flags;
if (port->cad == dev) {
printk(KERN_INFO "%s: %s already owner\n",
- dev->port->name,dev->name);
+ dev->port->name,dev->name);
return 0;
}
dev->waitprev = dev->waitnext = NULL;
}
- if (oldcad && port->irq != PARPORT_IRQ_NONE && !oldcad->irq_func)
- /*
- * If there was an irq pending it should hopefully happen
- * before return from enable_irq(). -arca
- */
- enable_irq(port->irq);
-
- /*
- * Avoid running irq handlers if the pardevice doesn' t use it. -arca
- */
- if (port->irq != PARPORT_IRQ_NONE && !dev->irq_func)
- disable_irq(port->irq);
-
/* Now we do the change of devices */
write_lock_irqsave(&port->cad_lock, flags);
port->cad = dev;
write_unlock_irqrestore(&port->cad_lock, flags);
+#ifdef CONFIG_PARPORT_1284
+ /* If it's a mux port, select it. */
+ if (dev->port->muxport >= 0) {
+ /* FIXME */
+ port->muxsel = dev->port->muxport;
+ }
+
+ /* If it's a daisy chain device, select it. */
+ if (dev->daisy >= 0) {
+ /* This could be lazier. */
+ if (!parport_daisy_select (port, dev->daisy,
+ IEEE1284_MODE_COMPAT))
+ port->daisy = dev->daisy;
+ }
+#endif /* IEEE1284.3 support */
+
/* Restore control registers */
port->ops->restore_state(port, dev->state);
dev->time = jiffies;
}
restore_flags(flags);
#ifdef PARPORT_DEBUG_SHARING
- if (dev->port->cad != dev)
- printk(KERN_DEBUG "%s: exiting parport_claim_or_block but %s owns port!\n", dev->name, dev->port->cad?dev->port->cad->name:"nobody");
+ if (dev->port->physport->cad != dev)
+ printk(KERN_DEBUG "%s: exiting parport_claim_or_block "
+ "but %s owns port!\n", dev->name,
+ dev->port->physport->cad ?
+ dev->port->physport->cad->name:"nobody");
#endif
}
dev->waiting = 0;
void parport_release(struct pardevice *dev)
{
- struct parport *port = dev->port;
+ struct parport *port = dev->port->physport;
struct pardevice *pd;
unsigned long flags;
"when not owner\n", port->name, dev->name);
return;
}
+
+#ifdef CONFIG_PARPORT_1284
+ /* If this is on a mux port, deselect it. */
+ if (dev->port->muxport >= 0) {
+ /* FIXME */
+ port->muxsel = -1;
+ }
+
+ /* If this is a daisy device, deselect it. */
+ if (dev->daisy >= 0) {
+ parport_daisy_deselect_all (port);
+ port->daisy = -1;
+ }
+#endif
+
write_lock_irqsave(&port->cad_lock, flags);
port->cad = NULL;
write_unlock_irqrestore(&port->cad_lock, flags);
- /*
- * Reenable irq and so discard the eventually pending irq while
- * cad is NULL. -arca
- */
- if (port->irq != PARPORT_IRQ_NONE && !dev->irq_func)
- enable_irq(port->irq);
-
/* Save control registers */
port->ops->save_state(port, dev->state);
#
# Plug and Play configuration
#
-
-mainmenu_option next_comment
-comment 'Plug and Play support'
-
-bool 'Plug and Play support' CONFIG_PNP
-
-if [ "$CONFIG_PNP" = "y" ]; then
- if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' Auto-probe for parallel devices' CONFIG_PNP_PARPORT $CONFIG_PARPORT
- fi
-fi
-
-endmenu
#
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes..
-#
-# Note 3! Plug and Play is the Borg. We have assimilated some other
-# drivers in the `char', `net' and `scsi' directories, but left them
-# there to allay suspicion.
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
MI_OBJS :=
MIX_OBJS :=
-ifeq ($(CONFIG_PNP_PARPORT),y)
- LX_OBJS += parport_probe.o
-else
- ifeq ($(CONFIG_PNP_PARPORT),m)
- MX_OBJS += parport_probe.o
- endif
-endif
-
include $(TOPDIR)/Rules.make
+++ /dev/null
-/* $Id: parport_probe.c,v 1.3 1997/10/19 18:18:46 phil Exp $
- * Parallel port device probing code
- *
- * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
- * Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/tasks.h>
-#include <linux/parport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/ctype.h>
-#include <linux/module.h>
-
-#include <linux/lp.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-
-#undef DEBUG_PROBE
-
-static inline int read_nibble(struct parport *port)
-{
- unsigned char i;
- i = parport_read_status(port)>>3;
- i &= ~8;
- if ((i & 0x10) == 0) i |= 8;
- return (i & 0x0f);
-}
-
-static void read_terminate(struct parport *port) {
- parport_write_control(port, (parport_read_control(port) & ~2) | 8);
- /* SelectIN high, AutoFeed low */
- if (parport_wait_peripheral(port, 0x80, 0))
- /* timeout, SelectIN high, Autofeed low */
- return;
- parport_write_control(port, parport_read_control(port) | 2);
- /* AutoFeed high */
- parport_wait_peripheral(port, 0x80, 0x80);
- /* no timeout possible, Autofeed low, SelectIN high */
- parport_write_control(port, (parport_read_control(port) & ~2) | 8);
-}
-
-static long read_polled(struct parport *port, char *buf,
- unsigned long length)
-{
- int i;
- char *temp=buf;
- unsigned int count = 0;
- unsigned char z=0;
- unsigned char Byte=0;
- unsigned long igiveupat=jiffies+5*HZ;
-
- for (i=0; time_before(jiffies, igiveupat); i++) {
- /* if(current->need_resched) schedule(); */
- parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed high */
- if (parport_wait_peripheral(port, 0x40, 0)) {
-#ifdef DEBUG_PROBE
- /* Some peripherals just time out when they've sent
- all their data. */
- printk("%s: read1 timeout.\n", port->name);
-#endif
- parport_write_control(port, parport_read_control(port) & ~2);
- break;
- }
- z = read_nibble(port);
- parport_write_control(port, parport_read_control(port) & ~2); /* AutoFeed low */
- if (parport_wait_peripheral(port, 0x40, 0x40)) {
- printk("%s: read2 timeout.\n", port->name);
- break;
- }
- if ((i & 1) != 0) {
- Byte |= (z<<4);
- if (temp)
- *(temp++) = Byte;
- if (++count == length)
- temp = NULL;
- /* Does the error line indicate end of data? */
- if ((parport_read_status(port) & LP_PERRORP) ==
- LP_PERRORP)
- break;
- } else
- Byte=z;
- }
- read_terminate(port);
- return count;
-}
-
-int parport_probe(struct parport *port, char *buffer, int len)
-{
- struct pardevice *dev = parport_register_device(port, "IEEE 1284 probe", NULL, NULL, NULL, 0, &dev);
-
- int result = 0;
-
- if (!dev) {
- printk("%s: unable to register for probe.\n", port->name);
- return -EINVAL;
- }
-
- parport_claim_or_block(dev);
-
- switch (parport_ieee1284_nibble_mode_ok(port, 4)) {
- case 2:
- current->state=TASK_INTERRUPTIBLE;
- /* HACK: wait 10ms because printer seems to ack wrong */
- schedule_timeout((HZ+99)/100);
- result = read_polled(port, buffer, len);
- break;
- default:
- result = -EIO;
- break;
- }
-
- parport_release(dev);
- parport_unregister_device(dev);
-
- return result;
-}
-
-static struct {
- char *token;
- char *descr;
-} classes[] = {
- { "", "Legacy device" },
- { "PRINTER", "Printer" },
- { "MODEM", "Modem" },
- { "NET", "Network device" },
- { "HDC", "Hard disk" },
- { "PCMCIA", "PCMCIA" },
- { "MEDIA", "Multimedia device" },
- { "FDC", "Floppy disk" },
- { "PORTS", "Ports" },
- { "SCANNER", "Scanner" },
- { "DIGICAM", "Digital camera" },
- { "", "Unknown device" },
- { "", "Unspecified" },
- { NULL, NULL }
-};
-
-static char *strdup(char *str)
-{
- int n = strlen(str)+1;
- char *s = kmalloc(n, GFP_KERNEL);
- if (!s) return NULL;
- return strcpy(s, str);
-}
-
-static void parse_data(struct parport *port, char *str)
-{
- char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
- char *p = txt, *q;
- int guessed_class = PARPORT_CLASS_UNSPEC;
-
- if (!txt) {
- printk("%s probe: memory squeeze\n", port->name);
- return;
- }
- strcpy(txt, str);
- while (p) {
- char *sep;
- q = strchr(p, ';');
- if (q) *q = 0;
- sep = strchr(p, ':');
- if (sep) {
- char *u = p;
- *(sep++) = 0;
- while (*u) {
- *u = toupper(*u);
- u++;
- }
- if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
- if (port->probe_info.mfr)
- kfree (port->probe_info.mfr);
- port->probe_info.mfr = strdup(sep);
- } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
- if (port->probe_info.model)
- kfree (port->probe_info.model);
- port->probe_info.model = strdup(sep);
- } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
- int i;
- if (port->probe_info.class_name)
- kfree (port->probe_info.class_name);
- port->probe_info.class_name = strdup(sep);
- for (u = sep; *u; u++)
- *u = toupper(*u);
- for (i = 0; classes[i].token; i++) {
- if (!strcmp(classes[i].token, sep)) {
- port->probe_info.class = i;
- goto rock_on;
- }
- }
- printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
- port->probe_info.class = PARPORT_CLASS_OTHER;
- } else if (!strcmp(p, "CMD") || !strcmp(p, "COMMAND SET")) {
- if (port->probe_info.cmdset)
- kfree (port->probe_info.cmdset);
- port->probe_info.cmdset = strdup(sep);
- /* if it speaks printer language, it's
- probably a printer */
- if (strstr(sep, "PJL") || strstr(sep, "PCL"))
- guessed_class = PARPORT_CLASS_PRINTER;
- } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
- if (port->probe_info.description)
- kfree (port->probe_info.description);
- port->probe_info.description = strdup(sep);
- }
- }
- rock_on:
- if (q) p = q+1; else p=NULL;
- }
-
- /* If the device didn't tell us its class, maybe we have managed to
- guess one from the things it did say. */
- if (port->probe_info.class == PARPORT_CLASS_UNSPEC)
- port->probe_info.class = guessed_class;
-
- kfree(txt);
-}
-
-static void pretty_print(struct parport *port)
-{
- printk(KERN_INFO "%s: %s", port->name,
- classes[port->probe_info.class].descr);
- if (port->probe_info.class) {
- printk(", %s %s", port->probe_info.mfr,
- port->probe_info.model);
- }
- printk("\n");
-}
-
-void parport_probe_one(struct parport *port)
-{
- char *buffer = kmalloc(2048, GFP_KERNEL);
- int r;
-
- MOD_INC_USE_COUNT;
- port->probe_info.model = strdup ("Unknown device");
- port->probe_info.mfr = strdup ("Unknown vendor");
- port->probe_info.description = port->probe_info.cmdset = NULL;
- port->probe_info.class = PARPORT_CLASS_UNSPEC;
- port->probe_info.class_name = NULL;
-
- if (!buffer) {
- printk(KERN_ERR "%s probe: Memory squeeze.\n", port->name);
- return;
- }
-
- r = parport_probe(port, buffer, 2047);
-
- if (r < 0) {
- printk(KERN_INFO "%s: no IEEE-1284 device present.\n",
- port->name);
- port->probe_info.class = PARPORT_CLASS_LEGACY;
- } else if (r == 0) {
- printk(KERN_INFO "%s: no ID data returned by device.\n",
- port->name);
- } else {
- buffer[r] = 0;
-#ifdef DEBUG_PROBE
- printk("%s id: %s\n", port->name, buffer+2);
-#endif
- parse_data(port, buffer+2);
- pretty_print(port);
- }
- kfree(buffer);
- MOD_DEC_USE_COUNT;
-}
-
-#if MODULE
-int init_module(void)
-{
- struct parport *p;
- for (p = parport_enumerate(); p; p = p->next)
- parport_probe_one(p);
- parport_probe_hook = &parport_probe_one;
- return 0;
-}
-
-void cleanup_module(void)
-{
- parport_probe_hook = NULL;
-}
-#endif
#endif
while (info->xmit_cnt || !info->all_sent) {
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0;
schedule_timeout(char_time);
if (signal_pending(current))
break;
printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
schedule_timeout(char_time);
if (signal_pending(current))
break;
*/
ppa_hosts[i].mode = PPA_NIBBLE;
- if (modes & PARPORT_MODE_PCPS2)
+ if (modes & PARPORT_MODE_TRISTATE)
ppa_hosts[i].mode = PPA_PS2;
- if (modes & PARPORT_MODE_PCECPPS2) {
+ if (modes & PARPORT_MODE_ECP) {
w_ecr(ppb, 0x20);
ppa_hosts[i].mode = PPA_PS2;
}
- if (modes & PARPORT_MODE_PCECPEPP)
+ if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))
w_ecr(ppb, 0x80);
/* Done configuration */
NULL, /* no special mmap-advise */
sgi_graphics_nopage, /* our magic no-page fault handler */
NULL, /* no special mmap-wppage */
- NULL, /* no special mmap-swapout */
- NULL /* no special mmap-swapin */
+ NULL /* no special mmap-swapout */
};
int
NULL, /* no special mmap-advise */
shmiq_nopage, /* our magic no-page fault handler */
NULL, /* no special mmap-wppage */
- NULL, /* no special mmap-swapout */
- NULL /* no special mmap-swapin */
+ NULL /* no special mmap-swapout */
};
static int
THANKS file in Inaky's driver):
The following corporations have helped us in the development
-of Linux USB / UUSBD:
+ of Linux USB / UUSBD:
- USAR Systems provided us with one of their excellent USB
Evaluation Kits. It allows us to test the Linux-USB driver
ifeq ($(CONFIG_FB_TGA),y)
L_OBJS += tgafb.o
+CONFIG_FBGEN_BUILTIN = y
else
ifeq ($(CONFIG_FB_TGA),m)
M_OBJS += tgafb.o
+ CONFIG_FBGEN_MODULE = y
endif
endif
extern void imsttfb_setup(char *options, int *ints);
extern void dnfb_init(void);
extern void tgafb_init(void);
+extern void tgafb_setup(char *options, int *ints);
extern void virgefb_init(void);
extern void virgefb_setup(char *options, int *ints);
extern void resolver_video_setup(char *options, int *ints);
{ "s3trio", s3triofb_init, s3triofb_setup },
#endif
#ifdef CONFIG_FB_TGA
- { "tga", tgafb_init, NULL },
+ { "tga", tgafb_init, tgafb_setup },
#endif
#ifdef CONFIG_FB_VIRGE
{ "virge", virgefb_init, virgefb_setup },
/*
* linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device
*
- * Copyright (C) 1997 Geert Uytterhoeven
+ * Copyright (C) 1999 Martin Lucina, Tom Zerucha
+ *
+ * $Id: tgafb.c,v 1.12 1999/07/01 13:39:23 mato Exp $
*
- * This driver is partly based on the original TGA console driver
+ * This driver is partly based on the original TGA framebuffer device, which
+ * was partly based on the original TGA console driver, which are
*
- * Copyright (C) 1995 Jay Estabrook
+ * Copyright (C) 1997 Geert Uytterhoeven
+ * Copyright (C) 1995 Jay Estabrook
*
* 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.
*/
-
/* KNOWN PROBLEMS/TO DO ===================================================== *
*
* - How to set a single color register on 24-plane cards?
*
- * - Hardware cursor (useful for other graphics boards too)
- *
- * - Support for more resolutions
+ * - Hardware cursor/other text acceleration methods
*
* - Some redraws can stall kernel for several seconds
+ * [This should now be solved by the fast memmove() patch in 2.3.6]
*
* KNOWN PROBLEMS/TO DO ==================================================== */
#include <video/fbcon.h>
#include <video/fbcon-cfb8.h>
#include <video/fbcon-cfb32.h>
+#include "tgafb.h"
-/* TGA hardware description (minimal) */
-/*
- * Offsets within Memory Space
- */
-#define TGA_ROM_OFFSET 0x0000000
-#define TGA_REGS_OFFSET 0x0100000
-#define TGA_8PLANE_FB_OFFSET 0x0200000
-#define TGA_24PLANE_FB_OFFSET 0x0804000
-#define TGA_24PLUSZ_FB_OFFSET 0x1004000
-
-#define TGA_PLANEMASK_REG 0x0028
-#define TGA_MODE_REG 0x0030
-#define TGA_RASTEROP_REG 0x0034
-#define TGA_DEEP_REG 0x0050
-#define TGA_PIXELMASK_REG 0x005c
-#define TGA_CURSOR_BASE_REG 0x0060
-#define TGA_HORIZ_REG 0x0064
-#define TGA_VERT_REG 0x0068
-#define TGA_BASE_ADDR_REG 0x006c
-#define TGA_VALID_REG 0x0070
-#define TGA_CURSOR_XY_REG 0x0074
-#define TGA_INTR_STAT_REG 0x007c
-#define TGA_RAMDAC_SETUP_REG 0x00c0
-#define TGA_BLOCK_COLOR0_REG 0x0140
-#define TGA_BLOCK_COLOR1_REG 0x0144
-#define TGA_CLOCK_REG 0x01e8
-#define TGA_RAMDAC_REG 0x01f0
-#define TGA_CMD_STAT_REG 0x01f8
-
-/*
- * useful defines for managing the BT485 on the 8-plane TGA
- */
-#define BT485_READ_BIT 0x01
-#define BT485_WRITE_BIT 0x00
-
-#define BT485_ADDR_PAL_WRITE 0x00
-#define BT485_DATA_PAL 0x02
-#define BT485_PIXEL_MASK 0x04
-#define BT485_ADDR_PAL_READ 0x06
-#define BT485_ADDR_CUR_WRITE 0x08
-#define BT485_DATA_CUR 0x0a
-#define BT485_CMD_0 0x0c
-#define BT485_ADDR_CUR_READ 0x0e
-#define BT485_CMD_1 0x10
-#define BT485_CMD_2 0x12
-#define BT485_STATUS 0x14
-#define BT485_CMD_3 0x14
-#define BT485_CUR_RAM 0x16
-#define BT485_CUR_LOW_X 0x18
-#define BT485_CUR_HIGH_X 0x1a
-#define BT485_CUR_LOW_Y 0x1c
-#define BT485_CUR_HIGH_Y 0x1e
-
-/*
- * useful defines for managing the BT463 on the 24-plane TGAs
- */
-#define BT463_ADDR_LO 0x0
-#define BT463_ADDR_HI 0x1
-#define BT463_REG_ACC 0x2
-#define BT463_PALETTE 0x3
+ /*
+ * Global declarations
+ */
-#define BT463_CUR_CLR_0 0x0100
-#define BT463_CUR_CLR_1 0x0101
+static struct tgafb_info fb_info;
+static struct tgafb_par current_par;
+static int current_par_valid = 0;
+static struct display disp;
-#define BT463_CMD_REG_0 0x0201
-#define BT463_CMD_REG_1 0x0202
-#define BT463_CMD_REG_2 0x0203
+static char __initdata default_fontname[40] = { 0 };
+static struct fb_var_screeninfo default_var;
+static int default_var_valid = 0;
-#define BT463_READ_MASK_0 0x0205
-#define BT463_READ_MASK_1 0x0206
-#define BT463_READ_MASK_2 0x0207
-#define BT463_READ_MASK_3 0x0208
+static int currcon = 0;
-#define BT463_BLINK_MASK_0 0x0209
-#define BT463_BLINK_MASK_1 0x020a
-#define BT463_BLINK_MASK_2 0x020b
-#define BT463_BLINK_MASK_3 0x020c
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
-#define BT463_WINDOW_TYPE_BASE 0x0300
+static struct { u_char red, green, blue, pad; } palette[256];
+#ifdef FBCON_HAS_CFB32
+static u32 fbcon_cfb32_cmap[16];
+#endif
-int tga_type;
-unsigned int tga_mem_base;
-unsigned long tga_fb_base;
-unsigned long tga_regs_base;
+ /*
+ * Hardware presets
+ */
-static unsigned int fb_offset_presets[4] __initdata = {
+static unsigned int fb_offset_presets[4] = {
TGA_8PLANE_FB_OFFSET,
TGA_24PLANE_FB_OFFSET,
0xffffffff,
TGA_24PLUSZ_FB_OFFSET
};
-static unsigned int deep_presets[4] __initdata = {
+static unsigned int deep_presets[4] = {
0x00014000,
0x0001440d,
0xffffffff,
0x0001441d
};
-static unsigned int rasterop_presets[4] __initdata = {
+static unsigned int rasterop_presets[4] = {
0x00000003,
0x00000303,
0xffffffff,
0x00000303
};
-static unsigned int mode_presets[4] __initdata = {
+static unsigned int mode_presets[4] = {
0x00002000,
0x00002300,
0xffffffff,
0x00002300
};
-static unsigned int base_addr_presets[4] __initdata = {
+static unsigned int base_addr_presets[4] = {
0x00000000,
0x00000001,
0xffffffff,
0x00000001
};
-#define TGA_WRITE_REG(v,r) \
- { writel((v), tga_regs_base+(r)); mb(); }
-
-#define TGA_READ_REG(r) readl(tga_regs_base+(r))
-
-#define BT485_WRITE(v,r) \
- TGA_WRITE_REG((r),TGA_RAMDAC_SETUP_REG); \
- TGA_WRITE_REG(((v)&0xff)|((r)<<8),TGA_RAMDAC_REG);
-
-#define BT463_LOAD_ADDR(a) \
- TGA_WRITE_REG(BT463_ADDR_LO<<2, TGA_RAMDAC_SETUP_REG); \
- TGA_WRITE_REG((BT463_ADDR_LO<<10)|((a)&0xff), TGA_RAMDAC_REG); \
- TGA_WRITE_REG(BT463_ADDR_HI<<2, TGA_RAMDAC_SETUP_REG); \
- TGA_WRITE_REG((BT463_ADDR_HI<<10)|(((a)>>8)&0xff), TGA_RAMDAC_REG);
-
-#define BT463_WRITE(m,a,v) \
- BT463_LOAD_ADDR((a)); \
- TGA_WRITE_REG(((m)<<2),TGA_RAMDAC_SETUP_REG); \
- TGA_WRITE_REG(((m)<<10)|((v)&0xff),TGA_RAMDAC_REG);
-
-
-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
+const unsigned int bt463_cursor_source[4] = {
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000
};
-const unsigned int bt463_cursor_source[256] __initdata = {
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
- 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,
- 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,
- 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,
- 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
-};
+ /*
+ * Predefined video modes
+ * This is a subset of the standard VESA modes, recalculated from XFree86.
+ *
+ * XXX Should we store these in terms of the encoded par structs? Even better,
+ * fbcon should provide a general mechanism for doing something like this.
+ */
-#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+static struct fb_videomode tgafb_predefined[] __initdata = {
+ { "640x480-60", {
+ 640, 480, 640, 480, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "800x600-56", {
+ 800, 600, 800, 600, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 27777, 128, 24, 22, 1, 72, 2,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "640x480-72", {
+ 640, 480, 640, 480, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 31746, 144, 40, 30, 8, 40, 3,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "800x600-60", {
+ 800, 600, 800, 600, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 25000, 88, 40, 23, 1, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "800x600-72", {
+ 800, 600, 800, 600, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 20000, 64, 56, 23, 37, 120, 6,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1024x768-60", {
+ 1024, 768, 1024, 768, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 15384, 168, 8, 29, 3, 144, 6,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1152x864-60", {
+ 1152, 864, 1152, 864, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 11123, 208, 64, 16, 4, 256, 8,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1024x768-70", {
+ 1024, 768, 1024, 768, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 13333, 144, 24, 29, 3, 136, 6,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1024x768-76", {
+ 1024, 768, 1024, 768, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 11764, 208, 8, 36, 16, 120, 3,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1152x864-70", {
+ 1152, 864, 1152, 864, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 10869, 106, 56, 20, 1, 160, 10,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1280x1024-61", {
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 9090, 200, 48, 26, 1, 184, 3,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1024x768-85", {
+ 1024, 768, 1024, 768, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 10111, 192, 32, 34, 14, 160, 6,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1280x1024-70", {
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 7905, 224, 32, 28, 8, 160, 8,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1152x864-84", {
+ 1152, 864, 1152, 864, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 7407, 184, 312, 32, 0, 128, 12,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1280x1024-76", {
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 7407, 248, 32, 34, 3, 104, 3,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "1280x1024-85", {
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 6349, 224, 64, 44, 1, 160, 3,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }},
+
+ /* These are modes used by the two fixed-frequency monitors I have at home.
+ * You may or may not find these useful.
+ */
-static int currcon = 0;
-static struct display disp;
-static struct fb_info fb_info;
-static struct { u_char red, green, blue, pad; } palette[256];
-#ifdef FBCON_HAS_CFB32
-static u32 fbcon_cfb32_cmap[16];
-#endif
+ { "WYSE1", { /* 1280x1024 @ 72 Hz, 130 Mhz clock */
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 7692, 192, 32, 47, 0, 192, 5,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED
+ }},
+ { "IBM3", { /* 1280x1024 @ 70 Hz, 120 Mhz clock */
+ 1280, 1024, 1280, 1024, 0, 0, 0, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCELF_TEXT, 8333, 192, 32, 47, 0, 192, 5,
+ 0,
+ FB_VMODE_NONINTERLACED
+ }}
+};
-static struct fb_fix_screeninfo fb_fix = { { "DEC TGA ", } };
-static struct fb_var_screeninfo fb_var = { 0, };
+#define NUM_TOTAL_MODES arraysize(tgafb_predefined)
/*
* Interface used by the world
*/
+static void tgafb_detect(void);
+static int tgafb_encode_fix(struct fb_fix_screeninfo *fix, const void *fb_par,
+ struct fb_info_gen *info);
+static int tgafb_decode_var(const struct fb_var_screeninfo *var, void *fb_par,
+ struct fb_info_gen *info);
+static int tgafb_encode_var(struct fb_var_screeninfo *var, const void *fb_par,
+ struct fb_info_gen *info);
+static void tgafb_get_par(void *fb_par, struct fb_info_gen *info);
+static void tgafb_set_par(const void *fb_par, struct fb_info_gen *info);
+static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ 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, struct fb_info *info);
+static int tgafb_pan_display(const struct fb_var_screeninfo *var,
+ struct fb_info_gen *info);
+static int tgafb_blank(int blank, struct fb_info_gen *info);
+static void tgafb_set_disp(const void *fb_par, struct display *disp,
+ struct fb_info_gen *info);
+
static int tgafb_open(struct fb_info *info, int user);
static int tgafb_release(struct fb_info *info, int user);
-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, struct fb_info *info);
-
-
- /*
- * Interface to the low level console driver
- */
-
+void tgafb_setup(char *options, int *ints);
void tgafb_init(void);
-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);
-
+void tgafb_cleanup(struct fb_info *info);
- /*
- * Internal routines
- */
-
-static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
- 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, struct fb_info *info);
+static void tgafb_set_pll(int f);
#if 1
-static void tga_update_palette(void);
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static void tgafb_update_palette(void);
#endif
-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
-};
/*
- * Open/Release the frame buffer device
+ * Chipset specific functions
*/
-static int tgafb_open(struct fb_info *info, int user)
-{
- /*
- * Nothing, only a usage count for the moment
- */
- MOD_INC_USE_COUNT;
- return(0);
-}
-
-static int tgafb_release(struct fb_info *info, int user)
+static void tgafb_detect(void)
{
- MOD_DEC_USE_COUNT;
- return(0);
+ return;
}
- /*
- * Get the Fixed Part of the Display
- */
-
-static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con,
- struct fb_info *info)
+static int tgafb_encode_fix(struct fb_fix_screeninfo *fix, const void *fb_par,
+ struct fb_info_gen *info)
{
- memcpy(fix, &fb_fix, sizeof(fb_fix));
- return 0;
-}
+ struct tgafb_par *par = (struct tgafb_par *)fb_par;
+ strcpy(fix->id, fb_info.gen.info.modename);
- /*
- * Get the User Defined Part of the Display
- */
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ if (fb_info.tga_type == 0) /* 8-plane */
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ else /* 24-plane or 24plusZ */
+ fix->visual = FB_VISUAL_TRUECOLOR;
+
+ fix->line_length = par->xres * (par->bits_per_pixel >> 3);
+ fix->smem_start = (char *)__pa(fb_info.tga_fb_base + dense_mem(fb_info.tga_fb_base));
+ fix->smem_len = fix->line_length * par->yres;
+ fix->mmio_start = (char *)__pa(fb_info.tga_regs_base);
+ fix->mmio_len = 0x1000; /* Is this sufficient? */
+ fix->xpanstep = fix->ypanstep = fix->ywrapstep = 0;
+ fix->accel = FB_ACCEL_DEC_TGA;
-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;
}
- /*
- * Set the User Defined Part of the Display
- */
-
-static int tgafb_set_var(struct fb_var_screeninfo *var, int con,
- struct fb_info *info)
+static int tgafb_decode_var(const struct fb_var_screeninfo *var, void *fb_par,
+ struct fb_info_gen *info)
{
- struct display *display;
- int oldbpp = -1, err;
-
- if (con >= 0)
- display = &fb_display[con];
- else
- display = &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 ||
- var->nonstd ||
- (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
- return -EINVAL;
- memcpy(var, &fb_var, sizeof(fb_var));
+ struct tgafb_par *par = (struct tgafb_par *)fb_par;
- if ((var->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, info);
+ /* round up some */
+ if (fb_info.tga_type == 0) {
+ if (var->bits_per_pixel > 8) {
+ return -EINVAL;
+ }
+ par->bits_per_pixel = 8;
+ } else {
+ if (var->bits_per_pixel > 32) {
+ return -EINVAL;
+ }
+ par->bits_per_pixel = 32;
}
- return 0;
-}
-
- /*
- * Pan or Wrap the Display
- *
- * 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,
- struct fb_info *info)
-{
- if (var->xoffset || var->yoffset)
+ /* check the values for sanity */
+ if (var->xres_virtual != var->xres ||
+ var->yres_virtual != var->yres ||
+ var->nonstd || (1000000000/var->pixclock) > TGA_PLL_MAX_FREQ ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED
+#if 0
+ || !fbmon_valid_timings(var->pixclock, var->htotal, var->vtotal, info))
+#else
+ )
+#endif
return -EINVAL;
- else
- return 0;
-}
- /*
- * Get the Colormap
- */
+ /* encode video timings */
+ par->htimings = ((var->xres/4) & TGA_HORIZ_ACT_LSB) |
+ (((var->xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB);
+ par->vtimings = (var->yres & TGA_VERT_ACTIVE);
+ par->htimings |= ((var->right_margin/4) << 9) & TGA_HORIZ_FP;
+ par->vtimings |= (var->lower_margin << 11) & TGA_VERT_FP;
+ par->htimings |= ((var->hsync_len/4) << 14) & TGA_HORIZ_SYNC;
+ par->vtimings |= (var->vsync_len << 16) & TGA_VERT_SYNC;
+ par->htimings |= ((var->left_margin/4) << 21) & TGA_HORIZ_BP;
+ par->vtimings |= (var->upper_margin << 22) & TGA_VERT_BP;
+
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->htimings |= TGA_HORIZ_POLARITY;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->vtimings |= TGA_VERT_POLARITY;
+ /* what about sync on green? */
+
+ /* store other useful values in par */
+ par->xres = var->xres;
+ par->yres = var->yres;
+ par->pll_freq = 1000000000/var->pixclock;
+ par->bits_per_pixel = var->bits_per_pixel;
-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, 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(256), cmap, kspc ? 0 : 2);
return 0;
}
- /*
- * Set the Colormap
- */
-static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
- struct fb_info *info)
+static int tgafb_encode_var(struct fb_var_screeninfo *var, const void *fb_par,
+ struct fb_info_gen *info)
{
- int err;
-
- if (!fb_display[con].cmap.len) { /* no colormap allocated? */
- if ((err = fb_alloc_cmap(&fb_display[con].cmap, 256, 0)))
- return err;
+ struct tgafb_par *par = (struct tgafb_par *)fb_par;
+
+ /* decode video timings */
+ var->xres = ((par->htimings & TGA_HORIZ_ACT_LSB) | ((par->htimings & TGA_HORIZ_ACT_MSB) >> 19)) * 4;
+ var->yres = (par->vtimings & TGA_VERT_ACTIVE);
+ var->right_margin = ((par->htimings & TGA_HORIZ_FP) >> 9) * 4;
+ var->lower_margin = ((par->vtimings & TGA_VERT_FP) >> 11);
+ var->hsync_len = ((par->htimings & TGA_HORIZ_SYNC) >> 14) * 4;
+ var->vsync_len = ((par->vtimings & TGA_VERT_SYNC) >> 16);
+ var->left_margin = ((par->htimings & TGA_HORIZ_BP) >> 21) * 4;
+ var->upper_margin = ((par->vtimings & TGA_VERT_BP) >> 22);
+
+ if (par->htimings & TGA_HORIZ_POLARITY)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (par->vtimings & TGA_VERT_POLARITY)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+ var->xoffset = var->yoffset = 0;
+
+ /* depth-related */
+ if (fb_info.tga_type == 0) {
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ } else {
+ /* XXX: is this correct? */
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
}
- if (con == currcon) { /* current console? */
- err = fb_set_cmap(cmap, kspc, tgafb_setcolreg, info);
-#if 1
- if (tga_type != 0)
- tga_update_palette();
-#endif
- return err;
- } else
- fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ var->bits_per_pixel = par->bits_per_pixel;
+ var->grayscale = 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;
+
+ /* others */
+ var->xoffset = var->yoffset = 0;
+ var->pixclock = 1000000000/par->pll_freq;
+ var->nonstd = 0;
+ var->activate = 0;
+ var->height = var->width = -1;
+ var->accel_flags = 0;
+
return 0;
}
-static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg, int con, struct fb_info *info)
+static void tgafb_get_par(void *fb_par, struct fb_info_gen *info)
{
- return -EINVAL;
-}
+ struct tgafb_par *par = (struct tgafb_par *)fb_par;
+ if (current_par_valid)
+ *par = current_par;
+ else {
+ if (fb_info.tga_type == 0)
+ default_var.bits_per_pixel = 8;
+ else
+ default_var.bits_per_pixel = 32;
- /*
- * Initialisation
- */
+ tgafb_decode_var(&default_var, par, info);
+ }
+}
-__initfunc(void tgafb_init(void))
+
+static void tgafb_set_par(const void *fb_par, struct fb_info_gen *info)
{
int i, j, temp;
- unsigned char *cbp;
- struct pci_dev *pdev;
-
- pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL);
- if (!pdev)
- return;
- 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 */
+ struct tgafb_par *par = (struct tgafb_par *)fb_par;
- tga_type = (readl((unsigned long)tga_mem_base) >> 12) & 0x0f;
- switch (tga_type) {
- case 0:
- strcat(fb_fix.id, "8plane");
- break;
- case 1:
- strcat(fb_fix.id, "24plane");
- break;
- case 3:
- strcat(fb_fix.id, "24plusZ");
- break;
- default:
- printk("TGA type (0x%x) unrecognized!\n", tga_type);
+#if 0
+ /* XXX this will break console switching with X11, maybe I need to test KD_GRAPHICS? */
+ /* if current_par is valid, check to see if we need to change anything */
+ if (current_par_valid) {
+ if (!memcmp(par, ¤t_par, sizeof current_par)) {
return;
+ }
}
-
- tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET);
- tga_fb_base = ((unsigned long)tga_mem_base + fb_offset_presets[tga_type]);
+#endif
+ current_par = *par;
+ current_par_valid = 1;
/* first, disable video timing */
TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */
-
+
/* write the DEEP register */
while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */
continue;
mb();
- TGA_WRITE_REG(deep_presets[tga_type], TGA_DEEP_REG);
+ TGA_WRITE_REG(deep_presets[fb_info.tga_type], TGA_DEEP_REG);
while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */
continue;
mb();
/* write some more registers */
- TGA_WRITE_REG(rasterop_presets[tga_type], TGA_RASTEROP_REG);
- TGA_WRITE_REG(mode_presets[tga_type], TGA_MODE_REG);
- TGA_WRITE_REG(base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
-
- /* write the PLL for 640x480 @ 60Hz */
- for (i = 0; i <= 6; i++) {
- for (j = 0; j <= 7; j++) {
- temp = (PLLbits[i] >> (7-j)) & 1;
- if (i == 6 && j == 7)
- temp |= 2;
- TGA_WRITE_REG(temp, TGA_CLOCK_REG);
- }
- }
+ TGA_WRITE_REG(rasterop_presets[fb_info.tga_type], TGA_RASTEROP_REG);
+ TGA_WRITE_REG(mode_presets[fb_info.tga_type], TGA_MODE_REG);
+ TGA_WRITE_REG(base_addr_presets[fb_info.tga_type], TGA_BASE_ADDR_REG);
+
+ /* calculate & write the PLL */
+ tgafb_set_pll(par->pll_freq);
/* write some more registers */
TGA_WRITE_REG(0xffffffff, TGA_PLANEMASK_REG);
TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR0_REG);
TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR1_REG);
- /* init video timing regs for 640x480 @ 60 Hz */
- TGA_WRITE_REG(0x018608a0, TGA_HORIZ_REG);
- TGA_WRITE_REG(0x084251e0, TGA_VERT_REG);
-
- if (tga_type == 0) { /* 8-plane */
+ /* init video timing regs */
+ TGA_WRITE_REG(par->htimings, TGA_HORIZ_REG);
+ TGA_WRITE_REG(par->vtimings, TGA_VERT_REG);
- fb_var.bits_per_pixel = 8;
- fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ /* initalise RAMDAC */
+ if (fb_info.tga_type == 0) { /* 8-plane */
/* init BT485 RAMDAC registers */
BT485_WRITE(0xa2, BT485_CMD_0);
BT485_WRITE(0x01, BT485_ADDR_PAL_WRITE);
BT485_WRITE(0x14, BT485_CMD_3); /* cursor 64x64 */
BT485_WRITE(0x40, BT485_CMD_1);
- BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */
+ BT485_WRITE(0x20, BT485_CMD_2); /* cursor off, for now */
BT485_WRITE(0xff, BT485_PIXEL_MASK);
/* fill palette registers */
TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
}
+#if 0
/* initialize RAMDAC cursor colors */
BT485_WRITE(0, BT485_ADDR_CUR_WRITE);
/* initialize RAMDAC cursor RAM */
BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE);
- cbp = (unsigned char *)bt485_cursor_source;
- for (i = 0; i < 512; i++) {
- BT485_WRITE(*cbp++, BT485_CUR_RAM);
- }
+
+ for (i = 0; i < tga_font_height_padded; i++)
+ for (j = 7; j >= 0; j--) {
+#if 0
+ /* note that this is for a top-right alignment
+ * - top left is commented out */
+ if( j > /*<*/ ((tga_font_width - 1) >> 3) ) {
+ BT485_WRITE(0, BT485_CUR_RAM);
+ }
+ else if( j == ((tga_font_width - 1) >> 3) ) {
+ BT485_WRITE((0xff >> /*<<*/
+ (7 - ((tga_font_width - 1)&7))) , BT485_CUR_RAM);
+ }
+ else {
+ BT485_WRITE(0xff, BT485_CUR_RAM);
+ }
+#else
+ BT485_WRITE(0, BT485_CUR_RAM);
+#endif
+ }
+ for (i = tga_font_height_padded; i < 64; i++)
+ for (j = 0; j < 8; j++) {
+ BT485_WRITE(0, BT485_CUR_RAM);
+ }
+ /* mask? */
+
for (i = 0; i < 512; i++) {
BT485_WRITE(0xff, BT485_CUR_RAM);
}
+#endif
} else { /* 24-plane or 24plusZ */
- fb_var.bits_per_pixel = 32;
- fb_fix.visual = FB_VISUAL_TRUECOLOR;
-
TGA_WRITE_REG(0x01, TGA_VALID_REG); /* SCANNING */
/*
TGA_WRITE_REG(0x80|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
}
+#if 0
/*
* init cursor colors
*/
temp = tga_fb_base - 1024; /* this assumes video starts at base
and base is beyond memory start*/
- for (i = 0; i < 256; i++) {
- writel(bt463_cursor_source[i], temp + i*4);
- }
+ for (i = 0; i < tga_font_height_padded*4; i++)
+ writel(bt463_cursor_source[i&3], temp + i*4);
+ for (i = tga_font_height_padded*4; i < 256; i++)
+ writel(0, temp + i*4);
TGA_WRITE_REG(temp & 0x000fffff, TGA_CURSOR_BASE_REG);
+#endif
}
/* finally, enable video scan
- (and pray for the monitor... :-) */
+ (and pray for the monitor... :-) */
TGA_WRITE_REG(0x01, TGA_VALID_REG); /* SCANNING */
+}
- 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 *)__pa(tga_fb_base + dense_mem(tga_fb_base));
- fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
- fb_fix.type = FB_TYPE_PACKED_PIXELS;
- fb_fix.type_aux = 0;
- fb_fix.mmio_start = (char *)__pa(tga_regs_base);
- fb_fix.mmio_len = 0x1000; /* Is this sufficient? */
- fb_fix.accel = FB_ACCEL_DEC_TGA;
-
- fb_var.xoffset = fb_var.yoffset = 0;
- fb_var.grayscale = 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;
+
+#define DIFFCHECK(x) { if( m <= 0x3f ) { \
+ int delta = f - (TGA_PLL_BASE_FREQ * (x)) / (r << shift); \
+ if (delta < 0) delta = -delta; \
+ if (delta < min_diff) min_diff = delta, vm = m, va = a, vr = r; } }
+
+static void tgafb_set_pll(int f)
+{
+ int n, shift, base, min_diff, target;
+ int r,a,m,vm = 34, va = 1, vr = 30;
+
+ for( r = 0 ; r < 12 ; r++ )
+ TGA_WRITE_REG(!r, TGA_CLOCK_REG);
+
+ if (f > TGA_PLL_MAX_FREQ)
+ f = TGA_PLL_MAX_FREQ;
+
+ if (f >= TGA_PLL_MAX_FREQ / 2)
+ shift = 0;
+ else if (f >= TGA_PLL_MAX_FREQ / 4)
+ shift = 1;
+ else
+ shift = 2;
+
+ TGA_WRITE_REG(shift & 1, TGA_CLOCK_REG);
+ TGA_WRITE_REG(shift >> 1, TGA_CLOCK_REG);
+
+ for( r = 0 ; r < 10 ; r++ ) {
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
}
- 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_flags = 0;
- fb_var.pixclock = 39722;
- fb_var.left_margin = 40;
- fb_var.right_margin = 24;
- fb_var.upper_margin = 32;
- fb_var.lower_margin = 11;
- fb_var.hsync_len = 96;
- fb_var.vsync_len = 2;
- 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 = (char *)tga_fb_base + dense_mem(tga_fb_base);
- 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;
- switch (tga_type) {
-#ifdef FBCON_HAS_CFB8
- case 0: /* 8-plane */
- disp.dispsw = &fbcon_cfb8;
- break;
-#endif
-#ifdef FBCON_HAS_CFB32
- case 1: /* 24-plane */
- case 3: /* 24plusZ */
- disp.dispsw = &fbcon_cfb32;
- disp.dispsw_data = &fbcon_cfb32_cmap;
- break;
-#endif
- default:
- disp.dispsw = &fbcon_dummy;
+
+ if (f <= 120000) {
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
}
- disp.scrollmode = SCROLL_YREDRAW;
-
- strcpy(fb_info.modename, fb_fix.id);
- fb_info.node = -1;
- fb_info.fbops = &tgafb_ops;
- 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.flags = FBINFO_FLAG_DEFAULT;
-
- tgafb_set_var(&fb_var, -1, &fb_info);
-
- if (register_framebuffer(&fb_info) < 0)
- return;
+ else if (f <= 200000) {
+ TGA_WRITE_REG(1, TGA_CLOCK_REG);
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ }
+ else {
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ TGA_WRITE_REG(1, TGA_CLOCK_REG);
+ }
+
+ TGA_WRITE_REG(1, TGA_CLOCK_REG);
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ TGA_WRITE_REG(1, TGA_CLOCK_REG);
+ TGA_WRITE_REG(0, TGA_CLOCK_REG);
+ TGA_WRITE_REG(1, TGA_CLOCK_REG);
+
+ target = (f << shift) / TGA_PLL_BASE_FREQ;
+ min_diff = TGA_PLL_MAX_FREQ;
+
+ r = 7 / target;
+ if (!r)
+ r = 1;
+
+ base = target * r;
+ while (base < 449) {
+ for (n = base < 7 ? 7 : base ; n < base + target && n < 449; n++) {
+ m = ((n + 3) / 7) - 1;
+ a = 0;
+ DIFFCHECK((m + 1) * 7);
+ m++;
+ DIFFCHECK((m + 1) * 7);
+ m = (n / 6) - 1;
+ if( (a = n % 6))
+ DIFFCHECK( n );
+ }
+ r++;
+ base += target;
+ }
+
+ vr--;
- printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
- fb_fix.id);
+ for( r=0; r<8 ; r++) {
+ TGA_WRITE_REG((vm >> r) & 1, TGA_CLOCK_REG);
+ }
+ for( r=0; r<8 ; r++) {
+ TGA_WRITE_REG((va >> r) & 1, TGA_CLOCK_REG);
+ }
+ for( r=0; r<7 ; r++) {
+ TGA_WRITE_REG((vr >> r) & 1, TGA_CLOCK_REG);
+ }
+ TGA_WRITE_REG(((vr >> 7) & 1)|2, TGA_CLOCK_REG);
}
-static int tgafbcon_switch(int con, struct fb_info *info)
+static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp, struct fb_info *info)
{
- /* Do we have to save the colormap? */
- if (fb_display[currcon].cmap.len)
- fb_get_cmap(&fb_display[currcon].cmap, 1, tgafb_getcolreg, info);
+ if (regno > 255)
+ return 1;
+ *red = (palette[regno].red<<8) | palette[regno].red;
+ *green = (palette[regno].green<<8) | palette[regno].green;
+ *blue = (palette[regno].blue<<8) | palette[regno].blue;
+ *transp = 0;
+ return 0;
+}
+
+
+static int tgafb_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;
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+
+#ifdef FBCON_HAS_CFB32
+ if (regno < 16 && fb_info.tga_type != 0)
+ fbcon_cfb32_cmap[regno] = (red << 16) | (green << 8) | blue;
+#endif
+
+ if (fb_info.tga_type == 0) { /* 8-plane */
+ BT485_WRITE(regno, BT485_ADDR_PAL_WRITE);
+ TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
+ TGA_WRITE_REG(red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ TGA_WRITE_REG(green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ TGA_WRITE_REG(blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ }
+ /* How to set a single color register on 24-plane cards?? */
- currcon = con;
- /* Install new colormap */
- do_install_cmap(con, info);
return 0;
}
+#if 1
/*
- * Update the `var' structure (called by fbcon.c)
+ * FIXME: since I don't know how to set a single arbitrary color register
+ * on 24-plane cards, all color palette registers have to be updated
*/
-static int tgafbcon_updatevar(int con, struct fb_info *info)
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
{
- /* Nothing */
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap, 256, 0)))
+ return err;
+ }
+ if (con == currcon) { /* current console? */
+ err = fb_set_cmap(cmap, kspc, tgafb_setcolreg, info);
+#if 1
+ if (fb_info.tga_type != 0)
+ tgafb_update_palette();
+#endif
+ return err;
+ } else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
return 0;
}
- /*
- * Blank and unblank the display.
- */
+static void tgafb_update_palette(void)
+{
+ int i;
+
+ BT463_LOAD_ADDR(0x0000);
+ TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG);
+
+ for (i = 0; i < 256; i++) {
+ TGA_WRITE_REG(palette[i].red|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].green|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].blue|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ }
+}
+#endif
+
+
+static int tgafb_pan_display(const struct fb_var_screeninfo *var,
+ struct fb_info_gen *info)
+{
+ if (var->xoffset || var->yoffset)
+ return -EINVAL;
+ else
+ return 0;
+}
-static void tgafbcon_blank(int blank, struct fb_info *info)
+
+static int tgafb_blank(int blank, struct fb_info_gen *info)
{
static int tga_vesa_blanked = 0;
u32 vhcr, vvcr;
}
restore_flags(flags);
-}
-
- /*
- * 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, struct fb_info *info)
-{
- if (regno > 255)
- return 1;
- *red = (palette[regno].red<<8) | palette[regno].red;
- *green = (palette[regno].green<<8) | palette[regno].green;
- *blue = (palette[regno].blue<<8) | palette[regno].blue;
- *transp = 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 tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
- u_int transp, struct fb_info *info)
+static void tgafb_set_disp(const void *fb_par, struct display *disp,
+ struct fb_info_gen *info)
{
- if (regno > 255)
- return 1;
- red >>= 8;
- green >>= 8;
- blue >>= 8;
- palette[regno].red = red;
- palette[regno].green = green;
- palette[regno].blue = blue;
-
+ disp->screen_base = (char *)fb_info.tga_fb_base + dense_mem(fb_info.tga_fb_base);
+ switch (fb_info.tga_type) {
+#ifdef FBCON_HAS_CFB8
+ case 0: /* 8-plane */
+ disp->dispsw = &fbcon_cfb8;
+ break;
+#endif
#ifdef FBCON_HAS_CFB32
- if (regno < 16 && tga_type != 0)
- fbcon_cfb32_cmap[regno] = (red << 16) | (green << 8) | blue;
+ case 1: /* 24-plane */
+ case 3: /* 24plusZ */
+ disp->dispsw = &fbcon_cfb32;
+ disp->dispsw_data = &fbcon_cfb32_cmap;
+ break;
#endif
+ default:
+ disp->dispsw = &fbcon_dummy;
+ }
- if (tga_type == 0) { /* 8-plane */
- BT485_WRITE(regno, BT485_ADDR_PAL_WRITE);
- TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
- TGA_WRITE_REG(red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
- TGA_WRITE_REG(green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
- TGA_WRITE_REG(blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
- }
- /* How to set a single color register on 24-plane cards?? */
-
- return 0;
+ disp->scrollmode = SCROLL_YREDRAW;
}
-#if 1
+struct fbgen_hwswitch tgafb_hwswitch = {
+ tgafb_detect, tgafb_encode_fix, tgafb_decode_var, tgafb_encode_var, tgafb_get_par,
+ tgafb_set_par, tgafb_getcolreg, tgafb_setcolreg, tgafb_pan_display, tgafb_blank,
+ tgafb_set_disp
+};
+
+
/*
- * FIXME: since I don't know how to set a single arbitrary color register
- * on 24-plane cards, all color palette registers have to be updated
+ * Hardware Independent functions
*/
-static void tga_update_palette(void)
-{
- int i;
- BT463_LOAD_ADDR(0x0000);
- TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG);
+ /*
+ * Frame buffer operations
+ */
- for (i = 0; i < 256; i++) {
- TGA_WRITE_REG(palette[i].red|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
- TGA_WRITE_REG(palette[i].green|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
- TGA_WRITE_REG(palette[i].blue|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
- }
+static int tgafb_open(struct fb_info *info, int user)
+{
+ MOD_INC_USE_COUNT;
+ return(0);
}
-#endif
+
-static void do_install_cmap(int con, struct fb_info *info)
+static int tgafb_release(struct fb_info *info, int user)
{
- if (con != currcon)
- return;
- if (fb_display[con].cmap.len)
- fb_set_cmap(&fb_display[con].cmap, 1, tgafb_setcolreg, info);
- else
- fb_set_cmap(fb_default_cmap(256), 1, tgafb_setcolreg, info);
-#if 1
- if (tga_type != 0)
- tga_update_palette();
-#endif
+ MOD_DEC_USE_COUNT;
+ return(0);
}
-#if 0 /* No cursor stuff yet */
-/*
- * Hide the cursor from view, during blanking, usually...
- */
-void
-hide_cursor(void)
-{
- unsigned long flags;
- save_flags(flags); cli();
+static struct fb_ops tgafb_ops = {
+ tgafb_open, tgafb_release, fbgen_get_fix, fbgen_get_var, fbgen_set_var,
+ fbgen_get_cmap, tgafb_set_cmap, fbgen_pan_display, fbgen_ioctl
+};
- if (tga_type == 0) {
- BT485_WRITE(0x20, BT485_CMD_2);
- } else {
- TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */
- }
- restore_flags(flags);
+ /*
+ * Setup
+ */
+
+__initfunc(void tgafb_setup(char *options, int *ints)) {
+ char *this_opt;
+ int i;
+
+ if (options && *options)
+ for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+ if (!*this_opt) continue;
+
+ if (!strncmp(this_opt, "font:", 5))
+ strncpy(default_fontname, this_opt+5, sizeof default_fontname);
+ else if (!strncmp(this_opt, "mode:", 5)) {
+ for (i = 0; i < NUM_TOTAL_MODES; i++) {
+ if (!strcmp(this_opt+5, tgafb_predefined[i].name))
+ default_var = tgafb_predefined[i].var;
+ default_var_valid = 1;
+ }
+ } else {
+ printk(KERN_ERR "tgafb: unknown parameter %s\n", this_opt);
+ }
+ }
}
-void
-set_cursor(int currcons)
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(void tgafb_init(void))
{
- unsigned int idx, xt, yt, row, col;
- unsigned long flags;
+ struct pci_dev *pdev;
- if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
- return;
+ pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL);
+ if (!pdev)
+ return;
+ fb_info.tga_mem_base = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
+#ifdef DEBUG
+ printk("tgafb_init: mem_base 0x%x\n", fb_info.tga_mem_base);
+#endif /* DEBUG */
- save_flags(flags); cli();
+ fb_info.tga_type = (readl((unsigned long)fb_info.tga_mem_base) >> 12) & 0x0f;
+ fb_info.tga_regs_base = ((unsigned long)fb_info.tga_mem_base + TGA_REGS_OFFSET);
+ fb_info.tga_fb_base = ((unsigned long)fb_info.tga_mem_base + fb_offset_presets[fb_info.tga_type]);
- if (deccm) {
- idx = (pos - video_mem_base) >> 1;
- col = idx % 80;
- row = (idx - col) / 80;
+ /* XXX Why the fuck is it called modename if it identifies the board? */
+ strcpy (fb_info.gen.info.modename,"DEC 21030 TGA ");
+ switch (fb_info.tga_type)
+ {
+ case 0: /* 8-plane */
+ strcat (fb_info.gen.info.modename, "8-plane");
+ break;
- if (tga_type == 0) { /* 8-plane */
+ case 1:
+ strcat (fb_info.gen.info.modename, "24-plane");
+ break;
- xt = col * TGA_F_WIDTH + 64;
- yt = row * TGA_F_HEIGHT_PADDED + 64;
+ case 3:
+ strcat (fb_info.gen.info.modename, "24plusZ");
+ break;
+ }
- /* make sure it's enabled */
- BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */
+ fb_info.gen.info.node = -1;
+ fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
+ fb_info.gen.info.fbops = &tgafb_ops;
+ fb_info.gen.info.disp = &disp;
+ fb_info.gen.info.changevar = NULL;
+ fb_info.gen.info.switch_con = &fbgen_switch;
+ fb_info.gen.info.updatevar = &fbgen_update_var;
+ fb_info.gen.info.blank = &fbgen_blank;
+ strcpy(fb_info.gen.info.fontname, default_fontname);
+ fb_info.gen.parsize = sizeof (struct tgafb_par);
+ fb_info.gen.fbhw = &tgafb_hwswitch;
+ fb_info.gen.fbhw->detect();
+
+ /* This should give a reasonable default video mode */
+ if (!default_var_valid)
+ default_var = tgafb_predefined[0].var;
+ fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
+ disp.var.activate = FB_ACTIVATE_NOW;
+ fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
+ fbgen_set_disp(-1, &fb_info.gen);
+ fbgen_install_cmap(0, &fb_info.gen);
+ if (register_framebuffer(&fb_info.gen.info) < 0)
+ return;
+ printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.gen.info.node),
+ fb_info.gen.info.modename);
+}
- BT485_WRITE(xt, BT485_CUR_LOW_X);
- BT485_WRITE((xt >> 8), BT485_CUR_HIGH_X);
- BT485_WRITE(yt, BT485_CUR_LOW_Y);
- BT485_WRITE((yt >> 8), BT485_CUR_HIGH_Y);
- } else {
+ /*
+ * Cleanup
+ */
- xt = col * TGA_F_WIDTH + 144;
- yt = row * TGA_F_HEIGHT_PADDED + 35;
+void tgafb_cleanup(struct fb_info *info)
+{
+ unregister_framebuffer(info);
+}
- TGA_WRITE_REG(0x05, TGA_VALID_REG); /* SCANNING and CURSOR */
- TGA_WRITE_REG(xt | (yt << 12), TGA_CURSOR_XY_REG);
- }
- } else
- hide_cursor();
- restore_flags(flags);
+ /*
+ * Modularisation
+ */
+
+#ifdef MODULE
+int init_module(void)
+{
+ tgafb_init();
+ return 0;
}
-#endif
+void cleanup_module(void)
+{
+ tgafb_cleanup(void);
+}
+#endif /* MODULE */
+
--- /dev/null
+/*
+ * linux/drivers/video/tgafb.h -- DEC 21030 TGA frame buffer device
+ *
+ * Copyright (C) 1999 Martin Lucina, Tom Zerucha
+ *
+ * $Id: tgafb.h,v 1.4 1999/05/15 08:44:31 mato Exp $
+ *
+ * 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 TGAFB_H
+#define TGAFB_H
+
+ /*
+ * TGA hardware description (minimal)
+ */
+
+
+ /*
+ * Offsets within Memory Space
+ */
+
+#define TGA_ROM_OFFSET 0x0000000
+#define TGA_REGS_OFFSET 0x0100000
+#define TGA_8PLANE_FB_OFFSET 0x0200000
+#define TGA_24PLANE_FB_OFFSET 0x0804000
+#define TGA_24PLUSZ_FB_OFFSET 0x1004000
+
+#define TGA_PLANEMASK_REG 0x0028
+#define TGA_MODE_REG 0x0030
+#define TGA_RASTEROP_REG 0x0034
+#define TGA_PIXELSHIFT_REG 0x0038
+#define TGA_DEEP_REG 0x0050
+#define TGA_PIXELMASK_REG 0x005c
+#define TGA_CURSOR_BASE_REG 0x0060
+#define TGA_HORIZ_REG 0x0064
+#define TGA_VERT_REG 0x0068
+#define TGA_BASE_ADDR_REG 0x006c
+#define TGA_VALID_REG 0x0070
+#define TGA_CURSOR_XY_REG 0x0074
+#define TGA_INTR_STAT_REG 0x007c
+#define TGA_RAMDAC_SETUP_REG 0x00c0
+#define TGA_BLOCK_COLOR0_REG 0x0140
+#define TGA_BLOCK_COLOR1_REG 0x0144
+#define TGA_CLOCK_REG 0x01e8
+#define TGA_RAMDAC_REG 0x01f0
+#define TGA_CMD_STAT_REG 0x01f8
+
+
+ /*
+ * useful defines for managing the video timing registers
+ */
+
+#define TGA_HORIZ_ODD 0x80000000
+#define TGA_HORIZ_POLARITY 0x40000000
+#define TGA_HORIZ_ACT_MSB 0x30000000
+#define TGA_HORIZ_BP 0x0fe00000
+#define TGA_HORIZ_SYNC 0x001fc000
+#define TGA_HORIZ_FP 0x00007c00
+#define TGA_HORIZ_ACT_LSB 0x000001ff
+
+#define TGA_VERT_SE 0x80000000
+#define TGA_VERT_POLARITY 0x40000000
+#define TGA_VERT_RESERVED 0x30000000
+#define TGA_VERT_BP 0x0fc00000
+#define TGA_VERT_SYNC 0x003f0000
+#define TGA_VERT_FP 0x0000f800
+#define TGA_VERT_ACTIVE 0x000007ff
+
+
+ /*
+ * useful defines for managing the ICS1562 PLL clock
+ */
+
+#define TGA_PLL_BASE_FREQ 14318 /* .18 */
+#define TGA_PLL_MAX_FREQ 230000
+
+
+ /*
+ * useful defines for managing the BT485 on the 8-plane TGA
+ */
+
+#define BT485_READ_BIT 0x01
+#define BT485_WRITE_BIT 0x00
+
+#define BT485_ADDR_PAL_WRITE 0x00
+#define BT485_DATA_PAL 0x02
+#define BT485_PIXEL_MASK 0x04
+#define BT485_ADDR_PAL_READ 0x06
+#define BT485_ADDR_CUR_WRITE 0x08
+#define BT485_DATA_CUR 0x0a
+#define BT485_CMD_0 0x0c
+#define BT485_ADDR_CUR_READ 0x0e
+#define BT485_CMD_1 0x10
+#define BT485_CMD_2 0x12
+#define BT485_STATUS 0x14
+#define BT485_CMD_3 0x14
+#define BT485_CUR_RAM 0x16
+#define BT485_CUR_LOW_X 0x18
+#define BT485_CUR_HIGH_X 0x1a
+#define BT485_CUR_LOW_Y 0x1c
+#define BT485_CUR_HIGH_Y 0x1e
+
+
+ /*
+ * useful defines for managing the BT463 on the 24-plane TGAs
+ */
+
+#define BT463_ADDR_LO 0x0
+#define BT463_ADDR_HI 0x1
+#define BT463_REG_ACC 0x2
+#define BT463_PALETTE 0x3
+
+#define BT463_CUR_CLR_0 0x0100
+#define BT463_CUR_CLR_1 0x0101
+
+#define BT463_CMD_REG_0 0x0201
+#define BT463_CMD_REG_1 0x0202
+#define BT463_CMD_REG_2 0x0203
+
+#define BT463_READ_MASK_0 0x0205
+#define BT463_READ_MASK_1 0x0206
+#define BT463_READ_MASK_2 0x0207
+#define BT463_READ_MASK_3 0x0208
+
+#define BT463_BLINK_MASK_0 0x0209
+#define BT463_BLINK_MASK_1 0x020a
+#define BT463_BLINK_MASK_2 0x020b
+#define BT463_BLINK_MASK_3 0x020c
+
+#define BT463_WINDOW_TYPE_BASE 0x0300
+
+
+ /*
+ * Macros for reading/writing TGA and RAMDAC registers
+ */
+
+#define TGA_WRITE_REG(v,r) \
+ { writel((v), fb_info.tga_regs_base+(r)); mb(); }
+
+#define TGA_READ_REG(r) readl(fb_info.tga_regs_base+(r))
+
+#define BT485_WRITE(v,r) \
+ TGA_WRITE_REG((r),TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG(((v)&0xff)|((r)<<8),TGA_RAMDAC_REG);
+
+#define BT463_LOAD_ADDR(a) \
+ TGA_WRITE_REG(BT463_ADDR_LO<<2, TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG((BT463_ADDR_LO<<10)|((a)&0xff), TGA_RAMDAC_REG); \
+ TGA_WRITE_REG(BT463_ADDR_HI<<2, TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG((BT463_ADDR_HI<<10)|(((a)>>8)&0xff), TGA_RAMDAC_REG);
+
+#define BT463_WRITE(m,a,v) \
+ BT463_LOAD_ADDR((a)); \
+ TGA_WRITE_REG(((m)<<2),TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG(((m)<<10)|((v)&0xff),TGA_RAMDAC_REG);
+
+
+ /*
+ * This structure describes the board.
+ */
+
+struct tgafb_info {
+ /* Use the generic framebuffer ops */
+ struct fb_info_gen gen;
+
+ /* Device dependent information */
+ int tga_type; /* TGA type: {8plane, 24plane, 24plusZ} */
+ unsigned int tga_mem_base;
+ unsigned long tga_fb_base;
+ unsigned long tga_regs_base;
+ struct fb_var_screeninfo default_var; /* default video mode */
+};
+
+
+ /*
+ * This structure uniquely defines a video mode.
+ */
+
+struct tgafb_par {
+ int xres, yres; /* resolution in pixels */
+ unsigned int htimings; /* horizontal timing register */
+ unsigned int vtimings; /* vertical timing register */
+ unsigned int pll_freq; /* pixclock in mhz */
+ unsigned int bits_per_pixel; /* bits per pixel */
+};
+
+#endif /* TGAFB_H */
return retval;
}
- current->mm->start_stack = create_aout_tables(bprm->p, bprm);
+ current->mm->start_stack =
+ (unsigned long) create_aout_tables((char *) bprm->p, bprm);
#ifdef __alpha__
regs->gp = ex.a_gpvalue;
#endif
bh = bh->b_this_page;
} while (bh != head);
if (rw == READ)
- ++current->maj_flt;
+ ++current->mm->maj_flt;
if ((rw == READ) && nr) {
if (Page_Uptodate(page))
BUG();
nr++;
} while (iblock++, (bh = bh->b_this_page) != head);
- ++current->maj_flt;
+ ++current->mm->maj_flt;
if (nr) {
if (Page_Uptodate(page))
BUG();
case EXT2_IOC_GETFLAGS:
flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
return put_user(inode->u.ext2_i.i_flags, (int *) arg);
- case EXT2_IOC_SETFLAGS:
+ case EXT2_IOC_SETFLAGS: {
+ unsigned int oldflags;
+
+ if (IS_RDONLY(inode))
+ return -EROFS;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
if (get_user(flags, (int *) arg))
return -EFAULT;
- flags = flags & EXT2_FL_USER_MODIFIABLE;
+
+ oldflags = inode->u.ext2_i.i_flags;
+
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the super user when the security level is zero.
+ * the relevant capability.
+ *
+ * This test looks nicer. Thanks to Pauline Middelink
*/
- if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^
- (inode->u.ext2_i.i_flags &
- (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
- /* This test looks nicer. Thanks to Pauline Middelink */
+ if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
- } else
- if ((current->fsuid != inode->i_uid) &&
- !capable(CAP_FOWNER))
- return -EPERM;
- if (IS_RDONLY(inode))
- return -EROFS;
- inode->u.ext2_i.i_flags = (inode->u.ext2_i.i_flags &
- ~EXT2_FL_USER_MODIFIABLE) | flags;
+ }
+
+ flags = flags & EXT2_FL_USER_MODIFIABLE;
+ flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
+ inode->u.ext2_i.i_flags = flags;
+
if (flags & EXT2_SYNC_FL)
inode->i_flags |= MS_SYNCHRONOUS;
else
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
return 0;
+ }
case EXT2_IOC_GETVERSION:
return put_user(inode->i_generation, (int *) arg);
case EXT2_IOC_SETVERSION:
NULL, /* advise */
fat_file_mmap_nopage, /* nopage */
NULL, /* wppage */
- NULL, /* swapout */
- NULL, /* swapin */
+ NULL /* swapout */
};
/*
return (cfl);
}
-int locks_verify_locked(struct inode *inode)
-{
- /* Candidates for mandatory locking have the setgid bit set
- * but no group execute bit - an otherwise meaningless combination.
- */
- if (IS_MANDLOCK(inode) &&
- (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
- return (locks_mandatory_locked(inode));
- return (0);
-}
-
-int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
- loff_t offset, size_t count)
-{
- /* Candidates for mandatory locking have the setgid bit set
- * but no group execute bit - an otherwise meaningless combination.
- */
- if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
- int retval;
- lock_kernel();
- retval = locks_mandatory_area(read_write, inode, filp, offset, count);
- unlock_kernel();
- return retval;
- }
- return 0;
-}
-
int locks_mandatory_locked(struct inode *inode)
{
fl_owner_t owner = current->files;
struct file_lock *fl;
- /* Search the lock list for this inode for any POSIX locks.
+ /*
+ * Search the lock list for this inode for any POSIX locks.
*/
+ lock_kernel();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if (fl->fl_owner != owner)
- return (-EAGAIN);
+ break;
}
- return (0);
+ unlock_kernel();
+ return fl ? -EAGAIN : 0;
}
int locks_mandatory_area(int read_write, struct inode *inode,
{
struct file_lock *fl;
struct file_lock tfl;
+ int error;
memset(&tfl, 0, sizeof(tfl));
tfl.fl_start = offset;
tfl.fl_end = offset + count - 1;
+ error = 0;
+ lock_kernel();
+
repeat:
/* Search the lock list for this inode for locks that conflict with
* the proposed read/write.
*/
- for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ for (fl = inode->i_flock; ; fl = fl->fl_next) {
+ error = 0;
+ if (!fl)
+ break;
if (!(fl->fl_flags & FL_POSIX))
continue;
/* Block for writes against a "read" lock,
* and both reads and writes against a "write" lock.
*/
if (posix_locks_conflict(fl, &tfl)) {
+ error = -EAGAIN;
if (filp && (filp->f_flags & O_NONBLOCK))
- return (-EAGAIN);
+ break;
+ error = -ERESTARTSYS;
if (signal_pending(current))
- return (-ERESTARTSYS);
+ break;
+ error = -EDEADLK;
if (posix_locks_deadlock(&tfl, fl))
- return (-EDEADLK);
+ break;
locks_insert_block(fl, &tfl);
interruptible_sleep_on(&tfl.fl_wait);
locks_delete_block(fl, &tfl);
- if (signal_pending(current))
- return (-ERESTARTSYS);
- /* If we've been sleeping someone might have
+ /*
+ * If we've been sleeping someone might have
* changed the permissions behind our back.
*/
if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
goto repeat;
}
}
- return (0);
+ unlock_kernel();
+ return error;
}
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
NULL, /* advise */
ncp_file_mmap_nopage, /* nopage */
NULL, /* wppage */
- NULL, /* swapout */
- NULL, /* swapin */
+ NULL /* swapout */
};
tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0,
tty_pgrp,
tsk->flags,
- tsk->min_flt,
- tsk->cmin_flt,
- tsk->maj_flt,
- tsk->cmaj_flt,
+ tsk->mm ? tsk->mm->min_flt : 0,
+ tsk->mm ? tsk->mm->cmin_flt : 0,
+ tsk->mm ? tsk->mm->maj_flt : 0,
+ tsk->mm ? tsk->mm->cmaj_flt : 0,
tsk->times.tms_utime,
tsk->times.tms_stime,
tsk->times.tms_cutime,
sigign .sig[0] & 0x7fffffffUL,
sigcatch .sig[0] & 0x7fffffffUL,
wchan,
- tsk->nswap,
- tsk->cnswap,
+ tsk->mm ? tsk->mm->nswap : 0,
+ tsk->mm ? tsk->mm->cnswap : 0,
tsk->exit_signal,
tsk->processor);
}
return -ENOMEM;
if (!pte_present(*src_table))
- handle_mm_fault(tsk, src_vma, stmp, 1);
+ handle_mm_fault(tsk->mm, src_vma, stmp, 1);
if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
- handle_mm_fault(tsk, src_vma, stmp, 1);
+ handle_mm_fault(tsk->mm, src_vma, stmp, 1);
set_pte(src_table, pte_mkdirty(*src_table));
set_pte(dest_table, *src_table);
}
if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) {
printk("You didn't specify the type of your ufs filesystem\n\n"
- " mount -t ufs -o ufstype=sun|sunx86|44bsd|old|nextstep|netxstep-cd|openstep ...\n\n"
+ "mount -t ufs -o ufstype="
+ "sun|sunx86|44bsd|old|nextstep|netxstep-cd|openstep ...\n\n"
">>>WARNING<<< Wrong ufstype may corrupt your filesystem, "
"default is ufstype=old\n");
ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD);
#ifdef __KERNEL__
-#include <linux/config.h>
#include <asm/head.h> /* for KERNBASE */
#include <asm/btfixup.h>
void *private_data;
};
+#define get_file(x) atomic_inc(&(x)->f_count)
+#define file_count(x) atomic_read(&(x)->f_count)
+
extern int init_private_file(struct file *, struct dentry *, int);
#define FL_POSIX 1
extern int locks_mandatory_locked(struct inode *);
extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
-extern inline int locks_verify_locked(struct inode *inode)
+/*
+ * Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit - an otherwise meaningless combination.
+ */
+#define MANDATORY_LOCK(inode) \
+ (IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+
+static inline int locks_verify_locked(struct inode *inode)
{
- /* Candidates for mandatory locking have the setgid bit set
- * but no group execute bit - an otherwise meaningless combination.
- */
- if (IS_MANDLOCK(inode) &&
- (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
- return (locks_mandatory_locked(inode));
- return (0);
+ if (MANDATORY_LOCK(inode))
+ return locks_mandatory_locked(inode);
+ return 0;
}
extern inline int locks_verify_area(int read_write, struct inode *inode,
struct file *filp, loff_t offset,
size_t count)
{
- /* Candidates for mandatory locking have the setgid bit set
- * but no group execute bit - an otherwise meaningless combination.
- */
- if (IS_MANDLOCK(inode) &&
- (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
- return (locks_mandatory_area(read_write, inode, filp, offset,
- count));
- return (0);
+ if (inode->i_flock && MANDATORY_LOCK(inode))
+ return locks_mandatory_area(read_write, inode, filp, offset, count);
+ return 0;
}
int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf);
void ide_delay_50ms (void);
+int ide_config_drive_speed (ide_drive_t *drive, byte speed);
/*
* ide_system_bus_speed() returns what we think is the system VESA/PCI
#ifdef __KERNEL__
-#ifndef STANDALONE
-#include <linux/config.h>
-#endif
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/major.h>
#define LP_ABORT 0x0040
#define LP_CAREFUL 0x0080 /* obsoleted -arca */
#define LP_ABORTOPEN 0x0100
-#define LP_TRUST_IRQ 0x0200
+
+#define LP_TRUST_IRQ_ 0x0200 /* obsolete */
+#define LP_NO_REVERSE 0x0400 /* No reverse mode available. */
+#define LP_DATA_AVAIL 0x0800 /* Data is available. */
+#define LP_HAVE_PORT_BIT 12 /* (0x1000) Port is claimed. */
+#define LP_PORT_BUSY (1<<13) /* Reading or writing. */
/* timeout for each character. This is relative to bus cycles -- it
* is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
#endif
#define LPGETFLAGS 0x060e /* get status flags */
-#define LPTRUSTIRQ 0x060f /* set/unset the LP_TRUST_IRQ flag */
/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
This is also used for re-checking error conditions if LP_ABORT is
#ifdef LP_STATS
#define LP_STAT(minor) lp_table[(minor)].stats /* statistics area */
#endif
-#define LP_BUFFER_SIZE 256
+#define LP_BUFFER_SIZE PAGE_SIZE
#define LP_BASE(x) lp_table[(x)].dev->port->base
unsigned int runchars;
struct lp_stats stats;
#endif
- wait_queue_head_t wait_q;
+ wait_queue_head_t waitq;
unsigned int last_error;
- volatile unsigned int irq_detected:1;
- volatile unsigned int irq_missed:1;
+ struct semaphore port_mutex;
+ wait_queue_head_t dataq;
};
/*
*/
#define LP_DELAY 50
-#define LP_POLLED(minor) (lp_table[(minor)].dev->port->irq == PARPORT_IRQ_NONE)
-#define LP_PREEMPTED(minor) (lp_table[(minor)].dev->port->waithead != NULL)
-
/*
* function prototypes
*/
unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address,
unsigned long page);
int (*swapout)(struct vm_area_struct *, struct page *);
- pte_t (*swapin)(struct vm_area_struct *, unsigned long, unsigned long);
};
/*
extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);
extern void vmtruncate(struct inode * inode, unsigned long offset);
-extern int handle_mm_fault(struct task_struct *tsk,struct vm_area_struct *vma, unsigned long address, int write_access);
+extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access);
extern void make_pages_present(unsigned long addr, unsigned long end);
extern int pgt_cache_water[2];
return vma;
}
-extern struct vm_area_struct *find_extend_vma(struct task_struct *tsk, unsigned long addr);
+extern struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr);
#define buffer_under_min() ((atomic_read(&buffermem) >> PAGE_SHIFT) * 100 < \
buffer_mem.min_percent * num_physpages)
#define PARPORT_CONTROL_AUTOFD 0x2
#define PARPORT_CONTROL_INIT 0x4
#define PARPORT_CONTROL_SELECT 0x8
-#define PARPORT_CONTROL_INTEN 0x10
-#define PARPORT_CONTROL_DIRECTION 0x20
#define PARPORT_STATUS_ERROR 0x8
#define PARPORT_STATUS_SELECT 0x10
PARPORT_CLASS_SCANNER,
PARPORT_CLASS_DIGCAM,
PARPORT_CLASS_OTHER, /* Anything else */
- PARPORT_CLASS_UNSPEC /* No CLS field in ID */
+ PARPORT_CLASS_UNSPEC, /* No CLS field in ID */
+ PARPORT_CLASS_SCSIADAPTER
} parport_device_class;
-/* The "modes" entry in parport is a bit field representing the following
- * modes.
- * Note that PARPORT_MODE_PCECPEPP is for the SMC EPP+ECP mode which is NOT
- * 100% compatible with EPP.
- */
-#define PARPORT_MODE_PCSPP 0x0001
-#define PARPORT_MODE_PCPS2 0x0002
-#define PARPORT_MODE_PCEPP 0x0004
-#define PARPORT_MODE_PCECP 0x0008
-#define PARPORT_MODE_PCECPEPP 0x0010
-#define PARPORT_MODE_PCECR 0x0020 /* ECR Register Exists */
-#define PARPORT_MODE_PCECPPS2 0x0040
+/* The "modes" entry in parport is a bit field representing the
+ capabilities of the hardware. */
+#define PARPORT_MODE_PCSPP (1<<0) /* IBM PC registers available. */
+#define PARPORT_MODE_TRISTATE (1<<1) /* Can tristate. */
+#define PARPORT_MODE_EPP (1<<2) /* Hardware EPP. */
+#define PARPORT_MODE_ECP (1<<3) /* Hardware ECP. */
+#define PARPORT_MODE_COMPAT (1<<4) /* Hardware 'printer protocol'. */
+#define PARPORT_MODE_DMA (1<<5) /* Hardware can DMA. */
+
+/* IEEE1284 modes:
+ Nibble mode, byte mode, ECP, ECPRLE and EPP are their own
+ 'extensibility request' values. Others are special.
+ 'Real' ECP modes must have the IEEE1284_MODE_ECP bit set. */
+#define IEEE1284_MODE_NIBBLE 0
+#define IEEE1284_MODE_BYTE (1<<0)
+#define IEEE1284_MODE_COMPAT (1<<8)
+#define IEEE1284_MODE_BECP (1<<9) /* Bounded ECP mode */
+#define IEEE1284_MODE_ECP (1<<4)
+#define IEEE1284_MODE_ECPRLE (IEEE1284_MODE_ECP | (1<<5))
+#define IEEE1284_MODE_ECPSWE (1<<10) /* Software-emulated */
+#define IEEE1284_MODE_EPP (1<<6)
+#define IEEE1284_MODE_EPPSL (1<<11) /* EPP 1.7 */
+#define IEEE1284_MODE_EPPSWE (1<<12) /* Software-emulated */
+#define IEEE1284_DEVICEID (1<<2) /* This is a flag */
/* The rest is for the kernel only */
#ifdef __KERNEL__
#include <asm/system.h>
#include <asm/ptrace.h>
#include <asm/spinlock.h>
+#include <asm/semaphore.h>
#include <linux/proc_fs.h>
#include <linux/config.h>
/* Define this later. */
struct parport;
+struct pardevice;
struct pc_parport_state {
unsigned int ctr;
unsigned int ecr;
};
+struct ax_parport_state {
+ unsigned int ctr;
+ unsigned int ecr;
+ unsigned int dcsr;
+};
+
+/* used by both parport_amiga and parport_mfc3 */
+struct amiga_parport_state {
+ unsigned char data; /* ciaa.prb */
+ unsigned char datadir; /* ciaa.ddrb */
+ unsigned char status; /* ciab.pra & 7 */
+ unsigned char statusdir;/* ciab.ddrb & 7 */
+};
+
struct parport_state {
union {
struct pc_parport_state pc;
/* ARC has no state. */
- /* AX uses same state information as PC */
+ struct ax_parport_state ax;
+ struct amiga_parport_state amiga;
+ /* Atari has not state. */
void *misc;
} u;
};
struct parport_operations {
+ /* IBM PC-style virtual registers. */
void (*write_data)(struct parport *, unsigned char);
unsigned char (*read_data)(struct parport *);
+
void (*write_control)(struct parport *, unsigned char);
unsigned char (*read_control)(struct parport *);
- unsigned char (*frob_control)(struct parport *, unsigned char mask, unsigned char val);
- void (*write_econtrol)(struct parport *, unsigned char);
- unsigned char (*read_econtrol)(struct parport *);
- unsigned char (*frob_econtrol)(struct parport *, unsigned char mask, unsigned char val);
- void (*write_status)(struct parport *, unsigned char);
+ unsigned char (*frob_control)(struct parport *, unsigned char mask,
+ unsigned char val);
+
unsigned char (*read_status)(struct parport *);
- void (*write_fifo)(struct parport *, unsigned char);
- unsigned char (*read_fifo)(struct parport *);
- void (*change_mode)(struct parport *, int);
+ /* IRQs. */
+ void (*enable_irq)(struct parport *);
+ void (*disable_irq)(struct parport *);
- void (*epp_write_data)(struct parport *, unsigned char);
- unsigned char (*epp_read_data)(struct parport *);
- void (*epp_write_addr)(struct parport *, unsigned char);
- unsigned char (*epp_read_addr)(struct parport *);
- int (*epp_check_timeout)(struct parport *);
- size_t (*epp_write_block)(struct parport *, void *, size_t);
- size_t (*epp_read_block)(struct parport *, void *, size_t);
+ /* Data direction. */
+ void (*data_forward) (struct parport *);
+ void (*data_reverse) (struct parport *);
- int (*ecp_write_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
- int (*ecp_read_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
+ /* For core parport code. */
+ void (*interrupt)(int, void *, struct pt_regs *); /* ? */
- void (*init_state)(struct parport_state *);
+ void (*init_state)(struct pardevice *, struct parport_state *);
void (*save_state)(struct parport *, struct parport_state *);
void (*restore_state)(struct parport *, struct parport_state *);
- void (*enable_irq)(struct parport *);
- void (*disable_irq)(struct parport *);
- void (*interrupt)(int, void *, struct pt_regs *);
-
void (*inc_use_count)(void);
void (*dec_use_count)(void);
- void (*fill_inode)(struct inode *inode, int fill);
+ void (*fill_inode)(struct inode *inode, int fill); /* ? */
+
+ /* Block read/write */
+ size_t (*epp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*epp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len,
+ int flags);
+
+ size_t (*ecp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*ecp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+
+ size_t (*compat_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*nibble_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
+ size_t (*byte_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
};
struct parport_device_info {
struct pardevice {
const char *name;
struct parport *port;
+ int daisy;
int (*preempt)(void *);
void (*wakeup)(void *);
void *private;
wait_queue_head_t wait_q;
unsigned long int time;
unsigned long int timeslice;
+ volatile long int timeout;
unsigned int waiting;
struct pardevice *waitprev;
struct pardevice *waitnext;
- void * sysctl_table;
+ void * sysctl_table;
};
-/* Directory information for the /proc interface */
-struct parport_dir {
- struct proc_dir_entry *entry; /* Directory /proc/parport/X */
- struct proc_dir_entry *irq; /* .../irq */
- struct proc_dir_entry *devices; /* .../devices */
- struct proc_dir_entry *hardware; /* .../hardware */
- struct proc_dir_entry *probe; /* .../autoprobe */
- char name[4];
+/* IEEE1284 information */
+
+/* IEEE1284 phases */
+enum ieee1284_phase {
+ IEEE1284_PH_FWD_DATA,
+ IEEE1284_PH_FWD_IDLE,
+ IEEE1284_PH_TERMINATE,
+ IEEE1284_PH_NEGOTIATION,
+ IEEE1284_PH_HBUSY_DNA,
+ IEEE1284_PH_REV_IDLE,
+ IEEE1284_PH_HBUSY_DAVAIL,
+ IEEE1284_PH_REV_DATA,
+ IEEE1284_PH_ECP_SETUP,
+ IEEE1284_PH_ECP_FWD_TO_REV,
+ IEEE1284_PH_ECP_REV_TO_FWD
+};
+struct ieee1284_info {
+ int mode;
+ volatile enum ieee1284_phase phase;
+ struct semaphore irq;
};
/* A parallel port */
struct parport {
unsigned long base; /* base address */
- unsigned long base_hi; /* base address (ECR) */
+ unsigned long base_hi; /* base address (hi - ECR) */
unsigned int size; /* IO extent */
const char *name;
+ unsigned int modes;
int irq; /* interrupt (or -1 for none) */
int dma;
- unsigned int modes;
+ int muxport; /* which muxport (if any) this is */
+ int portnum; /* which physical parallel port (not mux) */
+
+ struct parport *physport;
+ /* If this is a non-default mux
+ parport, i.e. we're a clone of a real
+ physical port, this is a pointer to that
+ port. The locking is only done in the
+ real port. For a clone port, the
+ following structure members are
+ meaningless: devices, cad, muxsel,
+ waithead, waittail, flags, pdir,
+ ieee1284, *_lock.
+
+ It this is a default mux parport, or
+ there is no mux involved, this points to
+ ourself. */
struct pardevice *devices;
struct pardevice *cad; /* port owner */
+ int daisy; /* currently selected daisy addr */
+ int muxsel; /* currently selected mux port */
struct pardevice *waithead;
struct pardevice *waittail;
struct parport *next;
unsigned int flags;
- struct parport_dir pdir;
- struct parport_device_info probe_info;
+ void *sysctl_table;
+ struct parport_device_info probe_info[5]; /* 0-3 + non-IEEE1284.3 */
+ struct ieee1284_info ieee1284;
struct parport_operations *ops;
void *private_data; /* for lowlevel driver */
spinlock_t pardevice_lock;
spinlock_t waitlist_lock;
rwlock_t cad_lock;
- void * sysctl_table;
+
+ int spintime;
};
+#define DEFAULT_SPIN_TIME 500 /* us */
+
struct parport_driver {
const char *name;
void (*attach) (struct parport *);
struct parport_driver *next;
};
-/* parport_register_port registers a new parallel port at the given address (if
- * one does not already exist) and returns a pointer to it. This entails
- * claiming the I/O region, IRQ and DMA.
- * NULL is returned if initialisation fails.
- */
+/* parport_register_port registers a new parallel port at the given
+ address (if one does not already exist) and returns a pointer to it.
+ This entails claiming the I/O region, IRQ and DMA. NULL is returned
+ if initialisation fails. */
struct parport *parport_register_port(unsigned long base, int irq, int dma,
struct parport_operations *ops);
/* Unregister a port. */
extern void parport_unregister_port(struct parport *port);
-/* parport_in_use returns nonzero if there are devices attached to a port. */
+/* parport_in_use returns nonzero if there are devices attached to a
+ port. */
#define parport_in_use(x) ((x)->devices != NULL)
-/* parport_enumerate returns a pointer to the linked list of all the ports
- * in this machine.
- */
+/* parport_enumerate returns a pointer to the linked list of all the
+ ports in this machine. */
struct parport *parport_enumerate(void);
/* Register a new high-level driver. */
/* Unregister a high-level driver. */
extern void parport_unregister_driver (struct parport_driver *);
-/* parport_register_device declares that a device is connected to a port, and
- * tells the kernel all it needs to know.
- * pf is the preemption function (may be NULL for no callback)
- * kf is the wake-up function (may be NULL for no callback)
- * irq_func is the interrupt handler (may be NULL for no interrupts)
- * handle is a user pointer that gets handed to callback functions.
- */
+/* parport_register_device declares that a device is connected to a
+ port, and tells the kernel all it needs to know. pf is the
+ preemption function (may be NULL for no callback) kf is the wake-up
+ function (may be NULL for no callback) irq_func is the interrupt
+ handler (may be NULL for no interrupts) handle is a user pointer
+ that gets handed to callback functions. */
struct pardevice *parport_register_device(struct parport *port,
const char *name,
int (*pf)(void *), void (*kf)(void *),
/* parport_unregister unlinks a device from the chain. */
extern void parport_unregister_device(struct pardevice *dev);
-/* parport_claim tries to gain ownership of the port for a particular driver.
- * This may fail (return non-zero) if another driver is busy. If this
- * driver has registered an interrupt handler, it will be enabled.
- */
+/* parport_claim tries to gain ownership of the port for a particular
+ driver. This may fail (return non-zero) if another driver is busy.
+ If this driver has registered an interrupt handler, it will be
+ enabled. */
extern int parport_claim(struct pardevice *dev);
-/* parport_claim_or_block is the same, but sleeps if the port cannot be
- claimed. Return value is 1 if it slept, 0 normally and -errno on error. */
+/* parport_claim_or_block is the same, but sleeps if the port cannot
+ be claimed. Return value is 1 if it slept, 0 normally and -errno
+ on error. */
extern int parport_claim_or_block(struct pardevice *dev);
-/* parport_release reverses a previous parport_claim. This can never fail,
- * though the effects are undefined (except that they are bad) if you didn't
- * previously own the port. Once you have released the port you should make
- * sure that neither your code nor the hardware on the port tries to initiate
- * any communication without first re-claiming the port.
- * If you mess with the port state (enabling ECP for example) you should
- * clean up before releasing the port.
- */
+/* parport_release reverses a previous parport_claim. This can never
+ fail, though the effects are undefined (except that they are bad)
+ if you didn't previously own the port. Once you have released the
+ port you should make sure that neither your code nor the hardware
+ on the port tries to initiate any communication without first
+ re-claiming the port. If you mess with the port state (enabling
+ ECP for example) you should clean up before releasing the port. */
extern void parport_release(struct pardevice *dev);
/* parport_yield relinquishes the port if it would be helpful to other
- * drivers. The return value is the same as for parport_claim.
- */
+ drivers. The return value is the same as for parport_claim. */
extern __inline__ int parport_yield(struct pardevice *dev)
{
unsigned long int timeslip = (jiffies - dev->time);
}
/* parport_yield_blocking is the same but uses parport_claim_or_block
- * instead of parport_claim.
- */
+ instead of parport_claim. */
extern __inline__ int parport_yield_blocking(struct pardevice *dev)
{
unsigned long int timeslip = (jiffies - dev->time);
return parport_claim_or_block(dev);
}
-/*
- * Lowlevel drivers _can_ call this support function to handle irqs.
- */
-extern __inline__ void parport_generic_irq(int irq, struct parport *port,
- struct pt_regs *regs)
-{
- read_lock(&port->cad_lock);
- if (!port->cad)
- goto out_unlock;
- if (port->cad->irq_func)
- port->cad->irq_func(irq, port->cad->private, regs);
- else
- printk(KERN_ERR "%s: irq%d happened with irq_func NULL "
- "with %s as cad!\n", port->name, irq, port->cad->name);
- out_unlock:
- read_unlock(&port->cad_lock);
-}
-
/* Flags used to identify what a device does. */
#define PARPORT_DEV_TRAN 0 /* WARNING !! DEPRECATED !! */
#define PARPORT_DEV_LURK (1<<0) /* WARNING !! DEPRECATED !! */
#define PARPORT_DEV_EXCL (1<<1) /* Need exclusive access. */
-#define PARPORT_FLAG_COMA_ (1<<0) /* No longer used. */
#define PARPORT_FLAG_EXCL (1<<1) /* EXCL driver registered. */
extern int parport_parse_irqs(int, const char *[], int irqval[]);
-extern int parport_parse_dmas(int, const char *[], int irqval[]);
-extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
-extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
- char);
+extern int parport_parse_dmas(int, const char *[], int dmaval[]);
+
+/* IEEE1284 functions */
+extern void parport_ieee1284_interrupt (int, void *, struct pt_regs *);
+extern int parport_negotiate (struct parport *, int mode);
+extern ssize_t parport_write (struct parport *, const void *buf, size_t len);
+extern ssize_t parport_read (struct parport *, void *buf, size_t len);
+extern long parport_set_timeout (struct pardevice *, long inactivity);
+extern int parport_wait_event (struct parport *, long timeout);
+extern int parport_wait_peripheral (struct parport *port,
+ unsigned char mask,
+ unsigned char val);
+
+/* For architectural drivers */
+extern void parport_ieee1284_wakeup (struct parport *port);
+extern size_t parport_ieee1284_write_compat (struct parport *,
+ const void *, size_t, int);
+extern size_t parport_ieee1284_read_nibble (struct parport *,
+ void *, size_t, int);
+extern size_t parport_ieee1284_read_byte (struct parport *,
+ void *, size_t, int);
+extern size_t parport_ieee1284_ecp_read_data (struct parport *,
+ void *, size_t, int);
+extern size_t parport_ieee1284_ecp_write_data (struct parport *,
+ const void *, size_t, int);
+extern size_t parport_ieee1284_ecp_write_addr (struct parport *,
+ const void *, size_t, int);
+extern size_t parport_ieee1284_epp_write_data (struct parport *,
+ const void *, size_t, int);
+extern size_t parport_ieee1284_epp_read_data (struct parport *,
+ void *, size_t, int);
+extern size_t parport_ieee1284_epp_write_addr (struct parport *,
+ const void *, size_t, int);
+extern size_t parport_ieee1284_epp_read_addr (struct parport *,
+ void *, size_t, int);
+
+/* IEEE1284.3 functions */
+extern int parport_daisy_init (struct parport *port);
+extern void parport_daisy_fini (struct parport *port);
+extern struct pardevice *parport_open (int devnum, const char *name,
+ int (*pf) (void *),
+ void (*kf) (void *),
+ void (*irqf) (int, void *,
+ struct pt_regs *),
+ int flags, void *handle);
+extern void parport_close (struct pardevice *dev);
+extern ssize_t parport_device_id (int devnum, char *buffer, size_t len);
+extern int parport_device_num (int parport, int mux, int daisy);
+extern int parport_device_coords (int devnum, int *parport, int *mux,
+ int *daisy);
+extern void parport_daisy_deselect_all (struct parport *port);
+extern int parport_daisy_select (struct parport *port, int daisy, int mode);
+
+/* For finding devices based on their device ID. Example usage:
+ int devnum = -1;
+ while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM, devnum)) != -1) {
+ struct pardevice *dev = parport_open (devnum, ...);
+ ...
+ }
+*/
+extern int parport_find_device (const char *mfg, const char *mdl, int from);
+extern int parport_find_class (parport_device_class cls, int from);
+
+/* Lowlevel drivers _can_ call this support function to handle irqs. */
+extern __inline__ void parport_generic_irq(int irq, struct parport *port,
+ struct pt_regs *regs)
+{
+ parport_ieee1284_interrupt (irq, port, regs);
+ read_lock(&port->cad_lock);
+ if (port->cad && port->cad->irq_func)
+ port->cad->irq_func(irq, port->cad->private, regs);
+ read_unlock(&port->cad_lock);
+}
/* Prototypes from parport_procfs */
extern int parport_proc_register(struct parport *pp);
extern void dec_parport_count(void);
extern void inc_parport_count(void);
-extern int parport_probe(struct parport *port, char *buffer, int len);
-extern void parport_probe_one(struct parport *port);
-extern void (*parport_probe_hook)(struct parport *port);
-
/* If PC hardware is the only type supported, we can optimise a bit. */
-#if (defined(CONFIG_PARPORT_PC) || defined(CONFIG_PARPORT_PC_MODULE)) && !(defined(CONFIG_PARPORT_AX) || defined(CONFIG_PARPORT_AX_MODULE)) && !(defined(CONFIG_PARPORT_ARC) || defined(CONFIG_PARPORT_ARC_MODULE)) && !defined(CONFIG_PARPORT_OTHER)
+#if (defined(CONFIG_PARPORT_PC) || defined(CONFIG_PARPORT_PC_MODULE)) && !(defined(CONFIG_PARPORT_AX) || defined(CONFIG_PARPORT_AX_MODULE)) && !(defined(CONFIG_PARPORT_ARC) || defined(CONFIG_PARPORT_ARC_MODULE)) && !(defined(CONFIG_PARPORT_AMIGA) || defined(CONFIG_PARPORT_AMIGA_MODULE)) && !(defined(CONFIG_PARPORT_MFC3) || defined(CONFIG_PARPORT_MFC3_MODULE)) && !(defined(CONFIG_PARPORT_ATARI) || defined(CONFIG_PARPORT_ATARI_MODULE)) && !defined(CONFIG_PARPORT_OTHER)
#undef PARPORT_NEED_GENERIC_OPS
#include <linux/parport_pc.h>
#define parport_write_data(p,x) parport_pc_write_data(p,x)
#define parport_write_control(p,x) parport_pc_write_control(p,x)
#define parport_read_control(p) parport_pc_read_control(p)
#define parport_frob_control(p,m,v) parport_pc_frob_control(p,m,v)
-#define parport_write_econtrol(p,x) parport_pc_write_econtrol(p,x)
-#define parport_read_econtrol(p) parport_pc_read_econtrol(p)
-#define parport_frob_econtrol(p,m,v) parport_pc_frob_econtrol(p,m,v)
-#define parport_write_status(p,v) parport_pc_write_status(p,v)
#define parport_read_status(p) parport_pc_read_status(p)
-#define parport_write_fifo(p,v) parport_pc_write_fifo(p,v)
-#define parport_read_fifo(p) parport_pc_read_fifo(p)
-#define parport_change_mode(p,m) parport_pc_change_mode(p,m)
-#define parport_release_resources(p) parport_pc_release_resources(p)
-#define parport_claim_resources(p) parport_pc_claim_resources(p)
-#define parport_epp_write_data(p,x) parport_pc_write_epp(p,x)
-#define parport_epp_read_data(p) parport_pc_read_epp(p)
-#define parport_epp_write_addr(p,x) parport_pc_write_epp_addr(p,x)
-#define parport_epp_read_addr(p) parport_pc_read_epp_addr(p)
-#define parport_epp_check_timeout(p) parport_pc_check_epp_timeout(p)
+#define parport_enable_irq(p) parport_pc_enable_irq(p)
+#define parport_disable_irq(p) parport_pc_disable_irq(p)
+#define parport_data_forward(p) parport_pc_data_forward(p)
+#define parport_data_reverse(p) parport_pc_data_reverse(p)
#endif
#ifdef PARPORT_NEED_GENERIC_OPS
#define parport_write_control(p,x) (p)->ops->write_control(p,x)
#define parport_read_control(p) (p)->ops->read_control(p)
#define parport_frob_control(p,m,v) (p)->ops->frob_control(p,m,v)
-#define parport_write_econtrol(p,x) (p)->ops->write_econtrol(p,x)
-#define parport_read_econtrol(p) (p)->ops->read_econtrol(p)
-#define parport_frob_econtrol(p,m,v) (p)->ops->frob_econtrol(p,m,v)
-#define parport_write_status(p,v) (p)->ops->write_status(p,v)
#define parport_read_status(p) (p)->ops->read_status(p)
-#define parport_write_fifo(p,v) (p)->ops->write_fifo(p,v)
-#define parport_read_fifo(p) (p)->ops->read_fifo(p)
-#define parport_change_mode(p,m) (p)->ops->change_mode(p,m)
-#define parport_release_resources(p) (p)->ops->release_resources(p)
-#define parport_claim_resources(p) (p)->ops->claim_resources(p)
-#define parport_epp_write_data(p,x) (p)->ops->epp_write_data(p,x)
-#define parport_epp_read_data(p) (p)->ops->epp_read_data(p)
-#define parport_epp_write_addr(p,x) (p)->ops->epp_write_addr(p,x)
-#define parport_epp_read_addr(p) (p)->ops->epp_read_addr(p)
-#define parport_epp_check_timeout(p) (p)->ops->epp_check_timeout(p)
+#define parport_enable_irq(p) (p)->ops->enable_irq(p)
+#define parport_disable_irq(p) (p)->ops->disable_irq(p)
+#define parport_data_forward(p) (p)->ops->data_forward(p)
+#define parport_data_reverse(p) (p)->ops->data_reverse(p)
#endif
#endif /* __KERNEL__ */
/* --- register definitions ------------------------------- */
-#define ECONTROL(p) ((p)->base_hi + 0x02)
-#define CONFIGB(p) ((p)->base_hi + 0x01)
-#define CONFIGA(p) ((p)->base_hi + 0x00)
-#define EPPDATA(p) ((p)->base + 0x04)
-#define EPPADDR(p) ((p)->base + 0x03)
-#define CONTROL(p) ((p)->base + 0x02)
-#define STATUS(p) ((p)->base + 0x01)
-#define DATA(p) ((p)->base + 0x00)
-
-/* Private data for PC low-level driver. */
+#define ECONTROL(p) ((p)->base_hi + 0x2)
+#define CONFIGB(p) ((p)->base_hi + 0x1)
+#define CONFIGA(p) ((p)->base_hi + 0x0)
+#define FIFO(p) ((p)->base_hi + 0x0)
+#define EPPDATA(p) ((p)->base + 0x4)
+#define EPPADDR(p) ((p)->base + 0x3)
+#define CONTROL(p) ((p)->base + 0x2)
+#define STATUS(p) ((p)->base + 0x1)
+#define DATA(p) ((p)->base + 0x0)
+
struct parport_pc_private {
/* Contents of CTR. */
unsigned char ctr;
-};
-extern int parport_pc_epp_clear_timeout(struct parport *pb);
+ /* Bitmask of writable CTR bits. */
+ unsigned char ctr_writable;
-extern volatile unsigned char parport_pc_ctr;
+ /* Whether or not there's an ECR. */
+ int ecr;
-extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d)
-{
- outb(d, EPPDATA(p));
-}
+ /* Number of PWords that FIFO will hold. */
+ int fifo_depth;
-extern __inline__ unsigned char parport_pc_read_epp(struct parport *p)
-{
- return inb(EPPDATA(p));
-}
+ /* Number of bytes per portword. */
+ int pword;
-extern __inline__ void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
-{
- outb(d, EPPADDR(p));
-}
+ /* Not used yet. */
+ int readIntrThreshold;
+ int writeIntrThreshold;
-extern __inline__ unsigned char parport_pc_read_epp_addr(struct parport *p)
-{
- return inb(EPPADDR(p));
-}
+ /* buffer suitable for DMA, if DMA enabled */
+ char *dma_buf;
+};
-extern __inline__ int parport_pc_check_epp_timeout(struct parport *p)
+extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
{
- if (!(inb(STATUS(p)) & 1))
- return 0;
- parport_pc_epp_clear_timeout(p);
- return 1;
+ outb(d, DATA(p));
}
-extern __inline__ unsigned char parport_pc_read_configb(struct parport *p)
+extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
{
- return inb(CONFIGB(p));
+ return inb(DATA(p));
}
-extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
+extern __inline__ unsigned char __frob_control (struct parport *p,
+ unsigned char mask,
+ unsigned char val)
{
- outb(d, DATA(p));
+ struct parport_pc_private *priv = p->physport->private_data;
+ unsigned char ctr = priv->ctr;
+ ctr = (ctr & ~mask) ^ val;
+ ctr &= priv->ctr_writable; /* only write writable bits. */
+ outb (ctr, CONTROL (p));
+ return priv->ctr = ctr; /* update soft copy */
}
-extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
+extern __inline__ void parport_pc_data_reverse (struct parport *p)
{
- return inb(DATA(p));
+ __frob_control (p, 0x20, 0x20);
}
-extern __inline__ void parport_pc_write_control(struct parport *p, unsigned char d)
+extern __inline__ void parport_pc_write_control (struct parport *p,
+ unsigned char d)
{
- struct parport_pc_private *priv = p->private_data;
- priv->ctr = d;/* update soft copy */
- outb(d, CONTROL(p));
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ /* Take this out when drivers have adapted to newer interface. */
+ if (d & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
+ }
+
+ __frob_control (p, wm, d & wm);
}
extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
{
- struct parport_pc_private *priv = p->private_data;
- return priv->ctr;
+ const struct parport_pc_private *priv = p->physport->private_data;
+ return priv->ctr; /* Use soft copy */
}
-extern __inline__ unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask, unsigned char val)
+extern __inline__ unsigned char parport_pc_frob_control (struct parport *p,
+ unsigned char mask,
+ unsigned char val)
{
- struct parport_pc_private *priv = p->private_data;
- unsigned char ctr = priv->ctr;
- ctr = (ctr & ~mask) ^ val;
- outb (ctr, CONTROL(p));
- return priv->ctr = ctr; /* update soft copy */
-}
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
-extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d)
-{
- outb(d, STATUS(p));
+ /* Take this out when drivers have adapted to newer interface. */
+ if (mask & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
+ }
+
+ /* Restrict mask and val to control lines. */
+ mask &= wm;
+ val &= wm;
+
+ return __frob_control (p, mask, val);
}
extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
return inb(STATUS(p));
}
-extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned char d)
+extern __inline__ void parport_pc_data_forward (struct parport *p)
{
- outb(d, ECONTROL(p));
+ __frob_control (p, 0x20, 0x00);
}
-extern __inline__ unsigned char parport_pc_read_econtrol(struct parport *p)
+extern __inline__ void parport_pc_disable_irq(struct parport *p)
{
- return inb(ECONTROL(p));
+ __frob_control (p, 0x10, 0x00);
}
-extern __inline__ unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val)
+extern __inline__ void parport_pc_enable_irq(struct parport *p)
{
- unsigned char old = inb(ECONTROL(p));
- outb(((old & ~mask) ^ val), ECONTROL(p));
- return old;
+ __frob_control (p, 0x10, 0x10);
}
-extern void parport_pc_change_mode(struct parport *p, int m);
-
-extern void parport_pc_write_fifo(struct parport *p, unsigned char v);
-
-extern unsigned char parport_pc_read_fifo(struct parport *p);
-
-extern void parport_pc_disable_irq(struct parport *p);
-
-extern void parport_pc_enable_irq(struct parport *p);
-
extern void parport_pc_release_resources(struct parport *p);
extern int parport_pc_claim_resources(struct parport *p);
-extern void parport_pc_init_state(struct parport_state *s);
+extern void parport_pc_init_state(struct pardevice *, struct parport_state *s);
extern void parport_pc_save_state(struct parport *p, struct parport_state *s);
extern void parport_pc_restore_state(struct parport *p, struct parport_state *s);
-extern size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length);
-
-extern size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length);
-
-extern int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
-
-extern int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
-
extern void parport_pc_inc_use_count(void);
extern void parport_pc_dec_use_count(void);
atomic_t count;
int map_count; /* number of VMAs */
struct semaphore mmap_sem;
+ rwlock_t page_table_lock;
unsigned long context;
+ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
+ int swappable:1;
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
swapper_pg_dir, \
ATOMIC_INIT(1), 1, \
__MUTEX_INITIALIZER(name.mmap_sem), \
+ RW_LOCK_UNLOCKED, \
+ 0, \
+ 0, 0, 0, 0, 0, 0, \
0, \
0, 0, 0, 0, \
0, 0, 0, \
struct tms times;
unsigned long start_time;
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
-/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
- unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
- int swappable:1;
/* process credentials */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
*/
#define _STK_LIM (8*1024*1024)
-#define DEF_PRIORITY (20*HZ/100) /* 210 ms time slices */
+#define DEF_PRIORITY (20*HZ/100) /* 200 ms time slices */
/*
* INIT_TASK is used to set up the first task table, touch at
/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
/* utime */ {0,0,0,0},0, \
/* per CPU times */ {0, }, {0, }, \
-/* flt */ 0,0,0,0,0,0, \
-/* swp */ 0, \
/* process credentials */ \
/* uid etc */ 0,0,0,0,0,0,0,0, \
/* suppl grps*/ 0, {0,}, \
extern void swap_after_unlock_page (unsigned long entry);
/* linux/mm/page_alloc.c */
-extern void swap_in(struct task_struct *, struct vm_area_struct *,
- pte_t *, unsigned long, int);
-
/* linux/mm/swap_state.c */
extern void show_swap_cache_info(void);
NULL, /* advise */
shm_nopage, /* nopage */
NULL, /* wppage */
- shm_swapout, /* swapout */
- NULL /* swapin */
+ shm_swapout /* swapout */
};
/* Insert shmd into the list shp->attaches */
unsigned int id;
struct shmid_kernel *shp;
+ lock_kernel();
id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
shp = shm_segs[id];
if (shp == IPC_UNUSED) {
shp->u.shm_nattch++;
shp->u.shm_atime = CURRENT_TIME;
shp->u.shm_lpid = current->pid;
+ unlock_kernel();
}
/*
struct shmid_kernel *shp;
int id;
+ lock_kernel();
/* remove from the list of attaches of the shm segment */
id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
shp = shm_segs[id];
shp->u.shm_dtime = CURRENT_TIME;
if (--shp->u.shm_nattch <= 0 && shp->u.shm_perm.mode & SHM_DEST)
killseg (id);
+ unlock_kernel();
}
/*
pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
shp->shm_pages[idx] = pte_val(pte);
} else
- --current->maj_flt; /* was incremented in do_no_page */
+ --current->mm->maj_flt; /* was incremented in do_no_page */
done: /* pte_val(pte) == shp->shm_pages[idx] */
- current->min_flt++;
+ current->mm->min_flt++;
get_page(mem_map + MAP_NR(pte_page(pte)));
return pte_page(pte);
}
write_unlock_irq(&tasklist_lock);
release_thread(p);
+#if 0 /* FIXME! How do we do this right for threads? */
current->cmin_flt += p->min_flt + p->cmin_flt;
current->cmaj_flt += p->maj_flt + p->cmaj_flt;
current->cnswap += p->nswap + p->cnswap;
+#endif
free_task_struct(p);
} else {
printk("task releasing itself\n");
flush_tlb_mm(mm);
destroy_context(mm);
tsk->mm = &init_mm;
- tsk->swappable = 0;
SET_PAGE_DIR(tsk, swapper_pg_dir);
mm_release();
mmput(mm);
mm->map_count = 0;
mm->def_flags = 0;
init_MUTEX_LOCKED(&mm->mmap_sem);
+ mm->page_table_lock = RW_LOCK_UNLOCKED;
/*
* Leave mm->pgd set to the parent's pgd
* so that pgd_offset() is always valid.
* cache or tlb.
*/
mm->cpu_vm_mask = 0;
+ mm->swappable = 0;
}
return mm;
}
goto fail_nomem;
tsk->mm = mm;
- tsk->min_flt = tsk->maj_flt = 0;
- tsk->cmin_flt = tsk->cmaj_flt = 0;
- tsk->nswap = tsk->cnswap = 0;
copy_segments(nr, tsk, mm);
retval = new_page_tables(tsk);
if (retval)
__MOD_INC_USE_COUNT(p->binfmt->module);
p->did_exec = 0;
- p->swappable = 0;
p->state = TASK_UNINTERRUPTIBLE;
copy_flags(clone_flags, p);
p->semundo = NULL;
/* ok, now we should be set up.. */
- p->swappable = 1;
+ p->mm->swappable = 1;
p->exit_signal = clone_flags & CSIGNAL;
p->pdeath_signal = 0;
unsigned long user = ticks - system;
if (p->pid) {
p->counter -= ticks;
- if (p->counter < 0) {
+ if (p->counter <= 0) {
p->counter = 0;
p->need_resched = 1;
}
* do a "normalization" of the priority (traditionally
* Unix nice values are -20 to 20; Linux doesn't really
* use that kind of thing, but uses the length of the
- * timeslice instead (default 210 ms). The rounding is
+ * timeslice instead (default 200 ms). The rounding is
* why we want to avoid negative values.
*/
newprio = (newprio * DEF_PRIORITY + 10) / 20;
* either stopped or zombied. In the zombied case the task won't get
* reaped till shortly after the call to getrusage(), in both cases the
* task being examined is in a frozen state so the counters won't change.
+ *
+ * FIXME! Get the fault counts properly!
*/
int getrusage(struct task_struct *p, int who, struct rusage *ru)
{
r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_utime);
r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_stime);
r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_stime);
- r.ru_minflt = p->min_flt;
- r.ru_majflt = p->maj_flt;
- r.ru_nswap = p->nswap;
+ r.ru_minflt = 0;
+ r.ru_majflt = 0;
+ r.ru_nswap = 0;
break;
case RUSAGE_CHILDREN:
r.ru_utime.tv_sec = CT_TO_SECS(p->times.tms_cutime);
r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_cutime);
r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_cstime);
r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_cstime);
- r.ru_minflt = p->cmin_flt;
- r.ru_majflt = p->cmaj_flt;
- r.ru_nswap = p->cnswap;
+ r.ru_minflt = 0;
+ r.ru_majflt = 0;
+ r.ru_nswap = 0;
break;
default:
r.ru_utime.tv_sec = CT_TO_SECS(p->times.tms_utime + p->times.tms_cutime);
r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_utime + p->times.tms_cutime);
r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_stime + p->times.tms_cstime);
r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_stime + p->times.tms_cstime);
- r.ru_minflt = p->min_flt + p->cmin_flt;
- r.ru_majflt = p->maj_flt + p->cmaj_flt;
- r.ru_nswap = p->nswap + p->cnswap;
+ r.ru_minflt = 0;
+ r.ru_majflt = 0;
+ r.ru_nswap = 0;
break;
}
return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
struct file * in_file, * out_file;
struct inode * in_inode, * out_inode;
- lock_kernel();
-
/*
* Get input file, and verify that it is ok..
*/
if (retval)
goto fput_out;
- unlock_kernel();
retval = 0;
if (count) {
read_descriptor_t desc;
ppos = &in_file->f_pos;
if (offset) {
if (get_user(pos, offset))
- goto fput_out_lock;
+ goto fput_out;
ppos = &pos;
}
put_user(pos, offset);
}
-fput_out_lock:
- lock_kernel();
fput_out:
fput(out_file);
fput_in:
fput(in_file);
out:
- unlock_kernel();
return retval;
}
new_page = 0;
offset = (address & PAGE_MASK) - area->vm_start + area->vm_offset;
if (offset >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm)
- goto no_page_nolock;
-
- unlock_kernel();
+ goto no_page;
/*
* Do we have something in the page cache already?
page_cache_free(new_page);
flush_page_to_ram(old_page);
- lock_kernel();
return old_page;
}
copy_page(new_page, old_page);
flush_page_to_ram(new_page);
page_cache_release(page);
- lock_kernel();
return new_page;
no_cached_page:
if (new_page)
page_cache_free(new_page);
no_page:
- lock_kernel();
-no_page_nolock:
return 0;
}
NULL, /* advise */
filemap_nopage, /* nopage */
NULL, /* wppage */
- filemap_swapout, /* swapout */
- NULL, /* swapin */
+ filemap_swapout /* swapout */
};
/*
NULL, /* advise */
filemap_nopage, /* nopage */
NULL, /* wppage */
- NULL, /* swapout */
- NULL, /* swapin */
+ NULL /* swapout */
};
/* This is used for a general mmap of a disk file */
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/swap.h>
+#include <linux/pagemap.h>
#include <linux/smp_lock.h>
+#include <linux/swapctl.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
* We also mark the page dirty at this point even though the page will
* change only once the write actually happens. This avoids a few races,
* and potentially makes it more efficient.
+ *
+ * We enter with the page table read-lock held, and need to exit without
+ * it.
*/
-static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
+static int do_wp_page(struct mm_struct * mm, struct vm_area_struct * vma,
unsigned long address, pte_t *page_table, pte_t pte)
{
unsigned long old_page, new_page;
struct page * page;
-
- new_page = __get_free_page(GFP_USER);
- /* Did swap_out() unmap the protected page while we slept? */
- if (pte_val(*page_table) != pte_val(pte))
- goto end_wp_page;
+
old_page = pte_page(pte);
if (MAP_NR(old_page) >= max_mapnr)
goto bad_wp_page;
- tsk->min_flt++;
+ mm->min_flt++;
page = mem_map + MAP_NR(old_page);
/*
/* FallThrough */
case 1:
flush_cache_page(vma, address);
- set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
+ set_pte(page_table, pte_mkyoung(pte_mkdirty(pte_mkwrite(pte))));
flush_tlb_page(vma, address);
-end_wp_page:
- /*
- * We can release the kernel lock now.. Now swap_out will see
- * a dirty page and so won't get confused and flush_tlb_page
- * won't SMP race. -Andrea
- */
- unlock_kernel();
-
- if (new_page)
- free_page(new_page);
+ read_unlock(&mm->page_table_lock);
return 1;
}
-
+
+ /*
+ * Ok, we need to copy. Oh, well..
+ */
+ read_unlock(&mm->page_table_lock);
+ new_page = __get_free_page(GFP_USER);
if (!new_page)
- goto no_new_page;
+ return 0;
+ read_lock(&mm->page_table_lock);
- if (PageReserved(page))
- ++vma->vm_mm->rss;
- copy_cow_page(old_page,new_page);
- flush_page_to_ram(old_page);
- flush_page_to_ram(new_page);
- flush_cache_page(vma, address);
- set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
- flush_tlb_page(vma, address);
- unlock_kernel();
- __free_page(page);
+ /*
+ * Re-check the pte - we dropped the lock
+ */
+ if (pte_val(*page_table) == pte_val(pte)) {
+ if (PageReserved(page))
+ ++vma->vm_mm->rss;
+ copy_cow_page(old_page,new_page);
+ flush_page_to_ram(old_page);
+ flush_page_to_ram(new_page);
+ flush_cache_page(vma, address);
+ set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
+ flush_tlb_page(vma, address);
+
+ /* Free the old page.. */
+ new_page = old_page;
+ }
+ read_unlock(&mm->page_table_lock);
+ free_page(new_page);
return 1;
bad_wp_page:
printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
- send_sig(SIGKILL, tsk, 1);
-no_new_page:
- unlock_kernel();
- if (new_page)
- free_page(new_page);
return 0;
}
}
-/*
- * This is called with the kernel lock held, we need
- * to return without it.
+
+/*
+ * Primitive swap readahead code. We simply read an aligned block of
+ * (1 << page_cluster) entries in the swap area. This method is chosen
+ * because it doesn't cost us any seek time. We also make sure to queue
+ * the 'original' request together with the readahead ones...
*/
-static int do_swap_page(struct task_struct * tsk,
+static void swapin_readahead(unsigned long entry)
+{
+ int i;
+ struct page *new_page;
+ unsigned long offset = SWP_OFFSET(entry);
+ struct swap_info_struct *swapdev = SWP_TYPE(entry) + swap_info;
+
+ offset = (offset >> page_cluster) << page_cluster;
+
+ i = 1 << page_cluster;
+ do {
+ /* Don't read-ahead past the end of the swap area */
+ if (offset >= swapdev->max)
+ break;
+ /* Don't block on I/O for read-ahead */
+ if (atomic_read(&nr_async_pages) >= pager_daemon.swap_cluster)
+ break;
+ /* Don't read in bad or busy pages */
+ if (!swapdev->swap_map[offset])
+ break;
+ if (swapdev->swap_map[offset] == SWAP_MAP_BAD)
+ break;
+
+ /* Ok, do the async read-ahead now */
+ new_page = read_swap_cache_async(SWP_ENTRY(SWP_TYPE(entry), offset), 0);
+ if (new_page != NULL)
+ __free_page(new_page);
+ offset++;
+ } while (--i);
+ return;
+}
+
+static int do_swap_page(struct mm_struct * mm,
struct vm_area_struct * vma, unsigned long address,
- pte_t * page_table, pte_t entry, int write_access)
+ pte_t * page_table, unsigned long entry, int write_access)
{
- if (!vma->vm_ops || !vma->vm_ops->swapin) {
- swap_in(tsk, vma, page_table, pte_val(entry), write_access);
- flush_page_to_ram(pte_page(*page_table));
- } else {
- pte_t page = vma->vm_ops->swapin(vma, address - vma->vm_start + vma->vm_offset, pte_val(entry));
- if (pte_val(*page_table) != pte_val(entry)) {
- free_page(pte_page(page));
- } else {
- if (page_count(mem_map + MAP_NR(pte_page(page))) > 1 &&
- !(vma->vm_flags & VM_SHARED))
- page = pte_wrprotect(page);
- ++vma->vm_mm->rss;
- ++tsk->maj_flt;
- flush_page_to_ram(pte_page(page));
- set_pte(page_table, page);
- }
+ struct page *page = lookup_swap_cache(entry);
+ pte_t pte;
+
+ if (!page) {
+ lock_kernel();
+ swapin_readahead(entry);
+ page = read_swap_cache(entry);
+ unlock_kernel();
+ if (!page)
+ return 0;
+
+ flush_page_to_ram(page_address(page));
}
- unlock_kernel();
+
+ vma->vm_mm->rss++;
+ mm->min_flt++;
+ swap_free(entry);
+
+ pte = mk_pte(page_address(page), vma->vm_page_prot);
+
+ if (write_access && !is_page_shared(page)) {
+ delete_from_swap_cache(page);
+ pte = pte_mkwrite(pte_mkdirty(pte));
+ }
+ set_pte(page_table, pte);
+
return 1;
}
/*
* This only needs the MM semaphore
*/
-static int do_anonymous_page(struct task_struct * tsk, struct vm_area_struct * vma, pte_t *page_table, int write_access, unsigned long addr)
+static int do_anonymous_page(struct mm_struct * mm, struct vm_area_struct * vma, pte_t *page_table, int write_access, unsigned long addr)
{
pte_t entry = pte_wrprotect(mk_pte(ZERO_PAGE(addr), vma->vm_page_prot));
if (write_access) {
clear_page(page);
entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
vma->vm_mm->rss++;
- tsk->min_flt++;
+ mm->min_flt++;
flush_page_to_ram(page);
}
set_pte(page_table, entry);
* This is called with the MM semaphore and the kernel lock held.
* We need to release the kernel lock as soon as possible..
*/
-static int do_no_page(struct task_struct * tsk, struct vm_area_struct * vma,
+static int do_no_page(struct mm_struct * mm, struct vm_area_struct * vma,
unsigned long address, int write_access, pte_t *page_table)
{
unsigned long page;
pte_t entry;
- if (!vma->vm_ops || !vma->vm_ops->nopage) {
- unlock_kernel();
- return do_anonymous_page(tsk, vma, page_table, write_access,
- address);
- }
+ if (!vma->vm_ops || !vma->vm_ops->nopage)
+ return do_anonymous_page(mm, vma, page_table, write_access, address);
/*
* The third argument is "no_share", which tells the low-level code
* to copy, not share the page even if sharing is possible. It's
* essentially an early COW detection.
*/
- page = vma->vm_ops->nopage(vma, address & PAGE_MASK,
- (vma->vm_flags & VM_SHARED)?0:write_access);
-
- unlock_kernel();
+ page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED)?0:write_access);
if (!page)
return 0;
- ++tsk->maj_flt;
+ ++mm->maj_flt;
++vma->vm_mm->rss;
/*
* This silly early PAGE_DIRTY setting removes a race
* There is also a hook called "update_mmu_cache()" that architectures
* with external mmu caches can use to update those (ie the Sparc or
* PowerPC hashed page tables that act as extended TLBs).
+ *
+ * Note the "page_table_lock". It is to protect against kswapd removing
+ * pages from under us. Note that kswapd only ever _removes_ pages, never
+ * adds them. As such, once we have noticed that the page is not present,
+ * we can drop the lock early.
+ *
+ * The adding of pages is protected by the MM semaphore (which we hold),
+ * so we don't need to worry about a page being suddenly been added into
+ * our VM.
*/
-static inline int handle_pte_fault(struct task_struct *tsk,
+static inline int handle_pte_fault(struct mm_struct *mm,
struct vm_area_struct * vma, unsigned long address,
int write_access, pte_t * pte)
{
pte_t entry;
- lock_kernel();
entry = *pte;
-
if (!pte_present(entry)) {
if (pte_none(entry))
- return do_no_page(tsk, vma, address, write_access, pte);
- return do_swap_page(tsk, vma, address, pte, entry, write_access);
+ return do_no_page(mm, vma, address, write_access, pte);
+ return do_swap_page(mm, vma, address, pte, pte_val(entry), write_access);
}
- entry = pte_mkyoung(entry);
- set_pte(pte, entry);
- flush_tlb_page(vma, address);
- if (write_access) {
- if (!pte_write(entry))
- return do_wp_page(tsk, vma, address, pte, entry);
+ /*
+ * Ok, the entry was present, we need to get the page table
+ * lock to synchronize with kswapd, and verify that the entry
+ * didn't change from under us..
+ */
+ read_lock(&mm->page_table_lock);
+ if (pte_val(entry) == pte_val(*pte)) {
+ if (write_access) {
+ if (!pte_write(entry))
+ return do_wp_page(mm, vma, address, pte, entry);
- entry = pte_mkdirty(entry);
- set_pte(pte, entry);
+ entry = pte_mkdirty(entry);
+ }
+ set_pte(pte, pte_mkyoung(entry));
flush_tlb_page(vma, address);
}
- unlock_kernel();
+ read_unlock(&mm->page_table_lock);
return 1;
}
/*
* By the time we get here, we already hold the mm semaphore
*/
-int handle_mm_fault(struct task_struct *tsk, struct vm_area_struct * vma,
+int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct * vma,
unsigned long address, int write_access)
{
pgd_t *pgd;
if (pmd) {
pte_t * pte = pte_alloc(pmd, address);
if (pte) {
- if (handle_pte_fault(tsk, vma, address, write_access, pte)) {
+ if (handle_pte_fault(mm, vma, address, write_access, pte)) {
update_mmu_cache(vma, address, *pte);
return 1;
}
void make_pages_present(unsigned long addr, unsigned long end)
{
int write;
+ struct mm_struct *mm = current->mm;
struct vm_area_struct * vma;
- vma = find_vma(current->mm, addr);
+ vma = find_vma(mm, addr);
write = (vma->vm_flags & VM_WRITE) != 0;
while (addr < end) {
- handle_mm_fault(current, vma, addr, write);
+ handle_mm_fault(mm, vma, addr, write);
addr += PAGE_SIZE;
}
}
return NULL;
}
-struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
+struct vm_area_struct * find_extend_vma(struct mm_struct * mm, unsigned long addr)
{
struct vm_area_struct * vma;
unsigned long start;
addr &= PAGE_MASK;
- vma = find_vma(tsk->mm,addr);
+ vma = find_vma(mm,addr);
if (!vma)
return NULL;
if (vma->vm_start <= addr)
}
return start_mem;
}
-
-/*
- * Primitive swap readahead code. We simply read an aligned block of
- * (1 << page_cluster) entries in the swap area. This method is chosen
- * because it doesn't cost us any seek time. We also make sure to queue
- * the 'original' request together with the readahead ones...
- */
-void swapin_readahead(unsigned long entry)
-{
- int i;
- struct page *new_page;
- unsigned long offset = SWP_OFFSET(entry);
- struct swap_info_struct *swapdev = SWP_TYPE(entry) + swap_info;
-
- offset = (offset >> page_cluster) << page_cluster;
-
- i = 1 << page_cluster;
- do {
- /* Don't read-ahead past the end of the swap area */
- if (offset >= swapdev->max)
- break;
- /* Don't block on I/O for read-ahead */
- if (atomic_read(&nr_async_pages) >= pager_daemon.swap_cluster)
- break;
- /* Don't read in bad or busy pages */
- if (!swapdev->swap_map[offset])
- break;
- if (swapdev->swap_map[offset] == SWAP_MAP_BAD)
- break;
-
- /* Ok, do the async read-ahead now */
- new_page = read_swap_cache_async(SWP_ENTRY(SWP_TYPE(entry), offset), 0);
- if (new_page != NULL)
- __free_page(new_page);
- offset++;
- } while (--i);
- return;
-}
-
-/*
- * The tests may look silly, but it essentially makes sure that
- * no other process did a swap-in on us just as we were waiting.
- *
- * Also, don't bother to add to the swap cache if this page-in
- * was due to a write access.
- */
-void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
- pte_t * page_table, unsigned long entry, int write_access)
-{
- unsigned long page;
- struct page *page_map = lookup_swap_cache(entry);
-
- if (!page_map) {
- swapin_readahead(entry);
- page_map = read_swap_cache(entry);
- }
- if (pte_val(*page_table) != entry) {
- if (page_map)
- free_page_and_swap_cache(page_address(page_map));
- return;
- }
- if (!page_map) {
- set_pte(page_table, BAD_PAGE);
- swap_free(entry);
- oom(tsk);
- return;
- }
-
- page = page_address(page_map);
- vma->vm_mm->rss++;
- tsk->min_flt++;
- swap_free(entry);
-
- if (!write_access || is_page_shared(page_map)) {
- set_pte(page_table, mk_pte(page, vma->vm_page_prot));
- return;
- }
-
- /*
- * The page is unshared and we're going to dirty it - so tear
- * down the swap cache and give exclusive access to the page to
- * this process.
- */
- delete_from_swap_cache(page_map);
- set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
- return;
-}
page_addr = pte_page(pte);
if (MAP_NR(page_addr) >= max_mapnr)
goto out_failed;
+
page = mem_map + MAP_NR(page_addr);
+ write_lock(&tsk->mm->page_table_lock);
+ if (pte_val(pte) != pte_val(*page_table))
+ goto out_failed_unlock;
/*
* Dont be too eager to get aging right if
*/
set_pte(page_table, pte_mkold(pte));
set_bit(PG_referenced, &page->flags);
- goto out_failed;
+ goto out_failed_unlock;
}
if (PageReserved(page)
|| PageLocked(page)
|| ((gfp_mask & __GFP_DMA) && !PageDMA(page)))
- goto out_failed;
+ goto out_failed_unlock;
/*
* Is the page already in the swap cache? If so, then
vma->vm_mm->rss--;
flush_tlb_page(vma, address);
__free_page(page);
- goto out_failed;
+ goto out_failed_unlock;
}
/*
* locks etc.
*/
if (!(gfp_mask & __GFP_IO))
- goto out_failed;
+ goto out_failed_unlock;
/*
* Ok, it's really dirty. That means that
if (vma->vm_ops && vma->vm_ops->swapout) {
pid_t pid = tsk->pid;
pte_clear(page_table);
+ write_unlock(&tsk->mm->page_table_lock);
flush_tlb_page(vma, address);
vma->vm_mm->rss--;
goto out_failed; /* No swap space left */
vma->vm_mm->rss--;
- tsk->nswap++;
+ tsk->mm->nswap++;
set_pte(page_table, __pte(entry));
+ write_unlock(&tsk->mm->page_table_lock);
+
flush_tlb_page(vma, address);
swap_duplicate(entry); /* One for the process, one for the swap cache */
out_free_success:
__free_page(page);
return 1;
+out_failed_unlock:
+ write_unlock(&tsk->mm->page_table_lock);
out_failed:
return 0;
}
read_lock(&tasklist_lock);
p = init_task.next_task;
for (; p != &init_task; p = p->next_task) {
- if (!p->swappable)
+ if (!p->mm->swappable)
continue;
if (p->mm->rss <= 0)
continue;
return (-EPERM);
if(sa->sat_family != AF_APPLETALK)
return (-EINVAL);
+ if (atif == NULL)
+ return (-EADDRNOTAVAIL);
/*
* give to aarp module to remove proxy entry
{
int i;
- printk(KERN_INFO "Linux NET4.0 for Linux 2.2\n");
+ printk(KERN_INFO "Linux NET4.0 for Linux 2.3\n");
printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n");
/*
#define __KERNEL_SYSCALLS__
#include <linux/version.h>
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/sched.h>
sk->state_change = xprt->old_state_change;
sk->write_space = xprt->old_write_space;
- if (xprt->file)
- fput(xprt->file);
- else
- sock_release(xprt->sock);
+ sock_release(xprt->sock);
/*
* TCP doesnt require the rpciod now - other things may
* but rpciod handles that not us.
return xprt;
}
-/*
- * Create and initialize an RPC client given an open file.
- * This is obsolete now.
- */
-#if 0
-struct rpc_xprt *
-xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to)
-{
- struct rpc_xprt *xprt;
- struct socket *sock;
- int proto;
-
- if (!file) {
- printk("RPC: file == NULL in xprt_create!\n");
- return NULL;
- }
-
- sock = &file->f_inode->u.socket_i;
- if (sock->ops->family != PF_INET) {
- printk(KERN_WARNING "RPC: only INET sockets supported\n");
- return NULL;
- }
-
- proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP;
- if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) {
- xprt->file = file;
- atomic_inc(&file->f_count);
- }
-
- return xprt;
-}
-#endif
-
/*
* Bind to a reserved port
*/
+++ /dev/null
-# Description file for ksymoops
-
-# Thu Nov 26 16:37:46 EST 1998
-# Version 0.6c
-# Add -c option.
-
-# Tue Nov 3 02:31:01 EST 1998
-# Version 0.6
-# Read lsmod (/proc/modules).
-# Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
-# Upper case variables.
-# Convert from a.out to bfd, using same format as ksymoops.
-
-DEFS = Makefile ksymoops.h
-
-# Defaults for vmlinux, ksyms, objects, lsmod, System.map. Externalised so
-# distributions can tweak to suit their own file system layout.
-
-# To default to not reading a source, set to any empty string.
-# To default to reading a source, supply a quoted and escaped string.
-
-# If the string contains *r (*m, *n, *s) then it is replaced at run time by
-# the current value of `uname -r` (-m, -n, -s). '*' was chosen as something
-# that rarely appears in filenames and does not cause problems like '%' or '$'.
-
-DEF_VMLINUX = # default no vmlinux
-DEF_OBJECTS = \"/lib/modules/*r/\" # default current modules
-DEF_KSYMS = \"/proc/ksyms\" # default current ksyms
-DEF_LSMOD = \"/proc/modules\" # default current lsmod
-DEF_MAP = \"/usr/src/linux/System.map\" # default current map
-DEF_CODE_BYTES = 1 # default bytes per code unit
-
-# RedHat users might want defaults like these
-# DEF_MAP = \"/boot/System.map-*r\"
-# DEF_OBJECTS = \"/boot/module-info-*r\"
-
-PROGS = ksymoops
-
-CC=gcc
-CFLAGS = -Dlinux \
- -Wall \
- -Wno-conversion \
- -Waggregate-return \
- -Wstrict-prototypes \
- -Wmissing-prototypes \
- $(DEBUG)
-
-ifneq ($(strip $(DEF_VMLINUX)),)
- CFLAGS += -DDEF_VMLINUX=$(strip $(DEF_VMLINUX))
-endif
-ifneq ($(strip $(DEF_OBJECTS)),)
- CFLAGS += -DDEF_OBJECTS=$(strip $(DEF_OBJECTS))
-endif
-ifneq ($(strip $(DEF_KSYMS)),)
- CFLAGS += -DDEF_KSYMS=$(strip $(DEF_KSYMS))
-endif
-ifneq ($(strip $(DEF_LSMOD)),)
- CFLAGS += -DDEF_LSMOD=$(strip $(DEF_LSMOD))
-endif
-ifneq ($(strip $(DEF_MAP)),)
- CFLAGS += -DDEF_MAP=$(strip $(DEF_MAP))
-endif
-
-CFLAGS += -DDEF_CODE_BYTES=$(strip $(DEF_CODE_BYTES))
-
-OBJECTS = io.o ksyms.o ksymoops.o map.o misc.o object.o oops.o re.o symbol.o
-
-all: $(PROGS)
-
-: $(OBJECTS)
-
-$(OBJECTS): $(DEFS)
-
-$(PROGS): %: %.o $(DEFS) $(OBJECTS)
- $(CC) $(OBJECTS) $(CFLAGS) -lbfd -liberty -o $@
- -@size $@
-
-clean:
- rm -f core *.o $(PROGS)
- ksymoops.
+ksymoops has been removed from the kernel. It was always meant to be a
+free standing utility, not linked to any particular kernel version.
+The latest version can be found in ftp://ftp.ocs.com.au/pub/ksymoops,
+together with patches to other utilities in order to give more accurate
+Oops debugging.
- Read a kernel Oops file and make the best stab at converting the code to
- instructions and mapping stack values to kernel symbols.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- To compile, simply type "make" in the ksymoops directory.
-
- TESTERS WANTED.
-
- ksymoops handles ix86. It appears to handle Alpha, Sparc, M68K, PPC,
- MIPS but I have no machine to test on. I would appreciate feedback
- from users of non ix86 machines. In particular, it would be nice if
- you could run
-
- ksymoops -VMO -k /proc/ksyms -dd <oops.file >/tmp/ksymoops.log 2>&1
-
- and mail /tmp/ksymoops.log to kaos@ocs.com.au
-
- TODO:
- Clean up these docs.
- Tweak System.map to include arch information.
- Tweak modutils to log at least one symbol for each module loaded,
- otherwise they are invisible to ksymoops. Also arch and version data.
- Include sparc/sparc64 patches from Jakub Jelinek <jj@sunsite.mff.cuni.cz>.
- Add object format override for sparc/soparc64 or any cross platform
- oops debugging.
-
- Mon Jan 4 09:48:13 EST 1999
- Version 0.6e
- Added to kernel.
- Add ARM support.
- Typo in oops_code.
- Add -c option.
- Add -1 option.
- Report if options were specified or defaulted.
- Remove false warnings when comparing ksyms and lsmod.
- Performance inprovements.
-
- Wed Oct 28 23:14:55 EST 1998
- Version 0.5
- No longer read vmlinux by default, it only duplicates System.map.
-
- Wed Oct 28 13:46:39 EST 1998
- Version 0.4
- Split into separate sources.
-
- Mon Oct 26 00:01:47 EST 1998
- Version 0.3c
- Add alpha (arm) processing.
-
- Mon Oct 26 00:01:47 EST 1998
- Version 0.3b
- Add sparc processing.
- Handle kernel symbol versions.
-
- Fri Oct 23 13:11:20 EST 1998
- Version 0.3
- Add -follow to find command for people who use symlinks to modules.
- Add Version_ checking.
-
- Thu Oct 22 22:28:30 EST 1998
- Version 0.2.
- Generalise text prefix handling.
- Handle messages on Code: line.
- Format addresses with leading zeroes.
- Minor bug fixes.
-
- Wed Oct 21 23:28:48 EST 1998
- Version 0.1. Rewrite from scratch in C.
-
- CREDITS.
- Oops disassembly based on ksymoops.cc,
- Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
- m68k code based on ksymoops.cc changes by
- Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
-
- This code subsumes the Perl script make_System.map.pl which is no longer
- supported.
-
- Why another ksymoops I hear you ask? Various complaints about
- ksymoops.cc -
-
- * It requires C++.
- * It has hard wired limitations on the number of symbols.
- * It does not handle modules at all.
- * Very rigid requirements on the format of input, especially the Oops
- log.
- * No cross checking between ksyms, modules, System.map etc.
- * Very little error checking, diagnostics are not suitable for
- beginners.
- * It only prints the trace and decoded code, users have to manually
- extract the other lines from the Oops.
- * Gives up on the slightest problem.
- * Only handles i386 and possibly m68k. The code is difficult to extend
- to other architectures.
- * Stops after the first Oops, you have to manually extract each one and
- run through ksymoops one at a time.
-
- This version is -
- * C.
- * No hard wired limitations (malloc as far as the eye can see).
- * Handles modules by default.
- * Uses regular pattern matching so it is a lot more forgiving about
- input formats.
- * By default, cross checks ksyms, modules, System.map and vmlinux.
- * Lots of diagnostics and error checking.
- * Prints all relevant lines for a complete Oops report.
- * Tries to provide output no matter how bad the input is. The level of
- progress and error reporting is aimed at beginners.
- * Handles i386, alpha, sparc, sparc64, m68k. It is a lot easier to extend
- to other architectures (patches and/or sample data gratefully accepted).
- * Handles all Oops in the input file(s).
-
-
- Usage: ksymoops
- [-v vmlinux] Where to read vmlinux
- [-V] No vmlinux is available
- [-o object_dir] Directory containing modules
- [-O] No modules is available
- [-k ksyms] Where to read ksyms
- [-K] No ksyms is available
- [-l lsmod] Where to read lsmod
- [-L] No lsmod is available
- [-m system.map] Where to read System.map
- [-M] No System.map is available
- [-s save.map] Save consolidated map
- [-c code_bytes] How many bytes in each unit of code
- [-1] One shot toggle (exit after first Oops)
- [-d] Increase debug level by 1
- [-h] Print help text
- Oops.file Oops to decode
-
- All flags can occur more than once. With the exception of -o
- and -d which are cumulative, the last occurrence of each flag is
- used. Note that "-v my.vmlinux -V" will be taken as "No vmlinux
- available" but "-V -v my.vmlinux" will read my.vmlinux. You
- will be warned about such combinations.
-
- Each occurrence of -d increases the debug level.
-
- Each -o flag can refer to a directory or to a single object
- file. If a directory is specified then all *.o files in that
- directory and its subdirectories are assumed to be modules.
-
- If any of the vmlinux, object_dir, ksyms or system.map options
- contain the string *r (*m, *n, *s) then it is replaced at run time
- by the current value of `uname -r` (-m, -n, -s).
-
- The defaults can be changed in the Makefile, typical options are
-
- Defaults: -V
- -o /lib/modules/%r
- -k /proc/ksyms
- -l /proc/modules
- -m /usr/src/linux/System.map
- -c 1
- Oops report is read from stdin
-
- Note: Unless you tell ksymoops *NOT* to read a particular file, it
- will try to read and reconcile almost all possible sources of kernel
- symbol information. This is intended for beginners, they just
- type
-
- ksymoops < /var/log/syslog
-
- no thinking required. Experts can point at different files or
- suppress the input from selected files. For example, if you
- save /proc/ksyms before doing a test that creates an Oops, you
- can point ksymoops at the saved ksyms instead of using
- /proc/ksyms.
-
- vmlinux is not read by default, it only duplicates the
- information in System.map. If you want to read vmlinux as well
- as or instead of System.map, use -v.
-
- To get the equivalent of the old ksymoops.cc (no vmlinux, no
- modules objects, no ksyms, no System.map) just do ksymoops
- -VOKLM. Or to just read System.map, ksymoops -VOKL -m mapfile.
-
-
- Return codes: 0 - normal.
- 1 - error(s) or warning(s) issued, results may not be
- reliable.
- 2 - fatal error, no useful results.
- 3 - One shot mode, end of input reached.
-
- Supported architectures
-
- i386 tested.
- m68k code derived from ksymoops.cc and reading traps.c, untested.
- MIPS tested.
- Sparc tested.
- Sparc64 tested.
- Alpha tested.
- ARM tested.
-
- The term "eip" is generic, for example it includes the i386 EIP
- and the m68k PC. Remember that objdump output always says EIP,
- no matter what the architecture, see objfile_head.
-
- To support another arch, check the Oops_ procedures between
- 'Start architecture sensitive code' and 'End architecture
- sensitive code'.
-
- The pattern matching should take care of different lengths for
- the address, i.e. addresses should not be arch sensitive. I
- assume that all addresses are at least 4 characters.
-
- If nm output has a different format on your arch, check for uses
- of re_nm.
-
-
-
- Because ksymoops reads kernel information from multiple sources, there
- could be mismatches. ksymoops does the following cross checks, but only
- if the specified files exist -
-
- * Compare Version_nnn numbers from all sources against each other. Pity
- that only vmlinux and System.map have these symbols (as at 2.1.125),
- however I check ksyms, modules and Oops as well. If somebody adds
- symbol Version_nnn to ksyms or modules or adds a Version_nnn line to
- the Oops log, this code is ready.
-
- * Compare kernel ksyms against vmlinux. vmlinux takes precedence.
-
- * Compare System.map against vmlinux. vmlinux takes precedence.
-
- * Compare vmlinux against System.map. vmlinux takes precedence.
-
- * Compare kernel ksyms against System.map. System.map takes precedence.
-
- * Compare modules against module ksyms. modules take precedence. Only
- if at least one module appears in ksyms.
-
- * Compare module names in ksyms against lsmod. Warn if a module
- appears in lsmod but not in ksyms. Error if a modules appears in
- ksyms but is not in lsmod. Only if both ksyms and lsmod have being
- read.
-
- The precedence order is somewhat arbitrary, however it only applies if
- there is any difference between the various sources.
-
- Handling modules is awkward. They can be loaded under different names
- (insmod -o dummy1 dummy.o) and the text, data and read only data are
- loaded at different offsets. Although you can give the -m option to
- insmod which will output the module map when it is loaded, this has a
- few problems -
-
- * No equivalent for removing a module. If you load and remove a lot of
- modules, you end up with multiple sets of symbols around the same
- offsets, which set is correct?
-
- * "insmod -o dummy1 dummy.o" still reports as dummy. That is, there is
- no way of telling which particular version of a multiply loaded
- module the insmod output refers to. Therefore there is no way of
- telling which instantiation failed.
-
- * Even if the above problems are fixed, how do you tell what the module
- environment looked like when the Oops occurred? What if a module is
- loaded or removed just after Oops, how is the user expected to edit
- the insmod log? Rule 1 - make ksymoops easy for beginners.
-
- Although those problems could be fixed, they require changes to
- modutils. Working from ksyms and the module objects can be done without
- changing modutils and without confusing beginners.
-
- Alas the ksyms plus object approach has another problem - matching ksyms
- to module objects. Nowhere does the kernel say that module dummy1 came
- from module /lib/modules/2.1.215/net/dummy.o, ksyms just says dummy1. I
- have to match ksyms to the relevant object by finding a globally unique
- external symbol in each module that can be used to map to the external
- symbols in ksyms. This assumes that each module exports at least one
- text symbol that is unique amongst all modules.
-
- It may not be possible to correctly map other sections such as data and
- readonly data for modules because they may not have exported symbols.
- Since the main aim of ksymoops is to map a code Oops, this should not be
- a problem.
-
- Unfortunately some modules export no symbols. They are marked as
- EXPORT_NO_SYMBOLS are simply do not export anything. It is
- impossible to detect these in ksyms because, by definition, ksyms
- only contains exported symbols for modules. Since all modules appear
- in lsmod (/proc/modules), a cross check of lsmod against the module
- names will find loaded modules with no symbols, at least I can warn
- about these.
-
- After merging the various sources, ksymoops has a (hopefully) accurate
- map including modules. The -s option lets you save the merged
- System.map, but remember that module data and readonly data sections may
- not be correctly relocated, see above.
-
- Environment Variables.
- KSYMOOPS_NM path for nm, defaults to /usr/bin/nm.
- KSYMOOPS_FIND path for find, defaults to /usr/bin/find.
- KSYMOOPS_OBJDUMP path for objdump, defaults to /usr/bin/objdump.
-
-
- Input Oops data.
-
- The ideal input is to feed the syslog straight into this program. If
- you cannot do that, you need to know what the program looks for.
- Especially if you are typing in the Oops by hand :(. All input is case
- insensitive.
-
- * White space in this context means space or tab. It does not include
- newline.
-
- * Oops in syslog has a syslog prefix. Leading text up to and including
- ' kernel: ' is always ignored, there is no need to edit syslog first.
- This leading text need not exist but if it does, it must end in
- ' kernel: '.
-
- * An alternative prefix is <n> where n is the kernel print level. Also
- ignored if present.
-
- * Leading white space is treated as a prefix and ignored, the input is
- not indentation sensitive.
-
- * In the following paragraphs, assume that any prefixes have been
- skipped. If there is more than one prefix, all are skipped, no matter
- which order they appear in.
-
- * A bracketed address is optional '[', required '<', at least 4 hex
- digits, required '>', optional ']'. For example [<01234567>] or
- <1234>.
-
- * The ix86 EIP line is identified by optional white space followed by
- 'EIP:', followed by a least one white space, followed by a bracketed
- address.
-
- * The m68k PC line is identified by optional white space followed by
- 'PC', optionally followed by white space, followed by '=', optionally
- followed by white space, followed by a bracketed address.
-
- * The sparc PC line starts with PSR and PC is the second hex value, not
- bracketed.
-
- * The sparc64 TPC line starts with TSTATE and TPC is the second hex value,
- not bracketed.
-
- * A call trace line is identified by 'Call Trace:' followed by at least
- one white space. Or it is a line starting with a bracketed address,
- but only if the previous line was a call trace line (I hate multi line
- output that relies on identation for recognition, especially when
- lines can have a variable prefix).
-
- * The Code line is identified by 'Code:' followed by a least one white
- space character followed by at least one hex value. The line can
- contain multiple hex values, each separated by at least one white
- space. Each hex value must be 2 to 8 digits and must be a multiple of
- 2 digits.
-
- On some architectures the Code: data is a stream of single bytes,
- in machine order. On other architectures, it is a stream of shorts
- or ints in human readable order which does not always match the
- machine order, endianess raises its ugly head. We are consistently
- inconsistent.
-
- To cater for these architecture inconsistencies, use the -c option.
- If the Code: line is already in machine order, use -c 1. If the
- Code: data is a stream of shorts or ints which do not match the
- machine order, use -c 2 or -c 4. Each set of 'c' bytes are swapped
- to (hopefully) reflect the machine order.
-
- Special cases where Code: can be followed by text.
- 'Code: general protection'
- 'Code: <n>'
- Dump the data anyway, the code was unavailable.
-
- * Formatted data is only output when the Code: line is seen. If any
- data has been stored and more than 5 lines other than Oops text (see
- Oops_print) or end of file are encountered then ksymoops assumes that
- the Code: line is missing or garbled and dumps the formatted data
- anyway. Fail safe, I hope.
-
- * By default, ksymoops reads its entire input file. If the -1 toggle
- is set, it will run in one shot mode and exit after the first Oops.
- This is useful for automatically mailing reports as they happen,
- like this :-
-
- #!/bin/sh
- # ksymoops1
- while (true)
- do
- ksymoops -1 > $HOME/oops1
- if [ $? -eq 3 ]
- then
- exit 0
- fi
- mail -s Oops admin < $HOME/oops1
- done
-
- tail -f /var/log/messages | ksymoops1
-
- Restarting after log rotation is left as an exercise for the reader.
+Keith Owens <kaos@ocs.com.au> Sat Jun 19 10:30:34 EST 1999
+++ /dev/null
-/*
- io.c.
-
- Local I/O routines for ksymoops.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- fwrite_local is redundant, replaced by bfd.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
-
- */
-
-#include "ksymoops.h"
-#include <errno.h>
-#include <malloc.h>
-#include <string.h>
-#include <sys/stat.h>
-
-int regular_file(const char *file, const char *msg)
-{
- struct stat statbuf;
- if (stat(file, &statbuf)) {
- fprintf(stderr, "%s: %s stat %s failed",
- prefix, msg, file);
- perror(" ");
- ++errors;
- return 0;
- }
-
- if (!S_ISREG(statbuf.st_mode)) {
- fprintf(stderr,
- "%s: %s %s is not a regular file, ignored\n",
- prefix, msg, file);
- ++errors;
- return 0;
- }
- return 1;
-}
-
-FILE *fopen_local(const char *file, const char *mode, const char *msg)
-{
- FILE *f;
- if (!(f = fopen(file, mode))) {
- fprintf(stderr, "%s: %s fopen '%s' failed",
- prefix, msg, file);
- perror(" ");
- ++errors;
- }
- return f;
-}
-
-void fclose_local(FILE *f, const char *msg)
-{
- int i;
- if ((i = fclose(f))) {
- fprintf(stderr, "%s: %s fclose failed %d", prefix, msg, i);
- perror(" ");
- ++errors;
- }
-}
-
-/* Read a line, increasing the size of the line as necessary until \n is read */
-#define INCREMENT 10 /* arbitrary */
-char *fgets_local(char **line, int *size, FILE *f, const char *msg)
-{
- char *l, *p, *r;
- int longline = 1;
-
- if (!*line) {
- *size = INCREMENT;
- *line = malloc(*size);
- if (!*line)
- malloc_error("fgets_local alloc line");
- }
-
- l = *line;
- while (longline) {
- r = fgets(l, *size-(l-*line), f);
- if (!r) {
- if (ferror(f)) {
- fprintf(stderr,
- "%s: %s fgets failed", prefix, msg);
- perror(" ");
- ++errors;
- }
- if (l != *line)
- return(*line);
- else
- return(r);
- }
- if (!(p = strchr(*line, '\n'))) {
- *size += INCREMENT;
- *line = realloc(*line, *size);
- if (!*line)
- malloc_error("fgets_local realloc line");
- l = *line+*size-INCREMENT-1;
- }
- else {
- *p = '\0';
- longline = 0;
- }
- }
-
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s line '%s'\n", msg, *line);
- return(*line);
-}
-
-FILE *popen_local(const char *cmd, const char *msg)
-{
- FILE *f;
- if (!(f = popen(cmd, "r"))) {
- fprintf(stderr, "%s: %s popen '%s' failed",
- prefix, msg, cmd);
- perror(" ");
- ++errors;
- }
- return f;
-}
-
-void pclose_local(FILE *f, const char *msg)
-{
- int i;
- errno = 0;
- if ((i = pclose(f))) {
- fprintf(stderr, "%s: %s pclose failed 0x%x", prefix, msg, i);
- if (errno)
- perror(" ");
- else
- fprintf(stderr, "\n");
- ++errors;
- }
-}
+++ /dev/null
-/*
- ksymoops.c.
-
- Read a kernel Oops file and make the best stab at converting the code to
- instructions and mapping stack values to kernel symbols.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-*/
-
-#define VERSION "0.6e"
-
-/*
-
- Tue Jan 5 19:26:02 EST 1999
- Version 0.6e
- Added to kernel.
-
- Mon Jan 4 09:48:13 EST 1999
- Version 0.6d
- Add ARM support.
-
- Thu Nov 26 16:37:46 EST 1998
- Version 0.6c
- Typo in oops_code.
- Add -c option.
- Add -1 option.
- Report if options were specified or defaulted.
-
- Fri Nov 6 10:38:42 EST 1998
- Version 0.6b
- Remove false warnings when comparing ksyms and lsmod.
-
- Tue Nov 3 23:33:04 EST 1998
- Version 0.6a
- Performance inprovements.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Read lsmod (/proc/modules).
- Ignore addresses 0-4095 when mapping address to symbol.
- Discard default objects if -o specified.
- Oops file must be regular.
- Add "invalid operand" to Oops_print.
- Move "Using_Version" copy to map.c.
- Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
- Minor adjustment to re for ppc.
- Minor adjustment to re for objdump lines with <_EIP+xxx>.
- Convert from a.out to bfd, using same format as ksymoops.
- Added MIPS.
- PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
-
- Wed Oct 28 23:14:55 EST 1998
- Version 0.5
- No longer read vmlinux by default, it only duplicates System.map.
-
- Wed Oct 28 13:47:38 EST 1998
- Version 0.4
- Split into separate sources.
-
- Mon Oct 26 00:01:47 EST 1998
- Version 0.3c
- Add alpha (arm) processing.
-
- Mon Oct 26 00:01:47 EST 1998
- Version 0.3b
- Add sparc processing.
- Handle kernel symbol versions.
-
- Fri Oct 23 13:11:20 EST 1998
- Version 0.3
- Add -follow to find command for people who use symlinks to modules.
- Add Version_ checking.
-
- Thu Oct 22 22:28:30 EST 1998
- Version 0.2.
- Generalise text prefix handling.
- Handle messages on Code: line.
- Format addresses with leading zeroes.
- Minor bug fixes.
-
- Wed Oct 21 23:28:48 EST 1998
- Version 0.1. Rewrite from scratch in C.
-
- CREDITS.
- Oops disassembly based on ksymoops.cc,
- Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
- m68k code based on ksymoops.cc changes by
- Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
- */
-
-#include "ksymoops.h"
-#include <ctype.h>
-#include <errno.h>
-#include <malloc.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/utsname.h>
-
-char *prefix;
-char *path_nm = "/usr/bin/nm"; /* env KSYMOOPS_NM */
-char *path_find = "/usr/bin/find"; /* env KSYMOOPS_FIND */
-char *path_objdump = "/usr/bin/objdump"; /* env KSYMOOPS_OBJDUMP */
-int debug = 0;
-int errors = 0;
-int warnings = 0;
-
-SYMBOL_SET ss_vmlinux;
-SYMBOL_SET ss_ksyms_base;
-SYMBOL_SET *ss_ksyms_module;
-int ss_ksyms_modules;
-SYMBOL_SET ss_lsmod;
-SYMBOL_SET *ss_object;
-int ss_objects;
-SYMBOL_SET ss_system_map;
-
-SYMBOL_SET ss_merged; /* merged map with info from all sources */
-SYMBOL_SET ss_Version; /* Version_ numbers where available */
-
-/* Regular expression stuff */
-
-regex_t re_nm;
-regmatch_t *re_nm_pmatch;
-regex_t re_bracketed_address;
-regmatch_t *re_bracketed_address_pmatch;
-regex_t re_unbracketed_address;
-regmatch_t *re_unbracketed_address_pmatch;
-
-static void usage(void)
-{
- fprintf(stderr, "Version " VERSION "\n");
- fprintf(stderr, "usage: %s\n", prefix);
- fprintf(stderr,
- "\t\t[-v vmlinux]\tWhere to read vmlinux\n"
- "\t\t[-V]\t\tNo vmlinux is available\n"
- "\t\t[-o object_dir]\tDirectory containing modules\n"
- "\t\t[-O]\t\tNo modules is available\n"
- "\t\t[-k ksyms]\tWhere to read ksyms\n"
- "\t\t[-K]\t\tNo ksyms is available\n"
- "\t\t[-l lsmod]\tWhere to read lsmod\n"
- "\t\t[-L]\t\tNo lsmod is available\n"
- "\t\t[-m system.map]\tWhere to read System.map\n"
- "\t\t[-M]\t\tNo System.map is available\n"
- "\t\t[-s save.map]\tSave consolidated map\n"
- "\t\t[-d]\t\tIncrease debug level by 1\n"
- "\t\t[-h]\t\tPrint help text\n"
- "\t\t[-c code_bytes]\tHow many bytes in each unit of code\n"
- "\t\t[-1]\t\tOne shot toggle (exit after first Oops)\n"
- "\t\t<Oops.file\tOops report to decode\n"
- "\n"
- "\t\tAll flags can occur more than once. With the exception "
- "of -o\n"
- "\t\tand -d which are cumulative, the last occurrence of each "
- "flag is\n"
- "\t\tused. Note that \"-v my.vmlinux -V\" will be taken as "
- "\"No vmlinux\n"
- "\t\tavailable\" but \"-V -v my.vmlinux\" will read "
- "my.vmlinux. You\n"
- "\t\twill be warned about such combinations.\n"
- "\n"
- "\t\tEach occurrence of -d increases the debug level.\n"
- "\n"
- "\t\tEach -o flag can refer to a directory or to a single "
- "object\n"
- "\t\tfile. If a directory is specified then all *.o files in "
- "that\n"
- "\t\tdirectory and its subdirectories are assumed to be "
- "modules.\n"
- "\n"
- "\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
- "options\n"
- "\t\tcontain the string *r (*m, *n, *s) then it is replaced "
- "at run\n"
- "\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
- "\n"
- "\t\tThe defaults can be changed in the Makefile, current "
- "defaults\n"
- "\t\tare\n\n"
- "\t\t\t"
-#ifdef DEF_VMLINUX
- "-v " DEF_LINUX
-#else
- "-V"
-#endif
- "\n"
- "\t\t\t"
-#ifdef DEF_OBJECTS
- "-o " DEF_OBJECTS
-#else
- "-O"
-#endif
- "\n"
- "\t\t\t"
-#ifdef DEF_KSYMS
- "-k " DEF_KSYMS
-#else
- "-K"
-#endif
- "\n"
- "\t\t\t"
-#ifdef DEF_LSMOD
- "-l " DEF_LSMOD
-#else
- "-L"
-#endif
- "\n"
- "\t\t\t"
-#ifdef DEF_MAP
- "-m " DEF_MAP
-#else
- "-M"
-#endif
- "\n"
- "\t\t\t-c %d\n" /* DEF_CODE_BYTES */
- "\t\t\tOops report is read from stdin\n"
- "\n",
- DEF_CODE_BYTES
- );
-}
-
-/* Check if possibly conflicting options were specified */
-static void multi_opt(int specl, int specu, char type, const char *using)
-{
- if (specl && specu) {
- fprintf(stderr,
- "Warning - you specified both -%c and -%c. Using '",
- type, toupper(type));
- ++warnings;
- if (using) {
- fprintf(stderr, "-%c %s", type, using);
- if (type == 'o')
- fprintf(stderr, " ...");
- fprintf(stderr, "'\n");
- }
- else
- fprintf(stderr, "-%c'\n", toupper(type));
- }
- else if (specl > 1 && type != 'o') {
- fprintf(stderr,
- "Warning - you specified -%c more than once. "
- "Using '-%c %s'\n",
- type, type, using);
- ++warnings;
- }
- else if (specu > 1) {
- fprintf(stderr,
- "Warning - you specified -%c more than once. "
- "Second and subsequent '-%c' ignored\n",
- toupper(type), toupper(type));
- ++warnings;
- }
-}
-
-/* If a name contains *r (*m, *n, *s), replace with the current value of
- * `uname -r` (-m, -n, -s). Actually uses uname system call rather than the
- * uname command but the result is the same.
- */
-static void convert_uname(char **name)
-{
- char *p, *newname, *oldname, *replacement;
- unsigned len;
- int free_oldname = 0;
- static char procname[] = "convert_uname";
-
- if (!*name)
- return;
-
- while ((p = strchr(*name, '*'))) {
- struct utsname buf;
- int i = uname(&buf);
- if (debug)
- fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
- if (i) {
- fprintf(stderr,
- "%s: uname failed, %s will not be processed\n",
- prefix, *name);
- perror(prefix);
- ++errors;
- return;
- }
- switch (*(p+1)) {
- case 'r':
- replacement = buf.release;
- break;
- case 'm':
- replacement = buf.machine;
- break;
- case 'n':
- replacement = buf.nodename;
- break;
- case 's':
- replacement = buf.sysname;
- break;
- default:
- fprintf(stderr,
- "%s: invalid replacement character '*%c' "
- "in %s\n",
- prefix, *(p+1), *name);
- ++errors;
- return;
- }
- len = strlen(*name)-2+strlen(replacement)+1;
- if (!(newname = malloc(len)))
- malloc_error(procname);
- strncpy(newname, *name, (p-*name));
- strcpy(newname+(p-*name), replacement);
- strcpy(newname+(p-*name)+strlen(replacement), p+2);
- p = newname+(p-*name)+strlen(replacement); /* no rescan */
- oldname = *name;
- *name = newname;
- if (free_oldname)
- free(oldname);
- free_oldname = 1;
- if (debug)
- fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
- }
- return;
-}
-
-/* Report if the option was specified or defaulted */
-static void spec_or_default(int spec, int *some_spec) {
- if (spec) {
- printf(" (specified)\n");
- if (some_spec)
- *some_spec = 1;
- }
- else
- printf(" (default)\n");
-}
-
-/* Parse the options. Verbose but what's new with getopt? */
-static void parse(int argc,
- char **argv,
- char **vmlinux,
- char ***object,
- int *objects,
- char **ksyms,
- char **lsmod,
- char **system_map,
- char **save_system_map,
- char ***filename,
- int *filecount,
- int *spec_h,
- int *code_bytes,
- int *one_shot
- )
-{
- int spec_v = 0, spec_V = 0;
- int spec_o = 0, spec_O = 0;
- int spec_k = 0, spec_K = 0;
- int spec_l = 0, spec_L = 0;
- int spec_m = 0, spec_M = 0;
- int spec_s = 0;
- int spec_c = 0;
-
- int c, i, some_spec = 0;
- char *p;
-
- while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dhc:1")) != EOF) {
- if (debug && c != 'd')
- fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
- switch(c) {
- case 'v':
- *vmlinux = optarg;
- ++spec_v;
- break;
- case 'V':
- *vmlinux = NULL;
- ++spec_V;
- break;
- case 'o':
- if (!spec_o) {
- /* First -o, discard default value(s) */
- for (i = 0; i < *objects; ++i)
- free((*object)[i]);
- free(*object);
- *object = NULL;
- *objects = 0;
- }
- *object = realloc(*object,
- ((*objects)+1)*sizeof(**object));
- if (!*object)
- malloc_error("object");
- if (!(p = strdup(optarg)))
- malloc_error("strdup -o");
- else {
- (*object)[(*objects)++] = p;
- ++spec_o;
- }
- break;
- case 'O':
- ++spec_O;
- for (i = 0; i < *objects; ++i)
- free((*object)[i]);
- free(*object);
- *object = NULL;
- *objects = 0;
- break;
- case 'k':
- *ksyms = optarg;
- ++spec_k;
- break;
- case 'K':
- *ksyms = NULL;
- ++spec_K;
- break;
- case 'l':
- *lsmod = optarg;
- ++spec_l;
- break;
- case 'L':
- *lsmod = NULL;
- ++spec_L;
- break;
- case 'm':
- *system_map = optarg;
- ++spec_m;
- break;
- case 'M':
- *system_map = NULL;
- ++spec_M;
- break;
- case 's':
- *save_system_map = optarg;
- ++spec_s;
- break;
- case 'd':
- ++debug;
- break;
- case 'h':
- usage();
- ++*spec_h;
- break;
- case 'c':
- ++spec_c;
- errno = 0;
- *code_bytes = strtoul(optarg, &p, 10);
- /* Oops_code_values assumes that code_bytes is a
- * multiple of 2.
- */
- if (!*optarg || *p || errno ||
- (*code_bytes != 1 &&
- *code_bytes != 2 &&
- *code_bytes != 4 &&
- *code_bytes != 8)) {
- fprintf(stderr,
- "%s Invalid value for -c '%s'\n",
- prefix, optarg);
- ++errors;
- if (errno)
- perror(" ");
- *code_bytes = DEF_CODE_BYTES;
- }
- break;
- case '1':
- *one_shot = !*one_shot;
- break;
- case '?':
- usage();
- exit(2);
- }
- }
-
- *filecount = argc - optind;
- *filename = argv + optind;
-
- /* Expand any requests for the current uname values */
- convert_uname(vmlinux);
- if (*objects) {
- for (i = 0; i < *objects; ++i)
- convert_uname(*object+i);
- }
- convert_uname(ksyms);
- convert_uname(lsmod);
- convert_uname(system_map);
-
- /* Check for multiple options specified */
- multi_opt(spec_v, spec_V, 'v', *vmlinux);
- multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
- multi_opt(spec_k, spec_K, 'k', *ksyms);
- multi_opt(spec_l, spec_L, 'l', *lsmod);
- multi_opt(spec_m, spec_M, 'm', *system_map);
-
- printf("Options used:");
- if (*vmlinux)
- printf(" -v %s", *vmlinux);
- else
- printf(" -V");
- spec_or_default(spec_v || spec_V, &some_spec);
-
- printf(" ");
- if (*objects) {
- for (i = 0; i < *objects; ++i)
- printf(" -o %s", (*object)[i]);
- }
- else
- printf(" -O");
- spec_or_default(spec_o || spec_O, &some_spec);
-
- printf(" ");
- if (*ksyms)
- printf(" -k %s", *ksyms);
- else
- printf(" -K");
- spec_or_default(spec_k || spec_K, &some_spec);
-
- printf(" ");
- if (*lsmod)
- printf(" -l %s", *lsmod);
- else
- printf(" -L");
- spec_or_default(spec_l || spec_L, &some_spec);
-
- printf(" ");
- if (*system_map)
- printf(" -m %s", *system_map);
- else
- printf(" -M");
- spec_or_default(spec_m || spec_M, &some_spec);
-
- printf(" ");
- printf(" -c %d", *code_bytes);
- spec_or_default(spec_c, NULL);
-
- if (*one_shot) {
- printf(" ");
- printf(" -1");
- }
-
- printf("\n");
-
- if (!some_spec) {
- printf(
-"You did not tell me where to find symbol information. I will assume\n"
-"that the log matches the kernel and modules that are running right now\n"
-"and I'll use the default options above for symbol resolution.\n"
-"If the current kernel and/or modules do not match the log, you can get\n"
-"more accurate output by telling me the kernel version and where to find\n"
-"map, modules, ksyms etc. ksymoops -h explains the options.\n"
- "\n");
- ++warnings;
- }
-}
-
-/* Read environment variables */
-static void read_env(const char *external, char **internal)
-{
- char *p;
- if ((p = getenv(external))) {
- *internal = p;
- if (debug)
- fprintf(stderr,
- "DEBUG: env override %s=%s\n",
- external, *internal);
- }
- else {
- if (debug)
- fprintf(stderr,
- "DEBUG: env default %s=%s\n",
- external, *internal);
- }
-}
-
-
-int main(int argc, char **argv)
-{
- char *vmlinux = NULL;
- char **object = NULL;
- int objects = 0;
- char *ksyms = NULL;
- char *lsmod = NULL;
- char *system_map = NULL;
- char *save_system_map = NULL;
- char **filename;
- int filecount = 0;
- int spec_h = 0; /* -h was specified */
- int code_bytes = DEF_CODE_BYTES;
- int one_shot = 0;
- int i, ret;
-
- prefix = *argv;
- setvbuf(stdout, NULL, _IONBF, 0);
-
-#ifdef DEF_VMLINUX
- vmlinux = DEF_LINUX;
-#endif
-#ifdef DEF_OBJECTS
- {
- char *p;
- object = realloc(object, (objects+1)*sizeof(*object));
- if (!object)
- malloc_error("DEF_OBJECTS");
- if (!(p = strdup(DEF_OBJECTS)))
- malloc_error("DEF_OBJECTS");
- else
- object[objects++] = p;
- }
-#endif
-#ifdef DEF_KSYMS
- ksyms = DEF_KSYMS;
-#endif
-#ifdef DEF_LSMOD
- lsmod = DEF_LSMOD;
-#endif
-#ifdef DEF_MAP
- system_map = DEF_MAP;
-#endif
-
- parse(argc,
- argv,
- &vmlinux,
- &object,
- &objects,
- &ksyms,
- &lsmod,
- &system_map,
- &save_system_map,
- &filename,
- &filecount,
- &spec_h,
- &code_bytes,
- &one_shot
- );
-
- if (spec_h && filecount == 0)
- return(0); /* just the help text */
-
- if (errors)
- return(1);
-
- if (debug)
- fprintf(stderr, "DEBUG: level %d\n", debug);
-
- read_env("KSYMOOPS_NM", &path_nm);
- read_env("KSYMOOPS_FIND", &path_find);
- read_env("KSYMOOPS_OBJDUMP", &path_objdump);
-
- re_compile_common();
- ss_init_common();
-
- read_vmlinux(vmlinux);
- read_ksyms(ksyms);
- /* No point in reading modules unless ksyms shows modules loaded */
- if (ss_ksyms_modules) {
- expand_objects(object, objects);
- for (i = 0; i < ss_objects; ++i)
- read_object(ss_object[i].source, i);
- }
- else if (objects)
- printf("No modules in ksyms, skipping objects\n");
- /* No point in reading lsmod without ksyms */
- if (ss_ksyms_modules || ss_ksyms_base.used)
- read_lsmod(lsmod);
- else if (lsmod)
- printf("No ksyms, skipping lsmod\n");
- read_system_map(system_map);
- merge_maps(save_system_map);
-
- /* After all that work, it is finally time to read the Oops report */
- ret = Oops_read(filecount, filename, code_bytes, one_shot);
-
- if (warnings || errors) {
- printf("\n");
- if (warnings)
- printf("%d warning%s ",
- warnings, warnings == 1 ? "" : "s");
- if (warnings && errors)
- printf("and ");
- if (errors)
- printf("%d error%s ", errors, errors == 1 ? "" : "s");
- printf("issued. Results may not be reliable.\n");
- if (!ret)
- return(1);
- }
-
- return(ret);
-}
+++ /dev/null
-/*
- ksymoops.h.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Read lsmod (/proc/modules).
- Convert from a.out to bfd, using same format as ksymoops.
- PPC trace addresses are not bracketed, add new re.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
-*/
-
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-
-
-/* Pity this is not externalised, see binfmt_elf.c */
-#define elf_addr_t unsigned long
-
-extern char *prefix;
-extern char *path_nm; /* env KSYMOOPS_NM */
-extern char *path_find; /* env KSYMOOPS_FIND */
-extern char *path_objdump; /* env KSYMOOPS_OBJDUMP */
-extern int debug;
-extern int errors;
-extern int warnings;
-
-typedef struct symbol SYMBOL;
-
-struct symbol {
- char *name; /* name of symbol */
- char type; /* type of symbol from nm/System.map */
- char keep; /* keep this symbol in merged map? */
- elf_addr_t address; /* address in kernel */
-};
-
-/* Header for symbols from one particular source */
-
-typedef struct symbol_set SYMBOL_SET;
-
-struct symbol_set {
- char *source; /* where the symbols came from */
- int used; /* number of symbols used */
- int alloc; /* number of symbols allocated */
- SYMBOL *symbol; /* dynamic array of symbols */
- SYMBOL_SET *related; /* any related symbol set */
-};
-
-extern SYMBOL_SET ss_vmlinux;
-extern SYMBOL_SET ss_ksyms_base;
-extern SYMBOL_SET *ss_ksyms_module;
-extern int ss_ksyms_modules;
-extern SYMBOL_SET ss_lsmod;
-extern SYMBOL_SET *ss_object;
-extern int ss_objects;
-extern SYMBOL_SET ss_system_map;
-
-extern SYMBOL_SET ss_merged; /* merged map with info from all sources */
-extern SYMBOL_SET ss_Version; /* Version_ numbers where available */
-
-/* Regular expression stuff */
-
-extern regex_t re_nm;
-extern regmatch_t *re_nm_pmatch;
-extern regex_t re_bracketed_address;
-extern regmatch_t *re_bracketed_address_pmatch;
-extern regex_t re_unbracketed_address;
-extern regmatch_t *re_unbracketed_address_pmatch;
-
-/* Bracketed address: optional '[', required '<', at least 4 hex characters,
- * required '>', optional ']', optional white space.
- */
-#define BRACKETED_ADDRESS "\\[*<([0-9a-fA-F]{4,})>\\]*[ \t]*"
-
-#define UNBRACKETED_ADDRESS "([0-9a-fA-F]{4,})[ \t]*"
-
-/* io.c */
-extern int regular_file(const char *file, const char *msg);
-extern FILE *fopen_local(const char *file, const char *mode, const char *msg);
-extern void fclose_local(FILE *f, const char *msg);
-extern char *fgets_local(char **line, int *size, FILE *f, const char *msg);
-extern int fwrite_local(void const *ptr, size_t size, size_t nmemb,
- FILE *stream, const char *msg);
-extern FILE *popen_local(const char *cmd, const char *msg);
-extern void pclose_local(FILE *f, const char *msg);
-
-/* ksyms.c */
-extern void read_ksyms(const char *ksyms);
-extern void map_ksyms_to_modules(void);
-extern void read_lsmod(const char *lsmod);
-extern void compare_ksyms_lsmod(void);
-
-/* misc.c */
-extern void malloc_error(const char *msg);
-extern const char *format_address(elf_addr_t address);
-extern char *find_fullpath(const char *program);
-
-/* map.c */
-extern void read_system_map(const char *system_map);
-extern void merge_maps(const char *save_system_map);
-extern void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
- int precedence);
-
-
-/* object.c */
-extern SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss);
-extern void read_vmlinux(const char *vmlinux);
-extern void expand_objects(char * const *object, int objects);
-extern void read_object(const char *object, int i);
-
-/* oops.c */
-extern int Oops_read(int filecount, char * const *filename, int code_bytes,
- int one_shot);
-
-/* re.c */
-extern void re_compile(regex_t *preg, const char *regex, int cflags,
- regmatch_t **pmatch);
-extern void re_compile_common(void);
-extern void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
- char ***string);
-extern void re_strings_free(const regex_t *preg, char ***string);
-extern void re_string_check(int need, int available, const char *msg);
-
-/* symbol.c */
-extern void ss_init(SYMBOL_SET *ss, const char *msg);
-extern void ss_free(SYMBOL_SET *ss);
-extern void ss_init_common(void);
-extern SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol,
- int *start);
-extern void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
- const char type, const char keep, const char *symbol);
-extern void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
- const char keep, const char *symbol);
-extern char *map_address(const SYMBOL_SET *ss, const elf_addr_t address);
-extern void ss_sort_atn(SYMBOL_SET *ss);
-extern void ss_sort_na(SYMBOL_SET *ss);
-extern SYMBOL_SET *ss_copy(const SYMBOL_SET *ss);
-extern void add_Version(const char *version, const char *source);
-extern void extract_Version(SYMBOL_SET *ss);
-extern void compare_Version(void);
+++ /dev/null
-/*
- ksyms.c.
-
- Process ksyms for ksymoops.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Fri Nov 6 10:38:42 EST 1998
- Version 0.6b
- Remove false warnings when comparing ksyms and lsmod.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Read lsmod (/proc/modules).
- Move "Using_Version" copy to map.c.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-
-/* Scan one line from ksyms. Split lines into the base symbols and the module
- * symbols. Separate ss for base and each module.
- */
-static void scan_ksyms_line(const char *line)
-{
- int i;
- char **string = NULL;
- SYMBOL_SET *ssp;
- static char *prev_module = NULL;
- static regex_t re_ksyms;
- static regmatch_t *re_ksyms_pmatch;
- static char const procname[] = "scan_ksyms_line";
-
- /* ksyms: address, symbol, optional module */
- re_compile(&re_ksyms,
- "^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
- REG_NEWLINE|REG_EXTENDED,
- &re_ksyms_pmatch);
-
- i = regexec(&re_ksyms, line,
- re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i)
- return;
-
- /* string [1] - address, [2] - symbol, [3] - white space+module,
- * [4] - module.
- */
- re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
- if (string[4]) {
- if (!prev_module || strcmp(prev_module, string[4])) {
- /* start of a new module in ksyms */
- ++ss_ksyms_modules;
- ss_ksyms_module = realloc(ss_ksyms_module,
- ss_ksyms_modules*sizeof(*ss_ksyms_module));
- if (!ss_ksyms_module)
- malloc_error("realloc ss_ksyms_module");
- ssp = ss_ksyms_module+ss_ksyms_modules-1;
- ss_init(ssp, string[4]);
- prev_module = strdup(string[4]);
- if (!prev_module)
- malloc_error("strdup prev_module");
- }
- ssp = ss_ksyms_module+ss_ksyms_modules-1;
- }
- else
- ssp = &ss_ksyms_base;
- add_symbol(ssp, string[1], ' ', 1, string[2]);
- re_strings_free(&re_ksyms, &string);
-}
-
-/* Read the symbols from ksyms. */
-void read_ksyms(const char *ksyms)
-{
- FILE *f;
- char *line = NULL;
- int i, size;
- static char const procname[] = "read_ksyms";
-
- if (!ksyms)
- return;
- ss_init(&ss_ksyms_base, "ksyms_base");
- if (debug)
- fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
-
- if (!regular_file(ksyms, procname))
- return;
-
- if (!(f = fopen_local(ksyms, "r", procname)))
- return;
-
- while (fgets_local(&line, &size, f, procname))
- scan_ksyms_line(line);
-
- fclose_local(f, procname);
- free(line);
-
- for (i = 0; i < ss_ksyms_modules; ++i) {
- ss_sort_na(ss_ksyms_module+i);
- extract_Version(ss_ksyms_module+i);
- }
- if (ss_ksyms_base.used) {
- ss_sort_na(&ss_ksyms_base);
- extract_Version(&ss_ksyms_base);
- }
- else {
- fprintf(stderr,
- "Warning, no kernel symbols in ksyms, is %s a valid "
- "ksyms file?\n",
- ksyms);
- ++warnings;
- }
-
- if (debug > 1) {
- for (i = 0; i < ss_ksyms_modules; ++i) {
- fprintf(stderr,
- "DEBUG: %s %s used %d out of %d entries\n",
- procname,
- ss_ksyms_module[i].source,
- ss_ksyms_module[i].used,
- ss_ksyms_module[i].alloc);
- }
- fprintf(stderr,
- "DEBUG: %s %s used %d out of %d entries\n",
- procname, ss_ksyms_base.source, ss_ksyms_base.used,
- ss_ksyms_base.alloc);
- }
-}
-
-/* Map each ksyms module entry to the corresponding object entry. Tricky,
- * see the comments in the docs about needing a unique symbol in each
- * module.
- */
-static void map_ksym_to_module(SYMBOL_SET *ss)
-{
- int i, j, matches;
- char *name = NULL;
-
- for (i = 0; i < ss->used; ++i) {
- matches = 0;
- for (j = 0; j < ss_objects; ++j) {
- name = (ss->symbol)[i].name;
- if (find_symbol_name(ss_object+j, name, NULL)) {
- ++matches;
- ss->related = ss_object+j;
- }
- }
- if (matches == 1)
- break; /* unique symbol over all objects */
- ss->related = NULL; /* keep looking */
- }
- if (!(ss->related)) {
- fprintf(stderr,
- "Warning: cannot match loaded module %s to any "
- "module object. Trace may not be reliable.\n",
- ss->source);
- ++warnings;
- }
- else if (debug)
- fprintf(stderr,
- "DEBUG: ksyms %s matches to %s based on unique "
- "symbol %s\n",
- ss->source, ss->related->source, name);
-}
-
-/* Map all ksyms module entries to their corresponding objects */
-void map_ksyms_to_modules(void)
-{
- int i;
- SYMBOL_SET *ss, *ssc;
-
- for (i = 0; i < ss_ksyms_modules; ++i) {
- ss = ss_ksyms_module+i;
- map_ksym_to_module(ss);
- if (ss->related) {
- ssc = adjust_object_offsets(ss);
- compare_maps(ss, ssc, 1);
- }
- }
-}
-
-/* Read the modules from lsmod. */
-void read_lsmod(const char *lsmod)
-{
- FILE *f;
- char *line = NULL;
- int i, size;
- char **string = NULL;
- static regex_t re_lsmod;
- static regmatch_t *re_lsmod_pmatch;
- static char const procname[] = "read_lsmod";
-
- if (!lsmod)
- return;
- ss_init(&ss_lsmod, "lsmod");
- if (debug)
- fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
-
- if (!regular_file(lsmod, procname))
- return;
-
- if (!(f = fopen_local(lsmod, "r", procname)))
- return;
-
- /* lsmod: module, size, use count, optional used by */
- re_compile(&re_lsmod,
- "^"
- "[ \t]*([^ \t]+)" /* 1 module */
- "[ \t]*([^ \t]+)" /* 2 size */
- "[ \t]*([^ \t]+)" /* 3 count */
- "[ \t]*(.*)" /* 4 used by */
- "$",
- REG_NEWLINE|REG_EXTENDED,
- &re_lsmod_pmatch);
-
- while (fgets_local(&line, &size, f, procname)) {
- i = regexec(&re_lsmod, line,
- re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i)
- continue;
- re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
- add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
- }
-
- fclose_local(f, procname);
- free(line);
- re_strings_free(&re_lsmod, &string);
- if (ss_lsmod.used)
- ss_sort_na(&ss_lsmod);
- else {
- fprintf(stderr,
- "Warning, no symbols in lsmod, is %s a valid "
- "lsmod file?\n",
- lsmod);
- ++warnings;
- }
-
- if (debug > 1)
- fprintf(stderr,
- "DEBUG: %s %s used %d out of %d entries\n",
- procname, ss_lsmod.source, ss_lsmod.used,
- ss_lsmod.alloc);
-}
-
-/* Compare modules from ksyms against module list in lsmod and vice versa.
- * There is one ss_ for each ksyms module and a single ss_lsmod to cross
- * check.
- */
-void compare_ksyms_lsmod(void)
-{
- int i, j;
- SYMBOL_SET *ss;
- SYMBOL *s;
- static char const procname[] = "compare_ksyms_lsmod";
-
- if (!(ss_lsmod.used && ss_ksyms_modules))
- return;
-
- s = ss_lsmod.symbol;
- for (i = 0; i < ss_lsmod.used; ++i, ++s) {
- for (j = 0; j < ss_ksyms_modules; ++j) {
- ss = ss_ksyms_module+j;
- if (strcmp(s->name, ss->source) == 0)
- break;
- }
- if (j >= ss_ksyms_modules) {
- fprintf(stderr,
- "Warning in %s, module %s is in lsmod but not "
- "in ksyms, probably no symbols exported\n",
- procname, s->name);
- ++warnings;
- }
- }
-
- for (i = 0; i < ss_ksyms_modules; ++i) {
- ss = ss_ksyms_module+i;
- if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
- fprintf(stderr,
- "Error in %s, module %s is in ksyms but not "
- "in lsmod\n",
- procname, ss->source);
- ++errors;
- }
- }
-}
+++ /dev/null
-/*
- map.c.
-
- Read System.map for ksymoops, create merged System.map.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Remove addresses 0-4095 from merged map after writing new map.
- Move "Using_Version" copy to map.c.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-
-/* Read the symbols from System.map */
-void read_system_map(const char *system_map)
-{
- FILE *f;
- char *line = NULL, **string = NULL;
- int i, size = 0;
- static char const procname[] = "read_system_map";
-
- if (!system_map)
- return;
- ss_init(&ss_system_map, "System.map");
- if (debug)
- fprintf(stderr, "DEBUG: %s %s\n", procname, system_map);
-
- if (!regular_file(system_map, procname))
- return;
-
- if (!(f = fopen_local(system_map, "r", procname)))
- return;
-
- while (fgets_local(&line, &size, f, procname)) {
- i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
- re_strings(&re_nm, line, re_nm_pmatch, &string);
- add_symbol(&ss_system_map, string[1], *string[2],
- 1, string[3]);
- }
- }
-
- fclose_local(f, procname);
- re_strings_free(&re_nm, &string);
- free(line);
- if (ss_system_map.used) {
- ss_sort_na(&ss_system_map);
- extract_Version(&ss_system_map);
- }
- else {
- fprintf(stderr,
- "Warning, no kernel symbols in System.map, is %s a "
- "valid System.map file?\n",
- system_map);
- ++warnings;
- }
-
- if (debug > 1)
- fprintf(stderr,
- "DEBUG: %s %s used %d out of %d entries\n",
- procname,
- ss_system_map.source,
- ss_system_map.used,
- ss_system_map.alloc);
-}
-
-/* Compare two maps, all symbols in the first should appear in the second. */
-void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
- int precedence)
-{
- int i, start = 0;
- SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1;
- const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1;
-
- if (!(ss1->used && ss2->used))
- return;
-
- if (debug > 1)
- fprintf(stderr,
- "DEBUG: compare_maps %s vs %s, %s takes precedence\n",
- ss1->source, ss2->source,
- precedence == 1 ? ss1->source : ss2->source);
-
- for (i = 0; i < ss1->used; ++i) {
- s1 = ss1->symbol+i;
- if (!(s1->keep))
- continue;
- s2 = find_symbol_name(ss2, s1->name, &start);
- if (!s2) {
- /* Some types only appear in nm output, not in things
- * like System.map. Silently ignore them.
- */
- if (s1->type == 'a' || s1->type == 't')
- continue;
- fprintf(stderr,
- "Warning: %s symbol %s not found in %s. "
- "Ignoring %s entry\n",
- ss1->source, s1->name,
- ss2->source, (*ssdrop)->source);
- ++warnings;
- if (*sdrop)
- (*sdrop)->keep = 0;
- }
- else if (s1->address != s2->address) {
- /* Type C symbols cannot be resolved from nm to ksyms,
- * silently ignore them.
- */
- if (s1->type == 'C' || s2->type == 'C')
- continue;
- fprintf(stderr,
- "Warning: mismatch on symbol %s %c, "
- "%s says %lx, %s says %lx. "
- "Ignoring %s entry\n",
- s1->name, s1->type, ss1->source, s1->address,
- ss2->source, s2->address, (*ssdrop)->source);
- ++warnings;
- if (*sdrop)
- (*sdrop)->keep = 0;
- }
- else
- ++start; /* step to next entry in ss2 */
- }
-}
-
-/* Append the second symbol set onto the first */
-static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2)
-{
- int i;
- SYMBOL *s;
-
- if (!ss2 || !ss2->used)
- return;
- if (debug > 1)
- fprintf(stderr, "DEBUG: append_map %s to %s\n",
- ss2->source, ss1->source);
-
- for (i = 0; i < ss2->used; ++i) {
- s = ss2->symbol+i;
- if (s->keep)
- add_symbol_n(ss1, s->address, s->type, 1,
- s->name);
- }
-}
-
-/* Compare the various sources and build a merged system map */
-void merge_maps(const char *save_system_map)
-{
- int i;
- SYMBOL *s;
- FILE *f;
- static char const procname[] = "merge_maps";
-
- if (debug)
- fprintf(stderr, "DEBUG: %s\n", procname);
-
- /* Using_Versions only appears in ksyms, copy to other tables */
- if ((s = find_symbol_name(&ss_ksyms_base,
- "Using_Versions", 0))) {
- if (ss_system_map.used) {
- add_symbol_n(&ss_system_map, s->address,
- s->type, s->keep, s->name);
- ss_sort_na(&ss_system_map);
- }
- if (ss_vmlinux.used) {
- add_symbol_n(&ss_vmlinux, s->address, s->type,
- s->keep, s->name);
- ss_sort_na(&ss_vmlinux);
- }
- }
-
- compare_Version(); /* highlight any version problems first */
- compare_ksyms_lsmod(); /* highlight any missing modules next */
- compare_maps(&ss_ksyms_base, &ss_vmlinux, 2);
- compare_maps(&ss_system_map, &ss_vmlinux, 2);
- compare_maps(&ss_vmlinux, &ss_system_map, 1);
- compare_maps(&ss_ksyms_base, &ss_system_map, 2);
-
- if (ss_objects) {
- map_ksyms_to_modules();
- }
-
- ss_init(&ss_merged, "merged");
- append_map(&ss_merged, &ss_vmlinux);
- append_map(&ss_merged, &ss_ksyms_base);
- append_map(&ss_merged, &ss_system_map);
- for (i = 0; i < ss_ksyms_modules; ++i)
- append_map(&ss_merged, (ss_ksyms_module+i)->related);
- if (!ss_merged.used) {
- fprintf(stderr, "Warning, no symbols in merged map\n");
- ++warnings;
- }
-
- /* drop duplicates, type a (registers) and gcc2_compiled. */
- ss_sort_atn(&ss_merged);
- s = ss_merged.symbol;
- for (i = 0; i < ss_merged.used-1; ++i) {
- if (s->type == 'a' ||
- (s->type == 't' && !strcmp(s->name, "gcc2_compiled.")))
- s->keep = 0;
- else if (strcmp(s->name, (s+1)->name) == 0 &&
- s->address == (s+1)->address) {
- if (s->type != ' ')
- (s+1)->keep = 0;
- else
- s->keep = 0;
- }
- ++s;
- }
- ss_sort_atn(&ss_merged); /* will remove dropped variables */
-
- if (save_system_map) {
- if (debug)
- fprintf(stderr, "DEBUG: writing merged map to %s\n",
- save_system_map);
- if (!(f = fopen_local(save_system_map, "w", procname)))
- return;
- s = ss_merged.symbol;
- for (i = 0; i < ss_merged.used; ++i) {
- if (s->keep)
- fprintf(f, "%s %c %s\n",
- format_address(s->address),
- s->type, s->name);
- ++s;
- }
- }
-
- /* The merged map may contain symbols with an address of 0, e.g.
- * Using_Versions. These give incorrect results for low addresses in
- * map_address, such addresses map to "Using_Versions+xxx". Remove
- * any addresses below (arbitrary) 4096 from the merged map. AFAIK,
- * Linux does not use the first page on any arch.
- */
- for (i = 0; i < ss_merged.used; ++i) {
- if ((ss_merged.symbol+i)->address < 4096)
- (ss_merged.symbol+i)->keep = 0;
- else
- break;
- }
- if (i)
- ss_sort_atn(&ss_merged); /* remove dropped variables */
-}
+++ /dev/null
-/*
- misc.c.
-
- Miscellaneous routines for ksymoops.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Convert from a.out to bfd, using same format as ksymoops.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-void malloc_error(const char *msg)
-{
- fprintf(stderr, "%s: fatal malloc error for %s\n", prefix, msg);
- exit(2);
-}
-
-/* Format an address with the correct number of leading zeroes */
-const char *format_address(elf_addr_t address)
-{
- /* Well oversized */
- static char format[10], text[200];
- if (!*format)
- snprintf(format, sizeof(format), "%%0%dlx",
- 2*sizeof(address));
- snprintf(text, sizeof(text), format, address);
- return(text);
-}
-
-/* Find the full pathname of a program. Code heavily based on
- * glibc-2.0.5/posix/execvp.c.
- */
-char *find_fullpath(const char *program)
-{
- char *fullpath = NULL;
- char *path, *p;
- size_t len;
- static const char procname[] = "find_fullpath";
-
- /* Don't search when it contains a slash. */
- if (strchr(program, '/')) {
- if (!(fullpath = strdup(program)))
- malloc_error(procname);
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s %s\n", procname, fullpath);
- return(fullpath);
- }
-
- path = getenv ("PATH");
- if (!path) {
- /* There is no `PATH' in the environment. The default search
- path is the current directory followed by the path `confstr'
- returns for `_CS_PATH'.
- */
- len = confstr(_CS_PATH, (char *) NULL, 0);
- if (!(path = malloc(1 + len)))
- malloc_error(procname);
- path[0] = ':';
- confstr(_CS_PATH, path+1, len);
- }
-
- len = strlen(program) + 1;
- if (!(fullpath = malloc(strlen(path) + len)))
- malloc_error(procname);
- p = path;
- do {
- path = p;
- p = strchr(path, ':');
- if (p == NULL)
- p = strchr(path, '\0');
-
- /* Two adjacent colons, or a colon at the beginning or the end
- * of `PATH' means to search the current directory.
- */
- if (p == path)
- memcpy(fullpath, program, len);
- else {
- /* Construct the pathname to try. */
- memcpy(fullpath, path, p - path);
- fullpath[p - path] = '/';
- memcpy(&fullpath[(p - path) + 1], program, len);
- }
-
- /* If we have execute access, assume this is the program. */
- if (access(fullpath, X_OK) == 0) {
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s %s\n",
- procname, fullpath);
- return(fullpath);
- }
- } while (*p++ != '\0');
-
- fprintf(stderr, "Error: %s %s could not find executable %s\n",
- prefix, procname, program);
- ++errors;
- return(NULL);
-}
+++ /dev/null
-/*
- object.c.
-
- object handling routines for ksymoops. Read modules, vmlinux, etc.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-#include <sys/stat.h>
-
-/* Extract all symbols definitions from an object using nm */
-static void read_nm_symbols(SYMBOL_SET *ss, const char *file)
-{
- FILE *f;
- char *cmd, *line = NULL, **string = NULL;
- int i, size = 0;
- static char const procname[] = "read_nm_symbols";
-
- if (!regular_file(file, procname))
- return;
-
- cmd = malloc(strlen(path_nm)+strlen(file)+2);
- if (!cmd)
- malloc_error("nm command");
- strcpy(cmd, path_nm);
- strcat(cmd, " ");
- strcat(cmd, file);
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
- if (!(f = popen_local(cmd, procname)))
- return;
- free(cmd);
-
- while (fgets_local(&line, &size, f, procname)) {
- i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
- re_strings(&re_nm, line, re_nm_pmatch, &string);
- add_symbol(ss, string[1], *string[2], 1, string[3]);
- }
- }
-
- pclose_local(f, procname);
- re_strings_free(&re_nm, &string);
- free(line);
- if (debug > 1)
- fprintf(stderr,
- "DEBUG: %s %s used %d out of %d entries\n",
- procname, ss->source, ss->used, ss->alloc);
-}
-
-/* Read the symbols from vmlinux */
-void read_vmlinux(const char *vmlinux)
-{
- if (!vmlinux)
- return;
- ss_init(&ss_vmlinux, "vmlinux");
- read_nm_symbols(&ss_vmlinux, vmlinux);
- if (ss_vmlinux.used) {
- ss_sort_na(&ss_vmlinux);
- extract_Version(&ss_vmlinux);
- }
- else {
- fprintf(stderr,
- "Warning, no kernel symbols in vmlinux, is %s a valid "
- "vmlinux file?\n",
- vmlinux);
- ++warnings;
- }
-}
-
-
-/* Read the symbols from one object (module) */
-void read_object(const char *object, int i)
-{
- ss_init(ss_object+i, object);
- read_nm_symbols(ss_object+i, object);
- if ((ss_object+i)->used) {
- ss_sort_na(ss_object+i);
- extract_Version(ss_object+i);
- }
- else {
- fprintf(stderr, "Warning, no symbols in %s\n", object);
- ++warnings;
- }
-}
-
-/* Add a new entry to the list of objects */
-static void add_ss_object(const char *file)
-{
- ++ss_objects;
- ss_object = realloc(ss_object, ss_objects*sizeof(*ss_object));
- if (!ss_object)
- malloc_error("realloc ss_object");
- ss_init(ss_object+ss_objects-1, file);
-}
-
-/* Run a directory and its subdirectories, looking for *.o files */
-static void find_objects(const char *dir)
-{
- FILE *f;
- char *cmd, *line = NULL;
- int size = 0, files = 0;
- static char const procname[] = "find_objects";
- static char const options[] = " -follow -name '*.o' -print";
-
- cmd = malloc(strlen(path_find)+1+strlen(dir)+strlen(options)+1);
- if (!cmd)
- malloc_error("find command");
- strcpy(cmd, path_find);
- strcat(cmd, " ");
- strcat(cmd, dir);
- strcat(cmd, options);
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
- if (!(f = popen_local(cmd, procname)))
- return;
- free(cmd);
-
- while (fgets_local(&line, &size, f, procname)) {
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
- add_ss_object(line);
- ++files;
- }
-
- pclose_local(f, procname);
- if (!files) {
- fprintf(stderr,
- "Warning: no *.o files in %s. "
- "Is %s a valid module directory?\n",
- dir, dir);
- ++warnings;
- }
-}
-
-/* Take the user supplied list of objects which can include directories.
- * Expand directories into any *.o files. The results are stored in
- * ss_object, leaving the user supplied options untouched.
- */
-void expand_objects(char * const *object, int objects)
-{
- struct stat statbuf;
- int i;
- const char *file;
- static char const procname[] = "expand_objects";
-
- for (i = 0; i < objects; ++i) {
- file = object[i];
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s checking '%s' - ",
- procname, file);
- if (!stat(file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
- if (debug > 1)
- fprintf(stderr, "directory, expanding\n");
- find_objects(file);
- }
- else {
- if (debug > 1)
- fprintf(stderr, "not directory\n");
- add_ss_object(file);
- }
- }
-}
-
-/* Map a symbol type to a section code. 0 - text, 1 - data, 2 - read only data,
- * 3 - C (cannot relocate), 4 - the rest.
- */
-static int section(char type)
-{
- switch (type) {
- case 'T':
- case 't':
- return 0;
- case 'D':
- case 'd':
- return 1;
- case 'R':
- case 'r':
- return 2;
- case 'C':
- return 3;
- default:
- return 4;
- }
-}
-
-/* Given ksyms module data which has a related object, create a copy of the
- * object data, adjusting the offsets to match where the module was loaded.
- */
-SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss)
-{
- int i;
- elf_addr_t adjust[] = {0, 0, 0, 0, 0};
- SYMBOL *sk, *so;
- SYMBOL_SET *ssc;
-
- if (debug > 1)
- fprintf(stderr,
- "DEBUG: adjust_object_offsets %s\n", ss->source);
-
- ssc = ss_copy(ss->related);
-
- /* For common symbols, calculate the adjustment */
- for (i = 0; i < ss->used; ++i) {
- sk = ss->symbol+i;
- if ((so = find_symbol_name(ssc, sk->name, NULL)))
- adjust[section(so->type)] = sk->address - so->address;
- }
- for (i = 0; i < ssc->used; ++i) {
- so = ssc->symbol+i;
- /* Type C does not relocate well, silently ignore */
- if (so->type != 'C' && adjust[section(so->type)])
- so->address += adjust[section(so->type)];
- else
- so->keep = 0; /* do not merge into final map */
- }
-
- ss->related = ssc; /* map using adjusted copy */
- return(ssc);
-}
+++ /dev/null
-/*
- oops.c.
-
- Oops processing for ksymoop.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Sun Jan 7 12:56:12 CET 1999
- Added SPARC64 support and some SPARC hacks by "Jakub Jelinek"
- <jj@ultra.linux.cz>
-
- Mon Jan 4 08:47:55 EST 1999
- Version 0.6d
- Add ARM support.
-
- Thu Nov 26 16:37:46 EST 1998
- Version 0.6c
- Typo in oops_code.
- Add -c option.
-
- Tue Nov 3 23:33:04 EST 1998
- Version 0.6a
- Performance inprovements.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Oops file must be regular.
- Add "invalid operand" to Oops_print.
- Minor adjustment to re for ppc.
- Minor adjustment to re for objdump lines with <_EIP+xxx>.
- Convert from a.out to bfd, using same format as ksymoops.
- Added MIPS.
- PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into seperate sources.
- */
-
-#include "ksymoops.h"
-#include <bfd.h>
-#include <ctype.h>
-#include <errno.h>
-#include <malloc.h>
-#include <memory.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-enum oops_arch {
- OOPS_NOARCH,
- OOPS_I386,
- OOPS_SPARC,
- OOPS_SPARC64,
- OOPS_ARM,
- OOPS_ALPHA,
- OOPS_MIPS,
- OOPS_PPC,
- OOPS_M68K
-} oops_arch;
-
-char *eip_names[] = { "IP", "EIP", "PC", "PC", "PC", "PC", "PC", "NIP", "PC" };
-
-/* Error detected by bfd */
-static void Oops_bfd_perror(const char *msg)
-{
- fprintf(stderr, "Error ");
- bfd_perror(msg);
- ++errors;
-}
-
-/* Safest way to get correct output bfd format is to copy ksymoops' format. */
-static int Oops_copy_bfd_format(bfd **ibfd, bfd **obfd, asection **isec,
- const char *file)
-{
- char *me, **matches, **match;
-
- if (!(*obfd = bfd_openw(file, NULL))) {
- Oops_bfd_perror(file);
- return(0);
- }
-
- me = find_fullpath(prefix);
- if (!me)
- return(0);
-
- if (!(*ibfd = bfd_openr(me, NULL))) {
- Oops_bfd_perror(me);
- return(0);
- }
- free(me); /* Who is Tommy? */
-
- if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) {
- Oops_bfd_perror(me);
- if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
- fprintf(stderr, "Matching formats:");
- match = matches;
- while (*match)
- fprintf(stderr, " %s", *match++);
- fprintf(stderr, "\n");
- free(matches);
- }
- return(0);
- }
-
- if (!(*isec = bfd_get_section_by_name(*ibfd, ".text"))) {
- Oops_bfd_perror("get_section");
- return(0);
- }
-
- bfd_set_format(*obfd, bfd_object);
- bfd_set_arch_mach(*obfd, bfd_get_arch(*ibfd), bfd_get_mach(*ibfd));
-
- if (!bfd_set_file_flags(*obfd, bfd_get_file_flags(*ibfd))) {
- Oops_bfd_perror("set_file_flags");
- return(0);
- }
-
- return(1);
-}
-
-/* Write the code values to a file using bfd. */
-static int Oops_write_bfd_data(bfd *ibfd, bfd *obfd, asection *isec,
- const char *code, int size)
-{
- asection *osec;
- asymbol *osym;
-
- if (!bfd_set_start_address(obfd, 0)) {
- Oops_bfd_perror("set_start_address");
- return(0);
- }
- if (!(osec = bfd_make_section(obfd, ".text"))) {
- Oops_bfd_perror("make_section");
- return(0);
- }
- if (!bfd_set_section_flags(obfd, osec,
- bfd_get_section_flags(ibfd, isec))) {
- Oops_bfd_perror("set_section_flags");
- return(0);
- }
- if (!bfd_set_section_alignment(obfd, osec,
- bfd_get_section_alignment(ibfd, isec))) {
- Oops_bfd_perror("set_section_alignment");
- return(0);
- }
- osec->output_section = osec;
- if (!(osym = bfd_make_empty_symbol(obfd))) {
- Oops_bfd_perror("make_empty_symbol");
- return(0);
- }
- osym->name = "_EIP";
- osym->section = osec;
- osym->flags = BSF_GLOBAL;
- osym->value = 0;
- if (!bfd_set_symtab(obfd, &osym, 1)) {
- Oops_bfd_perror("set_symtab");
- return(0);
- }
- if (!bfd_set_section_size(obfd, osec, size)) {
- Oops_bfd_perror("set_section_size");
- return(0);
- }
- if (!bfd_set_section_vma(obfd, osec, 0)) {
- Oops_bfd_perror("set_section_vma");
- return(0);
- }
- if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) {
- Oops_bfd_perror("set_section_contents");
- return(0);
- }
- if (!bfd_close(obfd)) {
- Oops_bfd_perror("close(obfd)");
- return(0);
- }
- if (!bfd_close(ibfd)) {
- Oops_bfd_perror("close(ibfd)");
- return(0);
- }
- return 1;
-}
-
-/* Write the Oops code to a temporary file with suitable header and trailer. */
-static char *Oops_code_to_file(const char *code, int size)
-{
- char *file;
- bfd *ibfd, *obfd;
- asection *isec;
-
- bfd_init();
- file = tmpnam(NULL);
- if (!Oops_copy_bfd_format(&ibfd, &obfd, &isec, file))
- return(NULL);
- if (!Oops_write_bfd_data(ibfd, obfd, isec, code, size))
- return(NULL);
- return(file);
-}
-
-/* Run objdump against the binary Oops code */
-static FILE *Oops_objdump(const char *file)
-{
- char *cmd;
- FILE *f;
- static char const options[] = "-dhf ";
- static char const procname[] = "Oops_objdump";
-
- cmd = malloc(strlen(path_objdump)+1+13+strlen(options)+strlen(file)+1);
- if (!cmd)
- malloc_error(procname);
- strcpy(cmd, path_objdump);
- strcat(cmd, " ");
- if (oops_arch == OOPS_SPARC64)
- strcat(cmd, "-m sparc:v9a ");
- strcat(cmd, options);
- strcat(cmd, file);
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
- f = popen_local(cmd, procname);
- free(cmd);
- return(f);
-}
-
-/* Process one code line from objdump, ignore everything else */
-static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
- int adjust)
-{
- int i, j;
- elf_addr_t address, eip_relative;
- char *line2, *map, **string = NULL, *p;
- static regex_t re_Oops_objdump;
- static regmatch_t *re_Oops_objdump_pmatch;
- static char const procname[] = "Oops_decode_one";
-
- /* objdump output. Optional whitespace, hex digits, optional
- * ' <_EIP+offset>', ':'. The '+offset' after _EIP is also optional.
- * Older binutils output 'xxxxxxxx <_EIP+offset>:', newer versions do
- * '00000000 <_EIP>:' first followed by ' xx:' lines.
- *
- * Just to complicate things even more, objdump recognises jmp, call,
- * etc., converts the code to something like this :-
- * " f: e8 32 34 00 00 call 3446 <_EIP+0x3446>"
- * Recognise this and append the eip adjusted address, followed by the
- * map_address text for that address.
- *
- * With any luck, objdump will take care of all such references which
- * makes this routine architecture insensitive. No need to test for
- * i386 jmp, call or m68k swl etc.
- */
- re_compile(&re_Oops_objdump,
- "^[ \t]*"
- "([0-9a-fA-F]+)" /* 1 */
- "( <_EIP[^>]*>)?" /* 2 */
- ":"
- "(" /* 3 */
- ".* +"
- "(0?x?[0-9a-fA-F]+ +)" /* 4 */
- "<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$" /* 5 */
- ")?"
- ".*"
- ,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_objdump_pmatch);
-
- i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1,
- re_Oops_objdump_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i != 0)
- return;
-
- re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string);
- errno = 0;
- address = strtoul(string[1], NULL, 16);
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in objdump line, "
- "treated as zero - '%s'\n"
- " objdump line '%s'\n",
- procname, string[1], line);
- perror(" ");
- ++errors;
- address = 0;
- }
- address += eip + adjust;
- if (string[5]) {
- /* EIP relative data to be adjusted */
- errno = 0;
- eip_relative = strtoul(string[5], NULL, 16);
- if (errno) {
-#ifdef __sparc__
- /* Try strtoull also, e.g. sparc binutils print <_PC+0xfffffffffffffd58> */
- errno = 0;
- eip_relative = strtoull(string[5], NULL, 16);
-#endif
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in objdump line, "
- "treated as zero - '%s'\n"
- " objdump line '%s'\n",
- procname, string[5], line);
- perror(" ");
- ++errors;
- eip_relative = 0;
- }
- }
- eip_relative += eip + adjust;
- map = map_address(&ss_merged, eip_relative);
- /* new text is original line, eip_relative in hex, map text */
- j = strlen(line);
- if (string[4])
- j = re_Oops_objdump_pmatch[4].rm_so;
- i = j+1+2*sizeof(eip_relative)+1+strlen(map)+1;
- line2 = malloc(i + 5);
- if (!line2)
- malloc_error(procname);
- snprintf(line2, i, "%.*s %s %s",
- j, line, format_address(eip_relative), map);
- } else {
- line2 = malloc(strlen(line) + 6);
- if (!line2)
- malloc_error(procname);
- strcpy(line2, line);
- }
- if (oops_arch != OOPS_I386) {
- p = line2;
- while ((p = strstr(p, "_EIP"))) {
- int l = strlen(eip_names[oops_arch]);
- memcpy(p + 1, eip_names[oops_arch], l);
- if (l < 3)
- strcpy(p + 1 + l, p + 4);
- p += 1 + l;
- }
- }
- if (address == eip)
- strcat(line2, " <==="); /* This makes it easier to locate visually the
- offending instruction */
- add_symbol_n(ss, address, 'C', 1, line2); /* as is */
- free(line2);
- re_strings_free(&re_Oops_objdump, &string);
-}
-
-/* Maximum number of code bytes to process. It needs to be a multiple of 2 for
- * code_byte (-c) swapping. Sparc and alpha dump 36 bytes so use 64.
- */
-#define CODE_SIZE 64
-
-/******************************************************************************/
-/* Start architecture sensitive code */
-/******************************************************************************/
-
-/* Extract the hex values from the Code: line and convert to binary */
-static int Oops_code_values(const unsigned char* code_text, char *code,
- int *adjust, char ***string, int string_max,
- int code_bytes)
-{
- int byte = 0, i, l;
- unsigned long c;
- char *value;
- const char *p;
- static regex_t re_Oops_code_value;
- static regmatch_t *re_Oops_code_value_pmatch;
- static const char procname[] = "Oops_code_values";
-
- /* Given by re_Oops_code: code_text is a message (e.g. "general
- * protection") or one or more hex fields separated by space or tab.
- * Some architectures bracket the current instruction with '<'
- * and '>', others use '(' and ')'. The first character is
- * nonblank.
- */
- if (!isxdigit(*code_text)) {
- fprintf(stderr,
- "Warning, Code looks like message, not hex digits. "
- "No disassembly attempted.\n");
- ++warnings;
- return(0);
- }
- memset(code, '\0', CODE_SIZE);
- p = code_text;
- *adjust = 0; /* EIP points to code byte 0 */
-
- /* Code values. Hex values separated by white space. On sparc, the
- * current instruction is bracketed in '<' and '>'.
- */
- re_compile(&re_Oops_code_value,
- "^"
- "([<(]?)" /* 1 */
- "([0-9a-fA-F]+)" /* 2 */
- "[>)]?"
- "[ \t]*"
- ,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_code_value_pmatch);
-
- re_string_check(re_Oops_code_value.re_nsub+1, string_max, procname);
- while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1,
- re_Oops_code_value_pmatch, 0) == 0) {
- re_strings(&re_Oops_code_value, p,
- re_Oops_code_value_pmatch, string);
- if (byte >= CODE_SIZE)
- break;
- errno = 0;
- value = (*string)[2];
- c = strtoul(value, NULL, 16);
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in code_value line, "
- "treated as zero - '%s'\n"
- " code_value line '%s'\n",
- procname, value, code_text);
- perror(" ");
- ++errors;
- c = 0;
- }
- if ((*string)[1] && *((*string)[1]))
- *adjust = -byte; /* this byte is EIP */
- /* i386 - 2 byte code, m68k - 4 byte, sparc - 8 byte.
- * On some architectures Code: is a stream of bytes, on some it
- * is a stream of shorts, on some it is a stream of ints.
- * Consistent we're not!
- */
- l = strlen(value);
- if (l%2) {
- fprintf(stderr,
- "%s invalid value 0x%s in Code line, not a "
- "multiple of 2 digits, value ignored\n",
- procname, value);
- ++errors;
- }
- else while (l) {
- if (byte >= CODE_SIZE) {
- fprintf(stderr,
- "%s Warning: extra values in Code "
- "line, ignored - '%s'\n",
- procname, value);
- ++warnings;
- break;
- }
- l -= 2;
- code[byte++] = (c >> l*4) & 0xff;
- value += 2;
- }
- p += re_Oops_code_value_pmatch[0].rm_eo;
- }
-
- if (*p) {
- fprintf(stderr,
- "Warning garbage '%s' at end of code line ignored "
- "by %s\n",
- p, procname);
- ++warnings;
- }
-
- /* The code_bytes parameter says how many readable bytes form a single
- * code unit in machine terms. -c 1 says that the text is already in
- * machine order, -c 2 (4, 8) says each chunk of 2 (4, 8) bytes must be
- * swapped to get back to machine order. Which end is up?
- */
- if (code_bytes != 1) {
- if (byte % code_bytes) {
- fprintf(stderr,
- "Warning: the number of code bytes (%d) is not "
- "a multiple of -c (%d)\n"
- "Byte swapping may not give sensible results\n",
- byte, code_bytes);
- ++warnings;
- }
- for (l = 0; l < byte; l+= code_bytes) {
- for (i = 0; i < code_bytes/2; ++i) {
- c = code[l+i];
- code[l+i] = code[l+code_bytes-i-1];
- code[l+code_bytes-i-1] = c;
- }
- }
- }
-
- return(1);
-}
-
-/* Look for the EIP: line, returns start of the relevant hex value */
-static char *Oops_eip(const char *line, char ***string, int string_max)
-{
- int i;
- static regex_t re_Oops_eip_sparc;
- static regmatch_t *re_Oops_eip_sparc_pmatch;
- static regex_t re_Oops_eip_sparc64;
- static regmatch_t *re_Oops_eip_sparc64_pmatch;
- static regex_t re_Oops_eip_ppc;
- static regmatch_t *re_Oops_eip_ppc_pmatch;
- static regex_t re_Oops_eip_mips;
- static regmatch_t *re_Oops_eip_mips_pmatch;
- static regex_t re_Oops_eip_other;
- static regmatch_t *re_Oops_eip_other_pmatch;
- static const char procname[] = "Oops_eip";
-
- /* Oops 'EIP:' line for sparc, actually PSR followed by PC */
- re_compile(&re_Oops_eip_sparc,
- "^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_eip_sparc_pmatch);
-
- i = regexec(&re_Oops_eip_sparc, line, re_Oops_eip_sparc.re_nsub+1,
- re_Oops_eip_sparc_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec sparc %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_eip_sparc.re_nsub+1, string_max,
- procname);
- re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
- string);
- oops_arch = OOPS_SPARC;
- return((*string)[re_Oops_eip_sparc.re_nsub]);
- }
-
- /* Oops 'EIP:' line for sparc64, actually TSTATE followed by TPC */
- re_compile(&re_Oops_eip_sparc64,
- "^TSTATE: [0-9a-fA-F]{16} TPC: " UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_eip_sparc64_pmatch);
-
- re_string_check(re_Oops_eip_sparc64.re_nsub+1, string_max, procname);
- i = regexec(&re_Oops_eip_sparc64, line, re_Oops_eip_sparc64.re_nsub+1,
- re_Oops_eip_sparc64_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec sparc64 %d\n", procname, i);
- if (i == 0) {
- re_strings(&re_Oops_eip_sparc64, line, re_Oops_eip_sparc64_pmatch,
- string);
- oops_arch = OOPS_SPARC64;
- return((*string)[re_Oops_eip_sparc64.re_nsub]);
- }
-
- /* Oops 'EIP:' line for PPC, all over the place */
- re_compile(&re_Oops_eip_ppc,
- "("
- "(kernel pc )"
- "|(trap at PC: )"
- "|(bad area pc )"
- "|(NIP: )"
- ")"
- UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_eip_ppc_pmatch);
-
- i = regexec(&re_Oops_eip_ppc, line, re_Oops_eip_ppc.re_nsub+1,
- re_Oops_eip_ppc_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec ppc %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_eip_ppc.re_nsub+1, string_max,
- procname);
- re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
- string);
- oops_arch = OOPS_PPC;
- return((*string)[re_Oops_eip_ppc.re_nsub]);
- }
-
- /* Oops 'EIP:' line for MIPS, epc, optional white space, ':',
- * optional white space, unbracketed address.
- */
- re_compile(&re_Oops_eip_mips,
- "^(epc[ \t]*:+[ \t]*)"
- UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_eip_mips_pmatch);
-
- i = regexec(&re_Oops_eip_mips, line, re_Oops_eip_mips.re_nsub+1,
- re_Oops_eip_mips_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec mips %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_eip_mips.re_nsub+1, string_max,
- procname);
- re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
- string);
- oops_arch = OOPS_MIPS;
- return((*string)[re_Oops_eip_mips.re_nsub]);
- }
-
- /* Oops 'EIP:' line for other architectures */
- re_compile(&re_Oops_eip_other,
- "^("
- /* i386 */ "(EIP:[ \t]+.*)"
- /* m68k */ "|(PC[ \t]*=[ \t]*)"
- /* ARM */ "|(pc *: *)"
- ")"
- BRACKETED_ADDRESS
- ,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_eip_other_pmatch);
-
- i = regexec(&re_Oops_eip_other, line, re_Oops_eip_other.re_nsub+1,
- re_Oops_eip_other_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec other %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_eip_other.re_nsub+1, string_max,
- procname);
- re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
- string);
- oops_arch = OOPS_I386;
- return((*string)[re_Oops_eip_other.re_nsub]);
- }
- return(NULL);
-}
-
-/* Set the eip from the EIP line */
-static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
-{
- static const char procname[] = "Oops_set_eip";
- char buf[10];
- errno = 0;
- *eip = strtoul(value, NULL, 16);
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in EIP line, ignored - '%s'\n",
- procname, value);
- perror(" ");
- ++errors;
- *eip = 0;
- }
- sprintf(buf, ">>%s:", eip_names[oops_arch]);
- add_symbol_n(ss, *eip, 'E', 1, buf);
-}
-
-/* Look for the MIPS ra line, returns start of the relevant hex value */
-static char *Oops_ra(const char *line, char ***string, int string_max)
-{
- int i;
- static regex_t re_Oops_ra;
- static regmatch_t *re_Oops_ra_pmatch;
- static const char procname[] = "Oops_ra";
-
- /* Oops 'ra:' line for MIPS, ra, optional white space, one or
- * more '=', optional white space, unbracketed address.
- */
- re_compile(&re_Oops_ra,
- "(ra[ \t]*=+[ \t]*)"
- UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_ra_pmatch);
-
- i = regexec(&re_Oops_ra, line, re_Oops_ra.re_nsub+1,
- re_Oops_ra_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_ra.re_nsub+1, string_max, procname);
- re_strings(&re_Oops_ra, line, re_Oops_ra_pmatch,
- string);
- return((*string)[re_Oops_ra.re_nsub]);
- }
- return(NULL);
-}
-
-/* Set the MIPS ra from the ra line */
-static void Oops_set_ra(const char *value, SYMBOL_SET *ss)
-{
- static const char procname[] = "Oops_set_ra";
- elf_addr_t ra;
- errno = 0;
- ra = strtoul(value, NULL, 16);
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in ra line, ignored - '%s'\n",
- procname, value);
- perror(" ");
- ++errors;
- ra = 0;
- }
- add_symbol_n(ss, ra, 'R', 1, ">>RA :");
-}
-
-/* Look for the SPARC o7/i7 registers line, returns start of the relevant hex value */
-static char *Oops_oi7(const char *line, char ***string, int string_max)
-{
- int i;
- static regex_t re_Oops_oi7;
- static regmatch_t *re_Oops_oi7_pmatch;
- static const char procname[] = "Oops_oi7";
-
- re_compile(&re_Oops_oi7,
- "^[io][04]: [0-9a-fA-F iosp:]+ ([io]7|ret_pc): "
- UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_oi7_pmatch);
-
- re_string_check(re_Oops_oi7.re_nsub+1, string_max, procname);
- i = regexec(&re_Oops_oi7, line, re_Oops_oi7.re_nsub+1,
- re_Oops_oi7_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
- re_strings(&re_Oops_oi7, line, re_Oops_oi7_pmatch,
- string);
- return((*string)[re_Oops_oi7.re_nsub]);
- }
- return(NULL);
-}
-
-/* Set the SPARC o7/i7 from the oi7 line */
-static void Oops_set_oi7(const char *value, char ***string, SYMBOL_SET *ss)
-{
- static const char procname[] = "Oops_set_oi7";
- elf_addr_t oi7;
- int o7 = 1;
- errno = 0;
- oi7 = strtoul(value, NULL, 16);
- if ((*string)[1] && !strcmp((*string)[1], "i7"))
- o7 = 0;
- if (errno) {
- fprintf(stderr,
- "%s Invalid hex value in oi7 line, ignored - '%s'\n",
- procname, value);
- perror(" ");
- ++errors;
- oi7 = 0;
- }
- add_symbol_n(ss, oi7, 'O', 1, o7 ? ">>O7:" : ">>I7:");
-}
-
-/* Look for the SPARC register dump lines end */
-static int Oops_sparc_regdump(const char *line)
-{
- int i;
- static regex_t re_Oops_sparc_regdump;
- static regmatch_t *re_Oops_sparc_regdump_pmatch;
- static const char procname[] = "Oops_sparc_regdump";
-
- re_compile(&re_Oops_sparc_regdump,
- "^(i[04]: "
- "|Instruction DUMP: "
- "|Caller\\["
- ")",
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_sparc_regdump_pmatch);
-
- i = regexec(&re_Oops_sparc_regdump, line, re_Oops_sparc_regdump.re_nsub+1,
- re_Oops_sparc_regdump_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0)
- return 1;
- return 0;
-}
-
-
-/* Look for the Trace multilines :(. Returns start of addresses. */
-static const char *Oops_trace(const char *line, char ***string, int string_max)
-{
- int i;
- const char *start = NULL;
- static int trace_line = 0;
- static regex_t re_Oops_trace;
- static regmatch_t *re_Oops_trace_pmatch;
- static const char procname[] = "Oops_trace";
-
- /* ppc is different, not a bracketed address, just an address */
- /* ARM is different, two bracketed addresses on each line */
-
- /* Oops 'Trace' lines */
- re_compile(&re_Oops_trace,
- "^(" /* 1 */
- "(Call Trace: )" /* 2 */
- /* alpha */ "|(Trace: )" /* 3 */
- /* various */ "|(" BRACKETED_ADDRESS ")" /* 4,5*/
- /* ppc */ "|(Call backtrace:)" /* 6 */
- /* ppc */ "|(" UNBRACKETED_ADDRESS ")" /* 7,8*/
- /* ARM */ "|(Function entered at (" BRACKETED_ADDRESS "))" /* 9,10,11 */
- /* sparc */
- /* sparc64 */ "|(Caller\\[" UNBRACKETED_ADDRESS "\\])"/*12,13*/
- ")",
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_trace_pmatch);
-
- i = regexec(&re_Oops_trace, line, re_Oops_trace.re_nsub+1,
- re_Oops_trace_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
-#undef MATCHED
-#define MATCHED(n) (re_Oops_trace_pmatch[n].rm_so != -1)
- if (MATCHED(2) || MATCHED(3)) {
- trace_line = 1;
- start = line + re_Oops_trace_pmatch[0].rm_eo;
- }
- else if (MATCHED(6)) {
- trace_line = 2; /* ppc */
- start = line + re_Oops_trace_pmatch[0].rm_eo;
- }
- else if (trace_line == 1 && MATCHED(5))
- start = line + re_Oops_trace_pmatch[5].rm_so;
- else if (trace_line == 2 && MATCHED(8)) /* ppc */
- start = line + re_Oops_trace_pmatch[8].rm_so;
- else if (MATCHED(10)){
- trace_line = 1; /* ARM */
- start = line + re_Oops_trace_pmatch[10].rm_so;
- } else if (MATCHED(12)) {
- trace_line = 0; /* sparc, sparc64 */
- start = line + re_Oops_trace_pmatch[13].rm_so;
- return start;
- } else
- trace_line = 0;
- }
- else
- trace_line = 0;
- if (trace_line)
- return(start);
- return(NULL);
-}
-
-/* Process a trace call line, extract addresses */
-static void Oops_trace_line(const char *line, const char *p, SYMBOL_SET *ss)
-{
- char **string = NULL;
- regex_t *pregex;
- regmatch_t *pregmatch;
- static const char procname[] = "Oops_trace_line";
-
- /* ppc does not bracket its addresses */
- if (isxdigit(*p)) {
- pregex = &re_unbracketed_address;
- pregmatch = re_unbracketed_address_pmatch;
- }
- else {
- pregex = &re_bracketed_address;
- pregmatch = re_bracketed_address_pmatch;
- }
-
- /* Loop over [un]?bracketed addresses */
- while (1) {
- if (regexec(pregex, p, pregex->re_nsub+1, pregmatch, 0) == 0) {
- re_strings(pregex, p, pregmatch, &string);
- add_symbol(ss, string[1], 'T', 1, "Trace:");
- p += pregmatch[0].rm_eo;
- }
- else if (strncmp(p, "from ", 5) == 0)
- p += 5; /* ARM does "address from address" */
- else
- break;
- }
-
- if (*p && !strcmp(p, "...")) {
- fprintf(stderr,
- "Warning garbage '%s' at end of trace line ignored "
- "by %s\n",
- p, procname);
- ++warnings;
- }
- re_strings_free(pregex, &string);
-}
-
-/* Do pattern matching to decide if the line should be printed. When reading a
- * syslog containing multiple Oops, you need the intermediate data (registers,
- * tss etc.) to go with the decoded text. Sets text to the start of the useful
- * text, after any prefix. Note that any leading white space is treated as part
- * of the prefix, later routines do not see any indentation.
- *
- * Note: If a line is not printed, it will not be scanned for any other text.
- */
-static int Oops_print(const char *line, const char **text, char ***string,
- int string_max)
-{
- int i, print = 0;
- static int stack_line = 0, trace_line = 0;
- static regex_t re_Oops_prefix;
- static regmatch_t *re_Oops_prefix_pmatch;
- static regex_t re_Oops_print_s;
- static regmatch_t *re_Oops_print_s_pmatch;
- static regex_t re_Oops_print_a;
- static regmatch_t *re_Oops_print_a_pmatch;
- static const char procname[] = "Oops_print";
-
- *text = line;
-
- /* Lines to be ignored. For some reason the "amuse the user" print in
- * some die_if_kernel routines causes regexec to run very slowly.
- */
-
- if (strstr(*text, "\\|/ ____ \\|/") ||
- strstr(*text, "\"@'/ ,. \\`@\"") ||
- strstr(*text, "/_| \\__/ |_\\") ||
- strstr(*text, " \\__U_/"))
- return(1); /* print but avoid regexec */
-
- /* Prefixes to be ignored */
- re_compile(&re_Oops_prefix,
- "^(" /* start of line */
- "([^ ]{3} [ 0-9][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} "
- "[^ ]+ kernel: +)" /* syslogd */
- "|(<[0-9]+>)" /* kmsg */
- "|([ \t]+)" /* leading white space */
- ")+" /* any prefixes, in any order */
- ,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_prefix_pmatch);
-
- i = regexec(&re_Oops_prefix, *text, re_Oops_prefix.re_nsub+1,
- re_Oops_prefix_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec prefix %d\n", procname, i);
- if (i == 0)
- *text += re_Oops_prefix_pmatch[0].rm_eo; /* step over prefix */
-
-
- /* Lots of possibilities. Expand as required for all architectures.
- *
- * Trial and error shows that regex does not like a lot of sub patterns
- * that start with "^". So split the patterns into two groups, one set
- * must appear at the start of the line, the other set can appear
- * anywhere.
- */
-
- /* These patterns must appear at the start of the line, after stripping
- * the prefix above.
- *
- * The order below is required to handle multiline outupt.
- * string 2 is defined if the text is 'Stack from '.
- * string 3 is defined if the text is 'Stack: '.
- * string 4 is defined if the text might be a stack continuation.
- * string 5 is defined if the text is 'Call Trace: '.
- * string 6 is defined if the text might be a trace continuation.
- * string 7 is the address part of the BRACKETED_ADDRESS.
- *
- * string 8 is defined if the text contains a version number. No Oops
- * report contains this as of 2.1.125 but IMHO it should be added. If
- * anybody wants to print a VERSION_nnnn line in their Oops, this code
- * is ready.
- *
- * string 9 is defined if the text is 'Trace: ' (alpha).
- * string 10 is defined if the text is 'Call backtrace:' (ppc).
- */
- re_compile(&re_Oops_print_s,
- /* arch type */ /* Required order */
- "^(" /* 1 */
- /* i386 */ "(Stack: )" /* 2 */
- /* m68k */ "|(Stack from )" /* 3 */
- /* various */ "|([0-9a-fA-F]{4,})" /* 4 */
- /* various */ "|(Call Trace: )" /* 5 */
- /* various */ "|(" BRACKETED_ADDRESS ")" /* 6,7*/
- /* various */ "|(Version_[0-9]+)" /* 8 */
- /* alpha */ "|(Trace: )" /* 9 */
- /* ppc */ "|(Call backtrace:)" /* 10 */
-
- /* order does not matter from here on */
-
- /* various */ "|(Process .*stackpage=)"
- /* various */ "|(Call Trace:[ \t])"
- /* various */ "|(Code *:[ \t])"
- /* various */ "|(Kernel panic)"
- /* various */ "|(In swapper task)"
-
- /* i386 2.0 */ "|(Corrupted stack page)"
- /* i386 */ "|(invalid operand: )"
- /* i386 */ "|(Oops: )"
- /* i386 */ "|(Cpu: +[0-9])"
- /* i386 */ "|(current->tss)"
- /* i386 */ "|(\\*pde +=)"
- /* i386 */ "|(EIP: )"
- /* i386 */ "|(EFLAGS: )"
- /* i386 */ "|(eax: )"
- /* i386 */ "|(esi: )"
- /* i386 */ "|(ds: )"
-
- /* m68k */ "|(pc[:=])"
- /* m68k */ "|(68060 access)"
- /* m68k */ "|(Exception at )"
- /* m68k */ "|(d[04]: )"
- /* m68k */ "|(Frame format=)"
- /* m68k */ "|(wb [0-9] stat)"
- /* m68k */ "|(push data: )"
- /* m68k */ "|(baddr=)"
- /* any other m68K lines to print? */
-
- /* alpha */ "|(Bad unaligned kernel)"
- /* alpha */ "|(Forwarding unaligned exception)"
- /* alpha */ "|(: unhandled unaligned exception)"
- /* alpha */ "|(<sc)"
- /* alpha */ "|(pc *=)"
- /* alpha */ "|(r[0-9]+ *=)"
- /* alpha */ "|(gp *=)"
- /* any other alpha lines to print? */
-
- /* sparc */ "|(tsk->)"
- /* sparc */ "|(PSR: )"
- /* sparc */ "|([goli][04]: )"
- /* sparc */ "|(Instruction DUMP: )"
- /* sparc */ "|(Caller\\[)"
- /* any other sparc lines to print? */
-
- /* sparc64 */ "|(TSTATE: )"
- /* any other sparc64 lines to print? */
-
- /* ppc */ "|(MSR: )"
- /* ppc */ "|(TASK = )"
- /* ppc */ "|(last math )"
- /* ppc */ "|(GPR[0-9]+: )"
- /* any other ppc lines to print? */
-
- /* MIPS */ "|(\\$[0-9 ]+:)"
- /* MIPS */ "|(epc )"
- /* MIPS */ "|(Status:)"
- /* MIPS */ "|(Cause :)"
- /* any other MIPS lines to print? */
-
- /* ARM */ "|(Backtrace:)"
- /* ARM */ "|(Function entered at)"
- /* ARM */ "|(\\*pgd =)"
- /* ARM */ "|(Internal error)"
- /* ARM */ "|(pc :)"
- /* ARM */ "|(sp :)"
- /* ARM */ "|(r[0-9][0-9 ]:)"
- /* ARM */ "|(Flags:)"
- /* ARM */ "|(Control:)"
- /* any other ARM lines to print? */
-
- ")",
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_print_s_pmatch);
-
- i = regexec(&re_Oops_print_s, *text, re_Oops_print_s.re_nsub+1,
- re_Oops_print_s_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec start %d\n", procname, i);
- print = 0;
- if (i == 0) {
-#undef MATCHED
-#define MATCHED(n) (re_Oops_print_s_pmatch[n].rm_so != -1)
- print = 1;
- /* Handle multiline messages, messy */
- if (!MATCHED(2) && !MATCHED(3) && !MATCHED(4))
- stack_line = 0;
- else if (MATCHED(2) || MATCHED(3))
- stack_line = 1;
- else if (stack_line && !MATCHED(4)) {
- print = 0;
- stack_line = 0;
- }
- if (!MATCHED(5) && !MATCHED(6) && !MATCHED(9) && !MATCHED(10))
- trace_line = 0;
- else if (MATCHED(5) || MATCHED(9) || MATCHED(10))
- trace_line = 1;
- else if (stack_line && !MATCHED(6)) {
- print = 0;
- trace_line = 0;
- }
- /* delay splitting into strings until we really them */
- if (MATCHED(8)) {
- re_string_check(re_Oops_print_s.re_nsub+1, string_max,
- procname);
- re_strings(&re_Oops_print_s, *text,
- re_Oops_print_s_pmatch,
- string);
- add_Version((*string)[8]+8, "Oops");
- }
- }
-
- /* These patterns can appear anywhere in the line, after stripping
- * the prefix above.
- */
- re_compile(&re_Oops_print_a,
- /* arch type */
-
- /* various */ "(Unable to handle kernel)"
- /* various */ "|(Aiee)" /* anywhere in text is a bad sign (TM) */
- /* various */ "|(die_if_kernel)" /* ditto */
-
- /* alpha */ "|(\\([0-9]\\): Oops )"
- /* alpha */ "|(: memory violation)"
- /* alpha */ "|(: Exception at)"
- /* alpha */ "|(: Arithmetic fault)"
- /* alpha */ "|(: Instruction fault)"
- /* alpha */ "|(: arithmetic trap)"
- /* alpha */ "|(: unaligned trap)"
-
- /* sparc die_if_kernel has no fixed text, identify by (pid): text.
- * Somebody has been playful with the texts.
- *
- * Alas adding this next pattern increases run time by 15% on
- * its own! It would be considerably faster if sparc had
- * consistent error texts.
- */
- /* sparc */ "|("
- "\\([0-9]+\\): "
- "("
- "(Whee)"
- "|(Oops)"
- "|(Kernel)"
- "|(Penguin)"
- "|(Too many Penguin)"
- "|(BOGUS)"
- ")"
- ")"
-
- /* ppc */ "|(kernel pc )"
- /* ppc */ "|(trap at PC: )"
- /* ppc */ "|(bad area pc )"
- /* ppc */ "|(NIP: )"
-
- /* MIPS */ "|( ra *=)"
-
- ")",
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_print_a_pmatch);
-
- i = regexec(&re_Oops_print_a, *text, re_Oops_print_a.re_nsub+1,
- re_Oops_print_a_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec anywhere %d\n", procname, i);
- if (i == 0)
- print = 1;
-
- return(print);
-}
-
-/* Look for the Code: line. Returns start of the code bytes. */
-static const char *Oops_code(const char *line, char ***string, int string_max)
-{
- int i;
- static regex_t re_Oops_code;
- static regmatch_t *re_Oops_code_pmatch;
- static const char procname[] = "Oops_code";
-
- /* Oops 'Code: ' hopefully followed by at least one hex code. sparc
- * brackets the PC in '<' and '>'. ARM brackets the PC in '(' and ')'.
- */
- re_compile(&re_Oops_code,
- "^(" /* 1 */
- /* sparc */ "(Instruction DUMP)" /* 2 */
- /* various */ "|(Code *)" /* 3 */
- ")"
- ":[ \t]+"
- "(" /* 4 */
- "(general protection.*)"
- "|(<[0-9]+>)"
- "|(([<(]?[0-9a-fA-F]+[>)]?[ \t]+)+[<(]?[0-9a-fA-F]+[>)]?)"
- ")"
- "(.*)$" /* trailing garbage */
- ,
- REG_NEWLINE|REG_EXTENDED|REG_ICASE,
- &re_Oops_code_pmatch);
-
- i = regexec(&re_Oops_code, line, re_Oops_code.re_nsub+1,
- re_Oops_code_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0) {
- re_string_check(re_Oops_code.re_nsub+1, string_max, procname);
- re_strings(&re_Oops_code, line, re_Oops_code_pmatch,
- string);
- if ((*string)[re_Oops_code.re_nsub] &&
- *((*string)[re_Oops_code.re_nsub])) {
- fprintf(stderr,
- "Warning: trailing garbage ignored on Code: "
- "line\n"
- " Text: '%s'\n"
- " Garbage: '%s'\n",
- line, (*string)[re_Oops_code.re_nsub]);
- ++warnings;
- }
- return((*string)[4]);
- }
- return(NULL);
-}
-
-/******************************************************************************/
-/* End architecture sensitive code */
-/******************************************************************************/
-
-/* Decode the Oops Code: via objdump*/
-static void Oops_decode(const unsigned char* code_text, elf_addr_t eip,
- SYMBOL_SET *ss, char ***string, int string_max,
- int code_bytes)
-{
- FILE *f;
- char *file, *line = NULL, code[CODE_SIZE];
- int size = 0, adjust;
- static char const procname[] = "Oops_decode";
-
- if (debug)
- fprintf(stderr, "DEBUG: %s\n", procname);
- /* text to binary */
- if (!Oops_code_values(code_text, code, &adjust, string, string_max,
- code_bytes))
- return;
- /* binary to same format as ksymoops */
- if (!(file = Oops_code_to_file(code, CODE_SIZE)))
- return;
- /* objdump the pseudo object */
- if (!(f = Oops_objdump(file)))
- return;
- while (fgets_local(&line, &size, f, procname)) {
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
- Oops_decode_one(ss, line, eip, adjust);
- }
- pclose_local(f, procname); /* opened in Oops_objdump */
- free(line);
- if (unlink(file)) {
- fprintf(stderr, "%s could not unlink %s", prefix, file);
- perror(" ");
- }
-}
-
-/* Reached the end of an Oops report, format the extracted data. */
-static void Oops_format(const SYMBOL_SET *ss_format)
-{
- int i;
- SYMBOL *s;
- static const char procname[] = "Oops_format";
-
- if (debug)
- fprintf(stderr, "DEBUG: %s\n", procname);
-
- compare_Version(); /* Oops might have a version one day */
- printf("\n");
- for (s = ss_format->symbol, i = 0; i < ss_format->used; ++i, ++s) {
- /* For type C data, print Code:, address, map, "name" (actually
- * the text of an objdump line). For other types print name,
- * address, map.
- */
- if (s->type == 'C')
- printf("Code: %s %-30s %s\n",
- format_address(s->address),
- map_address(&ss_merged, s->address),
- s->name);
- else
- printf("%s %s %s\n",
- s->name,
- format_address(s->address),
- map_address(&ss_merged, s->address));
- }
- printf("\n");
-}
-
-/* Select next Oops input file */
-static FILE *Oops_next_file(int *filecount, char * const **filename)
-{
- static FILE *f = NULL;
- static const char procname[] = "Oops_next_file";
- static int first_file = 1;
-
- if (first_file) {
- f = stdin;
- first_file = 0;
- }
- while (*filecount) {
- if (f)
- fclose_local(f, procname);
- f = NULL;
- if (regular_file(**filename, procname))
- f = fopen_local(**filename, "r", procname);
- if (f) {
- if (debug)
- fprintf(stderr,
- "DEBUG: reading Oops report "
- "from %s\n", **filename);
- }
- ++*filename;
- --*filecount;
- if (f)
- return(f);
- }
- return(f);
-}
-
-/* Read the Oops report */
-#define MAX_STRINGS 300 /* Maximum strings in any Oops re */
-int Oops_read(int filecount, char * const *filename, int code_bytes,
- int one_shot)
-{
- char *line = NULL, **string = NULL;
- const char *start, *text;
- int i, size = 0, lineno = 0, lastprint = 0, print = 0;
- elf_addr_t eip = 0;
- int sparc_regdump = 0;
- FILE *f;
- SYMBOL_SET ss_format;
- static const char procname[] = "Oops_read";
-
- ss_init(&ss_format, "Oops log data");
-
- if (!filecount && isatty(0))
- printf("Reading Oops report from the terminal\n");
-
- string = malloc(MAX_STRINGS*sizeof(*string));
- if (!string)
- malloc_error(procname);
- memset(string, '\0', MAX_STRINGS*sizeof(*string));
-
- do {
- if (!(f = Oops_next_file(&filecount, &filename)))
- continue;
- while (fgets_local(&line, &size, f, procname)) {
- if (debug > 2)
- fprintf(stderr,
- "DEBUG: %s - %s\n", procname, line);
- ++lineno;
- print = Oops_print(line, &text, &string, MAX_STRINGS);
- if (Oops_sparc_regdump (text)) {
- sparc_regdump = 1;
- } else {
- if ((oops_arch == OOPS_SPARC ||
- oops_arch == OOPS_SPARC64) &&
- sparc_regdump && ss_format.used) {
- Oops_format(&ss_format);
- ss_free(&ss_format);
- }
- sparc_regdump = 0;
- }
- if (print) {
- puts(line);
- lastprint = lineno;
- if ((start = Oops_eip(text,
- &string, MAX_STRINGS)))
- Oops_set_eip(start, &eip, &ss_format);
- if ((start = Oops_ra(text,
- &string, MAX_STRINGS)))
- Oops_set_ra(start, &ss_format);
- if ((start = Oops_oi7(text,
- &string, MAX_STRINGS)))
- Oops_set_oi7(start, &string,
- &ss_format);
- if ((start = Oops_trace(text,
- &string, MAX_STRINGS)))
- Oops_trace_line(text, start,
- &ss_format);
- if ((start = Oops_code(text,
- &string, MAX_STRINGS))) {
- Oops_decode(start, eip, &ss_format,
- &string, MAX_STRINGS,
- code_bytes);
- Oops_format(&ss_format);
- ss_free(&ss_format);
- if (one_shot)
- return(0);
- }
- }
- /* More than 5 (arbitrary) lines which were not printed
- * and there is some saved data, assume we missed the
- * Code: line.
- */
- if (ss_format.used && lineno > lastprint+5) {
- fprintf(stderr,
- "Warning, Code line not seen, dumping "
- "what data is available\n");
- ++warnings;
- Oops_format(&ss_format);
- ss_free(&ss_format);
- if (one_shot)
- return(0);
- }
- }
- if (ss_format.used) {
- if ((oops_arch != OOPS_SPARC &&
- oops_arch != OOPS_SPARC64) || !sparc_regdump) {
- fprintf(stderr,
- "Warning, Code line not seen, dumping "
- "what data is available\n");
- ++warnings;
- }
- Oops_format(&ss_format);
- ss_free(&ss_format);
- if (one_shot)
- return(0);
- }
- } while (filecount != 0);
-
- for (i = 0; i < sizeof(string); ++i) {
- free(string[i]);
- string[i] = NULL;
- }
- free(line);
- if (one_shot)
- return(3); /* one shot mode, end of input, no data */
- return(0);
-}
+++ /dev/null
-/*
- re.c.
-
- Regular expression processing for ksymoops.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- PPC trace addresses are not bracketed, add new re.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-
-/* Compile a regular expression */
-void re_compile(regex_t *preg, const char *regex, int cflags,
- regmatch_t **pmatch)
-{
- int i, l;
- char *p;
- static char const procname[] = "re_compile";
-
- if (preg->re_nsub)
- return; /* already compiled */
-
- if (debug)
- fprintf(stderr, "DEBUG: %s '%s'", procname, regex);
- if ((i = regcomp(preg, regex, cflags))) {
- l = regerror(i, preg, NULL, 0);
- ++l; /* doc is ambiguous, be safe */
- p = malloc(l);
- if (!p)
- malloc_error("regerror text");
- regerror(i, preg, p, l);
- fprintf(stderr,
- "%s: fatal %s error on '%s' - %s\n",
- prefix, procname, regex, p);
- exit(2);
- }
- if (debug)
- fprintf(stderr, " %d sub expression(s)\n", preg->re_nsub);
- /* [0] is entire match, [1] is first substring */
- *pmatch = malloc((preg->re_nsub+1)*sizeof(**pmatch));
- if (!*pmatch)
- malloc_error("pmatch");
-
-}
-
-/* Compile common regular expressions */
-void re_compile_common(void)
-{
-
- /* nm: address, type, symbol */
- re_compile(&re_nm,
- "^([0-9a-fA-F]{4,}) +([^ ]) +([^ ]+)$",
- REG_NEWLINE|REG_EXTENDED,
- &re_nm_pmatch);
-
- /* bracketed address preceded by optional white space */
- re_compile(&re_bracketed_address,
- "^[ \t]*" BRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED,
- &re_bracketed_address_pmatch);
-
- /* unbracketed address preceded by optional white space */
- re_compile(&re_unbracketed_address,
- "^[ \t*]*" UNBRACKETED_ADDRESS,
- REG_NEWLINE|REG_EXTENDED,
- &re_unbracketed_address_pmatch);
-
-}
-
-/* Split text into the matching re substrings - Perl is so much easier :).
- * Each element of *string is set to a malloced copy of the substring or
- * NULL if the substring did not match (undef). A zero length substring match
- * is represented by a zero length **string.
- */
-void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
- char ***string)
-{
- int i;
- if (!*string) {
- *string = malloc((preg->re_nsub+1)*sizeof(**string));
- if (!*string)
- malloc_error("re_strings base");
- for (i = 0; i < preg->re_nsub+1; ++i)
- (*string)[i] = NULL;
- }
- for (i = 0; i < preg->re_nsub+1; ++i) {
- if (debug > 4)
- fprintf(stderr,
- "DEBUG: re_string %d offsets %d %d",
- i, pmatch[i].rm_so, pmatch[i].rm_eo);
- if (pmatch[i].rm_so == -1) {
- /* no match for this sub expression */
- free((*string)[i]);
- (*string)[i] = NULL;
- if (debug > 4)
- fprintf(stderr, " (undef)\n");
- }
- else {
- int l = pmatch[i].rm_eo - pmatch[i].rm_so + 1;
- char *p;
- p = malloc(l);
- if (!p)
- malloc_error("re_strings");
- strncpy(p, text+pmatch[i].rm_so, l-1);
- *(p+l-1) = '\0';
- (*string)[i] = p;
- if (debug > 4)
- fprintf(stderr, " '%s'\n", p);
- }
- }
-}
-
-/* Free the matching re substrings */
-void re_strings_free(const regex_t *preg, char ***string)
-{
- if (*string) {
- int i;
- for (i = 0; i < preg->re_nsub+1; ++i)
- free((*string)[i]);
- free(*string);
- *string = NULL;
- }
-}
-
-/* Check that there are enough strings for an re */
-void re_string_check(int need, int available, const char *msg)
-{
- if (need > available) {
- fprintf(stderr,
- "%s: fatal not enough re_strings in %s. "
- "Need %d, available %d\n",
- prefix, msg, need, available);
- exit(2);
- }
-}
+++ /dev/null
-/*
- symbol.c.
-
- Symbol handling routines for ksymoops.
-
- Copyright Keith Owens <kaos@ocs.com.au>.
- Released under the GNU Public Licence, Version 2.
-
- Mon Jan 4 09:08:19 EST 1999
- Version 0.6d
- Cast Version to int, glibc 2.1 made elf_addr_t a long.
-
- Tue Nov 3 02:31:01 EST 1998
- Version 0.6
- Fix end of code calculation.
-
- Wed Oct 28 13:47:23 EST 1998
- Version 0.4
- Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <errno.h>
-#include <malloc.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* Initialise a symbol source */
-void ss_init(SYMBOL_SET *ss, const char *msg)
-{
- memset(ss, '\0', sizeof(*ss));
- ss->source = strdup(msg);
- if (!ss->source)
- malloc_error(msg);
-}
-
-/* Free dynamic data from a symbol source */
-void ss_free(SYMBOL_SET *ss)
-{
- int i;
- SYMBOL *s;
- for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
- free(s->name);
- free(ss->symbol);
- free(ss->source);
- memset(ss, '\0', sizeof(*ss));
-}
-
-/* Initialise common symbol sets */
-void ss_init_common(void)
-{
- ss_init(&ss_Version, "Version_");
-}
-
-/* Find a symbol name in a symbol source. Brute force ascending order search,
- * no hashing. If start is not NULL, it contains the starting point for the
- * scan and is updated to point to the found entry. If the entry is not found,
- * return NULL with start pointing to the next highest entry.
- * NOTE: Assumes that ss is sorted by name.
- */
-SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
-{
- int i, l;
- SYMBOL *s;
- for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
- if ((l = strcmp(symbol, s->name)) == 0) {
- if (start)
- *start = i;
- return(s);
- }
- if (l < 0)
- break;
- }
- if (start)
- *start = i;
- return NULL;
-}
-
-/* Find an address in a symbol source. Brute force ascending order search, no
- * hashing. If start is not NULL, it contains the starting point for the scan
- * and is updated to point to the found entry. If the entry is not found,
- * return NULL with start pointing to the next highest entry.
- * NOTE: Assumes that ss is sorted by address.
- */
-static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
- const elf_addr_t address, int *start)
-{
- int i;
- SYMBOL *s;
- for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
- if (address > s->address)
- continue;
- else if (address == s->address) {
- if (start)
- *start = i;
- return(s);
- }
- else
- break;
- }
- if (start)
- *start = i;
- return NULL;
-}
-
-/* Add a symbol to a symbol set, address in binary */
-void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
- const char type, const char keep, const char *symbol)
-{
- int i;
- char **string = NULL;
- SYMBOL *s;
- static regex_t re_symbol_ver;
- static regmatch_t *re_symbol_ver_pmatch;
- static const char procname[] = "add_symbol_n";
-
- /* Strip out any trailing symbol version _Rxxxxxxxx. */
- re_compile(&re_symbol_ver,
- "^(.*)_R[0-9a-fA-F]{8,}$",
- REG_NEWLINE|REG_EXTENDED,
- &re_symbol_ver_pmatch);
-
- i = regexec(&re_symbol_ver, symbol,
- re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
- if (i == 0)
- re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
- &string);
-
- if (debug > 3)
- fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
- procname, ss->source, format_address(address),
- type, keep, i == 0 ? string[1] : symbol);
- if (ss->used > ss->alloc) {
- fprintf(stderr,
- "%s: fatal %s ss %s used (%d) > alloc (%d)\n",
- procname, prefix, ss->source, ss->used, ss->alloc);
- exit(2);
- }
- if (ss->used == ss->alloc) {
- /* increase by 20% or 10, whichever is larger, arbitrary */
- int newsize = ss->alloc*120/100;
- if (newsize < ss->alloc+10)
- newsize = ss->alloc+10;
- if (debug > 3)
- fprintf(stderr,
- "DEBUG: %s increasing %s from %d to %d "
- "entries\n",
- procname, ss->source, ss->alloc, newsize);
- ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
- if (!ss->symbol)
- malloc_error("realloc ss");
- ss->alloc = newsize;
- }
- s = ss->symbol+ss->used;
- if (i == 0) {
- s->name = string[1];
- string[1] = NULL; /* don't free this one */
- }
- else {
- s->name = strdup(symbol);
- if (!s->name)
- malloc_error("strdup symbol");
- }
- s->type = type;
- s->keep = keep;
- s->address = address;
- ++ss->used;
- re_strings_free(&re_symbol_ver, &string);
-}
-
-/* Add a symbol to a symbol set, address in character */
-void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
- const char keep, const char *symbol)
-{
- elf_addr_t a;
- static char const procname[] = "add_symbol";
- errno = 0;
- a = strtoul(address, NULL, 16);
- if (errno) {
- fprintf(stderr,
- "%s: %s address '%s' is in error",
- prefix, procname, address);
- perror(" ");
- ++errors;
- }
- add_symbol_n(ss, a, type, 1, symbol);
-}
-
-/* Map an address to symbol, offset and length, address in binary */
-char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
-{
- int i = 0, l;
- SYMBOL *s;
- static char *map = NULL;
- static int size = 0;
- static const char procname[] = "map_address_n";
-
- if (debug > 2)
- fprintf(stderr, "DEBUG: %s %s %s\n",
- procname, ss->source, format_address(address));
- s = find_symbol_address(ss, address, &i);
- if (!s && --i >= 0)
- s = ss->symbol+i; /* address is between s and s+1 */
-
- /* Extra map text is always < 100 bytes */
- if (s)
- l = strlen(s->name) + 100;
- else
- l = 100;
- if (l > size) {
- map = realloc(map, l);
- if (!map)
- malloc_error(procname);
- size = l;
- }
- if (!s) {
- if (ss->used == 0)
- snprintf(map, size, "No symbols available");
- else
- snprintf(map, size, "Before first symbol");
- }
- else if ((i+1) >= ss->used) {
- /* Somewhere past last symbol. Length of last section of code
- * is unknown, arbitrary cutoff at 32K.
- */
- elf_addr_t offset = address - s->address;
- if (offset > 32768)
- snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
- else
- snprintf(map, size, "<%s+%lx/????>", s->name, offset);
- }
- else
- snprintf(map, size,
- "<%s+%lx/%lx>",
- s->name, address - s->address,
- (s+1)->address - s->address);
- return(map);
-}
-
-/* After sorting, obsolete symbols are at the top. Delete them. */
-static void ss_compress(SYMBOL_SET *ss)
-{
- int i, j;
- SYMBOL *s;
- static const char procname[] = "ss_compress";
-
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s on table %s, before %d ",
- procname, ss->source, ss->used);
- for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
- if (!s->keep) {
- for (j = i; j < ss->used; ++j, ++s) {
- if (s->keep) {
- fprintf(stderr,
- "%s: fatal %s table %s is not "
- "sorted\n",
- prefix, procname, ss->source);
- exit(2);
- }
- }
- break;
- }
- }
- for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
- free(s->name);
- ss->used = i;
- if (debug > 1)
- fprintf(stderr, "after %d\n", ss->used);
-}
-
-static int ss_compare_atn(const void *a, const void *b)
-{
- SYMBOL *c = (SYMBOL *) a;
- SYMBOL *d = (SYMBOL *) b;
- int i;
-
- /* obsolete symbols to the top */
- if (c->keep != d->keep)
- return(d->keep - c->keep);
- if (c->address > d->address)
- return(1);
- if (c->address < d->address)
- return(-1);
- if (c->type > d->type)
- return(1);
- if (c->type < d->type)
- return(-1);
- if ((i = strcmp(c->name, d->name)))
- return(i);
- return(0);
-}
-
-/* Sort a symbol set by address, type and name */
-void ss_sort_atn(SYMBOL_SET *ss)
-{
- if (debug)
- fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
- ss->source);
- qsort((char *) ss->symbol, (unsigned) ss->used,
- sizeof(*(ss->symbol)), ss_compare_atn);
- ss_compress(ss);
-}
-
-static int ss_compare_na(const void *a, const void *b)
-{
- SYMBOL *c = (SYMBOL *) a;
- SYMBOL *d = (SYMBOL *) b;
- int i;
-
- /* obsolete symbols to the top */
- if (c->keep != d->keep)
- return(d->keep - c->keep);
- if ((i = strcmp(c->name, d->name)))
- return(i);
- if (c->address > d->address)
- return(1);
- if (c->address < d->address)
- return(-1);
- return(0);
-}
-
-/* Sort a symbol set by name and address, drop duplicates. There should be
- * no duplicates but I have seen duplicates in ksyms on 2.0.35.
- */
-void ss_sort_na(SYMBOL_SET *ss)
-{
- int i;
- SYMBOL *s;
- if (debug)
- fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
- ss->source);
- qsort((char *) ss->symbol, (unsigned) ss->used,
- sizeof(*(ss->symbol)), ss_compare_na);
- ss_compress(ss);
- s = ss->symbol;
- for (i = 0; i < ss->used-1; ++i) {
- if (strcmp(s->name, (s+1)->name) == 0 &&
- s->address == (s+1)->address) {
- if (s->type != ' ')
- (s+1)->keep = 0;
- else
- s->keep = 0;
- }
- ++s;
- }
- qsort((char *) ss->symbol, (unsigned) ss->used,
- sizeof(*(ss->symbol)), ss_compare_na);
- ss_compress(ss);
-}
-
-/* Copy a symbol set, including all its strings */
-SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
-{
- SYMBOL_SET *ssc;
- if (debug > 3)
- fprintf(stderr,
- "DEBUG: ss_copy %s\n", ss->source);
- ssc = malloc(sizeof(*ssc));
- if (!ssc)
- malloc_error("copy ssc");
- ss_init(ssc, ss->source);
- ssc->used = ss->used;
- ssc->alloc = ss->used; /* shrink the copy */
- ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
- if (!(ssc->symbol))
- malloc_error("copy ssc symbols");
- memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
- return(ssc);
-}
-
-/* Convert version number to major, minor string. */
-static const char *format_Version(elf_addr_t Version)
-{
- static char string[12]; /* 255.255.255\0 worst case */
- snprintf(string, sizeof(string), "%d.%d.%d",
- (int) ((Version >> 16) & 0xff),
- (int) ((Version >> 8) & 0xff),
- (int) ((Version) & 0xff));
- return(string);
-}
-
-/* Save version number. The "address" is the version number, the "symbol" is
- * the source of the version.
- */
-void add_Version(const char *version, const char *source)
-{
- static char const procname[] = "add_Version";
- int i = atoi(version);
- if (debug > 1)
- fprintf(stderr, "DEBUG: %s %s %s %s\n",
- procname, source, version, format_Version(i));
- add_symbol_n(&ss_Version, i, 'V', 1, source);
-}
-
-/* Extract Version_ number from a symbol set and save it. */
-void extract_Version(SYMBOL_SET *ss)
-{
- int i = 0;
- SYMBOL *s;
-
- s = find_symbol_name(ss, "Version_", &i);
- if (!s && i < ss->used)
- s = ss->symbol+i; /* first symbol after "Version_" */
- if (!s || strncmp(s->name, "Version_", 8))
- return;
- add_Version(s->name+8, ss->source);
-}
-
-/* Compare all extracted Version numbers. Silent unless there is a problem. */
-void compare_Version(void)
-{
- int i = 0;
- SYMBOL *s, *s0;
- static int prev_used = 0;
-
- if (!ss_Version.used)
- return;
- /* Only check if the Version table has changed in size */
- if (prev_used == ss_Version.used)
- return;
-
- ss_sort_na(&ss_Version);
- s0 = s = ss_Version.symbol;
- if (debug)
- fprintf(stderr, "DEBUG: Version %s\n",
- format_Version(s0->address));
- for (i = 0; i < ss_Version.used; ++i, ++s) {
- if (s->address != s0->address) {
- fprintf(stderr,
- "Version mismatch error. %s says %s, ",
- s0->name,
- format_Version(s0->address));
- fprintf(stderr,
- "%s says %s. Expect lots of address "
- "mismatches.\n",
- s->name,
- format_Version(s->address));
- ++errors;
- }
- }
- prev_used = ss_Version.used;
-}