S: Ireland
N: Tigran A. Aivazian
-E: tigran@ocston.org
+E: tigran@veritas.com
W: http://www.ocston.org/~tigran
D: BFS filesystem
D: Intel P6 CPU microcode update support
+D: Various kernel patches
S: United Kingdom
N: Werner Almesberger
D: m68k port to HP9000/300
D: AUN network protocols
D: Co-architect of the parallel port sharing system
-S: Nexus Electronics Ltd
-S: 10 St Barnabas Road, Cambridge CB1 2BY
+D: IPv6 netfilter
+S: FutureTV Labs Ltd
+S: Brunswick House, 61-69 Newmarket Rd, Cambridge CB5 8EG
S: United Kingdom
N: Thomas Bogendörfer
N: Lennert Buytenhek
E: buytenh@gnu.org
D: Rewrite of the ethernet bridging code
-S: Handelstraat 35
-S: 3131 EK Vlaardingen
+S: Ravenhorst 58B
+S: 2317 AK Leiden
S: The Netherlands
N: Michael Callahan
D: Random SMP kernel hacker...
D: Uniform Multi-Platform E-IDE driver
D: Active-ATA-Chipset maddness..........
-D: Ultra DMA 66/33
+D: Ultra DMA 100/66/33
+D: ATA-Disconnect
D: ATA-Smart Kernel Daemon
S: Linux ATA Development (LAD)
S: Concord, CA
S: D-71679 Asperg
S: Germany
+N: Gareth Hughes
+E: gareth@valinux.com
+E: gareth@precisioninsight.com
+D: Pentium III FXSR, SSE support
+S: 11/187 West Street
+S: Crows Nest NSW 2065
+S: Australia
+
N: Kenn Humborg
E: kenn@wombat.ie
D: Mods to loop device to support sparse backing files
S: 9725 GA Groningen
S: The Netherlands
+N: Pekka Riikonen
+E: priikone@poseidon.pspt.fi
+E: priikone@ssh.com
+D: Random kernel hacking and bug fixes
+D: International kernel patch project
+S: Kasarmikatu 11 A4
+S: 70110 Kuopio
+S: Finland
+
N: William E. Roadcap
E: roadcapw@cfw.com
W: http://www.cfw.com/~roadcapw
S: The Australian National University, ACT 0200
S: Australia
+N: Aristeu Sergio Rozanski Filho
+E: aris@conectiva.com.br
+D: Support for EtherExpress 10 ISA (i82595) in eepro driver
+S: Conectiva S.A.
+S: R. Tocantins, 89 - Cristo Rei
+S: 80050-430 - Curitiba - Paraná
+S: Brazil
+
N: Alessandro Rubini
E: rubini@ipvvis.unipv.it
D: the gpm mouse server and kernel support for it
N: Paul `Rusty' Russell
E: rusty@linuxcare.com
-W: http://www.rustcorp.com
+W: http://www.samba.org/netfilter
D: Ruggedly handsome.
D: netfilter, ipchains with Michael Neuling.
S: 301/222 City Walk
S: The Netherlands
N: David Woodhouse
-E: David.Woodhouse@mvhi.com
-E: Dave@imladris.demon.co.uk
-D: Extensive ARCnet rewrite
-D: ARCnet COM20020, COM90xx IO-MAP drivers
-D: SO_BINDTODEVICE in 2.1.x (from Elliot Poger's code in 2.0.31)
-D: Contributed to NCPFS rewrite for 2.1.x dcache
-D: Alpha platforms: SX164, LX164 and Ruffian ported to 2.1.x
-S: 29, David Bull Way
-S: Milton, Cambridge. CB4 6DP
+E: dwmw2@infradead.org
+E: dwmw2@redhat.com
+D: ARCnet stuff, Applicom board driver, SO_BINDTODEVICE,
+D: some Alpha platform porting from 2.0, Memory Technology Devices,
+D: Acquire watchdog timer, PC speaker driver maintenance,
+D: various other stuff that annoyed me by not working.
+S: c/o Red Hat UK Limited
+S: 35-36 Cambridge Place
+S: Cambridge. CB2 1NS
S: England
N: Frank Xia
=====
This document is designed to provide a list of the minimum levels of
-software necessary to run the 2.3 kernels, as well as provide brief
+software necessary to run the 2.4 kernels, as well as provide brief
instructions regarding any other "Gotchas" users may encounter when
trying life on the Bleeding Edge. If upgrading from a pre-2.2.x
kernel, please consult the Changes file included with 2.2.x kernels for
here. Basically, this document assumes that your system is already
functional and running at least 2.2.x kernels.
- It is originally based on my "Changes" file for 2.2.x kernels and
-therefore owes credit to the same people as that file (Jared Mauch,
+This document is originally based on my "Changes" file for 2.0.x kernels
+and therefore owes credit to the same people as that file (Jared Mauch,
Axel Boldt, Alessandro Sigala, and countless other users all over the
-'net). Please feel free to submit changes, corrections, gripes,
-flames, money, etc. to me (kaboom@gatech.edu). If you do so, you don't
-need to bother doing so in the form of a diff, as this is generated by
-texinfo so a diff is useless anyway (though I can incorporate one by
-hand if you insist upon sending it that way ;-).
+'net).
- For those of you in Europe,
-http://www.datanet.hu/generations/linux/Changes2.html is an
-English-language HTML version.
+The latest revision of this document, in various formats, can always
+be found at http://cyberbuzz.gatech.edu/kaboom/linux/Changes-2.4/
+<http://cyberbuzz.gatech.edu/kaboom/linux/Changes-2.4/>.
- The most current version should always be available from
-http://cyberbuzz.gatech.edu/kaboom/linux/ as well.
+Feel free to translate this document. If you do so, please send me a
+URL to your translation for inclusion in future revisions of this
+document.
- Voir
-http://www.linux-france.com/article/sys/Changes-2.2/Changes-2.2.1.html
-pour la traduction français.
+Last updated: June 11, 2000
- Also, don't forget http://www.linuxhq.com/ for all your Linux kernel
-needs.
-
-Last updated: Feb 21, 2000
-Current Author: Chris Ricker (kaboom@gatech.edu or chris.ricker@m.cc.utah.edu).
+Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu).
Current Minimal Requirements
-****************************
+============================
- Upgrade to at *least* these software revisions before thinking you've
+Upgrade to at *least* these software revisions before thinking you've
encountered a bug! If you're unsure what version you're currently
running, the suggested command should tell you.
-- Kernel modutils 2.3.10 ; insmod -V
-- Gnu C 2.7.2.3 ; gcc --version
-- Binutils 2.9.1.0.7 ; ld -v
-- Linux libc5 C Library 5.4.46 ; ls -l /lib/libc*
-- Linux libc6 C Library 2.0.7pre6 ; ls -l /lib/libc*
-- Dynamic Linker (ld.so) 1.9.9 ; ldd --version or ldd -v
-- Linux C++ Library 2.7.2.8 ; ls -l /usr/lib/libg++.so.*
-- Procps 1.2.9 ; ps --version
-- Procinfo 16 ; procinfo -v
-- Psmisc 17 ; pstree -V
-- Net-tools 1.50 ; hostname -V
-- Loadlin 1.6a
-- Sh-utils 1.16 ; basename --v
-- Autofs 3.1.1 ; automount --version
-- NFS (client) 2.2beta40 ; showmount --version
-- nfs-utils (server) 0.1.4
-- Bash 1.14.7 ; bash -version
-- Ncpfs 2.2.0 ; ncpmount -v
-- Pcmcia-cs 3.1.2 ; cardmgr -V
-- PPP 2.4.0b1 ; pppd --version
-- Util-linux 2.9i ; chsh -v
-- isdn4k-utils v3.1beta7 ; isdnctrl 2>&1|grep version
-
-Upgrade notes
-*************
-
-General Information
-===================
-
- To use System V shared memory, you have to mount the shm filesystem
-somewhere. You can do that automatically by adding this line to /etc/fstab:
-
-none /var/shm shm defaults 0 0
-
-Remember to create the mountpoint directory; it does not have to be /var/shm.
-
- <CTRL><ALT><DEL> now performs a cold reboot instead of a warm reboot
-for increased hardware compatibility. If you want a warm reboot and
-know it works on your hardware, add a "reboot=warm" command line option
-in LILO. A small number of machines need "reboot=bios" to reboot via
-the BIOS.
-
- Also, please remember that cua* devices are now obsolete. Switch to
-the corresponding ttyS* device instead (e.g., cua0 -> ttyS0, cua1 ->
-ttyS1, etc.).
-
- In addition, some software still works, but needs to be compiled
-against 2.2 headers for complete functionality. Fdutils binaries
-compiled under 2.0 or earlier kernels should be replaced with ones
-compiled under 2.2, for example.
-
- As of 2.1.115, support for the deprecated major 4 /dev/ttyp* devices
-was removed. If necessary (eg, you get "out of pty" error messages when
-you obviously are not out of pty's), create major 3 /dev/tty* and major
-2 /dev/pty* devices (see Documentation/devices.txt for more
-information). In general, you should make sure that your /dev
-directory is up-to-date if you are experiencing any problems.
-
- Optional support for Unix98 pty devices has also been added. If you
-want to use the Unix98 ptys, you should be running at least
-glibc-2.0.9x, and you must switch completely to Unix98 pty's. The
-general procedure for configuring Unix98 pty support is:
-
-- Compile your kernel with CONFIG_UNIX98_PTYS and CONFIG_DEVPTS_FS.
-- mknod /dev/ptmx c 5 2
- chmod 666 /dev/ptmx
- mkdir /dev/pts
-- Add to /etc/fstab:
-
- none /dev/pts devpts gid=5,mode=620 0 0
-
- (Note: gid=5 is applicable for Red Hat systems for which group "tty" has
- gid 5. Adjust according to your distribution. Use mode=600 if you want
- "mesg n" to be default.)
-- Mount /dev/pts
-
- Frame buffer consoles ("fbcon") are now in the kernel for all
-platforms, not just those non-Intel ones for which VGA text mode is
-impossible. VGAcon is still available for those who want it, but fbcon
-has the advantage of providing a uniform graphical subsystem across all
-Linux ports, and it displays a spiffy penguin logo on boot-up ;-). For
-more information, see the files in Documentation/fb/ ; you may also
-need to download the fbset utilities.
-
-Libc (libc5)
-============
-
- Linux-2.2 is ELF-only. You can still compile a.out apps if you
-really want, but your kernel must be compiled ELF. If you can't
-currently compile ELF, consult the ELF howto at
-http://metalab.unc.edu/mdw/HOWTO/ELF-HOWTO.html and upgrade your system
-accordingly.
-
- For modules to work, you need to be running libc-5.4.x or greater.
-Since updates to libc fix other problems as well (security flaws, for
-example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can. Currently, libc-5.4.46 is the latest public
-release.
-
- If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
-linker (ld.so) to at least 1.9.9, or all sorts of weirdness will
-happen. Actually, ld.so-1.8.2 and later will work, but 1.9.9 is widely
-available, so if you need to upgrade, use it. If you get a release
-later than 1.8.5, avoid 1.8.10 as it introduces a few bugs that are
-fixed in later releases. Please make sure you don't install ld.so-2.x
-unless you're running glibc2 / libc6.
-
- If you upgrade to libc-5.4.x, you may also need to upgrade ypbind if
-you're using NIS. For ypbind and glibc, you'll probably need the
-ypbind-3.3-glibc5.diff patch available in the same place as the ypbind
-source.
-
- If you upgrade to libc-5.4.46, please read and pay attention to its
-accompanying release notes. The section about it breaking make is not a
-joke.
-
-GNU libc (libc6)
-================
-
- Older versions of GNU libc (libc6) have a bug in the dynamic linker.
-/etc/ld.so.cache gets mapped into memory and is never unmapped. If one
-of your boot scripts calls ldconfig, /etc/ld.so.cache is deleted. Init,
-however, still references that file; as of 2.1.122, the kernel will
-consequently not be able to remount the root file system r/o at system
-shutdown. To fix this, upgrade to at least the pre6 release of GNU
-libc 2.0.7. As a temporary workaround, modify your boot scripts to do
-the following before calling ldconfig:
-
- ln -f /etc/ld.so.cache /etc/ld.so.cache.old
-
-Modules
-=======
-
- You need to upgrade to the latest version of modutils for the Linux
-2.3 kernel. This version can also be built to work with your 2.0 kernel.
-
- As of 2.1.90-pre1, kerneld has been replaced by a kernel thread,
-kmod. See Documentation/kmod.txt for more information. The main
-user-level change this requires is modification to your init scripts to
-check for the absence of /proc/sys/kernel/modprobe before starting
-kerneld.
-
-Binutils
-========
-
- If you upgrade binutils, please read its accompanying release notes
-to find out the proper way to upgrade it. No, the instruction to "rm
-`which encaps`" is not a joke.
-
- You must use binutils 2.9.1.0.7 or later. Latest release is 2.9.1.0.25.
-Beware that binutils 2.9.1 (note the absence of a suffix) from the FSF
-does not work. If you are upgrading from earlier versions, you should
-consider upgrading to the latest 2.9.5.0.x (beta) release.
-
-Gnu C
-=====
-
- You need at least GCC 2.7.2 to compile the kernel. If you're
-upgrading from an earlier release, you might as well get GCC 2.7.2.3,
-the latest stable public release. If you already have GCC 2.7.2 on
-your system, you don't have to upgrade just so the kernel will work
-(though feel free to upgrade if you want the gcc bug fixes).
-
- Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad
-Things while compiling your kernel, particularly if absurd
-optimizations (like -O9) are used. Caveat emptor. Currently, the only
-C compiler available in a binary distribution is egcs. Version 1.0.3
-seems okay; if you have to have a binary, you may be successful using
-that. In general, however, gcc-2.7.2.3 is known to be stable, while
-egcs and others have not been as thoroughly tested yet.
-
-Networking Changes
+Again, keep in mind that this list assumes you are already
+functionally running a Linux 2.2 kernel. Also, not all tools are
+necessary on all systems; obviously, if you don't have any PCMCIA (PC
+Card) hardware, for example, you probably needn't concern yourself
+with pcmcia-cs.
+
+o Gnu C 2.7.2.3 # gcc --version
+o binutils 2.9.1.0.7 # ld -v
+o util-linux 2.10g # chsh -v
+o modutils 2.3.10 # insmod -V
+o e2fsprogs 1.18 # /sbin/tune2fs --version
+o pcmcia-cs 3.1.13 # cardmgr -V
+o PPP 2.4.0b1 # pppd --version
+o isdn4k-utils 3.1beta7 # isdnctrl 2>&1|grep version
+
+Kernel compilation
==================
- Please read Documentation/networking/routing.txt and
-Documentation/networking/policy-routing.txt for more information about
-changes in routing code. OSPF classes have been added, and interface
-routes are generated automatically.
-
- If for some reason you need to override this automatic default
-routing, you have to specify the complete route specification (netmask,
-device, etc.) for the kernel to accept it. Consequently, you need to
-either remove interface routes from your init scripts or add missing
-information to them if you need to replace the automatic routes.
-
- Also note that some routes, such as loopback routes, do not show up
-in some standard tools. Check in /proc/net/rt_local to verify their
-presence.
-
- To turn on IP forwarding, issue the following command: echo 1 >
-/proc/sys/net/ipv4/ip_forward
-
- Similar procedures are necessary to turn on other features. If
-something appears broken, check the /proc/sys/net/ipv4/ directory. "1"
-generally denotes enabled, while "0" generally denotes disabled.
-
- If you're experiencing reports of lots of network errors, chances
-are you need to upgrade to a more recent net-tools that understands the
-new /proc/net/dev format. This will also provide support for new
-features like IPv6.
-
- The IP firewalling and NAT code has been replaced again. The
-userspace tool `iptables' is distributed at:
- http://antarctica.penguincomputing.com/~netfilter/
- http://www.samba.org/netfilter/
- http://netfilter.kernelnotes.org
-
- DHCP clients for 2.0 do not work with the new networking code in the
-2.2 kernel. You will need to upgrade your dhcpcd / dhcpclient.
-
- In 2.0.x the kernel could be configured to drop source routed IP
-packets via a compile time configuration option. In 2.2.x, this has
-been replaced by a sysctl. See Documentation/networking/ip-sysctl.txt
-for more information.
-
-Memory
-======
+GCC
+---
- As of 2.1.41, the format of /proc/meminfo has changed. This broke
-many memory utils, which have to be upgraded. Get the new procps-1.2
-and you should be set.
+You will need at least gcc 2.7.2 to compile the kernel. You currently
+have several options for gcc-derived compilers: gcc 2.7.2.3, various
+versions of egcs, the new gcc 2.95 and upcoming gcc 3.0, and experimental
+compilers like pgcc. For absolute stability, it is still recommended
+that gcc 2.7.2.3 be used to compile your kernel. egcs 1.12 should also
+work. gcc 2.95 is known to have problems, and using pgcc for your kernel
+is just asking for trouble.
-Network File System
-===================
+In addition, please pay attention to compiler optimization. Anything
+greater than -O2 may not be wise. Similarly, if you choose to use gcc-2.95
+or derivatives, be sure not to use -fstrict-aliasing (which, depending on
+your version of gcc 2.95, may necessitate using -fno-strict-aliasing).
- The NFS code in the kernel is currently being revised, resulting in
-much-improved performance. Also, amd is being phased out in favor of
-the much better autofs. You'll also have to get the appropriate utils
-to use autofs as well as the new NFS utils. In addition, you have the
-choice of user-land NFS or kernel-level NFS (knfs).
-
-Util-linux (including mount)
-============================
-
- Among other changes made in the development of Linux kernel 2.2, the
-128 meg limit on IA32 swap partition sizes has been eliminated. To use
-larger swap spaces, you need the new mkswap found in util-linux. You
-also need to upgrade util-linux to get the latest version of mount.
-
- Partitions on 2048 byte sectored media (certain magneto opticals
-most prominently) were broken throughout the whole of 2.1 kernel
-series, meaning that you will be unable to use 2.1-partitioned media on
-Linux 2.2. This is not a 2.2 bug - 2.2 finally does the right thing!
-[If you have to interchange media between Linux 2.1 and 2.2, your best
-bet is to not use partitions at all but create the filesystem on the
-raw device (e.g. /dev/sda) instead. This is also known as the
-superfloppy format.]
-
- To properly create partitions on 2048 byte sectored media with Linux
-2.2, be sure to use no less than fdisk version 2.9i and invoke fdisk
-using '-b 2048' as an option.
-
-
-RPM
-===
-
- If you run Red Hat Linux or any other distribution that uses RPM,
-you need to upgrade RPM to a 2.5.x or later version.
-
-DOSEMU
-======
+Binutils
+--------
- A new "stable" version of DOSEMU is available for 2.2 kernels.
-Upgrade to 0.98.4 or later.
+Linux on IA/32 has recently switched from using as86 to using gas for
+assembling the 16-bit boot code, removing the need for as86 to compile
+your kernel. This change does, however, mean that you need a recent
+release of binutils.
-Loadlin
-=======
+If you can, upgrade to the latest 2.9.5 binutils release. Older
+releases such as 2.8, 2.8.xx, and the FSF's 2.9.1 should be avoided if
+at all possible. The later releases of 2.9.1.0.x (anything where x >= 7)
+can and do compile the kernel properly, but there are many benefits
+to upgrading to 2.9.5 if you're up to it.
- Linux 2.1.22 and later releases use a new method of memory size
-detection, requiring loadlin users to upgrade to loadlin-1.6a.
+System utils
+============
-Sh-utils
-========
+Architectural changes
+---------------------
- As of Linux-2.1.26, the Configure script ("make config") has been
-updated to be POSIX-compliant. As a result, your expr needs to be
-updated. Use sh-utils 1.16 or later.
+DevFS is now in the kernel. See Documentation/filesystems/devfs/* in
+the kernel source tree for all the gory details.
-Parallel Ports
-==============
+System V shared memory is now implemented via a virtual filesystem.
+You do not have to mount it to use it as long as you can live with the
+default maxima for shared memory and segments. If you wish to change
+these variables, you have to mount it with the options nr_blocks
+and/or nr_inodes. POSIX shared memory is also now implemented via a
+virtual filesystem. If you want to use it, you'll need to mount the
+filesystem. The recommended mount location is /dev/shm, and adding the
+following line to /etc/fstab should take care of things:
- As of 2.1.33, parallel port support can now by handled by the parport
-driver. Be aware that your parallel port may no longer be where you
-expect it; for example, LPT1 (under DOS) was sometimes /dev/lp1 in
-Linux, but will probably be /dev/lp0 with the new parport driver. If
-printing breaks with the new driver, try checking your lpd
-configuration. A good source of more information is the
-Documentation/parport.txt file included with the kernel.
+none /dev/shm shm defaults 0 0
-Setserial
-=========
+Remember to create the directory that you intend to mount shm on if
+necessary.
- If you experience random problems (stuck lines, lost characters,
-etc.) with serial lines under recent kernels, upgrading setserial
-should help.
+The Logical Volume Manager (LVM) is now in the kernel. If you want to
+use this, you'll need to install the necessary LVM toolset.
-Syncookies
-==========
+32-bit UID support is now in place. Have fun!
- When you build your kernel with Syncookie support
-(CONFIG_SYN_COOKIES) the syncookie code still defaults to off (unlike
-the 2.0.30+ behavior). You have to explicitly enable it by issuing the
-following command: echo 1 > /proc/sys/net/ipv4/tcp_syncookies
+Linux documentation for functions is transitioning to inline
+documentation via specially-formatted comments near their
+definitions in the source. These comments can be combined with the
+SGML templates in the Documentation/DocBook directory to make DocBook
+files, which can then be converted by DocBook stylesheets to PostScript,
+HTML, PDF files, and several other formats. In order to convert from
+DocBook format to a format of your choice, you'll need to install Jade as
+well as the desired DocBook stylesheets.
-Bash
-====
+Util-linux
+----------
- Old versions of Bash fail to properly handle symlinks, which can
-cause problems when compiling modules. Upgrade to at least 1.14 to fix
-this problem.
+New versions of util-linux provide *fdisk support for larger disks,
+support new options to mount, recognize more supported partition
+types, and similar goodies. You'll probably want to upgrade.
-Sysklogd
-========
+Ksymoops
+--------
- Older versions of sysklogd sometimes segfault under 2.2 kernels.
-Upgrading to the latest release fixes that problem as well as adding
-support for new features like system power-off on halt (with
-appropriate incantations of halt; see the man page) and automatic
-decoding of kernel oopses.
+If the unthinkable happens and your kernel oopses, you'll need a 2.3
+version of ksymoops to decode the report; see REPORTING-BUGS in the
+root of the Linux source for more information.
-Ncpfs
-=====
+Modutils
+--------
- To mount NetWare shares, you'll need to upgrade to a more recent
-version of the ncpfs utils.
+Upgrade to recent modutils to fix various outstanding bugs which are
+seen more frequently under 2.3.x, and to enable auto-loading of USB
+modules.
-SMBfs
-=====
+E2fsprogs
+---------
- To mount SMB (Samba / Windows) shares, you'll need to use the
-smbmount utility included with release 2.0 of Samba.
-Documentation/filesystems/smbfs.txt has more information about this.
-Note that smbmount must have been built against 2.2 headers to work
-with 2.2; if all else fails, recompile it and hope it works ;-). In
-addition, Mike Warfield has a script and some information at
-http://www.wittsend.com/mhw/smbmount.html that you will probably find
-useful.
+The latest version of e2fsprogs fixes several bugs in fsck and
+debugfs. Obviously, it's a good idea to upgrade.
Pcmcia-cs
-=========
-
- If you use pcmcia cards, you'll need to upgrade the daemon and
-support utils to the latest release of pcmcia-cs.
-
-PPP
-===
-
- The PPP driver has been restructured to support multilink and
-to enable it to operate over diverse kinds of media. Those of you
-using PPP networking will need to upgrade your pppd to at least
-version 2.4.0b1. See ftp://linuxcare.com.au/pub/ppp/ for the latest
-version.
+---------
- If you are not using devfs, you must make sure that the special
-device file /dev/ppp exists. It can be made by executing this command
-as root:
+PCMCIA (PC Card) support is now partially implemented in the main
+kernel source. Pay attention when you recompile your kernel ;-).
+Also, be sure to upgrade to the latest pcmcia-cs release.
- mknod /dev/ppp c 108 0
+Intel P6 microcode
+------------------
- If you have built ppp support as modules, you should put the lines
-below in your /etc/modules.conf file.
+A driver has been added to allow updating of Intel P6 microcode,
+accessible as both a devfs regular file and as a normal (misc)
+character device. If you are not using devfs you may need to:
- alias char-major-108 ppp_generic
- alias /dev/ppp ppp_generic
- alias tty-ldisc-3 ppp_async
- alias tty-ldisc-14 ppp_synctty
- alias ppp-compress-21 bsd_comp
- alias ppp-compress-24 ppp_deflate
- alias ppp-compress-26 ppp_deflate
+mkdir /dev/cpu
+mknod /dev/cpu/microcode c 10 184
+chmod 0644 /dev/cpu/microcode
-If you are using devfsd and you have ppp_generic as a module, put the
-following line in your /etc/devfsd.conf:
+as root before you can use this. You'll probably also want to
+get the user-space microcode_ctl utility to use with this.
- LOOKUP ppp MODLOAD
-
-iBCS
-====
-
- A new version of iBCS is necessary for 2.2 kernels.
-
-AppleTalk
-=========
-
- Use the Asun version of netatalk for AppleTalk support, as Umich's
-version is not compatible with 2.2 kernels.
-
-Psmisc
-======
-
- fuser, which comes with psmisc, reads /proc/*/fd/* to do its job.
-Upgrade psmisc if 2.2 changes to /proc broke the version you're using.
-
-PCI utils
-=========
-
- Linux PCI utils are available; these include lspci, which displays
-detailed information about your system's PCI devices (much more than
-the basic things in /proc/pci), and setpci, which allows you to read
-and write PCI configuration registers of your devices.
-
-Powertweak
-==========
-
- The PCI Bridge Optimization has been removed from the kernel. If you
-think your BIOS does a poor job when setting up your chipset, there
-is a utility called PowerTweak whose job is to tune chipset parameters.
-
-Xosview
-=======
-
- Changes to the /proc interface require a recent xosview.
-
-RealPlayer
+Networking
==========
- Current releases of Real Player 5.0 depend on a bug in the sound
-sub-system which is no longer there. Consequently, they don't work.
-Real is aware of the problem and should have an updated version of the
-software available shortly. In the mean time, you can always try
-backing up your copy of rvplayer, and then editing it by:
-
- dd if=/dev/zero of=rvplayer bs=1 count=1 seek=657586 conv=notrunc
- dd if=/dev/zero of=rvplayer bs=1 count=1 seek=665986 conv=notrunc
+General changes
+---------------
- If you're lucky, you'll then have sound....
+The IP firewalling and NAT code has been replaced again. The new
+netfilter software (including ipfwadm and ipchains backwards-
+compatible modules) is currently distributed separately.
- You may also need to edit it with
+If you have advanced network configuration needs, you should probably
+consider using the network tools from ip-route2.
- dd if=/dev/zero of=rvplayer bs=1 count=1 seek=702554 conv=notrunc
+PPP
+---
- as well. Alternately, download rpopen from
-http://onramp.i2k.com/~jeffd/rpopen/ and pre-load it before you run
-rvplayer (it's a shared object which blocks rvplayer from doing the
-NONBLOCKing open of /dev/dsp).
+The PPP driver has been restructured to support multilink and to
+enable it to operate over diverse media layers. If you use PPP,
+upgrade pppd to at least 2.4.0b1.
-Quotas
-======
+If you are not using devfs, you must have the device file /dev/ppp
+which can be made by:
- If you are using large quotas, you should upgrade your quota utils;
-newer versions count file sizes in blocks instead of bytes, providing
-an upper limit of terabytes instead of 4 GB.
+mknod /dev/ppp c 108 0
-Ping
-====
+as root.
- Most distributed ping clients are buggy. Get an updated one from the
-iputils package.
+If you build ppp support as modules, you will need the following in
+your /etc/modules.conf file:
-Patch
-=====
+alias char-major-108 ppp_generic
+alias /dev/ppp ppp_generic
+alias tty-ldisc-3 ppp_async
+alias tty-ldisc-14 ppp_synctty
+alias ppp-compress-21 bsd_comp
+alias ppp-compress-24 ppp_deflate
+alias ppp-compress-26 ppp_deflate
- Really old versions of patch cannot delete files. This can be a
-problem if you try to upgrade via patches. If, for example, you are
-unable to compile Linux 2.2, you may have an outdated version of patch.
-Upgrade, re-patch the kernel, and try again.
+If you use devfsd and build ppp support as modules, you will need
+the following in your /etc/devfsd.conf file:
-Process accounting
-==================
+LOOKUP PPP MODLOAD
- If you use process accounting, you need to recompile the package
-against 2.2 kernel includes for it to work properly. Furthermore, when
-you do so, watch out for a quirky configure script. Your generated
-config.h file needs to
+Isdn4k-utils
+------------
- #define HAVE_LINUX_ACCT_H
+Due to changes in the length of the phone number field, isdn4k-utils
+needs to be recompiled or (preferably) upgraded.
- but instead it often has
+Getting updated software
+========================
- /* #undef HAVE_LINUX_ACCT_H */
+Compilers
+*********
- so be sure to check that when you recompile.
+gcc 2.7.2.3
+-----------
+o ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz
+ <ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz>
+o ftp://metalab.unc.edu/pub/gnu/gcc-2.7.2.3.tar.gz
+ <ftp://metalab.unc.edu/pub/gnu/gcc-2.7.2.3.tar.gz>
-ISDN4Linux
-==========
-Since 2.3.27 here is a new length of the phonenumber field, old utils
-have to recompile, a upgrade to isdn4k-utils.v3.1beta7 or later is
-recomented.
-Older isdn4k-utils versions don't support EXTRAVERSION into kernel version
-string.
-
-Logical Volume Manager
-======================
-Since 2.3.47 the kernel contains the Logical Volume Manager aka LVM. To use it,
-you need to install the LVM tools. More information can be found at the home page
-of the LVM project at http://linux.msede.com/lvm/.
-
-Inline Documentation
-====================
-Many of the functions available for modules to use are now documented
-with specially-formatted comments near their definitions. These
-comments can be combined with the SGML templates in the
-Documentation/DocBook directory to make DocBook files, which can then
-be combined with DocBook stylesheets to make PostScript documents,
-HTML pages, PDF files, and so on. In order to convert from DocBook
-format to a format of your choice, you'll need to install jade, as
-well as some stylesheets.
-
-
-Where to get the files
-**********************
+egcs 1.12
+---------
+o ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-glibc.x86.tar.bz2
+ <ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-glibc.x86.tar.bz2>
+o ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-libc5.x86.tar.bz2
+ <ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-libc5.x86.tar.bz2>
+o ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-alpha.tar.bz2
+ <ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-alpha.tar.bz2>
Binutils
-========
-
-The 2.9.1.0.25 release:
-ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25-glibc.x86.tar.gz
-ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz
-Installation notes:
-ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/release.binutils-2.9.1.0.25
-
-The 2.9.5.0.22 release:
-ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.22.tar.bz2
-Installation notes:
-ftp://ftp.varesearch.com/pub/support/hjl/binutils/release.binutils-2.9.5.0.22
-
-Gnu C
-=====
-
-The egcs-1.0.3 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-glibc.x86.tar.bz2
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-libc5.x86.tar.bz2
-ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-glibc.x86.tar.bz2
-ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-libc5.x86.tar.bz2
-Installation notes:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.egcs-1.0.3
-ftp://metalab.unc.edu/pub/Linux/GCC/release.egcs-1.0.3
-
-Gnu C 2.7.2.3 source:
-ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz
-ftp://metalab.unc.edu/pub/gnu/gcc-2.7.2.3.tar.gz
-
-Linux C Library
-===============
-
-The (libc5) 5.4.46 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.46.bin.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/libc-5.4.46.bin.tar.gz
-Installation notes for 5.4.46:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.46
-ftp://metalab.unc.edu/pub/Linux/GCC/release.libc-5.4.46
-
-The (libc6) GNU libc 2.0.7pre6 release:
-ftp://ftp.kernel.org/pub/software/libs/glibc/glibc-2.0.7pre6.tar.gz
-ftp://ftp.kernel.org/pub/software/libs/glibc/glibc-2.0.7pre6.tar.bz2
-
-Linux C++ Library
-=================
-
-The 2.7.2 release:
-ftp://ftp.gnu.org/gnu/libg++/libg++-2.7.2.tar.gz
-
-Dynamic Linker
-==============
-
-The 1.9.9 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.9.9.tar.gz
-ftp://metalab.unc.edu/pub/Linux/GCC/ld.so-1.9.9.tar.gz
-
-Modules utilities
-=================
-
-The 2.3.10 release:
-ftp://ftp.ocs.com.au/pub/modutils/v2.3/modutils-2.3.10.tar.gz
-
-Procps utilities
-================
-
-The 1.2 release:
-ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.9.tar.gz
-ftp://metalab.unc.edu/pub/Linux/system/status/ps/procps-1.2.9.tgz
-
-Procinfo utilities
-==================
-
-The 16 release:
-ftp://ftp.cistron.nl/pub/people/svm/procinfo-16.tar.gz
+********
-Psmisc utilities
-================
+2.9.1 series
+------------
+o ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz
+ <ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz>
-The 17 release:
-ftp://lrcftp.epfl.ch/pub/linux/local/psmisc/psmisc-17.tar.gz
-ftp://metalab.unc.edu/pub/Linux/system/status/ps/psmisc-17.tar.gz
+2.9.5 series
+------------
+o ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.29.tar.gz
+ <ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.29.tar.bz2>
-RPM utilities
-=============
-
-The 2.5.1 source release:
-ftp://ftp.rpm.org/pub/rpm/dist/rpm-2.5.x/rpm-2.5.1-1.src.rpm
-ftp://ftp.rpm.org/pub/rpm/dist/rpm-2.5.x/rpm-2.5.1.tar.gz
-
-DOSEMU
-======
-
-The 0.98.1 release:
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu-0.98.4.tgz
-ftp://ftp.dosemu.org/dosemu/dosemu-0.98.4.tgz
-
-Loadlin
-=======
-
-The 1.6a release:
-ftp://ftp.suse.com/pub/loadlin/update-1.6a/loadlin.exe.gz
-ftp://elserv.ffm.fgan.de/pub/linux/loadlin-1.6/update-1.6a/loadlin.exe.gz
-
-Sh-utils
-========
-
-The 1.16 release:
-ftp://metalab.unc.edu/pub/gnu/sh-utils-1.16.tar.gz
-ftp://ftp.gnu.org/gnu/sh-utils/sh-utils-1.16.tar.gz
+System utilities
+****************
Util-linux
-==========
-
-The 2.9 release:
-ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9i.tar.gz
-
-Autofs
-======
-
-The 3.1.3 release:
-ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-3.1.3.tar.gz
-
-NFS
-===
-
-The user-land 2.2beta40 release:
-ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
-ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
-
-The kernel-level nfs-utils-0.1.4 release:
-ftp://nfs.sourceforge.net/pub/nfs/nfs-utils-0.1.4.tar.gz
-
-Net-tools
-=========
-
-The 1.50 release:
-ftp://ftp.cs-ipv6.lancs.ac.uk/pub/Code/Linux/Net_Tools/net-tools-1.50.tar.gz
-http://www.tazenda.demon.co.uk/phil/net-tools/net-tools-1.50.tar.gz
-
-Ypbind
-======
-
-The 3.3 release:
-ftp://ftp.kernel.org/pub/linux/utils/net/NIS/ypbind-3.3.tar.gz
-
-Sysklogd
-========
-
-The 1.3-31 release:
-ftp://metalab.unc.edu/pub/Linux/system/daemons/sysklogd-1.3-31.tar.gz
-
-Bash
-====
-
-The 1.14.7 release:
-ftp://ftp.gnu.org/gnu/bash/bash-1.14.7.tar.gz
-
-The 2.02.1 release:
-ftp://ftp.gnu.org/gnu/bash/bash-2.02.1.tar.gz
-
-Ncpfs
-=====
-
-The 2.2.0 release:
-ftp://ftp.gwdg.de/pub/linux/misc/ncpfs/ncpfs-2.2.0.tgz
-
-SMBfs
-=====
-
-The 2.0.0 release of Samba:
-ftp://ftp.samba.org/pub/samba/samba-2.0.0.tar.gz
+----------
+o ftp://ftp.cwi.nl/pub/aeb/util-linux/util-linux-2.10g.tar.gz
+ <ftp://ftp.cwi.nl/pub/aeb/util-linux/util-linux-2.10g.tar.gz>
+
+Ksymoops
+--------
+o ftp://ftp.kernel.org/pub/linux/utils/kernel/ksymoops/v2.3
+ <ftp://ftp.kernel.org/pub/linux/utils/kernel/ksymoops/v2.3>
+
+Modutils
+--------
+o ftp://ftp.kernel.org/pub/linux/utils/kernel/modutils/v2.3/modutils-2.3.9.tar.gz
+ <ftp://ftp.kernel.org/pub/linux/utils/kernel/modutils/v2.3/modutils-2.3.9.tar.gz>
+
+E2fsprogs
+---------
+o http://web.mit.edu/tytso/www/linux/dist/e2fsprogs-1.18.tar.gz
+ <http://web.mit.edu/tytso/www/linux/dist/e2fsprogs-1.18.tar.gz>
+o http://web.mit.edu/tytso/www/linux/dist/e2fsprogs-1.18.src.rpm
+ <http://web.mit.edu/tytso/www/linux/dist/e2fsprogs-1.18.src.rpm>
+
+LVM toolset
+-----------
+o http://linux.msede.com/lvm/ <http://linux.msede.com/lvm/>
Pcmcia-cs
-=========
-
-The 3.1.2 release:
-ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.2.tar.gz
-
-Setserial
-=========
-
-The 2.15 release:
-ftp://tsx-11.mit.edu/pub/linux/sources/sbin/setserial-2.15.tar.gz
-ftp://metalab.unc.edu/pub/Linux/system/serial/setserial-2.15.tar.gz
-
-PPP
-===
-
-The 2.4.0b1 release:
-ftp://linuxcare.com.au/pub/ppp/ppp-2.4.0b1.tar.gz
-
-iptables
-=========
-
-The 1.1.0 release:
-http://antarctica.penguincomputing.com/~netfilter/iptables-1.1.0.tar.bz2
-http://www.samba.org/netfilter/iptables-1.1.0.tar.bz2
-http://netfilter.kernelnotes.org/iptables-1.1.0.tar.bz2
-
-IP Masq Adm
-===========
-
-The 0.4.2 release:
-http://juanjox.linuxhq.com/ipmasqadm-0.4.2.tar.gz
-
-DHCP clients
-============
-
-The 2.0b1p18 ISC dhcpclient release:
-ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0b1pl8.tar.gz
-
-The 1.3.17-pl2 PhysTech dhcpcd release:
-ftp://ftp.phystech.com/pub/dhcpcd-1.3.17-pl2.tar.gz
-
-iBCS
-====
-
-The 11/05/98 release:
-ftp://tsx-11.mit.edu/pub/linux/BETA/ibcs2/ibcs-2.1-981105-ALPHA.tar.gz
-
-Asun netatalk
-=============
-
-The 2.0a18.2 release:
-ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.0a18.2.tar.gz
-
-Fbset
-=====
-
-The 11/04/98 release:
-http://www.cs.kuleuven.ac.be/~geert/bin/fbset-2.0-pre-19981104.tar.gz
-
-PCI utils
-=========
-
-The 2.1.5 release:
-ftp://atrey.karlin.mff.cuni.cz/pub/linux/pci/pciutils-2.1.5.tar.gz
-ftp://metalab.unc.edu/pub/Linux/hardware/pciutils-2.1.5.tar.gz
-
-Powertweak
-==========
-
-The 0.1.13 release:
-http://linux.powertweak.com/files/powertweak-0.1.13.tgz
-ftp://atrey.karlin.mff.cuni.cz/pub/linux/pci/powertweak/powertweak-0.1.13.tgz
-
-Xosview
-=======
-
-The 1.6.1 release:
-ftp://metalab.unc.edu/pub/Linux/system/status/xstatus/xosview-1.6.1.tar.gz
-
-Quota utils
-===========
-
-The 1.55 release:
-ftp://ftp.uk.linux.org/pub/linux/sct/quota/quota-1.55-10.i386.rpm
-ftp://ftp.uk.linux.org/pub/linux/sct/quota/quota-1.55-10.src.rpm
-
-IP utils
-========
-
-The 03/01/99 release:
-ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.1.99-now-ss990301.tar.gz
-
-Patch
-=====
-
-The 2.5 release:
-ftp://ftp.gnu.org/gnu/patch/patch-2.5.tar.gz
-
-ISDN4Linux
-==========
-
-The v3.1beta7 release:
-ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/testing/isdn4k-utils.v3.1beta7.tar.gz
-
-Logical Volume Manager
-======================
-
-The 0.7 release:
-ftp://linux.msede.com/lvm/v0.7/lvm_0.7.tar.gz
+---------
+o ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.13.tar.gz
+ <ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.13.tar.gz>
Jade
-====
+----
+o ftp://ftp.jclark.com/pub/jade/jade-1.2.1.tar.gz
+ <ftp://ftp.jclark.com/pub/jade/jade-1.2.1.tar.gz>
-The 1.2.1 release:
-ftp://ftp.jclark.com/pub/jade/jade-1.2.1.tar.gz
+DocBook Stylesheets
+-------------------
+o http://nwalsh.com/docbook/dsssl/
+ <http://nwalsh.com/docbook/dsssl/>
-DSSSL Stylesheets for the DocBook DTD
-=====================================
+Intel P6 microcode
+------------------
+o http://www.urbanmyth.org/microcode/
+ <http://www.urbanmyth.org/microcode/>
-http://nwalsh.com/docbook/dsssl/
-
-
-Other Info
-==========
-
- Please remember that most of these utils are available on your
-favorite local linux mirror. If you can, please get them from a closer
-site before checking metalab or tsx-11.
-
- You may also want to check for updated versions of this software in a
-package format for the distribution you use.
-
- For those of you running Red Hat (or RPM on a different
-distribution), most of these are available in RPM format. Check around
-your favorite Red Hat mirror site before installing the non-RPM
-version. Remember, you might need to use the --force option to get the
-upgrade to install. ftp://contrib.redhat.com/ ,
-ftp://developer.redhat.com/ , or ftp://updates.redhat.com/ will have
-almost everything you need, and Red Hat 6.1 ships with most necessary
-software.
-
- Those of you running Debian (or a different distribution that
-supports .deb packages) can look in the "unstable" and
-"project/experimental" directories of your favorite Debian mirror. The
-Debian 2.2 release will ship with most packages you need as well.
-
-Please send info about any other packages that 2.3 "broke" or about any
-new features of 2.3 that require extra or new packages for use to Chris
-Ricker (kaboom@gatech.edu or chris.ricker@m.cc.utah.edu).
+Network
+*******
+PPP
+---
+o ftp://linuxcare.com.au/pub/ppp/ppp-2.4.0b1.tar.gz
+ <ftp://linuxcare.com.au/pub/ppp/ppp-2.4.0b1.tar.gz>
+
+Isdn4k-utils
+------------
+o ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/testing/isdn4k-
+ utils.v3.1beta7.tar.gz
+ <ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/testing/isdn4k-
+ utils.v3.1beta7.tar.gz>
+
+Netfilter
+---------
+o http://netfilter.filewatcher.org/iptables-1.1.0.tar.bz2
+ <http://netfilter.filewatcher.org/iptables-1.1.0.tar.bz2>
+o http://www.samba.org/netfilter/iptables-1.1.0.tar.bz2
+ <http://www.samba.org/netfilter/iptables-1.1.0.tar.bz2>
+o http://netfilter.kernelnotes.org/iptables-1.1.0.tar.bz2
+ <http://netfilter.kernelnotes.org/iptables-1.1.0.tar.bz2>
+
+Ip-route2
+---------
+o ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss991023.tar.gz
+ <ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss991023.tar.gz>
+
+Suggestions and corrections
+===========================
+
+Please feel free to submit changes, corrections, gripes, flames,
+money, etc. to me <chris.ricker@genetics.utah.edu>. Happy Linuxing!
-BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml kernel-api.sgml parportbook.sgml kernel-hacking.sgml kernel-locking.sgml
+BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml \
+ kernel-api.sgml parportbook.sgml kernel-hacking.sgml \
+ kernel-locking.sgml via-audio.sgml mousedrivers.sgml
PS := $(patsubst %.sgml, %.ps, $(BOOKS))
PDF := $(patsubst %.sgml, %.pdf, $(BOOKS))
+HTML := $(patsubst %.sgml, %, $(BOOKS))
+IMG-parportbook := parport-share.fig parport-multi.fig parport-structure.fig
+EPS-parportbook := $(patsubst %.fig, %.eps, $(IMG-parportbook))
+JPG-parportbook := $(patsubst %.fig, %.jpeg, $(IMG-parportbook))
$(BOOKS): $(TOPDIR)/scripts/docproc
-.PHONY: books ps pdf clean mrproper db2ps db2pdf
+.PHONY: books ps pdf html clean mrproper db2ps db2pdf db2html
books: $(BOOKS)
pdf: $(PDF)
-db2ps db2pdf:
+html: $(HTML)
+
+db2ps db2pdf db2html:
@(which $@ > /dev/null 2>&1) || \
(echo "*** You need to install DocBook stylesheets ***"; \
exit 1)
%.eps: %.fig
-fig2dev -Leps $< $@
+%.jpeg: %.fig
+ -fig2dev -Ljpeg $< $@
+
$(TOPDIR)/scripts/docproc:
$(MAKE) -C $(TOPDIR)/scripts docproc
+mousedrivers.sgml: mousedrivers.tmpl
+ $(TOPDIR)/scripts/docgen <$< >$@
+
kernel-hacking.sgml: kernel-hacking.tmpl
$(TOPDIR)/scripts/docgen <$< >$@
kernel-locking.sgml: kernel-locking.tmpl
$(TOPDIR)/scripts/docgen <$< >$@
-wanbook.sgml: wanbook.tmpl
+wanbook.sgml: wanbook.tmpl $(TOPDIR)/drivers/net/wan/syncppp.c
$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
<wanbook.tmpl >wanbook.sgml
-z8530book.sgml: z8530book.tmpl
+z8530book.sgml: z8530book.tmpl $(TOPDIR)/drivers/net/wan/z85230.c
$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/z85230.c \
<z8530book.tmpl >z8530book.sgml
-mcabook.sgml: mcabook.tmpl
+via-audio.sgml: via-audio.tmpl $(TOPDIR)/drivers/sound/via82cxxx_audio.c
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/sound/via82cxxx_audio.c \
+ <via-audio.tmpl >via-audio.sgml
+
+mcabook.sgml: mcabook.tmpl $(TOPDIR)/arch/i386/kernel/mca.c
$(TOPDIR)/scripts/docgen $(TOPDIR)/arch/i386/kernel/mca.c \
<mcabook.tmpl >mcabook.sgml
-videobook.sgml: videobook.tmpl
+videobook.sgml: videobook.tmpl $(TOPDIR)/drivers/char/videodev.c
$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
<videobook.tmpl >videobook.sgml
-kernel-api.sgml: kernel-api.tmpl
- $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
+APISOURCES := $(TOPDIR)/drivers/char/videodev.c \
$(TOPDIR)/arch/i386/kernel/mca.c \
$(TOPDIR)/arch/i386/kernel/mtrr.c \
$(TOPDIR)/drivers/char/misc.c \
- $(TOPDIR)/drivers/char/videodev.c \
$(TOPDIR)/drivers/net/net_init.c \
$(TOPDIR)/drivers/net/8390.c \
$(TOPDIR)/drivers/char/serial.c \
$(TOPDIR)/fs/devfs/base.c \
$(TOPDIR)/kernel/pm.c \
$(TOPDIR)/kernel/ksyms.c \
- $(TOPDIR)/net/netsyms.c \
+ $(TOPDIR)/net/netsyms.c
+
+kernel-api.sgml: kernel-api.tmpl $(APISOURCES)
+ $(TOPDIR)/scripts/docgen $(APISOURCES) \
<kernel-api.tmpl >kernel-api.sgml
-parportbook.sgml: parportbook.tmpl
+parportbook: $(JPG-parportbook)
+parportbook.ps: $(EPS-parportbook)
+parportbook.sgml: parportbook.tmpl $(TOPDIR)/drivers/parport/init.c
$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/parport/init.c \
<parportbook.tmpl >parportbook.sgml
AUX := $(patsubst %.sgml, %.aux, $(BOOKS))
TEX := $(patsubst %.sgml, %.tex, $(BOOKS))
LOG := $(patsubst %.sgml, %.log, $(BOOKS))
+OUT := $(patsubst %.sgml, %.out, $(BOOKS))
clean:
-$(RM) core *~
-$(RM) $(BOOKS)
- -$(RM) $(DVI) $(AUX) $(TEX) $(LOG)
- -$(RM) parport-share.eps parport-multi.eps parport-structure.eps
+ -$(RM) $(DVI) $(AUX) $(TEX) $(LOG) $(OUT)
+ -$(RM) $(JPG-parportbook) $(EPS-parportbook)
mrproper: clean
-$(RM) $(PS) $(PDF)
-
-parportbook.ps: parport-share.eps parport-multi.eps parport-structure.eps
+ -$(RM) -r $(HTML)
%.ps : %.sgml db2ps
db2ps $<
-%.pdf : %.sgml
+%.pdf : %.sgml db2pdf
db2pdf $<
+%: %.sgml db2html
+ -$(RM) -r $@
+ db2html $<
+ if [ ! -z "$(JPG-$@)" ]; then cp $(JPG-$@) $@; fi
+
include $(TOPDIR)/Rules.make
</bookinfo>
<toc></toc>
+ <chapter id="adt">
+ <title>Data Types</title>
+ <sect1><title>Doubly Linked Lists</title>
+!Iinclude/linux/list.h
+ </sect1>
+ </chapter>
+
+ <chapter id="mm">
+ <title>Memory Management in Linux</title>
+ <sect1><title>The Slab Cache</title>
+!Emm/slab.c
+ </sect1>
+ </chapter>
+
<chapter id="vfs">
<title>The Linux VFS</title>
<sect1><title>The Directory Cache</title>
your task will put itself on the queue, and be woken up when the
semaphore is released. This means the CPU will do something
else while you are waiting, but there are many cases when you
- simply can't sleep, and so have to use a spinlock instead.
+ simply can't sleep (see <xref linkend="sleeping-things">), and so
+ have to use a spinlock instead.
+ </para>
+ <para>
+ Neither type of lock is recursive: see
+ <xref linkend="techniques-deadlocks">.
</para>
<sect1 id="uniprocessor">
<para>
Hardware interrupts usually communicate with a bottom half,
- tasklet or softirq. Frequently this involved putting work in a
+ tasklet or softirq. Frequently this involves putting work in a
queue, which the BH/softirq will take out.
</para>
<para>
There is a coding bug where a piece of code tries to grab a
spinlock twice: it will spin forever, waiting for the lock to
- be released (spinlocks and writelocks are not re-entrant in
- Linux). This is trivial to diagnose: not a
+ be released (spinlocks, rwlocks and semaphores are not
+ recursive in Linux). This is trivial to diagnose: not a
stay-up-five-nights-talk-to-fluffy-code-bunnies kind of
problem.
</para>
</para>
<para>
- Dropping or gaining a spinlock, and any atomic operation are
- all defined to act as memory barriers (ie. as per the
- <function>mb()</function> macro).
- </para>
-
- <para>
- There is a similar, but unrelated, problem with code like the
- following:
- </para>
-
- <programlisting>
- if (!(ctrack->status & IPS_CONFIRMED)) {
- spin_lock_bh(&ip_conntrack_lock);
- if (!(ctrack->status & IPS_CONFIRMED)) {
- clean_from_lists(h->ctrack);
- h->ctrack->status |= IPS_CONFIRMED;
- }
- spin_unlock_bh(&ip_conntrack_lock);
- }
- </programlisting>
-
- <para>
- In this case, the author has tried to be tricky: knowing that
- the CONFIRMED bit is set and never reset in the status word,
- you can test it outside the lock, and frequently avoid
- grabbing the lock at all. However, the compiler could cache
- the value in a register, rather than rereading it once the
- lock is obtained, creating a subtle race. The way to get
- around this is to declare the status field `volatile', or use
- a temporary volatile pointer to achieve the same effect in
- this one place.
+ Any atomic operation is defined to act as a memory barrier
+ (ie. as per the <function>mb()</function> macro). Also,
+ spinlock operations act as partial barriers: operations after
+ gaining a spinlock will never be moved to precede the
+ <function>spin_lock()</function> call, and operations before
+ releasing a spinlock will never be moved after the
+ <function>spin_unlock()</function> call.
+ <!-- Manfred Spraul <manfreds@colorfullife.com>
+ 24 May 2000 2.3.99-pre9 -->
</para>
</sect1>
<para>
You can never call the following routines while holding a
- spinlock, as they may sleep:
+ spinlock, as they may sleep. This also means you need to be in
+ user context.
</para>
<itemizedlist>
<listitem>
<para>
- <function>printk()</function>, which can be called from
- user context, interestingly enough.
+ <function>down_interruptible()</function> and
+ <function>down()</function>
+ </para>
+ <para>
+ There is a <function>down_trylock()</function> which can be
+ used inside interrupt context, as it will not sleep.
+ <function>up()</function> will also never sleep.
</para>
</listitem>
</itemizedlist>
+
+ <function>printk()</function> can be called in
+ <emphasis>any</emphasis> context, interestingly enough.
</sect1>
<sect1 id="sparc">
Another common problem is deleting timers which restart
themselves (by calling <function>add_timer()</function> at the end
of their timer function). Because this is a fairly common case
- which is prone to races, the function <function>del_timer_sync()</function>
- (<filename class=headerfile>include/linux/timer.h</filename>) is
- provided to handle this case. It returns the number of times the timer
+ which is prone to races, you can put a call to
+ <function>timer_exit()</function> at the very end of your timer function,
+ and user <function>del_timer_sync()</function>
+ (<filename class=headerfile>include/linux/timer.h</filename>)
+ to handle this case. It returns the number of times the timer
had to be deleted before we finally stopped it from adding itself back
in.
</para>
<para>
Thanks to Martin Pool, Philipp Rumpf, Stephen Rothwell, Paul
- Mackerras, Ruedi Aschwanden, Alan Cox for proofreading,
- correcting, flaming, commenting.
+ Mackerras, Ruedi Aschwanden, Alan Cox, Manfred Spraul and Tim
+ Waugh for proofreading, correcting, flaming, commenting.
</para>
<para>
--- /dev/null
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="MouseGuide">
+ <bookinfo>
+ <title>Mouse Drivers</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Cox</surname>
+ <affiliation>
+ <address>
+ <email>alan@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Alan Cox</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+ <toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <note>
+ <title>Earlier publication</title>
+ <para>
+ Parts of this document first appeared in Linux Magazine under a
+ ninety day exclusivity.
+ </para>
+ </note>
+
+ <para>
+ Mice are conceptually one of the simplest device interfaces in the
+ Linux operating system. Not all mice are handled by the kernel.
+ Instead there is a two layer abstraction.
+ </para>
+
+ <para>
+ The kernel mouse drivers and userspace drivers for the serial mice are
+ all managed by a system daemon called <application>gpm</application>
+ - the general purpose mouse driver. <application>gpm</application>
+ handles cutting and pasting on the text consoles. It provides a
+ general library for mouse-aware applications and it handles the
+ sharing of mouse services with the
+ <application>X Window System</application> user interface.
+ </para>
+ <para>
+ Sometimes a mouse speaks a sufficiently convoluted protocol that the
+ protocol is handled by <application>Gpm</application> itself. Most
+ of the mouse drivers follow a common interface called the bus mouse
+ protocol.
+ </para>
+ <para>
+ Each read from a bus mouse interface device returns a block of data.
+ The first three bytes of each read are defined as follows:
+
+ <table frame=all>
+ <title>Mouse Data Encoding</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>Byte 0</entry>
+ <entry>0x80 + the buttons currently down.</entry>
+ </row>
+ <row>
+ <entry>Byte 1</entry>
+ <entry>A signed value for the shift in X position</entry>
+ </row>
+ <row>
+ <entry>Byte 2</entry>
+ <entry>A signed value for the shift in Y position</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ An application can choose to read more than 3 bytes. The rest of the
+ bytes will be zero, or may optionally return some additional
+ device-specific information.
+ </para>
+ <para>
+ The position values are truncated if they exceed the 8bit range (that
+ is -127 <= delta <= 127). While the value -128 does fit into a
+ byte is not allowed.
+ </para>
+ <para>
+ The <mousebutton>buttons</mousebutton> are numbered left to right as
+ 0, 1, 2, 3.. and each button sets the relevant bit. So a user pressing
+ the left and right button of a three button mouse will set bits 0 and 2.
+ </para>
+ <para>
+ All mice are required to support the <function>poll</function>
+ operation. Indeed pretty much every user of a mouse device uses
+ <function>poll</function> to wait for mouse events to occur.
+ </para>
+ <para>
+ Finally the mice support asynchronous I/O. This is a topic we have not
+ yet covered but which I will explain after looking at a simple mouse
+ driver.
+ </para>
+ </chapter>
+
+ <chapter id="driver">
+ <title>A simple mouse driver</title>
+ <para>
+ First we will need the set up functions for our mouse device. To keep
+ this simple our imaginary mouse device has three I/O ports fixed at I/O
+ address 0x300 and always lives on interrupt 5. The ports will be the X
+ position, the Y position and the buttons in that order.
+ </para>
+
+ <programlisting>
+#define OURMOUSE_BASE 0x300
+
+static struct miscdevice our_mouse = {
+ OURMOUSE_MINOR, "ourmouse", &our_mouse_fops
+};
+
+__init ourmouse_init(void)
+{
+
+ if(check_region(OURMOUSE_BASE, 3))
+ return -ENODEV;
+ request_region(OURMOUSE_BASE, 3, "ourmouse");
+
+ misc_register(&our_mouse);
+ return 0;
+}
+ </programlisting>
+
+ <para>
+ The <structname>miscdevice</structname> is new here. Linux normally
+ parcels devices out by major number, and each device has 256 units.
+ For things like mice this is extremely wasteful so a device exists
+ which is used to accumulate all the odd individual devices that
+ computers tend to have.
+ </para>
+ <para>
+ Minor numbers in this space are allocated by a central source, although
+ you can look in the kernel <filename>Documentation/devices.txt</filename>
+ file and pick a free one for development use. This kernel file also
+ carries instructions for registering a device. This may change over time
+ so it is a good idea to obtain a current copy of this file first.
+ </para>
+ <para>
+ Our code then is fairly simple. We check nobody else has taken our
+ address space. Having done so we reserve it to ensure nobody stamps
+ on our device while probing for other ISA bus devices. Such a probe
+ might confuse our device.
+ </para>
+ <para>
+ Then we tell the misc driver that we wish to own a minor number. We also
+ hand it our name (which is used in
+ <filename class="directory">/proc/misc</filename>) and a set of file
+ operations that are to be used. The file operations work exactly like the
+ file operations you would register for a normal character device. The misc
+ device itself is simply acting as a redirector for requests.
+ </para>
+ <para>
+ Next, in order to be able to use and test our code we need to add some
+ module code to support it. This too is fairly simple:
+ </para>
+ <programlisting>
+#ifdef MODULE
+
+int init_module(void)
+{
+ if(ourmouse_init()<0)
+ return -ENODEV:
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&our_mouse);
+ free_region(OURMOUSE_BASE, 3);
+}
+
+
+#endif
+ </programlisting>
+
+ <para>
+ The module code provides the normal two functions. The
+ <function>init_module</function> function is called when the module is
+ loaded. In our case it simply calls the initialising function we wrote
+ and returns an error if this fails. This ensures the module will only
+ be loaded if it was successfully set up.
+ </para>
+ <para>
+ The <function>cleanup_module</function> function is called when the
+ module is unloaded. We give the miscellaneous device entry back, and
+ then free our I/O resources. If we didn't free the I/O resources then
+ the next time the module loaded it would think someone else had its I/O
+ space.
+ </para>
+ <para>
+ Once the <function>misc_deregister</function> has been called any
+ attempts to open the mouse device will fail with the error
+ <errorcode>ENODEV</errorcode> (<errorname>No such device</errorname>).
+ </para>
+ <para>
+ Next we need to fill in our file operations. A mouse doesn't need many
+ of these. We need to provide open, release, read and poll. That makes
+ for a nice simple structure:
+ </para>
+
+ <programlisting>
+struct file_operations our_mouse_fops = {
+ NULL, /* Mice don't seek */
+ read_mouse, /* You can read a mouse */
+ write_mouse, /* This won't do a lot */
+ NULL, /* No readdir - not a directory */
+ poll_mouse, /* Poll */
+ NULL, /* No ioctl calls */
+ NULL, /* No mmap */
+ open_mouse, /* Called on open */
+ NULL, /* Flush - 2.2+ only */
+ close_mouse, /* Called on close */
+};
+ </programlisting>
+
+ <para>
+ There is nothing particularly special needed here. We provide functions
+ for all the relevant or required operations and little else. There is
+ nothing stopping us providing an ioctl function for this mouse. Indeed
+ if you have a configurable mouse it may be very appropriate to provide
+ configuration interfaces via ioctl calls.
+ </para>
+ <para>
+ The open and close routines need to manage enabling and disabling the
+ interrupts for the mouse as well as stopping the mouse being unloaded
+ when it is no longer required.
+ </para>
+
+ <programlisting>
+static int mouse_users = 0; /* User count */
+static int mouse_dx = 0; /* Position changes */
+static int mouse_dy = 0;
+static int mouse_event = 0; /* Mouse has moved */
+
+static int open_mouse(struct inode *inode, struct file *file)
+{
+ if(mouse_users++)
+ return 0;
+
+ MOD_INC_USE_COUNT;
+
+ if(request_irq(mouse_intr, OURMOUSE_IRQ, 0, "ourmouse", NULL))
+ {
+ mouse_users--;
+ MOD_DEC_USE_COUNT;
+ return -EBUSY;
+ }
+ mouse_dx = 0;
+ mouse_dy = 0;
+ mouse_event = 0;
+ mouse_buttons = 0;
+ return 0;
+}
+ </programlisting>
+ <para>
+ The open function has to do a small amount of housework. We keep a count
+ of the number of times the mouse is open. This is because we do not want
+ to request the interrupt multiple times. If the mouse has at least one
+ user then it is set up and we simply add to the user count and return
+ <returnvalue>0</returnvalue> for success.
+ </para>
+ <para>
+ Firstly we use <function>MOD_INC_USE_COUNT</function> to ensure that
+ while the mouse is open nobody will unload it and cause a nasty crash.
+ We must do this before we sleep - and grabbing the interrupt might sleep.
+ </para>
+ <para>
+ We grab the interrupt and thus start mouse interrupts. If the interrupt
+ has been borrowed by some other driver then <function>request_irq</function>
+ will fail and we will return an error. If we were capable of sharing an
+ interrupt line we would specify <constant>SA_SHIRQ</constant> instead of
+ <constant>zero</constant>. Provided that everyone claiming an interrupt
+ sets this flag, they get to share the line. <hardware>PCI</hardware> can
+ share interrupts, <hardware>ISA</hardware> normally however cannot.
+ </para>
+ <para>
+ We do the housekeeping. We make the current mouse position the starting
+ point for accumulated changes and declare that nothing has happened
+ since the mouse driver was opened.
+ </para>
+ <para>
+ The release function needs to unwind all these:
+ </para>
+ <programlisting>
+static int close_mouse(struct inode *inode, struct file *file)
+{
+ if(--mouse_users)
+ return 0;
+ free_irq(OURMOUSE_IRQ, NULL);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+ </programlisting>
+ <para>
+ We count off a user and provided that there are still other users need
+ take no further action. The last person closing the mouse causes us to
+ free up the interrupt. This stopps interrupts from the mouse from using
+ our CPU time, and lets us use <function>MOD_DEC_USE_COUNT</function> so
+ that the mouse can now be unloaded.
+ </para>
+ <para>
+ We can fill in the write handler at this point as the write function for
+ our mouse simply declines to allow writes:
+ </para>
+
+ <programlisting>
+static ssize_t write_mouse(struct file *file, const char *buffer, size_t
+ count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+ </programlisting>
+
+ <para>
+ This is pretty much self-explanatory. Whenever you write you get told
+ it was an invalid function.
+ </para>
+ <para>
+ To make the poll and read functions work we have to consider how we
+ handle the mouse interrupt.
+ </para>
+
+ <programlisting>
+static struct wait_queue *mouse_wait;
+static spinlock_t mouse_lock = SPIN_LOCK_UNLOCKED;
+
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char delta_x;
+ char delta_y;
+ unsigned char new_buttons;
+
+ delta_x = inb(OURMOUSE_BASE);
+ delta_y = inb(OURMOUSE_BASE+1);
+ new_buttons = inb(OURMOUSE_BASE+2);
+
+ if(delta_x || delta_y || new_buttons != mouse_buttons)
+ {
+ /* Something happened */
+
+ spin_lock(&mouse_lock);
+ mouse_event = 1;
+ mouse_dx += delta_x;
+ mouse_dy += delta_y;
+ mouse_buttons = new_buttons;
+ spin_unlock(&mouse_lock);
+
+ wake_up_interruptible(&mouse_wait);
+ }
+}
+ </programlisting>
+
+ <para>
+ The interrupt handler reads the mouse status. The next thing we do is
+ to check whether something has changed. If the mouse was smart it would
+ only interrupt us if something had changed, but let's assume our mouse
+ is stupid as most mice actually tend to be.
+ </para>
+ <para>
+ If the mouse has changed we need to update the status variables. What we
+ don't want is the mouse functions reading these variables to read them
+ during a change. We add a spinlock that protects these variables while we
+ play with them.
+ </para>
+ <para>
+ If a change has occured we also need to wake sleeping processes, so we
+ add a wakeup call and a <structname>wait_queue</structname> to use when
+ we wish to await a mouse event.
+ </para>
+ <para>
+ Now we have the wait queue we can implement the poll function for the
+ mouse relatively easily:
+ </para>
+
+ <programlisting>
+static unsigned int mouse_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &mouse_wait, wait);
+ if(mouse_event)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+ </programlisting>
+
+ <para>
+ This is fairly standard poll code. First we add the wait queue to the
+ list of queues we want to monitor for an event. Secondly we check if an
+ event has occured. We only have one kind of event - the
+ <varname>mouse_event</varname> flag tells us that something happened.
+ We know that this something can only be mouse data. We return the flags
+ indicating input and normal reading will succeed.
+ </para>
+ <para>
+ You may be wondering what happens if the function returns saying 'no
+ event yet'. In this case the wake up from the wait queue we added to
+ the poll table will cause the function to be called again. Eventually
+ we will be woken up and have an event ready. At this point the
+ <function>poll</function> call will exit back to the user.
+ </para>
+ <para>
+ After the poll completes the user will want to read the data. We now
+ need to think about how our <function>mouse_read</function> function
+ will work:
+ </para>
+ <programlisting>
+static ssize_t mouse_read(struct file *file, char *buffer,
+ size_t count, loff_t *pos)
+{
+ int dx, dy;
+ unsigned char button;
+ unsigned long flags;
+ int n;
+
+ if(count<3)
+ return -EINVAL;
+
+ /*
+ * Wait for an event
+ */
+
+ while(!mouse_event)
+ {
+ if(file->f_flags&O_NDELAY)
+ return -EAGAIN;
+ interruptible_sleep_on(&mouse_wait);
+ if(signal_pending(current))
+ return -ERESTARTSYS;
+ }
+ </programlisting>
+
+ <para>
+ We start by validating that the user is reading enough data. We could
+ handle partial reads if we wanted but it isn't terribly useful and the
+ mouse drivers don't bother to try.
+ </para>
+ <para>
+ Next we wait for an event to occur. The loop is fairly standard event
+ waiting in Linux. Having checked that the event has not yet occured, we
+ then check if an event is pending and if not we need to sleep.
+ </para>
+ <para>
+ A user process can set the <constant>O_NDELAY</constant> flag on a file
+ to indicate that it wishes to be told immediately if no event is
+ pending. We check this and give the appropriate error if so.
+ </para>
+ <para>
+ Next we sleep until the mouse or a signal awakens us. A signal will
+ awaken us as we have used <function>wakeup_interruptible</function>.
+ This is important as it means a user can kill processes waiting for
+ the mouse - clearly a desireable property. If we are interrupted we
+ exit the call and the kernel will then process signals and maybe
+ restart the call again - from the beginning.
+ </para>
+ <para>
+ This code contains a classic Linux bug. All will be revealed later in this
+ article as well as explanations for how to avoid it.
+ </para>
+ <programlisting>
+ /* Grab the event */
+
+ spinlock_irqsave(&mouse_lock, flags);
+
+ dx = mouse_dx;
+ dy = mouse_dy;
+ button = mouse_buttons;
+
+ if(dx<=-127)
+ dx=-127;
+ if(dx>=127)
+ dx=127;
+ if(dy<=-127)
+ dy=-127;
+ if(dy>=127)
+ dy=127;
+
+ mouse_dx -= dx;
+ mouse_dy -= dy;
+
+ if(mouse_dx == 0 && mouse_dy == 0)
+ mouse_event = 0;
+
+ spin_unlock_irqrestore(&mouse_lock, flags);
+ </programlisting>
+ <para>
+ This is the next stage. Having established that there is an event
+ going, we capture it. To be sure that the event is not being updated
+ as we capture it we also take the spinlock and thus prevent parallel
+ updates. Note here we use <function>spinlock_irqsave</function>. We
+ need to disable interrupts on the local processor otherwise bad things
+ will happen.
+ </para>
+ <para>
+ What will occur is that we take the spinlock. While we hold the lock
+ an interrupt will occur. At this point our interrupt handler will try
+ and take the spinlock. It will sit in a loop waiting for the read
+ routine to release the lock. However because we are sitting in a loop
+ in the interrupt handler we will never release the lock. The machine
+ hangs and the user gets upset.
+ </para>
+ <para>
+ By blocking the interrupt on this processor we ensure that the lock
+ holder will always give the lock back without deadlocking.
+ </para>
+ <para>
+ There is a little cleverness in the reporting mechanism too. We can
+ only report a move of 127 per read. We don't however want to lose
+ information by throwing away further movement. Instead we keep
+ returning as much information as possible. Each time we return a
+ report we remove the amount from the pending movement in
+ <varname>mouse_dx</varname> and <varname>mouse_dy</varname>. Eventually
+ when these counts hit zero we clear the <varname>mouse_event</varname>
+ flag as there is nothing else left to report.
+ </para>
+
+ <programlisting>
+ if(put_user(button|0x80, buffer))
+ return -EFAULT;
+ if(put_user((char)dx, buffer+1))
+ return -EFAULT;
+ if(put_user((char)dy, buffer+2))
+ return -EFAULT;
+
+ for(n=3; n < count; n++)
+ if(put_user(0x00, buffer+n))
+ return -EFAULT;
+
+ return count;
+}
+ </programlisting>
+
+ <para>
+ Finally we must put the results in the user supplied buffer. We cannot
+ do this while holding the lock as a write to user memory may sleep.
+ For example the user memory may be residing on disk at this instant.
+ Thus we did our computation beforehand and now copy the data. Each
+ <function>put_user call</function> is filling in one byte of the buffer.
+ If it returns an error we inform the program that it passed us an
+ invalid buffer and abort.
+ </para>
+ <para>
+ Having written the data we blank the rest of the buffer that was read
+ and report the read as being successful.
+ </para>
+ </chapter>
+
+ <chapter id="debugging">
+ <title>Debugging the mouse driver</title>
+
+ <para>
+ We now have an almost perfectly usable mouse driver. If you were to
+ actually try and use it however you would eventually find a couple of
+ problems with it. A few programs will also not work with as it does not
+ yet support asynchronous I/O.
+ </para>
+ <para>
+ First let us look at the bugs. The most obvious one isn't really a driver
+ bug but a failure to consider the consequences. Imagine you bumped the
+ mouse hard by accident and sent it skittering across the desk. The mouse
+ interrupt routine will add up all that movement and report it in steps of
+ 127 until it has reported all of it. Clearly there is a point beyond
+ which mouse movement isn't worth reporting. We need to add this as a
+ limit to the interrupt handler:
+ </para>
+
+ <programlisting>
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char delta_x;
+ char delta_y;
+ unsigned char new_buttons;
+
+ delta_x = inb(OURMOUSE_BASE);
+ delta_y = inb(OURMOUSE_BASE+1);
+ new_buttons = inb(OURMOUSE_BASE+2);
+
+ if(delta_x || delta_y || new_buttons != mouse_buttons)
+ {
+ /* Something happened */
+
+ spin_lock(&mouse_lock);
+ mouse_event = 1;
+ mouse_dx += delta_x;
+ mouse_dy += delta_y;
+
+ if(mouse_dx < -4096)
+ mouse_dx = -4096;
+ if(mouse_dx > 4096)
+ mouse_dx = 4096;
+
+ if(mouse_dy < -4096)
+ mouse_dy = -4096;
+ if(mouse_dy > 4096)
+ mouse_dy = 4096;
+
+ mouse_buttons = new_buttons;
+ spin_unlock(&mouse_lock);
+
+ wake_up_interruptible(&mouse_wait);
+ }
+}
+ </programlisting>
+
+ <para>
+ By adding these checks we limit the range of accumulated movement to
+ something sensible.
+ </para>
+ <para>
+ The second bug is a bit more subtle, and that is perhaps why this is
+ such a common mistake. Remember, I said the waiting loop for the read
+ handler had a bug in it. Think about what happens when we execute:
+ </para>
+
+ <programlisting>
+ while(!mouse_event)
+ {
+ </programlisting>
+
+ <para>
+ and an interrupt occurs at this point here. This causes a mouse movement
+ and wakes up the queue.
+ </para>
+
+ <programlisting>
+ interruptible_sleep_on(&mouse_wait);
+ </programlisting>
+
+ <para>
+ Now we sleep on the queue. We missed the wake up and the application
+ will not see an event until the next mouse event occurs. This will
+ lead to just the odd instance when a mouse button gets delayed. The
+ consequences to the user will probably be almost undetectable with a
+ mouse driver. With other drivers this bug could be a lot more severe.
+ </para>
+ <para>
+ There are two ways to solve this. The first is to disable interrupts
+ during the testing and the sleep. This works because when a task sleeps
+ it ceases to disable interrupts, and when it resumes it disables them
+ again. Our code thus becomes:
+ </para>
+
+ <programlisting>
+ save_flags(flags);
+ cli();
+
+ while(!mouse_event)
+ {
+ if(file->f_flags&O_NDELAY)
+ {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ interruptible_sleep_on(&mouse_wait);
+ if(signal_pending(current))
+ {
+ restore_flags(flags);
+ return -ERESTARTSYS;
+ }
+ }
+ restore_flags(flags);
+ </programlisting>
+
+ <para>
+ This is the sledgehammer approach. It works but it means we spend a
+ lot more time turning interrupts on and off. It also affects
+ interrupts globally and has bad properties on multiprocessor machines
+ where turning interrupts off globally is not a simple operation, but
+ instead involves kicking each processor, waiting for them to disable
+ interrupts and reply.
+ </para>
+ <para>
+ The real problem is the race between the event testing and the sleeping.
+ We can avoid that by using the scheduling functions more directly.
+ Indeed this is the way they generally should be used for an interrupt.
+ </para>
+
+ <programlisting>
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(&mouse_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while(!mouse_event)
+ {
+ if(file->f_flags&O_NDELAY)
+ {
+ remove_wait_queue(&mouse_wait, &wait);
+ current->state = TASK_RUNNING;
+ return -EWOULDBLOCK;
+ }
+ if(signal_pending(current))
+ {
+ remove_wait_queue(&mouse_wait, &wait);
+ current->state = TASK_RUNNING;
+ return -ERESTARTSYS;
+ }
+ schedule();
+ current->state = TASK_INTERRUPTIBLE;
+ }
+
+ remove_wait_wait(&mouse_wait, &wait);
+ current->state = TASK_RUNNING;
+ </programlisting>
+
+ <para>
+ At first sight this probably looks like deep magic. To understand how
+ this works you need to understand how scheduling and events work on
+ Linux. Having a good grasp of this is one of the keys to writing clean
+ efficient device drivers.
+ </para>
+ <para>
+ <function>add_wait_queue</function> does what its name suggests. It adds
+ an entry to the <varname>mouse_wait</varname> list. The entry in this
+ case is the entry for our current process (<varname>current</varname>
+ is the current task pointer).
+ </para>
+ <para>
+ So we start by adding an entry for ourself onto the
+ <varname>mouse_wait</varname> list. This does not put us to sleep
+ however. We are merely tagged onto the list.
+ </para>
+ <para>
+ Next we set our status to <constant>TASK_INTERRUPTIBLE</constant>. Again
+ this does not mean we are now asleep. This flag says what should happen
+ next time the process sleeps. <constant>TASK_INTERRUPTIBLE</constant> says
+ that the process should not be rescheduled. It will run from now until it
+ sleeps and then will need to be woken up.
+ </para>
+ <para>
+ The <function>wakeup_interruptible</function> call in the interrupt
+ handler can now be explained in more detail. This function is also very
+ simple. It goes along the list of processes on the queue it is given and
+ any that are marked as <constant>TASK_INTERRUPTIBLE</constant> it changes
+ to <constant>TASK_RUNNING</constant> and tells the kernel that new
+ processes are runnable.
+ </para>
+ <para>
+ Behind all the wrappers in the original code what is happening is this
+ </para>
+
+ <procedure>
+ <step>
+ <para>
+ We add ourself to the mouse wait queue
+ </para>
+ </step>
+ <step>
+ <para>
+ We mark ourself as sleeping
+ </para>
+ </step>
+ <step>
+ <para>
+ We ask the kernel to schedule tasks again
+ </para>
+ </step>
+ <step>
+ <para>
+ The kernel sees we are asleep and schedules someone else.
+ </para>
+ </step>
+ <step>
+ <para>
+ The mouse interrupt sets our state to <constant>TASK_RUNNING</constant>
+ and makes a note that the kernel should reschedule tasks
+ </para>
+ </step>
+ <step>
+ <para>
+ The kernel sees we are running again and continues our execution
+ </para>
+ </step>
+ </procedure>
+ <para>
+ This is why the apparent magic works. Because we mark ourself as
+ <constant>TASK_INTERRUPTIBLE</constant> and as we add ourselves
+ to the queue before we check if there are events pending, the race
+ condition is removed.
+ </para>
+ <para>
+ Now if an interrupt occurs after we check the queue status and before
+ we call the <function>schedule</function> function in order to sleep,
+ things work out. Instead of missing an event, we are set back to
+ <constant>TASK_RUNNING</constant> by the mouse interrupt. We still call
+ <function>schedule</function> but it will continue running our task.
+ We go back around the loop and this time there may be an event.
+ </para>
+ <para>
+ There will not always be an event. Thus we set ourselves back to
+ <constant>TASK_INTERRUPTIBLE</constant> before resuming the loop.
+ Another process doing a read may already have cleared the event flag,
+ and if so we will need to go back to sleep again. Eventually we will
+ get our event and escape.
+ </para>
+ <para>
+ Finally when we exit the loop we remove ourselves from the
+ <varname>mouse_wait</varname> queue as we are no longer interested
+ in mouse events, and we set ourself back to
+ <constant>TASK_RUNNABLE</constant> as we do not wish to go to sleep
+ again just yet.
+ </para>
+ <note>
+ <title>Note</title>
+ <para>
+ This isn't an easy topic. Don't be afraid to reread the description a
+ few times and also look at other device drivers to see how it works.
+ Finally if you can't grasp it just yet, you can use the code as
+ boilerplate to write other drivers and trust me instead.
+ </para>
+ </note>
+ </chapter>
+
+ <chapter id="asyncio">
+ <title>Asynchronous I/O</title>
+ <para>
+ This leaves the missing feature - Asynchronous I/O. Normally UNIX
+ programs use the <function>poll</function> call (or its variant form
+ <function>select</function>) to wait for an event to occur on one of
+ multiple input or output devices. This model works well for most tasks
+ but because <function>poll</function> and <function>select</function>
+ wait for an event isn't suitable for tasks that are also continually
+ doing computation work. Such programs really want the kernel to kick
+ them when something happens rather than watch for events.
+ </para>
+ <para>
+ Poll is akin to having a row of lights in front of you. You can see at a
+ glance which ones if any are lit. You cannot however get anything useful
+ done while watching them. Asynchronous I/O uses signals which work more
+ like a door bell. Instead of you watching, it tells you that something
+ is up.
+ </para>
+ <para>
+ Asynchronous I/O sends the signal SIGIO to a user process when the I/O
+ events occur. In this case that means when people move the mouse. The
+ SIGIO signal causes the user process to jump to its signal handler and
+ execute code in that handler before returning to whatever was going on
+ previously. It is the application equivalent of an interrupt handler.
+ </para>
+ <para>
+ Most of the code needed for this operation is common to all its users.
+ The kernel provides a simple set of functions for managing asynchronous
+ I/O.
+ </para>
+ <para>
+ Our first job is to allow users to set asynchronous I/O on file handles.
+ To do that we need to add a new function to the file operations table for
+ our mouse:
+ </para>
+
+ <programlisting>
+struct file_operations our_mouse_fops = {
+ NULL, /* Mice don't seek */
+ read_mouse, /* You can read a mouse */
+ write_mouse, /* This won't do a lot */
+ NULL, /* No readdir - not a directory */
+ poll_mouse, /* Poll */
+ NULL, /* No ioctl calls */
+ NULL, /* No mmap */
+ open_mouse, /* Called on open */
+ NULL, /* Flush */
+ close_mouse, /* Called on close */
+ NULL, /* No fsync on a mouse */
+ fasync_mouse, /* Asynchronous I/O */
+};
+ </programlisting>
+
+ <para>
+ Once we have installed this entry the kernel knows we support
+ asynchronous I/O and will allow all the relevant operations on the
+ device. Whenever a user adds or removes asynchronous I/O notification
+ on a file handle it calls our <function>fasync_mouse</function> routine
+ we just added. This routine uses the helper functions to keep the queue
+ of handles up to date:
+ </para>
+
+ <programlisting>
+static struct fasync_struct *mouse_fasync = NULL;
+
+static int fasync_mouse(int fd, struct file *filp, int on)
+{
+ int retval = fasync_helper(fd, filp, on, &mouse_fasync);
+
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+ </programlisting>
+
+ <para>
+ The fasync helper adds and deletes entries by managing the supplied
+ list. We also need to remove entries from this list when the file is
+ closed. This requires we add one line to our close function:
+ </para>
+
+ <programlisting>
+static int close_mouse(struct inode *inode, struct file *file)
+{
+ fasync_mouse(-1, file, 0)
+ if(--mouse_users)
+ return 0;
+ free_irq(OURMOUSE_IRQ, NULL);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+ </programlisting>
+
+ <para>
+ When we close the file we now call our own fasync handler as if the
+ user had requested that this file cease to be used for asynchronous
+ I/O. This rather neatly cleans up any loose ends. We certainly don't
+ wait to deliver a signal for a file that no longer exists.
+ </para>
+ <para>
+ At this point the mouse driver supports all the asynchronous I/O
+ operations, and applications using them will not error. They won't
+ however work yet. We need to actually send the signals. Again the
+ kernel provides a function for handling this.
+ </para>
+ <para>
+ We update our interrupt handler a little:
+ </para>
+
+ <programlisting>
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char delta_x;
+ char delta_y;
+ unsigned char new_buttons;
+
+ delta_x = inb(OURMOUSE_BASE);
+ delta_y = inb(OURMOUSE_BASE+1);
+ new_buttons = inb(OURMOUSE_BASE+2);
+
+ if(delta_x || delta_y || new_buttons != mouse_buttons)
+ {
+ /* Something happened */
+
+ spin_lock(&mouse_lock);
+ mouse_event = 1;
+ mouse_dx += delta_x;
+ mouse_dy += delta_y;
+
+ if(mouse_dx < -4096)
+ mouse_dx = -4096;
+ if(mouse_dx > 4096)
+ mouse_dx = 4096;
+
+ if(mouse_dy < -4096)
+ mouse_dy = -4096;
+ if(mouse_dy > 4096)
+ mouse_dy = 4096;
+
+ mouse_buttons = new_buttons;
+ spin_unlock(&mouse_lock);
+
+ /* Now we do asynchronous I/O */
+ kill_fasync(&mouse_fasync, SIGIO);
+
+ wake_up_interruptible(&mouse_wait);
+ }
+}
+ </programlisting>
+
+ <para>
+ The new code simply calls the <function>kill_fasync</function> routine
+ provided by the kernel if the queue is non-empty. This sends the
+ required signal (SIGIO in this case) to the process each file handle
+ says should be informed about the exciting new mouse movement that
+ just happened.
+ </para>
+ <para>
+ With this in place and the bugs in the original version fixed, you now
+ have a fully functional mouse driver using the bus mouse protocol. It
+ will work with the <application>X window system</application>, will work
+ with <application>GPM</application> and should work with every other
+ application you need. <application>Doom</application> is of course the
+ ideal way to test your new mouse driver is functioning properly. Be sure
+ to test it thoroughly.
+ </para>
+ </chapter>
+</book>
+
--- /dev/null
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="ViaAudioGuide">
+ <bookinfo>
+ <title>Via 686 Audio Driver for Linux</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Jeff</firstname>
+ <surname>Garzik</surname>
+ <affiliation>
+ <address>
+ <email>jgarzik@mandrakesoft.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Jeff Garzik</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ The Via VT82C686A and VT82C686A "super southbridge" chips contain
+ AC97-compatible audio logic which features dual full-duplex 16-bit stereo
+ PCM sound channels, plus a third PCM channel intended for use
+ in hardware-assisted FM synthesis.
+ </para>
+ <para>
+ The current Linux kernel audio driver for this family of chips
+ supports audio playback, but recording and hardware-assisted
+ FM support features are not yet available.
+ </para>
+ <para>
+ This driver supports any Linux kernel version after 2.3.50.
+ </para>
+ <para>
+ Please send bug reports to the mailing list <email>linux-via@gtf.org</email>.
+ To subscribe, e-mail <email>majordomo@gtf.org</email> with
+ </para>
+ <programlisting>
+ subscribe linux-via
+ </programlisting>
+ <para>
+ in the body of the message.
+ </para>
+ </chapter>
+
+ <chapter id="install">
+ <title>Driver Installation</title>
+ <para>
+ To use this audio driver, select the
+ CONFIG_SOUND_VIA82CXXX option in the section Sound during kernel configuration.
+ Follow the usual kernel procedures for rebuilding the kernel,
+ or building and installing driver modules.
+ </para>
+ <para>
+ To make this driver the default audio driver, you can add the
+ following to your /etc/conf.modules file:
+ </para>
+ <programlisting>
+ alias sound via82cxxx_audio
+ </programlisting>
+ <para>
+ Note that soundcore and ac97_codec support modules
+ are also required for working audio, in addition to
+ the via82cxxx_audio module itself.
+ </para>
+ </chapter>
+
+ <chapter id="reportbug">
+ <title>Submitting a bug report</title>
+ <sect1 id="bugrepdesc"><title>Description of problem</title>
+ <para>
+ Describe the application you were using to play/record sound, and how
+ to reproduce the problem.
+ </para>
+ </sect1>
+ <sect1 id="bugrepdiag"><title>Diagnostic output</title>
+ <para>
+ Obtain the via-audio-diag diagnostics program from
+ http://gtf.org/garzik/drivers/via82cxxx/ and provide a dump of the
+ audio chip's registers while the problem is occurring. Sample command line:
+ </para>
+ <programlisting>
+ ./via-audio-diag -aps > diag-output.txt
+ </programlisting>
+ </sect1>
+ <sect1 id="bugrepdebug"><title>Driver debug output</title>
+ <para>
+ Define <constant>VIA_DEBUG</constant> at the beginning of the driver, then capture and email
+ the kernel log output. This can be viewed in the system kernel log (if
+ enabled), or via the dmesg program. Sample command line:
+ </para>
+ <programlisting>
+ dmesg > /tmp/dmesg-output.txt
+ </programlisting>
+ </sect1>
+ <sect1 id="bugrepprintk"><title>Bigger kernel message buffer</title>
+ <para>
+ If you wish to increase the size of the buffer displayed by dmesg, then
+ change the <constant>LOG_BUF_LEN</constant> macro at the top of linux/kernel/printk.c, recompile
+ your kernel, and pass the <constant>LOG_BUF_LEN</constant> value to dmesg. Sample command line with
+ <constant>LOG_BUF_LEN</constant> == 32768:
+ </para>
+ <programlisting>
+ dmesg -s 32768 > /tmp/dmesg-output.txt
+ </programlisting>
+ </sect1>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry><term>Recording support</term>
+ <listitem>
+ <para>
+ Recording support is currently missing.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>MMAP support</term>
+ <listitem>
+ <para>
+ MMAP support is currently missing. Make sure to
+ test with Quake.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>AC97 codec timeout during init</term>
+ <listitem>
+ <para>
+ A warning message "via82cxxx: timeout while reading AC97
+ codec" is printed during driver initialization. This
+ message can safely be ignored.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>Low volume</term>
+ <listitem>
+ <para>
+ Volume too low on many systems. Workaround: use mixer program
+ such as xmixer to increase volume.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>RealPlayer trouble</term>
+ <listitem>
+ <para>
+ RealPlayer output very scratchy. Workaround: use esd, and
+ configure RealPlayer to output to esd.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>Broken apps</term>
+ <listitem>
+ <para>
+ Applications which attempt to open the sound device in read/write
+ mode (O_RDWR) will fail. This is incorrect OSS behavior, but since
+ this driver will eventually support recording as well as playback,
+ we will be able to (in the future) support even broken programs which
+ unconditionally use O_RDWR.
+ </para>
+ </listitem></varlistentry>
+
+ </variablelist>
+
+ </para>
+ </chapter>
+
+ <chapter id="thanks">
+ <title>Thanks</title>
+ <para>
+ Via for providing e-mail support, specs, and NDA'd source code.
+ </para>
+ <para>
+ MandrakeSoft for providing hacking time.
+ </para>
+ <para>
+ AC97 mixer interface fixes and debugging by Ron Cemer <email>roncemer@gte.net</email>.
+ </para>
+ </chapter>
+
+ <chapter id="notes">
+ <title>Random Notes</title>
+ <para>
+ Two /proc pseudo-files provide diagnostic information. This is generally
+ not useful to most users. Power users can disable VIA_PROC_FS macro in the
+ driver source code, and remove the /proc support code. In any case, once
+ version 2.0.0 is released, the /proc support code will be disabled by
+ default. Available /proc pseudo-files:
+ </para>
+ <programlisting>
+ /proc/driver/via/0/info
+ /proc/driver/via/0/ac97
+ </programlisting>
+ <para>
+ This driver by default supports all PCI audio devices which report
+ a vendor id of 0x1106, and a device id of 0x3058. Subsystem vendor
+ and device ids are not examined.
+ </para>
+ <para>
+ Only supports a single sound chip, as this is a motherboard chipset.
+ Some architecture remains for multiple cards, feel free to submit
+ a patch to clean some of that up.
+ </para>
+ <para>
+ No consideration for SMP, this chipset is not known to be found on
+ any SMP motherboards. However, spin_locks must be used anyway in order
+ to handle interrupts correctly.
+ </para>
+ <para>
+ GNU indent formatting options: -kr -i8 -pcs
+ </para>
+ <para>
+ Via has graciously donated e-mail support and source code to help further
+ the development of this driver. Their assistance has been invaluable
+ in the design and coding of the next major version of this driver.
+ </para>
+ <para>
+ The Via audio chip apparently provides a second PCM scatter-gather
+ DMA channel just for FM data, but does not have a full hardware MIDI
+ processor. I haven't put much thought towards a solution here, but it
+ might involve using SoftOSS midi wave table, or simply disabling MIDI
+ support altogether and using the FM PCM channel as a second (input? output?)
+ </para>
+ </chapter>
+
+ <chapter id="changelog">
+ <title>Driver ChangeLog</title>
+
+<sect1 id="version118"><title>
+Version 1.1.8
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Clean up interrupt handler output. Fixes the following kernel error message:
+ </para>
+ <programlisting>
+ unhandled interrupt ...
+ </programlisting>
+ </listitem>
+
+ <listitem>
+ <para>
+ Convert documentation to DocBook, so that PDF, HTML and PostScript (.ps) output is readily
+ available.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version117"><title>
+Version 1.1.7
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Fix module unload bug where mixer device left registered
+ after driver exit
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version116"><title>
+Version 1.1.6
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Rewrite via_set_rate to mimic ALSA basic AC97 rate setting
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Remove much dead code
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Complete spin_lock_irqsave -> spin_lock_irq conversion in via_dsp_ioctl
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Fix build problem in via_dsp_ioctl
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Optimize included headers to eliminate headers found in linux/drivers/sound
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version115"><title>
+Version 1.1.5
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Disable some overly-verbose debugging code
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Remove unnecessary sound locks
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Fix some ioctls for better time resolution
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Begin spin_lock_irqsave -> spin_lock_irq conversion in via_dsp_ioctl
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version114"><title>
+Version 1.1.4
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Completed rewrite of driver. Eliminated SoundBlaster compatibility
+ completely, and now uses the much-faster scatter-gather DMA engine.
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+ </chapter>
+
+ <chapter id="intfunctions">
+ <title>Internal Functions</title>
+!Idrivers/sound/via82cxxx_audio.c
+ </chapter>
+
+</book>
If you have any patches, questions or suggestions regarding this BFS
implementation please contact the author:
-Tigran A. Aivazian <tigran@ocston.org>.
+Tigran A. Aivazian <tigran@veritas.com>
The latest version can be found at fdutils homepage:
http://fdutils.linux.lu
-The fdutils-5.3 release can be found at:
- http://fdutils.linux.lu/fdutils-5.3.src.tar.gz
- http://www.tux.org/pub/knaff/fdutils/fdutils-5.3.src.tar.gz
- ftp://tsx-11.mit.edu/pub/linux/sources/sbin/fdutils-5.3.src.tar.gz
- ftp://metalab.unc.edu/pub/Linux/utils/disk-management/fdutils-5.3.src.tar.gz
+The fdutils-5.4 release can be found at:
+ http://fdutils.linux.lu/fdutils-5.4.src.tar.gz
+ http://www.tux.org/pub/knaff/fdutils/fdutils-5.4.src.tar.gz
+ ftp://metalab.unc.edu/pub/Linux/utils/disk-management/fdutils-5.4.src.tar.gz
Reporting problems about the floppy driver
==========================================
0x22 all scsi/sg.h
'1' 00-1F <linux/timepps.h> PPS kit from Ulrich Windl
<ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>
+'6' 00-10 <asm-i386/processor.h> Intel P6 microcode update driver
+ <tigran@veritas.com>
'8' all SNP8023 advanced NIC card
<mailto:mcr@solidum.com>
'A' 00-1F linux/apm_bios.h
'V' all linux/vt.h
'W' 00-1F linux/watchdog.h conflict!
'W' 00-1F linux/wanrouter.h conflict!
+'X' all linux/xfs_fs.h
'Y' all linux/cyclades.h
'a' all ATM on linux
<http://lrcwww.epfl.ch/linux-atm/magic.html>
If you want to see man pages instead, you can do this:
$ cd linux
-$ scripts/kernel-doc -man $(find -name '*.c') | split-man.pl /tmp/man
+$ scripts/kernel-doc -man $(find -name '*.c' '*.h') | split-man.pl /tmp/man
Here is split-man.pl:
Take a look around the source tree for examples.
+
+How to make new SGML template files
+-----------------------------------
+
+SGML template files (*.tmpl) are like normal SGML files, except that
+they can contain escape sequences where extracted documentation should
+be inserted.
+
+!E<filename> is replaced by the documentation, in <filename>, for
+functions that are exported using EXPORT_SYMBOL: the function list is
+collected from files listed in Documentation/DocBook/Makefile.
+
+!I<filename> is replaced by the documentation for functions that are
+_not_ exported using EXPORT_SYMBOL.
+
+!F<filename> <function [functions...]> is replaced by the
+documentation, in <filename>, for the functions listed.
+
+
Tim.
*/ <twaugh@redhat.com>
-June 1999 Kernel Parameters v2.2.9
+June 2000 Kernel Parameters v2.4.0
~~~~~~~~~~~~~~~~~
-The following is a consolidated list of the kernel parameters as defined
-in the file init/main.c and sorted into English Dictionary order (defined
+The following is a consolidated list of the kernel parameters as implemented
+by the __setup() macro and sorted into English Dictionary order (defined
as ignoring all punctuation and sorting digits before letters in a case
insensitive manner), and with descriptions where known.
restrictions on the kernel for the said kernel parameter to be valid. The
restrictions referred to are that the relevant option is valid if:
+ ACPI ACPI support is enabled.
APIC APIC support is enabled.
APM Advanced Power Management support is enabled.
AX25 Appropriate AX.25 support is enabled.
CD Appropriate CD support is enabled.
+ DEVFS devfs support is enabled.
+ DRM Direct Rendering Management support is enabled.
EIDE EIDE/ATAPI support is enabled.
FB The frame buffer device is enabled.
HW Appropriate hardware is enabled.
JOY Appropriate joystick support is enabled.
LP Printer support is enabled.
LOOP Loopback device support is enabled.
+ M68k M68k architecture is enabled.
MCA MCA bus support is enabled.
MDA MDA console support is enabled.
MOUSE Appropriate mouse support is enabled.
53c7xx= [HW,SCSI] Amiga SCSI controllers.
+ acpi= [HW,ACPI] Advanced Configuration and Power Interface
+
+ ad1816= [HW,SOUND]
+
+ ad1848= [HW,SOUND]
+
adb_buttons= [HW,MOUSE]
+ adlib= [HW,SOUND]
+
advansys= [HW,SCSI]
+ aedsp16= [HW,SOUND]
+
aha152x= [HW,SCSI]
aha1542= [HW,SCSI]
apm= [APM] Advanced Power Management.
+ applicom= [HW]
+
arcrimi= [HW,NET]
- ataflop= [HW, M68k]
+ ataflop= [HW,M68k]
- atamouse= [HW,MOUSE] Atari Mouse.
+ atarimouse= [HW,MOUSE] Atari Mouse.
atascsi= [HW,SCSI] Atari SCSI.
+ awe= [HW,SOUND]
+
aztcd= [HW,CD] Aztec CD driver.
+ baycom_epp= [HW,AX25]
+
baycom_par= [HW,AX25] BayCom Parallel Port AX.25 Modem.
baycom_ser_fdx= [HW,AX25] BayCom Serial Port AX.25 Modem in Full
cdu31a= [HW,CD]
+ chandev= [HW,NET]
+
cm206= [HW,CD]
com20020= [HW,NET]
com90xx= [HW,NET]
+ condev= [HW]
+
console= [KNL] output console + comm spec (speed, control,
parity).
+ cpia_pp= [HW,PPT]
+
+ cs4232= [HW,SOUND]
+
+ cs89x0_dma= [HW,NET]
+
+ ctc= [HW,NET]
+
cyclades= [HW,SERIAL] Cyclades multi-serial port adapter.
+
+ dasd= [HW,NET]
debug [KNL] Enable kernel debugging (events log level).
decnet= [HW,NET]
+ devfs= [DEVFS]
+
digi= [HW,SERIAL] io parameters + enable/disable command.
digiepca= [HW,SERIAL]
edb= [HW,PS2]
+ eicon= [HW,ISDN]
+
+ es1370= [HW,SOUND]
+
+ es1371= [HW,SOUND]
+
ether= [HW,NET] Ethernet cards parameters (iomem, irq,
dev_name).
gscd= [HW,CD]
+ gus= [HW,SOUND]
+
gvp11= [HW,SCSI]
hd= [EIDE] (E)IDE hard drive subsystem geometry
hfmodem= [HW,AX25]
- HiSax= [HW,ISDN]
-
hisax= [HW,ISDN]
in2000= [HW,SCSI]
idebus= [HW] (E)IDE subsystem : VLB/PCI bus speed.
- in2000= [HW,SCSI]
-
- init= [KNL] Default init level.
-
ip= [PNP]
isp16= [HW,CD]
- js_14= [HW,JOY]
+ iucv= [HW,NET]
js_am= [HW,JOY]
js_db9_3= [HW,JOY]
+ js_l4= [HW,JOY]
+
+ js_pci= [HW,JOY,PCI]
+
js_tg= [HW,JOY]
js_tg_2= [HW,JOY]
load_ramdisk= [RAM] List of ramdisks to load from floppy.
+ logi_busmouse= [HW, MOUSE]
+
lp=0 [LP] Specify parallel ports to use, e.g,
lp=port[,port...] lp=none,parport0 (lp0 not configured, lp1 uses
lp=reset first parallel port). 'lp=0' disables the
mac5380= [HW,SCSI]
+ mac53c9x= [HW,SCSI]
+
+ mad16= [HW,SOUND]
+
+ maui= [HW,SOUND]
+
max_loop=[0-255] [LOOP] Set the maximum number of loopback devices
that can be mounted.
md= [HW] RAID subsystems devices and level.
+ mdisk= [HW]
+
mdacon= [MDA]
+ megaraid= [HW,SCSI]
+
mem= [KNL] force use XX Mb of memory when the kernel is not
able to see the whole system memory or for test.
+ memfrac= [KNL]
+
+ mpu401= [HW,SOUND]
+
msmouse= [HW,MOUSE] Microsoft Mouse.
ncr5380= [HW,SCSI]
ncr53c8xx= [HW,SCSI]
+ netdev= [NET]
+
nfsaddrs= [NFS]
nfsroot= [NFS] nfs root filesystem for disk-less boxes.
- nmi_watchdog= [KNL, BUGS=ix86] debugging features for SMP kernels.
+ nmi_watchdog= [KNL,BUGS=ix86] debugging features for SMP kernels.
no387 [BUGS=ix86] Tells the kernel to use the 387 maths
emulation library even if a 387 maths coprocessor
is present.
+ noalign [KNL,ARM]
+
noapic [SMP,APIC] Tells the kernel not to make use of any
APIC that may be present on the system.
noasync [HW, M68K] Disables async and sync negotiation for
all devices.
+ nocache [ARM]
+
nodisconnect [HW,SCSI, M68K] Disables SCSI disconnects.
- no-halt [BUGS=ix86]
+ nohlt [BUGS=ARM]
+
+ no-hlt [BUGS=ix86]
noinitrd [RAM] Tells the kernel not to load any configured
initial RAM disk.
+ nointroute [IA-64]
+
no-scroll [VGA]
nosmp [SMP] Tells an SMP kernel to act as a UP kernel.
nosync [HW, M68K] Disables sync negotiation for all devices.
+ nowb [ARM]
+
+ opl3= [HW,SOUND]
+
+ opl3sa= [HW,SOUND]
+
+ opl3sa2= [HW,SOUND]
+
optcd= [HW,CD]
panic= [KNL] kernel behaviour on panic.
order they are specified on the command
line, starting with parport0.
+ pas2= [HW,SOUND]
+
pas16= [HW,SCSI]
pcbit= [HW,ISDN]
prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk
before loading.
+ pss= [HW,SOUND]
+
pt. [PARIDE]
+ quiet= [KNL] Disable log messages.
+
ramdisk= [RAM] Sizes of RAM disks in kilobytes [deprecated].
+ ramdisk_blocksize=
+ [RAM]
+
ramdisk_size= [RAM] New name for the ramdisk parameter.
ramdisk_start= [RAM] Starting block of RAM disk image (so you can
S [KNL] run init in single mode.
+ sb= [HW,SOUND]
+
sbpcd= [HW,CD] Soundblaster CD adapter.
scsi_logging= [SCSI]
+ scsihosts= [SCSI]
+
+ sg_def_reserved_size=
+ [SCSI]
+
+ sgalaxy= [HW,SOUND]
+
+ sim710= [SCSI,HW]
+
sjcd= [HW,CD]
+ smart2= [HW]
+
+ sonicvibes= [HW,SOUND]
+
sonycd535= [HW,CD]
sound= [SOUND]
specialix= [HW,SERIAL] Specialix multi-serial port adapter.
- st= [HW] SCSI tape parameters (buffers, etc.).
+ sscape= [HW,SOUND]
+
+ st= [HW,SCSI] SCSI tape parameters (buffers, etc.).
st0x= [HW,SCSI]
t128= [HW,SCSI]
+ tdfx= [HW,DRM]
+
tmc8xx= [HW,SCSI]
tmscsim= [HW,SCSI]
tp720= [HW,PS2]
+ trix= [HW,SOUND]
+
u14-34f= [HW,SCSI]
+ uart401= [HW,SOUND]
+
+ uart6850= [HW,SOUND]
+
+ usbfix [BUGS=IA-64]
+
video= [FB] frame buffer configuration.
vga= [KNL] on ix386, enable to choose a peculiar video mode
(use vga=ask for menu).
+ vmhalt= [KNL,S390]
+
+ vmpoff= [KNL,S390]
+
+ waveartist= [HW,SOUND]
+
wd33c93= [HW,SCSI]
wd7000= [HW,SCSI]
(C) 1998 James Banks
(C) 1999-2000 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
-TLAN driver for Linux, version 1.5
+For driver information/updates visit http://tlan.kernel.dk
+
+
+TLAN driver for Linux, version 1.8a
README
if a card which only supports 10Mbs is forced into 100Mbs
mode.)
- 5. If the driver is built into the kernel, you can use the 3rd
+ 5. You have to use speed=X duplex=Y together now. If you just
+ do "insmod tlan.o speed=100" the driver will do Auto-Neg.
+ To force a 10Mbps Half-Duplex link do "insmod tlan.o speed=10
+ duplex=1".
+
+ 6. If the driver is built into the kernel, you can use the 3rd
and 4th parameters to set aui and debug respectively. For
example:
-/* kernel-parameters are currently not supported. I will fix this asap. */
-
ether=0,0,0x1,0x7,eth0
This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
The bits in the third byte are assigned as follows:
0x01 = aui
- 0x04 = use half duplex
- 0x08 = use full duplex
- 0x10 = use 10BaseT
- 0x20 = use 100BaseTx
+ 0x02 = use half duplex
+ 0x04 = use full duplex
+ 0x08 = use 10BaseT
+ 0x10 = use 100BaseTx
+ You also need to set both speed and duplex settings when forcing
+ speeds with kernel-parameters.
+ ether=0,0,0x12,0,eth0 will force link to 100Mbps Half-Duplex.
III. Things to try if you have problems.
1. Make sure your card's PCI id is among those listed in
There is also a tlan mailing list which you can join by sending "subscribe tlan"
in the body of an email to majordomo@vuser.vu.union.edu.
-
+There is also a tlan website at http://tlan.kernel.dk
Linux Serial Console
+To use a serial port as console you need to compile the support into your
+kernel - by default it is not compiled in. For PC style serial ports
+it's the config option next to "Standard/generic (dumb) serial support".
+You must compile serial support into the kernel and not as a module.
+
It is possible to specify multiple devices for console output. You can
define a new kernel command line option to select which device(s) to
use for console output.
append = "console=ttyS1,9600"
-4. Init and /etc/ioctl.save
+4. Make sure a getty runs on the serial port so that you can login to
+ it once the system is done booting. This is done by adding a line
+ like this to /etc/inittab (exact syntax depends on your getty):
+
+ S1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
+
+5. Init and /etc/ioctl.save
Sysvinit remembers its stty settings in a file in /etc, called
`/etc/ioctl.save'. REMOVE THIS FILE before using the serial
console for the first time, because otherwise init will probably
set the baudrate to 38400 (baudrate of the virtual console).
-5. /dev/console and X
+6. /dev/console and X
Programs that want to do something with the virtual console usually
open /dev/console. If you have created the new /dev/console device,
and your console is NOT the virtual console some programs will fail.
Xfree86, svgalib, gpm, SVGATextMode
- I have binary patched the above mentioned programs to use "tty0"
- instead of "console". This will be reported to the maintainers of
- said programs.
+ It should be fixed in modern versions of these programs though.
Note that if you boot without a console= option (or with
console=/dev/tty0), /dev/console is the same as /dev/tty0. In that
case everything will still work.
-6. Thanks
+7. Thanks
Thanks to Geert Uytterhoeven <geert@linux-m68k.org>
for porting the patches from 2.1.4x to 2.1.6x for taking care of
the integration of these patches into m68k, ppc and alpha.
-Miquel van Smoorenburg <miquels@cistron.nl>, 21-Mar-1998
+Miquel van Smoorenburg <miquels@cistron.nl>, 11-Jun-2000
------------------------------
The most recent version of this driver will hopefully always be available at
- http://people.redhat.com/zab/maestro/
+ http://www.zabbo.net/maestro/
I will try and maintain the most recent stable version of the driver
in both the stable and development kernel lines.
On SPARC - You press 'ALT-STOP-<command key>', I believe.
+On the serial console (PC style standard serial ports only) -
+ You send a BREAK, then within 5 seconds a command key. Sending
+ BREAK twice is interpreted as a normal BREAK.
+
On other - If you know of the key combos for other architectures, please
let me know so I can add them to this section.
Contact Information
People keep asking about the WDT watchdog timer hardware: The phone contacts
-for ICS Advent are:
+for Industrial Computer Source are:
-US: 619 677 0877 (sales) 0895 (fax)
-UK: 01243 533900
-France (1) 69.18.74.30
+Industrial Computer Source
+http://www.indcompsrc.com
+ICS Advent, San Diego
+6260 Sequence Dr.
+San Diego, CA 92121-4371
+Phone (858) 677-0877
+FAX: (858) 677-0895
+>
+ICS Advent Europe, UK
+Oving Road
+Chichester,
+West Sussex,
+PO19 4ET, UK
+Phone: 00.44.1243.533900
-ICS Advent
-9950 Barnes Canyon Road
-San Diego, CA
-
-http://www.icsadvent.com
and please mention Linux when enquiring.
BFS FILE SYSTEM
P: Tigran A. Aivazian
-M: tigran@ocston.org
+M: tigran@veritas.com
L: linux-kernel@vger.rutgers.edu
W: http://www.ocston.org/~tigran/patches/bfs
S: Maintained
W: http://www.fi.muni.cz/~kas/cosa/
S: Maintained
+CPUID/MSR DRIVER
+P: H. Peter Anvin
+M: hpa@zytor.com
+S: Maintained
+
CREDITS FILE
P: John A. Martin
M: jam@acm.org
DIGI RIGHTSWITCH NETWORK DRIVER
P: Rick Richardson
-M: rick@dgii.com
+M: rick@remotepoint.com
L: linux-net@vger.rutgers.edu
W: http://www.dgii.com/linux/
S: Maintained
+DISK GEOMETRY AND PARTITION HANDLING
+P: Andries Brouwer
+M: aeb@veritas.com
+W: http://www.win.tue.nl/~aeb/linux/Large-Disk.html
+W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html
+W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+S: Maintained
+
DISKQUOTA:
P: Marco van Wieringen
M: mvw@planets.elm.net
IBM ServeRAID RAID DRIVER
P: Keith Mitchell
M: ipslinux@us.ibm.com
-W: http://www.developer.ibm.com/welcome/netfinity/serveraid_beta.html
+W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html
S: Supported
IDE DRIVER [GENERAL]
INTEL P6 MICROCODE UPDATE SUPPORT
P: Tigran Aivazian
-M: tigran@sco.com
+M: tigran@veritas.com
S: Maintained
IP MASQUERADING:
W: http://www.isdn4linux.de
S: Maintained
+ISDN SUBSYSTEM (Eicon active card driver)
+P: Armin Schindler
+M: mac@melware.de
+L: isdn4linux@listserv.isdn4linux.de
+W: http://www.melware.de
+S: Maintained
+
JOYSTICK DRIVER
P: Vojtech Pavlik
M: vojtech@suse.cz
MISCELLANEOUS MCA-SUPPORT
P: David Weinehall
-M: tao@acc.umu.se (personal)
+M: Project MCA Team <mcalinux@acc.umu.se>
+M: David Weinehall <tao@acc.umu.se>
W: http://www.acc.umu.se/~tao/
W: http://www.acc.umu.se/~mcalinux/
L: linux-kernel@vger.rutgers.edu
NETFILTER
P: Rusty Russell
-M: Rusty.Russell@rustcorp.com.au
+M: rusty@linuxcare.com
P: Marc Boucher
M: marc@mbsi.ca
W: http://www.samba.org/netfilter/
W: http://netfilter.kernelnotes.org
-W: http://antarctica.penguincomputing.com/~netfilter/
+W: http://netfilter.filewatcher.org
L: netfilter@lists.samba.org
S: Supported
NI5010 NETWORK DRIVER
P: Jan-Pascal van Best and Andreas Mohr
-M: jvbest@qv3pluto.leidenuniv.nl (Best)
-M: 100.30936@germany.net (Mohr)
+M: Jan-Pascal van Best <jvbest@qv3pluto.leidenuniv.nl>
+M: Andreas Mohr <100.30936@germany.net>
L: linux-net@vger.rutgers.edu
S: Maintained
P: Andrea Arcangeli
M: andrea@e-mind.com
L: linux-parport@torque.net
-W: http://www.cyberelk.demon.co.uk/parport.html
+W: http://people.redhat.com/twaugh/parport/
S: Maintained
PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
-P: Grant Guenther
-M: grant@torque.net
+P: Tim Waugh
+M: tim@cyberelk.demon.co.uk
L: linux-parport@torque.net
W: http://www.torque.net/linux-pp.html
S: Maintained
M: torben.mathiasen@compaq.com
M: tmm@image.dk
L: tlan@vuser.vu.union.edu
+L: linux-net@vger.rutgers.edu
+W: http://tlan.kernel.dk
S: Maintained
TOKEN-RING NETWORK DRIVER
CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
endif
-ifdef CONFIG_M686FX
+ifdef CONFIG_M686FXSR
CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
endif
CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi) -malign-functions=4 -fschedule-insns2 -mwide-multiply -fexpensive-optimizations
endif
+ifdef CONFIG_MCRUSOE
+CFLAGS += $(shell if $(CC) -march=i586 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i586"; fi)
+endif
+
+ifdef CONFIG_MWINCHIPC6
+CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
+endif
+
+ifdef CONFIG_MWINCHIP2
+CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
+endif
+
+ifdef CONFIG_MWINCHIP3D
+CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
+endif
+
HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib
#
# For a description of the syntax of this configuration file,
-# see the Configure script.
+# see Documentation/kbuild/config-language.txt.
#
mainmenu_name "Linux Kernel Configuration"
comment 'Processor type and features'
choice 'Processor family' \
"386 CONFIG_M386 \
- 486/Cx486 CONFIG_M486 \
+ 486 CONFIG_M486 \
586/K5/5x86/6x86/6x86MX CONFIG_M586 \
- Pentium/TSC CONFIG_M586TSC \
- PPro/Pentium-II CONFIG_M686 \
- Pentium-III CONFIG_M686FX \
+ Pentium/Pentium-MMX CONFIG_M586TSC \
+ Pentium-Pro/Celeron/Pentium-II CONFIG_M686 \
+ Pentium-III CONFIG_M686FXSR \
K6/K6-II/K6-III CONFIG_MK6 \
- Athlon CONFIG_MK7 \
- Crusoe CONFIG_MCRUSOE" PPro
+ Athlon/K7 CONFIG_MK7 \
+ Crusoe CONFIG_MCRUSOE \
+ Winchip-C6 CONFIG_MWINCHIPC6 \
+ Winchip-2 CONFIG_MWINCHIP2 \
+ Winchip-2A/3 CONFIG_MWINCHIP3D" Pentium-Pro
#
# Define implied options from the CPU selection here
#
define_bool CONFIG_X86_PGE y
define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
fi
-if [ "$CONFIG_M686FX" = "y" ]; then
+if [ "$CONFIG_M686FXSR" = "y" ]; then
define_int CONFIG_X86_L1_CACHE_BYTES 32
define_bool CONFIG_X86_TSC y
define_bool CONFIG_X86_GOOD_APIC y
define_bool CONFIG_X86_PGE y
define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
- define_bool CONFIG_X86_FX y
+ define_bool CONFIG_X86_FXSR y
+ define_bool CONFIG_X86_XMM y
+else
+ define_bool CONFIG_X86_FXSR n
fi
if [ "$CONFIG_MK6" = "y" ]; then
define_int CONFIG_X86_L1_CACHE_BYTES 32
define_int CONFIG_X86_L1_CACHE_BYTES 32
define_bool CONFIG_X86_TSC y
fi
+if [ "$CONFIG_MWINCHIPC6" = "y" ]; then
+ define_int CONFIG_X86_L1_CACHE_BYTES 32
+ define_bool CONFIG_X86_ALIGNMENT_16 y
+ define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+fi
+if [ "$CONFIG_MWINCHIP2" = "y" ]; then
+ define_int CONFIG_X86_L1_CACHE_BYTES 32
+ define_bool CONFIG_X86_ALIGNMENT_16 y
+ define_bool CONFIG_X86_TSC y
+ define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+fi
+if [ "$CONFIG_MWINCHIP3D" = "y" ]; then
+ define_int CONFIG_X86_L1_CACHE_BYTES 32
+ define_bool CONFIG_X86_ALIGNMENT_16 y
+ define_bool CONFIG_X86_TSC y
+ define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+ define_bool CONFIG_X86_USE_3DNOW y
+fi
tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR
define_bool CONFIG_X86_PAE y
fi
-if [ "$CONFIG_X86_FX" != "y" ]; then
+if [ "$CONFIG_X86_FXSR" != "y" ]; then
bool 'Math emulation' CONFIG_MATH_EMULATION
fi
bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
# CONFIG_M586 is not set
# CONFIG_M586TSC is not set
CONFIG_M686=y
-# CONFIG_M686FX is not set
+# CONFIG_M686FXSR is not set
# CONFIG_MK6 is not set
# CONFIG_MK7 is not set
# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
CONFIG_X86_CMPXCHG=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_PGE=y
CONFIG_X86_USE_PPRO_CHECKSUM=y
+# CONFIG_X86_FXSR is not set
# CONFIG_MICROCODE is not set
# CONFIG_X86_MSR is not set
# CONFIG_X86_CPUID is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
#
# Telephony Support
if (!(pmregmisc & ACPI_PIIX4_PMIOSE))
return -ENODEV;
- base = dev->resource[PCI_BRIDGE_RESOURCES].start & PCI_BASE_ADDRESS_IO_MASK;
+ base = pci_resource_start (dev, PCI_BRIDGE_RESOURCES);
if (!base)
return -ENODEV;
if (!base)
return -ENODEV;
}
- base &= PCI_BASE_ADDRESS_IO_MASK;
pci_read_config_byte(dev, 0x42, &irq);
{acpi_init_via},
};
-const static struct pci_device_id acpi_pci_tbl[] =
+static struct pci_device_id acpi_pci_tbl[] __initdata =
{
{0x8086, 0x7113, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_INTEL_PIIX4},
{0x1106, 0x3040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_586},
acpi_sleep_start = get_cmos_time();
acpi_enter_dx(ACPI_D3);
+ // disable interrupts globally while suspended
+ cli();
acpi_sleep_state = state;
facp = (struct acpi_facp*) acpi_facp.table;
// finished sleeping, update system time
acpi_update_clock();
acpi_enter_dx(ACPI_D0);
+ // reenable interrupts globally after resume
+ sti();
acpi_sleep_state = ACPI_S0;
return 0;
* Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
* is now the way life works).
* Fix thinko in suspend() (wrong return).
+ * Notify drivers on critical suspend.
+ * Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
+ * modified by sfr).
+ * Disable interrupts while we are suspended (Andy Henroid
+ * <andy_henroid@yahoo.com> fixed by sfr).
+ * Make power off work on SMP again (Tony Hoyle
+ * <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
*
* APM 1.1 Reference:
*
#endif
}
+static int send_event(apm_event_t event)
+{
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_CRITICAL_SUSPEND:
+ case APM_USER_SUSPEND:
+ /* map all suspends to ACPI D3 */
+ if (pm_send_all(PM_SUSPEND, (void *)3)) {
+ if (event == APM_CRITICAL_SUSPEND) {
+ printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armageddon\n" );
+ return 0;
+ }
+ if (apm_bios_info.version > 0x100)
+ apm_set_power_state(APM_STATE_REJECT);
+ return 0;
+ }
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ /* map all resumes to ACPI D0 */
+ (void) pm_send_all(PM_RESUME, (void *)0);
+ break;
+ }
+
+ return 1;
+}
+
static int suspend(void)
{
int err;
struct apm_user *as;
get_time_diff();
+ cli();
err = apm_set_power_state(APM_STATE_SUSPEND);
reinit_timer();
set_time();
if (err == APM_NO_ERROR)
err = APM_SUCCESS;
- if (err != APM_SUCCESS)
+ if (err != APM_SUCCESS) {
apm_error("suspend", err);
+ send_event(APM_NORMAL_RESUME);
+ sti();
+ queue_event(APM_NORMAL_RESUME, NULL);
+ }
for (as = user_list; as != NULL; as = as->next) {
as->suspend_wait = 0;
as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO);
return 0;
}
-static int send_event(apm_event_t event, struct apm_user *sender)
-{
- switch (event) {
- case APM_SYS_SUSPEND:
- case APM_CRITICAL_SUSPEND:
- case APM_USER_SUSPEND:
- /* map all suspends to ACPI D3 */
- if (pm_send_all(PM_SUSPEND, (void *)3)) {
- if (event == APM_CRITICAL_SUSPEND) {
- printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armagedon\n" );
- return 0;
- }
- if (apm_bios_info.version > 0x100)
- apm_set_power_state(APM_STATE_REJECT);
- return 0;
- }
- break;
- case APM_NORMAL_RESUME:
- case APM_CRITICAL_RESUME:
- /* map all resumes to ACPI D0 */
- (void) pm_send_all(PM_RESUME, (void *)0);
- break;
- }
-
- return 1;
-}
-
static void check_events(void)
{
apm_event_t event;
switch (event) {
case APM_SYS_STANDBY:
case APM_USER_STANDBY:
- if (send_event(event, NULL)) {
+ if (send_event(event)) {
queue_event(event, NULL);
if (standbys_pending <= 0)
standby();
if (ignore_bounce)
break;
#endif
- /*
- * If we are already processing a SUSPEND,
- * then further SUSPEND events from the BIOS
- * will be ignored. We also return here to
- * cope with the fact that the Thinkpads keep
- * sending a SUSPEND event until something else
- * happens!
- */
+ /*
+ * If we are already processing a SUSPEND,
+ * then further SUSPEND events from the BIOS
+ * will be ignored. We also return here to
+ * cope with the fact that the Thinkpads keep
+ * sending a SUSPEND event until something else
+ * happens!
+ */
if (waiting_for_resume)
- return;
- if (send_event(event, NULL)) {
+ return;
+ if (send_event(event)) {
queue_event(event, NULL);
waiting_for_resume = 1;
if (suspends_pending <= 0)
ignore_bounce = 1;
#endif
set_time();
- send_event(event, NULL);
+ send_event(event);
+ sti();
queue_event(event, NULL);
break;
case APM_CAPABILITY_CHANGE:
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
- send_event(event, NULL);
+ send_event(event);
+ queue_event(event, NULL);
break;
case APM_UPDATE_TIME:
break;
case APM_CRITICAL_SUSPEND:
- send_event(event, NULL); /* We can only hope it worked; critical suspend may not fail */
+ send_event(event);
+ /*
+ * We can only hope it worked - we are not allowed
+ * to reject a critical suspend.
+ */
(void) suspend();
break;
}
int timeout = HZ;
DECLARE_WAITQUEUE(wait, current);
- if (smp_num_cpus > 1)
- return;
-
add_wait_queue(&apm_waitqueue, &wait);
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
for (;;) {
/* Nothing to do, just sleep for the timeout */
timeout = 2*timeout;
* Ok, check all events, check for idle (and mark us sleeping
* so as not to count towards the load average)..
*/
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
apm_event_handler();
#ifdef CONFIG_APM_CPU_IDLE
if (!system_idle())
unsigned long start = jiffies;
while ((!exit_kapmd) && system_idle()) {
apm_do_idle();
- if (jiffies - start > (5*APM_CHECK_TIMEOUT)) {
+ if ((jiffies - start) > APM_CHECK_TIMEOUT) {
apm_event_handler();
start = jiffies;
}
schedule();
goto repeat;
}
- current->state = TASK_RUNNING;
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&apm_waitqueue, &wait);
}
i = count;
as->standbys_read--;
as->standbys_pending--;
standbys_pending--;
- } else if (!send_event(APM_USER_STANDBY, as))
+ } else if (!send_event(APM_USER_STANDBY))
return -EAGAIN;
+ else
+ queue_event(APM_USER_STANDBY, as);
if (standbys_pending <= 0)
standby();
break;
as->suspends_read--;
as->suspends_pending--;
suspends_pending--;
- } else if (!send_event(APM_USER_SUSPEND, as))
+ } else if (!send_event(APM_USER_SUSPEND))
return -EAGAIN;
+ else
+ queue_event(APM_USER_SUSPEND, as);
if (suspends_pending <= 0) {
if (suspend() != APM_SUCCESS)
return -EIO;
break;
schedule();
}
- current->state = TASK_RUNNING;
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&apm_suspend_waitqueue, &wait);
return as->suspend_result;
}
as1->next = as->next;
}
kfree_s(as, sizeof(*as));
- MOD_DEC_USE_COUNT;
return 0;
}
{
struct apm_user * as;
- MOD_INC_USE_COUNT;
-
as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
sizeof(*as));
- MOD_DEC_USE_COUNT;
return -ENOMEM;
}
as->magic = APM_BIOS_MAGIC;
strcpy(current->comm, "kapmd");
sigfillset(¤t->blocked);
+ current->tty = NULL; /* get rid of controlling tty */
if (apm_bios_info.version > 0x100) {
/*
#ifdef CONFIG_MAGIC_SYSRQ
sysrq_power_off = apm_power_off;
#endif
+ if (smp_num_cpus == 1) {
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
- if (smp_num_cpus == 1)
console_blank_hook = apm_console_blank;
#endif
-
- pm_active = 1;
-
- apm_mainloop();
-
- pm_active = 0;
-
+ apm_mainloop();
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
- if (smp_num_cpus == 1)
console_blank_hook = NULL;
#endif
-#ifdef CONFIG_MAGIC_SYSRQ
- sysrq_power_off = NULL;
-#endif
- if (power_off)
- pm_power_off = NULL;
-
+ }
kapmd_running = 0;
return 0;
__setup("apm=", apm_setup);
static struct file_operations apm_bios_fops = {
+ owner: THIS_MODULE,
read: do_read,
poll: do_poll,
ioctl: do_ioctl,
printk(KERN_NOTICE "apm: overridden by ACPI.\n");
APM_INIT_ERROR_RETURN;
}
+ pm_active = 1;
/*
* Set up a segment that references the real mode segment 0x40
{
misc_deregister(&apm_device);
remove_proc_entry("apm", NULL);
+#ifdef CONFIG_MAGIC_SYSRQ
+ sysrq_power_off = NULL;
+#endif
+ if (power_off)
+ pm_power_off = NULL;
exit_kapmd = 1;
while (kapmd_running)
schedule();
+ pm_active = 0;
}
module_init(apm_init);
reg = PCI_BASE_ADDRESS_0 + 4*resource;
} else if (resource == PCI_ROM_RESOURCE) {
res->flags |= PCI_ROM_ADDRESS_ENABLE;
+ new |= PCI_ROM_ADDRESS_ENABLE;
reg = dev->rom_base_reg;
} else {
/* Somebody might have asked allocation of a non-standard resource */
return;
}
-#ifdef CONFIG_X86_FX
+#ifdef CONFIG_X86_FXSR
int i387_hard_to_user ( struct _fpstate * user,
struct i387_hard_struct * hard)
else {
start_at = HIGH_MEMORY;
mem_size -= HIGH_MEMORY;
+ usermem=0;
}
add_memory_region(start_at, mem_size, E820_RAM);
}
conswitchp = &dummy_con;
#endif
#endif
-#ifdef CONFIG_X86_FX
+#ifdef CONFIG_X86_FXSR
if (boot_cpu_data.x86_capability & X86_FEATURE_FXSR)
{
printk("Enabling extended fast FPU save and restore ... ");
cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
if (n >= 0x80000005) {
cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
- printk("CPU: L1 I Cache: %dK L1 D Cache: %dK\n",
- ecx>>24, edx>>24);
+ printk("CPU: L1 I Cache: %dK L1 D Cache: %dK (%d bytes/line)\n",
+ edx>>24, ecx>>24, edx&0xFF);
c->x86_cache_size=(ecx>>24)+(edx>>24);
}
if (n >= 0x80000006) {
name="C6";
fcr_set=ECX8|DSMC|EDCTLB|EMMX|ERETSTK;
fcr_clr=DPDC;
+ printk("Disabling bugged TSC.\n");
+ c->x86_capability &= ~X86_FEATURE_TSC;
break;
case 8:
switch(c->x86_mask) {
{ X86_VENDOR_INTEL, 6,
{ "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)",
NULL, "Pentium II (Deschutes)", "Mobile Pentium II",
- "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL,
- NULL, NULL, NULL, NULL }},
+ "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL,
+ "Pentium III (Cascades)", NULL, NULL, NULL, NULL }},
{ X86_VENDOR_AMD, 4,
{ NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB",
"486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT",
"K6-3", NULL, NULL, NULL, NULL, NULL, NULL }},
{ X86_VENDOR_AMD, 6,
{ "Athlon", "Athlon",
- NULL, NULL, NULL, NULL,
+ "Athlon", NULL, "Athlon", NULL,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
{ X86_VENDOR_UMC, 4,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
};
-void __init identify_cpu(struct cpuinfo_x86 *c)
+/*
+ * Detect a NexGen CPU running without BIOS hypercode new enough
+ * to have CPUID. (Thanks to Herbert Oppmann)
+ */
+
+static int deep_magic_nexgen_probe(void)
{
- int i=0;
- char *p = NULL;
-
- c->loops_per_sec = loops_per_sec;
- c->x86_cache_size = -1;
-
- get_cpu_vendor(c);
+ int ret;
+
+ __asm__ __volatile__ (
+ " movw $0x5555, %%ax\n"
+ " xorw %%dx,%%dx\n"
+ " movw $2, %%cx\n"
+ " divw %%cx\n"
+ " movl $0, %%eax\n"
+ " jnz 1f\n"
+ " movl $1, %%eax\n"
+ "1:\n"
+ : "=a" (ret) : : "cx", "dx" );
+ return ret;
+}
- /* It should be possible for the user to override this. */
+static void squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
+{
if(c->x86_capability&(1<<18)) {
/* Disable processor serial number */
unsigned long lo,hi;
wrmsr(0x119,lo,hi);
printk(KERN_INFO "CPU serial number disabled.\n");
}
+}
+
+void __init identify_cpu(struct cpuinfo_x86 *c)
+{
+ int i=0;
+ char *p = NULL;
+
+ c->loops_per_sec = loops_per_sec;
+ c->x86_cache_size = -1;
+
+ get_cpu_vendor(c);
+
switch (c->x86_vendor) {
case X86_VENDOR_UNKNOWN:
if (c->cpuid_level < 0)
+ {
+ /* It may be a nexgen with cpuid disabled.. */
+ if(deep_magic_nexgen_probe())
+ {
+ strcpy(c->x86_model_id, "Nx586");
+ c->x86_vendor = X86_VENDOR_NEXGEN;
+ }
return;
+ }
break;
case X86_VENDOR_CYRIX:
return;
case X86_VENDOR_INTEL:
+
+ squash_the_stupid_serial_number(c);
+
if (c->cpuid_level > 1) {
/* supports eax=2 call */
int edx, dummy;
c->x86_cache_size = 0;
break;
- case 0x41:
+ case 0x41: /* 4-way 128 */
c->x86_cache_size = 128;
break;
- case 0x42:
- case 0x82: /*Detect 256-Kbyte cache on Coppermine*/
+ case 0x42: /* 4-way 256 */
+ case 0x82: /* 8-way 256 */
c->x86_cache_size = 256;
break;
- case 0x43:
+ case 0x43: /* 4-way 512 */
c->x86_cache_size = 512;
break;
- case 0x44:
+ case 0x44: /* 4-way 1024 */
+ case 0x84: /* 8-way 1024 */
c->x86_cache_size = 1024;
break;
- case 0x45:
+ case 0x45: /* 4-way 2048 */
+ case 0x85: /* 8-way 2048 */
c->x86_cache_size = 2048;
break;
case X86_VENDOR_TRANSMETA:
transmeta_model(c);
+ squash_the_stupid_serial_number(c);
return;
}
+ /* may be changed in the switch so needs to be after */
+
+ if(c->x86_vendor == X86_VENDOR_NEXGEN)
+ c->x86_cache_size = 256; /* A few had 1Mb.. */
for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
if (cpu_models[i].vendor == c->x86_vendor &&
x86_cap_flags[10] = "sep";
if (c->x86 < 6)
x86_cap_flags[16] = "fcmov";
+ x86_cap_flags[16] = "pat";
x86_cap_flags[22] = "mmxext";
+ x86_cap_flags[24] = "fxsr";
x86_cap_flags[30] = "3dnowext";
x86_cap_flags[31] = "3dnow";
break;
return p - buffer;
}
+#ifndef CONFIG_X86_TSC
+static int tsc_disable __initdata = 0;
+
+static int __init tsc_setup(char *str)
+{
+ tsc_disable = 1;
+ return 1;
+}
+
+__setup("notsc", tsc_setup);
+#endif
+
static unsigned long cpu_initialized __initdata = 0;
/*
if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
+#ifndef CONFIG_X86_TSC
+ if (tsc_disable && cpu_has_tsc) {
+ printk("Disabling TSC...\n");
+ boot_cpu_data.x86_capability &= ~X86_FEATURE_TSC;
+ set_in_cr4(X86_CR4_TSD);
+ }
+#endif
__asm__ __volatile__("lgdt %0": "=m" (gdt_descr));
__asm__ __volatile__("lidt %0": "=m" (idt_descr));
{
struct desc_struct descriptor;
unsigned long base_address, limit, address, seg_top;
+ unsigned short selector;
segment--;
case PREFIX_FS_-1:
/* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
in the assembler statement. */
- __asm__("mov %%fs,%0":"=r" ((unsigned short)addr->selector));
+
+ __asm__("mov %%fs,%0":"=r" (selector));
+ addr->selector = selector;
break;
case PREFIX_GS_-1:
/* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
in the assembler statement. */
- __asm__("mov %%gs,%0":"=r" ((unsigned short)addr->selector));
+ __asm__("mov %%gs,%0":"=r" (selector));
+ addr->selector = selector;
break;
default:
addr->selector = PM_REG_(segment);
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/config-language.txt.
+#
mainmenu_name "Kernel configuration of Linux for IA-64 machines"
mainmenu_option next_comment
#include <linux/module.h>
#include <linux/types.h>
#include <linux/zorro.h>
+#include <asm/ptrace.h>
#include <asm/amigahw.h>
+#include <asm/amigaints.h>
#include <asm/amipcmcia.h>
extern volatile u_short amiga_audio_min_period;
* Add things here when you find the need for it.
*/
EXPORT_SYMBOL(amiga_model);
+EXPORT_SYMBOL(amiga_chipset);
EXPORT_SYMBOL(amiga_hw_present);
EXPORT_SYMBOL(amiga_eclock);
EXPORT_SYMBOL(amiga_colorclock);
EXPORT_SYMBOL(amiga_chip_size);
EXPORT_SYMBOL(amiga_audio_period);
EXPORT_SYMBOL(amiga_audio_min_period);
+EXPORT_SYMBOL(amiga_do_irq);
+EXPORT_SYMBOL(amiga_do_irq_list);
+EXPORT_SYMBOL(amiga_intena_vals);
#ifdef CONFIG_AMIGA_PCMCIA
EXPORT_SYMBOL(pcmcia_reset);
/* irq node variables for amiga interrupt sources */
static irq_node_t *ami_irq_list[AMI_STD_IRQS];
-unsigned short ami_intena_vals[AMI_STD_IRQS] = {
+unsigned short amiga_intena_vals[AMI_STD_IRQS] = {
IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT,
IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER
};
/* enable the interrupt */
if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq])
- custom.intena = IF_SETCLR | ami_intena_vals[irq];
+ custom.intena = IF_SETCLR | amiga_intena_vals[irq];
return error;
}
amiga_delete_irq(&ami_irq_list[irq], dev_id);
/* if server list empty, disable the interrupt */
if (!ami_irq_list[irq] && irq < IRQ_AMIGA_PORTS)
- custom.intena = ami_intena_vals[irq];
+ custom.intena = amiga_intena_vals[irq];
} else {
if (ami_irq_list[irq]->dev_id != dev_id)
printk("%s: removing probably wrong IRQ %d from %s\n",
ami_irq_list[irq]->flags = 0;
ami_irq_list[irq]->dev_id = NULL;
ami_irq_list[irq]->devname = NULL;
- custom.intena = ami_intena_vals[irq];
+ custom.intena = amiga_intena_vals[irq];
}
}
}
/* enable the interrupt */
- custom.intena = IF_SETCLR | ami_intena_vals[irq];
+ custom.intena = IF_SETCLR | amiga_intena_vals[irq];
}
void amiga_disable_irq(unsigned int irq)
}
/* disable the interrupt */
- custom.intena = ami_intena_vals[irq];
+ custom.intena = amiga_intena_vals[irq];
}
inline void amiga_do_irq(int irq, struct pt_regs *fp)
if (server->count++)
server->reentrance = 1;
- intena = ami_intena_vals[irq];
+ intena = amiga_intena_vals[irq];
custom.intreq = intena;
/* serve fast handler if present - there can only be one of these */
if (!AMIGAHW_PRESENT(CHIP_RAM))
return;
- chipram.end = amiga_chip_size;
+ chipram.end = amiga_chip_size-1;
request_resource(&iomem_resource, &chipram);
/* initialize start boundary */
#
# For a description of the syntax of this configuration file,
-# see the Configure script.
+# see Documentation/kbuild/config-language.txt.
#
define_bool CONFIG_UID16 y
# CONFIG_AMIGA_Z2RAM is not set
# CONFIG_BLK_DEV_XD is not set
# CONFIG_PARIDE is not set
-
-#
-# Additional Block Devices
-#
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
#endif
#ifdef CONFIG_HEARTBEAT
void (*mach_heartbeat) (int) = NULL;
+EXPORT_SYMBOL(mach_heartbeat);
#endif
#ifdef CONFIG_M68K_L2_CACHE
void (*mach_l2_flush) (int) = NULL;
#ifndef final_version
if (bdp->cbd_sc & BD_ENET_TX_READY) {
/* Ooops. All transmit buffers are full. Bail out.
- * This should not happen, since cep->tx_busy should be set.
+ * This should not happen, since cep->tx_full should be set.
*/
printk("%s: tx queue full!.\n", dev->name);
return 1;
cep->stats.tx_bytes += skb->len;
cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;
- /* Push the data cache so the CPM does not get stale memory
- * data.
- */
- flush_dcache_range((unsigned long)(skb->data),
- (unsigned long)(skb->data + skb->len));
-
spin_lock_irq(&cep->lock);
/* Send it on its way. Tell CPM its ready, interrupt when done,
else
bdp++;
- if (bdp->cbd_sc & BD_ENET_TX_READY)
+ if (bdp->cbd_sc & BD_ENET_TX_READY) {
netif_stop_queue(dev);
+ cep->tx_full = 1;
+ }
cep->cur_tx = (cbd_t *)bdp;
cep->cur_tx, cep->tx_full ? " (full)" : "",
cep->cur_rx);
bdp = cep->tx_bd_base;
+ printk(" Tx @base %p :\n", bdp);
for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)
printk("%04x %04x %08x\n",
bdp->cbd_sc,
bdp->cbd_datlen,
bdp->cbd_bufaddr);
bdp = cep->rx_bd_base;
+ printk(" Rx @base %p :\n", bdp);
for (i = 0 ; i < RX_RING_SIZE; i++, bdp++)
printk("%04x %04x %08x\n",
bdp->cbd_sc,
* full.
*/
if (cep->tx_full) {
+ cep->tx_full = 0;
if (netif_queue_stopped(dev)) {
netif_wake_queue(dev);
}
* _should_ pick up without having to reset any of our
* pointers either.
*/
+
cp = cpmp;
cp->cp_cpcr =
mk_cr_cmd(CPM_ENET_PAGE, CPM_ENET_BLOCK, 0,
if (info) {
/*memset(info, 0, sizeof(ser_info_t));*/
__clear_user(info,sizeof(ser_info_t));
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
info->magic = SERIAL_MAGIC;
info->flags = state->flags;
info->tqueue.routine = do_softint;
*/
host_buffer = host_page_addr; /* Host virtual page address */
host_end = host_page_addr + PAGE_SIZE;
- pte = find_pte(&init_mm, host_page_addr);
+ pte = va_to_pte(host_page_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
- flush_tlb_page(current->mm->mmap, host_buffer);
+ flush_tlb_page(init_mm.mmap, host_buffer);
/* Tell everyone where the comm processor resides.
*/
else
bdp++;
- if (bdp->cbd_sc & BD_ENET_TX_READY)
+ if (bdp->cbd_sc & BD_ENET_TX_READY) {
netif_stop_queue(dev);
+ cep->tx_full = 1;
+ }
cep->cur_tx = (cbd_t *)bdp;
* full.
*/
if (cep->tx_full) {
+ cep->tx_full = 0;
if (netif_queue_stopped(dev))
netif_wake_queue(dev);
}
/* Make it uncached.
*/
- pte = find_pte(&init_mm, mem_addr);
+ pte = va_to_pte(mem_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
flush_tlb_page(init_mm.mmap, mem_addr);
/*
- * Fast Ethernet Controller (FECC) driver for Motorola MPC8xx.
+ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
* Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
*
* This version of the driver is specific to the FADS implementation,
* will be much more memory efficient and will easily handle lots of
* small packets.
*
+ * Much better multiple PHY support by Magnus Damm.
+ * Copyright (c) 2000 Ericsson Radio Systems AB.
+ *
*/
+
+/* List of PHYs we wish to support.
+*/
+#define CONFIG_FEC_LXT970
+#define CONFIG_FEC_LXT971
+#define CONFIG_FEC_QS6612
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_FEC_PACKETHOOK
+#include <linux/pkthook.h>
+#endif
#include <asm/8xx_immap.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include "commproc.h"
+/* Forward declarations of some structures to support different PHYs
+*/
+
+typedef struct {
+ uint mii_data;
+ void (*funct)(uint mii_reg, struct net_device *dev);
+} phy_cmd_t;
+
+typedef struct {
+ uint id;
+ char *name;
+
+ const phy_cmd_t *config;
+ const phy_cmd_t *startup;
+ const phy_cmd_t *ack_int;
+ const phy_cmd_t *shutdown;
+} phy_info_t;
+
/* The number of Tx and Rx buffers. These are allocated from the page
* pool. The code may assume these are power of two, so it it best
* to keep them that size.
cbd_t *dirty_tx; /* The ring entries to be free()ed. */
scc_t *sccp;
struct net_device_stats stats;
- char tx_full;
- unsigned long lock;
+ uint tx_full;
+ spinlock_t lock;
+
+ uint phy_id;
+ uint phy_id_done;
+ uint phy_status;
+ uint phy_speed;
+ phy_info_t *phy;
+ struct tq_struct phy_task;
+
+ uint sequence_done;
+
+ uint phy_addr;
+
+ int link;
+ int old_link;
+ int full_duplex;
+
+#ifdef CONFIG_FEC_PACKETHOOK
+ unsigned long ph_lock;
+ fec_ph_func *ph_rxhandler;
+ fec_ph_func *ph_txhandler;
+ __u16 ph_proto;
+ volatile __u32 *ph_regaddr;
+ void *ph_priv;
+#endif
};
static int fec_enet_open(struct net_device *dev);
static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int fec_enet_rx(struct net_device *dev);
static void fec_enet_mii(struct net_device *dev);
-static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+#ifdef CONFIG_FEC_PACKETHOOK
+static void fec_enet_tx(struct net_device *dev, __u32 regval);
+static void fec_enet_rx(struct net_device *dev, __u32 regval);
+#else
+static void fec_enet_tx(struct net_device *dev);
+static void fec_enet_rx(struct net_device *dev);
+#endif
static int fec_enet_close(struct net_device *dev);
static struct net_device_stats *fec_enet_get_stats(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
-
-static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };
+static void fec_restart(struct net_device *dev, int duplex);
+static void fec_stop(struct net_device *dev);
+static ushort my_enet_addr[3];
/* MII processing. We keep this as simple as possible. Requests are
* placed on the list (if there is room). When the request is finished
*/
typedef struct mii_list {
uint mii_regval;
- void (*mii_func)(int val);
+ void (*mii_func)(uint val, struct net_device *dev);
struct mii_list *mii_next;
} mii_list_t;
-#define NMII 10
+#define NMII 20
mii_list_t mii_cmds[NMII];
mii_list_t *mii_free;
mii_list_t *mii_head;
mii_list_t *mii_tail;
-static int mii_queue(int request, void (*func)(int));
+static int mii_queue(struct net_device *dev, int request,
+ void (*func)(uint, struct net_device *));
/* Make MII read/write commands for the FEC.
*/
#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
(VAL & 0xffff))
+#define mk_mii_end 0
-static int
-fec_enet_open(struct net_device *dev)
+/* Transmitter timeout.
+*/
+#define TX_TIMEOUT (2*HZ)
+
+/* Register definitions for the PHY.
+*/
+
+#define MII_REG_CR 0 /* Control Register */
+#define MII_REG_SR 1 /* Status Register */
+#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */
+#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */
+#define MII_REG_ANAR 4 /* A-N Advertisement Register */
+#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */
+#define MII_REG_ANER 6 /* A-N Expansion Register */
+#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */
+#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */
+
+/* values for phy_status */
+
+#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */
+#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */
+#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */
+#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */
+#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */
+#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */
+#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */
+
+#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */
+#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */
+#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */
+#define PHY_STAT_SPMASK 0xf000 /* mask for speed */
+#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */
+#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */
+#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */
+#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */
+
+#ifdef CONFIG_FEC_PACKETHOOK
+int
+fec_register_ph(struct net_device *dev, fec_ph_func *rxfun, fec_ph_func *txfun,
+ __u16 proto, volatile __u32 *regaddr, void *priv)
{
+ struct fec_enet_private *fep;
+ int retval = 0;
- /* I should reset the ring buffers here, but I don't yet know
- * a simple way to do that.
- */
+ fep = dev->priv;
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
+ if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) {
+ /* Someone is messing with the packet hook */
+ return -EAGAIN;
+ }
+ if (fep->ph_rxhandler != NULL || fep->ph_txhandler != NULL) {
+ retval = -EBUSY;
+ goto out;
+ }
+ fep->ph_rxhandler = rxfun;
+ fep->ph_txhandler = txfun;
+ fep->ph_proto = proto;
+ fep->ph_regaddr = regaddr;
+ fep->ph_priv = priv;
- return 0; /* Always succeed */
+ out:
+ fep->ph_lock = 0;
+
+ return retval;
}
-static int
-fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+
+int
+fec_unregister_ph(struct net_device *dev)
{
- struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv;
- volatile cbd_t *bdp;
- unsigned long flags;
-
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 20)
- return 1;
- printk("%s: transmit timed out.\n", dev->name);
- fep->stats.tx_errors++;
-#ifndef final_version
- {
- int i;
- cbd_t *bdp;
- printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n",
- fep->cur_tx, fep->tx_full ? " (full)" : "",
- fep->cur_rx);
- bdp = fep->tx_bd_base;
- for (i = 0 ; i < TX_RING_SIZE; i++)
- printk("%04x %04x %08x\n",
- bdp->cbd_sc,
- bdp->cbd_datlen,
- bdp->cbd_bufaddr);
- bdp = fep->rx_bd_base;
- for (i = 0 ; i < RX_RING_SIZE; i++)
- printk("%04x %04x %08x\n",
- bdp->cbd_sc,
- bdp->cbd_datlen,
- bdp->cbd_bufaddr);
- }
-#endif
+ struct fec_enet_private *fep;
+ int retval = 0;
- dev->tbusy=0;
- dev->trans_start = jiffies;
+ fep = dev->priv;
- return 0;
+ if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) {
+ /* Someone is messing with the packet hook */
+ return -EAGAIN;
}
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+ fep->ph_rxhandler = fep->ph_txhandler = NULL;
+ fep->ph_proto = 0;
+ fep->ph_regaddr = NULL;
+ fep->ph_priv = NULL;
+
+ fep->ph_lock = 0;
+
+ return retval;
+}
+
+EXPORT_SYMBOL(fec_register_ph);
+EXPORT_SYMBOL(fec_unregister_ph);
+
+#endif /* CONFIG_FEC_PACKETHOOK */
+
+static int
+fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ volatile fec_t *fecp;
+ volatile cbd_t *bdp;
- if (test_and_set_bit(0, (void*)&fep->lock) != 0) {
- printk("%s: tx queue lock!.\n", dev->name);
- /* don't clear dev->tbusy flag. */
+ fep = dev->priv;
+ fecp = (volatile fec_t*)dev->base_addr;
+
+ if (!fep->link) {
+ /* Link is down or autonegotiation is in progress. */
return 1;
}
* This should not happen, since dev->tbusy should be set.
*/
printk("%s: tx queue full!.\n", dev->name);
- fep->lock = 0;
return 1;
}
#endif
/* Push the data cache so the CPM does not get stale memory
* data.
*/
- flush_dcache_range(skb->data, skb->data + skb->len);
+ flush_dcache_range((unsigned long)skb->data,
+ (unsigned long)skb->data + skb->len);
+
+ spin_lock_irq(&fep->lock);
- /* Send it on its way. Tell CPM its ready, interrupt when done,
+ /* Send it on its way. Tell FEC its ready, interrupt when done,
* its the last BD of the frame, and to put the CRC on the end.
*/
- save_flags(flags);
- cli();
- bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+ bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
+ | BD_ENET_TX_LAST | BD_ENET_TX_TC);
dev->trans_start = jiffies;
- (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_x_des_active = 0x01000000;
+
+ /* Trigger transmission start */
+ fecp->fec_x_des_active = 0x01000000;
/* If this was the last BD in the ring, start at the beginning again.
*/
- if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+ if (bdp->cbd_sc & BD_ENET_TX_WRAP) {
bdp = fep->tx_bd_base;
- else
+ } else {
bdp++;
+ }
- fep->lock = 0;
if (bdp->cbd_sc & BD_ENET_TX_READY)
- fep->tx_full = 1;
- else
- dev->tbusy=0;
- restore_flags(flags);
+ netif_stop_queue(dev);
fep->cur_tx = (cbd_t *)bdp;
+ spin_unlock_irq(&fep->lock);
+
return 0;
}
+static void
+fec_timeout(struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+
+ printk("%s: transmit timed out.\n", dev->name);
+ fep->stats.tx_errors++;
+#ifndef final_version
+ {
+ int i;
+ cbd_t *bdp;
+
+ printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n",
+ (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "",
+ (unsigned long)fep->dirty_tx,
+ (unsigned long)fep->cur_rx);
+
+ bdp = fep->tx_bd_base;
+ printk(" tx: %u buffers\n", TX_RING_SIZE);
+ for (i = 0 ; i < TX_RING_SIZE; i++) {
+ printk(" %08x: %04x %04x %08x\n",
+ (uint) bdp,
+ bdp->cbd_sc,
+ bdp->cbd_datlen,
+ bdp->cbd_bufaddr);
+ bdp++;
+ }
+
+ bdp = fep->rx_bd_base;
+ printk(" rx: %lu buffers\n", RX_RING_SIZE);
+ for (i = 0 ; i < RX_RING_SIZE; i++) {
+ printk(" %08x: %04x %04x %08x\n",
+ (uint) bdp,
+ bdp->cbd_sc,
+ bdp->cbd_datlen,
+ bdp->cbd_bufaddr);
+ bdp++;
+ }
+ }
+#endif
+ if (!fep->tx_full)
+ netif_wake_queue(dev);
+}
+
/* The interrupt handler.
* This is called from the MPC core interrupt.
*/
fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs)
{
struct net_device *dev = dev_id;
- struct fec_enet_private *fep;
- volatile cbd_t *bdp;
- volatile fec_t *ep;
+ volatile fec_t *fecp;
uint int_events;
- int c=0;
+#ifdef CONFIG_FEC_PACKETHOOK
+ struct fec_enet_private *fep = dev->priv;
+ __u32 regval;
- fep = (struct fec_enet_private *)dev->priv;
- ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
- dev->interrupt = 1;
+ if (fep->ph_regaddr) regval = *fep->ph_regaddr;
+#endif
+
+ fecp = (volatile fec_t*)dev->base_addr;
/* Get the interrupt events that caused us to be here.
*/
- while ((int_events = ep->fec_ievent) != 0) {
- ep->fec_ievent = int_events;
- if ((int_events &
- (FEC_ENET_HBERR | FEC_ENET_BABR |
- FEC_ENET_BABT | FEC_ENET_EBERR)) != 0)
- printk("FEC ERROR %x\n", int_events);
-
- /* Handle receive event in its own function.
- */
- if (int_events & (FEC_ENET_RXF | FEC_ENET_RXB))
- fec_enet_rx(dev_id);
+ while ((int_events = fecp->fec_ievent) != 0) {
+ fecp->fec_ievent = int_events;
+ if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR |
+ FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) {
+ printk("FEC ERROR %x\n", int_events);
+ }
- /* Transmit OK, or non-fatal error. Update the buffer descriptors.
- * FEC handles all errors, we just discover them as part of the
- * transmit process.
- */
- if (int_events & (FEC_ENET_TXF | FEC_ENET_TXB)) {
- bdp = fep->dirty_tx;
- while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) {
-#if 1
- if (bdp==fep->cur_tx)
- break;
+ /* Handle receive event in its own function.
+ */
+ if (int_events & FEC_ENET_RXF) {
+#ifdef CONFIG_FEC_PACKETHOOK
+ fec_enet_rx(dev, regval);
+#else
+ fec_enet_rx(dev);
+#endif
+ }
+
+ /* Transmit OK, or non-fatal error. Update the buffer
+ descriptors. FEC handles all errors, we just discover
+ them as part of the transmit process.
+ */
+ if (int_events & FEC_ENET_TXF) {
+#ifdef CONFIG_FEC_PACKETHOOK
+ fec_enet_tx(dev, regval);
+#else
+ fec_enet_tx(dev);
#endif
- if (++c>1) {/*we go here when an it has been lost*/};
+ }
+ if (int_events & FEC_ENET_MII) {
+ fec_enet_mii(dev);
+ }
+
+ }
+}
- if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */
- fep->stats.tx_heartbeat_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */
- fep->stats.tx_window_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */
- fep->stats.tx_aborted_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */
- fep->stats.tx_fifo_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */
- fep->stats.tx_carrier_errors++;
- fep->stats.tx_errors++;
-
- fep->stats.tx_packets++;
-
+static void
+#ifdef CONFIG_FEC_PACKETHOOK
+fec_enet_tx(struct net_device *dev, __u32 regval)
+#else
+fec_enet_tx(struct net_device *dev)
+#endif
+{
+ struct fec_enet_private *fep;
+ volatile cbd_t *bdp;
+ struct sk_buff *skb;
+
+ fep = dev->priv;
+ spin_lock(&fep->lock);
+ bdp = fep->dirty_tx;
+
+ while ((bdp->cbd_sc&BD_ENET_TX_READY) == 0) {
+ if (bdp == fep->cur_tx && fep->tx_full == 0) break;
+
+ skb = fep->tx_skbuff[fep->skb_dirty];
+ /* Check for errors. */
+ if (bdp->cbd_sc & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+ BD_ENET_TX_RL | BD_ENET_TX_UN |
+ BD_ENET_TX_CSL)) {
+ fep->stats.tx_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */
+ fep->stats.tx_heartbeat_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */
+ fep->stats.tx_window_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */
+ fep->stats.tx_aborted_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */
+ fep->stats.tx_fifo_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */
+ fep->stats.tx_carrier_errors++;
+ } else {
+#ifdef CONFIG_FEC_PACKETHOOK
+ /* Packet hook ... */
+ if (fep->ph_txhandler &&
+ ((struct ethhdr *)skb->data)->h_proto
+ == fep->ph_proto) {
+ fep->ph_txhandler((__u8*)skb->data, skb->len,
+ regval, fep->ph_priv);
+ }
+#endif
+ fep->stats.tx_packets++;
+ }
+
#ifndef final_version
if (bdp->cbd_sc & BD_ENET_TX_READY)
- printk("HEY! Enet xmit interrupt and TX_READY.\n");
+ printk("HEY! Enet xmit interrupt and TX_READY.\n");
#endif
/* Deferred means some collisions occurred during transmit,
* but we eventually sent the packet OK.
*/
if (bdp->cbd_sc & BD_ENET_TX_DEF)
- fep->stats.collisions++;
+ fep->stats.collisions++;
/* Free the sk buffer associated with this last transmit.
*/
- dev_kfree_skb(fep->tx_skbuff[fep->skb_dirty]/*, FREE_WRITE*/);
+#if 0
+printk("TXI: %x %x %x\n", bdp, skb, fep->skb_dirty);
+#endif
+ dev_kfree_skb(skb/*, FREE_WRITE*/);
+ fep->tx_skbuff[fep->skb_dirty] = NULL;
fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK;
/* Update pointer to next buffer descriptor to be transmitted.
*/
if (bdp->cbd_sc & BD_ENET_TX_WRAP)
- bdp = fep->tx_bd_base;
+ bdp = fep->tx_bd_base;
else
- bdp++;
+ bdp++;
/* Since we have freed up a buffer, the ring is no longer
* full.
*/
- if (fep->tx_full && dev->tbusy) {
- fep->tx_full = 0;
- dev->tbusy = 0;
- mark_bh(NET_BH);
+ if (fep->tx_full) {
+ fep->tx_full = 0;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
}
-
- fep->dirty_tx = (cbd_t *)bdp;
-#if 0
- if (bdp==fep->cur_tx)
- break;
+#ifdef CONFIG_FEC_PACKETHOOK
+ /* Re-read register. Not exactly guaranteed to be correct,
+ but... */
+ if (fep->ph_regaddr) regval = *fep->ph_regaddr;
#endif
- }/*while (bdp->cbd_sc&BD_ENET_TX_READY)==0*/
- } /* if tx events */
-
- if (int_events & FEC_ENET_MII)
- fec_enet_mii(dev_id);
-
- } /* while any events */
-
- dev->interrupt = 0;
-
- return;
+ }
+ fep->dirty_tx = (cbd_t *)bdp;
+ spin_unlock(&fep->lock);
}
+
/* During a receive, the cur_rx points to the current incoming buffer.
* When we update through the ring, if the next incoming buffer has
* not been given to the system, we just set the empty indicator,
* effectively tossing the packet.
*/
-static int
+static void
+#ifdef CONFIG_FEC_PACKETHOOK
+fec_enet_rx(struct net_device *dev, __u32 regval)
+#else
fec_enet_rx(struct net_device *dev)
+#endif
{
struct fec_enet_private *fep;
+ volatile fec_t *fecp;
volatile cbd_t *bdp;
struct sk_buff *skb;
ushort pkt_len;
- volatile fec_t *ep;
+ __u8 *data;
- fep = (struct fec_enet_private *)dev->priv;
- ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
+ fep = dev->priv;
+ fecp = (volatile fec_t*)dev->base_addr;
/* First, grab all of the stats for the incoming packet.
* These get messed up if we get called due to a busy condition.
*/
bdp = fep->cur_rx;
-for (;;) {
- if (bdp->cbd_sc & BD_ENET_RX_EMPTY)
- break;
-
+while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
+
#ifndef final_version
/* Since we have allocated space to hold a complete frame,
* the last indicator should be set.
printk("FEC ENET: rcv is not +last\n");
#endif
- /* Frame too long or too short.
- */
- if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))
- fep->stats.rx_length_errors++;
- if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */
- fep->stats.rx_frame_errors++;
- if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */
- fep->stats.rx_crc_errors++;
- if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */
- fep->stats.rx_crc_errors++;
+ /* Check for errors. */
+ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+ BD_ENET_RX_CR | BD_ENET_RX_OV)) {
+ fep->stats.rx_errors++;
+ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+ /* Frame too long or too short. */
+ fep->stats.rx_length_errors++;
+ }
+ if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */
+ fep->stats.rx_frame_errors++;
+ if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */
+ fep->stats.rx_crc_errors++;
+ if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */
+ fep->stats.rx_crc_errors++;
+ }
/* Report late collisions as a frame error.
* On this error, the BD is closed, but we don't know what we
* have in the buffer. So, just drop this frame on the floor.
*/
if (bdp->cbd_sc & BD_ENET_RX_CL) {
+ fep->stats.rx_errors++;
fep->stats.rx_frame_errors++;
+ goto rx_processing_done;
}
- else {
- /* Process the incoming frame.
- */
- fep->stats.rx_packets++;
- pkt_len = bdp->cbd_datlen;
- fep->stats.rx_bytes += pkt_len;
+ /* Process the incoming frame.
+ */
+ fep->stats.rx_packets++;
+ pkt_len = bdp->cbd_datlen;
+ fep->stats.rx_bytes += pkt_len;
+ data = (__u8*)__va(bdp->cbd_bufaddr);
+
+#ifdef CONFIG_FEC_PACKETHOOK
+ /* Packet hook ... */
+ if (fep->ph_rxhandler) {
+ if (((struct ethhdr *)data)->h_proto == fep->ph_proto) {
+ switch (fep->ph_rxhandler(data, pkt_len, regval,
+ fep->ph_priv)) {
+ case 1:
+ goto rx_processing_done;
+ break;
+ case 0:
+ break;
+ default:
+ fep->stats.rx_errors++;
+ goto rx_processing_done;
+ }
+ }
+ }
- /* This does 16 byte alignment, exactly what we need.
- */
- skb = dev_alloc_skb(pkt_len);
+ /* If it wasn't filtered - copy it to an sk buffer. */
+#endif
- if (skb == NULL) {
- printk("%s: Memory squeeze, dropping packet.\n", dev->name);
- fep->stats.rx_dropped++;
- }
- else {
- skb->dev = dev;
- skb_put(skb,pkt_len); /* Make room */
- eth_copy_and_sum(skb,
- (unsigned char *)__va(bdp->cbd_bufaddr),
- pkt_len, 0);
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- }
+ /* This does 16 byte alignment, exactly what we need.
+ */
+ skb = dev_alloc_skb(pkt_len);
+
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ fep->stats.rx_dropped++;
+ } else {
+ skb->dev = dev;
+ skb_put(skb,pkt_len); /* Make room */
+ eth_copy_and_sum(skb,
+ (unsigned char *)__va(bdp->cbd_bufaddr),
+ pkt_len, 0);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
}
+ rx_processing_done:
/* Clear the status flags for this buffer.
*/
* incoming frames. On a heavily loaded network, we should be
* able to keep up at the expense of system resources.
*/
- ep->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0x01000000;
+#endif
+#ifdef CONFIG_FEC_PACKETHOOK
+ /* Re-read register. Not exactly guaranteed to be correct,
+ but... */
+ if (fep->ph_regaddr) regval = *fep->ph_regaddr;
#endif
- }
+ } /* while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) */
fep->cur_rx = (cbd_t *)bdp;
#if 0
* our way back to the interrupt return only to come right back
* here.
*/
- ep->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0x01000000;
#endif
-
- return 0;
}
+
static void
fec_enet_mii(struct net_device *dev)
{
}
if (mip->mii_func != NULL)
- (*(mip->mii_func))(mii_reg);
+ (*(mip->mii_func))(mii_reg, dev);
mii_head = mip->mii_next;
mip->mii_next = mii_free;
}
static int
-mii_queue(int regval, void (*func)(int))
+mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *))
{
+ struct fec_enet_private *fep;
unsigned long flags;
mii_list_t *mip;
int retval;
+ /* Add PHY address to register command.
+ */
+ fep = dev->priv;
+ regval |= fep->phy_addr << 23;
+
retval = 0;
save_flags(flags);
return(retval);
}
-static void
-mii_status(uint mii_reg)
+static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
{
- if (((mii_reg >> 18) & 0x1f) == 1) {
- /* status register.
- */
- printk("fec: ");
- if (mii_reg & 0x0004)
- printk("link up");
- else
- printk("link down");
+ int k;
- if (mii_reg & 0x0010)
- printk(",remote fault");
- if (mii_reg & 0x0020)
- printk(",auto complete");
- printk("\n");
- }
- if (((mii_reg >> 18) & 0x1f) == 0x14) {
- /* Extended chip status register.
- */
- printk("fec: ");
- if (mii_reg & 0x0800)
- printk("100 Mbps");
- else
- printk("10 Mbps");
+ if(!c)
+ return;
- if (mii_reg & 0x1000)
- printk(", Full-Duplex\n");
- else
- printk(", Half-Duplex\n");
- }
- if (((mii_reg >> 18) & 0x1f) == 0x1f) {
- printk("fec: %x\n", mii_reg);
- }
+ for(k = 0; (c+k)->mii_data != mk_mii_end; k++)
+ mii_queue(dev, (c+k)->mii_data, (c+k)->funct);
}
-static void
-mii_startup_cmds(void)
+static void mii_parse_sr(uint mii_reg, struct net_device *dev)
{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
- /* Read status registers to clear any pending interrupt.
- */
- mii_queue(mk_mii_read(1), mii_status);
-#ifndef CONFIG_RPXCLASSIC
- mii_queue(mk_mii_read(18), mii_status);
+ *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
- /* Read extended chip status register.
- */
- mii_queue(mk_mii_read(0x14), mii_status);
+ if (mii_reg & 0x0004)
+ *s |= PHY_STAT_LINK;
+ if (mii_reg & 0x0010)
+ *s |= PHY_STAT_FAULT;
+ if (mii_reg & 0x0020)
+ *s |= PHY_STAT_ANC;
+}
- /* Enable Link status change interrupts.
- */
- mii_queue(mk_mii_write(0x11, 0x0002), NULL);
+static void mii_parse_cr(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
-#ifdef CONFIG_FADS
- /* FADS uses the TRSTE in the BCSR, which is kind of weird.
- * This really controls the startup default configuration.
- * Changing the state of TRSTE once powered up doesn't do
- * anything, you have to whack the control register.
- * This of course screws up any autoconfig that was done.......
- */
- mii_queue(mk_mii_write(0, 0x1000), NULL);
-#endif
-#else
- /* Experimenting with the QS6612 PHY....not done yet.
- */
- mii_queue(mk_mii_read(31), mii_status);
-#endif
+ *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP);
+
+ if (mii_reg & 0x1000)
+ *s |= PHY_CONF_ANE;
+ if (mii_reg & 0x4000)
+ *s |= PHY_CONF_LOOP;
}
-/* This supports the mii_link interrupt below.
- * We should get called three times. Once for register 1, once for
- * register 18, and once for register 20.
- */
-static uint mii_saved_reg1;
+static void mii_parse_anar(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
+
+ *s &= ~(PHY_CONF_SPMASK);
+
+ if (mii_reg & 0x0020)
+ *s |= PHY_CONF_10HDX;
+ if (mii_reg & 0x0040)
+ *s |= PHY_CONF_10FDX;
+ if (mii_reg & 0x0080)
+ *s |= PHY_CONF_100HDX;
+ if (mii_reg & 0x00100)
+ *s |= PHY_CONF_100FDX;
+}
+#if 0
+static void mii_disp_reg(uint mii_reg, struct net_device *dev)
+{
+ printk("reg %u = 0x%04x\n", (mii_reg >> 18) & 0x1f, mii_reg & 0xffff);
+}
+#endif
-static void
-mii_relink(uint mii_reg)
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT970 is used by many boards */
+
+#ifdef CONFIG_FEC_LXT970
+
+#define MII_LXT970_MIRROR 16 /* Mirror register */
+#define MII_LXT970_IER 17 /* Interrupt Enable Register */
+#define MII_LXT970_ISR 18 /* Interrupt Status Register */
+#define MII_LXT970_CONFIG 19 /* Configuration Register */
+#define MII_LXT970_CSR 20 /* Chip Status Register */
+
+static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
{
- if (((mii_reg >> 18) & 0x1f) == 1) {
- /* Just save the status register and get out.
- */
- mii_saved_reg1 = mii_reg;
- return;
- }
- if (((mii_reg >> 18) & 0x1f) == 18) {
- /* Not much here, but has to be read to clear the
- * interrupt condition.
- */
- if ((mii_reg & 0x8000) == 0)
- printk("fec: re-link and no IRQ?\n");
- if ((mii_reg & 0x4000) == 0)
- printk("fec: no PHY power?\n");
- }
- if (((mii_reg >> 18) & 0x1f) == 20) {
- /* Extended chip status register.
- * OK, now we have it all, so figure out what is going on.
- */
- printk("fec: ");
- if (mii_saved_reg1 & 0x0004)
- printk("link up");
- else
- printk("link down");
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
- if (mii_saved_reg1 & 0x0010)
- printk(", remote fault");
- if (mii_saved_reg1 & 0x0020)
- printk(", auto complete");
+ *s &= ~(PHY_STAT_SPMASK);
- if (mii_reg & 0x0800)
- printk(", 100 Mbps");
+ if (mii_reg & 0x0800) {
+ if (mii_reg & 0x1000)
+ *s |= PHY_STAT_100FDX;
else
- printk(", 10 Mbps");
-
+ *s |= PHY_STAT_100HDX;
+ }
+ else {
if (mii_reg & 0x1000)
- printk(", Full-Duplex\n");
+ *s |= PHY_STAT_10FDX;
else
- printk(", Half-Duplex\n");
+ *s |= PHY_STAT_10HDX;
}
}
-/* This interrupt occurs when the LTX970 detects a link change.
-*/
-static void
-mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)
-{
- struct net_device *dev = dev_id;
- struct fec_enet_private *fep;
- volatile fec_t *ep;
-
- fep = (struct fec_enet_private *)dev->priv;
- ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
+static phy_info_t phy_info_lxt970 = {
+ 0x07810000,
+ "LXT970",
- /* We need to sequentially read registers 1 and 18 to clear
- * the interrupt. We don't need to do that here because this
- * is an edge triggered interrupt that has already been acknowledged
- * by the top level handler. We also read the extended status
- * register 20. We just queue the commands and let them happen
- * as part of the "normal" processing.
- */
- mii_queue(mk_mii_read(1), mii_relink);
-#ifndef CONFIG_RPXCLASSIC
-
- /* Unique to LevelOne PHY.
- */
- mii_queue(mk_mii_read(18), mii_relink);
- mii_queue(mk_mii_read(20), mii_relink);
-#else
+ (const phy_cmd_t []) { /* config */
+#if 0
+// { mk_mii_write(MII_REG_ANAR, 0x0021), NULL },
- /* Unique to QS6612 PHY.
- */
- mii_queue(mk_mii_read(6), mii_relink);
- mii_queue(mk_mii_read(31), mii_relink);
+ /* Set default operation of 100-TX....for some reason
+ * some of these bits are set on power up, which is wrong.
+ */
+ { mk_mii_write(MII_LXT970_CONFIG, 0), NULL },
#endif
-}
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* read SR and ISR to acknowledge */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_read(MII_LXT970_ISR), NULL },
-static int
-fec_enet_close(struct net_device *dev)
-{
- /* Don't know what to do yet.
- */
+ /* find out the current status */
+
+ { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ { mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
+ { mk_mii_end, }
+ },
+};
+
+#endif /* CONFIG_FEC_LXT970 */
- return 0;
-}
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
-static struct net_device_stats *fec_enet_get_stats(struct net_device *dev)
-{
- struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv;
+#ifdef CONFIG_FEC_LXT971
- return &fep->stats;
-}
+/* register definitions for the 971 */
-/* Set or clear the multicast filter for this adaptor.
- * Skeleton taken from sunlance driver.
- * The CPM Ethernet implementation allows Multicast as well as individual
- * MAC address filtering. Some of the drivers check to make sure it is
- * a group multicast address, and discard those that are not. I guess I
- * will do the same for now, but just remove the test if you want
- * individual filtering as well (do the upper net layers want or support
- * this kind of feature?).
+#define MII_LXT971_PCR 16 /* Port Control Register */
+#define MII_LXT971_SR2 17 /* Status Register 2 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+#define MII_LXT971_LCR 20 /* LED Control Register */
+#define MII_LXT971_TCR 30 /* Transmit Control Register */
+
+/*
+ * I had some nice ideas of running the MDIO faster...
+ * The 971 should support 8MHz and I tried it, but things acted really
+ * wierd, so 2.5 MHz ought to be enough for anyone...
*/
-static void set_multicast_list(struct net_device *dev)
+static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
{
- struct fec_enet_private *fep;
- struct dev_mc_list *dmi;
- u_char *mcptr, *tdptr;
- volatile fec_t *ep;
- int i, j;
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
- fep = (struct fec_enet_private *)dev->priv;
+ *s &= ~(PHY_STAT_SPMASK);
+
+ if (mii_reg & 0x4000) {
+ if (mii_reg & 0x0200)
+ *s |= PHY_STAT_100FDX;
+ else
+ *s |= PHY_STAT_100HDX;
+ }
+ else {
+ if (mii_reg & 0x0200)
+ *s |= PHY_STAT_10FDX;
+ else
+ *s |= PHY_STAT_10HDX;
+ }
+ if (mii_reg & 0x0008)
+ *s |= PHY_STAT_FAULT;
+}
+
+static phy_info_t phy_info_lxt971 = {
+ 0x0001378e,
+ "LXT971",
+
+ (const phy_cmd_t []) { /* config */
+ /* limit to 10MBit because my protorype board
+ * doesn't work with 100. */
+
+ { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+
+ /* Somehow does the 971 tell me that the link is down
+ * the first read after power-up.
+ * read here to get a valid value in ack_int */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* find out the current status */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
+
+ /* we only need to read ISR to acknowledge */
+
+ { mk_mii_read(MII_LXT971_ISR), NULL },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
+ { mk_mii_end, }
+ },
+};
+
+#endif /* CONFIG_FEC_LXT970 */
+
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
+
+#ifdef CONFIG_FEC_QS6612
+
+/* register definitions */
+
+#define MII_QS6612_MCR 17 /* Mode Control Register */
+#define MII_QS6612_FTR 27 /* Factory Test Register */
+#define MII_QS6612_MCO 28 /* Misc. Control Register */
+#define MII_QS6612_ISR 29 /* Interrupt Source Register */
+#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
+#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
+
+static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
+
+ *s &= ~(PHY_STAT_SPMASK);
+
+ switch((mii_reg >> 2) & 7) {
+ case 1: *s |= PHY_STAT_10HDX; break;
+ case 2: *s |= PHY_STAT_100HDX; break;
+ case 5: *s |= PHY_STAT_10FDX; break;
+ case 6: *s |= PHY_STAT_100FDX; break;
+ }
+}
+
+static phy_info_t phy_info_qs6612 = {
+ 0x00181440,
+ "QS6612",
+
+ (const phy_cmd_t []) { /* config */
+// { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */
+
+ /* The PHY powers up isolated on the RPX,
+ * so send a command to allow operation.
+ */
+
+ { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
+
+ /* parse cr and anar to get some info */
+
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+
+ /* we need to read ISR, SR and ANER to acknowledge */
+
+ { mk_mii_read(MII_QS6612_ISR), NULL },
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_read(MII_REG_ANER), NULL },
+
+ /* read pcr to get info */
+
+ { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
+ { mk_mii_end, }
+ },
+};
+
+
+#endif /* CONFIG_FEC_QS6612 */
+
+
+static phy_info_t *phy_info[] = {
+
+#ifdef CONFIG_FEC_LXT970
+ &phy_info_lxt970,
+#endif /* CONFIG_FEC_LXT970 */
+
+#ifdef CONFIG_FEC_LXT971
+ &phy_info_lxt971,
+#endif /* CONFIG_FEC_LXT971 */
+
+#ifdef CONFIG_FEC_QS6612
+ &phy_info_qs6612,
+#endif /* CONFIG_FEC_LXT971 */
+
+ NULL
+};
+
+static void mii_display_status(struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
+
+ if (!fep->link && !fep->old_link) {
+ /* Link is still down - don't print anything */
+ return;
+ }
+
+ printk("%s: status: ", dev->name);
+
+ if (!fep->link) {
+ printk("link down");
+ } else {
+ printk("link up");
+
+ switch(*s & PHY_STAT_SPMASK) {
+ case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
+ case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
+ case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
+ case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
+ default:
+ printk(", Unknown speed/duplex");
+ }
+
+ if (*s & PHY_STAT_ANC)
+ printk(", auto-negotiation complete");
+ }
+
+ if (*s & PHY_STAT_FAULT)
+ printk(", remote fault");
+
+ printk(".\n");
+}
+
+static void mii_display_config(struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ volatile uint *s = &(fep->phy_status);
+
+ printk("%s: config: auto-negotiation ", dev->name);
+
+ if (*s & PHY_CONF_ANE)
+ printk("on");
+ else
+ printk("off");
+
+ if (*s & PHY_CONF_100FDX)
+ printk(", 100FDX");
+ if (*s & PHY_CONF_100HDX)
+ printk(", 100HDX");
+ if (*s & PHY_CONF_10FDX)
+ printk(", 10FDX");
+ if (*s & PHY_CONF_10HDX)
+ printk(", 10HDX");
+ if (!(*s & PHY_CONF_SPMASK))
+ printk(", No speed/duplex selected?");
+
+ if (*s & PHY_CONF_LOOP)
+ printk(", loopback enabled");
+
+ printk(".\n");
+
+ fep->sequence_done = 1;
+}
+
+static void mii_relink(struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+ int duplex;
+
+ fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
+ mii_display_status(dev);
+ fep->old_link = fep->link;
+
+ if (fep->link) {
+ duplex = 0;
+ if (fep->phy_status
+ & (PHY_STAT_100FDX | PHY_STAT_10FDX))
+ duplex = 1;
+ fec_restart(dev, duplex);
+ }
+ else
+ fec_stop(dev);
+
+#if 0
+ enable_irq(fep->mii_irq);
+#endif
+
+}
+
+static void mii_queue_relink(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+
+ fep->phy_task.routine = (void *)mii_relink;
+ fep->phy_task.data = dev;
+ queue_task(&fep->phy_task, &tq_scheduler);
+}
+
+static void mii_queue_config(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+
+ fep->phy_task.routine = (void *)mii_display_config;
+ fep->phy_task.data = dev;
+ queue_task(&fep->phy_task, &tq_scheduler);
+}
+
+
+
+phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink },
+ { mk_mii_end, } };
+phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config },
+ { mk_mii_end, } };
+
+
+
+/* Read remainder of PHY ID.
+*/
+static void
+mii_discover_phy3(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ int i;
+
+ fep = dev->priv;
+ fep->phy_id |= (mii_reg & 0xffff);
+ printk("fec: Phy @ 0x%x, type 0x%08x\n", fep->phy_addr, fep->phy_id);
+
+ for(i = 0; phy_info[i]; i++)
+ if(phy_info[i]->id == (fep->phy_id >> 4))
+ break;
+
+ if(!phy_info[i])
+ panic("%s: PHY id 0x%08x is not supported!\n",
+ dev->name, fep->phy_id);
+
+ fep->phy = phy_info[i];
+ fep->phy_id_done = 1;
+}
+
+/* Scan all of the MII PHY addresses looking for someone to respond
+ * with a valid ID. This usually happens quickly.
+ */
+static void
+mii_discover_phy(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ uint phytype;
+
+ fep = dev->priv;
+
+ if (fep->phy_addr < 32) {
+ if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
+
+ /* Got first part of ID, now get remainder.
+ */
+ fep->phy_id = phytype << 16;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),
+ mii_discover_phy3);
+ }
+ else {
+ fep->phy_addr++;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
+ mii_discover_phy);
+ }
+ }
+ else {
+ printk("FEC: No PHY device found.\n");
+ }
+}
+
+/* This interrupt occurs when the PHY detects a link change.
+*/
+static void
+#ifdef CONFIG_RPXCLASSIC
+mii_link_interrupt(void *dev_id)
+#else
+mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+#endif
+{
+ struct net_device *dev = dev_id;
+ struct fec_enet_private *fep = dev->priv;
+
+#if 0
+ disable_irq(fep->mii_irq); /* disable now, enable later */
+#endif
+
+ mii_do_cmd(dev, fep->phy->ack_int);
+ mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
+
+}
+
+static int
+fec_enet_open(struct net_device *dev)
+{
+ struct fec_enet_private *fep = dev->priv;
+
+ /* I should reset the ring buffers here, but I don't yet know
+ * a simple way to do that.
+ */
+
+ fep->sequence_done = 0;
+ fep->link = 0;
+
+ if (fep->phy) {
+ mii_do_cmd(dev, fep->phy->ack_int);
+ mii_do_cmd(dev, fep->phy->config);
+ mii_do_cmd(dev, phy_cmd_config); /* display configuration */
+
+ while(!fep->sequence_done)
+ schedule();
+
+ mii_do_cmd(dev, fep->phy->startup);
+ netif_start_queue(dev);
+ return 0; /* Success */
+ }
+
+ return -ENODEV; /* No PHY we understand */
+
+}
+
+static int
+fec_enet_close(struct net_device *dev)
+{
+ /* Don't know what to do yet.
+ */
+ netif_stop_queue(dev);
+ fec_stop(dev);
+
+ return 0;
+}
+
+static struct net_device_stats *fec_enet_get_stats(struct net_device *dev)
+{
+ struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv;
+
+ return &fep->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering. Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not. I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ volatile fec_t *ep;
+
+ fep = (struct fec_enet_private *)dev->priv;
ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
if (dev->flags&IFF_PROMISC) {
struct net_device *dev;
struct fec_enet_private *fep;
int i, j;
- unsigned char *eap;
+ unsigned char *eap, *iap;
unsigned long mem_addr;
pte_t *pte;
volatile cbd_t *bdp;
cbd_t *cbd_base;
volatile immap_t *immap;
volatile fec_t *fecp;
- unsigned char *iap;
bd_t *bd;
-
- bd = (bd_t *)__res;
+ extern uint _get_IMMR(void);
+#ifdef CONFIG_RPXCLASSIC
+ unsigned char tmpaddr[6];
+#endif
immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */
+ bd = (bd_t *)__res;
+
/* Allocate some private information.
*/
fep = (struct fec_enet_private *)kmalloc(sizeof(*fep), GFP_KERNEL);
fecp->fec_ecntrl = 1;
udelay(10);
- /* Enable interrupts we wish to service.
- */
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
-
- /* Clear any outstanding interrupt.
- */
- fecp->fec_ievent = 0xffc0;
-
- fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
-
- /* Right now, all of the boards supply the ethernet address in
- * the board descriptor. If someone doesn't we can just use
- * the hard coded address in this driver for testing (this is
- * a Motorola address for a board I have, so it is unlikely to
- * be used elsewhere).
+ /* Set the Ethernet address. If using multiple Enets on the 8xx,
+ * this needs some work to get unique addresses.
*/
- eap = (unsigned char *)&my_enet_addr[0];
-#if 1
+ eap = (unsigned char *)my_enet_addr;
iap = bd->bi_enetaddr;
+
+#ifdef CONFIG_RPXCLASSIC
+ /* The Embedded Planet boards have only one MAC address in
+ * the EEPROM, but can have two Ethernet ports. For the
+ * FEC port, we create another address by setting one of
+ * the address bits above something that would have (up to
+ * now) been allocated.
+ */
for (i=0; i<6; i++)
- dev->dev_addr[i] = *eap++ = *iap++;
-#else
- for (i=0; i<6; i++)
- dev->dev_addr[i] = *eap++;
+ tmpaddr[i] = *iap++;
+ tmpaddr[3] |= 0x80;
+ iap = tmpaddr;
#endif
- /* Set station address.
- */
- fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1];
- fecp->fec_addr_high = my_enet_addr[2];
-
- /* Reset all multicast.
- */
- fecp->fec_hash_table_high = 0;
- fecp->fec_hash_table_low = 0;
-
- /* Set maximum receive buffer size.
- */
- fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
- fecp->fec_r_hash = PKT_MAXBUF_SIZE;
+ for (i=0; i<6; i++)
+ dev->dev_addr[i] = *eap++ = *iap++;
/* Allocate memory for buffer descriptors.
*/
/* Make it uncached.
*/
- pte = find_pte(&init_mm, (int)mem_addr);
+ pte = va_to_pte(mem_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
- flush_tlb_page(current->mm->mmap, mem_addr);
+ flush_tlb_page(init_mm.mmap, mem_addr);
/* Set receive and transmit descriptor base.
*/
- fecp->fec_r_des_start = __pa(mem_addr);
fep->rx_bd_base = cbd_base;
- fecp->fec_x_des_start = __pa((unsigned long)(cbd_base + RX_RING_SIZE));
fep->tx_bd_base = cbd_base + RX_RING_SIZE;
fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
/* Make it uncached.
*/
- pte = find_pte(&init_mm, mem_addr);
+ pte = va_to_pte(mem_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
- flush_tlb_page(current->mm->mmap, mem_addr);
+ flush_tlb_page(init_mm.mmap, mem_addr);
/* Initialize the BD for every fragment in the page.
*/
bdp--;
bdp->cbd_sc |= BD_SC_WRAP;
+#ifdef CONFIG_FEC_PACKETHOOK
+ fep->ph_lock = 0;
+ fep->ph_rxhandler = fep->ph_txhandler = NULL;
+ fep->ph_proto = 0;
+ fep->ph_regaddr = NULL;
+ fep->ph_priv = NULL;
+#endif
+
+ /* Install our interrupt handler.
+ */
+ if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0)
+ panic("Could not allocate FEC IRQ!");
+#ifdef CONFIG_RPXCLASSIC
+ /* Make Port C, bit 15 an input that causes interrupts.
+ */
+ immap->im_ioport.iop_pcpar &= ~0x0001;
+ immap->im_ioport.iop_pcdir &= ~0x0001;
+ immap->im_ioport.iop_pcso &= ~0x0001;
+ immap->im_ioport.iop_pcint |= 0x0001;
+ cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev);
+
+ /* Make LEDS reflect Link status.
+ */
+ *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE;
+#endif
+#ifdef CONFIG_FADS
+ if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0)
+ panic("Could not allocate MII IRQ!");
+#endif
+
+ dev->base_addr = (unsigned long)fecp;
+ dev->priv = fep;
+
+ /* The FEC Ethernet specific entries in the device structure. */
+ dev->open = fec_enet_open;
+ dev->hard_start_xmit = fec_enet_start_xmit;
+ dev->tx_timeout = fec_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->stop = fec_enet_close;
+ dev->get_stats = fec_enet_get_stats;
+ dev->set_multicast_list = set_multicast_list;
+
+ for (i=0; i<NMII-1; i++)
+ mii_cmds[i].mii_next = &mii_cmds[i+1];
+ mii_free = mii_cmds;
+
+ /* Configure all of port D for MII.
+ */
+ immap->im_ioport.iop_pdpar = 0x1fff;
+
+ /* Bits moved from Rev. D onward.
+ */
+ if ((_get_IMMR() & 0xffff) < 0x0501)
+ immap->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */
+ else
+ immap->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */
+
+ /* Set MII speed to 2.5 MHz
+ */
+ fecp->fec_mii_speed = fep->phy_speed =
+ ((bd->bi_busfreq * 1000000) / 2500000) & 0x7e;
+
+ printk("%s: FEC ENET Version 0.2, ", dev->name);
+ for (i=0; i<5; i++)
+ printk("%02x:", dev->dev_addr[i]);
+ printk("%02x\n", dev->dev_addr[5]);
+
+ /* Queue up command to detect the PHY and initialize the
+ * remainder of the interface.
+ */
+ fep->phy_id_done = 0;
+ fep->phy_addr = 0;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
+
+ return 0;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change. This only happens when switching between half and full
+ * duplex.
+ */
+static void
+fec_restart(struct net_device *dev, int duplex)
+{
+ struct fec_enet_private *fep;
+ int i;
+ unsigned char *eap;
+ volatile cbd_t *bdp;
+ volatile immap_t *immap;
+ volatile fec_t *fecp;
+
+ immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */
+
+ fecp = &(immap->im_cpm.cp_fec);
+
+ fep = dev->priv;
+
+ /* Whack a reset. We should wait for this.
+ */
+ fecp->fec_ecntrl = 1;
+ udelay(10);
+
+ /* Enable interrupts we wish to service.
+ */
+ fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
+ FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
+
+ /* Clear any outstanding interrupt.
+ */
+ fecp->fec_ievent = 0xffc0;
+
+ fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
+
+ /* Set station address.
+ */
+ fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1];
+ fecp->fec_addr_high = my_enet_addr[2];
+
+ eap = (unsigned char *)&my_enet_addr[0];
+ for (i=0; i<6; i++)
+ dev->dev_addr[i] = *eap++;
+
+ /* Reset all multicast.
+ */
+ fecp->fec_hash_table_high = 0;
+ fecp->fec_hash_table_low = 0;
+
+ /* Set maximum receive buffer size.
+ */
+ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
+ fecp->fec_r_hash = PKT_MAXBUF_SIZE;
+
+ /* Set receive and transmit descriptor base.
+ */
+ fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
+ fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+
+ fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
+ fep->cur_rx = fep->rx_bd_base;
+
+ /* Reset SKB transmit buffers.
+ */
+ fep->skb_cur = fep->skb_dirty = 0;
+ for (i=0; i<=TX_RING_MOD_MASK; i++) {
+ if (fep->tx_skbuff[i] != NULL) {
+ dev_kfree_skb(fep->tx_skbuff[i]);
+ fep->tx_skbuff[i] = NULL;
+ }
+ }
+
+ /* Initialize the receive buffer descriptors.
+ */
+ bdp = fep->rx_bd_base;
+ for (i=0; i<RX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
+ bdp++;
+ }
+
+ /* Set the last buffer to wrap.
+ */
+ bdp--;
+ bdp->cbd_sc |= BD_SC_WRAP;
+
/* ...and the same for transmmit.
*/
bdp = fep->tx_bd_base;
bdp--;
bdp->cbd_sc |= BD_SC_WRAP;
- /* Enable MII mode, half-duplex until we know better..
+ /* Enable MII mode.
*/
- fecp->fec_r_cntrl = 0x0c;
- fecp->fec_x_cntrl = 0x00;
+ if (duplex) {
+ fecp->fec_r_cntrl = 0x04; /* MII enable */
+ fecp->fec_x_cntrl = 0x04; /* FD enable */
+ }
+ else {
+ fecp->fec_r_cntrl = 0x06; /* MII enable|No Rcv on Xmit */
+ fecp->fec_x_cntrl = 0x00;
+ }
+ fep->full_duplex = duplex;
/* Enable big endian and don't care about SDMA FC.
*/
fecp->fec_fun_code = 0x78000000;
- /* Set MII speed (50 MHz core).
+ /* Set MII speed.
*/
- fecp->fec_mii_speed = 0x14;
+ fecp->fec_mii_speed = fep->phy_speed;
- /* Configure all of port D for MII.
+ /* And last, enable the transmit and receive processing.
*/
- immap->im_ioport.iop_pdpar = 0x1fff;
- immap->im_ioport.iop_pddir = 0x1c58;
+ fecp->fec_ecntrl = 6;
+ fecp->fec_r_des_active = 0x01000000;
+}
- /* Install our interrupt handlers. The 860T FADS board uses
- * IRQ2 for the MII interrupt.
- */
- if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0)
- panic("Could not allocate FEC IRQ!");
- if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0)
- panic("Could not allocate MII IRQ!");
+static void
+fec_stop(struct net_device *dev)
+{
+ volatile immap_t *immap;
+ volatile fec_t *fecp;
+ struct fec_enet_private *fep;
- dev->base_addr = (unsigned long)fecp;
- dev->priv = fep;
- dev->name = "fec";
+ immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */
+
+ fecp = &(immap->im_cpm.cp_fec);
+
+ fep = dev->priv;
- /* The FEC Ethernet specific entries in the device structure. */
- dev->open = fec_enet_open;
- dev->hard_start_xmit = fec_enet_start_xmit;
- dev->stop = fec_enet_close;
- dev->get_stats = fec_enet_get_stats;
- dev->set_multicast_list = set_multicast_list;
- /* And last, enable the transmit and receive processing.
- */
- fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0x01000000;
+ fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */
- printk("FEC ENET Version 0.1, ");
- for (i=0; i<5; i++)
- printk("%02x:", dev->dev_addr[i]);
- printk("%02x\n", dev->dev_addr[5]);
+ while(!(fecp->fec_ievent & 0x10000000));
- for (i=0; i<NMII-1; i++)
- mii_cmds[i].mii_next = &mii_cmds[i+1];
- mii_free = mii_cmds;
+ /* Whack a reset. We should wait for this.
+ */
+ fecp->fec_ecntrl = 1;
+ udelay(10);
- mii_startup_cmds();
+ /* Clear outstanding MII command interrupts.
+ */
+ fecp->fec_ievent = FEC_ENET_MII;
- return 0;
-}
+ /* Enable MII command finihed interrupt
+ */
+ fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
+ fecp->fec_imask = FEC_ENET_MII;
+ /* Set MII speed.
+ */
+ fecp->fec_mii_speed = fep->phy_speed;
+}
*/
#define smc_scc_num hub6
+#ifdef CONFIG_8xxSMC2
/* SMC2 is sometimes used for low performance TDM interfaces. Define
* this as 1 if you want SMC2 as a serial port UART managed by this driver.
* Define this as 0 if you wish to use SMC2 for something else.
*/
#define USE_SMC2 1
+#else
+#define USE_SMC2 0
+#endif
/* Define SCC to ttySx mapping.
*/
#if USE_SMC2
{ 0, 0, PROFF_SMC2, CPMVEC_SMC2, 0, 1 }, /* SMC2 ttyS1 */
#endif
-#if defined(CONFIG_MPC860) || defined(CONFIG_MPC860T)
+#ifdef CONFIG_8xxSCC
{ 0, 0, PROFF_SCC2, CPMVEC_SCC2, 0, SCC_NUM_BASE}, /* SCC2 ttyS2 */
{ 0, 0, PROFF_SCC3, CPMVEC_SCC3, 0, SCC_NUM_BASE + 1}, /* SCC3 ttyS3 */
#endif
if (info) {
/*memset(info, 0, sizeof(ser_info_t));*/
__clear_user(info,sizeof(ser_info_t));
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
info->magic = SERIAL_MAGIC;
info->flags = state->flags;
info->tqueue.routine = do_softint;
-
+#
# For a description of the syntax of this configuration file,
-# see the Configure script.
+# see Documentation/kbuild/config-language.txt.
#
define_bool CONFIG_UID16 y
comment 'Kernel hacking'
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-bool 'Kernel profiling support' CONFIG_PROFILE
-if [ "$CONFIG_PROFILE" = "y" ]; then
- int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
-fi
if [ "$CONFIG_CTC" = "y" ]; then
bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
fi
#
# Kernel hacking
#
-# CONFIG_PROFILE is not set
# CONFIG_REMOTE_DEBUG is not set
force_sig(signr, tsk); \
}
-
-void page_exception(void);
-
/* TODO: define these as 'pgm_check_handler_t xxx;'
asmlinkage void divide_error(void);
asmlinkage void debug(void);
#if AMIGA_OLD_INT
AMI_MSE_INT_OFF();
#endif
- MOD_DEC_USE_COUNT;
return 0;
}
static int open_mouse(struct inode * inode, struct file * file)
{
- /* Lock module first - request_irq might sleep */
-
- MOD_INC_USE_COUNT;
-
/*
* use VBL to poll mouse deltas
*/
if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0,
"Amiga mouse", mouse_interrupt)) {
printk(KERN_INFO "Installing Amiga mouse failed.\n");
- MOD_DEC_USE_COUNT;
return -EIO;
}
}
static struct busmouse amigamouse = {
- AMIGAMOUSE_MINOR, "amigamouse", open_mouse, release_mouse, 7
+ AMIGAMOUSE_MINOR, "amigamouse", THIS_MODULE, open_mouse, release_mouse, 7
};
static int __init amiga_mouse_init(void)
kbd_pt_regs = NULL;
+ init_timer(&amikeyb_rep_timer);
amikeyb_rep_timer.expires = jiffies + key_repeat_rate;
- amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
add_timer(&amikeyb_rep_timer);
handle_scancode(rep_scancode, 1);
} else {
del_timer(&amikeyb_rep_timer);
rep_scancode = keycode;
+ init_timer(&amikeyb_rep_timer);
amikeyb_rep_timer.expires = jiffies + key_repeat_delay;
- amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
add_timer(&amikeyb_rep_timer);
}
handle_scancode(keycode, !break_flag);
if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
return -ENODEV;
+ /*
+ * We request SERDAT and SERPER only, because the serial registers are
+ * too spreaded over the custom register space
+ */
+ if (!request_mem_region(CUSTOM_PHYSADDR+0x30, 4, "amiserial [Paula]"))
+ return -EBUSY;
+
init_bh(SERIAL_BH, do_serial_bh);
IRQ_ports = NULL;
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
+ release_mem_region(CUSTOM_PHYSADDR+0x30, 4);
}
#endif /* MODULE */
{
ATIXL_MSE_INT_OFF(); /* Interrupts are really shut down here */
free_irq(ATIXL_MOUSE_IRQ, NULL);
- MOD_DEC_USE_COUNT;
return 0;
}
static int open_mouse(struct inode * inode, struct file * file)
{
- /* Lock module as request_irq may sleep */
- MOD_INC_USE_COUNT;
if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt, 0, "ATIXL mouse", NULL))
- {
- MOD_DEC_USE_COUNT;
return -EBUSY;
- }
ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */
return 0;
}
static struct busmouse atixlmouse = {
- ATIXL_BUSMOUSE, "atixl", open_mouse, release_mouse, 0
+ ATIXL_BUSMOUSE, "atixl", THIS_MODULE, open_mouse, release_mouse, 0
};
static int __init atixl_busmouse_init(void)
struct busmouse {
int minor;
const char *name;
+ struct module *owner;
int (*open)(struct inode * inode, struct file * file);
int (*release)(struct inode * inode, struct file * file);
int init_button_state;
static void con_flush_chars(struct tty_struct *tty)
{
+ unsigned long flags;
struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
pm_access(pm_con);
+ spin_lock_irqsave(&console_lock, flags);
set_cursor(vt->vc_num);
+ spin_unlock_irqrestore(&console_lock, flags);
}
/*
mouse_dy+=mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2];
wake_up_interruptible(&mouse_wait);
if (mouse_dx < -2048)
- mouse_dx = -2048;
- else
- if (mouse_dx > 2048)
- mouse_dx = 2048;
- if (mouse_dy < -2048)
- mouse_dy = -2048;
- else
- if (mouse_dy > 2048)
- mouse_dy = 2048;
- if (mouse_fasyncptr)
- kill_fasync(mouse_fasyncptr, SIGIO, POLL_IN);
+ mouse_dx = -2048;
+ else if (mouse_dx > 2048)
+ mouse_dx = 2048;
+ if (mouse_dy < -2048)
+ mouse_dy = -2048;
+ else if (mouse_dy > 2048)
+ mouse_dy = 2048;
+ kill_fasync(&mouse_fasyncptr, SIGIO, POLL_IN);
}
mouse_byte_count=0;
/* printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */
}
-static int release_mouse(struct inode * inode, struct file * file)
-{
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int open_mouse(struct inode * inode, struct file * file)
-{
- MOD_INC_USE_COUNT;
- return 0;
-}
-
static struct busmouse apollo_mouse = {
- APOLLO_MOUSE_MINOR, "apollomouse", open_mouse, release_mouse,7
+ APOLLO_MOUSE_MINOR, "apollomouse", THIS_MODULE, NULL, NULL, 7
};
int __init dn_keyb_init(void){
{
MSE_INT_OFF();
free_irq(mouse_irq, NULL);
- MOD_DEC_USE_COUNT;
return 0;
}
{
if (request_irq(mouse_irq, mouse_interrupt, 0, "busmouse", NULL))
return -EBUSY;
- MOD_INC_USE_COUNT;
MSE_INT_ON();
return 0;
}
static struct busmouse busmouse = {
- LOGITECH_BUSMOUSE, "busmouse", open_mouse, close_mouse, 7
+ LOGITECH_BUSMOUSE, "busmouse", THIS_MODULE, open_mouse, close_mouse, 7
};
static int __init logi_busmouse_init(void)
{
MS_MSE_INT_OFF();
free_irq(mouse_irq, NULL);
- MOD_DEC_USE_COUNT;
return 0;
}
return -EBUSY;
outb(MS_MSE_START, MS_MSE_CONTROL_PORT);
- MOD_INC_USE_COUNT;
MS_MSE_INT_ON();
return 0;
}
static struct busmouse msbusmouse = {
- MICROSOFT_BUSMOUSE, "msbusmouse", open_mouse, release_mouse, 0
+ MICROSOFT_BUSMOUSE, "msbusmouse", THIS_MODULE, open_mouse, release_mouse, 0
};
static int __init ms_bus_mouse_init(void)
mxser_pcibrds[b].device_id, pdev);
if (!pdev)
break;
+ if (pci_enable_device(pdev))
+ continue;
b++;
hwconf.pdev = pdev;
printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n",
if (!poll_qp_status())
printk("Warning: Mouse device busy in release_qp()\n");
free_irq(QP_IRQ, NULL);
- MOD_DEC_USE_COUNT;
}
return 0;
}
}
outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */
- MOD_INC_USE_COUNT;
return 0;
}
}
struct file_operations qp_fops = {
+ owner: THIS_MODULE,
read: read_qp,
write: write_qp,
poll: poll_qp,
return -EINVAL;
request_region(io, 2, "rtrack");
- printk(KERN_INFO "AIMSlab Radiotrack/radioreveal card driver.\n");
+ printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n");
/* Set up the I/O locking */
* add_interrupt_randomness() uses the inter-interrupt timing as random
* inputs to the entropy pool. Note that not all interrupts are good
* sources of randomness! For example, the timer interrupts is not a
- * good choice, because the periodicity of the interrupts is to
+ * good choice, because the periodicity of the interrupts is too
* regular, and hence predictable to an attacker. Disk interrupts are
* a better measure, since the timing of the disk interrupts are more
* unpredictable.
if (!dev)
return 0;
- rcktpt_io_addr[i] = dev->resource[0].start;
+ if (pci_enable_device(dev))
+ return 0;
+
+ rcktpt_io_addr[i] = pci_resource_start (dev, 0);
switch(dev->device) {
case PCI_DEVICE_ID_RP4QUAD:
str = "Quadcable";
--- /dev/null
+/*
+ * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x
+ *
+ * Based on acquirewdt.c by Alan Cox.
+ *
+ * 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.
+ *
+ * The author does NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (c) Copyright 2000 Jakob Oestergaard <jakob@ostenfeld.dk>
+ *
+ * 12/4 - 2000 [Initial revision]
+ * 25/4 - 2000 Added /dev/watchdog support
+ *
+ *
+ * Theory of operation:
+ * A Watchdog Timer (WDT) is a hardware circuit that can
+ * reset the computer system in case of a software fault.
+ * You probably knew that already.
+ *
+ * Usually a userspace daemon will notify the kernel WDT driver
+ * via the /proc/watchdog special device file that userspace is
+ * still alive, at regular intervals. When such a notification
+ * occurs, the driver will usually tell the hardware watchdog
+ * that everything is in order, and that the watchdog should wait
+ * for yet another little while to reset the system.
+ * If userspace fails (RAM error, kernel bug, whatever), the
+ * notifications cease to occur, and the hardware watchdog will
+ * reset the system (causing a reboot) after the timeout occurs.
+ *
+ * This WDT driver is different from the other Linux WDT
+ * drivers in several ways:
+ * *) The driver will ping the watchdog by itself, because this
+ * particular WDT has a very short timeout (one second) and it
+ * would be insane to count on any userspace daemon always
+ * getting scheduled within that time frame.
+ * *) This driver expects the userspace daemon to send a specific
+ * character code ('V') to /dev/watchdog before closing the
+ * /dev/watchdog file. If the userspace daemon closes the file
+ * without sending this special character, the driver will assume
+ * that the daemon (and userspace in general) died, and will
+ * stop pinging the WDT without disabling it first. This will
+ * cause a reboot.
+ *
+ * Why `V' ? Well, `V' is the character in ASCII for the value 86,
+ * and we all know that 86 is _the_ most random number in the universe.
+ * Therefore it is the letter that has the slightest chance of occuring
+ * by chance, when the system becomes corrupted.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+#define OUR_NAME "sbc60xxwdt"
+
+/*
+ * You must set these - The driver cannot probe for the settings
+ */
+
+#define WDT_STOP 0x45
+#define WDT_START 0x443
+
+/*
+ * The 60xx board can use watchdog timeout values from one second
+ * to several minutes. The default is one second, so if we reset
+ * the watchdog every ~250ms we should be safe.
+ */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 10 seconds.
+ * If the daemon pulses us every 5 seconds, we can still afford
+ * a 5 second scheduling delay on the (high priority) daemon. That
+ * should be sufficient for a box under any load.
+ */
+
+#define WDT_HEARTBEAT (HZ * 10)
+
+static void wdt_timer_ping(unsigned long);
+static struct timer_list timer;
+static unsigned long next_heartbeat = 0;
+static int wdt_is_open = 0;
+static int wdt_expect_close = 0;
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ if(time_before(jiffies, next_heartbeat))
+ {
+ /* Ping the WDT by reading from WDT_START */
+ inb_p(WDT_START);
+ /* Re-set the timer interval */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+ } else {
+ printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
+ }
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+
+ /* Start the timer */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+ printk(OUR_NAME ": Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer(&timer);
+ inb_p(WDT_STOP);
+ printk(OUR_NAME ": Watchdog timer is now disabled...\n");
+}
+
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
+{
+ /* We can't seek */
+ if(ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /* See if we got the magic character */
+ if(count)
+ {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ wdt_expect_close = 0;
+
+ /* now scan */
+ for(ofs = 0; ofs != count; ofs++)
+ if(buf[ofs] == 'V')
+ wdt_expect_close = 1;
+
+ /* Well, anyhow someone wrote to us, we should return that favour */
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ }
+ return 0;
+}
+
+static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
+{
+ /* No can do */
+ return -EINVAL;
+}
+
+static int fop_open(struct inode * inode, struct file * file)
+{
+ switch(MINOR(inode->i_rdev))
+ {
+ case WATCHDOG_MINOR:
+ /* Just in case we're already talking to someone... */
+ if(wdt_is_open)
+ return -EBUSY;
+ /* Good, fire up the show */
+ wdt_is_open = 1;
+ wdt_startup();
+ return 0;
+
+ default:
+ return -ENODEV;
+ }
+}
+
+static int fop_close(struct inode * inode, struct file * file)
+{
+ if(MINOR(inode->i_rdev) == WATCHDOG_MINOR)
+ {
+ if(wdt_expect_close)
+ wdt_turnoff();
+ else {
+ del_timer(&timer);
+ printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+ }
+ wdt_is_open = 0;
+ return 0;
+}
+
+static long long fop_llseek(struct file *file, long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ static struct watchdog_info ident=
+ {
+ 0,
+ 1,
+ "SB60xx"
+ };
+
+ switch(cmd)
+ {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
+ case WDIOC_KEEPALIVE:
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ return 0;
+ }
+}
+
+static struct file_operations wdt_fops = {
+ owner: THIS_MODULE,
+ llseek: fop_llseek,
+ read: fop_read,
+ write: fop_write,
+ open: fop_open,
+ release: fop_close,
+ ioctl: fop_ioctl
+};
+
+static struct miscdevice wdt_miscdev = {
+ WATCHDOG_MINOR,
+ "watchdog",
+ &wdt_fops
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if(code==SYS_DOWN || code==SYS_HALT)
+ wdt_turnoff();
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier=
+{
+ wdt_notify_sys,
+ 0,
+ 0
+};
+
+static void __exit sbc60xxwdt_unload(void)
+{
+ wdt_turnoff();
+
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(WDT_START,1);
+ release_region(WDT_STOP,1);
+}
+
+static int __init sbc60xxwdt_init(void)
+{
+ int rc = -EBUSY;
+
+ if (!request_region(WDT_STOP, 1, "SBC 60XX WDT"))
+ goto err_out;
+ if (!request_region(WDT_START, 1, "SBC 60XX WDT"))
+ goto err_out_region1;
+
+ init_timer(&timer);
+ timer.function = wdt_timer_ping;
+ timer.data = 0;
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc)
+ goto err_out_region2;
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc)
+ goto err_out_miscdev;
+
+ printk(KERN_INFO OUR_NAME ": WDT driver for 60XX single board computer initialised.\n");
+
+ return 0;
+
+err_out_miscdev:
+ misc_deregister(&wdt_miscdev);
+err_out_region2:
+ release_region(WDT_START,1);
+err_out_region1:
+ release_region(WDT_STOP,1);
+err_out:
+ return rc;
+}
+
+module_init(sbc60xxwdt_init);
+module_exit(sbc60xxwdt_unload)
saa->id = dev->device;
saa->irq = dev->irq;
saa->video_dev.minor = -1;
- saa->saa7146_adr = dev->resource[0].start;
+ saa->saa7146_adr = pci_resource_start(dev, 0);
pci_read_config_byte(dev, PCI_CLASS_REVISION, &saa->revision);
- saa->saa7146_mem = ioremap(((saa->saa7146_adr) &
- PCI_BASE_ADDRESS_MEM_MASK), 0x200);
+
+ saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200);
+ if (!saa->saa7146_mem)
+ return -EIO;
+
memcpy(&(saa->i2c), &saa7146_i2c_bus_template, sizeof(struct i2c_bus));
memcpy(&saa->video_dev, &saa_template, sizeof(saa_template));
sprintf(saa->i2c.name, "stradis%d", num);
*/
unsigned char keyboard_type = KB_101;
-#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__)
+#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
#endif
ucval = keyboard_type;
goto setchar;
-#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__)
+#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
/*
* These cannot be implemented on any machine that implements
* ioperm() in user level (such as Alpha PCs).
case KDGETKEYCODE:
case KDSETKEYCODE:
+ if(!capable(CAP_SYS_ADMIN))
+ perm=0;
return do_kbkeycode_ioctl(cmd, (struct kbkeycode *)arg, perm);
case KDGKBENT:
#
# Makefile for the kernel I2O OSM.
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now inherited from the
-# parent makefile.
-#
-
#
# Note : at this point, these files are compiled on all systems.
# In the future, some of these should be built conditionally.
#
-SUB_DIRS :=
-MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
-
-
-L_TARGET := i2o.a
-L_OBJS :=
-M_OBJS :=
-
-ifeq ($(CONFIG_I2O_PCI),y)
-L_OBJS += i2o_pci.o
-else
- ifeq ($(CONFIG_I2O_PCI),m)
- MX_OBJS += i2o_pci.o
- endif
-endif
-
-ifeq ($(CONFIG_I2O),y)
-LX_OBJS += i2o_core.o i2o_config.o
-else
- ifeq ($(CONFIG_I2O),m)
- MX_OBJS += i2o_core.o i2o_config.o
- endif
-endif
-
-ifeq ($(CONFIG_I2O_BLOCK),y)
-LX_OBJS += i2o_block.o
-else
- ifeq ($(CONFIG_I2O_BLOCK),m)
- MX_OBJS += i2o_block.o
- endif
-endif
+O_TARGET := i2o.o
-ifeq ($(CONFIG_I2O_LAN),y)
-LX_OBJS += i2o_lan.o
-else
- ifeq ($(CONFIG_I2O_LAN),m)
- MX_OBJS += i2o_lan.o
- endif
-endif
+export-objs := i2o_pci.o i2o_core.o i2o_config.o i2o_block.o i2o_lan.o i2o_scsi.o i2o_proc.o
-ifeq ($(CONFIG_I2O_SCSI),y)
-LX_OBJS += i2o_scsi.o
-else
- ifeq ($(CONFIG_I2O_SCSI),m)
- MX_OBJS += i2o_scsi.o
- endif
-endif
+obj-$(CONFIG_I2O_PCI) += i2o_pci.o
+obj-$(CONFIG_I2O) += i2o_core.o i2o_config.o
+obj-$(CONFIG_I2O_BLOCK) += i2o_block.o
+obj-$(CONFIG_I2O_LAN) += i2o_lan.o
+obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o
+obj-$(CONFIG_I2O_PROC) += i2o_proc.o
-ifeq ($(CONFIG_I2O_PROC),y)
-LX_OBJS += i2o_proc.o
-else
- ifeq ($(CONFIG_I2O_PROC),m)
- MX_OBJS += i2o_proc.o
- endif
-endif
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
include $(TOPDIR)/Rules.make
Lan:
o Performance tuning
o Test Fibre Channel code
-o Fix lan_set_mc_list()
Tape:
o Anyone seen anything implementing this ?
II. IOP Access
Access to the I2O subsystem is provided through the device file named
-/dev/i2octl. This file is a character file with major number 10 and minor
+/dev/i2o/ctl. This file is a character file with major number 10 and minor
number 166. It can be created through the following command:
- mknod /dev/i2octl c 10 166
+ mknod /dev/i2o/ctl c 10 166
III. Determining the IOP Count
In the process of determining this. Current idea is to have use
the select() interface to allow user apps to periodically poll
- the /dev/i2octl device for events. When select() notifies the user
+ the /dev/i2o/ctl device for events. When select() notifies the user
that an event is available, the user would call read() to retrieve
a list of all the events that are pending for the specific device.
static struct gendisk i2ob_gendisk =
{
MAJOR_NR,
- "i2ohd",
+ "i2o/hd",
4,
1<<4,
i2ob,
// printk(KERN_INFO "File %p w/id %d has %d events\n",
// inf->fp, inf->q_id, inf->q_len);
- if(inf->fasync)
- kill_fasync(inf->fasync, SIGIO, POLL_IN);
+ kill_fasync(&inf->fasync, SIGIO, POLL_IN);
}
return;
open_files = tmp;
spin_unlock_irqrestore(&i2o_config_lock, flags);
- MOD_INC_USE_COUNT;
return 0;
}
}
spin_unlock_irqrestore(&i2o_config_lock, flags);
- MOD_DEC_USE_COUNT;
return 0;
}
static struct file_operations config_fops =
{
+ owner: THIS_MODULE,
llseek: cfg_llseek,
read: cfg_read,
write: cfg_write,
* I2O configuration spinlock. This isnt a big deal for contention
* so we have one only
*/
-static struct semaphore i2o_configuration_lock;
+
+static DECLARE_MUTEX(i2o_configuration_lock);
/*
* Event spinlock. Used to keep event queue sane and from
switch(msg[4])
{
case I2O_EVT_IND_EXEC_RESOURCE_LIMITS:
- printk(KERN_ERR "iop%d: Out of resources\n", c->unit);
+ printk(KERN_ERR "%s: Out of resources\n", c->name);
break;
case I2O_EVT_IND_EXEC_POWER_FAIL:
- printk(KERN_ERR "iop%d: Power failure\n", c->unit);
+ printk(KERN_ERR "%s: Power failure\n", c->name);
break;
case I2O_EVT_IND_EXEC_HW_FAIL:
entries -= 3;
entries /= 9;
- dprintk(KERN_INFO "I2O: Dynamic LCT Update\n");
- dprintk(KERN_INFO "I2O: Dynamic LCT contains %d entries\n", entries);
+ dprintk(KERN_INFO "%s: Dynamic LCT Update\n",c->name);
+ dprintk(KERN_INFO "%s: Dynamic LCT contains %d entries\n", c->name, entries);
if(!entries)
{
- printk(KERN_INFO "iop%d: Empty LCT???\n", c->unit);
+ printk(KERN_INFO "%s: Empty LCT???\n", c->name);
continue;
}
}
if(!found)
{
- dprintk(KERN_INFO "Deleted device!\n");
+ dprintk(KERN_INFO "i2o_core: Deleted device!\n");
spin_lock(&i2o_dev_lock);
i2o_delete_device(d);
spin_unlock(&i2o_dev_lock);
struct i2o_message *m;
u32 mv;
u32 *msg;
- int count = 0;
/*
* Old 960 steppings had a bug in the I2O unit that caused
m=(struct i2o_message *)bus_to_virt(mv);
msg=(u32*)m;
- count++;
-
/*
* Temporary Debugging
*/
/* That 960 bug again... */
if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF)
mv=I2O_REPLY_READ32(c);
-
}
}
}
if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0)
{
-
buf[16]=0;
printk(KERN_INFO " Device: %s\n", buf);
}
* time, we assume the IOP could not reboot properly.
*/
- dprintk(KERN_INFO "Reset in progress, waiting for reboot\n");
+ dprintk(KERN_INFO "%s: Reset in progress, waiting for reboot...\n",
+ c->name);
time = jiffies;
m = I2O_POST_READ32(c);
m=i2o_wait_message(c, "StatusGet");
if(m==0xFFFFFFFF)
- return -ETIMEDOUT;
-
+ return -ETIMEDOUT;
msg=(u32 *)(c->mem_offset+m);
msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
{
struct i2o_controller *iop, *niop = NULL;
- printk(KERN_INFO "Activating I2O controllers\n");
+ printk(KERN_INFO "Activating I2O controllers...\n");
printk(KERN_INFO "This may take a few minutes if there are many devices\n");
/* In INIT state, Activate IOPs */
for (iop = i2o_controller_chain; iop; iop = niop) {
- dprintk(KERN_INFO "Calling i2o_activate_controller for %s\n",
+ dprintk(KERN_INFO "Calling i2o_activate_controller for %s...\n",
iop->name);
niop = iop->next;
if (i2o_activate_controller(iop) < 0)
* If build_sys_table fails, we kill everything and bail
* as we can't init the IOPs w/o a system table
*/
- dprintk(KERN_INFO "calling i2o_build_sys_table\n");
+ dprintk(KERN_INFO "i2o_core: Calling i2o_build_sys_table...\n");
if (i2o_build_sys_table() < 0) {
i2o_sys_shutdown();
return;
/* If IOP don't get online, we need to rebuild the System table */
for (iop = i2o_controller_chain; iop; iop = niop) {
niop = iop->next;
- dprintk(KERN_INFO "Calling i2o_online_controller for %s\n", iop->name);
+ dprintk(KERN_INFO "Calling i2o_online_controller for %s...\n", iop->name);
if (i2o_online_controller(iop) < 0) {
i2o_delete_controller(iop);
goto rebuild_sys_tab;
/* In READY state, Get status */
if (i2o_status_get(iop) < 0) {
- printk(KERN_INFO "Unable to obtain status of IOP, attempting a reset.\n");
+ printk(KERN_INFO "Unable to obtain status of %s, "
+ "attempting a reset.\n", iop->name);
if (i2o_reset_controller(iop) < 0)
return -1;
}
return -1;
}
+ if (iop->status_block->i2o_version > I2OVER15) {
+ printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O Specification.\n",
+ iop->name);
+ return -1;
+ }
+
if (iop->status_block->iop_state == ADAPTER_STATE_READY ||
iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL ||
iop->status_block->iop_state == ADAPTER_STATE_HOLD ||
iop->status_block->iop_state == ADAPTER_STATE_FAILED)
{
- u32 m[MSG_FRAME_SIZE];
- dprintk(KERN_INFO "%s: Already running, trying to reset\n",
+ dprintk(KERN_INFO "%s: Already running, trying to reset...\n",
iop->name);
-
- i2o_init_outbound_q(iop);
- I2O_REPLY_WRITE32(iop,virt_to_bus(m));
-
if (i2o_reset_controller(iop) < 0)
return -1;
}
u32 *msg;
u32 time;
- dprintk(KERN_INFO "%s: Initializing Outbound Queue\n", c->name);
+ dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name);
m=i2o_wait_message(c, "OutboundInit");
if(m==0xFFFFFFFF)
return -ETIMEDOUT;
/* In READY state */
- dprintk(KERN_INFO "Attempting to enable iop%d\n", iop->unit);
+ dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name);
if (i2o_enable_controller(iop) < 0)
return -1;
/* In OPERATIONAL state */
- dprintk(KERN_INFO "Attempting to get/parse lct iop%d\n", iop->unit);
+ dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name);
if (i2o_lct_get(iop) < 0)
return -1;
*/
if(i2o_status_get(iop)) {
printk(KERN_ERR "%s: Deleting b/c could not get status while"
- "attempting to build system table", iop->name);
+ "attempting to build system table\n", iop->name);
i2o_delete_controller(iop);
sys_tbl->num_entries--;
continue; // try the next one
}
while(m==0xFFFFFFFF && (jiffies-t)<HZ);
-
if(m==0xFFFFFFFF)
{
printk(KERN_ERR "%s: Timeout waiting for message frame!\n",
msg[8] = virt_to_bus(reslist);
if((wait_status = i2o_post_wait(iop, msg, sizeof(msg), 10)))
- return wait_status; /* -DetailedStatus */
+ return wait_status; /* -DetailedStatus */
/*
* Calculate number of bytes of Result LIST
return size;
memcpy(buf, resblk+8, buflen); /* cut off header */
- return buflen < size ? buflen : size;
+ return size;
}
/*
* else return specific fields
* ibuf contains fieldindexes
*
- * if oper == I2O_PARAMS_LIST_GET, gte form specific rows
+ * if oper == I2O_PARAMS_LIST_GET, get from specific rows
* if fieldcount == -1 return all fields
* ibuf contains rowcount, keyvalues
* else return specific fields
i2o_report_common_status(req_status);
if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF))
- i2o_report_common_dsc(detailed_status);
-
- if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F)
+ i2o_report_common_dsc(detailed_status);
+ else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F)
i2o_report_lan_dsc(detailed_status);
else
printk(" / DetailedStatus = %0#4x.\n", detailed_status);
return 0;
}
else
- printk(KERN_INFO "event thread created as pid %d\n", evt_pid);
+ printk(KERN_INFO "I2O: Event thread created as pid %d\n", evt_pid);
if(i2o_num_controllers)
i2o_sys_init();
stat = kill_proc(evt_pid, SIGTERM, 1);
if(!stat) {
int count = 10 * 100;
- while(evt_running && count) {
+ while(evt_running && count--) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
{
printk(KERN_INFO "Loading I2O Core - (c) Copyright 1999 Red Hat Software\n");
- init_MUTEX(&i2o_configuration_lock);
-
if (i2o_install_handler(&i2o_core_handler) < 0)
{
printk(KERN_ERR
#ifdef CONFIG_I2O_BLOCK
i2o_block_init();
#endif
-#ifdef CONFIG_I2O_SCSI
- i2o_scsi_init();
-#endif
#ifdef CONFIG_I2O_LAN
i2o_lan_init();
#endif
/*
* drivers/i2o/i2o_lan.c
*
- * I2O LAN CLASS OSM May 4th 2000
+ * I2O LAN CLASS OSM May 26th 2000
*
* (C) Copyright 1999, 2000 University of Helsinki,
* Department of Computer Science
static struct net_device *i2o_landevs[MAX_LAN_CARDS+1];
static int unit = -1; /* device unit number */
-extern rwlock_t dev_mc_lock;
-
-static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m);
+static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m);
static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m);
static int i2o_lan_receive_post(struct net_device *dev);
static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m);
NULL,
NULL,
NULL,
- "I2O Lan OSM send",
+ "I2O LAN OSM send",
-1,
I2O_CLASS_LAN
};
NULL,
NULL,
NULL,
- "I2O Lan OSM receive",
+ "I2O LAN OSM receive",
-1,
I2O_CLASS_LAN
};
NULL,
NULL,
NULL,
- "I2O Lan OSM",
+ "I2O LAN OSM",
-1,
I2O_CLASS_LAN
};
struct i2o_controller *iop = i2o_dev->controller;
u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]);
- // FIXME on 64-bit host
u32 *sgl_elem = &preserved_msg[4];
struct sk_buff *skb = NULL;
u8 le_flag;
i2o_report_status(KERN_INFO, dev->name, msg);
#endif
- /* DDM has handled transmit request(s), free sk_buffs */
-
+ /* DDM has handled transmit request(s), free sk_buffs.
+ * We get similar single transaction reply also in error cases
+ * (except if msg failure or transaction error).
+ */
while (trl_count) {
dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]);
- dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n",
+ dprintk(KERN_INFO "%s: tx skb freed (trl_count=%d).\n",
dev->name, trl_count);
atomic_dec(&priv->tx_out);
trl_count--;
if (i2o_lan_handle_status(dev, msg))
return;
- /* Getting unused buckets back? */
-
- if (msg[4] & I2O_LAN_DSC_CANCELED ||
- msg[4] & I2O_LAN_DSC_RECEIVE_ABORTED) {
- i2o_lan_release_buckets(dev, msg);
- return;
- }
-
- /* If other DetailedStatusCodes need special code, add it here */
+ i2o_lan_release_buckets(dev, msg);
+ return;
}
#ifdef DRIVERDEBUG
if (i2o_lan_handle_status(dev, msg))
return;
- /* This should NOT be reached */
+ /* In other error cases just report and continue */
+
+ i2o_report_status(KERN_INFO, dev->name, msg);
}
#ifdef DRIVERDEBUG
i2o_report_status(KERN_INFO, dev->name, msg);
#endif
-
switch (msg[1] >> 24) {
- case LAN_RESET:
- case LAN_SUSPEND:
- /* default reply without payload */
+ case LAN_RESET:
+ case LAN_SUSPEND:
+ /* default reply without payload */
break;
- case I2O_CMD_UTIL_EVT_REGISTER:
- case I2O_CMD_UTIL_EVT_ACK:
- i2o_lan_handle_event(dev, msg);
+
+ case I2O_CMD_UTIL_EVT_REGISTER:
+ case I2O_CMD_UTIL_EVT_ACK:
+ i2o_lan_handle_event(dev, msg);
break;
- default:
- printk(KERN_ERR "%s: No handler for the reply.\n",
- dev->name);
- i2o_report_status(KERN_INFO, dev->name, msg);
+
+ case I2O_CMD_UTIL_PARAMS_SET:
+ /* default reply, results in ReplyPayload (not examined) */
+ switch (msg[3] >> 16) {
+ case 1: dprintk(KERN_INFO "%s: Reply to set MAC filter mask.\n",
+ dev->name);
+ break;
+ case 2: dprintk(KERN_INFO "%s: Reply to set MAC table.\n",
+ dev->name);
+ break;
+ default: printk(KERN_WARNING "%s: Bad group 0x%04X\n",
+ dev->name,msg[3] >> 16);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: No handler for the reply.\n",
+ dev->name);
+ i2o_report_status(KERN_INFO, dev->name, msg);
}
}
u32 *pskb = &msg[6];
while (trl_count--) {
- dprintk(KERN_DEBUG "%s: Releasing unused sk_buff %p (trl_count=%d).\n",
+ dprintk(KERN_DEBUG "%s: Releasing unused rx skb %p (trl_count=%d).\n",
dev->name, (struct sk_buff*)(*pskb),trl_count+1);
dev_kfree_skb_irq((struct sk_buff *)(*pskb));
pskb += 1 + trl_elem_size;
struct i2o_controller *iop = i2o_dev->controller;
u32 max_evt_data_size =iop->status_block->inbound_frame_size-5;
struct i2o_reply {
- u8 version_offset;
- u8 msg_flags;
- u16 msg_size;
- u32 tid:12;
- u32 initiator:12;
- u32 function:8;
- u32 initiator_context;
- u32 transaction_context;
+ u32 header[4];
u32 evt_indicator;
- u32 data[max_evt_data_size]; /* max */
+ u32 data[max_evt_data_size];
} *evt = (struct i2o_reply *)msg;
- int evt_data_len = (evt->msg_size - 5) * 4; /* real */
+ int evt_data_len = ((msg[0]>>16) - 5) * 4; /* real size*/
printk(KERN_INFO "%s: I2O event - ", dev->name);
- if (evt->function == I2O_CMD_UTIL_EVT_ACK) {
+ if (msg[1]>>24 == I2O_CMD_UTIL_EVT_ACK) {
printk("Event acknowledgement reply.\n");
return;
}
break;
}
- case I2O_EVT_IND_GENERAL_WARNING:
- printk("General warning 0x%04x.\n", evt->data[0]);
- break;
-
- case I2O_EVT_IND_CONFIGURATION_FLAG:
- printk("Configuration requested.\n");
+ case I2O_EVT_IND_FIELD_MODIFIED: {
+ u16 *work16 = (u16 *)evt->data;
+ printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]);
break;
+ }
- case I2O_EVT_IND_CAPABILITY_CHANGE:
- printk("Capability change 0x%04x.\n", evt->data[0]);
+ case I2O_EVT_IND_VENDOR_EVT: {
+ int i;
+ printk("Vendor event:\n");
+ for (i = 0; i < evt_data_len / 4; i++)
+ printk(" 0x%08x\n", evt->data[i]);
break;
+ }
case I2O_EVT_IND_DEVICE_RESET:
/* Spec 2.0 p. 6-121:
printk("%s: Event Acknowledge timeout.\n", dev->name);
break;
+#if 0
case I2O_EVT_IND_EVT_MASK_MODIFIED:
printk("Event mask modified, 0x%08x.\n", evt->data[0]);
break;
- case I2O_EVT_IND_FIELD_MODIFIED: {
- u16 *work16 = (u16 *)evt->data;
- printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]);
+ case I2O_EVT_IND_GENERAL_WARNING:
+ printk("General warning 0x%04x.\n", evt->data[0]);
+ break;
+ case I2O_EVT_IND_CONFIGURATION_FLAG:
+ printk("Configuration requested.\n");
break;
- }
- case I2O_EVT_IND_VENDOR_EVT: {
- int i;
- printk("Vendor event:\n");
- for (i = 0; i < evt_data_len / 4; i++)
- printk(" 0x%08x\n", evt->data[i]);
+ case I2O_EVT_IND_CAPABILITY_CHANGE:
+ printk("Capability change 0x%04x.\n", evt->data[0]);
break;
- }
case I2O_EVT_IND_DEVICE_STATE:
printk("Device state changed 0x%08x.\n", evt->data[0]);
break;
-
+#endif
case I2O_LAN_EVT_LINK_DOWN:
+ netif_carrier_off(dev);
printk("Link to the physical device is lost.\n");
break;
case I2O_LAN_EVT_LINK_UP:
+ netif_carrier_on(dev);
printk("Link to the physical device is (re)established.\n");
break;
case I2O_LAN_EVT_MEDIA_CHANGE:
printk("Media change.\n");
break;
-
default:
printk("0x%08x. No handler.\n", evt->evt_indicator);
}
-
- /* Note: EventAck necessary only for events that cause the device to
- * syncronize with the user.
- */
}
/*
printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n",
dev->name);
else
- dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to &d.\n",
+ dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to %d.\n",
dev->name, val);
return;
}
struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
struct i2o_device *i2o_dev = priv->i2o_dev;
struct i2o_controller *iop = i2o_dev->controller;
+ u32 mc_addr_group[64];
MOD_INC_USE_COUNT;
i2o_lan_reset(dev);
+ /* Get the max number of multicast addresses */
+
+ if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1,
+ &mc_addr_group, sizeof(mc_addr_group)) < 0 ) {
+ printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name);
+ MOD_DEC_USE_COUNT;
+ return -EAGAIN;
+ }
+ priv->max_size_mc_table = mc_addr_group[8];
+
+ /* Malloc space for free bucket list to resuse reveive post buckets */
+
priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *),
GFP_KERNEL);
if (priv->i2o_fbl == NULL) {
/*
* i2o_lan_batch_send(): Send packets in batch.
* Both i2o_lan_sdu_send and i2o_lan_packet_send use this.
- *
- * This is a coarse first approximation for the tx_batching.
- * If you come up with something better, please tell me. -taneli
*/
static void i2o_lan_batch_send(struct net_device *dev)
{
* If tx_batch_mode = 0x01 forced to batch mode
* If tx_batch_mode = 0x10 switch automatically, current mode immediate
* If tx_batch_mode = 0x11 switch automatically, current mode batch
- * If gap between two packets is > 2 ticks, switch to immediate
+ * If gap between two packets is > 0 ticks, switch to immediate
*/
if (priv->tx_batch_mode >> 1) // switch automatically
priv->tx_batch_mode = tickssofar ? 0x02 : 0x03;
if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) {
dev->trans_start = jiffies;
i2o_post_message(iop, priv->m);
- dprintk(KERN_DEBUG"%s: %d packets sent.\n", dev->name, priv->tx_count);
+ dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count);
priv->tx_count = 0;
}
return (struct net_device_stats *)&priv->stats;
}
-/*
- * i2o_lan_set_mc_list(): Enable a network device to receive packets
- * not send to the protocol address.
+/*
+ * i2o_lan_set_mc_filter(): Post a request to set multicast filter.
*/
-
-static void i2o_lan_set_mc_list(struct net_device *dev)
+int i2o_lan_set_mc_filter(struct net_device *dev, u32 filter_mask)
{
- struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
struct i2o_device *i2o_dev = priv->i2o_dev;
struct i2o_controller *iop = i2o_dev->controller;
- u32 filter_mask;
- u32 max_size_mc_table;
- u32 mc_addr_group[64];
-
-// This isn't safe yet in SMP. Needs to be async.
-// Seems to work in uniprocessor environment.
-
-return;
-
-// read_lock_bh(&dev_mc_lock);
- spin_lock(&dev->xmit_lock);
- dev->xmit_lock_owner = smp_processor_id();
+ u32 msg[10];
+
+ msg[0] = TEN_WORD_MSG_SIZE | SGL_OFFSET_5;
+ msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid;
+ msg[2] = priv->unit << 16 | lan_context;
+ msg[3] = 0x0001 << 16 | 3 ; // TransactionContext: group&field
+ msg[4] = 0;
+ msg[5] = 0xCC000000 | 16; // Immediate data SGL
+ msg[6] = 1; // OperationCount
+ msg[7] = 0x0001<<16 | I2O_PARAMS_FIELD_SET; // Group, Operation
+ msg[8] = 3 << 16 | 1; // FieldIndex, FieldCount
+ msg[9] = filter_mask; // Value
+
+ return i2o_post_this(iop, msg, sizeof(msg));
+}
- if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1,
- &mc_addr_group, sizeof(mc_addr_group)) < 0 ) {
- printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name);
- return;
+/*
+ * i2o_lan_set_mc_table(): Post a request to set LAN_MULTICAST_MAC_ADDRESS table.
+ */
+int i2o_lan_set_mc_table(struct net_device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ struct dev_mc_list *mc;
+ u32 msg[10 + 2 * dev->mc_count];
+ u8 *work8 = (u8 *)(msg + 10);
+
+ msg[0] = I2O_MESSAGE_SIZE(10 + 2 * dev->mc_count) | SGL_OFFSET_5;
+ msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid;
+ msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+ msg[3] = 0x0002 << 16 | (u16)-1; // TransactionContext
+ msg[4] = 0; // OperationFlags
+ msg[5] = 0xCC000000 | (16 + 8 * dev->mc_count); // Immediate data SGL
+ msg[6] = 2; // OperationCount
+ msg[7] = 0x0002 << 16 | I2O_PARAMS_TABLE_CLEAR; // Group, Operation
+ msg[8] = 0x0002 << 16 | I2O_PARAMS_ROW_ADD; // Group, Operation
+ msg[9] = dev->mc_count << 16 | (u16)-1; // RowCount, FieldCount
+
+ for (mc = dev->mc_list; mc ; mc = mc->next, work8 += 8) {
+ memset(work8, 0, 8);
+ memcpy(work8, mc->dmi_addr, mc->dmi_addrlen); // Values
}
- max_size_mc_table = mc_addr_group[8];
+ return i2o_post_this(iop, msg, sizeof(msg));
+}
+
+/*
+ * i2o_lan_set_multicast_list(): Enable a network device to receive packets
+ * not send to the protocol address.
+ */
+static void i2o_lan_set_multicast_list(struct net_device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ u32 filter_mask;
if (dev->flags & IFF_PROMISC) {
filter_mask = 0x00000002;
- printk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name);
- } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > max_size_mc_table) {
+ dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name);
+ } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > priv->max_size_mc_table) {
filter_mask = 0x00000004;
- printk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name);
+ dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name);
} else if (dev->mc_count) {
- struct dev_mc_list *mc;
- u8 mc_table[2 + 8 * dev->mc_count]; // RowCount, Addresses
- u64 *work64 = (u64 *)(mc_table + 2);
-
filter_mask = 0x00000000;
- printk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name);
-
- /* Fill multicast addr table */
-
- memset(mc_table, 0, sizeof(mc_table));
- memcpy(mc_table, &dev->mc_count, 2);
- for (mc = dev->mc_list; mc ; mc = mc->next, work64++ )
- memcpy(work64, mc->dmi_addr, mc->dmi_addrlen);
-
- /* Clear old mc table, copy new table to <iop,tid> */
-
- if (i2o_clear_table(iop, i2o_dev->lct_data.tid, 0x0002) < 0)
- printk(KERN_INFO "%s: Unable to clear LAN_MULTICAST_MAC_ADDRESS table.\n", dev->name);
-
- if ((i2o_row_add_table(iop, i2o_dev->lct_data.tid, 0x0002, -1,
- mc_table, sizeof(mc_table))) < 0)
- printk(KERN_INFO "%s: Unable to set LAN_MULTICAST_MAC_ADDRESS table.\n", dev->name);
+ dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name);
+ if (i2o_lan_set_mc_table(dev) < 0)
+ printk(KERN_WARNING "%s: Unable to send MAC table.\n", dev->name);
} else {
filter_mask = 0x00000300; // Broadcast, Multicast disabled
- printk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name);
+ dprintk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name);
}
- /* Finally copy new FilterMask to <iop,tid> */
-
- if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0001, 3,
- &filter_mask, sizeof(filter_mask)) <0)
- printk(KERN_WARNING "%s: Unable to set MAC FilterMask.\n", dev->name);
-
- dev->xmit_lock_owner = -1;
- spin_unlock(&dev->xmit_lock);
-// read_unlock_bh(&dev_mc_lock);
-
- return;
-}
-
-static struct tq_struct i2o_lan_set_mc_list_task = {
- 0, 0, (void (*)(void *))i2o_lan_set_mc_list, (void *) 0
-};
+ /* Finally copy new FilterMask to DDM */
-/*
- * i2o_lan_set_multicast_list():
- * Queue routine i2o_lan_set_mc_list() to be called later.
- * Needs to be async.
- */
-static void i2o_lan_set_multicast_list(struct net_device *dev)
-{
- if (in_interrupt()) {
- i2o_lan_set_mc_list_task.data = (void *)dev;
- queue_task(&i2o_lan_set_mc_list_task, &tq_scheduler);
- } else
- i2o_lan_set_mc_list(dev);
+ if (i2o_lan_set_mc_filter(dev, filter_mask) < 0)
+ printk(KERN_WARNING "%s: Unable to send MAC FilterMask.\n", dev->name);
}
/*
/*
* i2o_lan.h I2O LAN Class definitions
*
- * I2O LAN CLASS OSM April 3rd 2000
+ * I2O LAN CLASS OSM May 26th 2000
*
* (C) Copyright 1999, 2000 University of Helsinki,
* Department of Computer Science
#define I2O_LAN_RX_COPYBREAK 200
#define I2O_LAN_TX_TIMEOUT (1*HZ)
#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */
-#define I2O_LAN_EVENT_MASK 0; /* 0=None, 0xFFC00002=All */
+#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */
/* LAN types */
#define I2O_LAN_ETHERNET 0x0030
#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E
#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F
#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10
-#define I2O_LAN_DSC_TEMP_SUSPENDED_STATE 0x11
+#define I2O_LAN_DSC_SUSPENDED 0x11
struct i2o_packet_info {
u32 offset : 24;
spinlock_t tx_lock;
+ u32 max_size_mc_table; /* max number of multicast addresses */
+
/* LAN OSM configurable parameters are here: */
u16 max_buckets_out; /* max nbr of buckets to send to DDM */
for(i=0; i<6; i++)
{
/* Skip I/O spaces */
- if(!(dev->resource[i].flags&PCI_BASE_ADDRESS_SPACE))
+ if(!(pci_resource_flags(dev, i) & IORESOURCE_IO))
{
- memptr=dev->resource[i].start;
+ memptr = pci_resource_start(dev, i);
break;
}
}
printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n");
continue;
}
+ if (pci_enable_device(dev))
+ continue;
printk(KERN_INFO "i2o: I2O controller on bus %d at %d.\n",
dev->bus->number, dev->devfn);
pci_set_master(dev);
extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int);
extern int i2o_scsi_bios_param(Disk *, kdev_t, int *);
extern void i2o_scsi_setup(char *str, int *ints);
+extern int i2o_scsi_release(struct Scsi_Host *host);
#define I2OSCSI { \
next: NULL, \
seekcard = GAZEL_R685;
for (nbseek = 0; nbseek < 3; nbseek++) {
if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) {
-
+ if (pci_enable_device(dev_tel))
+ return 1;
pci_irq = dev_tel->irq;
pci_ioaddr0 = dev_tel->resource[ 1].start;
pci_ioaddr1 = dev_tel->resource[ 2].start;
dev_hfcpci);
i++;
if (tmp_hfcpci) {
- if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->resource[ 0].start & PCI_BASE_ADDRESS_IO_MASK)))
+ if (pci_enable_device(tmp_hfcpci))
+ continue;
+ if ((card->para[0]) && (card->para[0] != pci_resource_start(tmp_hfcpci, 0)))
continue;
else
break;
}
if ((dev_netjet = pci_find_device(PCI_VENDOR_TRAVERSE_TECH,
PCI_NETJET_ID, dev_netjet))) {
+ if (pci_enable_device(dev_netjet))
+ return (0);
cs->irq = dev_netjet->irq;
if (!cs->irq) {
printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
return(0);
}
- cs->hw.njet.base = dev_netjet->resource[ 0].start
- & PCI_BASE_ADDRESS_IO_MASK;
+ cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
if (!cs->hw.njet.base) {
printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
return(0);
cs->subtyp = 0;
if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS,
PCI_NICCY_ID, niccy_dev))) {
+ if (pci_enable_device(niccy_dev))
+ return (0);
/* get IRQ */
if (!niccy_dev->irq) {
printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
return(0);
}
- cs->hw.niccy.cfg_reg = niccy_dev->resource[ 0].start & PCI_BASE_ADDRESS_IO_MASK;
+ cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0);
if (!niccy_dev->resource[ 1].start) {
printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
return(0);
}
- pci_ioaddr = niccy_dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK;
+ pci_ioaddr = pci_resource_start(niccy_dev, 1);
cs->subtyp = NICCY_PCI;
} else {
printk(KERN_WARNING "Niccy: No PCI card found\n");
}
if ((dev_sedl = pci_find_device(PCI_VENDOR_SEDLBAUER,
PCI_SPEEDPCI_ID, dev_sedl))) {
+ if (pci_enable_device(dev_sedl))
+ return (0);
cs->irq = dev_sedl->irq;
if (!cs->irq) {
printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
return(0);
}
- cs->hw.sedl.cfg_reg = dev_sedl->resource[ 0].start &
- PCI_BASE_ADDRESS_IO_MASK;
+ cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0);
} else {
printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
return(0);
}
cs->irq_flags |= SA_SHIRQ;
cs->hw.sedl.bus = SEDL_BUS_PCI;
- pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID,
- &sub_vendor_id);
- pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID,
- &sub_id);
+ sub_vendor_id = dev_sedl->subsystem_vendor;
+ sub_id = dev_sedl->subsystem_device;
printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
sub_vendor_id, sub_id);
printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
return(0);
}
if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) {
+ if (pci_enable_device(dev_tel))
+ return (0);
cs->irq = dev_tel->irq;
if (!cs->irq) {
printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
id_list[id_idx].device_id,
dev_w6692);
- if (dev_w6692)
+ if (dev_w6692) {
+ if (pci_enable_device(dev_w6692))
+ continue;
break;
+ }
id_idx++;
}
if (dev_w6692) {
pci_irq = dev_w6692->irq;
/* I think address 0 is allways the configuration area */
/* and address 1 is the real IO space KKe 03.09.99 */
- pci_ioaddr = dev_w6692->resource[ 1].start;
+ pci_ioaddr = pci_resource_start(dev_w6692, 1);
}
if (!found) {
printk(KERN_WARNING "W6692: No PCI card found\n");
printk(KERN_WARNING "W6692: No IRQ for PCI card found\n");
return (0);
}
- pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
if (!pci_ioaddr) {
printk(KERN_WARNING "W6692: NO I/O Base Address found\n");
return (0);
card_last = NULL;
while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_PLX,
akt_pcidev)) != NULL) {
-
+ if (pci_enable_device(akt_pcidev))
+ continue;
if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) {
printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
return;
card->myid = cardmax; /* set own id */
card->bus = akt_pcidev->bus->number;
card->devfn = akt_pcidev->devfn; /* slot + function */
- pcibios_read_config_word(card->bus, card->devfn, PCI_SUBSYSTEM_ID, &card->subsysid);
- pcibios_read_config_byte(card->bus, card->devfn, PCI_INTERRUPT_LINE, &irq);
- card->irq = irq;
- card->iobase = akt_pcidev->resource[ PCI_REG_PLX_IO_BASE].start & PCI_BASE_ADDRESS_IO_MASK;
- card->plxbase = akt_pcidev->resource[ PCI_REG_PLX_MEM_BASE].start;
- card->membase = akt_pcidev->resource[ PCI_REG_MEMORY_BASE].start;
+ card->subsysid = akt_pcidev->subsystem_device;
+ card->irq = akt_pcidev->irq;
+ card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
+ card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
+ card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
card->brdtype = BD_NONE; /* unknown */
card->debug_flags = DEF_DEB_FLAGS; /* set default debug */
card->faxchans = 0; /* default no fax channels */
: "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
: "memory", "ax");
#else
+ unsigned char* b = (unsigned char*) buff;
+ unsigned char* t = (unsigned char*) table;
while (n--)
- *buff++ = table[*(unsigned char *)buff];
+ *b++ = t[*b];
#endif
}
#define MODEM_PARANOIA_CHECK
#define MODEM_DO_RESTART
+#ifdef CONFIG_DEVFS_FS
+static char *isdn_ttyname_ttyI = "isdn/ttyI%d";
+static char *isdn_ttyname_cui = "isdn/cui%d";
+#else
static char *isdn_ttyname_ttyI = "ttyI";
static char *isdn_ttyname_cui = "cui";
+#endif
+
static int bit2si[8] =
{1, 5, 7, 7, 7, 7, 7, 7};
static int si2bit[8] =
if (base_addr > 0x1ff) /* Check a single specified location. */
return el2_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (addr = addrs; *addr; addr++) {
int i;
#if ! defined(no_probe_nonshared_memory) && ! defined (HAVE_DEVLIST)
return el2_pio_probe(dev);
#else
- return ENODEV;
+ return -ENODEV;
#endif
}
if (base_addr > 0x1ff) /* Check a single specified location. */
return el2_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; netcard_portlist[i]; i++) {
int ioaddr = netcard_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
/* Reset and/or avoid any lurking NE2000 */
if (inb(ioaddr + 0x408) == 0xff) {
mdelay(1);
- return ENODEV;
+ return -ENODEV;
}
/* We verify that it's a 3C503 board by checking the first three octets
/* ASIC location registers should be 0 or have only a single bit set. */
if ( (iobase_reg & (iobase_reg - 1))
|| (membase_reg & (membase_reg - 1))) {
- return ENODEV;
+ return -ENODEV;
}
saved_406 = inb_p(ioaddr + 0x406);
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
/* Restore the register we frobbed. */
outb(saved_406, ioaddr + 0x406);
- return ENODEV;
+ return -ENODEV;
}
- if (load_8390_module("3c503.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("3c503.c: Passed a NULL device.\n");
{
int this_dev, found = 0;
+ if (load_8390_module("3c503.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
struct net_device *dev = &dev_el2[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
if (base_addr > 0x1ff) /* Check a single specified location. */
return el16_probe1(dev, base_addr);
else if (base_addr != 0)
- return ENXIO; /* Don't probe at all. */
+ return -ENXIO; /* Don't probe at all. */
for (i = 0; netcard_portlist[i]; i++) {
int ioaddr = netcard_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
static int __init el16_probe1(struct net_device *dev, int ioaddr)
&& inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O')
;
else
- return ENODEV;
+ return -ENODEV;
/* Allocate a new 'dev' if needed. */
if (dev == NULL)
irqval = request_irq(irq, &el16_interrupt, 0, "3c507", dev);
if (irqval) {
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
- return EAGAIN;
+ return -EAGAIN;
}
/* We've committed to using the board, and can start filling in *dev. */
goto no_pnp;
for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
struct pci_dev *idev = NULL;
- int irq, j;
+ int irq;
while((idev = isapnp_find_dev(NULL,
corkscrew_isapnp_adapters[i].vendor,
corkscrew_isapnp_adapters[i].function,
entry = (++vp->cur_rx) % RX_RING_SIZE;
}
/* Refill the Rx ring buffers. */
- for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
struct sk_buff *skb;
entry = vp->dirty_rx % RX_RING_SIZE;
if (vp->rx_skbuff[entry] == NULL) {
unsigned int size = 0;
if (MCA_bus == 0) {
- return ENODEV;
+ return -ENODEV;
}
/* search through the slots for the 3c523. */
slot = mca_find_adapter(ELMC_MCA_ID, 0);
printk(KERN_ERR "%s: memprobe, Can't find memory at 0x%lx!\n", dev->name,
dev->mem_start);
release_region(dev->base_addr, ELMC_IO_EXTENT);
- return ENODEV;
+ return -ENODEV;
}
dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597
and the EtherLink XL 3c900 and 3c905 cards.
- The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ The author may be reached as becker@scyld.com, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- Increased the loop counter in wait_for_completion from 2,000 to 4,000.
LK1.1.5 28 April 2000, andrewm
- - Added powerpc defines
+ - Added powerpc defines (John Daniel <jdaniel@etresoft.com> said these work...)
- Some extra diagnostics
- In vortex_error(), reset the Tx on maxCollisions. Otherwise most
chips usually get a Tx timeout.
- In vortex_up(), don't make Wn3_config initialisation dependent upon has_nway
(this came across from 3c575_cb).
+ LK1.1.6 06 Jun 2000, andrewm
+ - Backed out the PPC defines.
+ - Use del_timer_sync(), mod_timer().
+ - Fix wrapped ulong comparison in boomerang_rx()
+ - Add IS_TORNADO, use it to suppress 3c905C checksum error msg
+ (Donald Becker, I Lee Hetherington <ilh@sls.lcs.mit.edu>)
+ - Replace union wn3_config with BFINS/BFEXT manipulation for
+ sparc64 (Pete Zaitcev, Peter Jones)
+ - In vortex_error, do_tx_reset and vortex_tx_timeout(Vortex):
+ do a netif_wake_queue() to better recover from errors. (Anders Pedersen,
+ Donald Becker)
+ - Print a warning on out-of-memory (rate limited to 1 per 10 secs)
+ - Added two more Cardbus 575 NICs: 5b57 and 6564 (Paul Wagland)
+
- See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.3 for more details.
*/
* elimination of all the tests and reduced cache footprint.
*/
+/* A few values that may be tweaked. */
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 32
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1512 effectively disables this feature. */
static int max_interrupt_work = 32;
/* Give the NIC an extra reset at the end of vortex_up() */
static int extra_reset = 0;
+/* Tx timeout interval (millisecs) */
+static int watchdog = 400;
/* Allow aggregation of Tx interrupts. Saves CPU load at the cost
* of possible Tx stalls if the system is blocking interrupts
* AKPM 26 April 2000: enabling this still gets vestigial Tx timeouts
* in a heavily loaded (collision-prone) 10BaseT LAN. Should be OK with
* switched Ethernet.
+ * AKPM 24May00: vestigial timeouts have been removed by later fixes.
*/
#define tx_interrupt_mitigation 1
debugging. */
static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
-/* A few values that may be tweaked. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 32
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
-
#ifndef __OPTIMIZE__
#error You must compile this file with the correct options!
#error See the last lines of the source file.
#include <asm/bitops.h>
#include <asm/io.h>
-/* John Daniel <jdaniel@etresoft.com> said these work... */
-#ifdef __powerpc__
-#define outsl outsl_ns
-#define insl insl_ns
-#endif
-
/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
This is only in the support-all-kernels source code. */
#include <linux/delay.h>
static char version[] __devinitdata =
-"3c59x.c:v0.99L+LK1.1.5 30 Apr 2000 Donald Becker and others http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html " "$Revision: 1.78 $\n";
+"3c59x.c:v0.99L+LK1.1.6 28 May 2000 Donald Becker and others. http://www.scyld.com/network/vortex.html " "$Revision: 1.97 $\n";
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("3Com 3c59x/3c90x/3c575 series Vortex/Boomerang/Cyclone driver");
MODULE_PARM(debug, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(compaq_ioaddr, "i");
MODULE_PARM(compaq_irq, "i");
MODULE_PARM(compaq_device_id, "i");
+MODULE_PARM(watchdog, "i");
/* Operational parameter that usually are not changed. */
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
};
-enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4,
- EEPROM_230=8, /* AKPM: Uses 0x230 as the base bitmpas for EEPROM reads */
- HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, };
+enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, IS_TORNADO=8,
+ EEPROM_230=0x10, /* AKPM: Uses 0x230 as the base bitmaps for EEPROM reads */
+ HAS_PWR_CTRL=0x20, HAS_MII=0x40, HAS_NWAY=0x80, HAS_CB_FNS=0x100, };
enum vortex_chips {
CH_3C595_2,
CH_3C595_3,
- CH_VORTEX,
CH_3C900_1,
CH_3C900_2,
CH_3C900_3,
-
CH_3C900_4,
+
CH_3C900_5,
CH_3C900B_FL,
CH_3C905_1,
CH_3C905_2,
-
CH_3C905B_1,
+
CH_3C905B_2,
CH_3C905B_FX,
CH_3C905C,
CH_3C980,
+ CH_3C9805,
CH_3CSOHO100_TX,
CH_3C555,
+ CH_3C575,
CH_3C575_1,
CH_3CCFE575,
- CH_3CCFE575CT,
+ CH_3CCFE575CT,
CH_3CCFE656,
CH_3CCFEM656,
+ CH_3CCFEM656_1,
CH_3C450,
};
int drv_flags;
int io_size;
} vortex_info_tbl[] __devinitdata = {
+#define EISA_TBL_OFFSET 0 /* Offset of this entry for vortex_eisa_init */
{"3c590 Vortex 10Mbps",
PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c592 EISA 10mbps Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */
{"3c595 Vortex 100base-MII",
PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
-#define EISA_TBL_OFFSET 6 /* Offset of this entry for vortex_eisa_init */
- {"3Com Vortex",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
{"3c900 Boomerang 10baseT",
PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
{"3c900 Boomerang 10Mbps Combo",
PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
{"3c900 Cyclone 10Mbps TPO", /* AKPM: from Don's 0.99M */
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
-
{"3c900 Cyclone 10Mbps Combo",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+
{"3c900 Cyclone 10Mbps TPC", /* AKPM: from Don's 0.99M */
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
{"3c900B-FL Cyclone 10base-FL",
PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
{"3c905 Boomerang 100baseT4",
PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
-
{"3c905B Cyclone 100baseTx",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+
{"3c905B Cyclone 10/100/BNC",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
{"3c905B-FX Cyclone 100baseFx",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
{"3c905C Tornado",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+ PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, },
{"3c980 Cyclone",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+ {"3c980 10/100 Base-TX NIC(Python-T)",
+ PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
{"3cSOHO100-TX Hurricane",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
{"3c555 Laptop Hurricane",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+ {"3c575 [Megahertz] 10/100 LAN CardBus",
+ PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_230, 128, },
{"3c575 Boomerang CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_230, 64, },
+ PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_230, 128, },
{"3CCFE575 Cyclone CardBus",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_230, 128, },
+
{"3CCFE575CT Cyclone CardBus",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_230, 128, },
-
{"3CCFE656 Cyclone CardBus",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_230, 128, },
{"3CCFEM656 Cyclone CardBus",
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_230, 128, },
- {"3c450 Cyclone/unknown", /* AKPM: from Don's 0.99N */
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+ {"3CCFEM656 Cyclone CardBus(0x6564)", /* From pcmcia-cs-3.1.5 */
+ PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_230, 128, },
+ {"3c450 HomePNA Tornado", /* AKPM: from Don's 0.99Q */
+ PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, },
+
{0,}, /* 0 terminated list. */
};
{ 0x10B7, 0x5951, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_2 },
{ 0x10B7, 0x5952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_3 },
- { 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VORTEX },
{ 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_1 },
{ 0x10B7, 0x9001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_2 },
{ 0x10B7, 0x9004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_3 },
-
{ 0x10B7, 0x9005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_4 },
+
{ 0x10B7, 0x9006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_5 },
{ 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL },
{ 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 },
{ 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 },
-
{ 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 },
+
{ 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 },
{ 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX },
{ 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C },
{ 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 },
+ { 0x10B7, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9805 },
{ 0x10B7, 0x7646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CSOHO100_TX },
{ 0x10B7, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C555 },
+ { 0x10B7, 0x5b57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575 },
{ 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_1 },
{ 0x10B7, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575 },
- { 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT },
+ { 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT },
{ 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 },
{ 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 },
+ { 0x10B7, 0x6564, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656_1 },
{ 0x10B7, 0x4500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C450 },
+
{0,} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, vortex_pci_tbl);
enum Window3 { /* Window 3: MAC/config bits. */
Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
};
-union wn3_config {
- int i;
- struct w3_config_fields {
- unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
- int pad8:8;
- unsigned int ram_split:2, pad18:2, xcvr:4, autoselect:1;
- int pad24:7;
- } u;
-};
+
+#define BFEXT(value, offset, bitcount) \
+ ((((unsigned long)(value)) >> (offset)) & ((1 << (bitcount)) - 1))
+
+#define BFINS(lhs, rhs, offset, bitcount) \
+ (((lhs) & ~((((1 << (bitcount)) - 1)) << (offset))) | \
+ (((rhs) & ((1 << (bitcount)) - 1)) << (offset)))
+
+#define RAM_SIZE(v) BFEXT(v, 0, 3)
+#define RAM_WIDTH(v) BFEXT(v, 3, 1)
+#define RAM_SPEED(v) BFEXT(v, 4, 2)
+#define ROM_SIZE(v) BFEXT(v, 6, 2)
+#define RAM_SPLIT(v) BFEXT(v, 16, 2)
+#define XCVR(v) BFEXT(v, 20, 4)
+#define AUTOSELECT(v) BFEXT(v, 24, 1)
enum Window4 { /* Window 4: Xcvr/media bits. */
Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
/* The Rx and Tx descriptor lists.
Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
alignment contraint on tx_ring[] and rx_ring[]. */
-#define LAST_FRAG 0x80000000 /* Last Addr/Len pair in descriptor. */
+#define LAST_FRAG 0x80000000 /* Last Addr/Len pair in descriptor. */
+#define DN_COMPLETE 0x00010000 /* This packet has been downloaded */
struct boom_rx_desc {
u32 next; /* Last entry points to 0. */
s32 status;
struct net_device *dev;
static int printed_version = 0;
int retval;
+ struct vortex_chip_info * const vci = &vortex_info_tbl[chip_idx];
if (!printed_version) {
printk (KERN_INFO "%s", version);
printk(KERN_INFO "%s: 3Com %s %s at 0x%lx, ",
dev->name,
pdev ? "PCI" : "EISA",
- vortex_info_tbl[chip_idx].name,
+ vci->name,
ioaddr);
/* private struct aligned and zeroed by init_etherdev */
dev->base_addr = ioaddr;
dev->irq = irq;
dev->mtu = mtu;
- vp->has_nway = (vortex_info_tbl[chip_idx].drv_flags & HAS_NWAY) ? 1 : 0;
- vp->io_size = vortex_info_tbl[chip_idx].io_size;
+ vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
+ vp->io_size = vci->io_size;
/* module list only for EISA devices */
if (pdev == NULL) {
/* PCI-only startup logic */
if (pdev) {
/* EISA resources already marked, so only PCI needs to do this here */
- if (!request_region (ioaddr, vortex_info_tbl[chip_idx].io_size,
- dev->name)) {
+ if (!request_region (ioaddr, vci->io_size, dev->name)) {
printk (KERN_ERR "%s: Cannot reserve I/O resource 0x%x @ 0x%lx, aborting\n",
- dev->name, vortex_info_tbl[chip_idx].io_size, ioaddr);
+ dev->name, vci->io_size, ioaddr);
retval = -EBUSY;
goto free_dev;
}
}
/* enable bus-mastering if necessary */
- if (vortex_info_tbl[chip_idx].flags & PCI_USES_MASTER)
+ if (vci->flags & PCI_USES_MASTER)
pci_set_master (pdev);
}
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
{
- int base = (vortex_info_tbl[chip_idx].drv_flags & EEPROM_230) ? 0x230 : EEPROM_Read;
+ int base = (vci->drv_flags & EEPROM_230) ? 0x230 : EEPROM_Read;
for (i = 0; i < 0x40; i++) {
int timer;
outw(base + i, ioaddr + Wn0EepromCmd);
checksum ^= eeprom[i++];
checksum = (checksum ^ (checksum >> 8)) & 0xff;
}
- if (checksum != 0x00)
+ if ((checksum != 0x00) && !(vci->drv_flags & IS_TORNADO))
printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
for (i = 0; i < 3; i++)
((u16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]);
dev->irq);
#endif
- if (pdev && vortex_info_tbl[chip_idx].drv_flags & HAS_CB_FNS) {
+ if (pdev && vci->drv_flags & HAS_CB_FNS) {
u32 fn_st_addr; /* Cardbus function status space */
fn_st_addr = pci_resource_start (pdev, 2);
if (fn_st_addr)
{
static const char * ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
- union wn3_config config;
+ unsigned int config;
EL3WINDOW(3);
vp->available_media = inw(ioaddr + Wn3_Options);
if ((vp->available_media & 0xff) == 0) /* Broken 3c916 */
vp->available_media = 0x40;
- config.i = inl(ioaddr + Wn3_Config);
+ config = inl(ioaddr + Wn3_Config);
if (vortex_debug > 1)
printk(KERN_DEBUG " Internal config register is %4.4x, "
- "transceivers %#x.\n", config.i, inw(ioaddr + Wn3_Options));
+ "transceivers %#x.\n", config, inw(ioaddr + Wn3_Options));
printk(KERN_INFO " %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
- 8 << config.u.ram_size,
- config.u.ram_width ? "word" : "byte",
- ram_split[config.u.ram_split],
- config.u.autoselect ? "autoselect/" : "",
- config.u.xcvr > XCVR_ExtMII ? "<invalid transceiver>" :
- media_tbl[config.u.xcvr].name);
- vp->default_media = config.u.xcvr;
- vp->autoselect = config.u.autoselect;
+ 8 << RAM_SIZE(config),
+ RAM_WIDTH(config) ? "word" : "byte",
+ ram_split[RAM_SPLIT(config)],
+ AUTOSELECT(config) ? "autoselect/" : "",
+ XCVR(config) > XCVR_ExtMII ? "<invalid transceiver>" :
+ media_tbl[XCVR(config)].name);
+ vp->default_media = XCVR(config);
+ vp->autoselect = AUTOSELECT(config);
}
if (vp->media_override != 7) {
dev->do_ioctl = &vortex_ioctl;
dev->set_multicast_list = &set_rx_mode;
dev->tx_timeout = &vortex_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
+ dev->watchdog_timeo = (watchdog * HZ) / 1000;
return 0;
free_region:
- release_region (ioaddr, vortex_info_tbl[chip_idx].io_size);
+ release_region (ioaddr, vci->io_size);
free_dev:
unregister_netdev(dev);
kfree (dev);
{
long ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
- union wn3_config config;
+ unsigned int config;
int i, device_id;
if (vp->pdev)
/* Before initializing select the active media port. */
EL3WINDOW(3);
- config.i = inl(ioaddr + Wn3_Config);
+ config = inl(ioaddr + Wn3_Config);
if (vp->media_override != 7) {
if (vortex_debug > 1)
dev->name, media_tbl[dev->if_port].name);
vp->full_duplex = vp->force_fd;
- config.u.xcvr = dev->if_port;
+ config = BFINS(config, dev->if_port, 20, 4);
//AKPM if (!vp->has_nway)
{
if (vortex_debug > 6)
printk(KERN_DEBUG "vortex_up(): writing 0x%x to InternalConfig\n",
- config.i);
- outl(config.i, ioaddr + Wn3_Config);
+ config);
+ outl(config, ioaddr + Wn3_Config);
}
if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
if (vortex_debug > 1) {
printk(KERN_DEBUG "%s: vortex_up() InternalConfig %8.8x.\n",
- dev->name, config.i);
+ dev->name, config);
}
wait_for_completion(dev, TxReset);
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
- netif_start_queue (dev);
-
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
/* Allow status bits to be seen. */
}
outw(TxEnable, ioaddr + EL3_CMD);
}
+ netif_start_queue (dev);
}
static int
int ok = 0;
int media_status, mii_status, old_window;
- if (vortex_debug > 1)
+ if (vortex_debug > 1) {
printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
dev->name, media_tbl[dev->if_port].name);
+ printk(KERN_DEBUG "dev->watchdog_timeo=%d\n", dev->watchdog_timeo);
+ }
disable_irq(dev->irq);
old_window = inw(ioaddr + EL3_CMD) >> 13;
ok = 1;
}
if ( ! ok) {
- union wn3_config config;
+ unsigned int config;
do {
dev->if_port = media_tbl[dev->if_port].next;
media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
EL3WINDOW(3);
- config.i = inl(ioaddr + Wn3_Config);
- config.u.xcvr = dev->if_port;
- outl(config.i, ioaddr + Wn3_Config);
+ config = inl(ioaddr + Wn3_Config);
+ config = BFINS(config, dev->if_port, 20, 4);
+ outl(config, ioaddr + Wn3_Config);
outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
ioaddr + EL3_CMD);
if (vortex_debug > 1)
- printk(KERN_DEBUG "wrote 0x%08x to Wn3_Config\n", config.i);
+ printk(KERN_DEBUG "wrote 0x%08x to Wn3_Config\n", config);
/* AKPM: FIXME: Should reset Rx & Tx here. P60 of 3c90xc.pdf */
}
EL3WINDOW(old_window);
printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
dev->name, media_tbl[dev->if_port].name);
- vp->timer.expires = RUN_AT(next_tick);
- add_timer(&vp->timer);
+ mod_timer(&vp->timer, RUN_AT(next_tick));
if (vp->deferred)
outw(FakeIntr, ioaddr + EL3_CMD);
+ timer_exit(&vp->timer);
return;
}
printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
dev->name, inb(ioaddr + TxStatus),
inw(ioaddr + EL3_STATUS));
+
/* Slight code bloat to be user friendly. */
if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
printk(KERN_ERR "%s: Transmitter encountered 16 collisions --"
}
}
- if (vortex_debug > 1)
+ if (vortex_debug > 0)
dump_tx_ring(dev);
wait_for_completion(dev, TxReset);
netif_stop_queue (dev);
outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
outw(DownUnstall, ioaddr + EL3_CMD);
- } else
+ } else {
vp->stats.tx_dropped++;
+ netif_wake_queue(dev);
+ }
/* Issue Tx Enable */
outw(TxEnable, ioaddr + EL3_CMD);
} else {
wait_for_completion(dev, TxReset);
outw(TxEnable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
}
}
}
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
while (vp->cur_tx - dirty_tx > 0) {
int entry = dirty_tx % TX_RING_SIZE;
+#if 1 /* AKPM: the latter is faster, but cyclone-only */
if (inl(ioaddr + DownListPtr) ==
vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc))
break; /* It still hasn't been processed. */
+#else
+ if ((vp->tx_ring[entry].status & DN_COMPLETE) == 0)
+ break; /* It still hasn't been processed. */
+#endif
+
if (vp->tx_skbuff[entry]) {
struct sk_buff *skb = vp->tx_skbuff[entry];
netif_wake_queue (dev);
}
}
- if (vp->tx_full)
- netif_stop_queue (dev);
/* Check for all uncommon interrupts at once. */
if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq))
if (vp->cb_fn_base) /* The PCMCIA people are idiots. */
writel(0x8000, vp->cb_fn_base + 4);
- } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+ } while ((status = inw(ioaddr + EL3_STATUS)) & IntLatch);
if (vortex_debug > 4)
printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
+ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
pci_dma_sync_single(vp->pdev, dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
entry = (++vp->cur_rx) % RX_RING_SIZE;
}
/* Refill the Rx ring buffers. */
- for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
struct sk_buff *skb;
entry = vp->dirty_rx % RX_RING_SIZE;
if (vp->rx_skbuff[entry] == NULL) {
skb = dev_alloc_skb(PKT_BUF_SZ);
- if (skb == NULL)
+ if (skb == NULL) {
+ static unsigned long last_jif;
+ if ((jiffies - last_jif) > 10 * HZ) {
+ printk(KERN_WARNING "%s: memory shortage\n", dev->name);
+ last_jif = jiffies;
+ }
break; /* Bad news! */
+ }
skb->dev = dev; /* Mark as being used by this device. */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
netif_stop_queue (dev);
- del_timer(&vp->timer);
+ del_timer_sync(&vp->timer);
/* Turn off statistics ASAP. We update vp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
int stalled = inl(ioaddr + PktStatus) & 0x04; /* Possible racy. But it's only debug stuff */
wait_for_completion(dev, DownStall);
- printk(KERN_DEBUG " Flags; bus-master %d, full %d; dirty %d(%d) "
+ printk(KERN_ERR " Flags; bus-master %d, full %d; dirty %d(%d) "
"current %d(%d).\n",
vp->full_bus_master_tx, vp->tx_full,
vp->dirty_tx, vp->dirty_tx % TX_RING_SIZE,
vp->cur_tx, vp->cur_tx % TX_RING_SIZE);
- printk(KERN_DEBUG " Transmit list %8.8x vs. %p.\n",
+ printk(KERN_ERR " Transmit list %8.8x vs. %p.\n",
inl(ioaddr + DownListPtr),
&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
for (i = 0; i < TX_RING_SIZE; i++) {
- printk(KERN_DEBUG " %d: @%p length %8.8x status %8.8x\n", i,
+ printk(KERN_ERR " %d: @%p length %8.8x status %8.8x\n", i,
&vp->tx_ring[i],
le32_to_cpu(vp->tx_ring[i].length),
le32_to_cpu(vp->tx_ring[i].status));
#include <asm/io.h>
-#define RTL8139_VERSION "0.9.5"
+#define RTL8139_VERSION "0.9.7"
#define RTL8139_MODULE_NAME "8139too"
#define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION
#define PFX RTL8139_MODULE_NAME ": "
IntrMask = 0x3C,
IntrStatus = 0x3E,
TxConfig = 0x40,
+ ChipVersion = 0x43,
RxConfig = 0x44,
Timer = 0x48, /* A general-purpose counter. */
RxMissed = 0x4C, /* 24 bits valid, write clears. */
typedef enum {
CH_8139 = 0,
+ CH_8139_K,
CH_8139A,
- CH_8139A_G,
CH_8139B,
CH_8130,
CH_8139C,
/* directly indexed by chip_t, above */
const static struct {
const char *name;
- u32 version; /* from RTL8139C docs */
+ u8 version; /* from RTL8139C docs */
u32 RxConfigMask; /* should clear the bits supported by this chip */
} rtl_chip_info[] = {
{ "RTL-8139",
- 0x60000000,
+ 0x40,
0xf0fe0040, /* XXX copied from RTL8139A, verify */
},
- { "RTL-8139A",
- 0x70000000,
- 0xf0fe0040,
+ { "RTL-8139 rev K",
+ 0x60,
+ 0xf0fe0040, /* XXX copied from RTL8139A, verify */
},
- { "RTL-8139A rev. G",
- 0x70800000,
+ { "RTL-8139A",
+ 0x70,
0xf0fe0040,
},
{ "RTL-8139B",
- 0x78000000,
+ 0x78,
0xf0fc0040
},
{ "RTL-8130",
- 0x7C000000,
+ 0x7C,
0xf0fe0040, /* XXX copied from RTL8139A, verify */
},
{ "RTL-8139C",
- 0x74000000,
+ 0x74,
0xf0fc0040, /* XXX copied from RTL8139B, verify */
},
}
/* identify chip attached to board */
- tmp = RTL_R32 (TxConfig) & TxVersionMask;
+ tmp = RTL_R8 (ChipVersion);
for (i = arraysize (rtl_chip_info) - 1; i >= 0; i--)
if (tmp == rtl_chip_info[i].version) {
tp->chipset = i;
tp->chipset = 0;
match:
- DPRINTK ("chipset id (%d/%d/%d) == %d, '%s'\n",
- CH_8139,
- CH_8139A,
- CH_8139B,
+ DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+ tmp,
tp->chipset,
rtl_chip_info[tp->chipset].name);
RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
- /* unlock Config[01234] and BMCR register writes */
- RTL_W8_F (Cfg9346, Cfg9346_Unlock);
- udelay (100);
-
- tp->cur_rx = 0;
-
- /* init Rx ring buffer DMA address */
- RTL_W32_F (RxBuf, tp->rx_ring_dma);
-
- /* init Tx buffer DMA addresses */
- for (i = 0; i < NUM_TX_DESC; i++)
- RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
-
/* Must enable Tx/Rx before setting transfer thresholds! */
RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) |
CmdRxEnb | CmdTxEnb);
/* Check this value: the documentation for IFG contradicts ifself. */
RTL_W32 (TxConfig, (TX_DMA_BURST << 8));
-#if 0
- /* if link status not ok... */
- if ((RTL_R16 (BasicModeStatus) & (1<<2)) == 0) {
- printk (KERN_INFO "%s: no link, starting NWay\n", dev->name);
-
- /* Reset N-Way to chipset defaults */
- RTL_W16 (BasicModeCtrl, RTL_R16 (BasicModeCtrl) | (1<<15));
- for (i = 1000; i > 0; i--)
- if ((RTL_R8 (BasicModeCtrl) & (1<<15)) == 0)
- break;
-
- /* Set N-Way to sane defaults */
- RTL_W16_F (FIFOTMS, RTL_R16 (FIFOTMS) & ~(1<<7));
- RTL_W16_F (NWayAdvert, RTL_R16 (NWayAdvert) |
- (1<<13)|(1<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<0));
- RTL_W16_F (BasicModeCtrl, RTL_R16 (BasicModeCtrl) |
- (1<<13)|(1<<12)|(1<<9)|(1<<8));
- RTL_W8_F (MediaStatus, RTL_R8 (MediaStatus) | (1<<7) | (1<<6));
-
- /* check_duplex() here. */
- /* XXX writing Config1 here is flat out wrong */
- /* RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); */
- }
-#endif
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ udelay (10);
- if (tp->chipset > CH_8139) {
+ tp->cur_rx = 0;
+
+ if (tp->chipset >= CH_8139A) {
tmp = RTL_R8 (Config1) & Config1Clear;
+ tmp |= Cfg1_Driver_Load;
tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */
RTL_W8_F (Config1, tmp);
+ } else {
+ u8 foo = RTL_R8 (Config1) & Config1Clear;
+ RTL_W8 (Config1, tp->full_duplex ? (foo|0x60) : (foo|0x20));
}
- if (tp->chipset == CH_8139B) {
+ if (tp->chipset >= CH_8139B) {
tmp = RTL_R8 (Config4) & ~(1<<2);
/* chip will clear Rx FIFO overflow automatically */
tmp |= (1<<7);
RTL_W8 (Config4, tmp);
- }
- /* disable magic packet scanning, which is enabled
- * when PM is enabled above (Config1) */
- RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5));
+ /* disable magic packet scanning, which is enabled
+ * when PM is enabled above (Config1) */
+ RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5));
+ }
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Lock);
+ udelay (10);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
RTL_W32_F (RxMissed, 0);
/* no early-rx interrupts */
RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+ /* make sure RxTx has started */
+ RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) |
+ CmdRxEnb | CmdTxEnb);
+
/* Enable all known interrupts by setting the interrupt mask. */
RTL_W16_F (IntrMask, rtl8139_intr_mask);
mii_reg5 = mdio_read (dev, tp->phys[0], 5);
+#if 0
if (!tp->duplex_lock && mii_reg5 != 0xffff) {
int duplex = (mii_reg5 & 0x0100)
|| (mii_reg5 & 0x01C0) == 0x0040;
tp->phys[0], mii_reg5);
RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
}
}
+#endif
rtl8139_tune_twister (dev, tp);
} else {
eth_copy_and_sum (skb,
&rx_ring[ring_offset + 4],
- rx_size, 0);
- skb_put (skb, rx_size);
+ rx_size - 4, 0);
+ skb_put (skb, rx_size - 4);
}
skb->protocol = eth_type_trans (skb, dev);
netif_rx (skb);
EXPORT_SYMBOL(ethdev_init);
EXPORT_SYMBOL(NS8390_init);
-struct module *NS8390_module = NULL;
-
int init_module(void)
{
- NS8390_module = &__this_module;
return 0;
}
void cleanup_module(void)
{
- NS8390_module = NULL;
}
#endif /* MODULE */
#if defined(LOAD_8390_BY_KMOD) && defined(MODULE) && !defined(NS8390_CORE)
/* Function pointers to be mapped onto the 8390 core support */
-static int (*S_ethdev_init)(struct net_device *dev);
-static void (*S_NS8390_init)(struct net_device *dev, int startp);
-static int (*S_ei_open)(struct net_device *dev);
-static int (*S_ei_close)(struct net_device *dev);
-static void (*S_ei_interrupt)(int irq, void *dev_id, struct pt_regs *regs);
+static int (*S_ethdev_init)(struct net_device *dev) = NULL;
+static void (*S_NS8390_init)(struct net_device *dev, int startp) = NULL;
+static int (*S_ei_open)(struct net_device *dev) = NULL;
+static int (*S_ei_close)(struct net_device *dev) = NULL;
+static void (*S_ei_interrupt)(int irq, void *dev_id, struct pt_regs *regs) = NULL;
-
-#define NS8390_KSYSMS_PRESENT ( \
- get_module_symbol(NULL, "ethdev_init") != 0 && \
- get_module_symbol(NULL, "NS8390_init") != 0 && \
- get_module_symbol(NULL, "ei_open") != 0 && \
- get_module_symbol(NULL, "ei_close") != 0 && \
- get_module_symbol(NULL, "ei_interrupt") != 0)
+extern __inline__ void unload_8390_module(void)
+{
+ if (S_ethdev_init) {
+ put_module_symbol((unsigned long)S_ethdev_init);
+ S_ethdev_init = NULL;
+ }
+ if (S_NS8390_init) {
+ put_module_symbol((unsigned long)S_NS8390_init);
+ S_NS8390_init = NULL;
+ }
+ if (S_ei_open) {
+ put_module_symbol((unsigned long)S_ei_open);
+ S_ei_open = NULL;
+ }
+ if (S_ei_close) {
+ put_module_symbol((unsigned long)S_ei_close);
+ S_ei_close = NULL;
+ }
+ if (S_ei_interrupt) {
+ put_module_symbol((unsigned long)S_ei_interrupt);
+ S_ei_interrupt = NULL;
+ }
+}
extern __inline__ int load_8390_module(const char *driver)
{
+ /* Do we actually need to handle this case? */
+ if (S_ethdev_init) {
+ printk(KERN_DEBUG "%s: load_8390_module called when pointers already present\n", driver);
+ return 0;
+ }
+
+ /* Attempt to get the first symbol */
+ S_ethdev_init = (void *)get_module_symbol(NULL, "ethdev_init");
- if (! NS8390_KSYSMS_PRESENT) {
+ if (!S_ethdev_init) {
+ /* It failed. See if we have request_module() */
int (*request_mod)(const char *module_name);
if (get_module_symbol("", "request_module") == 0) {
return -ENOSYS;
}
+ /* OK - we have request_module() - try it */
request_mod = (void*)get_module_symbol("", "request_module");
if (request_mod("8390")) {
printk("%s: request to load the 8390 module failed.\n", driver);
return -ENOSYS;
}
- /* Check if module really loaded and is valid */
- if (! NS8390_KSYSMS_PRESENT) {
- printk("%s: 8390.o not found/invalid or failed to load.\n", driver);
- return -ENOSYS;
- }
-
printk(KERN_INFO "%s: auto-loaded 8390 module.\n", driver);
+
+ /* Retry getting ethdev_init */
+ S_ethdev_init = (void *)get_module_symbol(NULL, "ethdev_init");
}
- /* Map the functions into place */
- S_ethdev_init = (void*)get_module_symbol(0, "ethdev_init");
+ /* Get addresses for the other functions */
S_NS8390_init = (void*)get_module_symbol(0, "NS8390_init");
S_ei_open = (void*)get_module_symbol(0, "ei_open");
S_ei_close = (void*)get_module_symbol(0, "ei_close");
S_ei_interrupt = (void*)get_module_symbol(0, "ei_interrupt");
- return 0;
-}
-
-/*
- * Since a kmod aware driver won't explicitly show a dependence on the
- * exported 8390 functions (due to the mapping above), the 8390 module
- * (if present, and not in-kernel) needs to be protected from garbage
- * collection. NS8390_module is only defined for a modular 8390 core.
- */
-
-extern __inline__ void lock_8390_module(void)
-{
- struct module **mod = (struct module**)get_module_symbol(0, "NS8390_module");
-
- if (mod != NULL && *mod != NULL)
- __MOD_INC_USE_COUNT(*mod);
+ /* Check if module really loaded and is valid */
+ if (!S_ethdev_init || !S_NS8390_init || !S_ei_open || !S_ei_close
+ || !S_ei_interrupt) {
+ unload_8390_module();
+ printk("%s: 8390.o not found/invalid or failed to load.\n", driver);
+ return -ENOSYS;
}
-extern __inline__ void unlock_8390_module(void)
-{
- struct module **mod = (struct module**)get_module_symbol(0, "NS8390_module");
-
- if (mod != NULL && *mod != NULL)
- __MOD_DEC_USE_COUNT(*mod);
+ return 0;
}
/*
#else /* not a module or kmod support not wanted */
#define load_8390_module(driver) 0
-#define lock_8390_module() do { } while (0)
-#define unlock_8390_module() do { } while (0)
+#define unload_8390_module() do { } while (0)
extern int ethdev_init(struct net_device *dev);
extern void NS8390_init(struct net_device *dev, int startp);
extern int ei_open(struct net_device *dev);
tristate ' MIPS JAZZ onboard SONIC Ethernet support' CONFIG_MIPS_JAZZ_SONIC
fi
if [ "$CONFIG_SGI_IP27" = "y" ]; then
- bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH
+ bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH
fi
if [ "$CONFIG_SUPERH" = "y" -a "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then
tristate ' National DP83902AV support' CONFIG_STNIC
tristate ' 3c503 "EtherLink II" support' CONFIG_EL2
tristate ' 3c505 "EtherLink Plus" support' CONFIG_ELPLUS
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' 3c507 support (EXPERIMENTAL)' CONFIG_EL16
+ tristate ' 3c507 "EtherLink 16" support (EXPERIMENTAL)' CONFIG_EL16
fi
tristate ' 3c509/3c529 (MCA)/3c579 "EtherLink III" support' CONFIG_EL3
- tristate ' 3c515 ISA Fast EtherLink' CONFIG_3C515
+ tristate ' 3c515 ISA "Fast EtherLink"' CONFIG_3C515
if [ "$CONFIG_MCA" = "y" ]; then
- tristate ' 3c523 EtherLinkMC support' CONFIG_ELMC
+ tristate ' 3c523 "EtherLink/MC" support' CONFIG_ELMC
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' 3c527 EtherLink/MC 32 support (EXPERIMENTAL)' CONFIG_ELMC_II
+ tristate ' 3c527 "EtherLink/MC 32" support (EXPERIMENTAL)' CONFIG_ELMC_II
fi
fi
tristate ' 3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
tristate ' EtherWORKS 3 (DE203, DE204, DE205) support' CONFIG_EWRK3
fi
tristate ' EtherExpress 16 support' CONFIG_EEXPRESS
- tristate ' EtherExpressPro support' CONFIG_EEXPRESS_PRO
+ tristate ' EtherExpressPro support/EtherExpress 10 (i82595) support' CONFIG_EEXPRESS_PRO
if [ "$CONFIG_OBSOLETE" = "y" ]; then
tristate ' FMV-181/182/183/184 support' CONFIG_FMV18X
fi
if [ "$CONFIG_MCA" = "y" ]; then
tristate ' SKnet MCA support' CONFIG_SKMC
tristate ' NE/2 (ne2000 MCA version) support' CONFIG_NE2_MCA
+ tristate ' IBM LAN Adapter/A support' CONFIG_IBMLANA
fi
bool ' EISA, VLB, PCI and on board controllers' CONFIG_NET_PCI
if [ "$CONFIG_NET_PCI" = "y" ]; then
mainmenu_option next_comment
comment 'Ethernet (1000 Mbit)'
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- # tristate 'Packet Engines Hamachi GNIC-II support (EXPERIMENTAL)' CONFIG_HAMACHI
- tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN
- fi
- tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC
- if [ "$CONFIG_ACENIC" != "n" ]; then
- bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I
- fi
- tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ # tristate 'Packet Engines Hamachi GNIC-II support (EXPERIMENTAL)' CONFIG_HAMACHI
+ tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN
+fi
+tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC
+if [ "$CONFIG_ACENIC" != "n" ]; then
+ bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I
+fi
+tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
+
endmenu
bool 'FDDI driver support' CONFIG_FDDI
dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP
+ dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP
fi
fi
endif
endif
-ifeq ($(CONFIG_ISDN),y)
- ifeq ($(CONFIG_ISDN_PPP),y)
- obj-y += slhc.o ppp_deflate.o
- endif
-else
- ifeq ($(CONFIG_ISDN),m)
- ifeq ($(CONFIG_ISDN_PPP),y)
- obj-m += slhc.o ppp_deflate.o
- endif
- endif
+ifeq ($(CONFIG_ISDN_PPP),y)
+ obj-$(CONFIG_ISDN) += slhc.o
endif
ifeq ($(CONFIG_ARCNET),y)
obj-$(CONFIG_EL16) += 3c507.o
obj-$(CONFIG_ELMC) += 3c523.o
obj-$(CONFIG_SKMC) += sk_mca.o
+obj-$(CONFIG_IBMLANA) += ibmlana.o
obj-$(CONFIG_ELMC_II) += 3c527.o
obj-$(CONFIG_EL3) += 3c509.o
obj-$(CONFIG_3C515) += 3c515.o
extern int sgiseeq_probe(struct net_device *);
extern int atarilance_probe(struct net_device *);
extern int sun3lance_probe(struct net_device *);
-extern int a2065_probe(struct net_device *);
-extern int ariadne_probe(struct net_device *);
extern int ariadne2_probe(struct net_device *);
extern int hydra_probe(struct net_device *);
extern int apne_probe(struct net_device *);
#ifdef CONFIG_SUN3LANCE /* sun3 onboard Lance chip */
{sun3lance_probe, 0},
#endif
-#ifdef CONFIG_A2065 /* Commodore/Ameristar A2065 Ethernet Board */
- {a2065_probe, 0},
-#endif
-#ifdef CONFIG_ARIADNE /* Village Tronic Ariadne Ethernet Board */
- {ariadne_probe, 0},
-#endif
#ifdef CONFIG_ARIADNE2 /* Village Tronic Ariadne II Ethernet Board */
{ariadne2_probe, 0},
#endif
int burst_sizes; /* ledma SBus burst sizes */
#endif
struct timer_list multicast_timer;
+ struct net_device *dev; /* Backpointer */
+ struct lance_private *next_module;
};
+#ifdef MODULE
+static struct lance_private *root_a2065_dev = NULL;
+#endif
+
#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
lp->tx_old+lp->tx_ring_mod_mask-lp->tx_new:\
lp->tx_old - lp->tx_new-1)
static int __init a2065_probe(void)
{
- struct net_device *dev = NULL;
- static int called = 0;
struct zorro_dev *z = NULL;
-
- if (called)
- return -ENODEV;
- called++;
+ struct net_device *dev;
+ struct lance_private *priv;
+ int res = -ENODEV;
while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
- unsigned long board, base_addr, ram_start;
+ unsigned long board, base_addr, mem_start;
int is_cbm;
- struct lance_private *priv;
if (z->id == ZORRO_PROD_CBM_A2065_1 ||
z->id == ZORRO_PROD_CBM_A2065_2)
board = z->resource.start;
base_addr = board+A2065_LANCE;
- ram_start = board+A2065_RAM;
+ mem_start = board+A2065_RAM;
if (!request_mem_region(base_addr, sizeof(struct lance_regs),
"Am7990"))
continue;
- if (!request_mem_region(ram_start, A2065_RAM_SIZE, "RAM")) {
+ if (!request_mem_region(mem_start, A2065_RAM_SIZE, "RAM")) {
release_mem_region(base_addr,
sizeof(struct lance_regs));
continue;
if (dev == NULL) {
release_mem_region(base_addr,
sizeof(struct lance_regs));
- release_mem_region(ram_start, A2065_RAM_SIZE);
+ release_mem_region(mem_start, A2065_RAM_SIZE);
return -ENOMEM;
}
priv = (struct lance_private *)dev->priv;
memset(priv, 0, sizeof(struct lance_private));
+ priv->dev = dev;
+ dev->dev_addr[0] = 0x00;
if (is_cbm) { /* Commodore */
- dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x80;
dev->dev_addr[2] = 0x10;
} else { /* Ameristar */
- dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x00;
dev->dev_addr[2] = 0x9f;
}
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
dev->base_addr = ZTWO_VADDR(base_addr);
- dev->mem_start = ZTWO_VADDR(ram_start);
+ dev->mem_start = ZTWO_VADDR(mem_start);
dev->mem_end = dev->mem_start+A2065_RAM_SIZE;
priv->ll = (volatile struct lance_regs *)dev->base_addr;
dev->set_multicast_list = &lance_set_multicast;
dev->dma = 0;
+#ifdef MODULE
+ priv->next_module = root_a2065_dev;
+ root_a2065_dev = priv;
+#endif
ether_setup(dev);
init_timer(&priv->multicast_timer);
priv->multicast_timer.data = (unsigned long) dev;
priv->multicast_timer.function =
(void (*)(unsigned long)) &lance_set_multicast;
- return(0);
+ res = 0;
}
- return(-ENODEV);
+ return res;
}
static void __exit a2065_cleanup(void)
{
#ifdef MODULE
- struct lance_private *priv = (struct lance_private *)a2065_dev.priv;
+ struct lance_private *next;
+ struct net_device *dev;
- unregister_netdev(&a2065_dev);
- release_mem_region(ZTWO_PADDR(a2065_dev.base_addr),
- sizeof(struct lance_regs));
- release_mem_region(ZTWO_PADDR(a2065_dev.mem_start), A2065_RAM_SIZE);
- kfree(priv);
+ while (root_a2065_dev) {
+ next = root_a2065_dev->next_module;
+ dev = root_a2065_dev->dev;
+ unregister_netdev(dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr),
+ sizeof(struct lance_regs));
+ release_mem_region(ZTWO_PADDR(dev->mem_start), A2065_RAM_SIZE);
+ kfree(dev);
+ root_a2065_dev = next;
+ }
#endif
}
if (ioaddr > 0x1ff) /* Check a single specified location. */
return ac_probe1(ioaddr, dev);
else if (ioaddr > 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
if ( ! EISA_bus)
- return ENXIO;
+ return -ENXIO;
for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
if (check_region(ioaddr, AC_IO_EXTENT))
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
static int __init ac_probe1(int ioaddr, struct net_device *dev)
|| inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
|| inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
printk(", not found (invalid prefix).\n");
- return ENODEV;
+ return -ENODEV;
}
#endif
printk (" nothing! Unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
printk(" IRQ %d, %s port\n", dev->irq, port_name[dev->if_port]);
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EINVAL;
+ return -EINVAL;
}
dev->mem_start = (unsigned long)ioremap(dev->mem_start, AC_STOP_PG*0x100);
if (dev->mem_start == 0) {
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
ei_status.reg0 = 1; /* Use as remap flag */
printk("ac3200.c: remapped %dkB card memory to virtual address %#lx\n",
{
int this_dev, found = 0;
+ if (load_8390_module("ac3200.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
struct net_device *dev = &dev_ac32[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
pdev = pci_find_slot(awc_pci_bus, awc_pci_dev);
if (!pdev)
continue;
+ if (pci_enable_device(pdev))
+ continue;
vendor = pdev->vendor;
device = pdev->device;
pci_irq_line = pdev->irq;
#include "am79c961a.h"
static void am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs);
-static void am79c961_rx (struct net_device *dev, struct dev_priv *priv);
-static void am79c961_tx (struct net_device *dev, struct dev_priv *priv);
static unsigned int net_debug = NET_DEBUG;
" : : "r" (val), "r" (reg), "r" (0xf0000464));
}
+static inline unsigned short
+read_rreg (unsigned int base_addr, unsigned int reg)
+{
+ unsigned short v;
+ __asm__("str%?h %1, [%2] @ NET_RAP
+ ldr%?h %0, [%2, #-4] @ NET_RDP
+ " : "=r" (v): "r" (reg), "r" (0xf0000464));
+ return v;
+}
+
static inline void
write_ireg (unsigned long base, unsigned int reg, unsigned short val)
{
__asm__("str%?h %1, [%2] @ NET_RAP
- str%?h %0, [%2, #8] @ NET_RDP
+ str%?h %0, [%2, #8] @ NET_IDP
" : : "r" (val), "r" (reg), "r" (0xf0000464));
}
"r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
static inline void
-am_writebuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
+am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
{
offset = 0xe0000000 + (offset << 1);
length = (length + 1) & ~1;
}
}
-static inline unsigned short
-read_rreg (unsigned int base_addr, unsigned int reg)
-{
- unsigned short v;
- __asm__("str%?h %1, [%2] @ NET_RAP
- ldr%?h %0, [%2, #-4] @ NET_IDP
- " : "=r" (v): "r" (reg), "r" (0xf0000464));
- return v;
-}
-
-static inline unsigned short
-am_readword (struct net_device *dev, unsigned long off)
+/*
+ * This reads a 16-bit quantity in little-endian
+ * mode from the am79c961 buffer.
+ */
+static inline unsigned short am_readword(struct net_device *dev, u_int off)
{
unsigned long address = 0xe0000000 + (off << 1);
unsigned short val;
}
static inline void
-am_readbuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
+am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
{
offset = 0xe0000000 + (offset << 1);
length = (length + 1) & ~1;
am79c961_init_for_open(struct net_device *dev)
{
struct dev_priv *priv = (struct dev_priv *)dev->priv;
- unsigned long hdr_addr, first_free_addr;
unsigned long flags;
unsigned char *p;
+ u_int hdr_addr, first_free_addr;
int i;
save_flags_cli (flags);
-
- write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */
write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
-
restore_flags (flags);
+ write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */
+ write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */
+ write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */
+ write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
+
+ for (i = LADRL; i <= LADRH; i++)
+ write_rreg (dev->base_addr, i, 0);
+
+ for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
+ write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
+
+ i = MODE_PORT_10BT;
+ if (dev->flags & IFF_PROMISC)
+ i |= MODE_PROMISC;
+
+ write_rreg (dev->base_addr, MODE, i);
+ write_rreg (dev->base_addr, POLLINT, 0);
+ write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
+ write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
+
first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
hdr_addr = 0;
for (i = 0; i < TX_BUFFERS; i++) {
priv->txbuffer[i] = first_free_addr;
am_writeword (dev, hdr_addr, first_free_addr);
- am_writeword (dev, hdr_addr + 2, 0);
- am_writeword (dev, hdr_addr + 4, 0);
+ am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP);
+ am_writeword (dev, hdr_addr + 4, 0xf000);
am_writeword (dev, hdr_addr + 6, 0);
first_free_addr += 1600;
hdr_addr += 8;
}
- for (i = LADRL; i <= LADRH; i++)
- write_rreg (dev->base_addr, i, 0);
-
- for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
- write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
-
- i = MODE_PORT0;
- if (dev->flags & IFF_PROMISC)
- i |= MODE_PROMISC;
-
- write_rreg (dev->base_addr, MODE, i);
write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
write_rreg (dev->base_addr, BASERXH, 0);
write_rreg (dev->base_addr, BASETXL, priv->txhdr);
write_rreg (dev->base_addr, BASERXH, 0);
- write_rreg (dev->base_addr, POLLINT, 0);
- write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
- write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
save_flags_cli (flags);
- write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
unsigned short multi_hash[4], mode;
int i, stopped;
- mode = MODE_PORT0;
+ mode = MODE_PORT_10BT;
if (dev->flags & IFF_PROMISC) {
mode |= MODE_PROMISC;
dev->trans_start = jiffies;
restore_flags (flags);
- if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN))
+ /*
+ * If the next packet is owned by the ethernet device,
+ * then the tx ring is full and we can't add another
+ * packet.
+ */
+ if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) {
+ printk(KERN_DEBUG"tx ring full, stopping queue\n");
netif_stop_queue(dev);
+ }
dev_kfree_skb(skb);
return 0;
}
-static void
-am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- struct dev_priv *priv = (struct dev_priv *)dev->priv;
- unsigned int status;
-
-#if NET_DEBUG > 1
- if(net_debug & DEBUG_INT)
- printk(KERN_DEBUG "am79c961irq: %d ", irq);
-#endif
-
- status = read_rreg (dev->base_addr, CSR0);
- write_rreg (dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
-
- if (status & CSR0_RINT) /* Got a packet(s). */
- am79c961_rx (dev, priv);
- if (status & CSR0_TINT) /* Packets transmitted */
- am79c961_tx (dev, priv);
- if (status & CSR0_MISS)
- priv->stats.rx_dropped ++;
-
-#if NET_DEBUG > 1
- if(net_debug & DEBUG_INT)
- printk("done\n");
-#endif
-}
-
/*
* If we have a good packet(s), get it/them out of the buffers.
*/
static void
am79c961_rx(struct net_device *dev, struct dev_priv *priv)
{
- unsigned long hdraddr;
- unsigned long pktaddr;
-
do {
- unsigned long status;
struct sk_buff *skb;
+ u_int hdraddr;
+ u_int pktaddr;
+ u_int status;
int len;
hdraddr = priv->rxhdr + (priv->rxtail << 3);
continue;
}
- len = am_readword (dev, hdraddr + 6);
- skb = dev_alloc_skb (len + 2);
+ len = am_readword(dev, hdraddr + 6);
+ skb = dev_alloc_skb(len + 2);
if (skb) {
- unsigned char *buf;
-
skb->dev = dev;
- skb_reserve (skb, 2);
- buf = skb_put (skb, len);
+ skb_reserve(skb, 2);
- am_readbuffer (dev, pktaddr, buf, len);
- am_writeword (dev, hdraddr + 2, RMD_OWN);
+ am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
+ am_writeword(dev, hdraddr + 2, RMD_OWN);
skb->protocol = eth_type_trans(skb, dev);
- netif_rx (skb);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ priv->stats.rx_bytes += len;
priv->stats.rx_packets ++;
} else {
am_writeword (dev, hdraddr + 2, RMD_OWN);
am79c961_tx(struct net_device *dev, struct dev_priv *priv)
{
do {
- unsigned long hdraddr;
- unsigned long status;
+ u_int hdraddr;
+ u_int status;
+int bufnum;
+bufnum = priv->txtail;
hdraddr = priv->txhdr + (priv->txtail << 3);
status = am_readword (dev, hdraddr + 2);
if (status & TMD_OWN)
priv->txtail = 0;
if (status & TMD_ERR) {
- unsigned long status2;
+ u_int status2;
priv->stats.tx_errors ++;
status2 = am_readword (dev, hdraddr + 6);
+
+ /*
+ * Clear the error byte
+ */
am_writeword (dev, hdraddr + 6, 0);
if (status2 & TST_RTRY)
netif_wake_queue(dev);
}
+static void
+am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct dev_priv *priv = (struct dev_priv *)dev->priv;
+ u_int status;
+
+ status = read_rreg(dev->base_addr, CSR0);
+ write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
+
+ if (status & CSR0_RINT)
+ am79c961_rx(dev, priv);
+ if (status & CSR0_TINT)
+ am79c961_tx(dev, priv);
+ if (status & CSR0_MISS)
+ priv->stats.rx_dropped ++;
+}
+
static int
am79c961_hw_init(struct net_device *dev)
{
save_flags_cli (flags);
- write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
8, 9+GAYLE_ODD, 0xa, 0xb+GAYLE_ODD,
0xc, 0xd+GAYLE_ODD, 0xe, 0xf+GAYLE_ODD };
- if (load_8390_module("apne.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk(KERN_ERR "apne.c: Passed a NULL device.\n");
stop_page = (wordlength == 2) ? 0x40 : 0x20;
} else {
printk(" not found.\n");
- return ENXIO;
+ return -ENXIO;
}
int init_module(void)
{
int err;
+
+ if (load_8390_module("apne.c"))
+ return -ENOSYS;
+
if ((err = register_netdev(&apne_dev))) {
if (err == -EIO)
printk("No PCMCIA NEx000 ethernet card found.\n");
+ unload_8390_module();
return (err);
}
- lock_8390_module();
return (0);
}
pcmcia_reset();
- unlock_8390_module();
+ unload_8390_module();
apne_owned = 0;
}
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/dma.h>
+#include <asm/io.h>
#include <linux/errno.h>
#include <linux/init.h>
int dirty_tx; /* The ring entries to be free()ed. */
struct net_device_stats stats;
char tx_full;
- unsigned long lock;
+ struct net_device *dev; /* Backpointer */
+ struct ariadne_private *next_module;
};
u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE/sizeof(u_short)];
};
+#ifdef MODULE
+static struct ariadne_private *root_ariadne_dev = NULL;
+#endif
static int ariadne_open(struct net_device *dev);
static void ariadne_init_ring(struct net_device *dev);
static int ariadne_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void ariadne_tx_timeout(struct net_device *dev);
static int ariadne_rx(struct net_device *dev);
+static void ariadne_reset(struct net_device *dev);
static void ariadne_interrupt(int irq, void *data, struct pt_regs *fp);
static int ariadne_close(struct net_device *dev);
static struct net_device_stats *ariadne_get_stats(struct net_device *dev);
}
-int __init ariadne_probe(struct net_device *dev)
+static int __init ariadne_probe(void)
{
struct zorro_dev *z = NULL;
+ struct net_device *dev;
+ struct ariadne_private *priv;
+ int res = -ENODEV;
while ((z = zorro_find_device(ZORRO_PROD_VILLAGE_TRONIC_ARIADNE, z))) {
unsigned long board = z->resource.start;
unsigned long base_addr = board+ARIADNE_LANCE;
- unsigned long ram_start = board+ARIADNE_RAM;
+ unsigned long mem_start = board+ARIADNE_RAM;
if (!request_mem_region(base_addr, sizeof(struct Am79C960),
"Am79C960"))
continue;
- if (!request_mem_region(ram_start, ARIADNE_RAM_SIZE, "RAM")) {
+ if (!request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM")) {
release_mem_region(base_addr, sizeof(struct Am79C960));
continue;
}
if (dev == NULL) {
release_mem_region(base_addr, sizeof(struct Am79C960));
- release_mem_region(ram_start, ARIADNE_RAM_SIZE);
+ release_mem_region(mem_start, ARIADNE_RAM_SIZE);
return -ENOMEM;
}
- memset(dev->priv, 0, sizeof(struct ariadne_private));
+ priv = (struct ariadne_private *)dev->priv;
+ memset(priv, 0, sizeof(struct ariadne_private));
+ priv->dev = dev;
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x60;
dev->dev_addr[2] = 0x30;
- dev->dev_addr[3] = (z->rom.er_SerialNumber>>16)&0xff;
- dev->dev_addr[4] = (z->rom.er_SerialNumber>>8)&0xff;
- dev->dev_addr[5] = z->rom.er_SerialNumber&0xff;
+ dev->dev_addr[3] = (z->rom.er_SerialNumber>>16) & 0xff;
+ dev->dev_addr[4] = (z->rom.er_SerialNumber>>8) & 0xff;
+ dev->dev_addr[5] = z->rom.er_SerialNumber & 0xff;
printk("%s: Ariadne at 0x%08lx, Ethernet Address "
"%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, board,
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
dev->base_addr = ZTWO_VADDR(base_addr);
- dev->mem_start = ZTWO_VADDR(ram_start);
+ dev->mem_start = ZTWO_VADDR(mem_start);
dev->mem_end = dev->mem_start+ARIADNE_RAM_SIZE;
dev->open = &ariadne_open;
dev->stop = &ariadne_close;
dev->hard_start_xmit = &ariadne_start_xmit;
+ dev->tx_timeout = &ariadne_tx_timeout;
+ dev->watchdog_timeo = 5*HZ;
dev->get_stats = &ariadne_get_stats;
dev->set_multicast_list = &set_multicast_list;
- return 0;
+#ifdef MODULE
+ priv->next_module = root_ariadne_dev;
+ root_ariadne_dev = priv;
+#endif
+ res = 0;
}
- return -ENODEV;
+ return res;
}
version |= swapw(lance->RDP)<<16;
if ((version & 0x00000fff) != 0x00000003) {
printk("ariadne_open: Couldn't find AMD Ethernet Chip\n");
- return(-EAGAIN);
+ return -EAGAIN;
}
if ((version & 0x0ffff000) != 0x00003000) {
printk("ariadne_open: Couldn't find Am79C960 (Wrong part number = %ld)\n",
(version & 0x0ffff000)>>12);
- return(-EAGAIN);
+ return -EAGAIN;
}
#if 0
printk("ariadne_open: Am79C960 (PCnet-ISA) Revision %ld\n",
lance->RAP = ISACSR7; /* LED3 Status */
lance->IDP = PSE|RCVE;
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
+ netif_start_queue(dev);
if (request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, SA_SHIRQ,
"Ariadne Ethernet", dev))
- return(-EAGAIN);
+ return -EAGAIN;
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA|STRT;
MOD_INC_USE_COUNT;
- return(0);
+ return 0;
}
volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start;
int i;
- priv->lock = 0, priv->tx_full = 0;
+ netif_stop_queue(dev);
+
+ priv->tx_full = 0;
priv->cur_rx = priv->cur_tx = 0;
priv->dirty_tx = 0;
struct ariadne_private *priv = (struct ariadne_private *)dev->priv;
volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
- dev->start = 0;
- dev->tbusy = 1;
+ netif_stop_queue(dev);
lance->RAP = CSR112; /* Missed Frame Count */
priv->stats.rx_missed_errors = swapw(lance->RDP);
MOD_DEC_USE_COUNT;
- return(0);
+ return 0;
+}
+
+
+static inline void ariadne_reset(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
+
+ lance->RAP = CSR0; /* PCnet-ISA Controller Status */
+ lance->RDP = STOP;
+ ariadne_init_ring(dev);
+ lance->RDP = INEA|STRT;
+ netif_start_queue(dev);
}
if (!(lance->RDP & INTR)) /* Check if any interrupt has been */
return; /* generated by the board. */
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
-
- dev->interrupt = 1;
-
priv = (struct ariadne_private *)dev->priv;
boguscnt = 10;
}
#endif
- if (priv->tx_full && dev->tbusy &&
+ if (priv->tx_full && netif_queue_stopped(dev) &&
dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) {
- /* The ring is no longer full, clear tbusy. */
+ /* The ring is no longer full. */
priv->tx_full = 0;
- dev->tbusy = 0;
- mark_bh(NET_BH);
+ netif_wake_queue(dev);
}
priv->dirty_tx = dirty_tx;
printk("%s: exiting interrupt, csr%d=%#4.4x.\n", dev->name, lance->RAP,
lance->RDP);
#endif
-
- dev->interrupt = 0;
return;
}
+static void ariadne_tx_timeout(struct net_device *dev)
+{
+ volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
+
+ printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n",
+ dev->name, lance->RDP);
+ ariadne_reset(dev);
+ netif_wake_queue(dev);
+}
+
+
static int ariadne_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ariadne_private *priv = (struct ariadne_private *)dev->priv;
int entry;
unsigned long flags;
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 20)
- return(1);
- lance->RAP = CSR0; /* PCnet-ISA Controller Status */
- printk("%s: transmit timed out, status %4.4x, resetting.\n", dev->name,
- lance->RDP);
- lance->RDP = STOP;
- priv->stats.tx_errors++;
-#ifndef final_version
- {
- int i;
- printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
- priv->dirty_tx, priv->cur_tx, priv->tx_full ? " (full)" : "",
- priv->cur_rx);
- for (i = 0 ; i < RX_RING_SIZE; i++)
- printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- (swapw((priv->rx_ring[i]->RMD1))<<16) |
- swapw(priv->rx_ring[i]->RMD0),
- swapw(-priv->rx_ring[i]->RMD2),
- swapw(priv->rx_ring[i]->RMD3));
- for (i = 0 ; i < TX_RING_SIZE; i++)
- printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- (swapw((priv->tx_ring[i]->TMD1))<<16) |
- swapw(priv->tx_ring[i]->TMD0),
- swapw(-priv->tx_ring[i]->TMD2), priv->tx_ring[i]->TMD3);
- printk("\n");
- }
-#endif
- ariadne_init_ring(dev);
- lance->RDP = INEA|STRT;
-
- dev->tbusy = 0;
- dev->trans_start = jiffies;
-
- return(0);
- }
-
#if 0
if (ariadne_debug > 3) {
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
}
#endif
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return(1);
- }
-
- if (test_and_set_bit(0, (void*)&priv->lock) != 0) {
- if (ariadne_debug > 0)
- printk("%s: tx queue lock!.\n", dev->name);
- /* don't clear dev->tbusy flag. */
- return(1);
- }
-
/* Fill in a Tx ring entry */
#if 0
priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len);
priv->tx_ring[entry]->TMD3 = 0x0000;
- memcpyw(priv->tx_buff[entry], (u_short *)skb->data, skb->len);
+ memcpyw(priv->tx_buff[entry], (u_short *)skb->data,
+ skb->len <= ETH_ZLEN ? ETH_ZLEN : skb->len);
#if 0
{
dev->trans_start = jiffies;
- priv->lock = 0;
- if (lowb(priv->tx_ring[(entry+1) % TX_RING_SIZE]->TMD1) == 0)
- dev->tbusy = 0;
- else
+ if (lowb(priv->tx_ring[(entry+1) % TX_RING_SIZE]->TMD1) != 0) {
+ netif_stop_queue(dev);
priv->tx_full = 1;
+ }
restore_flags(flags);
- return(0);
+ return 0;
}
/* We should check that at least two ring entries are free. If not,
we should free one and mark stats->rx_dropped++. */
- return(0);
+ return 0;
}
lance->RAP = saved_addr;
restore_flags(flags);
- return(&priv->stats);
+ return &priv->stats;
}
{
volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr;
+ if (!netif_running(dev))
+ return;
+
+ netif_stop_queue(dev);
+
/* We take the simple way out and always enable promiscuous mode. */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = STOP; /* Temporarily stop the lance. */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA|STRT|IDON; /* Resume normal operation. */
-}
+ netif_wake_queue(dev);
+}
-#ifdef MODULE
-static char devicename[9] = { 0, };
-static struct net_device ariadne_dev =
+static void __exit ariadne_cleanup(void)
{
- devicename, /* filled in by register_netdev() */
- 0, 0, 0, 0, /* memory */
- 0, 0, /* base, irq */
- 0, 0, 0, NULL, ariadne_probe,
-};
-
-int init_module(void)
-{
- int err;
-
- if ((err = register_netdev(&ariadne_dev))) {
- if (err == -EIO)
- printk("No Ariadne board found. Module not loaded.\n");
- return(err);
+#ifdef MODULE
+ struct ariadne_private *next;
+ struct net_device *dev;
+
+ while (root_ariadne_dev) {
+ next = root_ariadne_dev->next_module;
+ dev = root_ariadne_dev->dev;
+ unregister_netdev(dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960));
+ release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE);
+ kfree(dev);
+ root_ariadne_dev = next;
}
- return(0);
-}
-
-void cleanup_module(void)
-{
- struct ariadne_private *priv = (struct ariadne_private *)ariadne_dev.priv;
-
- unregister_netdev(&ariadne_dev);
- release_mem_region(ZTWO_PADDR(ariadne_dev.base_addr),
- sizeof(struct Am79C960));
- release_mem_region(ZTWO_PADDR(ariadne_dev.mem_start), ARIADNE_RAM_SIZE);
- kfree(priv);
+#endif
}
-#endif /* MODULE */
+module_init(ariadne_probe);
+module_exit(ariadne_cleanup);
};
unsigned long ioaddr = board+ARIADNE2_BASE*2;
- if (load_8390_module("ariadne2.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk(KERN_ERR "ariadne2.c: Passed a NULL device.\n");
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
printk("%s: DMAing conflict in ne_get_8390_hdr "
- "[DMAstat:%d][irqlock:%d][intr:%ld].\n", dev->name, ei_status.dmaing,
- ei_status.irqlock, dev->interrupt);
+ "[DMAstat:%d][irqlock:%d].\n", dev->name, ei_status.dmaing,
+ ei_status.irqlock);
return;
}
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
printk("%s: DMAing conflict in ne_block_input "
- "[DMAstat:%d][irqlock:%d][intr:%ld].\n",
- dev->name, ei_status.dmaing, ei_status.irqlock,
- dev->interrupt);
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
printk("%s: DMAing conflict in ne_block_output."
- "[DMAstat:%d][irqlock:%d][intr:%ld]\n", dev->name, ei_status.dmaing,
- ei_status.irqlock, dev->interrupt);
+ "[DMAstat:%d][irqlock:%d]\n", dev->name, ei_status.dmaing,
+ ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
int init_module(void)
{
int err;
+
+ if (load_8390_module("ariadne2.c"))
+ return -ENOSYS;
+
if ((err = register_netdev(&ariadne2_dev))) {
if (err == -EIO)
printk("No AriadNE2 ethernet card found.\n");
+ unload_8390_module();
return err;
}
- lock_8390_module();
return 0;
}
free_irq(IRQ_AMIGA_PORTS, &ariadne2_dev);
release_mem_region(ZTWO_PADDR(ariadne2_dev.base_addr), NE_IO_EXTENT*2);
unregister_netdev(&ariadne2_dev);
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
{
if (lastFoundAt == 0xbe000)
printk(KERN_ERR "arlan: No Arlan devices found \n");
- return ENODEV;
+ return -ENODEV;
}
else
return 0;
ARLAN_DEBUG_EXIT("arlan_probe_everywhere");
- return ENODEV;
+ return -ENODEV;
}
int __init arlan_find_devices(void)
printk("Arlan driver %s\n", arlan_version);
if (arlan_probe_everywhere(dev))
- return ENODEV;
+ return -ENODEV;
arlans_found++;
if (base_addr > 0x1ff) /* Check a single specified location. */
return at1700_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; at1700_probe_list[i]; i++) {
int ioaddr = at1700_probe_list[i];
if (at1700_probe1(dev, ioaddr) == 0)
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
int i;
if (!MACH_IS_ATARI || no_more_found)
- return ENODEV;
+ return -ENODEV;
printk("Probing for BioNet 100 Adapter...\n");
|| station_addr[2] != 'O' ) {
no_more_found = 1;
printk( "No BioNet 100 found.\n" );
- return ENODEV;
+ return -ENODEV;
}
if (dev == NULL)
- return ENODEV;
+ return -ENODEV;
if (bionet_debug > 0 && version_printed++ == 0)
printk(version);
static int no_more_found = 0;
if (no_more_found)
- return ENODEV;
+ return -ENODEV;
no_more_found = 1;
printk("No PAM's Net/GK found.\n");
if ((dev == NULL) || (lance_target < 0))
- return ENODEV;
+ return -ENODEV;
if (pamsnet_debug > 0 && version_printed++ == 0)
printk(version);
if (base_addr > 0x1ff) /* Check a single specified location. */
return atp_probe1(dev, base_addr);
else if (base_addr == 1) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (port = ports; *port; port++) {
int ioaddr = *port;
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
static int __init atp_probe1(struct net_device *dev, short ioaddr)
return 0;
}
-static int bond_close(struct net_device *master)
+static void release_one_slave(struct net_device *master, slave_t *slave)
{
bonding_t *bond = master->priv;
- slave_t *slave;
- while ((slave = bond->next) != (slave_t*)bond) {
+ spin_lock_bh(&master->xmit_lock);
+ if (bond->current_slave == slave)
+ bond->current_slave = slave->next;
+ slave->next->prev = slave->prev;
+ slave->prev->next = slave->next;
+ spin_unlock_bh(&master->xmit_lock);
- spin_lock_bh(&master->xmit_lock);
- slave->next->prev = slave->prev;
- slave->prev->next = slave->next;
- bond->current_slave = (slave_t*)bond;
- spin_unlock_bh(&master->xmit_lock);
+ netdev_set_master(slave->dev, NULL);
- netdev_set_master(slave->dev, NULL);
+ dev_put(slave->dev);
+ kfree(slave);
+ MOD_DEC_USE_COUNT;
+}
- kfree(slave);
- }
+static int bond_close(struct net_device *master)
+{
+ bonding_t *bond = master->priv;
+ slave_t *slave;
+
+ while ((slave = bond->next) != (slave_t*)bond)
+ release_one_slave(master, slave);
MOD_DEC_USE_COUNT;
return 0;
}
-/* Fake multicast ability.
-
- NB. It is possible and necessary to make it true one, otherwise
- the device is not functional.
- */
-static void bond_set_multicast_list(struct net_device *dev)
+static void bond_set_multicast_list(struct net_device *master)
{
+ bonding_t *bond = master->priv;
+ slave_t *slave;
+
+ for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) {
+ slave->dev->mc_list = master->mc_list;
+ slave->dev->mc_count = master->mc_count;
+ slave->dev->flags = master->flags;
+ slave->dev->set_multicast_list(slave->dev);
+ }
+
+ return 0;
}
static int bond_enslave(struct net_device *master, struct net_device *dev)
if (dev->master != master)
return -EINVAL;
- netdev_set_master(dev, NULL);
-
for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) {
if (slave->dev == dev) {
- spin_lock_bh(&master->xmit_lock);
- if (bond->current_slave == slave)
- bond->current_slave = slave->next;
- slave->next->prev = slave->prev;
- slave->prev->next = slave->next;
- spin_unlock_bh(&master->xmit_lock);
-
- kfree(slave);
- dev_put(dev);
- MOD_DEC_USE_COUNT;
+ release_one_slave(master, slave);
break;
}
}
if (slave == (slave_t*)bond)
continue;
- if (netif_running(slave->dev) && netif_carrier_ok(dev)) {
+ if (netif_running(slave->dev) && netif_carrier_ok(slave->dev)) {
bond->current_slave = slave->next;
skb->dev = slave->dev;
if (base_addr > 0x1ff) /* Check a single specified location. */
return cs89x0_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; netcard_portlist[i]; i++) {
int ioaddr = netcard_portlist[i];
return 0;
}
printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
- return ENODEV;
+ return -ENODEV;
}
extern int inline
if (ioaddr & 1) {
ioaddr &= ~1;
if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
- return ENODEV;
+ return -ENODEV;
outw(PP_ChipID, ioaddr + ADD_PORT);
}
static int ns8390_open(struct net_device *dev)
{
+ MOD_INC_USE_COUNT;
ei_open(dev);
/* At least on my card (a Focus Enhancements PDS card) I start */
if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev))
{
printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
- return EAGAIN;
+ MOD_DEC_USE_COUNT;
+ return -EAGAIN;
}
-
- MOD_INC_USE_COUNT;
return 0;
}
lp->chipset = device;
/* Get the board I/O address (64 bits on sparc64) */
- iobase = pdev->resource[0].start;
+ iobase = pci_resource_start(pdev, 0);
/* Fetch the IRQ to be used */
irq = pdev->irq;
lp->chipset = device;
/* Get the board I/O address (64 bits on sparc64) */
- iobase = this_dev->resource[0].start;
+ iobase = pci_resource_start(this_dev, 0);
/* Fetch the IRQ to be used */
irq = this_dev->irq;
de600_put_command(STOP_RESET);
if (de600_read_status(dev) & 0xf0) {
printk(": not at I/O %#3x.\n", DATA_PORT);
- return ENODEV;
+ return -ENODEV;
}
/*
dev->dev_addr[3] |= 0x70;
} else {
printk(" not identified in the printer port\n");
- return ENODEV;
+ return -ENODEV;
}
#if 0 /* Not yet */
if (check_region(DE600_IO, 3)) {
printk(", port 0x%x busy\n", DE600_IO);
- return EBUSY;
+ return -EBUSY;
}
#endif
request_region(DE600_IO, 3, "de600");
if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) {
printk(" not identified in the printer port\n");
- return ENODEV;
+ return -ENODEV;
}
#if 0 /* Not yet */
if (check_region(dev->base_addr, 3)) {
printk(", port 0x%x busy\n", dev->base_addr);
- return EBUSY;
+ return -EBUSY;
}
#endif
request_region(dev->base_addr, 3, "de620");
printk(version); /* we only display this string ONCE */
}
+ if (pci_enable_device(pdev))
+ continue;
+
/* Verify that I/O enable bit is set (PCI slot is enabled) */
pci_read_config_word(pdev, PCI_COMMAND, &command);
/* Get I/O base address from PCI Configuration Space */
- port = pdev->resource[1].start;
+ port = pci_resource_start (pdev, 1);
/* Verify port address range is not already being used */
* The RightSwitch is a 4 (EISA) or 6 (PCI) port etherswitch and
* a NIC on an internal board.
*
- * Author: Rick Richardson, rick@dgii.com, rick_richardson@dgii.com
+ * Author: Rick Richardson, rick@remotepoint.com
* Derived from the SVR4.2 (UnixWare) driver for the same card.
*
* Copyright 1995-1996 Digi International Inc.
*
*/
-static char *version = "$Id: dgrs.c,v 1.12 1996/12/21 13:43:58 rick Exp $";
+static char *version = "$Id: dgrs.c,v 1.13 2000/06/06 04:07:00 rick Exp $";
#include <linux/version.h>
#include <linux/module.h>
I596_RFD *rfdp; /* Current RFD list */
I596_RBD *rbdp; /* Current RBD list */
- int intrcnt; /* Count of interrupts */
+ volatile int intrcnt; /* Count of interrupts */
/*
* SE-4 (EISA) board variables
*/
if (priv->plxreg)
OUTL(dev->base_addr + PLX_LCL2PCI_DOORBELL, 1);
- rc = request_irq(dev->irq, &dgrs_intr, 0, "RightSwitch", dev);
+ rc = request_irq(dev->irq, &dgrs_intr, SA_SHIRQ, "RightSwitch", dev);
if (rc)
return (rc);
#define DMFE_AUTO 8
#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT HZ*1.5 /* tx packet time-out time 1.5 s" */
+#define DMFE_TX_TIMEOUT ((HZ*3)/2) /* tx packet time-out time 1.5 s" */
#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
continue;
- if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
+ if ((net_dev->device != PCI_DM9102_ID) && (net_dev->device != PCI_DM9132_ID))
continue;
- pci_iobase = net_dev->resource[0].start;
+ pci_iobase = pci_resource_start (net_dev, 0);
pci_irqline = net_dev->irq;
/* Enable Master/IO access, Disable memory access */
- pci_enable_device (net_dev); /* XXX check return val */
+ if (pci_enable_device(net_dev))
+ continue;
pci_set_master(net_dev);
/* Set Latency Timer 80h */
/* IO range check */
if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
- printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
continue;
}
/* Interrupt check */
if (base_addr > 0x1ff) /* Check a single specified location. */
return e21_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (port = e21_probe_list; *port; port++) {
if (check_region(*port, E21_IO_EXTENT))
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
int __init e21_probe1(struct net_device *dev, int ioaddr)
if (inb(ioaddr + E21_SAPROM + 0) != 0x00
|| inb(ioaddr + E21_SAPROM + 1) != 0x00
|| inb(ioaddr + E21_SAPROM + 2) != 0x1d)
- return ENODEV;
+ return -ENODEV;
/* Verify by making certain that there is a 8390 at there. */
outb(E8390_NODMA + E8390_STOP, ioaddr);
udelay(1); /* we want to delay one I/O cycle - which is 2MHz */
status = inb(ioaddr);
if (status != 0x21 && status != 0x23)
- return ENODEV;
+ return -ENODEV;
/* Read the station address PROM. */
for (i = 0; i < 6; i++)
inb(ioaddr + E21_MEDIA); /* Point to media selection. */
outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */
- if (load_8390_module("e2100.c"))
- return -ENOSYS;
-
if (ei_debug && version_printed++ == 0)
printk(version);
}
if (i >= 8) {
printk(" unable to get IRQ %d.\n", dev->irq);
- return EAGAIN;
+ return -EAGAIN;
}
} else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */
dev->irq = 9;
short ioaddr = dev->base_addr;
if (request_irq(dev->irq, ei_interrupt, 0, "e2100", dev)) {
- return EBUSY;
+ return -EBUSY;
}
/* Set the interrupt line and memory base on the hardware. */
{
int this_dev, found = 0;
+ if (load_8390_module("e2100.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
struct net_device *dev = &dev_e21[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
This is a compatibility hardware problem.
Versions:
+ 0.12a port of version 0.12a of 2.2.x kernels to 2.3.x
+ (aris (aris@conectiva.com.br), 05/19/2000)
0.11e some tweaks about multiple cards support (PdP, jul/aug 1999)
0.11d added __initdata, __init stuff; call spin_lock_init
in eepro_probe1. Replaced "eepro" by dev->name. Augmented
*/
static const char *version =
- "eepro.c: v0.11d 08/12/1998 dupuis@lei.ucl.ac.be\n";
+ "eepro.c: v0.12a 04/26/2000 aris@conectiva.com.br\n";
#include <linux/module.h>
#define LAN595 0
#define LAN595TX 1
#define LAN595FX 2
+#define LAN595FX_10ISA 3
/* Information that need to be kept for each board. */
struct eepro_local {
static void set_multicast_list(struct net_device *dev);
static void eepro_tx_timeout (struct net_device *dev);
-static int read_eeprom(int ioaddr, int location);
+static int read_eeprom(int ioaddr, int location, struct net_device *dev);
static void hardware_send_packet(struct net_device *dev, void *buf, short length);
static int eepro_grab_irq(struct net_device *dev);
buffer (transmit-buffer = 32K - receive-buffer).
*/
-#define RAM_SIZE 0x8000
-#define RCV_HEADER 8
-#define RCV_RAM 0x6000 /* 24KB default for RCV buffer */
-#define RCV_LOWER_LIMIT 0x00 /* 0x0000 */
-/* #define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) */ /* 0x5ffe */
-#define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8)
-/* #define XMT_RAM (RAM_SIZE - RCV_RAM) */ /* 8KB for XMT buffer */
-#define XMT_RAM (RAM_SIZE - (rcv_ram)) /* 8KB for XMT buffer */
-/* #define XMT_LOWER_LIMIT (RCV_RAM >> 8) */ /* 0x6000 */
-#define XMT_LOWER_LIMIT ((rcv_ram) >> 8)
-#define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8) /* 0x7ffe */
-#define XMT_HEADER 8
+/* now this section could be used by both boards: the oldies and the ee10:
+ * ee10 uses tx buffer before of rx buffer and the oldies the inverse.
+ * (aris)
+ */
+#define RAM_SIZE 0x8000
+
+#define RCV_HEADER 8
+#define RCV_DEFAULT_RAM 0x6000
+#define RCV_RAM rcv_ram
+
+static unsigned rcv_ram = RCV_DEFAULT_RAM;
+
+#define XMT_HEADER 8
+#define XMT_RAM (RAM_SIZE - RCV_RAM)
+
+#define XMT_START ((rcv_start + RCV_RAM) % RAM_SIZE)
+
+#define RCV_LOWER_LIMIT (rcv_start >> 8)
+#define RCV_UPPER_LIMIT (((rcv_start + RCV_RAM) - 2) >> 8)
+#define XMT_LOWER_LIMIT (XMT_START >> 8)
+#define XMT_UPPER_LIMIT (((XMT_START + XMT_RAM) - 2) >> 8)
+
+#define RCV_START_PRO 0x00
+#define RCV_START_10 XMT_RAM
+ /* by default the old driver */
+static unsigned rcv_start = RCV_START_PRO;
#define RCV_DONE 0x0008
#define RX_OK 0x2000
#define IO_32_BIT 0x10
#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
#define RCV_STOP 0x06
-#define XMT_BAR 0x0a
+
+#define XMT_BAR_PRO 0x0a
+#define XMT_BAR_10 0x0b
+static unsigned xmt_bar = XMT_BAR_PRO;
+
#define HOST_ADDRESS_REG 0x0c
#define IO_PORT 0x0e
#define IO_PORT_32_BIT 0x0c
#define INT_NO_REG 0x02
#define RCV_LOWER_LIMIT_REG 0x08
#define RCV_UPPER_LIMIT_REG 0x09
-#define XMT_LOWER_LIMIT_REG 0x0a
-#define XMT_UPPER_LIMIT_REG 0x0b
+
+#define XMT_LOWER_LIMIT_REG_PRO 0x0a
+#define XMT_UPPER_LIMIT_REG_PRO 0x0b
+#define XMT_LOWER_LIMIT_REG_10 0x0b
+#define XMT_UPPER_LIMIT_REG_10 0x0a
+static unsigned xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
+static unsigned xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
/* Bank 2 registers */
#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
#define I_ADD_REG4 0x08
#define I_ADD_REG5 0x09
-#define EEPROM_REG 0x0a
+#define EEPROM_REG_PRO 0x0a
+#define EEPROM_REG_10 0x0b
+static unsigned eeprom_reg = EEPROM_REG_PRO;
+
#define EESK 0x01
#define EECS 0x02
#define EEDI 0x04
#define EEDO 0x08
+/* do a full reset */
+#define eepro_reset(ioaddr) outb(RESET_CMD, ioaddr)
+
+/* do a nice reset */
+#define eepro_sel_reset(ioaddr) { \
+ outb(SEL_RESET_CMD, ioaddr); \
+ SLOW_DOWN; \
+ SLOW_DOWN; \
+ }
+
+/* disable all interrupts */
+#define eepro_dis_int(ioaddr) outb(ALL_MASK, ioaddr + INT_MASK_REG)
+
+/* clear all interrupts */
+#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG)
+
+/* enable tx/rx */
+#define eepro_en_int(ioaddr) outb(ALL_MASK & ~(RX_MASK | TX_MASK), \
+ ioaddr + INT_MASK_REG)
+
+/* enable exec event interrupt */
+#define eepro_en_intexec(ioaddr) outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG)
+
+/* enable rx */
+#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr)
+
+/* disable rx */
+#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr)
+
+/* switch bank */
+#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr)
+#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr)
+#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr)
+
+/* enable interrupt line */
+#define eepro_en_intline(ioaddr) outb(inb(ioaddr + REG1) | INT_ENABLE,\
+ ioaddr + REG1)
+
+/* disable interrupt line */
+#define eepro_dis_intline(ioaddr) outb(inb(ioaddr + REG1) & 0x7f, \
+ ioaddr + REG1);
+
+/* set diagnose flag */
+#define eepro_diag(ioaddr) outb(DIAGNOSE_CMD, ioaddr)
+
+/* ack for rx/tx int */
+#define eepro_ack_rxtx(ioaddr) outb (RX_INT | TX_INT, ioaddr + STATUS_REG)
+
+/* ack for rx int */
+#define eepro_ack_rx(ioaddr) outb (RX_INT, ioaddr + STATUS_REG)
+
+/* ack for tx int */
+#define eepro_ack_tx(ioaddr) outb (TX_INT, ioaddr + STATUS_REG)
/* Check for a network adaptor of this type, and return '0' if one exists.
If dev->base_addr == 0, probe all likely locations.
return eepro_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; eepro_portlist[i]; i++) {
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
-void printEEPROMInfo(short ioaddr)
+void printEEPROMInfo(short ioaddr, struct net_device *dev)
{
unsigned short Word;
int i,j;
for (i=0, j=ee_Checksum; i<ee_SIZE; i++)
- j+=read_eeprom(ioaddr,i);
+ j+=read_eeprom(ioaddr,i,dev);
printk("Checksum: %#x\n",j&0xffff);
- Word=read_eeprom(ioaddr, 0);
+ Word=read_eeprom(ioaddr, 0, dev);
printk(KERN_DEBUG "Word0:\n");
printk(KERN_DEBUG " Plug 'n Pray: %d\n",GetBit(Word,ee_PnP));
printk(KERN_DEBUG " Buswidth: %d\n",(GetBit(Word,ee_BusWidth)+1)*8 );
printk(KERN_DEBUG " IO Address: %#x\n", (Word>>ee_IO0)<<4);
if (net_debug>4) {
- Word=read_eeprom(ioaddr, 1);
+ Word=read_eeprom(ioaddr, 1, dev);
printk(KERN_DEBUG "Word1:\n");
printk(KERN_DEBUG " INT: %d\n", Word & ee_IntMask);
printk(KERN_DEBUG " LI: %d\n", GetBit(Word,ee_LI));
printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex));
}
- Word=read_eeprom(ioaddr, 5);
+ Word=read_eeprom(ioaddr, 5, dev);
printk(KERN_DEBUG "Word5:\n");
printk(KERN_DEBUG " BNC: %d\n",GetBit(Word,ee_BNC_TPE));
printk(KERN_DEBUG " NumConnectors: %d\n",GetBit(Word,ee_NumConn));
if (GetBit(Word,ee_PortAUI)) printk("AUI ");
printk("port(s) \n");
- Word=read_eeprom(ioaddr, 6);
+ Word=read_eeprom(ioaddr, 6, dev);
printk(KERN_DEBUG "Word6:\n");
printk(KERN_DEBUG " Stepping: %d\n",Word & ee_StepMask);
printk(KERN_DEBUG " BoardID: %d\n",Word>>ee_BoardID);
- Word=read_eeprom(ioaddr, 7);
+ Word=read_eeprom(ioaddr, 7, dev);
printk(KERN_DEBUG "Word7:\n");
printk(KERN_DEBUG " INT to IRQ:\n");
{
unsigned short station_addr[6], id, counter;
int i,j, irqMask;
- int eepro;
+ int eepro = 0;
+ struct eepro_local *lp;
const char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
enum iftype { AUI=0, BNC=1, TPE=2 };
id=inb(ioaddr + ID_REG);
- printk(KERN_DEBUG " id: %#x ",id);
- printk(" io: %#x ",ioaddr);
-
if (((id) & ID_REG_MASK) == ID_REG_SIG) {
/* We seem to have the 82595 signature, let's
(counter + 0x40)) {
/* Yes, the 82595 has been found */
+ printk(KERN_DEBUG " id: %#x ",id);
+ printk(" io: %#x ",ioaddr);
+
+ /* Initialize the device structure */
+ dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct eepro_local));
+
+ lp = (struct eepro_local *)dev->priv;
/* Now, get the ethernet hardware address from
the EEPROM */
- station_addr[0] = read_eeprom(ioaddr, 2);
- station_addr[1] = read_eeprom(ioaddr, 3);
- station_addr[2] = read_eeprom(ioaddr, 4);
+ station_addr[0] = read_eeprom(ioaddr, 2, dev);
+
+ /* FIXME - find another way to know that we've found
+ * an Etherexpress 10
+ */
+ if (station_addr[0] == 0x0000 ||
+ station_addr[0] == 0xffff) {
+ eepro = 3;
+ lp->eepro = LAN595FX_10ISA;
+ eeprom_reg = EEPROM_REG_10;
+ rcv_start = RCV_START_10;
+ xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
+ xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+
+ station_addr[0] = read_eeprom(ioaddr, 2, dev);
+ }
+
+ station_addr[1] = read_eeprom(ioaddr, 3, dev);
+ station_addr[2] = read_eeprom(ioaddr, 4, dev);
- /* Check the station address for the manufacturer's code */
- if (net_debug>3)
- printEEPROMInfo(ioaddr);
-
- if (read_eeprom(ioaddr,7)== ee_FX_INT2IRQ) { /* int to IRQ Mask */
+ if (eepro) {
+ printk("%s: Intel EtherExpress 10 ISA\n at %#x,",
+ dev->name, ioaddr);
+ } else if (read_eeprom(ioaddr,7,dev)== ee_FX_INT2IRQ) {
+ /* int to IRQ Mask */
eepro = 2;
printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,",
dev->name, ioaddr);
dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
}
+
+ dev->mem_start = (RCV_LOWER_LIMIT << 8);
if ((dev->mem_end & 0x3f) < 3 || /* RX buffer must be more than 3K */
(dev->mem_end & 0x3f) > 29) /* and less than 29K */
- dev->mem_end = RCV_RAM; /* or it will be set to 24K */
- else dev->mem_end = 1024*dev->mem_end; /* Maybe I should shift << 10 */
+ dev->mem_end = (RCV_UPPER_LIMIT << 8);
+ else {
+ dev->mem_end = (dev->mem_end * 1024) +
+ (RCV_LOWER_LIMIT << 8);
+ rcv_ram = dev->mem_end - (RCV_LOWER_LIMIT << 8);
+ }
- /* From now on, dev->mem_end contains the actual size of rx buffer */
+ /* From now on, dev->mem_end - dev->mem_start contains
+ * the actual size of rx buffer
+ */
if (net_debug > 3)
printk(", %dK RCV buffer", (int)(dev->mem_end)/1024);
/* ............... */
- if (GetBit( read_eeprom(ioaddr, 5),ee_BNC_TPE))
+ if (GetBit( read_eeprom(ioaddr, 5, dev),ee_BNC_TPE))
dev->if_port = BNC;
else dev->if_port = TPE;
if ((dev->irq < 2) && (eepro!=0)) {
- i = read_eeprom(ioaddr, 1);
- irqMask = read_eeprom(ioaddr, 7);
+ i = read_eeprom(ioaddr, 1, dev);
+ irqMask = read_eeprom(ioaddr, 7, dev);
i &= 0x07; /* Mask off INT number */
for (j=0; ((j<16) && (i>=0)); j++) {
i--; /* count bits set in irqMask */
}
}
- if (dev -> irq<2) {
+ if (dev->irq < 2) {
printk(" Duh! illegal interrupt vector stored in EEPROM.\n");
- return ENODEV;
+ return -ENODEV;
} else
- if (dev->irq==2) dev->irq = 9;
-
- else if (dev->irq == 2)
- dev->irq = 9;
+ if (dev->irq==2)
+ dev->irq = 9;
}
if (dev->irq > 2) {
net_debug = dev->mem_start & 7; /* still useful or not */
if (net_debug > 3) {
- i = read_eeprom(ioaddr, 5);
+ i = read_eeprom(ioaddr, 5, dev);
if (i & 0x2000) /* bit 13 of EEPROM word 5 */
printk(KERN_DEBUG "%s: Concurrent Processing is enabled but not used!\n",
dev->name);
/* Grab the region so we can find another board if autoIRQ fails. */
request_region(ioaddr, EEPRO_IO_EXTENT, dev->name);
- /* Initialize the device structure */
- dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct eepro_local));
-
((struct eepro_local *)dev->priv)->lock = SPIN_LOCK_UNLOCKED;
dev->open = eepro_open;
ether_setup(dev);
- outb(RESET_CMD, ioaddr); /* RESET the 82595 */
+ /* Check the station address for the manufacturer's code */
+ if (net_debug>3)
+ printEEPROMInfo(ioaddr, dev);
+
+ /* RESET the 82595 */
+ eepro_reset(ioaddr);
return 0;
}
- else return ENODEV;
+ else return -ENODEV;
}
else if (net_debug > 3)
printk ("EtherExpress Pro probed failed!\n");
- return ENODEV;
+ return -ENODEV;
}
/* Open/initialize the board. This is called (in the current kernel)
int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12, 0 };
int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
- outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
/* Enable the interrupt line. */
- temp_reg = inb(ioaddr + REG1);
- outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+ eepro_en_intline(ioaddr);
+
+ /* be CAREFUL, BANK 0 now */
+ eepro_sw2bank0(ioaddr);
- outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
-
/* clear all interrupts */
- outb(ALL_MASK, ioaddr + STATUS_REG);
+ eepro_clear_int(ioaddr);
/* Let EXEC event to interrupt */
- outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG);
+ eepro_en_intexec(ioaddr);
do {
- outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
temp_reg = inb(ioaddr + INT_NO_REG);
outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);
- outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
if (request_irq (*irqp, NULL, 0, "bogus", dev) != EBUSY) {
/* Twinkle the interrupt, and check if it's seen */
autoirq_setup(0);
- outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
+ eepro_diag(ioaddr); /* RESET the 82595 */
if (*irqp == autoirq_report(2)) /* It's a good IRQ line */
break;
/* clear all interrupts */
- outb(ALL_MASK, ioaddr + STATUS_REG);
+ eepro_clear_int(ioaddr);
}
} while (*++irqp);
- outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+ eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */
/* Disable the physical interrupt line. */
- temp_reg = inb(ioaddr + REG1);
- outb(temp_reg & 0x7f, ioaddr + REG1);
+ eepro_dis_intline(ioaddr);
- outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
/* Mask all the interrupts. */
- outb(ALL_MASK, ioaddr + INT_MASK_REG);
+ eepro_dis_int(ioaddr);
/* clear all interrupts */
- outb(ALL_MASK, ioaddr + STATUS_REG);
+ eepro_clear_int(ioaddr);
return dev->irq;
}
{
unsigned short temp_reg, old8, old9;
int irqMask;
- int i, ioaddr = dev->base_addr, rcv_ram = dev->mem_end;
+ int i, ioaddr = dev->base_addr;
struct eepro_local *lp = (struct eepro_local *)dev->priv;
if (net_debug > 3)
printk(KERN_DEBUG "%s: entering eepro_open routine.\n", dev->name);
- if ((irqMask=read_eeprom(ioaddr,7))== ee_FX_INT2IRQ) /* INT to IRQ Mask */
+ irqMask = read_eeprom(ioaddr,7,dev);
+
+ if (lp->eepro == LAN595FX_10ISA) {
+ if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 3;\n");
+ }
+ else if (irqMask == ee_FX_INT2IRQ) /* INT to IRQ Mask */
{
lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */
if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 2;\n");
/* Initialize the 82595. */
- outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
- temp_reg = inb(ioaddr + EEPROM_REG);
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ temp_reg = inb(ioaddr + eeprom_reg);
lp->stepping = temp_reg >> 5; /* Get the stepping number of the 595 */
printk(KERN_DEBUG "The stepping of the 82595 is %d\n", lp->stepping);
if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
- outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
+ outb(temp_reg & 0xef, ioaddr + eeprom_reg);
for (i=0; i < 6; i++)
outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
/* Set the receiving mode */
- outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
/* Set the interrupt vector */
temp_reg = inb(ioaddr + INT_NO_REG);
- if (lp->eepro == 2)
+ if (lp->eepro == 2 || lp->eepro == LAN595FX_10ISA)
outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG);
else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
temp_reg = inb(ioaddr + INT_NO_REG);
- if (lp->eepro == 2)
+ if (lp->eepro == 2 || lp->eepro == LAN595FX_10ISA)
outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG);
else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
/* Initialize the RCV and XMT upper and lower limits */
outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
- outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);
- outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, ioaddr + xmt_lower_limit_reg);
+ outb(XMT_UPPER_LIMIT, ioaddr + xmt_upper_limit_reg);
/* Enable the interrupt line. */
- temp_reg = inb(ioaddr + REG1);
- outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+ eepro_en_intline(ioaddr);
- outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+ /* Switch back to Bank 0 */
+ eepro_sw2bank0(ioaddr);
/* Let RX and TX events to interrupt */
- outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ eepro_en_int(ioaddr);
/* clear all interrupts */
- outb(ALL_MASK, ioaddr + STATUS_REG);
+ eepro_clear_int(ioaddr);
/* Initialize RCV */
outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);
/* Initialize XMT */
- outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);
+ outw(XMT_LOWER_LIMIT << 8, ioaddr + xmt_bar);
/* Check for the i82595TX and i82595FX */
old8 = inb(ioaddr + 8);
if (dev->if_port != TPE) { /* Hopefully, this will fix the
problem of using Pentiums and
pro/10 w/ BNC. */
- outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
temp_reg = inb(ioaddr + REG13);
/* disable the full duplex mode since it is not
applicable with the 10Base2 cable. */
outb(temp_reg & ~(FDX | A_N_ENABLE), REG13);
- outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
+ eepro_sw2bank0(ioaddr); /* be CAREFUL, BANK 0 now */
}
}
else if (net_debug > 3) {
}
}
- outb(SEL_RESET_CMD, ioaddr);
+ eepro_sel_reset(ioaddr);
- /* We are supposed to wait for 2 us after a SEL_RESET */
- SLOW_DOWN;
- SLOW_DOWN;
-
- lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
+ lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8;
lp->tx_last = 0;
netif_start_queue(dev);
if (net_debug > 3)
printk(KERN_DEBUG "%s: exiting eepro_open routine.\n", dev->name);
- outb(RCV_ENABLE_CMD, ioaddr);
+ /* enabling rx */
+ eepro_en_rx(ioaddr);
MOD_INC_USE_COUNT;
return 0;
{
struct eepro_local *lp = (struct eepro_local *) dev->priv;
int ioaddr = dev->base_addr;
- int rcv_ram = dev->mem_end;
/* if (net_debug > 1) */
printk (KERN_ERR "%s: transmit timed out, %s?\n", dev->name,
lp->stats.tx_errors++;
/* Try to restart the adaptor. */
- outb (SEL_RESET_CMD, ioaddr);
- /* We are supposed to wait for 2 us after a SEL_RESET */
- SLOW_DOWN;
- SLOW_DOWN;
-
+ eepro_sel_reset(ioaddr);
+
/* Do I also need to flush the transmit buffers here? YES? */
- lp->tx_start = lp->tx_end = rcv_ram;
+ lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT;
lp->tx_last = 0;
dev->trans_start = jiffies;
netif_wake_queue (dev);
- outb (RCV_ENABLE_CMD, ioaddr);
+ /* enabling interrupts */
+ eepro_en_int(ioaddr);
+
+ /* enabling rx */
+ eepro_en_rx(ioaddr);
}
lp->stats.tx_bytes+=skb->len;
hardware_send_packet(dev, buf, length);
+
dev->trans_start = jiffies;
}
ioaddr = dev->base_addr;
- do {
- status = inb(ioaddr + STATUS_REG);
-
+ while (((status = inb(ioaddr + STATUS_REG)) & 0x06) && (boguscount--))
+ {
+ switch (status & (RX_INT | TX_INT)) {
+ case (RX_INT | TX_INT):
+ eepro_ack_rxtx(ioaddr);
+ break;
+ case RX_INT:
+ eepro_ack_rx(ioaddr);
+ break;
+ case TX_INT:
+ eepro_ack_tx(ioaddr);
+ break;
+ }
if (status & RX_INT) {
if (net_debug > 4)
- printk(KERN_DEBUG "%s: packet received interrupt.\n", dev->name);
+ printk(KERN_DEBUG "%s: packet received interrupt.\n", dev->name);
- /* Acknowledge the RX_INT */
- outb(RX_INT, ioaddr + STATUS_REG);
/* Get the received packets */
eepro_rx(dev);
}
-
- else if (status & TX_INT) {
+ if (status & TX_INT) {
if (net_debug > 4)
- printk(KERN_DEBUG "%s: packet transmit interrupt.\n", dev->name);
-
- /* Acknowledge the TX_INT */
- outb(TX_INT, ioaddr + STATUS_REG);
+ printk(KERN_DEBUG "%s: packet transmit interrupt.\n", dev->name);
/* Process the status of transmitted packets */
eepro_transmit_interrupt(dev);
}
-
- } while ((boguscount-- > 0) && (status & 0x06));
+ }
if (net_debug > 5)
printk(KERN_DEBUG "%s: exiting eepro_interrupt routine.\n", dev->name);
{
struct eepro_local *lp = (struct eepro_local *)dev->priv;
int ioaddr = dev->base_addr;
- int rcv_ram = dev->mem_end;
short temp_reg;
netif_stop_queue(dev);
- outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+ eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */
/* Disable the physical interrupt line. */
temp_reg = inb(ioaddr + REG1);
outb(temp_reg & 0x7f, ioaddr + REG1);
- outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
/* Flush the Tx and disable Rx. */
outb(STOP_RCV_CMD, ioaddr);
- lp->tx_start = lp->tx_end = rcv_ram ;
+ lp->tx_start = lp->tx_end = (XMT_LOWER_LIMIT << 8);
lp->tx_last = 0;
/* Mask all the interrupts. */
- outb(ALL_MASK, ioaddr + INT_MASK_REG);
+ eepro_dis_int(ioaddr);
/* clear all interrupts */
- outb(ALL_MASK, ioaddr + STATUS_REG);
+ eepro_clear_int(ioaddr);
/* Reset the 82595 */
- outb(RESET_CMD, ioaddr);
+ eepro_reset(ioaddr);
/* release the interrupt */
free_irq(dev->irq, dev);
/* Update the statistics here. What statistics? */
- /* We are supposed to wait for 200 us after a RESET */
- SLOW_DOWN;
- SLOW_DOWN; /* May not be enough? */
-
MOD_DEC_USE_COUNT;
return 0;
}
*/
dev->flags|=IFF_PROMISC;
- outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
mode = inb(ioaddr + REG2);
outb(mode | PRMSC_Mode, ioaddr + REG2);
mode = inb(ioaddr + REG3);
outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
- outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
printk("%s: promiscuous mode enabled.\n", dev->name);
}
else if (dev->mc_count==0 )
{
- outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
mode = inb(ioaddr + REG2);
outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */
mode = inb(ioaddr + REG3);
outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
- outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
}
else
/* Disable RX and TX interrupts. Necessary to avoid
corruption of the HOST_ADDRESS_REG by interrupt
service routines. */
- outb(ALL_MASK, ioaddr + INT_MASK_REG);
+ eepro_dis_int(ioaddr);
- outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
mode = inb(ioaddr + REG2);
outb(mode | Multi_IA, ioaddr + REG2);
mode = inb(ioaddr + REG3);
outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
- outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG);
outw(MC_SETUP, ioaddr + IO_PORT);
outw(0, ioaddr + IO_PORT);
outw(eaddrs[0], ioaddr + IO_PORT);
outw(eaddrs[1], ioaddr + IO_PORT);
outw(eaddrs[2], ioaddr + IO_PORT);
- outw(lp->tx_end, ioaddr + XMT_BAR);
+ outw(lp->tx_end, ioaddr + xmt_bar);
outb(MC_SETUP, ioaddr);
/* Update the transmit queue */
} while (++boguscount < 100);
/* Re-enable RX and TX interrupts */
- outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
-
+ eepro_en_int(ioaddr);
}
- outb(RCV_ENABLE_CMD, ioaddr);
+ eepro_en_rx(ioaddr);
}
/* The horrible routine to read a word from the serial EEPROM. */
#define EE_READ_CMD (6 << 6)
int
-read_eeprom(int ioaddr, int location)
+read_eeprom(int ioaddr, int location, struct net_device *dev)
{
int i;
unsigned short retval = 0;
- short ee_addr = ioaddr + EEPROM_REG;
+ short ee_addr = ioaddr + eeprom_reg;
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
int read_cmd = location | EE_READ_CMD;
short ctrl_val = EECS ;
+
+ /* XXXX - this is not the final version. We must test this on other
+ * boards other than eepro10. I think that it won't let other
+ * boards to fail. (aris)
+ */
+ if (lp->eepro == LAN595FX_10ISA) {
+ eepro_sw2bank1(ioaddr);
+ outb(0x00, ioaddr + STATUS_REG);
+ }
- outb(BANK2_SELECT, ioaddr);
+ eepro_sw2bank2(ioaddr);
outb(ctrl_val, ee_addr);
/* Shift the read command bits out. */
eeprom_delay();
outb(ctrl_val, ee_addr);
eeprom_delay();
- outb(BANK0_SELECT, ioaddr);
+ eepro_sw2bank0(ioaddr);
return retval;
}
{
struct eepro_local *lp = (struct eepro_local *)dev->priv;
short ioaddr = dev->base_addr;
- int rcv_ram = dev->mem_end;
unsigned status, tx_available, last, end, boguscount = 100;
if (net_debug > 5)
/* Disable RX and TX interrupts. Necessary to avoid
corruption of the HOST_ADDRESS_REG by interrupt
service routines. */
- outb(ALL_MASK, ioaddr + INT_MASK_REG);
+ eepro_dis_int(ioaddr);
/* determine how much of the transmit buffer space is available */
if (lp->tx_end > lp->tx_start)
eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */
/* Enable RX and TX interrupts */
- outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ eepro_en_int(ioaddr);
continue;
}
last = lp->tx_end;
end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
- if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
-
- if ((RAM_SIZE - last) <= XMT_HEADER) {
+ if (end >= (XMT_UPPER_LIMIT << 8)) { /* the transmit buffer is wrapped around */
+ if (((XMT_UPPER_LIMIT << 8) - last) <= XMT_HEADER) {
/* Arrrr!!!, must keep the xmt header together,
several days were lost to chase this one down. */
- last = rcv_ram;
+ last = (XMT_LOWER_LIMIT << 8);
end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
}
- else end = rcv_ram + (end - RAM_SIZE);
+ else end = (XMT_LOWER_LIMIT << 8) + (end - XMT_RAM);
}
-
outw(last, ioaddr + HOST_ADDRESS_REG);
outw(XMT_CMD, ioaddr + IO_PORT);
outw(0, ioaddr + IO_PORT);
status = inw(ioaddr + IO_PORT);
if (lp->tx_start == lp->tx_end) {
- outw(last, ioaddr + XMT_BAR);
+ outw(last, ioaddr + xmt_bar);
outb(XMT_CMD, ioaddr);
lp->tx_start = last; /* I don't like to change tx_start here */
}
if (netif_queue_stopped(dev))
netif_wake_queue(dev);
+
+ /* now we are serializing tx. queue won't come back until
+ * the tx interrupt
+ */
+ if (lp->eepro == LAN595FX_10ISA)
+ netif_stop_queue(dev);
/* Enable RX and TX interrupts */
- outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ eepro_en_int(ioaddr);
if (net_debug > 5)
printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name);
return;
}
-
+ eepro_en_int(ioaddr);
+
netif_stop_queue(dev);
if (net_debug > 5)
printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name);
eepro_rx(struct net_device *dev)
{
struct eepro_local *lp = (struct eepro_local *)dev->priv;
- short ioaddr = dev->base_addr, rcv_ram = dev->mem_end;
+ short ioaddr = dev->base_addr;
short boguscount = 20;
- short rcv_car = lp->rx_start;
+ unsigned rcv_car = lp->rx_start;
unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
if (net_debug > 5)
printk(KERN_DEBUG "%s: entering eepro_rx routine.\n", dev->name);
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
/* Set the read pointer to the start of the RCV */
outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
if (net_debug > 5)
printk(KERN_DEBUG "%s: exiting eepro_rx routine.\n", dev->name);
+
+ /* enable tx/rx interrupts */
+ eepro_en_int(ioaddr);
}
static void
struct eepro_local *lp = (struct eepro_local *)dev->priv;
short ioaddr = dev->base_addr;
short boguscount = 20;
- short xmt_status;
+ unsigned xmt_status;
/*
if (dev->tbusy == 0) {
dev->name);
}
*/
+ while (lp->tx_start != lp->tx_end && boguscount) {
- while (lp->tx_start != lp->tx_end) {
-
outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
xmt_status = inw(ioaddr+IO_PORT);
- if ((xmt_status & TX_DONE_BIT) == 0) break;
+ if ((xmt_status & TX_DONE_BIT) == 0) {
+ udelay(40);
+ boguscount--;
+ continue;
+ }
xmt_status = inw(ioaddr+IO_PORT);
lp->tx_start = inw(ioaddr+IO_PORT);
+ if (lp->eepro == LAN595FX_10ISA) {
+ lp->tx_start = (XMT_LOWER_LIMIT << 8);
+ lp->tx_end = lp->tx_start;
+
+ /* yeah, black magic :( */
+ eepro_sw2bank0(ioaddr);
+ eepro_en_int(ioaddr);
+
+ /* disabling rx */
+ eepro_dis_rx(ioaddr);
+
+ /* enabling rx */
+ eepro_en_rx(ioaddr);
+ }
+
netif_wake_queue (dev);
if (xmt_status & 0x2000)
lp->stats.tx_packets++;
else {
lp->stats.tx_errors++;
- if (xmt_status & 0x0400)
+ if (xmt_status & 0x0400) {
lp->stats.tx_carrier_errors++;
- printk("%s: XMT status = %#x\n",
- dev->name, xmt_status);
- printk(KERN_DEBUG "%s: XMT status = %#x\n",
- dev->name, xmt_status);
+ printk(KERN_DEBUG "%s: carrier error\n",
+ dev->name);
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ else {
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ if (lp->eepro == LAN595FX_10ISA) {
+ /* Try to restart the adaptor. */
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ eepro_sel_reset(ioaddr);
+
+ /* first enable interrupts */
+ eepro_sw2bank0(ioaddr);
+ outb(ALL_MASK & ~(RX_INT | TX_INT), ioaddr + STATUS_REG);
+
+ /* enabling rx */
+ eepro_en_rx(ioaddr);
+ }
}
-
if (xmt_status & 0x000f) {
lp->stats.collisions += (xmt_status & 0x000f);
}
lp->stats.tx_heartbeat_errors++;
}
- if (--boguscount == 0)
- break;
+ boguscount--;
}
}
#define MAX_EEPRO 8
static struct net_device dev_eepro[MAX_EEPRO];
-static int io[MAX_EEPRO] = {
-#ifdef PnPWakeup
- 0x210, /*: default for PnP enabled FX chips */
-#else
- 0x200, /* Why? */
-#endif
- [1 ... MAX_EEPRO - 1] = -1 };
+static int io[MAX_EEPRO];
static int irq[MAX_EEPRO] = { [0 ... MAX_EEPRO-1] = 0 };
static int mem[MAX_EEPRO] = { /* Size of the rx buffer in KB */
- [0 ... MAX_EEPRO-1] = RCV_RAM/1024
+ [0 ... MAX_EEPRO-1] = RCV_DEFAULT_RAM/1024
};
+static int autodetect;
static int n_eepro = 0;
/* For linux 2.1.xx */
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_EEPRO) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_EEPRO) "i");
MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_EEPRO) "i");
+MODULE_PARM(autodetect, "1-" __MODULE_STRING(1) "i");
#ifdef MODULE
int
init_module(void)
{
- if (io[0] == 0)
- printk("eepro_init_module: You should not use auto-probing with insmod!\n");
+ int i;
+ if (io[0] == 0 && autodetect == 0) {
+ printk("eepro_init_module: Probe is very dangerous in ISA boards!\n");
+ printk("eepro_init_module: Please add \"autodetect=1\" to force probe\n");
+ return 1;
+ }
+ else if (autodetect) {
+ /* if autodetect is set then we must force detection */
+ io[0] = 0;
+
+ printk("eepro_init_module: Auto-detecting boards (May God protect us...)\n");
+ }
- while (n_eepro < MAX_EEPRO && io[n_eepro] >= 0) {
+ for (i = 0; i < MAX_EEPRO; i++) {
struct net_device *d = &dev_eepro[n_eepro];
d->mem_end = mem[n_eepro];
- d->base_addr = io[n_eepro];
+ d->base_addr = io[0];
d->irq = irq[n_eepro];
d->init = eepro_probe;
May not compile for kernels 2.3.43-47.
Written 1996-1999 by Donald Becker.
+ The driver also contains updates by different kernel developers
+ (see incomplete list below).
+ Current maintainer is Andrey V. Savochkin <saw@saw.sw.com.sg>.
+ Please use this email address and linux-kernel mailing list for bug reports.
+
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
It should work with all i82557/558/559 boards.
- The author may be reached as becker@CESDIS.usra.edu, or C/O
- Center of Excellence in Space Data and Information Sciences
- Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
- For updates see
- http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html
- For installation instructions
- http://cesdis.gsfc.nasa.gov/linux/misc/modules.html
- There is a Majordomo mailing list based at
- linux-eepro100@cesdis.gsfc.nasa.gov
-
- The driver also contains updates by different kernel developers
- (see incomplete list below).
- This driver clone is maintained by Andrey V. Savochkin <saw@saw.sw.com.sg>.
- Please use this email address and linux-kernel mailing list for bug reports.
-
Version history:
1998 Apr - 2000 Feb Andrey V. Savochkin <saw@saw.sw.com.sg>
Serious fixes for multicast filter list setting, TX timeout routine;
Convert to new PCI driver interface
2000 Mar 24 Dragan Stancevic <visitor@valinux.com>
Disabled FC and ER, to avoid lockups when when we get FCP interrupts.
- Dragan Stancevic <visitor@valinux.com> March 24th, 2000.
*/
static const char *version =
"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"
-"eepro100.c: $Revision: 1.29 $ 2000/03/30 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
+"eepro100.c: $Revision: 1.33 $ 2000/05/24 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
/* A few user-configurable values that apply to all boards.
First set is undocumented and spelled per Intel recommendations. */
}
#endif
-/* The total I/O port extent of the board.
- The registers beyond 0x18 only exist on the i82558. */
-#define SPEEDO3_TOTAL_SIZE 0x20
+#ifndef PCI_DEVICE_ID_INTEL_ID1029
+#define PCI_DEVICE_ID_INTEL_ID1029 0x1029
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ID1030
+#define PCI_DEVICE_ID_INTEL_ID1030 0x1030
+#endif
+
int speedo_debug = 1;
*/
-static int speedo_found1(struct pci_dev *pdev, long ioaddr, int irq, int chip_idx, int fnd_cnt, int acpi_idle_state);
-
-#ifdef USE_IO
-#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR1
-#define SPEEDO_SIZE 32
-#else
-#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR0
-#define SPEEDO_SIZE 0x1000
-#endif
+static int speedo_found1(struct pci_dev *pdev, long ioaddr, int fnd_cnt, int acpi_idle_state);
enum pci_flags_bit {
PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
};
+static inline unsigned int io_inw(unsigned long port)
+{
+ return inw(port);
+}
+static inline void io_outw(unsigned int val, unsigned long port)
+{
+ outw(val, port);
+}
+
#ifndef USE_IO
/* Currently alpha headers define in/out macros.
Undefine them. 2000/03/30 SAW */
int wait = 1000;
do ;
while(inb(cmd_ioaddr) && --wait >= 0);
+#ifndef final_version
+ if (wait < 0)
+ printk(KERN_ALERT "eepro100: wait_for_cmd_done timeout!\n");
+#endif
}
/* Offsets to the various registers.
#endif
enum SCBCmdBits {
- SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
- SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
- SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
- /* The rest are Rx and Tx commands. */
- CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
- CUCmdBase=0x0060, /* CU Base address (set to zero) . */
- CUDumpStats=0x0070, /* Dump then reset stats counters. */
- RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
- RxResumeNoResources=0x0007,
+ SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
+ SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+ SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
+ /* The rest are Rx and Tx commands. */
+ CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
+ CUCmdBase=0x0060, /* CU Base address (set to zero) . */
+ CUDumpStats=0x0070, /* Dump then reset stats counters. */
+ RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
+ RxResumeNoResources=0x0007,
};
enum SCBPort_cmds {
pci_set_master(pdev);
- if (speedo_found1(pdev, ioaddr, irq, 0, cards_found, acpi_idle_state) == 0)
+ if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0)
cards_found++;
else
goto err_out_iounmap;
}
static int speedo_found1(struct pci_dev *pdev,
- long ioaddr, int irq, int chip_idx, int card_idx, int acpi_idle_state)
+ long ioaddr, int card_idx, int acpi_idle_state)
{
struct net_device *dev;
struct speedo_private *sp;
The size test is for 6 bit vs. 8 bit address serial EEPROMs.
*/
{
- u16 sum = 0;
- int j;
+ unsigned long iobase;
int read_cmd, ee_size;
+ u16 sum;
+ int j;
- if ((do_eeprom_cmd(ioaddr, EE_READ_CMD << 24, 27) & 0xffe0000)
+ /* Use IO only to avoid postponed writes and satisfy EEPROM timing
+ requirements. */
+ iobase = pci_resource_start(pdev, 1);
+ if ((do_eeprom_cmd(iobase, EE_READ_CMD << 24, 27) & 0xffe0000)
== 0xffe0000) {
ee_size = 0x100;
read_cmd = EE_READ_CMD << 24;
read_cmd = EE_READ_CMD << 22;
}
- for (j = 0, i = 0; i < ee_size; i++) {
- u16 value = do_eeprom_cmd(ioaddr, read_cmd | (i << 16), 27);
+ for (j = 0, i = 0, sum = 0; i < ee_size; i++) {
+ u16 value = do_eeprom_cmd(iobase, read_cmd | (i << 16), 27);
eeprom[i] = value;
sum += value;
if (i < 3) {
"check settings before activating this device!\n",
dev->name, sum);
/* Don't unregister_netdev(dev); as the EEPro may actually be
- usable, especially if the MAC address is set later. */
+ usable, especially if the MAC address is set later.
+ On the other hand, it may be unusable if MDI data is corrupted. */
}
/* Reset the chip: stop Tx and Rx processes and clear counters.
#ifdef USE_IO
printk("I/O at %#3lx, ", ioaddr);
#endif
- printk("IRQ %d.\n", irq);
+ printk("IRQ %d.\n", pdev->irq);
#if 1 || defined(kernel_bloat)
/* OK, this is pure kernel bloat. I don't like it when other drivers
pdev->driver_data = dev;
dev->base_addr = ioaddr;
- dev->irq = irq;
+ dev->irq = pdev->irq;
sp = dev->priv;
sp->pdev = pdev;
#define EE_WRITE_1 0x4806
#define EE_OFFSET SCBeeprom
-/* Delay between EEPROM clock transitions.
- The code works with no delay on 33Mhz PCI. */
-#define eeprom_delay() inw(ee_addr)
-
+/* The fixes for the code were kindly provided by Dragan Stancevic
+ <visitor@valinux.com> to strictly follow Intel specifications of EEPROM
+ access timing.
+ The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
+ interval for serial EEPROM. However, it looks like that there is an
+ additional requirement dictating larger udelay's in the code below.
+ 2000/05/24 SAW */
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
{
unsigned retval = 0;
long ee_addr = ioaddr + SCBeeprom;
- outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ io_outw(EE_ENB, ee_addr); udelay(2);
+ io_outw(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
/* Shift the command bits out. */
do {
short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
- outw(dataval, ee_addr);
- eeprom_delay();
- outw(dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ io_outw(dataval, ee_addr); udelay(2);
+ io_outw(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
+ retval = (retval << 1) | ((io_inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
} while (--cmd_len >= 0);
- outw(EE_ENB, ee_addr);
+ io_outw(EE_ENB, ee_addr); udelay(2);
/* Terminate the EEPROM access. */
- outw(EE_ENB & ~EE_CS, ee_addr);
+ io_outw(EE_ENB & ~EE_CS, ee_addr); udelay(4);
return retval;
}
int partner = mdio_read(ioaddr, phy_num, 5);
if (partner != sp->partner) {
int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0;
- if (speedo_debug > 2)
+ if (speedo_debug > 2) {
printk(KERN_DEBUG "%s: Link status change.\n", dev->name);
+ printk(KERN_DEBUG "%s: Old partner %x, new %x, adv %x.\n",
+ dev->name, sp->partner, partner, sp->advertising);
+ }
sp->partner = partner;
if (flow_ctrl != sp->flow_ctrl) {
sp->flow_ctrl = flow_ctrl;
/* We must continue to monitor the media. */
sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */
add_timer(&sp->timer);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,43)
+ timer_exit(&sp->timer);
+#endif /* LINUX_VERSION_CODE */
}
static void speedo_show_state(struct net_device *dev)
i, (sp->rx_ringp[i] != NULL) ?
(unsigned)sp->rx_ringp[i]->status : 0);
+#if 0
for (i = 0; i < 16; i++) {
/* FIXME: what does it mean? --SAW */
if (i == 6) i = 21;
printk(KERN_DEBUG "%s: PHY index %d register %d is %4.4x.\n",
dev->name, phy_num, i, mdio_read(ioaddr, phy_num, i));
}
+#endif
}
netif_wake_queue(dev);
}
+static void reset_mii(struct net_device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
+ if ((sp->phy[0] & 0x8000) == 0) {
+ int phy_addr = sp->phy[0] & 0x1f;
+ int advertising = mdio_read(ioaddr, phy_addr, 4);
+ int mii_bmcr = mdio_read(ioaddr, phy_addr, 0);
+ mdio_write(ioaddr, phy_addr, 0, 0x0400);
+ mdio_write(ioaddr, phy_addr, 1, 0x0000);
+ mdio_write(ioaddr, phy_addr, 4, 0x0000);
+ mdio_write(ioaddr, phy_addr, 0, 0x8000);
+#ifdef honor_default_port
+ mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
+#else
+ mdio_read(ioaddr, phy_addr, 0);
+ mdio_write(ioaddr, phy_addr, 0, mii_bmcr);
+ mdio_write(ioaddr, phy_addr, 4, advertising);
+#endif
+ }
+}
+
static void speedo_tx_timeout(struct net_device *dev)
{
struct speedo_private *sp = (struct speedo_private *)dev->priv;
outl(cpu_to_le32(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE])),
ioaddr + SCBPointer);
outw(CUStart, ioaddr + SCBCmd);
+ reset_mii(dev);
} else {
#else
{
del_timer(&sp->timer);
end_bh_atomic();
#else /* LINUX_VERSION_CODE */
-#ifdef CONFIG_SMP
del_timer_sync(&sp->timer);
-#else /* SMP */
- del_timer(&sp->timer);
-#endif /* SMP */
#endif /* LINUX_VERSION_CODE */
/* Reset the Tx and Rx units. */
outl(PortReset, ioaddr + SCBPort);
dev->trans_start = jiffies;
spin_unlock_irqrestore(&sp->lock, flags);
set_rx_mode(dev); /* it takes the spinlock itself --SAW */
+ /* Reset MII transceiver. Do it before starting the timer to serialize
+ mdio_xxx operations. Yes, it's a paranoya :-) 2000/05/09 SAW */
+ reset_mii(dev);
sp->timer.expires = RUN_AT(2*HZ);
add_timer(&sp->timer);
}
- /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
- if ((sp->phy[0] & 0x8000) == 0) {
- int phy_addr = sp->phy[0] & 0x1f;
- int advertising = mdio_read(ioaddr, phy_addr, 4);
- int mii_bmcr = mdio_read(ioaddr, phy_addr, 0);
- mdio_write(ioaddr, phy_addr, 0, 0x0400);
- mdio_write(ioaddr, phy_addr, 1, 0x0000);
- mdio_write(ioaddr, phy_addr, 4, 0x0000);
- mdio_write(ioaddr, phy_addr, 0, 0x8000);
-#ifdef honor_default_port
- mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
-#else
- mdio_read(ioaddr, phy_addr, 0);
- mdio_write(ioaddr, phy_addr, 0, mii_bmcr);
- mdio_write(ioaddr, phy_addr, 4, advertising);
-#endif
- }
return;
}
sp->tx_ring[entry].link =
cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
sp->tx_ring[entry].tx_desc_addr =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE) +
- TX_DESCR_BUF_OFFSET);
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, entry) + TX_DESCR_BUF_OFFSET);
/* The data region is always in one buffer descriptor. */
sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold);
sp->tx_ring[entry].tx_buf_addr0 =
if (netif_running(dev)) {
unsigned long flags;
/* Take a spinlock to make wait_for_cmd_done and sending the
- * command atomic. --SAW */
+ command atomic. --SAW */
spin_lock_irqsave(&sp->lock, flags);
wait_for_cmd_done(ioaddr + SCBCmd);
outb(CUDumpStats, ioaddr + SCBCmd);
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = phy;
case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- /* FIXME: these operations probably need to be serialized with MDIO
- access from the timer routine and timeout handler. 2000/03/08 SAW */
+ /* FIXME: these operations need to be serialized with MDIO
+ access from the timeout handler.
+ They are currently serialized only with MDIO access from the
+ timer routine. 2000/05/09 SAW */
saved_acpi = pci_set_power_state(sp->pdev, 0);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+ start_bh_atomic();
+ data[3] = mdio_read(ioaddr, data[0], data[1]);
+ end_bh_atomic();
+#else /* LINUX_VERSION_CODE */
+ del_timer_sync(&sp->timer);
data[3] = mdio_read(ioaddr, data[0], data[1]);
+ add_timer(&sp->timer); /* may be set to the past --SAW */
+#endif /* LINUX_VERSION_CODE */
pci_set_power_state(sp->pdev, saved_acpi);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
if (!capable(CAP_NET_ADMIN))
return -EPERM;
saved_acpi = pci_set_power_state(sp->pdev, 0);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+ start_bh_atomic();
+ mdio_write(ioaddr, data[0], data[1], data[2]);
+ end_bh_atomic();
+#else /* LINUX_VERSION_CODE */
+ del_timer_sync(&sp->timer);
mdio_write(ioaddr, data[0], data[1], data[2]);
+ add_timer(&sp->timer); /* may be set to the past --SAW */
+#endif /* LINUX_VERSION_CODE */
pci_set_power_state(sp->pdev, saved_acpi);
return 0;
default:
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER,
PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1029,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0,}
};
MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
if (ioaddr&0xfe00)
return eexp_hw_probe(dev,ioaddr);
else if (ioaddr)
- return ENXIO;
+ return -ENXIO;
for (port=&ports[0] ; *port ; port++ )
{
dev->priv = lp = kmalloc(sizeof(struct net_local), GFP_KERNEL);
if (!dev->priv)
- return ENOMEM;
+ return -ENOMEM;
memset(dev->priv, 0, sizeof(struct net_local));
default:
printk(") bad memory size (%dk).\n", memory_size);
kfree(dev->priv);
- return ENODEV;
+ return -ENODEV;
break;
}
duplex = full_duplex[card_idx];
}
+ pdev->driver_data = dev;
+
dev->base_addr = ioaddr;
dev->irq = pdev->irq;
printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry,
ep->rx_ring[entry].rxstatus);
/* If we own the next entry, it's a new packet. Send it up. */
- while ( ! le32_to_cpu(ep->rx_ring[entry].rxstatus) & DescOwn) {
+ while (!(le32_to_cpu(ep->rx_ring[entry].rxstatus) & DescOwn)) {
int status = le32_to_cpu(ep->rx_ring[entry].rxstatus);
if (debug > 4)
{
struct net_device *dev = pdev->driver_data;
- if (!dev)
- BUG();
-
unregister_netdev(dev);
#ifndef USE_IO_OPS
iounmap ((void*) dev->base_addr);
struct net_device *dev = pdev->driver_data;
long ioaddr = dev->base_addr;
- if (!dev)
- BUG();
-
epic_pause(dev);
/* Put the chip into low-power mode. */
outl(0x0008, ioaddr + GENCTL);
{
struct net_device *dev = pdev->driver_data;
- if (!dev)
- BUG();
-
epic_restart (dev);
}
return ENODEV;
}
- if (load_8390_module("es3210.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("es3210.c: Passed a NULL device.\n");
{
int this_dev, found = 0;
+ if (load_8390_module("es3210.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
struct net_device *dev = &dev_es3210[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "es3210.c: No es3210 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
if(base_addr > 0x1ff) /* Check only single location */
return eth16i_probe1(dev, base_addr);
else if(base_addr != 0) /* Don't probe at all */
- return ENXIO;
+ return -ENXIO;
/* Seek card from the ISA io address space */
for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) {
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < clone_list[i].vendor_id != 0; i++)
while ((pdev = pci_find_device(clone_list[i].vendor_id, clone_list[i].device_id, pdev))) {
- unsigned short pci_command;
+ unsigned short pci_command;
+ if (pci_enable_device(pdev))
+ continue;
if (count < MAX_FC_CARDS) {
fc[count] = kmalloc(sizeof(struct fc_info), GFP_ATOMIC);
if (fc[count] == NULL) {
host->hostt->use_new_eh_code = 1;
host->this_id = tmpt->this_id;
- pci_maddr = pdev->resource[0].start;
- if ( (pdev->resource[0].flags & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ pci_maddr = pci_resource_start(pdev, 0);
+ if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
printk("iph5526.c : Cannot find proper PCI device base address.\n");
scsi_unregister(host);
kfree(fc[count]);
if (base_addr > 0x1ff) /* Check a single specified location. */
return fmv18x_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; fmv18x_probe_list[i]; i++) {
int ioaddr = fmv18x_probe_list[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
if (request_irq(irq, &net_interrupt, 0, "fmv18x", dev)) {
printk ("FMV-18x found at %#3x, but it's unusable due to a conflict on"
"IRQ %d.\n", ioaddr, irq);
- return EAGAIN;
+ return -EAGAIN;
}
/* Allocate a new 'dev' if needed. */
if (irqval) {
printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
dev->irq, irqval);
- return EAGAIN;
+ return -EAGAIN;
}
}
if (irqval) {
printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
dev->irq, irqval);
- return EAGAIN;
+ return -EAGAIN;
}
}
if (base_addr > 0x1ff) /* Check a single specified location. */
return hpp_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; hpplus_portlist[i]; i++) {
int ioaddr = hpplus_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
/* Check for the HP+ signature, 50 48 0x 53. */
if (inw(ioaddr + HP_ID) != 0x4850
|| (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
- return ENODEV;
-
- if (load_8390_module("hp-plus.c"))
- return -ENOSYS;
+ return -ENODEV;
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
if (checksum != 0xff) {
printk(" bad checksum %2.2x.\n", checksum);
- return ENODEV;
+ return -ENODEV;
} else {
/* Point at the Software Configuration Flags. */
outw(ID_Page, ioaddr + HP_PAGING);
{
int this_dev, found = 0;
+ if (load_8390_module("hp-plus.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
struct net_device *dev = &dev_hpp[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
if (base_addr > 0x1ff) /* Check a single specified location. */
return hp_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; hppclan_portlist[i]; i++) {
int ioaddr = hppclan_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
|| inb(ioaddr+1) != 0x00
|| inb(ioaddr+2) != 0x09
|| inb(ioaddr+14) == 0x57)
- return ENODEV;
+ return -ENODEV;
/* Set up the parameters based on the board ID.
If you have additional mappings, please mail them to me -djb. */
wordmode = 0;
}
- if (load_8390_module("hp.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("hp.c: Passed a NULL device.\n");
printk(" no free IRQ lines.\n");
kfree(dev->priv);
dev->priv = NULL;
- return EBUSY;
+ return -EBUSY;
}
} else {
if (dev->irq == 2)
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return EBUSY;
+ return -EBUSY;
}
}
{
int this_dev, found = 0;
+ if (load_8390_module("hp.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
struct net_device *dev = &dev_hp[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
** Extended for new busmaster capable chipsets by
** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
**
-** Maintained by: Jaroslav Kysela <perex@jcu.cz>
+** Maintained by: Jaroslav Kysela <perex@suse.cz>
**
** This driver has only been tested with
** -- HP J2585B 10/100 Mbit/s PCI Busmaster
#include <linux/delay.h>
#include <linux/init.h>
-#if LINUX_VERSION_CODE >= 0x020100
#define LINUX_2_1
typedef struct net_device_stats hp100_stats_t;
EXPORT_NO_SYMBOLS;
-#else
-#include <linux/bios32.h>
-#define ioremap vremap
-#define iounmap vfree
-typedef struct enet_statistics hp100_stats_t;
-#endif
#include "hp100.h"
u_short priority_tx; /* != 0 - priority tx */
u_short mode; /* PIO, Shared Mem or Busmaster */
u_char bus;
-#ifndef LINUX_2_1
- u_char pci_bus;
- u_char pci_device_fn;
-#else
struct pci_dev *pci_dev;
-#endif
short mem_mapped; /* memory mapped access */
void *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */
unsigned long mem_ptr_phys; /* physical memory mapped area */
static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
static int hp100_mode = 1;
-#ifdef LINUX_2_1
MODULE_PARM( hp100_rx_ratio, "1i" );
MODULE_PARM( hp100_priority_tx, "1i" );
MODULE_PARM( hp100_mode, "1i" );
-#endif
/*
* prototypes
*/
-#ifdef LINUX_2_1
static int hp100_probe1( struct net_device *dev, int ioaddr, u_char bus, struct pci_dev *pci_dev );
-#else
-static int hp100_probe1( struct net_device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn );
-#endif
static int hp100_open( struct net_device *dev );
static int hp100_close( struct net_device *dev );
static int hp100_start_xmit( struct sk_buff *skb, struct net_device *dev );
{
if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
if ( base_addr < 0x400 )
-#ifdef LINUX_2_1
return hp100_probe1( dev, base_addr, HP100_BUS_ISA, NULL );
-#else
- return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
-#endif
if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 )
-#ifdef LINUX_2_1
return hp100_probe1( dev, base_addr, HP100_BUS_EISA, NULL );
-#else
- return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
-#endif
#ifdef CONFIG_PCI
-#ifdef LINUX_2_1
printk( "hp100: %s: You must specify card # in i/o address parameter for PCI bus...", dev->name );
-#else
- printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name );
- return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 );
-#endif
#else
return -ENODEV;
#endif
if ( pcibios_present() )
{
int pci_index;
-#ifdef LINUX_2_1
struct pci_dev *pci_dev = NULL;
int pci_id_index;
u_short pci_command;
-#endif
#ifdef HP100_DEBUG_PCI
printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name );
#endif
-#ifdef LINUX_2_1
pci_index = 0;
for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ ) {
while ( (pci_dev = pci_find_device( hp100_pci_ids[ pci_id_index ].vendor,
pci_index++;
continue;
}
+ if (pci_enable_device(pci_dev))
+ continue;
/* found... */
- ioaddr = pci_dev ->resource[ 0 ].start;
+ ioaddr = pci_resource_start (pci_dev, 0);
if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
pci_read_config_word( pci_dev, PCI_COMMAND, &pci_command );
if ( !( pci_command & PCI_COMMAND_IO ) ) {
return 0;
}
}
-#else /* old PCI interface */
- for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
- {
- u_char pci_bus, pci_device_fn;
- u_short pci_command;
- int pci_id_index;
-
- for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ )
- if ( pcibios_find_device( hp100_pci_ids[ pci_id_index ].vendor,
- hp100_pci_ids[ pci_id_index ].device,
- pci_index, &pci_bus,
- &pci_device_fn ) == 0 ) goto __pci_found;
- break;
-
- __pci_found:
- pcibios_read_config_dword( pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &ioaddr );
-
- ioaddr &= ~3; /* remove I/O space marker in bit 0. */
-
- if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-
- pcibios_read_config_word( pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command );
- if ( !( pci_command & PCI_COMMAND_IO ) )
- {
-#ifdef HP100_DEBUG
- printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name );
-#endif
- pci_command |= PCI_COMMAND_IO;
- pcibios_write_config_word( pci_bus, pci_device_fn,
- PCI_COMMAND, pci_command );
- }
- if ( !( pci_command & PCI_COMMAND_MASTER ) )
- {
-#ifdef HP100_DEBUG
- printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name );
-#endif
- pci_command |= PCI_COMMAND_MASTER;
- pcibios_write_config_word( pci_bus, pci_device_fn,
- PCI_COMMAND, pci_command );
- }
-#ifdef HP100_DEBUG
- printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr );
-#endif
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
- return 0;
- }
-#endif
}
if ( pci_start_index > 0 ) return -ENODEV;
#endif /* CONFIG_PCI */
for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
{
if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-#ifdef LINUX_2_1
if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, NULL ) == 0 ) return 0;
-#else
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
-#endif
}
/* Third Probe all ISA possible port regions */
for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
{
if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-#ifdef LINUX_2_1
if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, NULL ) == 0 ) return 0;
-#else
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
-#endif
}
return -ENODEV;
}
\f
-#ifdef LINUX_2_1
static int __init hp100_probe1( struct net_device *dev, int ioaddr, u_char bus, struct pci_dev *pci_dev )
-#else
-static int __init hp100_probe1( struct net_device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )
-#endif
{
int i;
#ifdef HP100_DEBUG
printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name );
#endif
- return EIO;
+ return -EIO;
}
if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE )
lp->chip = chip;
lp->mode = local_mode;
lp->bus = bus;
-#ifdef LINUX_2_1
lp->pci_dev = pci_dev;
-#else
- lp->pci_bus = pci_bus;
- lp->pci_device_fn = pci_device_fn;
-#endif
lp->priority_tx = hp100_priority_tx;
lp->rx_ratio = hp100_rx_ratio;
lp->mem_ptr_phys = mem_ptr_phys;
dev->set_multicast_list = &hp100_set_multicast_list;
/* Ask the card for which IRQ line it is configured */
-#ifdef LINUX_2_1
if ( bus == HP100_BUS_PCI ) {
dev->irq = pci_dev->irq;
} else {
-#endif
hp100_page( HW_MAP );
dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
if ( dev->irq == 2 )
dev->irq = 9;
-#ifdef LINUX_2_1
}
-#endif
if(lp->mode==1) /* busmaster */
dev->dma=4;
if ( skb==NULL )
{
-#ifndef LINUX_2_1
- dev_tint( dev );
-#endif
return 0;
}
/* Update statistics */
lp->stats.tx_packets++;
-#ifdef LINUX_2_1
lp->stats.tx_bytes += skb->len;
-#endif
dev->trans_start = jiffies;
return 0;
hp100_inb(TX_PDL),
donecount);
#endif
-#ifdef LINUX_2_1
dev_kfree_skb_any( lp->txrhead->skb );
-#else
- dev_kfree_skb_any( lp->txrhead->skb, FREE_WRITE );
-#endif
lp->txrhead->skb=(void *)NULL;
lp->txrhead=lp->txrhead->next;
lp->txrcommit--;
if ( skb==NULL )
{
-#ifndef LINUX_2_1
- dev_tint( dev );
-#endif
return 0;
}
hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
lp->stats.tx_packets++;
-#ifdef LINUX_2_1
lp->stats.tx_bytes += skb->len;
-#endif
dev->trans_start=jiffies;
hp100_ints_on();
-#ifdef LINUX_2_1
dev_kfree_skb_any( skb );
-#else
- dev_kfree_skb_any( skb, FREE_WRITE );
-#endif
#ifdef HP100_DEBUG_TX
printk( "hp100: %s: start_xmit: end\n", dev->name );
netif_rx( skb );
lp->stats.rx_packets++;
-#ifdef LINUX_2_1
lp->stats.rx_bytes += skb->len;
-#endif
#ifdef HP100_DEBUG_RX
printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
netif_rx( ptr->skb ); /* Up and away... */
lp->stats.rx_packets++;
-#ifdef LINUX_2_1
lp->stats.rx_bytes += ptr->skb->len;
-#endif
}
switch ( header & 0x00070000 ) {
printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len);
#endif
if(ptr->skb!=NULL)
-#ifdef LINUX_2_1
dev_kfree_skb_any( ptr->skb );
-#else
- dev_kfree_skb_any( ptr->skb, FREE_READ );
-#endif
lp->stats.rx_errors++;
}
/* Parameters set by insmod */
int hp100_port[5] = { 0, -1, -1, -1, -1 };
-#ifdef LINUX_2_1
MODULE_PARM(hp100_port, "1-5i");
-#endif
/* Allocate 5 string of length IFNAMSIZ, one string for each device */
char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
-#ifdef LINUX_2_1
/* Allow insmod to write those 5 strings individually */
MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
-#endif
/* List of devices */
static struct net_device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
}
/* OK, return success, or ENODEV if we didn't find any cards */
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
--- /dev/null
+/*
+net-3-driver for the IBM LAN Adapter/A
+
+This is an extension to the Linux operating system, and is covered by the
+same Gnu Public License that covers that work.
+
+Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de, aarnold@elsa.de)
+
+This driver is based both on the SK_MCA driver, which is itself based on the
+SK_G16 and 3C523 driver.
+
+paper sources:
+ 'PC Hardware: Aufbau, Funktionsweise, Programmierung' by
+ Hans-Peter Messmer for the basic Microchannel stuff
+
+ 'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
+ for help on Ethernet driver programming
+
+ 'DP83934CVUL-20/25 MHz SONIC-T Ethernet Controller Datasheet' by National
+ Semiconductor for info on the MAC chip
+
+ 'LAN Technical Reference Ethernet Adapter Interface Version 1 Release 1.0
+ Document Number SC30-3661-00' by IBM for info on the adapter itself
+
+ Also see http://www.natsemi.com/
+
+special acknowledgements to:
+ - Bob Eager for helping me out with documentation from IBM
+ - Jim Shorney for his endless patience with me while I was using
+ him as a beta tester to trace down the address filter bug ;-)
+
+ Missing things:
+
+ -> set debug level via ioctl instead of compile-time switches
+ -> I didn't follow the development of the 2.1.x kernels, so my
+ assumptions about which things changed with which kernel version
+ are probably nonsense
+
+History:
+ Nov 6th, 1999
+ startup from SK_MCA driver
+ Dec 6th, 1999
+ finally got docs about the card. A big thank you to Bob Eager!
+ Dec 12th, 1999
+ first packet received
+ Dec 13th, 1999
+ recv queue done, tcpdump works
+ Dec 15th, 1999
+ transmission part works
+ Dec 28th, 1999
+ added usage of the isa_functions for Linux 2.3 . Things should
+ still work with 2.0.x....
+ Jan 28th, 2000
+ in Linux 2.2.13, the version.h file mysteriously didn't get
+ included. Added a workaround for this. Futhermore, it now
+ not only compiles as a modules ;-)
+ Jan 30th, 2000
+ newer kernels automatically probe more than one board, so the
+ 'startslot' as a variable is also needed here
+ Apr 12th, 2000
+ the interrupt mask register is not set 'hard' instead of individually
+ setting registers, since this seems to set bits that shouldn't be
+ set
+ May 21st, 2000
+ reset interrupt status immediately after CAM load
+ add a recovery delay after releasing the chip's reset line
+ May 24th, 2000
+ finally found the bug in the address filter setup - damned signed
+ chars!
+ June 1st, 2000
+ corrected version codes, added support for the latest 2.3 changes
+
+ *************************************************************************/
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/mca.h>
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define _IBM_LANA_DRIVER_
+#include "ibmlana.h"
+
+#undef DEBUG
+
+/* ------------------------------------------------------------------------
+ * global static data - not more since we can handle multiple boards and
+ * have to pack all state info into the device struct!
+ * ------------------------------------------------------------------------ */
+
+static char *MediaNames[Media_Count] =
+ { "10BaseT", "10Base5", "Unknown", "10Base2" };
+
+/* ------------------------------------------------------------------------
+ * private subfunctions
+ * ------------------------------------------------------------------------ */
+
+#ifdef DEBUG
+ /* dump all registers */
+
+static void dumpregs(struct IBMLANA_NETDEV *dev)
+{
+ int z;
+
+ for (z = 0; z < 160; z += 2) {
+ if (!(z & 15))
+ printk("REGS: %04x:", z);
+ printk(" %04x", inw(dev->base_addr + z));
+ if ((z & 15) == 14)
+ printk("\n");
+ }
+}
+
+/* dump parts of shared memory - only needed during debugging */
+
+static void dumpmem(struct IBMLANA_NETDEV *dev, u32 start, u32 len)
+{
+ int z;
+
+ printk("Address %04x:\n", start);
+ for (z = 0; z < len; z++) {
+ if ((z & 15) == 0)
+ printk("%04x:", z);
+ printk(" %02x", IBMLANA_READB(dev->mem_start + start + z));
+ if ((z & 15) == 15)
+ printk("\n");
+ }
+ if ((z & 15) != 0)
+ printk("\n");
+}
+
+/* print exact time - ditto */
+
+static void PrTime(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ printk("%9d:%06d: ", (int) tv.tv_sec, (int) tv.tv_usec);
+}
+#endif /* DEBUG */
+
+/* deduce resources out of POS registers */
+
+static void getaddrs(int slot, int *base, int *memlen, int *iobase,
+ int *irq, ibmlana_medium * medium)
+{
+ u_char pos0, pos1;
+
+ pos0 = mca_read_stored_pos(slot, 2);
+ pos1 = mca_read_stored_pos(slot, 3);
+
+ *base = 0xc0000 + ((pos1 & 0xf0) << 9);
+ *memlen = (pos1 & 0x01) ? 0x8000 : 0x4000;
+ *iobase = (pos0 & 0xe0) << 7;
+ switch (pos0 & 0x06) {
+ case 0:
+ *irq = 5;
+ break;
+ case 2:
+ *irq = 15;
+ break;
+ case 4:
+ *irq = 10;
+ break;
+ case 6:
+ *irq = 11;
+ break;
+ }
+ *medium = (pos0 & 0x18) >> 3;
+}
+
+/* wait on register value with mask and timeout */
+
+static int wait_timeout(struct IBMLANA_NETDEV *dev, int regoffs, u16 mask,
+ u16 value, int timeout)
+{
+ unsigned long fin = jiffies + timeout;
+
+ while (jiffies != fin)
+ if ((inw(dev->base_addr + regoffs) & mask) == value)
+ return 1;
+
+ return 0;
+}
+
+
+/* reset the whole board */
+
+static void ResetBoard(struct IBMLANA_NETDEV *dev)
+{
+ unsigned char bcmval;
+
+ /* read original board control value */
+
+ bcmval = inb(dev->base_addr + BCMREG);
+
+ /* set reset bit for a while */
+
+ bcmval |= BCMREG_RESET;
+ outb(bcmval, dev->base_addr + BCMREG);
+ udelay(10);
+ bcmval &= ~BCMREG_RESET;
+ outb(bcmval, dev->base_addr + BCMREG);
+
+ /* switch over to RAM again */
+
+ bcmval |= BCMREG_RAMEN | BCMREG_RAMWIN;
+ outb(bcmval, dev->base_addr + BCMREG);
+}
+
+/* calculate RAM layout & set up descriptors in RAM */
+
+static void InitDscrs(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ u32 addr, baddr, raddr;
+ int z;
+ tda_t tda;
+ rda_t rda;
+ rra_t rra;
+
+ /* initialize RAM */
+
+ IBMLANA_SETIO(dev->mem_start, 0xaa,
+ dev->mem_start - dev->mem_start);
+
+ /* setup n TX descriptors - independent of RAM size */
+
+ priv->tdastart = addr = 0;
+ priv->txbufstart = baddr = sizeof(tda_t) * TXBUFCNT;
+ for (z = 0; z < TXBUFCNT; z++) {
+ tda.status = 0;
+ tda.config = 0;
+ tda.length = 0;
+ tda.fragcount = 1;
+ tda.startlo = baddr;
+ tda.starthi = 0;
+ tda.fraglength = 0;
+ if (z == TXBUFCNT - 1)
+ tda.link = priv->tdastart;
+ else
+ tda.link = addr + sizeof(tda_t);
+ tda.link |= 1;
+ IBMLANA_TOIO(dev->mem_start + addr, &tda, sizeof(tda_t));
+ addr += sizeof(tda_t);
+ baddr += PKTSIZE;
+ }
+
+ /* calculate how many receive buffers fit into remaining memory */
+
+ priv->rxbufcnt = (dev->mem_end - dev->mem_start - baddr) /
+ (sizeof(rra_t) + sizeof(rda_t) + PKTSIZE);
+
+ /* calculate receive addresses */
+
+ priv->rrastart = raddr = priv->txbufstart + (TXBUFCNT * PKTSIZE);
+ priv->rdastart = addr =
+ priv->rrastart + (priv->rxbufcnt * sizeof(rra_t));
+ priv->rxbufstart = baddr =
+ priv->rdastart + (priv->rxbufcnt * sizeof(rda_t));
+ for (z = 0; z < priv->rxbufcnt; z++) {
+ rra.startlo = baddr;
+ rra.starthi = 0;
+ rra.cntlo = PKTSIZE >> 1;
+ rra.cnthi = 0;
+ IBMLANA_TOIO(dev->mem_start + raddr, &rra, sizeof(rra_t));
+
+ rda.status = 0;
+ rda.length = 0;
+ rda.startlo = 0;
+ rda.starthi = 0;
+ rda.seqno = 0;
+ if (z < priv->rxbufcnt - 1)
+ rda.link = addr + sizeof(rda_t);
+ else
+ rda.link = 1;
+ rda.inuse = 1;
+ IBMLANA_TOIO(dev->mem_start + addr, &rda, sizeof(rda_t));
+
+ baddr += PKTSIZE;
+ raddr += sizeof(rra_t);
+ addr += sizeof(rda_t);
+ }
+
+ /* initialize current pointers */
+
+ priv->nextrxdescr = 0;
+ priv->lastrxdescr = priv->rxbufcnt - 1;
+ priv->nexttxdescr = 0;
+ priv->currtxdescr = 0;
+ priv->txusedcnt = 0;
+ memset(priv->txused, 0, sizeof(priv->txused));
+}
+
+/* set up Rx + Tx descriptors in SONIC */
+
+static int InitSONIC(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+
+ /* set up start & end of resource area */
+
+ outw(0, SONIC_URRA);
+ outw(priv->rrastart, dev->base_addr + SONIC_RSA);
+ outw(priv->rrastart + (priv->rxbufcnt * sizeof(rra_t)),
+ dev->base_addr + SONIC_REA);
+ outw(priv->rrastart, dev->base_addr + SONIC_RRP);
+ outw(priv->rrastart, dev->base_addr + SONIC_RWP);
+
+ /* set EOBC so that only one packet goes into one buffer */
+
+ outw((PKTSIZE - 4) >> 1, dev->base_addr + SONIC_EOBC);
+
+ /* let SONIC read the first RRA descriptor */
+
+ outw(CMDREG_RRRA, dev->base_addr + SONIC_CMDREG);
+ if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_RRRA, 0, 2)) {
+ printk
+ ("%s: SONIC did not respond on RRRA command - giving up.",
+ dev->name);
+ return 0;
+ }
+
+ /* point SONIC to the first RDA */
+
+ outw(0, dev->base_addr + SONIC_URDA);
+ outw(priv->rdastart, dev->base_addr + SONIC_CRDA);
+
+ /* set upper half of TDA address */
+
+ outw(0, dev->base_addr + SONIC_UTDA);
+
+ return 1;
+}
+
+/* stop SONIC so we can reinitialize it */
+
+static void StopSONIC(struct IBMLANA_NETDEV *dev)
+{
+ /* disable interrupts */
+
+ outb(inb(dev->base_addr + BCMREG) & (~BCMREG_IEN),
+ dev->base_addr + BCMREG);
+ outb(0, dev->base_addr + SONIC_IMREG);
+
+ /* reset the SONIC */
+
+ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+ udelay(10);
+ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+}
+
+/* initialize card and SONIC for proper operation */
+
+static void putcam(camentry_t * cams, int *camcnt, char *addr)
+{
+ camentry_t *pcam = cams + (*camcnt);
+ u8 *uaddr = (u8 *) addr;
+
+ pcam->index = *camcnt;
+ pcam->addr0 = (((u16) uaddr[1]) << 8) | uaddr[0];
+ pcam->addr1 = (((u16) uaddr[3]) << 8) | uaddr[2];
+ pcam->addr2 = (((u16) uaddr[5]) << 8) | uaddr[4];
+ (*camcnt)++;
+}
+
+static void InitBoard(struct IBMLANA_NETDEV *dev)
+{
+ int camcnt;
+ camentry_t cams[16];
+ u32 cammask;
+ struct dev_mc_list *mcptr;
+ u16 rcrval;
+
+ /* reset the SONIC */
+
+ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+ udelay(10);
+
+ /* clear all spurious interrupts */
+
+ outw(inw(dev->base_addr + SONIC_ISREG),
+ dev->base_addr + SONIC_ISREG);
+
+ /* set up the SONIC's bus interface - constant for this adapter -
+ must be done while the SONIC is in reset */
+
+ outw(DCREG_USR1 | DCREG_USR0 | DCREG_WC1 | DCREG_DW32,
+ dev->base_addr + SONIC_DCREG);
+ outw(0, dev->base_addr + SONIC_DCREG2);
+
+ /* remove reset form the SONIC */
+
+ outw(0, dev->base_addr + SONIC_CMDREG);
+ udelay(10);
+
+ /* data sheet requires URRA to be programmed before setting up the CAM contents */
+
+ outw(0, dev->base_addr + SONIC_URRA);
+
+ /* program the CAM entry 0 to the device address */
+
+ camcnt = 0;
+ putcam(cams, &camcnt, dev->dev_addr);
+
+ /* start putting the multicast addresses into the CAM list. Stop if
+ it is full. */
+
+ for (mcptr = dev->mc_list; mcptr != NULL; mcptr = mcptr->next) {
+ putcam(cams, &camcnt, mcptr->dmi_addr);
+ if (camcnt == 16)
+ break;
+ }
+
+ /* calculate CAM mask */
+
+ cammask = (1 << camcnt) - 1;
+
+ /* feed CDA into SONIC, initialize RCR value (always get broadcasts) */
+
+ IBMLANA_TOIO(dev->mem_start, cams, sizeof(camentry_t) * camcnt);
+ IBMLANA_TOIO(dev->mem_start + (sizeof(camentry_t) * camcnt),
+ &cammask, sizeof(cammask));
+
+#ifdef DEBUG
+ printk("CAM setup:\n");
+ dumpmem(dev, 0, sizeof(camentry_t) * camcnt + sizeof(cammask));
+#endif
+
+ outw(0, dev->base_addr + SONIC_CAMPTR);
+ outw(camcnt, dev->base_addr + SONIC_CAMCNT);
+ outw(CMDREG_LCAM, dev->base_addr + SONIC_CMDREG);
+ if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_LCAM, 0, 2)) {
+ printk
+ ("%s:SONIC did not respond on LCAM command - giving up.",
+ dev->name);
+ return;
+ } else {
+ /* clear interrupt condition */
+
+ outw(ISREG_LCD, dev->base_addr + SONIC_ISREG);
+
+#ifdef DEBUG
+ printk("Loading CAM done, address pointers %04x:%04x\n",
+ inw(dev->base_addr + SONIC_URRA),
+ inw(dev->base_addr + SONIC_CAMPTR));
+ {
+ int z;
+
+ printk("\n-->CAM: PTR %04x CNT %04x\n",
+ inw(dev->base_addr + SONIC_CAMPTR),
+ inw(dev->base_addr + SONIC_CAMCNT));
+ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+ for (z = 0; z < camcnt; z++) {
+ outw(z, dev->base_addr + SONIC_CAMEPTR);
+ printk("Entry %d: %04x %04x %04x\n", z,
+ inw(dev->base_addr +
+ SONIC_CAMADDR0),
+ inw(dev->base_addr +
+ SONIC_CAMADDR1),
+ inw(dev->base_addr +
+ SONIC_CAMADDR2));
+ }
+ outw(0, dev->base_addr + SONIC_CMDREG);
+ }
+#endif
+ }
+
+ rcrval = RCREG_BRD | RCREG_LB_NONE;
+
+ /* if still multicast addresses left or ALLMULTI is set, set the multicast
+ enable bit */
+
+ if ((dev->flags & IFF_ALLMULTI) || (mcptr != NULL))
+ rcrval |= RCREG_AMC;
+
+ /* promiscous mode ? */
+
+ if (dev->flags & IFF_PROMISC)
+ rcrval |= RCREG_PRO;
+
+ /* program receive mode */
+
+ outw(rcrval, dev->base_addr + SONIC_RCREG);
+#ifdef DEBUG
+ printk("\nRCRVAL: %04x\n", rcrval);
+#endif
+
+ /* set up descriptors in shared memory + feed them into SONIC registers */
+
+ InitDscrs(dev);
+ if (!InitSONIC(dev))
+ return;
+
+ /* reset all pending interrupts */
+
+ outw(0xffff, dev->base_addr + SONIC_ISREG);
+
+ /* enable transmitter + receiver interrupts */
+
+ outw(CMDREG_RXEN, dev->base_addr + SONIC_CMDREG);
+ outw(IMREG_PRXEN | IMREG_RBEEN | IMREG_PTXEN | IMREG_TXEREN,
+ dev->base_addr + SONIC_IMREG);
+
+ /* turn on card interrupts */
+
+ outb(inb(dev->base_addr + BCMREG) | BCMREG_IEN,
+ dev->base_addr + BCMREG);
+
+#ifdef DEBUG
+ printk("Register dump after initialization:\n");
+ dumpregs(dev);
+#endif
+}
+
+/* start transmission of a descriptor */
+
+static void StartTx(struct IBMLANA_NETDEV *dev, int descr)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ int addr;
+
+ addr = priv->tdastart + (descr * sizeof(tda_t));
+
+ /* put descriptor address into SONIC */
+
+ outw(addr, dev->base_addr + SONIC_CTDA);
+
+ /* trigger transmitter */
+
+ priv->currtxdescr = descr;
+ outw(CMDREG_TXP, dev->base_addr + SONIC_CMDREG);
+}
+
+/* ------------------------------------------------------------------------
+ * interrupt handler(s)
+ * ------------------------------------------------------------------------ */
+
+/* receive buffer area exhausted */
+
+static void irqrbe_handler(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+
+ /* point the SONIC back to the RRA start */
+
+ outw(priv->rrastart, dev->base_addr + SONIC_RRP);
+ outw(priv->rrastart, dev->base_addr + SONIC_RWP);
+}
+
+/* receive interrupt */
+
+static void irqrx_handler(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ rda_t rda;
+ u32 rdaaddr, lrdaaddr;
+
+ /* loop until ... */
+
+ while (1) {
+ /* read descriptor that was next to be filled by SONIC */
+
+ rdaaddr =
+ priv->rdastart + (priv->nextrxdescr * sizeof(rda_t));
+ lrdaaddr =
+ priv->rdastart + (priv->lastrxdescr * sizeof(rda_t));
+ IBMLANA_FROMIO(&rda, dev->mem_start + rdaaddr,
+ sizeof(rda_t));
+
+ /* iron out upper word halves of fields we use - SONIC will duplicate
+ bits 0..15 to 16..31 */
+
+ rda.status &= 0xffff;
+ rda.length &= 0xffff;
+ rda.startlo &= 0xffff;
+
+ /* stop if the SONIC still owns it, i.e. there is no data for us */
+
+ if (rda.inuse)
+ break;
+
+ /* good packet? */
+
+ else if (rda.status & RCREG_PRX) {
+ struct sk_buff *skb;
+
+ /* fetch buffer */
+
+ skb = dev_alloc_skb(rda.length + 2);
+ if (skb == NULL)
+ priv->stat.rx_dropped++;
+ else {
+ /* copy out data */
+
+ IBMLANA_FROMIO(skb_put(skb, rda.length),
+ dev->mem_start +
+ rda.startlo, rda.length);
+
+ /* set up skb fields */
+
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* bookkeeping */
+
+ priv->stat.rx_packets++;
+#if (LINUX_VERSION_CODE >= 0x20119) /* byte counters for kernel >= 2.1.25 */
+ priv->stat.rx_bytes += rda.length;
+#endif
+
+ /* pass to the upper layers */
+
+ netif_rx(skb);
+ }
+ }
+
+ /* otherwise check error status bits and increase statistics */
+
+ else {
+ priv->stat.rx_errors++;
+
+ if (rda.status & RCREG_FAER)
+ priv->stat.rx_frame_errors++;
+
+ if (rda.status & RCREG_CRCR)
+ priv->stat.rx_crc_errors++;
+ }
+
+ /* descriptor processed, will become new last descriptor in queue */
+
+ rda.link = 1;
+ rda.inuse = 1;
+ IBMLANA_TOIO(dev->mem_start + rdaaddr, &rda,
+ sizeof(rda_t));
+
+ /* set up link and EOL = 0 in currently last descriptor. Only write
+ the link field since the SONIC may currently already access the
+ other fields. */
+
+ IBMLANA_TOIO(dev->mem_start + lrdaaddr + 20, &rdaaddr, 4);
+
+ /* advance indices */
+
+ priv->lastrxdescr = priv->nextrxdescr;
+ if ((++priv->nextrxdescr) >= priv->rxbufcnt)
+ priv->nextrxdescr = 0;
+ }
+}
+
+/* transmit interrupt */
+
+static void irqtx_handler(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ tda_t tda;
+
+ /* fetch descriptor (we forgot the size ;-) */
+
+ IBMLANA_FROMIO(&tda,
+ dev->mem_start + priv->tdastart +
+ (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
+
+ /* update statistics */
+
+ priv->stat.tx_packets++;
+#if (LINUX_VERSION_CODE >= 0x020119)
+ priv->stat.tx_bytes += tda.length;
+#endif
+
+ /* update our pointers */
+
+ priv->txused[priv->currtxdescr] = 0;
+ priv->txusedcnt--;
+
+ /* if there are more descriptors present in RAM, start them */
+
+ if (priv->txusedcnt > 0)
+ StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
+
+ /* tell the upper layer we can go on transmitting */
+
+#if LINUX_VERSION_CODE >= 0x02032a
+ netif_wake_queue(dev);
+#else
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+#endif
+}
+
+static void irqtxerr_handler(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ tda_t tda;
+
+ /* fetch descriptor to check status */
+
+ IBMLANA_FROMIO(&tda,
+ dev->mem_start + priv->tdastart +
+ (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
+
+ /* update statistics */
+
+ priv->stat.tx_errors++;
+ if (tda.status & (TCREG_NCRS | TCREG_CRSL))
+ priv->stat.tx_carrier_errors++;
+ if (tda.status & TCREG_EXC)
+ priv->stat.tx_aborted_errors++;
+ if (tda.status & TCREG_OWC)
+ priv->stat.tx_window_errors++;
+ if (tda.status & TCREG_FU)
+ priv->stat.tx_fifo_errors++;
+
+ /* update our pointers */
+
+ priv->txused[priv->currtxdescr] = 0;
+ priv->txusedcnt--;
+
+ /* if there are more descriptors present in RAM, start them */
+
+ if (priv->txusedcnt > 0)
+ StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
+
+ /* tell the upper layer we can go on transmitting */
+
+#if LINUX_VERSION_CODE >= 0x02032a
+ netif_wake_queue(dev);
+#else
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+#endif
+}
+
+/* general interrupt entry */
+
+static void irq_handler(int irq, void *device, struct pt_regs *regs)
+{
+ struct IBMLANA_NETDEV *dev = (struct IBMLANA_NETDEV *) device;
+ u16 ival;
+
+ /* in case we're not meant... */
+
+ if (!(inb(dev->base_addr + BCMREG) & BCMREG_IPEND))
+ return;
+
+#if (LINUX_VERSION_CODE >= 0x02032a)
+#if 0
+ set_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+#else
+ dev->interrupt = 1;
+#endif
+
+ /* loop through the interrupt bits until everything is clear */
+
+ while (1) {
+ ival = inw(dev->base_addr + SONIC_ISREG);
+
+ if (ival & ISREG_RBE) {
+ irqrbe_handler(dev);
+ outw(ISREG_RBE, dev->base_addr + SONIC_ISREG);
+ }
+
+ if (ival & ISREG_PKTRX) {
+ irqrx_handler(dev);
+ outw(ISREG_PKTRX, dev->base_addr + SONIC_ISREG);
+ }
+
+ if (ival & ISREG_TXDN) {
+ irqtx_handler(dev);
+ outw(ISREG_TXDN, dev->base_addr + SONIC_ISREG);
+ }
+
+ if (ival & ISREG_TXER) {
+ irqtxerr_handler(dev);
+ outw(ISREG_TXER, dev->base_addr + SONIC_ISREG);
+ }
+
+ break;
+ }
+
+#if (LINUX_VERSION_CODE >= 0x02032a)
+#if 0
+ clear_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+#else
+ dev->interrupt = 0;
+#endif
+}
+
+/* ------------------------------------------------------------------------
+ * driver methods
+ * ------------------------------------------------------------------------ */
+
+/* MCA info */
+
+static int ibmlana_getinfo(char *buf, int slot, void *d)
+{
+ int len = 0, i;
+ struct IBMLANA_NETDEV *dev = (struct IBMLANA_NETDEV *) d;
+ ibmlana_priv *priv;
+
+ /* can't say anything about an uninitialized device... */
+
+ if (dev == NULL)
+ return len;
+ if (dev->priv == NULL)
+ return len;
+ priv = (ibmlana_priv *) dev->priv;
+
+ /* print info */
+
+ len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
+ len += sprintf(buf + len, "I/O: %#lx\n", dev->base_addr);
+ len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start,
+ dev->mem_end - 1);
+ len +=
+ sprintf(buf + len, "Transceiver: %s\n",
+ MediaNames[priv->medium]);
+ len += sprintf(buf + len, "Device: %s\n", dev->name);
+ len += sprintf(buf + len, "MAC address:");
+ for (i = 0; i < 6; i++)
+ len += sprintf(buf + len, " %02x", dev->dev_addr[i]);
+ buf[len++] = '\n';
+ buf[len] = 0;
+
+ return len;
+}
+
+/* open driver. Means also initialization and start of LANCE */
+
+static int ibmlana_open(struct IBMLANA_NETDEV *dev)
+{
+ int result;
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+
+ /* register resources - only necessary for IRQ */
+
+ result =
+ request_irq(priv->realirq, irq_handler,
+ SA_SHIRQ | SA_SAMPLE_RANDOM, "ibm_lana", dev);
+ if (result != 0) {
+ printk("%s: failed to register irq %d\n", dev->name,
+ dev->irq);
+ return result;
+ }
+ dev->irq = priv->realirq;
+
+ /* set up the card and SONIC */
+
+ InitBoard(dev);
+
+ /* initialize operational flags */
+
+#if (LINUX_VERSION_CODE >= 0x02032a)
+ netif_start_queue(dev);
+#else
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+#endif
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/* close driver. Shut down board and free allocated resources */
+
+static int ibmlana_close(struct IBMLANA_NETDEV *dev)
+{
+ /* turn off board */
+
+ /* release resources */
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/* transmit a block. */
+
+static int ibmlana_tx(struct sk_buff *skb, struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+ int retval = 0, tmplen, addr;
+ unsigned long flags;
+ tda_t tda;
+ int baddr;
+
+ /* if we get called with a NULL descriptor, the Ethernet layer thinks
+ our card is stuck an we should reset it. We'll do this completely: */
+
+ if (skb == NULL) {
+ printk("%s: Resetting SONIC\n", dev->name);
+ StopSONIC(dev);
+ InitBoard(dev);
+ return 0; /* don't try to free the block here ;-) */
+ }
+
+ /* find out if there are free slots for a frame to transmit. If not,
+ the upper layer is in deep desperation and we simply ignore the frame. */
+
+ if (priv->txusedcnt >= TXBUFCNT) {
+ retval = -EIO;
+ priv->stat.tx_dropped++;
+ goto tx_done;
+ }
+
+ /* copy the frame data into the next free transmit buffer - fillup missing */
+
+ tmplen = skb->len;
+ if (tmplen < 60)
+ tmplen = 60;
+ baddr = priv->txbufstart + (priv->nexttxdescr * PKTSIZE);
+ IBMLANA_TOIO(dev->mem_start + baddr, skb->data, skb->len);
+
+ /* copy filler into RAM - in case we're filling up...
+ we're filling a bit more than necessary, but that doesn't harm
+ since the buffer is far larger...
+ Sorry Linus for the filler string but I couldn't resist ;-) */
+
+ if (tmplen > skb->len) {
+ char *fill = "NetBSD is a nice OS too! ";
+ unsigned int destoffs = skb->len, l = strlen(fill);
+
+ while (destoffs < tmplen) {
+ IBMLANA_TOIO(dev->mem_start + baddr + destoffs,
+ fill, l);
+ destoffs += l;
+ }
+ }
+
+ /* set up the new frame descriptor */
+
+ addr = priv->tdastart + (priv->nexttxdescr * sizeof(tda_t));
+ IBMLANA_FROMIO(&tda, dev->mem_start + addr, sizeof(tda_t));
+ tda.length = tda.fraglength = tmplen;
+ IBMLANA_TOIO(dev->mem_start + addr, &tda, sizeof(tda_t));
+
+ /* if there were no active descriptors, trigger the SONIC */
+
+ save_flags(flags);
+ cli();
+
+ priv->txusedcnt++;
+ priv->txused[priv->nexttxdescr] = 1;
+
+ /* are all transmission slots used up ? */
+
+ if (priv->txusedcnt >= TXBUFCNT)
+#if (LINUX_VERSION_CODE >= 0x02032a)
+ netif_stop_queue(dev);
+#else
+ dev->tbusy = 1;
+#endif
+
+ if (priv->txusedcnt == 1)
+ StartTx(dev, priv->nexttxdescr);
+ priv->nexttxdescr = (priv->nexttxdescr + 1) % TXBUFCNT;
+
+ restore_flags(flags);
+
+ tx_done:
+
+ /* When did that change exactly ? */
+
+#if (LINUX_VERSION_CODE >= 0x20200)
+ dev_kfree_skb(skb);
+#else
+ dev_kfree_skb(skb, FREE_WRITE);
+#endif
+ return retval;
+}
+
+/* return pointer to Ethernet statistics */
+
+static struct enet_statistics *ibmlana_stats(struct IBMLANA_NETDEV *dev)
+{
+ ibmlana_priv *priv = (ibmlana_priv *) dev->priv;
+
+ return &(priv->stat);
+}
+
+/* we don't support runtime reconfiguration, since am MCA card can
+ be unambigously identified by its POS registers. */
+
+static int ibmlana_config(struct IBMLANA_NETDEV *dev, struct ifmap *map)
+{
+ return 0;
+}
+
+/* switch receiver mode. */
+
+static void ibmlana_set_multicast_list(struct IBMLANA_NETDEV *dev)
+{
+ /* first stop the SONIC... */
+
+ StopSONIC(dev);
+
+ /* ...then reinit it with the new flags */
+
+ InitBoard(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * hardware check
+ * ------------------------------------------------------------------------ */
+
+static int startslot; /* counts through slots when probing multiple devices */
+
+int ibmlana_probe(struct IBMLANA_NETDEV *dev)
+{
+ int force_detect = 0;
+ int slot, z;
+ int base = 0, irq = 0, iobase = 0, memlen = 0;
+ ibmlana_priv *priv;
+ ibmlana_medium medium;
+
+ /* can't work without an MCA bus ;-) */
+
+ if (MCA_bus == 0)
+ return ENODEV;
+
+ /* start address of 1 --> forced detection */
+
+ if (dev->mem_start == 1)
+ force_detect = 1;
+
+ /* search through slots */
+
+ if (dev != NULL) {
+ base = dev->mem_start;
+ irq = dev->irq;
+ }
+ slot = mca_find_adapter(IBM_LANA_ID, startslot);
+
+ while (slot != -1) {
+ /* deduce card addresses */
+
+ getaddrs(slot, &base, &memlen, &iobase, &irq, &medium);
+
+#if (LINUX_VERSION_CODE >= 0x20300)
+ /* slot already in use ? */
+
+ if (mca_is_adapter_used(slot)) {
+ slot = mca_find_adapter(IBM_LANA_ID, slot + 1);
+ continue;
+ }
+#endif
+
+ /* were we looking for something different ? */
+
+ if ((dev->irq != 0) || (dev->mem_start != 0)) {
+ if ((dev->irq != 0) && (dev->irq != irq)) {
+ slot =
+ mca_find_adapter(IBM_LANA_ID,
+ slot + 1);
+ continue;
+ }
+ if ((dev->mem_start != 0)
+ && (dev->mem_start != base)) {
+ slot =
+ mca_find_adapter(IBM_LANA_ID,
+ slot + 1);
+ continue;
+ }
+ }
+
+ /* found something that matches */
+
+ break;
+ }
+
+ /* nothing found ? */
+
+ if (slot == -1)
+ return ((base != 0) || (irq != 0)) ? ENXIO : ENODEV;
+
+ /* announce success */
+ printk("%s: IBM LAN Adapter/A found in slot %d\n", dev->name,
+ slot + 1);
+
+ /* try to obtain I/O range */
+ if (check_region(iobase, IBM_LANA_IORANGE) < 0) {
+ printk("cannot allocate I/O range at %#x!\n", iobase);
+ startslot = slot + 1;
+ return 0;
+ }
+ request_region(iobase, IBM_LANA_IORANGE, "ibm_lana");
+
+ /* make procfs entries */
+
+ mca_set_adapter_name(slot, "IBM LAN Adapter/A");
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) ibmlana_getinfo, dev);
+
+#if (LINUX_VERSION_CODE >= 0x20200)
+ mca_mark_as_used(slot);
+#endif
+
+ /* allocate structure */
+
+ priv = dev->priv =
+ (ibmlana_priv *) kmalloc(sizeof(ibmlana_priv), GFP_KERNEL);
+ priv->slot = slot;
+ priv->realirq = irq;
+ priv->medium = medium;
+ memset(&(priv->stat), 0, sizeof(struct enet_statistics));
+
+ /* set base + irq for this device (irq not allocated so far) */
+
+ dev->irq = 0;
+ dev->mem_start = base;
+ dev->mem_end = base + memlen;
+ dev->base_addr = iobase;
+
+ /* set methods */
+
+ dev->open = ibmlana_open;
+ dev->stop = ibmlana_close;
+ dev->set_config = ibmlana_config;
+ dev->hard_start_xmit = ibmlana_tx;
+ dev->do_ioctl = NULL;
+ dev->get_stats = ibmlana_stats;
+ dev->set_multicast_list = ibmlana_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+
+ /* generic setup */
+
+ ether_setup(dev);
+
+ /* copy out MAC address */
+
+ for (z = 0; z < sizeof(dev->dev_addr); z++)
+ dev->dev_addr[z] = inb(dev->base_addr + MACADDRPROM + z);
+
+ /* print config */
+
+ printk("%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, "
+ "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n",
+ dev->name, priv->realirq, dev->base_addr,
+ dev->mem_start, dev->mem_end - 1,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+ printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]);
+
+ /* reset board */
+
+ ResetBoard(dev);
+
+ /* next probe will start at next slot */
+
+ startslot = slot + 1;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * modularization support
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+
+#define DEVMAX 5
+
+#if (LINUX_VERSION_CODE >= 0x020363)
+static struct IBMLANA_NETDEV moddevs[DEVMAX] =
+ { {" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe}
+};
+#else
+static char NameSpace[8 * DEVMAX];
+static struct IBMLANA_NETDEV moddevs[DEVMAX] =
+ { {NameSpace + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{NameSpace + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{NameSpace + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{NameSpace + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe},
+{NameSpace + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, ibmlana_probe}
+};
+#endif
+
+int irq = 0;
+int io = 0;
+
+int init_module(void)
+{
+ int z, res;
+
+ startslot = 0;
+ for (z = 0; z < DEVMAX; z++) {
+ strcpy(moddevs[z].name, " ");
+ res = register_netdev(moddevs + z);
+ if (res != 0)
+ return (z > 0) ? 0 : -EIO;
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct IBMLANA_NETDEV *dev;
+ ibmlana_priv *priv;
+ int z;
+
+ if (MOD_IN_USE) {
+ printk("cannot unload, module in use\n");
+ return;
+ }
+
+ for (z = 0; z < DEVMAX; z++) {
+ dev = moddevs + z;
+ if (dev->priv != NULL) {
+ priv = (ibmlana_priv *) dev->priv;
+ /*DeinitBoard(dev); */
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+ release_region(dev->base_addr, IBM_LANA_IORANGE);
+ unregister_netdev(dev);
+#if (LINUX_VERSION_CODE >= 0x20200)
+ mca_mark_as_unused(priv->slot);
+#endif
+ mca_set_adapter_name(priv->slot, "");
+ mca_set_adapter_procfn(priv->slot, NULL, NULL);
+ kfree_s(dev->priv, sizeof(ibmlana_priv));
+ dev->priv = NULL;
+ }
+ }
+}
+#endif /* MODULE */
--- /dev/null
+#ifndef _IBM_LANA_INCLUDE_
+#define _IBM_LANA_INCLUDE_
+
+#ifdef _IBM_LANA_DRIVER_
+
+/* version-dependent functions/structures */
+
+#if LINUX_VERSION_CODE >= 0x020318
+#define IBMLANA_READB(addr) isa_readb(addr)
+#define IBMLANA_TOIO(dest, src, len) isa_memcpy_toio(dest, src, len)
+#define IBMLANA_FROMIO(dest, src, len) isa_memcpy_fromio(dest, src, len)
+#define IBMLANA_SETIO(dest, val, len) isa_memset_io(dest, val, len)
+#define IBMLANA_NETDEV net_device
+#else
+#define IBMLANA_READB(addr) readb(addr)
+#define IBMLANA_TOIO(dest, src, len) memcpy_toio(dest, src, len)
+#define IBMLANA_FROMIO(dest, src, len) memcpy_fromio(dest, src, len)
+#define IBMLANA_SETIO(dest, val, len) memset_io(dest, val, len)
+#define IBMLANA_NETDEV device
+#endif
+
+/* maximum packet size */
+
+#define PKTSIZE 1524
+
+/* number of transmit buffers */
+
+#define TXBUFCNT 4
+
+/* Adapter ID's */
+#define IBM_LANA_ID 0xffe0
+
+/* media enumeration - defined in a way that it fits onto the LAN/A's
+ POS registers... */
+
+typedef enum { Media_10BaseT, Media_10Base5,
+ Media_Unknown, Media_10Base2, Media_Count
+} ibmlana_medium;
+
+/* private structure */
+
+typedef struct {
+ unsigned int slot; /* MCA-Slot-# */
+ struct enet_statistics stat; /* packet statistics */
+ int realirq; /* memorizes actual IRQ, even when
+ currently not allocated */
+ ibmlana_medium medium; /* physical cannector */
+ u32 tdastart, txbufstart, /* addresses */
+ rrastart, rxbufstart, rdastart, rxbufcnt, txusedcnt;
+ int nextrxdescr, /* next rx descriptor to be used */
+ lastrxdescr, /* last free rx descriptor */
+ nexttxdescr, /* last tx descriptor to be used */
+ currtxdescr, /* tx descriptor currently tx'ed */
+ txused[TXBUFCNT]; /* busy flags */
+} ibmlana_priv;
+
+/* this card uses quite a lot of I/O ports...luckily the MCA bus decodes
+ a full 64K I/O range... */
+
+#define IBM_LANA_IORANGE 0xa0
+
+/* Command Register: */
+
+#define SONIC_CMDREG 0x00
+#define CMDREG_HTX 0x0001 /* halt transmission */
+#define CMDREG_TXP 0x0002 /* start transmission */
+#define CMDREG_RXDIS 0x0004 /* disable receiver */
+#define CMDREG_RXEN 0x0008 /* enable receiver */
+#define CMDREG_STP 0x0010 /* stop timer */
+#define CMDREG_ST 0x0020 /* start timer */
+#define CMDREG_RST 0x0080 /* software reset */
+#define CMDREG_RRRA 0x0100 /* force SONIC to read first RRA */
+#define CMDREG_LCAM 0x0200 /* force SONIC to read CAM descrs */
+
+/* Data Configuration Register */
+
+#define SONIC_DCREG 0x02
+#define DCREG_EXBUS 0x8000 /* Extended Bus Mode */
+#define DCREG_LBR 0x2000 /* Latched Bus Retry */
+#define DCREG_PO1 0x1000 /* Programmable Outputs */
+#define DCREG_PO0 0x0800
+#define DCREG_SBUS 0x0400 /* Synchronous Bus Mode */
+#define DCREG_USR1 0x0200 /* User Definable Pins */
+#define DCREG_USR0 0x0100
+#define DCREG_WC0 0x0000 /* 0..3 Wait States */
+#define DCREG_WC1 0x0040
+#define DCREG_WC2 0x0080
+#define DCREG_WC3 0x00c0
+#define DCREG_DW16 0x0000 /* 16 bit Bus Mode */
+#define DCREG_DW32 0x0020 /* 32 bit Bus Mode */
+#define DCREG_BMS 0x0010 /* Block Mode Select */
+#define DCREG_RFT4 0x0000 /* 4/8/16/24 bytes RX Threshold */
+#define DCREG_RFT8 0x0004
+#define DCREG_RFT16 0x0008
+#define DCREG_RFT24 0x000c
+#define DCREG_TFT8 0x0000 /* 8/16/24/28 bytes TX Threshold */
+#define DCREG_TFT16 0x0001
+#define DCREG_TFT24 0x0002
+#define DCREG_TFT28 0x0003
+
+/* Receive Control Register */
+
+#define SONIC_RCREG 0x04
+#define RCREG_ERR 0x8000 /* accept damaged and collided pkts */
+#define RCREG_RNT 0x4000 /* accept packets that are < 64 */
+#define RCREG_BRD 0x2000 /* accept broadcasts */
+#define RCREG_PRO 0x1000 /* promiscous mode */
+#define RCREG_AMC 0x0800 /* accept all multicasts */
+#define RCREG_LB_NONE 0x0000 /* no loopback */
+#define RCREG_LB_MAC 0x0200 /* MAC loopback */
+#define RCREG_LB_ENDEC 0x0400 /* ENDEC loopback */
+#define RCREG_LB_XVR 0x0600 /* Transceiver loopback */
+#define RCREG_MC 0x0100 /* Multicast received */
+#define RCREG_BC 0x0080 /* Broadcast received */
+#define RCREG_LPKT 0x0040 /* last packet in RBA */
+#define RCREG_CRS 0x0020 /* carrier sense present */
+#define RCREG_COL 0x0010 /* recv'd packet with collision */
+#define RCREG_CRCR 0x0008 /* recv'd packet with CRC error */
+#define RCREG_FAER 0x0004 /* recv'd packet with inv. framing */
+#define RCREG_LBK 0x0002 /* recv'd loopback packet */
+#define RCREG_PRX 0x0001 /* recv'd packet is OK */
+
+/* Transmit Control Register */
+
+#define SONIC_TCREG 0x06
+#define TCREG_PINT 0x8000 /* generate interrupt after TDA read */
+#define TCREG_POWC 0x4000 /* timer start out of window detect */
+#define TCREG_CRCI 0x2000 /* inhibit CRC generation */
+#define TCREG_EXDIS 0x1000 /* disable excessive deferral timer */
+#define TCREG_EXD 0x0400 /* excessive deferral occured */
+#define TCREG_DEF 0x0200 /* single deferral occured */
+#define TCREG_NCRS 0x0100 /* no carrier detected */
+#define TCREG_CRSL 0x0080 /* carrier lost */
+#define TCREG_EXC 0x0040 /* excessive collisions occured */
+#define TCREG_OWC 0x0020 /* out of window collision occured */
+#define TCREG_PMB 0x0008 /* packet monitored bad */
+#define TCREG_FU 0x0004 /* FIFO underrun */
+#define TCREG_BCM 0x0002 /* byte count mismatch of fragments */
+#define TCREG_PTX 0x0001 /* packet transmitted OK */
+
+/* Interrupt Mask Register */
+
+#define SONIC_IMREG 0x08
+#define IMREG_BREN 0x4000 /* interrupt when bus retry occured */
+#define IMREG_HBLEN 0x2000 /* interrupt when heartbeat lost */
+#define IMREG_LCDEN 0x1000 /* interrupt when CAM loaded */
+#define IMREG_PINTEN 0x0800 /* interrupt when PINT in TDA set */
+#define IMREG_PRXEN 0x0400 /* interrupt when packet received */
+#define IMREG_PTXEN 0x0200 /* interrupt when packet was sent */
+#define IMREG_TXEREN 0x0100 /* interrupt when send failed */
+#define IMREG_TCEN 0x0080 /* interrupt when timer completed */
+#define IMREG_RDEEN 0x0040 /* interrupt when RDA exhausted */
+#define IMREG_RBEEN 0x0020 /* interrupt when RBA exhausted */
+#define IMREG_RBAEEN 0x0010 /* interrupt when RBA too short */
+#define IMREG_CRCEN 0x0008 /* interrupt when CRC counter rolls */
+#define IMREG_FAEEN 0x0004 /* interrupt when FAE counter rolls */
+#define IMREG_MPEN 0x0002 /* interrupt when MP counter rolls */
+#define IMREG_RFOEN 0x0001 /* interrupt when Rx FIFO overflows */
+
+/* Interrupt Status Register */
+
+#define SONIC_ISREG 0x0a
+#define ISREG_BR 0x4000 /* bus retry occured */
+#define ISREG_HBL 0x2000 /* heartbeat lost */
+#define ISREG_LCD 0x1000 /* CAM loaded */
+#define ISREG_PINT 0x0800 /* PINT in TDA set */
+#define ISREG_PKTRX 0x0400 /* packet received */
+#define ISREG_TXDN 0x0200 /* packet was sent */
+#define ISREG_TXER 0x0100 /* send failed */
+#define ISREG_TC 0x0080 /* timer completed */
+#define ISREG_RDE 0x0040 /* RDA exhausted */
+#define ISREG_RBE 0x0020 /* RBA exhausted */
+#define ISREG_RBAE 0x0010 /* RBA too short for received frame */
+#define ISREG_CRC 0x0008 /* CRC counter rolls over */
+#define ISREG_FAE 0x0004 /* FAE counter rolls over */
+#define ISREG_MP 0x0002 /* MP counter rolls over */
+#define ISREG_RFO 0x0001 /* Rx FIFO overflows */
+
+#define SONIC_UTDA 0x0c /* current transmit descr address */
+#define SONIC_CTDA 0x0e
+
+#define SONIC_URDA 0x1a /* current receive descr address */
+#define SONIC_CRDA 0x1c
+
+#define SONIC_CRBA0 0x1e /* current receive buffer address */
+#define SONIC_CRBA1 0x20
+
+#define SONIC_RBWC0 0x22 /* word count in receive buffer */
+#define SONIC_RBWC1 0x24
+
+#define SONIC_EOBC 0x26 /* minimum space to be free in RBA */
+
+#define SONIC_URRA 0x28 /* upper address of CDA & Recv Area */
+
+#define SONIC_RSA 0x2a /* start of receive resource area */
+
+#define SONIC_REA 0x2c /* end of receive resource area */
+
+#define SONIC_RRP 0x2e /* resource read pointer */
+
+#define SONIC_RWP 0x30 /* resource write pointer */
+
+#define SONIC_CAMEPTR 0x42 /* CAM entry pointer */
+
+#define SONIC_CAMADDR2 0x44 /* CAM address ports */
+#define SONIC_CAMADDR1 0x46
+#define SONIC_CAMADDR0 0x48
+
+#define SONIC_CAMPTR 0x4c /* lower address of CDA */
+
+#define SONIC_CAMCNT 0x4e /* # of CAM descriptors to load */
+
+/* Data Configuration Register 2 */
+
+#define SONIC_DCREG2 0x7e
+#define DCREG2_EXPO3 0x8000 /* extended programmable outputs */
+#define DCREG2_EXPO2 0x4000
+#define DCREG2_EXPO1 0x2000
+#define DCREG2_EXPO0 0x1000
+#define DCREG2_HD 0x0800 /* heartbeat disable */
+#define DCREG2_JD 0x0200 /* jabber timer disable */
+#define DCREG2_AUTO 0x0100 /* enable AUI/TP auto selection */
+#define DCREG2_XWRAP 0x0040 /* TP transceiver loopback */
+#define DCREG2_PH 0x0010 /* HOLD request timing */
+#define DCREG2_PCM 0x0004 /* packet compress when matched */
+#define DCREG2_PCNM 0x0002 /* packet compress when not matched */
+#define DCREG2_RJCM 0x0001 /* inverse packet match via CAM */
+
+/* Board Control Register: Enable RAM, Interrupts... */
+
+#define BCMREG 0x80
+#define BCMREG_RAMEN 0x80 /* switch over to RAM */
+#define BCMREG_IPEND 0x40 /* interrupt pending ? */
+#define BCMREG_RESET 0x08 /* reset board */
+#define BCMREG_16BIT 0x04 /* adapter in 16-bit slot */
+#define BCMREG_RAMWIN 0x02 /* enable RAM window */
+#define BCMREG_IEN 0x01 /* interrupt enable */
+
+/* MAC Address PROM */
+
+#define MACADDRPROM 0x92
+
+/* structure of a CAM entry */
+
+typedef struct {
+ u32 index; /* pointer into CAM area */
+ u32 addr0; /* address part (bits 0..15 used) */
+ u32 addr1;
+ u32 addr2;
+} camentry_t;
+
+/* structure of a receive resource */
+
+typedef struct {
+ u32 startlo; /* start address (bits 0..15 used) */
+ u32 starthi;
+ u32 cntlo; /* size in 16-bit quantities */
+ u32 cnthi;
+} rra_t;
+
+/* structure of a receive descriptor */
+
+typedef struct {
+ u32 status; /* packet status */
+ u32 length; /* length in bytes */
+ u32 startlo; /* start address */
+ u32 starthi;
+ u32 seqno; /* frame sequence */
+ u32 link; /* pointer to next descriptor */
+ /* bit 0 = EOL */
+ u32 inuse; /* !=0 --> free for SONIC to write */
+} rda_t;
+
+/* structure of a transmit descriptor */
+
+typedef struct {
+ u32 status; /* transmit status */
+ u32 config; /* value for TCR */
+ u32 length; /* total length */
+ u32 fragcount; /* number of fragments */
+ u32 startlo; /* start address of fragment */
+ u32 starthi;
+ u32 fraglength; /* length of this fragment */
+ /* more address/length triplets may */
+ /* follow here */
+ u32 link; /* pointer to next descriptor */
+ /* bit 0 = EOL */
+} tda_t;
+
+#endif /* _IBM_LANA_DRIVER_ */
+
+extern int ibmlana_probe(struct IBMLANA_NETDEV *);
+
+
+#endif /* _IBM_LANA_INCLUDE_ */
-/* $Id$
- *
+/*
* 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.
*
* To do:
*
- * - ioc3_close() should attempt to shutdown the adapter somewhat more
- * gracefully.
- * - Free rings and buffers when closing or before re-initializing rings.
* - Handle allocation failures in ioc3_alloc_skb() more gracefully.
* - Handle allocation failures in ioc3_init_rings().
- * - Maybe implement private_ioctl().
* - Use prefetching for large packets. What is a good lower limit for
* prefetching?
* - We're probably allocating a bit too much memory.
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci_ids.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
/* 32 RX buffers. This is tunable in the range of 16 <= x < 512. */
#define RX_BUFFS 32
-static void ioc3_set_multicast_list(struct net_device *dev);
-static int ioc3_open(struct net_device *dev);
-static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void ioc3_timeout(struct net_device *dev);
-static int ioc3_close(struct net_device *dev);
-static inline unsigned int ioc3_hash(const unsigned char *addr);
+/* Private ioctls that de facto are well known and used for examply
+ by mii-tool. */
+#define SIOCGMIIPHY (SIOCDEVPRIVATE) /* Read from current PHY */
+#define SIOCGMIIREG (SIOCDEVPRIVATE+1) /* Read any PHY register */
+#define SIOCSMIIREG (SIOCDEVPRIVATE+2) /* Write any PHY register */
-static const char ioc3_str[] = "IOC3 Ethernet";
+/* These exist in other drivers; we don't use them at this time. */
+#define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters */
+#define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters */
/* Private per NIC data of the driver. */
struct ioc3_private {
struct ioc3 *regs;
int phy;
- unsigned long rxr; /* pointer to receiver ring */
+ unsigned long *rxr; /* pointer to receiver ring */
struct ioc3_etxd *txr;
struct sk_buff *rx_skbs[512];
struct sk_buff *tx_skbs[128];
int rx_pi; /* RX producer index */
int tx_ci; /* TX consumer index */
int tx_pi; /* TX producer index */
+ int txqlen;
+ u32 emcr, ehar_h, ehar_l;
spinlock_t ioc3_lock;
};
+static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void ioc3_set_multicast_list(struct net_device *dev);
+static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void ioc3_timeout(struct net_device *dev);
+static inline unsigned int ioc3_hash(const unsigned char *addr);
+static void ioc3_stop(struct net_device *dev);
+static void ioc3_clean_tx_ring(struct ioc3_private *ip);
+static void ioc3_clean_rx_ring(struct ioc3_private *ip);
+static void ioc3_init(struct net_device *dev);
+
+static const char ioc3_str[] = "IOC3 Ethernet";
+
/* We use this to acquire receive skb's that we can DMA directly into. */
#define ALIGNED_RX_SKB_ADDR(addr) \
((((unsigned long)(addr) + (128 - 1)) & ~(128 - 1)) - (unsigned long)(addr))
#define BARRIER() \
__asm__("sync" ::: "memory")
-/* This El Cheapo implementatin of TX_BUFFS_AVAIL may leave on entry unused.
- Since the TX ring has 128 entries which is fairly large we don't care and
- use this, more efficient implementation. */
-#define TX_BUFFS_AVAIL(ip) \
-({ \
- struct ioc3_private *_ip = (ip); \
- ((512 + _ip->tx_ci + 1) - _ip->tx_pi) & 511; \
-})
-//#undef TX_BUFFS_AVAIL
-//#define TX_BUFFS_AVAIL(ip) (1)
-
#define IOC3_SIZE 0x100000
return presence;
}
+static inline int
+nic_read_bit(struct ioc3 *ioc3)
+{
+ int result;
+
+ ioc3_w(mcr, mcr_pack(6, 13));
+ result = nic_wait(ioc3);
+ ioc3_w(mcr, mcr_pack(0, 100));
+ nic_wait(ioc3);
+
+ return result;
+}
+
+static inline void
+nic_write_bit(struct ioc3 *ioc3, int bit)
+{
+ if (bit)
+ ioc3_w(mcr, mcr_pack(6, 110));
+ else
+ ioc3_w(mcr, mcr_pack(80, 30));
+
+ nic_wait(ioc3);
+}
+
/*
* Read a byte from an iButton device
*/
u32 result = 0;
int i;
- for (i = 0; i < 8; i++) {
- ioc3_w(mcr, mcr_pack(6, 13));
- result = (result >> 1) | (nic_wait(ioc3) << 7);
-
- ioc3_w(mcr, mcr_pack(0, 100));
- nic_wait(ioc3);
- }
+ for (i = 0; i < 8; i++)
+ result = (result >> 1) | (nic_read_bit(ioc3) << 7);
return result;
}
bit = byte & 1;
byte >>= 1;
- if (bit)
- ioc3_w(mcr, mcr_pack(6, 110));
- else
- ioc3_w(mcr, mcr_pack(80, 30));
- nic_wait(ioc3);
+ nic_write_bit(ioc3, bit);
}
}
-static void nic_show_regnr(struct ioc3 *ioc3)
+static u64
+nic_find(struct ioc3 *ioc3, int *last)
+{
+ int a, b, index, disc;
+ u64 address = 0;
+
+ nic_reset(ioc3);
+ /* Search ROM. */
+ nic_write_byte(ioc3, 0xf0);
+
+ /* Algorithm from ``Book of iButton Standards''. */
+ for (index = 0, disc = 0; index < 64; index++) {
+ a = nic_read_bit(ioc3);
+ b = nic_read_bit(ioc3);
+
+ if (a && b) {
+ printk("NIC search failed (not fatal).\n");
+ *last = 0;
+ return 0;
+ }
+
+ if (!a && !b) {
+ if (index == *last) {
+ address |= 1UL << index;
+ } else if (index > *last) {
+ address &= ~(1UL << index);
+ disc = index;
+ } else if ((address & (1UL << index)) == 0)
+ disc = index;
+ nic_write_bit(ioc3, address & (1UL << index));
+ continue;
+ } else {
+ if (a)
+ address |= 1UL << index;
+ else
+ address &= ~(1UL << index);
+ nic_write_bit(ioc3, a);
+ continue;
+ }
+ }
+
+ *last = disc;
+
+ return address;
+}
+
+static int nic_init(struct ioc3 *ioc3)
{
const char *type;
- u8 regnr[8];
- int i;
+ u8 crc;
+ u8 serial[6];
+ int save = 0, i;
+
+ type = "unknown";
+
+ while (1) {
+ u64 reg;
+ reg = nic_find(ioc3, &save);
+
+ switch (reg & 0xff) {
+ case 0x91:
+ type = "DS1981U";
+ break;
+ default:
+ if (save == 0) {
+ /* Let the caller try again. */
+ return -1;
+ }
+ continue;
+ }
- nic_write_byte(ioc3, 0x33);
- for (i = 0; i < 8; i++)
- regnr[i] = nic_read_byte(ioc3);
+ nic_reset(ioc3);
- switch(regnr[0]) {
- case 0x01: type = "DS1990A"; break;
- case 0x91: type = "DS1981U"; break;
- default: type = "unknown"; break;
+ /* Match ROM. */
+ nic_write_byte(ioc3, 0x55);
+ for (i = 0; i < 8; i++)
+ nic_write_byte(ioc3, (reg >> (i << 3)) & 0xff);
+
+ reg >>= 8; /* Shift out type. */
+ for (i = 0; i < 6; i++) {
+ serial[i] = reg & 0xff;
+ reg >>= 8;
+ }
+ crc = reg & 0xff;
+ break;
}
- printk("Found %s NIC, registration number "
- "%02x:%02x:%02x:%02x:%02x:%02x, CRC %02x.\n", type,
- regnr[1], regnr[2], regnr[3], regnr[4], regnr[5], regnr[6],
- regnr[7]);
+ printk("Found %s NIC", type);
+ if (type != "unknown") {
+ printk (" registration number %02x:%02x:%02x:%02x:%02x:%02x,"
+ " CRC %02x", serial[0], serial[1], serial[2],
+ serial[3], serial[4], serial[5], crc);
+ }
+ printk(".\n");
+
+ return 0;
}
/*
{
u8 nic[14];
int i;
+ int tries = 2; /* There may be some problem with the battery? */
ioc3_w(gpcr_s, (1 << 21));
- nic_reset(ioc3);
- nic_show_regnr(ioc3);
+ while (tries--) {
+ if (!nic_init(ioc3))
+ break;
+ udelay(500);
+ }
- nic_reset(ioc3);
- nic_write_byte(ioc3, 0xcc);
+ if (tries < 0) {
+ printk("Failed to read MAC address\n");
+ return;
+ }
+
+ /* Read Memory. */
nic_write_byte(ioc3, 0xf0);
nic_write_byte(ioc3, 0x00);
nic_write_byte(ioc3, 0x00);
printk(".\n");
}
+/* Caller must hold the ioc3_lock ever for MII readers. This is also
+ used to protect the transmitter side but it's low contention. */
static u16 mii_read(struct ioc3 *ioc3, int phy, int reg)
{
while (ioc3->micr & MICR_BUSY);
w0 = rxb->w0;
while (w0 & ERXBUF_V) {
- ioc3->eisr = EISR_RXTIMERINT; /* Ack */
- ioc3->eisr; /* Flush */
-
err = rxb->err; /* It's valid ... */
if (err & ERXBUF_GOODPKT) {
len = (w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff;
ip->stats.rx_packets++; /* Statistics */
ip->stats.rx_bytes += len;
- goto next;
- }
- if (err & (ERXBUF_CRCERR | ERXBUF_FRAMERR | ERXBUF_CODERR |
- ERXBUF_INVPREAMB | ERXBUF_BADPKT | ERXBUF_CARRIER)) {
- /* We don't send the skbuf to the network layer, so
- just recycle it. */
- new_skb = skb;
-
- if (err & ERXBUF_CRCERR) /* Statistics */
- ip->stats.rx_crc_errors++;
- if (err & ERXBUF_FRAMERR)
- ip->stats.rx_frame_errors++;
- ip->stats.rx_errors++;
+ } else {
+ /* The frame is invalid and the skb never
+ reached the network layer so we can just
+ recycle it. */
+ new_skb = skb;
+ ip->stats.rx_errors++;
}
-
+ if (err & ERXBUF_CRCERR) /* Statistics */
+ ip->stats.rx_crc_errors++;
+ if (err & ERXBUF_FRAMERR)
+ ip->stats.rx_frame_errors++;
next:
ip->rx_skbs[n_entry] = new_skb;
rxr[n_entry] = (0xa5UL << 56) |
}
ip->rx_pi = n_entry;
ip->rx_ci = rx_entry;
-
- return;
}
static inline void
-ioc3_tx(struct ioc3_private *ip, struct ioc3 *ioc3)
+ioc3_tx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
{
+ unsigned long packets, bytes;
int tx_entry, o_entry;
struct sk_buff *skb;
u32 etcir;
spin_lock(&ip->ioc3_lock);
etcir = ioc3->etcir;
+
tx_entry = (etcir >> 7) & 127;
o_entry = ip->tx_ci;
+ packets = 0;
+ bytes = 0;
while (o_entry != tx_entry) {
- ioc3->eisr = EISR_TXEXPLICIT; /* Ack */
- ioc3->eisr; /* Flush */
-
+ packets++;
skb = ip->tx_skbs[o_entry];
- ip->stats.tx_packets++;
- ip->stats.tx_bytes += skb->len;
+ bytes += skb->len;
dev_kfree_skb_irq(skb);
ip->tx_skbs[o_entry] = NULL;
etcir = ioc3->etcir; /* More pkts sent? */
tx_entry = (etcir >> 7) & 127;
}
+
+ ip->stats.tx_packets += packets;
+ ip->stats.tx_bytes += bytes;
+ ip->txqlen -= packets;
+
+ if (ip->txqlen < 128)
+ netif_wake_queue(dev);
+
ip->tx_ci = o_entry;
spin_unlock(&ip->ioc3_lock);
}
/*
- * Deal with fatal IOC3 errors. For now let's panic. This condition might
- * be caused by a hard or software problems, so we should try to recover
- * more gracefully if this ever happens.
+ * Deal with fatal IOC3 errors. This condition might be caused by a hard or
+ * software problems, so we should try to recover
+ * more gracefully if this ever happens. In theory we might be flooded
+ * with such error interrupts if something really goes wrong, so we might
+ * also consider to take the interface down.
*/
static void
ioc3_error(struct net_device *dev, struct ioc3_private *ip,
{
if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
if (eisr & EISR_RXMEMERR) {
- panic("%s: RX PCI error.\n", dev->name);
+ printk(KERN_ERR "%s: RX PCI error.\n", dev->name);
}
if (eisr & EISR_TXMEMERR) {
- panic("%s: TX PCI error.\n", dev->name);
+ printk(KERN_ERR "%s: TX PCI error.\n", dev->name);
}
}
+
+ ioc3_stop(dev);
+ ioc3_clean_tx_ring(dev->priv);
+ ioc3_init(dev);
+
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
}
/* The interrupt handler does all of the Rx thread work and cleans up
struct net_device *dev = (struct net_device *)_dev;
struct ioc3_private *ip = dev->priv;
struct ioc3 *ioc3 = ip->regs;
- u32 eisr, eier;
+ const u32 enabled = EISR_RXTIMERINT | EISR_TXEXPLICIT |
+ EISR_RXMEMERR | EISR_TXMEMERR;
+ u32 eisr;
- ip = dev->priv;
-
- eier = ioc3->eier; /* Disable eth ints */
- ioc3->eier = 0;
- eisr = ioc3->eisr;
- __sti();
-
- if (eisr & EISR_RXTIMERINT) {
- ioc3_rx(dev, ip, ioc3);
- }
- if (eisr & EISR_TXEXPLICIT) {
- ioc3_tx(ip, ioc3);
- }
- if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
- ioc3_error(dev, ip, ioc3, eisr);
- }
+ eisr = ioc3->eisr & enabled;
+ while (eisr) {
+ ioc3->eisr = eisr;
+ ioc3->eisr; /* Flush */
- if ((TX_BUFFS_AVAIL(ip) >= 0) && netif_queue_stopped(dev)) {
- netif_wake_queue(dev);
+ if (eisr & EISR_RXTIMERINT)
+ ioc3_rx(dev, ip, ioc3);
+ if (eisr & EISR_TXEXPLICIT)
+ ioc3_tx(dev, ip, ioc3);
+ if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR))
+ ioc3_error(dev, ip, ioc3, eisr);
+ eisr = ioc3->eisr & enabled;
}
-
- __cli();
- ioc3->eier = eier;
-
- return;
}
-int
-ioc3_eth_init(struct net_device *dev, struct ioc3_private *p, struct ioc3 *ioc3)
+/* One day this will do the autonegotiation. */
+int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
{
u16 word, mii0, mii_status, mii2, mii3, mii4;
u32 vendor, model, rev;
int i, phy;
- ioc3->emcr = EMCR_RST; /* Reset */
- ioc3->emcr; /* flush WB */
- udelay(4); /* Give it time ... */
- ioc3->emcr = 0;
-
+ spin_lock_irq(&ip->ioc3_lock);
phy = -1;
for (i = 0; i < 32; i++) {
word = mii_read(ioc3, i, 2);
}
}
if (phy == -1) {
+ spin_unlock_irq(&ip->ioc3_lock);
printk("Didn't find a PHY, goodbye.\n");
return -ENODEV;
}
- p->phy = phy;
+ ip->phy = phy;
mii0 = mii_read(ioc3, phy, 0);
mii_status = mii_read(ioc3, phy, 1);
/* Autonegotiate 100mbit and fullduplex. */
mii_write(ioc3, phy, 0, mii0 | 0x3100);
- mdelay(1000);
+
+ spin_unlock_irq(&ip->ioc3_lock);
+ mdelay(1000); /* XXX Yikes XXX */
+ spin_lock_irq(&ip->ioc3_lock);
+
mii_status = mii_read(ioc3, phy, 1);
+ spin_unlock_irq(&ip->ioc3_lock);
- return 0; /* XXX */
+ return 0;
}
-/* To do: For reinit of the ring we have to cleanup old skbs first ... */
static void
-ioc3_init_rings(struct net_device *dev, struct ioc3_private *p,
- struct ioc3 *ioc3)
+ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
{
struct ioc3_erxbuf *rxb;
unsigned long *rxr;
- unsigned long ring;
int i;
- /* Allocate and initialize rx ring. 4kb = 512 entries */
- p->rxr = get_free_page(GFP_KERNEL);
- rxr = (unsigned long *) p->rxr;
+ if (ip->rxr == NULL) {
+ /* Allocate and initialize rx ring. 4kb = 512 entries */
+ ip->rxr = (unsigned long *) get_free_page(GFP_KERNEL);
+ rxr = (unsigned long *) ip->rxr;
+
+ /* Now the rx buffers. The RX ring may be larger but
+ we only allocate 16 buffers for now. Need to tune
+ this for performance and memory later. */
+ for (i = 0; i < RX_BUFFS; i++) {
+ struct sk_buff *skb;
+
+ skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
+ if (!skb) {
+ show_free_areas();
+ continue;
+ }
- /* Now the rx buffers. The RX ring may be larger but we only
- allocate 16 buffers for now. Need to tune this for performance
- and memory later. */
- for (i = 0; i < RX_BUFFS; i++) {
- struct sk_buff *skb;
+ ip->rx_skbs[i] = skb;
+ skb->dev = dev;
- skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
- if (!skb) {
- show_free_areas();
- continue;
+ /* Because we reserve afterwards. */
+ skb_put(skb, (1664 + RX_OFFSET));
+ rxb = (struct ioc3_erxbuf *) skb->data;
+ rxr[i] = (0xa5UL << 56)
+ | ((unsigned long) rxb & TO_PHYS_MASK);
+ skb_reserve(skb, RX_OFFSET);
}
+ ip->rx_ci = 0;
+ ip->rx_pi = RX_BUFFS;
+ }
- p->rx_skbs[i] = skb;
- skb->dev = dev;
-
- /* Because we reserve afterwards. */
- skb_put(skb, (1664 + RX_OFFSET));
- rxb = (struct ioc3_erxbuf *) skb->data;
- rxb->w0 = 0; /* Clear valid bit */
- rxr[i] = (0xa5UL << 56) | ((unsigned long) rxb & TO_PHYS_MASK);
- skb_reserve(skb, RX_OFFSET);
+ if (ip->txr == NULL) {
+ /* Allocate and initialize tx rings. 16kb = 128 bufs. */
+ ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
+ ip->tx_pi = 0;
+ ip->tx_ci = 0;
}
+}
+
+static void
+ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
+{
+ unsigned long ring;
+
+ ioc3_alloc_rings(dev, ip, ioc3);
+
+ ioc3_clean_tx_ring(ip);
+ ioc3_clean_rx_ring(ip);
/* Now the rx ring base, consume & produce registers. */
- ring = (0xa5UL << 56) | (p->rxr & TO_PHYS_MASK);
+ ring = (0xa5UL << 56) | ((unsigned long)ip->rxr & TO_PHYS_MASK);
ioc3->erbr_h = ring >> 32;
ioc3->erbr_l = ring & 0xffffffff;
- p->rx_ci = 0;
- ioc3->ercir = (p->rx_ci << 3);
- p->rx_pi = RX_BUFFS;
- ioc3->erpir = (p->rx_pi << 3) | ERPIR_ARM;
+ ioc3->ercir = (ip->rx_ci << 3);
+ ioc3->erpir = (ip->rx_pi << 3) | ERPIR_ARM;
- /* Allocate and initialize tx rings. 16kb = 128 bufs. */
- p->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
- ring = (0xa5UL << 56) | ((unsigned long)p->txr & TO_PHYS_MASK);
+ ring = (0xa5UL << 56) | ((unsigned long)ip->txr & TO_PHYS_MASK);
+
+ ip->txqlen = 0; /* nothing queued */
/* Now the tx ring base, consume & produce registers. */
ioc3->etbr_h = ring >> 32;
ioc3->etbr_l = ring & 0xffffffff;
- p->tx_pi = 0;
- ioc3->etpir = (p->tx_pi << 7);
- p->tx_ci = 0;
- ioc3->etcir = (p->tx_pi << 7);
+ ioc3->etpir = (ip->tx_pi << 7);
+ ioc3->etcir = (ip->tx_ci << 7);
ioc3->etcir; /* Flush */
}
-void
+static void
+ioc3_clean_tx_ring(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i=0; i < 128; i++) {
+ skb = ip->tx_skbs[i];
+ if (skb) {
+ ip->tx_skbs[i] = NULL;
+ dev_kfree_skb_any(skb);
+ }
+ ip->txr[i].cmd = 0;
+ }
+}
+
+static void
+ioc3_clean_rx_ring(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < RX_BUFFS; i++) {
+ struct ioc3_erxbuf *rxb;
+ skb = ip->rx_skbs[i];
+ rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET);
+
+ rxb->w0 = 0;
+ }
+}
+
+static void
+ioc3_free_rings(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int rx_entry, n_entry;
+
+ ioc3_clean_tx_ring(ip);
+ free_pages((unsigned long)ip->txr, 2);
+ ip->txr = NULL;
+
+ n_entry = ip->rx_ci;
+ rx_entry = ip->rx_pi;
+
+ while (n_entry != rx_entry) {
+ skb = ip->rx_skbs[n_entry];
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ n_entry = (n_entry + 1) & 511;
+ }
+ free_page((unsigned long)ip->rxr);
+ ip->rxr = NULL;
+}
+
+static inline void
ioc3_ssram_disc(struct ioc3_private *ip)
{
struct ioc3 *ioc3 = ip->regs;
if ((*ssram0 & IOC3_SSRAM_DM) != pattern ||
(*ssram1 & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) {
/* set ssram size to 64 KB */
+ ip->emcr = EMCR_RAMPAR;
ioc3->emcr &= ~EMCR_BUFSIZ;
- printk("IOC3 SSRAM has 64 kbyte.\n");
} else {
- //ei->ei_ssram_bits = EMCR_BUFSIZ | EMCR_RAMPAR;
- printk("IOC3 SSRAM has 64 kbyte.\n");
+ ip->emcr = EMCR_BUFSIZ | EMCR_RAMPAR;
}
}
-static void ioc3_probe1(struct net_device *dev, struct ioc3 *ioc3)
+static void ioc3_init(struct net_device *dev)
{
- struct ioc3_private *ip;
-
- dev = init_etherdev(dev, 0);
-
- /*
- * This probably needs to be register_netdevice, or call
- * init_etherdev so that it calls register_netdevice. Quick
- * hack for now.
- */
- netif_device_attach(dev);
-
- ip = (struct ioc3_private *) kmalloc(sizeof(*ip), GFP_KERNEL);
- memset(ip, 0, sizeof(*ip));
- dev->priv = ip;
- dev->irq = IOC3_ETH_INT;
-
- ip->regs = ioc3;
-
- ioc3_eth_init(dev, ip, ioc3);
- ioc3_ssram_disc(ip);
- ioc3_get_eaddr(dev, ioc3);
- ioc3_init_rings(dev, ip, ioc3);
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
- spin_lock_init(&ip->ioc3_lock);
+ ioc3->emcr = EMCR_RST; /* Reset */
+ ioc3->emcr; /* flush WB */
+ udelay(4); /* Give it time ... */
+ ioc3->emcr = 0;
+ ioc3->emcr;
/* Misc registers */
ioc3->erbar = 0;
ioc3->emar_l = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) |
(dev->dev_addr[1] << 8) | dev->dev_addr[0];
ioc3->ehar_h = ioc3->ehar_l = 0;
- ioc3->ersr = 42; /* XXX should be random */
+ ioc3->ersr = 42; /* XXX should be random */
//ioc3->erpir = ERPIR_ARM;
- /* The IOC3-specific entries in the device structure. */
- dev->open = &ioc3_open;
- dev->hard_start_xmit = &ioc3_start_xmit;
- dev->tx_timeout = ioc3_timeout;
- dev->watchdog_timeo = (400 * HZ) / 1000;
- dev->stop = &ioc3_close;
- dev->get_stats = &ioc3_get_stats;
- dev->set_multicast_list = &ioc3_set_multicast_list;
+ ioc3_init_rings(dev, ip, ioc3);
+
+ ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
+ EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
+ ioc3->emcr = ip->emcr;
+ ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */
+ EISR_RXMEMERR | EISR_TXMEMERR;
+ ioc3->eier;
}
-int
-ioc3_probe(struct net_device *dev)
+static void ioc3_stop(struct net_device *dev)
{
- static int initialized;
- struct ioc3 *ioc3;
- nasid_t nid;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
- if (initialized) /* Only initialize once ... */
- return 0;
- initialized++;
+ ioc3->emcr = 0; /* Shutup */
+ ip->emcr = 0;
+ ioc3->eier = 0; /* Disable interrupts */
+ ioc3->eier; /* Flush */
+}
+
+static int
+ioc3_open(struct net_device *dev)
+{
+ if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
+ printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq);
+
+ return -EAGAIN;
+ }
+
+ ((struct ioc3_private *)dev->priv)->ehar_h = 0;
+ ((struct ioc3_private *)dev->priv)->ehar_l = 0;
+ ioc3_init(dev);
- nid = get_nasid();
- ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
- ioc3_probe1(dev, ioc3);
+ netif_start_queue(dev);
+
+ MOD_INC_USE_COUNT;
return 0;
}
static int
-ioc3_open(struct net_device *dev)
+ioc3_close(struct net_device *dev)
{
struct ioc3_private *ip = dev->priv;
- struct ioc3 *ioc3 = ip->regs;
- unsigned long flags;
- save_flags(flags); cli();
- if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
- printk("%s: Can't get irq %d\n", dev->name, dev->irq);
- restore_flags(flags);
+ netif_stop_queue(dev);
- return -EAGAIN;
- }
+ ioc3_stop(dev); /* Flush */
+ free_irq(dev->irq, dev);
- //ioc3_eth_init(dev, p, ioc3);
+ ioc3_free_rings(ip);
- ioc3->emcr = ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
- EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
- ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */
- EISR_RXMEMERR | EISR_TXMEMERR;
+ MOD_DEC_USE_COUNT;
- netif_wake_queue(dev);
- restore_flags(flags);
+ return 0;
+}
- MOD_INC_USE_COUNT;
+static void ioc3_pci_init(struct pci_dev *pdev)
+{
+ struct net_device *dev = NULL; // XXX
+ struct ioc3_private *ip;
+ struct ioc3 *ioc3;
+ unsigned long ioc3_base, ioc3_size;
+
+ dev = init_etherdev(dev, 0);
+
+ /*
+ * This probably needs to be register_netdevice, or call
+ * init_etherdev so that it calls register_netdevice. Quick
+ * hack for now.
+ */
+ netif_device_attach(dev);
+
+ ip = (struct ioc3_private *) kmalloc(sizeof(*ip), GFP_KERNEL);
+ memset(ip, 0, sizeof(*ip));
+ dev->priv = ip;
+ dev->irq = pdev->irq;
+
+ ioc3_base = pdev->resource[0].start;
+ ioc3_size = pdev->resource[0].end - ioc3_base;
+ ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size);
+ ip->regs = ioc3;
+
+ spin_lock_init(&ip->ioc3_lock);
+
+ ioc3_stop(dev);
+ ip->emcr = 0;
+ ioc3_init(dev);
+ ioc3_mii_init(dev, ip, ioc3);
+
+ ioc3_ssram_disc(ip);
+ printk("IOC3 SSRAM has %d kbyte.\n", ip->emcr & EMCR_BUFSIZ ? 128 : 64);
- return 0;
+ ioc3_get_eaddr(dev, ioc3);
+
+ /* The IOC3-specific entries in the device structure. */
+ dev->open = ioc3_open;
+ dev->hard_start_xmit = ioc3_start_xmit;
+ dev->tx_timeout = ioc3_timeout;
+ dev->watchdog_timeo = 5 * HZ;
+ dev->stop = ioc3_close;
+ dev->get_stats = ioc3_get_stats;
+ dev->do_ioctl = ioc3_ioctl;
+ dev->set_multicast_list = ioc3_set_multicast_list;
+}
+
+static int __init ioc3_probe(void)
+{
+ static int called = 0;
+ int cards = 0;
+
+ if (called)
+ return -ENODEV;
+ called = 1;
+
+ if (pci_present()) {
+ struct pci_dev *pdev = NULL;
+
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_SGI,
+ PCI_DEVICE_ID_SGI_IOC3, pdev))) {
+ ioc3_pci_init(pdev);
+ cards++;
+ }
+ }
+
+ return cards ? -ENODEV : 0;
+}
+
+static void __exit ioc3_cleanup_module(void)
+{
+ /* Later, when we really support modules. */
}
static int
struct ioc3_etxd *desc;
int produce;
- if (!TX_BUFFS_AVAIL(ip)) {
- return 1;
- }
-
spin_lock_irq(&ip->ioc3_lock);
+
data = (unsigned long) skb->data;
len = skb->len;
ip->tx_pi = produce;
ioc3->etpir = produce << 7; /* Fire ... */
- if (TX_BUFFS_AVAIL(ip))
- netif_wake_queue(dev);
+ ip->txqlen++;
+
+ if (ip->txqlen > 127)
+ netif_stop_queue(dev);
+
spin_unlock_irq(&ip->ioc3_lock);
return 0;
static void ioc3_timeout(struct net_device *dev)
{
- printk("%s: transmit timed out, resetting\n", dev->name);
- /* XXX should reset device here. */
+ printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+
+ ioc3_stop(dev);
+ ioc3_clean_tx_ring(dev->priv);
+ ioc3_init(dev);
dev->trans_start = jiffies;
netif_wake_queue(dev);
}
-static int
-ioc3_close(struct net_device *dev)
-{
- struct ioc3_private *ip = dev->priv;
- struct ioc3 *ioc3 = ip->regs;
-
- netif_stop_queue(dev);
-
- ioc3->emcr = 0; /* Shutup */
- ioc3->eier = 0; /* Disable interrupts */
- ioc3->eier; /* Flush */
- free_irq(dev->irq, dev);
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-}
-
-#if 0
-/* Initialize the NIC */
-static int
-ioc3_init(struct ioc3_private *p, struct pci_dev *dev)
-{
-#if 0
- unsigned long base;
- struct ioc3 *ioc3;
-
- pci_set_master(dev);
- base = dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
- printk("Base address at %08lx\n", base);
-
- p->regs = ioc3 = ioremap (base, IOC3_SIZE);
- printk("Remapped base address to %08lx\n", (unsigned long) regs);
-
- read_nic(ioc3);
-
- return 0;
-#endif
- panic(__FUNCTION__" has been called.\n");
-}
-#endif
-
/*
* Given a multicast ethernet address, this routine calculates the
* address's bit index in the logical address filter mask
return temp;
}
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct ioc3_private *ip = (struct ioc3_private *) dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+ struct ioc3 *ioc3 = ip->regs;
+ int phy = ip->phy;
+
+ switch (cmd) {
+ case SIOCGMIIPHY: /* Get the address of the PHY in use. */
+ if (phy == -1)
+ return -ENODEV;
+ data[0] = phy;
+ return 0;
+
+ case SIOCGMIIREG: /* Read any PHY register. */
+ spin_lock_irq(&ip->ioc3_lock);
+ data[3] = mii_read(ioc3, data[0], data[1]);
+ spin_unlock_irq(&ip->ioc3_lock);
+ return 0;
+
+ case SIOCSMIIREG: /* Write any PHY register. */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ spin_lock_irq(&ip->ioc3_lock);
+ mii_write(ioc3, data[0], data[1], data[2]);
+ spin_unlock_irq(&ip->ioc3_lock);
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
static void ioc3_set_multicast_list(struct net_device *dev)
{
struct dev_mc_list *dmi = dev->mc_list;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
char *addr = dmi->dmi_addr;
- struct ioc3_private *p;
- struct ioc3 *ioc3;
u64 ehar = 0;
int i;
- p = dev->priv;
- ioc3 = p->regs;
-
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
/* Unconditionally log net taps. */
printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
- ioc3->emcr |= EMCR_PROMISC;
+ ip->emcr |= EMCR_PROMISC;
+ ioc3->emcr = ip->emcr;
ioc3->emcr;
} else {
- ioc3->emcr &= ~EMCR_PROMISC; /* Clear promiscuous. */
+ ip->emcr &= ~EMCR_PROMISC;
+ ioc3->emcr = ip->emcr; /* Clear promiscuous. */
ioc3->emcr;
if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
/* Too many for hashing to make sense or we want all
multicast packets anyway, so skip computing all the
hashes and just accept all packets. */
- ioc3->ehar_h = 0xffffffff;
- ioc3->ehar_l = 0xffffffff;
+ ip->ehar_h = 0xffffffff;
+ ip->ehar_l = 0xffffffff;
} else {
for (i = 0; i < dev->mc_count; i++) {
dmi = dmi->next;
ehar |= (1 << ioc3_hash(addr));
}
- ioc3->ehar_h = ehar >> 32;
- ioc3->ehar_l = ehar & 0xffffffff;
+ ip->ehar_h = ehar >> 32;
+ ip->ehar_l = ehar & 0xffffffff;
}
+ ioc3->ehar_h = ip->ehar_h;
+ ioc3->ehar_l = ip->ehar_l;
}
}
#ifdef MODULE
MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");
MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
-
-int
-init_module(void)
-{
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_find_device(PCI_VENDOR_ID_SGI, 0x0003, dev))) {
- ioc3_init(&p, dev);
- }
-
- return -EBUSY;
-}
-
-void
-cleanup_module(void)
-{
- printk("cleanup_module called\n");
-}
#endif /* MODULE */
+
+module_init(ioc3_probe);
+module_exit(ioc3_cleanup_module);
lance_need_isa_bounce_buffers = 0;
#if defined(CONFIG_PCI)
- if (pci_present())
{
struct pci_dev *pdev = NULL;
if (lance_debug > 1)
while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, pdev))) {
unsigned int pci_ioaddr;
- unsigned short pci_command;
+ if (pci_enable_device(pdev))
+ continue;
+ pci_set_master(pdev);
pci_irq_line = pdev->irq;
- pci_ioaddr = pdev->resource[0].start;
- /* PCI Spec 2.1 states that it is either the driver or PCI card's
- * responsibility to set the PCI Master Enable Bit if needed.
- * (From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
- */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- if ( ! (pci_command & PCI_COMMAND_MASTER)) {
- printk("PCI Master Bit has not been set. Setting...\n");
- pci_command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, pci_command);
- }
+ pci_ioaddr = pci_resource_start (pdev, 0);
printk("Found PCnet/PCI at %#x, irq %d.\n",
pci_ioaddr, pci_irq_line);
result = lance_probe1(dev, pci_ioaddr, pci_irq_line, 0);
if (ioaddr > 0x1ff) /* Check a single specified location. */
return lne390_probe1(dev, ioaddr);
else if (ioaddr > 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
if (!EISA_bus) {
#if LNE390_DEBUG & LNE390_D_PROBE
printk("lne390-debug: Not an EISA bus. Not probing high ports.\n");
#endif
- return ENXIO;
+ return -ENXIO;
}
/* EISA spec allows for up to 16 slots, but 8 is typical. */
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
int __init lne390_probe1(struct net_device *dev, int ioaddr)
/* Check the EISA ID of the card. */
eisa_id = inl(ioaddr + LNE390_ID_PORT);
if ((eisa_id != LNE390_ID0) && (eisa_id != LNE390_ID1)) {
- return ENODEV;
+ return -ENODEV;
}
revision = (eisa_id >> 24) & 0x01; /* 0 = rev A, 1 rev B */
for(i = 0; i < ETHER_ADDR_LEN; i++)
printk(" %02x", inb(ioaddr + LNE390_SA_PROM + i));
printk(" (invalid prefix).\n");
- return ENODEV;
+ return -ENODEV;
}
#endif
- if (load_8390_module("lne390.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("lne390.c: Passed a NULL device.\n");
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
if (dev->mem_start == 0) {
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EINVAL;
+ return -EINVAL;
}
dev->mem_start = (unsigned long)ioremap(dev->mem_start, LNE390_STOP_PG*0x100);
if (dev->mem_start == 0) {
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
ei_status.reg0 = 1; /* Use as remap flag */
printk("lne390.c: remapped %dkB card memory to virtual address %#lx\n",
{
int this_dev, found = 0;
+ if (load_8390_module("lne390.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
struct net_device *dev = &dev_lne[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "lne390.c: No LNE390 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
unsigned short sig;
if (once_is_enough)
- return ENODEV;
+ return -ENODEV;
once_is_enough = 1;
/* We might have to parameterize this later */
slot = 0xE;
/* Get out now if there's a real NuBus card in slot E */
if (nubus_find_slot(slot, NULL) != NULL)
- return ENODEV;
+ return -ENODEV;
/* The pseudo-ISA bits always live at offset 0x300 (gee,
wonder why...) */
restore_flags(flags);
if (!card_present)
- return ENODEV;
+ return -ENODEV;
}
writew(0, ioaddr + ADD_PORT);
sig = readw(ioaddr + DATA_PORT);
if (sig != swab16(CHIP_EISA_ID_SIG))
- return ENODEV;
+ return -ENODEV;
/* Initialize the net_device structure. */
if (dev->priv == NULL) {
/* Try to read the MAC address */
if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
printk("\nmac89x0: No EEPROM, giving up now.\n");
- return ENODEV;
+ return -ENODEV;
} else {
for (i = 0; i < ETH_ALEN; i += 2) {
/* Big-endian (why??!) */
mp->reg_size, "MyriCOM Regs");
if (!mp->regs) {
printk("MyriCOM: Cannot map MyriCOM registers.\n");
- return ENODEV;
+ return -ENODEV;
}
mp->lanai = (unsigned short *) (mp->regs + (256 * 1024));
mp->lanai3 = (unsigned int *) mp->lanai;
if (request_irq(dev->irq, &myri_interrupt,
SA_SHIRQ, "MyriCOM Ethernet", (void *) dev)) {
printk("MyriCOM: Cannot register interrupt handler.\n");
- return ENODEV;
+ return -ENODEV;
}
DET(("ether_setup()\n"));
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
for_each_sbus(bus) {
}
}
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
if (base_addr > 0x1ff) /* Check a single specified location. */
return ne_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
#ifdef CONFIG_PCI
/* Then look for any installed PCI clones */
}
#endif
- return ENODEV;
+ return -ENODEV;
}
#endif
unsigned int pci_ioaddr;
while ((pdev = pci_find_device(pci_clone_list[i].vendor, pci_clone_list[i].dev_id, pdev))) {
- pci_ioaddr = pdev->resource[0].start;
+ if (pci_enable_device(pdev))
+ continue;
+ pci_ioaddr = pci_resource_start (pdev, 0);
/* Avoid already found cards from previous calls */
if (check_region(pci_ioaddr, NE_IO_EXTENT))
continue;
static unsigned version_printed = 0;
if (reg0 == 0xFF)
- return ENODEV;
+ return -ENODEV;
/* Do a preliminary verification that we have a 8390. */
{
if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
outb_p(reg0, ioaddr);
outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
- return ENODEV;
+ return -ENODEV;
}
}
- if (load_8390_module("ne.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL)
{
break;
} else {
printk(" not found (no reset ack).\n");
- return ENODEV;
+ return -ENODEV;
}
}
{
printk(" not found (invalid signature %2.2x %2.2x).\n",
SA_prom[14], SA_prom[15]);
- return ENXIO;
+ return -ENXIO;
}
#else
printk(" not found.\n");
- return ENXIO;
+ return -ENXIO;
#endif
}
if (! dev->irq) {
printk(" failed to detect IRQ line.\n");
- return EAGAIN;
+ return -EAGAIN;
}
/* Allocate dev->priv and fill in 8390 specific dev fields. */
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
}
dev->base_addr = ioaddr;
{
int this_dev, found = 0;
+ if (load_8390_module("ne.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
struct net_device *dev = &dev_ne[this_dev];
dev->irq = irq[this_dev];
continue;
}
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
if (io[this_dev] != 0)
printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]);
else
printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n");
+ unload_8390_module();
return -ENXIO;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
return ne2_probe1(dev, current_mca_slot);
}
}
- return ENODEV;
+ return -ENODEV;
}
POS = mca_read_stored_pos(slot, 2);
if(!(POS % 2)) {
printk(" disabled.\n");
- return ENODEV;
+ return -ENODEV;
}
i = (POS & 0xE)>>1;
outb(0x21, base_addr + NE_CMD);
if (inb(base_addr + NE_CMD) != 0x21) {
printk("NE/2 adapter not responding\n");
- return ENODEV;
+ return -ENODEV;
}
/* In the crynwr sources they do a RAM-test here. I skip it. I suppose
while ((inb_p(base_addr + EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2*HZ/100) {
printk(" not found (no reset ack).\n");
- return ENODEV;
+ return -ENODEV;
}
outb_p(0xff, base_addr + EN0_ISR); /* Ack all intr. */
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n",
dev->irq, +irqval);
- return EAGAIN;
+ return -EAGAIN;
}
}
outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
}
- if (load_8390_module("ne2k-pci.c")) {
- printk (KERN_ERR "ne2k-pci: cannot load 8390 module\n");
- goto err_out_free_netdev;
- }
-
/* Read the 16 bytes of station address PROM.
We must first initialize registers, similar to NS8390_init(eifdev, 0).
We can't reliably read the SAPROM address without this.
{
int rc;
- lock_8390_module();
+ if (load_8390_module("ne2k-pci.c"))
+ return -ENOSYS;
rc = pci_module_init (&ne2k_driver);
/* XXX should this test CONFIG_HOTPLUG like pci_module_init? */
+
+ /* YYY No. If we're returning non-zero, we're being unloaded
+ * immediately. dwmw2
+ */
if (rc <= 0)
- unlock_8390_module();
+ unload_8390_module();
return rc;
}
static void __exit ne2k_pci_cleanup(void)
{
pci_unregister_driver (&ne2k_driver);
- unlock_8390_module();
+ unload_8390_module();
}
module_init(ne2k_pci_init);
if (ioaddr > 0x1ff) /* Check a single specified location. */
return ne3210_probe1(dev, ioaddr);
else if (ioaddr > 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
if (!EISA_bus) {
#if NE3210_DEBUG & NE3210_D_PROBE
printk("ne3210-debug: Not an EISA bus. Not probing high ports.\n");
#endif
- return ENXIO;
+ return -ENXIO;
}
/* EISA spec allows for up to 16 slots, but 8 is typical. */
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
int __init ne3210_probe1(struct net_device *dev, int ioaddr)
/* Check the EISA ID of the card. */
eisa_id = inl(ioaddr + NE3210_ID_PORT);
if (eisa_id != NE3210_ID) {
- return ENODEV;
+ return -ENODEV;
}
for(i = 0; i < ETHER_ADDR_LEN; i++)
printk(" %02x", inb(ioaddr + NE3210_SA_PROM + i));
printk(" (invalid prefix).\n");
- return ENODEV;
+ return -ENODEV;
}
#endif
- if (load_8390_module("ne3210.c"))
- return -ENOSYS;
-
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("ne3210.c: Passed a NULL device.\n");
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
if (dev->mem_start == 0) {
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EINVAL;
+ return -EINVAL;
}
dev->mem_start = (unsigned long)ioremap(dev->mem_start, NE3210_STOP_PG*0x100);
if (dev->mem_start == 0) {
free_irq(dev->irq, dev);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
ei_status.reg0 = 1; /* Use as remap flag */
printk("ne3210.c: remapped %dkB card memory to virtual address %#lx\n",
{
int this_dev, found = 0;
+ if (load_8390_module("ne3210.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_NE3210_CARDS; this_dev++) {
struct net_device *dev = &dev_ne3210[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "ne3210.c: No NE3210 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
(inb(base_addr+NI52_MAGIC2) == NI52_MAGICVAL2))
return ni52_probe1(dev, base_addr);
} else if (base_addr > 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
#ifdef MODULE
printk("%s: no autoprobing allowed for modules.\n",dev->name);
#endif
dev->base_addr = base_addr;
- return ENODEV;
+ return -ENODEV;
}
static int __init ni52_probe1(struct net_device *dev,int ioaddr)
if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
|| dev->dev_addr[2] != NI52_ADDR2)
- return ENODEV;
+ return -ENODEV;
printk("%s: NI5210 found at %#3lx, ",dev->name,dev->base_addr);
if(size != 0x2000 && size != 0x4000)
{
printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n",dev->name,size);
- return ENODEV;
+ return -ENODEV;
}
if(!check586(dev,(char *) dev->mem_start,size))
{
printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size);
- return ENODEV;
+ return -ENODEV;
}
#else
if(dev->mem_start != 0) /* no auto-mem-probe */
size = 0x2000; /* check for 8K mem */
if(!check586(dev,(char *) dev->mem_start,size)) {
printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start);
- return ENODEV;
+ return -ENODEV;
}
}
}
{
if(!memaddrs[i]) {
printk("?memprobe, Can't find io-memory!\n");
- return ENODEV;
+ return -ENODEV;
}
dev->mem_start = memaddrs[i];
size = 0x2000; /* check for 8K mem */
return (ENODEV);
}
- /*
- * We're dependent on the 8390 generic driver module, make
- * sure its symbols are loaded.
- */
-
- if (load_8390_module("oaknet.c")) {
- release_region(dev->base_addr, OAKNET_IO_SIZE);
- return (-ENOSYS);
- }
-
/*
* We're not using the old-style probing API, so we have to allocate
* our own device structure.
{
int status;
+ /*
+ * We're dependent on the 8390 generic driver module, make
+ * sure its symbols are loaded.
+ */
+
+ if (load_8390_module("oaknet.c"))
+ return (-ENOSYS);
+
if (oaknet_devs != NULL)
return (-EBUSY);
status = oaknet_init()
- if (status == 0)
- lock_8390_module();
+ if (status != 0)
+ unload_8390_module();
return (status);
}
oaknet_devs = NULL;
- unlock_8390_module();
+ unload_8390_module();
}
module_init(oaknet_init_module);
tristate ' Xircom Tulip-like CardBus support' CONFIG_PCMCIA_XIRTULIP
fi
- bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
+ bool ' Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then
- dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
- dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
- dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
- dep_tristate ' Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA
+ dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+ dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
+ dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
+ dep_tristate ' Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA
fi
fi
int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *);
};
-static struct pcnet32_pci_id_info pcnet32_tbl[] = {
- { "AMD PCnetPCI series",
- PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0, 0,
- PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
- pcnet32_probe1},
- { "AMD PCnetPCI series (IBM)",
- PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000,
- PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
- pcnet32_probe1},
- { "AMD PCnetHome series",
- PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0,
- PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
- pcnet32_probe1},
- {0,}
-};
/*
* PCI device identifiers for "new style" Linux PCI Device Drivers
}
if (pcnet32_debug > 0)
- printk(KERN_INFO, version);
+ printk(KERN_INFO "%s", version);
/* The PCNET32-specific entries in the device structure. */
dev->open = &pcnet32_open;
lp->stats.rx_errors++;
} else {
int rx_in_place = 0;
- dma_addr_t rx_dma_addr = lp->rx_dma_addr[entry];
-
+
if (pkt_len > rx_copybreak) {
struct sk_buff *newskb;
/* find the PCI devices */
#define USE_PCI_REGISTER_DRIVER
#ifdef USE_PCI_REGISTER_DRIVER
- if (err = pci_module_init(&pcnet32_driver) < 0 )
+ if ((err = pci_module_init(&pcnet32_driver)) < 0 )
return err;
#else
{
-/* $Id: ptifddi.c,v 1.11 1999/10/25 01:50:16 zaitcev Exp $
+/* $Id: ptifddi.c,v 1.12 2000/06/19 06:24:46 davem Exp $
* ptifddi.c: Network driver for Performance Technologies single-attach
* and dual-attach FDDI sbus cards.
*
&sdep->resource[0], 0, sizeof(sturct dfddi_ram), "PTI FDDI DPRAM");
if(!pp->dpram) {
printk("ptiFDDI: Cannot map DPRAM I/O area.\n");
- return ENODEV;
+ return -ENODEV;
}
/* Next, register 1 contains reset byte. */
&sdep->resource[1], 0, 1, "PTI FDDI RESET Byte");
if(!pp->reset) {
printk("ptiFDDI: Cannot map RESET byte.\n");
- return ENODEV;
+ return -ENODEV;
}
/* Register 2 contains unreset byte. */
&sdep->resource[2], 0, 1, "PTI FDDI UNRESET Byte");
if(!pp->unreset) {
printk("ptiFDDI: Cannot map UNRESET byte.\n");
- return ENODEV;
+ return -ENODEV;
}
/* Reset the card. */
i = pti_card_test(pp);
if(i) {
printk("ptiFDDI: Bootup card test fails.\n");
- return ENODEV;
+ return -ENODEV;
}
/* Clear DPRAM, start afresh. */
int cards = 0, v;
if(called)
- return ENODEV;
+ return -ENODEV;
called++;
for_each_sbus(bus) {
}
}
if(!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
!((pdev = pci_find_slot(pci_bus, pci_device_fn))))
break;
pci_irq_line = pdev->irq;
- pci_ioaddr = pdev->resource[0].start;
+ pci_ioaddr = pci_resource_start (pdev, 0);
#ifdef RCDEBUG
printk("rc: Found RedCreek PCI adapter\n");
printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
#endif
+ if (pci_enable_device(pdev))
+ break;
pci_set_master(pdev);
if (!RCfound_device(pci_ioaddr, pci_irq_line,
PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER,
pdev)))
{
+ if (pci_enable_device(pdev))
+ continue;
+
if (pdev == opdev)
return 0;
continue;
pdev = pci_find_slot(pci_bus, pci_device_fn);
- ioaddr = pdev->resource[0].start;
+
+ ioaddr = pci_resource_start(pdev, 0);
irq = pdev->irq;
+ if (pci_enable_device(pdev))
+ continue;
+
if ((pci_tbl[chip_idx].flags & PCI_USES_IO) &&
check_region(ioaddr, pci_tbl[chip_idx].io_size))
continue;
if (base_addr > 0x1ff) /* Check a single specified location. */
return seeq8005_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; seeq8005_portlist[i]; i++) {
int ioaddr = seeq8005_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
old_stat = inw(SEEQ_STATUS); /* read status register */
if (old_stat == 0xffff)
- return ENODEV; /* assume that 0xffff == no device */
+ return -ENODEV; /* assume that 0xffff == no device */
if ( (old_stat & 0x1800) != 0x1800 ) { /* assume that unused bits are 1, as my manual says */
if (net_debug>1) {
printk("seeq8005: reserved stat bits != 0x1800\n");
printk(" == 0x%04x\n",old_stat);
}
- return ENODEV;
+ return -ENODEV;
}
old_rear = inw(SEEQ_REA);
if (old_rear == 0xffff) {
outw(0,SEEQ_REA);
if (inw(SEEQ_REA) == 0xffff) { /* assume that 0xffff == no device */
- return ENODEV;
+ return -ENODEV;
}
} else if ((old_rear & 0xff00) != 0xff00) { /* assume that unused bits are 1 */
if (net_debug>1) {
printk("seeq8005: unused rear bits != 0xff00\n");
printk(" == 0x%04x\n",old_rear);
}
- return ENODEV;
+ return -ENODEV;
}
old_cfg2 = inw(SEEQ_CFG2); /* read CFG2 register */
outw( old_stat, SEEQ_STATUS);
outw( old_dmaar, SEEQ_DMAAR);
outw( old_cfg1, SEEQ_CFG1);
- return ENODEV;
+ return -ENODEV;
}
#endif
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
- return EAGAIN;
+ return -EAGAIN;
}
}
#endif
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
- return EAGAIN;
+ return -EAGAIN;
}
}
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
Copyright 1999 Silicon Integrated System Corporation
- Revision: 1.06.04 Feb 11 2000
+ Revision: 1.07 Mar. 07 2000
Modified from the driver which is originally written by Donald Becker.
preliminary Rev. 1.0 Jan. 18, 1998
http://www.sis.com.tw/support/databook.htm
+ Rev 1.07 Mar. 07 2000 Ollie Lho bug fix in Rx buffer ring
Rev 1.06.04 Feb. 11 2000 Jeff Garzik <jgarzik@mandrakesoft.com> softnet and init for kernel 2.4
Rev 1.06.03 Dec. 23 1999 Ollie Lho Third release
Rev 1.06.02 Nov. 23 1999 Ollie Lho bug in mac probing fixed
char *card_name);
enum {
SIS_900 = 0,
- SIS_7018
+ SIS_7016
};
static char * card_names[] = {
"SiS 900 PCI Fast Ethernet",
"SiS 7016 PCI Fast Ethernet"
};
-static struct pci_device_id sis900_pci_tbl [] __initdata = {
+static struct pci_device_id sis900_pci_tbl [] __devinitdata = {
{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900},
{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7016},
{0,}
};
MODULE_DEVICE_TABLE (pci, sis900_pci_tbl);
unsigned int cur_phy;
struct timer_list timer; /* Link status detection timer. */
+
unsigned int cur_rx, dirty_rx;
unsigned int cur_tx, dirty_tx;
struct sk_buff *rx_skbuff[NUM_RX_DESC];
BufferDesc tx_ring[NUM_TX_DESC];
BufferDesc rx_ring[NUM_RX_DESC];
- unsigned int tx_full; /* The Tx queue is full. */
+ unsigned int tx_full; /* The Tx queue is full. */
int LinkOn;
};
return -ENODEV;
}
- pci_io_base = pci_dev->resource[0].start;
+ pci_io_base = pci_resource_start(pci_dev, 0);
if (check_region(pci_io_base, SIS900_TOTAL_SIZE)) {
printk(KERN_ERR "sis900.c: can't allocate I/O space at 0x%08x\n",
pci_io_base);
/* do the real low level jobs */
if (sis900_mac_probe(pci_dev, card_names[pci_id->driver_data]) == NULL)
- return -1;
+ return -ENODEV;
return 0;
}
static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, char * card_name)
{
struct sis900_private *sis_priv;
- long ioaddr = pci_dev->resource[0].start;
+ long ioaddr = pci_resource_start(pci_dev, 0);
struct net_device *net_dev = NULL;
int irq = pci_dev->irq;
u16 signature;
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
long ioaddr = net_dev->base_addr;
+ MOD_INC_USE_COUNT;
+
/* Soft reset the chip. */
sis900_reset(net_dev);
if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
+ MOD_DEC_USE_COUNT;
return -EAGAIN;
}
- MOD_INC_USE_COUNT;
-
sis900_init_rxfilter(net_dev);
sis900_init_tx_ring(net_dev);
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
unregister_netdev(net_dev);
- release_region(net_dev->base_addr,
- SIS900_TOTAL_SIZE);
+ release_region(net_dev->base_addr, SIS900_TOTAL_SIZE);
kfree(sis_priv);
kfree(net_dev);
if (!pci_present()) /* is PCI support present? */
return -ENODEV;
- while((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev)))
- {
- dev = NULL;
-
- if (pdev->vendor != PCI_VENDOR_ID_SYSKONNECT ||
- pdev->device != PCI_DEVICE_ID_SYSKONNECT_GE) {
+ while((pdev = pci_find_device(PCI_VENDOR_ID_SYSKONNECT,
+ PCI_DEVICE_ID_SYSKONNECT_GE, pdev)) != NULL) {
+ if (pci_enable_device(pdev))
continue;
- }
dev = init_etherdev(dev, sizeof(SK_AC));
- if (dev == NULL || dev->priv == NULL){
+ if (dev == NULL) {
printk(KERN_ERR "Unable to allocate etherdev "
"structure!\n");
break;
}
- memset(dev->priv, 0, sizeof(SK_AC));
-
pAC = dev->priv;
pAC->PciDev = *pdev;
pAC->PciDevId = pdev->device;
pci_set_master(pdev);
- base_address = pdev->resource[0].start;
+ base_address = pci_resource_start (pdev, 0);
#ifdef SK_BIG_ENDIAN
/*
return SK_probe(dev, base_addr);
}
- return ENODEV; /* Sorry, but on specified address NO SK_G16 */
+ return -ENODEV; /* Sorry, but on specified address NO SK_G16 */
}
else if (base_addr > 0) /* Don't probe at all */
{
- return ENXIO;
+ return -ENXIO;
}
/* Autoprobe base_addr */
dev->base_addr = base_addr; /* Write back original base_addr */
- return ENODEV; /* Failed to find or init driver */
+ return -ENODEV; /* Failed to find or init driver */
} /* End of SK_init */
{
PRINTK(("## %s: We did not find SK_G16 at RAM location.\n",
SK_NAME));
- return ENODEV; /* NO SK_G16 found */
+ return -ENODEV; /* NO SK_G16 found */
}
printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n",
implemented LANCE multicast filter
Jun 6th, 1999
additions for Linux 2.2
- Aug 2nd, 1999
- small fixes (David Weinehall)
+ Dec 25th, 1999
+ unfortunately there seem to be newer MC2+ boards that react
+ on IRQ 3/5/9/10 instead of 3/5/10/11, so we have to autoprobe
+ in questionable cases...
+ Dec 28th, 1999
+ integrated patches from David Weinehall & Bill Wendling for 2.3
+ kernels (isa_...functions). Things are defined in a way that
+ it still works with 2.0.x 8-)
+ Dec 30th, 1999
+ added handling of the remaining interrupt conditions. That
+ should cure the spurious hangs.
+ Jan 30th, 2000
+ newer kernels automatically probe more than one board, so the
+ 'startslot' as a variable is also needed here
+ June 1st, 2000
+ added changes for recent 2.3 kernels
*************************************************************************/
-#include <linux/module.h>
-#include <linux/version.h>
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
* have to pack all state info into the device struct!
* ------------------------------------------------------------------------ */
-static char *MediaNames[Media_Count] = {
- "10Base2", "10BaseT", "10Base5", "Unknown" };
+static char *MediaNames[Media_Count] =
+ { "10Base2", "10BaseT", "10Base5", "Unknown" };
-static unsigned char poly[] = {
- 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,
+static unsigned char poly[] =
+ { 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0
};
/* dump parts of shared memory - only needed during debugging */
#ifdef DEBUG
-static void dumpmem(struct net_device *dev, u32 start, u32 len)
+static void dumpmem(struct SKMCA_NETDEV *dev, u32 start, u32 len)
{
int z;
for (z = 0; z < len; z++) {
if ((z & 15) == 0)
printk("%04x:", z);
- printk(" %02x", readb(dev->mem_start + start + z));
+ printk(" %02x", SKMCA_READB(dev->mem_start + start + z));
if ((z & 15) == 15)
printk("\n");
}
do_gettimeofday(&tv);
printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec);
}
-
#endif
/* deduce resources out of POS registers */
*irq = 5;
break;
case 8:
- *irq = 10;
+ *irq = -10;
break;
case 12:
- *irq = 11;
+ *irq = -11;
break;
}
*medium = (pos2 >> 6) & 3;
/* reset the whole board */
-static void ResetBoard(struct net_device *dev)
+static void ResetBoard(struct SKMCA_NETDEV *dev)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
- writeb(CTRL_RESET_ON, priv->ctrladdr);
+ SKMCA_WRITEB(CTRL_RESET_ON, priv->ctrladdr);
udelay(10);
- writeb(CTRL_RESET_OFF, priv->ctrladdr);
+ SKMCA_WRITEB(CTRL_RESET_OFF, priv->ctrladdr);
+}
+
+/* wait for LANCE interface to become not busy */
+
+static int WaitLANCE(struct SKMCA_NETDEV *dev)
+{
+ skmca_priv *priv = (skmca_priv *) dev->priv;
+ int t = 0;
+
+ while ((SKMCA_READB(priv->ctrladdr) & STAT_IO_BUSY) ==
+ STAT_IO_BUSY) {
+ udelay(1);
+ if (++t > 1000) {
+ printk("%s: LANCE access timeout", dev->name);
+ return 0;
+ }
+ }
+
+ return 1;
}
/* set LANCE register - must be atomic */
-static void SetLANCE(struct net_device *dev, u16 addr, u16 value)
+static void SetLANCE(struct SKMCA_NETDEV *dev, u16 addr, u16 value)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
unsigned long flags;
/* wait until no transfer is pending */
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ WaitLANCE(dev);
/* transfer register address to RAP */
- writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP,
- priv->ctrladdr);
- writew(addr, priv->ioregaddr);
- writeb(IOCMD_GO, priv->cmdaddr);
+ SKMCA_WRITEB(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP,
+ priv->ctrladdr);
+ SKMCA_WRITEW(addr, priv->ioregaddr);
+ SKMCA_WRITEB(IOCMD_GO, priv->cmdaddr);
udelay(1);
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ WaitLANCE(dev);
/* transfer data to register */
- writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA,
- priv->ctrladdr);
- writew(value, priv->ioregaddr);
- writeb(IOCMD_GO, priv->cmdaddr);
+ SKMCA_WRITEB(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA,
+ priv->ctrladdr);
+ SKMCA_WRITEW(value, priv->ioregaddr);
+ SKMCA_WRITEB(IOCMD_GO, priv->cmdaddr);
udelay(1);
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ WaitLANCE(dev);
/* reenable interrupts */
/* get LANCE register */
-static u16 GetLANCE(struct net_device *dev, u16 addr)
+static u16 GetLANCE(struct SKMCA_NETDEV *dev, u16 addr)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
unsigned long flags;
/* wait until no transfer is pending */
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ WaitLANCE(dev);
/* transfer register address to RAP */
- writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP,
- priv->ctrladdr);
- writew(addr, priv->ioregaddr);
- writeb(IOCMD_GO, priv->cmdaddr);
+ SKMCA_WRITEB(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP,
+ priv->ctrladdr);
+ SKMCA_WRITEW(addr, priv->ioregaddr);
+ SKMCA_WRITEB(IOCMD_GO, priv->cmdaddr);
udelay(1);
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ WaitLANCE(dev);
/* transfer data from register */
- writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA,
- priv->ctrladdr);
- writeb(IOCMD_GO, priv->cmdaddr);
+ SKMCA_WRITEB(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA,
+ priv->ctrladdr);
+ SKMCA_WRITEB(IOCMD_GO, priv->cmdaddr);
udelay(1);
- while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
- res = readw(priv->ioregaddr);
+ WaitLANCE(dev);
+ res = SKMCA_READW(priv->ioregaddr);
/* reenable interrupts */
/* build up descriptors in shared RAM */
-static void InitDscrs(struct net_device *dev)
+static void InitDscrs(struct SKMCA_NETDEV *dev)
{
u32 bufaddr;
descr.Flags = 0;
descr.Len = 0xf000;
descr.Status = 0;
- isa_memcpy_toio(dev->mem_start + RAM_TXBASE +
- (z * sizeof(LANCE_TxDescr)),
- &descr, sizeof(LANCE_TxDescr));
- memset_io(dev->mem_start + bufaddr, 0,
- RAM_BUFSIZE);
+ SKMCA_TOIO(dev->mem_start + RAM_TXBASE +
+ (z * sizeof(LANCE_TxDescr)), &descr,
+ sizeof(LANCE_TxDescr));
+ SKMCA_SETIO(dev->mem_start + bufaddr, 0,
+ RAM_BUFSIZE);
bufaddr += RAM_BUFSIZE;
}
}
descr.Flags = RXDSCR_FLAGS_OWN;
descr.MaxLen = -RAM_BUFSIZE;
descr.Len = 0;
- isa_memcpy_toio(dev->mem_start + RAM_RXBASE +
- (z * sizeof(LANCE_RxDescr)),
- &descr, sizeof(LANCE_RxDescr));
- isa_memset_io(dev->mem_start + bufaddr, 0,
- RAM_BUFSIZE);
+ SKMCA_TOIO(dev->mem_start + RAM_RXBASE +
+ (z * sizeof(LANCE_RxDescr)), &descr,
+ sizeof(LANCE_RxDescr));
+ SKMCA_SETIO(dev->mem_start + bufaddr, 0,
+ RAM_BUFSIZE);
bufaddr += RAM_BUFSIZE;
}
}
/* feed ready-built initialization block into LANCE */
-static void InitLANCE(struct net_device *dev)
+static void InitLANCE(struct SKMCA_NETDEV *dev)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
/* we don't get ready until the LANCE has read the init block */
+#if (LINUX_VERSION_CODE >= 0x02032a)
netif_stop_queue(dev);
-
+#else
+ dev->tbusy = 1;
+#endif
+
/* let LANCE read the initialization block. LANCE is ready
when we receive the corresponding interrupt. */
/* stop the LANCE so we can reinitialize it */
-static void StopLANCE(struct net_device *dev)
+static void StopLANCE(struct SKMCA_NETDEV *dev)
{
/* can't take frames any more */
+#if (LINUX_VERSION_CODE >= 0x02032a)
netif_stop_queue(dev);
-
+#else
+ dev->tbusy = 1;
+#endif
+
/* disable interrupts, stop it */
SetLANCE(dev, LANCE_CSR0, CSR0_STOP);
/* initialize card and LANCE for proper operation */
-static void InitBoard(struct net_device *dev)
+static void InitBoard(struct SKMCA_NETDEV *dev)
{
LANCE_InitBlock block;
block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29);
block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29);
- isa_memcpy_toio(dev->mem_start + RAM_INITBASE, &block,
- sizeof(block));
+ SKMCA_TOIO(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
/* initialize LANCE. Implicitly sets up other structures in RAM. */
/* deinitialize card and LANCE */
-static void DeinitBoard(struct net_device *dev)
+static void DeinitBoard(struct SKMCA_NETDEV *dev)
{
/* stop LANCE */
ResetBoard(dev);
}
+/* probe for device's irq */
+
+static int ProbeIRQ(struct SKMCA_NETDEV *dev)
+{
+ unsigned long imaskval, njiffies, irq;
+ u16 csr0val;
+
+ /* enable all interrupts */
+
+ imaskval = probe_irq_on();
+
+ /* initialize the board. Wait for interrupt 'Initialization done'. */
+
+ ResetBoard(dev);
+ InitBoard(dev);
+
+ njiffies = jiffies + 100;
+ do {
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
+ while (((csr0val & CSR0_IDON) == 0) && (jiffies != njiffies));
+
+ /* turn of interrupts again */
+
+ irq = probe_irq_off(imaskval);
+
+ /* if we found something, ack the interrupt */
+
+ if (irq)
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_IDON);
+
+ /* back to idle state */
+
+ DeinitBoard(dev);
+
+ return irq;
+}
+
/* ------------------------------------------------------------------------
* interrupt handler(s)
* ------------------------------------------------------------------------ */
-/* LANCE has read initializazion block -> start it */
+/* LANCE has read initialization block -> start it */
-static u16 irqstart_handler(struct net_device *dev, u16 oldcsr0)
+static u16 irqstart_handler(struct SKMCA_NETDEV *dev, u16 oldcsr0)
{
/* now we're ready to transmit */
+#if (LINUX_VERSION_CODE >= 0x02032a)
netif_wake_queue(dev);
-
+#else
+ dev->tbusy = 0;
+#endif
+
/* reset IDON bit, start LANCE */
SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT);
return GetLANCE(dev, LANCE_CSR0);
}
+/* did we loose blocks due to a FIFO overrun ? */
+
+static u16 irqmiss_handler(struct SKMCA_NETDEV *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = (skmca_priv *) dev->priv;
+
+ /* update statistics */
+
+ priv->stat.rx_fifo_errors++;
+
+ /* reset MISS bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_MISS);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
/* receive interrupt */
-static u16 irqrx_handler(struct net_device *dev, u16 oldcsr0)
+static u16 irqrx_handler(struct SKMCA_NETDEV *dev, u16 oldcsr0)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
LANCE_RxDescr descr;
unsigned int descraddr;
- /* did we loose blocks due to a FIFO overrun ? */
-
- if (oldcsr0 & CSR0_MISS)
- priv->stat.rx_fifo_errors++;
-
/* run through queue until we reach a descriptor we do not own */
descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr));
while (1) {
/* read descriptor */
- isa_memcpy_fromio(&descr, dev->mem_start + descraddr,
- sizeof(LANCE_RxDescr));
+ SKMCA_FROMIO(&descr, dev->mem_start + descraddr,
+ sizeof(LANCE_RxDescr));
/* if we reach a descriptor we do not own, we're done */
if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0)
if (skb == NULL)
priv->stat.rx_dropped++;
else {
- isa_memcpy_fromio(skb_put(skb, descr.Len),
- dev->mem_start +
- descr.LowAddr,
- descr.Len);
+ SKMCA_FROMIO(skb_put(skb, descr.Len),
+ dev->mem_start +
+ descr.LowAddr, descr.Len);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
descr.Flags |= RXDSCR_FLAGS_OWN;
/* update descriptor in shared RAM */
- isa_memcpy_toio(dev->mem_start + descraddr, &descr,
- sizeof(LANCE_RxDescr));
+ SKMCA_TOIO(dev->mem_start + descraddr, &descr,
+ sizeof(LANCE_RxDescr));
/* go to next descriptor */
priv->nextrx++;
/* transmit interrupt */
-static u16 irqtx_handler(struct net_device *dev, u16 oldcsr0)
+static u16 irqtx_handler(struct SKMCA_NETDEV *dev, u16 oldcsr0)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
LANCE_TxDescr descr;
RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr));
while (priv->txbusy > 0) {
/* read descriptor */
- isa_memcpy_fromio(&descr, dev->mem_start + descraddr,
- sizeof(LANCE_TxDescr));
+ SKMCA_FROMIO(&descr, dev->mem_start + descraddr,
+ sizeof(LANCE_TxDescr));
/* if the LANCE still owns this one, we've worked out all sent packets */
if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0)
/* at least one descriptor is freed. Therefore we can accept
a new one */
+ /* inform upper layers we're in business again */
+#if (LINUX_VERSION_CODE >= 0x02032a)
netif_wake_queue(dev);
-
+#else
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+#endif
+
return oldcsr0;
}
static void irq_handler(int irq, void *device, struct pt_regs *regs)
{
- struct net_device *dev = (struct net_device *) device;
+ struct SKMCA_NETDEV *dev = (struct SKMCA_NETDEV *) device;
u16 csr0val;
/* read CSR0 to get interrupt cause */
if ((csr0val & CSR0_INTR) == 0)
return;
+#if (LINUX_VERSION_CODE >= 0x02032a)
+#if 0
+ set_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+#else
+ dev->interrupt = 1;
+#endif
+
/* loop through the interrupt bits until everything is clear */
do {
csr0val = irqstart_handler(dev, csr0val);
if ((csr0val & CSR0_RINT) != 0)
csr0val = irqrx_handler(dev, csr0val);
+ if ((csr0val & CSR0_MISS) != 0)
+ csr0val = irqmiss_handler(dev, csr0val);
if ((csr0val & CSR0_TINT) != 0)
csr0val = irqtx_handler(dev, csr0val);
+ if ((csr0val & CSR0_MERR) != 0) {
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_MERR);
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
+ if ((csr0val & CSR0_BABL) != 0) {
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_BABL);
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
}
while ((csr0val & CSR0_INTR) != 0);
+
+#if (LINUX_VERSION_CODE >= 0x02032a)
+#if 0
+ clear_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+#else
+ dev->interrupt = 0;
+#endif
}
/* ------------------------------------------------------------------------
static int skmca_getinfo(char *buf, int slot, void *d)
{
int len = 0, i;
- struct net_device *dev = (struct net_device *) d;
+ struct SKMCA_NETDEV *dev = (struct SKMCA_NETDEV *) d;
skmca_priv *priv;
/* can't say anything about an uninitialized device... */
/* open driver. Means also initialization and start of LANCE */
-static int skmca_open(struct net_device *dev)
+static int skmca_open(struct SKMCA_NETDEV *dev)
{
int result;
skmca_priv *priv = (skmca_priv *) dev->priv;
dev->irq = priv->realirq;
/* set up the card and LANCE */
+
InitBoard(dev);
+ /* set up flags */
+
+#if (LINUX_VERSION_CODE >= 0x02032a)
+ netif_start_queue(dev);
+#else
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 0;
+#endif
+
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
/* close driver. Shut down board and free allocated resources */
-static int skmca_close(struct net_device *dev)
+static int skmca_close(struct SKMCA_NETDEV *dev)
{
/* turn off board */
DeinitBoard(dev);
/* transmit a block. */
-static int skmca_tx(struct sk_buff *skb, struct net_device *dev)
+static int skmca_tx(struct sk_buff *skb, struct SKMCA_NETDEV *dev)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
LANCE_TxDescr descr;
int tmplen, retval = 0;
unsigned long flags;
- netif_stop_queue(dev);
-
+ /* if we get called with a NULL descriptor, the Ethernet layer thinks
+ our card is stuck an we should reset it. We'll do this completely: */
+
+ if (skb == NULL) {
+ DeinitBoard(dev);
+ InitBoard(dev);
+ return 0; /* don't try to free the block here ;-) */
+ }
+
/* is there space in the Tx queue ? If no, the upper layer gave us a
packet in spite of us not being ready and is really in trouble.
We'll do the dropping for him: */
/* get TX descriptor */
address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr));
- isa_memcpy_fromio(&descr, dev->mem_start + address,
- sizeof(LANCE_TxDescr));
+ SKMCA_FROMIO(&descr, dev->mem_start + address,
+ sizeof(LANCE_TxDescr));
/* enter packet length as 2s complement - assure minimum length */
tmplen = skb->len;
unsigned int destoffs = 0, l = strlen(fill);
while (destoffs < tmplen) {
- isa_memcpy_toio(dev->mem_start + descr.LowAddr +
- destoffs, fill, l);
+ SKMCA_TOIO(dev->mem_start + descr.LowAddr +
+ destoffs, fill, l);
destoffs += l;
}
}
/* do the real data copying */
- isa_memcpy_toio(dev->mem_start + descr.LowAddr, skb->data,
- skb->len);
+ SKMCA_TOIO(dev->mem_start + descr.LowAddr, skb->data, skb->len);
/* hand descriptor over to LANCE - this is the first and last chunk */
descr.Flags =
if (priv->nexttxput >= TXCOUNT)
priv->nexttxput = 0;
priv->txbusy++;
- if (priv->txbusy < TXCOUNT)
- netif_wake_queue(dev);
+
+ /* are we saturated ? */
+
+ if (priv->txbusy >= TXCOUNT)
+#if (LINUX_VERSION_CODE >= 0x02032a)
+ netif_stop_queue(dev);
+#else
+ dev->tbusy = 1;
+#endif
/* write descriptor back to RAM */
- isa_memcpy_toio(dev->mem_start + address, &descr,
- sizeof(LANCE_TxDescr));
+ SKMCA_TOIO(dev->mem_start + address, &descr,
+ sizeof(LANCE_TxDescr));
/* if no descriptors were active, give the LANCE a hint to read it
immediately */
restore_flags(flags);
-tx_done:
+ tx_done:
+
/* When did that change exactly ? */
+
+#if LINUX_VERSION_CODE >= 0x020200
dev_kfree_skb(skb);
+#else
+ dev_kfree_skb(skb, FREE_WRITE);
+#endif
return retval;
}
/* return pointer to Ethernet statistics */
-static struct enet_statistics *skmca_stats(struct net_device *dev)
+static struct enet_statistics *skmca_stats(struct SKMCA_NETDEV *dev)
{
skmca_priv *priv = (skmca_priv *) dev->priv;
+
return &(priv->stat);
}
/* we don't support runtime reconfiguration, since an MCA card can
be unambigously identified by its POS registers. */
-static int skmca_config(struct net_device *dev, struct ifmap *map)
+static int skmca_config(struct SKMCA_NETDEV *dev, struct ifmap *map)
{
return 0;
}
/* switch receiver mode. We use the LANCE's multicast filter to prefilter
multicast addresses. */
-static void skmca_set_multicast_list(struct net_device *dev)
+static void skmca_set_multicast_list(struct SKMCA_NETDEV *dev)
{
LANCE_InitBlock block;
StopLANCE(dev);
/* ...then modify the initialization block... */
- isa_memcpy_fromio(&block, dev->mem_start + RAM_INITBASE,
- sizeof(block));
+ SKMCA_FROMIO(&block, dev->mem_start + RAM_INITBASE, sizeof(block));
if (dev->flags & IFF_PROMISC)
block.Mode |= LANCE_INIT_PROM;
else
}
}
- isa_memcpy_toio(dev->mem_start + RAM_INITBASE, &block,
- sizeof(block));
+ SKMCA_TOIO(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
/* ...then reinit LANCE with the correct flags */
InitLANCE(dev);
* hardware check
* ------------------------------------------------------------------------ */
-#ifdef MODULE
static int startslot; /* counts through slots when probing multiple devices */
-#else
-#define startslot 0 /* otherwise a dummy, since there is only eth0 in-kern */
-#endif
-int skmca_probe(struct net_device *dev)
+int skmca_probe(struct SKMCA_NETDEV *dev)
{
int force_detect = 0;
int junior, slot, i;
/* can't work without an MCA bus ;-) */
if (MCA_bus == 0)
- return ENODEV;
+ return -ENODEV;
/* start address of 1 --> forced detection */
getaddrs(slot, junior, &base, &irq, &medium);
-#if LINUX_VERSION_CODE >= 0x020200
+#if LINUX_VERSION_CODE >= 0x020300
/* slot already in use ? */
if (mca_is_adapter_used(slot)) {
priv->ioregaddr = base + 0x3ff0;
priv->ctrladdr = base + 0x3ff2;
priv->cmdaddr = base + 0x3ff3;
- priv->realirq = irq;
priv->medium = medium;
memset(&(priv->stat), 0, sizeof(struct enet_statistics));
dev->mem_start = base;
dev->mem_end = base + 0x4000;
+ /* autoprobe ? */
+ if (irq < 0) {
+ int nirq;
+
+ printk
+ ("%s: ambigous POS bit combination, must probe for IRQ...\n",
+ dev->name);
+ nirq = ProbeIRQ(dev);
+ if (nirq <= 0)
+ printk("%s: IRQ probe failed, assuming IRQ %d",
+ dev->name, priv->realirq = -irq);
+ else
+ priv->realirq = nirq;
+ } else
+ priv->realirq = irq;
+
/* set methods */
dev->open = skmca_open;
dev->stop = skmca_close;
/* copy out MAC address */
for (i = 0; i < 6; i++)
- dev->dev_addr[i] = readb(priv->macbase + (i << 1));
+ dev->dev_addr[i] = SKMCA_READB(priv->macbase + (i << 1));
/* print config */
printk("%s: IRQ %d, memory %#lx-%#lx, "
ResetBoard(dev);
-#ifdef MODULE
startslot = slot + 1;
-#endif
return 0;
}
#define DEVMAX 5
-static struct net_device moddevs[DEVMAX] =
- { {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
-{"", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
-{"", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
-{"", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
-{"", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}
+#if (LINUX_VERSION_CODE >= 0x020369)
+static struct SKMCA_NETDEV moddevs[DEVMAX] =
+ { {" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{" ", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}
};
+#else
+static char NameSpace[8 * DEVMAX];
+static struct SKMCA_NETDEV moddevs[DEVMAX] =
+ { {NameSpace + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{NameSpace + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{NameSpace + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{NameSpace + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+{NameSpace + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}
+};
+#endif
int irq = 0;
int io = 0;
void cleanup_module(void)
{
- struct net_device *dev;
+ struct SKMCA_NETDEV *dev;
skmca_priv *priv;
int z;
#ifdef _SK_MCA_DRIVER_
+/* version-dependent functions/structures */
+
+#if LINUX_VERSION_CODE >= 0x020318
+#define SKMCA_READB(addr) isa_readb(addr)
+#define SKMCA_READW(addr) isa_readw(addr)
+#define SKMCA_WRITEB(data, addr) isa_writeb(data, addr)
+#define SKMCA_WRITEW(data, addr) isa_writew(data, addr)
+#define SKMCA_TOIO(dest, src, len) isa_memcpy_toio(dest, src, len)
+#define SKMCA_FROMIO(dest, src, len) isa_memcpy_fromio(dest, src, len)
+#define SKMCA_SETIO(dest, val, len) isa_memset_io(dest, val, len)
+#define SKMCA_NETDEV net_device
+#else
+#define SKMCA_READB(addr) readb(addr)
+#define SKMCA_READW(addr) readw(addr)
+#define SKMCA_WRITEB(data, addr) writeb(data, addr)
+#define SKMCA_WRITEW(data, addr) writew(data, addr)
+#define SKMCA_TOIO(dest, src, len) memcpy_toio(dest, src, len)
+#define SKMCA_FROMIO(dest, src, len) memcpy_fromio(dest, src, len)
+#define SKMCA_SETIO(dest, val, len) memset_io(dest, val, len)
+#define SKMCA_NETDEV device
+#endif
+
/* Adapter ID's */
#define SKNET_MCA_ID 0x6afd
#define SKNET_JUNIOR_MCA_ID 0x6be9
/* media enumeration - defined in a way that it fits onto the MC2+'s
POS registers... */
-
-typedef enum {Media_10Base2, Media_10BaseT,
- Media_10Base5, Media_Unknown, Media_Count} skmca_medium;
+
+typedef enum { Media_10Base2, Media_10BaseT,
+ Media_10Base5, Media_Unknown, Media_Count
+} skmca_medium;
/* private structure */
-typedef struct
- {
- unsigned int slot; /* MCA-Slot-# */
- unsigned int macbase; /* base address of MAC address PROM */
- unsigned int ioregaddr; /* address of I/O-register (Lo) */
- unsigned int ctrladdr; /* address of control/stat register */
- unsigned int cmdaddr; /* address of I/O-command register */
- int nextrx; /* index of next RX descriptor to
- be read */
- int nexttxput; /* index of next free TX descriptor */
- int nexttxdone; /* index of next TX descriptor to
- be finished */
- int txbusy; /* # of busy TX descriptors */
- struct enet_statistics stat; /* packet statistics */
- int realirq; /* memorizes actual IRQ, even when
- currently not allocated */
- skmca_medium medium; /* physical cannector */
- } skmca_priv;
+typedef struct {
+ unsigned int slot; /* MCA-Slot-# */
+ unsigned int macbase; /* base address of MAC address PROM */
+ unsigned int ioregaddr; /* address of I/O-register (Lo) */
+ unsigned int ctrladdr; /* address of control/stat register */
+ unsigned int cmdaddr; /* address of I/O-command register */
+ int nextrx; /* index of next RX descriptor to
+ be read */
+ int nexttxput; /* index of next free TX descriptor */
+ int nexttxdone; /* index of next TX descriptor to
+ be finished */
+ int txbusy; /* # of busy TX descriptors */
+ struct enet_statistics stat; /* packet statistics */
+ int realirq; /* memorizes actual IRQ, even when
+ currently not allocated */
+ skmca_medium medium; /* physical cannector */
+} skmca_priv;
/* card registers: control/status register bits */
-#define CTRL_ADR_DATA 0 /* Bit 0 = 0 ->access data register */
-#define CTRL_ADR_RAP 1 /* Bit 0 = 1 ->access RAP register */
-#define CTRL_RW_WRITE 0 /* Bit 1 = 0 ->write register */
-#define CTRL_RW_READ 2 /* Bit 1 = 1 ->read register */
-#define CTRL_RESET_ON 0 /* Bit 3 = 0 ->reset board */
-#define CTRL_RESET_OFF 8 /* Bit 3 = 1 ->no reset of board */
+#define CTRL_ADR_DATA 0 /* Bit 0 = 0 ->access data register */
+#define CTRL_ADR_RAP 1 /* Bit 0 = 1 ->access RAP register */
+#define CTRL_RW_WRITE 0 /* Bit 1 = 0 ->write register */
+#define CTRL_RW_READ 2 /* Bit 1 = 1 ->read register */
+#define CTRL_RESET_ON 0 /* Bit 3 = 0 ->reset board */
+#define CTRL_RESET_OFF 8 /* Bit 3 = 1 ->no reset of board */
-#define STAT_ADR_DATA 0 /* Bit 0 of ctrl register read back */
+#define STAT_ADR_DATA 0 /* Bit 0 of ctrl register read back */
#define STAT_ADR_RAP 1
-#define STAT_RW_WRITE 0 /* Bit 1 of ctrl register read back */
+#define STAT_RW_WRITE 0 /* Bit 1 of ctrl register read back */
#define STAT_RW_READ 2
-#define STAT_RESET_ON 0 /* Bit 3 of ctrl register read back */
+#define STAT_RESET_ON 0 /* Bit 3 of ctrl register read back */
#define STAT_RESET_OFF 8
-#define STAT_IRQ_ACT 0 /* interrupt pending */
-#define STAT_IRQ_NOACT 16 /* no interrupt pending */
-#define STAT_IO_NOBUSY 0 /* no transfer busy */
-#define STAT_IO_BUSY 32 /* transfer busy */
+#define STAT_IRQ_ACT 0 /* interrupt pending */
+#define STAT_IRQ_NOACT 16 /* no interrupt pending */
+#define STAT_IO_NOBUSY 0 /* no transfer busy */
+#define STAT_IO_BUSY 32 /* transfer busy */
/* I/O command register bits */
-#define IOCMD_GO 128 /* Bit 7 = 1 -> start register xfer */
+#define IOCMD_GO 128 /* Bit 7 = 1 -> start register xfer */
/* LANCE registers */
-#define LANCE_CSR0 0 /* Status/Control */
-
-#define CSR0_ERR 0x8000 /* general error flag */
-#define CSR0_BABL 0x4000 /* transmitter timeout */
-#define CSR0_CERR 0x2000 /* collision error */
-#define CSR0_MISS 0x1000 /* lost Rx block */
-#define CSR0_MERR 0x0800 /* memory access error */
-#define CSR0_RINT 0x0400 /* receiver interrupt */
-#define CSR0_TINT 0x0200 /* transmitter interrupt */
-#define CSR0_IDON 0x0100 /* initialization done */
-#define CSR0_INTR 0x0080 /* general interrupt flag */
-#define CSR0_INEA 0x0040 /* interrupt enable */
-#define CSR0_RXON 0x0020 /* receiver enabled */
-#define CSR0_TXON 0x0010 /* transmitter enabled */
-#define CSR0_TDMD 0x0008 /* force transmission now */
-#define CSR0_STOP 0x0004 /* stop LANCE */
-#define CSR0_STRT 0x0002 /* start LANCE */
-#define CSR0_INIT 0x0001 /* read initialization block */
-
-#define LANCE_CSR1 1 /* addr bit 0..15 of initialization */
-#define LANCE_CSR2 2 /* 16..23 block */
-
-#define LANCE_CSR3 3 /* Bus control */
-#define CSR3_BCON_HOLD 0 /* Bit 0 = 0 -> BM1,BM0,HOLD */
-#define CSR3_BCON_BUSRQ 1 /* Bit 0 = 1 -> BUSAK0,BYTE,BUSRQ */
-#define CSR3_ALE_HIGH 0 /* Bit 1 = 0 -> ALE asserted high */
-#define CSR3_ALE_LOW 2 /* Bit 1 = 1 -> ALE asserted low */
-#define CSR3_BSWAP_OFF 0 /* Bit 2 = 0 -> no byte swap */
-#define CSR3_BSWAP_ON 0 /* Bit 2 = 1 -> byte swap */
+#define LANCE_CSR0 0 /* Status/Control */
+
+#define CSR0_ERR 0x8000 /* general error flag */
+#define CSR0_BABL 0x4000 /* transmitter timeout */
+#define CSR0_CERR 0x2000 /* collision error */
+#define CSR0_MISS 0x1000 /* lost Rx block */
+#define CSR0_MERR 0x0800 /* memory access error */
+#define CSR0_RINT 0x0400 /* receiver interrupt */
+#define CSR0_TINT 0x0200 /* transmitter interrupt */
+#define CSR0_IDON 0x0100 /* initialization done */
+#define CSR0_INTR 0x0080 /* general interrupt flag */
+#define CSR0_INEA 0x0040 /* interrupt enable */
+#define CSR0_RXON 0x0020 /* receiver enabled */
+#define CSR0_TXON 0x0010 /* transmitter enabled */
+#define CSR0_TDMD 0x0008 /* force transmission now */
+#define CSR0_STOP 0x0004 /* stop LANCE */
+#define CSR0_STRT 0x0002 /* start LANCE */
+#define CSR0_INIT 0x0001 /* read initialization block */
+
+#define LANCE_CSR1 1 /* addr bit 0..15 of initialization */
+#define LANCE_CSR2 2 /* 16..23 block */
+
+#define LANCE_CSR3 3 /* Bus control */
+#define CSR3_BCON_HOLD 0 /* Bit 0 = 0 -> BM1,BM0,HOLD */
+#define CSR3_BCON_BUSRQ 1 /* Bit 0 = 1 -> BUSAK0,BYTE,BUSRQ */
+#define CSR3_ALE_HIGH 0 /* Bit 1 = 0 -> ALE asserted high */
+#define CSR3_ALE_LOW 2 /* Bit 1 = 1 -> ALE asserted low */
+#define CSR3_BSWAP_OFF 0 /* Bit 2 = 0 -> no byte swap */
+#define CSR3_BSWAP_ON 0 /* Bit 2 = 1 -> byte swap */
/* LANCE structures */
-typedef struct /* LANCE initialization block */
- {
- u16 Mode; /* mode flags */
- u8 PAdr[6]; /* MAC address */
- u8 LAdrF[8]; /* Multicast filter */
- u32 RdrP; /* Receive descriptor */
- u32 TdrP; /* Transmit descriptor */
- } LANCE_InitBlock;
-
+typedef struct { /* LANCE initialization block */
+ u16 Mode; /* mode flags */
+ u8 PAdr[6]; /* MAC address */
+ u8 LAdrF[8]; /* Multicast filter */
+ u32 RdrP; /* Receive descriptor */
+ u32 TdrP; /* Transmit descriptor */
+} LANCE_InitBlock;
+
/* Mode flags init block */
-#define LANCE_INIT_PROM 0x8000 /* enable promiscous mode */
-#define LANCE_INIT_INTL 0x0040 /* internal loopback */
-#define LANCE_INIT_DRTY 0x0020 /* disable retry */
-#define LANCE_INIT_COLL 0x0010 /* force collision */
-#define LANCE_INIT_DTCR 0x0008 /* disable transmit CRC */
-#define LANCE_INIT_LOOP 0x0004 /* loopback */
-#define LANCE_INIT_DTX 0x0002 /* disable transmitter */
-#define LANCE_INIT_DRX 0x0001 /* disable receiver */
-
-typedef struct /* LANCE Tx descriptor */
- {
- u16 LowAddr; /* bit 0..15 of address */
- u16 Flags; /* bit 16..23 of address + Flags */
- u16 Len; /* 2s complement of packet length */
- u16 Status; /* Result of transmission */
- } LANCE_TxDescr;
-
-#define TXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
-#define TXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
-#define TXDSCR_FLAGS_MORE 0x1000 /* more than one retry needed? */
-#define TXDSCR_FLAGS_ONE 0x0800 /* one retry? */
-#define TXDSCR_FLAGS_DEF 0x0400 /* transmission deferred? */
-#define TXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
-#define TXDSCR_FLAGS_ENP 0x0100 /* last packet in chain? */
-
-#define TXDSCR_STATUS_BUFF 0x8000 /* buffer error? */
-#define TXDSCR_STATUS_UFLO 0x4000 /* silo underflow during transmit? */
-#define TXDSCR_STATUS_LCOL 0x1000 /* late collision? */
-#define TXDSCR_STATUS_LCAR 0x0800 /* loss of carrier? */
-#define TXDSCR_STATUS_RTRY 0x0400 /* retry error? */
-
-typedef struct /* LANCE Rx descriptor */
- {
- u16 LowAddr; /* bit 0..15 of address */
- u16 Flags; /* bit 16..23 of address + Flags */
- u16 MaxLen; /* 2s complement of buffer length */
- u16 Len; /* packet length */
- } LANCE_RxDescr;
-
-#define RXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
-#define RXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
-#define RXDSCR_FLAGS_FRAM 0x2000 /* framing error flag */
-#define RXDSCR_FLAGS_OFLO 0x1000 /* FIFO overflow? */
-#define RXDSCR_FLAGS_CRC 0x0800 /* CRC error? */
-#define RXDSCR_FLAGS_BUFF 0x0400 /* buffer error? */
-#define RXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
-#define RXDCSR_FLAGS_ENP 0x0100 /* last packet in chain? */
+#define LANCE_INIT_PROM 0x8000 /* enable promiscous mode */
+#define LANCE_INIT_INTL 0x0040 /* internal loopback */
+#define LANCE_INIT_DRTY 0x0020 /* disable retry */
+#define LANCE_INIT_COLL 0x0010 /* force collision */
+#define LANCE_INIT_DTCR 0x0008 /* disable transmit CRC */
+#define LANCE_INIT_LOOP 0x0004 /* loopback */
+#define LANCE_INIT_DTX 0x0002 /* disable transmitter */
+#define LANCE_INIT_DRX 0x0001 /* disable receiver */
+
+typedef struct { /* LANCE Tx descriptor */
+ u16 LowAddr; /* bit 0..15 of address */
+ u16 Flags; /* bit 16..23 of address + Flags */
+ u16 Len; /* 2s complement of packet length */
+ u16 Status; /* Result of transmission */
+} LANCE_TxDescr;
+
+#define TXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
+#define TXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
+#define TXDSCR_FLAGS_MORE 0x1000 /* more than one retry needed? */
+#define TXDSCR_FLAGS_ONE 0x0800 /* one retry? */
+#define TXDSCR_FLAGS_DEF 0x0400 /* transmission deferred? */
+#define TXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
+#define TXDSCR_FLAGS_ENP 0x0100 /* last packet in chain? */
+
+#define TXDSCR_STATUS_BUFF 0x8000 /* buffer error? */
+#define TXDSCR_STATUS_UFLO 0x4000 /* silo underflow during transmit? */
+#define TXDSCR_STATUS_LCOL 0x1000 /* late collision? */
+#define TXDSCR_STATUS_LCAR 0x0800 /* loss of carrier? */
+#define TXDSCR_STATUS_RTRY 0x0400 /* retry error? */
+
+typedef struct { /* LANCE Rx descriptor */
+ u16 LowAddr; /* bit 0..15 of address */
+ u16 Flags; /* bit 16..23 of address + Flags */
+ u16 MaxLen; /* 2s complement of buffer length */
+ u16 Len; /* packet length */
+} LANCE_RxDescr;
+
+#define RXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
+#define RXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
+#define RXDSCR_FLAGS_FRAM 0x2000 /* framing error flag */
+#define RXDSCR_FLAGS_OFLO 0x1000 /* FIFO overflow? */
+#define RXDSCR_FLAGS_CRC 0x0800 /* CRC error? */
+#define RXDSCR_FLAGS_BUFF 0x0400 /* buffer error? */
+#define RXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
+#define RXDCSR_FLAGS_ENP 0x0100 /* last packet in chain? */
/* RAM layout */
-#define TXCOUNT 4 /* length of TX descriptor queue */
-#define LTXCOUNT 2 /* log2 of it */
-#define RXCOUNT 4 /* length of RX descriptor queue */
-#define LRXCOUNT 2 /* log2 of it */
+#define TXCOUNT 4 /* length of TX descriptor queue */
+#define LTXCOUNT 2 /* log2 of it */
+#define RXCOUNT 4 /* length of RX descriptor queue */
+#define LRXCOUNT 2 /* log2 of it */
-#define RAM_INITBASE 0 /* LANCE init block */
-#define RAM_TXBASE 24 /* Start of TX descriptor queue */
+#define RAM_INITBASE 0 /* LANCE init block */
+#define RAM_TXBASE 24 /* Start of TX descriptor queue */
#define RAM_RXBASE \
-(RAM_TXBASE + (TXCOUNT * 8)) /* Start of RX descriptor queue */
+(RAM_TXBASE + (TXCOUNT * 8)) /* Start of RX descriptor queue */
#define RAM_DATABASE \
-(RAM_RXBASE + (RXCOUNT * 8)) /* Start of data area for frames */
-#define RAM_BUFSIZE 1580 /* max. frame size - should never be
- reached */
+(RAM_RXBASE + (RXCOUNT * 8)) /* Start of data area for frames */
+#define RAM_BUFSIZE 1580 /* max. frame size - should never be
+ reached */
-#endif /* _SK_MCA_DRIVER_ */
+#endif /* _SK_MCA_DRIVER_ */
-extern int skmca_probe(struct net_device *);
+extern int skmca_probe(struct SKMCA_NETDEV *);
-#endif /* _SK_MCA_INCLUDE_ */
\ No newline at end of file
+#endif /* _SK_MCA_INCLUDE_ */
pdev)) == 0) {
break;
}
+ if (pci_enable_device(pdev))
+ continue;
#ifndef MEM_MAPPED_IO
/* Verify that I/O enable bit is set (PCI slot is enabled) */
command &= ~PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, command);
- port = pdev->resource[0].start;
+ port = pci_resource_start(pdev, 0);
port = (unsigned long)ioremap(port, 0x4000);
if (!port){
int irq = dev ? dev->irq : 0;
if (!MCA_bus) {
- return ENODEV;
+ return -ENODEV;
}
if (base_addr || irq) {
reg4 = inb(ioaddr + 4) & 0x7f;
outb(reg4, ioaddr + 4);
- if (load_8390_module("wd.c"))
- return -ENOSYS;
-
printk(KERN_INFO "%s: Parameters: %#3x,", dev->name, ioaddr);
for (i = 0; i < 6; i++)
{
int this_dev, found = 0;
+ if (load_8390_module("wd.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
struct net_device *dev = &dev_ultra[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
printk(KERN_NOTICE "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
if (base_addr > 0x1ff) /* Check a single specified location. */
return ultra_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; ultra_portlist[i]; i++) {
int ioaddr = ultra_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
/* Check the ID nibble. */
if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
&& (idreg & 0xF0) != 0x40) /* SMC EtherEZ */
- return ENODEV;
+ return -ENODEV;
/* Select the station address register set. */
outb(reg4, ioaddr + 4);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
if ((checksum & 0xff) != 0xFF)
- return ENODEV;
-
- if (load_8390_module("smc-ultra.c"))
- return -ENOSYS;
+ return -ENODEV;
if (dev == NULL)
dev = init_etherdev(0, 0);
{
int this_dev, found = 0;
+ if (load_8390_module("smc-ultra.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
struct net_device *dev = &dev_ultra[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) return 0; /* Got at least one. */
+ unload_8390_module();
return -ENXIO;
}
found++;
kfree(dev->priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
\f
const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
int ioaddr, edge, media;
- if (!EISA_bus) return ENODEV;
+ if (!EISA_bus) return -ENODEV;
/* EISA spec allows for up to 16 slots, but 8 is typical. */
for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000)
if (ultra32_probe1(dev, ioaddr) == 0)
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
int __init ultra32_probe1(struct net_device *dev, int ioaddr)
/* Check the ID nibble. */
if ((idreg & 0xf0) != 0x20) /* SMC Ultra */
- return ENODEV;
+ return -ENODEV;
/* Select the station address register set. */
outb(reg4, ioaddr + 4);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
if ((checksum & 0xff) != 0xff)
- return ENODEV;
-
- if (load_8390_module("smc-ultra32.c"))
- return -ENOSYS;
+ return -ENODEV;
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
printk("\nsmc-ultra32: Card RAM is disabled! "
"Run EISA config utility.\n");
- return ENODEV;
+ return -ENODEV;
}
if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. "
{
int this_dev, found = 0;
+ if (load_8390_module("smc-ultra32.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
struct net_device *dev = &dev_ultra[this_dev];
dev->init = ultra32_probe;
if (register_netdev(dev) != 0) {
if (found > 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
kfree(priv);
}
}
- unlock_8390_module();
+ unload_8390_module();
}
#endif /* MODULE */
. Fixed bug reported by Gardner Buchanan in
. smc_enable, with outw instead of outb
. 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert
+ . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory
+ . allocation
----------------------------------------------------------------------------*/
static const char *version =
- "smc9194.c:v0.12 03/06/96 by Erik Stahlman (erik@vt.edu)\n";
+ "smc9194.c:v0.13 04/14/00 by Erik Stahlman (erik@vt.edu)\n";
#include <linux/module.h>
#include <linux/version.h>
length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
/*
- . the MMU wants the number of pages to be the number of 256 bytes
- . 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
+ ** The MMU wants the number of pages to be the number of 256 bytes
+ ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
+ **
+ ** Pkt size for allocating is data length +6 (for additional status words,
+ ** length and ctl!) If odd size last byte is included in this header.
*/
- numPages = length / 256;
+ numPages = ((length & 0xfffe) + 6) / 256;
if (numPages > 7 ) {
printk(CARDNAME": Far too big packet error. \n");
// if (sonic_request_irq(dev->irq, &sonic_interrupt, 0, "sonic", dev)) {
if (sonic_request_irq(dev->irq, &sonic_interrupt, SA_INTERRUPT, "sonic", dev)) {
printk ("\n%s: unable to get IRQ %d .\n", dev->name, dev->irq);
- return EAGAIN;
+ return -EAGAIN;
}
/*
*(vhalf *) PA_83902_RST = 0;
udelay (5);
if (ei_debug > 1)
- printk("8390 reset done (%ld).", jiffies);
+ printk("8390 reset done (%ld).\n", jiffies);
*(vhalf *) PA_83902_RST = ~0;
udelay (5);
}
stnic_block_output (struct net_device *dev, int length,
const unsigned char *buf, int output_page)
{
-
+#if 0
STNIC_WRITE (PG0_RBCR0, 1);
STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+#else /* XXX: I don't know why but this works. -- gniibe */
+ STNIC_WRITE (PG0_RBCR0, 0x42);
+ STNIC_WRITE (PG0_RBCR1, 0x00);
+ STNIC_WRITE (PG0_RBCR0, 0x42);
+ STNIC_WRITE (PG0_RBCR1, 0x00);
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+ STNIC_DELAY ();
+#endif
STNIC_WRITE (PG0_RSAR0, 0);
STNIC_WRITE (PG0_RSAR1, output_page);
}
module_init(stnic_probe);
+/* No cleanup routine - if there were one, it should do a:
+ unload_8390_module()
+*/
/* Return "not found", so that dev_init() will unlink
* the placeholder device entry for us.
*/
- return ENODEV;
+ return -ENODEV;
#endif
}
-/* $Id: sunbmac.c,v 1.18 2000/02/18 13:49:21 davem Exp $
+/* $Id: sunbmac.c,v 1.19 2000/06/19 06:24:46 davem Exp $
* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters.
*
* Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
for_each_sbus(sbus) {
}
}
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
-/* $Id: sunhme.c,v 1.95 2000/03/25 05:18:15 davem Exp $
+/* $Id: sunhme.c,v 1.96 2000/06/09 07:35:27 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
if (is_qfe) {
qp = quattro_sbus_find(sdev);
if (qp == NULL)
- return ENODEV;
+ return -ENODEV;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
if (qp->happy_meals[qfe_slot] == NULL)
break;
if (qfe_slot == 4)
- return ENODEV;
+ return -ENODEV;
}
if (dev == NULL) {
dev = init_etherdev(0, sizeof(struct happy_meal));
printk(KERN_ERR "happymeal: Device does not have 5 regs, it has %d.\n",
sdev->num_registers);
printk(KERN_ERR "happymeal: Would you like that for here or to go?\n");
- return ENODEV;
+ return -ENODEV;
}
if (qp != NULL) {
GREG_REG_SIZE, "HME Global Regs");
if (!hp->gregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal global registers.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->etxregs = sbus_ioremap(&sdev->resource[1], 0,
ETX_REG_SIZE, "HME TX Regs");
if (!hp->etxregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Transmit registers.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->erxregs = sbus_ioremap(&sdev->resource[2], 0,
ERX_REG_SIZE, "HME RX Regs");
if (!hp->erxregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Receive registers.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->bigmacregs = sbus_ioremap(&sdev->resource[3], 0,
BMAC_REG_SIZE, "HME BIGMAC Regs");
if (!hp->bigmacregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal BIGMAC registers.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->tcvregs = sbus_ioremap(&sdev->resource[4], 0,
TCVR_REG_SIZE, "HME Tranceiver Regs");
if (!hp->tcvregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal Tranceiver registers.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->hm_revision = prom_getintdefault(sdev->prom_node, "hm-rev", 0xff);
pcp = pdev->sysdata;
if (pcp == NULL || pcp->prom_node == -1) {
printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n");
- return ENODEV;
+ return -ENODEV;
}
node = pcp->prom_node;
if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) {
qp = quattro_pci_find(pdev);
if (qp == NULL)
- return ENODEV;
+ return -ENODEV;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
if (qp->happy_meals[qfe_slot] == NULL)
break;
if (qfe_slot == 4)
- return ENODEV;
+ return -ENODEV;
}
if (dev == NULL) {
dev = init_etherdev(0, sizeof(struct happy_meal));
qp->happy_meals[qfe_slot] = dev;
}
- hpreg_base = pdev->resource[0].start;
+ hpreg_base = pci_resource_start(pdev, 0);
if ((pdev->resource[0].flags & IORESOURCE_IO) != 0) {
printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
- return ENODEV;
+ return -ENODEV;
}
- hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK;
hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000);
if (qfe_slot != -1 && prom_getproplen(node, "local-mac-address") == 6)
if (!hp->happy_block) {
printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n");
- return ENODEV;
+ return -ENODEV;
}
hp->linkcheck = 0;
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
cards = 0;
cards += happy_meal_pci_probe(dev);
#endif
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
-/* $Id: sunlance.c,v 1.100 2000/02/27 09:38:12 anton Exp $
+/* $Id: sunlance.c,v 1.101 2000/06/19 06:24:46 davem Exp $
* lance.c: Linux/Sparc/Lance driver
*
* Written 1995, 1996 by Miguel de Icaza
fail:
if (lp != NULL)
lance_free_hwresources(lp);
- return ENODEV;
+ return -ENODEV;
}
/* On 4m, find the associated dma for the lance chip */
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) ||
sdev.irqs[0] = 6;
return sparc_lance_init(NULL, &sdev, 0, 0);
}
- return ENODEV;
+ return -ENODEV;
}
#else /* !CONFIG_SUN4 */
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
for_each_sbus (bus) {
} /* for each sbusdev */
} /* for each sbus */
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
#endif /* !CONFIG_SUN4 */
-/* $Id: sunqe.c,v 1.45 2000/02/16 10:36:20 davem Exp $
+/* $Id: sunqe.c,v 1.46 2000/06/19 06:24:46 davem Exp $
* sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver.
* Once again I am out to prove that every ethernet
* controller out there can be most efficiently programmed
#endif
if (called)
- return ENODEV;
+ return -ENODEV;
called++;
for_each_sbus(bus) {
}
}
if (!cards)
- return ENODEV;
+ return -ENODEV;
return 0;
}
* - TODO: Port completely to new PCI/DMA API
* Auto-Neg fallback.
*
+ * v1.6 April 04, 2000 - Fixed driver support for kernel-parameters. Haven't
+ * tested it though, as the kernel support is currently
+ * broken (2.3.99p4p3).
+ * - Updated tlan.txt accordingly.
+ * - Adjusted minimum/maximum frame length.
+ * - There is now a TLAN website up at
+ * http://tlan.kernel.dk
+ *
+ * v1.7 April 07, 2000 - Started to implement custom ioctls. Driver now
+ * reports PHY information when used with Donald
+ * Beckers userspace MII diagnostics utility.
+ *
+ * v1.8 April 23, 2000 - Fixed support for forced speed/duplex settings.
+ * - Added link information to Auto-Neg and forced
+ * modes. When NIC operates with auto-neg the driver
+ * will report Link speed & duplex modes as well as
+ * link partner abilities. When forced link is used,
+ * the driver will report status of the established
+ * link.
+ * Please read tlan.txt for additional information.
+ * - Removed call to check_region(), and used
+ * return value of request_region() instead.
+ *
+ * v1.8a May 28, 2000 - Minor updates.
+ *
*******************************************************************************/
static int duplex = 0;
static int speed = 0;
+MODULE_AUTHOR("Maintainer: Torben Mathiasen <torben.mathiasen@compaq.com>");
+MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
MODULE_PARM(aui, "i");
MODULE_PARM(duplex, "i");
MODULE_PARM(speed, "i");
static int bbuf = 0;
static u8 *TLanPadBuffer;
static char TLanSignature[] = "TLAN";
-static int TLanVersionMajor = 1;
-static int TLanVersionMinor = 5;
+static const char *tlan_banner = "ThunderLAN driver v1.8a\n";
+
+const char *media[] = {
+ "10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ",
+ "100baseTx-FD", "100baseT4", 0
+};
+int media_map[] = { 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,};
static TLanAdapterEntry TLanAdapterList[] __initdata = {
{ PCI_VENDOR_ID_COMPAQ,
static int TLan_Close(struct net_device *);
static struct net_device_stats *TLan_GetStats( struct net_device * );
static void TLan_SetMulticastList( struct net_device * );
+static int TLan_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static u32 TLan_HandleInvalid( struct net_device *, u16 );
static u32 TLan_HandleTxEOF( struct net_device *, u16 );
u32 io_base, index;
int found;
- printk(KERN_INFO "ThunderLAN driver v%d.%d\n",
- TLanVersionMajor,
- TLanVersionMinor);
+ printk(KERN_INFO "%s", tlan_banner);
TLanPadBuffer = (u8 *) kmalloc(TLAN_MIN_FRAME_SIZE,
(GFP_KERNEL | GFP_DMA));
dev->irq = irq;
priv->adapter = &TLanAdapterList[index];
priv->adapterRev = rev;
- priv->aui = aui;
- if ( ( duplex != 1 ) && ( duplex != 2 ) )
- duplex = 0;
- priv->duplex = duplex;
+
+ /* Kernel parameters */
+ if (dev->mem_start) {
+ priv->aui = dev->mem_start & 0x01;
+ priv->duplex = ((dev->mem_start & 0x06) == 0x06) ? 0 : (dev->mem_start & 0x06) >> 1;
+ priv->speed = ((dev->mem_start & 0x18) == 0x18) ? 0 : (dev->mem_start & 0x18) >> 3;
+
+ if (priv->speed == 0x1) {
+ priv->speed = TLAN_SPEED_10;
+ } else if (priv->speed == 0x2) {
+ priv->speed = TLAN_SPEED_100;
+ }
+ debug = priv->debug = dev->mem_end;
+ } else {
- if ( ( speed != 10 ) && ( speed != 100 ) )
- speed = 0;
+ if ( ( duplex != 1 ) && ( duplex != 2 ) )
+ duplex = 0;
+
+ priv->duplex = duplex;
- priv->speed = speed;
- priv->debug = debug;
+ if ( ( speed != 10 ) && ( speed != 100 ) )
+ speed = 0;
+
+ priv->aui = aui;
+ priv->speed = speed;
+ priv->debug = debug;
+
+ }
+
spin_lock_init(&priv->lock);
if (TLan_Init(dev)) {
TLanAdapterList[dl_index].deviceId
);
+ if (pci_enable_device(pdev))
+ continue;
+
*pci_irq = pdev->irq;
- *pci_io_base = pdev->resource[0].start;
+ *pci_io_base = pci_resource_start (pdev, 0);
*pci_dfn = pdev->devfn;
pci_read_config_byte ( pdev, PCI_REVISION_ID, pci_rev);
pci_read_config_word ( pdev, PCI_COMMAND, &pci_command);
static int TLan_Init( struct net_device *dev )
{
int dma_size;
- int err;
+ int err;
int i;
TLanPrivateInfo *priv;
priv = (TLanPrivateInfo *) dev->priv;
- err = check_region( dev->base_addr, 0x10 );
- if ( err ) {
+
+ if (!request_region( dev->base_addr, 0x10, TLanSignature )) {
printk(KERN_ERR "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n",
dev->name,
dev->base_addr,
return -EIO;
}
- request_region( dev->base_addr, 0x10, TLanSignature );
if ( bbuf ) {
dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
dev->stop = &TLan_Close;
dev->get_stats = &TLan_GetStats;
dev->set_multicast_list = &TLan_SetMulticastList;
-
+ dev->do_ioctl = &TLan_ioctl;
return 0;
+ /**************************************************************
+ * TLan_ioctl
+ *
+ * Returns:
+ * 0 on success, error code otherwise
+ * Params:
+ * dev structure of device to receive ioctl.
+ *
+ * rq ifreq structure to hold userspace data.
+ *
+ * cmd ioctl command.
+ *
+ *
+ *************************************************************/
+
+static int TLan_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+ u32 phy = priv->phy[priv->phyNum];
+
+ if (!priv->phyOnline)
+ return -EAGAIN;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE:
+ data[0] = phy;
+
+ case SIOCDEVPRIVATE+1: /* Read MII register */
+ TLan_MiiReadReg(dev, data[0], data[1], &data[3]);
+ return 0;
+
+ case SIOCDEVPRIVATE+2: /* Write MII register */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ TLan_MiiWriteReg(dev, data[0], data[1], data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+} /* tlan_ioctl */
+
+
/***************************************************************
* TLan_StartTx
u32 phy;
u8 sio;
u16 status;
+ u16 partner;
u16 tlphy_ctl;
+ u16 tlphy_par;
+ int i;
phy = priv->phy[priv->phyNum];
udelay( 1000 );
TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
if ( status & MII_GS_LINK ) {
- printk( "TLAN: %s: Link active.\n", dev->name );
+ TLan_MiiReadReg( dev, phy, MII_AN_LPA, &partner );
+ TLan_MiiReadReg( dev, phy, TLAN_TLPHY_PAR, &tlphy_par );
+
+ printk( "TLAN: %s: Link active with ", dev->name );
+ if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) {
+ printk( "forced 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? "" : "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ? "Full" : "Half");
+ } else {
+ printk( "AutoNegotiation enabled, at 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? "" : "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ? "Full" : "Half");
+
+ printk("TLAN: Partner capability: ");
+ for (i = 5; i <= 10; i++)
+ if (partner & (1<<i))
+ printk("%s", media[i-5]);
+ printk("\n");
+ }
+
TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
}
}
outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
} else {
- printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name );
+ printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name );
TLan_SetTimer( dev, (10*HZ), TLAN_TIMER_FINISH_RESET );
return;
}
TLan_MiiSync( dev->base_addr );
value = MII_GC_LOOPBK;
TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
-
+ TLan_MiiSync(dev->base_addr);
/* Wait for 500 ms and reset the
* tranceiver. The TLAN docs say both 50 ms and
- * 500 ms, so do the longer, just in case
+ * 500 ms, so do the longer, just in case.
*/
TLan_SetTimer( dev, (HZ/2), TLAN_TIMER_PHY_RESET );
while ( value & MII_GC_RESET ) {
TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
}
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
/* Wait for 500 ms and initialize.
* I don't remember why I wait this long.
+ * I've changed this to 50ms, as it seems long enough.
*/
- TLan_SetTimer( dev, (HZ/2), TLAN_TIMER_PHY_START_LINK );
+ TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_START_LINK );
} /* TLan_PhyReset */
u16 tctl;
phy = priv->phy[priv->phyNum];
-
TLAN_DBG( TLAN_DEBUG_GNRL, "%s: Trying to activate link.\n", dev->name );
TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+ TLan_MiiReadReg( dev, phy, MII_GEN_STS, &ability );
if ( ( status & MII_GS_AUTONEG ) &&
- ( priv->duplex == TLAN_DUPLEX_DEFAULT ) &&
- ( priv->speed == TLAN_SPEED_DEFAULT ) &&
( ! priv->aui ) ) {
- ability = status >> 11;
- if ( priv->speed == TLAN_SPEED_10 ) {
- ability &= 0x0003;
- } else if ( priv->speed == TLAN_SPEED_100 ) {
- ability &= 0x001C;
- }
-
- if ( priv->duplex == TLAN_DUPLEX_FULL ) {
- ability &= 0x000A;
- } else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
- ability &= 0x0005;
+ ability = status >> 11;
+ if ( priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x0000);
+ } else if ( priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x0100);
+ } else if ( priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x2000);
+ } else if ( priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x2100);
+ } else {
+
+ /* Set Auto-Neg advertisement */
+ TLan_MiiWriteReg( dev, phy, MII_AN_ADV, (ability << 5) | 1);
+ /* Enablee Auto-Neg */
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+ /* Restart Auto-Neg */
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+ /* Wait for 4 sec for autonegotiation
+ * to complete. The max spec time is less than this
+ * but the card need additional time to start AN.
+ * .5 sec should be plenty extra.
+ */
+ printk( "TLAN: %s: Starting autonegotiation.\n", dev->name );
+ TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_PHY_FINISH_AN );
+ return;
}
-
- TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
-
- /* Wait for 4 sec for autonegotiation
- * to complete. The max spec time is less than this
- * but the card need additional time to start AN.
- * .5 sec should be plenty extra.
- */
- printk( "TLAN: %s: Starting autonegotiation.\n", dev->name );
- TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_PHY_FINISH_AN );
- return;
- }
-
+
+ }
+
if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
priv->phyNum = 0;
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
- TLan_SetTimer( dev, (4*(HZ/1000)), TLAN_TIMER_PHY_PDOWN );
+ TLan_SetTimer( dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN );
return;
- } else if ( priv->phyNum == 0 ) {
+ } else if ( priv->phyNum == 0 ) {
TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
if ( priv->aui ) {
tctl |= TLAN_TC_AUISEL;
- } else {
+ } else {
tctl &= ~TLAN_TC_AUISEL;
control = 0;
if ( priv->duplex == TLAN_DUPLEX_FULL ) {
TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
}
- /* Wait for 1 sec to give the tranceiver time
+ /* Wait for 2 sec to give the tranceiver time
* to establish link.
*/
- TLan_SetTimer( dev, HZ, TLAN_TIMER_FINISH_RESET );
+ TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_FINISH_RESET );
} /* TLan_PhyStartLink */
priv->phyNum = 0;
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
- TLan_SetTimer( dev, (400*(HZ/1000)), TLAN_TIMER_PHY_PDOWN );
+ TLan_SetTimer( dev, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN );
return;
}
if ( priv->phyNum == 0 ) {
if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
- printk( "TLAN: Starting internal PHY with DUPLEX\n" );
+ printk( "TLAN: Starting internal PHY with FULL-DUPLEX\n" );
} else {
TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB );
printk( "TLAN: Starting internal PHY with HALF-DUPLEX\n" );
#define FALSE 0
#define TRUE 1
-#define TLAN_MIN_FRAME_SIZE 64
-#define TLAN_MAX_FRAME_SIZE 1600
+#define TLAN_MIN_FRAME_SIZE 60
+#define TLAN_MAX_FRAME_SIZE 1536
#define TLAN_NUM_RX_LISTS 4
#define TLAN_NUM_TX_LISTS 8
#define TLAN_TS_POLOK 0x2000
#define TLAN_TS_TPENERGY 0x1000
#define TLAN_TS_RESERVED 0x0FFF
-
+#define TLAN_TLPHY_PAR 0x19
+#define TLAN_PHY_CIM_STAT 0x0020
+#define TLAN_PHY_SPEED_100 0x0040
+#define TLAN_PHY_DUPLEX_FULL 0x0080
+#define TLAN_PHY_AN_EN_STAT 0x0400
#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
{
struct tok_info *ti=(struct tok_info *)dev->priv;
+ /* init the spinlock */
+ spin_lock_init(&ti->lock);
+
SET_PAGE(ti->srb_page);
ti->open_status = CLOSED;
{
struct tok_info *ti=(struct tok_info *)dev->priv;
- /* init the spinlock */
- spin_lock_init(&ti->lock);
-
if (ti->open_status==CLOSED) tok_init_card(dev);
if (ti->open_status==IN_PROGRESS) sleep_on(&ti->wait_for_reset);
/* Copy the payload... */
for (;;) {
if (IPv4_p)
- chksum = csum_partial_copy_generic(bus_to_virt(rbufdata), data,
+ chksum = csum_partial_copy_nocheck(bus_to_virt(rbufdata), data,
length < rbuffer_len ? length : rbuffer_len,
- chksum, NULL, NULL);
+ chksum);
else
isa_memcpy_fromio(data, rbufdata, rbuffer_len);
rbuffer = ntohs(isa_readw(rbuffer));
{
while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
{
+ if (pci_enable_device(pci_device))
+ continue;
pci_set_master(pci_device);
/* Check to see if io has been allocated, if so, we've already done this card,
so continue on the card discovery loop */
- if (check_region(pci_device->resource[0].start & (~3), STREAMER_IO_SPACE))
+ if (check_region(pci_resource_start(pci_device,0), STREAMER_IO_SPACE))
{
card_no++;
continue;
pci_device, dev, dev->priv);
#endif
dev->irq = pci_device->irq;
- dev->base_addr = pci_device->resource[0].start & (~3);
+ dev->base_addr = pci_resource_start(pci_device, 0);
dev->init = &streamer_init;
streamer_priv->streamer_card_name = (char *)pci_device->resource[0].name;
- streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
+ streamer_priv->streamer_mmio =
+ ioremap(pci_resource_start(pci_device, 1), 256);
if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
* 2/23/00 - Updated to dev_kfree_irq
* 3/10/00 - Fixed FDX enable which triggered other bugs also
* squashed.
+ * 5/20/00 - Changes to handle Olympic on LinuxPPC. Endian changes.
+ * The odd thing about the changes is that the fix for
+ * endian issues with the big-endian data in the arb, asb...
+ * was to always swab() the bytes, no matter what CPU.
+ * That's because the read[wl]() functions always swap the
+ * bytes on the way in on PPC.
+ * Fixing the hardware descriptors was another matter,
+ * because they weren't going through read[wl](), there all
+ * the results had to be in memory in le32 values. kdaaker
+ *
*
* To Do:
*
if (pci_present()) {
while((pci_device=pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR_WAKE, pci_device))) {
-
+ __u16 pci_command ;
+
+ if (pci_enable_device(pci_device))
+ continue;
+
+ /* These lines are needed by the PowerPC, it appears
+that these flags
+ * are not being set properly for the PPC, this may
+well be fixed with
+ * the new PCI code */
+ pci_read_config_word(pci_device, PCI_COMMAND, &pci_command);
+ pci_command |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_write_config_word(pci_device, PCI_COMMAND,pci_command);
pci_set_master(pci_device);
/* Check to see if io has been allocated, if so, we've already done this card,
so continue on the card discovery loop */
- if (check_region(pci_device->resource[0].start, OLYMPIC_IO_SPACE)) {
+ if (check_region(pci_resource_start(pci_device, 0), OLYMPIC_IO_SPACE)) {
card_no++ ;
continue ;
}
printk("pci_device: %p, dev:%p, dev->priv: %p\n", pci_device, dev, dev->priv);
#endif
dev->irq=pci_device->irq;
- dev->base_addr=pci_device->resource[0].start;
+ dev->base_addr=pci_resource_start(pci_device, 0);
dev->init=&olympic_init;
olympic_priv->olympic_card_name = (char *)pci_device->resource[0].name ;
- olympic_priv->olympic_mmio=ioremap(pci_device->resource[1].start,256);
- olympic_priv->olympic_lap=ioremap(pci_device->resource[2].start,2048);
+ olympic_priv->olympic_mmio =
+ ioremap(pci_resource_start(pci_device,1),256);
+ olympic_priv->olympic_lap =
+ ioremap(pci_resource_start(pci_device,2),2048);
if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000) )
olympic_priv->pkt_buf_sz = PKT_BUF_SZ ;
}
}
- uaa_addr=ntohs(readw(init_srb+8));
+ uaa_addr=swab16(readw(init_srb+8));
#if OLYMPIC_DEBUG
printk("UAA resides at %x\n",uaa_addr);
memcpy_fromio(&dev->dev_addr[0], adapter_addr,6);
- olympic_priv->olympic_addr_table_addr = ntohs(readw(init_srb + 12)) ;
- olympic_priv->olympic_parms_addr = ntohs(readw(init_srb + 14)) ;
+ olympic_priv->olympic_addr_table_addr = swab16(readw(init_srb + 12));
+ olympic_priv->olympic_parms_addr = swab16(readw(init_srb + 14));
return 0;
/* If Network Monitor, instruct card to copy MAC frames through the ARB */
#if OLYMPIC_NETWORK_MONITOR
- writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON),init_srb+8);
+ writew(swab16(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), init_srb+8);
#else
- writew(ntohs(OPEN_ADAPTER_ENABLE_FDX),init_srb+8);
+ writew(swab16(OPEN_ADAPTER_ENABLE_FDX), init_srb+8);
#endif
if (olympic_priv->olympic_laa[0]) {
#if OLYMPIC_DEBUG
printk("init_srb(%p): ",init_srb);
for(i=0;i<20;i++)
- printk("%x ",readb(init_srb+i));
+ printk("%02x ",readb(init_srb+i));
printk("\n");
#endif
if (olympic_priv->olympic_message_level)
printk(KERN_INFO "%s: Opened in %d Mbps mode\n",dev->name, olympic_priv->olympic_ring_speed);
- olympic_priv->asb=ntohs(readw(init_srb+8));
- olympic_priv->srb=ntohs(readw(init_srb+10));
- olympic_priv->arb=ntohs(readw(init_srb+12));
- olympic_priv->trb=ntohs(readw(init_srb+16));
+ olympic_priv->asb = swab16(readw(init_srb+8));
+ olympic_priv->srb = swab16(readw(init_srb+10));
+ olympic_priv->arb = swab16(readw(init_srb+12));
+ olympic_priv->trb = swab16(readw(init_srb+16));
olympic_priv->olympic_receive_options = 0x01 ;
olympic_priv->olympic_copy_all_options = 0 ;
skb->dev = dev;
- olympic_priv->olympic_rx_ring[i].buffer=virt_to_bus(skb->data);
- olympic_priv->olympic_rx_ring[i].res_length = olympic_priv->pkt_buf_sz ;
+ olympic_priv->olympic_rx_ring[i].buffer = cpu_to_le32(virt_to_bus(skb->data));
+ olympic_priv->olympic_rx_ring[i].res_length = cpu_to_le32(olympic_priv->pkt_buf_sz);
olympic_priv->rx_ring_skb[i]=skb;
}
return -EIO;
}
- writel(virt_to_bus(&olympic_priv->olympic_rx_ring[0]),olympic_mmio+RXDESCQ);
- writel(virt_to_bus(&olympic_priv->olympic_rx_ring[0]),olympic_mmio+RXCDA);
- writew(i,olympic_mmio+RXDESCQCNT);
+ writel(virt_to_bus(&olympic_priv->olympic_rx_ring[0]), olympic_mmio+RXDESCQ);
+ writel(virt_to_bus(&olympic_priv->olympic_rx_ring[0]), olympic_mmio+RXCDA);
+ writew(i, olympic_mmio+RXDESCQCNT);
- writel(virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]),olympic_mmio+RXSTATQ);
- writel(virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]),olympic_mmio+RXCSA);
+ writel(virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]), olympic_mmio+RXSTATQ);
+ writel(virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]), olympic_mmio+RXCSA);
- olympic_priv->rx_ring_last_received=OLYMPIC_RX_RING_SIZE-1; /* last processed rx status */
- olympic_priv->rx_status_last_received = OLYMPIC_RX_RING_SIZE-1;
+ olympic_priv->rx_ring_last_received = OLYMPIC_RX_RING_SIZE - 1; /* last processed rx status */
+ olympic_priv->rx_status_last_received = OLYMPIC_RX_RING_SIZE - 1;
- writew(i,olympic_mmio+RXSTATQCNT);
+ writew(i, olympic_mmio+RXSTATQCNT);
#if OLYMPIC_DEBUG
printk("# of rx buffers: %d, RXENQ: %x\n",i, readw(olympic_mmio+RXENQ));
olympic_priv->olympic_tx_ring[i].buffer=0xdeadbeef;
olympic_priv->free_tx_ring_entries=OLYMPIC_TX_RING_SIZE;
- writel(virt_to_bus(&olympic_priv->olympic_tx_ring[0]),olympic_mmio+TXDESCQ_1);
- writel(virt_to_bus(&olympic_priv->olympic_tx_ring[0]),olympic_mmio+TXCDA_1);
- writew(OLYMPIC_TX_RING_SIZE,olympic_mmio+TXDESCQCNT_1);
+ writel(virt_to_bus(&olympic_priv->olympic_tx_ring[0]), olympic_mmio+TXDESCQ_1);
+ writel(virt_to_bus(&olympic_priv->olympic_tx_ring[0]), olympic_mmio+TXCDA_1);
+ writew(OLYMPIC_TX_RING_SIZE, olympic_mmio+TXDESCQCNT_1);
writel(virt_to_bus(&olympic_priv->olympic_tx_status_ring[0]),olympic_mmio+TXSTATQ_1);
writel(virt_to_bus(&olympic_priv->olympic_tx_status_ring[0]),olympic_mmio+TXCSA_1);
rx_status=&(olympic_priv->olympic_rx_status_ring[(olympic_priv->rx_status_last_received + 1) & (OLYMPIC_RX_RING_SIZE - 1)]) ;
while (rx_status->status_buffercnt) {
+ __u32 l_status_buffercnt;
olympic_priv->rx_status_last_received++ ;
olympic_priv->rx_status_last_received &= (OLYMPIC_RX_RING_SIZE -1);
#if OLYMPIC_DEBUG
printk(" stat_ring addr: %x \n", &(olympic_priv->olympic_rx_status_ring[olympic_priv->rx_status_last_received]) );
- printk("rx status: %x rx len: %x \n",rx_status->status_buffercnt,rx_status->fragmentcnt_framelen);
+ printk("rx status: %x rx len: %x \n", le32_to_cpu(rx_status->status_buffercnt), le32_to_cpu(rx_status->fragmentcnt_framelen));
#endif
- length=rx_status->fragmentcnt_framelen & 0xffff;
- buffer_cnt = rx_status->status_buffercnt & 0xffff ;
+ length = le32_to_cpu(rx_status->fragmentcnt_framelen) & 0xffff;
+ buffer_cnt = le32_to_cpu(rx_status->status_buffercnt) & 0xffff;
i = buffer_cnt ; /* Need buffer_cnt later for rxenq update */
- frag_len = rx_status->fragmentcnt_framelen >> 16 ;
+ frag_len = le32_to_cpu(rx_status->fragmentcnt_framelen) >> 16;
#if OLYMPIC_DEBUG
- printk("length: %x, frag_len: %x, buffer_cnt: %x\n",length,frag_len,buffer_cnt);
+ printk("length: %x, frag_len: %x, buffer_cnt: %x\n", length, frag_len, buffer_cnt);
#endif
-
- if(rx_status->status_buffercnt & 0xC0000000) {
- if (rx_status->status_buffercnt & 0x3B000000) {
+ l_status_buffercnt = le32_to_cpu(rx_status->status_buffercnt);
+ if(l_status_buffercnt & 0xC0000000) {
+ if (l_status_buffercnt & 0x3B000000) {
if (olympic_priv->olympic_message_level) {
- if (rx_status->status_buffercnt & (1<<29)) /* Rx Frame Truncated */
+ if (l_status_buffercnt & (1<<29)) /* Rx Frame Truncated */
printk(KERN_WARNING "%s: Rx Frame Truncated \n",dev->name);
- if (rx_status->status_buffercnt & (1<<28)) /*Rx receive overrun */
+ if (l_status_buffercnt & (1<<28)) /*Rx receive overrun */
printk(KERN_WARNING "%s: Rx Frame Receive overrun \n",dev->name);
- if (rx_status->status_buffercnt & (1<<27)) /* No receive buffers */
+ if (l_status_buffercnt & (1<<27)) /* No receive buffers */
printk(KERN_WARNING "%s: No receive buffers \n",dev->name);
- if (rx_status->status_buffercnt & (1<<25)) /* Receive frame error detect */
+ if (l_status_buffercnt & (1<<25)) /* Receive frame error detect */
printk(KERN_WARNING "%s: Receive frame error detect \n",dev->name);
- if (rx_status->status_buffercnt & (1<<24)) /* Received Error Detect */
+ if (l_status_buffercnt & (1<<24)) /* Received Error Detect */
printk(KERN_WARNING "%s: Received Error Detect \n",dev->name);
}
olympic_priv->rx_ring_last_received += i ;
skb2=olympic_priv->rx_ring_skb[rx_ring_last_received] ;
skb_put(skb2,length);
skb2->protocol = tr_type_trans(skb2,dev);
- olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer=virt_to_bus(skb->data);
- olympic_priv->olympic_rx_ring[rx_ring_last_received].res_length = olympic_priv->pkt_buf_sz ;
+ olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer = cpu_to_le32(virt_to_bus(skb->data));
+ olympic_priv->olympic_rx_ring[rx_ring_last_received].res_length = cpu_to_le32(olympic_priv->pkt_buf_sz);
olympic_priv->rx_ring_skb[rx_ring_last_received] = skb ;
netif_rx(skb2) ;
} else {
olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1);
rx_ring_last_received = olympic_priv->rx_ring_last_received ;
rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]);
- cpy_length = (i == 1 ? frag_len : rx_desc->res_length);
- memcpy(skb_put(skb, cpy_length), bus_to_virt(rx_desc->buffer), cpy_length) ;
+ cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length));
+ memcpy(skb_put(skb, cpy_length), bus_to_virt(le32_to_cpu(rx_desc->buffer)), cpy_length) ;
} while (--i) ;
skb->protocol = tr_type_trans(skb,dev);
netif_stop_queue(dev);
if(olympic_priv->free_tx_ring_entries) {
- olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].buffer=virt_to_bus(skb->data);
- olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].status_length=skb->len | (0x80000000);
+ olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].buffer = cpu_to_le32(virt_to_bus(skb->data));
+ olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].status_length = cpu_to_le32(skb->len | (0x80000000));
olympic_priv->tx_ring_skb[olympic_priv->tx_ring_free]=skb;
olympic_priv->free_tx_ring_entries--;
if (readb(arb_block+0) == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */
header_len = readb(arb_block+8) ; /* 802.5 Token-Ring Header Length */
- frame_len = ntohs(readw(arb_block + 10)) ;
+ frame_len = swab16(readw(arb_block + 10)) ;
- buff_off = ntohs(readw(arb_block + 6)) ;
+ buff_off = swab16(readw(arb_block + 6)) ;
buf_ptr = olympic_priv->olympic_lap + buff_off ;
do {
frame_data = buf_ptr+offsetof(struct mac_receive_buffer,frame_data) ;
- buffer_len = ntohs(readw(buf_ptr+offsetof(struct mac_receive_buffer,buffer_length)));
+ buffer_len = swab16(readw(buf_ptr+offsetof(struct mac_receive_buffer,buffer_length)));
memcpy_fromio(skb_put(mac_frame, buffer_len), frame_data , buffer_len ) ;
next_ptr=readw(buf_ptr+offsetof(struct mac_receive_buffer,next));
return ;
} else if (readb(arb_block) == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */
- lan_status = ntohs(readw(arb_block+6));
+ lan_status = swab16(readw(arb_block+6));
fdx_prot_error = readb(arb_block+8) ;
/* Issue ARB Free */
readb(opt+offsetof(struct olympic_parameters_table, poll_addr)+3),
readb(opt+offsetof(struct olympic_parameters_table, poll_addr)+4),
readb(opt+offsetof(struct olympic_parameters_table, poll_addr)+5),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, acc_priority))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, auth_source_class))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, att_code))));
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, acc_priority))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, auth_source_class))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, att_code))));
size += sprintf(buffer+size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n",
dev->name) ;
readb(opt+offsetof(struct olympic_parameters_table, source_addr)+3),
readb(opt+offsetof(struct olympic_parameters_table, source_addr)+4),
readb(opt+offsetof(struct olympic_parameters_table, source_addr)+5),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, beacon_type))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, major_vector))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, lan_status))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, local_ring))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, mon_error))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, frame_correl))));
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_type))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, major_vector))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, lan_status))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, local_ring))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, mon_error))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, frame_correl))));
size += sprintf(buffer+size, "%6s: Beacon Details : Tx : Rx : NAUN Node Address : NAUN Node Phys : \n",
dev->name) ;
size += sprintf(buffer+size, "%6s: : %02x : %02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x : \n",
dev->name,
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, beacon_transmit))),
- ntohs(readw(opt+offsetof(struct olympic_parameters_table, beacon_receive))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_transmit))),
+ swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_receive))),
readb(opt+offsetof(struct olympic_parameters_table, beacon_naun)),
readb(opt+offsetof(struct olympic_parameters_table, beacon_naun)+1),
readb(opt+offsetof(struct olympic_parameters_table, beacon_naun)+2),
/* Olympic data structures */
+/* xxxx These structures are all little endian in hardware. */
+
struct olympic_tx_desc {
__u32 buffer;
__u32 status_length;
struct olympic_rx_desc {
__u32 buffer;
- __u32 res_length ;
+ __u32 res_length;
};
struct olympic_rx_status {
__u32 fragmentcnt_framelen;
__u32 status_buffercnt;
};
+/* xxxx END These structures are all little endian in hardware. */
+/* xxxx There may be more, but I'm pretty sure about these */
struct mac_receive_buffer {
__u16 next ;
struct olympic_private {
- __u16 srb;
- __u16 trb;
- __u16 arb;
- __u16 asb;
+ __u16 srb; /* be16 */
+ __u16 trb; /* be16 */
+ __u16 arb; /* be16 */
+ __u16 asb; /* be16 */
__u8 *olympic_mmio;
__u8 *olympic_lap;
*/
#include "tulip.h"
+#include <linux/pci.h>
+#include <linux/delay.h>
static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
-u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
+u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
of available transceivers. */
void t21142_timer(unsigned long data)
tp->csr6 &= 0x00D5;
tp->csr6 |= new_csr6;
outl(0x0301, ioaddr + CSR12);
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
}
next_tick = 3*HZ;
}
int csr14 = ((tp->to_advertise & 0x0780) << 9) |
((tp->to_advertise&0x0020)<<1) | 0xffbf;
+ DPRINTK("ENTER\n");
+
dev->if_port = 0;
tp->nway = tp->mediasense = 1;
tp->nwayset = tp->lpar = 0;
if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
+ printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, csr14=%8.8x.\n",
dev->name, csr14);
outl(0x0001, ioaddr + CSR13);
+ udelay(100);
outl(csr14, ioaddr + CSR14);
if (tp->chip_id == PNIC2)
tp->csr6 = 0x01a80000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
else
tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
- tulip_outl_CSR6(tp, tp->csr6);
+ tulip_outl_csr(tp, tp->csr6, CSR6);
if (tp->mtable && tp->mtable->csr15dir) {
outl(tp->mtable->csr15dir, ioaddr + CSR15);
outl(tp->mtable->csr15val, ioaddr + CSR15);
outl(1, ioaddr + CSR13);
}
#if 0 /* Restart shouldn't be needed. */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0000);
+ tulip_outl_csr(tp, tp->csr6 | csr6_sr, CSR6);
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
dev->name, inl(ioaddr + CSR5));
#endif
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_outl_csr(tp, tp->csr6 | csr6_st | csr6_sr, CSR6);
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
dev->name, tp->csr6, inl(ioaddr + CSR6),
tp->csr6 = 0x83860000;
outl(0x0003FF7F, ioaddr + CSR14);
outl(0x0301, ioaddr + CSR12);
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
}
}
if (tp->flags & CSR12_IN_SROM)
csr12dir = *p++;
count = *p++;
+
+ /* there is no phy information, don't even try to build mtable */
+ if (count == 0) {
+ DPRINTK("no phy info, aborting mtable build\n");
+ return;
+ }
+
mtable = (struct mediatable *)
kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
GFP_KERNEL);
int maxtx = TX_RING_SIZE;
int maxoi = TX_RING_SIZE;
int work_count = tulip_max_interrupt_work;
-
+
tp->nir++;
do {
if (status & 0x0002) tp->stats.tx_fifo_errors++;
if ((status & 0x0080) && tp->full_duplex == 0)
tp->stats.tx_heartbeat_errors++;
-#ifdef ETHER_STATS
- if (status & 0x0100) tp->stats.collisions16++;
-#endif
} else {
-#ifdef ETHER_STATS
- if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
tp->stats.tx_bytes +=
tp->tx_buffers[entry].skb->len;
tp->stats.collisions += (status >> 3) & 15;
printk(KERN_WARNING "%s: The transmitter stopped."
" CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
}
spin_unlock(&tp->lock);
}
else
tp->csr6 |= 0x00200000; /* Store-n-forward. */
/* Restart the transmit process. */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
outl(0, ioaddr + CSR1);
}
if (csr5 & RxDied) { /* Missed a Rx frame. */
tp->stats.rx_errors++;
tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_outl_csr(tp, tp->csr6 | csr6_st | csr6_sr, CSR6);
}
if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
if (tp->link_change)
tp->csr6 &= ~0x00400000;
if (tp->full_duplex) tp->csr6 |= 0x0200;
else tp->csr6 &= ~0x0200;
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
if (tulip_debug > 0)
printk(KERN_INFO "%s: Setting %s-duplex based on MII"
"#%d link partner capability of %4.4x.\n",
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
/* Restart Tx */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
dev->trans_start = jiffies;
}
}
outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
if (! tp->nwayset || jiffies - dev->trans_start > 1*HZ) {
tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
- tulip_outl_CSR6(tp, tp->csr6);
+ tulip_outl_csr(tp, tp->csr6, CSR6);
outl(0x30, ioaddr + CSR12);
outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
dev->trans_start = jiffies;
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
/* Restart Tx */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
dev->trans_start = jiffies;
if (tulip_debug > 1)
printk(KERN_INFO "%s: Changing PNIC configuration to %s "
medianame[tp->mtable->mleaf[tp->cur_index].media]);
tulip_select_media(dev, 0);
/* Restart the transmit process. */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
next_tick = (24*HZ)/10;
break;
}
csr6_ra = (1<<30),
csr6_ign_dest_msb = (1<<26),
csr6_mbo = (1<<25),
- csr6_scr = (1<<24),
- csr6_pcs = (1<<23),
- csr6_ttm = (1<<22),
- csr6_sf = (1<<21),
- csr6_hbd = (1<<19),
- csr6_ps = (1<<18),
- csr6_ca = (1<<17),
- csr6_st = (1<<13),
- csr6_fc = (1<<12),
- csr6_om_int_loop = (1<<10),
- csr6_om_ext_loop = (1<<11),
- csr6_fd = (1<<9),
- csr6_pm = (1<<7),
- csr6_pr = (1<<6),
- csr6_sb = (1<<5),
- csr6_if = (1<<4),
- csr6_pb = (1<<3),
- csr6_ho = (1<<2),
- csr6_sr = (1<<1),
- csr6_hp = (1<<0),
+ csr6_scr = (1<<24), /* scramble mode flag: can't be set */
+ csr6_pcs = (1<<23), /* Enables PCS functions (symbol mode requires csr6_ps be set) default is set */
+ csr6_ttm = (1<<22), /* Transmit Threshold Mode, set for 10baseT, 0 for 100BaseTX */
+ csr6_sf = (1<<21), /* Store and forward. If set ignores TR bits */
+ csr6_hbd = (1<<19), /* Heart beat disable. Disables SQE function in 10baseT */
+ csr6_ps = (1<<18), /* Port Select. 0 (defualt) = 10baseT, 1 = 100baseTX: can't be set */
+ csr6_ca = (1<<17), /* Collision Offset Enable. If set uses special algorithm in low collision situations */
+ csr6_trh = (1<<15), /* Transmit Threshold high bit */
+ csr6_trl = (1<<14), /* Transmit Threshold low bit */
+
+ /***************************************************************
+ * This table shows transmit threshold values based on media *
+ * and these two registers (from PNIC1 & 2 docs) Note: this is *
+ * all meaningless if sf is set. *
+ ***************************************************************/
+
+ /***********************************
+ * (trh,trl) * 100BaseTX * 10BaseT *
+ ***********************************
+ * (0,0) * 128 * 72 *
+ * (0,1) * 256 * 96 *
+ * (1,0) * 512 * 128 *
+ * (1,1) * 1024 * 160 *
+ ***********************************/
+
+ csr6_st = (1<<13), /* Transmit conrol: 1 = transmit, 0 = stop */
+ csr6_fc = (1<<12), /* Forces a collision in next transmission (for testing in loopback mode) */
+ csr6_om_int_loop = (1<<10), /* internal (FIFO) loopback flag */
+ csr6_om_ext_loop = (1<<11), /* external (PMD) loopback flag */
+ /* set both and you get (PHY) loopback */
+ csr6_fd = (1<<9), /* Full duplex mode, disables hearbeat, no loopback */
+ csr6_pm = (1<<7), /* Pass All Multicast */
+ csr6_pr = (1<<6), /* Promiscuous mode */
+ csr6_sb = (1<<5), /* Start(1)/Stop(0) backoff counter */
+ csr6_if = (1<<4), /* Inverse Filtering, rejects only addresses in address table: can't be set */
+ csr6_pb = (1<<3), /* Pass Bad Frames, (1) causes even bad frames to be passed on */
+ csr6_ho = (1<<2), /* Hash-only filtering mode: can't be set */
+ csr6_sr = (1<<1), /* Start(1)/Stop(0) Receive */
+ csr6_hp = (1<<0), /* Hash/Perfect Receive Filtering Mode: can't be set */
csr6_mask_capture = (csr6_sc | csr6_ca),
csr6_mask_defstate = (csr6_mask_capture | csr6_mbo),
- csr6_mask_fullcap = (csr6_mask_defstate | csr6_hbd |
- csr6_ps | (3<<14) | csr6_fd),
+ csr6_mask_hdcap = (csr6_mask_defstate | csr6_hbd | csr6_ps),
+ csr6_mask_hdcaptt = (csr6_mask_hdcap | csr6_trh | csr6_trl),
+ csr6_mask_fullcap = (csr6_mask_hdcaptt | csr6_fd),
+ csr6_mask_fullpromisc = (csr6_pr | csr6_pm),
+ csr6_mask_filters = (csr6_hp | csr6_ho | csr6_if),
+ csr6_mask_100bt = (csr6_scr | csr6_pcs | csr6_hbd),
};
extern u16 t21041_csr15[];
-extern inline void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6)
+static inline void tulip_outl_csr (struct tulip_private *tp, u32 newValue, enum tulip_offsets offset)
{
- long ioaddr = tp->base_addr;
+ outl (newValue, tp->base_addr + offset);
+}
- outl (newcsr6, ioaddr + CSR6);
+static inline void tulip_stop_rxtx(struct tulip_private *tp, u32 csr6mask)
+{
+ tulip_outl_csr(tp, csr6mask & ~(csr6_st | csr6_sr), CSR6);
+}
+
+static inline void tulip_restart_rxtx(struct tulip_private *tp, u32 csr6mask)
+{
+ tulip_outl_csr(tp, csr6mask | csr6_sr, CSR6);
+ tulip_outl_csr(tp, csr6mask | csr6_st | csr6_sr, CSR6);
}
*/
-static const char version[] = "Linux Tulip driver version 0.9.4.3 (Apr 14, 2000)\n";
+static const char version[] = "Linux Tulip driver version 0.9.6 (May 31, 2000)\n";
#include <linux/module.h>
#include "tulip.h"
};
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#if defined(__alpha__) || defined(__arm__) || defined(__sparc__)
+#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
+ || defined(__sparc_)
static int rx_copybreak = 1518;
#else
static int rx_copybreak = 100;
#if defined(__alpha__)
static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__i386__) || defined(__powerpc__)
+#elif defined(__i386__) || defined(__powerpc__) || defined(__hppa__)
static int csr0 = 0x01A00000 | 0x8000;
#elif defined(__sparc__)
/* The UltraSparc PCI controllers will disconnect at every 64-byte
int next_tick = 3*HZ;
int i;
+ DPRINTK("ENTER\n");
+
/* Wake the chip from sleep/snooze mode. */
if (tp->flags & HAS_ACPI)
pci_write_config_dword(tp->pdev, 0x40, 0);
/* On some chip revs we must set the MII/SYM port before the reset!? */
if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- tulip_outl_CSR6 (tp, 0x00040000);
+ tulip_outl_csr (tp, 0x00040000, CSR6);
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
outl(0x00000001, ioaddr + CSR0);
tp->csr6 = 0;
tp->cur_index = i;
tp->nwayset = 0;
- if (dev->if_port == 0 && tp->chip_id == DC21041) {
+ if (dev->if_port == 0 && tp->chip_id == DC21041)
tp->nway = 1;
- }
+
if (dev->if_port == 0 && tp->chip_id == DC21142) {
if (tp->mii_cnt) {
tulip_select_media(dev, 1);
printk(KERN_INFO "%s: Using MII transceiver %d, status "
"%4.4x.\n",
dev->name, tp->phys[0], tulip_mdio_read(dev, tp->phys[0], 1));
- tulip_outl_CSR6(tp, 0x82020000);
- tp->csr6 = 0x820E0000;
+ tulip_outl_csr(tp, csr6_mask_defstate, CSR6);
+ tp->csr6 = csr6_mask_hdcap;
dev->if_port = 11;
outl(0x0000, ioaddr + CSR13);
outl(0x0000, ioaddr + CSR14);
tulip_select_media(dev, 1);
/* Start the chip's Tx to process setup frame. */
- tulip_outl_CSR6(tp, tp->csr6);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2000);
+ tulip_outl_csr(tp, tp->csr6, CSR6);
+ tulip_outl_csr(tp, tp->csr6 | csr6_st, CSR6);
/* Enable interrupts by setting the interrupt mask. */
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_outl_csr(tp, tp->csr6 | csr6_st | csr6_sr, CSR6);
outl(0, ioaddr + CSR2); /* Rx poll demand */
if (tulip_debug > 2) {
long ioaddr = dev->base_addr;
unsigned long flags;
+ DPRINTK("ENTER\n");
+
spin_lock_irqsave (&tp->lock, flags);
if (tulip_media_cap[dev->if_port] & MediaIsMII) {
#endif
/* Stop and restart the chip's Tx processes . */
- tulip_outl_CSR6(tp, tp->csr6 | 0x0002);
- tulip_outl_CSR6(tp, tp->csr6 | 0x2002);
+ tulip_restart_rxtx(tp, tp->csr6);
/* Trigger an immediate transmit demand. */
outl(0, ioaddr + CSR1);
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int i;
+ DPRINTK("ENTER\n");
+
tp->tx_full = 0;
tp->cur_rx = tp->cur_tx = 0;
tp->dirty_rx = tp->dirty_tx = 0;
int entry;
u32 flag;
dma_addr_t mapping;
- unsigned long cpuflags;
/* Caution: the write order is important here, set the field
with the ownership bits last. */
- spin_lock_irqsave(&tp->lock, cpuflags);
+ spin_lock_irq(&tp->lock);
/* Calculate the next Tx descriptor entry. */
entry = tp->cur_tx % TX_RING_SIZE;
/* Trigger an immediate transmit demand. */
outl(0, dev->base_addr + CSR1);
- spin_unlock_irqrestore(&tp->lock, cpuflags);
+ spin_unlock_irq(&tp->lock);
dev->trans_start = jiffies;
outl (0x00000000, ioaddr + CSR7);
/* Stop the Tx and Rx processes. */
- tulip_outl_CSR6 (tp, inl (ioaddr + CSR6) & ~0x2002);
+ tulip_stop_rxtx(tp, inl(ioaddr + CSR6));
/* 21040 -- Leave the card in 10baseT state. */
if (tp->chip_id == DC21040)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
- int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+ int csr6, need_lock = 0;
+ unsigned long flags;
+
+ DPRINTK("ENTER\n");
+
+ spin_lock_irqsave(&tp->lock, flags);
+ csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+ spin_unlock_irqrestore(&tp->lock, flags);
tp->csr6 &= ~0x00D5;
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
csr6 |= 0x00C0;
/* Unconditionally log net taps. */
printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+
+ need_lock = 1;
} else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter well -- accept all multicasts. */
tp->csr6 |= 0x0080;
csr6 |= 0x0080;
+
+ need_lock = 1;
} else if (tp->flags & MC_HASH_ONLY) {
/* Some work-alikes have only a 64-entry hash filter table. */
/* Should verify correctness on big-endian/__powerpc__ */
if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */
tp->csr6 |= 0x0080;
csr6 |= 0x0080;
+ need_lock = 1;
} else {
mc_filter[1] = mc_filter[0] = 0;
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next)
set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
+
if (tp->chip_id == AX88140) {
+ spin_lock_irqsave(&tp->lock, flags);
outl(2, ioaddr + CSR13);
outl(mc_filter[0], ioaddr + CSR14);
outl(3, ioaddr + CSR13);
outl(mc_filter[1], ioaddr + CSR14);
+ /* need_lock = 0; */
} else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
+ spin_lock_irqsave(&tp->lock, flags);
outl(mc_filter[0], ioaddr + 0xAC);
outl(mc_filter[1], ioaddr + 0xB0);
+ /* need_lock = 0; */
+ } else {
+ need_lock = 1;
}
}
+
} else {
u16 *eaddrs, *setup_frm = tp->setup_frame;
struct dev_mc_list *mclist;
u32 tx_flags = 0x08000000 | 192;
int i;
- unsigned long flags;
/* Note that only the low-address shortword of setup_frame is valid!
The values are doubled for big-endian architectures. */
if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
/* Same setup recently queued, we need not add it. */
} else {
- unsigned int entry;
+ unsigned int entry, dummy = -1;
/* Now add this frame to the Tx list. */
tp->tx_ring[entry].length =
(entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
tp->tx_ring[entry].buffer1 = 0;
- tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
+ /* Must set DescOwned later to avoid race with chip */
+ dummy = entry;
entry = tp->cur_tx++ % TX_RING_SIZE;
}
tp->tx_ring[entry].buffer1 =
cpu_to_le32(tp->tx_buffers[entry].mapping);
tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
+ if (dummy >= 0)
+ tp->tx_ring[dummy].status = cpu_to_le32(DescOwned);
if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
netif_stop_queue(dev);
tp->tx_full = 1;
outl(0, ioaddr + CSR1);
}
-
- spin_unlock_irqrestore(&tp->lock, flags);
}
- tulip_outl_CSR6(tp, csr6 | 0x0000);
+
+ if (need_lock)
+ spin_lock_irqsave(&tp->lock, flags);
+
+ /* Can someone explain to me what the OR here is supposed to accomplish???? */
+ tulip_outl_csr(tp, csr6 | 0x0000, CSR6);
+
+ spin_unlock_irqrestore(&tp->lock, flags);
}
ioaddr = pci_resource_start (pdev, 0);
irq = pdev->irq;
- /* init_etherdev ensures qword aligned structures */
+ /* init_etherdev ensures aligned and zeroed private structures */
dev = init_etherdev (NULL, sizeof (*tp));
if (!dev) {
printk (KERN_ERR PFX "ether device alloc failed, aborting\n");
return -ENOMEM;
}
- /* We do a request_region() only to register /proc/ioports info. */
- /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
- if (!request_region (ioaddr, tulip_tbl[chip_idx].io_size, dev->name)) {
+ /* grab all resources from both PIO and MMIO regions, as we
+ * don't want anyone else messing around with our hardware */
+ if (!request_region (pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0),
+ dev->name)) {
printk (KERN_ERR PFX "I/O ports (0x%x@0x%lx) unavailable, "
"aborting\n", tulip_tbl[chip_idx].io_size, ioaddr);
goto err_out_free_netdev;
}
-
- if (pci_enable_device(pdev)) {
- printk (KERN_ERR PFX "cannot enable PCI device (id %04x:%04x, "
- "bus %d, devfn %d), aborting\n",
- pdev->vendor, pdev->device,
- pdev->bus->number, pdev->devfn);
- goto err_out_free_netdev;
+ if (!request_mem_region (pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1),
+ dev->name)) {
+ printk (KERN_ERR PFX "MMIO resource (0x%x@0x%lx) unavailable, "
+ "aborting\n", tulip_tbl[chip_idx].io_size, ioaddr);
+ goto err_out_free_pio_res;
}
+ if (pci_enable_device(pdev))
+ goto err_out_free_mmio_res;
+
pci_set_master(pdev);
pci_read_config_byte (pdev, PCI_REVISION_ID, &chip_rev);
tp->pdev = pdev;
tp->base_addr = ioaddr;
tp->revision = chip_rev;
+ tp->csr0 = csr0;
spin_lock_init(&tp->lock);
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ pdev->driver_data = dev;
+
#ifdef TULIP_FULL_DUPLEX
tp->full_duplex = 1;
tp->full_duplex_lock = 1;
printk(KERN_INFO "%s: %s rev %d at %#3lx,",
dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+ /* bugfix: the ASIX must have a burst limit or horrible things happen. */
+ if (chip_idx == AX88140) {
+ if ((tp->csr0 & 0x3f00) == 0)
+ tp->csr0 |= 0x2000;
+ }
+ else if (chip_idx == DC21143 && chip_rev == 65)
+ tp->csr0 &= ~0x01000000;
+
/* Stop the chip's Tx and Rx processes. */
- tulip_outl_CSR6(tp, inl(ioaddr + CSR6) & ~0x2002);
+ tulip_stop_rxtx(tp, inl(ioaddr + CSR6));
+
/* Clear the missed-packet counter. */
(volatile int)inl(ioaddr + CSR8);
printk(", IRQ %d.\n", irq);
last_irq = irq;
- pdev->driver_data = dev;
- dev->base_addr = ioaddr;
- dev->irq = irq;
- tp->csr0 = csr0;
-
- /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
- And the ASIX must have a burst limit or horrible things happen. */
- if (chip_idx == DC21143 && chip_rev == 65)
- tp->csr0 &= ~0x01000000;
- else if (chip_idx == AX88140) {
- if ((tp->csr0 & 0x3f00) == 0)
- tp->csr0 |= 0x2000;
- }
-
/* The lower four bits are the media type. */
if (board_idx >= 0 && board_idx < MAX_UNITS) {
tp->default_port = options[board_idx] & 15;
else
tp->to_advertise = 0x01e1;
- /* This is logically part of probe1(), but too complex to write inline. */
+ /* This is logically part of _init_one(), but too complex to write inline. */
if (tp->flags & HAS_MEDIA_TABLE) {
memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
tulip_parse_eeprom(dev);
outl(0x00000000, ioaddr + CSR13);
outl(0xFFFFFFFF, ioaddr + CSR14);
outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- tulip_outl_CSR6(tp, inl(ioaddr + CSR6) | 0x0200);
+ tulip_outl_csr(tp, inl(ioaddr + CSR6) | csr6_fd, CSR6);
outl(0x0000EF05, ioaddr + CSR13);
break;
case DC21040:
case DC21142:
case PNIC2:
if (tp->mii_cnt || tulip_media_cap[dev->if_port] & MediaIsMII) {
- tulip_outl_CSR6(tp, 0x82020000);
+ tulip_outl_csr(tp, csr6_mask_defstate, CSR6);
outl(0x0000, ioaddr + CSR13);
outl(0x0000, ioaddr + CSR14);
- tulip_outl_CSR6(tp, 0x820E0000);
+ tulip_outl_csr(tp, csr6_mask_hdcap, CSR6);
} else
t21142_start_nway(dev);
break;
if ( ! tp->mii_cnt) {
tp->nway = 1;
tp->nwayset = 0;
- tulip_outl_CSR6(tp, 0x00420000);
+ tulip_outl_csr(tp, csr6_ttm | csr6_ca, CSR6);
outl(0x30, ioaddr + CSR12);
- tulip_outl_CSR6(tp, 0x0001F078);
- tulip_outl_CSR6(tp, 0x0201F078); /* Turn on autonegotiation. */
+ tulip_outl_csr(tp, 0x0001F078, CSR6);
+ tulip_outl_csr(tp, 0x0201F078, CSR6); /* Turn on autonegotiation. */
}
break;
- case MX98713: case COMPEX9881:
- tulip_outl_CSR6(tp, 0x00000000);
+ case MX98713:
+ case COMPEX9881:
+ tulip_outl_csr(tp, 0x00000000, CSR6);
outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
outl(0x00000001, ioaddr + CSR13);
break;
- case MX98715: case MX98725:
- tulip_outl_CSR6(tp, 0x01a80000);
+ case MX98715:
+ case MX98725:
+ tulip_outl_csr(tp, 0x01a80000, CSR6);
outl(0xFFFFFFFF, ioaddr + CSR14);
outl(0x00001000, ioaddr + CSR12);
break;
return 0;
+err_out_free_mmio_res:
+ release_mem_region (pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1));
+err_out_free_pio_res:
+ release_region (pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0));
err_out_free_netdev:
unregister_netdev (dev);
kfree (dev);
tp->rx_ring,
tp->rx_ring_dma);
unregister_netdev(dev);
- release_region(dev->base_addr,
- tulip_tbl[tp->chip_id].io_size);
+ release_mem_region (pci_resource_start (pdev, 1),
+ pci_resource_len (pdev, 1));
+ release_region (pci_resource_start (pdev, 0),
+ pci_resource_len (pdev, 0));
kfree(dev);
}
}
if (base_addr > 0x1ff) /* Check a single specified location. */
return wd_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
+ return -ENXIO;
for (i = 0; wd_portlist[i]; i++) {
int ioaddr = wd_portlist[i];
return 0;
}
- return ENODEV;
+ return -ENODEV;
}
#endif
if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
|| inb(ioaddr + 9) == 0xff
|| (checksum & 0xff) != 0xFF)
- return ENODEV;
-
- /* Looks like we have a card. Make sure 8390 support is available. */
- if (load_8390_module("wd.c"))
- return -ENOSYS;
+ return -ENODEV;
/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return EAGAIN;
+ return -EAGAIN;
}
/* OK, were are certain this is going to work. Setup the device. */
{
int this_dev, found = 0;
+ if (load_8390_module("wd.c"))
+ return -ENOSYS;
+
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
struct net_device *dev = &dev_wd[this_dev];
dev->irq = irq[this_dev];
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) { /* Got at least one. */
- lock_8390_module();
return 0;
}
+ unload_8390_module();
return -ENXIO;
}
found++;
}
- lock_8390_module();
return 0;
}
release_region(ioaddr, WD_IO_EXTENT);
unregister_netdev(dev);
kfree(priv);
- unlock_8390_module();
}
}
+ unload_8390_module();
}
#endif /* MODULE */
\f
if (p >= (char *)phys_to_virt(0x100000)) {
if (znet_debug > 1)
printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
- return ENODEV;
+ return -ENODEV;
}
netinfo = (struct netidblk *)p;
dev->base_addr = netinfo->iobase1;
|| request_dma(zn.rx_dma,"ZNet rx")
|| request_dma(zn.tx_dma,"ZNet tx")) {
printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
- return EBUSY;
+ return -EBUSY;
}
/* Allocate buffer memory. We can cross a 128K boundary, so we
Currently known (or at least suspected) bugs in parport:
-o lp doesn't allow you to read status while printing is in progress.
+o lp doesn't allow you to read status while printing is in progress (is
+ this still true?).
-See <URL:http://www.cyberelk.demon.co.uk/parport.html>.
+See <URL:http://people.redhat.com/twaugh/parport/>.
+2000-06-13 Tim Waugh <twaugh@redhat.com>
+
+ * procfs.c: Break 'hardware' out into separate files.
+
+2000-05-28 Gunther Mayer <gunther.mayer@braunschweig.okersurf.de>
+
+ * Fix PCI ID printk for non-superio PCI cards.
+
+2000-05-28 Tim Waugh <twaugh@redhat.com>
+
+ * share.c (call_driver_chain): Get the driverlist_lock.
+ (parport_register_device): Make sure that port->devices always
+ looks consistent.
+ (parport_register_driver): Ensure that parport drivers are given
+ parameters that are valid for the duration of the callback by
+ locking the portlist against changes.
+ (parport_unregister_driver): Likewise.
+ (parport_claim): Don't overwrite flags.
+
+2000-05-28 Tim Waugh <twaugh@redhat.com>
+
+ * daisy.c (assign_addrs): Avoid double-probing daisy-chain devices
+ if the first probe succeeds.
+
2000-05-16 Tim Waugh <twaugh@redhat.com>
* share.c (parport_claim): Fix SMP race.
#
# For a description of the syntax of this configuration file,
-# see the Configure script.
+# see Documentation/kbuild/config-language.txt.
#
# Parport configuration.
#
4. A better PLIP (make use of bidirectional/ECP/EPP ports).
-See <URL:http://www.cyberelk.demon.co.uk/parport.html>.
+See <URL:http://people.redhat.com/twaugh/parport/>.
unsigned char s, last_dev;
unsigned char daisy;
int thisdev = numdevs;
+ int detected;
char *deviceid;
parport_data_forward (port);
}
parport_write_data (port, 0xff); udelay (2);
+ detected = numdevs - thisdev;
DPRINTK (KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name,
- numdevs - thisdev);
+ detected);
/* Ask the new devices to introduce themselves. */
deviceid = kmalloc (1000, GFP_KERNEL);
parport_device_id (thisdev, deviceid, 1000);
kfree (deviceid);
- return numdevs - thisdev;
+ return detected;
}
/* Find a device with a particular manufacturer and model string,
#include <linux/module.h>
#include <linux/init.h>
#include <linux/parport.h>
+#include <linux/ioport.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
struct parport *p;
if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) {
+ if (!request_mem_region(CIAA_PHYSADDR+0x100, 1, "parallel"))
+ return 0;
ciaa.ddrb = 0xff;
ciab.ddra &= 0xf8;
if (!(p = parport_register_port((unsigned long)&ciaa.prb,
IRQ_AMIGA_CIAA_FLG, PARPORT_DMA_NONE,
- &pp_amiga_ops)))
+ &pp_amiga_ops))) {
+ release_mem_region(CIAA_PHYSADDR+0x100, 1);
return 0;
+ }
if (!request_irq(IRQ_AMIGA_CIAA_FLG, amiga_interrupt, 0, p->name, p)) {
parport_unregister_port (p);
+ release_mem_region(CIAA_PHYSADDR+0x100, 1);
return 0;
}
void cleanup_module(void)
{
if (this_port->irq != PARPORT_IRQ_NONE)
- free_irq(IRQ_MFP_BUSY, this_port);
+ free_irq(IRQ_AMIGA_CIAA_FLG, this_port);
parport_proc_unregister(this_port);
parport_unregister_port(this_port);
+ release_mem_region(CIAA_PHYSADDR+0x100, 1);
}
#endif
def.) */
/* TODO: test if sharing interrupts works */
printk (KERN_DEBUG "PCI parallel port detected: %04x:%04x, "
- "I/O at %#lx(%#lx)\n", parport_pc_pci_tbl[i].vendor,
- parport_pc_pci_tbl[i].device, io_lo, io_hi);
+ "I/O at %#lx(%#lx)\n",
+ parport_pc_pci_tbl[i + last_sio].vendor,
+ parport_pc_pci_tbl[i + last_sio].device, io_lo, io_hi);
if (parport_pc_probe_port (io_lo, io_hi, PARPORT_IRQ_NONE,
PARPORT_DMA_NONE, dev))
count++;
}
#endif /* IEEE1284.3 support. */
-static int do_hardware(ctl_table *table, int write, struct file *filp,
- void *result, size_t *lenp)
+static int do_hardware_base_addr (ctl_table *table, int write,
+ struct file *filp, void *result,
+ size_t *lenp)
{
struct parport *port = (struct parport *)table->extra1;
- char buffer[256];
+ char buffer[20];
int len = 0;
if (filp->f_pos) {
*lenp = 0;
return 0;
}
-
- if (write) /* can't happen anyway */
+
+ if (write) /* permissions prevent this anyway */
return -EACCES;
-
- len += sprintf(buffer+len, "base:\t0x%lx", port->base);
- if (port->base_hi)
- len += sprintf(buffer+len, " (0x%lx)", port->base_hi);
- buffer[len++] = '\n';
- if (port->irq == PARPORT_IRQ_NONE) {
- len += sprintf(buffer+len, "irq:\tnone\n");
- } else {
- len += sprintf(buffer+len, "irq:\t%d\n", port->irq);
+ len += sprintf (buffer, "%lu\t%lu\n", port->base, port->base_hi);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ filp->f_pos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_irq (ctl_table *table, int write,
+ struct file *filp, void *result,
+ size_t *lenp)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (filp->f_pos) {
+ *lenp = 0;
+ return 0;
}
- if (port->dma == PARPORT_DMA_NONE)
- len += sprintf(buffer+len, "dma:\tnone\n");
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ len += sprintf (buffer, "%d\n", port->irq);
+
+ if (len > *lenp)
+ len = *lenp;
else
- len += sprintf(buffer+len, "dma:\t%d\n", port->dma);
+ *lenp = len;
+
+ filp->f_pos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_dma (ctl_table *table, int write,
+ struct file *filp, void *result,
+ size_t *lenp)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (filp->f_pos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
+
+ len += sprintf (buffer, "%d\n", port->dma);
+
+ if (len > *lenp)
+ len = *lenp;
+ else
+ *lenp = len;
+
+ filp->f_pos += len;
+
+ return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+}
+
+static int do_hardware_modes (ctl_table *table, int write,
+ struct file *filp, void *result,
+ size_t *lenp)
+{
+ struct parport *port = (struct parport *)table->extra1;
+ char buffer[20];
+ int len = 0;
+
+ if (filp->f_pos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) /* permissions prevent this anyway */
+ return -EACCES;
- len += sprintf(buffer+len, "modes:\t");
{
#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
int f = 0;
struct parport_sysctl_table {
struct ctl_table_header *sysctl_header;
- ctl_table vars[9];
+ ctl_table vars[12];
ctl_table device_dir[2];
ctl_table port_dir[2];
ctl_table parport_dir[2];
&proc_dointvec_minmax, NULL, NULL,
(void*) &parport_min_spintime_value,
(void*) &parport_max_spintime_value },
- { DEV_PARPORT_HARDWARE, "hardware",
+ { DEV_PARPORT_BASE_ADDR, "base-addr",
+ NULL, 0, 0444, NULL,
+ &do_hardware_base_addr },
+ { DEV_PARPORT_IRQ, "irq",
+ NULL, 0, 0444, NULL,
+ &do_hardware_irq },
+ { DEV_PARPORT_DMA, "dma",
+ NULL, 0, 0444, NULL,
+ &do_hardware_dma },
+ { DEV_PARPORT_MODES, "modes",
NULL, 0, 0444, NULL,
- &do_hardware },
+ &do_hardware_modes },
PARPORT_DEVICES_ROOT_DIR,
#ifdef CONFIG_PARPORT_1284
{ DEV_PARPORT_AUTOPROBE, "autoprobe",
t->vars[i].extra1 = port;
t->vars[0].data = &port->spintime;
- t->vars[2].child = t->device_dir;
+ t->vars[5].child = t->device_dir;
for (i = 0; i < 5; i++)
- t->vars[3 + i].extra2 = &port->probe_info[i];
+ t->vars[6 + i].extra2 = &port->probe_info[i];
t->port_dir[0].procname = port->name;
t->port_dir[0].ctl_name = port->number + 1; /* nb 0 isn't legal here */
{
struct parport_driver *drv;
+ spin_lock (&driverlist_lock);
for (drv = driver_chain; drv; drv = drv->next) {
if (attach)
drv->attach (port);
else
drv->detach (port);
}
+ spin_unlock (&driverlist_lock);
}
/* Ask kmod for some lowlevel drivers. */
driver_chain = drv;
spin_unlock (&driverlist_lock);
+ /* We have to take the portlist lock for this to be sure
+ * that port is valid for the duration of the callback. */
+ spin_lock (&parportlist_lock);
for (port = portlist; port; port = port->next)
drv->attach (port);
+ spin_unlock (&parportlist_lock);
if (!portlist)
get_lowlevel_driver ();
/* Call the driver's detach routine for each
* port to clean up any resources that the
* attach routine acquired. */
+ spin_lock (&parportlist_lock);
for (port = portlist; port; port = port->next)
drv->detach (port);
+ spin_unlock (&parportlist_lock);
return;
}
struct parport *parport_enumerate(void)
{
+ /* Don't use this: use parport_register_driver instead. */
+
if (!portlist)
get_lowlevel_driver ();
* This function must not run from an irq handler so we don' t need
* to clear irq on the local CPU. -arca
*/
+
spin_lock(&parportlist_lock);
+
+ /* We are locked against anyone else performing alterations, but
+ * because of parport_enumerate people can still _read_ the list
+ * while we are changing it; so be careful..
+ *
+ * It's okay to have portlist_tail a little bit out of sync
+ * since it's only used for changing the list, not for reading
+ * from it.
+ */
+
if (portlist_tail)
portlist_tail->next = tmp;
portlist_tail = tmp;
#endif
spin_lock(&parportlist_lock);
+
+ /* We are protected from other people changing the list, but
+ * they can see see it (using parport_enumerate). So be
+ * careful about the order of writes.. */
if (portlist == port) {
if ((portlist = port->next) == NULL)
portlist_tail = NULL;
}
spin_unlock(&parportlist_lock);
+ /* Yes, parport_enumerate _is_ unsafe. Don't use it. */
if (!port->devices)
free_port (port);
}
}
tmp->next = port->physport->devices;
+ wmb(); /* Make sure that tmp->next is written before it's
+ added to the list; see comments marked 'no locking
+ required' */
if (port->physport->devices)
port->physport->devices->prev = tmp;
port->physport->devices = tmp;
* free up the resources. */
if (port->ops == &dead_ops && !port->devices)
free_port (port);
+
+ /* Yes, that's right, someone _could_ still have a pointer to
+ * port, if they used parport_enumerate. That's why they
+ * shouldn't use it (and use parport_register_driver instead)..
+ */
}
/**
dev->waiting = 0;
/* Take ourselves out of the wait list again. */
- spin_lock_irqsave (&port->waitlist_lock, flags);
+ spin_lock_irq (&port->waitlist_lock);
if (dev->waitprev)
dev->waitprev->waitnext = dev->waitnext;
else
dev->waitnext->waitprev = dev->waitprev;
else
port->waittail = dev->waitprev;
- spin_unlock_irqrestore (&port->waitlist_lock, flags);
+ spin_unlock_irq (&port->waitlist_lock);
dev->waitprev = dev->waitnext = NULL;
}
}
/* Nobody was waiting, so walk the list to see if anyone is
- interested in being woken up. */
+ interested in being woken up. (Note: no locking required) */
for (pd = port->devices; (port->cad == NULL) && pd; pd = pd->next) {
if (pd->wakeup && pd != dev)
pd->wakeup(pd->private);
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
+#include <linux/spinlock.h>
#include <linux/pm.h>
#include <asm/page.h>
100a Phoenix Technologies
100b National Semiconductor Corporation
0001 DP83810
- 0002 87415
+ 0002 87415/87560 IDE
+ 000e 87560 Legacy I/O
000f OHCI Compliant FireWire Controller
0011 National PCI System I/O
0012 USB Controller
- d001 87410
+ d001 87410 IDE
100c Tseng Labs Inc
3202 ET4000/W32p rev A
3205 ET4000/W32p rev B
1058 Electronics & Telecommunications RSH
1059 Teknor Industrial Computers Inc
105a Promise Technology, Inc.
+ 4d30 20267
4d33 20246
4d38 20262
5300 DC5300
0646 PCI0646
0647 PCI0647
0648 PCI0648
+ 0649 PCI0649
0650 PBC0650A
0670 USB0670
0673 USB0673
5055 3c555 Laptop Hurricane
5057 3c575 [Megahertz] 10/100 LAN CardBus
10b7 5a57 3C575 Megahertz 10/100 LAN Cardbus PC Card
+ 5b57 3c575 [Megahertz] 10/100 LAN CardBus
+ 10b7 5a57 3C575 Megahertz 10/100 LAN Cardbus
5157 3c575 [Megahertz] 10/100 LAN CardBus
10b7 5b57 3C575 Megahertz 10/100 LAN Cardbus PC Card
5257 3CCFE575CT Cyclone CardBus
5970 3c597 EISA Fast Demon/Vortex
6560 3CCFE656 Cyclone CardBus
6562 3CCFEM656 Cyclone CardBus
+ 6564 3CCFEM656 Cyclone CardBus (0x6564)
7646 3cSOHO100-TX Hurricane
8811 Token ring
9000 3c900 10BaseT [Boomerang]
10b7 1000 3C905C-TX Fast Etherlink for PC Management NIC
9800 3c980-TX [Fast Etherlink XL Server Adapter]
10b7 9800 3c980-TX Fast Etherlink XL Server Adapter
+ 9805 3c980-TX [10/100 Base-TX NIC(Python-T)]
+ 10b7 9805 3c980 10/100 Base-TX NIC(Python-T)
10b8 Standard Microsystems Corp [SMC]
0005 83C170QF
1055 e000 LANEPIC
11d4 0048 SoundMAX Integrated Digital Audio
2426 82801AB 82810 AC'97 Modem
2428 82801AB 82810 PCI Bridge
+ 2440 82820 820 (Camino 2) Chipset ISA Bridge (ICH2)
+ 2442 82820 820 (Camino 2) Chipset USB (Hub A)
+ 2443 82820 820 (Camino 2) Chipset SMBus
+ 2444 82820 820 (Camino 2) Chipset USB (Hub B)
+ 2449 82820 820 (Camino 2) Chipset Ethernet
+ 244b 82820 820 (Camino 2) Chipset IDE U100
+ 244e 82820 820 (Camino 2) Chipset PCI
2500 82820 820 (Camino) Chipset Host Bridge (MCH)
1043 801c P3C-2000 system chipset
2501 82820 820 (Camino) Chipset Host Bridge (MCH)
* 0xE0 (64 bytes of ACPI registers)
* 0xE2 (32 bytes of SMB registers)
*/
-static void __init quirk_ali7101(struct pci_dev *dev)
+static void __init quirk_ali7101_acpi(struct pci_dev *dev)
{
u16 region;
* 0x40 (64 bytes of ACPI registers)
* 0x90 (32 bytes of SMB registers)
*/
-static void __init quirk_piix4acpi(struct pci_dev *dev)
+static void __init quirk_piix4_acpi(struct pci_dev *dev)
{
u32 region;
* VIA ACPI: One IO region pointed to by longword at
* 0x48 or 0x20 (256 bytes of ACPI registers)
*/
-static void __init quirk_via_acpi(struct pci_dev *dev)
+static void __init quirk_vt82c586_acpi(struct pci_dev *dev)
{
u8 rev;
u32 region;
}
}
+/*
+ * VIA VT82C686 ACPI: Three IO region pointed to by (long)words at
+ * 0x48 (256 bytes of ACPI registers)
+ * 0x70 (128 bytes of hardware monitoring register)
+ * 0x90 (16 bytes of SMB registers)
+ */
+static void __init quirk_vt82c686_acpi(struct pci_dev *dev)
+{
+ u16 hm;
+ u32 smb;
+
+ quirk_vt82c586_acpi(dev);
+
+ pci_read_config_word(dev, 0x70, &hm);
+ hm &= PCI_BASE_ADDRESS_IO_MASK;
+ quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1);
+
+ pci_read_config_dword(dev, 0x90, &smb);
+ smb &= PCI_BASE_ADDRESS_IO_MASK;
+ quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2);
+}
+
+/*
+ * PIIX3 USB: We have to disable USB interrupts that are
+ * hardwired to PIRQD# and may be shared with an
+ * external device.
+ *
+ * Legacy Support Register (LEGSUP):
+ * bit13: USB PIRQ Enable (USBPIRQDEN),
+ * bit4: Trap/SMI ON IRQ Enable (USBSMIEN).
+ *
+ * We mask out all r/wc bits, too.
+ */
+static void __init quirk_piix3usb(struct pci_dev *dev)
+{
+ u16 legsup;
+
+ pci_read_config_word(dev, 0xc0, &legsup);
+ legsup &= 0x50ef;
+ pci_write_config_word(dev, 0xc0, legsup);
+}
+
/*
* The main table of quirks.
*/
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci },
- { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_via_acpi },
- { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_via_acpi },
- { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4acpi },
- { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101 },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101_acpi },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3usb },
{ 0 }
};
NULL) {
pci_device_id_cnt++;
} else {
- pci_devicep[pci_card_cnt_max++] = pci_devp;
+ if (pci_enable_device(pci_devp) == 0)
+ pci_devicep[pci_card_cnt_max++] = pci_devp;
}
}
#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,13)
iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK;
#else /* version >= v2.3.13 */
- iop = pci_devp->resource[0].start & PCI_IOADDRESS_MASK;
+ iop = pci_resource_start(pci_devp, 0);
#endif /* version >= v2.3.13 */
ASC_DBG2(1,
"advansys_detect: vendorID %X, deviceID %X\n",
#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,13)
pci_memory_address = pci_devp->base_address[1];
#else /* version >= v2.3.13 */
- pci_memory_address = pci_devp->resource[1].start;
+ pci_memory_address = pci_resource_start(pci_devp, 1);
#endif /* version >= v2.3.13 */
ASC_DBG1(1, "advansys_detect: pci_memory_address: %x\n",
pci_memory_address);
printk(KERN_ERR "aha152x%d: IRQ %d possibly wrong. Please verify.\n", HOSTNO, shpnt->irq);
- scsi_unregister(shpnt);
registered_count--;
release_region(shpnt->io_port, IO_RANGE);
- shpnt = aha152x_host[shpnt->irq - IRQ_MIN] = 0;
+ aha152x_host[shpnt->irq - IRQ_MIN] = 0;
+ scsi_unregister(shpnt);
+ shpnt=NULL;
continue;
}
printk("ok.\n");
pdev = NULL;
while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
aic_pdevs[i].device_id,
- pdev)))
+ pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
#else
index = 0;
while (!(pcibios_find_device(aic_pdevs[i].vendor_id,
aic_pdevs[i].device_id,
- index++, &pci_bus, &pci_devfn)) )
+ index++, &pci_bus, &pci_devfn)) ) {
#endif
- {
if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
{
if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
temp_p->pdev = pdev;
temp_p->pci_bus = pdev->bus->number;
temp_p->pci_device_fn = pdev->devfn;
- temp_p->base = pdev->resource[0].start;
- temp_p->mbase = pdev->resource[1].start;
+ temp_p->base = pci_resource_start(pdev, 0);
+ temp_p->mbase = pci_resource_start(pdev, 1);
current_p = list_p;
while(current_p && temp_p)
{
/* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $
* linux/kernel/atp870u.c
*
- * Copyright (C) 1997 Wu Ching Chen
+ * Copyright (C) 1997 Wu Ching Chen
* 2.1.x update (C) 1998 Krzysztof G. Baranowski
- *
- * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
- *
+ *
+ * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
+ *
+ * Wu Ching Chen : NULL pointer fixes 2000/06/02
+ * support atp876 chip
+ * enable 32 bit fifo transfer
+ * support cdrom & remove device run ultra speed
*/
#include <linux/module.h>
dev->in_int = 1;
workportu = dev->ioport;
tmport = workportu;
-
- if (dev->working != 0)
+
+ if (dev->working != 0)
{
tmport += 0x1f;
j = inb(tmport);
- if((j&0x80)==0)
+ if ((j & 0x80) == 0)
{
- dev->in_int=0;
+ dev->in_int = 0;
return;
}
tmpcip = dev->pciport;
- if ((inb(tmpcip) & 0x08) != 0)
+ if ((inb(tmpcip) & 0x08) != 0)
{
tmpcip += 0x2;
for (k=0; k < 1000; k++)
if ((inb(tmpcip) & 0x01) == 0)
{
goto stop_dma;
- }
+ }
}
}
stop_dma:
tmport -= 0x08;
i = inb(tmport);
- if ((j & 0x40) == 0)
+ if ((j & 0x40) == 0)
{
- if ((dev->last_cmd & 0x40) == 0)
+ if ((dev->last_cmd & 0x40) == 0)
{
dev->last_cmd = 0xff;
}
/*
* Remap wide devices onto id numbers
*/
-
+
if ((target_id & 0x40) != 0) {
target_id = (target_id & 0x07) | 0x08;
} else {
target_id &= 0x07;
}
-
- if (i == 0x85)
+
+ if (i == 0x85)
{
/*
* Flip wide
*/
- if (dev->wide_idu != 0)
+ if (dev->wide_idu != 0)
{
tmport = workportu + 0x1b;
- j = inb(tmport) & 0x0e;
- j |= 0x01;
- outb(j, tmport);
+ outb(0x01,tmport);
+ while ((inb(tmport) & 0x01) != 0x01)
+ {
+ outb(0x01,tmport);
+ }
}
/*
* Issue more commands
*/
if (((dev->quhdu != dev->quendu) || (dev->last_cmd != 0xff)) &&
- (dev->in_snd == 0))
+ (dev->in_snd == 0))
{
send_s870(h);
}
dev->in_int = 0;
return;
}
- if (i == 0x21)
+ if (i == 0x21)
{
tmport -= 0x05;
adrcntu = 0;
dev->in_int = 0;
return;
}
- if ((i == 0x80) || (i == 0x8f))
+ if ((i == 0x80) || (i == 0x8f))
{
lun = 0;
tmport -= 0x07;
tmport += 0x0d;
lun = inb(tmport) & 0x07;
} else {
- if (j == 0x41)
+ if (j == 0x41)
{
tmport += 0x02;
adrcntu = 0;
dev->in_int = 0;
return;
}
- else
+ else
{
outb(0x46, tmport);
dev->id[target_id].dirctu = 0x00;
/*
* Remap wide identifiers
*/
- if ((target_id & 0x10) != 0)
+ if ((target_id & 0x10) != 0)
{
target_id = (target_id & 0x07) | 0x08;
} else {
j |= dev->id[target_id].dirctu;
outb(j, tmport++);
outb(0x80, tmport);
+
+ /* enable 32 bit fifo transfer */
+ tmport = workportu + 0x3a;
+ if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) ||
+ (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a))
+ {
+ outb((unsigned char)((inb(tmport) & 0xf3) | 0x08),tmport);
+ }
+ else
+ {
+ outb((unsigned char)(inb(tmport) & 0xf3),tmport);
+ }
+
tmport = workportu + 0x1b;
- j = inb(tmport) & 0x0e;
+ j = 0;
id = 1;
id = id << target_id;
/*
j |= 0x01;
}
outb(j, tmport);
-
+ while ((inb(tmport) & 0x01) != j)
+ {
+ outb(j,tmport);
+ }
+
if (dev->id[target_id].last_lenu == 0) {
tmport = workportu + 0x18;
outb(0x08, tmport);
return;
}
prd = dev->id[target_id].prd_posu;
- while (adrcntu != 0)
+ while (adrcntu != 0)
{
id = ((unsigned short int *) (prd))[2];
if (id == 0) {
dev->in_int = 0;
return;
}
-
+
/*
* Current scsi request on this target
*/
-
+
workrequ = dev->id[target_id].curr_req;
-
+
if (i == 0x42) {
errstus = 0x02;
workrequ->result = errstus;
goto go_42;
}
- if (i == 0x16)
+ if (i == 0x16)
{
errstus = 0;
tmport -= 0x08;
*/
if (dev->wide_idu != 0) {
tmport = workportu + 0x1b;
- j = inb(tmport) & 0x0e;
- j |= 0x01;
- outb(j, tmport);
+ outb(0x01,tmport);
+ while ((inb(tmport) & 0x01) != 0x01)
+ {
+ outb(0x01,tmport);
+ }
}
/*
* If there is stuff to send and nothing going then send it
*/
if (((dev->last_cmd != 0xff) || (dev->quhdu != dev->quendu)) &&
- (dev->in_snd == 0))
+ (dev->in_snd == 0))
{
send_s870(h);
}
dev->in_int = 0;
return;
} else {
- tmport = workportu + 0x17;
- inb(tmport);
- dev->working = 0;
+// tmport = workportu + 0x17;
+// inb(tmport);
+// dev->working = 0;
dev->in_int = 0;
return;
}
/*
* Fake a timeout for missing targets
*/
-
+
if ((m & dev->active_idu) == 0) {
req_p->result = 0x00040000;
done(req_p);
if ((dev->last_cmd != 0xff) && ((dev->last_cmd & 0x40) != 0)) {
dev->last_cmd &= 0x0f;
workrequ = dev->id[dev->last_cmd].curr_req;
- goto cmd_subp;
+ if (workrequ != NULL) /* check NULL pointer */
+ {
+ goto cmd_subp;
+ }
+ dev->last_cmd = 0xff;
+ if (dev->quhdu == dev->quendu)
+ {
+ dev->in_snd = 0;
+ restore_flags(flags);
+ return ;
+ }
}
dev->working++;
j = dev->quhdu;
workrequ->request_bufflen = 0x08;
}
}
+ if (dev->ata_cdbu[0] == 0x00) {
+ workrequ->request_bufflen = 0;
+ }
/*
* Why limit this ????
*/
dev->ata_cdbu[4] = 0x24;
}
}
-
+
tmport = workportu + 0x1b;
- j = inb(tmport) & 0x0e;
+ j = 0;
target_id = workrequ->target;
-
+
/*
* Wide ?
*/
w = w << target_id;
if ((w & dev->wide_idu) != 0) {
j |= 0x01;
- }
+ }
outb(j, tmport);
-
+ while ((inb(tmport) & 0x01) != j)
+ {
+ outb(j,tmport);
+ }
+
/*
* Write the command
*/
-
+
tmport = workportu;
outb(workrequ->cmd_len, tmport++);
outb(0x2c, tmport++);
* Write the target
*/
outb(dev->id[target_id].devspu, tmport++);
-
+
/*
* Figure out the transfer size
*/
- if (workrequ->use_sg)
+ if (workrequ->use_sg)
{
l = 0;
sgpnt = (struct scatterlist *) workrequ->request_buffer;
- for (i = 0; i < workrequ->use_sg; i++)
+ for (i = 0; i < workrequ->use_sg; i++)
{
- if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER)
+ if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER)
{
panic("Foooooooood fight!");
}
tmpcip = dev->pciport;
prd = dev->id[target_id].prd_tableu;
dev->id[target_id].prd_posu = prd;
-
+
/*
* Now write the request list. Either as scatter/gather or as
* a linear chain.
*/
-
- if (workrequ->use_sg)
+
+ if (workrequ->use_sg)
{
sgpnt = (struct scatterlist *) workrequ->request_buffer;
i = 0;
outb(0x06, tmpcip);
outb(0x00, tmpcip);
tmpcip = tmpcip - 2;
+
+ tmport = workportu + 0x3a;
+ if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) ||
+ (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a))
+ {
+ outb((unsigned char)((inb(tmport) & 0xf3) | 0x08),tmport);
+ }
+ else
+ {
+ outb((unsigned char)(inb(tmport) & 0xf3),tmport);
+ }
+ tmport = workportu + 0x1c;
+
if ((dev->ata_cdbu[0] == WRITE_6) || (dev->ata_cdbu[0] == WRITE_10) ||
- (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT))
+ (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT))
{
dev->id[target_id].dirctu = 0x20;
if (inb(tmport) == 0) {
restore_flags(flags);
return;
}
- if (inb(tmport) == 0)
+ if (inb(tmport) == 0)
{
tmport = workportu + 0x18;
outb(0x08, tmport);
goto FUN_D7;
}
}
- *val |= 0x4000; /* assert DB6 */
+ *val |= 0x4000; /* assert DB6 */
outw(*val, tmport);
- *val &= 0xdfff; /* assert DB5 */
+ *val &= 0xdfff; /* assert DB5 */
outw(*val, tmport);
FUN_D5:
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
+ if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
goto FUN_D5;
}
}
- *val |= 0x8000; /* no DB4-0, assert DB7 */
+ *val |= 0x8000; /* no DB4-0, assert DB7 */
*val &= 0xe0ff;
outw(*val, tmport);
- *val &= 0xbfff; /* release DB6 */
+ *val &= 0xbfff; /* release DB6 */
outw(*val, tmport);
FUN_D6:
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
outb(k, tmport++);
tmport = dev->ioport + 0x1b;
if (dev->chip_veru == 4) {
- outb((unsigned char) ((inb(tmport) & 0x0e) | 0x01), tmport);
+ outb(0x01, tmport);
} else {
- outb((unsigned char) (inb(tmport) & 0x0e), tmport);
+ outb(0x00, tmport);
}
wait_rdyok:
tmport = dev->ioport + 0x18;
outb(0, 0x80);
- val = 0x0080; /* bsy */
+ val = 0x0080; /* bsy */
tmport = dev->ioport + 0x1c;
outw(val, tmport);
- val |= 0x0040; /* sel */
+ val |= 0x0040; /* sel */
outw(val, tmport);
- val |= 0x0004; /* msg */
+ val |= 0x0004; /* msg */
outw(val, tmport);
inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
val &= 0x007f; /* no bsy */
outw(val, tmport);
- mydlyu(0xffff); /* recommanded SCAM selection response time */
+ mydlyu(0xffff); /* recommanded SCAM selection response time */
mydlyu(0xffff);
val &= 0x00fb; /* after 1ms no msg */
outw(val, tmport);
val |= 0x3f00;
fun_scam(dev, &val);
outb(3, 0x80);
- val &= 0x00ff; /* isolation */
+ val &= 0x00ff; /* isolation */
val |= 0x2000;
fun_scam(dev, &val);
outb(4, 0x80);
printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
i = 15;
j = mbuf[0];
- if ((j & 0x20) != 0) { /* bit5=1:ID upto 7 */
+ if ((j & 0x20) != 0) { /* bit5=1:ID upto 7 */
i = 7;
}
- if ((j & 0x06) == 0) { /* IDvalid? */
+ if ((j & 0x06) == 0) { /* IDvalid? */
goto G2Q5;
}
k = mbuf[1];
goto small_id;
}
G2Q5: /* srch from max acceptable ID# */
- k = i; /* max acceptable ID# */
+ k = i; /* max acceptable ID# */
G2Q_LP:
m = 1;
m <<= k;
k--;
goto G2Q_LP;
}
-G2Q_QUIN: /* k=binID#, */
+G2Q_QUIN: /* k=binID#, */
assignid_map |= m;
if (k < 8) {
quintet[0] = 0x38; /* 1st dft ID<8 */
} else {
- quintet[0] = 0x31; /* 1st ID>=8 */
+ quintet[0] = 0x31; /* 1st ID>=8 */
}
k &= 0x07;
quintet[1] = g2q_tab[k];
void is870(unsigned long host, unsigned int wkport)
{
unsigned int tmport;
- unsigned char i, j, k, rmb;
+ unsigned char i, j, k, rmb, n;
unsigned short int m;
static unsigned char mbuf[512];
- static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6};
+ static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6};
static unsigned char inqd[9] = {0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6};
- static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
+ static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
static unsigned char synu[6] = {0x80, 1, 3, 1, 0x0c, 0x0e};
static unsigned char synw[6] = {0x80, 1, 3, 1, 0x0c, 0x07};
- static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0};
+ static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0};
struct atp_unit *dev = &atp_unit[host];
sync_idu = 0;
printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_idu);
continue;
}
+ tmport = wkport + 0x1b;
if (dev->chip_veru == 4) {
- tmport = wkport + 0x1b;
- j = (inb(tmport) & 0x0e) | 0x01;
- outb(j, tmport);
+ outb(0x01, tmport);
+ }
+ else
+ {
+ outb(0x00, tmport);
}
tmport = wkport + 1;
outb(0x08, tmport++);
continue;
}
while (inb(tmport) != 0x8e);
+ tmport = wkport + 0x1b;
if (dev->chip_veru == 4) {
- tmport = wkport + 0x1b;
- j = inb(tmport) & 0x0e;
- outb(j, tmport);
+ outb(0x00, tmport);
}
tmport = wkport + 0x18;
outb(0x08, tmport);
printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
dev->id[i].devtypeu = mbuf[0];
rmb = mbuf[1];
+ n = mbuf[7];
if (dev->chip_veru != 4) {
goto not_wide;
}
goto not_wide;
}
tmport = wkport + 0x1b;
- j = (inb(tmport) & 0x0e) | 0x01;
- outb(j, tmport);
+ outb(0x01, tmport);
tmport = wkport + 3;
outb(satn[0], tmport++);
outb(satn[1], tmport++);
m = m << i;
dev->wide_idu |= m;
not_wide:
- if ((dev->id[i].devtypeu == 0x00) || (dev->id[i].devtypeu == 0x07)) {
+ if ((dev->id[i].devtypeu == 0x00) || (dev->id[i].devtypeu == 0x07) ||
+ ((dev->id[i].devtypeu == 0x05) && ((n & 0x10) != 0)))
+ {
goto set_sync;
}
continue;
set_sync:
tmport = wkport + 0x1b;
- j = inb(tmport) & 0x0e;
+ j = 0;
if ((m & dev->wide_idu) != 0) {
j |= 0x01;
}
while ((inb(tmport) & 0x80) == 0) {
if ((inb(tmport) & 0x01) != 0) {
tmport -= 0x06;
- if (rmb != 0) {
- outb(synn[j++], tmport);
+ if ((m & dev->wide_idu) != 0) {
+ outb(synw[j++], tmport);
} else {
- if ((m & dev->wide_idu) != 0) {
- outb(synw[j++], tmport);
+ if ((m & dev->ultra_map) != 0) {
+ outb(synu[j++], tmport);
} else {
- if ((m & dev->ultra_map) != 0) {
- outb(synu[j++], tmport);
- } else {
- outb(synn[j++], tmport);
- }
+ outb(synn[j++], tmport);
}
}
tmport += 0x06;
int tmpcnt = 0;
int count = 0;
int result;
-
- static unsigned short devid[7] = {
- 0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0
+
+ static unsigned short devid[8] = {
+ 0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0x8060, 0
};
printk(KERN_INFO "aec671x_detect: \n");
h = 0;
while (devid[h] != 0) {
pdev[2] = pci_find_device(0x1191, devid[h], pdev[2]);
- if (pdev[2] == NULL) {
+ if (pdev[2] == NULL || pci_enable_device(pdev[2])) {
h++;
index = 0;
continue;
}
/* Found an atp870u/w. */
- base_io = pdev[h]->resource[0].start;
+ base_io = pci_resource_start(pdev[h], 0);
irq = pdev[h]->irq;
error = pci_read_config_byte(pdev[h],0x49,&host_id);
printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n");
goto unregister;
}
+
+ if (chip_ver[h] > 0x07) /* check if atp876 chip */
+ { /* then enable terminator */
+ tmport = base_io + 0x3e;
+ outb(0x30, tmport);
+ }
+
tmport = base_io + 0x3a;
k = (inb(tmport) & 0xf3) | 0x10;
outb(k, tmport);
shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
shpnt->irq = irq;
restore_flags(flags);
- request_region(base_io, 0x40, "atp870u"); /* Register the IO ports that we use */
+ request_region(base_io, 0x40, "atp870u"); /* Register the IO ports that we use */
count++;
index++;
continue;
}
panic("Reset bus host not found !");
find_host:
-/* SCpnt->result = 0x00080000;
+/* SCpnt->result = 0x00080000;
SCpnt->scsi_done(SCpnt);
dev->working=0;
dev->quhdu=0;
{
static char buffer[128];
- strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.0+ac ");
+ strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.1+ac ");
return buffer;
}
int atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
{
- return -ENOSYS; /* Currently this is a no-op */
+ return -ENOSYS; /* Currently this is a no-op */
}
#define BLS buffer + len + size
if (offset == 0) {
memset(buff, 0, sizeof(buff));
}
- size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.0+ac\n");
+ size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.1+ac\n");
len += size;
pos = begin + len;
size = 0;
int atp870u_release (struct Scsi_Host *pshost)
{
int h;
- for (h = 0; h <= admaxu; h++)
+ for (h = 0; h <= admaxu; h++)
{
if (pshost == atp_host[h]) {
int k;
}
}
panic("atp870u: bad scsi host passed.\n");
-
+
}
#ifdef MODULE
while ((pdev = pci_find_device(PCI_VENDOR_ID_DOMEX,
PCI_DEVICE_ID_DOMEX_DMX3191D, pdev))) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
- unsigned long port = pdev->base_address[0] & PCI_IOADDRESS_MASK;
-#else
- unsigned long port = pdev->resource[0].start;
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) */
+ unsigned long port = pci_resource_start (pdev, 0);
+
+ if (pci_enable_device(pdev))
+ continue;
if (check_region(port, DMX3191D_REGION)) {
dmx3191d_printk("region 0x%lx-0x%lx already reserved\n",
if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
- if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
+ addr = pci_resource_start (dev, 0);
+
+ pci_enable_device (dev); /* XXX handle error */
#if defined(DEBUG_PCI_DETECT)
printk("%s: tune_pci_port, bus %d, devfn 0x%x, addr 0x%x.\n",
#ifndef CONFIG_PCI
printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
#else
- struct pci_dev *dev;
+ struct pci_dev *dev = NULL;
u32 base, x;
u8 pal1, pal2, pal3;
- for(dev=NULL; dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev);) {
+ while ((dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) {
DBG(DBG_PROBE && DBG_PCI,
printk("eata_dma: find_PCI, HBA at %s\n", dev->name));
+ if (pci_enable_device(dev))
+ continue;
pci_set_master(dev);
- base = dev->resource[0].flags;
- if (!(base & PCI_BASE_ADDRESS_SPACE_IO)) {
+ base = pci_resource_flags(dev, 0);
+ if (base & IORESOURCE_MEM) {
printk("eata_dma: invalid base address of device %s\n", dev->name);
continue;
}
- base = dev->resource[0].start;
+ base = pci_resource_start(dev, 0);
/* EISA tag there ? */
pal1 = inb(base);
pal2 = inb(base + 1);
#ifndef CONFIG_PCI
printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
#else
- struct pci_dev *dev;
+ struct pci_dev *dev = NULL;
u32 base, x;
- for(dev=NULL; dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev);) {
+ while ((dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) {
DBG(DBG_PROBE && DBG_PCI,
printk("eata_pio: find_PCI, HBA at %s\n", dev->name));
+ if (pci_enable_device(dev))
+ continue;
pci_set_master(dev);
- base = dev->resource[0].flags;
- if (!(base & PCI_BASE_ADDRESS_SPACE_IO)) {
+ base = pci_resource_flags(dev, 0);
+ if (base & IORESOURCE_MEM) {
printk("eata_pio: invalid base address of device %s\n", dev->name);
continue;
}
- base = dev->resource[0].start;
+ base = pci_resource_start(dev, 0);
/* EISA tag there ? */
if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
continue; /* Jep, it's forced, so move on */
PCI_DEVICE_ID_FD_36C70,
pdev)) == NULL)
return 0;
+ if (pci_enable_device(pdev)) return 0;
#if DEBUG_DETECT
printk( "scsi: <fdomain> TMC-3260 detect:"
/* We now have the appropriate device function for the FD board so we
just read the PCI config info from the registers. */
- pci_base = pdev->resource[0].start;
+ pci_base = pci_resource_start(pdev, 0);
pci_irq = pdev->irq;
/* Now we have the I/O base address and interrupt from the PCI
pdev = NULL;
while ((pdev = pci_find_device(PCI_VENDOR_ID_VORTEX,device_id,pdev))
!= NULL) {
+ if (pci_enable_device(pdev))
+ continue;
if (cnt >= MAXHA)
return cnt;
/* GDT PCI controller found, resources are already in pdev */
#define DEBUGGING_ON /* enable command-line debugging bitmask */
#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
+#ifdef __i386__
#define FAST_READ_IO /* No problems with these on my machine */
#define FAST_WRITE_IO
+#endif
#ifdef DEBUGGING_ON
#define DB(f,a) if (hostdata->args & (f)) a;
#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
+#ifdef __i386__
/* These inline assembly defines are derived from a patch
* sent to me by Bill Earnest. He's done a lot of very
* valuable thinking, testing, and coding during his effort
: "2" (f), "0" (sp), "1" (i) /* input */ \
); /* trashed */ \
})
+#endif
/* IN2000 io_port offsets */
#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
for (i = 0; i < TULSZ(i91u_pci_devices); i++)
{
while ((pDev = pci_find_device(i91u_pci_devices[i].vendor_id, i91u_pci_devices[i].device_id, pDev)) != NULL) {
+ if (pci_enable_device(pDev))
+ continue;
pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
wBIOS = (UWORD) (dRegValue & 0xFF);
if (((dRegValue & 0xFF00) >> 8) == 0xFF)
pHCB->pSRB_head = NULL; /* Initial SRB save queue */
pHCB->pSRB_tail = NULL; /* Initial SRB save queue */
pHCB->pSRB_lock = SPIN_LOCK_UNLOCKED; /* SRB save queue lock */
- request_region(pHCB->HCS_Base, 0x100, "i91u"); /* Register */
-
get_tulipPCIConfig(pHCB, i);
dBiosAdr = pHCB->HCS_BIOS;
pbBiosAdr = phys_to_virt(dBiosAdr);
init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10);
+ request_region(pHCB->HCS_Base, 256, "i91u"); /* Register */
+
pHCB->HCS_Index = i; /* 7/29/98 */
hreg = scsi_register(tpnt, sizeof(HCS));
hreg->io_port = pHCB->HCS_Base;
printk("\ni91u_panic: %s\n", msg);
panic("i91u panic");
}
+
+/*
+ * Release ressources
+ */
+int i91u_release(struct Scsi_Host *hreg)
+{
+ free_irq(hreg->irq, hreg);
+ release_region(hreg->io_port, 256);
+ return 0;
+}
#include "sd.h"
extern int i91u_detect(Scsi_Host_Template *);
+extern int i91u_release(struct Scsi_Host *);
extern int i91u_command(Scsi_Cmnd *);
extern int i91u_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
extern int i91u_abort(Scsi_Cmnd *);
proc_info: NULL, \
name: i91u_REVID, \
detect: i91u_detect, \
- release: NULL, \
+ release: i91u_release, \
info: NULL, \
command: i91u_command, \
queuecommand: i91u_queue, \
inia100_pci_devices[i].device_id,
pdev)))
{
+ if (pci_enable_device(pdev))
+ continue;
if (iAdapters >= MAX_SUPPORTED_ADAPTERS)
break; /* Never greater than maximum */
*/
bPCIBusNum = pdev->bus->number;
bPCIDeviceNum = pdev->devfn;
- dRegValue = pdev->resource[0].start;
+ dRegValue = pci_resource_start(pdev, 0);
if (dRegValue == -1) { /* Check return code */
printk("\n\rinia100: orchid read configuration error.\n");
return (0); /* Read configuration space error */
}
+
/* <02> read from base address + 0x50 offset to get the wBIOS balue. */
wBASE = (WORD) dRegValue;
- /* Now read the interrupt line */
+ /* Now read the interrupt line value */
dRegValue = pdev->irq;
- bInterrupt = dRegValue & 0xFF; /* Assign interrupt line */
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_write_config_word(pdev, PCI_COMMAND,
- command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+ bInterrupt = dRegValue; /* Assign interrupt line */
- wBASE &= PCI_BASE_ADDRESS_IO_MASK;
wBIOS = ORC_RDWORD(wBASE, 0x50);
+ pci_set_master(pdev);
+
#ifdef MMAPIO
base = wBASE & PAGE_MASK;
page_offset = wBASE - base;
memset((unsigned char *) pHCB->HCS_virEscbArray, 0, sz);
pHCB->HCS_physEscbArray = (U32) VIRT_TO_BUS(pHCB->HCS_virEscbArray);
- request_region(pHCB->HCS_Base, 0x100, "inia100"); /* Register */
get_orcPCIConfig(pHCB, i);
dBiosAdr = pHCB->HCS_BIOS;
printk("inia100: initial orchid fail!!\n");
return (0);
}
+ request_region(pHCB->HCS_Base, 256, "inia100"); /* Register */
+
hreg = scsi_register(tpnt, sizeof(ORC_HCS));
if (hreg == NULL) {
printk("Invalid scsi_register pointer.\n");
/* Initial orc chip */
switch (i) {
case 0:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr0, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr0, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 1:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr1, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr1, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 2:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr2, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr2, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 3:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr3, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr3, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 4:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr4, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr4, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 5:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr5, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr5, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 6:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr6, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr6, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
case 7:
- ok = request_irq(pHCB->HCS_Intr, inia100_intr7, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+ ok = request_irq(pHCB->HCS_Intr, inia100_intr7, SA_INTERRUPT | SA_SHIRQ, "inia100", hreg);
break;
default:
inia100_panic("inia100: Too many host adapters\n");
panic("inia100 panic");
}
+/*
+ * Release ressources
+ */
+int inia100_release(struct Scsi_Host *hreg)
+{
+ free_irq(hreg->irq, hreg);
+ release_region(hreg->io_port, 256);
+ return 0;
+}
+
/*#include "inia100scsi.c" */
#include "sd.h"
extern int inia100_detect(Scsi_Host_Template *);
+extern int inia100_release(struct Scsi_Host *);
extern int inia100_command(Scsi_Cmnd *);
extern int inia100_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
extern int inia100_abort(Scsi_Cmnd *);
proc_info: NULL, \
name: inia100_REVID, \
detect: inia100_detect, \
- release: NULL, \
+ release: inia100_release, \
info: NULL, \
command: inia100_command, \
queuecommand: inia100_queue, \
if (!(dev = pci_find_device(IPS_VENDORID, IPS_DEVICEID, dev)))
break;
-
+ if (pci_enable_device(dev))
+ break;
/* stuff that we get in dev */
irq = dev->irq;
bus = dev->bus->number;
func = dev->devfn;
- io_addr = dev->resource[0].start;
+ io_addr = pci_resource_start(dev, 0);
/* get planer status */
if (pci_read_config_word(dev, 0x04, &planer)) {
}
/* check I/O address */
- if ((dev->resource[0].flags & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+ if (pci_resource_flags(dev, 0) & IORESOURCE_MEM)
continue;
/* check to see if an onboard planer controller is disabled */
struct pci_dev *pdev = NULL;
while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
if ((flag & BOARD_QUARTZ) && (skip_id == -1)) {
u16 magic;
pci_read_config_word(pdev, PCI_CONF_AMISIG, &magic);
}
/* Read the base port and IRQ from PCI */
- megaBase = pdev->resource[0].start;
+ megaBase = pci_resource_start (pdev, 0);
megaIrq = pdev->irq;
- if (flag & BOARD_QUARTZ) {
-
- megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (flag & BOARD_QUARTZ)
megaBase = (long) ioremap (megaBase, 128);
- }
- else {
- megaBase &= PCI_BASE_ADDRESS_IO_MASK;
+ else
megaBase += 0x10;
- }
/* Initialize SCSI Host structure */
host = scsi_register (pHostTmpl, sizeof (mega_host_config));
"megaraid: to protect your data, please upgrade your firmware to version\n"
"megaraid: 3.10 or later, available from the Dell Technical Support web\n"
"megaraid: site at\n"
-"http://support.dell.com/us/en/filelib/download/index.asp?fileid=2489\n");
+"http://support.dell.com/us/en/filelib/download/index.asp?fileid=2940\n");
megaraid_release (host);
#ifdef MODULE
continue;
while ( (pdev = pci_find_device (VENDOR_PSI, DEVICE_ROY_1, pdev)) != NULL )
{
+ if (pci_enable_device(pdev))
+ continue;
pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
padapter = HOSTDATA(pshost);
- padapter->basePort = pdev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
+ padapter->basePort = pci_resource_start (pdev, 1);
DEB (printk ("\nBase Regs = %#04X", padapter->basePort)); // get the base I/O port address
padapter->mb0 = padapter->basePort + RTR_MAILBOX; // get the 32 bit mail boxes
padapter->mb1 = padapter->basePort + RTR_MAILBOX + 4;
memset (&DaleSetup, 0, sizeof (DaleSetup));
memset (DiskMirror, 0, sizeof (DiskMirror));
- zr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
- zl = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK;
+ zr = pci_resource_start (pcidev, 1);
+ zl = pci_resource_start (pcidev, 2);
padapter->basePort = zr;
padapter->regRemap = zr + RTR_LOCAL_REMAP; // 32 bit local space remap
while ( (pcidev = pci_find_device (VENDOR_PSI, DEVICE_DALE_1, pcidev)) != NULL )
{
+ if (pci_enable_device(pcidev))
+ continue;
pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
padapter = HOSTDATA(pshost);
for( i=0; bdp->device_id != 0 && i < NUM_OF_ISP_DEVICES; i++, bdp++ ) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,95)
while ((pdev = pci_find_device(QLA1280_VENDOR_ID,
- bdp->device_id, pdev ) ))
+ bdp->device_id, pdev ) )) {
+ if (pci_enable_device(pdev)) continue;
#else
while (!(pcibios_find_device(QLA1280_VENDOR_ID,
bdp->device_id,
- index++, &pci_bus, &pci_devfn)) )
+ index++, &pci_bus, &pci_devfn)) ) {
#endif
- {
/* found a adapter */
host = scsi_register(template, sizeof(scsi_qla_host_t));
ha = (scsi_qla_host_t *) host->hostdata;
/* Sanitize the information from PCI BIOS. */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,95)
host->irq = pdev->irq;
-/* this depends on release 2.3.18 */
- host->io_port = pdev->resource[0].start;
-/* MRS host->io_port = (unsigned int) pdev->base_address[0]; */
+ host->io_port = pci_resource_start(pdev, 0);
ha->pci_bus = pdev->bus->number;
ha->pci_device_fn = pdev->devfn;
ha->pdev = pdev;
pcibios_read_config_dword(pci_bus, pci_devfn, OFFSET(cfgp->base_port), &piobase);
host->irq = pci_irq;
host->io_port = (unsigned int) piobase;
+ host->io_port &= PCI_BASE_ADDRESS_IO_MASK;
ha->pci_bus = pci_bus;
ha->pci_device_fn = pci_devfn;
#endif
ha->device_id = bdp->device_id;
- host->io_port &= PCI_BASE_ADDRESS_IO_MASK;
ha->devnum = i;
- host->io_port &= PCI_BASE_ADDRESS_IO_MASK;
if( qla1280_mem_alloc(ha) ) {
printk(KERN_INFO "qla1280: Failed to allocate memory for adapter\n");
}
for (i=0; i<2; i++){
while ((pdev = pci_find_device(PCI_VENDOR_ID_QLOGIC, device_ids[i], pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
host = scsi_register(tmpt, sizeof(struct isp2x00_hostdata));
host->max_id = QLOGICFC_MAX_ID + 1;
printk("qlogicfc%d : error reading PCI configuration\n", hostdata->host_id);
return 1;
}
- io_base = pdev->resource[0].start;
+ io_base = pci_resource_start(pdev, 0);
irq = pdev->irq;
while ((pdev = pci_find_device(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020, pdev)))
{
+ if (pci_enable_device(pdev))
+ continue;
+
host = scsi_register(tmpt, sizeof(struct isp1020_hostdata));
hostdata = (struct isp1020_hostdata *) host->hostdata;
return 1;
}
- io_base = pdev->resource[0].start;
- mem_base = pdev->resource[1].start;
- io_flags = pdev->resource[0].flags;
- mem_flags = pdev->resource[1].flags;
+ io_base = pci_resource_start(pdev, 0);
+ mem_base = pci_resource_start(pdev, 1);
+ io_flags = pci_resource_flags(pdev, 0);
+ mem_flags = pci_resource_flags(pdev, 1);
irq = pdev->irq;
if (pdev->vendor != PCI_VENDOR_ID_QLOGIC) {
this_count = this_count >> 2;
}
}
+ if (dpnt->device->sector_size == 4096) {
+ if ((block & 7) || (SCpnt->request.nr_sectors & 7)) {
+ printk("sd.c:Bad block number requested");
+ return 0;
+ } else {
+ block = block >> 3;
+ this_count = this_count >> 3;
+ }
+ }
switch (SCpnt->request.cmd) {
case WRITE:
if (!dpnt->device->writeable) {
if (block_sectors < 4)
block_sectors = 4;
break;
+ case 4096:
+ error_sector <<=3;
+ if (block_sectors < 8)
+ block_sectors = 8;
+ break;
case 256:
error_sector >>= 1;
break;
*/
rscsi_disks[i].capacity = 0;
}
- if (sector_size == 2048) {
+ if (sector_size > 1024) {
int m;
/*
* of partial sectors.
*/
for (m = i << 4; m < ((i + 1) << 4); m++) {
- sd_blocksizes[m] = 2048;
+ sd_blocksizes[m] = sector_size;
}
} {
/*
{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
{"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
{"FUTURE DOMAIN TMC-950", 5, 21, FD},
+ /* Added for 2.2.16 by Matthias_Heidbrink@b.maus.de */
+ {"IBM F1 V1.2009/22/93", 5, 25, FD},
};
#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
# define PCI_PRESENT pci_present ()
# define PCI_SET_MASTER pci_set_master (pdev)
# define PCI_FIND_DEVICE(vend, id) (pdev = pci_find_device (vend, id, pdev))
-# define PCI_GET_IO_AND_IRQ io_port = pdev->resource[0].start; irq = pdev->irq
+# define PCI_GET_IO_AND_IRQ io_port = pci_resource_start(pdev, 0); irq = pdev->irq;
#else
# include <linux/bios32.h>
# define PDEV pbus, pdevfn
if ( PCI_PRESENT )
while (PCI_FIND_DEVICE (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974))
{
+ if (pci_enable_device(pdev))
+ continue;
DC390_LOCK_IO; /* Remove this when going to new eh */
PCI_GET_IO_AND_IRQ;
DEBUG0(printk(KERN_INFO "DC390(%i): IO_PORT=%04x,IRQ=%x\n", dc390_adapterCnt, (UINT) io_port, irq);)
int removed = 0;
prev = NULL;
- for (p = sf->infos; p; prev = p, p = next) {
+ for (p = sf->infos; p; p = next) {
next = p->next;
if (p->type == V_ST_NORMAL &&
p->bank == bank && p->instr == instr) {
sf->num_info--;
removed++;
kfree(p);
- }
+ } else
+ prev = p;
}
+ if (removed)
+ rebuild_preset_list();
return removed;
}
}
break;
case AWE_WR_REPLACE:
- /* replace mode - remoe the instrument if it already exists */
+ /* replace mode - remove the instrument if it already exists */
remove_info(sf, hdr.bank, hdr.instr);
break;
}
if(synthio != -1)
printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n");
#endif
+ if(io==-1||irq==-1||dma==-1)
+ {
+ printk(KERN_ERR "cs4232: Must set io, irq and dma.\n");
+ return -ENODEV;
+ }
cfg.io_base = io;
cfg.irq = irq;
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <linux/vmalloc.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pm.h>
+#include <linux/delay.h>
#include "sound_config.h"
#include "soundmodule.h"
#include "nm256.h"
if (pas_model == 4)
{
- outw(data | (data << 8), (ioaddr ^ translate_code) - 1);
+ outw(data | (data << 8), (ioaddr + translate_code) - 1);
outb((0x80), 0);
} else
pas_write(data, ioaddr);
*
* 06-05-2000 added another card. Daniel M. Newman <dmnewman@pobox.com>
*
+ * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2).
+ * PÃ¥l-Kristian Engstad <engstad@att.net>
+ *
*/
#include <linux/config.h>
ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
0,0,0,0,
0,1,1,-1},
+ {"Sound Blaster 16",
+ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028),
+ ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+ 0,0,0,0,
+ 0,1,1,-1},
{"Sound Blaster 16",
ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029),
ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044),
0,0,0,0,
0,1,1,-1},
+ {"Sound Blaster AWE 64 Gold",
+ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2),
+ ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044),
+ 0,0,0,0,
+ 0,1,1,-1},
{"Sound Blaster AWE 64",
ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1),
ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042),
* For the example we will only initialise the MSS
*/
- iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
- mssbase = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
- mpubase = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+ iobase = pci_resource_start(pcidev, 0);
+ mssbase = pci_resource_start(pcidev, 1);
+ mpubase = pci_resource_start(pcidev, 2);
/*
* Reset the board
while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL)
{
+ if (pci_enable_device(pcidev))
+ continue;
count+=mycard_install(pcidev);
if(count)
return 0;
comment 'Miscellaneous USB options'
bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH
+ else
+ define_bool CONFIG_USB_BANDWIDTH n
+ fi
comment 'USB Controllers'
if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET
dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB
dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV
+ dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI
fi
comment 'USB HID'
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
+obj-$(CONFIG_USB_MICROTEK) += microtek.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
if (!ACM_READY(acm)) return -EINVAL;
if (acm->writeurb.status == -EINPROGRESS) return 0;
+ if (!count) return 0;
count = (count > acm->writesize) ? acm->writesize : count;
dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff);
continue;
}
- for (j = i; j < ms->numch; i++) {
+ for (j = i; j < ms->numch; j++) {
if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
continue;
mask |= 1 << j;
}
/* first generate smask */
smask = bmask = 0;
- for (j = i; j < ms->numch; i++) {
+ for (j = i; j < ms->numch; j++) {
if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
continue;
smask |= 1 << ms->ch[j].osschannel;
continue;
if (j > 1)
srcmask &= ~bmask;
- for (j = i; j < ms->numch; i++) {
+ for (j = i; j < ms->numch; j++) {
if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
continue;
if (!(srcmask & (1 << ms->ch[j].osschannel)))
file->private_data = ms;
s->count++;
- MOD_INC_USE_COUNT;
up(&open_sem);
return 0;
}
down(&open_sem);
release(s);
- MOD_DEC_USE_COUNT;
return 0;
}
}
static /*const*/ struct file_operations usb_mixer_fops = {
+ owner: THIS_MODULE,
llseek: usb_audio_llseek,
ioctl: usb_audio_ioctl_mixdev,
open: usb_audio_open_mixdev,
file->private_data = as;
as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
s->count++;
- MOD_INC_USE_COUNT;
up(&open_sem);
return 0;
}
as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
release(s);
wake_up(&open_wait);
- MOD_DEC_USE_COUNT;
return 0;
}
static /*const*/ struct file_operations usb_audio_fops = {
+ owner: THIS_MODULE,
llseek: usb_audio_llseek,
read: usb_audio_read,
write: usb_audio_write,
state->termtype = 0;
}
+static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid)
+{
+ unsigned int i;
+
+ for (i = 0; i < state->nrmixch; i++)
+ if (state->mixch[i].unitid == unitid)
+ return &state->mixch[i];
+ return NULL;
+}
+
static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector)
{
unsigned int chnum, i, mixch;
usb_audio_recurseunit(state, selector[5]);
if (state->nrmixch != mixch) {
mch = &state->mixch[state->nrmixch-1];
- mch->slctunitid = selector[5] | (1 << 8);
+ mch->slctunitid = selector[3] | (1 << 8);
+ } else if ((mch = slctsrc_findunit(state, selector[5]))) {
+ mch->slctunitid = selector[3] | (1 << 8);
} else {
printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]);
}
}
if (state->nrmixch != mixch) {
mch = &state->mixch[state->nrmixch-1];
- mch->slctunitid = selector[5] | ((i + 1) << 8);
+ mch->slctunitid = selector[3] | ((i + 1) << 8);
+ } else if ((mch = slctsrc_findunit(state, selector[5+i]))) {
+ mch->slctunitid = selector[3] | ((i + 1) << 8);
} else {
printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1);
}
#endif
if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO ||
config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) {
+#if 0
printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n",
dev->descriptor.idVendor, dev->descriptor.idProduct);
+#endif
return NULL;
}
/*
int devnum = MINOR (inode->i_rdev);
pdabusb_t s;
- if (devnum < DABUSB_MINOR || devnum > (DABUSB_MINOR + NRDABUSB))
+ if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB))
return -EIO;
- MOD_INC_USE_COUNT;
s = &dabusb[devnum - DABUSB_MINOR];
dbg("dabusb_open");
up (&s->mutex);
if (file->f_flags & O_NONBLOCK) {
- MOD_DEC_USE_COUNT;
return -EBUSY;
}
schedule_timeout (HZ / 2);
if (signal_pending (current)) {
- MOD_DEC_USE_COUNT;
return -EAGAIN;
}
down (&s->mutex);
}
if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
err("set_interface failed");
- MOD_DEC_USE_COUNT;
return -EINVAL;
}
s->opened = 1;
else
wake_up (&s->remove_ok);
- MOD_DEC_USE_COUNT;
s->opened = 0;
return 0;
}
static struct file_operations dabusb_fops =
{
+ owner: THIS_MODULE,
llseek: dabusb_llseek,
read: dabusb_read,
ioctl: dabusb_ioctl,
#define _DABUSB_IF 2
-#define _DABUSB_ISOPIPE 0x89
+#define _DABUSB_ISOPIPE 0x09
#define _ISOPIPESIZE 16384
#define _BULK_DATA_LEN 64
/* table of cameras that work through this driver */
static const struct camera {
- short idVendor;
- short idProduct;
+ unsigned short idVendor;
+ unsigned short idProduct;
/* plus hooks for camera-specific info if needed */
} cameras [] = {
/* These have the same application level protocol */
{ 0x040a, 0x0110 }, // Kodak DC-260
{ 0x040a, 0x0111 }, // Kodak DC-265
{ 0x040a, 0x0112 }, // Kodak DC-290
-// { 0x03f0, 0xffff }, // HP PhotoSmart C500
+ { 0xf003, 0x6002 }, // HP PhotoSmart C500
/* Other USB devices may well work here too, so long as they
* just stick to half duplex bulk packet exchanges. That
}
dbg ("open");
-
- /* Keep driver from being unloaded while it's in use */
- MOD_INC_USE_COUNT;
camera->isActive = 0;
file->private_data = camera;
kfree (camera);
}
- MOD_DEC_USE_COUNT;
-
dbg ("close");
return 0;
*/
static /* const */ struct file_operations usb_camera_fops = {
/* Uses GCC initializer extension; simpler to maintain */
+ owner: THIS_MODULE,
read: camera_read,
write: camera_write,
open: camera_open,
ssize_t ret = 0;
unsigned len;
loff_t pos;
+ int i;
pos = *ppos;
down_read(&ps->devsem);
- if (!ps->dev)
+ if (!ps->dev) {
ret = -ENODEV;
- else if (pos < 0)
+ goto err;
+ } else if (pos < 0) {
ret = -EINVAL;
- else if (pos < sizeof(struct usb_device_descriptor)) {
+ goto err;
+ }
+
+ if (pos < sizeof(struct usb_device_descriptor)) {
len = sizeof(struct usb_device_descriptor) - pos;
if (len > nbytes)
len = nbytes;
- if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len))
+ if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) {
ret = -EFAULT;
- else {
+ goto err;
+ }
+
+ *ppos += len;
+ buf += len;
+ nbytes -= len;
+ ret += len;
+ }
+
+ pos = sizeof(struct usb_device_descriptor);
+ for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) {
+ struct usb_config_descriptor *config =
+ (struct usb_config_descriptor *)ps->dev->rawdescriptors[i];
+ unsigned int length = le16_to_cpu(config->wTotalLength);
+
+ if (*ppos < pos + length) {
+ len = length - (*ppos - pos);
+ if (len > nbytes)
+ len = nbytes;
+
+ if (copy_to_user(buf,
+ ps->dev->rawdescriptors[i] + (*ppos - pos), len)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
*ppos += len;
buf += len;
nbytes -= len;
ret += len;
}
+
+ pos += length;
}
+
+err:
up_read(&ps->devsem);
return ret;
}
}
struct usb_driver usbdevfs_driver = {
- "usbdevfs",
- driver_probe,
- driver_disconnect,
- LIST_HEAD_INIT(usbdevfs_driver.driver_list),
- NULL,
- 0
+ name: "usbdevfs",
+ probe: driver_probe,
+ disconnect: driver_disconnect,
};
static int claimintf(struct dev_state *ps, unsigned int intf)
return -ENOENT;
}
+extern struct list_head usb_driver_list;
+
+static int finddriver(struct usb_driver **driver, char *name)
+{
+ struct list_head *tmp;
+
+ tmp = usb_driver_list.next;
+ while (tmp != &usb_driver_list) {
+ struct usb_driver *d = list_entry(tmp, struct usb_driver,
+ driver_list);
+
+ if (!strcmp(d->name, name)) {
+ *driver = d;
+ return 0;
+ }
+
+ tmp = tmp->next;
+ }
+
+ return -EINVAL;
+}
+
/*
* file operations
*/
return 0;
}
+static int proc_getdriver(struct dev_state *ps, void *arg)
+{
+ struct usbdevfs_getdriver gd;
+ struct usb_interface *interface;
+ int ret;
+
+ copy_from_user_ret(&gd, arg, sizeof(gd), -EFAULT);
+ if ((ret = findintfif(ps->dev, gd.interface)) < 0)
+ return ret;
+ interface = usb_ifnum_to_if(ps->dev, gd.interface);
+ if (!interface)
+ return -EINVAL;
+ if (!interface->driver)
+ return -ENODATA;
+ strcpy(gd.driver, interface->driver->name);
+ copy_to_user_ret(arg, &gd, sizeof(gd), -EFAULT);
+ return 0;
+}
+
+static int proc_connectinfo(struct dev_state *ps, void *arg)
+{
+ struct usbdevfs_connectinfo ci;
+
+ ci.devnum = ps->dev->devnum;
+ ci.slow = ps->dev->slow;
+ copy_to_user_ret(arg, &ci, sizeof(ci), -EFAULT);
+ return 0;
+}
+
+static int proc_resetdevice(struct dev_state *ps)
+{
+ int i, ret;
+
+ ret = usb_reset_device(ps->dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *intf = &ps->dev->actconfig->interface[i];
+
+ /* Don't simulate interfaces we've claimed */
+ if (test_bit(i, &ps->ifclaimed))
+ continue;
+
+ if (intf->driver) {
+ down(&intf->driver->serialize);
+ intf->driver->disconnect(ps->dev, intf->private_data);
+ intf->driver->probe(ps->dev, i);
+ up(&intf->driver->serialize);
+ }
+ }
+
+ return 0;
+}
+
static int proc_setintf(struct dev_state *ps, void *arg)
{
struct usbdevfs_setinterface setintf;
+ struct usb_interface *interface;
int ret;
copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT);
if ((ret = findintfif(ps->dev, setintf.interface)) < 0)
return ret;
- if ((ret = checkintf(ps, ret)))
- return ret;
+ interface = usb_ifnum_to_if(ps->dev, setintf.interface);
+ if (!interface)
+ return -EINVAL;
+ if (interface->driver) {
+ if ((ret = checkintf(ps, ret)))
+ return ret;
+ }
if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting))
return -EINVAL;
return 0;
return releaseintf(ps, intf);
}
+static int proc_ioctl (struct dev_state *ps, void *arg)
+{
+ struct usbdevfs_ioctl ctrl;
+ int size;
+ void *buf = 0;
+ int retval = 0;
+
+ /* get input parameters and alloc buffer */
+ copy_from_user_ret (&ctrl, (void *) arg, sizeof (ctrl), -EFAULT);
+ if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
+ if ((buf = kmalloc (size, GFP_KERNEL)) == 0)
+ return -ENOMEM;
+ if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0
+ && copy_from_user (buf, ctrl.data, size) != 0) {
+ kfree (buf);
+ return -EFAULT;
+ } else
+ memset (arg, size, 0);
+ }
+
+ /* ioctl to device */
+ if (ctrl.ifno < 0) {
+ switch (ctrl.ioctl_code) {
+ /* access/release token for issuing control messages
+ * ask a particular driver to bind/unbind, ... etc
+ */
+ }
+ retval = -ENOSYS;
+
+ /* ioctl to the driver which has claimed a given interface */
+ } else {
+ struct usb_interface *ifp = 0;
+ if (!ps->dev)
+ retval = -ENODEV;
+ else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces)
+ retval = -EINVAL;
+ else {
+ if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
+ retval = -EINVAL;
+ else if (ifp->driver == 0 || ifp->driver->ioctl == 0)
+ retval = -ENOSYS;
+ }
+ if (retval == 0)
+ /* ifno might usefully be passed ... */
+ retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf);
+ /* size = min (size, retval)? */
+ }
+
+ /* cleanup and return */
+ if (retval >= 0
+ && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
+ && size > 0
+ && copy_to_user (ctrl.data, buf, size) != 0)
+ retval = -EFAULT;
+ if (buf != 0)
+ kfree (buf);
+ return retval;
+}
+
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
inode->i_mtime = CURRENT_TIME;
break;
+ case USBDEVFS_RESET:
+ ret = proc_resetdevice(ps);
+ break;
+
+ case USBDEVFS_GETDRIVER:
+ ret = proc_getdriver(ps, (void *)arg);
+ break;
+
+ case USBDEVFS_CONNECTINFO:
+ ret = proc_connectinfo(ps, (void *)arg);
+ break;
+
case USBDEVFS_SETINTERFACE:
ret = proc_setintf(ps, (void *)arg);
break;
ret = proc_releaseinterface(ps, (void *)arg);
break;
+ case USBDEVFS_IOCTL:
+ ret = proc_ioctl(ps, (void *) arg);
+ break;
}
up_read(&ps->devsem);
if (ret >= 0)
History:
+ Version 0.23:
+ Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
+
Version 0.22:
Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
thanks to Mike Cox for pointing the problem out.
typedef struct
{ struct urb readurb,writeurb;
struct usb_device *dev;
- char transfer_buffer[TB_LEN];
+ unsigned char transfer_buffer[TB_LEN];
int curfreq;
int stereo;
int ifnum;
/*
- * evdev.c Version 0.1
+ * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* Event char devices, giving access to raw input device events.
*
list->buffer[list->head].value = value;
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
- if (list->fasync)
- kill_fasync(list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
list = list->next;
}
kfree(list);
- MOD_DEC_USE_COUNT;
return 0;
}
if (i > EVDEV_MINORS || !evdev_table[i])
return -ENODEV;
- MOD_INC_USE_COUNT;
-
if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) {
- MOD_DEC_USE_COUNT;
return -ENOMEM;
}
memset(list, 0, sizeof(struct evdev_list));
struct evdev_list *list = file->private_data;
struct evdev *evdev = list->evdev;
struct input_dev *dev = evdev->handle.dev;
+ int retval;
switch (cmd) {
case EVIOCGVERSION:
- return put_user(EV_VERSION, (__u32 *) arg);
+ return put_user(EV_VERSION, (int *) arg);
+
case EVIOCGID:
- return copy_to_user(&dev->id, (void *) arg,
- sizeof(struct input_id)) ? -EFAULT : 0;
+ if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval;
+ if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval;
+ if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval;
+ if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval;
+ return 0;
+
default:
if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
- long *bits = NULL;
- int len = 0;
+ long *bits;
+ int len;
switch (_IOC_NR(cmd) & EV_MAX) {
case 0: bits = dev->evbit; len = EV_MAX; break;
default: return -EINVAL;
}
len = NBITS(len) * sizeof(long);
- if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
- return copy_to_user((void *) arg, bits, len) ? -EFAULT : len;
+ if (len > _IOC_SIZE(cmd)) {
+ printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n",
+ len, _IOC_SIZE(cmd));
+ len = _IOC_SIZE(cmd);
+ }
+ return copy_to_user((char *) arg, bits, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
- int len = strlen(dev->name) + 1;
+ int len;
+ if (!dev->name) return 0;
+ len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len;
}
+
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+ int t = _IOC_NR(cmd) & ABS_MAX;
+
+ if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval;
+ if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval;
+ if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval;
+ if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval;
+ if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval;
+
+ return 0;
+ }
}
return -EINVAL;
}
static struct file_operations evdev_fops = {
+ owner: THIS_MODULE,
read: evdev_read,
write: evdev_write,
poll: evdev_poll,
evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE);
- printk("event%d: Event device for input%d\n", minor, dev->number);
+ printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number);
return &evdev->handle;
}
/*
- * driver/usb/hid-debug.h
+ * $Id: hid-debug.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $
*
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
* (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
/*
- * hid.c Version 0.8
+ * $Id: hid.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000 Vojtech Pavlik
__s32 y;
} hid_hat_to_axis[] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0, 0}};
+static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
+ "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+
/*
* Register a new report for a device.
*/
return 0;
}
+static int hid_open(struct input_dev *dev)
+{
+ struct hid_device *hid = dev->private;
+
+ if (hid->open++)
+ return 0;
+
+ if (usb_submit_urb(&hid->urb))
+ return -EIO;
+
+ return 0;
+}
+
+static void hid_close(struct input_dev *dev)
+{
+ struct hid_device *hid = dev->private;
+
+ if (!--hid->open)
+ usb_unlink_urb(&hid->urb);
+}
+
/*
* Configure the input layer interface
* Read all reports and initalize the absoulte field values.
hid->input.private = hid;
hid->input.event = hid_event;
+ hid->input.open = hid_open;
+ hid->input.close = hid_close;
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
{ 0, 0 }
};
-static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
+static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, char *name)
{
struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0;
struct hid_descriptor *hdesc;
FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval);
- if (usb_submit_urb(&hid->urb)) {
- dbg("submitting interrupt URB failed");
- continue;
- }
-
break;
}
hid->dr.index = hid->ifnum;
hid->dr.length = 1;
+ hid->input.name = hid->name;
+ hid->input.idbus = BUS_USB;
+ hid->input.idvendor = dev->descriptor.idVendor;
+ hid->input.idproduct = dev->descriptor.idProduct;
+ hid->input.idversion = dev->descriptor.bcdDevice;
+
+ if (strlen(name))
+ strcpy(hid->name, name);
+ else
+ sprintf(hid->name, "USB HID %s %04x:%04x",
+ ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ?
+ hid_types[hid->application & 0xffff] : "Device",
+ hid->input.idvendor, hid->input.idproduct);
+
FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0),
(void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid);
static void* hid_probe(struct usb_device *dev, unsigned int ifnum)
{
- char *hid_name[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
- "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
struct hid_device *hid;
+ char name[128];
+ char *buf;
dbg("HID probe called for ifnum %d", ifnum);
- if (!(hid = usb_hid_configure(dev, ifnum)))
+ name[0] = 0;
+
+ if (!(buf = kmalloc(63, GFP_KERNEL)))
+ return NULL;
+
+ if (dev->descriptor.iManufacturer &&
+ usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+ strcat(name, buf);
+ if (dev->descriptor.iProduct &&
+ usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ sprintf(name, "%s %s", name, buf);
+
+ kfree(buf);
+
+ if (!(hid = usb_hid_configure(dev, ifnum, name)))
return NULL;
hid_dump_device(hid);
hid_init_input(hid);
input_register_device(&hid->input);
- printk(KERN_INFO "input%d: USB HID v%x.%02x %s\n",
- hid->input.number, hid->version >> 8, hid->version & 0xff,
+ printk(KERN_INFO "input%d: USB HID v%x.%02x %s",
+ hid->input.number,
+ hid->version >> 8, hid->version & 0xff,
((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ?
- hid_name[hid->application & 0xffff] : "device");
+ hid_types[hid->application & 0xffff] : "Device");
+
+ if (strlen(name))
+ printk(" [%s]", name);
+ else
+ printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct);
+
+ printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum);
return hid;
}
#define __HID_H
/*
- * drivers/usb/hid.h Version 0.8
+ * $Id: hid.h,v 1.4 2000/05/29 09:01:52 vojtech Exp $
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000 Vojtech Pavlik
struct urb urb; /* USB URB structure */
struct urb urbout; /* Output URB */
struct input_dev input; /* input device structure */
+ int open; /* is the device open by input? */
int quirks; /* Various nasty tricks the device can pull on us */
+ char name[128]; /* Device name */
};
#define HID_GLOBAL_STACK_SIZE 4
#undef DEBUG
#endif
#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
/* Wakes up khubd */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(usb_address0_sem);
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */
static int usb_hub_configure(struct usb_hub *hub)
{
struct usb_device *dev = hub->dev;
- unsigned char buffer[4], *bitmap;
+ unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap;
struct usb_hub_descriptor *descriptor;
struct usb_descriptor_header *header;
struct usb_hub_status *hubsts;
- int i;
-
- /* Get the length first */
- if (usb_get_hub_descriptor(dev, buffer, 4) < 0)
- return -1;
+ int i, ret;
+ /* Request the entire hub descriptor. */
header = (struct usb_descriptor_header *)buffer;
- bitmap = kmalloc(header->bLength, GFP_KERNEL);
- if (!bitmap)
+ ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer));
+ /* <buffer> is large enough for a hub with 127 ports;
+ * the hub can/will return fewer bytes here. */
+ if (ret < 0) {
+ err("Unable to get hub descriptor (err = %d)", ret);
return -1;
+ }
- if (usb_get_hub_descriptor(dev, bitmap, header->bLength) < 0) {
- kfree(bitmap);
+ bitmap = kmalloc(header->bLength, GFP_KERNEL);
+ if (!bitmap) {
+ err("Unable to kmalloc %d bytes for bitmap", header->bLength);
return -1;
}
+ memcpy (bitmap, buffer, header->bLength);
descriptor = (struct usb_hub_descriptor *)bitmap;
hub->nports = dev->maxchild = descriptor->bNbrPorts;
kfree(bitmap);
- if (usb_get_hub_status(dev, buffer) < 0)
+ ret = usb_get_hub_status(dev, buffer);
+ if (ret < 0) {
+ err("Unable to get hub status (err = %d)", ret);
return -1;
+ }
hubsts = (struct usb_hub_status *)buffer;
dbg("local power source is %s",
endpoint = &interface->endpoint[0];
/* Output endpoint? Curiousier and curiousier.. */
- if (!(endpoint->bEndpointAddress & USB_DIR_IN))
+ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {
+ err("Device is hub class, but has output endpoint?");
return NULL;
+ }
/* If it's not an interrupt endpoint, we'd better punt! */
- if ((endpoint->bmAttributes & 3) != 3)
+ if ((endpoint->bmAttributes & 3) != 3) {
+ err("Device is hub class, but has endpoint other than interrupt?");
return NULL;
+ }
/* We found a hub */
info("USB hub found");
kfree(hub);
}
+static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data)
+{
+ /* assert ifno == 0 (part of hub spec) */
+ switch (code) {
+ case USBDEVFS_HUB_PORTINFO: {
+ struct usbdevfs_hub_portinfo *info = user_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave (&hub_event_lock, flags);
+ if (hub->devnum <= 0)
+ info->nports = 0;
+ else {
+ info->nports = hub->maxchild;
+ for (i = 0; i < info->nports; i++) {
+ if (hub->children [i] == NULL)
+ info->port [i] = 0;
+ else
+ info->port [i] = hub->children [i]->devnum;
+ }
+ }
+ spin_unlock_irqrestore (&hub_event_lock, flags);
+
+ return info->nports + 1;
+ }
+
+ default:
+ return -ENOSYS;
+ }
+}
+
static void usb_hub_port_connect_change(struct usb_device *hub, int port)
{
struct usb_device *usb;
struct usb_port_status portsts;
unsigned short portstatus, portchange;
- int tries;
+ int ret, tries;
wait_ms(100);
- /* Check status */
- if (usb_get_port_status(hub, port + 1, &portsts)<0) {
- err("get_port_status failed");
+
+ ret = usb_get_port_status(hub, port + 1, &portsts);
+ if (ret < 0) {
+ err("get_port_status(%d) failed (err = %d)", port + 1, ret);
return;
}
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return;
}
- wait_ms(400);
+ wait_ms(400);
- /* Reset the port */
+ down(&usb_address0_sem);
#define MAX_TRIES 5
-
- for(tries=0;tries<MAX_TRIES;tries++) {
-
+ /* Reset the port */
+ for (tries = 0; tries < MAX_TRIES ; tries++) {
usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
- wait_ms(200);
-
- if (usb_get_port_status(hub, port + 1, &portsts)<0) {
- err("get_port_status failed");
- return;
+ wait_ms(200);
+
+ ret = usb_get_port_status(hub, port + 1, &portsts);
+ if (ret < 0) {
+ err("get_port_status(%d) failed (err = %d)", port + 1, ret);
+ goto out;
}
+
portstatus = le16_to_cpu(portsts.wPortStatus);
portchange = le16_to_cpu(portsts.wPortChange);
dbg("portstatus %x, change %x, %s", portstatus ,portchange,
if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
!(portstatus & USB_PORT_STAT_CONNECTION))
- return;
+ goto out;
if (portstatus & USB_PORT_STAT_ENABLE)
break;
wait_ms(200);
}
- if (tries==MAX_TRIES) {
+ if (tries >= MAX_TRIES) {
err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES);
err("Maybe the USB cable is bad?");
- return;
+ goto out;
}
usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
/* Allocate a new device struct for it */
-
usb = usb_alloc_dev(hub, hub->bus);
if (!usb) {
err("couldn't allocate usb_device");
- return;
+ goto out;
}
usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
usb_connect(usb);
/* Run it through the hoops (find a driver, etc) */
- if (usb_new_device(usb)) {
- usb_disconnect(&hub->children[port]);
- /* Woops, disable the port */
- dbg("hub: disabling port %d", port + 1);
- usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
+ ret = usb_new_device(usb);
+ if (ret) {
+ /* Try resetting the device. Windows does this and it */
+ /* gets some devices working correctly */
+ usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+ ret = usb_new_device(usb);
+ if (ret) {
+ usb_disconnect(&hub->children[port]);
+
+ /* Woops, disable the port */
+ dbg("hub: disabling port %d", port + 1);
+ usb_clear_port_feature(hub, port + 1,
+ USB_PORT_FEAT_ENABLE);
+ }
}
+
+out:
+ up(&usb_address0_sem);
}
static void usb_hub_events(void)
static int usb_hub_thread(void *__hub)
{
-/*
- MOD_INC_USE_COUNT;
-*/
-
khubd_running = 1;
lock_kernel();
interruptible_sleep_on(&khubd_wait);
} while (!signal_pending(current));
-/*
- MOD_DEC_USE_COUNT;
-*/
-
dbg("usb_hub_thread exiting");
khubd_running = 0;
}
static struct usb_driver hub_driver = {
- "hub",
- hub_probe,
- hub_disconnect,
- { NULL, NULL }
+ name: "hub",
+ probe: hub_probe,
+ ioctl: hub_ioctl,
+ disconnect: hub_disconnect
};
/*
{
int pid;
- if (usb_register(&hub_driver) < 0)
+ if (usb_register(&hub_driver) < 0) {
+ err("Unable to register USB hub driver");
return -1;
+ }
pid = kernel_thread(usb_hub_thread, NULL,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
+/*
+ * WARNING - If a driver calls usb_reset_device, you should simulate a
+ * disconnect() and probe() for other interfaces you doesn't claim. This
+ * is left up to the driver writer right now. This insures other drivers
+ * have a chance to re-setup their interface.
+ *
+ * Take a look at proc_resetdevice in devio.c for some sample code to
+ * do this.
+ */
int usb_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
- int i;
+ struct usb_device_descriptor descriptor;
+ int i, ret, port = -1;
if (!parent) {
err("attempting to reset root hub!");
return -EINVAL;
}
- for (i = 0; i < parent->maxchild; i++) {
+ for (i = 0; i < parent->maxchild; i++)
if (parent->children[i] == dev) {
- usb_set_port_feature(parent, i + 1,
- USB_PORT_FEAT_RESET);
+ port = i;
+ break;
+ }
+
+ if (port < 0)
+ return -ENOENT;
+
+ down(&usb_address0_sem);
+
+ /* Send a reset to the device */
+ usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET);
+
+ wait_ms(200);
- usb_disconnect(&dev);
- usb_hub_port_connect_change(parent, i);
+ usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET);
- return 0;
+ /* Reprogram the Address */
+ ret = usb_set_address(dev);
+ if (ret < 0) {
+ err("USB device not accepting new address (error=%d)", ret);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ up(&usb_address0_sem);
+ return ret;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ up(&usb_address0_sem);
+
+ /*
+ * Now we fetch the configuration descriptors for the device and
+ * see if anything has changed. If it has, we dump the current
+ * parsed descriptors and reparse from scratch. Then we leave
+ * the device alone for the caller to finish setting up.
+ *
+ * If nothing changed, we reprogram the configuration and then
+ * the alternate settings.
+ */
+ ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor,
+ sizeof(descriptor));
+ if (ret < 0)
+ return ret;
+
+ le16_to_cpus(&descriptor.bcdUSB);
+ le16_to_cpus(&descriptor.idVendor);
+ le16_to_cpus(&descriptor.idProduct);
+ le16_to_cpus(&descriptor.bcdDevice);
+
+ if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) {
+ usb_destroy_configuration(dev);
+
+ ret = usb_get_device_descriptor(dev);
+ if (ret < sizeof(dev->descriptor)) {
+ if (ret < 0)
+ err("unable to get device descriptor (error=%d)", ret);
+ else
+ err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret);
+
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return -EIO;
+ }
+
+ ret = usb_get_configuration(dev);
+ if (ret < 0) {
+ err("unable to get configuration (error=%d)", ret);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
}
+
+ dev->actconfig = dev->config;
+ usb_set_maxpacket(dev);
+
+ return 1;
+ } else {
+ ret = usb_set_configuration(dev,
+ dev->actconfig->bConfigurationValue);
+ if (ret < 0) {
+ err("failed to set active configuration (error=%d)",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+ struct usb_interface *intf =
+ &dev->actconfig->interface[i];
+ struct usb_interface_descriptor *as =
+ &intf->altsetting[intf->act_altsetting];
+
+ ret = usb_set_interface(dev, as->bInterfaceNumber,
+ as->bAlternateSetting);
+ if (ret < 0) {
+ err("failed to set active alternate setting for interface %d (error=%d)", i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
}
- return -ENOENT;
+ return 0;
}
#define HUB_CHANGE_LOCAL_POWER 0x0001
#define HUB_CHANGE_OVERCURRENT 0x0002
+#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */
+
/* Hub descriptor */
struct usb_hub_descriptor {
__u8 bLength;
/*
- * input.c Version 0.1
+ * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* The input layer module itself
*
case EV_ABS:
- if (code > ABS_MAX || !test_bit(code, dev->absbit) || (value == dev->abs[code]))
+ if (code > ABS_MAX || !test_bit(code, dev->absbit))
return;
- dev->abs[code] = value;
+ if (dev->absfuzz[code]) {
+ if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+ return;
+
+ if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+ (value < dev->abs[code] + dev->absfuzz[code]))
+ value = (dev->abs[code] * 3 + value) >> 2;
+ if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+ value = (dev->abs[code] + value) >> 1;
+ }
+
+ if (dev->abs[code] == value)
+ return;
+
+ dev->abs[code] = value;
break;
case EV_REL:
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5];
+ struct file_operations *old_fops;
+ int err;
if (!handler || !handler->fops || !handler->fops->open)
return -ENODEV;
- file->f_op = handler->fops;
-
- return handler->fops->open(inode, file);
+ old_fops = file->f_op;
+ file->f_op = fops_get(handler->fops);
+ err = handler->fops->open(inode, file);
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ return err;
}
static struct file_operations input_fops = {
+ owner: THIS_MODULE,
open: input_open_file,
};
/*
- * joydev.c Version 0.1
+ * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
* Copyright (c) 1999 Colin Van Dyke
*
* Joystick device driver for the input driver suite.
int used;
int open;
int minor;
- char name[32];
struct input_handle handle;
wait_queue_head_t wait;
devfs_handle_t devfs;
if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
list->startup = 0;
- if (list->fasync)
- kill_fasync(list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
list = list->next;
}
kfree(list);
- MOD_DEC_USE_COUNT;
return 0;
}
if (i > JOYDEV_MINORS || !joydev_table[i])
return -ENODEV;
- MOD_INC_USE_COUNT;
-
if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) {
- MOD_DEC_USE_COUNT;
return -ENOMEM;
}
memset(list, 0, sizeof(struct joydev_list));
{
struct joydev_list *list = file->private_data;
struct joydev *joydev = list->joydev;
+ struct input_dev *dev = joydev->handle.dev;
+
switch (cmd) {
sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
default:
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
- int len = strlen(joydev->name) + 1;
+ int len;
+ if (!dev->name) return 0;
+ len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
- if (copy_to_user((char *) arg, joydev->name, len)) return -EFAULT;
+ if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT;
return len;
}
}
}
static struct file_operations joydev_fops = {
+ owner: THIS_MODULE,
read: joydev_read,
write: joydev_write,
poll: joydev_poll,
init_waitqueue_head(&joydev->wait);
- sprintf(joydev->name, "joydev%d", joydev->minor);
-
joydev->minor = minor;
joydev_table[minor] = joydev;
joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE);
- printk("js%d: Joystick device for input%d\n", minor, dev->number);
+ printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number);
return &joydev->handle;
}
/*
- * keybdev.c Version 0.1
+ * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* Input driver to keyboard driver binding.
*
input_open_device(handle);
- printk("keybdev.c: Adding keyboard: input%d\n", dev->number);
+ printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
return handle;
}
static void keybdev_disconnect(struct input_handle *handle)
{
- printk("keybdev.c: Removing keyboard: input%d\n", handle->dev->number);
+ printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number);
input_close_device(handle);
kfree(handle);
}
{
int retval=0;
- MOD_INC_USE_COUNT;
-
if (mdc800->state == NOT_CONNECTED)
{
- MOD_DEC_USE_COUNT;
return -EBUSY;
}
if (mdc800->open)
{
- MOD_DEC_USE_COUNT;
return -EBUSY;
}
if (usb_submit_urb (mdc800->irq_urb))
{
err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
- MOD_DEC_USE_COUNT;
return -EIO;
}
retval=-EIO;
}
- MOD_DEC_USE_COUNT;
-
return retval;
}
/* File Operations of this drivers */
static struct file_operations mdc800_device_ops =
{
+ owner: THIS_MODULE,
read: mdc800_device_read,
write: mdc800_device_write,
open: mdc800_device_open,
--- /dev/null
+/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
+ *
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * Parts shamelessly stolen from usb-storage and copyright by their
+ * authors. Thanks to Matt Dharm for giving us permission!
+ *
+ * This driver implements a SCSI host controller driver and a USB
+ * device driver. To avoid confusion, all the USB related stuff is
+ * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
+ *
+ * Microtek (www.microtek.com) did not release the specifications for
+ * their USB protocol to us, so we had to reverse engineer them. We
+ * don't know for which models they are valid.
+ *
+ * The X6 USB has three bulk endpoints, one output (0x1) down which
+ * commands and outgoing data are sent, and two input: 0x82 from which
+ * normal data is read from the scanner (in packets of maximum 32
+ * bytes) and from which the status byte is read, and 0x83 from which
+ * the results of a scan (or preview) are read in up to 64 * 1024 byte
+ * chunks by the Windows driver. We don't know how much it is possible
+ * to read at a time from 0x83.
+ *
+ * It seems possible to read (with URB transfers) everything from 0x82
+ * in one go, without bothering to read in 32 byte chunks.
+ *
+ * There seems to be an optimisation of a further READ implicit if
+ * you simply read from 0x83.
+ *
+ * Guessed protocol:
+ *
+ * Send raw SCSI command to EP 0x1
+ *
+ * If there is data to receive:
+ * If the command was READ datatype=image:
+ * Read a lot of data from EP 0x83
+ * Else:
+ * Read data from EP 0x82
+ * Else:
+ * If there is data to transmit:
+ * Write it to EP 0x1
+ *
+ * Read status byte from EP 0x82
+ *
+ * References:
+ *
+ * The SCSI command set for the scanner is available from
+ * ftp://ftp.microtek.com/microtek/devpack/
+ *
+ * Microtek NV sent us a more up to date version of the document. If
+ * you want it, just send mail.
+ *
+ * Status:
+ *
+ * This driver does not work properly yet.
+ * Untested with multiple scanners.
+ * Untested on SMP.
+ * Untested on UHCI.
+ *
+ * History:
+ *
+ * 20000417 starting history
+ * 20000417 fixed load oops
+ * 20000417 fixed unload oops
+ * 20000419 fixed READ IMAGE detection
+ * 20000424 started conversion to use URBs
+ * 20000502 handled short transfers as errors
+ * 20000513 rename and organisation of functions (john)
+ * 20000513 added IDs for all products supported by Windows driver (john)
+ * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
+ * 20000514 Version 0.0.8j
+ * 20000514 Fix reporting of non-existant devices to SCSI layer (john)
+ * 20000514 Added MTS_DEBUG_INT (john)
+ * 20000514 Changed "usb-microtek" to "microtek" for consistency (john)
+ * 20000514 Stupid bug fixes (john)
+ * 20000514 Version 0.0.9j
+ * 20000515 Put transfer context and URB in mts_desc (john)
+ * 20000515 Added prelim turn off debugging support (john)
+ * 20000515 Version 0.0.10j
+ * 20000515 Fixed up URB allocation (clear URB on alloc) (john)
+ * 20000515 Version 0.0.11j
+ * 20000516 Removed unnecessary spinlock in mts_transfer_context (john)
+ * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
+ * 20000516 Implemented (badly) scsi_abort (john)
+ * 20000516 Version 0.0.12j
+ * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
+ * 20000517 Added mts_debug_dump to print ll USB info (john)
+ * 20000518 Tweaks and documentation updates (john)
+ * 20000518 Version 0.0.13j
+ * 20000518 Cleaned up abort handling (john)
+ * 20000523 Removed scsi_command and various scsi_..._resets (john)
+ * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
+ * 20000523 Fixed last tiresome compile warning (john)
+ * 20000523 Version 0.0.14j (though version 0.1 has come out?)
+ * 20000602 Added primitive reset
+ * 20000602 Version 0.2.0
+ * 20000603 various cosmetic changes
+ * 20000603 Version 0.2.1
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/proc_fs.h>
+
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "microtek.h"
+
+/* Constants */
+
+#define MTS_ABORT_TIMEOUT HZ /*jiffies*/
+
+
+/* Should we do debugging? */
+
+#define MTS_DO_DEBUG
+
+
+/* USB layer driver interface */
+
+static void *mts_usb_probe(struct usb_device *dev, unsigned int interface);
+static void mts_usb_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver mts_usb_driver = {
+ "microtek",
+ mts_usb_probe,
+ mts_usb_disconnect,
+ { NULL, NULL } /* we need no fops. let gcc fill it */
+};
+
+
+/* Internal driver stuff */
+
+#define MTS_VERSION "0.2.1"
+#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
+
+#define MTS_WARNING(x...) \
+ printk( KERN_WARNING MTS_NAME ## x )
+#define MTS_ERROR(x...) \
+ printk( KERN_ERR MTS_NAME ## x )
+#define MTS_INT_ERROR(x...) \
+ MTS_ERROR( ## x )
+#define MTS_MESSAGE(x...) \
+ printk( KERN_INFO MTS_NAME ## x )
+
+#if defined MTS_DO_DEBUG
+
+#define MTS_DEBUG(x...) \
+ printk( KERN_DEBUG MTS_NAME ## x )
+
+#define MTS_DEBUG_GOT_HERE() \
+ MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
+#define MTS_DEBUG_INT() \
+ do { MTS_DEBUG_GOT_HERE(); \
+ MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \
+ MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
+ mts_debug_dump(context->instance);\
+ } while(0)
+#else
+
+#define MTS_NUL_STATEMENT do { } while(0)
+
+#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
+#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
+#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
+
+#endif
+
+
+
+#define MTS_INT_INIT()\
+ do {\
+ context = (struct mts_transfer_context*)transfer->context; \
+ if (atomic_read(&context->do_abort)) {\
+ mts_transfer_cleanup(transfer);\
+ return;\
+ }\
+ MTS_DEBUG_INT();\
+ } while (0)
+
+static inline void mts_debug_dump(struct mts_desc* desc) {
+ MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n",
+ (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
+ (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
+ );
+ MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
+ usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+ usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
+ usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
+ );
+}
+
+
+static inline void mts_show_command(Scsi_Cmnd *srb)
+{
+ char *what = NULL;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default:
+ MTS_DEBUG("can't decode command\n");
+ goto out;
+ break;
+ }
+ MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
+
+ out:
+ MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
+ srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
+
+static inline int mts_is_aborting(struct mts_desc* desc) {
+ return (atomic_read(&desc->context.do_abort));
+}
+
+static inline void mts_request_abort(struct mts_desc* desc) {
+ MTS_DEBUG_GOT_HERE();
+ mts_debug_dump(desc);
+ atomic_set(&desc->context.do_abort,1);
+}
+
+static inline void mts_urb_abort(struct mts_desc* desc) {
+ MTS_DEBUG_GOT_HERE();
+ mts_debug_dump(desc);
+ if ( desc->urb.status == USB_ST_URB_PENDING ) {
+ usb_unlink_urb( &desc->urb );
+ }
+}
+
+static inline void mts_wait_abort(struct mts_desc* desc)
+{
+ mts_request_abort(desc);
+
+ while( !atomic_read(&desc->lock.count) ) {
+/* Is there a function to check if the semaphore is locked? */
+ schedule_timeout( MTS_ABORT_TIMEOUT );
+ MTS_DEBUG_GOT_HERE();
+ mts_urb_abort(desc);
+ }
+
+}
+
+
+static struct mts_desc * mts_list; /* list of active scanners */
+struct semaphore mts_list_semaphore;
+
+/* Internal list operations */
+
+static
+void mts_remove_nolock( struct mts_desc* to_remove )
+{
+ MTS_DEBUG( "removing 0x%x from list\n",
+ (int)to_remove );
+
+ mts_wait_abort(to_remove);
+
+ down( &to_remove->lock );
+
+ MTS_DEBUG_GOT_HERE();
+
+ if ( to_remove != mts_list ) {
+ MTS_DEBUG_GOT_HERE();
+ if (to_remove->prev && to_remove->next)
+ to_remove->prev->next = to_remove->next;
+ } else {
+ MTS_DEBUG_GOT_HERE();
+ mts_list = to_remove->next;
+ if (mts_list) {
+ MTS_DEBUG_GOT_HERE();
+ mts_list->prev = 0;
+ }
+ }
+
+ if ( to_remove->next ) {
+ MTS_DEBUG_GOT_HERE();
+ to_remove->next->prev = to_remove->prev;
+ }
+
+ MTS_DEBUG_GOT_HERE();
+ scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl));
+
+ kfree( to_remove );
+}
+
+static
+void mts_add_nolock( struct mts_desc* to_add )
+{
+ MTS_DEBUG( "adding 0x%x to list\n", (int)to_add );
+
+ to_add->prev = 0;
+ to_add->next = mts_list;
+ if ( mts_list ) {
+ mts_list->prev = to_add;
+ }
+
+ mts_list = to_add;
+}
+
+
+
+
+/* SCSI driver interface */
+
+/* scsi related functions - dummies for now mostly */
+
+static int mts_scsi_release(struct Scsi_Host *psh)
+{
+ MTS_DEBUG_GOT_HERE();
+
+ return 0;
+}
+
+static int mts_scsi_abort (Scsi_Cmnd *srb)
+/* interrupt context (!) */
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+ MTS_DEBUG_GOT_HERE();
+
+ mts_request_abort(desc);
+ mts_urb_abort(desc);
+
+ return SCSI_ABORT_PENDING;
+}
+
+static int mts_scsi_host_reset (Scsi_Cmnd *srb)
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+ MTS_DEBUG_GOT_HERE();
+ mts_debug_dump(desc);
+
+ usb_reset_device(desc->usb_dev);
+ return 0; /* RANT why here 0 and not SUCCESS */
+}
+
+/* the core of the scsi part */
+
+/* faking a detection - which can't fail :-) */
+
+static int mts_scsi_detect (struct SHT * sht)
+{
+ /* Whole function stolen from usb-storage */
+
+ struct mts_desc * desc = (struct mts_desc *)sht->proc_dir;
+ /* What a hideous hack! */
+
+ char local_name[48];
+
+ MTS_DEBUG_GOT_HERE();
+
+ /* set up the name of our subdirectory under /proc/scsi/ */
+ sprintf(local_name, "microtek-%d", desc->host_number);
+ sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+ /* FIXME: where is this freed ? */
+
+ if (!sht->proc_name) {
+ MTS_ERROR( "unable to allocate memory for proc interface!!\n" );
+ return 0;
+ }
+
+ strcpy(sht->proc_name, local_name);
+
+ sht->proc_dir = NULL;
+
+ /* In host->hostdata we store a pointer to desc */
+ desc->host = scsi_register(sht, sizeof(desc));
+ desc->host->hostdata[0] = (unsigned long)desc;
+/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */
+
+ return 1;
+}
+
+
+
+/* Main entrypoint: SCSI commands are dispatched to here */
+
+
+
+static
+int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
+
+static void mts_transfer_cleanup( struct urb *transfer );
+
+
+inline static
+void mts_int_submit_urb (struct urb* transfer,
+ int pipe,
+ void* data,
+ unsigned length,
+ mts_usb_urb_callback callback )
+/* Interrupt context! */
+
+/* Holding transfer->context->lock! */
+{
+ int res;
+ struct mts_transfer_context* context;
+
+ MTS_INT_INIT();
+
+ FILL_BULK_URB(transfer,
+ context->instance->usb_dev,
+ pipe,
+ data,
+ length,
+ callback,
+ context
+ );
+
+/* transfer->transfer_flags = USB_DISABLE_SPD;*/
+ transfer->transfer_flags = USB_ASYNC_UNLINK;
+ transfer->status = 0;
+ transfer->timeout = 100;
+
+ res = usb_submit_urb( transfer );
+ if ( res ) {
+ MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
+ context->srb->result = DID_ERROR << 16;
+ context->state = mts_con_error;
+ mts_transfer_cleanup(transfer);
+ }
+ return;
+}
+
+
+static void mts_transfer_cleanup( struct urb *transfer )
+/* Interrupt context! */
+{
+ struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context;
+
+ up( &context->instance->lock );
+ if ( context->final_callback )
+ context->final_callback(context->srb);
+
+}
+
+static void mts_transfer_done( struct urb *transfer )
+{
+ struct mts_transfer_context* context;
+
+ MTS_INT_INIT();
+
+ context->srb->result &= MTS_MAX_CHUNK_MASK;
+ context->srb->result |= (unsigned)context->status<<1;
+
+ mts_transfer_cleanup(transfer);
+
+ return;
+}
+
+
+static void mts_get_status( struct urb *transfer )
+/* Interrupt context! */
+{
+ struct mts_transfer_context* context;
+
+ MTS_INT_INIT();
+
+ context->state = mts_con_status;
+
+ mts_int_submit_urb(transfer,
+ usb_rcvbulkpipe(context->instance->usb_dev,
+ context->instance->ep_response),
+ &context->status,
+ 1,
+ mts_transfer_done );
+
+
+ return;
+}
+
+static void mts_data_done( struct urb* transfer )
+/* Interrupt context! */
+{
+ struct mts_transfer_context* context;
+
+ MTS_INT_INIT();
+
+ if ( context->data_length != transfer->actual_length ) {
+ context->srb->resid = context->data_length - transfer->actual_length;
+ } else if ( transfer->status ) {
+ context->srb->result = DID_ERROR<<16;
+ }
+
+ mts_get_status(transfer);
+
+ return;
+}
+
+
+static void mts_command_done( struct urb *transfer )
+/* Interrupt context! */
+{
+ struct mts_transfer_context* context;
+
+ MTS_INT_INIT();
+
+ if ( transfer->status ) {
+ context->srb->result = DID_ERROR<<16;
+ mts_transfer_cleanup(transfer);
+
+ return;
+ }
+
+ if ( context->data ) {
+ context->state = mts_con_data;
+ mts_int_submit_urb(transfer,
+ context->data_pipe,
+ context->data,
+ context->data_length,
+ mts_data_done);
+ } else mts_get_status(transfer);
+
+ return;
+}
+
+
+
+ static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
+ static const u8 mts_read_image_sig_len = 4;
+ static const unsigned char mts_direction[256/8] = {
+ 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+ 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+
+#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
+
+static void
+mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
+{
+
+ int pipe;
+
+
+ MTS_DEBUG_GOT_HERE();
+
+ desc->context.instance = desc;
+ desc->context.srb = srb;
+ desc->context.state = mts_con_command;
+ atomic_set(&desc->context.do_abort,0);
+
+ if ( !srb->bufflen ){
+ desc->context.data = 0;
+ desc->context.data_length = 0;
+ return;
+ } else {
+ desc->context.data = srb->buffer;
+ desc->context.data_length = srb->bufflen;
+ }
+
+ /* can't rely on srb->sc_data_direction */
+
+ /* Brutally ripped from usb-storage */
+
+ if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
+) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
+ MTS_DEBUG( "transfering from desc->ep_image == %d\n",
+ (int)desc->ep_image );
+ } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
+ pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
+ MTS_DEBUG( "transfering from desc->ep_response == %d\n",
+ (int)desc->ep_response);
+ } else {
+ MTS_DEBUG("transfering to desc->ep_out == %d\n",
+ (int)desc->ep_out);
+ pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
+ }
+ desc->context.data_pipe = pipe;
+}
+
+
+static
+int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+ int err = 0;
+ int res;
+
+ MTS_DEBUG_GOT_HERE();
+ mts_show_command(srb);
+ mts_debug_dump(desc);
+
+ if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+
+ MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
+
+ MTS_DEBUG("this device doesn't exist\n");
+
+ srb->result = DID_BAD_TARGET << 16;
+
+ if(callback)
+ callback(srb);
+
+ goto out;
+ }
+
+ down(&desc->lock);
+
+ MTS_DEBUG_GOT_HERE();
+ mts_show_command(srb);
+
+
+ FILL_BULK_URB(&desc->urb,
+ desc->usb_dev,
+ usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+ srb->cmnd,
+ srb->cmd_len,
+ mts_command_done,
+ &desc->context
+ );
+
+
+ mts_build_transfer_context( srb, desc );
+ desc->context.final_callback = callback;
+ desc->urb.timeout = 100;
+ desc->urb.transfer_flags = USB_ASYNC_UNLINK;
+
+/* desc->urb.transfer_flags = USB_DISABLE_SPD;*/
+
+ res=usb_submit_urb(&desc->urb);
+
+ if(res){
+ MTS_ERROR("error %d submitting URB\n",(int)res);
+ srb->result = DID_ERROR << 16;
+
+ if(callback)
+ callback(srb);
+
+ goto out;
+ }
+
+ MTS_DEBUG_GOT_HERE();
+
+ out:
+ return err;
+}
+/*
+ * this defines our 'host'
+ */
+
+/* NOTE: This is taken from usb-storage, should be right. */
+
+
+static Scsi_Host_Template mts_scsi_host_template = {
+ name: "microtek",
+ detect: mts_scsi_detect,
+ release: mts_scsi_release,
+ command: 0,
+ queuecommand: mts_scsi_queuecommand,
+
+ eh_abort_handler: mts_scsi_abort,
+ eh_device_reset_handler:0,
+ eh_bus_reset_handler: 0,
+ eh_host_reset_handler: mts_scsi_host_reset,
+
+ can_queue: 1,
+ this_id: -1,
+ cmd_per_lun: 1,
+ present: 0,
+ unchecked_isa_dma: FALSE,
+ use_clustering: FALSE,
+ use_new_eh_code: TRUE,
+ emulated: TRUE
+};
+
+
+/* USB layer driver interface implementation */
+
+static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+ struct mts_desc* to_remove = (struct mts_desc*)ptr;
+
+ MTS_DEBUG_GOT_HERE();
+
+ /* leave the list - lock it */
+ down(&mts_list_semaphore);
+
+ mts_remove_nolock(to_remove);
+
+ up(&mts_list_semaphore);
+}
+
+struct vendor_product
+{
+ u16 idVendor;
+ u16 idProduct;
+ char* name;
+ enum
+ {
+ mts_sup_unknown=0,
+ mts_sup_alpha,
+ mts_sup_full
+ }
+ support_status;
+} ;
+
+
+/* These are taken from the msmUSB.inf file on the Windows driver CD */
+const static struct vendor_product mts_supported_products[] =
+{
+ {
+ 0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown
+ },
+ {
+ 0x5da, 0x94,"Phantom 336CX",mts_sup_unknown
+ },
+ {
+ 0x5da, 0x99,"Scanmaker X6",mts_sup_alpha
+ },
+ {
+ 0x5da, 0x9a,"Phantom C6",mts_sup_unknown
+ },
+ {
+ 0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown
+ },
+ {
+ 0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown
+ },
+ {
+ 0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown
+ },
+ {
+ 0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown
+ }
+}
+;
+const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ];
+ /* Must never be derefed, points to one after last entry */
+
+
+static void * mts_usb_probe (struct usb_device *dev, unsigned int interface)
+{
+ int i;
+ int result;
+ int ep_out = -1;
+ int ep_in_set[3]; /* this will break if we have more than three endpoints
+ which is why we check */
+ int *ep_in_current = ep_in_set;
+
+ struct mts_desc * new_desc;
+ struct vendor_product const* p;
+
+ /* the altsettting 0 on the interface we're probing */
+ struct usb_interface_descriptor *altsetting;
+
+ MTS_DEBUG_GOT_HERE();
+ MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
+
+ MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
+ (int)dev->descriptor.idProduct,
+ (int)dev->descriptor.idVendor );
+
+ MTS_DEBUG_GOT_HERE();
+
+ /* checking IDs */
+ for( p = mts_supported_products; p != mts_last_product; p++ )
+ if ( dev->descriptor.idVendor == p->idVendor &&
+ dev->descriptor.idProduct == p->idProduct )
+ goto is_supported;
+ else
+ MTS_DEBUG( "doesn't appear to be model %s\n", p->name );
+
+ MTS_DEBUG( "returning NULL: unsupported\n" );
+
+ return NULL;
+
+ is_supported:
+
+ MTS_DEBUG_GOT_HERE();
+
+ MTS_DEBUG( "found model %s\n", p->name );
+ if ( p->support_status != mts_sup_full )
+ MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
+ p->name );
+
+ /* the altsettting 0 on the interface we're probing */
+ altsetting =
+ &(dev->actconfig->interface[interface].altsetting[0]);
+
+
+ /* Check if the config is sane */
+
+ if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
+ MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
+ (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints );
+ return NULL;
+ }
+
+ for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
+
+ MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
+ (int)altsetting->endpoint[i].bEndpointAddress );
+ } else {
+ if (altsetting->endpoint[i].bEndpointAddress &
+ USB_DIR_IN)
+ *ep_in_current++
+ = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else {
+ if ( ep_out != -1 ) {
+ MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
+ return NULL;
+ }
+
+ ep_out = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ }
+
+ }
+
+
+ if ( ep_out == -1 ) {
+ MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
+ return NULL;
+ }
+
+
+ /* I don't understand the following fully (it's from usb-storage) -- John */
+
+ /* set the interface -- STALL is an acceptable response here */
+ result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+
+ MTS_DEBUG("usb_set_interface returned %d.\n",result);
+ switch( result )
+ {
+ case 0: /* no error */
+ break;
+
+ case -EPIPE:
+ usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+ MTS_DEBUG( "clearing clearing stall on control interface\n" );
+ break;
+
+ default:
+ MTS_DEBUG( "unknown error %d from usb_set_interface\n",
+ (int)result );
+ return NULL;
+ }
+
+
+ /* allocating a new descriptor */
+ new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
+ if (new_desc == NULL)
+ {
+ MTS_ERROR("couldn't allocate scanner desc, bailing out!\n");
+ return NULL;
+ }
+
+ /* As done by usb_alloc_urb */
+ memset( new_desc, 0, sizeof(*new_desc) );
+ spin_lock_init(&new_desc->urb.lock);
+
+
+ /* initialising that descriptor */
+ new_desc->usb_dev = dev;
+ new_desc->interface = interface;
+
+ init_MUTEX(&new_desc->lock);
+
+ if(mts_list){
+ new_desc->host_number = mts_list->host_number+1;
+ } else {
+ new_desc->host_number = 0;
+ }
+
+ /* endpoints */
+
+ new_desc->ep_out = ep_out;
+ new_desc->ep_response = ep_in_set[0];
+ new_desc->ep_image = ep_in_set[1];
+
+
+ if ( new_desc->ep_out != MTS_EP_OUT )
+ MTS_WARNING( "will this work? Command EP is not usually %d\n",
+ (int)new_desc->ep_out );
+
+ if ( new_desc->ep_response != MTS_EP_RESPONSE )
+ MTS_WARNING( "will this work? Response EP is not usually %d\n",
+ (int)new_desc->ep_response );
+
+ if ( new_desc->ep_image != MTS_EP_IMAGE )
+ MTS_WARNING( "will this work? Image data EP is not usually %d\n",
+ (int)new_desc->ep_image );
+
+
+ /* Initialize the host template based on the default one */
+ memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template));
+ /* HACK from usb-storage - this is needed for scsi detection */
+ (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
+
+ MTS_DEBUG("registering SCSI module\n");
+
+ new_desc->ctempl.module = THIS_MODULE;
+ result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl));
+ /* Will get hit back in microtek_detect by this func */
+ if ( result )
+ {
+ MTS_ERROR( "error %d from scsi_register_module! Help!\n",
+ (int)result );
+
+ /* FIXME: need more cleanup? */
+ kfree( new_desc );
+ return NULL;
+ }
+ MTS_DEBUG_GOT_HERE();
+
+ /* FIXME: the bomb is armed, must the host be registered under lock ? */
+ /* join the list - lock it */
+ down(&mts_list_semaphore);
+
+ mts_add_nolock( new_desc );
+
+ up(&mts_list_semaphore);
+
+
+ MTS_DEBUG("completed probe and exiting happily\n");
+
+ return (void *)new_desc;
+}
+
+
+
+/* get us noticed by the rest of the kernel */
+
+int __init microtek_drv_init(void)
+{
+ int result;
+
+ MTS_DEBUG_GOT_HERE();
+ init_MUTEX(&mts_list_semaphore);
+
+ if ((result = usb_register(&mts_usb_driver)) < 0) {
+ MTS_DEBUG("usb_register returned %d\n", result );
+ return -1;
+ } else {
+ MTS_DEBUG("driver registered.\n");
+ }
+
+ return 0;
+}
+
+void __exit microtek_drv_exit(void)
+{
+ struct mts_desc* next;
+
+ MTS_DEBUG_GOT_HERE();
+
+ usb_deregister(&mts_usb_driver);
+
+ down(&mts_list_semaphore);
+
+ while (mts_list) {
+ /* keep track of where the next one is */
+ next = mts_list->next;
+
+ mts_remove_nolock( mts_list );
+
+ /* advance the list pointer */
+ mts_list = next;
+ }
+
+ up(&mts_list_semaphore);
+}
+
+module_init(microtek_drv_init);
+module_exit(microtek_drv_exit);
--- /dev/null
+ /*
+ * Driver for Microtek Scanmaker X6 USB scanner and possibly others.
+ *
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * See microtek.c for history
+ *
+ */
+
+typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
+typedef void (*mts_usb_urb_callback) (struct urb *);
+
+
+struct mts_transfer_context
+{
+ struct mts_desc* instance;
+ mts_scsi_cmnd_callback final_callback;
+ Scsi_Cmnd *srb;
+
+ void* data;
+ unsigned data_length;
+ int data_pipe;
+
+ enum {
+ mts_con_none,
+ mts_con_command,
+ mts_con_data,
+ mts_con_status,
+ mts_con_error,
+ mts_con_done
+ }
+ state;
+
+ atomic_t do_abort; /* when != 0 URB completion routines will
+ return straightaway */
+
+ u8 status; /* status returned from ep_response after command completion */
+};
+
+
+struct mts_desc {
+ struct mts_desc *next;
+ struct mts_desc *prev;
+
+ struct usb_device *usb_dev;
+
+ int interface;
+
+ /* Endpoint addresses */
+ u8 ep_out;
+ u8 ep_response;
+ u8 ep_image;
+
+ struct Scsi_Host * host;
+ Scsi_Host_Template ctempl;
+ int host_number;
+
+ struct semaphore lock;
+
+ struct urb urb;
+ struct mts_transfer_context context;
+};
+
+
+#define MTS_EP_OUT 0x1
+#define MTS_EP_RESPONSE 0x2
+#define MTS_EP_IMAGE 0x3
+#define MTS_EP_TOTAL 0x3
+
+#define MTS_MAX_CHUNK_MASK ~0x3fu
+/*maximum amount the scanner will transmit at once */
+
/*
- * mousedev.c Version 0.1
+ * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* Input driver to PS/2 or ImPS/2 device driver module.
*
list->ready = 1;
- if (list->fasync)
- kill_fasync(list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
list = list->next;
}
kfree(list);
- MOD_DEC_USE_COUNT;
return 0;
}
if (i > MOUSEDEV_MINORS || !mousedev_table[i])
return -ENODEV;
- MOD_INC_USE_COUNT;
-
- if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) {
- MOD_DEC_USE_COUNT;
+ if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
return -ENOMEM;
- }
memset(list, 0, sizeof(struct mousedev_list));
list->mousedev = mousedev_table[i];
list->buffer = list->bufsiz;
}
- if (list->fasync)
- kill_fasync(list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&list->mousedev->wait);
}
struct file_operations mousedev_fops = {
+ owner: THIS_MODULE,
read: mousedev_read,
write: mousedev_write,
poll: mousedev_poll,
if (mousedev_mix.open)
input_open_device(&mousedev->handle);
- printk("mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
+ printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
return &mousedev->handle;
}
mousedev_mix.minor = MOUSEDEV_MIX;
mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
- printk("mice: PS/2 mouse device common for all mice\n");
+ printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
* OmniVision OV511 Camera-to-USB Bridge Driver
*
* Copyright (c) 1999-2000 Mark W. McClelland
- * Many improvements by Bret Wallach
- * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
* Changes by Claudio Matsuoka <claudio@conectiva.com>
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-static const char version[] = "1.15";
+static const char version[] = "1.17";
#define __NO_VERSION__
#include "ov511.h"
+#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */
+
#define OV511_I2C_RETRIES 3
/* Video Size 640 x 480 x 3 bytes for RGB */
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
-#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_RGB24 ? 384 : 256)
-#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_RGB24 ? 24 : 8)
+#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24)
/* PARAMETER VARIABLES: */
static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
/* IMPORTANT: This output MUST be kept under PAGE_SIZE
* or we need to get more sophisticated. */
+ out += sprintf (out, "driver_version : %s\n", version);
out += sprintf (out, "custom_id : %d\n", ov511->customid);
out += sprintf (out, "model : %s\n", ov511->desc ?
clist[ov511->desc].description : "unknown");
ov511->frame[i].depth);
out += sprintf (out, " size : %d %d\n",
ov511->frame[i].width, ov511->frame[i].height);
-#if 0
- out += sprintf (out, " hdr_size : %d %d\n",
- ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight);
-#endif
out += sprintf (out, " format : ");
for (j = 0; plist[j].num >= 0; j++) {
if (plist[j].num == ov511->frame[i].format) {
ov511->frame[i].segsize);
out += sprintf (out, " data_buffer : 0x%p\n",
ov511->frame[i].data);
-#if 0
- out += sprintf (out, " bytesread : %ld\n",
- ov511->frame[i].bytes_read);
-#endif
}
out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled));
out += sprintf (out, "bridge : %s\n",
return 0;
}
-/* FIXME: add 400x300, 176x144, 160x140 */
+/* FIXME: 176x144, 160x140 */
+/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
static struct mode_list mlist[] = {
- { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00,
- 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e },
- { 640, 480, VIDEO_PALETTE_RGB24,0x4f, 0x3d, 0x00, 0x00,
- 0x4f, 0x3d, 0x00, 0x00, 0x06, 0x03, 0x24, 0x04, 0x9e },
- { 320, 240, VIDEO_PALETTE_GREY, 0x27, 0x1f, 0x00, 0x00,
- 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e },
- { 320, 240, VIDEO_PALETTE_RGB24,0x27, 0x1f, 0x00, 0x00,
- 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e },
- { 352, 288, VIDEO_PALETTE_GREY, 0x2b, 0x25, 0x00, 0x00,
- 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00,
- 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 384, 288, VIDEO_PALETTE_GREY, 0x2f, 0x25, 0x00, 0x00,
- 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 384, 288, VIDEO_PALETTE_RGB24,0x2f, 0x25, 0x00, 0x00,
- 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 448, 336, VIDEO_PALETTE_GREY, 0x37, 0x29, 0x00, 0x00,
- 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
- { 448, 336, VIDEO_PALETTE_RGB24,0x37, 0x29, 0x00, 0x00,
- 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+ /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */
+ { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
+ { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
+ { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
+ { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
+ { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+ { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
{ 0, 0 }
};
ov511_mode_init_regs(struct usb_ov511 *ov511,
int width, int height, int mode, int sub_flag)
{
- int rc = 0;
- struct usb_device *dev = ov511->dev;
- int hwsbase = 0;
- int hwebase = 0;
int i;
+ struct usb_device *dev = ov511->dev;
+ int hwsbase = 0, hwebase = 0;
PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
width, height, mode, sub_flag);
break;
}
-#if 0
- /* FIXME: subwindow support is currently broken!
- */
- if (width == 640 && height == 480) {
- if (sub_flag) {
- /* horizontal window start */
- ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
- /* horizontal window end */
- ov511_i2c_write(dev, 0x18,
- hwebase+((ov511->subx+ov511->subw)>>2));
- /* vertical window start */
- ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
- /* vertical window end */
- ov511_i2c_write(dev, 0x1a,
- 0x5+((ov511->suby+ov511->subh)>>1));
- ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1);
- ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1);
- /* clock rate control */
- ov511_i2c_write(dev, 0x11, 0x01);
-
- /* Snapshot additions */
- ov511_reg_write(dev, 0x1a, (ov511->subw>>3)-1);
- ov511_reg_write(dev, 0x1b, (ov511->subh>>3)-1);
- ov511_reg_write(dev, 0x1c, 0x00);
- ov511_reg_write(dev, 0x1d, 0x00);
- } else {
- ov511_i2c_write(dev, 0x17, hwsbase);
- ov511_i2c_write(dev, 0x18, hwebase + (640>>2));
- ov511_i2c_write(dev, 0x19, 0x5);
- ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
- ov511_reg_write(dev, 0x12, 0x4f);
- ov511_reg_write(dev, 0x13, 0x3d);
-
- /* Snapshot additions */
- ov511_reg_write(dev, 0x1a, 0x4f);
- ov511_reg_write(dev, 0x1b, 0x3d);
- ov511_reg_write(dev, 0x1c, 0x00);
- ov511_reg_write(dev, 0x1d, 0x00);
-
- if (mode == VIDEO_PALETTE_GREY) {
- ov511_i2c_write(dev, 0x11, 4); /* check */
- } else {
- ov511_i2c_write(dev, 0x11, 6); /* check */
- }
- }
-
- ov511_reg_write(dev, 0x14, 0x00); /* Pixel divisor */
- ov511_reg_write(dev, 0x15, 0x00); /* Line divisor */
-
- /* FIXME?? Shouldn't below be true only for YUV420? */
- ov511_reg_write(dev, 0x18, 0x03); /* YUV420/422, YFIR */
+ if (sub_flag) {
+ ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
+ ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2));
+ ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
+ ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1));
+ } else {
+ ov511_i2c_write(dev, 0x17, hwsbase);
+ ov511_i2c_write(dev, 0x18, hwebase + (640>>2));
+ ov511_i2c_write(dev, 0x19, 0x5);
+ ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
+ }
- ov511_i2c_write(dev, 0x12, 0x24); /* Common A */
- ov511_i2c_write(dev, 0x14, 0x04); /* Common C */
+ for (i = 0; mlist[i].width; i++) {
+ int lncnt, pxcnt, clock;
- /* 7620 doesn't have register 0x35, so play it safe */
- if (ov511->sensor != SEN_OV7620)
- ov511_i2c_write(dev, 0x35, 0x9e);
-#endif
+ if (width != mlist[i].width || height != mlist[i].height)
+ continue;
- for (i = 0; mlist[i].width; i++) {
- if (width != mlist[i].width ||
- height != mlist[i].height ||
- mode != mlist[i].mode)
+ if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
continue;
- ov511_reg_write(dev, 0x12, mlist[i].pxcnt);
- ov511_reg_write(dev, 0x13, mlist[i].lncnt);
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+ lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+
+ ov511_reg_write(dev, 0x12, pxcnt);
+ ov511_reg_write(dev, 0x13, lncnt);
ov511_reg_write(dev, 0x14, mlist[i].pxdv);
ov511_reg_write(dev, 0x15, mlist[i].lndv);
ov511_reg_write(dev, 0x18, mlist[i].m420);
/* Snapshot additions */
- ov511_reg_write(dev, 0x1a, mlist[i].s_pxcnt);
- ov511_reg_write(dev, 0x1b, mlist[i].s_lncnt);
- ov511_reg_write(dev, 0x1c, mlist[i].s_pxdv);
- ov511_reg_write(dev, 0x1d, mlist[i].s_lndv);
-
- ov511_i2c_write(dev, 0x11, mlist[i].clock); /* check */
-
+ ov511_reg_write(dev, 0x1a, pxcnt);
+ ov511_reg_write(dev, 0x1b, lncnt);
+ ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
+ ov511_reg_write(dev, 0x1d, mlist[i].lndv);
+
+ /* Calculate and set the clock divisor */
+ clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+ * (mlist[i].color ? 3 : 2) / 2) / 66000;
+ ov511_i2c_write(dev, 0x11, clock);
+
+#ifdef OV511_GBR422
+ ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
+#else
ov511_i2c_write(dev, 0x12, mlist[i].common_A);
+#endif
ov511_i2c_write(dev, 0x14, mlist[i].common_C);
/* 7620 doesn't have register 0x35, so play it safe */
break;
}
+ if (ov511_restart(ov511->dev) < 0)
+ return -EIO;
+
if (mlist[i].width == 0) {
err("Unknown mode (%d, %d): %d", width, height, mode);
- rc = -EINVAL;
+ return -EINVAL;
}
- if (ov511_restart(ov511->dev) < 0)
- return -EIO;
-
#ifdef OV511_DEBUG
if (debug >= 5)
ov511_dump_i2c_regs(dev);
#endif
- return rc;
+ return 0;
}
/**********************************************************************
* 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
* 8 9 ... 15 72 73 ... 79 200 201 ... 207
* ... ... ...
- * 56 57 ... 63 120 121 127 248 249 ... 255
+ * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255
*
* Note that the U and V data in one segment represents a 16 x 16 pixel
* area, but the Y data represents a 32 x 8 pixel area.
#undef OV511_DUMPPIX
+#ifdef OV511_GBR422
+static void
+ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+ int k, l, m;
+ unsigned char *pIn;
+ unsigned char *pOut, *pOut1;
+
+ pIn = pIn0;
+ pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
+
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+ *(pOut1 + iWidth*3 + 3) = *pIn++;
+ pOut1 += 6;
+ }
+ pOut += iWidth*3*2;
+ }
+
+ pIn = pIn0 + 64;
+ pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+ *(pOut1 + iWidth*3 + 3) = *pIn++;
+ pOut1 += 6;
+ }
+ pOut += iWidth*3*2;
+ }
+
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY + 1;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut1 = *pIn++;
+ pOut1 += 3;
+ }
+ pOut1 += (iWidth - 8) * 3;
+ }
+ pOut += 8 * 3;
+ }
+}
+
+#else
+
static void
ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
int iOutY, int iOutUV, int iHalf, int iWidth)
}
#endif
}
+#endif
/*
* For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
for (m = 0; m < 8; m++) {
*pOut1++ = *pIn++;
}
- pOut1 += iWidth - WDIV;
+ pOut1 += iWidth - 8;
}
pOut += 8;
}
/* Frame end */
if (cdata[8] & 0x80) {
-#if 0
struct timeval *ts;
ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
- do_gettimeofday (ts);
-#endif
+ do_gettimeofday (ts);
PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
}
/*
- * iY counts segment lines
- * jY counts segment columns
- * iOutY is the offset (in bytes) of the segment upper left corner
+ * i counts segment lines
+ * j counts segment columns
+ * iOut is the offset (in bytes) of the upper left corner
*/
iY = iSegY / (frame->width / WDIV);
jY = iSegY - iY * (frame->width / WDIV);
jUV = iSegUV - iUV * (frame->width / WDIV * 2);
iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3);
- if (frame->format == VIDEO_PALETTE_GREY)
+ switch (frame->format) {
+ case VIDEO_PALETTE_GREY:
ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
- else if (frame->format == VIDEO_PALETTE_RGB24)
+ break;
+ case VIDEO_PALETTE_RGB24:
ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
iY & 1, frame->width);
+ break;
+ }
pData = &cdata[iPix];
}
if (frame->segment < frame->width * frame->height / 256) {
ov511->scratchlen = (ov511->packet_size - 1) - iPix;
if (ov511->scratchlen < frame->segsize)
- memmove(ov511->scratch, pData,
- ov511->scratchlen);
+ memmove(ov511->scratch, pData, ov511->scratchlen);
else
ov511->scratchlen = 0;
}
static int ov511_open(struct video_device *dev, int flags)
{
- int err = -EBUSY;
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- int i;
+ int i, err = 0;
+ MOD_INC_USE_COUNT;
PDEBUG(4, "opening");
-
down(&ov511->lock);
- if (ov511->user) {
- up(&ov511->lock);
- return -EBUSY;
- }
+ err = -EBUSY;
+ if (ov511->user)
+ goto out;
err = -ENOMEM;
/* Allocate memory for the frame buffers */
ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
if (!ov511->fbuf)
- return err;
+ goto out;
ov511->sub_flag = 0;
open_free_ret:
while (--i) kfree(ov511->sbuf[i].data);
rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE);
- return err;
+ goto out;
}
PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
}
goto open_free_ret;
ov511->user++;
+
+out:
up(&ov511->lock);
- MOD_INC_USE_COUNT;
+ if (err)
+ MOD_DEC_USE_COUNT;
- return 0;
+ return err;
}
static void ov511_close(struct video_device *dev)
down(&ov511->lock);
ov511->user--;
- MOD_DEC_USE_COUNT;
-
ov511_stop_isoc(ov511);
rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
video_unregister_device(&ov511->vdev);
kfree(ov511);
}
+
+ MOD_DEC_USE_COUNT;
+
}
static int ov511_init_done(struct video_device *dev)
return -EINVAL;
if (vc.decimation)
return -EINVAL;
-#if 0
- vc.x /= 4;
- vc.x *= 4;
- vc.y /= 2;
- vc.y *= 2;
- vc.width /= 32;
- vc.width *= 32;
-#else
+
vc.x &= ~3L;
vc.y &= ~1L;
vc.y &= ~31L;
-#endif
+
if (vc.width == 0)
vc.width = 32;
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
+ int ret;
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
before changing modes */
interruptible_sleep_on(&ov511->wq);
if (signal_pending(current)) return -EINTR;
- ov511_mode_init_regs(ov511, vm.width, vm.height,
+ ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
vm.format, ov511->sub_flag);
+#if 0
+ if (ret < 0)
+ return ret;
+#endif
}
ov511->frame[vm.frame].width = vm.width;
int i, success;
int rc;
+ /* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
static struct ov511_regvals aRegvalsNorm7610[] = {
{ OV511_I2C_BUS, 0x10, 0xff },
{ OV511_I2C_BUS, 0x16, 0x06 },
{ OV511_I2C_BUS, 0x06, 0x00 },
{ OV511_I2C_BUS, 0x12, 0x00 },
{ OV511_I2C_BUS, 0x38, 0x81 },
- { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
+ { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
{ OV511_I2C_BUS, 0x05, 0x00 },
- { OV511_I2C_BUS, 0x0f, 0x05 },
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
{ OV511_I2C_BUS, 0x15, 0x01 },
{ OV511_I2C_BUS, 0x20, 0x1c },
{ OV511_I2C_BUS, 0x23, 0x2a },
{ OV511_I2C_BUS, 0x24, 0x10 },
{ OV511_I2C_BUS, 0x25, 0x8a },
{ OV511_I2C_BUS, 0x27, 0xc2 },
- { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */
+ { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */
{ OV511_I2C_BUS, 0x2a, 0x04 },
{ OV511_I2C_BUS, 0x2c, 0xfe },
{ OV511_I2C_BUS, 0x30, 0x71 },
{ OV511_I2C_BUS, 0x12, 0x00 },
{ OV511_I2C_BUS, 0x28, 0x24 },
{ OV511_I2C_BUS, 0x05, 0x00 },
- { OV511_I2C_BUS, 0x0f, 0x05 },
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
{ OV511_I2C_BUS, 0x15, 0x01 },
{ OV511_I2C_BUS, 0x23, 0x00 },
{ OV511_I2C_BUS, 0x24, 0x10 },
usb_driver_release_interface(&ov511_driver,
&dev->actconfig->interface[ov511->iface]);
- kfree(ov511);
- ov511 = NULL;
-
return -EBUSY;
}
if (interface->bInterfaceSubClass != 0x00)
return NULL;
+ /* Since code below may sleep, we use this as a lock */
+ MOD_INC_USE_COUNT;
+
if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
err("couldn't kmalloc ov511 struct");
- return NULL;
+ goto error;
}
memset(ov511, 0, sizeof(*ov511));
/* Lifeview USB Life TV not supported */
if (clist[i].id == 38) {
err("This device is not supported yet.");
- return NULL;
+ goto error;
}
if (clist[i].id == -1) {
if (!ov511_configure(ov511)) {
ov511->user = 0;
init_MUTEX(&ov511->lock); /* to 1 == available */
- return ov511;
} else {
err("Failed to configure camera");
goto error;
}
+ MOD_DEC_USE_COUNT;
return ov511;
error:
ov511 = NULL;
}
+ MOD_DEC_USE_COUNT;
return NULL;
}
{
struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
-// video_unregister_device(&ov511->vdev);
+ MOD_INC_USE_COUNT;
/* We don't want people trying to open up the device */
if (!ov511->user)
#endif
/* Free the memory */
- if (!ov511->user) {
+ if (ov511 && !ov511->user) {
kfree(ov511);
ov511 = NULL;
}
+
+ MOD_DEC_USE_COUNT;
}
static struct usb_driver ov511_driver = {
struct mode_list {
int width;
int height;
- int mode;
- u8 pxcnt;
- u8 lncnt;
- u8 pxdv;
- u8 lndv;
- u8 s_pxcnt;
- u8 s_lncnt;
- u8 s_pxdv;
- u8 s_lndv;
- u8 clock;
+ int color; /* 0=grayscale, 1=color */
+ u8 pxcnt; /* pixel counter */
+ u8 lncnt; /* line counter */
+ u8 pxdv; /* pixel divisor */
+ u8 lndv; /* line divisor */
u8 m420;
u8 common_A;
u8 common_C;
#include <linux/usb.h>
-static const char *version = __FILE__ ": v0.3.12 2000/05/22 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n";
#define PEGASUS_MTU 1500
{"D-Link DSB-650TX", 0x2001, 0x4001, NULL},
{"D-Link DSB-650TX", 0x2001, 0x4002, NULL},
{"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL},
+ {"D-Link DU-10", 0x07b8, 0xabc1, NULL},
+ {"D-Link DU-E100", 0x07b8, 0x4002, NULL},
{"Linksys USB100TX", 0x066b, 0x2203, NULL},
{"Linksys USB100TX", 0x066b, 0x2204, NULL},
+ {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL},
{"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL},
{"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL},
{"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL},
#define pegasus_set_registers(dev, indx, size, data)\
usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
#define pegasus_set_register(dev, indx, value) \
- { __u8 data = value; \
- usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
+ { __u8 __data = value; \
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);}
static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
__u16 pkt_len;
if (urb->status) {
- info("%s: RX status %d", net->name, urb->status);
+ dbg("%s: RX status %d", net->name, urb->status);
goto goon;
}
netif_stop_queue(net);
- usb_unlink_urb(&pegasus->rx_urb);
- usb_unlink_urb(&pegasus->tx_urb);
- usb_unlink_urb(&pegasus->intr_urb);
+ if ( pegasus->rx_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->rx_urb);
+ if ( pegasus->tx_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->tx_urb);
+ if ( pegasus->intr_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->intr_urb);
MOD_DEC_USE_COUNT;
unregister_netdev(pegasus->net);
- usb_unlink_urb(&pegasus->rx_urb);
- usb_unlink_urb(&pegasus->tx_urb);
- usb_unlink_urb(&pegasus->intr_urb);
+ if ( pegasus->rx_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->rx_urb);
+ if ( pegasus->tx_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->tx_urb);
+ if ( pegasus->intr_urb.status == -EINPROGRESS )
+ usb_unlink_urb(&pegasus->intr_urb);
kfree(pegasus);
}
/*
- * printer.c Version 0.4
+ * printer.c Version 0.5
*
- * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com>
- * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
- * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com>
+ * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
+ * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000 Randy Dunlap <randy.dunlap@intel.com>
*
* USB Printer Device Class driver for USB printers and printer cables
*
* v0.2 - some more cleanups
* v0.3 - cleaner again, waitqueue fixes
* v0.4 - fixes in unidirectional mode
+ * v0.5 - add DEVICE_ID string support
*/
/*
#include <linux/usb.h>
#define USBLP_BUF_SIZE 8192
+#define DEVICE_ID_SIZE 1024
+
+#define IOCNR_GET_DEVICE_ID 1
+#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* get device_id string */
+
+/*
+ * A DEVICE_ID string may include the printer's serial number.
+ * It should end with a semi-colon (';').
+ * An example from an HP 970C DeskJet printer is (this is one long string,
+ * with the serial number changed):
+MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ: ;
+ */
/*
* USB Printer Requests
int minor; /* minor number of device */
unsigned char used; /* True if open */
unsigned char bidir; /* interface is bidirectional */
+ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */
+ /* first 2 bytes are (big-endian) length */
};
static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ };
* Functions for usblp control messages.
*/
-static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, void *buf, int len)
+static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, int value, void *buf, int len)
{
int retval = usb_control_msg(usblp->dev,
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
- request, USB_TYPE_CLASS | dir | recip, 0, usblp->ifnum, buf, len, HZ * 5);
- dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d len: %#x result: %d", request, !!dir, recip, len, retval);
+ request, USB_TYPE_CLASS | dir | recip, value, usblp->ifnum, buf, len, HZ * 5);
+ dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d",
+ request, !!dir, recip, value, len, retval);
return retval < 0 ? retval : 0;
}
#define usblp_read_status(usblp, status)\
- usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, status, 1)
-#define usblp_get_id(usblp, id, maxlen)\
- usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, id, maxlen)
+ usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1)
+#define usblp_get_id(usblp, config, id, maxlen)\
+ usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)
#define usblp_reset(usblp)\
- usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, NULL, 0)
+ usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)
/*
* URB callback.
return -EIO;
}
- if (status & LP_PERRORP) {
-
+ if (~status & LP_PERRORP) {
if (status & LP_POUTPA) {
info("usblp%d: out of paper", usblp->minor);
return -ENOSPC;
if (usblp->used)
return -EBUSY;
- MOD_INC_USE_COUNT;
-
if ((retval = usblp_check_status(usblp))) {
- MOD_DEC_USE_COUNT;
return retval;
}
struct usblp *usblp = file->private_data;
usblp->used = 0;
-
+
if (usblp->dev) {
if (usblp->bidir)
- usb_unlink_urb(&usblp->readurb);
- usb_unlink_urb(&usblp->writeurb);
- MOD_DEC_USE_COUNT;
+ usb_unlink_urb(&usblp->readurb);
+ usb_unlink_urb(&usblp->writeurb);
return 0;
}
usblp_table[usblp->minor] = NULL;
+ kfree(usblp->device_id_string);
kfree(usblp);
- MOD_DEC_USE_COUNT;
return 0;
}
| (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM);
}
+static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int length;
+ struct usblp *usblp = file->private_data;
+
+ if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ))
+ return -EINVAL;
+
+ switch (_IOC_NR(cmd)) {
+ case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+ length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+#if 0
+ dbg ("usblp_ioctl GET_DEVICE_ID: actlen=%d, user size=%d, string='%s'",
+ length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
+#endif
+ if (length > _IOC_SIZE(cmd))
+ length = _IOC_SIZE(cmd); /* truncate */
+ if (copy_to_user ((unsigned char *)arg, usblp->device_id_string, (unsigned long) length))
+ return -EFAULT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct usblp *usblp = file->private_data;
if (signal_pending(current))
return writecount ? writecount : -EINTR;
- timeout = interruptible_sleep_on_timeout(&usblp->wait, timeout);
+ timeout = interruptible_sleep_on_timeout(&usblp->wait, timeout);
}
}
usblp->writeurb.transfer_buffer_length = 0;
} else {
if (!(retval = usblp_check_status(usblp))) {
- err("usblp%d: error %d writing to printer",
- usblp->minor, usblp->writeurb.status);
+ err("usblp%d: error %d writing to printer (retval=%d)",
+ usblp->minor, usblp->writeurb.status, retval);
return -EIO;
}
return retval;
}
-
+
if (writecount == count)
continue;
while (usblp->readurb.status == -EINPROGRESS) {
if (signal_pending(current))
return -EINTR;
- interruptible_sleep_on(&usblp->wait);
+ interruptible_sleep_on(&usblp->wait);
}
}
struct usb_endpoint_descriptor *epread, *epwrite;
struct usblp *usblp;
int minor, i, alts = -1, bidir = 0;
+ int length, err;
char *buf;
for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) {
return NULL;
}
+ if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) {
+ err("out of memory");
+ kfree(usblp);
+ kfree(buf);
+ return NULL;
+ }
+
FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
buf, 0, usblp_bulk, usblp);
FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp);
+ /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */
+ err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1);
+ if (err >= 0) {
+ length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+ if (length < DEVICE_ID_SIZE)
+ usblp->device_id_string[length] = '\0';
+ else
+ usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0';
+ dbg ("usblp%d Device ID string [%d]=%s",
+ minor, length, &usblp->device_id_string[2]);
+ }
+ else {
+ err ("usblp%d: error = %d reading IEEE-1284 Device ID string",
+ minor, err);
+ usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';
+ }
+
+#ifdef DEBUG
+ usblp_check_status(usblp);
+#endif
+
info("usblp%d: USB %sdirectional printer dev %d if %d alt %d",
minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts);
if (usblp->used) return;
+ kfree(usblp->device_id_string);
+
usblp_table[usblp->minor] = NULL;
kfree(usblp);
}
static struct file_operations usblp_fops = {
+ owner: THIS_MODULE,
read: usblp_read,
write: usblp_write,
+ poll: usblp_poll,
+ ioctl: usblp_ioctl,
open: usblp_open,
release: usblp_release,
- poll: usblp_poll,
};
static struct usb_driver usblp_driver = {
/* -*- linux-c -*- */
/*
- * Driver for USB Scanners (linux-2.3.99-pre6-3)
+ * Driver for USB Scanners (linux-2.4.0test1-ac7)
*
* Copyright (C) 1999, 2000 David E. Nelson
*
* - Added wait queues to read_scanner().
*
*
+ * 0.4.3.1
+ *
+ * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud
+ * Linders <rlinders@xs4all.nl>.
+ *
+ *
* TODO
*
* - Performance
/*
- * Driver for USB Scanners (linux-2.3.99-pre6-3)
+ * Driver for USB Scanners (linux-2.4.0test1-ac7)
*
* Copyright (C) 1999, 2000 David E. Nelson
*
{ 0x03f0, 0x0205 }, /* 3300C */
{ 0x03f0, 0x0101 }, /* 4100C */
{ 0x03f0, 0x0105 }, /* 4200C */
- { 0x03f0, 0x0202 }, /* PhotoSmart S20 */
+ { 0x03f0, 0x0102 }, /* PhotoSmart S20 */
{ 0x03f0, 0x0401 }, /* 5200C */
{ 0x03f0, 0x0201 }, /* 6200C */
{ 0x03f0, 0x0601 }, /* 6300C */
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (6/4/2000) pberger and borchers
+* -- Replaced separate calls to spin_unlock_irqrestore and
+* interruptible_sleep_on_interruptible with a new function
+* cond_wait_interruptible_timeout_irqrestore. This eliminates
+* the race condition where the wake up could happen after
+* the unlock and before the sleep.
+* -- Close now waits for output to drain.
+* -- Open waits until any close in progress is finished.
+* -- All out of band responses are now processed, not just the
+* first in a USB packet.
+* -- Fixed a bug that prevented the driver from working when the
+* first Digi port was not the first USB serial port--the driver
+* was mistakenly using the external USB serial port number to
+* try to index into its internal ports.
+* -- Fixed an SMP bug -- write_bulk_callback is called directly from
+* an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are
+* needed for locks outside write_bulk_callback that are also
+* acquired by write_bulk_callback to prevent deadlocks.
+* -- Fixed support for select() by making digi_chars_in_buffer()
+* return 256 when -EINPROGRESS is set, as the line discipline
+* code in n_tty.c expects.
+* -- Fixed an include file ordering problem that prevented debugging
+* messages from working.
+* -- Fixed an intermittent timeout problem that caused writes to
+* sometimes get stuck on some machines on some kernels. It turns
+* out in these circumstances write_chan() (in n_tty.c) was
+* asleep waiting for our wakeup call. Even though we call
+* wake_up_interruptible() in digi_write_bulk_callback(), there is
+* a race condition that could cause the wakeup to fail: if our
+* wake_up_interruptible() call occurs between the time that our
+* driver write routine finishes and write_chan() sets current->state
+* to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state
+* to TASK_RUNNING will be lost and write_chan's subsequent call to
+* schedule() will never return (unless it catches a signal).
+* This race condition occurs because write_bulk_callback() (and thus
+* the wakeup) are called asynchonously from an interrupt, rather than
+* from the scheduler. We can avoid the race by calling the wakeup
+* from the scheduler queue and that's our fix: Now, at the end of
+* write_bulk_callback() we queue up a wakeup call on the scheduler
+* task queue. We still also invoke the wakeup directly since that
+* squeezes a bit more performance out of the driver, and any lost
+* race conditions will get cleaned up at the next scheduler run.
+*
+* NOTE: The problem also goes away if you comment out
+* the two code lines in write_chan() where current->state
+* is set to TASK_RUNNING just before calling driver.write() and to
+* TASK_INTERRUPTIBLE immediately afterwards. This is why the
+* problem did not show up with the 2.2 kernels -- they do not
+* include that code.
+*
* (5/16/2000) pberger and borchers
-* -- added timeouts to sleeps
-* -- handle transition to/from B0 in digi_set_termios
+* -- Added timeouts to sleeps, to defend against lost wake ups.
+* -- Handle transition to/from B0 baud rate in digi_set_termios.
*
* (5/13/2000) pberger and borchers
-* -- all commands now sent on out of band port, using digi_write_oob
-* -- get modem control signals whenever they change, support TIOCMGET/
-* SET/BIS/BIC ioctls
+* -- All commands now sent on out of band port, using
+* digi_write_oob_command.
+* -- Get modem control signals whenever they change, support TIOCMGET/
+* SET/BIS/BIC ioctls.
* -- digi_set_termios now supports parity, word size, stop bits, and
-* receive enable
-* -- cleaned up open and close, use digi_set_termios and digi_write_oob
-* to set port parameters
-* -- added digi_startup_device to start read chains on all ports
-* -- write buffer is only used when count==1, to be sure put_char can
-* write a char (unless the buffer is full)
+* receive enable.
+* -- Cleaned up open and close, use digi_set_termios and
+* digi_write_oob_command to set port parameters.
+* -- Added digi_startup_device to start read chains on all ports.
+* -- Write buffer is only used when count==1, to be sure put_char can
+* write a char (unless the buffer is full).
*
* (5/10/2000) pberger and borchers
-* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls
+* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close.
* -- Fixed problem where the first incoming character is lost on
* port opens after the first close on that port. Now we keep
* the read_urb chain open until shutdown.
* (5/3/2000) pberger and borchers
* -- First alpha version of the driver--many known limitations and bugs.
*
-* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $
+*
+* Locking and SMP
+*
+* - Each port, including the out-of-band port, has a lock used to
+* serialize all access to the port's private structure.
+* - The port lock is also used to serialize all writes and access to
+* the port's URB.
+* - The port lock is also used for the port write_wait condition
+* variable. Holding the port lock will prevent a wake up on the
+* port's write_wait; this can be used with cond_wait_... to be sure
+* the wake up is not lost in a race when dropping the lock and
+* sleeping waiting for the wakeup.
+* - digi_write() does not sleep, since it is sometimes called on
+* interrupt time.
+* - digi_write_bulk_callback() and digi_read_bulk_callback() are
+* called directly from interrupts. Hence spin_lock_irqsave()
+* and spin_lock_irqrestore() are used in the rest of the code
+* for any locks they acquire.
+* - digi_write_bulk_callback() gets the port lock before waking up
+* processes sleeping on the port write_wait. It also schedules
+* wake ups so they happen from the scheduler, because the tty
+* system can miss wake ups from interrupts.
+* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to
+* recheck the condition they are sleeping on. This is defensive,
+* in case a wake up is lost.
+*
+* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $
*/
#include <linux/config.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <linux/usb.h>
-#include "usb-serial.h"
+#include <linux/tqueue.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
#define DEBUG
#undef DEBUG
#endif
+#include <linux/usb.h>
+#include "usb-serial.h"
+
/* Defines */
/* port buffer length -- must be <= transfer buffer length - 2 */
/* so we can be sure to send the full buffer in one urb */
-#define DIGI_PORT_BUF_LEN 16
+#define DIGI_PORT_BUF_LEN 8
-/* retry timeout while waiting for urb->status to go to 0 */
+/* retry timeout while sleeping */
#define DIGI_RETRY_TIMEOUT (HZ/10)
+/* timeout while waiting for tty output to drain in close */
+/* this delay is used twice in close, so the total delay could */
+/* be twice this value */
+#define DIGI_CLOSE_TIMEOUT (5*HZ)
+
+
/* AccelePort USB Defines */
/* ids */
#define DIGI_FLUSH_RX 2
#define DIGI_RESUME_TX 4 /* clears xoff condition */
+#define DIGI_TRANSMIT_NOT_IDLE 0
+#define DIGI_TRANSMIT_IDLE 1
+
#define DIGI_DISABLE 0
#define DIGI_ENABLE 1
/* Structures */
typedef struct digi_private {
+ int dp_port_num;
spinlock_t dp_port_lock;
int dp_buf_len;
unsigned char dp_buf[DIGI_PORT_BUF_LEN];
unsigned int dp_modem_signals;
+ int dp_transmit_idle;
+ int dp_in_close;
+ wait_queue_head_t dp_close_wait; /* wait queue for close */
+ struct tq_struct dp_tasks;
} digi_private_t;
/* Local Function Declarations */
-static int digi_write_oob( unsigned char *buf, int count );
+static void digi_wakeup_write( struct usb_serial_port *port );
+static void digi_wakeup_write_lock( struct usb_serial_port *port );
+static int digi_write_oob_command( unsigned char *buf, int count );
+static int digi_write_inb_command( struct usb_serial_port *port,
+ unsigned char *buf, int count ) __attribute__((unused));
static int digi_set_modem_signals( struct usb_serial_port *port,
unsigned int modem_signals );
+static int digi_transmit_idle( struct usb_serial_port *port,
+ unsigned long timeout );
static void digi_rx_throttle (struct usb_serial_port *port);
static void digi_rx_unthrottle (struct usb_serial_port *port);
static void digi_set_termios( struct usb_serial_port *port,
static int digi_startup( struct usb_serial *serial );
static void digi_shutdown( struct usb_serial *serial );
static void digi_read_bulk_callback( struct urb *urb );
-static void digi_read_oob( struct urb *urb );
+static void digi_read_oob_callback( struct urb *urb );
/* Statics */
/* device info needed for the Digi serial converter */
-static __u16 digi_vendor_id = DIGI_VENDOR_ID;
-static __u16 digi_product_id = DIGI_ID;
+static u16 digi_vendor_id = DIGI_VENDOR_ID;
+static u16 digi_product_id = DIGI_ID;
/* out of band port */
static int oob_port_num; /* index of out-of-band port */
static struct usb_serial_port *oob_port; /* out-of-band port */
static int device_startup = 0;
-/* startup lock -- used to by digi_startup_device */
-spinlock_t startup_lock;
+spinlock_t startup_lock; /* used by startup_device */
+
+static wait_queue_head_t modem_change_wait;
+static wait_queue_head_t transmit_idle_wait;
/* Globals */
/* Functions */
/*
-* Digi Write OOB
+* Cond Wait Interruptible Timeout Irqrestore
+*
+* Do spin_unlock_irqrestore and interruptible_sleep_on_timeout
+* so that wake ups are not lost if they occur between the unlock
+* and the sleep. In other words, spin_lock_irqrestore and
+* interruptible_sleep_on_timeout are "atomic" with respect to
+* wake ups. This is used to implement condition variables.
+*/
+
+static long cond_wait_interruptible_timeout_irqrestore(
+ wait_queue_head_t *q, long timeout,
+ spinlock_t *lock, unsigned long flags )
+{
+
+ wait_queue_t wait;
+
+
+ init_waitqueue_entry( &wait, current );
+
+ set_current_state( TASK_INTERRUPTIBLE );
+
+ add_wait_queue( q, &wait );
+
+ spin_unlock_irqrestore( lock, flags );
+
+ timeout = schedule_timeout(timeout);
+
+ set_current_state( TASK_RUNNING );
+
+ remove_wait_queue( q, &wait );
+
+ return( timeout );
+
+}
+
+
+/*
+* Digi Wakeup Write
+*
+* Wake up port, line discipline, and tty processes sleeping
+* on writes.
+*/
+
+static void digi_wakeup_write_lock( struct usb_serial_port *port )
+{
+
+ unsigned long flags;
+ digi_private_t *priv = (digi_private_t *)(port->private);
+
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ digi_wakeup_write( port );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+}
+
+static void digi_wakeup_write( struct usb_serial_port *port )
+{
+
+ struct tty_struct *tty = port->tty;
+
+
+ /* wake up port processes */
+ wake_up_interruptible( &port->write_wait );
+
+ /* wake up line discipline */
+ if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup )
+ (tty->ldisc.write_wakeup)(tty);
+
+ /* wake up other tty processes */
+ wake_up_interruptible( &tty->write_wait );
+
+}
+
+
+/*
+* Digi Write OOB Command
*
* Write commands on the out of band port. Commands are 4
* bytes each, multiple commands can be sent at once, and
* a negative error returned by usb_submit_urb.
*/
-static int digi_write_oob( unsigned char *buf, int count )
+static int digi_write_oob_command( unsigned char *buf, int count )
{
int ret = 0;
int len;
digi_private_t *oob_priv = (digi_private_t *)(oob_port->private);
+ unsigned long flags = 0;
-dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count );
+dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count );
- spin_lock( &oob_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
while( count > 0 ) {
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock( &oob_priv->dp_port_lock );
- interruptible_sleep_on_timeout( &oob_port->write_wait,
- DIGI_RETRY_TIMEOUT );
+ cond_wait_interruptible_timeout_irqrestore(
+ &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+ &oob_priv->dp_port_lock, flags );
if( signal_pending(current) ) {
return( -EINTR );
}
- spin_lock( &oob_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
}
/* len must be a multiple of 4, so commands are not split */
count -= len;
buf += len;
} else {
- dbg( "digi_write_oob: usb_submit_urb failed, ret=%d",
- ret );
+ dbg(
+ "digi_write_oob_command: usb_submit_urb failed, ret=%d",
+ ret );
break;
}
}
- spin_unlock( &oob_priv->dp_port_lock );
+ spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+
+ return( ret );
+
+}
+
+
+/*
+* Digi Write In Band Command
+*
+* Write commands on the given port. Commands are 4
+* bytes each, multiple commands can be sent at once, and
+* no command will be split across USB packets. Returns 0
+* if successful, or a negative error returned by digi_write.
+*/
+
+static int digi_write_inb_command( struct usb_serial_port *port,
+ unsigned char *buf, int count )
+{
+
+ int ret = 0;
+ int len;
+ digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned long flags = 0;
+
+
+dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num,
+count );
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+ while( count > 0 ) {
+
+ while( port->write_urb->status == -EINPROGRESS ) {
+ cond_wait_interruptible_timeout_irqrestore(
+ &port->write_wait, DIGI_RETRY_TIMEOUT,
+ &priv->dp_port_lock, flags );
+ if( signal_pending(current) ) {
+ return( -EINTR );
+ }
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ }
+
+ /* len must be a multiple of 4 and small enough to */
+ /* guarantee the write will send all data (or none), */
+ /* so commands are not split */
+ len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len );
+ if( len > 4 )
+ len &= ~3;
+
+ /* write any buffered data first */
+ if( priv->dp_buf_len > 0 ) {
+ data[0] = DIGI_CMD_SEND_DATA;
+ data[1] = priv->dp_buf_len;
+ memcpy( data+2, priv->dp_buf, priv->dp_buf_len );
+ memcpy( data+2+priv->dp_buf_len, buf, len );
+ port->write_urb->transfer_buffer_length
+ = priv->dp_buf_len+2+len;
+ } else {
+ memcpy( data, buf, len );
+ port->write_urb->transfer_buffer_length = len;
+ }
+
+#ifdef DEBUG_DATA
+ {
+ int i;
+
+ printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
+ priv->dp_port_num, port->write_urb->transfer_buffer_length );
+ for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
+ printk( "%.2x ",
+ ((unsigned char *)port->write_urb->transfer_buffer)[i] );
+ }
+ printk( "\n" );
+ }
+#endif
+
+ if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
+ priv->dp_buf_len = 0;
+ count -= len;
+ buf += len;
+ } else {
+ dbg(
+ "digi_write_inb_command: usb_submit_urb failed, ret=%d",
+ ret );
+ break;
+ }
+
+ }
+
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( ret );
unsigned char *data = oob_port->write_urb->transfer_buffer;
digi_private_t *port_priv = (digi_private_t *)(port->private);
digi_private_t *oob_priv = (digi_private_t *)(oob_port->private);
+ unsigned long flags = 0;
dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
-port->number, modem_signals );
+port_priv->dp_port_num, modem_signals );
- spin_lock( &oob_priv->dp_port_lock );
- spin_lock( &port_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+ spin_lock_irqsave( &port_priv->dp_port_lock, flags );
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock( &port_priv->dp_port_lock );
- spin_unlock( &oob_priv->dp_port_lock );
- interruptible_sleep_on_timeout( &oob_port->write_wait,
- DIGI_RETRY_TIMEOUT );
+ spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ cond_wait_interruptible_timeout_irqrestore(
+ &oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+ &oob_priv->dp_port_lock, flags );
if( signal_pending(current) ) {
return( -EINTR );
}
- spin_lock( &oob_priv->dp_port_lock );
- spin_lock( &port_priv->dp_port_lock );
+ spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+ spin_lock_irqsave( &port_priv->dp_port_lock, flags );
}
- /* command is 4 bytes: command, line, argument, pad */
data[0] = DIGI_CMD_SET_DTR_SIGNAL;
- data[1] = port->number;
+ data[1] = port_priv->dp_port_num;
data[2] = (modem_signals&TIOCM_DTR) ?
DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE;
data[3] = 0;
data[4] = DIGI_CMD_SET_RTS_SIGNAL;
- data[5] = port->number;
+ data[5] = port_priv->dp_port_num;
data[6] = (modem_signals&TIOCM_RTS) ?
DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE;
data[7] = 0;
ret );
}
- spin_unlock( &port_priv->dp_port_lock );
- spin_unlock( &oob_priv->dp_port_lock );
+ spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
return( ret );
}
+/*
+* Digi Transmit Idle
+*
+* Digi transmit idle waits, up to timeout ticks, for the transmitter
+* to go idle. It returns 0 if successful or a negative error.
+*
+* There are race conditions here if more than one process is calling
+* digi_transmit_idle on the same port at the same time. However, this
+* is only called from close, and only one process can be in close on a
+* port at a time, so its ok.
+*/
+
+static int digi_transmit_idle( struct usb_serial_port *port,
+ unsigned long timeout )
+{
+
+ int ret;
+ unsigned char buf[2];
+ digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
+
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ priv->dp_transmit_idle = 0;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ buf[0] = DIGI_CMD_TRANSMIT_IDLE;
+ buf[1] = 0;
+
+ if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 )
+ return( ret );
+
+ timeout += jiffies;
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+ while( jiffies < timeout && !priv->dp_transmit_idle ) {
+ cond_wait_interruptible_timeout_irqrestore(
+ &transmit_idle_wait, DIGI_RETRY_TIMEOUT,
+ &priv->dp_port_lock, flags );
+ if( signal_pending(current) ) {
+ return( -EINTR );
+ }
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ }
+
+ priv->dp_transmit_idle = 0;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ return( 0 );
+
+}
+
+
static void digi_rx_throttle( struct usb_serial_port *port )
{
-dbg( "digi_rx_throttle: TOP: port=%d", port->number );
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num );
/* stop receiving characters. We just turn off the URB request, and
let chars pile up in the device. If we're doing hardware
static void digi_rx_unthrottle( struct usb_serial_port *port )
{
-dbg( "digi_rx_unthrottle: TOP: port=%d", port->number );
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num );
/* just restart the receive interrupt URB */
//if (usb_submit_urb(port->interrupt_in_urb))
struct termios *old_termios )
{
+ digi_private_t *priv = (digi_private_t *)(port->private);
unsigned int iflag = port->tty->termios->c_iflag;
unsigned int cflag = port->tty->termios->c_cflag;
unsigned int old_iflag = old_termios->c_iflag;
int i = 0;
-dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", port->number, iflag, old_iflag, cflag, old_cflag );
+dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag );
/* set baud rate */
if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
if( arg != -1 ) {
buf[i++] = DIGI_CMD_SET_BAUD_RATE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
}
buf[i++] = DIGI_CMD_SET_PARITY;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
if( arg != -1 ) {
buf[i++] = DIGI_CMD_SET_WORD_SIZE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
arg = DIGI_STOP_BITS_1;
buf[i++] = DIGI_CMD_SET_STOP_BITS;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
arg = DIGI_DISABLE;
buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
- buf[i++] = port->number;
+ buf[i++] = priv->dp_port_num;
buf[i++] = arg;
buf[i++] = 0;
}
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, i )) != 0 )
dbg( "digi_set_termios: write oob failed, ret=%d", ret );
}
static void digi_break_ctl( struct usb_serial_port *port, int break_state )
{
-dbg( "digi_break_ctl: TOP: port=%d", port->number );
+
+#ifdef DEBUG
+ digi_private_t *priv = (digi_private_t *)(port->private);
+#endif
+
+
+dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num );
+
}
digi_private_t *priv = (digi_private_t *)(port->private);
unsigned int val;
+ unsigned long flags = 0;
-dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd );
+dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd );
switch (cmd) {
case TIOCMGET:
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
val = priv->dp_modem_signals;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) )
return( -EFAULT );
return( 0 );
case TIOCMBIC:
if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) )
return( -EFAULT );
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
if( cmd == TIOCMBIS )
val = priv->dp_modem_signals | val;
else if( cmd == TIOCMBIC )
val = priv->dp_modem_signals & ~val;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( digi_set_modem_signals( port, val ) );
case TIOCMIWAIT:
int ret,data_len,new_len;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned long flags = 0;
dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d",
-port->number, count, from_user, in_interrupt() );
+priv->dp_port_num, count, from_user, in_interrupt() );
/* be sure only one write proceeds at a time */
/* there are races on the port private buffer */
/* and races to check write_urb->status */
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
/* wait for urb status clear to submit another urb */
if( port->write_urb->status == -EINPROGRESS ) {
new_len = 0;
}
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( new_len );
data_len = new_len + priv->dp_buf_len;
if( data_len == 0 ) {
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( 0 );
}
- *((unsigned char *)(port->write_urb->transfer_buffer))
- = (unsigned char)DIGI_CMD_SEND_DATA;
- *((unsigned char *)(port->write_urb->transfer_buffer)+1)
- = (unsigned char)data_len;
-
port->write_urb->transfer_buffer_length = data_len+2;
+ *data++ = DIGI_CMD_SEND_DATA;
+ *data++ = data_len;
+
/* copy in buffered data first */
- memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf,
- priv->dp_buf_len );
+ memcpy( data, priv->dp_buf, priv->dp_buf_len );
+ data += priv->dp_buf_len;
/* copy in new data */
if( from_user ) {
- copy_from_user(
- port->write_urb->transfer_buffer+2+priv->dp_buf_len,
- buf, new_len );
+ if( copy_from_user( data, buf, new_len ) ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return( -EFAULT );
+ }
} else {
- memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len,
- buf, new_len );
+ memcpy( data, buf, new_len );
}
#ifdef DEBUG_DATA
-{
+ {
int i;
printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- port->number, port->write_urb->transfer_buffer_length );
+ priv->dp_port_num, port->write_urb->transfer_buffer_length );
for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
printk( "%.2x ",
((unsigned char *)port->write_urb->transfer_buffer)[i] );
}
printk( "\n" );
-}
+ }
#endif
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
} else {
dbg( "digi_write: usb_submit_urb failed, ret=%d",
ret );
- /* no bytes written - should we return the error code or 0? */
- ret = 0;
}
/* return length of new data written, or error */
dbg( "digi_write: returning %d", ret );
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return( ret );
}
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
- struct tty_struct *tty = port->tty;
digi_private_t *priv = (digi_private_t *)(port->private);
int ret;
-dbg( "digi_write_bulk_callback: TOP: port=%d", port->number );
+dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
/* handle callback on out-of-band port */
- if( port->number == oob_port_num ) {
+ if( priv->dp_port_num == oob_port_num ) {
dbg( "digi_write_bulk_callback: oob callback" );
+ spin_lock( &priv->dp_port_lock );
wake_up_interruptible( &port->write_wait );
+ spin_unlock( &priv->dp_port_lock );
return;
}
priv->dp_buf_len );
#ifdef DEBUG_DATA
-{
+ {
int i;
printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=",
- port->number, port->write_urb->transfer_buffer_length );
+ priv->dp_port_num, port->write_urb->transfer_buffer_length );
for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
printk( "%.2x ",
((unsigned char *)port->write_urb->transfer_buffer)[i] );
}
printk( "\n" );
-}
+ }
#endif
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
}
}
- spin_unlock( &priv->dp_port_lock );
- /* wake up port processes */
- wake_up_interruptible( &port->write_wait );
+ /* wake up processes sleeping on writes immediately */
+ digi_wakeup_write( port );
- /* wake up line discipline */
- tty = port->tty;
- if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
- && tty->ldisc.write_wakeup )
- (tty->ldisc.write_wakeup)(tty);
+ spin_unlock( &priv->dp_port_lock );
- /* wake up other tty processes */
- wake_up_interruptible( &tty->write_wait );
+ /* also queue up a wakeup at scheduler time, in case we */
+ /* lost the race in write_chan(). */
+ priv->dp_tasks.routine = (void *)digi_wakeup_write_lock;
+ priv->dp_tasks.data = (void *)port;
+ queue_task( &(priv->dp_tasks), &tq_scheduler );
}
int room;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
-dbg( "digi_write_room: TOP: port=%d", port->number );
+dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num );
- spin_lock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
if( port->write_urb->status == -EINPROGRESS )
room = 0;
else
room = port->bulk_out_size - 2 - priv->dp_buf_len;
- spin_unlock( &priv->dp_port_lock );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
-dbg( "digi_write_room: port=%d, room=%d", port->number, room );
+dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room );
return( room );
}
digi_private_t *priv = (digi_private_t *)(port->private);
-dbg( "digi_chars_in_buffer: TOP: port=%d", port->number );
+dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num );
if( port->write_urb->status == -EINPROGRESS ) {
-dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, port->bulk_out_size - 2 );
- return( port->bulk_out_size - 2 );
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 );
+ /* return( port->bulk_out_size - 2 ); */
+ return( 256 );
} else {
-dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len );
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len );
return( priv->dp_buf_len );
}
static int digi_open( struct usb_serial_port *port, struct file *filp )
{
- int i = 0;
int ret;
unsigned char buf[32];
digi_private_t *priv = (digi_private_t *)(port->private);
struct termios not_termios;
+ unsigned long flags = 0;
-dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active );
+dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active );
/* be sure the device is started up */
if( digi_startup_device( port->serial ) != 0 )
return( -ENXIO );
- MOD_INC_USE_COUNT;
-
/* if port is already open, just return */
/* be sure exactly one open proceeds */
- spin_lock( &priv->dp_port_lock );
- if( port->active++ ) {
- spin_unlock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ if( port->active >= 1 && !priv->dp_in_close ) {
+ ++port->active;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ MOD_INC_USE_COUNT;
return( 0 );
}
- spin_unlock( &priv->dp_port_lock );
+
+ /* don't wait on a close in progress for non-blocking opens */
+ if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return( -EAGAIN );
+ }
+
+ /* prevent other opens from proceeding, before giving up lock */
+ ++port->active;
+
+ /* wait for close to finish */
+ while( priv->dp_in_close ) {
+ cond_wait_interruptible_timeout_irqrestore(
+ &priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
+ &priv->dp_port_lock, flags );
+ if( signal_pending(current) ) {
+ --port->active;
+ return( -EINTR );
+ }
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ }
+
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ MOD_INC_USE_COUNT;
/* read modem signals automatically whenever they change */
- buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS;
- buf[i++] = port->number;
- buf[i++] = DIGI_ENABLE;
- buf[i++] = 0;
+ buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
+ buf[1] = priv->dp_port_num;
+ buf[2] = DIGI_ENABLE;
+ buf[3] = 0;
/* flush fifos */
- buf[i++] = DIGI_CMD_IFLUSH_FIFO;
- buf[i++] = port->number;
- buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
- buf[i++] = 0;
+ buf[4] = DIGI_CMD_IFLUSH_FIFO;
+ buf[5] = priv->dp_port_num;
+ buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+ buf[7] = 0;
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, 8 )) != 0 )
dbg( "digi_open: write oob failed, ret=%d", ret );
/* set termios settings */
static void digi_close( struct usb_serial_port *port, struct file *filp )
{
- int i = 0;
int ret;
unsigned char buf[32];
+ struct tty_struct *tty = port->tty;
digi_private_t *priv = (digi_private_t *)(port->private);
+ unsigned long flags = 0;
-dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active );
+dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active );
/* do cleanup only after final close on this port */
- spin_lock( &priv->dp_port_lock );
- if( --port->active ) {
- spin_unlock( &priv->dp_port_lock );
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ if( port->active > 1 ) {
+ --port->active;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
MOD_DEC_USE_COUNT;
return;
+ } else if( port->active <= 0 ) {
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return;
}
- spin_unlock( &priv->dp_port_lock );
-
+ priv->dp_in_close = 1;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+ /* tell line discipline to process only XON/XOFF */
+ tty->closing = 1;
+
+ /* wait for output to drain */
+ if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+ tty_wait_until_sent( tty, DIGI_CLOSE_TIMEOUT );
+ }
+
+ /* flush driver and line discipline buffers */
+ if( tty->driver.flush_buffer )
+ tty->driver.flush_buffer( tty );
+ if( tty->ldisc.flush_buffer )
+ tty->ldisc.flush_buffer( tty );
+
+ /* wait for transmit idle */
+ if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+ digi_transmit_idle( port, DIGI_CLOSE_TIMEOUT );
+ }
+
/* drop DTR and RTS */
digi_set_modem_signals( port, 0 );
/* disable input flow control */
- buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+ buf[1] = priv->dp_port_num;
+ buf[2] = DIGI_DISABLE;
+ buf[3] = 0;
/* disable output flow control */
- buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+ buf[5] = priv->dp_port_num;
+ buf[6] = DIGI_DISABLE;
+ buf[7] = 0;
/* disable reading modem signals automatically */
- buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[8] = DIGI_CMD_READ_INPUT_SIGNALS;
+ buf[9] = priv->dp_port_num;
+ buf[10] = DIGI_DISABLE;
+ buf[11] = 0;
/* flush fifos */
- buf[i++] = DIGI_CMD_IFLUSH_FIFO;
- buf[i++] = port->number;
- buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
- buf[i++] = 0;
+ buf[12] = DIGI_CMD_IFLUSH_FIFO;
+ buf[13] = priv->dp_port_num;
+ buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+ buf[15] = 0;
/* disable receive */
- buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
- buf[i++] = port->number;
- buf[i++] = DIGI_DISABLE;
- buf[i++] = 0;
+ buf[16] = DIGI_CMD_RECEIVE_ENABLE;
+ buf[17] = priv->dp_port_num;
+ buf[18] = DIGI_DISABLE;
+ buf[19] = 0;
- if( (ret=digi_write_oob( buf, i )) != 0 )
+ if( (ret=digi_write_oob_command( buf, 20 )) != 0 )
dbg( "digi_close: write oob failed, ret=%d", ret );
/* wait for final commands on oob port to complete */
break;
}
}
-
+
/* shutdown any outstanding bulk writes */
usb_unlink_urb (port->write_urb);
+ tty->closing = 0;
+
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ --port->active;
+ priv->dp_in_close = 0;
+ wake_up_interruptible( &priv->dp_close_wait );
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
MOD_DEC_USE_COUNT;
+dbg( "digi_close: done" );
}
dbg( "digi_startup: TOP" );
spin_lock_init( &startup_lock );
+ init_waitqueue_head( &modem_change_wait );
+ init_waitqueue_head( &transmit_idle_wait );
/* allocate the private data structures for all ports */
/* number of regular ports + 1 for the out-of-band port */
return( 1 ); /* error */
/* initialize private structure */
+ priv->dp_port_num = i;
priv->dp_buf_len = 0;
priv->dp_modem_signals = 0;
+ priv->dp_transmit_idle = 0;
+ priv->dp_in_close = 0;
+ init_waitqueue_head( &priv->dp_close_wait );
+ priv->dp_tasks.next = NULL;
+ priv->dp_tasks.data = NULL;
spin_lock_init( &priv->dp_port_lock );
/* initialize write wait queue for this port */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
struct tty_struct *tty = port->tty;
+ digi_private_t *priv = (digi_private_t *)(port->private);
int opcode = ((unsigned char *)urb->transfer_buffer)[0];
int len = ((unsigned char *)urb->transfer_buffer)[1];
int status = ((unsigned char *)urb->transfer_buffer)[2];
int ret,i;
-dbg( "digi_read_bulk_callback: TOP: port=%d", port->number );
+dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
/* handle oob callback */
- if( port->number == oob_port_num ) {
- digi_read_oob( urb );
+ if( priv->dp_port_num == oob_port_num ) {
+ digi_read_oob_callback( urb );
return;
}
if( urb->status ) {
dbg( "digi_read_bulk_callback: nonzero read bulk status: %d",
urb->status );
+ if( urb->status == -ENOENT )
+ return;
goto resubmit;
}
#ifdef DEBUG_DATA
if( urb->actual_length ) {
printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=",
- port->number, urb->actual_length );
+ priv->dp_port_num, urb->actual_length );
for( i=0; i<urb->actual_length; ++i ) {
printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] );
}
#endif
if( urb->actual_length != len + 2 )
- err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", port->number, opcode, len, urb->actual_length, status );
+ err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status );
/* receive data */
if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) {
/* continue read */
resubmit:
- if( (ret=usb_submit_urb(urb)) != 0 )
+ if( (ret=usb_submit_urb(urb)) != 0 ) {
dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d",
ret );
+ }
}
-static void digi_read_oob( struct urb *urb )
+static void digi_read_oob_callback( struct urb *urb )
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
digi_private_t *priv;
- int oob_opcode = ((unsigned char *)urb->transfer_buffer)[0];
- int oob_line = ((unsigned char *)urb->transfer_buffer)[1];
- int oob_status = ((unsigned char *)urb->transfer_buffer)[2];
- int oob_ret = ((unsigned char *)urb->transfer_buffer)[3];
- int ret;
+ int opcode, line, status, val;
+ int i,ret;
-dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret );
+dbg( "digi_read_oob_callback: len=%d", urb->actual_length );
if( urb->status ) {
- dbg( "digi_read_oob: nonzero read bulk status on oob: %d",
+ dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d",
urb->status );
+ if( urb->status == -ENOENT )
+ return;
goto resubmit;
}
- if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) {
+ for( i=0; i<urb->actual_length-3; ) {
- priv = serial->port[oob_line].private;
+ opcode = ((unsigned char *)urb->transfer_buffer)[i++];
+ line = ((unsigned char *)urb->transfer_buffer)[i++];
+ status = ((unsigned char *)urb->transfer_buffer)[i++];
+ val = ((unsigned char *)urb->transfer_buffer)[i++];
- spin_lock( &priv->dp_port_lock );
+dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val );
- /* convert from digi flags to termiox flags */
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_CTS )
- priv->dp_modem_signals |= TIOCM_CTS;
- else
- priv->dp_modem_signals &= ~TIOCM_CTS;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_DSR )
- priv->dp_modem_signals |= TIOCM_DSR;
- else
- priv->dp_modem_signals &= ~TIOCM_DSR;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_RI )
- priv->dp_modem_signals |= TIOCM_RI;
- else
- priv->dp_modem_signals &= ~TIOCM_RI;
- if( oob_ret & DIGI_READ_INPUT_SIGNALS_DCD )
- priv->dp_modem_signals |= TIOCM_CD;
- else
- priv->dp_modem_signals &= ~TIOCM_CD;
+ if( status != 0 )
+ continue;
- spin_unlock( &priv->dp_port_lock );
+ priv = serial->port[line].private;
+
+ if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) {
+
+ spin_lock( &priv->dp_port_lock );
+
+ /* convert from digi flags to termiox flags */
+ if( val & DIGI_READ_INPUT_SIGNALS_CTS )
+ priv->dp_modem_signals |= TIOCM_CTS;
+ else
+ priv->dp_modem_signals &= ~TIOCM_CTS;
+ if( val & DIGI_READ_INPUT_SIGNALS_DSR )
+ priv->dp_modem_signals |= TIOCM_DSR;
+ else
+ priv->dp_modem_signals &= ~TIOCM_DSR;
+ if( val & DIGI_READ_INPUT_SIGNALS_RI )
+ priv->dp_modem_signals |= TIOCM_RI;
+ else
+ priv->dp_modem_signals &= ~TIOCM_RI;
+ if( val & DIGI_READ_INPUT_SIGNALS_DCD )
+ priv->dp_modem_signals |= TIOCM_CD;
+ else
+ priv->dp_modem_signals &= ~TIOCM_CD;
+
+ wake_up_interruptible( &modem_change_wait );
+ spin_unlock( &priv->dp_port_lock );
+
+ } else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) {
+
+ spin_lock( &priv->dp_port_lock );
+ priv->dp_transmit_idle = 1;
+ wake_up_interruptible( &transmit_idle_wait );
+ spin_unlock( &priv->dp_port_lock );
+
+ }
}
+
resubmit:
if( (ret=usb_submit_urb(urb)) != 0 ) {
- dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d",
+ dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d",
ret );
}
}
-
#endif
{
usb_major_init();
- usbdevfs_init();
+ usbdevfs_init();
usb_hub_init();
#ifndef CONFIG_USB_MODULE
#ifdef DEBUG
urb_print (urb, "UNLINK", 1);
#endif
-
- usb_dec_dev_use (urb->dev);
- if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
+ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) {
+ usb_dec_dev_use(urb->dev);
return rh_unlink_urb (urb); /* a request to the virtual root hub */
-
+ }
+
if (urb->hcpriv) {
/* URB active? */
if (urb->status == USB_ST_URB_PENDING && !ohci->disabled) {
urb_priv->ed->state |= ED_URB_DEL;
spin_unlock_irqrestore (&usb_ed_lock, flags);
if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+ usb_dec_dev_use (urb->dev);
add_wait_queue (&op_wakeup, &wait);
current->state = TASK_UNINTERRUPTIBLE;
if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */
err("unlink URB timeout!");
remove_wait_queue (&op_wakeup, &wait);
urb->status = -ENOENT;
- } else
+ } else {
+ /* usb_dec_dev_use done in dl_del_list() */
urb->status = -EINPROGRESS;
+ }
} else {
+ usb_dec_dev_use (urb->dev);
urb_rm_priv (urb);
if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) {
urb->complete (urb);
return -ENODEV;
pci_set_master (dev);
- mem_base = dev->resource[0].start;
+ mem_base = pci_resource_start(dev, 0);
mem_base = (unsigned long) ioremap_nocache (mem_base, 4096);
if (!mem_base) {
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.231 2000/05/13 15:34:17 acher Exp $
+ * $Id: usb-uhci.c,v 1.232 2000/06/11 13:18:30 acher Exp $
*/
#include <linux/config.h>
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
-#define VERSTR "$Revision: 1.231 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.232 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
#include "usb-uhci.h"
s->running = 1;
}
-_static void __exit uhci_cleanup_dev(uhci_t *s)
+_static void uhci_cleanup_dev(uhci_t *s)
{
struct usb_device *root_hub = s->bus->root_hub;
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
#endif
#include <linux/usb.h>
+static const int usb_bandwidth_option =
+#ifdef CONFIG_USB_BANDWIDTH
+ 1;
+#else
+ 0;
+#endif
+
/*
* Prototypes for the device driver probing/loading functions
*/
}
info("registered new driver %s", new_driver->name);
+
+ init_MUTEX(&new_driver->serialize);
+
/* Add it to the list of known drivers */
list_add(&new_driver->driver_list, &usb_driver_list);
struct usb_interface *interface = &dev->actconfig->interface[i];
if (interface->driver == driver) {
+ down(&driver->serialize);
driver->disconnect(dev, interface->private_data);
+ up(&driver->serialize);
usb_driver_release_interface(driver, interface);
/*
* This will go through the list looking for another
}
}
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+ int i;
+
+ for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+ if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum)
+ return &dev->actconfig->interface[i];
+
+ return NULL;
+}
/*
- * calc_bus_time:
+ * usb_calc_bus_time:
*
* returns (approximate) USB bus time in nanoseconds for a USB transaction.
*/
-static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
+static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
{
unsigned long tmp;
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
-} /* end calc_bus_time */
+}
/*
- * check_bandwidth_alloc():
+ * usb_check_bandwidth():
*
* old_alloc is from host_controller->bandwidth_allocated in microseconds;
* bustime is from calc_bus_time(), but converted to microseconds.
*
- * returns 0 if successful,
- * -1 if bandwidth request fails.
+ * returns <bustime in us> if successful,
+ * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails.
*
* FIXME:
* This initial implementation does not use Endpoint.bInterval
* However, this first cut at USB bandwidth allocation does not
* contain any frame allocation tracking.
*/
-static int check_bandwidth_alloc (unsigned int old_alloc, long bustime)
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
{
- unsigned int new_alloc;
+ int new_alloc;
+ int old_alloc = dev->bus->bandwidth_allocated;
+ unsigned int pipe = urb->pipe;
+ long bustime;
+
+ bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe),
+ usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+ if (usb_pipeisoc(pipe))
+ bustime = NS_TO_US(bustime) / urb->number_of_packets;
+ else
+ bustime = NS_TO_US(bustime);
- new_alloc = old_alloc + bustime;
+ new_alloc = old_alloc + (int)bustime;
/* what new total allocated bus time would be */
- dbg("usb-bandwidth-alloc: was: %u, new: %u, "
- "bustime = %ld us, Pipe allowed: %s",
- old_alloc, new_alloc, bustime,
- (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ?
- "yes" : "no");
+ if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
+ dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us",
+ usb_bandwidth_option ? "" : "would have ",
+ old_alloc, new_alloc, bustime);
+
+ if (!usb_bandwidth_option) /* don't enforce it */
+ return (bustime);
+ return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR;
+}
+
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+ dev->bus->bandwidth_allocated += bustime;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs++;
+ else
+ dev->bus->bandwidth_int_reqs++;
+ urb->bandwidth = bustime;
+
+ dbg("bw_alloc increased by %d to %d for %d requesters",
+ bustime,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release a pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
+{
+ dev->bus->bandwidth_allocated -= urb->bandwidth;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs--;
+ else
+ dev->bus->bandwidth_int_reqs--;
- return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? 0 : -1;
-} /* end check_bandwidth_alloc */
+ dbg("bw_alloc reduced by %d to %d for %d requesters",
+ urb->bandwidth,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+ urb->bandwidth = 0;
+}
/*
* New functions for (de)registering a controller
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD(&bus->bus_list);
- INIT_LIST_HEAD(&bus->inodes);
+ INIT_LIST_HEAD(&bus->inodes);
return bus;
}
driver_list);
tmp = tmp->next;
- if (!(private = driver->probe(dev, ifnum)))
+ down(&driver->serialize);
+ private = driver->probe(dev, ifnum);
+ up(&driver->serialize);
+ if (!private)
continue;
usb_driver_claim_interface(driver, interface, private);
dev->bus = bus;
dev->parent = parent;
atomic_set(&dev->refcnt, 1);
- INIT_LIST_HEAD(&dev->inodes);
- INIT_LIST_HEAD(&dev->filelist);
+ INIT_LIST_HEAD(&dev->inodes);
+ INIT_LIST_HEAD(&dev->filelist);
dev->bus->op->allocate(dev);
return usb_start_wait_urb(urb,timeout,actual_length);
}
-/*
- * usb_release_bandwidth():
- *
- * called to release an interrupt pipe's bandwidth (in microseconds)
- */
-void usb_release_bandwidth(struct usb_device *dev, int bw_alloc)
-{
- dev->bus->bandwidth_allocated -= bw_alloc;
- dev->bus->bandwidth_int_reqs--;
- dbg("bw_alloc reduced to %d for %d requesters",
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs +
- dev->bus->bandwidth_isoc_reqs);
-}
-
/*
* usb_get_current_frame_number()
*
return usb_dev->bus->op->get_frame_number (usb_dev);
}
/*-------------------------------------------------------------------*/
+
static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size)
{
struct usb_descriptor_header *header;
begin = buffer;
numskipped = 0;
- /* Skip over at Interface class or vendor descriptors */
+ /* Skip over any interface, class or vendor descriptors */
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
if (!dev->config)
return;
+ if (dev->rawdescriptors) {
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+ kfree(dev->rawdescriptors[i]);
+
+ kfree(dev->rawdescriptors);
+ }
+
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
struct usb_config_descriptor *cf = &dev->config[c];
struct usb_interface *interface = &dev->actconfig->interface[i];
struct usb_driver *driver = interface->driver;
if (driver) {
+ down(&driver->serialize);
driver->disconnect(dev, interface->private_data);
+ up(&driver->serialize);
usb_driver_release_interface(driver, interface);
}
}
}
/* remove /proc/bus/usb entry */
- usbdevfs_remove_device(dev);
+ usbdevfs_remove_device(dev);
/* Free up the device itself, including its device number */
if (dev->devnum > 0)
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
-
+
usb_free_dev(dev);
-
}
/*
(duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT);
}
-static void usb_set_maxpacket(struct usb_device *dev)
+void usb_set_maxpacket(struct usb_device *dev)
{
int i, b;
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
- struct usb_interface *iface = NULL;
- int ret, i;
+ struct usb_interface *iface;
+ int ret;
- for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
- if (dev->actconfig->interface[i].altsetting->bInterfaceNumber == interface) {
- iface = &dev->actconfig->interface[i];
- break;
- }
- }
+ iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
warn("selecting invalid interface %d", interface);
return -EINVAL;
int usb_get_configuration(struct usb_device *dev)
{
- int result;
- unsigned int cfgno;
+ int result;
+ unsigned int cfgno, length;
unsigned char buffer[8];
unsigned char *bigbuffer;
- unsigned int tmp;
struct usb_config_descriptor *desc =
(struct usb_config_descriptor *)buffer;
memset(dev->config, 0, dev->descriptor.bNumConfigurations *
sizeof(struct usb_config_descriptor));
- for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
-
+ dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *
+ dev->descriptor.bNumConfigurations, GFP_KERNEL);
+ if (!dev->rawdescriptors) {
+ err("out of memory");
+ return -1;
+ }
+ for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
/* We grab the first 8 bytes so we know how long the whole */
/* configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
if (result < 0)
err("unable to get descriptor");
else
- err("config descriptor too short (expected %i, got %i)",8,result);
+ err("config descriptor too short (expected %i, got %i)", 8, result);
goto err;
}
/* Get the full buffer */
- le16_to_cpus(&desc->wTotalLength);
+ length = le16_to_cpu(desc->wTotalLength);
- bigbuffer = kmalloc(desc->wTotalLength, GFP_KERNEL);
+ bigbuffer = kmalloc(length, GFP_KERNEL);
if (!bigbuffer) {
err("unable to allocate memory for configuration descriptors");
- result=-ENOMEM;
+ result = -ENOMEM;
goto err;
}
- tmp=desc->wTotalLength;
+
/* Now that we know the length, get the whole thing */
- result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, desc->wTotalLength);
+ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
if (result < 0) {
err("couldn't get all of config descriptors");
kfree(bigbuffer);
goto err;
}
- if (result < tmp) {
- err("config descriptor too short (expected %i, got %i)",tmp,result);
+ if (result < length) {
+ err("config descriptor too short (expected %i, got %i)", length, result);
kfree(bigbuffer);
goto err;
}
- result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
- kfree(bigbuffer);
+ dev->rawdescriptors[cfgno] = bigbuffer;
+
+ result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
if (result > 0)
dbg("descriptor data left");
- else if (result < 0)
- {
- result=-1;
+ else if (result < 0) {
+ result = -1;
goto err;
}
}
*/
int usb_new_device(struct usb_device *dev)
{
- int addr, err;
- int tmp;
+ int err;
info("USB new device connect, assigned device number %d", dev->devnum);
dev->epmaxpacketin [0] = 8;
dev->epmaxpacketout[0] = 8;
- /* Even though we have assigned an address for the device, we */
- /* haven't told it what it's address is yet */
- addr = dev->devnum;
- dev->devnum = 0;
+ err = usb_set_address(dev);
+ if (err < 0) {
+ err("USB device not accepting new address (error=%d)", err);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
if (err < 8) {
err("USB device not responding, giving up (error=%d)", err);
else
err("USB device descriptor short read (expected %i, got %i)",8,err);
- clear_bit(addr, &dev->bus->devmap.devicemap);
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
dev->devnum = -1;
return 1;
}
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
- dev->devnum = addr;
-
- err = usb_set_address(dev);
-
- if (err < 0) {
- err("USB device not accepting new address (error=%d)", err);
- clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
- dev->devnum = -1;
- return 1;
- }
-
- wait_ms(10); /* Let the SET_ADDRESS settle */
-
- tmp = sizeof(dev->descriptor);
-
err = usb_get_device_descriptor(dev);
- if (err < tmp) {
+ if (err < sizeof(dev->descriptor)) {
if (err < 0)
- err("unable to get device descriptor (error=%d)",err);
+ err("unable to get device descriptor (error=%d)", err);
else
- err("USB device descriptor short read (expected %i, got %i)",tmp,err);
+ err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err);
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
dev->devnum = -1;
#endif
/* now that the basic setup is over, add a /proc/bus/usb entry */
- usbdevfs_add_device(dev);
+ usbdevfs_add_device(dev);
/* find drivers willing to handle this device */
usb_find_drivers(dev);
{
int minor = MINOR(inode->i_rdev);
struct usb_driver *c = usb_minors[minor/16];
-
- file->f_op = NULL;
-
- if (c && (file->f_op = c->fops) && file->f_op->open)
- return file->f_op->open(inode,file);
- else
- return -ENODEV;
+ int err = -ENODEV;
+ struct file_operations *old_fops;
+
+ if (!c || !c->fops)
+ return err;
+ old_fops = file->f_op;
+ file->f_op = fops_get(c->fops);
+ if (file->f_op->open)
+ err = file->f_op->open(inode,file);
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ return err;
}
static struct file_operations usb_fops = {
+ owner: THIS_MODULE,
open: usb_open,
};
* into the kernel, and other device drivers are built as modules,
* then these symbols need to be exported for the modules to use.
*/
+EXPORT_SYMBOL(usb_ifnum_to_if);
+
EXPORT_SYMBOL(usb_register);
EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_alloc_bus);
EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(usb_check_bandwidth);
+EXPORT_SYMBOL(usb_claim_bandwidth);
EXPORT_SYMBOL(usb_release_bandwidth);
EXPORT_SYMBOL(usb_set_address);
/*
- * usbkbd.c Version 0.1
+ * $Id: usbkbd.c,v 1.11 2000/05/29 09:01:52 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* USB HIDBP Keyboard support
*
struct urb irq, led;
devrequest dr;
unsigned char leds;
+ char name[128];
+ int open;
};
static void usb_kbd_irq(struct urb *urb)
warn("led urb status %d received", urb->status);
}
+static int usb_kbd_open(struct input_dev *dev)
+{
+ struct usb_kbd *kbd = dev->private;
+
+ if (kbd->open++)
+ return 0;
+
+ if (usb_submit_urb(&kbd->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void usb_kbd_close(struct input_dev *dev)
+{
+ struct usb_kbd *kbd = dev->private;
+
+ if (!--kbd->open)
+ usb_unlink_urb(&kbd->irq);
+}
+
static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
- int i;
+ int i, pipe, maxp;
+ char *buf;
if (dev->descriptor.bNumConfigurations != 1) return NULL;
interface = dev->config[0].interface[ifnum].altsetting + 0;
if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
if ((endpoint->bmAttributes & 3) != 3) return NULL;
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
usb_set_protocol(dev, interface->bInterfaceNumber, 0);
usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
kbd->dev.private = kbd;
kbd->dev.event = usb_kbd_event;
+ kbd->dev.open = usb_kbd_open;
+ kbd->dev.close = usb_kbd_close;
- {
- int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- int maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp,
- usb_kbd_irq, kbd, endpoint->bInterval);
- }
+ FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp,
+ usb_kbd_irq, kbd, endpoint->bInterval);
kbd->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->dr.request = USB_REQ_SET_REPORT;
kbd->dr.index = interface->bInterfaceNumber;
kbd->dr.length = 1;
- FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0),
- (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd);
-
- if (usb_submit_urb(&kbd->irq)) {
+ kbd->dev.name = kbd->name;
+ kbd->dev.idbus = BUS_USB;
+ kbd->dev.idvendor = dev->descriptor.idVendor;
+ kbd->dev.idproduct = dev->descriptor.idProduct;
+ kbd->dev.idversion = dev->descriptor.bcdDevice;
+
+ if (!(buf = kmalloc(63, GFP_KERNEL))) {
kfree(kbd);
return NULL;
}
- input_register_device(&kbd->dev);
+ if (dev->descriptor.iManufacturer &&
+ usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+ strcat(kbd->name, buf);
+ if (dev->descriptor.iProduct &&
+ usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ sprintf(kbd->name, "%s %s", kbd->name, buf);
+
+ if (!strlen(kbd->name))
+ sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x",
+ kbd->dev.idvendor, kbd->dev.idproduct);
- printk(KERN_INFO "input%d: USB HIDBP keyboard\n", kbd->dev.number);
+ kfree(buf);
+
+ FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0),
+ (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd);
+
+ input_register_device(&kbd->dev);
+ printk(KERN_INFO "input%d: %s on on usb%d:%d.%d\n",
+ kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum);
return kbd;
}
/*
- * usbmouse.c Version 0.1
+ * $Id: usbmouse.c,v 1.5 2000/05/29 09:01:52 vojtech Exp $
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 1999-2000 Vojtech Pavlik
*
* USB HIDBP Mouse support
*
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
-#define USBMOUSE_EXTRA
-
struct usb_mouse {
signed char data[8];
+ char name[128];
struct input_dev dev;
struct urb irq;
+ int open;
};
static void usb_mouse_irq(struct urb *urb)
if (urb->status) return;
- input_report_key(dev, BTN_LEFT, !!(data[0] & 0x01));
- input_report_key(dev, BTN_RIGHT, !!(data[0] & 0x02));
- input_report_key(dev, BTN_MIDDLE, !!(data[0] & 0x04));
- input_report_rel(dev, REL_X, data[1]);
- input_report_rel(dev, REL_Y, data[2]);
-#ifdef USBMOUSE_EXTRA
- input_report_key(dev, BTN_SIDE, !!(data[0] & 0x08));
- input_report_key(dev, BTN_EXTRA, !!(data[0] & 0x10));
+ input_report_key(dev, BTN_LEFT, data[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
+ input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
+ input_report_key(dev, BTN_SIDE, data[0] & 0x08);
+ input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
+
+ input_report_rel(dev, REL_X, data[1]);
+ input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
-#endif
+}
+
+static int usb_mouse_open(struct input_dev *dev)
+{
+ struct usb_mouse *mouse = dev->private;
+
+ if (mouse->open++)
+ return 0;
+
+ if (usb_submit_urb(&mouse->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void usb_mouse_close(struct input_dev *dev)
+{
+ struct usb_mouse *mouse = dev->private;
+
+ if (!--mouse->open)
+ usb_unlink_urb(&mouse->irq);
}
static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum)
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
+ int pipe, maxp;
+ char *buf;
if (dev->descriptor.bNumConfigurations != 1) return NULL;
interface = dev->config[0].interface[ifnum].altsetting + 0;
if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
if ((endpoint->bmAttributes & 3) != 3) return NULL;
-#ifndef USBMOUSE_EXTRA
- usb_set_protocol(dev, interface->bInterfaceNumber, 0);
-#endif
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL;
mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
-#ifdef USBMOUSE_EXTRA
mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
mouse->dev.relbit[0] |= BIT(REL_WHEEL);
-#endif
- {
- int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- int maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+ mouse->dev.private = mouse;
+ mouse->dev.open = usb_mouse_open;
+ mouse->dev.close = usb_mouse_close;
- FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp,
- usb_mouse_irq, mouse, endpoint->bInterval);
- }
+ mouse->dev.name = mouse->name;
+ mouse->dev.idbus = BUS_USB;
+ mouse->dev.idvendor = dev->descriptor.idVendor;
+ mouse->dev.idproduct = dev->descriptor.idProduct;
+ mouse->dev.idversion = dev->descriptor.bcdDevice;
- if (usb_submit_urb(&mouse->irq)) {
+ if (!(buf = kmalloc(63, GFP_KERNEL))) {
kfree(mouse);
return NULL;
}
+ if (dev->descriptor.iManufacturer &&
+ usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+ strcat(mouse->name, buf);
+ if (dev->descriptor.iProduct &&
+ usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ sprintf(mouse->name, "%s %s", mouse->name, buf);
+
+ if (!strlen(mouse->name))
+ sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x",
+ mouse->dev.idvendor, mouse->dev.idproduct);
+
+ kfree(buf);
+
+ FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp,
+ usb_mouse_irq, mouse, endpoint->bInterval);
+
input_register_device(&mouse->dev);
- printk(KERN_INFO "input%d: USB HIDBP mouse\n", mouse->dev.number);
+ printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+ mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum);
return mouse;
}
struct parport *pp;
int i;
- printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n",
- usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
-
if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) &&
(usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) &&
(usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284))
return NULL;
+ printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n",
+ usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
+
/* our known interfaces have 3 alternate settings */
if (usbdev->actconfig->interface[ifnum].num_altsetting != 3)
return NULL;
/*
- * wacom.c Version 0.5
+ * $Id: wacom.c,v 1.9 2000/05/29 09:01:52 vojtech Exp $
*
* Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
* relative mode, proximity.
* v0.5 (vp) - Big cleanup, nifty features removed,
* they belong in userspace
+ * v1.8 (vp) - Submit URB only when operating, moved to CVS,
+ * use input_report_key instead of report_btn and
+ * other cleanups
*/
/*
struct urb irq;
struct wacom_features *features;
int tool;
+ int open;
};
static void wacom_graphire_irq(struct urb *urb)
switch ((data[1] >> 5) & 3) {
case 0: /* Pen */
- input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
+ input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80);
break;
case 1: /* Rubber */
- input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+ input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
break;
case 2: /* Mouse */
- input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
- input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
- input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
- input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
+ input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24);
+ input_report_key(dev, BTN_LEFT, data[1] & 0x01);
+ input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
+ input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
input_report_abs(dev, ABS_DISTANCE, data[7]);
input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
return;
input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
- input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
- input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
+ input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
+ input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
}
static void wacom_intuos_irq(struct urb *urb)
switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
case 0x012: wacom->tool = BTN_TOOL_PENCIL; break; /* Inking pen */
+ case 0x822:
case 0x022: wacom->tool = BTN_TOOL_PEN; break; /* Pen */
case 0x032: wacom->tool = BTN_TOOL_BRUSH; break; /* Stroke pen */
case 0x094: wacom->tool = BTN_TOOL_MOUSE; break; /* Mouse 4D */
case 0x096: wacom->tool = BTN_TOOL_LENS; break; /* Lens cursor */
+ case 0x82a:
case 0x0fa: wacom->tool = BTN_TOOL_RUBBER; break; /* Eraser */
case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH; break; /* Airbrush */
default: wacom->tool = BTN_TOOL_PEN; break; /* Unknown tool */
}
- input_report_btn(dev, wacom->tool, 1);
+ input_report_key(dev, wacom->tool, 1);
return;
}
if ((data[1] | data[2] | data[3] | data[4] | data[5] |
data[6] | data[7] | data[8] | data[9]) == 0x80) { /* Exit report */
- input_report_btn(dev, wacom->tool, 0);
+ input_report_key(dev, wacom->tool, 0);
return;
}
input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
- input_report_btn(dev, BTN_STYLUS, data[1] & 2);
- input_report_btn(dev, BTN_STYLUS2, data[1] & 4);
- input_report_btn(dev, BTN_TOUCH, t > 10);
+ input_report_key(dev, BTN_STYLUS, data[1] & 2);
+ input_report_key(dev, BTN_STYLUS2, data[1] & 4);
+ input_report_key(dev, BTN_TOUCH, t > 10);
}
#define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS))
struct wacom_features wacom_features[] = {
- { "Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq,
+ { "Wacom Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq,
BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 },
- { "Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq,
+ { "Wacom Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq,
0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
- { "Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq,
+ { "Wacom Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq,
0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
- { "Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq,
+ { "Wacom Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq,
0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
- { "Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq,
+ { "Wacom Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq,
0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
- { "Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq,
+ { "Wacom Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq,
0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
{ NULL , 0 }
};
+static int wacom_open(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ if (wacom->open++)
+ return 0;
+
+ if (usb_submit_urb(&wacom->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ if (!--wacom->open)
+ usb_unlink_urb(&wacom->irq);
+}
+
static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_endpoint_descriptor *endpoint;
wacom->dev.absmax[ABS_TILT_X] = 127;
wacom->dev.absmax[ABS_TILT_Y] = 127;
+ wacom->dev.private = wacom;
+ wacom->dev.open = wacom_open;
+ wacom->dev.close = wacom_close;
+
+ wacom->dev.name = wacom->features->name;
+ wacom->dev.idbus = BUS_USB;
+ wacom->dev.idvendor = dev->descriptor.idVendor;
+ wacom->dev.idproduct = dev->descriptor.idProduct;
+ wacom->dev.idversion = dev->descriptor.bcdDevice;
+
FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval);
- if (usb_submit_urb(&wacom->irq)) {
- kfree(wacom);
- return NULL;
- }
-
input_register_device(&wacom->dev);
- printk(KERN_INFO "input%d: Wacom %s on usb%d\n", wacom->dev.number, wacom->features->name, dev->devnum);
+ printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+ wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum);
return wacom;
}
/*
- * wmforce.c Version 0.1
+ * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $
*
* Copyright (c) 2000 Vojtech Pavlik
*
signed char data[8];
struct input_dev dev;
struct urb irq;
+ int open;
};
static struct {
__s32 y;
} wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+static char *wmforce_name = "Logitech WingMan Force";
+
static void wmforce_irq(struct urb *urb)
{
struct wmforce *wmforce = urb->context;
input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x);
input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y);
- input_report_key(dev, BTN_TRIGGER, !!(data[6] & 0x01));
- input_report_key(dev, BTN_TOP, !!(data[6] & 0x02));
- input_report_key(dev, BTN_THUMB, !!(data[6] & 0x04));
- input_report_key(dev, BTN_TOP2, !!(data[6] & 0x08));
- input_report_key(dev, BTN_BASE, !!(data[6] & 0x10));
- input_report_key(dev, BTN_BASE2, !!(data[6] & 0x20));
- input_report_key(dev, BTN_BASE3, !!(data[6] & 0x40));
- input_report_key(dev, BTN_BASE4, !!(data[6] & 0x80));
- input_report_key(dev, BTN_BASE5, !!(data[7] & 0x01));
+ input_report_key(dev, BTN_TRIGGER, data[6] & 0x01);
+ input_report_key(dev, BTN_TOP, data[6] & 0x02);
+ input_report_key(dev, BTN_THUMB, data[6] & 0x04);
+ input_report_key(dev, BTN_TOP2, data[6] & 0x08);
+ input_report_key(dev, BTN_BASE, data[6] & 0x10);
+ input_report_key(dev, BTN_BASE2, data[6] & 0x20);
+ input_report_key(dev, BTN_BASE3, data[6] & 0x40);
+ input_report_key(dev, BTN_BASE4, data[6] & 0x80);
+ input_report_key(dev, BTN_BASE5, data[7] & 0x01);
+}
+
+static int wmforce_open(struct input_dev *dev)
+{
+ struct wmforce *wmforce = dev->private;
+
+ if (wmforce->open++)
+ return 0;
+
+ if (usb_submit_urb(&wmforce->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void wmforce_close(struct input_dev *dev)
+{
+ struct wmforce *wmforce = dev->private;
+
+ if (!--wmforce->open)
+ usb_unlink_urb(&wmforce->irq);
}
static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum)
for (i = ABS_X; i <= ABS_Y; i++) {
wmforce->dev.absmax[i] = 1920;
wmforce->dev.absmin[i] = -1920;
- wmforce->dev.absfuzz[i] = 0;
wmforce->dev.absflat[i] = 128;
}
- wmforce->dev.absmax[ABS_THROTTLE] = 0;
- wmforce->dev.absmin[ABS_THROTTLE] = 255;
- wmforce->dev.absfuzz[ABS_THROTTLE] = 0;
- wmforce->dev.absflat[ABS_THROTTLE] = 0;
+ wmforce->dev.absmax[ABS_THROTTLE] = 255;
+ wmforce->dev.absmin[ABS_THROTTLE] = 0;
for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) {
wmforce->dev.absmax[i] = 1;
wmforce->dev.absmin[i] = -1;
- wmforce->dev.absfuzz[i] = 0;
- wmforce->dev.absflat[i] = 0;
}
+ wmforce->dev.private = wmforce;
+ wmforce->dev.open = wmforce_open;
+ wmforce->dev.close = wmforce_close;
+
+ wmforce->dev.name = wmforce_name;
+ wmforce->dev.idbus = BUS_USB;
+ wmforce->dev.idvendor = dev->descriptor.idVendor;
+ wmforce->dev.idproduct = dev->descriptor.idProduct;
+ wmforce->dev.idversion = dev->descriptor.bcdDevice;
+
FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval);
- if (usb_submit_urb(&wmforce->irq)) {
- kfree(wmforce);
- return NULL;
- }
-
input_register_device(&wmforce->dev);
- printk(KERN_INFO "input%d: Logitech WingMan Force USB\n", wmforce->dev.number);
+ printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+ wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum);
return wmforce;
}
#define SO_SECURITY_ENCRYPTION_TRANSPORT 20
#define SO_SECURITY_ENCRYPTION_NETWORK 21
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
+
#endif /* _ASM_SOCKET_H */
--- /dev/null
+/*
+ * Machine dependent access functions for RTC registers.
+ */
+#ifndef _ASM_MC146818RTC_H
+#define _ASM_MC146818RTC_H
+
+#include <asm/io.h>
+
+#ifndef RTC_PORT
+#define RTC_PORT(x) (0x70 + (x))
+#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
+#endif
+
+/*
+ * The yet supported machines all access the RTC index register via
+ * an ISA port access but the way to access the date register differs ...
+ */
+#define CMOS_READ(addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+inb_p(RTC_PORT(1)); \
+})
+#define CMOS_WRITE(val, addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+outb_p((val),RTC_PORT(1)); \
+})
+
+#endif /* _ASM_MC146818RTC_H */
#define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap)
#define INVALID_IO_BITMAP_OFFSET 0x8000
-#ifndef CONFIG_X86_FX
+#ifndef CONFIG_X86_FXSR
#define i387_save_hard(x) \
__asm__("fnsave %0\n\tfwait": :"m" (x))
#include <linux/linkage.h>
+#ifdef __KERNEL__
+
/*
* SMP- and interrupt-safe semaphores..
*
}
#endif
+#endif
datasel;
struct _fpreg _st[8];
unsigned long status;
-#ifdef CONFIG_X86_FX
+#ifdef CONFIG_X86_FXSR
unsigned long mxcsr;
unsigned long _xmm[4*22];
#endif
#define SO_PEERNAME 28
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
+
#endif /* _ASM_SOCKET_H */
#define SO_PEERNAME 28
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
+
#endif /* _ASM_IA64_SOCKET_H */
extern void amiga_do_irq(int irq, struct pt_regs *fp);
extern void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server);
+extern unsigned short amiga_intena_vals[];
+
/* CIA interrupt control register bits */
#define CIA_ICR_TA 0x01
#define SO_PEERNAME 28
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
+
#endif /* _ASM_SOCKET_H */
#define SO_PEERNAME 28
-/* Types of sockets. */
-#define SOCK_DGRAM 1 /* Connectionless, unreliable datagrams
- of fixed maximum length. */
-#define SOCK_STREAM 2 /* Sequenced, reliable, connection-based
- byte streams. */
-#define SOCK_RAW 3 /* Raw protocol interface. */
-#define SOCK_RDM 4 /* Reliably-delivered messages. */
-#define SOCK_SEQPACKET 5 /* Sequenced, reliable, connection-based,
- datagrams of fixed maximum length. */
-#define SOCK_PACKET 10 /* Linux specific way of getting packets at
- the dev level. For writing rarp and
- other similar things on the user level. */
-
-#endif /* __KERNEL__ */
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_DGRAM 1 /* datagram (conn.less) socket */
+#define SOCK_STREAM 2 /* stream (connection) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
#endif /* _ASM_SOCKET_H */
#define SO_ATTACH_FILTER 26
#define SO_DETACH_FILTER 27
-#ifdef __KERNEL__
-
#define SO_PEERNAME 28
-/* Types of sockets. */
-#define SOCK_DGRAM 1 /* Connectionless, unreliable datagrams
- of fixed maximum length. */
-#define SOCK_STREAM 2 /* Sequenced, reliable, connection-based
- byte streams. */
-#define SOCK_RAW 3 /* Raw protocol interface. */
-#define SOCK_RDM 4 /* Reliably-delivered messages. */
-#define SOCK_SEQPACKET 5 /* Sequenced, reliable, connection-based,
- datagrams of fixed maximum length. */
-#define SOCK_PACKET 10 /* Linux specific way of getting packets at
- the dev level. For writing rarp and
- other similar things on the user level. */
-#endif /* __KERNEL__ */
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_DGRAM 1 /* datagram (conn.less) socket */
+#define SOCK_STREAM 2 /* stream (connection) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
#endif /* _ASM_SOCKET_H */
-/* $Id: socket.h,v 1.6 2000/02/27 19:47:58 davem Exp $ */
+/* $Id: socket.h,v 1.7 2000/06/09 07:35:28 davem Exp $ */
#ifndef _ASM_SOCKET_H
#define _ASM_SOCKET_H
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
#define SO_SECURITY_ENCRYPTION_NETWORK 0x5004
+/* Nast libc5 fixup - bletch */
+#if defined(__KERNEL__)
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif
+
#endif /* _ASM_SOCKET_H */
short root; /* midi root key */
short tune; /* pitch tuning (in cents) */
- char low, high; /* key note range */
- char vellow, velhigh; /* velocity range */
- char fixkey, fixvel; /* fixed key, velocity */
- char pan, fixpan; /* panning, fixed panning */
+ signed char low, high; /* key note range */
+ signed char vellow, velhigh; /* velocity range */
+ signed char fixkey, fixvel; /* fixed key, velocity */
+ signed char pan, fixpan; /* panning, fixed panning */
short exclusiveClass; /* exclusive class (0 = none) */
unsigned char amplitude; /* sample volume (127 max) */
unsigned char attenuation; /* attenuation (0.375dB) */
* differs in spirit from the above ffz (man ffs).
*/
-extern __inline__ int generic_ffs(int x)
+static inline int generic_ffs(int x)
{
int r = 1;
* of bits set) of a N-bit word
*/
-extern __inline__ unsigned int generic_hweight32(unsigned int w)
+static inline unsigned int generic_hweight32(unsigned int w)
{
unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
}
-extern __inline__ unsigned int generic_hweight16(unsigned int w)
+static inline unsigned int generic_hweight16(unsigned int w)
{
unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555);
res = (res & 0x3333) + ((res >> 2) & 0x3333);
return (res & 0x00FF) + ((res >> 8) & 0x00FF);
}
-extern __inline__ unsigned int generic_hweight8(unsigned int w)
+static inline unsigned int generic_hweight8(unsigned int w)
{
unsigned int res = (w & 0x55) + ((w >> 1) & 0x55);
res = (res & 0x33) + ((res >> 2) & 0x33);
\
128, /* read_latency */ \
8192, /* write_latency */ \
- 4, /* max_bomb_segments */ \
+ 32, /* max_bomb_segments */ \
\
0, /* nr_segments */ \
0, /* read_pendings */ \
asmlinkage void do_softirq(void);
extern void open_softirq(int nr, void (*action)(struct softirq_action*), void *data);
-extern __inline__ void __cpu_raise_softirq(int cpu, int nr)
+static inline void __cpu_raise_softirq(int cpu, int nr)
{
softirq_state[cpu].active |= (1<<nr);
}
/* I do not want to use atomic variables now, so that cli/sti */
-extern __inline__ void raise_softirq(int nr)
+static inline void raise_softirq(int nr)
{
unsigned long flags;
#define tasklet_unlock(t) do { } while (0)
#endif
-extern __inline__ void tasklet_schedule(struct tasklet_struct *t)
+static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
int cpu = smp_processor_id();
}
}
-extern __inline__ void tasklet_hi_schedule(struct tasklet_struct *t)
+static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
int cpu = smp_processor_id();
}
-extern __inline__ void tasklet_disable_nosync(struct tasklet_struct *t)
+static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
}
-extern __inline__ void tasklet_disable(struct tasklet_struct *t)
+static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
}
-extern __inline__ void tasklet_enable(struct tasklet_struct *t)
+static inline void tasklet_enable(struct tasklet_struct *t)
{
atomic_dec(&t->count);
}
/* It is exported _ONLY_ for wait_on_irq(). */
extern spinlock_t global_bh_lock;
-extern __inline__ void mark_bh(int nr)
+static inline void mark_bh(int nr)
{
tasklet_hi_schedule(bh_task_vec+nr);
}
prev->next = new;
}
-/*
- * Insert a new entry after the specified head..
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
-/*
- * Insert a new entry before the specified head..
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
{
prev->next = next;
}
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ */
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
static __inline__ int list_empty(struct list_head *head)
{
return head->next == head;
}
-/*
- * Splice in "list" into "head"
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
*/
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
{
}
}
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define CM206_CDROM_MAJOR 32
#define IDE2_MAJOR 33
#define IDE3_MAJOR 34
+#define XPRAM_MAJOR 35 /* expanded storage on S/390 = "slow ram" */
+ /* proposed by Peter */
#define NETLINK_MAJOR 36
#define PS2ESDI_MAJOR 36
#define IDETAPE_MAJOR 37
#define SUN_OPENPROM_MINOR 139
#define NVRAM_MINOR 144
#define I2O_MINOR 166
+#define MICROCODE_MINOR 184
#define MISC_DYNAMIC_MINOR 255
#define SGI_GRAPHICS_MINOR 146
/* Find a symbol exported by the kernel or another module */
extern unsigned long get_module_symbol(char *, char *);
+extern void put_module_symbol(unsigned long);
extern int try_inc_mod_count(struct module *mod);
#define PCI_VENDOR_ID_NS 0x100b
#define PCI_DEVICE_ID_NS_87415 0x0002
+#define PCI_DEVICE_ID_NS_87560_LIO 0x000e
+#define PCI_DEVICE_ID_NS_87560_USB 0x0012
#define PCI_DEVICE_ID_NS_87410 0xd001
#define PCI_VENDOR_ID_TSENG 0x100c
#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001
#define PCI_DEVICE_ID_AMD_SCSI 0x2020
#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006
-#define PCI_DEVICE_ID_AMD_VIPER_7403 0x7403
+#define PCI_DEVICE_ID_AMD_COBRA_7400 0x7400
+#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401
+#define PCI_DEVICE_ID_AMD_COBRA_7403 0x7403
+#define PCI_DEVICE_ID_AMD_COBRA_7404 0x7404
#define PCI_DEVICE_ID_AMD_VIPER_7408 0x7408
#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409
#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B
#define PCI_DEVICE_ID_MOTOROLA_CPX8216 0x4806
#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_20267 0x4d30
#define PCI_DEVICE_ID_PROMISE_20246 0x4d33
#define PCI_DEVICE_ID_PROMISE_20262 0x4d38
#define PCI_DEVICE_ID_PROMISE_5300 0x5300
#define PCI_DEVICE_ID_CMD_646 0x0646
#define PCI_DEVICE_ID_CMD_647 0x0647
#define PCI_DEVICE_ID_CMD_648 0x0648
+#define PCI_DEVICE_ID_CMD_649 0x0649
#define PCI_DEVICE_ID_CMD_670 0x0670
#define PCI_VENDOR_ID_VISION 0x1098
#define PCI_DEVICE_ID_DATABOOK_87144 0xb106
#define PCI_VENDOR_ID_PLX 0x10b5
+#define PCI_VENDOR_ID_PLX_ROMULUS 0x106a
+#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076
#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103
#define PCI_DEVICE_ID_PLX_9050 0x9050
#define PCI_DEVICE_ID_PLX_9060 0x9060
#define PCI_VENDOR_ID_AFAVLAB 0x14db
#define PCI_DEVICE_ID_AFAVLAB_TK9902 0x2120
+#define PCI_VENDOR_ID_MORETON 0x15aa
+#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000
+
#define PCI_VENDOR_ID_SYMPHONY 0x1c1c
#define PCI_DEVICE_ID_SYMPHONY_101 0x0001
#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425
#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426
#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428
+#define PCI_DEVICE_ID_INTEL_82820FW_0 0x2440
+#define PCI_DEVICE_ID_INTEL_82820FW_1 0x2442
+#define PCI_DEVICE_ID_INTEL_82820FW_2 0x2443
+#define PCI_DEVICE_ID_INTEL_82820FW_3 0x2444
+#define PCI_DEVICE_ID_INTEL_82820FW_4 0x2449
+#define PCI_DEVICE_ID_INTEL_82820FW_5 0x244b
+#define PCI_DEVICE_ID_INTEL_82820FW_6 0x244e
#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120
#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121
#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122
#include <asm/system.h>
extern void __kfree_skb(struct sk_buff *skb);
-extern void skb_queue_head_init(struct sk_buff_head *list);
-extern void skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
-extern void skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
-extern struct sk_buff * skb_dequeue(struct sk_buff_head *list);
-extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk);
-extern void skb_append(struct sk_buff *old,struct sk_buff *newsk);
-extern void skb_unlink(struct sk_buff *buf);
-extern __u32 skb_queue_len(struct sk_buff_head *list);
extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list);
extern struct sk_buff * alloc_skb(unsigned int size, int priority);
-extern struct sk_buff * dev_alloc_skb(unsigned int size);
extern void kfree_skbmem(struct sk_buff *skb);
extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority);
extern struct sk_buff * skb_copy(const struct sk_buff *skb, int priority);
int newtailroom,
int priority);
#define dev_kfree_skb(a) kfree_skb(a)
-extern unsigned char * skb_put(struct sk_buff *skb, unsigned int len);
-extern unsigned char * skb_push(struct sk_buff *skb, unsigned int len);
-extern unsigned char * skb_pull(struct sk_buff *skb, unsigned int len);
-extern int skb_headroom(const struct sk_buff *skb);
-extern int skb_tailroom(const struct sk_buff *skb);
-extern void skb_reserve(struct sk_buff *skb, unsigned int len);
-extern void skb_trim(struct sk_buff *skb, unsigned int len);
extern void skb_over_panic(struct sk_buff *skb, int len, void *here);
extern void skb_under_panic(struct sk_buff *skb, int len, void *here);
#define skb_realloc_headroom(skb, nhr) skb_copy_expand(skb, nhr, skb_tailroom(skb), GFP_ATOMIC)
/* Internal */
-extern __inline__ atomic_t *skb_datarefp(struct sk_buff *skb)
+static inline atomic_t *skb_datarefp(struct sk_buff *skb)
{
return (atomic_t *)(skb->end);
}
* Returns true if the queue is empty, false otherwise.
*/
-extern __inline__ int skb_queue_empty(struct sk_buff_head *list)
+static inline int skb_queue_empty(struct sk_buff_head *list)
{
return (list->next == (struct sk_buff *) list);
}
* to the buffer.
*/
-extern __inline__ struct sk_buff *skb_get(struct sk_buff *skb)
+static inline struct sk_buff *skb_get(struct sk_buff *skb)
{
atomic_inc(&skb->users);
return skb;
* hit zero.
*/
-extern __inline__ void kfree_skb(struct sk_buff *skb)
+static inline void kfree_skb(struct sk_buff *skb)
{
if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
__kfree_skb(skb);
}
/* Use this if you didn't touch the skb state [for fast switching] */
-extern __inline__ void kfree_skb_fast(struct sk_buff *skb)
+static inline void kfree_skb_fast(struct sk_buff *skb)
{
if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
kfree_skbmem(skb);
* shared data so must not be written to under normal circumstances.
*/
-extern __inline__ int skb_cloned(struct sk_buff *skb)
+static inline int skb_cloned(struct sk_buff *skb)
{
return skb->cloned && atomic_read(skb_datarefp(skb)) != 1;
}
* buffer.
*/
-extern __inline__ int skb_shared(struct sk_buff *skb)
+static inline int skb_shared(struct sk_buff *skb)
{
return (atomic_read(&skb->users) != 1);
}
* NULL is returned on a memory allocation failure.
*/
-extern __inline__ struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
+static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
{
if (skb_shared(skb)) {
struct sk_buff *nskb;
* %NULL is returned on a memory allocation failure.
*/
-extern __inline__ struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
+static inline struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
{
struct sk_buff *nskb;
if(!skb_cloned(skb))
* volatile. Use with caution.
*/
-extern __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+static inline struct sk_buff *skb_peek(struct sk_buff_head *list_)
{
struct sk_buff *list = ((struct sk_buff *)list_)->next;
if (list == (struct sk_buff *)list_)
* volatile. Use with caution.
*/
-extern __inline__ struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
+static inline struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
{
struct sk_buff *list = ((struct sk_buff *)list_)->prev;
if (list == (struct sk_buff *)list_)
* Return the length of an &sk_buff queue.
*/
-extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
+static inline __u32 skb_queue_len(struct sk_buff_head *list_)
{
return(list_->qlen);
}
-extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
+static inline void skb_queue_head_init(struct sk_buff_head *list)
{
spin_lock_init(&list->lock);
list->prev = (struct sk_buff *)list;
* A buffer cannot be placed on two lists at the same time.
*/
-extern __inline__ void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+static inline void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
{
struct sk_buff *prev, *next;
* A buffer cannot be placed on two lists at the same time.
*/
-extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+static inline void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
{
unsigned long flags;
*/
-extern __inline__ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+static inline void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{
struct sk_buff *prev, *next;
* A buffer cannot be placed on two lists at the same time.
*/
-extern __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+static inline void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{
unsigned long flags;
* returned or %NULL if the list is empty.
*/
-extern __inline__ struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
+static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
{
struct sk_buff *next, *prev, *result;
* returned or %NULL if the list is empty.
*/
-extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{
long flags;
struct sk_buff *result;
* Insert a packet on a list.
*/
-extern __inline__ void __skb_insert(struct sk_buff *newsk,
+static inline void __skb_insert(struct sk_buff *newsk,
struct sk_buff * prev, struct sk_buff *next,
struct sk_buff_head * list)
{
* A buffer cannot be placed on two lists at the same time.
*/
-extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+static inline void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
* Place a packet after a given packet in a list.
*/
-extern __inline__ void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+static inline void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
__skb_insert(newsk, old, old->next, old->list);
}
*/
-extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+static inline void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
* the list known..
*/
-extern __inline__ void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
{
struct sk_buff * next, * prev;
* destroyed.
*/
-extern __inline__ void skb_unlink(struct sk_buff *skb)
+static inline void skb_unlink(struct sk_buff *skb)
{
struct sk_buff_head *list = skb->list;
* returned or %NULL if the list is empty.
*/
-extern __inline__ struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
+static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
{
struct sk_buff *skb = skb_peek_tail(list);
if (skb)
* returned or %NULL if the list is empty.
*/
-extern __inline__ struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
+static inline struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
{
long flags;
struct sk_buff *result;
* Add data to an sk_buff
*/
-extern __inline__ unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
+static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
* first byte of the extra data is returned.
*/
-extern __inline__ unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
+static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
return tmp;
}
-extern __inline__ unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
+static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
* panic. A pointer to the first byte of the extra data is returned.
*/
-extern __inline__ unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
return skb->data;
}
-extern __inline__ char *__skb_pull(struct sk_buff *skb, unsigned int len)
+static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len-=len;
return skb->data+=len;
* the old data.
*/
-extern __inline__ unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
+static inline unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
{
if (len > skb->len)
return NULL;
* Return the number of bytes of free space at the head of an &sk_buff.
*/
-extern __inline__ int skb_headroom(const struct sk_buff *skb)
+static inline int skb_headroom(const struct sk_buff *skb)
{
return skb->data-skb->head;
}
* Return the number of bytes of free space at the tail of an sk_buff
*/
-extern __inline__ int skb_tailroom(const struct sk_buff *skb)
+static inline int skb_tailroom(const struct sk_buff *skb)
{
return skb->end-skb->tail;
}
* room. This is only allowed for an empty buffer.
*/
-extern __inline__ void skb_reserve(struct sk_buff *skb, unsigned int len)
+static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
{
skb->data+=len;
skb->tail+=len;
}
-extern __inline__ void __skb_trim(struct sk_buff *skb, unsigned int len)
+static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->len = len;
skb->tail = skb->data+len;
* the buffer is already under the length specified it is not modified.
*/
-extern __inline__ void skb_trim(struct sk_buff *skb, unsigned int len)
+static inline void skb_trim(struct sk_buff *skb, unsigned int len)
{
if (skb->len > len) {
__skb_trim(skb, len);
*/
-extern __inline__ void skb_orphan(struct sk_buff *skb)
+static inline void skb_orphan(struct sk_buff *skb)
{
if (skb->destructor)
skb->destructor(skb);
*/
-extern __inline__ void skb_queue_purge(struct sk_buff_head *list)
+static inline void skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb=skb_dequeue(list))!=NULL)
*/
-extern __inline__ void __skb_queue_purge(struct sk_buff_head *list)
+static inline void __skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb=__skb_dequeue(list))!=NULL)
* allocates memory it can be called from an interrupt.
*/
-extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length)
+static inline struct sk_buff *dev_alloc_skb(unsigned int length)
{
struct sk_buff *skb;
*/
-extern __inline__ struct sk_buff *
+static inline struct sk_buff *
skb_cow(struct sk_buff *skb, unsigned int headroom)
{
headroom = (headroom+15)&~15;
extern void skb_add_mtu(int mtu);
#ifdef CONFIG_NETFILTER
-extern __inline__ void
+static inline void
nf_conntrack_put(struct nf_ct_info *nfct)
{
if (nfct && atomic_dec_and_test(&nfct->master->use))
nfct->master->destroy(nfct->master);
}
-extern __inline__ void
+static inline void
nf_conntrack_get(struct nf_ct_info *nfct)
{
if (nfct)
*/
#ifdef __KERNEL__
-#define __KINLINE extern __inline__
+#define __KINLINE static inline
#elif defined(__GNUC__)
#define __KINLINE static __inline__
#elif defined(__cplusplus)
/* /proc/sys/dev/parport/parport n */
enum {
DEV_PARPORT_SPINTIME=1,
- DEV_PARPORT_HARDWARE=2,
- DEV_PARPORT_DEVICES=3,
+ DEV_PARPORT_BASE_ADDR=2,
+ DEV_PARPORT_IRQ=3,
+ DEV_PARPORT_DMA=4,
+ DEV_PARPORT_MODES=5,
+ DEV_PARPORT_DEVICES=6,
DEV_PARPORT_AUTOPROBE=16
};
/*
* queue_task
*/
-extern __inline__ void queue_task(struct tq_struct *bh_pointer,
+static inline void queue_task(struct tq_struct *bh_pointer,
task_queue *bh_list)
{
if (!test_and_set_bit(0,&bh_pointer->sync)) {
/*
* Call all "bottom halfs" on a given list.
*/
-extern __inline__ void run_task_queue(task_queue *list)
+static inline void run_task_queue(task_queue *list)
{
if (*list) {
unsigned long flags;
struct file_operations *fops;
int minor;
+
+ struct semaphore serialize;
+
+ int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);
};
/*
* New USB Structures *
*----------------------------------------------------------------------------*/
+/*
+ * urb->transfer_flags:
+ */
#define USB_DISABLE_SPD 0x0001
#define USB_ISO_ASAP 0x0002
#define USB_URB_EARLY_COMPLETE 0x0004
void *transfer_buffer; // associated data buffer
int transfer_buffer_length; // data buffer length
int actual_length; // actual data buffer length
+ int bandwidth; // bandwidth for this transfer request (INT or ISO)
unsigned char *setup_packet; // setup packet (control only)
//
int start_frame; // start frame (iso/irq only)
- int number_of_packets; // number of packets in this request (iso/irq only)
+ int number_of_packets; // number of packets in this request (iso)
int interval; // polling interval (irq only)
int error_count; // number of errors in this transfer (iso only)
int timeout; // timeout (in jiffies)
struct list_head bus_list;
void *hcpriv; /* Host Controller private data */
- unsigned int bandwidth_allocated; /* on this Host Controller; */
+ int bandwidth_allocated; /* on this Host Controller; */
/* applies to Int. and Isoc. pipes; */
/* measured in microseconds/frame; */
/* range is 0..900, where 900 = */
int bandwidth_isoc_reqs; /* number of Isoc. requesters */
/* usbdevfs inode list */
- struct list_head inodes;
+ struct list_head inodes;
};
#define USB_MAXCHILDREN (8) /* This is arbitrary */
unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */
/* [0] = IN, [1] = OUT */
- struct usb_config_descriptor *actconfig;/* the active configuration */
int epmaxpacketin[16]; /* INput endpoint specific maximums */
int epmaxpacketout[16]; /* OUTput endpoint specific maximums */
struct usb_device_descriptor descriptor;/* Descriptor */
struct usb_config_descriptor *config; /* All of the configs */
+ struct usb_config_descriptor *actconfig;/* the active configuration */
+
+ char **rawdescriptors; /* Raw descriptors for each config */
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
void *hcpriv; /* Host Controller private data */
/* usbdevfs inode list */
- struct list_head inodes;
- struct list_head filelist;
+ struct list_head inodes;
+ struct list_head filelist;
/*
* Child devices - these can be either new devices
struct usb_device *children[USB_MAXCHILDREN];
};
+extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum);
+
extern int usb_register(struct usb_driver *);
extern void usb_deregister(struct usb_driver *);
extern void usb_free_dev(struct usb_device *);
extern void usb_inc_dev_use(struct usb_device *);
#define usb_dec_dev_use usb_free_dev
-extern void usb_release_bandwidth(struct usb_device *, int);
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc);
+extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc);
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
int usb_get_device_descriptor(struct usb_device *dev);
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr);
int usb_get_status(struct usb_device *dev, int type, int target, void *data);
+int usb_get_configuration(struct usb_device *dev);
int usb_get_protocol(struct usb_device *dev, int ifnum);
int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol);
int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
unsigned char id, void *buf, int size);
int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
int usb_clear_halt(struct usb_device *dev, int pipe);
+void usb_set_maxpacket(struct usb_device *dev);
#define usb_get_extra_descriptor(ifpoint,type,ptr)\
__usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,type,(void**)ptr)
void *context;
};
+#define USBDEVFS_MAXDRIVERNAME 255
+
+struct usbdevfs_getdriver {
+ unsigned int interface;
+ char driver[USBDEVFS_MAXDRIVERNAME + 1];
+};
+
+struct usbdevfs_connectinfo {
+ unsigned int devnum;
+ unsigned char slow;
+};
+
#define USBDEVFS_URB_DISABLE_SPD 1
#define USBDEVFS_URB_ISO_ASAP 2
struct usbdevfs_iso_packet_desc iso_frame_desc[0];
};
+/* ioctls for talking to drivers in the usbcore module: */
+struct usbdevfs_ioctl {
+ int ifno; /* interface 0..N ; negative numbers reserved */
+ int ioctl_code; /* MUST encode size + direction of data so the
+ * macros in <asm/ioctl.h> give correct values */
+ void *data; /* param buffer (in, or out) */
+};
+
+/* You can do most things with hubs just through control messages,
+ * except find out what device connects to what port. */
+struct usbdevfs_hub_portinfo {
+ char nports; /* number of downstream ports in this hub */
+ char port [127]; /* e.g. port 3 connects to device 27 */
+};
+
#define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer)
#define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer)
#define USBDEVFS_RESETEP _IOR('U', 3, unsigned int)
#define USBDEVFS_SETINTERFACE _IOR('U', 4, struct usbdevfs_setinterface)
#define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int)
+#define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver)
#define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb)
#define USBDEVFS_DISCARDURB _IO('U', 11)
#define USBDEVFS_REAPURB _IOW('U', 12, void *)
#define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal)
#define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int)
#define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int)
+#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo)
+#define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl)
+#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo)
+#define USBDEVFS_RESET _IO('U', 20)
/* --------------------------------------------------------------------- */
extern struct file_operations usbdevfs_bus_file_operations;
extern void usbdevfs_conn_disc_event(void);
-
#endif /* __KERNEL__ */
/* --------------------------------------------------------------------- */
so that we select tick to get range about 4 seconds.
*/
-#if HZ == 20
+#if HZ <= 16 || HZ > 4096
+# error Unsupported: HZ <= 16 or HZ > 4096
+#elif HZ <= 32
# define TCP_TW_RECYCLE_TICK (5+2-TCP_TW_RECYCLE_SLOTS_LOG)
-#elif HZ == 64
+#elif HZ <= 64
# define TCP_TW_RECYCLE_TICK (6+2-TCP_TW_RECYCLE_SLOTS_LOG)
-#elif HZ == 100 || HZ == 128
+#elif HZ <= 128
# define TCP_TW_RECYCLE_TICK (7+2-TCP_TW_RECYCLE_SLOTS_LOG)
-#elif HZ == 1024 || HZ == 1000
+#elif HZ <= 256
+# define TCP_TW_RECYCLE_TICK (8+2-TCP_TW_RECYCLE_SLOTS_LOG)
+#elif HZ <= 512
+# define TCP_TW_RECYCLE_TICK (9+2-TCP_TW_RECYCLE_SLOTS_LOG)
+#elif HZ <= 1024
# define TCP_TW_RECYCLE_TICK (10+2-TCP_TW_RECYCLE_SLOTS_LOG)
+#elif HZ <= 2048
+# define TCP_TW_RECYCLE_TICK (11+2-TCP_TW_RECYCLE_SLOTS_LOG)
#else
-# error HZ != 20 && HZ != 64 && HZ != 100 && HZ != 1000 && HZ != 1024
+# define TCP_TW_RECYCLE_TICK (12+2-TCP_TW_RECYCLE_SLOTS_LOG)
#endif
/*
#include "util.h"
+/**
+ * ipc_init - initialise IPC subsystem
+ *
+ * The various system5 IPC resources (semaphores, messages and shared
+ * memory are initialised
+ */
+
void __init ipc_init (void)
{
sem_init();
return;
}
+/**
+ * ipc_init_ids - initialise IPC identifiers
+ * @ids: Identifier set
+ * @size: Number of identifiers
+ *
+ * Given a size for the ipc identifier range (limited below IPCMNI)
+ * set up the sequence range to use then allocate and initialise the
+ * array itself.
+ */
+
void __init ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
ids->entries[i].p = NULL;
}
+/**
+ * ipc_findkey - find a key in an ipc identifier set
+ * @ids: Identifier set
+ * @key: The key to find
+ *
+ * Returns the identifier if found or -1 if not.
+ */
+
int ipc_findkey(struct ipc_ids* ids, key_t key)
{
int id;
return ids->size;
}
+/**
+ * ipc_addid - add an IPC identifier
+ * @ids: IPC identifier set
+ * @new: new IPC permission set
+ * @size: new size limit for the id array
+ *
+ * Add an entry 'new' to the IPC arrays. The permissions object is
+ * initialised and the first free entry is set up and the id assigned
+ * is returned. The list is returned in a locked state on success.
+ * On failure the list is not locked and -1 is returned.
+ */
+
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{
int id;
return id;
}
+/**
+ * ipc_rmid - remove an IPC identifier
+ * @ids: identifier set
+ * @id: Identifier to remove
+ *
+ * The identifier must be valid, and in use. The kernel will panic if
+ * fed an invalid identifier. The entry is removed and internal
+ * variables recomputed. The object associated with the identifier
+ * is returned.
+ */
+
struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
{
struct kern_ipc_perm* p;
return p;
}
+/**
+ * ipc_alloc - allocate ipc space
+ * @size: size desired
+ *
+ * Allocate memory from the appropriate pools and return a pointer to it.
+ * NULL is returned if the allocation fails
+ */
+
void* ipc_alloc(int size)
{
void* out;
return out;
}
+/**
+ * ipc_free - free ipc space
+ * @ptr: pointer returned by ipc_alloc
+ * @size: size of block
+ *
+ * Free a block created with ipc_alloc. The caller must know the size
+ * used in the allocation call.
+ */
+
void ipc_free(void* ptr, int size)
{
if(size > PAGE_SIZE)
kfree(ptr);
}
-/*
- * Check user, group, other permissions for access
- * to ipc resources. return 0 if allowed
+/**
+ * ipcperms - check IPC permissions
+ * @ipcp: IPC permission set
+ * @flag: desired permission set.
+ *
+ * Check user, group, other permissions for access
+ * to ipc resources. return 0 if allowed
*/
+
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
int requested_mode, granted_mode;
* old/new ipc_perm structures
*/
+/**
+ * kernel_to_ipc64_perm - convert kernel ipc permissions to user
+ * @in: kernel permissions
+ * @out: new style IPC permissions
+ *
+ * Turn the kernel object 'in' into a set of permissions descriptions
+ * for returning to userspace (out).
+ */
+
+
void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
{
out->key = in->key;
out->seq = in->seq;
}
+/**
+ * ipc64_perm_to_ipc_perm - convert old ipc permissions to new
+ * @in: new style IPC permissions
+ * @out: old style IPC permissions
+ *
+ * Turn the new style permissions object in into a compatibility
+ * object and store it into the 'out' pointer.
+ */
+
void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
{
out->key = in->key;
out->seq = in->seq;
}
+/**
+ * ipc_parse_version - IPC call version
+ * @cmd: pointer to command
+ *
+ * Return IPC_64 for new style IPC and IPC_OLD for old style IPC.
+ * The cmd value is turned from an encoding command and version into
+ * just the command code.
+ */
+
int ipc_parse_version (int *cmd)
{
if (*cmd & IPC_64) {
* one race (and leak) in BSD implementation.
* OK, that's better. ANOTHER race and leak in BSD variant. There always
* is one more bug... 10/11/98, AV.
+ *
+ * Oh, fsck... Oopsable SMP race in do_process_acct() - we must hold
+ * ->mmap_sem to walk the vma list of current->mm. Nasty, since it leaks
+ * a struct file opened for write. Fixed. 2/6/2000, AV.
*/
#include <linux/config.h>
/*
* External references and all of the globals.
*/
-void acct_timeout(unsigned long);
static volatile int acct_active;
static volatile int acct_needcheck;
/*
* Called whenever the timer says to check the free space.
*/
-void acct_timeout(unsigned long unused)
+static void acct_timeout(unsigned long unused)
{
acct_needcheck = 1;
}
vsize = 0;
if (current->mm) {
- struct vm_area_struct *vma = current->mm->mmap;
+ struct vm_area_struct *vma;
+ down(¤t->mm->mmap_sem);
+ vma = current->mm->mmap;
while (vma) {
vsize += vma->vm_end - vma->vm_start;
vma = vma->vm_next;
}
+ up(¤t->mm->mmap_sem);
}
vsize = vsize / 1024;
ac.ac_mem = encode_comp_t(vsize);
* Gets the address for a symbol in the given module. If modname is
* NULL, it looks for the name in any registered symbol table. If the
* modname is an empty string, it looks for the symbol in kernel exported
- * symbol tables.
+ * symbol tables. Increase the usage count of the module in which the
+ * symbol was found - it's the only way we can guarantee that it's still
+ * there by the time our caller actually uses it.
*/
unsigned long
get_module_symbol(char *modname, char *symname)
struct module_symbol *sym;
int i;
+ spin_lock(&unload_lock);
for (mp = module_list; mp; mp = mp->next) {
if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) &&
MOD_CAN_QUERY(mp) &&
i > 0; --i, ++sym) {
if (strcmp(sym->name, symname) == 0) {
+ __MOD_INC_USE_COUNT(mp);
+ spin_unlock(&unload_lock);
return sym->value;
}
}
}
}
+ spin_unlock(&unload_lock);
return 0;
}
+/* Decrease the use count of the module containing a symbol with the
+ * address passed.
+ */
+void put_module_symbol(unsigned long addr)
+{
+ struct module *mp;
+
+ for (mp = module_list; mp; mp = mp->next) {
+ if (MOD_CAN_QUERY(mp) &&
+ addr >= (unsigned long)mp &&
+ addr < (unsigned long)mp + mp->size) {
+ __MOD_DEC_USE_COUNT(mp);
+ return;
+ }
+ }
+}
+
#else /* CONFIG_MODULES */
/* Dummy syscalls for people who don't want modules */
vmlist_modify_lock(current->mm);
insert_vm_struct(current->mm, new_vma);
merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end);
- vmlist_modify_unlock(vma->vm_mm);
+ vmlist_modify_unlock(current->mm);
do_munmap(current->mm, addr, old_len);
current->mm->total_vm += new_len >> PAGE_SHIFT;
if (new_vma->vm_flags & VM_LOCKED) {
return (wastage + gfporder + (extra * *num));
}
-/* Create a cache:
+/**
+ * kmem_cache_create - Create a cache.
+ * @name: A string which is used in /proc/slabinfo to identify this cache.
+ * @size: The size of objects to be created in this cache.
+ * @offset: The offset to use within the page.
+ * @flags: SLAB flags
+ * @ctor: A constructor for the objects.
+ * @dtor: A destructor for the objects.
+ *
* Returns a ptr to the cache on success, NULL on failure.
* Cannot be called within a int, but can be interrupted.
+ * The @ctor is run when new pages are allocated by the cache
+ * and the @dtor is run before the pages are handed back.
+ * The flags are
+ *
+ * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
+ * to catch references to uninitialised memory.
+ *
+ * %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check
+ * for buffer overruns.
+ *
+ * %SLAB_NO_REAP - Don't automatically reap this cache when we're under
+ * memory pressure.
+ *
+ * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
+ * cacheline. This can be beneficial if you're counting cycles as closely
+ * as davem.
*/
kmem_cache_t *
kmem_cache_create(const char *name, size_t size, size_t offset,
return ret;
}
-/* Shrink a cache. Releases as many slabs as possible for a cache.
+/**
+ * kmem_cache_shrink - Shrink a cache.
+ * @cachep: The cache to shrink.
+ *
+ * Releases as many slabs as possible for a cache.
* To help debugging, a zero exit status indicates all slabs were released.
*/
int
return __kmem_cache_shrink(cachep);
}
-/*
- * Remove a kmem_cache_t object from the slab cache. When returns 0 it
- * completed succesfully. -arca
+/**
+ * kmem_cache_destroy - delete a cache
+ * @cachep: the cache to destroy
+ *
+ * Remove a kmem_cache_t object from the slab cache.
+ * Returns 0 on success.
*
* It is expected this function will be called by a module when it is
* unloaded. This will remove the cache completely, and avoid a duplicate
return;
}
+/**
+ * kmem_cache_alloc - Allocate an object
+ * @cachep: The cache to allocate from.
+ * @flags: See kmalloc().
+ *
+ * Allocate an object from this cache. The flags are only relevant
+ * if the cache has no available objects.
+ */
void *
kmem_cache_alloc(kmem_cache_t *cachep, int flags)
{
return __kmem_cache_alloc(cachep, flags);
}
+/**
+ * kmem_cache_free - Deallocate an object
+ * @cachep: The cache the allocation was from.
+ * @objp: The previously allocated object.
+ *
+ * Free an object which was previously allocated from this
+ * cache.
+ */
void
kmem_cache_free(kmem_cache_t *cachep, void *objp)
{
__kmem_cache_free(cachep, objp);
}
+/**
+ * kmalloc - allocate memory
+ * @size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate.
+ *
+ * kmalloc is the normal method of allocating memory
+ * in the kernel. The @flags argument may be one of:
+ *
+ * %GFP_BUFFER - XXX
+ *
+ * %GFP_ATOMIC - allocation will not sleep. Use inside interrupt handlers.
+ *
+ * %GFP_USER - allocate memory on behalf of user. May sleep.
+ *
+ * %GFP_KERNEL - allocate normal kernel ram. May sleep.
+ *
+ * %GFP_NFS - has a slightly lower probability of sleeping than %GFP_KERNEL.
+ * Don't use unless you're in the NFS code.
+ *
+ * %GFP_KSWAPD - Don't use unless you're modifying kswapd.
+ */
void *
kmalloc(size_t size, int flags)
{
return NULL;
}
+/**
+ * kfree - free previously allocated memory
+ * @objp: pointer returned by kmalloc.
+ *
+ * Don't free memory not originally allocated by kmalloc()
+ * or you will run into trouble.
+ */
void
kfree(const void *objp)
{
return;
}
+/**
+ * kfree_s - free previously allocated memory
+ * @objp: pointer returned by kmalloc.
+ * @size: size of object which is being freed.
+ *
+ * This function performs the same task as kfree() except
+ * that it can use the extra information to speed up deallocation
+ * or perform additional tests.
+ * Don't free memory not originally allocated by kmalloc()
+ * or allocated with a different size, or you will run into trouble.
+ */
void
kfree_s(const void *objp, size_t size)
{
}
-/* Called from try_to_free_page().
+/**
+ * kmem_cache_reap - Reclaim memory from caches.
+ * @gfp_mask: the type of memory required.
+ *
+ * Called from try_to_free_page().
* This function _cannot_ be called within a int, but it
* can be interrupted.
*/
#endif /* SLAB_SELFTEST */
#if defined(CONFIG_PROC_FS)
-/* /proc/slabinfo
- * cache-name num-active-objs total-objs num-active-slabs total-slabs num-pages-per-slab
+/**
+ * get_slabinfo - generates /proc/slabinfo
+ * @buf: the buffer to write it into
+ *
+ * The contents of the buffer are
+ * cache-name
+ * num-active-objs
+ * total-objs
+ * num-active-slabs
+ * total-slabs
+ * num-pages-per-slab
*/
int
get_slabinfo(char *buf)
if [ "$CONFIG_DECNET" != "n" ]; then
source net/decnet/Config.in
fi
+tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
- tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE
bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
# if [ "$CONFIG_LLC" = "y" ]; then
# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
* Note. ddp-> becomes invalid at the realloc.
*/
if (skb_headroom(skb) < 22)
+ {
+ struct sk_buff *newskb;
/* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */
- skb = skb_realloc_headroom(skb, 32);
+ newskb = skb_realloc_headroom(skb, 32);
+ kfree(skb);
+ }
else
skb = skb_unshare(skb, GFP_ATOMIC);
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
- * $Id: br.c,v 1.42 2000/04/14 10:10:34 davem Exp $
+ * $Id: br.c,v 1.43 2000/05/25 02:21:36 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
MOD_INC_USE_COUNT;
}
-static int __init br_init(void)
+int __init br_init(void)
{
printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
- * $Id: br_stp.c,v 1.3 2000/05/05 02:17:17 davem Exp $
+ * $Id: br_stp.c,v 1.4 2000/06/19 10:13:35 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
/* called under bridge lock */
void br_topology_change_detection(struct net_bridge *br)
{
- printk(KERN_INFO "%s: topology change detected, ", br->dev.name);
+ printk(KERN_INFO "%s: topology change detected", br->dev.name);
if (br_is_root_bridge(br)) {
- printk("propagating\n");
+ printk(", propagating");
br->topology_change = 1;
br_timer_set(&br->topology_change_timer, jiffies);
} else if (!br->topology_change_detected) {
- printk("sending tcn bpdu\n");
+ printk(", sending tcn bpdu");
br_transmit_tcn(br);
br_timer_set(&br->tcn_timer, jiffies);
}
+ printk("\n");
br->topology_change_detected = 1;
}
/*
- * $Id: ipconfig.c,v 1.31 2000/05/03 06:37:06 davem Exp $
+ * $Id: ipconfig.c,v 1.32 2000/06/19 06:24:59 davem Exp $
*
* Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
* information to configure own IP address and routes.
#include <net/ip.h>
#include <net/ipconfig.h>
-#include <asm/segment.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
memcpy(skb2->h.raw, skb->h.raw, skb->len);
}
kfree_skb(skb);
-
- return (NULL);
+ return (skb2);
}
static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
the AF_UNIX size (see net/unix/af_unix.c
:unix_mkname()).
*/
+
+/**
+ * move_addr_to_kernel - copy a socket address into kernel space
+ * @uaddr: Address in user space
+ * @kaddr: Address in kernel space
+ * @ulen: Length in user space
+ *
+ * The address is copied into kernel space. If the provided address is
+ * too long an error code of -EINVAL is returned. If the copy gives
+ * invalid addresses -EFAULT is returned. On a success 0 is returned.
+ */
int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr)
{
return 0;
}
+/**
+ * move_addr_to_user - copy an address to user space
+ * @kaddr: kernel space address
+ * @klen: length of address in kernel
+ * @uaddr: user space address
+ * @ulen: pointer to user length field
+ *
+ * The value pointed to by ulen on entry is the buffer length available.
+ * This is overwritten with the buffer space used. -EINVAL is returned
+ * if an overlong buffer is specified or a negative buffer size. -EFAULT
+ * is returned if either the buffer or the length field are not
+ * accessible.
+ * After copying the data up to the limit the user specifies, the true
+ * length of the data is written over the length limit the user
+ * specified. Zero is returned for a success.
+ */
+
int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen)
{
int err;
return &inode->u.socket_i;
}
-/*
- * Go from a file number to its socket slot.
+/**
+ * sockfd_lookup - Go from a file number to its socket slot
+ * @fd: file handle
+ * @err: pointer to an error code return
+ *
+ * The file handle passed in is locked and the socket it is bound
+ * too is returned. If an error occurs the err pointer is overwritten
+ * with a negative errno code and NULL is returned. The function checks
+ * for both invalid handles and passing a handle which is not a socket.
+ *
+ * On a success the socket object pointer is returned.
*/
-extern struct socket *sockfd_lookup(int fd, int *err)
+struct socket *sockfd_lookup(int fd, int *err)
{
struct file *file;
struct inode *inode;
fput(sock->file);
}
-/*
- * Allocate a socket.
+/**
+ * sock_alloc - allocate a socket
+ *
+ * Allocate a new inode and socket object. The two are bound together
+ * and initialised. The socket is then returned. If we are out of inodes
+ * NULL is returned.
*/
struct socket *sock_alloc(void)
return -ENXIO;
}
+/**
+ * sock_release - close a socket
+ * @sock: socket to close
+ *
+ * The socket is released from the protocol stack if it has a release
+ * callback, and the inode is then released if the socket is bound to
+ * an inode not a file.
+ */
+
void sock_release(struct socket *sock)
{
if (sock->ops)
}
extern void sk_init(void);
+
+#ifdef CONFIG_BRIDGE
+extern int br_init(void);
+#endif
+
#ifdef CONFIG_WAN_ROUTER
extern void wanrouter_init(void);
#endif
skb_init();
#endif
+ /*
+ * Ethernet bridge layer.
+ */
+
+#ifdef CONFIG_BRIDGE
+ br_init();
+#endif
/*
* Wan router layer.
*/
#include <asm/system.h>
-#include <asm/segment.h>
#include <linux/types.h>
#include <linux/mm.h>
echo "# Using defaults found in" $DEFAULTS
echo "#"
. $DEFAULTS
- sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > .config-is-not.$$
+ sed -e 's/# \(CONFIG_[^ ]*\) is not.*/\1=n/' <$DEFAULTS >.config-is-not.$$
. .config-is-not.$$
rm .config-is-not.$$
else
# Semantics of + and ? in GNU expr changed, so
# we avoid them:
- if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
+ if expr "$answer" : '0$' '|' "$answer" : '[1-9][0-9]*$' '|' "$answer" : '-[1-9][0-9]*$' >/dev/null
then
eval $2="$answer"
else
if(!orig) return 0;
if(orig->size==newfile->size && orig->uncompressed && !memcmp(orig->uncompressed,newfile->uncompressed,orig->size)) {
newfile->same=orig;
- return 0;
+ return 1;
}
return find_identical_file(orig->child,newfile) ||
find_identical_file(orig->next,newfile);
size -= input;
if (!is_zero (uncompressed, input)) {
compress(base + curr, &len, uncompressed, input);
- uncompressed += input;
curr += len;
}
+ uncompressed += input;
if (len > blksize*2) {
/* (I don't think this can happen with zlib.) */
wm geometry $w +$winx+$winy
}
+bind all <Alt-q> {maybe_exit .maybe}
+
proc maybe_exit { w } {
catch {destroy $w}
toplevel $w -class Dialog
-width 20 -command "destroy $w; focus $oldFocus"
pack $w.f.back $w.f.canc -side left -pady 10 -padx 45
pack $w.f -pady 10 -side bottom -padx 10 -anchor w
+ bind $w <Return> "exit"
+ bind $w <Escape> "destroy $w; focus $oldFocus"
focus $w
global winx; global winy
set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]
proc menusplit {w m n} {
if { $n > 2 } then {
+ update idletasks
set menuoptsize [expr [$m yposition 2] - [$m yposition 1]]
set maxsize [winfo screenheight $w]
set splitpoint [expr $maxsize * 4 / 5 / $menuoptsize - 1]
}
}
+proc menutitle {text menu w} {
+ wm title $w "$text"
+}
+
proc submenu { w mnum line text subnum } {
frame $w.x$line
button $w.x$line.l -text "" -width 15 -relief groove
wm maxsize $w [winfo width $w] $sizy
}
+bind all <Alt-s> { catch {exec cp -f .config .config.old}; \
+ writeconfig .config include/linux/autoconf.h; wrapup .wrap }
+
proc wrapup {w } {
catch {destroy $w}
toplevel $w -class Dialog
pack $w.f.back -side bottom -pady 10 -anchor s
pack $w.f -pady 10 -side top -padx 10 -anchor s
focus $w
+ bind $w <Return> "exit"
global winx; global winy
set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]
wm geometry $w +$winx+$winy
-CC = $(HOSTCC)
-CPP = $(HOSTCC) -E
-
-CFLAGS = $(HOSTCFLAGS) -DLOCALE
-LDFLAGS = -s -L .
-LDLIBS = -lncurses
+HOSTCFLAGS += -DLOCALE
+LIBS = -lncurses
ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h))
- CFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"
+ HOSTCFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"
else
ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h))
- CFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
+ HOSTCFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
else
ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h))
- CFLAGS += -DCURSES_LOC="<ncurses.h>"
+ HOSTCFLAGS += -DCURSES_LOC="<ncurses.h>"
else
- CFLAGS += -DCURSES_LOC="<curses.h>"
+ HOSTCFLAGS += -DCURSES_LOC="<curses.h>"
endif
endif
endif
OBJS = checklist.o menubox.o textbox.o yesno.o inputbox.o \
util.o lxdialog.o msgbox.o
-SRCS = $(OBJS:.o=.c)
+%.o: %.c
+ $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $<
all: ncurses lxdialog
lxdialog: $(OBJS)
+ $(HOSTCC) -o lxdialog $(OBJS) $(LIBS)
ncurses:
@echo "main() {}" > lxtemp.c
- @if $(CC) -lncurses lxtemp.c ; then \
+ @if $(HOSTCC) -lncurses lxtemp.c ; then \
rm -f lxtemp.c a.out; \
else \
rm -f lxtemp.c; \
update_define 1 $total_menus 0
update_mainmenu
-button .f0.right.save -anchor w -text "Save and Exit" \
+button .f0.right.save -anchor w -text "Save and Exit" -underline 0\
-command { catch {exec cp -f .config .config.old}; \
writeconfig .config include/linux/autoconf.h; wrapup .wrap }
-button .f0.right.quit -anchor w -text "Quit Without Saving" \
+button .f0.right.quit -anchor w -text "Quit Without Saving" -underline 0\
-command { maybe_exit .maybe }
button .f0.right.load -anchor w -text "Load Configuration from File" \
printf( "\tpack $w.m -pady 10 -side top -padx 10\n" );
printf( "\twm title $w \"%s\" \n\n", label );
- /*
- * Attach the "Prev", "Next" and "OK" buttons at the end of the window.
- */
- printf( "\tframe $w.f\n" );
- if ( toplevel )
- printf( "\tbutton $w.f.back -text \"Main Menu\" \\\n" );
- else
- printf( "\tbutton $w.f.back -text \"OK\" \\\n" );
- printf( "\t\t-width 15 -command \"catch {focus $oldFocus}; destroy $w; unregister_active %d\"\n",
- menu_num );
- printf( "\tbutton $w.f.next -text \"Next\" \\\n" );
- printf( "\t\t-width 15 -command \"catch {focus $oldFocus}; " );
+ printf( "\tbind $w <Escape> \"catch {focus $oldFocus}; destroy $w; unregister_active %d; break\"\n", menu_num);
+
+ printf("\tset nextscript ");
+ printf("\"catch {focus $oldFocus}; " );
/*
* We are checking which windows should be destroyed and which are
* common parrents with the next one. Remember that menu_num field
}
printf( "menu%d .menu%d \\\"$title\\\"\"\n",
menu_num+1, menu_num+1 );
- if ( menu_num == tot_menu_num )
- printf( "\t$w.f.next configure -state disabled\n" );
- printf( "\tbutton $w.f.prev -text \"Prev\" \\\n" );
+
+ /*
+ * Attach the "Prev", "Next" and "OK" buttons at the end of the window.
+ */
+ printf( "\tframe $w.f\n" );
+ if ( toplevel )
+ printf( "\tbutton $w.f.back -text \"Main Menu\" \\\n" );
+ else
+ printf( "\tbutton $w.f.back -text \"OK\" \\\n" );
+ printf( "\t\t-width 15 -command \"catch {focus $oldFocus}; destroy $w; unregister_active %d\"\n",
+ menu_num );
+ printf( "\tbutton $w.f.next -text \"Next\" -underline 0\\\n" );
+ printf( "\t\t-width 15 -command $nextscript\n");
+
+ if ( menu_num == tot_menu_num ) {
+ printf( "\t$w.f.next configure -state disabled\n" );
+ /*
+ * this is a bit hackish but Alt-n must be rebound
+ * otherwise if the user press Alt-n on the last menu
+ * it will give him/her the next menu of one of the
+ * previous options
+ */
+ printf( "\tbind all <Alt-n> \"puts \\\"no more menus\\\" \"\n");
+ }
+ else
+ {
+ /*
+ * I should be binding to $w not all - but if I do nehat I get the error "unknown path"
+ */
+ printf( "\tbind all <Alt-n> $nextscript\n");
+ }
+ printf( "\tbutton $w.f.prev -text \"Prev\" -underline 0\\\n" );
printf( "\t\t-width 15 -command \"catch {focus $oldFocus}; destroy $w; unregister_active %d; menu%d .menu%d \\\"$title\\\"\"\n",
menu_num, menu_num-1, menu_num-1 );
- if ( menu_num == 1 )
+ if ( menu_num == 1 ) {
printf( "\t$w.f.prev configure -state disabled\n" );
+ }
+ else
+ {
+ printf( "\tbind $w <Alt-p> \"catch {focus $oldFocus}; destroy $w; unregister_active %d; menu%d .menu%d \\\"$title\\\";break\"\n",
+ menu_num, menu_num-1, menu_num-1 );
+ }
printf( "\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n" );
printf( "\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n" );
printf( "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n" );
printf( "\t\t-width [expr [winfo screenwidth .] * 1 / 2] \n" );
printf( "\tframe $w.config.f\n" );
+ printf( "\tbind $w <Key-Down> \"$w.config.canvas yview scroll 1 unit;break;\"\n");
+ printf( "\tbind $w <Key-Up> \"$w.config.canvas yview scroll -1 unit;break;\"\n");
+ printf( "\tbind $w <Key-Next> \"$w.config.canvas yview scroll 1 page;break;\"\n");
+ printf( "\tbind $w <Key-Prior> \"$w.config.canvas yview scroll -1 page;break;\"\n");
+ printf( "\tbind $w <Key-Home> \"$w.config.canvas yview moveto 0;break;\"\n");
+ printf( "\tbind $w <Key-End> \"$w.config.canvas yview moveto 1 ;break;\"\n");
printf( "\tpack $w.config.canvas -side right -fill y\n" );
printf("\n\n");
}
}
else
printf( "\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n" );
- printf( "\twm geometry $w +$winx+$winy\n" );
+ printf( "\tif {[winfo exists $w]} then {wm geometry $w +$winx+$winy}\n" );
/*
* Now that the whole window is in place, we need to wait for an "update"
* around frames. Sigh.
*/
printf( "\tupdate idletasks\n" );
- printf( "\t$w.config.canvas create window 0 0 -anchor nw -window $w.config.f\n\n" );
+ printf( "\tif {[winfo exists $w]} then {$w.config.canvas create window 0 0 -anchor nw -window $w.config.f\n\n" );
printf( "\t$w.config.canvas configure \\\n" );
printf( "\t\t-width [expr [winfo reqwidth $w.config.f] + 1]\\\n" );
printf( "\t\t-scrollregion \"-1 -1 [expr [winfo reqwidth $w.config.f] + 1] \\\n" );
printf( "\t\t$w.config.canvas configure -height $canvtotal\n" );
printf( "\t} else {\n" );
printf( "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n" );
- printf( "\t}\n" );
+ printf( "\t\t}\n\t}\n" );
/*
* Limit the min/max window size. Height can vary, but not width,
* because of the limitations of canvas and our laziness.
*/
printf( "\tupdate idletasks\n" );
- printf( "\twm maxsize $w [winfo width $w] [winfo screenheight $w]\n" );
+ printf( "\tif {[winfo exists $w]} then {\n\twm maxsize $w [winfo width $w] [winfo screenheight $w]\n" );
printf( "\twm minsize $w [winfo width $w] 100\n\n" );
printf( "\twm deiconify $w\n" );
- printf( "}\n\n\n" );
+ printf( "}\n}\n\n" );
/*
* Now we generate the companion procedure for the menu we just
printf( "\tminimenu $w.config.f %d %d \"%s\" tmpvar_%d %s\n",
cfg->menu_number, cfg->menu_line, cfg->label,
-(cfg->nameindex), vartable[cfg->next->nameindex].name );
- printf( "\tmenu $w.config.f.x%d.x.menu -title \"%s\"\n",
+ printf( "\tmenu $w.config.f.x%d.x.menu -tearoffcommand \"menutitle \\\"%s\\\"\"\n",
cfg->menu_line, cfg->label );
cfg1 = cfg;
opt_count = 0;
+/*
+ * Get a quoted or unquoted string. It is recognized by the first
+ * non-white character. '"' and '"' are not allowed inside the string.
+ */
+static const char * get_qnqstring( const char * pnt, char ** label )
+{
+ char quote_char;
+
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+
+ if ( *pnt == '\0' )
+ return pnt;
+ quote_char = *pnt;
+ if ( quote_char == '"' || quote_char == '\'' )
+ return get_qstring( pnt, label );
+ else
+ return get_string( pnt, label );
+}
+
+
+
/*
* Tokenize an 'if' statement condition.
*/
if ( last_menuoption != NULL )
{
pnt = get_qstring(pnt, &cfg->label);
+ if (cfg->label == NULL)
+ syntax_error( "missing comment text" );
last_menuoption->label = cfg->label;
last_menuoption = NULL;
}
case token_define_string:
pnt = get_string( pnt, &buffer );
cfg->nameindex = get_varnum( buffer );
- pnt = get_qstring( pnt, &cfg->value );
+ pnt = get_qnqstring( pnt, &cfg->value );
+ if (cfg->value == NULL)
+ syntax_error( "missing value" );
break;
case token_dep_bool:
pnt = get_qstring ( pnt, &cfg->label );
pnt = get_string ( pnt, &buffer );
cfg->nameindex = get_varnum( buffer );
- pnt = get_qstring ( pnt, &cfg->value );
+ pnt = get_qnqstring ( pnt, &cfg->value );
+ if (cfg->value == NULL)
+ syntax_error( "missing initial value" );
break;
case token_if: