## Automatically generated incremental diff ## From: linux-2.5.6-pre3 ## To: linux-2.5.6 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.6-pre3/Documentation/DocBook/kernel-locking.tmpl linux-2.5.6/Documentation/DocBook/kernel-locking.tmpl --- linux-2.5.6-pre3/Documentation/DocBook/kernel-locking.tmpl Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/Documentation/DocBook/kernel-locking.tmpl Thu Mar 7 18:24:40 2002 @@ -788,8 +788,18 @@ - Note that the atomic operations are defined to act as both - read and write barriers on all platforms. + Note that the atomic operations do in general not act as memory + barriers. Instead you can insert a memory barrier before or + after atomic_inc() or + atomic_dec() by inserting + smp_mb__before_atomic_inc(), + smp_mb__after_atomic_inc(), + smp_mb__before_atomic_dec() or + smp_mb__after_atomic_dec() + respectively. The advantage of using those macros instead of + smp_mb() is, that they are cheaper on some + platforms. + diff -urN linux-2.5.6-pre3/Documentation/filesystems/cramfs.txt linux-2.5.6/Documentation/filesystems/cramfs.txt --- linux-2.5.6-pre3/Documentation/filesystems/cramfs.txt Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/Documentation/filesystems/cramfs.txt Thu Mar 7 18:24:40 2002 @@ -10,7 +10,7 @@ You can't write to a cramfs filesystem (making it compressible and compact also makes it _very_ hard to update on-the-fly), so you have to -create the disk image with the "mkcramfs" utility in scripts/cramfs. +create the disk image with the "mkcramfs" utility. Usage Notes @@ -19,9 +19,7 @@ File sizes are limited to less than 16MB. Maximum filesystem size is a little over 256MB. (The last file on the -filesystem is allowed to extend past 256MB.) (Comments in mkcramfs.c -suggest that ROM sizes may be limited to 64MB, though that's not a -limitation in cramfs code.) +filesystem is allowed to extend past 256MB.) Only the low 8 bits of gid are stored. The current version of mkcramfs simply truncates to 8 bits, which is a potential security @@ -48,18 +46,28 @@ For /usr/share/magic ------------------- +-------------------- -0 long 0x28cd3d45 Linux cramfs ->4 long x size %d ->8 long x flags 0x%x ->12 long x future 0x%x +0 ulelong 0x28cd3d45 Linux cramfs offset 0 +>4 ulelong x size %d +>8 ulelong x flags 0x%x +>12 ulelong x future 0x%x >16 string >\0 signature "%.16s" ->32 long x fsid.crc 0x%x ->36 long x fsid.edition %d ->40 long x fsid.blocks %d ->44 long x fsid.files %d +>32 ulelong x fsid.crc 0x%x +>36 ulelong x fsid.edition %d +>40 ulelong x fsid.blocks %d +>44 ulelong x fsid.files %d >48 string >\0 name "%.16s" +512 ulelong 0x28cd3d45 Linux cramfs offset 512 +>516 ulelong x size %d +>520 ulelong x flags 0x%x +>524 ulelong x future 0x%x +>528 string >\0 signature "%.16s" +>544 ulelong x fsid.crc 0x%x +>548 ulelong x fsid.edition %d +>552 ulelong x fsid.blocks %d +>556 ulelong x fsid.files %d +>560 string >\0 name "%.16s" Hacker Notes diff -urN linux-2.5.6-pre3/Documentation/networking/driver.txt linux-2.5.6/Documentation/networking/driver.txt --- linux-2.5.6-pre3/Documentation/networking/driver.txt Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/networking/driver.txt Thu Mar 7 18:24:41 2002 @@ -0,0 +1,84 @@ +Documents about softnet driver issues in general can be found +at: + + http://www.firstfloor.org/~andi/softnet/ + +Transmit path guidelines: + +1) The hard_start_xmit method must never return '1' under any + normal circumstances. It is considered a hard error unless + there is no way your device can tell ahead of time when it's + transmit function will become busy. + + Instead it must maintain the queue properly. For example, + for a driver implementing scatter-gather this means: + + static int drv_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) + { + struct drv *dp = dev->priv; + + lock_tx(dp); + ... + /* This is a hard error log it. */ + if (TX_BUFFS_AVAIL(dp) <= (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + unlock_tx(dp); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + ... queue packet to card ... + ... update tx consumer index ... + + if (TX_BUFFS_AVAIL(dp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + + ... + unlock_tx(dp); + ... + } + + And then at the end of your TX reclaimation event handling: + + if (netif_queue_stopped(dp->dev) && + TX_BUFFS_AVAIL(dp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(dp->dev); + + For a non-scatter-gather supporting card, the three tests simply become: + + /* This is a hard error log it. */ + if (TX_BUFFS_AVAIL(dp) <= 0) + + and: + + if (TX_BUFFS_AVAIL(dp) == 0) + + and: + + if (netif_queue_stopped(dp->dev) && + TX_BUFFS_AVAIL(dp) > 0) + netif_wake_queue(dp->dev); + +2) Do not forget to update netdev->trans_start to jiffies after + each new tx packet is given to the hardware. + +3) Do not forget that once you return 0 from your hard_start_xmit + method, it is your driver's responsibility to free up the SKB + and in some finite amount of time. + + For example, this means that it is not allowed for your TX + mitigation scheme to let TX packets "hang out" in the TX + ring unreclaimed forever if no new TX packets are sent. + This error can deadlock sockets waiting for send buffer room + to be freed up. + + If you return 1 from the hard_start_xmit method, you must not keep + any reference to that SKB and you must not attempt to free it up. + +Probing guidelines: + +1) Any hardware layer address you obtain for your device should + be verified. For example, for ethernet check it with + linux/etherdevice.h:is_valid_ether_addr() diff -urN linux-2.5.6-pre3/Documentation/networking/generic-hdlc.txt linux-2.5.6/Documentation/networking/generic-hdlc.txt --- linux-2.5.6-pre3/Documentation/networking/generic-hdlc.txt Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/networking/generic-hdlc.txt Thu Mar 7 18:24:41 2002 @@ -0,0 +1,115 @@ +Generic HDLC layer for Linux kernel 2.4/2.5 +Krzysztof Halasa +May, 2001 + + +Generic HDLC layer currently supports: +- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP), +- raw HDLC (IPv4 only), +- Cisco HDLC, +- PPP (uses syncppp.c), +- X.25 (uses X.25 routines). + +There are hardware drivers for the following cards: +- C101 by Moxa Technologies Co., Ltd. +- RISCom/N2 by SDL Communications Inc. +- and others, some not in the official kernel. + +Make sure the hdlc.o and the hardware driver are loaded. It should +create a number of "hdlc" (hdlc0 etc) network devices, one for each +WAN port. You'll need the "sethdlc" utility, get it from: + http://hq.pm.waw.pl/hdlc/ + +Compile sethdlc.c utility: + gcc -O2 -Wall -o sethdlc sethdlc.c +Make sure you're using a correct version of sethdlc for your kernel. + +Use sethdlc to set physical interface, clock rate, HDLC mode used, +and add any required PVCs if using Frame Relay. +Usually you want something like: + + sethdlc hdlc0 clock int rate 128000 + sethdlc hdlc0 cisco interval 10 timeout 25 +or + sethdlc hdlc0 rs232 clock ext + sethdlc fr lmi ansi + sethdlc create 99 + +In Frame Relay mode, ifconfig master hdlc device up (without assigning +any IP address to it) before using pvc devices. + + +Setting interface: + +* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port + if the card has software-selectable interfaces + loopback - activate hardware loopback (for testing only) +* clock ext - external clock (uses DTE RX and TX clock) +* clock int - internal clock (provides clock signal on DCE clock output) +* clock txint - TX internal, RX external (provides TX clock on DCE output) +* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output) +* rate - sets clock rate in bps (not required for external clock or + for txfromrx) + +Setting protocol: + +* hdlc - sets raw HDLC (IP-only) mode + nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code + no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu + crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity + +* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported) + interval - time in seconds between keepalive packets + timeout - time in seconds after last received keepalive packet before + we assume the link is down + +* ppp - sets synchronous PPP mode + +* x25 - sets X.25 mode + +* fr - Frame Relay mode + lmi ansi / ccitt / none - LMI (link management) type + dce - Frame Relay DCE (network) side LMI instead of default DTE (user). + It has nothing to do with clocks! + t391 - link integrity verification polling timer (in seconds) - user + t392 - polling verification timer (in seconds) - network + n391 - full status polling counter - user + n392 - error threshold - both user and network + n393 - monitored events count - both user and network + +* create | delete n - FR only - adds / deletes PVC interface with DLCI #n. + + + + +Board-specific issues +--------------------- + +n2.o and c101.o need parameters to work (note double quotes): + + insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"' +example: + insmod n2 hw='"0x300,10,0xD0000,01"' + +or + insmod c101 hw='"irq,ram[:irq,...]" +example: + insmod c101 hw='"9,0xdc000"' + +If built into the kernel, these drivers need kernel (command line) parameters: + n2=io,irq,ram,ports:... +or + c101=irq,ram:... + + + +If you have a problem with N2 or C101 card, you can issue the "private" +command to see port's packet descriptor rings: + + sethdlc hdlc0 private + +The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS. +Attaching this info to bug reports would be helpful. Anyway, let me know +if you have problems using this. + +For patches and other info look at http://hq.pm.waw.pl/hdlc/ diff -urN linux-2.5.6-pre3/Documentation/pci.txt linux-2.5.6/Documentation/pci.txt --- linux-2.5.6-pre3/Documentation/pci.txt Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/Documentation/pci.txt Thu Mar 7 18:24:41 2002 @@ -156,6 +156,11 @@ which enables the bus master bit in PCI_COMMAND register and also fixes the latency timer value if it's set to something bogus by the BIOS. + If you want to use the PCI Memory-Write-Invalidate transaction, +call pci_set_mwi(). This enables bit PCI_COMMAND bit for Mem-Wr-Inval +and also ensures that the cache line size register is set correctly. +Make sure to check the return value of pci_set_mwi(), not all architectures +may support Memory-Write-Invalidate. 4. How to access PCI config space ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -202,6 +207,8 @@ pci_resource_len() Returns the byte length of a PCI region pci_set_drvdata() Set private driver data pointer for a pci_dev pci_get_drvdata() Return private driver data pointer for a pci_dev +pci_set_mwi() Enable Memory-Write-Invalidate transactions. +pci_clear_mwi() Disable Memory-Write-Invalidate transactions. 7. Miscellaneous hints diff -urN linux-2.5.6-pre3/Documentation/sound/AD1816 linux-2.5.6/Documentation/sound/AD1816 --- linux-2.5.6-pre3/Documentation/sound/AD1816 Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/Documentation/sound/AD1816 Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ -Documentation for the AD1816(A) sound driver -============================================ - -Installation: -------------- - -To get your AD1816(A) based sound card work, you'll have to enable support for -experimental code ("Prompt for development and/or incomplete code/drivers") -and isapnp ("Plug and Play support", "ISA Plug and Play support"). Enable -"Sound card support", "OSS modules support" and "Support for AD1816(A) based -cards (EXPERIMENTAL)" in the sound configuration menu, too. Now build, install -and reboot the new kernel as usual. - -Features: ---------- - -List of features supported by this driver: -- full-duplex support -- supported audio formats: unsigned 8bit, signed 16bit little endian, - signed 16bit big endian, µ-law, A-law -- supported channels: mono and stereo -- supported recording sources: Master, CD, Line, Line1, Line2, Mic -- supports phat 3d stereo circuit (Line 3) - - -Supported cards: ----------------- - -The following cards are known to work with this driver: -- Terratec Base 1 -- Terratec Base 64 -- HP Kayak -- Acer FX-3D -- SY-1816 -- Highscreen Sound-Boostar 32 Wave 3D -- Highscreen Sound-Boostar 16 -- AVM Apex Pro card -- (Aztech SC-16 3D) -- (Newcom SC-16 3D) -- (Terratec EWS64S) - -Cards listed in brackets are not supported reliable. If you have such a card -you should add the extra parameter: - options=1 -when loading the ad1816 module via modprobe. - - -Troubleshooting: ----------------- - -First of all you should check, if the driver has been loaded -properly. - -If loading of the driver succeeds, but playback/capture fails, check -if you used the correct values for irq, dma and dma2 when loading the module. -If one of them is wrong you usually get the following error message: - -Nov 6 17:06:13 tek01 kernel: Sound: DMA (output) timed out - IRQ/DRQ config error? - -If playback/capture is too fast or to slow, you should have a look at -the clock chip of your sound card. The AD1816 was designed for a 33MHz -oscillator, however most sound card manufacturer use slightly -different oscillators as they are cheaper than 33MHz oscillators. If -you have such a card you have to adjust the ad1816_clockfreq parameter -above. For example: For a card using a 32.875MHz oscillator use -ad1816_clockfreq=32875 instead of ad1816_clockfreq=33000. - - -Updates, bugfixes and bugreports: --------------------------------- - -As the driver is still experimental and under development, you should -watch out for updates. Updates of the driver are available on the -Internet from one of my home pages: - http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html -or: - http://www.tu-darmstadt.de/~tek01/projects/linux.html - -Bugreports, bugfixes and related questions should be sent via E-Mail to: - tek@rbg.informatik.tu-darmstadt.de - -Thorsten Knabe -Christoph Hellwig - Last modified: 2000/09/20 diff -urN linux-2.5.6-pre3/Documentation/sound/ALS linux-2.5.6/Documentation/sound/ALS --- linux-2.5.6-pre3/Documentation/sound/ALS Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/Documentation/sound/ALS Wed Dec 31 16:00:00 1969 @@ -1,66 +0,0 @@ -ALS-007/ALS-100/ALS-200 based sound cards -========================================= - -Support for sound cards based around the Avance Logic -ALS-007/ALS-100/ALS-200 chip is included. These chips are a single -chip PnP sound solution which is mostly hardware compatible with the -Sound Blaster 16 card, with most differences occurring in the use of -the mixer registers. For this reason the ALS code is integrated -as part of the Sound Blaster 16 driver (adding only 800 bytes to the -SB16 driver). - -To use an ALS sound card under Linux, enable the following options as -modules in the sound configuration section of the kernel config: - - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - FM synthesizer (YM3812/OPL-3) support - - standalone MPU401 support may be required for some cards; for the - ALS-007, when using isapnptools, it is required -Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be -compiled in. If kernel level PnP support is not included, isapnptools will -be required to configure the card before the sound modules are loaded. - -When using kernel level ISAPnP, the kernel should correctly identify and -configure all resources required by the card when the "sb" module is -inserted. Note that the ALS-007 does not have a 16 bit DMA channel and that -the MPU401 interface on this card uses a different interrupt to the audio -section. This should all be correctly configured by the kernel; if problems -with the MPU401 interface surface, try using the standalone MPU401 module, -passing "0" as the "sb" module's "mpu_io" module parameter to prevent the -soundblaster driver attempting to register the MPU401 itself. The onboard -synth device can be accessed using the "opl3" module. - -If isapnptools is used to wake up the sound card (as in 2.2.x), the settings -of the card's resources should be passed to the kernel modules ("sb", "opl3" -and "mpu401") using the module parameters. When configuring an ALS-007, be -sure to specify different IRQs for the audio and MPU401 sections - this card -requires they be different. For "sb", "io", "irq" and "dma" should be set -to the same values used to configure the audio section of the card with -isapnp. "dma16" should be explicitly set to "-1" for an ALS-007 since this -card does not have a 16 bit dma channel; if not specified the kernel will -default to using channel 5 anyway which will cause audio not to work. -"mpu_io" should be set to 0. The "io" parameter of the "opl3" module should -also agree with the setting used by isapnp. To get the MPU401 interface -working on an ALS-007 card, the "mpu401" module will be required since this -card uses separate IRQs for the audio and MPU401 sections and there is no -parameter available to pass a different IRQ to the "sb" driver (whose -inbuilt MPU401 driver would otherwise be fine). Insert the mpu401 module -passing appropriate values using the "io" and "irq" parameters. - -The resulting sound driver will provide the following capabilities: - - 8 and 16 bit audio playback - - 8 and 16 bit audio recording - - Software selection of record source (line in, CD, FM, mic, master) - - Record and playback of midi data via the external MPU-401 - - Playback of midi data using inbuilt FM synthesizer - - Control of the ALS-007 mixer via any OSS-compatible mixer programs. - Controls available are Master (L&R), Line in (L&R), CD (L&R), - DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). - -Jonathan Woithe -jwoithe@physics.adelaide.edu.au -30 March 1998 - -Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 -Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. -Modified 2000-11-19 by Jonathan Woithe, jwoithe@physics.adelaide.edu.au - - updated information for kernel 2.4.x. diff -urN linux-2.5.6-pre3/Documentation/sound/AWE32 linux-2.5.6/Documentation/sound/AWE32 --- linux-2.5.6-pre3/Documentation/sound/AWE32 Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/Documentation/sound/AWE32 Wed Dec 31 16:00:00 1969 @@ -1,76 +0,0 @@ - Installing and using Creative AWE midi sound under Linux. - -This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and -SB32. - -1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This - is important, because the driver works only with real Creative cards. - -2) The first thing you need to do is re-compile your kernel with support for - your sound card. Run your favourite tool to configure the kernel and when - you get to the "Sound" menu you should enable support for the following: - - Sound card support, - OSS sound modules, - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, - AWE32 synth - - If your card is "Plug and Play" you will also need to enable these two - options, found under the "Plug and Play configuration" menu: - - Plug and Play support - ISA Plug and Play support - - Now compile and install the kernel in normal fashion. If you don't know - how to do this you can find instructions for this in the README file - located in the root directory of the kernel source. - -3) Before you can start playing midi files you will have to load a sound - bank file. The utility needed for doing this is called "sfxload", and it - is one of the utilities found in a package called "awesfx". If this - package is not available in your distribution you can download the AWE - snapshot from Creative Labs Open Source website: - - http://www.opensource.creative.com/snapshot.html - - Once you have unpacked the AWE snapshot you will see a "awesfx" - directory. Follow the instructions in awesfx/docs/INSTALL to install the - utilities in this package. After doing this, sfxload should be installed - as: - - /usr/local/bin/sfxload - - To enable AWE general midi synthesis you should also get the sound bank - file for general midi from: - - http://members.xoom.com/yar/synthgm.sbk.gz - - Copy it to a directory of your choice, and unpack it there. - -4) Edit /etc/modules.conf, and insert the following lines at the end of the - file: - - alias sound-slot-0 sb - alias sound-service-0-1 awe_wave - post-install awe_wave /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE - - You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full - path of of the sound bank file. That will enable the Sound Blaster and AWE - wave synthesis. To play midi files you should get one of these programs if - you don't already have them: - - Playmidi: http://playmidi.openprojects.net - - AWEMidi Player (drvmidi) Included in the previously mentioned AWE - snapshot. - - You will probably have to pass the "-e" switch to playmidi to have it use - your midi device. drvmidi should work without switches. - - If something goes wrong please e-mail me. All comments and suggestions are - welcome. - - Yaroslav Rosomakho (alons55@dialup.ptt.ru) - http://www.yar.opennet.ru - -Last Updated: Feb 3 2001 diff -urN linux-2.5.6-pre3/Documentation/sound/AudioExcelDSP16 linux-2.5.6/Documentation/sound/AudioExcelDSP16 --- linux-2.5.6-pre3/Documentation/sound/AudioExcelDSP16 Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/Documentation/sound/AudioExcelDSP16 Wed Dec 31 16:00:00 1969 @@ -1,101 +0,0 @@ -Driver ------- - -Informations about Audio Excel DSP 16 driver can be found in the source -file lowlevel/aedsp16.c -Please, read the head of the source before using it. It contain useful -informations. - -Configuration -------------- - -The Audio Excel configuration, is now done with the standard Linux setup. -You have to configure the sound card (Sound Blaster or Microsoft Sound System) -and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401, -SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select -the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ -of the Audio Excel initialization: it must be the same as the SBPRO (or MSS) -setup. If the parameters are different, correct it. -I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support. -If you want to change the configuration of the sound board, be sure to -check off all the configuration items before re-configure it. - -Module parameters ------------------ -To use this driver as a module, you must configure some module parameters, to -set up I/O addresses, IRQ lines and DMA channels. Some parameters are -mandatory while some others are optional. Here a list of parameters you can -use with this module: - -Name Description -==== =========== -MANDATORY -io I/O base address (0x220 or 0x240) -irq irq line (5, 7, 9, 10 or 11) -dma dma channel (0, 1 or 3) - -OPTIONAL -mss_base I/O base address for activate MSS mode (default SBPRO) - (0x530 or 0xE80) -mpu_base I/O base address for activate MPU-401 mode - (0x300, 0x310, 0x320 or 0x330) -mpu_irq MPU-401 irq line (5, 7, 9, 10 or 0) - -The /etc/modules.conf will have lines like this: - -options opl3 io=0x388 -options ad1848 io=0x530 irq=11 dma=3 -options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530 - -Where the aedsp16 options are the options for this driver while opl3 and -ad1848 are the corresponding options for the MSS and OPL3 modules. - -Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly -the sound card. Installation dependencies must be written in the modules.conf -file: - -pre-install ad1848 modprobe aedsp16 -pre-install opl3 modprobe aedsp16 - -Then you must load the sound modules stack in this order: -sound -> aedsp16 -> [ ad1848, opl3 ] - -With the above configuration, loading ad1848 or opl3 modules, will -automatically load all the sound stack. - -Sound cards supported ---------------------- -This driver supports the SC-6000 and SC-6600 based Gallant's sound card. -It don't support the Audio Excel DSP 16 III (try the SC-6600 code). -I'm working on the III version of the card: if someone have useful -informations about it, please let me know. -For all the non-supported audio cards, you have to boot MS-DOS (or WIN95) -activating the audio card with the MS-DOS device driver, then you have to --- and boot Linux. -Follow these steps: - -1) Compile Linux kernel with standard sound driver, using the emulation - you want, with the parameters of your audio card, - e.g. Microsoft Sound System irq10 dma3 -2) Install your new kernel as the default boot kernel. -3) Boot MS-DOS and configure the audio card with the boot time device - driver, for MSS irq10 dma3 in our example. -4) -- and boot Linux. This will maintain the DOS configuration - and will boot the new kernel with sound driver. The sound driver will find - the audio card and will recognize and attach it. - -Reports on User successes -------------------------- - -> Date: Mon, 29 Jul 1996 08:35:40 +0100 -> From: Mr S J Greenaway -> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti) -> Subject: Re: Audio Excel DSP 16 initialization code -> -> Just to let you know got my Audio Excel (emulating a MSS) working -> with my original SB16, thanks for the driver! - - -Last revised: 20 August 1998 -Riccardo Facchetti -fizban@tin.it diff -urN linux-2.5.6-pre3/Documentation/sound/CMI8330 linux-2.5.6/Documentation/sound/CMI8330 --- linux-2.5.6-pre3/Documentation/sound/CMI8330 Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/Documentation/sound/CMI8330 Wed Dec 31 16:00:00 1969 @@ -1,153 +0,0 @@ -Documentation for CMI 8330 (SoundPRO) -------------------------------------- -Alessandro Zummo - -( Be sure to read Documentation/sound/SoundPro too ) - - -This adapter is now directly supported by the sb driver. - - The only thing you have to do is to compile the kernel sound -support as a module and to enable kernel ISAPnP support, -as shown below. - - -CONFIG_SOUND=m -CONFIG_SOUND_SB=m - -CONFIG_PNP=y -CONFIG_ISAPNP=y - - -and optionally: - - -CONFIG_SOUND_MPU401=m - - for MPU401 support. - - -(I suggest you to use "make menuconfig" or "make xconfig" - for a more comfortable configuration editing) - - - -Then you can do - - modprobe sb - -and everything will be (hopefully) configured. - -You should get something similar in syslog: - -sb: CMI8330 detected. -sb: CMI8330 sb base located at 0x220 -sb: CMI8330 mpu base located at 0x330 -sb: CMI8330 mail reports to Alessandro Zummo -sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5 - - - - -The old documentation file follows for reference -purposes. - - -How to enable CMI 8330 (SOUNDPRO) soundchip on Linux ------------------------------------------- -Stefan Laudat - -[Note: The CMI 8338 is unrelated and is supported by cmpci.o] - - - In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but -you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. - - Of course you will have to compile kernel sound support as module, as shown below: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MPU401=m -# Mikro$chaft sound system (kinda useful here ;)) -CONFIG_SOUND_MSS=m - - The /etc/isapnp.conf file will be: - - - - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -(VERIFYLD N) - - -# WSS - -(CONFIGURE CMI0001/16777472 (LD 0 -(IO 0 (SIZE 8) (BASE 0x0530)) -(IO 1 (SIZE 8) (BASE 0x0388)) -(INT 0 (IRQ 7 (MODE +E))) -(DMA 0 (CHANNEL 0)) -(NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# MPU - -(CONFIGURE CMI0001/16777472 (LD 1 -(IO 0 (SIZE 2) (BASE 0x0330)) -(INT 0 (IRQ 11 (MODE +E))) -(NAME "CMI0001/16777472[1]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# Joystick - -(CONFIGURE CMI0001/16777472 (LD 2 -(IO 0 (SIZE 8) (BASE 0x0200)) -(NAME "CMI0001/16777472[2]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# SoundBlaster - -(CONFIGURE CMI0001/16777472 (LD 3 -(IO 0 (SIZE 16) (BASE 0x0220)) -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 5)) -(NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - - -(WAITFORKEY) - - - - The module sequence is trivial: - -/sbin/insmod soundcore -/sbin/insmod sound -/sbin/insmod uart401 -# insert this first -/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 -# The sb module is an alternative to the ad1848 (Microsoft Sound System) -# Anyhow, this is full duplex and has MIDI -/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 - - - -Alma Chao suggests the following /etc/modules.conf: - -alias sound ad1848 -alias synth0 opl3 -options ad1848 io=0x530 irq=7 dma=0 soundpro=1 -options opl3 io=0x388 - - diff -urN linux-2.5.6-pre3/Documentation/sound/CMI8338 linux-2.5.6/Documentation/sound/CMI8338 --- linux-2.5.6-pre3/Documentation/sound/CMI8338 Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/Documentation/sound/CMI8338 Wed Dec 31 16:00:00 1969 @@ -1,85 +0,0 @@ -Audio driver for CM8338/CM8738 chips by Chen-Li Tien - - -HARDWARE SUPPORTED -================================================================================ -C-Media CMI8338 -C-Media CMI8738 -On-board C-Media chips - - -STEPS TO BUILD DRIVER -================================================================================ - - 1. Backup the Config.in and Makefile in the sound driver directory - (/usr/src/linux/driver/sound). - The Configure.help provide help when you config driver in step - 4, please backup the original one (/usr/src/linux/Document) and - copy this file. - The cmpci is document for the driver in detail, please copy it - to /usr/src/linux/Document/sound so you can refer it. Backup if - there is already one. - - 2. Extract the tar file by 'tar xvzf cmpci-xx.tar.gz' in the above - directory. - - 3. Change directory to /usr/src/linux - - 4. Config cm8338 driver by 'make menuconfig', 'make config' or - 'make xconfig' command. - - 5. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI - driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. - For driver option, please refer 'DRIVER PARAMETER' - - 6. Compile the kernel if necessary. - - 7. Compile the modules by 'make modules'. - - 8. Install the modules by 'make modules_install' - - -INSTALL DRIVER -================================================================================ - - 1. Before first time to run the driver, create module dependency by - 'depmod -a' - - 2. To install the driver manually, enter 'modprobe cmpci'. - - 3. Driver installation for various distributions: - - a. Slackware 4.0 - Add the 'modprobe cmpci' command in your /etc/rc.d/rc.modules - file.so you can start the driver automatically each time booting. - - b. Caldera OpenLinux 2.2 - Use LISA to load the cmpci module. - - c. RedHat 6.0 and S.u.S.E. 6.1 - Add following command in /etc/conf.modules: - - alias sound cmpci - - also visit http://www.cmedia.com.tw for installation instruction. - -DRIVER PARAMETER -================================================================================ - - Some functions for the cm8738 can be configured in Kernel Configuration - or modules parameters. Set these parameters to 1 to enable. - - mpuio: I/O ports base for MPU-401, 0 if disabled. - fmio: I/O ports base for OPL-3, 0 if disabled. - spdif_inverse:Inverse the S/PDIF-in signal, this depends on your - CD-ROM or DVD-ROM. - spdif_loop: Enable S/PDIF loop, this route S/PDIF-in to S/PDIF-out - directly. - speakers: Number of speakers used. - use_line_as_rear:Enable this if you want to use line-in as - rear-out. - use_line_as_bass:Enable this if you want to use line-in as - bass-out. - joystick: Enable joystick. You will need to install Linux joystick - driver. - diff -urN linux-2.5.6-pre3/Documentation/sound/CS4232 linux-2.5.6/Documentation/sound/CS4232 --- linux-2.5.6-pre3/Documentation/sound/CS4232 Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/Documentation/sound/CS4232 Wed Dec 31 16:00:00 1969 @@ -1,23 +0,0 @@ -To configure the Crystal CS423x sound chip and activate its DSP functions, -modules may be loaded in this order: - - modprobe sound - insmod ad1848 - insmod uart401 - insmod cs4232 io=* irq=* dma=* dma2=* - -This is the meaning of the parameters: - - io--I/O address of the Windows Sound System (normally 0x534) - irq--IRQ of this device - dma and dma2--DMA channels (DMA2 may be 0) - -On some cards, the board attempts to do non-PnP setup, and fails. If you -have problems, use Linux' PnP facilities. - -To get MIDI facilities add - - insmod opl3 io=* - -where "io" is the I/O address of the OPL3 synthesizer. This will be shown -in /proc/sys/pnp and is normally 0x388. diff -urN linux-2.5.6-pre3/Documentation/sound/ChangeLog.awe linux-2.5.6/Documentation/sound/ChangeLog.awe --- linux-2.5.6-pre3/Documentation/sound/ChangeLog.awe Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/ChangeLog.awe Wed Dec 31 16:00:00 1969 @@ -1,230 +0,0 @@ -ver.0.4.3p4 - - Bug fix for invalid memory detection when initialized twice - - Add sample sharing function - works together with awesfx-0.4.3p3 - - Add AWE_PROBE_DATA for probing sample id - -ver.0.4.3p3 - - Replace memset to MEMSET (for FreeBSD) - - Add PAN_EXCHANGE switch - -ver.0.4.3p2 - - MIDI emulation device is added - - Controls volume and filter targets - - Include chorus/reverb/equalizer values in MISC_MODE - -ver.0.4.3p1 - - Change the volume calculation method - - Support for Tom Lees' PnP driver (v0.3) - -ver.0.4.2d - - Support for OSS/Free 3.8 on 2.0 kernels. - - Support for Linux PnP driver - - Support for module (for recent 2.1 kernels and RH5.0) - - Support for FreeBSD-3.0 system - -ver.0.4.2c - - Add a mode to enable drum channel toggle via bank number - change. - -ver.0.4.2b - - Clear voice position after note on - - Change nrvoices according to the current playing mode - -ver.0.4.2a - - Fix a bug in pitch calculation with scale parameter - - Change default chorus & reverb modes - -ver.0.4.2 - - Use indirect voice allocation mode; used as default mode - - Add preset mapping - - Free buffers when resetting samples - - Set default preset/bank/drumset as variable - - Fix a bug in exclusive note-off - - Add channel reset control macro - - Change modwheel sensitivity as variable - - Add lock option in open_patch - - Add channel priority mode macro, and disable it as default - - Add unset effect macro - - Add user defined chorus/reverb modes - - Do not initialize effect parameters when allocating voices - - Accept realtime filter-Q parameter change - - Check value range of set/add effects - - Change drum flags automatically when receiving bank #128 - -ver.0.4.1 development versions - -ver.0.4.0c - - Fix kernel oops when setting AWE_FX_ATTEN - -ver.0.4.0b - - Do not kill_note in start_note when velocity is zero - -ver.0.4.0a - - Fix a bug in channel pressure effects - -ver.0.4.0 - - Support dynamic buffer allocation - - Add functions to open/close/unload a patch - - Change from pointer to integer index in voice/sample lists - - Support for Linux/Alpha-AXP - - Fix for FreeBSD - - Add sostenuto control - - Add midi channel priority - - Fix a bug in all notes off control - - Use AWE_DEFAULT_MEMSIZE always if defined - - Fix a bug in awe_reset causes seg fault when no DRAM onboard - - Use awe_mem_start variable instead of constant - -ver.0.3.3c - - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25) - - Fix i/o macros for mixer controls - -ver.0.3.3b - - Fix version number in awe_version.h - - Fix a small bug in noteoff/release all - -ver.0.3.3a - - Fix all notes/sounds off - - Add layer effect control - - Add misc mode controls; realtime pan, version number, etc. - - Move gus bank control in misc mode control - - Modify awe_operations for OSS3.8b5 - - Fix installation script - -ver.0.3.3 - - Add bass/treble control in Emu8000 chip - - Add mixer device - - Fix sustain on to value 127 - -ver.0.3.2 - - Refuse linux-2.0.0 at installation - - Move awe_voice.h to /usr/include/linux - -ver.0.3.1b (not released) - - Rewrite chorus/reverb mode change functions - - Rewrite awe_detect & awe_check_dram routines - -ver.0.3.1a - - Fix a bug to reset voice counter in awe_reset - - Fix voice balance on GUS mode - - Make symlink on /usr/include/asm in install script - -ver.0.3.1 - - Remove zero size arrays from awe_voice.h - - Fix init_fm routine - - Remove all samples except primary samples in REMOVE_LAST_SAMPLES - -ver.0.3.0a - - Add AWE_NOTEOFF_ALL control - - Remove AWE_INIT_ATTEN control - -ver.0.3.0 - - Fix decay time table - - Add exclusive sounds mode - - Add capability to get current status - -ver.0.2.99e - - Add #ifdef for all sounds/notes off controls. - - Fix bugs on searching the default drumset/preset. - - Fix usslite patch to modify the default Config.in. - -ver.0.2.99d - - Fix bugs of attack/hold parameters - - Fix attack & decay time table - -ver.0.2.99c - - Change volume control messages (main & expression volume) - to accesspt normal MIDI parameters in channel mode. - - Use channel mode in SEQ2 controls. - -ver.0.2.99b - - #ifdef patch manager functions (for OSS-3.7) - -ver.0.2.99a - - Fix sustain bug - -ver.0.2.99 (0.3 beta) - - Support multiple instruments - -ver.0.2.0c - - Add copyright notice - - FreeBSD 2.2-ALPHA integration - -ver.0.2.0b - - Remove buffered reading appended in v0.2.0a - - Remove SMAxW register check on writing - - Support Linux 2.1.x kernel - - Rewrite installation script - -ver.0.2.0a - - Define SEQUENCER_C for tuning.h for FreeBSD system - - Improvement of sample loading speed - - Fix installation script - - Add PnP driver functions for ISA PnP driver support - -ver.0.2.0 - - Includes FreeBSD port - - Can load GUS compatible patches - - Change values of hardware control parameters for compatibility - with GUS driver - - Accept 8bit or unsigned wave data - - Accept no blank loop data - - Add sample mode flags in sample_info - -ver.0.1.6 - - Add voice effects control - - Fix awe_voice.h for word alignment - -ver.0.1.5c - - Fix FM(OPL) playback problem - -ver.0.1.5b - - Fix pitch calculation for fixed midi key - -ver.0.1.5a - - Fix bugs in removing samples from linked list. - -ver.0.1.5 - - Add checksum verification for sample uploading - (not compatible from older sample_info structure) - - Fix sample offset pointers to (actual value - 1) - - Add sequencer command to initialize awe32 - -ver.0.1.4c - - Fix card detection and memory check function to avoid system crash - at booting - -ver.0.1.4b - - Add release sustain mode - - Initialize FM each time after loading samples - -ver.0.1.4a - - Fix AWE card detection code - - Correct FM initialize position - - Add non-releasing mode on voice info - -ver.0.1.4 - - Add AWE card and DRAM detection codes - - Add FM initialization code - - Modify volume control - - Remove linear volume mode - - Change memory management; not using malloc dynamically - - Add remove-samples command - - Use internal id implicitly at loading samples - -ver.0.1.3 - - Fix a bug on patch uploading to RAM - -ver.0.1.2 - - Divide to separated packages - - Fix disagreed macro conditions - - Fix unresolved function bugs - - Integrate VoxWare and USS-Lite driver source (awe_voice.c) - and remove awe_card.c - -ver.0.1.1 - - Fix wrong sample numbers in sbktext - - Fix txt2sfx bug - - Fix pan parameter calculation - - Append USS-Lite/Linux2.0 driver - diff -urN linux-2.5.6-pre3/Documentation/sound/ChangeLog.multisound linux-2.5.6/Documentation/sound/ChangeLog.multisound --- linux-2.5.6-pre3/Documentation/sound/ChangeLog.multisound Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/Documentation/sound/ChangeLog.multisound Wed Dec 31 16:00:00 1969 @@ -1,213 +0,0 @@ -1998-12-04 Andrew T. Veliath - - * Update version to 0.8.2.2 - - * Add msndreset program to shell archive. - -1998-11-11 Andrew T. Veliath - - * msnd_pinnacle.c (mixer_ioctl): Add a mixer ioctl for - SOUND_MIXER_PRIVATE1 which does a full reset on the card. - (mixer_set): Move line in recording source to input monitor, aux - input level added, some mixer fixes. - -1998-09-10 Andrew Veliath - - * Update version to 0.8.2 - - * Add SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls. - -1998-09-09 Andrew Veliath - - * Update version to 0.8.1 - - * msnd_pinnacle.c: Fix resetting of default audio parameters. Turn - flush code from dsp_halt into dsp_write_flush, and use that for - SNDCTL_DSP_SYNC. - -1998-09-07 Andrew Veliath - - * Update version to 0.8.0 - - * Provide separate signal parameters for play and record. - - * Cleanups to locking and interrupt handling, change default - fifosize to 128kB. - - * Update version to 0.7.15 - - * Interprocess full-duplex support (ie `cat /dev/dsp > /dev/dsp'). - - * More mutex sections for read and write fifos (read + write locks - added). - -1998-09-05 Andrew Veliath - - * msnd_pinnacle.c: (chk_send_dsp_cmd) Do full DSP reset upon DSP - timeout (when not in interrupt; maintains mixer settings). Fixes - to flushing and IRQ ref counting. Rewrote queuing for smoother - playback and fixed initial playback cutoff problem. - -1998-09-03 Andrew Veliath - - * Replaced packed structure accesses with standard C equivalents. - -1998-09-01 Andrew Veliath - - * msnd_pinnacle.c: Add non-PnP configuration to driver code, which - will facilitate compiled-in operation. - -1998-08-29 Andrew Veliath - - * Update version to 0.7.6 - - * msnd_pinnacle.c (dsp_ioctl): Add DSP_GETFMTS, change SAMPLESIZE - to DSP_SETFMT. - - * Update version to 0.7.5 - - * Create pinnaclecfg.c and turn MultiSound doc into a shell - archive with pinnaclecfg.c included. pinnaclecfg.c can - now fully configure the card in non-PnP mode, including the - joystick and IDE controller. Also add an isapnp conf - example. - - * Reduce DSP reset timeout from 20000 to 100 - -1998-08-06 Andrew Veliath - - * Update version to 0.7.2 - - * After A/D calibration, do an explicit set to the line input, - rather than using set_recsrc - -1998-07-20 Andrew Veliath - - * Update version to 0.7.1 - - * Add more OSS ioctls - -1998-07-19 Andrew Veliath - - * Update doc file - - * Bring back DIGITAL1 with digital parameter to msnd_pinnacle.c - and CONFIG_MSNDPIN_DIGITAL. I'm not sure this actually works, - since I find audio playback goes into a very speeded mode of - operation, however it might be due to a lack of a digital - source, which I don't have to test. - -1998-07-18 Andrew Veliath - - * Update version to 0.7.0 - - * Can now compile with Alan Cox' 2.0.34-modular-sound patch (so - now it requires >= 2.1.106 or 2.0.34-ms) (note for 2.0.34-ms it - is in the Experimental section) - - * More modularization, consolidation, also some MIDI hooks - installed for future MIDI modules - - * Write flush - - * Change default speed, channels, bit size to OSS/Free defaults - -1998-06-02 Andrew Veliath - - * Update version to 0.5b - - * Fix version detection - - * Remove underflow and overflow resets (delay was too long) - - * Replace spinlocked bitops with atomic bit ops - -1998-05-27 Andrew Veliath - - * Update version to 0.5a - - * Better recovery from underflow or overflow conditions - - * Fix a deadlock condition with one thread reading and the other - writing - -1998-05-26 Andrew Veliath - - * Update version to 0.5 - - * Separate reset queue functions for play and record - - * Add delays in dsp_halt - -1998-05-24 Andrew Veliath - - * Add a check for Linux >= 2.1.95 - - * Remove DIGITAL1 input until I figure out how to make it work - - * Add HAVE_DSPCODEH which when not defined will load firmware from - files using mod_firmware_load, then release memory after they - are uploaded (requires reorganized OSS). - -1998-05-22 Andrew Veliath - - * Update version to 0.4c - - * Hopefully fix the mixer volume problem - -1998-05-19 Andrew Veliath - - * Add __initfuncs and __initdatas to reduce resident code size - - * Move bunch of code around, remove some protos - - * Integrate preliminary changes for Alan Cox's OSS reorganization - for non-OSS drivers to coexist with OSS devices on the same - major. To compile standalone, must now define STANDALONE. - -1998-05-16 Andrew Veliath - - * Update version to 0.4b - - * Integrated older card support into a unified driver, tested on a - MultiSound Classic c/o Kendrick Vargas. - -1998-05-15 Andrew Veliath - - * Update version to 0.4 - - * Fix read/write return values - -1998-05-13 Andrew Veliath - - * Update version to 0.3 - - * Stop play gracefully - - * Add busy flag - - * Add major and calibrate_signal module parameters - - * Add ADC calibration - - * Add some OSS compatibility ioctls - - * Add mixer record selection - - * Add O_NONBLOCK support, separate read/write wait queues - - * Add sample bit size ioctl, expanded sample rate ioctl - - * Playback suspension now resumes - - * Use signal_pending after interruptible_sleep_on - - * Add recording, change ints to bit flags - -1998-05-11 Andrew Veliath - - * Update version to 0.2 - - * Add preliminary playback support - - * Use new Turtle Beach DSP code \ No newline at end of file diff -urN linux-2.5.6-pre3/Documentation/sound/ESS linux-2.5.6/Documentation/sound/ESS --- linux-2.5.6-pre3/Documentation/sound/ESS Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/Documentation/sound/ESS Wed Dec 31 16:00:00 1969 @@ -1,34 +0,0 @@ -Documentation for the ESS AudioDrive chips - -In 2.4 kernels the SoundBlaster driver not only tries to detect an ESS chip, it -tries to detect the type of ESS chip too. The correct detection of the chip -doesn't always succeed however, so unless you use the kernel isapnp facilities -(and you chip is pnp capable) the default behaviour is 2.0 behaviour which -means: only detect ES688 and ES1688. - -All ESS chips now have a recording level setting. This is a need-to-have for -people who want to use their ESS for recording sound. - -Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic -master volume control. - -Every chip that's detected as a ES1887 now has Full Duplex support. Made a -little testprogram that shows that is works, haven't seen a real program that -needs this however. - -For ESS chips an additional parameter "esstype" can be specified. This controls -the (auto) detection of the ESS chips. It can have 3 kinds of values: - --1 Act like 2.0 kernels: only detect ES688 or ES1688. -0 Try to auto-detect the chip (may fail for ES1688) -688 The chip will be treated as ES688 -1688 ,, ,, ,, ,, ,, ,, ES1688 -1868 ,, ,, ,, ,, ,, ,, ES1868 -1869 ,, ,, ,, ,, ,, ,, ES1869 -1788 ,, ,, ,, ,, ,, ,, ES1788 -1887 ,, ,, ,, ,, ,, ,, ES1887 -1888 ,, ,, ,, ,, ,, ,, ES1888 - -Because Full Duplex is supported for ES1887 you can specify a second DMA -channel by specifying module parameter dma16. It can be one of: 0, 1, 3 or 5. - diff -urN linux-2.5.6-pre3/Documentation/sound/ESS1868 linux-2.5.6/Documentation/sound/ESS1868 --- linux-2.5.6-pre3/Documentation/sound/ESS1868 Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/Documentation/sound/ESS1868 Wed Dec 31 16:00:00 1969 @@ -1,55 +0,0 @@ -Documentation for the ESS1868F AudioDrive PnP sound card - -The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. - -It should be automatically detected by the Linux Kernel isapnp support when you -load the sb.o module. Otherwise you should take care of: - - * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 - may only be used. - - * isapnptools version 1.14 does work with ESS1868. Earlier versions might - not. - - * Sound support MUST be compiled as MODULES, not statically linked - into the kernel. - - -NOTE: this is only needed when not using the kernel isapnp support! - -For configuring the sound card's I/O addresses, IRQ and DMA, here is a -sample copy of the isapnp.conf directives regarding the ESS1868: - -(CONFIGURE ESS1868/-1 (LD 1 -(IO 0 (BASE 0x0220)) -(IO 1 (BASE 0x0388)) -(IO 2 (BASE 0x0330)) -(DMA 0 (CHANNEL 1)) -(INT 0 (IRQ 5 (MODE +E))) -(ACT Y) -)) - -(for a full working isapnp.conf file, remember the -(ISOLATE) -(IDENTIFY *) -at the beginning and the -(WAITFORKEY) -at the end.) - -In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and -the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. - -After configuring the sound card via isapnp, to use the card you must load -the sound modules with the proper I/O information. Here is my setup: - -# ESS1868F AudioDrive initialization - -/sbin/modprobe sound -/sbin/insmod uart401 -/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 -/sbin/insmod mpu401 io=0x330 -/sbin/insmod opl3 io=0x388 -/sbin/insmod v_midi - -opl3 is the FM synthesizer -/sbin/insmod opl3 io=0x388 diff -urN linux-2.5.6-pre3/Documentation/sound/INSTALL.awe linux-2.5.6/Documentation/sound/INSTALL.awe --- linux-2.5.6-pre3/Documentation/sound/INSTALL.awe Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/Documentation/sound/INSTALL.awe Wed Dec 31 16:00:00 1969 @@ -1,134 +0,0 @@ -================================================================ - INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX - Takashi Iwai -================================================================ - ----------------------------------------------------------------- -* Attention to SB-PnP Card Users - -If you're using PnP cards, the initialization of PnP is required -before loading this driver. You have now three options: - 1. Use isapnptools. - 2. Use in-kernel isapnp support. - 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. -In this document, only the case 1 case is treated. - ----------------------------------------------------------------- -* Installation on Red Hat 5.0 Sound Driver - -Please use install-rh.sh under RedHat5.0 directory. -DO NOT USE install.sh below. -See INSTALL.RH for more details. - ----------------------------------------------------------------- -* Installation/Update by Shell Script - - 1. Become root - - % su - - 2. If you have never configured the kernel tree yet, run make config - once (to make dependencies and symlinks). - - # cd /usr/src/linux - # make xconfig - - 3. Run install.sh script - - # sh ./install.sh - - 4. Configure your kernel - - (for Linux 2.[01].x user) - # cd /usr/src/linux - # make xconfig (or make menuconfig) - - (for Linux 1.2.x user) - # cd /usr/src/linux - # make config - - Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items - in Sound menu. ("lowlevel drivers" will appear only in 2.x - kernel.) - - 5. Make your kernel (and modules), and install them as usual. - - 5a. make kernel image - # make zImage - - 5b. make modules and install them - # make modules && make modules_install - - 5c. If you're using lilo, copy the kernel image and run lilo. - Otherwise, copy the kernel image to suitable directory or - media for your system. - - 6. Reboot the kernel if necessary. - - If you updated only the modules, you don't have to reboot - the system. Just remove the old sound modules here. - in - # rmmod sound.o (linux-2.0 or OSS/Free) - # rmmod awe_wave.o (linux-2.1) - - 7. If your AWE card is a PnP and not initialized yet, you'll have to - do it by isapnp tools. Otherwise, skip to 8. - - This section described only a brief explanation. For more - details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. - - 7a. If you have no isapnp.conf file, generate it by pnpdump. - Otherwise, skip to 7d. - # pnpdump > /etc/isapnp.conf - - 7b. Edit isapnp.conf file. Comment out the appropriate - lines containing desirable I/O ports, DMA and IRQs. - Don't forget to enable (ACT Y) line. - - 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. - ex) - (CONFIGURE CTL0048/58128 (LD 2 - # ANSI string -->WaveTable<-- - (IO 0 (BASE 0x0620)) - (IO 1 (BASE 0x0A20)) - (IO 2 (BASE 0x0E20)) - (ACT Y) - )) - - 7d. Load the config file. - CAUTION: This will reset all PnP cards! - - # isapnp /etc/isapnp.conf - - 8. Load the sound module (if you configured it as a module): - - for 2.0 kernel or OSS/Free monolithic module: - - # modprobe sound.o - - for 2.1 kernel: - - # modprobe sound - # insmod uart401 - # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 - (These values depend on your settings.) - # insmod awe_wave - (Be sure to load awe_wave after sb!) - - See /usr/src/linux/Documentation/sound/AWE32 for - more details. - - 9. (only for obsolete systems) If you don't have /dev/sequencer - device file, make it according to Readme.linux file on - /usr/src/linux/drivers/sound. (Run a shell script included in - that file). <-- This file no longer exists in the recent kernels! - - 10. OK, load your own soundfont file, and enjoy MIDI! - - % sfxload synthgm.sbk - % drvmidi foo.mid - - 11. For more advanced use (eg. dynamic loading, virtual bank and - etc.), please read the awedrv FAQ or the instructions in awesfx - and awemidi packages. - -Good luck! diff -urN linux-2.5.6-pre3/Documentation/sound/Introduction linux-2.5.6/Documentation/sound/Introduction --- linux-2.5.6-pre3/Documentation/sound/Introduction Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/Introduction Wed Dec 31 16:00:00 1969 @@ -1,461 +0,0 @@ -Introduction Notes on Modular Sound Drivers and Soundcore -Wade Hampton -2/14/2001 - -Purpose: -======== -This document provides some general notes on the modular -sound drivers and their configuration, along with the -support modules sound.o and soundcore.o. - -Note, some of this probably should be added to the Sound-HOWTO! - -Note, soundlow.o was present with 2.2 kernels but is not -required for 2.4.x kernels. References have been removed -to this. - - -Copying: -======== -none - - -History: -======== -0.1.0 11/20/1998 First version, draft -1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0 - as /usr/src/linux/Documentation/sound/Introduction -1.1.0 6/30/1999 Second version, added notes on making the drivers, - added info on multiple sound cards of similar types,] - added more diagnostics info, added info about esd. - added info on OSS and ALSA. -1.1.1 19991031 Added notes on sound-slot- and sound-service. - (Alan Cox) -1.1.2 20000920 Modified for Kernel 2.4 (Christoph Hellwig) -1.1.3 20010214 Minor notes and corrections (Wade Hampton) - Added examples of sound-slot-0, etc. - - -Modular Sound Drivers: -====================== - -Thanks to the GREAT work by Alan Cox (alan@lxorguk.ukuu.org.uk), - -[And Oleg Drokin, Thomas Sailer, Andrew Veliath and more than a few - others - not to mention Hannu's original code being designed well - enough to cope with that kind of chopping up](Alan) - -the standard Linux kernels support a modular sound driver. From -Alan's comments in linux/drivers/sound/README.FIRST: - - The modular sound driver patches were funded by Red Hat Software - (www.redhat.com). The sound driver here is thus a modified version of - Hannu's code. Please bear that in mind when considering the appropriate - forums for bug reporting. - -The modular sound drivers may be loaded via insmod or modprobe. -To support all the various sound modules, there are two general -support modules that must be loaded first: - - soundcore.o: Top level handler for the sound system, provides - a set of functions for registration of devices - by type. - - sound.o: Common sound functions required by all modules. - -For the specific sound modules (e.g., sb.o for the Soundblaster), -read the documentation on that module to determine what options -are available, for example IRQ, address, DMA. - -Warning, the options for different cards sometime use different names -for the same or a similar feature (dma1= versus dma16=). As a last -resort, inspect the code (search for MODULE_PARM). - -Notes: - -1. There is a new OpenSource sound driver called ALSA which is - currently under development: http://www.alsa-project.org/ - The ALSA drivers support some newer hardware that may not - be supported by this sound driver and also provide some - additional features. - -2. The commercial OSS driver may be obtained from the site: - http://www/opensound.com. This may be used for cards that - are unsupported by the kernel driver, or may be used - by other operating systems. - -3. The enlightenment sound daemon may be used for playing - multiple sounds at the same time via a single card, eliminating - some of the requirements for multiple sound card systems. For - more information, see: http://www.tux.org/~ricdude/EsounD.html - The "esd" program may be used with the real-player and mpeg - players like mpg123 and x11amp. The newer real-player - and some games even include built-in support for ESD! - - -Building the Modules: -===================== - -This document does not provide full details on building the -kernel, etc. The notes below apply only to making the kernel -sound modules. If this conflicts with the kernel's README, -the README takes precedence. - -1. To make the kernel sound modules, cd to your /usr/src/linux - directory (typically) and type make config, make menuconfig, - or make xconfig (to start the command line, dialog, or x-based - configuration tool). - -2. Select the Sound option and a dialog will be displayed. - -3. Select M (module) for "Sound card support". - -4. Select your sound driver(s) as a module. For ProAudio, Sound - Blaster, etc., select M (module) for OSS sound modules. - [thanks to Marvin Stodolsky ]A - -5. Make the kernel (e.g., make dep ; make bzImage), and install - the kernel. - -6. Make the modules and install them (make modules; make modules_install). - -Note, for 2.4.x kernels, make sure you have the newer modutils -loaded or modules will not be loaded properly. 2.4.x changed the -layout of /lib/modules/2.4.x and requires an updated modutils. - - -Plug and Play (PnP: -=================== - -If the sound card is an ISA PnP card, isapnp may be used -to configure the card. See the file isapnp.txt in the -directory one level up (e.g., /usr/src/linux/Documentation). - -Also the 2.4.x kernels provide PnP capabilities, see the -file NEWS in this directory. - -PCI sound cards are highly recommended, as they are far -easier to configure and from what I have read, they use -less resources and are more CPU efficient. - - -INSMOD: -======= - -If loading via insmod, the common modules must be loaded in the -order below BEFORE loading the other sound modules. The card-specific -modules may then be loaded (most require parameters). For example, -I use the following via a shell script to load my SoundBlaster: - -SB_BASE=0x240 -SB_IRQ=9 -SB_DMA=3 -SB_DMA2=5 -SB_MPU=0x300 -# -echo Starting sound -/sbin/insmod soundcore -/sbin/insmod sound -# -echo Starting sound blaster.... -/sbin/insmod uart401 -/sbin/insmod sb io=$SB_BASE irq=$SB_IRQ dma=$SB_DMA dma16=$SB_DMA2 mpu_io=$SB_MP - -When using sound as a module, I typically put these commands -in a file such as /root/soundon.sh. - - -MODPROBE: -========= - -If loading via modprobe, these common files are automatically loaded -when requested by modprobe. For example, my /etc/modules.conf contains: - -alias sound sb -options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300 - -All you need to do to load the module is: - - /sbin/modprobe sb - - -Sound Status: -============= - -The status of sound may be read/checked by: - cat (anyfile).au >/dev/audio - -[WWH: This may not work properly for SoundBlaster PCI 128 cards -such as the es1370/1 (see the es1370/1 files in this directory) -as they do not automatically support uLaw on /dev/audio.] - -The status of the modules and which modules depend on -which other modules may be checked by: - /sbin/lsmod - -/sbin/lsmod should show something like the following: - sb 26280 0 - uart401 5640 0 [sb] - sound 57112 0 [sb uart401] - soundcore 1968 8 [sb sound] - - -Removing Sound: -=============== - -Sound may be removed by using /sbin/rmmod in the reverse order -in which you load the modules. Note, if a program has a sound device -open (e.g., xmixer), that module (and the modules on which it -depends) may not be unloaded. - -For example, I use the following to remove my Soundblaster (rmmod -in the reverse order in which I loaded the modules): - -/sbin/rmmod sb -/sbin/rmmod uart401 -/sbin/rmmod sound -/sbin/rmmod soundcore - -When using sound as a module, I typically put these commands -in a script such as /root/soundoff.sh. - - -Removing Sound for use with OSS: -================================ - -If you get really stuck or have a card that the kernel modules -will not support, you can get a commercial sound driver from -http://www.opensound.com. Before loading the commercial sound -driver, you should do the following: - -1. remove sound modules (detailed above) -2. remove the sound modules from /etc/modules.conf -3. move the sound modules from /lib/modules//misc - (for example, I make a /lib/modules//misc/tmp - directory and copy the sound module files to that - directory). - - -Multiple Sound Cards: -===================== - -The sound drivers will support multiple sound cards and there -are some great applications like multitrack that support them. -Typically, you need two sound cards of different types. Note, this -uses more precious interrupts and DMA channels and sometimes -can be a configuration nightmare. I have heard reports of 3-4 -sound cards (typically I only use 2). You can sometimes use -multiple PCI sound cards of the same type. - -On my machine I have two sound cards (cs4232 and Soundblaster Vibra -16). By loading sound as modules, I can control which is the first -sound device (/dev/dsp, /dev/audio, /dev/mixer) and which is -the second. Normally, the cs4232 (Dell sound on the motherboard) -would be the first sound device, but I prefer the Soundblaster. -All you have to do is to load the one you want as /dev/dsp -first (in my case "sb") and then load the other one -(in my case "cs4232"). - -If you have two cards of the same type that are jumpered -cards or different PnP revisions, you may load the same -module twice. For example, I have a SoundBlaster vibra 16 -and an older SoundBlaster 16 (jumpers). To load the module -twice, you need to do the following: - -1. Copy the sound modules to a new name. For example - sb.o could be copied (or symlinked) to sb1.o for the - second SoundBlaster. - -2. Make a second entry in /etc/modules.conf, for example, - sound1 or sb1. This second entry should refer to the - new module names for example sb1, and should include - the I/O, etc. for the second sound card. - -3. Update your soundon.sh script, etc. - -Warning: I have never been able to get two PnP sound cards of the -same type to load at the same time. I have tried this several times -with the Soundblaster Vibra 16 cards. OSS has indicated that this -is a PnP problem.... If anyone has any luck doing this, please -send me an E-MAIL. PCI sound cards should not have this problem.a -Since this was originally release, I have received a couple of -mails from people who have accomplished this! - -NOTE: In Linux 2.4 the Sound Blaster driver (and only this one yet) -supports multiple cards with one module by default. -Read the file 'Soundblaster' in this directory for details. - - -Sound Problems: -=============== - -First RTFM (including the troubleshooting section -in the Sound-HOWTO). - -1) If you are having problems loading the modules (for - example, if you get device conflict errors) try the - following: - - A) If you have Win95 or NT on the same computer, - write down what addresses, IRQ, and DMA channels - those were using for the same hardware. You probably - can use these addresses, IRQs, and DMA channels. - You should really do this BEFORE attempting to get - sound working! - - B) Check (cat) /proc/interrupts, /proc/ioports, - and /proc/dma. Are you trying to use an address, - IRQ or DMA port that another device is using? - - C) Check (cat) /proc/isapnp - - D) Inspect your /var/log/messages file. Often that will - indicate what IRQ or IO port could not be obtained. - - E) Try another port or IRQ. Note this may involve - using the PnP tools to move the sound card to - another location. Sometimes this is the only way - and it is more or less trial and error. - -2) If you get motor-boating (the same sound or part of a - sound clip repeated), you probably have either an IRQ - or DMA conflict. Move the card to another IRQ or DMA - port. This has happened to me when playing long files - when I had an IRQ conflict. - -3. If you get dropouts or pauses when playing high sample - rate files such as using mpg123 or x11amp/xmms, you may - have too slow of a CPU and may have to use the options to - play the files at 1/2 speed. For example, you may use - the -2 or -4 option on mpg123. You may also get this - when trying to play mpeg files stored on a CD-ROM - (my Toshiba T8000 PII/366 sometimes has this problem). - -4. If you get "cannot access device" errors, your /dev/dsp - files, etc. may be set to owner root, mode 600. You - may have to use the command: - chmod 666 /dev/dsp /dev/mixer /dev/audio - -5. If you get "device busy" errors, another program has the - sound device open. For example, if using the Enlightenment - sound daemon "esd", the "esd" program has the sound device. - If using "esd", please RTFM the docs on ESD. For example, - esddsp may be used to play files via a non-esd - aware program. - -6) Ask for help on the sound list or send E-MAIL to the - sound driver author/maintainer. - -7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). - -8) If the system reports insufficient DMA memory then you may want to - load sound with the "dmabufs=1" option. Or in /etc/conf.modules add - - preinstall sound dmabufs=1 - - This makes the sound system allocate its buffers and hang onto them. - - You may also set persistent DMA when building a 2.4.x kernel. - - -Configuring Sound: -================== - -There are several ways of configuring your sound: - -1) On the kernel command line (when using the sound driver(s) - compiled in the kernel). Check the driver source and - documentation for details. - -2) On the command line when using insmod or in a bash script - using command line calls to load sound. - -3) In /etc/modules.conf when using modprobe. - -4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). - -5) Via the OSS soundconf program (with the commercial version - of the OSS driver. - -6) By just loading the module and let isapnp do everything relevant - for you. This works only with a few drivers yet and - of course - - only with isapnp hardware. - -And I am sure, several other ways. - -Anyone want to write a linuxconf module for configuring sound? - - -Module Loading: -=============== - -When a sound card is first referenced and sound is modular, the sound system -will ask for the sound devices to be loaded. Initially it requests that -the driver for the sound system is loaded. It then will ask for -sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and -so on). Thus you can do - -alias sound-slot-0 sb - -To load a soundblaster at this point. If the slot loading does not provide -the desired device - for example a soundblaster does not directly provide -a midi synth in all cases then it will request "sound-service-0-n" where n -is - - 0 Mixer - - 2 MIDI - - 3, 4 DSP audio - - -For example, I use the following to load my Soundblaster PCI 128 -(ES 1371) card first, followed by my SoundBlaster Vibra 16 card, -then by my TV card: - -# Load the Soundblaster PCI 128 as /dev/dsp, /dev/dsp1, /dev/mixer -alias sound-slot-0 es1371 - -# Load the Soundblaster Vibra 16 as /dev/dsp2, /dev/mixer1 -alias sound-slot-1 sb -options sb io=0x240 irq=5 dma=1 dma16=5 mpu_io=0x330 - -# Load the BTTV (TV card) as /dev/mixer2 -alias sound-slot-2 bttv -alias sound-service-2-0 tvmixer - -pre-install bttv modprobe tuner ; modprobe tvmixer -pre-install tvmixer modprobe msp3400; modprobe tvaudio -options tuner debug=0 type=8 -options bttv card=0 radio=0 pll=0 - - -For More Information (RTFM): -============================ -1) Information on kernel modules: linux/Documentation/modules.txt - and manual pages for insmod and modprobe. - -2) Information on PnP, RTFM manual pages for isapnp. - -3) Sound-HOWTO and Sound-Playing-HOWTO. - -4) OSS's WWW site at http://www.opensound.com. - -5) All the files in linux/Documentation/sound. - -6) The comments and code in linux/drivers/sound. - -7) The sndconfig and rhsound documentation from Red Hat. - -8) The Linux-sound mailing list: sound-list@redhat.com. - -9) Enlightenment documentation (for info on esd) - http://www.tux.org/~ricdude/EsounD.html. - -10) ALSA home page: http://www.alsa-project.org/ - - -Contact Information: -==================== -Wade Hampton: (whampton@staffnet.com) - diff -urN linux-2.5.6-pre3/Documentation/sound/MAD16 linux-2.5.6/Documentation/sound/MAD16 --- linux-2.5.6-pre3/Documentation/sound/MAD16 Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/Documentation/sound/MAD16 Wed Dec 31 16:00:00 1969 @@ -1,55 +0,0 @@ -(This recipe has been edited to update the configuration symbols.) - -From: Shaw Carruthers - -I have been using mad16 sound for some time now with no problems, current -kernel 2.1.89 - -lsmod shows: - -mad16 5176 0 -sb 22044 0 [mad16] -uart401 5576 0 [mad16 sb] -ad1848 14176 1 [mad16] -sound 61928 0 [mad16 sb uart401 ad1848] - -.config has: - -CONFIG_SOUND=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MAD16=m -CONFIG_SOUND_YM3812=m - -modules.conf has: - -alias char-major-14 mad16 -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - - -To get the built in mixer to work this needs to be: - -options adlib_card io=0x388 # FM synthesizer -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - -The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is - ------------------------------------------------------------------------- -The mad16 module in addition supports the following options: - -option: meaning: default: -joystick=0,1 disabled, enabled disabled -cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled - 0x06,0x08,0x0a Mitsumi, Panasonic, - Secondary IDE, Primary IDE -cdport=0x340,0x320, 0x340 - 0x330,0x360 -cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled -cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE -cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic -opl4=0,1 OPL3, OPL4 OPL3 - -for more details see linux/drivers/sound/mad16.c - -Rui Sousa diff -urN linux-2.5.6-pre3/Documentation/sound/Maestro linux-2.5.6/Documentation/sound/Maestro --- linux-2.5.6-pre3/Documentation/sound/Maestro Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/Documentation/sound/Maestro Wed Dec 31 16:00:00 1969 @@ -1,123 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro family of sound cards - - Zach Brown, December 1999 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - 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. - -ESS Maestro Chip Family ------------------------ - -There are 3 main variants of the ESS Maestro PCI sound chip. The first -is the Maestro 1. It was originally produced by Platform Tech as the -'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with -0x0100 as the device ID. It was put on some sound boards and a few laptops. -ESS bought the design and cleaned it up as the Maestro 2. This starts -their marking with the ESS vendor ID 0x125D and the 'year' device IDs. -The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. - -The various families of Maestro are mostly identical as far as this -driver is concerned. It doesn't touch the DSP parts that differ (though -it could for FM synthesis). - -Driver OSS Behavior --------------------- - -This OSS driver exports /dev/mixer and /dev/dsp to applications, which -mostly adhere to the OSS spec. This driver doesn't register itself -with /dev/sndstat, so don't expect information to appear there. - -The /dev/dsp device exported behaves almost as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, and mmap()ing for playback behaves. Capture/recording -is limited due to oddities with the Maestro hardware. One can only -record in 16bit stereo. For recording the maestro uses non interleaved -stereo buffers so that mmap()ing the incoming data does not result in -a ring buffer of LRLR data. mmap()ing of the read buffers is therefore -disallowed until this can be cleaned up. - -/dev/mixer is an interface to the AC'97 codec on the Maestro. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. - -The driver doesn't support MIDI or FM playback at the moment. Typically -the Maestro is wired to an MPU MIDI chip, but some hardware implementations -don't. We need to assemble a white list of hardware implementations that -have MIDI wired properly before we can claim to support it safely. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' -should also be added to your module configs (typically /etc/conf.modules) -if you're using modular OSS/Lite sound and want to default to using a -maestro chip. - -As this is a PCI device, the module does not need to be informed of -any IO or IRQ resources it should use, it devines these from the -system. Sometimes, on sucky PCs, the BIOS fails to allocated resources -for the maestro. This will result in a message like: - maestro: PCI subsystem reports IRQ 0, this might not be correct. -from the kernel. Should this happen the sound chip most likely will -not operate correctly. To solve this one has to dig through their BIOS -(typically entered by hitting a hot key at boot time) and figure out -what magic needs to happen so that the BIOS will reward the maestro with -an IRQ. This operation is incredibly system specific, so you're on your -own. Sometimes the magic lies in 'PNP Capable Operating System' settings. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -The other, more interesting option, is 'dsps_order'. Typically at -install time the driver will only register one available /dev/dsp device -for its use. The 'dsps_order' module parameter allows for more devices -to be allocated, as a power of two. Up to 4 devices can be registered -( dsps_order=2 ). These devices act as fully distinct units and use -separate channels in the maestro. - -Power Management ----------------- - -As of version 0.14, this driver has a minimal understanding of PCI -Power Management. If it finds a valid power management capability -on the PCI device it will attempt to use the power management -functions of the maestro. It will only do this on Maestro 2Es and -only on machines that are known to function well. You can -force the use of power management by setting the 'use_pm' module -option to 1, or can disable it entirely by setting it to 0. - -When using power management, the driver does a few things -differently. It will keep the chip in a lower power mode -when the module is inserted but /dev/dsp is not open. This -allows the mixer to function but turns off the clocks -on other parts of the chip. When /dev/dsp is opened the chip -is brought into full power mode, and brought back down -when it is closed. It also powers down the chip entirely -when the module is removed or the machine is shutdown. This -can have nonobvious consequences. CD audio may not work -after a power managing driver is removed. Also, software that -doesn't understand power management may not be able to talk -to the powered down chip until the machine goes through a hard -reboot to bring it back. - -.. more details .. ------------------- - -drivers/sound/maestro.c contains comments that hopefully explain -the maestro implementation. diff -urN linux-2.5.6-pre3/Documentation/sound/Maestro3 linux-2.5.6/Documentation/sound/Maestro3 --- linux-2.5.6-pre3/Documentation/sound/Maestro3 Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/Documentation/sound/Maestro3 Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro3 family of sound chips - - Zach Brown, January 2001 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - http://www.zabbo.net/maestro3/ - -I will try and maintain the most recent stable version of the driver -in both the stable and development kernel lines. - -Historically I've sucked pretty hard at actually doing that, however. - -ESS Maestro3 Chip Family ------------------------ - -The 'Maestro3' is much like the Maestro2 chip. The noted improvement -is the removal of the silicon in the '2' that did PCM mixing. All that -work is now done through a custom DSP called the ASSP, the Asynchronus -Specific Signal Processor. - -The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear -on the extent of the differences, but the driver supports them both :) - -The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, -both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as -0x199a when hardware strapping is used. - -The chip can also act as a multi function device. The modem IDs follow -the audio multimedia device IDs. (so the modem part of an Allegro shows -up as 0x1989) - -Driver OSS Behavior --------------------- - -This OSS driver exports /dev/mixer and /dev/dsp to applications, which -mostly adhere to the OSS spec. This driver doesn't register itself -with /dev/sndstat, so don't expect information to appear there. - -The /dev/dsp device exported behaves as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, with both read()/write(), and mmap(). - -/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro3. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. -The Allegro has an onchip AC'97. - -The driver doesn't support MIDI or FM playback at the moment. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 -maestro3' should also be added to your module configs (typically -/etc/modules.conf) if you're using modular OSS/Lite sound and want to -default to using a maestro3 chip. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -The other is 'external_amp', which tells the driver to attempt to enable -an external amplifier. This defaults to '1', you can tell the driver -not to bother enabling such an amplifier by setting it to '0'. - -Power Management ----------------- - -This driver has a minimal understanding of PCI Power Management. It will -try and power down the chip when the system is suspended, and power -it up with it is resumed. It will also try and power down the chip -when the machine is shut down. diff -urN linux-2.5.6-pre3/Documentation/sound/MultiSound linux-2.5.6/Documentation/sound/MultiSound --- linux-2.5.6-pre3/Documentation/sound/MultiSound Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/MultiSound Wed Dec 31 16:00:00 1969 @@ -1,1137 +0,0 @@ -#! /bin/sh -# -# Turtle Beach MultiSound Driver Notes -# -- Andrew Veliath -# -# Last update: September 10, 1998 -# Corresponding msnd driver: 0.8.3 -# -# ** This file is a README (top part) and shell archive (bottom part). -# The corresponding archived utility sources can be unpacked by -# running `sh MultiSound' (the utilities are only needed for the -# Pinnacle and Fiji cards). ** -# -# -# -=-=- Getting Firmware -=-=- -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# See the section `Obtaining and Creating Firmware Files' in this -# document for instructions on obtaining the necessary firmware -# files. -# -# -# Supported Features -# ~~~~~~~~~~~~~~~~~~ -# -# Currently, full-duplex digital audio (/dev/dsp only, /dev/audio is -# not currently available) and mixer functionality (/dev/mixer) are -# supported (memory mapped digital audio is not yet supported). -# Digital transfers and monitoring can be done as well if you have -# the digital daughterboard (see the section on using the S/PDIF port -# for more information). -# -# Support for the Turtle Beach MultiSound Hurricane architecture is -# composed of the following modules (these can also operate compiled -# into the kernel): -# -# msnd - MultiSound base (requires soundcore) -# -# msnd_classic - Base audio/mixer support for Classic, Monetery and -# Tahiti cards -# -# msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards -# -# -# Important Notes - Read Before Using -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# The firmware files are not included (may change in future). You -# must obtain these images from Turtle Beach (they are included in -# the MultiSound Development Kits), and place them in /etc/sound for -# example, and give the full paths in the Linux configuration. If -# you are compiling in support for the MultiSound driver rather than -# using it as a module, these firmware files must be accessible -# during kernel compilation. -# -# Please note these files must be binary files, not assembler. See -# the section later in this document for instructions to obtain these -# files. -# -# -# Configuring Card Resources -# ~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# ** This section is very important, as your card may not work at all -# or your machine may crash if you do not do this correctly. ** -# -# * Classic/Monterey/Tahiti -# -# These cards are configured through the driver msnd_classic. You must -# know the io port, then the driver will select the irq and memory resources -# on the card. It is up to you to know if these are free locations or now, -# a conflict can lock the machine up. -# -# * Pinnacle/Fiji -# -# The Pinnacle and Fiji cards have an extra config port, either -# 0x250, 0x260 or 0x270. This port can be disabled to have the card -# configured strictly through PnP, however you lose the ability to -# access the IDE controller and joystick devices on this card when -# using PnP. The included pinnaclecfg program in this shell archive -# can be used to configure the card in non-PnP mode, and in PnP mode -# you can use isapnptools. These are described briefly here. -# -# pinnaclecfg is not required; you can use the msnd_pinnacle module -# to fully configure the card as well. However, pinnaclecfg can be -# used to change the resource values of a particular device after the -# msnd_pinnacle module has been loaded. If you are compiling the -# driver into the kernel, you must set these values during compile -# time, however other peripheral resource values can be changed with -# the pinnaclecfg program after the kernel is loaded. -# -# -# *** PnP mode -# -# Use pnpdump to obtain a sample configuration if you can; I was able -# to obtain one with the command `pnpdump 1 0x203' -- this may vary -# for you (running pnpdump by itself did not work for me). Then, -# edit this file and use isapnp to uncomment and set the card values. -# Use these values when inserting the msnd_pinnacle module. Using -# this method, you can set the resources for the DSP and the Kurzweil -# synth (Pinnacle). Since Linux does not directly support PnP -# devices, you may have difficulty when using the card in PnP mode -# when it the driver is compiled into the kernel. Using non-PnP mode -# is preferable in this case. -# -# Here is an example mypinnacle.conf for isapnp that sets the card to -# io base 0x210, irq 5 and mem 0xd8000, and also sets the Kurzweil -# synth to 0x330 and irq 9 (may need editing for your system): -# -# (READPORT 0x0203) -# (CSN 2) -# (IDENTIFY *) -# -# # DSP -# (CONFIGURE BVJ0440/-1 (LD 0 -# (INT 0 (IRQ 5 (MODE +E))) (IO 0 (BASE 0x0210)) (MEM 0 (BASE 0x0d8000)) -# (ACT Y))) -# -# # Kurzweil Synth (Pinnacle Only) -# (CONFIGURE BVJ0440/-1 (LD 1 -# (IO 0 (BASE 0x0330)) (INT 0 (IRQ 9 (MODE +E))) -# (ACT Y))) -# -# (WAITFORKEY) -# -# -# *** Non-PnP mode -# -# The second way is by running the card in non-PnP mode. This -# actually has some advantages in that you can access some other -# devices on the card, such as the joystick and IDE controller. To -# configure the card, unpack this shell archive and build the -# pinnaclecfg program. Using this program, you can assign the -# resource values to the card's devices, or disable the devices. As -# an alternative to using pinnaclecfg, you can specify many of the -# configuration values when loading the msnd_pinnacle module (or -# during kernel configuration when compiling the driver into the -# kernel). -# -# If you specify cfg=0x250 for the msnd_pinnacle module, it -# automatically configure the card to the given io, irq and memory -# values using that config port (the config port is jumper selectable -# on the card to 0x250, 0x260 or 0x270). -# -# See the `msnd_pinnacle Additional Options' section below for more -# information on these parameters (also, if you compile the driver -# directly into the kernel, these extra parameters can be useful -# here). -# -# -# ** It is very easy to cause problems in your machine if you choose a -# resource value which is incorrect. ** -# -# -# Examples -# ~~~~~~~~ -# -# * MultiSound Classic/Monterey/Tahiti: -# -# modprobe soundcore -# insmod msnd -# insmod msnd_classic io=0x290 irq=7 mem=0xd0000 -# -# * MultiSound Pinnacle in PnP mode: -# -# modprobe soundcore -# insmod msnd -# isapnp mypinnacle.conf -# insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 <-- match mypinnacle.conf values -# -# * MultiSound Pinnacle in non-PnP mode (replace 0x250 with your configuration port, -# one of 0x250, 0x260 or 0x270): -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in PnP -# mode, add the following (assumes you did `isapnp mypinnacle.conf'): -# -# insmod sound -# insmod mpu401 io=0x330 irq=9 <-- match mypinnacle.conf values -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in non-PnP -# mode, add the following. Note how we first configure the peripheral's -# resources, _then_ install a Linux driver for it: -# -# insmod sound -# pinnaclecfg 0x250 mpu 0x330 9 -# insmod mpu401 io=0x330 irq=9 -# -# -- OR you can use the following sequence without pinnaclecfg in non-PnP mode: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 mpu_io=0x330 mpu_irq=9 -# insmod sound -# insmod mpu401 io=0x330 irq=9 -# -# * To setup the joystick port on the Pinnacle in non-PnP mode (though -# you have to find the actual Linux joystick driver elsewhere), you -# can use pinnaclecfg: -# -# pinnaclecfg 0x250 joystick 0x200 -# -# -- OR you can configure this using msnd_pinnacle with the following: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 joystick_io=0x200 -# -# -# msnd_classic, msnd_pinnacle Required Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If the following options are not given, the module will not load. -# Examine the kernel message log for informative error messages. -# WARNING--probing isn't supported so try to make sure you have the -# correct shared memory area, otherwise you may experience problems. -# -# io I/O base of DSP, e.g. io=0x210 -# irq IRQ number, e.g. irq=5 -# mem Shared memory area, e.g. mem=0xd8000 -# -# -# msnd_classic, msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# fifosize The digital audio FIFOs, in kilobytes. If not -# specified, the default will be used. Increasing -# this value will reduce the chance of a FIFO -# underflow at the expense of increasing overall -# latency. For example, fifosize=512 will -# allocate 512kB read and write FIFOs (1MB total). -# While this may reduce dropouts, a heavy machine -# load will undoubtedly starve the FIFO of data -# and you will eventually get dropouts. One -# option is to alter the scheduling priority of -# the playback process, using `nice' or some form -# of POSIX soft real-time scheduling. -# -# calibrate_signal Setting this to one calibrates the ADCs to the -# signal, zero calibrates to the card (defaults -# to zero). -# -# -# msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# digital Specify digital=1 to enable the S/PDIF input -# if you have the digital daughterboard -# adapter. This will enable access to the -# DIGITAL1 input for the soundcard in the mixer. -# Some mixer programs might have trouble setting -# the DIGITAL1 source as an input. If you have -# trouble, you can try the setdigital.c program -# at the bottom of this document. -# -# cfg Non-PnP configuration port for the Pinnacle -# and Fiji (typically 0x250, 0x260 or 0x270, -# depending on the jumper configuration). If -# this option is omitted, then it is assumed -# that the card is in PnP mode, and that the -# specified DSP resource values are already -# configured with PnP (i.e. it won't attempt to -# do any sort of configuration). -# -# When the Pinnacle is in non-PnP mode, you can use the following -# options to configure particular devices. If a full specification -# for a device is not given, then the device is not configured. Note -# that you still must use a Linux driver for any of these devices -# once their resources are setup (such as the Linux joystick driver, -# or the MPU401 driver from OSS for the Kurzweil synth). -# -# mpu_io I/O port of MPU (on-board Kurzweil synth) -# mpu_irq IRQ of MPU (on-board Kurzweil synth) -# ide_io0 First I/O port of IDE controller -# ide_io1 Second I/O port of IDE controller -# ide_irq IRQ IDE controller -# joystick_io I/O port of joystick -# -# -# Obtaining and Creating Firmware Files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For the Classic/Tahiti/Monterey -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach: -# -# ftp://ftp.voyetra.com/pub/tbs/msndcl/msndvkit.zip -# -# When unzipped, unzip the file named MsndFiles.zip. Then copy the -# following firmware files to /etc/sound (note the file renaming): -# -# cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin -# cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin -# -# When configuring the Linux kernel, specify /etc/sound/msndinit.bin and -# /etc/sound/msndperm.bin for the two firmware files (Linux kernel -# versions older than 2.2 do not ask for firmware paths, and are -# hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# For the Pinnacle/Fiji -# ~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach (be -# sure to use the entire URL; some have had trouble navigating to the -# URL): -# -# ftp://ftp.voyetra.com/pub/tbs/pinn/pnddk100.zip -# -# Unpack this shell archive, and run make in the created directory -# (you need a C compiler and flex to build the utilities). This -# should give you the executables conv, pinnaclecfg and setdigital. -# conv is only used temporarily here to create the firmware files, -# while pinnaclecfg is used to configure the Pinnacle or Fiji card in -# non-PnP mode, and setdigital can be used to set the S/PDIF input on -# the mixer (pinnaclecfg and setdigital should be copied to a -# convenient place, possibly run during system initialization). -# -# To generating the firmware files with the `conv' program, we create -# the binary firmware files by doing the following conversion -# (assuming the archive unpacked into a directory named PINNDDK): -# -# ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin -# ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin -# -# The conv (and conv.l) program is not needed after conversion and can -# be safely deleted. Then, when configuring the Linux kernel, specify -# /etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two -# firmware files (Linux kernel versions older than 2.2 do not ask for -# firmware paths, and are hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# Using Digital I/O with the S/PDIF Port -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If you have a Pinnacle or Fiji with the digital daughterboard and -# want to set it as the input source, you can use this program if you -# have trouble trying to do it with a mixer program (be sure to -# insert the module with the digital=1 option, or say Y to the option -# during compiled-in kernel operation). Upon selection of the S/PDIF -# port, you should be able monitor and record from it. -# -# There is something to note about using the S/PDIF port. Digital -# timing is taken from the digital signal, so if a signal is not -# connected to the port and it is selected as recording input, you -# will find PCM playback to be distorted in playback rate. Also, -# attempting to record at a sampling rate other than the DAT rate may -# be problematic (i.e. trying to record at 8000Hz when the DAT signal -# is 44100Hz). If you have a problem with this, set the recording -# input to analog if you need to record at a rate other than that of -# the DAT rate. -# -# -# -- Shell archive attached below, just run `sh MultiSound' to extract. -# Contains Pinnacle/Fiji utilities to convert firmware, configure -# in non-PnP mode, and select the DIGITAL1 input for the mixer. -# -# -#!/bin/sh -# This is a shell archive (produced by GNU sharutils 4.2). -# To extract the files from this archive, save it to some FILE, remove -# everything before the `!/bin/sh' line above, then type `sh FILE'. -# -# Made on 1998-12-04 10:07 EST by . -# Source directory was `/home/andrewtv/programming/pinnacle/pinnacle'. -# -# Existing files will *not* be overwritten unless `-c' is specified. -# -# This shar contains: -# length mode name -# ------ ---------- ------------------------------------------ -# 2046 -rw-rw-r-- MultiSound.d/setdigital.c -# 10235 -rw-rw-r-- MultiSound.d/pinnaclecfg.c -# 106 -rw-rw-r-- MultiSound.d/Makefile -# 141 -rw-rw-r-- MultiSound.d/conv.l -# 1472 -rw-rw-r-- MultiSound.d/msndreset.c -# -save_IFS="${IFS}" -IFS="${IFS}:" -gettext_dir=FAILED -locale_dir=FAILED -first_param="$1" -for dir in $PATH -do - if test "$gettext_dir" = FAILED && test -f $dir/gettext \ - && ($dir/gettext --version >/dev/null 2>&1) - then - set `$dir/gettext --version 2>&1` - if test "$3" = GNU - then - gettext_dir=$dir - fi - fi - if test "$locale_dir" = FAILED && test -f $dir/shar \ - && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) - then - locale_dir=`$dir/shar --print-text-domain-dir` - fi -done -IFS="$save_IFS" -if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED -then - echo=echo -else - TEXTDOMAINDIR=$locale_dir - export TEXTDOMAINDIR - TEXTDOMAIN=sharutils - export TEXTDOMAIN - echo="$gettext_dir/gettext -s" -fi -touch -am 1231235999 $$.touch >/dev/null 2>&1 -if test ! -f 1231235999 && test -f $$.touch; then - shar_touch=touch -else - shar_touch=: - echo - $echo 'WARNING: not restoring timestamps. Consider getting and' - $echo "installing GNU \`touch', distributed in GNU File Utilities..." - echo -fi -rm -f 1231235999 $$.touch -# -if mkdir _sh01426; then - $echo 'x -' 'creating lock directory' -else - $echo 'failed to create lock directory' - exit 1 -fi -# ============= MultiSound.d/setdigital.c ============== -if test ! -d 'MultiSound.d'; then - $echo 'x -' 'creating directory' 'MultiSound.d' - mkdir 'MultiSound.d' -fi -if test -f 'MultiSound.d/setdigital.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/setdigital.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/setdigital.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/setdigital.c' && -/********************************************************************* -X * -X * setdigital.c - sets the DIGITAL1 input for a mixer -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -int main(int argc, char *argv[]) -{ -X int fd; -X unsigned long recmask, recsrc; -X -X if (argc != 2) { -X fprintf(stderr, "usage: setdigital \n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) { -X fprintf(stderr, "error: ioctl read recording mask failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X if (!(recmask & SOUND_MASK_DIGITAL1)) { -X fprintf(stderr, "error: cannot find DIGITAL1 device in mixer\n"); -X close(fd); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl read recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X recsrc |= SOUND_MASK_DIGITAL1; -X -X if (ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl write recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/setdigital.c' && - chmod 0664 'MultiSound.d/setdigital.c' || - $echo 'restore of' 'MultiSound.d/setdigital.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/setdigital.c:' 'MD5 check failed' -e87217fc3e71288102ba41fd81f71ec4 MultiSound.d/setdigital.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/setdigital.c'`" - test 2046 -eq "$shar_count" || - $echo 'MultiSound.d/setdigital.c:' 'original size' '2046,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/pinnaclecfg.c ============== -if test -f 'MultiSound.d/pinnaclecfg.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/pinnaclecfg.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/pinnaclecfg.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/pinnaclecfg.c' && -/********************************************************************* -X * -X * pinnaclecfg.c - Pinnacle/Fiji Device Configuration Program -X * -X * This is for NON-PnP mode only. For PnP mode, use isapnptools. -X * -X * This is Linux-specific, and must be run with root permissions. -X * -X * Part of the Turtle Beach MultiSound Sound Card Driver for Linux -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -#define IREG_LOGDEVICE 0x07 -#define IREG_ACTIVATE 0x30 -#define LD_ACTIVATE 0x01 -#define LD_DISACTIVATE 0x00 -#define IREG_EECONTROL 0x3F -#define IREG_MEMBASEHI 0x40 -#define IREG_MEMBASELO 0x41 -#define IREG_MEMCONTROL 0x42 -#define IREG_MEMRANGEHI 0x43 -#define IREG_MEMRANGELO 0x44 -#define MEMTYPE_8BIT 0x00 -#define MEMTYPE_16BIT 0x02 -#define MEMTYPE_RANGE 0x00 -#define MEMTYPE_HIADDR 0x01 -#define IREG_IO0_BASEHI 0x60 -#define IREG_IO0_BASELO 0x61 -#define IREG_IO1_BASEHI 0x62 -#define IREG_IO1_BASELO 0x63 -#define IREG_IRQ_NUMBER 0x70 -#define IREG_IRQ_TYPE 0x71 -#define IRQTYPE_HIGH 0x02 -#define IRQTYPE_LOW 0x00 -#define IRQTYPE_LEVEL 0x01 -#define IRQTYPE_EDGE 0x00 -X -#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) -#define LOBYTE(w) ((BYTE)(w)) -#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) -X -typedef __u8 BYTE; -typedef __u16 USHORT; -typedef __u16 WORD; -X -static int config_port = -1; -X -static int msnd_write_cfg(int cfg, int reg, int value) -{ -X outb(reg, cfg); -X outb(value, cfg + 1); -X if (value != inb(cfg + 1)) { -X fprintf(stderr, "error: msnd_write_cfg: I/O error\n"); -X return -EIO; -X } -X return 0; -} -X -static int msnd_read_cfg(int cfg, int reg) -{ -X outb(reg, cfg); -X return inb(cfg + 1); -} -X -static int msnd_write_cfg_io0(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io0(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO0_BASELO), -X msnd_read_cfg(cfg, IREG_IO0_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_io1(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io1(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO1_BASELO), -X msnd_read_cfg(cfg, IREG_IO1_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_irq(int cfg, int num, WORD irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_irq(int cfg, int num, WORD *irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *irq = msnd_read_cfg(cfg, IREG_IRQ_NUMBER); -X -X return 0; -} -X -static int msnd_write_cfg_mem(int cfg, int num, int mem) -{ -X WORD wmem; -X -X mem >>= 8; -X mem &= 0xfff; -X wmem = (WORD)mem; -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) -X return -EIO; -X if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_mem(int cfg, int num, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *mem = MAKEWORD(msnd_read_cfg(cfg, IREG_MEMBASELO), -X msnd_read_cfg(cfg, IREG_MEMBASEHI)); -X *mem <<= 8; -X -X return 0; -} -X -static int msnd_activate_logical(int cfg, int num) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) -X return -EIO; -X return 0; -} -X -static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_write_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_write_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_write_cfg_mem(cfg, num, mem)) -X return -EIO; -X if (msnd_activate_logical(cfg, num)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_logical(int cfg, int num, WORD *io0, WORD *io1, WORD *irq, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_read_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_read_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_read_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_read_cfg_mem(cfg, num, mem)) -X return -EIO; -X return 0; -} -X -static void usage(void) -{ -X fprintf(stderr, -X "\n" -X "pinnaclecfg 1.0\n" -X "\n" -X "usage: pinnaclecfg [device config]\n" -X "\n" -X "This is for use with the card in NON-PnP mode only.\n" -X "\n" -X "Available devices (not all available for Fiji):\n" -X "\n" -X " Device Description\n" -X " -------------------------------------------------------------------\n" -X " reset Reset all devices (i.e. disable)\n" -X " show Display current device configurations\n" -X "\n" -X " dsp Audio device\n" -X " mpu Internal Kurzweil synth\n" -X " ide On-board IDE controller\n" -X " joystick Joystick port\n" -X "\n"); -X exit(1); -} -X -static int cfg_reset(void) -{ -X int i; -X -X for (i = 0; i < 4; ++i) -X msnd_write_cfg_logical(config_port, i, 0, 0, 0, 0); -X -X return 0; -} -X -static int cfg_show(void) -{ -X int i; -X int count = 0; -X -X for (i = 0; i < 4; ++i) { -X WORD io0, io1, irq; -X int mem; -X msnd_read_cfg_logical(config_port, i, &io0, &io1, &irq, &mem); -X switch (i) { -X case 0: -X if (io0 || irq || mem) { -X printf("dsp 0x%x %d 0x%x\n", io0, irq, mem); -X ++count; -X } -X break; -X case 1: -X if (io0 || irq) { -X printf("mpu 0x%x %d\n", io0, irq); -X ++count; -X } -X break; -X case 2: -X if (io0 || io1 || irq) { -X printf("ide 0x%x 0x%x %d\n", io0, io1, irq); -X ++count; -X } -X break; -X case 3: -X if (io0) { -X printf("joystick 0x%x\n", io0); -X ++count; -X } -X break; -X } -X } -X -X if (count == 0) -X fprintf(stderr, "no devices configured\n"); -X -X return 0; -} -X -static int cfg_dsp(int argc, char *argv[]) -{ -X int io, irq, mem; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1 || -X sscanf(argv[2], "0x%x", &mem) != 1) -X usage(); -X -X if (!(io == 0x290 || -X io == 0x260 || -X io == 0x250 || -X io == 0x240 || -X io == 0x230 || -X io == 0x220 || -X io == 0x210 || -X io == 0x3e0)) { -X fprintf(stderr, "error: io must be one of " -X "210, 220, 230, 240, 250, 260, 290, or 3E0\n"); -X usage(); -X } -X -X if (!(irq == 5 || -X irq == 7 || -X irq == 9 || -X irq == 10 || -X irq == 11 || -X irq == 12)) { -X fprintf(stderr, "error: irq must be one of " -X "5, 7, 9, 10, 11 or 12\n"); -X usage(); -X } -X -X if (!(mem == 0xb0000 || -X mem == 0xc8000 || -X mem == 0xd0000 || -X mem == 0xd8000 || -X mem == 0xe0000 || -X mem == 0xe8000)) { -X fprintf(stderr, "error: mem must be one of " -X "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); -X usage(); -X } -X -X return msnd_write_cfg_logical(config_port, 0, io, 0, irq, mem); -} -X -static int cfg_mpu(int argc, char *argv[]) -{ -X int io, irq; -X -X if (argc < 2 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 1, io, 0, irq, 0); -} -X -static int cfg_ide(int argc, char *argv[]) -{ -X int io0, io1, irq; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io0) != 1 || -X sscanf(argv[0], "0x%x", &io1) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 2, io0, io1, irq, 0); -} -X -static int cfg_joystick(int argc, char *argv[]) -{ -X int io; -X -X if (argc < 1 || -X sscanf(argv[0], "0x%x", &io) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 3, io, 0, 0, 0); -} -X -int main(int argc, char *argv[]) -{ -X char *device; -X int rv = 0; -X -X --argc; ++argv; -X -X if (argc < 2) -X usage(); -X -X sscanf(argv[0], "0x%x", &config_port); -X if (config_port != 0x250 && config_port != 0x260 && config_port != 0x270) { -X fprintf(stderr, "error: must be 0x250, 0x260 or 0x270\n"); -X exit(1); -X } -X if (ioperm(config_port, 2, 1)) { -X perror("ioperm"); -X fprintf(stderr, "note: pinnaclecfg must be run as root\n"); -X exit(1); -X } -X device = argv[1]; -X -X argc -= 2; argv += 2; -X -X if (strcmp(device, "reset") == 0) -X rv = cfg_reset(); -X else if (strcmp(device, "show") == 0) -X rv = cfg_show(); -X else if (strcmp(device, "dsp") == 0) -X rv = cfg_dsp(argc, argv); -X else if (strcmp(device, "mpu") == 0) -X rv = cfg_mpu(argc, argv); -X else if (strcmp(device, "ide") == 0) -X rv = cfg_ide(argc, argv); -X else if (strcmp(device, "joystick") == 0) -X rv = cfg_joystick(argc, argv); -X else { -X fprintf(stderr, "error: unknown device %s\n", device); -X usage(); -X } -X -X if (rv) -X fprintf(stderr, "error: device configuration failed\n"); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/pinnaclecfg.c' && - chmod 0664 'MultiSound.d/pinnaclecfg.c' || - $echo 'restore of' 'MultiSound.d/pinnaclecfg.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/pinnaclecfg.c:' 'MD5 check failed' -366bdf27f0db767a3c7921d0a6db20fe MultiSound.d/pinnaclecfg.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/pinnaclecfg.c'`" - test 10235 -eq "$shar_count" || - $echo 'MultiSound.d/pinnaclecfg.c:' 'original size' '10235,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/Makefile ============== -if test -f 'MultiSound.d/Makefile' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/Makefile' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/Makefile' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/Makefile' && -CC = gcc -CFLAGS = -O -PROGS = setdigital msndreset pinnaclecfg conv -X -all: $(PROGS) -X -clean: -X rm -f $(PROGS) -SHAR_EOF - $shar_touch -am 1204092398 'MultiSound.d/Makefile' && - chmod 0664 'MultiSound.d/Makefile' || - $echo 'restore of' 'MultiSound.d/Makefile' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/Makefile:' 'MD5 check failed' -76ca8bb44e3882edcf79c97df6c81845 MultiSound.d/Makefile -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/Makefile'`" - test 106 -eq "$shar_count" || - $echo 'MultiSound.d/Makefile:' 'original size' '106,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/conv.l ============== -if test -f 'MultiSound.d/conv.l' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/conv.l' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/conv.l' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/conv.l' && -%% -[ \n\t,\r] -\;.* -DB -[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } -%% -int yywrap() { return 1; } -main() { yylex(); } -SHAR_EOF - $shar_touch -am 0828231798 'MultiSound.d/conv.l' && - chmod 0664 'MultiSound.d/conv.l' || - $echo 'restore of' 'MultiSound.d/conv.l' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/conv.l:' 'MD5 check failed' -d2411fc32cd71a00dcdc1f009e858dd2 MultiSound.d/conv.l -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/conv.l'`" - test 141 -eq "$shar_count" || - $echo 'MultiSound.d/conv.l:' 'original size' '141,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/msndreset.c ============== -if test -f 'MultiSound.d/msndreset.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/msndreset.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/msndreset.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/msndreset.c' && -/********************************************************************* -X * -X * msndreset.c - resets the MultiSound card -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include -#include -#include -#include -#include -#include -#include -X -int main(int argc, char *argv[]) -{ -X int fd; -X -X if (argc != 2) { -X fprintf(stderr, "usage: msndreset \n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_PRIVATE1, 0) < 0) { -X fprintf(stderr, "error: msnd ioctl reset failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204100698 'MultiSound.d/msndreset.c' && - chmod 0664 'MultiSound.d/msndreset.c' || - $echo 'restore of' 'MultiSound.d/msndreset.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/msndreset.c:' 'MD5 check failed' -c52f876521084e8eb25e12e01dcccb8a MultiSound.d/msndreset.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/msndreset.c'`" - test 1472 -eq "$shar_count" || - $echo 'MultiSound.d/msndreset.c:' 'original size' '1472,' 'current size' "$shar_count!" - fi -fi -rm -fr _sh01426 -exit 0 diff -urN linux-2.5.6-pre3/Documentation/sound/NEWS linux-2.5.6/Documentation/sound/NEWS --- linux-2.5.6-pre3/Documentation/sound/NEWS Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/Documentation/sound/NEWS Wed Dec 31 16:00:00 1969 @@ -1,42 +0,0 @@ -Linux 2.4 Sound Changes -2000-September-25 -Christoph Hellwig, - - - -=== isapnp support - -The Linux 2.4 Kernel does have reliable in-kernel isapnp support. -Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically -detecting and configuring isapnp devices. -If you have a not yet supported isapnp soundcard, mail me the content -of '/proc/isapnp' on your system and some information about your card -and its driver(s) so I can try to get isapnp working for it. - - - -=== soundcard resources on kernel commandline - -Before Linux 2.4 you had to specify the resources for sounddrivers -statically linked into the kernel at compile time -(in make config/menuconfig/xconfig). In Linux 2.4 the ressources are -now specified at the boot-time kernel commandline (e.g. the lilo -'append=' line or everything that's after the kernel name in grub). -Read the Configure.help entry for your card for the parameters. - - -=== softoss is gone - -In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. -Use a user space software synthesizer like timidity instead. - - - -=== /dev/sndstat and /proc/sound are gone - -In older Linux versions those files exported some information about the -OSS/Free configuration to userspace. In Linux 2.3 they were removed because -they did not support the growing number of pci soundcards and there were -some general problems with this interface. - - diff -urN linux-2.5.6-pre3/Documentation/sound/NM256 linux-2.5.6/Documentation/sound/NM256 --- linux-2.5.6-pre3/Documentation/sound/NM256 Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/Documentation/sound/NM256 Wed Dec 31 16:00:00 1969 @@ -1,280 +0,0 @@ -======================================================= -Documentation for the NeoMagic 256AV/256ZX sound driver -======================================================= - -You're looking at version 1.1 of the driver. (Woohoo!) It has been -successfully tested against the following laptop models: - - Sony Z505S/Z505SX/Z505DX/Z505RX - Sony F150, F160, F180, F250, F270, F280, PCG-F26 - Dell Latitude CPi, CPt (various submodels) - -There are a few caveats, which is why you should read the entirety of -this document first. - -This driver was developed without any support or assistance from -NeoMagic. There is no warranty, expressed, implied, or otherwise. It -is free software in the public domain; feel free to use it, sell it, -give it to your best friends, even claim that you wrote it (but why?!) -but don't go whining to me, NeoMagic, Sony, Dell, or anyone else -when it blows up your computer. - -Version 1.1 contains a change to try and detect non-AC97 versions of -the hardware, and not install itself appropriately. It should also -reinitialize the hardware on an APM resume event, assuming that APM -was configured into your kernel. - -============ -Installation -============ - -Enable the sound drivers, the OSS sound drivers, and then the NM256 -driver. The NM256 driver *must* be configured as a module (it won't -give you any other choice). - -Next, do the usual "make modules" and "make modules_install". -Finally, insmod the soundcore, sound and nm256 modules. - -When the nm256 driver module is loaded, you should see a couple of -confirmation messages in the kernel logfile indicating that it found -the device (the device does *not* use any I/O ports or DMA channels). -Now try playing a wav file, futz with the CD-ROM if you have one, etc. - -The NM256 is entirely a PCI-based device, and all the necessary -information is automatically obtained from the card. It can only be -configured as a module in a vain attempt to prevent people from -hurting themselves. It works correctly if it shares an IRQ with -another device (it normally shares IRQ 9 with the builtin eepro100 -ethernet on the Sony Z505 laptops). - -It does not run the card in any sort of compatibility mode. It will -not work on laptops that have the SB16-compatible, AD1848-compatible -or CS4232-compatible codec/mixer; you will want to use the appropriate -compatible OSS driver with these chipsets. I cannot provide any -assistance with machines using the SB16, AD1848 or CS4232 compatible -versions. (The driver now attempts to detect the mixer version, and -will refuse to load if it believes the hardware is not -AC97-compatible.) - -The sound support is very basic, but it does include simultaneous -playback and record capability. The mixer support is also quite -simple, although this is in keeping with the rather limited -functionality of the chipset. - -There is no hardware synthesizer available, as the Losedows OPL-3 and -MIDI support is done via hardware emulation. - -Only three recording devices are available on the Sony: the -microphone, the CD-ROM input, and the volume device (which corresponds -to the stereo output). (Other devices may be available on other -models of laptops.) The Z505 series does not have a builtin CD-ROM, -so of course the CD-ROM input doesn't work. It does work on laptops -with a builtin CD-ROM drive. - -The mixer device does not appear to have any tone controls, at least -on the Z505 series. The mixer module checks for tone controls in the -AC97 mixer, and will enable them if they are available. - -============== -Known problems -============== - - * There are known problems with PCMCIA cards and the eepro100 ethernet - driver on the Z505S/Z505SX/Z505DX. Keep reading. - - * There are also potential problems with using a virtual X display, and - also problems loading the module after the X server has been started. - Keep reading. - - * The volume control isn't anywhere near linear. Sorry. This will be - fixed eventually, when I get sufficiently annoyed with it. (I doubt - it will ever be fixed now, since I've never gotten sufficiently - annoyed with it and nobody else seems to care.) - - * There are reports that the CD-ROM volume is very low. Since I do not - have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to - do remotely). - - * Only 8 fixed-rate speeds are supported. This is mainly a chipset - limitation. It may be possible to support other speeds in the future. - - * There is no support for the telephone mixer/codec. There is support - for a phonein/phoneout device in the mixer driver; whether or not - it does anything is anyone's guess. (Reports on this would be - appreciated. You'll have to figure out how to get the phone to - go off-hook before it'll work, tho.) - - * This driver was not written with any cooperation or support from - NeoMagic. If you have any questions about this, see their website - for their official stance on supporting open source drivers. - -============ -Video memory -============ - -The NeoMagic sound engine uses a portion of the display memory to hold -the sound buffer. (Crazy, eh?) The NeoMagic video BIOS sets up a -special pointer at the top of video RAM to indicate where the top of -the audio buffer should be placed. - -At the present time XFree86 is apparently not aware of this. It will -thus write over either the pointer or the sound buffer with abandon. -(Accelerated-X seems to do a better job here.) - -This implies a few things: - - * Sometimes the NM256 driver has to guess at where the buffer - should be placed, especially if the module is loaded after the - X server is started. It's usually correct, but it will consistently - fail on the Sony F250. - - * Virtual screens greater than 1024x768x16 under XFree86 are - problematic on laptops with only 2.5MB of screen RAM. This - includes all of the 256AV-equipped laptops. (Virtual displays - may or may not work on the 256ZX, which has at least 4MB of - video RAM.) - -If you start having problems with random noise being output either -constantly (this is the usual symptom on the F250), or when windows -are moved around (this is the usual symptom when using a virtual -screen), the best fix is to - - * Don't use a virtual frame buffer. - * Make sure you load the NM256 module before the X server is - started. - -On the F250, it is possible to force the driver to load properly even -after the XFree86 server is started by doing: - - insmod nm256 buffertop=0x25a800 - -This forces the audio buffers to the correct offset in screen RAM. - -One user has reported a similar problem on the Sony F270, although -others apparently aren't seeing any problems. His suggested command -is - - insmod nm256 buffertop=0x272800 - -================= -Official WWW site -================= - -The official site for the NM256 driver is: - - http://www.uglx.org/sony.html - -You should always be able to get the latest version of the driver there, -and the driver will be supported for the foreseeable future. - -============== -Z505RX and IDE -============== - -There appears to be a problem with the IDE chipset on the Z505RX; one -of the symptoms is that sound playback periodically hangs (when the -disk is accessed). The user reporting the problem also reported that -enabling all of the IDE chipset workarounds in the kernel solved the -problem, tho obviously only one of them should be needed--if someone -can give me more details I would appreciate it. - -============================== -Z505S/Z505SX on-board Ethernet -============================== - -If you're using the on-board Ethernet Pro/100 ethernet support on the Z505 -series, I strongly encourage you to download the latest eepro100 driver from -Donald Becker's site: - - ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c - -There was a reported problem on the Z505SX that if the ethernet -interface is disabled and reenabled while the sound driver is loaded, -the machine would lock up. I have included a workaround that is -working satisfactorily. However, you may occasionally see a message -about "Releasing interrupts, over 1000 bad interrupts" which indicates -that the workaround is doing its job. - -================================== -PCMCIA and the Z505S/Z505SX/Z505DX -================================== - -There is also a known problem with the Sony Z505S and Z505SX hanging -if a PCMCIA card is inserted while the ethernet driver is loaded, or -in some cases if the laptop is suspended. This is caused by tons of -spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges. - -There is currently no fix for the problem that works in every case. -The only known workarounds are to disable the ethernet interface -before inserting or removing a PCMCIA card, or with some cards -disabling the PCMCIA card before ejecting it will also help the -problem with the laptop hanging when the card is ejected. - -One user has reported that setting the tcic's cs_irq to some value -other than 9 (like 11) fixed the problem. This doesn't work on my -Z505S, however--changing the value causes the cardmgr to stop seeing -card insertions and removals, cards don't seem to work correctly, and -I still get hangs if a card is inserted when the kernel is booted. - -Using the latest ethernet driver and pcmcia package allows me to -insert an Adaptec 1480A SlimScsi card without the laptop hanging, -although I still have to shut down the card before ejecting or -powering down the laptop. However, similar experiments with a DE-660 -ethernet card still result in hangs when the card is inserted. I am -beginning to think that the interrupts are CardBus-related, since the -Adaptec card is a CardBus card, and the DE-660 is not; however, I -don't have any other CardBus cards to test with. - -====== -Thanks -====== - -First, I want to thank everyone (except NeoMagic of course) for their -generous support and encouragement. I'd like to list everyone's name -here that replied during the development phase, but the list is -amazingly long. - -I will be rather unfair and single out a few people, however: - - Justin Maurer, for being the first random net.person to try it, - and for letting me login to his Z505SX to get it working there - - Edi Weitz for trying out several different versions, and giving - me a lot of useful feedback - - Greg Rumple for letting me login remotely to get the driver - functional on the 256ZX, for his assistance on tracking - down all sorts of random stuff, and for trying out Accel-X - - Zach Brown, for the initial AC97 mixer interface design - - Jeff Garzik, for various helpful suggestions on the AC97 - interface - - "Mr. Bumpy" for feedback on the Z505RX - - Bill Nottingham, for generous assistance in getting the mixer ID - code working - -================= -Previous versions -================= - -Versions prior to 0.3 (aka `noname') had problems with weird artifacts -in the output and failed to set the recording rate properly. These -problems have long since been fixed. - -Versions prior to 0.5 had problems with clicks in the output when -anything other than 16-bit stereo sound was being played, and also had -periodic clicks when recording. - -Version 0.7 first incorporated support for the NM256ZX chipset, which -is found on some Dell Latitude laptops (the CPt, and apparently -some CPi models as well). It also included the generic AC97 -mixer module. - -Version 0.75 renamed all the functions and files with slightly more -generic names. - -Note that previous versions of this document claimed that recording was -8-bit only; it actually has been working for 16-bits all along. diff -urN linux-2.5.6-pre3/Documentation/sound/OPL3 linux-2.5.6/Documentation/sound/OPL3 --- linux-2.5.6-pre3/Documentation/sound/OPL3 Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/Documentation/sound/OPL3 Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -A pure OPL3 card is nice and easy to configure. Simply do - -insmod opl3 io=0x388 - -Change the I/O address in the very unlikely case this card is differently -configured diff -urN linux-2.5.6-pre3/Documentation/sound/OPL3-SA linux-2.5.6/Documentation/sound/OPL3-SA --- linux-2.5.6-pre3/Documentation/sound/OPL3-SA Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/OPL3-SA Wed Dec 31 16:00:00 1969 @@ -1,52 +0,0 @@ -OPL3-SA1 sound driver (opl3sa.o) - ---- -Note: This howto only describes how to setup the OPL3-SA1 chip; this info -does not apply to the SA2, SA3, or SA4. ---- - -The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and -it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 -and OPL3 FM Synth capabilities. - -You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or -CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. - -You'll need to know all of the relevant info (irq, dma, and io port) for the -chip's WSS mode, since that is the mode the kernel sound driver uses, and of -course you'll also need to know about where the MPU401 and OPL3 ports and -IRQs are if you want to use those. - -Here's the skinny on how to load it as a module: - - modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 - -Module options in detail: - - io: This is the WSS's port base. - irq: This is the WSS's IRQ. - dma: This is the WSS's DMA line. In my BIOS setup screen this was - listed as "WSS Play DMA" - dma2: This is the WSS's secondary DMA line. My BIOS calls it the - "WSS capture DMA" - - mpu_io: This is the MPU401's port base. - mpu_irq: This is the MPU401's IRQ. - -If you'd like to use the OPL3 FM Synthesizer, make sure you enable -CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module. - -Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. - -You can also use the SoftOSS software synthesizer instead of the builtin OPL3. -Here's how: - -Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. - -If you said yes, the software synth is available once you boot your new -kernel. - -If you chose to build it as a module, just insmod the resulting softoss2.o - -Questions? Comments? - diff -urN linux-2.5.6-pre3/Documentation/sound/OPL3-SA2 linux-2.5.6/Documentation/sound/OPL3-SA2 --- linux-2.5.6-pre3/Documentation/sound/OPL3-SA2 Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/Documentation/sound/OPL3-SA2 Wed Dec 31 16:00:00 1969 @@ -1,210 +0,0 @@ -Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o) ---------------------------------------------------------------- - -Scott Murray, scott@spiteful.org -January 7, 2001 - -NOTE: All trade-marked terms mentioned below are properties of their - respective owners. - - -Supported Devices ------------------ - -This driver is for PnP soundcards based on the following Yamaha audio -controller chipsets: - -YMF711 aka OPL3-SA2 -YMF715 and YMF719 aka OPL3-SA3 - -Up until recently (December 2000), I'd thought the 719 to be a -different chipset, the OPL3-SAx. After an email exhange with -Yamaha, however, it turns out that the 719 is just a re-badged -715, and the chipsets are identical. The chipset detection code -has been updated to reflect this. - -Anyways, all of these chipsets implement the following devices: - -OPL3 FM synthesizer -Soundblaster Pro -Microsoft/Windows Sound System -MPU401 MIDI interface - -Note that this driver uses the MSS device, and to my knowledge these -chipsets enforce an either/or situation with the Soundblaster Pro -device and the MSS device. Since the MSS device has better -capabilities, I have implemented the driver to use it. - - -Mixer Channels --------------- - -Older versions of this driver (pre-December 2000) had two mixers, -an OPL3-SA2 or SA3 mixer and a MSS mixer. The OPL3-SA[23] mixer -device contained a superset of mixer channels consisting of its own -channels and all of the MSS mixer channels. To simplify the driver -considerably, and to partition functionality better, the OPL3-SA[23] -mixer device now contains has its own specific mixer channels. They -are: - -Volume - Hardware master volume control -Bass - SA3 only, now supports left and right channels -Treble - SA3 only, now supports left and right channels -Microphone - Hardware microphone input volume control -Digital1 - Yamaha 3D enhancement "Wide" mixer - -All other mixer channels (e.g. "PCM", "CD", etc.) now have to be -controlled via the "MS Sound System (CS4231)" mixer. To facilitate -this, the mixer device creation order has been switched so that -the MSS mixer is created first. This allows accessing the majority -of the useful mixer channels even via single mixer-aware tools -such as "aumix". - - -Plug 'n Play ------------- - -In previous kernels (2.2.x), some configuration was required to -get the driver to talk to the card. Being the new millennium and -all, the 2.4.x kernels now support auto-configuration if ISA PnP -support is configured in. Theoretically, the driver even supports -having more than one card in this case. - -With the addition of PnP support to the driver, two new parameters -have been added to control it: - -isapnp - set to 0 to disable ISA PnP card detection - -multiple - set to 0 to disable multiple PnP card detection - - -Optional Parameters -------------------- - -Recent (December 2000) additions to the driver (based on a patch -provided by Peter Englmaier) are two new parameters: - -ymode - Set Yamaha 3D enhancement mode: - 0 = Desktop/Normal 5-12 cm speakers - 1 = Notebook PC (1) 3 cm speakers - 2 = Notebook PC (2) 1.5 cm speakers - 3 = Hi-Fi 16-38 cm speakers - -loopback - Set A/D input source. Useful for echo cancellation: - 0 = Mic Right channel (default) - 1 = Mono output loopback - -The ymode parameter has been tested and does work. The loopback -parameter, however, is untested. Any feedback on its usefulness -would be appreciated. - - -Manual Configuration --------------------- - -If for some reason you decide not to compile ISA PnP support into -your kernel, or disabled the driver's usage of it by setting the -isapnp parameter as discussed above, then you will need to do some -manual configuration. There are two ways of doing this. The most -common is to use the isapnptools package to initialize the card, and -use the kernel module form of the sound subsystem and sound drivers. -Alternatively, some BIOS's allow manual configuration of installed -PnP devices in a BIOS menu, which should allow using the non-modular -sound drivers, i.e. built into the kernel. - -I personally use isapnp and modules, and do not have access to a PnP -BIOS machine to test. If you have such a beast, configuring the -driver to be built into the kernel should just work (thanks to work -done by David Luyer ). You will still need -to specify settings, which can be done by adding: - -opl3sa2=,,,,, - -to the kernel command line. For example: - -opl3sa2=0x370,5,0,1,0x530,0x330 - -If you are instead using the isapnp tools (as most people have been -before Linux 2.4.x), follow the directions in their documentation to -produce a configuration file. Here is the relevant excerpt I used to -use for my SA3 card from my isapnp.conf: - -(CONFIGURE YMH0800/-1 (LD 0 - -# NOTE: IO 0 is for the unused SoundBlaster part of the chipset. -(IO 0 (BASE 0x0220)) -(IO 1 (BASE 0x0530)) -(IO 2 (BASE 0x0388)) -(IO 3 (BASE 0x0330)) -(IO 4 (BASE 0x0370)) -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 0)) -(DMA 1 (CHANNEL 1)) - -Here, note that: - -Port Acceptable Range Purpose ----- ---------------- ------- -IO 0 0x0220 - 0x0280 SB base address, unused. -IO 1 0x0530 - 0x0F48 MSS base address -IO 2 0x0388 - 0x03F8 OPL3 base address -IO 3 0x0300 - 0x0334 MPU base address -IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports - -The IRQ and DMA values can be any that are considered acceptable for a -MSS. Assuming you've got isapnp all happy, then you should be able to -do something like the following (which matches up with the isapnp -configuration above): - -modprobe mpu401 -modprobe ad1848 -modprobe opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=5 dma=0 dma2=1 -modprobe opl3 io=0x388 - -See the section "Automatic Module Loading" below for how to set up -/etc/modules.conf to automate this. - -An important thing to remember that the opl3sa2 module's io argument is -for it's own control port, which handles the card's master mixer for -volume (on all cards), and bass and treble (on SA3 cards). - - -Troubleshooting ---------------- - -If all goes well and you see no error messages, you should be able to -start using the sound capabilities of your system. If you get an -error message while trying to insert the opl3sa2 module, then make -sure that the values of the various arguments match what you specified -in your isapnp configuration file, and that there is no conflict with -another device for an I/O port or interrupt. Checking the contents of -/proc/ioports and /proc/interrupts can be useful to see if you're -butting heads with another device. - -If you still cannot get the module to load, look at the contents of -your system log file, usually /var/log/messages. If you see the -message "opl3sa2: Unknown Yamaha audio controller version", then you -have a different chipset version than I've encountered so far. Look -for all messages in the log file that start with "opl3sa2: " and see -if they provide any clues. If you do not see the chipset version -message, and none of the other messages present in the system log are -helpful, email me some details and I'll try my best to help. - - -Automatic Module Loading ------------------------- - -Lastly, if you're using modules and want to set up automatic module -loading with kmod, the kernel module loader, here is the section I -currently use in my modules.conf file: - -# Sound -alias sound-slot-0 opl3sa2 -options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3 -options opl3 io=0x388 - -That's all it currently takes to get an OPL3-SA3 card working on my -system. Once again, if you have any other problems, email me at the -address listed above. - -Scott diff -urN linux-2.5.6-pre3/Documentation/sound/Opti linux-2.5.6/Documentation/sound/Opti --- linux-2.5.6-pre3/Documentation/sound/Opti Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/Documentation/sound/Opti Wed Dec 31 16:00:00 1969 @@ -1,222 +0,0 @@ -Support for the OPTi 82C931 chip --------------------------------- -Note: parts of this README file apply also to other -cards that use the mad16 driver. - -Some items in this README file are based on features -added to the sound driver after Linux-2.1.91 was out. -By the time of writing this I do not know which official -kernel release will include these features. -Please do not report inconsistencies on older Linux -kernels. - -The OPTi 82C931 is supported in its non-PnP mode. -Usually you do not need to set jumpers, etc. The sound driver -will check the card status and if it is required it will -force the card into a mode in which it can be programmed. - -If you have another OS installed on your computer it is recommended -that Linux and the other OS use the same resources. - -Also, it is recommended that resources specified in /etc/modules.conf -and resources specified in /etc/isapnp.conf agree. - -Compiling the sound driver --------------------------- -I highly recommend that you build a modularized sound driver. -This document does not cover a sound-driver which is built in -the kernel. - -Sound card support should be enabled as a module (chose m). -Answer 'm' for these items: - Generic OPL2/OPL3 FM synthesizer support (CONFIG_SOUND_ADLIB) - Microsoft Sound System support (CONFIG_SOUND_MSS) - Support for OPTi MAD16 and/or Mozart based cards (CONFIG_SOUND_MAD16) - FM synthesizer (YM3812/OPL-3) support (CONFIG_SOUND_YM3812) - -The configuration menu may ask for addresses, IRQ lines or DMA -channels. If the card is used as a module the module loading -options will override these values. - -For the OPTi 931 you can answer 'n' to: - Support MIDI in older MAD16 based cards (requires SB) (CONFIG_SOUND_MAD16_OLDCARD) -If you do need MIDI support in a Mozart or C928 based card you -need to answer 'm' to the above question. In that case you will -also need to answer 'm' to: - '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SOUND_SB) - -Go on and compile your kernel and modules. Install the modules. Run depmod -a. - -Using isapnptools ------------------ -In most systems with a PnP BIOS you do not need to use isapnp. The -initialization provided by the BIOS is sufficient for the driver -to pick up the card and continue initialization. - -If that fails, or if you have other PnP cards, you need to use isapnp -to initialize the card. -This was tested with isapnptools-1.11 but I recommend that you use -isapnptools-1.13 (or newer). Run pnpdump to dump the information -about your PnP cards. Then edit the resulting file and select -the options of your choice. This file is normally installed as -/etc/isapnp.conf. - -The driver has one limitation with respect to I/O port resources: -IO3 base must be 0x0E0C. Although isapnp allows other ports, this -address is hard-coded into the driver. - -Using kmod and autoloading the sound driver -------------------------------------------- -Comment: as of linux-2.1.90 kmod is replacing kerneld. -The config file '/etc/modules.conf' is used as before. - -This is the sound part of my /etc/modules.conf file. -Following that I will explain each line. - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 -options sb mad16=1 -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 -post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -If you have an MPU daughtercard or onboard MPU you will want to add to the -"options mad16" line - eg - -options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 - -To set the I/O and IRQ of the MPU. - - -Explain: - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 - -When any sound device is opened the kernel requests auto-loading -of char-major-14. There is a built-in alias that translates this -request to loading the main sound module. - -The sound module in its turn will request loading of a sub-driver -for mixer, audio, midi or synthesizer device. The first 3 are -supported by the mad16 driver. The synth device is supported -by the opl3 driver. - -There is currently no way to autoload the sound device driver -if more than one card is installed. - -options sb mad16=1 - -This is left for historical reasons. If you enable the -config option 'Support MIDI in older MAD16 based cards (requires SB)' -or if you use an older mad16 driver it will force loading of the -SoundBlaster driver. This option tells the SB driver not to look -for a SB card but to wait for the mad16 driver. - -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 - -post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -This sets resources and options for the mad16 and opl3 drivers. -I use two DMA channels (only one is required) to enable full duplex. -joystick=1 enables the joystick port. cdtype=0 disables the cd port. -You can also set mpu_io and mpu_irq in the mad16 options for the -uart401 driver. - -This tells modprobe to run /sbin/ad1848_mixer_reroute after -mad16 is successfully loaded and initialized. The source -for ad1848_mixer_reroute is appended to the end of this readme -file. It is impossible for the sound driver to know the actual -connections to the mixer. The 3 inputs intended for cd, synth -and line-in are mapped to the generic inputs line1, line2 and -line3. This program reroutes these mixer channels to their -right names (note the right mapping depends on the actual sound -card that you use). -The numeric parameters mean: - 14=line1 8=cd - reroute line1 to the CD input. - 15=line2 3=synth - reroute line2 to the synthesizer input. - 16=line3 6=line - reroute line3 to the line input. -For reference on other input names look at the file -/usr/include/linux/soundcard.h. - -Using a joystick ------------------ -You must enable a joystick in the mad16 options. (also -in /etc/isapnp.conf if you use it). -Tested with regular analog joysticks. - -A CDROM drive connected to the sound card ------------------------------------------ -The 82C931 chip has support only for secondary ATAPI cdrom. -(cdtype=8). Loading the mad16 driver resets the C931 chip -and if a cdrom was already mounted it may cause a complete -system hang. Do not use the sound card if you have an alternative. -If you do use the sound card it is important that you load -the mad16 driver (use "modprobe mad16" to prevent auto-unloading) -before the cdrom is accessed the first time. - -Using the sound driver built-in to the kernel may help here, but... -Most new systems have a PnP BIOS and also two IDE controllers. -The IDE controller on the sound card may be needed only on older -systems (which have only one IDE controller) but these systems -also do not have a PnP BIOS - requiring isapnptools and a modularized -driver. - -Known problems --------------- -1. See the section on "A CDROM drive connected to the sound card". - -2. On my system the codec cannot capture companded sound samples. - (eg., recording from /dev/audio). When any companded capture is - requested I get stereo-16 bit samples instead. Playback of - companded samples works well. Apparently this problem is not common - to all C931 based cards. I do not know how to identify cards that - have this problem. - -Source for ad1848_mixer_reroute.c ---------------------------------- -#include -#include -#include - -static char *mixer_names[SOUND_MIXER_NRDEVICES] = - SOUND_DEVICE_LABELS; - -int -main(int argc, char **argv) { - int val, from, to; - int i, fd; - - fd = open("/dev/mixer", O_RDWR); - if(fd < 0) { - perror("/dev/mixer"); - return 1; - } - - for(i = 2; i < argc; i += 2) { - from = atoi(argv[i-1]); - to = atoi(argv[i]); - - if(to == SOUND_MIXER_NONE) - fprintf(stderr, "%s: turning off mixer %s\n", - argv[0], mixer_names[to]); - else - fprintf(stderr, "%s: rerouting mixer %s to %s\n", - argv[0], mixer_names[from], mixer_names[to]); - - val = from << 8 | to; - - if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { - perror("AD1848 mixer reroute"); - return 1; - } - } - - return 0; -} - diff -urN linux-2.5.6-pre3/Documentation/sound/PAS16 linux-2.5.6/Documentation/sound/PAS16 --- linux-2.5.6-pre3/Documentation/sound/PAS16 Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/Documentation/sound/PAS16 Wed Dec 31 16:00:00 1969 @@ -1,163 +0,0 @@ -Pro Audio Spectrum 16 for 2.3.99 and later -========================================= -by Thomas Molina (tmolina@home.com) -last modified 3 Mar 2001 -Acknowledgement to Axel Boldt (boldt@math.ucsb.edu) for stuff taken -from Configure.help, Riccardo Facchetti for stuff from README.OSS, -and others whose names I could not find. - -This documentation is relevant for the PAS16 driver (pas2_card.c and -friends) under kernel version 2.3.99 and later. If you are -unfamiliar with configuring sound under Linux, please read the -Sound-HOWTO, linux/Documentation/sound/Introduction and other -relevant docs first. - -The following information is relevant information from README.OSS -and legacy docs for the Pro Audio Spectrum 16 (PAS16): -================================================================== - -The pas2_card.c driver supports the following cards -- -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the question about (PAS16) if you don't really have a PAS16. Sound -configuration may also be made modular by answering m to configuration -options presented. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - The PAS16 has an OPL3-compatible FM chip. - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - - -The new stuff for 2.3.99 and later -============================================================================ -The following configuration options from linux/Documentation/Configure.help -are relevant to configuring the PAS16: - -Sound card support -CONFIG_SOUND - If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information - about your sound card and its configuration down (I/O port, - interrupt and DMA channel), because you will be asked for it. - - You want to read the Sound-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . General information - about the modular sound system is contained in the files - Documentation/sound/Introduction. The file - Documentation/sound/README.OSS contains some slightly outdated but - still useful information as well. - -OSS sound modules -CONFIG_SOUND_OSS - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or M - here (the module will be called sound.o) if you haven't found a - driver for your sound card above, then pick your driver from the - list below. - -Persistent DMA buffers -CONFIG_SOUND_DMAP - Linux can often have problems allocating DMA buffers for ISA sound - cards on machines with more than 16MB of RAM. This is because ISA - DMA buffers must exist below the 16MB boundary and it is quite - possible that a large enough free block in this region cannot be - found after the machine has been running for a while. If you say Y - here the DMA buffers (64Kb) will be allocated at boot time and kept - until the shutdown. This option is only useful if you said Y to - "OSS sound modules", above. If you said M to "OSS sound modules" - then you can get the persistent DMA buffer functionality by passing - the command-line argument "dmabuf=1" to the sound.o module. - - Say y here for PAS16. - -ProAudioSpectrum 16 support -CONFIG_SOUND_PAS - Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have - some other card made by Media Vision or Logitech since they are not - PAS16 compatible. It is not necessary to enable the separate - Sound Blaster support; it is included in the PAS driver. - - If you compile the driver into the kernel, you have to add - "pas2=,,,,,,, - to the kernel command line. - -FM Synthesizer (YM3812/OPL-3) support -CONFIG_SOUND_YM3812 - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. - If you compile the driver into the kernel, you have to add - "opl3=" to the kernel command line. - - If you compile your drivers into the kernel, you MUST configure - OPL3 support as a module for PAS16 support to work properly. - You can then get OPL3 functionality by issuing the command: - insmod opl3 - In addition, you must either add the following line to - /etc/modules.conf: - options opl3 io=0x388 - or else add the following line to /etc/lilo.conf: - opl3=0x388 - - -EXAMPLES -=================================================================== -To use the PAS16 in my computer I have enabled the following sound -configuration options: - -CONFIG_SOUND=y -CONFIG_SOUND_OSS=y -CONFIG_SOUND_TRACEINIT=y -CONFIG_SOUND_DMAP=y -CONFIG_SOUND_PAS=y -CONFIG_SOUND_SB=n -CONFIG_SOUND_YM3812=m - -I have also included the following append line in /etc/lilo.conf: -append="pas2=0x388,10,3,-1,0x220,5,1,-1 sb=0x220,5,1,-1 opl3=0x388" - -The io address of 0x388 is default configuration on the PAS16. The -irq of 10 and dma of 3 may not match your installation. The above -configuration enables PAS16, 8-bit Soundblaster and OPL3 -functionality. If Soundblaster functionality is not desired, the -following line would be appropriate: -append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388" - -If sound is built totally modular, the above options may be -specified in /etc/modules.conf for pas2.o, sb.o and opl3.o -respectively. diff -urN linux-2.5.6-pre3/Documentation/sound/PSS linux-2.5.6/Documentation/sound/PSS --- linux-2.5.6-pre3/Documentation/sound/PSS Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/Documentation/sound/PSS Wed Dec 31 16:00:00 1969 @@ -1,41 +0,0 @@ -The PSS cards and other ECHO based cards provide an onboard DSP with -downloadable programs and also has an AD1848 "Microsoft Sound System" -device. The PSS driver enables MSS and MPU401 modes of the card. SB -is not enabled since it doesn't work concurrently with MSS. - -If you build this driver as a module then the driver takes the following -parameters - -pss_io. The I/O base the PSS card is configured at (normally 0x220 - or 0x240) - -mss_io The base address of the Microsoft Sound System interface. - This is normally 0x530, but may be 0x604 or other addresses. - -mss_irq The interrupt assigned to the Microsoft Sound System - emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you - get IRQ errors be sure to check the interrupt is set to - "ISA/Legacy" in the BIOS on modern machines. - -mss_dma The DMA channel used by the Microsoft Sound System. - This can be 0, 1, or 3. DMA 0 is not available on older - machines and will cause a crash on them. - -mpu_io The MPU emulation base address. This sets the base of the - synthesizer. It is typically 0x330 but can be altered. - -mpu_irq The interrupt to use for the synthesizer. It must differ - from the IRQ used by the Microsoft Sound System port. - - -The mpu_io/mpu_irq fields are optional. If they are not specified the -synthesizer parts are not configured. - -When the module is loaded it looks for a file called -/etc/sound/pss_synth. This is the firmware file from the DOS install disks. -This fil holds a general MIDI emulation. The file expected is called -genmidi.ld on newer DOS driver install disks and synth.ld on older ones. - -You can also load alternative DSP algorithms into the card if you wish. One -alternative driver can be found at http://www.mpg123.de/ - diff -urN linux-2.5.6-pre3/Documentation/sound/PSS-updates linux-2.5.6/Documentation/sound/PSS-updates --- linux-2.5.6-pre3/Documentation/sound/PSS-updates Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/Documentation/sound/PSS-updates Wed Dec 31 16:00:00 1969 @@ -1,88 +0,0 @@ - This file contains notes for users of PSS sound cards who wish to use the -newly added features of the newest version of this driver. - - The major enhancements present in this new revision of this driver is the -addition of two new module parameters that allow you to take full advantage of -all the features present on your PSS sound card. These features include the -ability to enable both the builtin CDROM and joystick ports. - -pss_enable_joystick - - This parameter is basically a flag. A 0 will leave the joystick port -disabled, while a non-zero value would enable the joystick port. The default -setting is pss_enable_joystick=0 as this keeps this driver fully compatable -with systems that were using previous versions of this driver. If you wish to -enable the joystick port you will have to add pss_enable_joystick=1 as an -argument to the driver. To actually use the joystick port you will then have -to load the joystick driver itself. Just remember to load the joystick driver -AFTER the pss sound driver. - -pss_cdrom_port - - This parameter takes a port address as its parameter. Any available port -address can be specified to enable the CDROM port, except for 0x0 and -1 as -these values would leave the port disabled. Like the joystick port, the cdrom -port will require that an appropiate CDROM driver be loaded before you can make -use of the newly enabled CDROM port. Like the joystick port option above, -remember to load the CDROM driver AFTER the pss sound driver. While it may -differ on some PSS sound cards, all the PSS sound cards that I have seen have a -builtin Wearnes CDROM port. If this is the case with your PSS sound card you -should load aztcd with the appropiate port option that matches the port you -assigned to the CDROM port when you loaded your pss sound driver. (ex. -modprobe pss pss_cdrom_port=0x340 && modprobe aztcd aztcd=0x340) The default -setting of this parameter leaves the CDROM port disabled to maintain full -compatability with systems using previous versions of this driver. - - Other options have also been added for the added convenience and utility -of the user. These options are only available if this driver is loaded as a -module. - -pss_no_sound - - This module parameter is a flag that can be used to tell the driver to -just configure non-sound components. 0 configures all components, a non-0 -value will only attept to configure the CDROM and joystick ports. This -parameter can be used by a user who only wished to use the builtin joystick -and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this -parameter and with the paramter below set to true then a user can safely unload -this driver with the following command "rmmod pss && rmmod ad1848 && rmmod -mpu401 && rmmod sound && rmmod soundcore" and retain the full functionality of -his CDROM and/or joystick port(s) while gaining back the memory previously used -by the sound drivers. This default setting of this parameter is 0 to retain -full behavioral compatability with previous versions of this driver. - -pss_keep_settings - - This parameter can be used to specify whether you want the driver to reset -all emulations whenever its unloaded. This can be useful for those who are -sharing resources (io ports, IRQ's, DMA's) between different ISA cards. This -flag can also be useful in that future versions of this driver may reset all -emulations by default on the driver's unloading (as it probably should), so -specifying it now will ensure that all future versions of this driver will -continue to work as expected. The default value of this parameter is 1 to -retain full behavioral compatability with previous versions of this driver. - -pss_firmware - - This parameter can be used to specify the file containing the firmware -code so that a user could tell the driver where that file is located instead -of having to put it in a predefined location with a predefined name. The -default setting of this parameter is "/etc/sound/pss_synth" as this was the -path and filename the hardcoded value in the previous versions of this driver. - -Examples: - -# Normal PSS sound card system, loading of drivers. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 mpu_io=0x338 mpu_irq=9 mss_io=0x530 mss_irq=10 mss_dma=1 pss_cdrom_port=0x340 pss_enable_joystick=1 -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick - -# System using the PSS sound card just for its CDROM and joystick ports. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 pss_cdrom_port=0x340 pss_enable_joystick=1 pss_no_sound=1 -/sbin/rmmod pss && /sbin/rmmod ad1848 && /sbin/rmmod mpu401 && /sbin/rmmod sound && /sbin/rmmod soundcore # This line not needed, but saves memory. -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick diff -urN linux-2.5.6-pre3/Documentation/sound/README.OSS linux-2.5.6/Documentation/sound/README.OSS --- linux-2.5.6-pre3/Documentation/sound/README.OSS Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/Documentation/sound/README.OSS Wed Dec 31 16:00:00 1969 @@ -1,1456 +0,0 @@ -Introduction ------------- - -This file is a collection of all the old Readme files distributed with -OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded -on it I think these information may still be interesting for users that -have to configure their sound system. - -Be warned: Alan Cox is the current maintainer of the Linux sound driver so if -you have problems with it, please contact him or the current device-specific -driver maintainer (e.g. for aedsp16 specific problems contact me). If you have -patches, contributions or suggestions send them to Alan: I'm sure they are -welcome. - -In this document you will find a lot of references about OSS/Lite or ossfree: -they are gone forever. Keeping this in mind and with a grain of salt this -document can be still interesting and very helpful. - -[ File edited 17.01.1999 - Riccardo Facchetti ] -[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] - -OSS/Free version 3.8 release notes ----------------------------------- - -Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP -sites). It gives instructions about using sound with Linux. It's bit out of -date but still very useful. Information about bug fixes and such things -is available from the web page (see above). - -Please check http://www.opensound.com/pguide for more info about programming -with OSS API. - - ==================================================== -- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. - ==================================================== - -Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" -contain useful utilities to be used with this driver. -See http://www.opensound.com/ossfree/getting.html for -download instructions. - -If you are looking for the installation instructions, please -look forward into this document. - -Supported sound cards ---------------------- - -See below. - -Contributors ------------- - -This driver contains code by several contributors. In addition several other -persons have given useful suggestions. The following is a list of major -contributors. (I could have forgotten some names.) - - Craig Metz 1/2 of the PAS16 Mixer and PCM support - Rob Hooft Volume computation algorithm for the FM synth. - Mika Liljeberg uLaw encoding and decoding routines - Jeff Tranter Linux SOUND HOWTO document - Greg Lee Volume computation algorithm for the GUS and - lots of valuable suggestions. - Andy Warner ISC port - Jim Lowe, - Amancio Hasty Jr FreeBSD/NetBSD port - Anders Baekgaard Bug hunting and valuable suggestions. - Joerg Schubert SB16 DSP support (initial version). - Andrew Robinson Improvements to the GUS driver - Megens SA MIDI recording for SB and SB Pro (initial version). - Mikael Nordqvist Linear volume support for GUS and - nonblocking /dev/sequencer. - Ian Hartas SVR4.2 port - Markus Aroharju and - Risto Kankkunen Major contributions to the mixer support - of GUS v3.7. - Hunyue Yau Mixer support for SG NX Pro. - Marc Hoffman PSS support (initial version). - Rainer Vranken Initialization for Jazz16 (initial version). - Peter Trattler Initial version of loadable module support for Linux. - JRA Gibson 16 bit mode for Jazz16 (initial version) - Davor Jadrijevic MAD16 support (initial version) - Gregor Hoffleit Mozart support (initial version) - Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support - James Hightower Spotting a tiny but important bug in CS423x support. - Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) - Tim MacKenzie Full duplex support for OPTi 82C930. - - Please look at lowlevel/README for more contributors. - -There are probably many other names missing. If you have sent me some -patches and your name is not in the above list, please inform me. - -Sending your contributions or patches -------------------------------------- - -First of all it's highly recommended to contact me before sending anything -or before even starting to do any work. Tell me what you suggest to be -changed or what you have planned to do. Also ensure you are using the -very latest (development) version of OSS/Free since the change may already be -implemented there. In general it's a major waste of time to try to improve a -several months old version. Information about the latest version can be found -from http://www.opensound.com/ossfree. In general there is no point in -sending me patches relative to production kernels. - -Sponsors etc. -------------- - -The following companies have greatly helped development of this driver -in form of a free copy of their product: - -Novell, Inc. UnixWare personal edition + SDK -The Santa Cruz Operation, Inc. A SCO OpenServer + SDK -Ensoniq Corp, a SoundScape card and extensive amount of assistance -MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK -Acer, Inc. a pair of AcerMagic S23 cards. - -In addition the following companies have provided me sufficient amount -of technical information at least some of their products (free or $$$): - -Advanced Gravis Computer Technology Ltd. -Media Vision Inc. -Analog Devices Inc. -Logitech Inc. -Aztech Labs Inc. -Crystal Semiconductor Corporation, -Integrated Circuit Systems Inc. -OAK Technology -OPTi -Turtle Beach -miro -Ad Lib Inc. ($$) -Music Quest Inc. ($$) -Creative Labs ($$$) - -If you have some problems -========================= - -Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). -Also look at the home page (http://www.opensound.com/ossfree). It may -contain info about some recent bug fixes. - -It's likely that you have some problems when trying to use the sound driver -first time. Sound cards don't have standard configuration so there are no -good default configuration to use. Please try to use same I/O, DMA and IRQ -values for the sound card than with DOS. - -If you get an error message when trying to use the driver, please look -at /var/adm/messages for more verbose error message. - - -The following errors are likely with /dev/dsp and /dev/audio. - - - "No such device or address". - This error indicates that there are no suitable hardware for the - device file or the sound driver has been compiled without support for - this particular device. For example /dev/audio and /dev/dsp will not - work if "digitized voice support" was not enabled during "make config". - - - "Device or resource busy". Probably the IRQ (or DMA) channel - required by the sound card is in use by some other device/driver. - - - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. - Look at the kernel messages in /var/adm/notice for more info. - - - "Invalid argument". The application is calling ioctl() - with impossible parameters. Check that the application is - for sound driver version 2.X or later. - -Linux installation -================== - -IMPORTANT! Read this if you are installing a separately - distributed version of this driver. - - Check that your kernel version works with this - release of the driver (see Readme). Also verify - that your current kernel version doesn't have more - recent sound driver version than this one. IT'S HIGHLY - RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT - IS DISTRIBUTED WITH KERNEL SOURCES. - -- When installing separately distributed sound driver you should first - read the above notice. Then try to find proper directory where and how - to install the driver sources. You should not try to install a separately - distributed driver version if you are not able to find the proper way - yourself (in this case use the version that is distributed with kernel - sources). Remove old version of linux/drivers/sound directory before - installing new files. - -- To build the device files you need to run the enclosed shell script - (see below). You need to do this only when installing sound driver - first time or when upgrading to much recent version than the earlier - one. - -- Configure and compile Linux as normally (remember to include the - sound support during "make config"). Please refer to kernel documentation - for instructions about configuring and compiling kernel. File Readme.cards - contains card specific instructions for configuring this driver for - use with various sound cards. - -Boot time configuration (using lilo and insmod) ------------------------------------------------ - -This information has been removed. Too many users didn't believe -that it's really not necessary to use this method. Please look at -Readme of sound driver version 3.0.1 if you still want to use this method. - -Problems --------- - -Common error messages: - -- /dev/???????: No such file or directory. -Run the script at the end of this file. - -- /dev/???????: No such device. -You are not running kernel which contains the sound driver. When using -modularized sound driver this error means that the sound driver is not -loaded. - -- /dev/????: No such device or address. -Sound driver didn't detect suitable card when initializing. Please look at -Readme.cards for info about configuring the driver with your card. Also -check for possible boot (insmod) time error messages in /var/adm/messages. - -- Other messages or problems -Please check http://www.opensound.com/ossfree for more info. - -Configuring version 3.8 (for Linux) with some common sound cards -================================================================ - -This document describes configuring sound cards with the freeware version of -Open Sound Systems (OSS/Free). Information about the commercial version -(OSS/Linux) and its configuration is available from -http://www.opensound.com/linux.html. Information presented here is -not valid for OSS/Linux. - -If you are unsure about how to configure OSS/Free -you can download the free evaluation version of OSS/Linux from the above -address. There is a chance that it can autodetect your sound card. In this case -you can use the information included in soundon.log when configuring OSS/Free. - - -IMPORTANT! This document covers only cards that were "known" when - this driver version was released. Please look at - http://www.opensound.com/ossfree for info about - cards introduced recently. - - When configuring the sound driver, you should carefully - check each sound configuration option (particularly - "Support for /dev/dsp and /dev/audio"). The default values - offered by these programs are not necessarily valid. - - -THE BIGGEST MISTAKES YOU CAN MAKE -================================= - -1. Assuming that the card is Sound Blaster compatible when it's not. --------------------------------------------------------------------- - -The number one mistake is to assume that your card is compatible with -Sound Blaster. Only the cards made by Creative Technology or which have -one or more chips labeled by Creative are SB compatible. In addition there -are few sound chipsets which are SB compatible in Linux such as ESS1688 or -Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything -in Linux. - -IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF -THIS CHAPTER. - -For most other "supposed to be SB compatible" cards you have to use other -than SB drivers (see below). It is possible to get most sound cards to work -in SB mode but in general it's a complete waste of time. There are several -problems which you will encounter by using SB mode with cards that are not -truly SB compatible: - -- The SB emulation is at most SB Pro (DSP version 3.x) which means that -you get only 8 bit audio (there is always an another ("native") mode which -gives the 16 bit capability). The 8 bit only operation is the reason why -many users claim that sound quality in Linux is much worse than in DOS. -In addition some applications require 16 bit mode and they produce just -noise with a 8 bit only device. -- The card may work only in some cases but refuse to work most of the -time. The SB compatible mode always requires special initialization which is -done by the DOS/Windows drivers. This kind of cards work in Linux after -you have warm booted it after DOS but they don't work after cold boot -(power on or reset). -- You get the famous "DMA timed out" messages. Usually all SB clones have -software selectable IRQ and DMA settings. If the (power on default) values -currently used by the card don't match configuration of the driver you will -get the above error message whenever you try to record or play. There are -few other reasons to the DMA timeout message but using the SB mode seems -to be the most common cause. - -2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card --------------------------------------------------------------------------- - -Plug & Play is a protocol defined by Intel and Microsoft. It lets operating -systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA -cards. The problem with PnP cards is that the standard Linux doesn't currently -(versions 2.1.x and earlier) don't support PnP. This means that you will have -to use some special tricks (see later) to get a PnP card alive. Many PnP cards -work after they have been initialized but this is not always the case. - -There are sometimes both PnP and non-PnP versions of the same sound card. -The non-PnP version is the original model which usually has been discontinued -more than an year ago. The PnP version has the same name but with "PnP" -appended to it (sometimes not). This causes major confusion since the non-PnP -model works with Linux but the PnP one doesn't. - -You should carefully check if "Plug & Play" or "PnP" is mentioned in the name -of the card or in the documentation or package that came with the card. -Everything described in the rest of this document is not necessarily valid for -PnP models of sound cards even you have managed to wake up the card properly. -Many PnP cards are simply too different from their non-PnP ancestors which are -covered by this document. - - -Cards that are not (fully) supported by this driver -=================================================== - -See http://www.opensound.com/ossfree for information about sound cards -to be supported in future. - - -How to use sound without recompiling kernel and/or sound driver -=============================================================== - -There is a commercial sound driver which comes in precompiled form and doesn't -require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for -more info. - - -Configuring PnP cards -===================== - -New versions of most sound cards use the so-called ISA PnP protocol for -soft configuring their I/O, IRQ, DMA and shared memory resources. -Currently at least cards made by Creative Technology (SB32 and SB32AWE -PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and -Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio -chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other -motherboards) is also based on PnP technology but there is a "native" driver -available for it (see information about CS4232 later in this document). - -PnP sound cards (as well as most other PnP ISA cards) are not supported -by this version of the driver . Proper -support for them should be released during 97 once the kernel level -PnP support is available. - -There is a method to get most of the PnP cards to work. The basic method -is the following: - -1) Boot DOS so the card's DOS drivers have a chance to initialize it. -2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del -works with older machines but causes a hard reset of all cards on recent -(Pentium) machines. -3) If you have the sound driver in Linux configured properly, the card should -work now. "Proper" means that I/O, IRQ and DMA settings are the same as in -DOS. The hard part is to find which settings were used. See the documentation of -your card for more info. - -Windows 95 could work as well as DOS but running loadlin may be difficult. -Probably you should "shut down" your machine to MS-DOS mode before running it. - -Some machines have a BIOS utility for setting PnP resources. This is a good -way to configure some cards. In this case you don't need to boot DOS/Win95 -before starting Linux. - -Another way to initialize PnP cards without DOS/Win95 is a Linux based -PnP isolation tool. When writing this there is a pre alpha test version -of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The -file is called isapnptools-*. Please note that this tool is just a temporary -solution which may be incompatible with future kernel versions having proper -support for PnP cards. There are bugs in setting DMA channels in earlier -versions of isapnptools so at least version 1.6 is required with sound cards. - -Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See -http://www.opensound.com/linux.html for more info. This is probably the way you -should do it if you don't want to spend time recompiling the kernel and -required tools. - - -Read this before trying to configure the driver -=============================================== - -There are currently many cards that work with this driver. Some of the cards -have native support while others work since they emulate some other -card (usually SB, MSS/WSS and/or MPU401). The following cards have native -support in the driver. Detailed instructions for configuring these cards -will be given later in this document. - -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -Media Vision Jazz16 based cards - Pro Sonic 16 - Logitech SoundMan Wave - (Other Jazz based cards should work but I don't have any reports - about them). - -Sound Blasters - SB 1.0 to 2.0 - SB Pro - SB 16 - SB32/64/AWE - Configure SB32/64/AWE just like SB16. See lowlevel/README.awe - for information about using the wave table synth. - NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated - using isapnptools before they work with OSS/Free. - SB16 compatible cards by other manufacturers than Creative. - You have been fooled since there are _no_ SB16 compatible - cards on the market (as of May 1997). It's likely that your card - is compatible just with SB Pro but there is also a non-SB- - compatible 16 bit mode. Usually it's MSS/WSS but it could also - be a proprietary one like MV Jazz16 or ESS ES688. OPTi - MAD16 chips are very common in so called "SB 16 bit cards" - (try with the MAD16 driver). - - ====================================================================== - "Supposed to be SB compatible" cards. - Forget the SB compatibility and check for other alternatives - first. The only cards that work with the SB driver in - Linux have been made by Creative Technology (there is at least - one chip on the card with "CREATIVE" printed on it). The - only other SB compatible chips are ESS and Jazz16 chips - (maybe ALSxxx chips too but they probably don't work). - Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or - "Crystal" are _NOT_ SB compatible in Linux. - - Practically all sound cards have some kind of SB emulation mode - in addition to their native (16 bit) mode. In most cases this - (8 bit only) SB compatible mode doesn't work with Linux. If - you get it working it may cause problems with games and - applications which require 16 bit audio. Some 16 bit only - applications don't check if the card actually supports 16 bits. - They just dump 16 bit data to a 8 bit card which produces just - noise. - - In most cases the 16 bit native mode is supported by Linux. - Use the SB mode with "clones" only if you don't find anything - better from the rest of this doc. - ====================================================================== - -Gravis Ultrasound (GUS) - GUS - GUS + the 16 bit option - GUS MAX - GUS ACE (No MIDI port and audio recording) - GUS PnP (with RAM) - -MPU-401 and compatibles - The driver works both with the full (intelligent mode) MPU-401 - cards (such as MPU IPC-T and MQX-32M) and with the UART only - dumb MIDI ports. MPU-401 is currently the most common MIDI - interface. Most sound cards are compatible with it. However, - don't enable MPU401 mode blindly. Many cards with native support - in the driver have their own MPU401 driver. Enabling the standard one - will cause a conflict with these cards. So check if your card is - in the list of supported cards before enabling MPU401. - -Windows Sound System (MSS/WSS) - Even when Microsoft has discontinued their own Sound System card - they managed to make it a standard. MSS compatible cards are based on - a codec chip which is easily available from at least two manufacturers - (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). - Currently most sound cards are based on one of the MSS compatible codec - chips. The CS4231 is used in the high quality cards such as GUS MAX, - MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). - - Having a AD1848, CS4248 or CS4231 codec chip on the card is a good - sign. Even if the card is not MSS compatible, it could be easy to write - support for it. Note also that most MSS compatible cards - require special boot time initialization which may not be present - in the driver. Also, some MSS compatible cards have native support. - Enabling the MSS support with these cards is likely to - cause a conflict. So check if your card is listed in this file before - enabling the MSS support. - -Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) - Most sound cards have a FM synthesizer chip. The OPL2 is a 2 - operator chip used in the original AdLib card. Currently it's used - only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator - FM chip which provides better sound quality and/or more available - voices than the OPL2. The OPL4 is a new chip that has an OPL3 and - a wave table synthesizer packed onto the same chip. The driver supports - just the OPL3 mode directly. Most cards with an OPL4 (like - SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401 - emulation. Writing a native OPL4 support is difficult - since Yamaha doesn't give information about their sample ROM chip. - - Enable the generic OPL2/OPL3 FM synthesizer support if your - card has a FM chip made by Yamaha. Don't enable it if your card - has a software (TRS) based FM emulator. - - ---------------------------------------------------------------- - NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition - to the FM synth this chip has also digital audio (WSS) and - MIDI (MPU401) capabilities. Support for OPL3-SA is described below. - ---------------------------------------------------------------- - -Yamaha OPL3-SA1 - - Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some - (Intel) motherboards and on cheap sound cards. It should not be - confused with the original OPL3 chip (YMF278) which is entirely - different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro - (not used in OSS/Free) in addition to the OPL3 FM synth. - - There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They - are PnP chips and will not work with the OPL3-SA1 driver. You should - use the standard MSS, MPU401 and OPL3 options with these chips and to - activate the card using isapnptools. - -4Front Technologies SoftOSS - - SoftOSS is a software based wave table emulation which works with - any 16 bit stereo sound card. Due to its nature a fast CPU is - required (P133 is minimum). Although SoftOSS does _not_ use MMX - instructions it has proven out that recent processors (which appear - to have MMX) perform significantly better with SoftOSS than earlier - ones. For example a P166MMX beats a PPro200. SoftOSS should not be used - on 486 or 386 machines. - - The amount of CPU load caused by SoftOSS can be controlled by - selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES - parameters properly (they will be prompted by make config). It's - recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a - P166MMX or faster (PPro200 is not faster) you can set - CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it - recommended to use sampling rates around 22050 or even 16000 kHz. - Selecting too high values for these parameters may hang your - system when playing MIDI files with hight degree of polyphony - (number of concurrently playing notes). It's also possible to - decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use - higher sampling rates. However using fewer voices decreases - playback quality more than decreasing the sampling rate. - - SoftOSS keeps the samples loaded on the system's RAM so much RAM is - required. SoftOSS should never be used on machines with less than 16 MB - of RAM since this is potentially dangerous (you may accidentally run out - of memory which probably crashes the machine). - - SoftOSS implements the wave table API originally designed for GUS. For - this reason all applications designed for GUS should work (at least - after minor modifications). For example gmod/xgmod and playmidi -g are - known to work. - - To work SoftOSS will require GUS compatible - patch files to be installed on the system (in /dos/ultrasnd/midi). You - can use the public domain MIDIA patchset available from several ftp - sites. - - ********************************************************************* - IMPORTANT NOTICE! The original patch set distributed with the Gravis - Ultrasound card is not in public domain (even though it's available from - some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) - if you like to use these patches with SoftOSS included in OSS/Free. - ********************************************************************* - -PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) - Analog Devices and Echo Speech have together defined a sound card - architecture based on the above chips. The DSP chip is used - for emulation of SB Pro, FM and General MIDI/MT32. - - There are several cards based on this architecture. The most known - ones are Orchid SW32 and Cardinal DSP16. - - The driver supports downloading DSP algorithms to these cards. - - NOTE! You will have to use the "old" config script when configuring - PSS cards. - -MediaTrix AudioTrix Pro - The ATP card is built around a CS4231 codec and an OPL4 synthesizer - chips. The OPL4 mode is supported by a microcontroller running a - General MIDI emulator. There is also a SB 1.5 compatible playback mode. - -Ensoniq SoundScape and compatibles - Ensoniq has designed a sound card architecture based on the - OTTO synthesizer chip used in their professional MIDI synthesizers. - Several companies (including Ensoniq, Reveal and Spea) are selling - cards based on this architecture. - - NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and - VIVO90 cards are not compatible with Soundscapes so the Soundscape - driver will not work with them. You may want to use OSS/Linux with these - cards. - -OPTi MAD16 and Mozart based cards - The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), - OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface - chips are used in many different sound cards, including some - cards by Reveal miro and Turtle Beach (Tropez). The purpose of these - chips is to connect other audio components to the PC bus. The - interface chip performs address decoding for the other chips. - NOTE! Tropez Plus is not MAD16 but CS4232 based. - NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible - in the PnP mode. You will have to use them in MSS mode after having - initialized them using isapnptools or DOS. 82C931 probably requires - initialization using DOS/Windows (running isapnptools is not enough). - It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP - mode (provided that the card has a jumper for this). In non-PnP mode - 82C931 is compatible with 82C930 and should work with the MAD16 driver - (without need to use isapnptools or DOS to initialize it). All OPTi - chips are supported by OSS/Linux (both in PnP and non-PnP modes). - -Audio Excel DSP16 - Support for this card was written by Riccardo Faccetti - (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in - the lowlevel/ directory. To use it you should enable the - "Additional low level drivers" option. - -Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and - many PC motherboards (Compaq, HP, Intel, ...) - CS4232 is a PnP multimedia chip which contains a CS3231A codec, - SB and MPU401 emulations. There is support for OPL3 too. - Unfortunately the MPU401 mode doesn't work (I don't know how to - initialize it). CS4236 is an enhanced (compatible) version of CS4232. - NOTE! Don't ever try to use isapnptools with CS4232 since this will just - freeze your machine (due to chip bugs). If you have problems in getting - CS4232 working you could try initializing it with DOS (CS4232C.EXE) and - then booting Linux using loadlin. CS4232C.EXE loads a secret firmware - patch which is not documented by Crystal. - -Turtle Beach Maui and Tropez "classic" - This driver version supports sample, patch and program loading commands - described in the Maui/Tropez User's manual. - There is now full initialization support too. The audio side of - the Tropez is based on the MAD16 chip (see above). - NOTE! Tropez Plus is different card than Tropez "classic" and will not - work fully in Linux. You can get audio features working by configuring - the card as a CS4232 based card (above). - - -Jumpers and software configuration -================================== - -Some of the earliest sound cards were jumper configurable. You have to -configure the driver use I/O, IRQ and DMA settings -that match the jumpers. Just few 8 bit cards are fully jumper -configurable (SB 1.x/2.x, SB Pro and clones). -Some cards made by Aztech have an EEPROM which contains the -config info. These cards behave much like hardware jumpered cards. - -Most cards have jumper for the base I/O address but other parameters -are software configurable. Sometimes there are few other jumpers too. - -Latest cards are fully software configurable or they are PnP ISA -compatible. There are no jumpers on the board. - -The driver handles software configurable cards automatically. Just configure -the driver to use I/O, IRQ and DMA settings which are known to work. -You could usually use the same values than with DOS and/or Windows. -Using different settings is possible but not recommended since it may cause -some trouble (for example when warm booting from an OS to another or -when installing new hardware to the machine). - -Sound driver sets the soft configurable parameters of the card automatically -during boot. Usually you don't need to run any extra initialization -programs when booting Linux but there are some exceptions. See the -card-specific instructions below for more info. - -The drawback of software configuration is that the driver needs to know -how the card must be initialized. It cannot initialize unknown cards -even if they are otherwise compatible with some other cards (like SB, -MPU401 or Windows Sound System). - - -What if your card was not listed above? -======================================= - -The first thing to do is to look at the major IC chips on the card. -Many of the latest sound cards are based on some standard chips. If you -are lucky, all of them could be supported by the driver. The most common ones -are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures -listed above. Also look at the end of this file for list of unsupported -cards and the ones which could be supported later. - -The last resort is to send _exact_ name and model information of the card -to me together with a list of the major IC chips (manufactured, model) to -me. I could then try to check if your card looks like something familiar. - -There are many more cards in the world than listed above. The first thing to -do with these cards is to check if they emulate some other card or interface -such as SB, MSS and/or MPU401. In this case there is a chance to get the -card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del -and boot Linux without hard resetting the machine). In this method the -DOS based driver initializes the hardware to use known I/O, IRQ and DMA -settings. If sound driver is configured to use the same settings, everything -should work OK. - - -Configuring sound driver (with Linux) -===================================== - -The sound driver is currently distributed as part of the Linux kernel. The -files are in /usr/src/linux/drivers/sound/. - -**************************************************************************** -* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * -* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * -* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * -* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * -* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * -* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * -* INCOMPATIBILITY PROBLEMS. * -* * -* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * -* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * -* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * -* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * -* COMPILED. * -**************************************************************************** - -To configure the driver, run "make config" in the kernel source directory -(/usr/src/linux). Answer "y" or "m" to the question about Sound card support -(after the questions about mouse, CD-ROM, ftape, etc. support). Questions -about options for sound will then be asked. - -After configuring the kernel and sound driver, run "make dep" and compile -the kernel following instructions in the kernel README. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the first question (PAS16) if you don't really have a PAS16. Don't enable -more cards than you really need since they just consume memory. Also -some drivers (like MPU401) may conflict with your SCSI controller and -prevent kernel from booting. If you card was in the list of supported -cards (above), please look at the card specific config instructions -(later in this file) before starting to configure. Some cards must be -configured in way which is not obvious. - -So here is the beginning of the config dialog. Answer 'y' or 'n' to these -questions. The default answer is shown so that (y/n) means 'y' by default and -(n/y) means 'n'. To use the default value, just hit ENTER. But be careful -since using the default _doesn't_ guarantee anything. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - "Sound Blaster support", - - Answer 'y' if you have an original SB card made by Creative Labs - or a full 100% hardware compatible clone (like Thunderboard or - SM Games). If your card was in the list of supported cards (above), - please look at the card specific instructions later in this file - before answering this question. For an unknown card you may answer - 'y' if the card claims to be SB compatible. - Enable this option also with PAS16 (changed since v3.5-beta9). - - Don't enable SB if you have a MAD16 or Mozart compatible card. - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering 'y' is usually a safe and recommended choice. However some - cards may have software (TSR) FM emulation. Enabling FM support - with these cards may cause trouble. However I don't currently know - such cards. - "Gravis Ultrasound support", - - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't - have GUS since the GUS driver consumes much memory. - Currently I don't have experiences with the GUS ACE so I don't - know what to answer with it. - "MPU-401 support (NOT for SB16)", - - Be careful with this question. The MPU401 interface is supported - by almost any sound card today. However some natively supported cards - have their own driver for MPU401. Enabling the MPU401 option with - these cards will cause a conflict. Also enabling MPU401 on a system - that doesn't really have a MPU401 could cause some trouble. If your - card was in the list of supported cards (above), please look at - the card specific instructions later in this file. - - In MOST cases this MPU401 driver should only be used with "true" - MIDI-only MPU401 professional cards. In most other cases there - is another way to get the MPU401 compatible interface of a - sound card to work. - Support for the MPU401 compatible MIDI port of SB16, ESS1688 - and MV Jazz16 cards is included in the SB driver. Use it instead - of this separate MPU401 driver with these cards. As well - Soundscape, PSS and Maui drivers include their own MPU401 - options. - - It's safe to answer 'y' if you have a true MPU401 MIDI interface - card. - "6850 UART Midi support", - - It's safe to answer 'n' to this question in all cases. The 6850 - UART interface is so rarely used. - "PSS (ECHO-ADI2111) support", - - Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some - other card based on the PSS chipset (AD1848 codec + ADSP-2115 - DSP chip + Echo ESC614 ASIC CHIP). - "16 bit sampling option of GUS (_NOT_ GUS MAX)", - - Answer 'y' if you have installed the 16 bit sampling daughtercard - to your GUS. Answer 'n' if you have GUS MAX. Enabling this option - disables GUS MAX support. - "GUS MAX support", - - Answer 'y' only if you have a GUS MAX. - "Microsoft Sound System support", - - Again think carefully before answering 'y' to this question. It's - safe to answer 'y' in case you have the original Windows Sound - System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). - Also you may answer 'y' in case your card was not listed earlier - in this file. For cards having native support in the driver, consult - the card specific instructions later in this file. Some drivers - have their own MSS support and enabling this option will cause a - conflict. - Note! The MSS driver permits configuring two DMA channels. This is a - "nonstandard" feature and works only with very few cards (if any). - In most cases the second DMA channel should be disabled or set to - the same channel than the first one. Trying to configure two separate - channels with cards that don't support this feature will prevent - audio (at least recording) from working. - "Ensoniq Soundscape support", - - Answer 'y' if you have a sound card based on the Ensoniq SoundScape - chipset. Such cards are being manufactured at least by Ensoniq, - Spea and Reveal (note that Reveal makes other cards also). The oldest - cards made by Spea don't work properly with Linux. - Soundscape PnP as well as Ensoniq VIVO work only with the commercial - OSS/Linux version. - "MediaTrix AudioTrix Pro support", - - Answer 'y' if you have the AudioTrix Pro. - "Support for MAD16 and/or Mozart based cards", - - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 - (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. - These chips are - currently quite common so it's possible that many no-name cards - have one of them. In addition the MAD16 chip is used in some - cards made by known manufacturers such as Turtle Beach (Tropez), - Reveal (some models) and Diamond (some recent models). - Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP - mode (jumper selectable on many cards). - "Support for TB Maui" - - This enables TB Maui specific initialization. Works with TB Maui - and TB Tropez (may not work with Tropez Plus). - - -Then the configuration program asks some y/n questions about the higher -level services. It's recommended to answer 'y' to each of these questions. -Answer 'n' only if you know you will not need the option. - - "MIDI interface support", - - Answering 'n' disables /dev/midi## devices and access to any - MIDI ports using /dev/sequencer and /dev/music. This option - also affects any MPU401 and/or General MIDI compatible devices. - "FM synthesizer (YM3812/OPL-3) support", - - Answer 'y' here. - "/dev/sequencer support", - - Answering 'n' disables /dev/sequencer and /dev/music. - -Entering the I/O, IRQ and DMA config parameters ------------------------------------------------ - -After the above questions the configuration program prompts for the -card specific configuration information. Usually just a set of -I/O address, IRQ and DMA numbers are asked. With some cards the program -asks for some files to be used during initialization of the card. For example -many cards have a DSP chip or microprocessor which must be initialized by -downloading a program (microcode) file to the card. - -Instructions for answering these questions are given in the next section. - - -Card specific information -========================= - -This section gives additional instructions about configuring some cards. -Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using -the same settings with DOS/Windows and Linux is recommended. Using -different values could cause some problems when switching between -different operating systems. - -Sound Blasters (the original ones by Creative) ---------------------------------------------- - -NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 - are almost certainly PnP ones). With PnP cards you should use isapnptools - to activate them (see above). - -It's possible to configure these cards to use different I/O, IRQ and -DMA settings. Since the possible/default settings have changed between various -models, you have to consult manual of your card for the proper ones. It's -a good idea to use the same values than with DOS/Windows. With SB and SB Pro -it's the only choice. SB16 has software selectable IRQ and DMA channels but -using different values with DOS and Linux is likely to cause troubles. The -DOS driver is not able to reset the card properly after warm boot from Linux -if Linux has used different IRQ or DMA values. - -The original (steam) Sound Blaster (versions 1.x and 2.x) use always -DMA1. There is no way to change it. - -The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for -8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory -it's possible to use just one (8 bit) DMA channel by answering the 8 bit -one when the configuration program asks for the 16 bit one. This may work -in some systems but is likely to cause terrible noise on some other systems. - -It's possible to use two SB16/32/64 at the same time. To do this you should -first configure OSS/Free for one card. Then edit local.h manually and define -SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get -the OPL3, MIDI and EMU8000 devices of the second card to work. If you are -going to use two PnP Sound Blasters, ensure that they are of different model -and have different PnP IDs. There is no way to get two cards with the same -card ID and serial number to work. The easiest way to check this is trying -if isapnptools can see both cards or just one. - -NOTE! Don't enable the SM Games option (asked by the configuration program) - if you are not 101% sure that your card is a Logitech Soundman Games - (not a SM Wave or SM16). - -SB Clones ---------- - -First of all: There are no SB16 clones. There are SB Pro clones with a -16 bit mode which is not SB16 compatible. The most likely alternative is that -the 16 bit mode means MSS/WSS. - -There are just a few fully 100% hardware SB or SB Pro compatible cards. -I know just Thunderboard and SM Games. Other cards require some kind of -hardware initialization before they become SB compatible. Check if your card -was listed in the beginning of this file. In this case you should follow -instructions for your card later in this file. - -For other not fully SB clones you may try initialization using DOS in -the following way: - - - Boot DOS so that the card specific driver gets run. - - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't - switch off power or press the reset button. - - If you use the same I/O, IRQ and DMA settings in Linux, the - card should work. - -If your card is both SB and MSS compatible, I recommend using the MSS mode. -Most cards of this kind are not able to work in the SB and the MSS mode -simultaneously. Using the MSS mode provides 16 bit recording and playback. - -ProAudioSpectrum 16 and compatibles ------------------------------------ - -PAS16 has a SB emulation chip which can be used together with the native -(16 bit) mode of the card. To enable this emulation you should configure -the driver to have SB support too (this has been changed since version -3.5-beta9 of this driver). - -With current driver versions it's also possible to use PAS16 together with -another SB compatible card. In this case you should configure SB support -for the other card and to disable the SB emulation of PAS16 (there is a -separate questions about this). - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - -Gravis Ultrasound ------------------ - -There are many different revisions of the Ultrasound card (GUS). The -earliest ones (pre 3.7) don't have a hardware mixer. With these cards -the driver uses a software emulation for synth and pcm playbacks. It's -also possible to switch some of the inputs (line in, mic) off by setting -mixer volume of the channel level below 10%. For recording you have -to select the channel as a recording source and to use volume above 10%. - -GUS 3.7 has a hardware mixer. - -GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which -also contains a mixer. - -Configuring GUS is simple. Just enable the GUS support and GUS MAX or -the 16 bit daughtercard if you have them. Note that enabling the daughter -card disables GUS MAX driver. - -NOTE for owners of the 16 bit daughtercard: By default the daughtercard -uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp" -selects the daughter card as the default device. - -With just the standard GUS enabled the configuration program prompts -for the I/O, IRQ and DMA numbers for the card. Use the same values than -with DOS. - -With the daughter card option enabled you will be prompted for the I/O, -IRQ and DMA numbers for the daughter card. You have to use different I/O -and DMA values than for the standard GUS. The daughter card permits -simultaneous recording and playback. Use /dev/dsp (the daughtercard) for -recording and /dev/dsp1 (GUS GF1) for playback. - -GUS MAX uses the same I/O address and IRQ settings than the original GUS -(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. -Using two DMA channels permits simultaneous playback using two devices -(dev/dsp0 and /dev/dsp1). The second DMA channel is required for -full duplex audio. -To enable the second DMA channels, give a valid DMA channel when the config -program asks for the GUS MAX DMA (entering -1 disables the second DMA). -Using 16 bit DMA channels (5,6 or 7) is recommended. - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. - -Microphone input of GUS MAX is connected to mixer in little bit nonstandard -way. There is actually two microphone volume controls. Normal "mic" controls -only recording level. Mixer control "speaker" is used to control volume of -microphone signal connected directly to line/speaker out. So just decrease -volume of "speaker" if you have problems with microphone feedback. - -GUS ACE works too but any attempt to record or to use the MIDI port -will fail. - -GUS PnP (with RAM) is partially supported but it needs to be initialized using -DOS or isapnptools before starting the driver. - -MPU401 and Windows Sound System -------------------------------- - -Again. Don't enable these options in case your card is listed -somewhere else in this file. - -Configuring these cards is obvious (or it should be). With MSS -you should probably enable the OPL3 synth also since -most MSS compatible cards have it. However check that this is true -before enabling OPL3. - -Sound driver supports more than one MPU401 compatible cards at the same time -but the config program asks config info for just the first of them. -Adding the second or third MPU interfaces must be done manually by -editing sound/local.h (after running the config program). Add defines for -MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. - -CAUTION! - -The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which -is also the default of the MPU401 driver. Don't configure the sound driver to -use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot -if you make this mistake. - -PSS ---- - -Even the PSS cards are compatible with SB, MSS and MPU401, you must not -enable these options when configuring the driver. The configuration -program handles these options itself. (You may use the SB, MPU and MSS options -together with PSS if you have another card on the system). - -The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled -since it doesn't work concurrently with MSS. The driver loads also a -DSP algorithm which is used to for the general MIDI emulation. The -algorithm file (.ld) is read by the config program and written to a -file included when the pss.c is compiled. For this reason the config -program asks if you want to download the file. Use the genmidi.ld file -distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). -With some cards the file is called 'synth.ld'. You must have access to -the file when configuring the driver. The easiest way is to mount the DOS -partition containing the file with Linux. - -It's possible to load your own DSP algorithms and run them with the card. -Look at the directory pss_test of snd-util-3.0.tar.gz for more info. - -AudioTrix Pro -------------- - -You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition -to the native AudioTrix driver. Don't enable MSS or MPU drivers. - -Configuring ATP is little bit tricky since it uses so many I/O, IRQ and -DMA numbers. Using the same values than with DOS/Win is a good idea. Don't -attempt to use the same IRQ or DMA channels twice. - -The SB mode of ATP is implemented so the ATP driver just enables SB -in the proper address. The SB driver handles the rest. You have to configure -both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O -settings. - -Also the ATP has a microcontroller for the General MIDI emulation (OPL4). -For this reason the driver asks for the name of a file containing the -microcode (TRXPRO.HEX). This file is usually located in the directory -where the DOS drivers were installed. You must have access to this file -when configuring the driver. - -If you have the effects daughtercard, it must be initialized by running -the setfx program of snd-util-3.0.tar.gz package. This step is not required -when using the (future) binary distribution version of the driver. - -Ensoniq SoundScape ------------------- - -NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible - cards made by Reveal don't work with Linux. They use older revision - of the Soundscape chipset which is not fully compatible with - newer cards made by Ensoniq. - -The SoundScape driver handles initialization of MSS and MPU supports -itself so you don't need to enable other drivers than SoundScape -(enable also the /dev/dsp, /dev/sequencer and MIDI supports). - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!! !!!! -!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio !!!! -!!!!! device files (/dev/dsp0 and /dev/dsp1). The first one WAS !!!! -!!!!! used only for card initialization and the second for audio !!!! -!!!!! purposes. It WAS required to change /dev/dsp (a symlink) to !!!! -!!!!! point to /dev/dsp1. !!!! -!!!!! !!!! -!!!!! This is not required with OSS versions 3.5-beta6 and later !!!! -!!!!! since there is now just one audio device file. Please !!!! -!!!!! change /dev/dsp to point back to /dev/dsp0 if you are !!!! -!!!!! upgrading from an earlier driver version using !!!! -!!!!! (cd /dev;rm dsp;ln -s dsp0 dsp). !!!! -!!!!! !!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -The configuration program asks one DMA channel and two interrupts. One IRQ -and one DMA is used by the MSS codec. The second IRQ is required for the -MPU401 mode (you have to use different IRQs for both purposes). -There were earlier two DMA channels for SoundScape but the current driver -version requires just one. - -The SoundScape card has a Motorola microcontroller which must initialized -_after_ boot (the driver doesn't initialize it during boot). -The initialization is done by running the 'ssinit' program which is -distributed in the snd-util-3.0.tar.gz package. You have to edit two -defines in the ssinit.c and then compile the program. You may run ssinit -manually (after each boot) or add it to /etc/rc.d/rc.local. - -The ssinit program needs the microcode file that comes with the DOS/Windows -driver of the card. You will need to use version 1.30.00 or later -of the microcode file (sndscape.co0 or sndscape.co1 depending on -your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR -MACHINE. The only way to get the new microcode file is to download -and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. - -Then you have to select the proper microcode file to use: soundscape.co0 -is the right one for most cards and sndscape.co1 is for few (older) cards -made by Reveal and/or Spea. The driver has capability to detect the card -version during boot. Look at the boot log messages in /var/adm/messages -and locate the sound driver initialization message for the SoundScape -card. If the driver displays string , you have -an old card and you will need to use sndscape.co1. For other cards use -soundscape.co0. New Soundscape revisions such as Elite and PnP use -code files with higher numbers (.co2, .co3, etc.). - -NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. - Currently it's possible to use it in Linux only with OSS/Linux - drivers. - -Check /var/adm/messages after running ssinit. The driver prints -the board version after downloading the microcode file. That version -number must match the number in the name of the microcode file (extension). - -Running ssinit with a wrong version of the sndscape.co? file is not -dangerous as long as you don't try to use a file called sndscape.cod. -If you have initialized the card using a wrong microcode file (sounds -are terrible), just modify ssinit.c to use another microcode file and try -again. It's possible to use an earlier version of sndscape.co[01] but it -may sound weird. - -MAD16 (Pro) and Mozart ----------------------- - -You need to enable just the MAD16 /Mozart support when configuring -the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the -/dev/audio, /dev/sequencer and MIDI supports. - -Mozart and OPTi 82C928 (the original MAD16) chips don't support -MPU401 mode so enter just 0 when the configuration program asks the -MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401 -mode. - -TB Tropez is based on the 82C929 chip. It has two MIDI ports. -The one connected to the MAD16 chip is the second one (there is a second -MIDI connector/pins somewhere??). If you have not connected the second MIDI -port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of -Tropez is jumper configurable and not connected to the MAD16 chip (the -Maui driver can be used with it). - -Some MAD16 based cards may cause feedback, whistle or terrible noise if the -line3 mixer channel is turned too high. This happens at least with Shuttle -Sound System. Current driver versions set volume of line3 low enough so -this should not be a problem. - -If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer -chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4 -to the file linux/drivers/sound/local.h (after running make config). - -MAD16 cards having a CS4231 codec support full duplex mode. This mode -can be enabled by configuring the card to use two DMA channels. Possible -DMA channel pairs are: 0&1, 1&0 and 3&0. - -NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in -non-PnP mode (usually jumper selectable). The PnP mode is supported only -by OSS/Linux. - -MV Jazz (ProSonic) ------------------- - -The Jazz16 driver is just a hack made to the SB Pro driver. However it works -fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports -when configuring the driver. The configuration program asks later if you -want support for MV Jazz16 based cards (after asking SB base address). Answer -'y' here and the driver asks the second (16 bit) DMA channel. - -The Jazz16 driver uses the MPU401 driver in a way which will cause -problems if you have another MPU401 compatible card. In this case you must -give address of the Jazz16 based MPU401 interface when the config -program prompts for the MPU401 information. Then look at the MPU401 -specific section for instructions about configuring more than one MPU401 cards. - -Logitech Soundman Wave ----------------------- - -Read the above MV Jazz specific instructions first. - -The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is -a MV Jazz based card which has an additional OPL4 based wave table -synthesizer. The OPL4 chip is handled by an on board microcontroller -which must be initialized during boot. The config program asks if -you have a SM Wave immediately after asking the second DMA channel of jazz16. -If you answer 'y', the config program will ask name of the file containing -code to be loaded to the microcontroller. The file is usually called -MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file -may also be called as TSUNAMI.BIN or something else (older cards?). - -The OPL4 synth will be inaccessible without loading the microcontroller code. - -Also remember to enable SB MPU401 support if you want to use the OPL4 mode. -(Don't enable the 'normal' MPU401 device as with some earlier driver -versions (pre 3.5-alpha8)). - -NOTE! Don't answer 'y' when the driver asks about SM Games support - (the next question after the MIDI0001.BIN name). However - answering 'y' doesn't cause damage your computer so don't panic. - -Sound Galaxies --------------- - -There are many different Sound Galaxy cards made by Aztech. The 8 bit -ones are fully SB or SB Pro compatible and there should be no problems -with them. - -The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have -an EEPROM chip for storing the configuration data. There is a microcontroller -which initializes the card to match the EEPROM settings when the machine -is powered on. These cards actually behave just like they have jumpers -for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 -supports with these cards. - -There are some new Sound Galaxies in the market. I have no experience with -them so read the card's manual carefully. - -ESS ES1688 and ES688 'AudioDrive' based cards ---------------------------------------------- - -Support for these two ESS chips is embedded in the SB driver. -Configure these cards just like SB. Enable the 'SB MPU401 MIDI port' -if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode -so you don't need to enable it (the driver uses normal SB MIDI automatically -with ES688). - -NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support -of OSS doesn't work with it. - -There are some ES1688/688 based sound cards and (particularly) motherboards -which use software configurable I/O port relocation feature of the chip. -This ESS proprietary feature is supported only by OSS/Linux. - -There are ES1688 based cards which use different interrupt pin assignment than -recommended by ESS (5, 7, 9/2 and 10). In this case all IRQs don't work. -At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't -work. - -ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 -probably works with OSS/Free after initialization using isapnptools. - -Reveal cards ------------- - -There are several different cards made/marketed by Reveal. Some of them -are compatible with SoundScape and some use the MAD16 chip. You may have -to look at the card and try to identify its origin. - -Diamond -------- - -The oldest (Sierra Aria based) sound cards made by Diamond are not supported -(they may work if the card is initialized using DOS). The recent (LX?) -models are based on the MAD16 chip which is supported by the driver. - -Audio Excel DSP16 ------------------ - -Support for this card is currently not functional. A new driver for it -should be available later this year. - -PCMCIA cards ------------- - -Sorry, can't help. Some cards may work and some don't. - -TI TM4000M notebooks --------------------- - -These computers have a built in sound support based on the Jazz chipset. -Look at the instructions for MV Jazz (above). It's also important to note -that there is something wrong with the mouse port and sound at least on -some TM models. Don't enable the "C&T 82C710 mouse port support" when -configuring Linux. Having it enabled is likely to cause mysterious problems -and kernel failures when sound is used. - -miroSOUND ---------- - -The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used -successfully. These cards are based on the MAD16, OPL4, and CS4231A chips -and everything said in the section about MAD16 cards applies here, -too. The only major difference between the PCMxx and other MAD16 cards -is that instead of the mixer in the CS4231 codec a separate mixer -controlled by an on-board 80C32 microcontroller is used. Control of -the mixer takes place via the ACI (miro's audio control interface) -protocol that is implemented in a separate lowlevel driver. Make sure -you compile this ACI driver together with the normal MAD16 support -when you use a miroSOUND PCMxx card. The ACI mixer is controlled by -/dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load -time). Only in special cases you want to change something regularly on -the CS4231 mixer. - -The miroSOUND PCM12 and PCM20 radio is capable of full duplex -operation (simultaneous PCM replay and recording), which allows you to -implement nice real-time signal processing audio effect software and -network telephones. The ACI mixer has to be switched into the "solo" -mode for duplex operation in order to avoid feedback caused by the -mixer (input hears output signal). You can de-/activate this mode -through toggleing the record button for the wave controller with an -OSS-mixer. - -The PCM20 contains a radio tuner, which is also controlled by -ACI. This radio tuner is supported by the ACI driver together with the -miropcm20.o module. Also the 7-band equalizer is integrated -(limited by the OSS-design). Developement has started and maybe -finished for the RDS decoder on this card, too. You will be able to -read RadioText, the Programme Service name, Programme TYpe and -others. Even the v4l radio module benefits from it with a refined -strength value. See aci.[ch] and miropcm20*.[ch] for more details. - -The following configuration parameters have worked fine for the PCM12 -in Markus Kuhn's system, many other configurations might work, too: -CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, -CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, -DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. - -Bas van der Linden is using his PCM1-pro with a configuration that -differs in: CONFIG_MAD16_IRQ=7, CONFIG_MAD16_DMA=1, CONFIG_MAD16_MPU_IRQ=9 - -Compaq Deskpro XL ------------------ - -The builtin sound hardware of Compaq Deskpro XL is now supported. -You need to configure the driver with MSS and OPL3 supports enabled. -In addition you need to manually edit linux/drivers/sound/local.h and -to add a line containing "#define DESKPROXL" if you used -make menuconfig/xconfig. - -Others? -------- - -Since there are so many different sound cards, it's likely that I have -forgotten to mention many of them. Please inform me if you know yet another -card which works with Linux, please inform me (or is anybody else -willing to maintain a database of supported cards (just like in XF86)?). - -Cards not supported yet -======================= - -Please check the version of sound driver you are using before -complaining that your card is not supported. It's possible you are -using a driver version which was released months before your card was -introduced. - -First of all, there is an easy way to make most sound cards work with Linux. -Just use the DOS based driver to initialize the card to a known state, then use -loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and -DMA numbers as DOS, the card could work. -(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with -new motherboards). This method works also with all/most PnP sound cards. - -Don't get fooled with SB compatibility. Most cards are compatible with -SB but that may require a TSR which is not possible with Linux. If -the card is compatible with MSS, it's a better choice. Some cards -don't work in the SB and MSS modes at the same time. - -Then there are cards which are no longer manufactured and/or which -are relatively rarely used (such as the 8 bit ProAudioSpectrum -models). It's extremely unlikely that such cards ever get supported. -Adding support for a new card requires much work and increases time -required in maintaining the driver (some changes need to be done -to all low level drivers and be tested too, maybe with multiple -operating systems). For this reason I have made a decision to not support -obsolete cards. It's possible that someone else makes a separately -distributed driver (diffs) for the card. - -Writing a driver for a new card is not possible if there are no -programming information available about the card. If you don't -find your new card from this file, look from the home page -(http://www.opensound.com/ossfree). Then please contact -manufacturer of the card and ask if they have (or are willing to) -released technical details of the card. Do this before contacting me. I -can only answer 'no' if there are no programming information available. - -I have made decision to not accept code based on reverse engineering -to the driver. There are three main reasons: First I don't want to break -relationships to sound card manufacturers. The second reason is that -maintaining and supporting a driver without any specs will be a pain. -The third reason is that companies have freedom to refuse selling their -products to other than Windows users. - -Some companies don't give low level technical information about their -products to public or at least their require signing a NDA. It's not -possible to implement a freeware driver for them. However it's possible -that support for such cards become available in the commercial version -of this driver (see http://www.4Front-tech.com/oss.html for more info). - -There are some common audio chipsets that are not supported yet. For example -Sierra Aria and IBM Mwave. It's possible that these architectures -get some support in future but I can't make any promises. Just look -at the home page (http://www.opensound.com/ossfree/new_cards.html) -for latest info. - -Information about unsupported sound cards and chipsets is welcome as well -as free copies of sound cards, SDKs and operating systems. - -If you have any corrections and/or comments, please contact me. - -Hannu Savolainen -hannu@opensound.com - -Personal home page: http://www.compusonic.fi/~hannu -home page of OSS/Free: http://www.opensound.com/ossfree - -home page of commercial OSS -(Open Sound System) drivers: http://www.opensound.com/oss.html diff -urN linux-2.5.6-pre3/Documentation/sound/README.awe linux-2.5.6/Documentation/sound/README.awe --- linux-2.5.6-pre3/Documentation/sound/README.awe Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/README.awe Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -================================================================ - AWE32 Sound Driver for Linux / FreeBSD - version 0.4.3; Nov. 1, 1998 - - Takashi Iwai -================================================================ - -* GENERAL NOTES - -This is a sound driver extension for SoundBlaster AWE32 and other -compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable -the wave synth operations. The driver is provided for Linux 1.2.x -and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC -Alpha systems. - -This driver was written by Takashi Iwai , -and provided "as is". The original source (awedrv-0.4.3.tar.gz) and -binary packages are available on the following URL: - http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ -Note that since the author is apart from this web site, the update is -not frequent now. - - -* NOTE TO LINUX USERS - -To enable this driver on linux-2.[01].x kernels, you need turn on -"AWE32 synth" options in sound menu when configure your linux kernel -and modules. The precise installation procedure is described in the -AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. - -If you're using PnP cards, the card must be initialized before loading -the sound driver. There're several options to do this: - - Initialize the card via ISA PnP tools, and load the sound module. - - Initialize the card on DOS, and load linux by loadlin.exe - - Use PnP kernel driver (for Linux-2.x.x) -The detailed instruction for the solution using isapnp tools is found -in many documents like above. A brief instruction is also included in -the installation document of this package. -For PnP driver project, please refer to the following URL: - http://www-jcr.lmh.ox.ac.uk/~pnp/ - - -* USING THE DRIVER - -The awedrv has several different playing modes to realize easy channel -allocation for MIDI songs. To hear the exact sound quality, you need -to obtain the extended sequencer program, drvmidi or playmidi-2.5. - -For playing MIDI files, you *MUST* load the soundfont file on the -driver previously by sfxload utility. Otherwise you'll here no sounds -at all! All the utilities and driver source packages are found in the -above URL. The sfxload program is included in the package -awesfx-0.4.3.tgz. Binary packages are available there, too. See the -instruction in each package for installation. - -Loading a soundfont file is very simple. Just execute the command - - % sfxload synthgm.sbk - -Then, sfxload transfers the file "synthgm.sbk" to the driver. -Both SF1 and SF2 formats are accepted. - -Now you can hear midi musics by a midi player. - - % drvmidi foo.mid - -If you run MIDI player after MOD player, you need to load soundfont -files again, since MOD player programs clear the previous loaded -samples by their own data. - -If you have only 512kb on the sound card, I recommend to use dynamic -sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is -available in most midi files. - - % sfxload synthgm - % drvmidi -L 2mbgmgs foo.mid - -This makes a big difference (believe me)! For more details, please -refer to the FAQ list which is available on the URL above. - -The current chorus, reverb and equalizer status can be changed by -aweset utility program (included in awesfx package). Note that -some awedrv-native programs (like drvmidi and xmp) will change the -current settings by themselves. The aweset program is effective -only for other programs like playmidi. - -Enjoy. - - -* COMPILE FLAGS - -Compile conditions are defined in awe_config.h. - -[Compatibility Conditions] -The following flags are defined automatically when using installation -shell script. - -- AWE_MODULE_SUPPORT - indicates your Linux kernel supports module for each sound card - (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels - as distributed in the RH5.0 package). - This flag is automatically set when you're using 2.1.x kernels. - You can pass the base address and memory size via the following - module options, - io = base I/O port address (eg. 0x620) - memsize = DRAM size in kilobytes (eg. 512) - As default, AWE driver probes these values automatically. - - -[Hardware Conditions] -You DON'T have to define the following two values. -Define them only when the driver couldn't detect the card properly. - -- AWE_DEFAULT_BASE_ADDR (default: not defined) - specifies the base port address of your AWE32 card. - 0 means to autodetect the address. - -- AWE_DEFAULT_MEM_SIZE (default: not defined) - specifies the memory size of your AWE32 card in kilobytes. - -1 means to autodetect its size. - - -[Sample Table Size] -From ver.0.4.0, sample tables are allocated dynamically (except -Linux-1.2.x system), so you need NOT to touch these parameters. -Linux-1.2.x users may need to increase these values to appropriate size -if the sound card is equipped with more DRAM. - -- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS - - -[Other Conditions] - -- AWE_ALWAYS_INIT_FM (default: not defined) - indicates the AWE driver always initialize FM passthrough even - without DRAM on board. Emu8000 chip has a restriction for playing - samples on DRAM that at least two channels must be occupied as - passthrough channels. - -- AWE_DEBUG_ON (default: defined) - turns on debugging messages if defined. - -- AWE_HAS_GUS_COMPATIBILITY (default: defined) - Enables GUS compatibility mode if defined, reading GUS patches and - GUS control commands. Define this option to use GMOD or other - GUS module players. - -- CONFIG_AWE32_MIDIEMU (default: defined) - Adds a MIDI emulation device by Emu8000 wavetable. The emulation - device can be accessed as an external MIDI, and sends the MIDI - control codes directly. XG and GS sysex/NRPN are accepted. - No MIDI input is supported. - -- CONFIG_AWE32_MIXER (default: not defined) - Adds a mixer device for AWE32 bass/treble equalizer control. - You can access this device using /dev/mixer?? (usually mixer01). - -- AWE_USE_NEW_VOLUME_CALC (default: defined) - Use the new method to calculate the volume change as compatible - with DOS/Win drivers. This option can be toggled via aweset - program, or drvmidi player. - -- AWE_CHECK_VTARGET (default: defined) - Check the current volume target value when searching for an - empty channel to allocate a new voice. This is experimentally - implemented in this version. (probably, this option doesn't - affect the sound quality severely...) - -- AWE_ALLOW_SAMPLE_SHARING (default: defined) - Allow sample sharing for differently loaded patches. - This function is available only together with awesfx-0.4.3p3. - Note that this is still an experimental option. - -- DEF_FM_CHORUS_DEPTH (default: 0x10) - The default strength to be sent to the chorus effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - -- DEF_FM_REVERB_DEPTH (default: 0x10) - The default strength to be sent to the reverb effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - - -* ACKNOWLEDGMENTS - -Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice -on programming of AWE32. Much code is brought from his AWE32-native -MOD player, ALMP. -The port of awedrv to FreeBSD is done by Randall Hopper -(rhh@ct.picker.com). -The new volume calculation routine was derived from Mark Weaver's -ADIP compatible routines. -I also thank linux-awe-ml members for their efforts -to reboot their system many times :-) - - -* TODO'S - -- Complete DOS/Win compatibility -- DSP-like output - - -* COPYRIGHT - -Copyright (C) 1996-1998 Takashi Iwai - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff -urN linux-2.5.6-pre3/Documentation/sound/README.modules linux-2.5.6/Documentation/sound/README.modules --- linux-2.5.6-pre3/Documentation/sound/README.modules Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/Documentation/sound/README.modules Wed Dec 31 16:00:00 1969 @@ -1,105 +0,0 @@ -Building a modular sound driver -================================ - - The following information is current as of linux-2.1.85. Check the other -readme files, especially README.OSS, for information not specific to -making sound modular. - - First, configure your kernel. This is an idea of what you should be -setting in the sound section: - - Sound card support - - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - I have SoundBlaster. Select your card from the list. - - Generic OPL2/OPL3 FM synthesizer support - FM synthesizer (YM3812/OPL-3) support - - If you don't set these, you will probably find you can play .wav files -but not .midi. As the help for them says, set them unless you know your -card does not use one of these chips for FM support. - - Once you are configured, make zlilo, modules, modules_install; reboot. -Note that it is no longer necessary or possible to configure sound in the -drivers/sound dir. Now one simply configures and makes one's kernel and -modules in the usual way. - - Then, add to your /etc/modules.conf something like: - -alias char-major-14 sb -post-install sb /sbin/modprobe "-k" "adlib_card" -options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -options adlib_card io=0x388 # FM synthesizer - - Alternatively, if you have compiled in kernel level ISAPnP support: - -alias char-major-14 sb -post-install sb /sbin/modprobe "-k" "adlib_card" -options adlib_card io=0x388 - - The effect of this is that the sound driver and all necessary bits and -pieces autoload on demand, assuming you use kerneld (a sound choice) and -autoclean when not in use. Also, options for the device drivers are -set. They will not work without them. Change as appropriate for your card. -If you are not yet using the very cool kerneld, you will have to "modprobe --k sb" yourself to get things going. Eventually things may be fixed so -that this kludgery is not necessary; for the time being, it seems to work -well. - - Replace 'sb' with the driver for your card, and give it the right -options. To find the filename of the driver, look in -/lib/modules//misc. Mine looks like: - -adlib_card.o # This is the generic OPLx driver -opl3.o # The OPL3 driver -sb.o # <> -sound.o # The sound driver -uart401.o # Used by sb, maybe other cards - - Whichever card you have, try feeding it the options that would be the -default if you were making the driver wired, not as modules. You can look -at the init_module() code for the card to see what args are expected. - - Note that at present there is no way to configure the io, irq and other -parameters for the modular drivers as one does for the wired drivers.. One -needs to pass the modules the necessary parameters as arguments, either -with /etc/modules.conf or with command-line args to modprobe, e.g. - -modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -modprobe -k adlib_card io=0x388 - - recommend using /etc/modules.conf. - -Persistent DMA Buffers: - -The sound modules normally allocate DMA buffers during open() and -deallocate them during close(). Linux can often have problems allocating -DMA buffers for ISA cards on machines with more than 16MB RAM. This is -because ISA DMA buffers must exist below the 16MB boundary and it is quite -possible that we can't find a large enough free block in this region after -the machine has been running for any amount of time. The way to avoid this -problem is to allocate the DMA buffers during module load and deallocate -them when the module is unloaded. For this to be effective we need to load -the sound modules right after the kernel boots, either manually or by an -init script, and keep them around until we shut down. This is a little -wasteful of RAM, but it guarantees that sound always works. - -To make the sound driver use persistent DMA buffers we need to pass the -sound.o module a "dmabuf=1" command-line argument. This is normally done -in /etc/modules.conf like so: - -options sound dmabuf=1 - -If you have 16MB or less RAM or a PCI sound card, this is wasteful and -unnecessary. It is possible that machine with 16MB or less RAM will find -this option useful, but if your machine is so memory-starved that it -cannot find a 64K block free, you will be wasting even more RAM by keeping -the sound modules loaded and the DMA buffers allocated when they are not -needed. The proper solution is to upgrade your RAM. But you do also have -this improper solution as well. Use it wisely. - - I'm afraid I know nothing about anything but my setup, being more of a -text-mode guy anyway. If you have options for other cards or other helpful -hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff -urN linux-2.5.6-pre3/Documentation/sound/README.ymfsb linux-2.5.6/Documentation/sound/README.ymfsb --- linux-2.5.6-pre3/Documentation/sound/README.ymfsb Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/Documentation/sound/README.ymfsb Wed Dec 31 16:00:00 1969 @@ -1,107 +0,0 @@ -Legacy audio driver for YMF7xx PCI cards. - - -FIRST OF ALL -============ - - This code references YAMAHA's sample codes and data sheets. - I respect and thank for all people they made open the informations - about YMF7xx cards. - - And this codes heavily based on Jeff Garzik 's - old VIA 82Cxxx driver (via82cxxx.c). I also respect him. - - -DISCLIMER -========= - - This driver is currently at early ALPHA stage. It may cause serious - damage to your computer when used. - PLEASE USE IT AT YOUR OWN RISK. - - -ABOUT THIS DRIVER -================= - - This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754 - cards. When enabled, your card acts as "SoundBlaster Pro" compatible card. - It can only play 22.05kHz / 8bit / Stereo samples, control external MIDI - port. - If you want to use your card as recent "16-bit" card, you should use - Alsa or OSS/Linux driver. Of course you can write native PCI driver for - your cards :) - - -USAGE -===== - - # modprobe ymfsb (options) - - -OPTIONS FOR MODULE -================== - - io : SB base address (0x220, 0x240, 0x260, 0x280) - synth_io : OPL3 base address (0x388, 0x398, 0x3a0, 0x3a8) - dma : DMA number (0,1,3) - master_volume: AC'97 PCM out Vol (0-100) - spdif_out : SPDIF-out flag (0:disable 1:enable) - - These options will change in future... - - -FREQUENCY -========= - - When playing sounds via this driver, you will hear its pitch is slightly - lower than original sounds. Since this driver recognizes your card acts - with 21.739kHz sample rates rather than 22.050kHz (I think it must be - hardware restriction). So many players become tone deafness. - To prevent this, you should express some options to your sound player - that specify correct sample frequency. For example, to play your MP3 file - correctly with mpg123, specify the frequency like following: - - % mpg123 -r 21739 foo.mp3 - - -SPDIF OUT -========= - - With installing modules with option 'spdif_out=1', you can enjoy your - sounds from SPDIF-out of your card (if it had). - Its Fs is fixed to 48kHz (It never means the sample frequency become - up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your - digital-in capable components has to be able to handle 48kHz Fs. - - -COPYING -======= - - 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, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - -TODO -==== - * support for multiple cards - (set the different SB_IO,MPU_IO,OPL_IO for each cards) - - * support for OPL (dmfm) : There will be no requirements... :-< - - -AUTHOR -====== - - Daisuke Nagano - diff -urN linux-2.5.6-pre3/Documentation/sound/SoundPro linux-2.5.6/Documentation/sound/SoundPro --- linux-2.5.6-pre3/Documentation/sound/SoundPro Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/SoundPro Wed Dec 31 16:00:00 1969 @@ -1,105 +0,0 @@ -Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o) ------------------------------------------------------------------------------- - -( Be sure to read Documentation/sound/CMI8330 too ) - -Ion Badulescu, ionut@cs.columbia.edu -February 24, 1999 - -(derived from the OPL3-SA2 documentation by Scott Murray) - -The SoundPro CMI8330 (ISA) is a chip usually found on some Taiwanese -motherboards. The official name in the documentation is CMI8330, SoundPro -is the nickname and the big inscription on the chip itself. - -The chip emulates a WSS as well as a SB16, but it has certain differences -in the mixer section which require separate support. It also emulates an -MPU401 and an OPL3 synthesizer, so you probably want to enable support -for these, too. - -The chip identifies itself as an AD1848, but its mixer is significantly -more advanced than the original AD1848 one. If your system works with -either WSS or SB16 and you are having problems with some mixer controls -(no CD audio, no line-in, etc), you might want to give this driver a try. -Detection should work, but it hasn't been widely tested, so it might still -mis-identify the chip. You can still force soundpro=1 in the modprobe -parameters for ad1848. Please let me know if it happens to you, so I can -adjust the detection routine. - -The chip is capable of doing full-duplex, but since the driver sees it as an -AD1848, it cannot take advantage of this. Moreover, the full-duplex mode is -not achievable through the WSS interface, b/c it needs a dma16 line which is -assigned only to the SB16 subdevice (with isapnp). Windows documentation -says the user must use WSS Playback and SB16 Recording for full-duplex, so -it might be possible to do the same thing under Linux. You can try loading -up both ad1848 and sb then use one for playback and the other for -recording. I don't know if this works, b/c I haven't tested it. Anyway, if -you try it, be very careful: the SB16 mixer *mostly* works, but certain -settings can have unexpected effects. Use the WSS mixer for best results. - -There is also a PCI SoundPro chip. I have not seen this chip, so I have -no idea if the driver will work with it. I suspect it won't. - -As with PnP cards, some configuration is required. There are two ways -of doing this. The most common is to use the isapnptools package to -initialize the card, and use the kernel module form of the sound -subsystem and sound drivers. Alternatively, some BIOS's allow manual -configuration of installed PnP devices in a BIOS menu, which should -allow using the non-modular sound drivers, i.e. built into the kernel. -Since in this latter case you cannot use module parameters, you will -have to enable support for the SoundPro at compile time. - -The IRQ and DMA values can be any that are considered acceptable for a -WSS. Assuming you've got isapnp all happy, then you should be able to -do something like the following (which *must* match the isapnp/BIOS -configuration): - -modprobe ad1848 io=0x530 irq=11 dma=0 soundpro=1 --and maybe- -modprobe sb io=0x220 irq=5 dma=1 dma16=5 - --then- -modprobe mpu401 io=0x330 irq=9 -modprobe opl3 io=0x388 - -If all goes well and you see no error messages, you should be able to -start using the sound capabilities of your system. If you get an -error message while trying to insert the module(s), then make -sure that the values of the various arguments match what you specified -in your isapnp configuration file, and that there is no conflict with -another device for an I/O port or interrupt. Checking the contents of -/proc/ioports and /proc/interrupts can be useful to see if you're -butting heads with another device. - -If you do not see the chipset version message, and none of the other -messages present in the system log are helpful, try adding 'debug=1' -to the ad1848 parameters, email me the syslog results and I'll do -my best to help. - -Lastly, if you're using modules and want to set up automatic module -loading with kmod, the kernel module loader, here is the section I -currently use in my conf.modules file: - -# Sound -post-install sound modprobe -k ad1848; modprobe -k mpu401; modprobe -k opl3 -options ad1848 io=0x530 irq=11 dma=0 -options sb io=0x220 irq=5 dma=1 dma16=5 -options mpu401 io=0x330 irq=9 -options opl3 io=0x388 - -The above ensures that ad1848 will be loaded whenever the sound system -is being used. - -Good luck. - -Ion - -NOT REALLY TESTED: -- recording -- recording device selection -- full-duplex - -TODO: -- implement mixer support for surround, loud, digital CD switches. -- come up with a scheme which allows recording volumes for each subdevice. -This is a major OSS API change. diff -urN linux-2.5.6-pre3/Documentation/sound/Soundblaster linux-2.5.6/Documentation/sound/Soundblaster --- linux-2.5.6-pre3/Documentation/sound/Soundblaster Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/Documentation/sound/Soundblaster Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -modprobe sound -insmod uart401 -insmod sb ... - -This loads the driver for the Sound Blaster and assorted clones. Cards that -are covered by other drivers should not be using this driver. - -The Sound Blaster module takes the following arguments - -io I/O address of the Sound Blaster chip (0x220,0x240,0x260,0x280) -irq IRQ of the Sound Blaster chip (5,7,9,10) -dma 8-bit DMA channel for the Sound Blaster (0,1,3) -dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) -mpu_io I/O for MPU chip if present (0x300,0x330) - -sm_games=1 Set if you have a Logitech soundman games -acer=1 Set this to detect cards in some ACER notebooks -mwave_bug=1 Set if you are trying to use this driver with mwave (see on) -type Use this to specify a specific card type - -The following arguments are taken if ISAPnP support is compiled in - -isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) -multiple=0 Set to disable detection of multiple Soundblaster cards. - Consider it a bug if this option is needed, and send in a - report. -pnplegacy=1 Set this to be able to use a PnP card(s) along with a single - non-PnP (legacy) card. Above options for io, irq, etc. are - needed, and will apply only to the legacy card. -reverse=1 Reverses the order of the search in the PnP table. -uart401=1 Set to enable detection of mpu devices on some clones. -isapnpjump=n Jumps to slot n in the driver's PnP table. Use the source, - Luke. - -You may well want to load the opl3 driver for synth music on most SB and -clone SB devices - -insmod opl3 io=0x388 - -Using Mwave - -To make this driver work with Mwave you must set mwave_bug. You also need -to warm boot from DOS/Windows with the required firmware loaded under this -OS. IBM are being difficult about documenting how to load this firmware. - -Avance Logic ALS007 - -This card is supported; see the separate file ALS007 for full details. - -Avance Logic ALS100 - -This card is supported; setup should be as for a standard Sound Blaster 16. -The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff -urN linux-2.5.6-pre3/Documentation/sound/Tropez+ linux-2.5.6/Documentation/sound/Tropez+ --- linux-2.5.6-pre3/Documentation/sound/Tropez+ Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/Documentation/sound/Tropez+ Wed Dec 31 16:00:00 1969 @@ -1,26 +0,0 @@ -From: Paul Barton-Davis - -Here is the configuration I use with a Tropez+ and my modular -driver: - - alias char-major-14 wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - pre-install wavefront modprobe "-k" "cs4232" - post-install wavefront modprobe "-k" "opl3" - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - -Please see drivers/sound/README.wavefront for more details. diff -urN linux-2.5.6-pre3/Documentation/sound/VIA-chipset linux-2.5.6/Documentation/sound/VIA-chipset --- linux-2.5.6-pre3/Documentation/sound/VIA-chipset Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/Documentation/sound/VIA-chipset Wed Dec 31 16:00:00 1969 @@ -1,43 +0,0 @@ -Running sound cards on VIA chipsets - -o There are problems with VIA chipsets and sound cards that appear to - lock the hardware solidly. Test programs under DOS have verified the - problem exists on at least some (but apparently not all) VIA boards - -o VIA have so far failed to bother to answer support mail on the subject - so if you are a VIA engineer feeling aggrieved as you read this - document go chase your own people. If there is a workaround please - let us know so we can implement it. - - -Certain patterns of ISA DMA access used for most PC sound cards cause the -VIA chipsets to lock up. From the collected reports this appears to cover a -wide range of boards. Some also lock up with sound cards under Win* as well. - -Linux implements a workaround providing your chipset is PCI and you compiled -with PCI Quirks enabled. If so you will see a message - "Activating ISA DMA bug workarounds" - -during booting. If you have a VIA PCI chipset that hangs when you use the -sound and is not generating this message even with PCI quirks enabled -please report the information to the linux-kernel list (see REPORTING-BUGS). - -If you are one of the tiny number of unfortunates with a 486 ISA/VLB VIA -chipset board you need to do the following to build a special kernel for -your board - - edit linux/include/asm-i386/dma.h - -change - -#define isa_dma_bridge_buggy (0) - -to - -#define isa_dma_bridge_buggy (1) - -and rebuild a kernel without PCI quirk support. - - -Other than this particular glitch the VIA [M]VP* chipsets appear to work -perfectly with Linux. diff -urN linux-2.5.6-pre3/Documentation/sound/VIBRA16 linux-2.5.6/Documentation/sound/VIBRA16 --- linux-2.5.6-pre3/Documentation/sound/VIBRA16 Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/Documentation/sound/VIBRA16 Wed Dec 31 16:00:00 1969 @@ -1,80 +0,0 @@ -Sound Blaster 16X Vibra addendum --------------------------------- -by Marius Ilioaea - Stefan Laudat - -Sat Mar 6 23:55:27 EET 1999 - - Hello again, - - Playing with a SB Vibra 16x soundcard we found it very difficult -to setup because the kernel reported a lot of DMA errors and wouldn't -simply play any sound. - A good starting point is that the vibra16x chip full-duplex facility -is neither still exploited by the sb driver found in the linux kernel -(tried it with a 2.2.2-ac7), nor in the commercial OSS package (it reports -it as half-duplex soundcard). Oh, I almost forgot, the RedHat sndconfig -failed detecting it ;) - So, the big problem still remains, because the sb module wants a -8-bit and a 16-bit dma, which we could not allocate for vibra... it supports -only two 8-bit dma channels, the second one will be passed to the module -as a 16 bit channel, the kernel will yield about that but everything will -be okay, trust us. - The only inconvenient you may find is that you will have -some sound playing jitters if you have HDD dma support enabled - but this -will happen with almost all soundcards... - - A fully working isapnp.conf is just here: - - - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -# SB 16 and OPL3 devices -(CONFIGURE CTL00f0/-1 (LD 0 -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 3)) -(IO 0 (SIZE 16) (BASE 0x0220)) -(IO 2 (SIZE 4) (BASE 0x0388)) -(NAME "CTL00f0/-1[0]{Audio }") -(ACT Y) -)) - -# Joystick device - only if you need it :-/ - -(CONFIGURE CTL00f0/-1 (LD 1 -(IO 0 (SIZE 1) (BASE 0x0200)) -(NAME "CTL00f0/-1[1]{Game }") -(ACT Y) -)) -(WAITFORKEY) - - - - So, after a good kernel modules compilation and a 'depmod -a kernel_ver' -you may want to: - -modprobe sb io=0x220 irq=5 dma=1 dma16=3 - - Or, take the hard way: - -insmod souncore -insmod sound -insmod uart401 -insmod sb io=0x220 irq=5 dma=1 dma16=3 -# do you need MIDI? -insmod opl3=0x388 - - Just in case, the kernel sound support should be: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m - - Enjoy your new noisy Linux box! ;) - - diff -urN linux-2.5.6-pre3/Documentation/sound/WaveArtist linux-2.5.6/Documentation/sound/WaveArtist --- linux-2.5.6-pre3/Documentation/sound/WaveArtist Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/Documentation/sound/WaveArtist Wed Dec 31 16:00:00 1969 @@ -1,170 +0,0 @@ - - (the following is from the armlinux CVS) - - WaveArtist mixer and volume levels can be accessed via these commands: - - nn30 read registers nn, where nn = 00 - 09 for mixer settings - 0a - 13 for channel volumes - mm31 write the volume setting in pairs, where mm = (nn - 10) / 2 - rr32 write the mixer settings in pairs, where rr = nn/2 - xx33 reset all settings to default - 0y34 select mono source, y=0 = left, y=1 = right - - bits - nn 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 00 | 0 | 0 0 1 1 | left line mixer gain | left aux1 mixer gain |lmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 01 | 0 | 0 1 0 1 | left aux2 mixer gain | right 2 left mic gain |mmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 05 | 0 | 1 1 0 1 | right aux2 mixer gain | left 2 right mic gain |test | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 09 | 1 | 0 1 0 1 |loopb|left linout|loop|ADCch|TxFch|OffCD|test |loopb|loopb|osamp| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0a | 0 | left PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0b | 0 | right PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0c | 0 | left FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0d | 0 | right FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0e | 0 | left wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0f | 0 | right wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 10 | 0 | left PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 11 | 0 | right PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 12 | 0 | left FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 13 | 0 | right FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - - lmute: left mute - mmute: mono mute - dith: dithds - lrfg: - rmute: right mute - rbyps: right bypass - rrfg: - ADCch: - TxFch: - OffCD: - osamp: - - And the following diagram is derived from the description in the CVS archive: - - MIC L (mouthpiece) - +------+ - -->PreAmp>-\ - +--^---+ | - | | - r2b4-5 | +--------+ - /----*-------------------------------->5 | - | | | - | /----------------------------------->4 | - | | | | - | | /--------------------------------->3 1of5 | +---+ - | | | | mux >-->AMP>--> ADC L - | | | /------------------------------->2 | +-^-+ - | | | | | | | - Line | | | | +----+ +------+ +---+ /---->1 | r3b3-0 - ------------*->mute>--> Gain >--> | | | | - L | | | +----+ +------+ | | | *->0 | - | | | | | | +---^----+ - Aux2 | | | +----+ +------+ | | | | - ----------*--->mute>--> Gain >--> M | | r8b0-2 - L | | +----+ +------+ | | | - | | | | \------\ - Aux1 | | +----+ +------+ | | | - --------*----->mute>--> Gain >--> I | | - L | +----+ +------+ | | | - | | | | - | +----+ +------+ | | +---+ | - *------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - | | | | | - | +----+ +------+ | | r2b1-3 | - | /----->mute>--> Gain >--> E | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | | /--->mute>--> Gain >--> R | | - | | | +----+ +------+ | | | - | | | | | | r9b8-9 - | | | +----+ +------+ | | | | - | | | /->mute>--> Gain >--> | | +---v---+ - | | | | +----+ +------+ +---+ /-*->0 | - DAC | | | | | | | - ------------*----------------------------------->? | +----+ - L | | | | | Mux >-->mute>--> L output - | | | | /->? | +--^-+ - | | | | | | | | - | | | /--------->? | r0b0 - | | | | | | +-------+ - | | | | | | - Mono | | | | | | +-------+ - ----------* | \---> | +----+ - | | | | | | Mix >-->mute>--> Mono output - | | | | *-> | +--^-+ - | | | | | +-------+ | - | | | | | r1b0 - DAC | | | | | +-------+ - ------------*-------------------------*--------->1 | +----+ - R | | | | | | Mux >-->mute>--> R output - | | | | +----+ +------+ +---+ *->0 | +--^-+ - | | | \->mute>--> Gain >--> | | +---^---+ | - | | | +----+ +------+ | | | | r5b0 - | | | | | | r6b0 - | | | +----+ +------+ | | | - | | \--->mute>--> Gain >--> M | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | *----->mute>--> Gain >--> I | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | +---+ | - \------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - /--/ | | | | - Aux1 | +----+ +------+ | | r6b1-3 | - -------*------>mute>--> Gain >--> E | | - R | | +----+ +------+ | | | - | | | | | - Aux2 | | +----+ +------+ | | /------/ - ---------*---->mute>--> Gain >--> R | | - R | | | +----+ +------+ | | | - | | | | | | +--------+ - Line | | | +----+ +------+ | | | *->0 | - -----------*-->mute>--> Gain >--> | | | | - R | | | | +----+ +------+ +---+ \---->1 | - | | | | | | - | | | \-------------------------------->2 | +---+ - | | | | Mux >-->AMP>--> ADC R - | | \---------------------------------->3 | +-^-+ - | | | | | - | \------------------------------------>4 | r7b3-0 - | | | - \-----*-------------------------------->5 | - | +---^----+ - r6b4-5 | | - | | r8b3-5 - +--v---+ | - -->PreAmp>-/ - +------+ - MIC R (electret mic) diff -urN linux-2.5.6-pre3/Documentation/sound/Wavefront linux-2.5.6/Documentation/sound/Wavefront --- linux-2.5.6-pre3/Documentation/sound/Wavefront Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/Documentation/sound/Wavefront Wed Dec 31 16:00:00 1969 @@ -1,341 +0,0 @@ - An OSS/Free Driver for WaveFront soundcards - (Turtle Beach Maui, Tropez, Tropez Plus) - - Paul Barton-Davis, July 1998 - - VERSION 0.2.5 - -Driver Status -------------- - -Requires: Kernel 2.1.106 or later (the driver is included with kernels -2.1.109 and above) - -As of 7/22/1998, this driver is currently in *BETA* state. This means -that it compiles and runs, and that I use it on my system (Linux -2.1.106) with some reasonably demanding applications and uses. I -believe the code is approaching an initial "finished" state that -provides bug-free support for the Tropez Plus. - -Please note that to date, the driver has ONLY been tested on a Tropez -Plus. I would very much like to hear (and help out) people with Tropez -and Maui cards, since I think the driver can support those cards as -well. - -Finally, the driver has not been tested (or even compiled) as a static -(non-modular) part of the kernel. Alan Cox's good work in modularizing -OSS/Free for Linux makes this rather unnecessary. - -Some Questions --------------- - -********************************************************************** -0) What does this driver do that the maui driver did not ? -********************************************************************** - -* can fully initialize a WaveFront card from cold boot - no DOS - utilities needed -* working patch/sample/program loading and unloading (the maui - driver didn't document how to make this work, and assumed - user-level preparation of the patch data for writing - to the board. ick.) -* full user-level access to all WaveFront commands -* for the Tropez Plus, (primitive) control of the YSS225 FX processor -* Virtual MIDI mode supported - 2 MIDI devices accessible via the - WaveFront's MPU401/UART emulation. One - accesses the WaveFront synth, the other accesses the - external MIDI connector. Full MIDI read/write semantics - for both devices. -* OSS-compliant /dev/sequencer interface for the WaveFront synth, - including native and GUS-format patch downloading. -* semi-intelligent patch management (prototypical at this point) - -********************************************************************** -1) What to do about MIDI interfaces ? -********************************************************************** - -The Tropez Plus (and perhaps other WF cards) can in theory support up -to 2 physical MIDI interfaces. One of these is connected to the -ICS2115 chip (the WaveFront synth itself) and is controlled by -MPU/UART-401 emulation code running as part of the WaveFront OS. The -other is controlled by the CS4232 chip present on the board. However, -physical access to the CS4232 connector is difficult, and it is -unlikely (though not impossible) that you will want to use it. - -An older version of this driver introduced an additional kernel config -variable which controlled whether or not the CS4232 MIDI interface was -configured. Because of Alan Cox's work on modularizing the sound -drivers, and now backporting them to 2.0.34 kernels, there seems to be -little reason to support "static" configuration variables, and so this -has been abandoned in favor of *only* module parameters. Specifying -"mpuio" and "mpuirq" for the cs4232 parameter will result in the -CS4232 MIDI interface being configured; leaving them unspecified will -leave it unconfigured (and thus unusable). - -BTW, I have heard from one Tropez+ user that the CS4232 interface is -more reliable than the ICS2115 one. I have had no problems with the -latter, and I don't have the right cable to test the former one -out. Reports welcome. - -********************************************************************** -2) Why does line XXX of the code look like this .... ? -********************************************************************** - -Either because its not finished yet, or because you're a better coder -than I am, or because you don't understand some aspect of how the card -or the code works. - -I absolutely welcome comments, criticisms and suggestions about the -design and implementation of the driver. - -********************************************************************** -3) What files are included ? -********************************************************************** - - drivers/sound/README.wavefront -- this file - - drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers - needed to make the rest of this work - DO NOT USE IF YOU'VE APPLIED THEM - BEFORE, OR HAVE 2.1.109 OR ABOVE - - drivers/sound/wavfront.c -- the driver - drivers/sound/ys225.h -- data declarations for FX config - drivers/sound/ys225.c -- data definitions for FX config - drivers/sound/wf_midi.c -- the "uart401" driver - to support virtual MIDI mode. - include/wavefront.h -- the header file - Documentation/sound/Tropez+ -- short docs on configuration - -********************************************************************** -4) How do I compile/install/use it ? -********************************************************************** - -PART ONE: install the source code into your sound driver directory - - cd - tar -zxvf - -PART TWO: apply the patches - - DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 - AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). - - cd drivers/sound - patch < wavefront.patch - -PART THREE: configure your kernel - - cd - make xconfig (or whichever config option you use) - - - choose YES for Sound Support - - choose MODULE (M) for OSS Sound Modules - - choose MODULE(M) to YM3812/OPL3 support - - choose MODULE(M) for WaveFront support - - choose MODULE(M) for CS4232 support - - - choose "N" for everything else (unless you have other - soundcards you want support for) - - - make dep - make boot - . - . - . - - make modules - . - . - . - make modules_install - -Here's my autoconf.h SOUND section: - -/* - * Sound - */ -#define CONFIG_SOUND 1 -#undef CONFIG_SOUND_OSS -#define CONFIG_SOUND_OSS_MODULE 1 -#undef CONFIG_SOUND_PAS -#undef CONFIG_SOUND_SB -#undef CONFIG_SOUND_ADLIB -#undef CONFIG_SOUND_GUS -#undef CONFIG_SOUND_MPU401 -#undef CONFIG_SOUND_PSS -#undef CONFIG_SOUND_MSS -#undef CONFIG_SOUND_SSCAPE -#undef CONFIG_SOUND_TRIX -#undef CONFIG_SOUND_MAD16 -#undef CONFIG_SOUND_WAVEFRONT -#define CONFIG_SOUND_WAVEFRONT_MODULE 1 -#undef CONFIG_SOUND_CS4232 -#define CONFIG_SOUND_CS4232_MODULE 1 -#undef CONFIG_SOUND_MAUI -#undef CONFIG_SOUND_SGALAXY -#undef CONFIG_SOUND_OPL3SA1 -#undef CONFIG_SOUND_SOFTOSS -#undef CONFIG_SOUND_YM3812 -#define CONFIG_SOUND_YM3812_MODULE 1 -#undef CONFIG_SOUND_VMIDI -#undef CONFIG_SOUND_UART6850 -/* - * Additional low level sound drivers - */ -#undef CONFIG_LOWLEVEL_SOUND - -************************************************************ -6) How do I configure my card ? -************************************************************ - -You need to edit /etc/modules.conf. Here's mine (edited to show the -relevant details): - - # Sound system - alias char-major-14 wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - pre-install wavefront modprobe "-k" "cs4232" - post-install wavefront modprobe "-k" "opl3" - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] FM synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - In theory, any not-in-use IO port address would work, but - just use 0x388 and stick with the crowd. - -********************************************************************** -7) What about firmware ? -********************************************************************** - -Turtle Beach have not given me permission to distribute their firmware -for the ICS2115. However, if you have a WaveFront card, then you -almost certainly have the firmware, and if not, its freely available -on their website, at: - - http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus - -The file is called WFOS2001.MOT (for the Tropez+). - -This driver, however, doesn't use the pure firmware as distributed, -but instead relies on a somewhat processed form of it. You can -generate this very easily. Following an idea from Andrew Veliath's -Pinnacle driver, the following flex program will generate the -processed version: - ----- cut here ------------------------- -%option main -%% -^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); -<> { fputc ('\0', stdout); return; } -\n {} -. {} ----- cut here ------------------------- - -To use it, put the above in file (say, ws.l) compile it like this: - - shell> flex -ows.c ws.l - shell> cc -o ws ws.c - -and then use it like this: - - ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os - -If you put it somewhere else, you'll always have to use the wf_ospath -module parameter (see below) or alter the source code. - -********************************************************************** -7) How do I get it working ? -********************************************************************** - -Optionally, you can reboot with the "new" kernel (even though the only -changes have really been made to a module). - -Then, as root do: - - modprobe wavefront - -You should get something like this in /var/log/messages: - - WaveFront: firmware 1.20 already loaded. - -or - - WaveFront: no response to firmware probe, assume raw. - -then: - - WaveFront: waiting for memory configuration ... - WaveFront: hardware version 1.64 - WaveFront: available DRAM 8191k - WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty - WaveFront: 128 programs slots in use - WaveFront: 256 patch slots filled, 142 in use - -The whole process takes about 16 seconds, the longest waits being -after reporting the hardware version (during the firmware download), -and after reporting program status (during patch status inquiry). Its -shorter (about 10 secs) if the firmware is already loaded (i.e. only -warm reboots since the last firmware load). - -The "available DRAM" line will vary depending on how much added RAM -your card has. Mine has 8MB. - -To check basically functionality, use play(1) or splay(1) to send a -.WAV or other audio file through the audio portion. Then use playmidi -to play a General MIDI file. Try the "-D 0" to hear the -difference between sending MIDI to the WaveFront and using the OPL/3, -which is the default (I think ...). If you have an external synth(s) -hooked to the soundcard, you can use "-e" to route to the -external synth(s) (in theory, -D 1 should work as well, but I think -there is a bug in playmidi which prevents this from doing what it -should). - -********************************************************************** -8) What are the module parameters ? -********************************************************************** - -Its best to read wavefront.c for this, but here is a summary: - -integers: - wf_raw - if set, ignore apparent presence of firmware - loaded onto the ICS2115, reset the whole - board, and initialize it from scratch. (default = 0) - - fx_raw - if set, always initialize the YSS225 processor - on the Tropez plus. (default = 1) - - < The next 4 are basically for kernel hackers to allow - tweaking the driver for testing purposes. > - - wait_usecs - loop timer used when waiting for - status conditions on the board. - The default is 150. - - debug_default - debugging flags. See sound/wavefront.h - for WF_DEBUG_* values. Default is zero. - Setting this allows you to debug the - driver during module installation. -strings: - ospath - path to get to the pre-processed OS firmware. - (default: /etc/sound/wavefront.os) - -********************************************************************** -9) Who should I contact if I have problems? -********************************************************************** - -Just me: Paul Barton-Davis - - diff -urN linux-2.5.6-pre3/Documentation/sound/alsa/CMIPCI.txt linux-2.5.6/Documentation/sound/alsa/CMIPCI.txt --- linux-2.5.6-pre3/Documentation/sound/alsa/CMIPCI.txt Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/alsa/CMIPCI.txt Thu Mar 7 18:24:41 2002 @@ -0,0 +1,228 @@ + Brief Notes on C-Media 8738/8338 Driver + ======================================= + + Takashi Iwai + + +Front/Rear Multi-channel Playback +--------------------------------- + +CM8x38 chip can use ADC as the second DAC so that two different stereo +channels can be used for front/rear playbacks. Since there are two +DACs, both streams are handled independently unlike the 4/6ch multi- +channel playbacks in the section below. + +As default, ALSA driver assigns the first PCM device (i.e. hw:0,0 for +card#0) for front and 4/6ch playbacks, while the second PCM device +(hw:0,1) is assigned to the second DAC for rear playback. + +There are slight difference between two DACs. + +- The first DAC supports U8 and S16LE formats, while the second DAC + supports only S16LE. +- The second DAC supports only two channel stereo. + +Please note that the CM8x38 DAC doesn't support continuous playback +rate but only fixed rates: 5512, 8000, 11025, 16000, 22050, 32000, +44100 and 48000 Hz. + +The rear output can be heard only when "Four Channel Mode" switch is +disabled. Otherwise no signal will be routed to the rear speakers. +As default it's turned on. + +*** WARNING *** +When "Four Channel Mode" switch is off, the output from rear speakers +will be FULL VOLUME regardless of Master and PCM volumes. +This might damage your audio equipment. Please disconnect speakers +before your turn off this switch. +*** WARNING *** + +[ Well.. I once got the output with correct volume (i.e. same with the + front one) and was so excited. It was even with "Four Channel" bit + on and "double DAC" mode. Actually I could hear separate 4 channels + from front and rear speakers! But.. after reboot, all was gone. + It's a very pity that I didn't save the register dump at that + time.. Maybe there is an unknown register to achieve this... ] + +If your card has an extra output jack for the rear output, the rear +playback should be routed there as default. If not, there is a +control switch in the driver "Line-In As Rear", which you can change +via alsamixer or somewhat else. When this switch is on, line-in jack +is used as rear output. + +There are two more controls regarding to the rear output. +The "Exchange DAC" switch is used to exchange front and rear playback +routes, i.e. the 2nd DAC is output from front output. + + +4/6 Multi-Channel Playback +-------------------------- + +The recent CM8738 chips support for the 4/6 multi-channel playback +function. This is useful especially for AC3 decoding. + +When the multi-channel is supported, the driver name has a suffix +"-MC" such like "CMI8738-MC6". You can check this name from +/proc/asound/cards. + +When the 4/6-ch output is enabled, the front DAC accepts up to 6 (or +4) channels. This is different from the dual DACs described in the +previous section. While the dual DAC supports two different rates or +formats, the 4/6-ch playback supports only the same condition for all +channels. + +For using 4/6 channel playback, you need to specify the PCM channels +as you like and set the format S16LE. For example, for playback with +4 channels, + + snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); + // or mmap if you like + snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(pcm, hw, 4); + +and use the interleaved 4 channel data. + +There is a control switch, "Line-In As Bass". As you can imagine from +its name, the line-in jack is used for the bass (5th and 6th channels) +output. + + +Digital I/O +----------- + +The CM8x38 provides the excellent SPDIF capability with very chip +price (yes, that's the reason I bought the card :) + +The SPDIF playback and capture are done via the third PCM device +(hw:0,2). Usually this is assigned to the PCM device "spdif". +The available rates are 44100 and 48000 Hz. +For playback with aplay, you can run like below: + + % aplay -Dhw:0,2 foo.wav + +or + + % aplay -Dspdif foo.wav + +So far, only S16LE format is supported. Still no 24bit. Sorry, not +enough info for this. + +The playback and capture over SPDIF use normal DAC and ADC, +respectively, so you cannot playback both analog and digital streams +simultaneously. + +To enable SPDIF output, you need to turn on "IEC958 Output Switch" +control via mixer or alsactl. Then you'll see the red light on from +the card so you know that's working obviously :) +The SPDIF input is always enabled, so you can hear SPDIF input data +from line-out with "IEC958 In Monitor" switch at any time (see +below). + +You can play via SPDIF even with the first device (hw:0,0), +but SPDIF is enabled only when the proper format (S16LE), sample rate +(441100 or 48000) and channels (2) are used. Otherwise it's turned +off. (Also don't forget to turn on "IEC958 Output Switch", too.) + + +Additionally there are relevant control switches: + +"IEC958 Mix Analog" - Mix analog PCM playback and FM-OPL/3 streams and + output through SPDIF. This switch appears only on old chip + models (CM8738 033 and 037). + Note: without this control you can output PCM to SPDIF. + This is "mixing" of streams, so e.g. it's not for AC3 output + (see the next section). + +"IEC958 In Select" - Select SPDIF input, the internal CD-in (false) + and the external input (true). This switch appears only on + the chip models 039 or later. + +"IEC958 Loop" - SPDIF input data is loop back into SPDIF + output (aka bypass) + +"IEC958 Copyright" - Set the copyright bit. + +"IEC958 5V" - Select 0.5V (coax) or 5V (optical) interface. + On some cards this doesn't work and you need to change the + configuration with hardware dip-switch. + +"IEC958 In Monitor" - SPDIF input is routed to DAC. + +"IEC958 In Phase Inverse" - Set SPDIF input format as inverse. + [FIXME: this doesn't work on all chips..] + +"IEC958 In Valid" - Set input validity flag detection. + +Note: When "PCM Playback Switch" is on, you'll hear the digital output +stream through analog line-out. + + +The AC3 (RAW DIGITAL) OUTPUT +---------------------------- + +The driver supports raw digital (typically AC3) i/o over SPDIF. This +can be toggled via IEC958 playback control, but usually you need to +access it via alsa-lib. See alsa-lib documents for more details. + +On the raw digital mode, the "PCM Playback Switch" is automatically +turned off so that non-audio data is heard from the analog line-out. +Similarly the following switches are off: "IEC958 Mix Analog" and +"IEC958 Loop". The switches are resumed after closing the SPDIF PCM +device automatically to the previous state. + + +ANALOG MIXER INTERFACE +---------------------- + +The mixer interface on CM8x38 is similar to SB16. +There are Master, PCM, Synth, CD, Line, Mic and PC Speaker playback +volumes. Synth, CD, Line and Mic have playback and capture switches, +too, as well as SB16. + +In addition to the standard SB mixer, CM8x38 provides more functions. +- PCM playback switch +- PCM capture switch (to capture the data sent to DAC) +- Mic Boost switch +- Mic capture volume +- Aux playback volume/switch and capture switch +- 3D control switch + + +MIDI CONTROLLER +--------------- + +The MPU401-UART interface is enabled as default only for the first +(CMIPCI) card. You need to set module option "snd_midi_port" properly +for the 2nd (CMIPCI) card. + +There is _no_ hardware wavetable function on this chip (except for +OPL3 synth below). +What's said as MIDI synth on Windows is a software synthesizer +emulation. On Linux use TiMidity or other softsynth program for +playing MIDI music. + + +FM OPL/3 Synth +-------------- + +The FM OPL/3 is also enabled as default only for the first card. +Set "snd_fm_port" module option for more cards. + +The output quality of FM OPL/3 is, however, very weird. +I don't know why.. + + +Joystick and Modem +------------------ + +The joystick and modem should be available by enabling the control +switch "Joystick" and "Modem" respectively. But I myself have never +tested them yet. + + +Debugging Information +--------------------- + +The registers are shown in /proc/asound/cardX/cmipci. If you have any +problem (especially unexpected behavior of mixer), please attach the +output of this proc file together with the bug report. diff -urN linux-2.5.6-pre3/Documentation/sound/alsa/SB-Live-mixer.txt linux-2.5.6/Documentation/sound/alsa/SB-Live-mixer.txt --- linux-2.5.6-pre3/Documentation/sound/alsa/SB-Live-mixer.txt Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/alsa/SB-Live-mixer.txt Thu Mar 7 18:24:41 2002 @@ -0,0 +1,356 @@ + + Sound Blaster Live mixer / default DSP code + =========================================== + + +The EMU10K1 chips have a DSP part which can be programmed to support +various ways of sample processing, which is described here. +(This acticle does not deal with the overall functionality of the +EMU10K1 chips. See the manuals section for further details.) + +The ALSA driver programs this portion of chip by default code +(can be altered later) which offers the following functionality: + + +1) IEC958 (S/PDIF) raw PCM +-------------------------- + +This PCM device (it's the 4th PCM device (index 3!) and first subdevice +(index 0) for a given card) allows to forward 48kHz, stereo, 16-bit +little endian streams without any modifications to the digital output +(coaxial or optical). The universal interface allows the creation of up +to 8 raw PCM devices operating at 48kHz, 16-bit little endian. It would +be easy to add support for multichannel devices to the current code, +but the conversion routines exist only for stereo (2-channel streams) +at the time. + +Look to tram_poke routines in lowlevel/emu10k1/emufx.c for more details. + + +2) Digital mixer controls +------------------------- + +These controls are built using the DSP instructions. They offer extended +functionality. Only the default build-in code in the ALSA driver is described +here. Note that the controls work as attenuators: the maximum value is the +neutral position leaving the signal unchanged. Note that if the same destination +is mentioned in multiple controls, the signal is accumulated and can be wrapped +(set to maximal or minimal value without checking of overflow). + + +Explanation of used abbreviations: + +DAC - digital to analog converter +ADC - analog to digital converter +I2S - one-way three wire serial bus for digital sound by Philips Semiconductors + (this standard is used for connecting standalone DAC and ADC converters) +LFE - low frequency effects (subwoofer signal) +AC97 - a chip containing an analog mixer, DAC and ADC converters +IEC958 - S/PDIF +FX-bus - the EMU10K1 chip has an effect bus containing 16 accumulators. + Each of the synthesizer voices can feed its output to these accumulators + and the DSP microcontroller can operate with the resulting sum. + + +name='Wave Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Wave Surround Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operates +separately (they are not inside the AC97 codec). + +name='Wave Center Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? right DAC PCM slot of the AC97 codec. + +name='Wave LFE Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is mixed to mono signal (single channel) and forwarded to +the ??rear?? left DAC PCM slot of the AC97 codec. + +name='Wave Capture Volume',index=0 +name='Wave Capture Switch',index=0 + +These controls are used to attenuate samples for left and right PCM FX-bus +accumulator. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Music Playback Volume',index=0 + +This control is used to attenuate samples for left and right MIDI FX-bus +accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Music Capture Volume',index=0 +name='Music Capture Switch',index=0 + +These controls are used to attenuate samples for left and right MIDI FX-bus +accumulator. ALSA uses accumulators 4 and 5 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Surround Digital Playback Volume',index=0 + +This control is used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result samples are forwarded to the rear I2S DACs. These DACs operate +separately (they are not inside the AC97 codec). + +name='Surround Digital Capture Volume',index=0 +name='Surround Digital Capture Switch',index=0 + +These controls are used to attenuate samples for left and right rear PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Center Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? right DAC PCM slot of the AC97 codec. + +name='LFE Playback Volume',index=0 + +This control is used to attenuate sample for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample. The result sample is forwarded +to the ??rear?? left DAC PCM slot of the AC97 codec. + +name='AC97 Playback Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result samples are forwarded to the front DAC PCM +slots of the AC97 codec. +******************************************************************************** +*** Note: This control should be zero for the standard operations, otherwise *** +*** a digital loopback is activated. *** +******************************************************************************** + +name='AC97 Capture Volume',index=0 + +This control is used to attenuate samples for left and right front ADC PCM slots +of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to +the standard capture PCM device). +******************************************************************************** +*** Note: This control should be 100 (maximal value), otherwise no analog *** +*** inputs of the AC97 codec can be captured (recorded). *** +******************************************************************************** + +name='IEC958 TTL Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='IEC958 TTL Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='Zoom Video Playback Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the AC97 codec. + +name='Zoom Video Capture Volume',index=0 + +This control is used to attenuate samples from left and right zoom video +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='IEC958 Optical Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital input. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Optical Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='IEC958 Coaxial Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the front DAC PCM slots +of the AC97 codec. + +name='IEC958 Coaxial Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 coaxial +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='Line LiveDrive Playback Volume',index=0 +name='Line LiveDrive Playback Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the front +DAC PCM slots of the AC97 codec. + +name='Line LiveDrive Capture Volume',index=1 +name='Line LiveDrive Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the LiveDrive). The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Tone Control - Switch',index=0 + +This control turns the tone control on or off. The samples for front, rear +and center / LFE outputs are affected. + +name='Tone Control - Bass',index=0 + +This control sets the bass intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='Tone Control - Treble',index=0 + +This control sets the treble intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='IEC958 Optical Raw Playback Switch',index=0 + +If this switch is on, then the samples for the IEC958 (S/PDIF) digital +output are taken only from the raw FX8010 PCM, otherwise standard front +PCM samples are taken. + +name='Headphone Playback Volume',index=1 + +This control attenuates the samples for the headphone output. + +name='Headphone Center Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +left headphone output (useful for SB Live cards without separate center/LFE +output). + +name='Headphone LFE Playback Switch',index=1 + +If this switch is on, then the sample for the center PCM is put to the +right headphone output (useful for SB Live cards without separate center/LFE +output). + + +3) PCM stream related controls +------------------------------ + +name='EMU10K1 PCM Volume',index 0-31 + +Channel volume attenuation in range 0-0xffff. The maximum value (no +attenuation) is default. The channel mapping for three values is +as follows: + + 0 - mono, default 0xffff (no attenuation) + 1 - left, default 0xffff (no attenuation) + 2 - right, default 0xffff (no attenuation) + +name='EMU10K1 PCM Send Routing',index 0-31 + +This control specifies the destination - FX-bus accumulators. There are +twelve values with this mapping: + + 0 - mono, A destination (FX-bus 0-15), default 0 + 1 - mono, B destination (FX-bus 0-15), default 1 + 2 - mono, C destination (FX-bus 0-15), default 2 + 3 - mono, D destination (FX-bus 0-15), default 3 + 4 - left, A destination (FX-bus 0-15), default 0 + 5 - left, B destination (FX-bus 0-15), default 1 + 6 - left, C destination (FX-bus 0-15), default 2 + 7 - left, D destination (FX-bus 0-15), default 3 + 8 - right, A destination (FX-bus 0-15), default 0 + 9 - right, B destination (FX-bus 0-15), default 1 + 10 - right, C destination (FX-bus 0-15), default 2 + 11 - right, D destination (FX-bus 0-15), default 3 + +Don't forget that it's illegal to assign a channel to the same FX-bus accumulator +more than once (it means 0=0 && 1=0 is an invalid combination). + +name='EMU10K1 PCM Send Volume',index 0-31 + +It specifies the attenuation (amount) for given destination in range 0-255. +The channel mapping is following: + + 0 - mono, A destination attn, default 255 (no attenuation) + 1 - mono, B destination attn, default 255 (no attenuation) + 2 - mono, C destination attn, default 0 (mute) + 3 - mono, D destination attn, default 0 (mute) + 4 - left, A destination attn, default 255 (no attenuation) + 5 - left, B destination attn, default 0 (mute) + 6 - left, C destination attn, default 0 (mute) + 7 - left, D destination attn, default 0 (mute) + 8 - right, A destination attn, default 0 (mute) + 9 - right, B destination attn, default 255 (no attenuation) + 10 - right, C destination attn, default 0 (mute) + 11 - right, D destination attn, default 0 (mute) + + + +4) MANUALS/PATENTS: +------------------- + +ftp://opensource.creative.com/pub/doc +------------------------------------- + + Files: + LM4545.pdf AC97 Codec + + m2049.pdf The EMU10K1 Digital Audio Processor + + hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects + + +WIPO Patents +------------ + Patent numbers: + WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999) + streams + + WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999) + + WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction + Execution and Audio Data Sequencing (Jan. 14, 1999) + + +US Patents (http://www.uspto.gov/) +---------------------------------- + + US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999) + + US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999) + with a multiport memory onto which multiple asynchronous + digital sound samples can be concurrently loaded + + US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999) + + US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000) + + US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000) + system bus with prioritization and modification of bus transfers + in accordance with loop ends and minimum block sizes + + US 6151670 Method for conserving memory storage using a (Nov. 21, 2000) + pool of short term memory registers + + US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001) + a common interrupt by associating programs to GP registers, + defining interrupt register, polling GP registers, and invoking + callback routine associated with defined interrupt register diff -urN linux-2.5.6-pre3/Documentation/sound/alsa/seq_oss.html linux-2.5.6/Documentation/sound/alsa/seq_oss.html --- linux-2.5.6-pre3/Documentation/sound/alsa/seq_oss.html Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/alsa/seq_oss.html Thu Mar 7 18:24:41 2002 @@ -0,0 +1,409 @@ + + + + OSS Sequencer Emulation on ALSA + + + +
+

+ +

+ +
+

+OSS Sequencer Emulation on ALSA

+ +
+

Copyright (c) 1998,1999 by Takashi Iwai +<iwai@ww.uni-erlangen.de> +

ver.0.1.8; Nov. 16, 1999 +

+ +

+ +

+1. Description

+This directory contains the OSS sequencer emulation driver on ALSA. Note +that this program is still in the development state. +

What this does - it provides the emulation of the OSS sequencer, access +via +/dev/sequencer and /dev/music devices. +The most of applications using OSS can run if the appropriate ALSA +sequencer is prepared. +

The following features are emulated by this driver: +

    +
  • +Normal sequencer and MIDI events:
  • + +
    They are converted to the ALSA sequencer events, and sent to the corresponding +port. +
  • +Timer events:
  • + +
    The timer is not selectable by ioctl. The control rate is fixed to +100 regardless of HZ. That is, even on Alpha system, a tick is always +1/100 second. The base rate and tempo can be changed in /dev/music. + +
  • +Patch loading:
  • + +
    It purely depends on the synth drivers whether it's supported since +the patch loading is realized by callback to the synth driver. +
  • +I/O controls:
  • + +
    Most of controls are accepted. Some controls +are dependent on the synth driver, as well as even on original OSS.
+Furthermore, you can find the following advanced features: +
    +
  • +Better queue mechanism:
  • + +
    The events are queued before processing them. +
  • +Multiple applications:
  • + +
    You can run two or more applications simultaneously (even for OSS sequencer)! +However, each MIDI device is exclusive - that is, if a MIDI device is opened +once by some application, other applications can't use it. No such a restriction +in synth devices. +
  • +Real-time event processing:
  • + +
    The events can be processed in real time without using out of bound +ioctl. To switch to real-time mode, send ABSTIME 0 event. The followed +events will be processed in real-time without queued. To switch off the +real-time mode, send RELTIME 0 event. +
  • +/proc interface:
  • + +
    The status of applications and devices can be shown via /proc/asound/seq/oss +at any time. In the later version, configuration will be changed via /proc +interface, too.
+ +

+2. Installation

+Run configure script with both sequencer support (--with-sequencer=yes) +and OSS emulation (--with-oss=yes) options. A module snd-seq-oss.o +will be created. If the synth module of your sound card supports for OSS +emulation (so far, only Emu8000 driver), this module will be loaded automatically. +Otherwise, you need to load this module manually. +

At beginning, this module probes all the MIDI ports which have been +already connected to the sequencer. Once after that, the creation and deletion +of ports are watched by announcement mechanism of ALSA sequencer. +

The available synth and MIDI devices can be found in proc interface. +Run "cat /proc/asound/seq/oss", and check the devices. For example, +if you use an AWE64 card, you'll see like the following: +

        OSS sequencer emulation version 0.1.8
+        ALSA client number 63
+        ALSA receiver port 0
+
+        Number of applications: 0
+
+        Number of synth devices: 1
+
+        synth 0: [EMU8000]
+          type 0x1 : subtype 0x20 : voices 32
+          capabilties : ioctl enabled / load_patch enabled
+
+        Number of MIDI devices: 3
+
+        midi 0: [Emu8000 Port-0] ALSA port 65:0
+          capability write / opened none
+
+        midi 1: [Emu8000 Port-1] ALSA port 65:1
+          capability write / opened none
+
+        midi 2: [0: MPU-401 (UART)] ALSA port 64:0
+          capability read/write / opened none
+Note that the device number may be different from the information of +/proc/asound/oss-devices +or ones of the original OSS driver. Use the device number listed in /proc/asound/seq/oss +to play via OSS sequencer emulation. +

+3. Using Synthesizer Devices

+Run your favorite program. I've tested playmidi-2.4, awemidi-0.4.3, gmod-3.1 +and xmp-1.1.5. You can load samples via /dev/sequencer like sfxload, +too. +

If the lowlevel driver supports multiple access to synth devices (like +Emu8000 driver), two or more applications are allowed to run at the same +time. +

+4. Using MIDI Devices

+So far, only MIDI output was tested. MIDI input was not checked at all, +but hopefully it will work. Use the device number listed in /proc/asound/seq/oss. +Be aware that these numbers are mostly different from the list in +/proc/asound/oss-devices. +

+5. Module Options

+The following module options are available: +
    +
  • +maxqlen
  • + +
    specifies the maximum read/write queue length. This queue is private +for OSS sequencer, so that it is independent from the queue length of ALSA +sequencer. Default value is 1024. +
  • +seq_oss_debug
  • + +
    specifies the debug level and accepts zero (= no debug message) or +positive integer. Default value is 0.
+ +

+6. Queue Mechanism

+OSS sequencer emulation uses an ALSA priority queue. The +events from /dev/sequencer are processed and put onto the queue +specified by module option. +

All the events from /dev/sequencer are parsed at beginning. +The timing events are also parsed at this moment, so that the events may +be processed in real-time. Sending an event ABSTIME 0 switches the operation +mode to real-time mode, and sending an event RELTIME 0 switches it off. +In the real-time mode, all events are dispatched immediately. +

The queued events are dispatched to the corresponding ALSA sequencer +ports after scheduled time by ALSA sequencer dispatcher. +

If the write-queue is full, the application sleeps until a certain amount +(as default one half) becomes empty in blocking mode. The synchronization +to write timing was implemented, too. +

The input from MIDI devices or echo-back events are stored on read FIFO +queue. If application reads /dev/sequencer in blocking mode, the +process will be awaked. + +

+7. Interface to Synthesizer Device

+ +

+7.1. Registration

+To register an OSS synthesizer device, use snd_seq_oss_synth_register +function. +
int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
+                              snd_seq_oss_callback_t *oper, void *private_data)
+The arguments name, type, subtype and +nvoices +are used for making the appropriate synth_info structure for ioctl. The +return value is an index number of this device. This index must be remembered +for unregister. If registration is failed, -errno will be returned. +

To release this device, call snd_seq_oss_synth_unregister function: +

int snd_seq_oss_synth_unregister(int index),
+where the index is the index number returned by register function. +

+7.2. Callbacks

+OSS synthesizer devices have capability for sample downloading and ioctls +like sample reset. In OSS emulation, these special features are realized +by using callbacks. The registration argument oper is used to specify these +callbacks. The following callback functions must be defined: +
snd_seq_oss_callback_t:
+        int (*open)(snd_seq_oss_arg_t *p, void *closure);
+        int (*close)(snd_seq_oss_arg_t *p);
+        int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+        int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+        int (*reset)(snd_seq_oss_arg_t *p);
+Except for open and close callbacks, they are allowed
+to be NULL.
+

Each callback function takes the argument type snd_seq_oss_arg_t as the +first argument. +

struct snd_seq_oss_arg_t {
+        int app_index;
+        int file_mode;
+        int seq_mode;
+        snd_seq_addr_t addr;
+        void *private_data;
+        int event_passing;
+};
+The first three fields, app_index, file_mode and +seq_mode +are initialized by OSS sequencer. The app_index is the application +index which is unique to each application opening OSS sequencer. The +file_mode +is bit-flags indicating the file operation mode. See +seq_oss.h +for its meaning. The seq_mode is sequencer operation mode. In +the current version, only SND_OSSSEQ_MODE_SYNTH is used. +

The next two fields, addr and private_data, must be +filled by the synth driver at open callback. The addr contains +the address of ALSA sequencer port which is assigned to this device. If +the driver allocates memory for private_data, it must be released +in close callback by itself. +

The last field, event_passing, indicates how to translate note-on +/ off events. In PROCESS_EVENTS mode, the note 255 is regarded +as velocity change, and key pressure event is passed to the port. In PASS_EVENTS +mode, all note on/off events are passed to the port without modified. PROCESS_KEYPRESS +mode checks the note above 128 and regards it as key pressure event (mainly +for Emu8000 driver). +

+7.2.1. Open Callback

+The open is called at each time this device is opened by an application +using OSS sequencer. This must not be NULL. Typically, the open callback +does the following procedure: +
    +
  1. +Allocate private data record.
  2. + +
  3. +Create an ALSA sequencer port.
  4. + +
  5. +Set the new port address on arg->addr.
  6. + +
  7. +Set the private data record pointer on arg->private_data.
  8. +
+Note that the type bit-flags in port_info of this synth port must NOT contain +TYPE_MIDI_GENERIC +bit. Instead, TYPE_SPECIFIC should be used. Also, CAP_SUBSCRIPTION +bit should NOT be included, too. This is necessary to tell it from other +normal MIDI devices. If the open procedure succeeded, return zero. Otherwise, +return -errno. +

+7.2.2 Ioctl Callback

+The ioctl callback is called when the sequencer receives device-specific +ioctls. The following two ioctls should be processed by this callback: +
    +
  • +IOCTL_SEQ_RESET_SAMPLES
  • + +
    reset all samples on memory -- return 0 +
  • +IOCTL_SYNTH_MEMAVL
  • + +
    return the available memory size +
  • +FM_4OP_ENABLE
  • + +
    can be ignored usually
+The other ioctls are processed inside the sequencer without passing to +the lowlevel driver. +

+7.2.3 Load_Patch Callback

+The load_patch callback is used for sample-downloading. This callback +must read the data on user-space and transfer to each device. Return 0 +if succeeded, and -errno if failed. The format argument is the patch key +in patch_info record. The buf is user-space pointer where patch_info record +is stored. The offs can be ignored. The count is total data size of this +sample data. +

+7.2.4 Close Callback

+The close callback is called when this device is closed by the +applicaion. If any private data was allocated in open callback, it must +be released in the close callback. The deletion of ALSA port should be +done here, too. This callback must not be NULL. +

+7.2.5 Reset Callback

+The reset callback is called when sequencer device is reset or +closed by applications. The callback should turn off the sounds on the +relevant port immediately, and initialize the status of the port. If this +callback is undefined, OSS seq sends a HEARTBEAT event to the +port. +

+7.3 Events

+Most of the events are processed by sequencer and translated to the adequate +ALSA sequencer events, so that each synth device can receive by input_event +callback of ALSA sequencer port. The following ALSA events should be implemented +by the driver: +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ALSA eventOriginal OSS events
NOTEONSEQ_NOTEON +
MIDI_NOTEON
NOTESEQ_NOTEOFF +
MIDI_NOTEOFF
KEYPRESSMIDI_KEY_PRESSURE
CHANPRESSSEQ_AFTERTOUCH +
MIDI_CHN_PRESSURE
PGMCHANGESEQ_PGMCHANGE +
MIDI_PGM_CHANGE
PITCHBENDSEQ_CONTROLLER(CTRL_PITCH_BENDER) +
MIDI_PITCH_BEND
CONTROLLERMIDI_CTL_CHANGE +
SEQ_BALANCE (with CTL_PAN)
CONTROL14SEQ_CONTROLLER
REGPARAMSEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)
SYSEXSEQ_SYSEX
+ +

The most of these behavior can be realized by MIDI emulation driver +included in the Emu8000 lowlevel driver. In the future release, this module +will be independent. +

Some OSS events (SEQ_PRIVATE and SEQ_VOLUME events) are passed as event +type SND_SEQ_OSS_PRIVATE. The OSS sequencer passes these event 8 byte +packets without any modification. The lowlevel driver should process these +events appropriately. +

+8. Interface to MIDI Device

+Since the OSS emulation probes the creation and deletion of ALSA MIDI sequencer +ports automatically by receiving announcement from ALSA sequencer, the +MIDI devices don't need to be registered explicitly like synth devices. +However, the MIDI port_info registered to ALSA sequencer must include a group +name SND_SEQ_GROUP_DEVICE and a capability-bit CAP_READ or +CAP_WRITE. Also, subscription capabilities, CAP_SUBS_READ or CAP_SUBS_WRITE, +must be defined, too. If these conditions are not satisfied, the port is not +registered as OSS sequencer MIDI device. +

The events via MIDI devices are parsed in OSS sequencer and converted +to the corresponding ALSA sequencer events. The input from MIDI sequencer +is also converted to MIDI byte events by OSS sequencer. This works just +a reverse way of seq_midi module. +

+9. Known Problems / TODO's

+ +
    +
  • +Patch loading via ALSA instrument layer is not implemented yet.
  • +
+ + + diff -urN linux-2.5.6-pre3/Documentation/sound/btaudio linux-2.5.6/Documentation/sound/btaudio --- linux-2.5.6-pre3/Documentation/sound/btaudio Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/Documentation/sound/btaudio Wed Dec 31 16:00:00 1969 @@ -1,92 +0,0 @@ - -Intro -===== - -people start bugging me about this with questions, looks like I -should write up some documentation for this beast. That way I -don't have to answer that much mails I hope. Yes, I'm lazy... - - -You might have noticed that the bt878 grabber cards have actually -_two_ PCI functions: - -$ lspci -[ ... ] -00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02) -00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02) -[ ... ] - -The first does video, it is backward compatible to the bt848. The second -does audio. btaudio is a driver for the second function. It's a sound -driver which can be used for recording sound (and _only_ recording, no -playback). As most TV cards come with a short cable which can be plugged -into your sound card's line-in you probably don't need this driver if all -you want to do is just watching TV... - - -Driver Status -============= - -Still somewhat experimental. The driver should work stable, i.e. it -should'nt crash your box. It might not work as expected, have bugs, -not being fully OSS API compilant, ... - -Latest versions are available from http://bytesex.org/bttv/, the -driver is in the bttv tarball. Kernel patches might be available too, -have a look at http://bytesex.org/bttv/listing.html. - -The chip knows two different modes. btaudio registers two dsp -devices, one for each mode. They can not be used at the same time. - - -Digital audio mode -================== - -The chip gives you 16 bit stereo sound. The sample rate depends on -the external source which feeds the bt878 with digital sound via I2S -interface. There is a insmod option (rate) to tell the driver which -sample rate the hardware uses (32000 is the default). - -One possible source for digital sound is the msp34xx audio processor -chip which provides digital sound via I2S with 32 kHz sample rate. My -Hauppauge board works this way. - -The Osprey-200 reportly gives you digital sound with 44100 Hz sample -rate. It is also possible that you get no sound at all. - - -analog mode (A/D) -================= - -You can tell the driver to use this mode with the insmod option "analog=1". -The chip has three analog inputs. Consequently you'll get a mixer device -to control these. - -The analog mode supports mono only. Both 8 + 16 bit. Both are _signed_ -int, which is uncommon for the 8 bit case. Sample rate range is 119 kHz -to 448 kHz. Yes, the number of digits is correct. The driver supports -downsampling by powers of two, so you can ask for more usual sample rates -like 44 kHz too. - -With my Hauppauge I get noisy sound on the second input (mapped to line2 -by the mixer device). Others get a useable signal on line1. - - -some examples -============= - -* read audio data from btaudio (dsp2), send to es1730 (dsp,dsp1): - $ sox -w -r 32000 -t ossdsp /dev/dsp2 -t ossdsp /dev/dsp - -* read audio data from btaudio, send to esound daemon (which might be - running on another host): - $ sox -c 2 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -r 32000 - $ sox -c 1 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -m -r 32000 - - -Have fun, - - Gerd - --- -Gerd Knorr diff -urN linux-2.5.6-pre3/Documentation/sound/cs46xx linux-2.5.6/Documentation/sound/cs46xx --- linux-2.5.6-pre3/Documentation/sound/cs46xx Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/Documentation/sound/cs46xx Wed Dec 31 16:00:00 1969 @@ -1,138 +0,0 @@ - -Documentation for the Cirrus Logic/Crystal SoundFusion cs46xx/cs4280 audio -controller chips (2001/05/11) - -The cs46xx audio driver supports the DSP line of Cirrus controllers. -Specifically, the cs4610, cs4612, cs4614, cs4622, cs4624, cs4630 and the cs4280 -products. This driver uses the generic ac97_codec driver for AC97 codec -support. - - -Features: - -Full Duplex Playback/Capture supported from 8k-48k. -16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. - -APM/PM - 2.2.x PM is enabled and functional. APM can also -be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro -definition. - -DMA playback buffer size is configurable from 16k (defaultorder=2) up to 2Meg -(defaultorder=11). DMA capture buffer size is fixed at a single 4k page as -two 2k fragments. - -MMAP seems to work well with QuakeIII, and test XMMS plugin. - -Myth2 works, but the polling logic is not fully correct, but is functional. - -The 2.4.4-ac6 gameport code in the cs461x joystick driver has been tested -with a Microsoft Sidewinder joystick (cs461x.o and sidewinder.o). This -audio driver must be loaded prior to the joystick driver to enable the -DSP task image supporting the joystick device. - - -Limitations: - -SPDIF is currently not supported. - -Primary codec support only. No secondary codec support is implemented. - - - -NOTES: - -Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, -and has been tested. -Module parameter hercules_egpio_disable set to 1, will force a 0 to EGPIODR -to disable the external amplifier. - -VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control -the external amplifier for the "back" speakers, since we do not -support the secondary codec then this external amp is not -turned on. The primary codec external amplifier is supported but -note that the AC97 EAPD bit is inverted logic (amp_voyetra()). - -DMA buffer size - there are issues with many of the Linux applications -concerning the optimal buffer size. Several applications request a -certain fragment size and number and then do not verify that the driver -has the ability to support the requested configuration. -SNDCTL_DSP_SETFRAGMENT ioctl is used to request a fragment size and -number of fragments. Some applications exit if an error is returned -on this particular ioctl. Therefore, in alignment with the other OSS audio -drivers, no error is returned when a SETFRAGs IOCTL is received, but the -values passed from the app are not used in any buffer calculation -(ossfragshift/ossmaxfrags are not used). -Use the "defaultorder=N" module parameter to change the buffer size if -you have an application that requires a specific number of fragments -or a specific buffer size (see below). - -Debug Interface ---------------- -There is an ioctl debug interface to allow runtime modification of the -debug print levels. This debug interface code can be disabled from the -compilation process with commenting the following define: -#define CSDEBUG_INTERFACE 1 -There is also a debug print methodolgy to select printf statements from -different areas of the driver. A debug print level is also used to allow -additional printfs to be active. Comment out the following line in the -driver to disable compilation of the CS_DBGOUT print statements: -#define CSDEBUG 1 - -Please see the defintions for cs_debuglevel and cs_debugmask for additional -information on the debug levels and sections. - -There is also a csdbg executable to allow runtime manipulation of these -parameters. for a copy email: twoller@crystal.cirrus.com - - - -MODULE_PARMS definitions ------------------------- -MODULE_PARM(defaultorder, "i"); -defaultorder=N -where N is a value from 1 to 12 -The buffer order determines the size of the dma buffer for the driver. -under Linux, a smaller buffer allows more responsiveness from many of the -applications (e.g. games). A larger buffer allows some of the apps (esound) -to not underrun the dma buffer as easily. As default, use 32k (order=3) -rather than 64k as some of the games work more responsively. -(2^N) * PAGE_SIZE = allocated buffer size - -MODULE_PARM(cs_debuglevel, "i"); -MODULE_PARM(cs_debugmask, "i"); -cs_debuglevel=N -cs_debugmask=0xMMMMMMMM -where N is a value from 0 (no debug printfs), to 9 (maximum) -0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source). - -MODULE_PARM(hercules_egpio_disable, "i"); -hercules_egpio_disable=N -where N is a 0 (enable egpio), or a 1 (disable egpio support) - -MODULE_PARM(initdelay, "i"); -initdelay=N -This value is used to determine the millescond delay during the initialization -code prior to powering up the PLL. On laptops this value can be used to -assist with errors on resume, mostly with IBM laptops. Basically, if the -system is booted under battery power then the mdelay()/udelay() functions fail to -properly delay the required time. Also, if the system is booted under AC power -and then the power removed, the mdelay()/udelay() functions will not delay properly. - -MODULE_PARM(powerdown, "i"); -powerdown=N -where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown) - - -MODULE_PARM(external_amp, "i"); -external_amp=1 -if N is set to 1, then force enabling the EAPD support in the primary AC97 codec. -override the detection logic and force the external amp bit in the AC97 0x26 register -to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz -card has inverted logic, so there is a special function for these cards. - -MODULE_PARM(thinkpad, "i"); -thinkpad=1 -if N is set to 1, then force enabling the clkrun functionality. -Currently, when the part is being used, then clkrun is disabled for the entire system, -but re-enabled when the driver is released or there is no outstanding open count. - diff -urN linux-2.5.6-pre3/Documentation/sound/es1370 linux-2.5.6/Documentation/sound/es1370 --- linux-2.5.6-pre3/Documentation/sound/es1370 Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/Documentation/sound/es1370 Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -This soundcard does not have any hardware MIDI synthesizer; -MIDI synthesis has to be done in software. To allow this -the driver/soundcard supports two PCM (/dev/dsp) interfaces. -The second one goes to the mixer "synth" setting and supports -only a limited set of sampling rates (44100, 22050, 11025, 5512). -By setting lineout to 1 on the driver command line -(eg. insmod es1370 lineout=1) it is even possible on some -cards to convert the LINEIN jack into a second LINEOUT jack, thus -making it possible to output four independent audio channels! - -There is a freely available software package that allows -MIDI file playback on this soundcard called Timidity. -See http://www.cgs.fi/~tt/timidity/. - - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/es1371 linux-2.5.6/Documentation/sound/es1371 --- linux-2.5.6-pre3/Documentation/sound/es1371 Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/Documentation/sound/es1371 Wed Dec 31 16:00:00 1969 @@ -1,64 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -This soundcard does not have any hardware MIDI synthesizer; -MIDI synthesis has to be done in software. To allow this -the driver/soundcard supports two PCM (/dev/dsp) interfaces. - -There is a freely available software package that allows -MIDI file playback on this soundcard called Timidity. -See http://www.cgs.fi/~tt/timidity/. - - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/mwave linux-2.5.6/Documentation/sound/mwave --- linux-2.5.6-pre3/Documentation/sound/mwave Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/Documentation/sound/mwave Wed Dec 31 16:00:00 1969 @@ -1,185 +0,0 @@ - How to try to survive an IBM Mwave under Linux SB drivers - - -+ IBM have now released documentation of sorts and Torsten is busy - trying to make the Mwave work. This is not however a trivial task. - ----------------------------------------------------------------------------- - -OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or -not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the -latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! - -I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. - -The steps, then: - - Boot to Linux. - Mount Windows 95 file system (assume mount point = /dos95). - mkdir /dos95/linux - mkdir /dos95/linux/boot - mkdir /dos95/linux/boot/parms - - Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. - - Reboot to Windows 95. - - Edit C:/msdos.sys and add or change the following: - - Logo=0 - BootGUI=0 - - Note that msdos.sys is a text file but it needs to be made 'unhidden', - readable and writable before it can be edited. This can be done with - DOS' "attrib" command. - - Edit config.sys to have multiple config menus. I have one for windows 95 and - five for Linux, like this: ------------- -[menu] -menuitem=W95, Windows 95 -menuitem=LINTP, Linux - ThinkPad -menuitem=LINTP3, Linux - ThinkPad Console -menuitem=LINDOC, Linux - Docked -menuitem=LINDOC3, Linux - Docked Console -menuitem=LIN1, Linux - Single User Mode -REM menudefault=W95,10 - -[W95] - -[LINTP] - -[LINDOC] - -[LINTP3] - -[LINDOC3] - -[LIN1] - -[COMMON] -FILES=30 -REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. -DOS=HIGH,UMB -DEVICE=C:\MWW\MANAGER\MWD50430.EXE -SHELL=c:\command.com /e:2048 -------------------- - -The important things are the SHELL and DEVICE statements. - - Then change autoexec.bat. Basically everything in there originally should be - done ONLY when Windows 95 is booted. Then you add new things specifically - for Linux. Mine is as follows - ---------------- -@ECHO OFF -if "%CONFIG%" == "W95" goto W95 - -REM -REM Linux stuff -REM -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\MWW\DLL; -CALL MWAVE START NOSHOW -c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par - -:W95 -REM -REM Windows 95 stuff -REM -c:\toolkit\guard -SET MSINPUT=C:\MSINPUT -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -REM The following is used by DOS games to recognize Sound Blaster hardware. -REM If hardware settings are changed, please change this line as well. -REM See the Mwave README file for instructions. -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; -SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE -SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB -win - ------------------------- - -Now build a file in c:\linux\boot\parms for each Linux config that you have. - -For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no -initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: - ------------------------ -# LOADLIN @param_file image=other_image root=/dev/other -# -# Linux Console in docking station -# -c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. -root=/dev/hda3 # device which gets mounted as root FS -ro # Other kernel arguments go here. -apm=off -doc=yes -3 ------------------------ - -The doc=yes parameter is an environment variable used by my init scripts, not -a kernel argument. - -However, the apm=off parameter IS a kernel argument! APM, at least in my setup, -causes the kernel to crash when loaded via loadlin (but NOT when loaded via -LILO). The APM stuff COULD be forced out of the kernel via the kernel compile -options. Instead, I got an unofficial patch to the APM drivers that allows them -to be dynamically deactivated via kernel arguments. Whatever you chose to -document, APM, it seems, MUST be off for setups like mine. - -Now make sure C:\MWW\MWCONFIG.REF looks like this: - ----------------------- -[NativeDOS] -Default=SB1.5 -SBInputSource=CD -SYNTH=FM -QSound=OFF -Reverb=OFF -Chorus=OFF -ReverbDepth=5 -ChorusDepth=5 -SBInputVolume=5 -SBMainVolume=10 -SBWaveVolume=10 -SBSynthVolume=10 -WaveTableVolume=10 -AudioPowerDriver=ON - -[FastCFG] -Show=No -HideOption=Off ------------------------------ - -OR the Default= line COULD be - -Default=SBPRO - -Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure -the sound modules and voilà - ThinkPad sound with Linux. - -Now the gotchas - you can either have CD sound OR Mixers but not both. That's a -problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why -this is! - -For some reason MPEG3 files, when played through mpg123, sound like they -are playing at 1/8th speed - not very useful! If you have ANY insight -on why this second thing might be happening, I would be grateful. - -=========================================================== - _/ _/_/_/_/ - _/_/ _/_/ _/ - _/ _/_/ _/_/_/_/ Martin John Bartlett - _/ _/ _/ _/ (martin@nitram.demon.co.uk) -_/ _/_/_/_/ - _/ -_/ _/ - _/_/ -=========================================================== diff -urN linux-2.5.6-pre3/Documentation/sound/oss/AD1816 linux-2.5.6/Documentation/sound/oss/AD1816 --- linux-2.5.6-pre3/Documentation/sound/oss/AD1816 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/AD1816 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,84 @@ +Documentation for the AD1816(A) sound driver +============================================ + +Installation: +------------- + +To get your AD1816(A) based sound card work, you'll have to enable support for +experimental code ("Prompt for development and/or incomplete code/drivers") +and isapnp ("Plug and Play support", "ISA Plug and Play support"). Enable +"Sound card support", "OSS modules support" and "Support for AD1816(A) based +cards (EXPERIMENTAL)" in the sound configuration menu, too. Now build, install +and reboot the new kernel as usual. + +Features: +--------- + +List of features supported by this driver: +- full-duplex support +- supported audio formats: unsigned 8bit, signed 16bit little endian, + signed 16bit big endian, µ-law, A-law +- supported channels: mono and stereo +- supported recording sources: Master, CD, Line, Line1, Line2, Mic +- supports phat 3d stereo circuit (Line 3) + + +Supported cards: +---------------- + +The following cards are known to work with this driver: +- Terratec Base 1 +- Terratec Base 64 +- HP Kayak +- Acer FX-3D +- SY-1816 +- Highscreen Sound-Boostar 32 Wave 3D +- Highscreen Sound-Boostar 16 +- AVM Apex Pro card +- (Aztech SC-16 3D) +- (Newcom SC-16 3D) +- (Terratec EWS64S) + +Cards listed in brackets are not supported reliable. If you have such a card +you should add the extra parameter: + options=1 +when loading the ad1816 module via modprobe. + + +Troubleshooting: +---------------- + +First of all you should check, if the driver has been loaded +properly. + +If loading of the driver succeeds, but playback/capture fails, check +if you used the correct values for irq, dma and dma2 when loading the module. +If one of them is wrong you usually get the following error message: + +Nov 6 17:06:13 tek01 kernel: Sound: DMA (output) timed out - IRQ/DRQ config error? + +If playback/capture is too fast or to slow, you should have a look at +the clock chip of your sound card. The AD1816 was designed for a 33MHz +oscillator, however most sound card manufacturer use slightly +different oscillators as they are cheaper than 33MHz oscillators. If +you have such a card you have to adjust the ad1816_clockfreq parameter +above. For example: For a card using a 32.875MHz oscillator use +ad1816_clockfreq=32875 instead of ad1816_clockfreq=33000. + + +Updates, bugfixes and bugreports: +-------------------------------- + +As the driver is still experimental and under development, you should +watch out for updates. Updates of the driver are available on the +Internet from one of my home pages: + http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html +or: + http://www.tu-darmstadt.de/~tek01/projects/linux.html + +Bugreports, bugfixes and related questions should be sent via E-Mail to: + tek@rbg.informatik.tu-darmstadt.de + +Thorsten Knabe +Christoph Hellwig + Last modified: 2000/09/20 diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ALS linux-2.5.6/Documentation/sound/oss/ALS --- linux-2.5.6-pre3/Documentation/sound/oss/ALS Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ALS Thu Mar 7 18:24:41 2002 @@ -0,0 +1,66 @@ +ALS-007/ALS-100/ALS-200 based sound cards +========================================= + +Support for sound cards based around the Avance Logic +ALS-007/ALS-100/ALS-200 chip is included. These chips are a single +chip PnP sound solution which is mostly hardware compatible with the +Sound Blaster 16 card, with most differences occurring in the use of +the mixer registers. For this reason the ALS code is integrated +as part of the Sound Blaster 16 driver (adding only 800 bytes to the +SB16 driver). + +To use an ALS sound card under Linux, enable the following options as +modules in the sound configuration section of the kernel config: + - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + - FM synthesizer (YM3812/OPL-3) support + - standalone MPU401 support may be required for some cards; for the + ALS-007, when using isapnptools, it is required +Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be +compiled in. If kernel level PnP support is not included, isapnptools will +be required to configure the card before the sound modules are loaded. + +When using kernel level ISAPnP, the kernel should correctly identify and +configure all resources required by the card when the "sb" module is +inserted. Note that the ALS-007 does not have a 16 bit DMA channel and that +the MPU401 interface on this card uses a different interrupt to the audio +section. This should all be correctly configured by the kernel; if problems +with the MPU401 interface surface, try using the standalone MPU401 module, +passing "0" as the "sb" module's "mpu_io" module parameter to prevent the +soundblaster driver attempting to register the MPU401 itself. The onboard +synth device can be accessed using the "opl3" module. + +If isapnptools is used to wake up the sound card (as in 2.2.x), the settings +of the card's resources should be passed to the kernel modules ("sb", "opl3" +and "mpu401") using the module parameters. When configuring an ALS-007, be +sure to specify different IRQs for the audio and MPU401 sections - this card +requires they be different. For "sb", "io", "irq" and "dma" should be set +to the same values used to configure the audio section of the card with +isapnp. "dma16" should be explicitly set to "-1" for an ALS-007 since this +card does not have a 16 bit dma channel; if not specified the kernel will +default to using channel 5 anyway which will cause audio not to work. +"mpu_io" should be set to 0. The "io" parameter of the "opl3" module should +also agree with the setting used by isapnp. To get the MPU401 interface +working on an ALS-007 card, the "mpu401" module will be required since this +card uses separate IRQs for the audio and MPU401 sections and there is no +parameter available to pass a different IRQ to the "sb" driver (whose +inbuilt MPU401 driver would otherwise be fine). Insert the mpu401 module +passing appropriate values using the "io" and "irq" parameters. + +The resulting sound driver will provide the following capabilities: + - 8 and 16 bit audio playback + - 8 and 16 bit audio recording + - Software selection of record source (line in, CD, FM, mic, master) + - Record and playback of midi data via the external MPU-401 + - Playback of midi data using inbuilt FM synthesizer + - Control of the ALS-007 mixer via any OSS-compatible mixer programs. + Controls available are Master (L&R), Line in (L&R), CD (L&R), + DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). + +Jonathan Woithe +jwoithe@physics.adelaide.edu.au +30 March 1998 + +Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 +Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. +Modified 2000-11-19 by Jonathan Woithe, jwoithe@physics.adelaide.edu.au + - updated information for kernel 2.4.x. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/AWE32 linux-2.5.6/Documentation/sound/oss/AWE32 --- linux-2.5.6-pre3/Documentation/sound/oss/AWE32 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/AWE32 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,76 @@ + Installing and using Creative AWE midi sound under Linux. + +This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and +SB32. + +1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This + is important, because the driver works only with real Creative cards. + +2) The first thing you need to do is re-compile your kernel with support for + your sound card. Run your favourite tool to configure the kernel and when + you get to the "Sound" menu you should enable support for the following: + + Sound card support, + OSS sound modules, + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, + AWE32 synth + + If your card is "Plug and Play" you will also need to enable these two + options, found under the "Plug and Play configuration" menu: + + Plug and Play support + ISA Plug and Play support + + Now compile and install the kernel in normal fashion. If you don't know + how to do this you can find instructions for this in the README file + located in the root directory of the kernel source. + +3) Before you can start playing midi files you will have to load a sound + bank file. The utility needed for doing this is called "sfxload", and it + is one of the utilities found in a package called "awesfx". If this + package is not available in your distribution you can download the AWE + snapshot from Creative Labs Open Source website: + + http://www.opensource.creative.com/snapshot.html + + Once you have unpacked the AWE snapshot you will see a "awesfx" + directory. Follow the instructions in awesfx/docs/INSTALL to install the + utilities in this package. After doing this, sfxload should be installed + as: + + /usr/local/bin/sfxload + + To enable AWE general midi synthesis you should also get the sound bank + file for general midi from: + + http://members.xoom.com/yar/synthgm.sbk.gz + + Copy it to a directory of your choice, and unpack it there. + +4) Edit /etc/modules.conf, and insert the following lines at the end of the + file: + + alias sound-slot-0 sb + alias sound-service-0-1 awe_wave + post-install awe_wave /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE + + You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full + path of of the sound bank file. That will enable the Sound Blaster and AWE + wave synthesis. To play midi files you should get one of these programs if + you don't already have them: + + Playmidi: http://playmidi.openprojects.net + + AWEMidi Player (drvmidi) Included in the previously mentioned AWE + snapshot. + + You will probably have to pass the "-e" switch to playmidi to have it use + your midi device. drvmidi should work without switches. + + If something goes wrong please e-mail me. All comments and suggestions are + welcome. + + Yaroslav Rosomakho (alons55@dialup.ptt.ru) + http://www.yar.opennet.ru + +Last Updated: Feb 3 2001 diff -urN linux-2.5.6-pre3/Documentation/sound/oss/AudioExcelDSP16 linux-2.5.6/Documentation/sound/oss/AudioExcelDSP16 --- linux-2.5.6-pre3/Documentation/sound/oss/AudioExcelDSP16 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/AudioExcelDSP16 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,101 @@ +Driver +------ + +Informations about Audio Excel DSP 16 driver can be found in the source +file lowlevel/aedsp16.c +Please, read the head of the source before using it. It contain useful +informations. + +Configuration +------------- + +The Audio Excel configuration, is now done with the standard Linux setup. +You have to configure the sound card (Sound Blaster or Microsoft Sound System) +and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401, +SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select +the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ +of the Audio Excel initialization: it must be the same as the SBPRO (or MSS) +setup. If the parameters are different, correct it. +I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support. +If you want to change the configuration of the sound board, be sure to +check off all the configuration items before re-configure it. + +Module parameters +----------------- +To use this driver as a module, you must configure some module parameters, to +set up I/O addresses, IRQ lines and DMA channels. Some parameters are +mandatory while some others are optional. Here a list of parameters you can +use with this module: + +Name Description +==== =========== +MANDATORY +io I/O base address (0x220 or 0x240) +irq irq line (5, 7, 9, 10 or 11) +dma dma channel (0, 1 or 3) + +OPTIONAL +mss_base I/O base address for activate MSS mode (default SBPRO) + (0x530 or 0xE80) +mpu_base I/O base address for activate MPU-401 mode + (0x300, 0x310, 0x320 or 0x330) +mpu_irq MPU-401 irq line (5, 7, 9, 10 or 0) + +The /etc/modules.conf will have lines like this: + +options opl3 io=0x388 +options ad1848 io=0x530 irq=11 dma=3 +options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530 + +Where the aedsp16 options are the options for this driver while opl3 and +ad1848 are the corresponding options for the MSS and OPL3 modules. + +Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly +the sound card. Installation dependencies must be written in the modules.conf +file: + +pre-install ad1848 modprobe aedsp16 +pre-install opl3 modprobe aedsp16 + +Then you must load the sound modules stack in this order: +sound -> aedsp16 -> [ ad1848, opl3 ] + +With the above configuration, loading ad1848 or opl3 modules, will +automatically load all the sound stack. + +Sound cards supported +--------------------- +This driver supports the SC-6000 and SC-6600 based Gallant's sound card. +It don't support the Audio Excel DSP 16 III (try the SC-6600 code). +I'm working on the III version of the card: if someone have useful +informations about it, please let me know. +For all the non-supported audio cards, you have to boot MS-DOS (or WIN95) +activating the audio card with the MS-DOS device driver, then you have to +-- and boot Linux. +Follow these steps: + +1) Compile Linux kernel with standard sound driver, using the emulation + you want, with the parameters of your audio card, + e.g. Microsoft Sound System irq10 dma3 +2) Install your new kernel as the default boot kernel. +3) Boot MS-DOS and configure the audio card with the boot time device + driver, for MSS irq10 dma3 in our example. +4) -- and boot Linux. This will maintain the DOS configuration + and will boot the new kernel with sound driver. The sound driver will find + the audio card and will recognize and attach it. + +Reports on User successes +------------------------- + +> Date: Mon, 29 Jul 1996 08:35:40 +0100 +> From: Mr S J Greenaway +> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti) +> Subject: Re: Audio Excel DSP 16 initialization code +> +> Just to let you know got my Audio Excel (emulating a MSS) working +> with my original SB16, thanks for the driver! + + +Last revised: 20 August 1998 +Riccardo Facchetti +fizban@tin.it diff -urN linux-2.5.6-pre3/Documentation/sound/oss/CMI8330 linux-2.5.6/Documentation/sound/oss/CMI8330 --- linux-2.5.6-pre3/Documentation/sound/oss/CMI8330 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/CMI8330 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,153 @@ +Documentation for CMI 8330 (SoundPRO) +------------------------------------- +Alessandro Zummo + +( Be sure to read Documentation/sound/SoundPro too ) + + +This adapter is now directly supported by the sb driver. + + The only thing you have to do is to compile the kernel sound +support as a module and to enable kernel ISAPnP support, +as shown below. + + +CONFIG_SOUND=m +CONFIG_SOUND_SB=m + +CONFIG_PNP=y +CONFIG_ISAPNP=y + + +and optionally: + + +CONFIG_SOUND_MPU401=m + + for MPU401 support. + + +(I suggest you to use "make menuconfig" or "make xconfig" + for a more comfortable configuration editing) + + + +Then you can do + + modprobe sb + +and everything will be (hopefully) configured. + +You should get something similar in syslog: + +sb: CMI8330 detected. +sb: CMI8330 sb base located at 0x220 +sb: CMI8330 mpu base located at 0x330 +sb: CMI8330 mail reports to Alessandro Zummo +sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5 + + + + +The old documentation file follows for reference +purposes. + + +How to enable CMI 8330 (SOUNDPRO) soundchip on Linux +------------------------------------------ +Stefan Laudat + +[Note: The CMI 8338 is unrelated and is supported by cmpci.o] + + + In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but +you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. + + Of course you will have to compile kernel sound support as module, as shown below: + +CONFIG_SOUND=m +CONFIG_SOUND_OSS=m +CONFIG_SOUND_SB=m +CONFIG_SOUND_ADLIB=m +CONFIG_SOUND_MPU401=m +# Mikro$chaft sound system (kinda useful here ;)) +CONFIG_SOUND_MSS=m + + The /etc/isapnp.conf file will be: + + + + +(READPORT 0x0203) +(ISOLATE PRESERVE) +(IDENTIFY *) +(VERBOSITY 2) +(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING +(VERIFYLD N) + + +# WSS + +(CONFIGURE CMI0001/16777472 (LD 0 +(IO 0 (SIZE 8) (BASE 0x0530)) +(IO 1 (SIZE 8) (BASE 0x0388)) +(INT 0 (IRQ 7 (MODE +E))) +(DMA 0 (CHANNEL 0)) +(NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# MPU + +(CONFIGURE CMI0001/16777472 (LD 1 +(IO 0 (SIZE 2) (BASE 0x0330)) +(INT 0 (IRQ 11 (MODE +E))) +(NAME "CMI0001/16777472[1]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# Joystick + +(CONFIGURE CMI0001/16777472 (LD 2 +(IO 0 (SIZE 8) (BASE 0x0200)) +(NAME "CMI0001/16777472[2]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + +# SoundBlaster + +(CONFIGURE CMI0001/16777472 (LD 3 +(IO 0 (SIZE 16) (BASE 0x0220)) +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 1)) +(DMA 1 (CHANNEL 5)) +(NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") +(ACT Y) +)) + + +(WAITFORKEY) + + + + The module sequence is trivial: + +/sbin/insmod soundcore +/sbin/insmod sound +/sbin/insmod uart401 +# insert this first +/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 +# The sb module is an alternative to the ad1848 (Microsoft Sound System) +# Anyhow, this is full duplex and has MIDI +/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 + + + +Alma Chao suggests the following /etc/modules.conf: + +alias sound ad1848 +alias synth0 opl3 +options ad1848 io=0x530 irq=7 dma=0 soundpro=1 +options opl3 io=0x388 + + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/CMI8338 linux-2.5.6/Documentation/sound/oss/CMI8338 --- linux-2.5.6-pre3/Documentation/sound/oss/CMI8338 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/CMI8338 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,85 @@ +Audio driver for CM8338/CM8738 chips by Chen-Li Tien + + +HARDWARE SUPPORTED +================================================================================ +C-Media CMI8338 +C-Media CMI8738 +On-board C-Media chips + + +STEPS TO BUILD DRIVER +================================================================================ + + 1. Backup the Config.in and Makefile in the sound driver directory + (/usr/src/linux/driver/sound). + The Configure.help provide help when you config driver in step + 4, please backup the original one (/usr/src/linux/Document) and + copy this file. + The cmpci is document for the driver in detail, please copy it + to /usr/src/linux/Document/sound so you can refer it. Backup if + there is already one. + + 2. Extract the tar file by 'tar xvzf cmpci-xx.tar.gz' in the above + directory. + + 3. Change directory to /usr/src/linux + + 4. Config cm8338 driver by 'make menuconfig', 'make config' or + 'make xconfig' command. + + 5. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI + driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. + For driver option, please refer 'DRIVER PARAMETER' + + 6. Compile the kernel if necessary. + + 7. Compile the modules by 'make modules'. + + 8. Install the modules by 'make modules_install' + + +INSTALL DRIVER +================================================================================ + + 1. Before first time to run the driver, create module dependency by + 'depmod -a' + + 2. To install the driver manually, enter 'modprobe cmpci'. + + 3. Driver installation for various distributions: + + a. Slackware 4.0 + Add the 'modprobe cmpci' command in your /etc/rc.d/rc.modules + file.so you can start the driver automatically each time booting. + + b. Caldera OpenLinux 2.2 + Use LISA to load the cmpci module. + + c. RedHat 6.0 and S.u.S.E. 6.1 + Add following command in /etc/conf.modules: + + alias sound cmpci + + also visit http://www.cmedia.com.tw for installation instruction. + +DRIVER PARAMETER +================================================================================ + + Some functions for the cm8738 can be configured in Kernel Configuration + or modules parameters. Set these parameters to 1 to enable. + + mpuio: I/O ports base for MPU-401, 0 if disabled. + fmio: I/O ports base for OPL-3, 0 if disabled. + spdif_inverse:Inverse the S/PDIF-in signal, this depends on your + CD-ROM or DVD-ROM. + spdif_loop: Enable S/PDIF loop, this route S/PDIF-in to S/PDIF-out + directly. + speakers: Number of speakers used. + use_line_as_rear:Enable this if you want to use line-in as + rear-out. + use_line_as_bass:Enable this if you want to use line-in as + bass-out. + joystick: Enable joystick. You will need to install Linux joystick + driver. + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/CS4232 linux-2.5.6/Documentation/sound/oss/CS4232 --- linux-2.5.6-pre3/Documentation/sound/oss/CS4232 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/CS4232 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,23 @@ +To configure the Crystal CS423x sound chip and activate its DSP functions, +modules may be loaded in this order: + + modprobe sound + insmod ad1848 + insmod uart401 + insmod cs4232 io=* irq=* dma=* dma2=* + +This is the meaning of the parameters: + + io--I/O address of the Windows Sound System (normally 0x534) + irq--IRQ of this device + dma and dma2--DMA channels (DMA2 may be 0) + +On some cards, the board attempts to do non-PnP setup, and fails. If you +have problems, use Linux' PnP facilities. + +To get MIDI facilities add + + insmod opl3 io=* + +where "io" is the I/O address of the OPL3 synthesizer. This will be shown +in /proc/sys/pnp and is normally 0x388. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ChangeLog.awe linux-2.5.6/Documentation/sound/oss/ChangeLog.awe --- linux-2.5.6-pre3/Documentation/sound/oss/ChangeLog.awe Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ChangeLog.awe Thu Mar 7 18:24:41 2002 @@ -0,0 +1,230 @@ +ver.0.4.3p4 + - Bug fix for invalid memory detection when initialized twice + - Add sample sharing function - works together with awesfx-0.4.3p3 + - Add AWE_PROBE_DATA for probing sample id + +ver.0.4.3p3 + - Replace memset to MEMSET (for FreeBSD) + - Add PAN_EXCHANGE switch + +ver.0.4.3p2 + - MIDI emulation device is added + - Controls volume and filter targets + - Include chorus/reverb/equalizer values in MISC_MODE + +ver.0.4.3p1 + - Change the volume calculation method + - Support for Tom Lees' PnP driver (v0.3) + +ver.0.4.2d + - Support for OSS/Free 3.8 on 2.0 kernels. + - Support for Linux PnP driver + - Support for module (for recent 2.1 kernels and RH5.0) + - Support for FreeBSD-3.0 system + +ver.0.4.2c + - Add a mode to enable drum channel toggle via bank number + change. + +ver.0.4.2b + - Clear voice position after note on + - Change nrvoices according to the current playing mode + +ver.0.4.2a + - Fix a bug in pitch calculation with scale parameter + - Change default chorus & reverb modes + +ver.0.4.2 + - Use indirect voice allocation mode; used as default mode + - Add preset mapping + - Free buffers when resetting samples + - Set default preset/bank/drumset as variable + - Fix a bug in exclusive note-off + - Add channel reset control macro + - Change modwheel sensitivity as variable + - Add lock option in open_patch + - Add channel priority mode macro, and disable it as default + - Add unset effect macro + - Add user defined chorus/reverb modes + - Do not initialize effect parameters when allocating voices + - Accept realtime filter-Q parameter change + - Check value range of set/add effects + - Change drum flags automatically when receiving bank #128 + +ver.0.4.1 development versions + +ver.0.4.0c + - Fix kernel oops when setting AWE_FX_ATTEN + +ver.0.4.0b + - Do not kill_note in start_note when velocity is zero + +ver.0.4.0a + - Fix a bug in channel pressure effects + +ver.0.4.0 + - Support dynamic buffer allocation + - Add functions to open/close/unload a patch + - Change from pointer to integer index in voice/sample lists + - Support for Linux/Alpha-AXP + - Fix for FreeBSD + - Add sostenuto control + - Add midi channel priority + - Fix a bug in all notes off control + - Use AWE_DEFAULT_MEMSIZE always if defined + - Fix a bug in awe_reset causes seg fault when no DRAM onboard + - Use awe_mem_start variable instead of constant + +ver.0.3.3c + - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25) + - Fix i/o macros for mixer controls + +ver.0.3.3b + - Fix version number in awe_version.h + - Fix a small bug in noteoff/release all + +ver.0.3.3a + - Fix all notes/sounds off + - Add layer effect control + - Add misc mode controls; realtime pan, version number, etc. + - Move gus bank control in misc mode control + - Modify awe_operations for OSS3.8b5 + - Fix installation script + +ver.0.3.3 + - Add bass/treble control in Emu8000 chip + - Add mixer device + - Fix sustain on to value 127 + +ver.0.3.2 + - Refuse linux-2.0.0 at installation + - Move awe_voice.h to /usr/include/linux + +ver.0.3.1b (not released) + - Rewrite chorus/reverb mode change functions + - Rewrite awe_detect & awe_check_dram routines + +ver.0.3.1a + - Fix a bug to reset voice counter in awe_reset + - Fix voice balance on GUS mode + - Make symlink on /usr/include/asm in install script + +ver.0.3.1 + - Remove zero size arrays from awe_voice.h + - Fix init_fm routine + - Remove all samples except primary samples in REMOVE_LAST_SAMPLES + +ver.0.3.0a + - Add AWE_NOTEOFF_ALL control + - Remove AWE_INIT_ATTEN control + +ver.0.3.0 + - Fix decay time table + - Add exclusive sounds mode + - Add capability to get current status + +ver.0.2.99e + - Add #ifdef for all sounds/notes off controls. + - Fix bugs on searching the default drumset/preset. + - Fix usslite patch to modify the default Config.in. + +ver.0.2.99d + - Fix bugs of attack/hold parameters + - Fix attack & decay time table + +ver.0.2.99c + - Change volume control messages (main & expression volume) + to accesspt normal MIDI parameters in channel mode. + - Use channel mode in SEQ2 controls. + +ver.0.2.99b + - #ifdef patch manager functions (for OSS-3.7) + +ver.0.2.99a + - Fix sustain bug + +ver.0.2.99 (0.3 beta) + - Support multiple instruments + +ver.0.2.0c + - Add copyright notice + - FreeBSD 2.2-ALPHA integration + +ver.0.2.0b + - Remove buffered reading appended in v0.2.0a + - Remove SMAxW register check on writing + - Support Linux 2.1.x kernel + - Rewrite installation script + +ver.0.2.0a + - Define SEQUENCER_C for tuning.h for FreeBSD system + - Improvement of sample loading speed + - Fix installation script + - Add PnP driver functions for ISA PnP driver support + +ver.0.2.0 + - Includes FreeBSD port + - Can load GUS compatible patches + - Change values of hardware control parameters for compatibility + with GUS driver + - Accept 8bit or unsigned wave data + - Accept no blank loop data + - Add sample mode flags in sample_info + +ver.0.1.6 + - Add voice effects control + - Fix awe_voice.h for word alignment + +ver.0.1.5c + - Fix FM(OPL) playback problem + +ver.0.1.5b + - Fix pitch calculation for fixed midi key + +ver.0.1.5a + - Fix bugs in removing samples from linked list. + +ver.0.1.5 + - Add checksum verification for sample uploading + (not compatible from older sample_info structure) + - Fix sample offset pointers to (actual value - 1) + - Add sequencer command to initialize awe32 + +ver.0.1.4c + - Fix card detection and memory check function to avoid system crash + at booting + +ver.0.1.4b + - Add release sustain mode + - Initialize FM each time after loading samples + +ver.0.1.4a + - Fix AWE card detection code + - Correct FM initialize position + - Add non-releasing mode on voice info + +ver.0.1.4 + - Add AWE card and DRAM detection codes + - Add FM initialization code + - Modify volume control + - Remove linear volume mode + - Change memory management; not using malloc dynamically + - Add remove-samples command + - Use internal id implicitly at loading samples + +ver.0.1.3 + - Fix a bug on patch uploading to RAM + +ver.0.1.2 + - Divide to separated packages + - Fix disagreed macro conditions + - Fix unresolved function bugs + - Integrate VoxWare and USS-Lite driver source (awe_voice.c) + and remove awe_card.c + +ver.0.1.1 + - Fix wrong sample numbers in sbktext + - Fix txt2sfx bug + - Fix pan parameter calculation + - Append USS-Lite/Linux2.0 driver + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ChangeLog.multisound linux-2.5.6/Documentation/sound/oss/ChangeLog.multisound --- linux-2.5.6-pre3/Documentation/sound/oss/ChangeLog.multisound Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ChangeLog.multisound Thu Mar 7 18:24:41 2002 @@ -0,0 +1,213 @@ +1998-12-04 Andrew T. Veliath + + * Update version to 0.8.2.2 + + * Add msndreset program to shell archive. + +1998-11-11 Andrew T. Veliath + + * msnd_pinnacle.c (mixer_ioctl): Add a mixer ioctl for + SOUND_MIXER_PRIVATE1 which does a full reset on the card. + (mixer_set): Move line in recording source to input monitor, aux + input level added, some mixer fixes. + +1998-09-10 Andrew Veliath + + * Update version to 0.8.2 + + * Add SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls. + +1998-09-09 Andrew Veliath + + * Update version to 0.8.1 + + * msnd_pinnacle.c: Fix resetting of default audio parameters. Turn + flush code from dsp_halt into dsp_write_flush, and use that for + SNDCTL_DSP_SYNC. + +1998-09-07 Andrew Veliath + + * Update version to 0.8.0 + + * Provide separate signal parameters for play and record. + + * Cleanups to locking and interrupt handling, change default + fifosize to 128kB. + + * Update version to 0.7.15 + + * Interprocess full-duplex support (ie `cat /dev/dsp > /dev/dsp'). + + * More mutex sections for read and write fifos (read + write locks + added). + +1998-09-05 Andrew Veliath + + * msnd_pinnacle.c: (chk_send_dsp_cmd) Do full DSP reset upon DSP + timeout (when not in interrupt; maintains mixer settings). Fixes + to flushing and IRQ ref counting. Rewrote queuing for smoother + playback and fixed initial playback cutoff problem. + +1998-09-03 Andrew Veliath + + * Replaced packed structure accesses with standard C equivalents. + +1998-09-01 Andrew Veliath + + * msnd_pinnacle.c: Add non-PnP configuration to driver code, which + will facilitate compiled-in operation. + +1998-08-29 Andrew Veliath + + * Update version to 0.7.6 + + * msnd_pinnacle.c (dsp_ioctl): Add DSP_GETFMTS, change SAMPLESIZE + to DSP_SETFMT. + + * Update version to 0.7.5 + + * Create pinnaclecfg.c and turn MultiSound doc into a shell + archive with pinnaclecfg.c included. pinnaclecfg.c can + now fully configure the card in non-PnP mode, including the + joystick and IDE controller. Also add an isapnp conf + example. + + * Reduce DSP reset timeout from 20000 to 100 + +1998-08-06 Andrew Veliath + + * Update version to 0.7.2 + + * After A/D calibration, do an explicit set to the line input, + rather than using set_recsrc + +1998-07-20 Andrew Veliath + + * Update version to 0.7.1 + + * Add more OSS ioctls + +1998-07-19 Andrew Veliath + + * Update doc file + + * Bring back DIGITAL1 with digital parameter to msnd_pinnacle.c + and CONFIG_MSNDPIN_DIGITAL. I'm not sure this actually works, + since I find audio playback goes into a very speeded mode of + operation, however it might be due to a lack of a digital + source, which I don't have to test. + +1998-07-18 Andrew Veliath + + * Update version to 0.7.0 + + * Can now compile with Alan Cox' 2.0.34-modular-sound patch (so + now it requires >= 2.1.106 or 2.0.34-ms) (note for 2.0.34-ms it + is in the Experimental section) + + * More modularization, consolidation, also some MIDI hooks + installed for future MIDI modules + + * Write flush + + * Change default speed, channels, bit size to OSS/Free defaults + +1998-06-02 Andrew Veliath + + * Update version to 0.5b + + * Fix version detection + + * Remove underflow and overflow resets (delay was too long) + + * Replace spinlocked bitops with atomic bit ops + +1998-05-27 Andrew Veliath + + * Update version to 0.5a + + * Better recovery from underflow or overflow conditions + + * Fix a deadlock condition with one thread reading and the other + writing + +1998-05-26 Andrew Veliath + + * Update version to 0.5 + + * Separate reset queue functions for play and record + + * Add delays in dsp_halt + +1998-05-24 Andrew Veliath + + * Add a check for Linux >= 2.1.95 + + * Remove DIGITAL1 input until I figure out how to make it work + + * Add HAVE_DSPCODEH which when not defined will load firmware from + files using mod_firmware_load, then release memory after they + are uploaded (requires reorganized OSS). + +1998-05-22 Andrew Veliath + + * Update version to 0.4c + + * Hopefully fix the mixer volume problem + +1998-05-19 Andrew Veliath + + * Add __initfuncs and __initdatas to reduce resident code size + + * Move bunch of code around, remove some protos + + * Integrate preliminary changes for Alan Cox's OSS reorganization + for non-OSS drivers to coexist with OSS devices on the same + major. To compile standalone, must now define STANDALONE. + +1998-05-16 Andrew Veliath + + * Update version to 0.4b + + * Integrated older card support into a unified driver, tested on a + MultiSound Classic c/o Kendrick Vargas. + +1998-05-15 Andrew Veliath + + * Update version to 0.4 + + * Fix read/write return values + +1998-05-13 Andrew Veliath + + * Update version to 0.3 + + * Stop play gracefully + + * Add busy flag + + * Add major and calibrate_signal module parameters + + * Add ADC calibration + + * Add some OSS compatibility ioctls + + * Add mixer record selection + + * Add O_NONBLOCK support, separate read/write wait queues + + * Add sample bit size ioctl, expanded sample rate ioctl + + * Playback suspension now resumes + + * Use signal_pending after interruptible_sleep_on + + * Add recording, change ints to bit flags + +1998-05-11 Andrew Veliath + + * Update version to 0.2 + + * Add preliminary playback support + + * Use new Turtle Beach DSP code \ No newline at end of file diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ESS linux-2.5.6/Documentation/sound/oss/ESS --- linux-2.5.6-pre3/Documentation/sound/oss/ESS Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ESS Thu Mar 7 18:24:41 2002 @@ -0,0 +1,34 @@ +Documentation for the ESS AudioDrive chips + +In 2.4 kernels the SoundBlaster driver not only tries to detect an ESS chip, it +tries to detect the type of ESS chip too. The correct detection of the chip +doesn't always succeed however, so unless you use the kernel isapnp facilities +(and you chip is pnp capable) the default behaviour is 2.0 behaviour which +means: only detect ES688 and ES1688. + +All ESS chips now have a recording level setting. This is a need-to-have for +people who want to use their ESS for recording sound. + +Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic +master volume control. + +Every chip that's detected as a ES1887 now has Full Duplex support. Made a +little testprogram that shows that is works, haven't seen a real program that +needs this however. + +For ESS chips an additional parameter "esstype" can be specified. This controls +the (auto) detection of the ESS chips. It can have 3 kinds of values: + +-1 Act like 2.0 kernels: only detect ES688 or ES1688. +0 Try to auto-detect the chip (may fail for ES1688) +688 The chip will be treated as ES688 +1688 ,, ,, ,, ,, ,, ,, ES1688 +1868 ,, ,, ,, ,, ,, ,, ES1868 +1869 ,, ,, ,, ,, ,, ,, ES1869 +1788 ,, ,, ,, ,, ,, ,, ES1788 +1887 ,, ,, ,, ,, ,, ,, ES1887 +1888 ,, ,, ,, ,, ,, ,, ES1888 + +Because Full Duplex is supported for ES1887 you can specify a second DMA +channel by specifying module parameter dma16. It can be one of: 0, 1, 3 or 5. + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ESS1868 linux-2.5.6/Documentation/sound/oss/ESS1868 --- linux-2.5.6-pre3/Documentation/sound/oss/ESS1868 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ESS1868 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,55 @@ +Documentation for the ESS1868F AudioDrive PnP sound card + +The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. + +It should be automatically detected by the Linux Kernel isapnp support when you +load the sb.o module. Otherwise you should take care of: + + * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 + may only be used. + + * isapnptools version 1.14 does work with ESS1868. Earlier versions might + not. + + * Sound support MUST be compiled as MODULES, not statically linked + into the kernel. + + +NOTE: this is only needed when not using the kernel isapnp support! + +For configuring the sound card's I/O addresses, IRQ and DMA, here is a +sample copy of the isapnp.conf directives regarding the ESS1868: + +(CONFIGURE ESS1868/-1 (LD 1 +(IO 0 (BASE 0x0220)) +(IO 1 (BASE 0x0388)) +(IO 2 (BASE 0x0330)) +(DMA 0 (CHANNEL 1)) +(INT 0 (IRQ 5 (MODE +E))) +(ACT Y) +)) + +(for a full working isapnp.conf file, remember the +(ISOLATE) +(IDENTIFY *) +at the beginning and the +(WAITFORKEY) +at the end.) + +In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and +the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. + +After configuring the sound card via isapnp, to use the card you must load +the sound modules with the proper I/O information. Here is my setup: + +# ESS1868F AudioDrive initialization + +/sbin/modprobe sound +/sbin/insmod uart401 +/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 +/sbin/insmod mpu401 io=0x330 +/sbin/insmod opl3 io=0x388 +/sbin/insmod v_midi + +opl3 is the FM synthesizer +/sbin/insmod opl3 io=0x388 diff -urN linux-2.5.6-pre3/Documentation/sound/oss/INSTALL.awe linux-2.5.6/Documentation/sound/oss/INSTALL.awe --- linux-2.5.6-pre3/Documentation/sound/oss/INSTALL.awe Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/INSTALL.awe Thu Mar 7 18:24:41 2002 @@ -0,0 +1,134 @@ +================================================================ + INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX + Takashi Iwai +================================================================ + +---------------------------------------------------------------- +* Attention to SB-PnP Card Users + +If you're using PnP cards, the initialization of PnP is required +before loading this driver. You have now three options: + 1. Use isapnptools. + 2. Use in-kernel isapnp support. + 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. +In this document, only the case 1 case is treated. + +---------------------------------------------------------------- +* Installation on Red Hat 5.0 Sound Driver + +Please use install-rh.sh under RedHat5.0 directory. +DO NOT USE install.sh below. +See INSTALL.RH for more details. + +---------------------------------------------------------------- +* Installation/Update by Shell Script + + 1. Become root + + % su + + 2. If you have never configured the kernel tree yet, run make config + once (to make dependencies and symlinks). + + # cd /usr/src/linux + # make xconfig + + 3. Run install.sh script + + # sh ./install.sh + + 4. Configure your kernel + + (for Linux 2.[01].x user) + # cd /usr/src/linux + # make xconfig (or make menuconfig) + + (for Linux 1.2.x user) + # cd /usr/src/linux + # make config + + Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items + in Sound menu. ("lowlevel drivers" will appear only in 2.x + kernel.) + + 5. Make your kernel (and modules), and install them as usual. + + 5a. make kernel image + # make zImage + + 5b. make modules and install them + # make modules && make modules_install + + 5c. If you're using lilo, copy the kernel image and run lilo. + Otherwise, copy the kernel image to suitable directory or + media for your system. + + 6. Reboot the kernel if necessary. + - If you updated only the modules, you don't have to reboot + the system. Just remove the old sound modules here. + in + # rmmod sound.o (linux-2.0 or OSS/Free) + # rmmod awe_wave.o (linux-2.1) + + 7. If your AWE card is a PnP and not initialized yet, you'll have to + do it by isapnp tools. Otherwise, skip to 8. + + This section described only a brief explanation. For more + details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. + + 7a. If you have no isapnp.conf file, generate it by pnpdump. + Otherwise, skip to 7d. + # pnpdump > /etc/isapnp.conf + + 7b. Edit isapnp.conf file. Comment out the appropriate + lines containing desirable I/O ports, DMA and IRQs. + Don't forget to enable (ACT Y) line. + + 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. + ex) + (CONFIGURE CTL0048/58128 (LD 2 + # ANSI string -->WaveTable<-- + (IO 0 (BASE 0x0620)) + (IO 1 (BASE 0x0A20)) + (IO 2 (BASE 0x0E20)) + (ACT Y) + )) + + 7d. Load the config file. + CAUTION: This will reset all PnP cards! + + # isapnp /etc/isapnp.conf + + 8. Load the sound module (if you configured it as a module): + + for 2.0 kernel or OSS/Free monolithic module: + + # modprobe sound.o + + for 2.1 kernel: + + # modprobe sound + # insmod uart401 + # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 + (These values depend on your settings.) + # insmod awe_wave + (Be sure to load awe_wave after sb!) + + See /usr/src/linux/Documentation/sound/AWE32 for + more details. + + 9. (only for obsolete systems) If you don't have /dev/sequencer + device file, make it according to Readme.linux file on + /usr/src/linux/drivers/sound. (Run a shell script included in + that file). <-- This file no longer exists in the recent kernels! + + 10. OK, load your own soundfont file, and enjoy MIDI! + + % sfxload synthgm.sbk + % drvmidi foo.mid + + 11. For more advanced use (eg. dynamic loading, virtual bank and + etc.), please read the awedrv FAQ or the instructions in awesfx + and awemidi packages. + +Good luck! diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Introduction linux-2.5.6/Documentation/sound/oss/Introduction --- linux-2.5.6-pre3/Documentation/sound/oss/Introduction Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Introduction Thu Mar 7 18:24:41 2002 @@ -0,0 +1,461 @@ +Introduction Notes on Modular Sound Drivers and Soundcore +Wade Hampton +2/14/2001 + +Purpose: +======== +This document provides some general notes on the modular +sound drivers and their configuration, along with the +support modules sound.o and soundcore.o. + +Note, some of this probably should be added to the Sound-HOWTO! + +Note, soundlow.o was present with 2.2 kernels but is not +required for 2.4.x kernels. References have been removed +to this. + + +Copying: +======== +none + + +History: +======== +0.1.0 11/20/1998 First version, draft +1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0 + as /usr/src/linux/Documentation/sound/Introduction +1.1.0 6/30/1999 Second version, added notes on making the drivers, + added info on multiple sound cards of similar types,] + added more diagnostics info, added info about esd. + added info on OSS and ALSA. +1.1.1 19991031 Added notes on sound-slot- and sound-service. + (Alan Cox) +1.1.2 20000920 Modified for Kernel 2.4 (Christoph Hellwig) +1.1.3 20010214 Minor notes and corrections (Wade Hampton) + Added examples of sound-slot-0, etc. + + +Modular Sound Drivers: +====================== + +Thanks to the GREAT work by Alan Cox (alan@lxorguk.ukuu.org.uk), + +[And Oleg Drokin, Thomas Sailer, Andrew Veliath and more than a few + others - not to mention Hannu's original code being designed well + enough to cope with that kind of chopping up](Alan) + +the standard Linux kernels support a modular sound driver. From +Alan's comments in linux/drivers/sound/README.FIRST: + + The modular sound driver patches were funded by Red Hat Software + (www.redhat.com). The sound driver here is thus a modified version of + Hannu's code. Please bear that in mind when considering the appropriate + forums for bug reporting. + +The modular sound drivers may be loaded via insmod or modprobe. +To support all the various sound modules, there are two general +support modules that must be loaded first: + + soundcore.o: Top level handler for the sound system, provides + a set of functions for registration of devices + by type. + + sound.o: Common sound functions required by all modules. + +For the specific sound modules (e.g., sb.o for the Soundblaster), +read the documentation on that module to determine what options +are available, for example IRQ, address, DMA. + +Warning, the options for different cards sometime use different names +for the same or a similar feature (dma1= versus dma16=). As a last +resort, inspect the code (search for MODULE_PARM). + +Notes: + +1. There is a new OpenSource sound driver called ALSA which is + currently under development: http://www.alsa-project.org/ + The ALSA drivers support some newer hardware that may not + be supported by this sound driver and also provide some + additional features. + +2. The commercial OSS driver may be obtained from the site: + http://www/opensound.com. This may be used for cards that + are unsupported by the kernel driver, or may be used + by other operating systems. + +3. The enlightenment sound daemon may be used for playing + multiple sounds at the same time via a single card, eliminating + some of the requirements for multiple sound card systems. For + more information, see: http://www.tux.org/~ricdude/EsounD.html + The "esd" program may be used with the real-player and mpeg + players like mpg123 and x11amp. The newer real-player + and some games even include built-in support for ESD! + + +Building the Modules: +===================== + +This document does not provide full details on building the +kernel, etc. The notes below apply only to making the kernel +sound modules. If this conflicts with the kernel's README, +the README takes precedence. + +1. To make the kernel sound modules, cd to your /usr/src/linux + directory (typically) and type make config, make menuconfig, + or make xconfig (to start the command line, dialog, or x-based + configuration tool). + +2. Select the Sound option and a dialog will be displayed. + +3. Select M (module) for "Sound card support". + +4. Select your sound driver(s) as a module. For ProAudio, Sound + Blaster, etc., select M (module) for OSS sound modules. + [thanks to Marvin Stodolsky ]A + +5. Make the kernel (e.g., make dep ; make bzImage), and install + the kernel. + +6. Make the modules and install them (make modules; make modules_install). + +Note, for 2.4.x kernels, make sure you have the newer modutils +loaded or modules will not be loaded properly. 2.4.x changed the +layout of /lib/modules/2.4.x and requires an updated modutils. + + +Plug and Play (PnP: +=================== + +If the sound card is an ISA PnP card, isapnp may be used +to configure the card. See the file isapnp.txt in the +directory one level up (e.g., /usr/src/linux/Documentation). + +Also the 2.4.x kernels provide PnP capabilities, see the +file NEWS in this directory. + +PCI sound cards are highly recommended, as they are far +easier to configure and from what I have read, they use +less resources and are more CPU efficient. + + +INSMOD: +======= + +If loading via insmod, the common modules must be loaded in the +order below BEFORE loading the other sound modules. The card-specific +modules may then be loaded (most require parameters). For example, +I use the following via a shell script to load my SoundBlaster: + +SB_BASE=0x240 +SB_IRQ=9 +SB_DMA=3 +SB_DMA2=5 +SB_MPU=0x300 +# +echo Starting sound +/sbin/insmod soundcore +/sbin/insmod sound +# +echo Starting sound blaster.... +/sbin/insmod uart401 +/sbin/insmod sb io=$SB_BASE irq=$SB_IRQ dma=$SB_DMA dma16=$SB_DMA2 mpu_io=$SB_MP + +When using sound as a module, I typically put these commands +in a file such as /root/soundon.sh. + + +MODPROBE: +========= + +If loading via modprobe, these common files are automatically loaded +when requested by modprobe. For example, my /etc/modules.conf contains: + +alias sound sb +options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300 + +All you need to do to load the module is: + + /sbin/modprobe sb + + +Sound Status: +============= + +The status of sound may be read/checked by: + cat (anyfile).au >/dev/audio + +[WWH: This may not work properly for SoundBlaster PCI 128 cards +such as the es1370/1 (see the es1370/1 files in this directory) +as they do not automatically support uLaw on /dev/audio.] + +The status of the modules and which modules depend on +which other modules may be checked by: + /sbin/lsmod + +/sbin/lsmod should show something like the following: + sb 26280 0 + uart401 5640 0 [sb] + sound 57112 0 [sb uart401] + soundcore 1968 8 [sb sound] + + +Removing Sound: +=============== + +Sound may be removed by using /sbin/rmmod in the reverse order +in which you load the modules. Note, if a program has a sound device +open (e.g., xmixer), that module (and the modules on which it +depends) may not be unloaded. + +For example, I use the following to remove my Soundblaster (rmmod +in the reverse order in which I loaded the modules): + +/sbin/rmmod sb +/sbin/rmmod uart401 +/sbin/rmmod sound +/sbin/rmmod soundcore + +When using sound as a module, I typically put these commands +in a script such as /root/soundoff.sh. + + +Removing Sound for use with OSS: +================================ + +If you get really stuck or have a card that the kernel modules +will not support, you can get a commercial sound driver from +http://www.opensound.com. Before loading the commercial sound +driver, you should do the following: + +1. remove sound modules (detailed above) +2. remove the sound modules from /etc/modules.conf +3. move the sound modules from /lib/modules//misc + (for example, I make a /lib/modules//misc/tmp + directory and copy the sound module files to that + directory). + + +Multiple Sound Cards: +===================== + +The sound drivers will support multiple sound cards and there +are some great applications like multitrack that support them. +Typically, you need two sound cards of different types. Note, this +uses more precious interrupts and DMA channels and sometimes +can be a configuration nightmare. I have heard reports of 3-4 +sound cards (typically I only use 2). You can sometimes use +multiple PCI sound cards of the same type. + +On my machine I have two sound cards (cs4232 and Soundblaster Vibra +16). By loading sound as modules, I can control which is the first +sound device (/dev/dsp, /dev/audio, /dev/mixer) and which is +the second. Normally, the cs4232 (Dell sound on the motherboard) +would be the first sound device, but I prefer the Soundblaster. +All you have to do is to load the one you want as /dev/dsp +first (in my case "sb") and then load the other one +(in my case "cs4232"). + +If you have two cards of the same type that are jumpered +cards or different PnP revisions, you may load the same +module twice. For example, I have a SoundBlaster vibra 16 +and an older SoundBlaster 16 (jumpers). To load the module +twice, you need to do the following: + +1. Copy the sound modules to a new name. For example + sb.o could be copied (or symlinked) to sb1.o for the + second SoundBlaster. + +2. Make a second entry in /etc/modules.conf, for example, + sound1 or sb1. This second entry should refer to the + new module names for example sb1, and should include + the I/O, etc. for the second sound card. + +3. Update your soundon.sh script, etc. + +Warning: I have never been able to get two PnP sound cards of the +same type to load at the same time. I have tried this several times +with the Soundblaster Vibra 16 cards. OSS has indicated that this +is a PnP problem.... If anyone has any luck doing this, please +send me an E-MAIL. PCI sound cards should not have this problem.a +Since this was originally release, I have received a couple of +mails from people who have accomplished this! + +NOTE: In Linux 2.4 the Sound Blaster driver (and only this one yet) +supports multiple cards with one module by default. +Read the file 'Soundblaster' in this directory for details. + + +Sound Problems: +=============== + +First RTFM (including the troubleshooting section +in the Sound-HOWTO). + +1) If you are having problems loading the modules (for + example, if you get device conflict errors) try the + following: + + A) If you have Win95 or NT on the same computer, + write down what addresses, IRQ, and DMA channels + those were using for the same hardware. You probably + can use these addresses, IRQs, and DMA channels. + You should really do this BEFORE attempting to get + sound working! + + B) Check (cat) /proc/interrupts, /proc/ioports, + and /proc/dma. Are you trying to use an address, + IRQ or DMA port that another device is using? + + C) Check (cat) /proc/isapnp + + D) Inspect your /var/log/messages file. Often that will + indicate what IRQ or IO port could not be obtained. + + E) Try another port or IRQ. Note this may involve + using the PnP tools to move the sound card to + another location. Sometimes this is the only way + and it is more or less trial and error. + +2) If you get motor-boating (the same sound or part of a + sound clip repeated), you probably have either an IRQ + or DMA conflict. Move the card to another IRQ or DMA + port. This has happened to me when playing long files + when I had an IRQ conflict. + +3. If you get dropouts or pauses when playing high sample + rate files such as using mpg123 or x11amp/xmms, you may + have too slow of a CPU and may have to use the options to + play the files at 1/2 speed. For example, you may use + the -2 or -4 option on mpg123. You may also get this + when trying to play mpeg files stored on a CD-ROM + (my Toshiba T8000 PII/366 sometimes has this problem). + +4. If you get "cannot access device" errors, your /dev/dsp + files, etc. may be set to owner root, mode 600. You + may have to use the command: + chmod 666 /dev/dsp /dev/mixer /dev/audio + +5. If you get "device busy" errors, another program has the + sound device open. For example, if using the Enlightenment + sound daemon "esd", the "esd" program has the sound device. + If using "esd", please RTFM the docs on ESD. For example, + esddsp may be used to play files via a non-esd + aware program. + +6) Ask for help on the sound list or send E-MAIL to the + sound driver author/maintainer. + +7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). + +8) If the system reports insufficient DMA memory then you may want to + load sound with the "dmabufs=1" option. Or in /etc/conf.modules add + + preinstall sound dmabufs=1 + + This makes the sound system allocate its buffers and hang onto them. + + You may also set persistent DMA when building a 2.4.x kernel. + + +Configuring Sound: +================== + +There are several ways of configuring your sound: + +1) On the kernel command line (when using the sound driver(s) + compiled in the kernel). Check the driver source and + documentation for details. + +2) On the command line when using insmod or in a bash script + using command line calls to load sound. + +3) In /etc/modules.conf when using modprobe. + +4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). + +5) Via the OSS soundconf program (with the commercial version + of the OSS driver. + +6) By just loading the module and let isapnp do everything relevant + for you. This works only with a few drivers yet and - of course - + only with isapnp hardware. + +And I am sure, several other ways. + +Anyone want to write a linuxconf module for configuring sound? + + +Module Loading: +=============== + +When a sound card is first referenced and sound is modular, the sound system +will ask for the sound devices to be loaded. Initially it requests that +the driver for the sound system is loaded. It then will ask for +sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and +so on). Thus you can do + +alias sound-slot-0 sb + +To load a soundblaster at this point. If the slot loading does not provide +the desired device - for example a soundblaster does not directly provide +a midi synth in all cases then it will request "sound-service-0-n" where n +is + + 0 Mixer + + 2 MIDI + + 3, 4 DSP audio + + +For example, I use the following to load my Soundblaster PCI 128 +(ES 1371) card first, followed by my SoundBlaster Vibra 16 card, +then by my TV card: + +# Load the Soundblaster PCI 128 as /dev/dsp, /dev/dsp1, /dev/mixer +alias sound-slot-0 es1371 + +# Load the Soundblaster Vibra 16 as /dev/dsp2, /dev/mixer1 +alias sound-slot-1 sb +options sb io=0x240 irq=5 dma=1 dma16=5 mpu_io=0x330 + +# Load the BTTV (TV card) as /dev/mixer2 +alias sound-slot-2 bttv +alias sound-service-2-0 tvmixer + +pre-install bttv modprobe tuner ; modprobe tvmixer +pre-install tvmixer modprobe msp3400; modprobe tvaudio +options tuner debug=0 type=8 +options bttv card=0 radio=0 pll=0 + + +For More Information (RTFM): +============================ +1) Information on kernel modules: linux/Documentation/modules.txt + and manual pages for insmod and modprobe. + +2) Information on PnP, RTFM manual pages for isapnp. + +3) Sound-HOWTO and Sound-Playing-HOWTO. + +4) OSS's WWW site at http://www.opensound.com. + +5) All the files in linux/Documentation/sound. + +6) The comments and code in linux/drivers/sound. + +7) The sndconfig and rhsound documentation from Red Hat. + +8) The Linux-sound mailing list: sound-list@redhat.com. + +9) Enlightenment documentation (for info on esd) + http://www.tux.org/~ricdude/EsounD.html. + +10) ALSA home page: http://www.alsa-project.org/ + + +Contact Information: +==================== +Wade Hampton: (whampton@staffnet.com) + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/MAD16 linux-2.5.6/Documentation/sound/oss/MAD16 --- linux-2.5.6-pre3/Documentation/sound/oss/MAD16 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/MAD16 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,55 @@ +(This recipe has been edited to update the configuration symbols.) + +From: Shaw Carruthers + +I have been using mad16 sound for some time now with no problems, current +kernel 2.1.89 + +lsmod shows: + +mad16 5176 0 +sb 22044 0 [mad16] +uart401 5576 0 [mad16 sb] +ad1848 14176 1 [mad16] +sound 61928 0 [mad16 sb uart401 ad1848] + +.config has: + +CONFIG_SOUND=m +CONFIG_SOUND_ADLIB=m +CONFIG_SOUND_MAD16=m +CONFIG_SOUND_YM3812=m + +modules.conf has: + +alias char-major-14 mad16 +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + + +To get the built in mixer to work this needs to be: + +options adlib_card io=0x388 # FM synthesizer +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + +The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is + +------------------------------------------------------------------------ +The mad16 module in addition supports the following options: + +option: meaning: default: +joystick=0,1 disabled, enabled disabled +cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled + 0x06,0x08,0x0a Mitsumi, Panasonic, + Secondary IDE, Primary IDE +cdport=0x340,0x320, 0x340 + 0x330,0x360 +cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled +cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE +cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic +opl4=0,1 OPL3, OPL4 OPL3 + +for more details see linux/drivers/sound/mad16.c + +Rui Sousa diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Maestro linux-2.5.6/Documentation/sound/oss/Maestro --- linux-2.5.6-pre3/Documentation/sound/oss/Maestro Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Maestro Thu Mar 7 18:24:41 2002 @@ -0,0 +1,123 @@ + An OSS/Lite Driver for the ESS Maestro family of sound cards + + Zach Brown, December 1999 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + 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. + +ESS Maestro Chip Family +----------------------- + +There are 3 main variants of the ESS Maestro PCI sound chip. The first +is the Maestro 1. It was originally produced by Platform Tech as the +'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with +0x0100 as the device ID. It was put on some sound boards and a few laptops. +ESS bought the design and cleaned it up as the Maestro 2. This starts +their marking with the ESS vendor ID 0x125D and the 'year' device IDs. +The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. + +The various families of Maestro are mostly identical as far as this +driver is concerned. It doesn't touch the DSP parts that differ (though +it could for FM synthesis). + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves almost as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, and mmap()ing for playback behaves. Capture/recording +is limited due to oddities with the Maestro hardware. One can only +record in 16bit stereo. For recording the maestro uses non interleaved +stereo buffers so that mmap()ing the incoming data does not result in +a ring buffer of LRLR data. mmap()ing of the read buffers is therefore +disallowed until this can be cleaned up. + +/dev/mixer is an interface to the AC'97 codec on the Maestro. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. + +The driver doesn't support MIDI or FM playback at the moment. Typically +the Maestro is wired to an MPU MIDI chip, but some hardware implementations +don't. We need to assemble a white list of hardware implementations that +have MIDI wired properly before we can claim to support it safely. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' +should also be added to your module configs (typically /etc/conf.modules) +if you're using modular OSS/Lite sound and want to default to using a +maestro chip. + +As this is a PCI device, the module does not need to be informed of +any IO or IRQ resources it should use, it devines these from the +system. Sometimes, on sucky PCs, the BIOS fails to allocated resources +for the maestro. This will result in a message like: + maestro: PCI subsystem reports IRQ 0, this might not be correct. +from the kernel. Should this happen the sound chip most likely will +not operate correctly. To solve this one has to dig through their BIOS +(typically entered by hitting a hot key at boot time) and figure out +what magic needs to happen so that the BIOS will reward the maestro with +an IRQ. This operation is incredibly system specific, so you're on your +own. Sometimes the magic lies in 'PNP Capable Operating System' settings. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other, more interesting option, is 'dsps_order'. Typically at +install time the driver will only register one available /dev/dsp device +for its use. The 'dsps_order' module parameter allows for more devices +to be allocated, as a power of two. Up to 4 devices can be registered +( dsps_order=2 ). These devices act as fully distinct units and use +separate channels in the maestro. + +Power Management +---------------- + +As of version 0.14, this driver has a minimal understanding of PCI +Power Management. If it finds a valid power management capability +on the PCI device it will attempt to use the power management +functions of the maestro. It will only do this on Maestro 2Es and +only on machines that are known to function well. You can +force the use of power management by setting the 'use_pm' module +option to 1, or can disable it entirely by setting it to 0. + +When using power management, the driver does a few things +differently. It will keep the chip in a lower power mode +when the module is inserted but /dev/dsp is not open. This +allows the mixer to function but turns off the clocks +on other parts of the chip. When /dev/dsp is opened the chip +is brought into full power mode, and brought back down +when it is closed. It also powers down the chip entirely +when the module is removed or the machine is shutdown. This +can have nonobvious consequences. CD audio may not work +after a power managing driver is removed. Also, software that +doesn't understand power management may not be able to talk +to the powered down chip until the machine goes through a hard +reboot to bring it back. + +.. more details .. +------------------ + +drivers/sound/maestro.c contains comments that hopefully explain +the maestro implementation. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Maestro3 linux-2.5.6/Documentation/sound/oss/Maestro3 --- linux-2.5.6-pre3/Documentation/sound/oss/Maestro3 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Maestro3 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,84 @@ + An OSS/Lite Driver for the ESS Maestro3 family of sound chips + + Zach Brown, January 2001 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://www.zabbo.net/maestro3/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +Historically I've sucked pretty hard at actually doing that, however. + +ESS Maestro3 Chip Family +----------------------- + +The 'Maestro3' is much like the Maestro2 chip. The noted improvement +is the removal of the silicon in the '2' that did PCM mixing. All that +work is now done through a custom DSP called the ASSP, the Asynchronus +Specific Signal Processor. + +The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear +on the extent of the differences, but the driver supports them both :) + +The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, +both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as +0x199a when hardware strapping is used. + +The chip can also act as a multi function device. The modem IDs follow +the audio multimedia device IDs. (so the modem part of an Allegro shows +up as 0x1989) + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, with both read()/write(), and mmap(). + +/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro3. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. +The Allegro has an onchip AC'97. + +The driver doesn't support MIDI or FM playback at the moment. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 +maestro3' should also be added to your module configs (typically +/etc/modules.conf) if you're using modular OSS/Lite sound and want to +default to using a maestro3 chip. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other is 'external_amp', which tells the driver to attempt to enable +an external amplifier. This defaults to '1', you can tell the driver +not to bother enabling such an amplifier by setting it to '0'. + +Power Management +---------------- + +This driver has a minimal understanding of PCI Power Management. It will +try and power down the chip when the system is suspended, and power +it up with it is resumed. It will also try and power down the chip +when the machine is shut down. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/MultiSound linux-2.5.6/Documentation/sound/oss/MultiSound --- linux-2.5.6-pre3/Documentation/sound/oss/MultiSound Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/MultiSound Thu Mar 7 18:24:41 2002 @@ -0,0 +1,1137 @@ +#! /bin/sh +# +# Turtle Beach MultiSound Driver Notes +# -- Andrew Veliath +# +# Last update: September 10, 1998 +# Corresponding msnd driver: 0.8.3 +# +# ** This file is a README (top part) and shell archive (bottom part). +# The corresponding archived utility sources can be unpacked by +# running `sh MultiSound' (the utilities are only needed for the +# Pinnacle and Fiji cards). ** +# +# +# -=-=- Getting Firmware -=-=- +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# See the section `Obtaining and Creating Firmware Files' in this +# document for instructions on obtaining the necessary firmware +# files. +# +# +# Supported Features +# ~~~~~~~~~~~~~~~~~~ +# +# Currently, full-duplex digital audio (/dev/dsp only, /dev/audio is +# not currently available) and mixer functionality (/dev/mixer) are +# supported (memory mapped digital audio is not yet supported). +# Digital transfers and monitoring can be done as well if you have +# the digital daughterboard (see the section on using the S/PDIF port +# for more information). +# +# Support for the Turtle Beach MultiSound Hurricane architecture is +# composed of the following modules (these can also operate compiled +# into the kernel): +# +# msnd - MultiSound base (requires soundcore) +# +# msnd_classic - Base audio/mixer support for Classic, Monetery and +# Tahiti cards +# +# msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards +# +# +# Important Notes - Read Before Using +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# The firmware files are not included (may change in future). You +# must obtain these images from Turtle Beach (they are included in +# the MultiSound Development Kits), and place them in /etc/sound for +# example, and give the full paths in the Linux configuration. If +# you are compiling in support for the MultiSound driver rather than +# using it as a module, these firmware files must be accessible +# during kernel compilation. +# +# Please note these files must be binary files, not assembler. See +# the section later in this document for instructions to obtain these +# files. +# +# +# Configuring Card Resources +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# ** This section is very important, as your card may not work at all +# or your machine may crash if you do not do this correctly. ** +# +# * Classic/Monterey/Tahiti +# +# These cards are configured through the driver msnd_classic. You must +# know the io port, then the driver will select the irq and memory resources +# on the card. It is up to you to know if these are free locations or now, +# a conflict can lock the machine up. +# +# * Pinnacle/Fiji +# +# The Pinnacle and Fiji cards have an extra config port, either +# 0x250, 0x260 or 0x270. This port can be disabled to have the card +# configured strictly through PnP, however you lose the ability to +# access the IDE controller and joystick devices on this card when +# using PnP. The included pinnaclecfg program in this shell archive +# can be used to configure the card in non-PnP mode, and in PnP mode +# you can use isapnptools. These are described briefly here. +# +# pinnaclecfg is not required; you can use the msnd_pinnacle module +# to fully configure the card as well. However, pinnaclecfg can be +# used to change the resource values of a particular device after the +# msnd_pinnacle module has been loaded. If you are compiling the +# driver into the kernel, you must set these values during compile +# time, however other peripheral resource values can be changed with +# the pinnaclecfg program after the kernel is loaded. +# +# +# *** PnP mode +# +# Use pnpdump to obtain a sample configuration if you can; I was able +# to obtain one with the command `pnpdump 1 0x203' -- this may vary +# for you (running pnpdump by itself did not work for me). Then, +# edit this file and use isapnp to uncomment and set the card values. +# Use these values when inserting the msnd_pinnacle module. Using +# this method, you can set the resources for the DSP and the Kurzweil +# synth (Pinnacle). Since Linux does not directly support PnP +# devices, you may have difficulty when using the card in PnP mode +# when it the driver is compiled into the kernel. Using non-PnP mode +# is preferable in this case. +# +# Here is an example mypinnacle.conf for isapnp that sets the card to +# io base 0x210, irq 5 and mem 0xd8000, and also sets the Kurzweil +# synth to 0x330 and irq 9 (may need editing for your system): +# +# (READPORT 0x0203) +# (CSN 2) +# (IDENTIFY *) +# +# # DSP +# (CONFIGURE BVJ0440/-1 (LD 0 +# (INT 0 (IRQ 5 (MODE +E))) (IO 0 (BASE 0x0210)) (MEM 0 (BASE 0x0d8000)) +# (ACT Y))) +# +# # Kurzweil Synth (Pinnacle Only) +# (CONFIGURE BVJ0440/-1 (LD 1 +# (IO 0 (BASE 0x0330)) (INT 0 (IRQ 9 (MODE +E))) +# (ACT Y))) +# +# (WAITFORKEY) +# +# +# *** Non-PnP mode +# +# The second way is by running the card in non-PnP mode. This +# actually has some advantages in that you can access some other +# devices on the card, such as the joystick and IDE controller. To +# configure the card, unpack this shell archive and build the +# pinnaclecfg program. Using this program, you can assign the +# resource values to the card's devices, or disable the devices. As +# an alternative to using pinnaclecfg, you can specify many of the +# configuration values when loading the msnd_pinnacle module (or +# during kernel configuration when compiling the driver into the +# kernel). +# +# If you specify cfg=0x250 for the msnd_pinnacle module, it +# automatically configure the card to the given io, irq and memory +# values using that config port (the config port is jumper selectable +# on the card to 0x250, 0x260 or 0x270). +# +# See the `msnd_pinnacle Additional Options' section below for more +# information on these parameters (also, if you compile the driver +# directly into the kernel, these extra parameters can be useful +# here). +# +# +# ** It is very easy to cause problems in your machine if you choose a +# resource value which is incorrect. ** +# +# +# Examples +# ~~~~~~~~ +# +# * MultiSound Classic/Monterey/Tahiti: +# +# modprobe soundcore +# insmod msnd +# insmod msnd_classic io=0x290 irq=7 mem=0xd0000 +# +# * MultiSound Pinnacle in PnP mode: +# +# modprobe soundcore +# insmod msnd +# isapnp mypinnacle.conf +# insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 <-- match mypinnacle.conf values +# +# * MultiSound Pinnacle in non-PnP mode (replace 0x250 with your configuration port, +# one of 0x250, 0x260 or 0x270): +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 +# +# * To use the MPU-compatible Kurzweil synth on the Pinnacle in PnP +# mode, add the following (assumes you did `isapnp mypinnacle.conf'): +# +# insmod sound +# insmod mpu401 io=0x330 irq=9 <-- match mypinnacle.conf values +# +# * To use the MPU-compatible Kurzweil synth on the Pinnacle in non-PnP +# mode, add the following. Note how we first configure the peripheral's +# resources, _then_ install a Linux driver for it: +# +# insmod sound +# pinnaclecfg 0x250 mpu 0x330 9 +# insmod mpu401 io=0x330 irq=9 +# +# -- OR you can use the following sequence without pinnaclecfg in non-PnP mode: +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 mpu_io=0x330 mpu_irq=9 +# insmod sound +# insmod mpu401 io=0x330 irq=9 +# +# * To setup the joystick port on the Pinnacle in non-PnP mode (though +# you have to find the actual Linux joystick driver elsewhere), you +# can use pinnaclecfg: +# +# pinnaclecfg 0x250 joystick 0x200 +# +# -- OR you can configure this using msnd_pinnacle with the following: +# +# insmod soundcore +# insmod msnd +# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 joystick_io=0x200 +# +# +# msnd_classic, msnd_pinnacle Required Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# If the following options are not given, the module will not load. +# Examine the kernel message log for informative error messages. +# WARNING--probing isn't supported so try to make sure you have the +# correct shared memory area, otherwise you may experience problems. +# +# io I/O base of DSP, e.g. io=0x210 +# irq IRQ number, e.g. irq=5 +# mem Shared memory area, e.g. mem=0xd8000 +# +# +# msnd_classic, msnd_pinnacle Additional Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# fifosize The digital audio FIFOs, in kilobytes. If not +# specified, the default will be used. Increasing +# this value will reduce the chance of a FIFO +# underflow at the expense of increasing overall +# latency. For example, fifosize=512 will +# allocate 512kB read and write FIFOs (1MB total). +# While this may reduce dropouts, a heavy machine +# load will undoubtedly starve the FIFO of data +# and you will eventually get dropouts. One +# option is to alter the scheduling priority of +# the playback process, using `nice' or some form +# of POSIX soft real-time scheduling. +# +# calibrate_signal Setting this to one calibrates the ADCs to the +# signal, zero calibrates to the card (defaults +# to zero). +# +# +# msnd_pinnacle Additional Options +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# digital Specify digital=1 to enable the S/PDIF input +# if you have the digital daughterboard +# adapter. This will enable access to the +# DIGITAL1 input for the soundcard in the mixer. +# Some mixer programs might have trouble setting +# the DIGITAL1 source as an input. If you have +# trouble, you can try the setdigital.c program +# at the bottom of this document. +# +# cfg Non-PnP configuration port for the Pinnacle +# and Fiji (typically 0x250, 0x260 or 0x270, +# depending on the jumper configuration). If +# this option is omitted, then it is assumed +# that the card is in PnP mode, and that the +# specified DSP resource values are already +# configured with PnP (i.e. it won't attempt to +# do any sort of configuration). +# +# When the Pinnacle is in non-PnP mode, you can use the following +# options to configure particular devices. If a full specification +# for a device is not given, then the device is not configured. Note +# that you still must use a Linux driver for any of these devices +# once their resources are setup (such as the Linux joystick driver, +# or the MPU401 driver from OSS for the Kurzweil synth). +# +# mpu_io I/O port of MPU (on-board Kurzweil synth) +# mpu_irq IRQ of MPU (on-board Kurzweil synth) +# ide_io0 First I/O port of IDE controller +# ide_io1 Second I/O port of IDE controller +# ide_irq IRQ IDE controller +# joystick_io I/O port of joystick +# +# +# Obtaining and Creating Firmware Files +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For the Classic/Tahiti/Monterey +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Download to /tmp and unzip the following file from Turtle Beach: +# +# ftp://ftp.voyetra.com/pub/tbs/msndcl/msndvkit.zip +# +# When unzipped, unzip the file named MsndFiles.zip. Then copy the +# following firmware files to /etc/sound (note the file renaming): +# +# cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin +# cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin +# +# When configuring the Linux kernel, specify /etc/sound/msndinit.bin and +# /etc/sound/msndperm.bin for the two firmware files (Linux kernel +# versions older than 2.2 do not ask for firmware paths, and are +# hardcoded to /etc/sound). +# +# If you are compiling the driver into the kernel, these files must +# be accessible during compilation, but will not be needed later. +# The files must remain, however, if the driver is used as a module. +# +# +# For the Pinnacle/Fiji +# ~~~~~~~~~~~~~~~~~~~~~ +# +# Download to /tmp and unzip the following file from Turtle Beach (be +# sure to use the entire URL; some have had trouble navigating to the +# URL): +# +# ftp://ftp.voyetra.com/pub/tbs/pinn/pnddk100.zip +# +# Unpack this shell archive, and run make in the created directory +# (you need a C compiler and flex to build the utilities). This +# should give you the executables conv, pinnaclecfg and setdigital. +# conv is only used temporarily here to create the firmware files, +# while pinnaclecfg is used to configure the Pinnacle or Fiji card in +# non-PnP mode, and setdigital can be used to set the S/PDIF input on +# the mixer (pinnaclecfg and setdigital should be copied to a +# convenient place, possibly run during system initialization). +# +# To generating the firmware files with the `conv' program, we create +# the binary firmware files by doing the following conversion +# (assuming the archive unpacked into a directory named PINNDDK): +# +# ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin +# ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin +# +# The conv (and conv.l) program is not needed after conversion and can +# be safely deleted. Then, when configuring the Linux kernel, specify +# /etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two +# firmware files (Linux kernel versions older than 2.2 do not ask for +# firmware paths, and are hardcoded to /etc/sound). +# +# If you are compiling the driver into the kernel, these files must +# be accessible during compilation, but will not be needed later. +# The files must remain, however, if the driver is used as a module. +# +# +# Using Digital I/O with the S/PDIF Port +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# If you have a Pinnacle or Fiji with the digital daughterboard and +# want to set it as the input source, you can use this program if you +# have trouble trying to do it with a mixer program (be sure to +# insert the module with the digital=1 option, or say Y to the option +# during compiled-in kernel operation). Upon selection of the S/PDIF +# port, you should be able monitor and record from it. +# +# There is something to note about using the S/PDIF port. Digital +# timing is taken from the digital signal, so if a signal is not +# connected to the port and it is selected as recording input, you +# will find PCM playback to be distorted in playback rate. Also, +# attempting to record at a sampling rate other than the DAT rate may +# be problematic (i.e. trying to record at 8000Hz when the DAT signal +# is 44100Hz). If you have a problem with this, set the recording +# input to analog if you need to record at a rate other than that of +# the DAT rate. +# +# +# -- Shell archive attached below, just run `sh MultiSound' to extract. +# Contains Pinnacle/Fiji utilities to convert firmware, configure +# in non-PnP mode, and select the DIGITAL1 input for the mixer. +# +# +#!/bin/sh +# This is a shell archive (produced by GNU sharutils 4.2). +# To extract the files from this archive, save it to some FILE, remove +# everything before the `!/bin/sh' line above, then type `sh FILE'. +# +# Made on 1998-12-04 10:07 EST by . +# Source directory was `/home/andrewtv/programming/pinnacle/pinnacle'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 2046 -rw-rw-r-- MultiSound.d/setdigital.c +# 10235 -rw-rw-r-- MultiSound.d/pinnaclecfg.c +# 106 -rw-rw-r-- MultiSound.d/Makefile +# 141 -rw-rw-r-- MultiSound.d/conv.l +# 1472 -rw-rw-r-- MultiSound.d/msndreset.c +# +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir=FAILED +locale_dir=FAILED +first_param="$1" +for dir in $PATH +do + if test "$gettext_dir" = FAILED && test -f $dir/gettext \ + && ($dir/gettext --version >/dev/null 2>&1) + then + set `$dir/gettext --version 2>&1` + if test "$3" = GNU + then + gettext_dir=$dir + fi + fi + if test "$locale_dir" = FAILED && test -f $dir/shar \ + && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`$dir/shar --print-text-domain-dir` + fi +done +IFS="$save_IFS" +if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED +then + echo=echo +else + TEXTDOMAINDIR=$locale_dir + export TEXTDOMAINDIR + TEXTDOMAIN=sharutils + export TEXTDOMAIN + echo="$gettext_dir/gettext -s" +fi +touch -am 1231235999 $$.touch >/dev/null 2>&1 +if test ! -f 1231235999 && test -f $$.touch; then + shar_touch=touch +else + shar_touch=: + echo + $echo 'WARNING: not restoring timestamps. Consider getting and' + $echo "installing GNU \`touch', distributed in GNU File Utilities..." + echo +fi +rm -f 1231235999 $$.touch +# +if mkdir _sh01426; then + $echo 'x -' 'creating lock directory' +else + $echo 'failed to create lock directory' + exit 1 +fi +# ============= MultiSound.d/setdigital.c ============== +if test ! -d 'MultiSound.d'; then + $echo 'x -' 'creating directory' 'MultiSound.d' + mkdir 'MultiSound.d' +fi +if test -f 'MultiSound.d/setdigital.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/setdigital.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/setdigital.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/setdigital.c' && +/********************************************************************* +X * +X * setdigital.c - sets the DIGITAL1 input for a mixer +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +int main(int argc, char *argv[]) +{ +X int fd; +X unsigned long recmask, recsrc; +X +X if (argc != 2) { +X fprintf(stderr, "usage: setdigital \n"); +X exit(1); +X } +X +X if ((fd = open(argv[1], O_RDWR)) < 0) { +X perror(argv[1]); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) { +X fprintf(stderr, "error: ioctl read recording mask failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X if (!(recmask & SOUND_MASK_DIGITAL1)) { +X fprintf(stderr, "error: cannot find DIGITAL1 device in mixer\n"); +X close(fd); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) { +X fprintf(stderr, "error: ioctl read recording source failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X recsrc |= SOUND_MASK_DIGITAL1; +X +X if (ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) { +X fprintf(stderr, "error: ioctl write recording source failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X close(fd); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204092598 'MultiSound.d/setdigital.c' && + chmod 0664 'MultiSound.d/setdigital.c' || + $echo 'restore of' 'MultiSound.d/setdigital.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/setdigital.c:' 'MD5 check failed' +e87217fc3e71288102ba41fd81f71ec4 MultiSound.d/setdigital.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/setdigital.c'`" + test 2046 -eq "$shar_count" || + $echo 'MultiSound.d/setdigital.c:' 'original size' '2046,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/pinnaclecfg.c ============== +if test -f 'MultiSound.d/pinnaclecfg.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/pinnaclecfg.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/pinnaclecfg.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/pinnaclecfg.c' && +/********************************************************************* +X * +X * pinnaclecfg.c - Pinnacle/Fiji Device Configuration Program +X * +X * This is for NON-PnP mode only. For PnP mode, use isapnptools. +X * +X * This is Linux-specific, and must be run with root permissions. +X * +X * Part of the Turtle Beach MultiSound Sound Card Driver for Linux +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +#define IREG_LOGDEVICE 0x07 +#define IREG_ACTIVATE 0x30 +#define LD_ACTIVATE 0x01 +#define LD_DISACTIVATE 0x00 +#define IREG_EECONTROL 0x3F +#define IREG_MEMBASEHI 0x40 +#define IREG_MEMBASELO 0x41 +#define IREG_MEMCONTROL 0x42 +#define IREG_MEMRANGEHI 0x43 +#define IREG_MEMRANGELO 0x44 +#define MEMTYPE_8BIT 0x00 +#define MEMTYPE_16BIT 0x02 +#define MEMTYPE_RANGE 0x00 +#define MEMTYPE_HIADDR 0x01 +#define IREG_IO0_BASEHI 0x60 +#define IREG_IO0_BASELO 0x61 +#define IREG_IO1_BASEHI 0x62 +#define IREG_IO1_BASELO 0x63 +#define IREG_IRQ_NUMBER 0x70 +#define IREG_IRQ_TYPE 0x71 +#define IRQTYPE_HIGH 0x02 +#define IRQTYPE_LOW 0x00 +#define IRQTYPE_LEVEL 0x01 +#define IRQTYPE_EDGE 0x00 +X +#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) +X +typedef __u8 BYTE; +typedef __u16 USHORT; +typedef __u16 WORD; +X +static int config_port = -1; +X +static int msnd_write_cfg(int cfg, int reg, int value) +{ +X outb(reg, cfg); +X outb(value, cfg + 1); +X if (value != inb(cfg + 1)) { +X fprintf(stderr, "error: msnd_write_cfg: I/O error\n"); +X return -EIO; +X } +X return 0; +} +X +static int msnd_read_cfg(int cfg, int reg) +{ +X outb(reg, cfg); +X return inb(cfg + 1); +} +X +static int msnd_write_cfg_io0(int cfg, int num, WORD io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_io0(int cfg, int num, WORD *io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO0_BASELO), +X msnd_read_cfg(cfg, IREG_IO0_BASEHI)); +X +X return 0; +} +X +static int msnd_write_cfg_io1(int cfg, int num, WORD io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_io1(int cfg, int num, WORD *io) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO1_BASELO), +X msnd_read_cfg(cfg, IREG_IO1_BASEHI)); +X +X return 0; +} +X +static int msnd_write_cfg_irq(int cfg, int num, WORD irq) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_irq(int cfg, int num, WORD *irq) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *irq = msnd_read_cfg(cfg, IREG_IRQ_NUMBER); +X +X return 0; +} +X +static int msnd_write_cfg_mem(int cfg, int num, int mem) +{ +X WORD wmem; +X +X mem >>= 8; +X mem &= 0xfff; +X wmem = (WORD)mem; +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) +X return -EIO; +X if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_mem(int cfg, int num, int *mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X +X *mem = MAKEWORD(msnd_read_cfg(cfg, IREG_MEMBASELO), +X msnd_read_cfg(cfg, IREG_MEMBASEHI)); +X *mem <<= 8; +X +X return 0; +} +X +static int msnd_activate_logical(int cfg, int num) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) +X return -EIO; +X return 0; +} +X +static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_write_cfg_io0(cfg, num, io0)) +X return -EIO; +X if (msnd_write_cfg_io1(cfg, num, io1)) +X return -EIO; +X if (msnd_write_cfg_irq(cfg, num, irq)) +X return -EIO; +X if (msnd_write_cfg_mem(cfg, num, mem)) +X return -EIO; +X if (msnd_activate_logical(cfg, num)) +X return -EIO; +X return 0; +} +X +static int msnd_read_cfg_logical(int cfg, int num, WORD *io0, WORD *io1, WORD *irq, int *mem) +{ +X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) +X return -EIO; +X if (msnd_read_cfg_io0(cfg, num, io0)) +X return -EIO; +X if (msnd_read_cfg_io1(cfg, num, io1)) +X return -EIO; +X if (msnd_read_cfg_irq(cfg, num, irq)) +X return -EIO; +X if (msnd_read_cfg_mem(cfg, num, mem)) +X return -EIO; +X return 0; +} +X +static void usage(void) +{ +X fprintf(stderr, +X "\n" +X "pinnaclecfg 1.0\n" +X "\n" +X "usage: pinnaclecfg [device config]\n" +X "\n" +X "This is for use with the card in NON-PnP mode only.\n" +X "\n" +X "Available devices (not all available for Fiji):\n" +X "\n" +X " Device Description\n" +X " -------------------------------------------------------------------\n" +X " reset Reset all devices (i.e. disable)\n" +X " show Display current device configurations\n" +X "\n" +X " dsp Audio device\n" +X " mpu Internal Kurzweil synth\n" +X " ide On-board IDE controller\n" +X " joystick Joystick port\n" +X "\n"); +X exit(1); +} +X +static int cfg_reset(void) +{ +X int i; +X +X for (i = 0; i < 4; ++i) +X msnd_write_cfg_logical(config_port, i, 0, 0, 0, 0); +X +X return 0; +} +X +static int cfg_show(void) +{ +X int i; +X int count = 0; +X +X for (i = 0; i < 4; ++i) { +X WORD io0, io1, irq; +X int mem; +X msnd_read_cfg_logical(config_port, i, &io0, &io1, &irq, &mem); +X switch (i) { +X case 0: +X if (io0 || irq || mem) { +X printf("dsp 0x%x %d 0x%x\n", io0, irq, mem); +X ++count; +X } +X break; +X case 1: +X if (io0 || irq) { +X printf("mpu 0x%x %d\n", io0, irq); +X ++count; +X } +X break; +X case 2: +X if (io0 || io1 || irq) { +X printf("ide 0x%x 0x%x %d\n", io0, io1, irq); +X ++count; +X } +X break; +X case 3: +X if (io0) { +X printf("joystick 0x%x\n", io0); +X ++count; +X } +X break; +X } +X } +X +X if (count == 0) +X fprintf(stderr, "no devices configured\n"); +X +X return 0; +} +X +static int cfg_dsp(int argc, char *argv[]) +{ +X int io, irq, mem; +X +X if (argc < 3 || +X sscanf(argv[0], "0x%x", &io) != 1 || +X sscanf(argv[1], "%d", &irq) != 1 || +X sscanf(argv[2], "0x%x", &mem) != 1) +X usage(); +X +X if (!(io == 0x290 || +X io == 0x260 || +X io == 0x250 || +X io == 0x240 || +X io == 0x230 || +X io == 0x220 || +X io == 0x210 || +X io == 0x3e0)) { +X fprintf(stderr, "error: io must be one of " +X "210, 220, 230, 240, 250, 260, 290, or 3E0\n"); +X usage(); +X } +X +X if (!(irq == 5 || +X irq == 7 || +X irq == 9 || +X irq == 10 || +X irq == 11 || +X irq == 12)) { +X fprintf(stderr, "error: irq must be one of " +X "5, 7, 9, 10, 11 or 12\n"); +X usage(); +X } +X +X if (!(mem == 0xb0000 || +X mem == 0xc8000 || +X mem == 0xd0000 || +X mem == 0xd8000 || +X mem == 0xe0000 || +X mem == 0xe8000)) { +X fprintf(stderr, "error: mem must be one of " +X "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); +X usage(); +X } +X +X return msnd_write_cfg_logical(config_port, 0, io, 0, irq, mem); +} +X +static int cfg_mpu(int argc, char *argv[]) +{ +X int io, irq; +X +X if (argc < 2 || +X sscanf(argv[0], "0x%x", &io) != 1 || +X sscanf(argv[1], "%d", &irq) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 1, io, 0, irq, 0); +} +X +static int cfg_ide(int argc, char *argv[]) +{ +X int io0, io1, irq; +X +X if (argc < 3 || +X sscanf(argv[0], "0x%x", &io0) != 1 || +X sscanf(argv[0], "0x%x", &io1) != 1 || +X sscanf(argv[1], "%d", &irq) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 2, io0, io1, irq, 0); +} +X +static int cfg_joystick(int argc, char *argv[]) +{ +X int io; +X +X if (argc < 1 || +X sscanf(argv[0], "0x%x", &io) != 1) +X usage(); +X +X return msnd_write_cfg_logical(config_port, 3, io, 0, 0, 0); +} +X +int main(int argc, char *argv[]) +{ +X char *device; +X int rv = 0; +X +X --argc; ++argv; +X +X if (argc < 2) +X usage(); +X +X sscanf(argv[0], "0x%x", &config_port); +X if (config_port != 0x250 && config_port != 0x260 && config_port != 0x270) { +X fprintf(stderr, "error: must be 0x250, 0x260 or 0x270\n"); +X exit(1); +X } +X if (ioperm(config_port, 2, 1)) { +X perror("ioperm"); +X fprintf(stderr, "note: pinnaclecfg must be run as root\n"); +X exit(1); +X } +X device = argv[1]; +X +X argc -= 2; argv += 2; +X +X if (strcmp(device, "reset") == 0) +X rv = cfg_reset(); +X else if (strcmp(device, "show") == 0) +X rv = cfg_show(); +X else if (strcmp(device, "dsp") == 0) +X rv = cfg_dsp(argc, argv); +X else if (strcmp(device, "mpu") == 0) +X rv = cfg_mpu(argc, argv); +X else if (strcmp(device, "ide") == 0) +X rv = cfg_ide(argc, argv); +X else if (strcmp(device, "joystick") == 0) +X rv = cfg_joystick(argc, argv); +X else { +X fprintf(stderr, "error: unknown device %s\n", device); +X usage(); +X } +X +X if (rv) +X fprintf(stderr, "error: device configuration failed\n"); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204092598 'MultiSound.d/pinnaclecfg.c' && + chmod 0664 'MultiSound.d/pinnaclecfg.c' || + $echo 'restore of' 'MultiSound.d/pinnaclecfg.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/pinnaclecfg.c:' 'MD5 check failed' +366bdf27f0db767a3c7921d0a6db20fe MultiSound.d/pinnaclecfg.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/pinnaclecfg.c'`" + test 10235 -eq "$shar_count" || + $echo 'MultiSound.d/pinnaclecfg.c:' 'original size' '10235,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/Makefile ============== +if test -f 'MultiSound.d/Makefile' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/Makefile' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/Makefile' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/Makefile' && +CC = gcc +CFLAGS = -O +PROGS = setdigital msndreset pinnaclecfg conv +X +all: $(PROGS) +X +clean: +X rm -f $(PROGS) +SHAR_EOF + $shar_touch -am 1204092398 'MultiSound.d/Makefile' && + chmod 0664 'MultiSound.d/Makefile' || + $echo 'restore of' 'MultiSound.d/Makefile' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/Makefile:' 'MD5 check failed' +76ca8bb44e3882edcf79c97df6c81845 MultiSound.d/Makefile +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/Makefile'`" + test 106 -eq "$shar_count" || + $echo 'MultiSound.d/Makefile:' 'original size' '106,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/conv.l ============== +if test -f 'MultiSound.d/conv.l' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/conv.l' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/conv.l' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/conv.l' && +%% +[ \n\t,\r] +\;.* +DB +[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } +%% +int yywrap() { return 1; } +main() { yylex(); } +SHAR_EOF + $shar_touch -am 0828231798 'MultiSound.d/conv.l' && + chmod 0664 'MultiSound.d/conv.l' || + $echo 'restore of' 'MultiSound.d/conv.l' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/conv.l:' 'MD5 check failed' +d2411fc32cd71a00dcdc1f009e858dd2 MultiSound.d/conv.l +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/conv.l'`" + test 141 -eq "$shar_count" || + $echo 'MultiSound.d/conv.l:' 'original size' '141,' 'current size' "$shar_count!" + fi +fi +# ============= MultiSound.d/msndreset.c ============== +if test -f 'MultiSound.d/msndreset.c' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'MultiSound.d/msndreset.c' '(file already exists)' +else + $echo 'x -' extracting 'MultiSound.d/msndreset.c' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/msndreset.c' && +/********************************************************************* +X * +X * msndreset.c - resets the MultiSound card +X * +X * Copyright (C) 1998 Andrew Veliath +X * +X * This program is free software; you can redistribute it and/or modify +X * it under the terms of the GNU General Public License as published by +X * the Free Software Foundation; either version 2 of the License, or +X * (at your option) any later version. +X * +X * This program is distributed in the hope that it will be useful, +X * but WITHOUT ANY WARRANTY; without even the implied warranty of +X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +X * GNU General Public License for more details. +X * +X * You should have received a copy of the GNU General Public License +X * along with this program; if not, write to the Free Software +X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +X * +X ********************************************************************/ +X +#include +#include +#include +#include +#include +#include +#include +X +int main(int argc, char *argv[]) +{ +X int fd; +X +X if (argc != 2) { +X fprintf(stderr, "usage: msndreset \n"); +X exit(1); +X } +X +X if ((fd = open(argv[1], O_RDWR)) < 0) { +X perror(argv[1]); +X exit(1); +X } +X +X if (ioctl(fd, SOUND_MIXER_PRIVATE1, 0) < 0) { +X fprintf(stderr, "error: msnd ioctl reset failed\n"); +X perror("ioctl"); +X close(fd); +X exit(1); +X } +X +X close(fd); +X +X return 0; +} +SHAR_EOF + $shar_touch -am 1204100698 'MultiSound.d/msndreset.c' && + chmod 0664 'MultiSound.d/msndreset.c' || + $echo 'restore of' 'MultiSound.d/msndreset.c' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'MultiSound.d/msndreset.c:' 'MD5 check failed' +c52f876521084e8eb25e12e01dcccb8a MultiSound.d/msndreset.c +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/msndreset.c'`" + test 1472 -eq "$shar_count" || + $echo 'MultiSound.d/msndreset.c:' 'original size' '1472,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh01426 +exit 0 diff -urN linux-2.5.6-pre3/Documentation/sound/oss/NEWS linux-2.5.6/Documentation/sound/oss/NEWS --- linux-2.5.6-pre3/Documentation/sound/oss/NEWS Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/NEWS Thu Mar 7 18:24:41 2002 @@ -0,0 +1,42 @@ +Linux 2.4 Sound Changes +2000-September-25 +Christoph Hellwig, + + + +=== isapnp support + +The Linux 2.4 Kernel does have reliable in-kernel isapnp support. +Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically +detecting and configuring isapnp devices. +If you have a not yet supported isapnp soundcard, mail me the content +of '/proc/isapnp' on your system and some information about your card +and its driver(s) so I can try to get isapnp working for it. + + + +=== soundcard resources on kernel commandline + +Before Linux 2.4 you had to specify the resources for sounddrivers +statically linked into the kernel at compile time +(in make config/menuconfig/xconfig). In Linux 2.4 the ressources are +now specified at the boot-time kernel commandline (e.g. the lilo +'append=' line or everything that's after the kernel name in grub). +Read the Configure.help entry for your card for the parameters. + + +=== softoss is gone + +In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. +Use a user space software synthesizer like timidity instead. + + + +=== /dev/sndstat and /proc/sound are gone + +In older Linux versions those files exported some information about the +OSS/Free configuration to userspace. In Linux 2.3 they were removed because +they did not support the growing number of pci soundcards and there were +some general problems with this interface. + + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/NM256 linux-2.5.6/Documentation/sound/oss/NM256 --- linux-2.5.6-pre3/Documentation/sound/oss/NM256 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/NM256 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,280 @@ +======================================================= +Documentation for the NeoMagic 256AV/256ZX sound driver +======================================================= + +You're looking at version 1.1 of the driver. (Woohoo!) It has been +successfully tested against the following laptop models: + + Sony Z505S/Z505SX/Z505DX/Z505RX + Sony F150, F160, F180, F250, F270, F280, PCG-F26 + Dell Latitude CPi, CPt (various submodels) + +There are a few caveats, which is why you should read the entirety of +this document first. + +This driver was developed without any support or assistance from +NeoMagic. There is no warranty, expressed, implied, or otherwise. It +is free software in the public domain; feel free to use it, sell it, +give it to your best friends, even claim that you wrote it (but why?!) +but don't go whining to me, NeoMagic, Sony, Dell, or anyone else +when it blows up your computer. + +Version 1.1 contains a change to try and detect non-AC97 versions of +the hardware, and not install itself appropriately. It should also +reinitialize the hardware on an APM resume event, assuming that APM +was configured into your kernel. + +============ +Installation +============ + +Enable the sound drivers, the OSS sound drivers, and then the NM256 +driver. The NM256 driver *must* be configured as a module (it won't +give you any other choice). + +Next, do the usual "make modules" and "make modules_install". +Finally, insmod the soundcore, sound and nm256 modules. + +When the nm256 driver module is loaded, you should see a couple of +confirmation messages in the kernel logfile indicating that it found +the device (the device does *not* use any I/O ports or DMA channels). +Now try playing a wav file, futz with the CD-ROM if you have one, etc. + +The NM256 is entirely a PCI-based device, and all the necessary +information is automatically obtained from the card. It can only be +configured as a module in a vain attempt to prevent people from +hurting themselves. It works correctly if it shares an IRQ with +another device (it normally shares IRQ 9 with the builtin eepro100 +ethernet on the Sony Z505 laptops). + +It does not run the card in any sort of compatibility mode. It will +not work on laptops that have the SB16-compatible, AD1848-compatible +or CS4232-compatible codec/mixer; you will want to use the appropriate +compatible OSS driver with these chipsets. I cannot provide any +assistance with machines using the SB16, AD1848 or CS4232 compatible +versions. (The driver now attempts to detect the mixer version, and +will refuse to load if it believes the hardware is not +AC97-compatible.) + +The sound support is very basic, but it does include simultaneous +playback and record capability. The mixer support is also quite +simple, although this is in keeping with the rather limited +functionality of the chipset. + +There is no hardware synthesizer available, as the Losedows OPL-3 and +MIDI support is done via hardware emulation. + +Only three recording devices are available on the Sony: the +microphone, the CD-ROM input, and the volume device (which corresponds +to the stereo output). (Other devices may be available on other +models of laptops.) The Z505 series does not have a builtin CD-ROM, +so of course the CD-ROM input doesn't work. It does work on laptops +with a builtin CD-ROM drive. + +The mixer device does not appear to have any tone controls, at least +on the Z505 series. The mixer module checks for tone controls in the +AC97 mixer, and will enable them if they are available. + +============== +Known problems +============== + + * There are known problems with PCMCIA cards and the eepro100 ethernet + driver on the Z505S/Z505SX/Z505DX. Keep reading. + + * There are also potential problems with using a virtual X display, and + also problems loading the module after the X server has been started. + Keep reading. + + * The volume control isn't anywhere near linear. Sorry. This will be + fixed eventually, when I get sufficiently annoyed with it. (I doubt + it will ever be fixed now, since I've never gotten sufficiently + annoyed with it and nobody else seems to care.) + + * There are reports that the CD-ROM volume is very low. Since I do not + have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to + do remotely). + + * Only 8 fixed-rate speeds are supported. This is mainly a chipset + limitation. It may be possible to support other speeds in the future. + + * There is no support for the telephone mixer/codec. There is support + for a phonein/phoneout device in the mixer driver; whether or not + it does anything is anyone's guess. (Reports on this would be + appreciated. You'll have to figure out how to get the phone to + go off-hook before it'll work, tho.) + + * This driver was not written with any cooperation or support from + NeoMagic. If you have any questions about this, see their website + for their official stance on supporting open source drivers. + +============ +Video memory +============ + +The NeoMagic sound engine uses a portion of the display memory to hold +the sound buffer. (Crazy, eh?) The NeoMagic video BIOS sets up a +special pointer at the top of video RAM to indicate where the top of +the audio buffer should be placed. + +At the present time XFree86 is apparently not aware of this. It will +thus write over either the pointer or the sound buffer with abandon. +(Accelerated-X seems to do a better job here.) + +This implies a few things: + + * Sometimes the NM256 driver has to guess at where the buffer + should be placed, especially if the module is loaded after the + X server is started. It's usually correct, but it will consistently + fail on the Sony F250. + + * Virtual screens greater than 1024x768x16 under XFree86 are + problematic on laptops with only 2.5MB of screen RAM. This + includes all of the 256AV-equipped laptops. (Virtual displays + may or may not work on the 256ZX, which has at least 4MB of + video RAM.) + +If you start having problems with random noise being output either +constantly (this is the usual symptom on the F250), or when windows +are moved around (this is the usual symptom when using a virtual +screen), the best fix is to + + * Don't use a virtual frame buffer. + * Make sure you load the NM256 module before the X server is + started. + +On the F250, it is possible to force the driver to load properly even +after the XFree86 server is started by doing: + + insmod nm256 buffertop=0x25a800 + +This forces the audio buffers to the correct offset in screen RAM. + +One user has reported a similar problem on the Sony F270, although +others apparently aren't seeing any problems. His suggested command +is + + insmod nm256 buffertop=0x272800 + +================= +Official WWW site +================= + +The official site for the NM256 driver is: + + http://www.uglx.org/sony.html + +You should always be able to get the latest version of the driver there, +and the driver will be supported for the foreseeable future. + +============== +Z505RX and IDE +============== + +There appears to be a problem with the IDE chipset on the Z505RX; one +of the symptoms is that sound playback periodically hangs (when the +disk is accessed). The user reporting the problem also reported that +enabling all of the IDE chipset workarounds in the kernel solved the +problem, tho obviously only one of them should be needed--if someone +can give me more details I would appreciate it. + +============================== +Z505S/Z505SX on-board Ethernet +============================== + +If you're using the on-board Ethernet Pro/100 ethernet support on the Z505 +series, I strongly encourage you to download the latest eepro100 driver from +Donald Becker's site: + + ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c + +There was a reported problem on the Z505SX that if the ethernet +interface is disabled and reenabled while the sound driver is loaded, +the machine would lock up. I have included a workaround that is +working satisfactorily. However, you may occasionally see a message +about "Releasing interrupts, over 1000 bad interrupts" which indicates +that the workaround is doing its job. + +================================== +PCMCIA and the Z505S/Z505SX/Z505DX +================================== + +There is also a known problem with the Sony Z505S and Z505SX hanging +if a PCMCIA card is inserted while the ethernet driver is loaded, or +in some cases if the laptop is suspended. This is caused by tons of +spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges. + +There is currently no fix for the problem that works in every case. +The only known workarounds are to disable the ethernet interface +before inserting or removing a PCMCIA card, or with some cards +disabling the PCMCIA card before ejecting it will also help the +problem with the laptop hanging when the card is ejected. + +One user has reported that setting the tcic's cs_irq to some value +other than 9 (like 11) fixed the problem. This doesn't work on my +Z505S, however--changing the value causes the cardmgr to stop seeing +card insertions and removals, cards don't seem to work correctly, and +I still get hangs if a card is inserted when the kernel is booted. + +Using the latest ethernet driver and pcmcia package allows me to +insert an Adaptec 1480A SlimScsi card without the laptop hanging, +although I still have to shut down the card before ejecting or +powering down the laptop. However, similar experiments with a DE-660 +ethernet card still result in hangs when the card is inserted. I am +beginning to think that the interrupts are CardBus-related, since the +Adaptec card is a CardBus card, and the DE-660 is not; however, I +don't have any other CardBus cards to test with. + +====== +Thanks +====== + +First, I want to thank everyone (except NeoMagic of course) for their +generous support and encouragement. I'd like to list everyone's name +here that replied during the development phase, but the list is +amazingly long. + +I will be rather unfair and single out a few people, however: + + Justin Maurer, for being the first random net.person to try it, + and for letting me login to his Z505SX to get it working there + + Edi Weitz for trying out several different versions, and giving + me a lot of useful feedback + + Greg Rumple for letting me login remotely to get the driver + functional on the 256ZX, for his assistance on tracking + down all sorts of random stuff, and for trying out Accel-X + + Zach Brown, for the initial AC97 mixer interface design + + Jeff Garzik, for various helpful suggestions on the AC97 + interface + + "Mr. Bumpy" for feedback on the Z505RX + + Bill Nottingham, for generous assistance in getting the mixer ID + code working + +================= +Previous versions +================= + +Versions prior to 0.3 (aka `noname') had problems with weird artifacts +in the output and failed to set the recording rate properly. These +problems have long since been fixed. + +Versions prior to 0.5 had problems with clicks in the output when +anything other than 16-bit stereo sound was being played, and also had +periodic clicks when recording. + +Version 0.7 first incorporated support for the NM256ZX chipset, which +is found on some Dell Latitude laptops (the CPt, and apparently +some CPi models as well). It also included the generic AC97 +mixer module. + +Version 0.75 renamed all the functions and files with slightly more +generic names. + +Note that previous versions of this document claimed that recording was +8-bit only; it actually has been working for 16-bits all along. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/OPL3 linux-2.5.6/Documentation/sound/oss/OPL3 --- linux-2.5.6-pre3/Documentation/sound/oss/OPL3 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/OPL3 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,6 @@ +A pure OPL3 card is nice and easy to configure. Simply do + +insmod opl3 io=0x388 + +Change the I/O address in the very unlikely case this card is differently +configured diff -urN linux-2.5.6-pre3/Documentation/sound/oss/OPL3-SA linux-2.5.6/Documentation/sound/oss/OPL3-SA --- linux-2.5.6-pre3/Documentation/sound/oss/OPL3-SA Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/OPL3-SA Thu Mar 7 18:24:41 2002 @@ -0,0 +1,52 @@ +OPL3-SA1 sound driver (opl3sa.o) + +--- +Note: This howto only describes how to setup the OPL3-SA1 chip; this info +does not apply to the SA2, SA3, or SA4. +--- + +The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and +it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 +and OPL3 FM Synth capabilities. + +You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or +CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. + +You'll need to know all of the relevant info (irq, dma, and io port) for the +chip's WSS mode, since that is the mode the kernel sound driver uses, and of +course you'll also need to know about where the MPU401 and OPL3 ports and +IRQs are if you want to use those. + +Here's the skinny on how to load it as a module: + + modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 + +Module options in detail: + + io: This is the WSS's port base. + irq: This is the WSS's IRQ. + dma: This is the WSS's DMA line. In my BIOS setup screen this was + listed as "WSS Play DMA" + dma2: This is the WSS's secondary DMA line. My BIOS calls it the + "WSS capture DMA" + + mpu_io: This is the MPU401's port base. + mpu_irq: This is the MPU401's IRQ. + +If you'd like to use the OPL3 FM Synthesizer, make sure you enable +CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module. + +Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. + +You can also use the SoftOSS software synthesizer instead of the builtin OPL3. +Here's how: + +Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. + +If you said yes, the software synth is available once you boot your new +kernel. + +If you chose to build it as a module, just insmod the resulting softoss2.o + +Questions? Comments? + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/OPL3-SA2 linux-2.5.6/Documentation/sound/oss/OPL3-SA2 --- linux-2.5.6-pre3/Documentation/sound/oss/OPL3-SA2 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/OPL3-SA2 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,210 @@ +Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o) +--------------------------------------------------------------- + +Scott Murray, scott@spiteful.org +January 7, 2001 + +NOTE: All trade-marked terms mentioned below are properties of their + respective owners. + + +Supported Devices +----------------- + +This driver is for PnP soundcards based on the following Yamaha audio +controller chipsets: + +YMF711 aka OPL3-SA2 +YMF715 and YMF719 aka OPL3-SA3 + +Up until recently (December 2000), I'd thought the 719 to be a +different chipset, the OPL3-SAx. After an email exhange with +Yamaha, however, it turns out that the 719 is just a re-badged +715, and the chipsets are identical. The chipset detection code +has been updated to reflect this. + +Anyways, all of these chipsets implement the following devices: + +OPL3 FM synthesizer +Soundblaster Pro +Microsoft/Windows Sound System +MPU401 MIDI interface + +Note that this driver uses the MSS device, and to my knowledge these +chipsets enforce an either/or situation with the Soundblaster Pro +device and the MSS device. Since the MSS device has better +capabilities, I have implemented the driver to use it. + + +Mixer Channels +-------------- + +Older versions of this driver (pre-December 2000) had two mixers, +an OPL3-SA2 or SA3 mixer and a MSS mixer. The OPL3-SA[23] mixer +device contained a superset of mixer channels consisting of its own +channels and all of the MSS mixer channels. To simplify the driver +considerably, and to partition functionality better, the OPL3-SA[23] +mixer device now contains has its own specific mixer channels. They +are: + +Volume - Hardware master volume control +Bass - SA3 only, now supports left and right channels +Treble - SA3 only, now supports left and right channels +Microphone - Hardware microphone input volume control +Digital1 - Yamaha 3D enhancement "Wide" mixer + +All other mixer channels (e.g. "PCM", "CD", etc.) now have to be +controlled via the "MS Sound System (CS4231)" mixer. To facilitate +this, the mixer device creation order has been switched so that +the MSS mixer is created first. This allows accessing the majority +of the useful mixer channels even via single mixer-aware tools +such as "aumix". + + +Plug 'n Play +------------ + +In previous kernels (2.2.x), some configuration was required to +get the driver to talk to the card. Being the new millennium and +all, the 2.4.x kernels now support auto-configuration if ISA PnP +support is configured in. Theoretically, the driver even supports +having more than one card in this case. + +With the addition of PnP support to the driver, two new parameters +have been added to control it: + +isapnp - set to 0 to disable ISA PnP card detection + +multiple - set to 0 to disable multiple PnP card detection + + +Optional Parameters +------------------- + +Recent (December 2000) additions to the driver (based on a patch +provided by Peter Englmaier) are two new parameters: + +ymode - Set Yamaha 3D enhancement mode: + 0 = Desktop/Normal 5-12 cm speakers + 1 = Notebook PC (1) 3 cm speakers + 2 = Notebook PC (2) 1.5 cm speakers + 3 = Hi-Fi 16-38 cm speakers + +loopback - Set A/D input source. Useful for echo cancellation: + 0 = Mic Right channel (default) + 1 = Mono output loopback + +The ymode parameter has been tested and does work. The loopback +parameter, however, is untested. Any feedback on its usefulness +would be appreciated. + + +Manual Configuration +-------------------- + +If for some reason you decide not to compile ISA PnP support into +your kernel, or disabled the driver's usage of it by setting the +isapnp parameter as discussed above, then you will need to do some +manual configuration. There are two ways of doing this. The most +common is to use the isapnptools package to initialize the card, and +use the kernel module form of the sound subsystem and sound drivers. +Alternatively, some BIOS's allow manual configuration of installed +PnP devices in a BIOS menu, which should allow using the non-modular +sound drivers, i.e. built into the kernel. + +I personally use isapnp and modules, and do not have access to a PnP +BIOS machine to test. If you have such a beast, configuring the +driver to be built into the kernel should just work (thanks to work +done by David Luyer ). You will still need +to specify settings, which can be done by adding: + +opl3sa2=,,,,, + +to the kernel command line. For example: + +opl3sa2=0x370,5,0,1,0x530,0x330 + +If you are instead using the isapnp tools (as most people have been +before Linux 2.4.x), follow the directions in their documentation to +produce a configuration file. Here is the relevant excerpt I used to +use for my SA3 card from my isapnp.conf: + +(CONFIGURE YMH0800/-1 (LD 0 + +# NOTE: IO 0 is for the unused SoundBlaster part of the chipset. +(IO 0 (BASE 0x0220)) +(IO 1 (BASE 0x0530)) +(IO 2 (BASE 0x0388)) +(IO 3 (BASE 0x0330)) +(IO 4 (BASE 0x0370)) +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 0)) +(DMA 1 (CHANNEL 1)) + +Here, note that: + +Port Acceptable Range Purpose +---- ---------------- ------- +IO 0 0x0220 - 0x0280 SB base address, unused. +IO 1 0x0530 - 0x0F48 MSS base address +IO 2 0x0388 - 0x03F8 OPL3 base address +IO 3 0x0300 - 0x0334 MPU base address +IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports + +The IRQ and DMA values can be any that are considered acceptable for a +MSS. Assuming you've got isapnp all happy, then you should be able to +do something like the following (which matches up with the isapnp +configuration above): + +modprobe mpu401 +modprobe ad1848 +modprobe opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=5 dma=0 dma2=1 +modprobe opl3 io=0x388 + +See the section "Automatic Module Loading" below for how to set up +/etc/modules.conf to automate this. + +An important thing to remember that the opl3sa2 module's io argument is +for it's own control port, which handles the card's master mixer for +volume (on all cards), and bass and treble (on SA3 cards). + + +Troubleshooting +--------------- + +If all goes well and you see no error messages, you should be able to +start using the sound capabilities of your system. If you get an +error message while trying to insert the opl3sa2 module, then make +sure that the values of the various arguments match what you specified +in your isapnp configuration file, and that there is no conflict with +another device for an I/O port or interrupt. Checking the contents of +/proc/ioports and /proc/interrupts can be useful to see if you're +butting heads with another device. + +If you still cannot get the module to load, look at the contents of +your system log file, usually /var/log/messages. If you see the +message "opl3sa2: Unknown Yamaha audio controller version", then you +have a different chipset version than I've encountered so far. Look +for all messages in the log file that start with "opl3sa2: " and see +if they provide any clues. If you do not see the chipset version +message, and none of the other messages present in the system log are +helpful, email me some details and I'll try my best to help. + + +Automatic Module Loading +------------------------ + +Lastly, if you're using modules and want to set up automatic module +loading with kmod, the kernel module loader, here is the section I +currently use in my modules.conf file: + +# Sound +alias sound-slot-0 opl3sa2 +options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3 +options opl3 io=0x388 + +That's all it currently takes to get an OPL3-SA3 card working on my +system. Once again, if you have any other problems, email me at the +address listed above. + +Scott diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Opti linux-2.5.6/Documentation/sound/oss/Opti --- linux-2.5.6-pre3/Documentation/sound/oss/Opti Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Opti Thu Mar 7 18:24:41 2002 @@ -0,0 +1,222 @@ +Support for the OPTi 82C931 chip +-------------------------------- +Note: parts of this README file apply also to other +cards that use the mad16 driver. + +Some items in this README file are based on features +added to the sound driver after Linux-2.1.91 was out. +By the time of writing this I do not know which official +kernel release will include these features. +Please do not report inconsistencies on older Linux +kernels. + +The OPTi 82C931 is supported in its non-PnP mode. +Usually you do not need to set jumpers, etc. The sound driver +will check the card status and if it is required it will +force the card into a mode in which it can be programmed. + +If you have another OS installed on your computer it is recommended +that Linux and the other OS use the same resources. + +Also, it is recommended that resources specified in /etc/modules.conf +and resources specified in /etc/isapnp.conf agree. + +Compiling the sound driver +-------------------------- +I highly recommend that you build a modularized sound driver. +This document does not cover a sound-driver which is built in +the kernel. + +Sound card support should be enabled as a module (chose m). +Answer 'm' for these items: + Generic OPL2/OPL3 FM synthesizer support (CONFIG_SOUND_ADLIB) + Microsoft Sound System support (CONFIG_SOUND_MSS) + Support for OPTi MAD16 and/or Mozart based cards (CONFIG_SOUND_MAD16) + FM synthesizer (YM3812/OPL-3) support (CONFIG_SOUND_YM3812) + +The configuration menu may ask for addresses, IRQ lines or DMA +channels. If the card is used as a module the module loading +options will override these values. + +For the OPTi 931 you can answer 'n' to: + Support MIDI in older MAD16 based cards (requires SB) (CONFIG_SOUND_MAD16_OLDCARD) +If you do need MIDI support in a Mozart or C928 based card you +need to answer 'm' to the above question. In that case you will +also need to answer 'm' to: + '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SOUND_SB) + +Go on and compile your kernel and modules. Install the modules. Run depmod -a. + +Using isapnptools +----------------- +In most systems with a PnP BIOS you do not need to use isapnp. The +initialization provided by the BIOS is sufficient for the driver +to pick up the card and continue initialization. + +If that fails, or if you have other PnP cards, you need to use isapnp +to initialize the card. +This was tested with isapnptools-1.11 but I recommend that you use +isapnptools-1.13 (or newer). Run pnpdump to dump the information +about your PnP cards. Then edit the resulting file and select +the options of your choice. This file is normally installed as +/etc/isapnp.conf. + +The driver has one limitation with respect to I/O port resources: +IO3 base must be 0x0E0C. Although isapnp allows other ports, this +address is hard-coded into the driver. + +Using kmod and autoloading the sound driver +------------------------------------------- +Comment: as of linux-2.1.90 kmod is replacing kerneld. +The config file '/etc/modules.conf' is used as before. + +This is the sound part of my /etc/modules.conf file. +Following that I will explain each line. + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 +options sb mad16=1 +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +If you have an MPU daughtercard or onboard MPU you will want to add to the +"options mad16" line - eg + +options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 + +To set the I/O and IRQ of the MPU. + + +Explain: + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 + +When any sound device is opened the kernel requests auto-loading +of char-major-14. There is a built-in alias that translates this +request to loading the main sound module. + +The sound module in its turn will request loading of a sub-driver +for mixer, audio, midi or synthesizer device. The first 3 are +supported by the mad16 driver. The synth device is supported +by the opl3 driver. + +There is currently no way to autoload the sound device driver +if more than one card is installed. + +options sb mad16=1 + +This is left for historical reasons. If you enable the +config option 'Support MIDI in older MAD16 based cards (requires SB)' +or if you use an older mad16 driver it will force loading of the +SoundBlaster driver. This option tells the SB driver not to look +for a SB card but to wait for the mad16 driver. + +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 + +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +This sets resources and options for the mad16 and opl3 drivers. +I use two DMA channels (only one is required) to enable full duplex. +joystick=1 enables the joystick port. cdtype=0 disables the cd port. +You can also set mpu_io and mpu_irq in the mad16 options for the +uart401 driver. + +This tells modprobe to run /sbin/ad1848_mixer_reroute after +mad16 is successfully loaded and initialized. The source +for ad1848_mixer_reroute is appended to the end of this readme +file. It is impossible for the sound driver to know the actual +connections to the mixer. The 3 inputs intended for cd, synth +and line-in are mapped to the generic inputs line1, line2 and +line3. This program reroutes these mixer channels to their +right names (note the right mapping depends on the actual sound +card that you use). +The numeric parameters mean: + 14=line1 8=cd - reroute line1 to the CD input. + 15=line2 3=synth - reroute line2 to the synthesizer input. + 16=line3 6=line - reroute line3 to the line input. +For reference on other input names look at the file +/usr/include/linux/soundcard.h. + +Using a joystick +----------------- +You must enable a joystick in the mad16 options. (also +in /etc/isapnp.conf if you use it). +Tested with regular analog joysticks. + +A CDROM drive connected to the sound card +----------------------------------------- +The 82C931 chip has support only for secondary ATAPI cdrom. +(cdtype=8). Loading the mad16 driver resets the C931 chip +and if a cdrom was already mounted it may cause a complete +system hang. Do not use the sound card if you have an alternative. +If you do use the sound card it is important that you load +the mad16 driver (use "modprobe mad16" to prevent auto-unloading) +before the cdrom is accessed the first time. + +Using the sound driver built-in to the kernel may help here, but... +Most new systems have a PnP BIOS and also two IDE controllers. +The IDE controller on the sound card may be needed only on older +systems (which have only one IDE controller) but these systems +also do not have a PnP BIOS - requiring isapnptools and a modularized +driver. + +Known problems +-------------- +1. See the section on "A CDROM drive connected to the sound card". + +2. On my system the codec cannot capture companded sound samples. + (eg., recording from /dev/audio). When any companded capture is + requested I get stereo-16 bit samples instead. Playback of + companded samples works well. Apparently this problem is not common + to all C931 based cards. I do not know how to identify cards that + have this problem. + +Source for ad1848_mixer_reroute.c +--------------------------------- +#include +#include +#include + +static char *mixer_names[SOUND_MIXER_NRDEVICES] = + SOUND_DEVICE_LABELS; + +int +main(int argc, char **argv) { + int val, from, to; + int i, fd; + + fd = open("/dev/mixer", O_RDWR); + if(fd < 0) { + perror("/dev/mixer"); + return 1; + } + + for(i = 2; i < argc; i += 2) { + from = atoi(argv[i-1]); + to = atoi(argv[i]); + + if(to == SOUND_MIXER_NONE) + fprintf(stderr, "%s: turning off mixer %s\n", + argv[0], mixer_names[to]); + else + fprintf(stderr, "%s: rerouting mixer %s to %s\n", + argv[0], mixer_names[from], mixer_names[to]); + + val = from << 8 | to; + + if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { + perror("AD1848 mixer reroute"); + return 1; + } + } + + return 0; +} + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/PAS16 linux-2.5.6/Documentation/sound/oss/PAS16 --- linux-2.5.6-pre3/Documentation/sound/oss/PAS16 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/PAS16 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,163 @@ +Pro Audio Spectrum 16 for 2.3.99 and later +========================================= +by Thomas Molina (tmolina@home.com) +last modified 3 Mar 2001 +Acknowledgement to Axel Boldt (boldt@math.ucsb.edu) for stuff taken +from Configure.help, Riccardo Facchetti for stuff from README.OSS, +and others whose names I could not find. + +This documentation is relevant for the PAS16 driver (pas2_card.c and +friends) under kernel version 2.3.99 and later. If you are +unfamiliar with configuring sound under Linux, please read the +Sound-HOWTO, linux/Documentation/sound/Introduction and other +relevant docs first. + +The following information is relevant information from README.OSS +and legacy docs for the Pro Audio Spectrum 16 (PAS16): +================================================================== + +The pas2_card.c driver supports the following cards -- +Pro Audio Spectrum 16 (PAS16) and compatibles: + Pro Audio Spectrum 16 + Pro Audio Studio 16 + Logitech Sound Man 16 + NOTE! The original Pro Audio Spectrum as well as the PAS+ are not + and will not be supported by the driver. + +The sound driver configuration dialog +------------------------------------- + +Sound configuration starts by making some yes/no questions. Be careful +when answering to these questions since answering y to a question may +prevent some later ones from being asked. For example don't answer y to +the question about (PAS16) if you don't really have a PAS16. Sound +configuration may also be made modular by answering m to configuration +options presented. + +Note also that all questions may not be asked. The configuration program +may disable some questions depending on the earlier choices. It may also +select some options automatically as well. + + "ProAudioSpectrum 16 support", + - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, + Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that + you read the above list correctly). Don't answer 'y' if you + have some other card made by Media Vision or Logitech since they + are not PAS16 compatible. + NOTE! Since 3.5-beta10 you need to enable SB support (next question) + if you want to use the SB emulation of PAS16. It's also possible to + the emulation if you want to use a true SB card together with PAS16 + (there is another question about this that is asked later). + + "Generic OPL2/OPL3 FM synthesizer support", + - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + The PAS16 has an OPL3-compatible FM chip. + +With PAS16 you can use two audio device files at the same time. /dev/dsp (and +/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and +/dev/audio1) is connected to the SB emulation (8 bit mono only). + + +The new stuff for 2.3.99 and later +============================================================================ +The following configuration options from linux/Documentation/Configure.help +are relevant to configuring the PAS16: + +Sound card support +CONFIG_SOUND + If you have a sound card in your computer, i.e. if it can say more + than an occasional beep, say Y. Be sure to have all the information + about your sound card and its configuration down (I/O port, + interrupt and DMA channel), because you will be asked for it. + + You want to read the Sound-HOWTO, available from + http://www.linuxdoc.org/docs.html#howto . General information + about the modular sound system is contained in the files + Documentation/sound/Introduction. The file + Documentation/sound/README.OSS contains some slightly outdated but + still useful information as well. + +OSS sound modules +CONFIG_SOUND_OSS + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or M + here (the module will be called sound.o) if you haven't found a + driver for your sound card above, then pick your driver from the + list below. + +Persistent DMA buffers +CONFIG_SOUND_DMAP + Linux can often have problems allocating DMA buffers for ISA sound + cards on machines with more than 16MB of RAM. This is because ISA + DMA buffers must exist below the 16MB boundary and it is quite + possible that a large enough free block in this region cannot be + found after the machine has been running for a while. If you say Y + here the DMA buffers (64Kb) will be allocated at boot time and kept + until the shutdown. This option is only useful if you said Y to + "OSS sound modules", above. If you said M to "OSS sound modules" + then you can get the persistent DMA buffer functionality by passing + the command-line argument "dmabuf=1" to the sound.o module. + + Say y here for PAS16. + +ProAudioSpectrum 16 support +CONFIG_SOUND_PAS + Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio + 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have + some other card made by Media Vision or Logitech since they are not + PAS16 compatible. It is not necessary to enable the separate + Sound Blaster support; it is included in the PAS driver. + + If you compile the driver into the kernel, you have to add + "pas2=,,,,,,, + to the kernel command line. + +FM Synthesizer (YM3812/OPL-3) support +CONFIG_SOUND_YM3812 + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). + Please read the file Documentation/sound/OPL3 if your card has an + OPL3 chip. + If you compile the driver into the kernel, you have to add + "opl3=" to the kernel command line. + + If you compile your drivers into the kernel, you MUST configure + OPL3 support as a module for PAS16 support to work properly. + You can then get OPL3 functionality by issuing the command: + insmod opl3 + In addition, you must either add the following line to + /etc/modules.conf: + options opl3 io=0x388 + or else add the following line to /etc/lilo.conf: + opl3=0x388 + + +EXAMPLES +=================================================================== +To use the PAS16 in my computer I have enabled the following sound +configuration options: + +CONFIG_SOUND=y +CONFIG_SOUND_OSS=y +CONFIG_SOUND_TRACEINIT=y +CONFIG_SOUND_DMAP=y +CONFIG_SOUND_PAS=y +CONFIG_SOUND_SB=n +CONFIG_SOUND_YM3812=m + +I have also included the following append line in /etc/lilo.conf: +append="pas2=0x388,10,3,-1,0x220,5,1,-1 sb=0x220,5,1,-1 opl3=0x388" + +The io address of 0x388 is default configuration on the PAS16. The +irq of 10 and dma of 3 may not match your installation. The above +configuration enables PAS16, 8-bit Soundblaster and OPL3 +functionality. If Soundblaster functionality is not desired, the +following line would be appropriate: +append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388" + +If sound is built totally modular, the above options may be +specified in /etc/modules.conf for pas2.o, sb.o and opl3.o +respectively. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/PSS linux-2.5.6/Documentation/sound/oss/PSS --- linux-2.5.6-pre3/Documentation/sound/oss/PSS Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/PSS Thu Mar 7 18:24:41 2002 @@ -0,0 +1,41 @@ +The PSS cards and other ECHO based cards provide an onboard DSP with +downloadable programs and also has an AD1848 "Microsoft Sound System" +device. The PSS driver enables MSS and MPU401 modes of the card. SB +is not enabled since it doesn't work concurrently with MSS. + +If you build this driver as a module then the driver takes the following +parameters + +pss_io. The I/O base the PSS card is configured at (normally 0x220 + or 0x240) + +mss_io The base address of the Microsoft Sound System interface. + This is normally 0x530, but may be 0x604 or other addresses. + +mss_irq The interrupt assigned to the Microsoft Sound System + emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you + get IRQ errors be sure to check the interrupt is set to + "ISA/Legacy" in the BIOS on modern machines. + +mss_dma The DMA channel used by the Microsoft Sound System. + This can be 0, 1, or 3. DMA 0 is not available on older + machines and will cause a crash on them. + +mpu_io The MPU emulation base address. This sets the base of the + synthesizer. It is typically 0x330 but can be altered. + +mpu_irq The interrupt to use for the synthesizer. It must differ + from the IRQ used by the Microsoft Sound System port. + + +The mpu_io/mpu_irq fields are optional. If they are not specified the +synthesizer parts are not configured. + +When the module is loaded it looks for a file called +/etc/sound/pss_synth. This is the firmware file from the DOS install disks. +This fil holds a general MIDI emulation. The file expected is called +genmidi.ld on newer DOS driver install disks and synth.ld on older ones. + +You can also load alternative DSP algorithms into the card if you wish. One +alternative driver can be found at http://www.mpg123.de/ + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/PSS-updates linux-2.5.6/Documentation/sound/oss/PSS-updates --- linux-2.5.6-pre3/Documentation/sound/oss/PSS-updates Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/PSS-updates Thu Mar 7 18:24:41 2002 @@ -0,0 +1,88 @@ + This file contains notes for users of PSS sound cards who wish to use the +newly added features of the newest version of this driver. + + The major enhancements present in this new revision of this driver is the +addition of two new module parameters that allow you to take full advantage of +all the features present on your PSS sound card. These features include the +ability to enable both the builtin CDROM and joystick ports. + +pss_enable_joystick + + This parameter is basically a flag. A 0 will leave the joystick port +disabled, while a non-zero value would enable the joystick port. The default +setting is pss_enable_joystick=0 as this keeps this driver fully compatable +with systems that were using previous versions of this driver. If you wish to +enable the joystick port you will have to add pss_enable_joystick=1 as an +argument to the driver. To actually use the joystick port you will then have +to load the joystick driver itself. Just remember to load the joystick driver +AFTER the pss sound driver. + +pss_cdrom_port + + This parameter takes a port address as its parameter. Any available port +address can be specified to enable the CDROM port, except for 0x0 and -1 as +these values would leave the port disabled. Like the joystick port, the cdrom +port will require that an appropiate CDROM driver be loaded before you can make +use of the newly enabled CDROM port. Like the joystick port option above, +remember to load the CDROM driver AFTER the pss sound driver. While it may +differ on some PSS sound cards, all the PSS sound cards that I have seen have a +builtin Wearnes CDROM port. If this is the case with your PSS sound card you +should load aztcd with the appropiate port option that matches the port you +assigned to the CDROM port when you loaded your pss sound driver. (ex. +modprobe pss pss_cdrom_port=0x340 && modprobe aztcd aztcd=0x340) The default +setting of this parameter leaves the CDROM port disabled to maintain full +compatability with systems using previous versions of this driver. + + Other options have also been added for the added convenience and utility +of the user. These options are only available if this driver is loaded as a +module. + +pss_no_sound + + This module parameter is a flag that can be used to tell the driver to +just configure non-sound components. 0 configures all components, a non-0 +value will only attept to configure the CDROM and joystick ports. This +parameter can be used by a user who only wished to use the builtin joystick +and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this +parameter and with the paramter below set to true then a user can safely unload +this driver with the following command "rmmod pss && rmmod ad1848 && rmmod +mpu401 && rmmod sound && rmmod soundcore" and retain the full functionality of +his CDROM and/or joystick port(s) while gaining back the memory previously used +by the sound drivers. This default setting of this parameter is 0 to retain +full behavioral compatability with previous versions of this driver. + +pss_keep_settings + + This parameter can be used to specify whether you want the driver to reset +all emulations whenever its unloaded. This can be useful for those who are +sharing resources (io ports, IRQ's, DMA's) between different ISA cards. This +flag can also be useful in that future versions of this driver may reset all +emulations by default on the driver's unloading (as it probably should), so +specifying it now will ensure that all future versions of this driver will +continue to work as expected. The default value of this parameter is 1 to +retain full behavioral compatability with previous versions of this driver. + +pss_firmware + + This parameter can be used to specify the file containing the firmware +code so that a user could tell the driver where that file is located instead +of having to put it in a predefined location with a predefined name. The +default setting of this parameter is "/etc/sound/pss_synth" as this was the +path and filename the hardcoded value in the previous versions of this driver. + +Examples: + +# Normal PSS sound card system, loading of drivers. +# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). + +/sbin/modprobe pss pss_io=0x220 mpu_io=0x338 mpu_irq=9 mss_io=0x530 mss_irq=10 mss_dma=1 pss_cdrom_port=0x340 pss_enable_joystick=1 +/sbin/modprobe aztcd aztcd=0x340 +/sbin/modprobe joystick + +# System using the PSS sound card just for its CDROM and joystick ports. +# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). + +/sbin/modprobe pss pss_io=0x220 pss_cdrom_port=0x340 pss_enable_joystick=1 pss_no_sound=1 +/sbin/rmmod pss && /sbin/rmmod ad1848 && /sbin/rmmod mpu401 && /sbin/rmmod sound && /sbin/rmmod soundcore # This line not needed, but saves memory. +/sbin/modprobe aztcd aztcd=0x340 +/sbin/modprobe joystick diff -urN linux-2.5.6-pre3/Documentation/sound/oss/README.OSS linux-2.5.6/Documentation/sound/oss/README.OSS --- linux-2.5.6-pre3/Documentation/sound/oss/README.OSS Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/README.OSS Thu Mar 7 18:24:41 2002 @@ -0,0 +1,1456 @@ +Introduction +------------ + +This file is a collection of all the old Readme files distributed with +OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded +on it I think these information may still be interesting for users that +have to configure their sound system. + +Be warned: Alan Cox is the current maintainer of the Linux sound driver so if +you have problems with it, please contact him or the current device-specific +driver maintainer (e.g. for aedsp16 specific problems contact me). If you have +patches, contributions or suggestions send them to Alan: I'm sure they are +welcome. + +In this document you will find a lot of references about OSS/Lite or ossfree: +they are gone forever. Keeping this in mind and with a grain of salt this +document can be still interesting and very helpful. + +[ File edited 17.01.1999 - Riccardo Facchetti ] +[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] + +OSS/Free version 3.8 release notes +---------------------------------- + +Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP +sites). It gives instructions about using sound with Linux. It's bit out of +date but still very useful. Information about bug fixes and such things +is available from the web page (see above). + +Please check http://www.opensound.com/pguide for more info about programming +with OSS API. + + ==================================================== +- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. + ==================================================== + +Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" +contain useful utilities to be used with this driver. +See http://www.opensound.com/ossfree/getting.html for +download instructions. + +If you are looking for the installation instructions, please +look forward into this document. + +Supported sound cards +--------------------- + +See below. + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given useful suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Jeff Tranter Linux SOUND HOWTO document + Greg Lee Volume computation algorithm for the GUS and + lots of valuable suggestions. + Andy Warner ISC port + Jim Lowe, + Amancio Hasty Jr FreeBSD/NetBSD port + Anders Baekgaard Bug hunting and valuable suggestions. + Joerg Schubert SB16 DSP support (initial version). + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro (initial version). + Mikael Nordqvist Linear volume support for GUS and + nonblocking /dev/sequencer. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Mixer support for SG NX Pro. + Marc Hoffman PSS support (initial version). + Rainer Vranken Initialization for Jazz16 (initial version). + Peter Trattler Initial version of loadable module support for Linux. + JRA Gibson 16 bit mode for Jazz16 (initial version) + Davor Jadrijevic MAD16 support (initial version) + Gregor Hoffleit Mozart support (initial version) + Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support + James Hightower Spotting a tiny but important bug in CS423x support. + Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) + Tim MacKenzie Full duplex support for OPTi 82C930. + + Please look at lowlevel/README for more contributors. + +There are probably many other names missing. If you have sent me some +patches and your name is not in the above list, please inform me. + +Sending your contributions or patches +------------------------------------- + +First of all it's highly recommended to contact me before sending anything +or before even starting to do any work. Tell me what you suggest to be +changed or what you have planned to do. Also ensure you are using the +very latest (development) version of OSS/Free since the change may already be +implemented there. In general it's a major waste of time to try to improve a +several months old version. Information about the latest version can be found +from http://www.opensound.com/ossfree. In general there is no point in +sending me patches relative to production kernels. + +Sponsors etc. +------------- + +The following companies have greatly helped development of this driver +in form of a free copy of their product: + +Novell, Inc. UnixWare personal edition + SDK +The Santa Cruz Operation, Inc. A SCO OpenServer + SDK +Ensoniq Corp, a SoundScape card and extensive amount of assistance +MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK +Acer, Inc. a pair of AcerMagic S23 cards. + +In addition the following companies have provided me sufficient amount +of technical information at least some of their products (free or $$$): + +Advanced Gravis Computer Technology Ltd. +Media Vision Inc. +Analog Devices Inc. +Logitech Inc. +Aztech Labs Inc. +Crystal Semiconductor Corporation, +Integrated Circuit Systems Inc. +OAK Technology +OPTi +Turtle Beach +miro +Ad Lib Inc. ($$) +Music Quest Inc. ($$) +Creative Labs ($$$) + +If you have some problems +========================= + +Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). +Also look at the home page (http://www.opensound.com/ossfree). It may +contain info about some recent bug fixes. + +It's likely that you have some problems when trying to use the sound driver +first time. Sound cards don't have standard configuration so there are no +good default configuration to use. Please try to use same I/O, DMA and IRQ +values for the sound card than with DOS. + +If you get an error message when trying to use the driver, please look +at /var/adm/messages for more verbose error message. + + +The following errors are likely with /dev/dsp and /dev/audio. + + - "No such device or address". + This error indicates that there are no suitable hardware for the + device file or the sound driver has been compiled without support for + this particular device. For example /dev/audio and /dev/dsp will not + work if "digitized voice support" was not enabled during "make config". + + - "Device or resource busy". Probably the IRQ (or DMA) channel + required by the sound card is in use by some other device/driver. + + - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. + Look at the kernel messages in /var/adm/notice for more info. + + - "Invalid argument". The application is calling ioctl() + with impossible parameters. Check that the application is + for sound driver version 2.X or later. + +Linux installation +================== + +IMPORTANT! Read this if you are installing a separately + distributed version of this driver. + + Check that your kernel version works with this + release of the driver (see Readme). Also verify + that your current kernel version doesn't have more + recent sound driver version than this one. IT'S HIGHLY + RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT + IS DISTRIBUTED WITH KERNEL SOURCES. + +- When installing separately distributed sound driver you should first + read the above notice. Then try to find proper directory where and how + to install the driver sources. You should not try to install a separately + distributed driver version if you are not able to find the proper way + yourself (in this case use the version that is distributed with kernel + sources). Remove old version of linux/drivers/sound directory before + installing new files. + +- To build the device files you need to run the enclosed shell script + (see below). You need to do this only when installing sound driver + first time or when upgrading to much recent version than the earlier + one. + +- Configure and compile Linux as normally (remember to include the + sound support during "make config"). Please refer to kernel documentation + for instructions about configuring and compiling kernel. File Readme.cards + contains card specific instructions for configuring this driver for + use with various sound cards. + +Boot time configuration (using lilo and insmod) +----------------------------------------------- + +This information has been removed. Too many users didn't believe +that it's really not necessary to use this method. Please look at +Readme of sound driver version 3.0.1 if you still want to use this method. + +Problems +-------- + +Common error messages: + +- /dev/???????: No such file or directory. +Run the script at the end of this file. + +- /dev/???????: No such device. +You are not running kernel which contains the sound driver. When using +modularized sound driver this error means that the sound driver is not +loaded. + +- /dev/????: No such device or address. +Sound driver didn't detect suitable card when initializing. Please look at +Readme.cards for info about configuring the driver with your card. Also +check for possible boot (insmod) time error messages in /var/adm/messages. + +- Other messages or problems +Please check http://www.opensound.com/ossfree for more info. + +Configuring version 3.8 (for Linux) with some common sound cards +================================================================ + +This document describes configuring sound cards with the freeware version of +Open Sound Systems (OSS/Free). Information about the commercial version +(OSS/Linux) and its configuration is available from +http://www.opensound.com/linux.html. Information presented here is +not valid for OSS/Linux. + +If you are unsure about how to configure OSS/Free +you can download the free evaluation version of OSS/Linux from the above +address. There is a chance that it can autodetect your sound card. In this case +you can use the information included in soundon.log when configuring OSS/Free. + + +IMPORTANT! This document covers only cards that were "known" when + this driver version was released. Please look at + http://www.opensound.com/ossfree for info about + cards introduced recently. + + When configuring the sound driver, you should carefully + check each sound configuration option (particularly + "Support for /dev/dsp and /dev/audio"). The default values + offered by these programs are not necessarily valid. + + +THE BIGGEST MISTAKES YOU CAN MAKE +================================= + +1. Assuming that the card is Sound Blaster compatible when it's not. +-------------------------------------------------------------------- + +The number one mistake is to assume that your card is compatible with +Sound Blaster. Only the cards made by Creative Technology or which have +one or more chips labeled by Creative are SB compatible. In addition there +are few sound chipsets which are SB compatible in Linux such as ESS1688 or +Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything +in Linux. + +IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF +THIS CHAPTER. + +For most other "supposed to be SB compatible" cards you have to use other +than SB drivers (see below). It is possible to get most sound cards to work +in SB mode but in general it's a complete waste of time. There are several +problems which you will encounter by using SB mode with cards that are not +truly SB compatible: + +- The SB emulation is at most SB Pro (DSP version 3.x) which means that +you get only 8 bit audio (there is always an another ("native") mode which +gives the 16 bit capability). The 8 bit only operation is the reason why +many users claim that sound quality in Linux is much worse than in DOS. +In addition some applications require 16 bit mode and they produce just +noise with a 8 bit only device. +- The card may work only in some cases but refuse to work most of the +time. The SB compatible mode always requires special initialization which is +done by the DOS/Windows drivers. This kind of cards work in Linux after +you have warm booted it after DOS but they don't work after cold boot +(power on or reset). +- You get the famous "DMA timed out" messages. Usually all SB clones have +software selectable IRQ and DMA settings. If the (power on default) values +currently used by the card don't match configuration of the driver you will +get the above error message whenever you try to record or play. There are +few other reasons to the DMA timeout message but using the SB mode seems +to be the most common cause. + +2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card +-------------------------------------------------------------------------- + +Plug & Play is a protocol defined by Intel and Microsoft. It lets operating +systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA +cards. The problem with PnP cards is that the standard Linux doesn't currently +(versions 2.1.x and earlier) don't support PnP. This means that you will have +to use some special tricks (see later) to get a PnP card alive. Many PnP cards +work after they have been initialized but this is not always the case. + +There are sometimes both PnP and non-PnP versions of the same sound card. +The non-PnP version is the original model which usually has been discontinued +more than an year ago. The PnP version has the same name but with "PnP" +appended to it (sometimes not). This causes major confusion since the non-PnP +model works with Linux but the PnP one doesn't. + +You should carefully check if "Plug & Play" or "PnP" is mentioned in the name +of the card or in the documentation or package that came with the card. +Everything described in the rest of this document is not necessarily valid for +PnP models of sound cards even you have managed to wake up the card properly. +Many PnP cards are simply too different from their non-PnP ancestors which are +covered by this document. + + +Cards that are not (fully) supported by this driver +=================================================== + +See http://www.opensound.com/ossfree for information about sound cards +to be supported in future. + + +How to use sound without recompiling kernel and/or sound driver +=============================================================== + +There is a commercial sound driver which comes in precompiled form and doesn't +require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for +more info. + + +Configuring PnP cards +===================== + +New versions of most sound cards use the so-called ISA PnP protocol for +soft configuring their I/O, IRQ, DMA and shared memory resources. +Currently at least cards made by Creative Technology (SB32 and SB32AWE +PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and +Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio +chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other +motherboards) is also based on PnP technology but there is a "native" driver +available for it (see information about CS4232 later in this document). + +PnP sound cards (as well as most other PnP ISA cards) are not supported +by this version of the driver . Proper +support for them should be released during 97 once the kernel level +PnP support is available. + +There is a method to get most of the PnP cards to work. The basic method +is the following: + +1) Boot DOS so the card's DOS drivers have a chance to initialize it. +2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del +works with older machines but causes a hard reset of all cards on recent +(Pentium) machines. +3) If you have the sound driver in Linux configured properly, the card should +work now. "Proper" means that I/O, IRQ and DMA settings are the same as in +DOS. The hard part is to find which settings were used. See the documentation of +your card for more info. + +Windows 95 could work as well as DOS but running loadlin may be difficult. +Probably you should "shut down" your machine to MS-DOS mode before running it. + +Some machines have a BIOS utility for setting PnP resources. This is a good +way to configure some cards. In this case you don't need to boot DOS/Win95 +before starting Linux. + +Another way to initialize PnP cards without DOS/Win95 is a Linux based +PnP isolation tool. When writing this there is a pre alpha test version +of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The +file is called isapnptools-*. Please note that this tool is just a temporary +solution which may be incompatible with future kernel versions having proper +support for PnP cards. There are bugs in setting DMA channels in earlier +versions of isapnptools so at least version 1.6 is required with sound cards. + +Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See +http://www.opensound.com/linux.html for more info. This is probably the way you +should do it if you don't want to spend time recompiling the kernel and +required tools. + + +Read this before trying to configure the driver +=============================================== + +There are currently many cards that work with this driver. Some of the cards +have native support while others work since they emulate some other +card (usually SB, MSS/WSS and/or MPU401). The following cards have native +support in the driver. Detailed instructions for configuring these cards +will be given later in this document. + +Pro Audio Spectrum 16 (PAS16) and compatibles: + Pro Audio Spectrum 16 + Pro Audio Studio 16 + Logitech Sound Man 16 + NOTE! The original Pro Audio Spectrum as well as the PAS+ are not + and will not be supported by the driver. + +Media Vision Jazz16 based cards + Pro Sonic 16 + Logitech SoundMan Wave + (Other Jazz based cards should work but I don't have any reports + about them). + +Sound Blasters + SB 1.0 to 2.0 + SB Pro + SB 16 + SB32/64/AWE + Configure SB32/64/AWE just like SB16. See lowlevel/README.awe + for information about using the wave table synth. + NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated + using isapnptools before they work with OSS/Free. + SB16 compatible cards by other manufacturers than Creative. + You have been fooled since there are _no_ SB16 compatible + cards on the market (as of May 1997). It's likely that your card + is compatible just with SB Pro but there is also a non-SB- + compatible 16 bit mode. Usually it's MSS/WSS but it could also + be a proprietary one like MV Jazz16 or ESS ES688. OPTi + MAD16 chips are very common in so called "SB 16 bit cards" + (try with the MAD16 driver). + + ====================================================================== + "Supposed to be SB compatible" cards. + Forget the SB compatibility and check for other alternatives + first. The only cards that work with the SB driver in + Linux have been made by Creative Technology (there is at least + one chip on the card with "CREATIVE" printed on it). The + only other SB compatible chips are ESS and Jazz16 chips + (maybe ALSxxx chips too but they probably don't work). + Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or + "Crystal" are _NOT_ SB compatible in Linux. + + Practically all sound cards have some kind of SB emulation mode + in addition to their native (16 bit) mode. In most cases this + (8 bit only) SB compatible mode doesn't work with Linux. If + you get it working it may cause problems with games and + applications which require 16 bit audio. Some 16 bit only + applications don't check if the card actually supports 16 bits. + They just dump 16 bit data to a 8 bit card which produces just + noise. + + In most cases the 16 bit native mode is supported by Linux. + Use the SB mode with "clones" only if you don't find anything + better from the rest of this doc. + ====================================================================== + +Gravis Ultrasound (GUS) + GUS + GUS + the 16 bit option + GUS MAX + GUS ACE (No MIDI port and audio recording) + GUS PnP (with RAM) + +MPU-401 and compatibles + The driver works both with the full (intelligent mode) MPU-401 + cards (such as MPU IPC-T and MQX-32M) and with the UART only + dumb MIDI ports. MPU-401 is currently the most common MIDI + interface. Most sound cards are compatible with it. However, + don't enable MPU401 mode blindly. Many cards with native support + in the driver have their own MPU401 driver. Enabling the standard one + will cause a conflict with these cards. So check if your card is + in the list of supported cards before enabling MPU401. + +Windows Sound System (MSS/WSS) + Even when Microsoft has discontinued their own Sound System card + they managed to make it a standard. MSS compatible cards are based on + a codec chip which is easily available from at least two manufacturers + (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). + Currently most sound cards are based on one of the MSS compatible codec + chips. The CS4231 is used in the high quality cards such as GUS MAX, + MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). + + Having a AD1848, CS4248 or CS4231 codec chip on the card is a good + sign. Even if the card is not MSS compatible, it could be easy to write + support for it. Note also that most MSS compatible cards + require special boot time initialization which may not be present + in the driver. Also, some MSS compatible cards have native support. + Enabling the MSS support with these cards is likely to + cause a conflict. So check if your card is listed in this file before + enabling the MSS support. + +Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) + Most sound cards have a FM synthesizer chip. The OPL2 is a 2 + operator chip used in the original AdLib card. Currently it's used + only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator + FM chip which provides better sound quality and/or more available + voices than the OPL2. The OPL4 is a new chip that has an OPL3 and + a wave table synthesizer packed onto the same chip. The driver supports + just the OPL3 mode directly. Most cards with an OPL4 (like + SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401 + emulation. Writing a native OPL4 support is difficult + since Yamaha doesn't give information about their sample ROM chip. + + Enable the generic OPL2/OPL3 FM synthesizer support if your + card has a FM chip made by Yamaha. Don't enable it if your card + has a software (TRS) based FM emulator. + + ---------------------------------------------------------------- + NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition + to the FM synth this chip has also digital audio (WSS) and + MIDI (MPU401) capabilities. Support for OPL3-SA is described below. + ---------------------------------------------------------------- + +Yamaha OPL3-SA1 + + Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some + (Intel) motherboards and on cheap sound cards. It should not be + confused with the original OPL3 chip (YMF278) which is entirely + different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro + (not used in OSS/Free) in addition to the OPL3 FM synth. + + There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They + are PnP chips and will not work with the OPL3-SA1 driver. You should + use the standard MSS, MPU401 and OPL3 options with these chips and to + activate the card using isapnptools. + +4Front Technologies SoftOSS + + SoftOSS is a software based wave table emulation which works with + any 16 bit stereo sound card. Due to its nature a fast CPU is + required (P133 is minimum). Although SoftOSS does _not_ use MMX + instructions it has proven out that recent processors (which appear + to have MMX) perform significantly better with SoftOSS than earlier + ones. For example a P166MMX beats a PPro200. SoftOSS should not be used + on 486 or 386 machines. + + The amount of CPU load caused by SoftOSS can be controlled by + selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES + parameters properly (they will be prompted by make config). It's + recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a + P166MMX or faster (PPro200 is not faster) you can set + CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it + recommended to use sampling rates around 22050 or even 16000 kHz. + Selecting too high values for these parameters may hang your + system when playing MIDI files with hight degree of polyphony + (number of concurrently playing notes). It's also possible to + decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use + higher sampling rates. However using fewer voices decreases + playback quality more than decreasing the sampling rate. + + SoftOSS keeps the samples loaded on the system's RAM so much RAM is + required. SoftOSS should never be used on machines with less than 16 MB + of RAM since this is potentially dangerous (you may accidentally run out + of memory which probably crashes the machine). + + SoftOSS implements the wave table API originally designed for GUS. For + this reason all applications designed for GUS should work (at least + after minor modifications). For example gmod/xgmod and playmidi -g are + known to work. + + To work SoftOSS will require GUS compatible + patch files to be installed on the system (in /dos/ultrasnd/midi). You + can use the public domain MIDIA patchset available from several ftp + sites. + + ********************************************************************* + IMPORTANT NOTICE! The original patch set distributed with the Gravis + Ultrasound card is not in public domain (even though it's available from + some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) + if you like to use these patches with SoftOSS included in OSS/Free. + ********************************************************************* + +PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) + Analog Devices and Echo Speech have together defined a sound card + architecture based on the above chips. The DSP chip is used + for emulation of SB Pro, FM and General MIDI/MT32. + + There are several cards based on this architecture. The most known + ones are Orchid SW32 and Cardinal DSP16. + + The driver supports downloading DSP algorithms to these cards. + + NOTE! You will have to use the "old" config script when configuring + PSS cards. + +MediaTrix AudioTrix Pro + The ATP card is built around a CS4231 codec and an OPL4 synthesizer + chips. The OPL4 mode is supported by a microcontroller running a + General MIDI emulator. There is also a SB 1.5 compatible playback mode. + +Ensoniq SoundScape and compatibles + Ensoniq has designed a sound card architecture based on the + OTTO synthesizer chip used in their professional MIDI synthesizers. + Several companies (including Ensoniq, Reveal and Spea) are selling + cards based on this architecture. + + NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and + VIVO90 cards are not compatible with Soundscapes so the Soundscape + driver will not work with them. You may want to use OSS/Linux with these + cards. + +OPTi MAD16 and Mozart based cards + The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), + OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface + chips are used in many different sound cards, including some + cards by Reveal miro and Turtle Beach (Tropez). The purpose of these + chips is to connect other audio components to the PC bus. The + interface chip performs address decoding for the other chips. + NOTE! Tropez Plus is not MAD16 but CS4232 based. + NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible + in the PnP mode. You will have to use them in MSS mode after having + initialized them using isapnptools or DOS. 82C931 probably requires + initialization using DOS/Windows (running isapnptools is not enough). + It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP + mode (provided that the card has a jumper for this). In non-PnP mode + 82C931 is compatible with 82C930 and should work with the MAD16 driver + (without need to use isapnptools or DOS to initialize it). All OPTi + chips are supported by OSS/Linux (both in PnP and non-PnP modes). + +Audio Excel DSP16 + Support for this card was written by Riccardo Faccetti + (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in + the lowlevel/ directory. To use it you should enable the + "Additional low level drivers" option. + +Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and + many PC motherboards (Compaq, HP, Intel, ...) + CS4232 is a PnP multimedia chip which contains a CS3231A codec, + SB and MPU401 emulations. There is support for OPL3 too. + Unfortunately the MPU401 mode doesn't work (I don't know how to + initialize it). CS4236 is an enhanced (compatible) version of CS4232. + NOTE! Don't ever try to use isapnptools with CS4232 since this will just + freeze your machine (due to chip bugs). If you have problems in getting + CS4232 working you could try initializing it with DOS (CS4232C.EXE) and + then booting Linux using loadlin. CS4232C.EXE loads a secret firmware + patch which is not documented by Crystal. + +Turtle Beach Maui and Tropez "classic" + This driver version supports sample, patch and program loading commands + described in the Maui/Tropez User's manual. + There is now full initialization support too. The audio side of + the Tropez is based on the MAD16 chip (see above). + NOTE! Tropez Plus is different card than Tropez "classic" and will not + work fully in Linux. You can get audio features working by configuring + the card as a CS4232 based card (above). + + +Jumpers and software configuration +================================== + +Some of the earliest sound cards were jumper configurable. You have to +configure the driver use I/O, IRQ and DMA settings +that match the jumpers. Just few 8 bit cards are fully jumper +configurable (SB 1.x/2.x, SB Pro and clones). +Some cards made by Aztech have an EEPROM which contains the +config info. These cards behave much like hardware jumpered cards. + +Most cards have jumper for the base I/O address but other parameters +are software configurable. Sometimes there are few other jumpers too. + +Latest cards are fully software configurable or they are PnP ISA +compatible. There are no jumpers on the board. + +The driver handles software configurable cards automatically. Just configure +the driver to use I/O, IRQ and DMA settings which are known to work. +You could usually use the same values than with DOS and/or Windows. +Using different settings is possible but not recommended since it may cause +some trouble (for example when warm booting from an OS to another or +when installing new hardware to the machine). + +Sound driver sets the soft configurable parameters of the card automatically +during boot. Usually you don't need to run any extra initialization +programs when booting Linux but there are some exceptions. See the +card-specific instructions below for more info. + +The drawback of software configuration is that the driver needs to know +how the card must be initialized. It cannot initialize unknown cards +even if they are otherwise compatible with some other cards (like SB, +MPU401 or Windows Sound System). + + +What if your card was not listed above? +======================================= + +The first thing to do is to look at the major IC chips on the card. +Many of the latest sound cards are based on some standard chips. If you +are lucky, all of them could be supported by the driver. The most common ones +are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures +listed above. Also look at the end of this file for list of unsupported +cards and the ones which could be supported later. + +The last resort is to send _exact_ name and model information of the card +to me together with a list of the major IC chips (manufactured, model) to +me. I could then try to check if your card looks like something familiar. + +There are many more cards in the world than listed above. The first thing to +do with these cards is to check if they emulate some other card or interface +such as SB, MSS and/or MPU401. In this case there is a chance to get the +card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del +and boot Linux without hard resetting the machine). In this method the +DOS based driver initializes the hardware to use known I/O, IRQ and DMA +settings. If sound driver is configured to use the same settings, everything +should work OK. + + +Configuring sound driver (with Linux) +===================================== + +The sound driver is currently distributed as part of the Linux kernel. The +files are in /usr/src/linux/drivers/sound/. + +**************************************************************************** +* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * +* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * +* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * +* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * +* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * +* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * +* INCOMPATIBILITY PROBLEMS. * +* * +* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * +* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * +* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * +* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * +* COMPILED. * +**************************************************************************** + +To configure the driver, run "make config" in the kernel source directory +(/usr/src/linux). Answer "y" or "m" to the question about Sound card support +(after the questions about mouse, CD-ROM, ftape, etc. support). Questions +about options for sound will then be asked. + +After configuring the kernel and sound driver, run "make dep" and compile +the kernel following instructions in the kernel README. + +The sound driver configuration dialog +------------------------------------- + +Sound configuration starts by making some yes/no questions. Be careful +when answering to these questions since answering y to a question may +prevent some later ones from being asked. For example don't answer y to +the first question (PAS16) if you don't really have a PAS16. Don't enable +more cards than you really need since they just consume memory. Also +some drivers (like MPU401) may conflict with your SCSI controller and +prevent kernel from booting. If you card was in the list of supported +cards (above), please look at the card specific config instructions +(later in this file) before starting to configure. Some cards must be +configured in way which is not obvious. + +So here is the beginning of the config dialog. Answer 'y' or 'n' to these +questions. The default answer is shown so that (y/n) means 'y' by default and +(n/y) means 'n'. To use the default value, just hit ENTER. But be careful +since using the default _doesn't_ guarantee anything. + +Note also that all questions may not be asked. The configuration program +may disable some questions depending on the earlier choices. It may also +select some options automatically as well. + + "ProAudioSpectrum 16 support", + - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, + Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that + you read the above list correctly). Don't answer 'y' if you + have some other card made by Media Vision or Logitech since they + are not PAS16 compatible. + NOTE! Since 3.5-beta10 you need to enable SB support (next question) + if you want to use the SB emulation of PAS16. It's also possible to + the emulation if you want to use a true SB card together with PAS16 + (there is another question about this that is asked later). + "Sound Blaster support", + - Answer 'y' if you have an original SB card made by Creative Labs + or a full 100% hardware compatible clone (like Thunderboard or + SM Games). If your card was in the list of supported cards (above), + please look at the card specific instructions later in this file + before answering this question. For an unknown card you may answer + 'y' if the card claims to be SB compatible. + Enable this option also with PAS16 (changed since v3.5-beta9). + + Don't enable SB if you have a MAD16 or Mozart compatible card. + + "Generic OPL2/OPL3 FM synthesizer support", + - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering 'y' is usually a safe and recommended choice. However some + cards may have software (TSR) FM emulation. Enabling FM support + with these cards may cause trouble. However I don't currently know + such cards. + "Gravis Ultrasound support", + - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't + have GUS since the GUS driver consumes much memory. + Currently I don't have experiences with the GUS ACE so I don't + know what to answer with it. + "MPU-401 support (NOT for SB16)", + - Be careful with this question. The MPU401 interface is supported + by almost any sound card today. However some natively supported cards + have their own driver for MPU401. Enabling the MPU401 option with + these cards will cause a conflict. Also enabling MPU401 on a system + that doesn't really have a MPU401 could cause some trouble. If your + card was in the list of supported cards (above), please look at + the card specific instructions later in this file. + + In MOST cases this MPU401 driver should only be used with "true" + MIDI-only MPU401 professional cards. In most other cases there + is another way to get the MPU401 compatible interface of a + sound card to work. + Support for the MPU401 compatible MIDI port of SB16, ESS1688 + and MV Jazz16 cards is included in the SB driver. Use it instead + of this separate MPU401 driver with these cards. As well + Soundscape, PSS and Maui drivers include their own MPU401 + options. + + It's safe to answer 'y' if you have a true MPU401 MIDI interface + card. + "6850 UART Midi support", + - It's safe to answer 'n' to this question in all cases. The 6850 + UART interface is so rarely used. + "PSS (ECHO-ADI2111) support", + - Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some + other card based on the PSS chipset (AD1848 codec + ADSP-2115 + DSP chip + Echo ESC614 ASIC CHIP). + "16 bit sampling option of GUS (_NOT_ GUS MAX)", + - Answer 'y' if you have installed the 16 bit sampling daughtercard + to your GUS. Answer 'n' if you have GUS MAX. Enabling this option + disables GUS MAX support. + "GUS MAX support", + - Answer 'y' only if you have a GUS MAX. + "Microsoft Sound System support", + - Again think carefully before answering 'y' to this question. It's + safe to answer 'y' in case you have the original Windows Sound + System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). + Also you may answer 'y' in case your card was not listed earlier + in this file. For cards having native support in the driver, consult + the card specific instructions later in this file. Some drivers + have their own MSS support and enabling this option will cause a + conflict. + Note! The MSS driver permits configuring two DMA channels. This is a + "nonstandard" feature and works only with very few cards (if any). + In most cases the second DMA channel should be disabled or set to + the same channel than the first one. Trying to configure two separate + channels with cards that don't support this feature will prevent + audio (at least recording) from working. + "Ensoniq Soundscape support", + - Answer 'y' if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, + Spea and Reveal (note that Reveal makes other cards also). The oldest + cards made by Spea don't work properly with Linux. + Soundscape PnP as well as Ensoniq VIVO work only with the commercial + OSS/Linux version. + "MediaTrix AudioTrix Pro support", + - Answer 'y' if you have the AudioTrix Pro. + "Support for MAD16 and/or Mozart based cards", + - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 + (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. + These chips are + currently quite common so it's possible that many no-name cards + have one of them. In addition the MAD16 chip is used in some + cards made by known manufacturers such as Turtle Beach (Tropez), + Reveal (some models) and Diamond (some recent models). + Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP + mode (jumper selectable on many cards). + "Support for TB Maui" + - This enables TB Maui specific initialization. Works with TB Maui + and TB Tropez (may not work with Tropez Plus). + + +Then the configuration program asks some y/n questions about the higher +level services. It's recommended to answer 'y' to each of these questions. +Answer 'n' only if you know you will not need the option. + + "MIDI interface support", + - Answering 'n' disables /dev/midi## devices and access to any + MIDI ports using /dev/sequencer and /dev/music. This option + also affects any MPU401 and/or General MIDI compatible devices. + "FM synthesizer (YM3812/OPL-3) support", + - Answer 'y' here. + "/dev/sequencer support", + - Answering 'n' disables /dev/sequencer and /dev/music. + +Entering the I/O, IRQ and DMA config parameters +----------------------------------------------- + +After the above questions the configuration program prompts for the +card specific configuration information. Usually just a set of +I/O address, IRQ and DMA numbers are asked. With some cards the program +asks for some files to be used during initialization of the card. For example +many cards have a DSP chip or microprocessor which must be initialized by +downloading a program (microcode) file to the card. + +Instructions for answering these questions are given in the next section. + + +Card specific information +========================= + +This section gives additional instructions about configuring some cards. +Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using +the same settings with DOS/Windows and Linux is recommended. Using +different values could cause some problems when switching between +different operating systems. + +Sound Blasters (the original ones by Creative) +--------------------------------------------- + +NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 + are almost certainly PnP ones). With PnP cards you should use isapnptools + to activate them (see above). + +It's possible to configure these cards to use different I/O, IRQ and +DMA settings. Since the possible/default settings have changed between various +models, you have to consult manual of your card for the proper ones. It's +a good idea to use the same values than with DOS/Windows. With SB and SB Pro +it's the only choice. SB16 has software selectable IRQ and DMA channels but +using different values with DOS and Linux is likely to cause troubles. The +DOS driver is not able to reset the card properly after warm boot from Linux +if Linux has used different IRQ or DMA values. + +The original (steam) Sound Blaster (versions 1.x and 2.x) use always +DMA1. There is no way to change it. + +The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for +8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory +it's possible to use just one (8 bit) DMA channel by answering the 8 bit +one when the configuration program asks for the 16 bit one. This may work +in some systems but is likely to cause terrible noise on some other systems. + +It's possible to use two SB16/32/64 at the same time. To do this you should +first configure OSS/Free for one card. Then edit local.h manually and define +SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get +the OPL3, MIDI and EMU8000 devices of the second card to work. If you are +going to use two PnP Sound Blasters, ensure that they are of different model +and have different PnP IDs. There is no way to get two cards with the same +card ID and serial number to work. The easiest way to check this is trying +if isapnptools can see both cards or just one. + +NOTE! Don't enable the SM Games option (asked by the configuration program) + if you are not 101% sure that your card is a Logitech Soundman Games + (not a SM Wave or SM16). + +SB Clones +--------- + +First of all: There are no SB16 clones. There are SB Pro clones with a +16 bit mode which is not SB16 compatible. The most likely alternative is that +the 16 bit mode means MSS/WSS. + +There are just a few fully 100% hardware SB or SB Pro compatible cards. +I know just Thunderboard and SM Games. Other cards require some kind of +hardware initialization before they become SB compatible. Check if your card +was listed in the beginning of this file. In this case you should follow +instructions for your card later in this file. + +For other not fully SB clones you may try initialization using DOS in +the following way: + + - Boot DOS so that the card specific driver gets run. + - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't + switch off power or press the reset button. + - If you use the same I/O, IRQ and DMA settings in Linux, the + card should work. + +If your card is both SB and MSS compatible, I recommend using the MSS mode. +Most cards of this kind are not able to work in the SB and the MSS mode +simultaneously. Using the MSS mode provides 16 bit recording and playback. + +ProAudioSpectrum 16 and compatibles +----------------------------------- + +PAS16 has a SB emulation chip which can be used together with the native +(16 bit) mode of the card. To enable this emulation you should configure +the driver to have SB support too (this has been changed since version +3.5-beta9 of this driver). + +With current driver versions it's also possible to use PAS16 together with +another SB compatible card. In this case you should configure SB support +for the other card and to disable the SB emulation of PAS16 (there is a +separate questions about this). + +With PAS16 you can use two audio device files at the same time. /dev/dsp (and +/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and +/dev/audio1) is connected to the SB emulation (8 bit mono only). + +Gravis Ultrasound +----------------- + +There are many different revisions of the Ultrasound card (GUS). The +earliest ones (pre 3.7) don't have a hardware mixer. With these cards +the driver uses a software emulation for synth and pcm playbacks. It's +also possible to switch some of the inputs (line in, mic) off by setting +mixer volume of the channel level below 10%. For recording you have +to select the channel as a recording source and to use volume above 10%. + +GUS 3.7 has a hardware mixer. + +GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which +also contains a mixer. + +Configuring GUS is simple. Just enable the GUS support and GUS MAX or +the 16 bit daughtercard if you have them. Note that enabling the daughter +card disables GUS MAX driver. + +NOTE for owners of the 16 bit daughtercard: By default the daughtercard +uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp" +selects the daughter card as the default device. + +With just the standard GUS enabled the configuration program prompts +for the I/O, IRQ and DMA numbers for the card. Use the same values than +with DOS. + +With the daughter card option enabled you will be prompted for the I/O, +IRQ and DMA numbers for the daughter card. You have to use different I/O +and DMA values than for the standard GUS. The daughter card permits +simultaneous recording and playback. Use /dev/dsp (the daughtercard) for +recording and /dev/dsp1 (GUS GF1) for playback. + +GUS MAX uses the same I/O address and IRQ settings than the original GUS +(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. +Using two DMA channels permits simultaneous playback using two devices +(dev/dsp0 and /dev/dsp1). The second DMA channel is required for +full duplex audio. +To enable the second DMA channels, give a valid DMA channel when the config +program asks for the GUS MAX DMA (entering -1 disables the second DMA). +Using 16 bit DMA channels (5,6 or 7) is recommended. + +If you have problems in recording with GUS MAX, you could try to use +just one 8 bit DMA channel. Recording will not work with one DMA +channel if it's a 16 bit one. + +Microphone input of GUS MAX is connected to mixer in little bit nonstandard +way. There is actually two microphone volume controls. Normal "mic" controls +only recording level. Mixer control "speaker" is used to control volume of +microphone signal connected directly to line/speaker out. So just decrease +volume of "speaker" if you have problems with microphone feedback. + +GUS ACE works too but any attempt to record or to use the MIDI port +will fail. + +GUS PnP (with RAM) is partially supported but it needs to be initialized using +DOS or isapnptools before starting the driver. + +MPU401 and Windows Sound System +------------------------------- + +Again. Don't enable these options in case your card is listed +somewhere else in this file. + +Configuring these cards is obvious (or it should be). With MSS +you should probably enable the OPL3 synth also since +most MSS compatible cards have it. However check that this is true +before enabling OPL3. + +Sound driver supports more than one MPU401 compatible cards at the same time +but the config program asks config info for just the first of them. +Adding the second or third MPU interfaces must be done manually by +editing sound/local.h (after running the config program). Add defines for +MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. + +CAUTION! + +The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which +is also the default of the MPU401 driver. Don't configure the sound driver to +use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot +if you make this mistake. + +PSS +--- + +Even the PSS cards are compatible with SB, MSS and MPU401, you must not +enable these options when configuring the driver. The configuration +program handles these options itself. (You may use the SB, MPU and MSS options +together with PSS if you have another card on the system). + +The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled +since it doesn't work concurrently with MSS. The driver loads also a +DSP algorithm which is used to for the general MIDI emulation. The +algorithm file (.ld) is read by the config program and written to a +file included when the pss.c is compiled. For this reason the config +program asks if you want to download the file. Use the genmidi.ld file +distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). +With some cards the file is called 'synth.ld'. You must have access to +the file when configuring the driver. The easiest way is to mount the DOS +partition containing the file with Linux. + +It's possible to load your own DSP algorithms and run them with the card. +Look at the directory pss_test of snd-util-3.0.tar.gz for more info. + +AudioTrix Pro +------------- + +You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition +to the native AudioTrix driver. Don't enable MSS or MPU drivers. + +Configuring ATP is little bit tricky since it uses so many I/O, IRQ and +DMA numbers. Using the same values than with DOS/Win is a good idea. Don't +attempt to use the same IRQ or DMA channels twice. + +The SB mode of ATP is implemented so the ATP driver just enables SB +in the proper address. The SB driver handles the rest. You have to configure +both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O +settings. + +Also the ATP has a microcontroller for the General MIDI emulation (OPL4). +For this reason the driver asks for the name of a file containing the +microcode (TRXPRO.HEX). This file is usually located in the directory +where the DOS drivers were installed. You must have access to this file +when configuring the driver. + +If you have the effects daughtercard, it must be initialized by running +the setfx program of snd-util-3.0.tar.gz package. This step is not required +when using the (future) binary distribution version of the driver. + +Ensoniq SoundScape +------------------ + +NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible + cards made by Reveal don't work with Linux. They use older revision + of the Soundscape chipset which is not fully compatible with + newer cards made by Ensoniq. + +The SoundScape driver handles initialization of MSS and MPU supports +itself so you don't need to enable other drivers than SoundScape +(enable also the /dev/dsp, /dev/sequencer and MIDI supports). + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!! !!!! +!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio !!!! +!!!!! device files (/dev/dsp0 and /dev/dsp1). The first one WAS !!!! +!!!!! used only for card initialization and the second for audio !!!! +!!!!! purposes. It WAS required to change /dev/dsp (a symlink) to !!!! +!!!!! point to /dev/dsp1. !!!! +!!!!! !!!! +!!!!! This is not required with OSS versions 3.5-beta6 and later !!!! +!!!!! since there is now just one audio device file. Please !!!! +!!!!! change /dev/dsp to point back to /dev/dsp0 if you are !!!! +!!!!! upgrading from an earlier driver version using !!!! +!!!!! (cd /dev;rm dsp;ln -s dsp0 dsp). !!!! +!!!!! !!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +The configuration program asks one DMA channel and two interrupts. One IRQ +and one DMA is used by the MSS codec. The second IRQ is required for the +MPU401 mode (you have to use different IRQs for both purposes). +There were earlier two DMA channels for SoundScape but the current driver +version requires just one. + +The SoundScape card has a Motorola microcontroller which must initialized +_after_ boot (the driver doesn't initialize it during boot). +The initialization is done by running the 'ssinit' program which is +distributed in the snd-util-3.0.tar.gz package. You have to edit two +defines in the ssinit.c and then compile the program. You may run ssinit +manually (after each boot) or add it to /etc/rc.d/rc.local. + +The ssinit program needs the microcode file that comes with the DOS/Windows +driver of the card. You will need to use version 1.30.00 or later +of the microcode file (sndscape.co0 or sndscape.co1 depending on +your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR +MACHINE. The only way to get the new microcode file is to download +and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. + +Then you have to select the proper microcode file to use: soundscape.co0 +is the right one for most cards and sndscape.co1 is for few (older) cards +made by Reveal and/or Spea. The driver has capability to detect the card +version during boot. Look at the boot log messages in /var/adm/messages +and locate the sound driver initialization message for the SoundScape +card. If the driver displays string , you have +an old card and you will need to use sndscape.co1. For other cards use +soundscape.co0. New Soundscape revisions such as Elite and PnP use +code files with higher numbers (.co2, .co3, etc.). + +NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. + Currently it's possible to use it in Linux only with OSS/Linux + drivers. + +Check /var/adm/messages after running ssinit. The driver prints +the board version after downloading the microcode file. That version +number must match the number in the name of the microcode file (extension). + +Running ssinit with a wrong version of the sndscape.co? file is not +dangerous as long as you don't try to use a file called sndscape.cod. +If you have initialized the card using a wrong microcode file (sounds +are terrible), just modify ssinit.c to use another microcode file and try +again. It's possible to use an earlier version of sndscape.co[01] but it +may sound weird. + +MAD16 (Pro) and Mozart +---------------------- + +You need to enable just the MAD16 /Mozart support when configuring +the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the +/dev/audio, /dev/sequencer and MIDI supports. + +Mozart and OPTi 82C928 (the original MAD16) chips don't support +MPU401 mode so enter just 0 when the configuration program asks the +MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401 +mode. + +TB Tropez is based on the 82C929 chip. It has two MIDI ports. +The one connected to the MAD16 chip is the second one (there is a second +MIDI connector/pins somewhere??). If you have not connected the second MIDI +port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of +Tropez is jumper configurable and not connected to the MAD16 chip (the +Maui driver can be used with it). + +Some MAD16 based cards may cause feedback, whistle or terrible noise if the +line3 mixer channel is turned too high. This happens at least with Shuttle +Sound System. Current driver versions set volume of line3 low enough so +this should not be a problem. + +If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer +chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4 +to the file linux/drivers/sound/local.h (after running make config). + +MAD16 cards having a CS4231 codec support full duplex mode. This mode +can be enabled by configuring the card to use two DMA channels. Possible +DMA channel pairs are: 0&1, 1&0 and 3&0. + +NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in +non-PnP mode (usually jumper selectable). The PnP mode is supported only +by OSS/Linux. + +MV Jazz (ProSonic) +------------------ + +The Jazz16 driver is just a hack made to the SB Pro driver. However it works +fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports +when configuring the driver. The configuration program asks later if you +want support for MV Jazz16 based cards (after asking SB base address). Answer +'y' here and the driver asks the second (16 bit) DMA channel. + +The Jazz16 driver uses the MPU401 driver in a way which will cause +problems if you have another MPU401 compatible card. In this case you must +give address of the Jazz16 based MPU401 interface when the config +program prompts for the MPU401 information. Then look at the MPU401 +specific section for instructions about configuring more than one MPU401 cards. + +Logitech Soundman Wave +---------------------- + +Read the above MV Jazz specific instructions first. + +The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is +a MV Jazz based card which has an additional OPL4 based wave table +synthesizer. The OPL4 chip is handled by an on board microcontroller +which must be initialized during boot. The config program asks if +you have a SM Wave immediately after asking the second DMA channel of jazz16. +If you answer 'y', the config program will ask name of the file containing +code to be loaded to the microcontroller. The file is usually called +MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file +may also be called as TSUNAMI.BIN or something else (older cards?). + +The OPL4 synth will be inaccessible without loading the microcontroller code. + +Also remember to enable SB MPU401 support if you want to use the OPL4 mode. +(Don't enable the 'normal' MPU401 device as with some earlier driver +versions (pre 3.5-alpha8)). + +NOTE! Don't answer 'y' when the driver asks about SM Games support + (the next question after the MIDI0001.BIN name). However + answering 'y' doesn't cause damage your computer so don't panic. + +Sound Galaxies +-------------- + +There are many different Sound Galaxy cards made by Aztech. The 8 bit +ones are fully SB or SB Pro compatible and there should be no problems +with them. + +The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have +an EEPROM chip for storing the configuration data. There is a microcontroller +which initializes the card to match the EEPROM settings when the machine +is powered on. These cards actually behave just like they have jumpers +for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 +supports with these cards. + +There are some new Sound Galaxies in the market. I have no experience with +them so read the card's manual carefully. + +ESS ES1688 and ES688 'AudioDrive' based cards +--------------------------------------------- + +Support for these two ESS chips is embedded in the SB driver. +Configure these cards just like SB. Enable the 'SB MPU401 MIDI port' +if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode +so you don't need to enable it (the driver uses normal SB MIDI automatically +with ES688). + +NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support +of OSS doesn't work with it. + +There are some ES1688/688 based sound cards and (particularly) motherboards +which use software configurable I/O port relocation feature of the chip. +This ESS proprietary feature is supported only by OSS/Linux. + +There are ES1688 based cards which use different interrupt pin assignment than +recommended by ESS (5, 7, 9/2 and 10). In this case all IRQs don't work. +At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't +work. + +ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 +probably works with OSS/Free after initialization using isapnptools. + +Reveal cards +------------ + +There are several different cards made/marketed by Reveal. Some of them +are compatible with SoundScape and some use the MAD16 chip. You may have +to look at the card and try to identify its origin. + +Diamond +------- + +The oldest (Sierra Aria based) sound cards made by Diamond are not supported +(they may work if the card is initialized using DOS). The recent (LX?) +models are based on the MAD16 chip which is supported by the driver. + +Audio Excel DSP16 +----------------- + +Support for this card is currently not functional. A new driver for it +should be available later this year. + +PCMCIA cards +------------ + +Sorry, can't help. Some cards may work and some don't. + +TI TM4000M notebooks +-------------------- + +These computers have a built in sound support based on the Jazz chipset. +Look at the instructions for MV Jazz (above). It's also important to note +that there is something wrong with the mouse port and sound at least on +some TM models. Don't enable the "C&T 82C710 mouse port support" when +configuring Linux. Having it enabled is likely to cause mysterious problems +and kernel failures when sound is used. + +miroSOUND +--------- + +The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used +successfully. These cards are based on the MAD16, OPL4, and CS4231A chips +and everything said in the section about MAD16 cards applies here, +too. The only major difference between the PCMxx and other MAD16 cards +is that instead of the mixer in the CS4231 codec a separate mixer +controlled by an on-board 80C32 microcontroller is used. Control of +the mixer takes place via the ACI (miro's audio control interface) +protocol that is implemented in a separate lowlevel driver. Make sure +you compile this ACI driver together with the normal MAD16 support +when you use a miroSOUND PCMxx card. The ACI mixer is controlled by +/dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load +time). Only in special cases you want to change something regularly on +the CS4231 mixer. + +The miroSOUND PCM12 and PCM20 radio is capable of full duplex +operation (simultaneous PCM replay and recording), which allows you to +implement nice real-time signal processing audio effect software and +network telephones. The ACI mixer has to be switched into the "solo" +mode for duplex operation in order to avoid feedback caused by the +mixer (input hears output signal). You can de-/activate this mode +through toggleing the record button for the wave controller with an +OSS-mixer. + +The PCM20 contains a radio tuner, which is also controlled by +ACI. This radio tuner is supported by the ACI driver together with the +miropcm20.o module. Also the 7-band equalizer is integrated +(limited by the OSS-design). Developement has started and maybe +finished for the RDS decoder on this card, too. You will be able to +read RadioText, the Programme Service name, Programme TYpe and +others. Even the v4l radio module benefits from it with a refined +strength value. See aci.[ch] and miropcm20*.[ch] for more details. + +The following configuration parameters have worked fine for the PCM12 +in Markus Kuhn's system, many other configurations might work, too: +CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, +CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, +DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. + +Bas van der Linden is using his PCM1-pro with a configuration that +differs in: CONFIG_MAD16_IRQ=7, CONFIG_MAD16_DMA=1, CONFIG_MAD16_MPU_IRQ=9 + +Compaq Deskpro XL +----------------- + +The builtin sound hardware of Compaq Deskpro XL is now supported. +You need to configure the driver with MSS and OPL3 supports enabled. +In addition you need to manually edit linux/drivers/sound/local.h and +to add a line containing "#define DESKPROXL" if you used +make menuconfig/xconfig. + +Others? +------- + +Since there are so many different sound cards, it's likely that I have +forgotten to mention many of them. Please inform me if you know yet another +card which works with Linux, please inform me (or is anybody else +willing to maintain a database of supported cards (just like in XF86)?). + +Cards not supported yet +======================= + +Please check the version of sound driver you are using before +complaining that your card is not supported. It's possible you are +using a driver version which was released months before your card was +introduced. + +First of all, there is an easy way to make most sound cards work with Linux. +Just use the DOS based driver to initialize the card to a known state, then use +loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and +DMA numbers as DOS, the card could work. +(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with +new motherboards). This method works also with all/most PnP sound cards. + +Don't get fooled with SB compatibility. Most cards are compatible with +SB but that may require a TSR which is not possible with Linux. If +the card is compatible with MSS, it's a better choice. Some cards +don't work in the SB and MSS modes at the same time. + +Then there are cards which are no longer manufactured and/or which +are relatively rarely used (such as the 8 bit ProAudioSpectrum +models). It's extremely unlikely that such cards ever get supported. +Adding support for a new card requires much work and increases time +required in maintaining the driver (some changes need to be done +to all low level drivers and be tested too, maybe with multiple +operating systems). For this reason I have made a decision to not support +obsolete cards. It's possible that someone else makes a separately +distributed driver (diffs) for the card. + +Writing a driver for a new card is not possible if there are no +programming information available about the card. If you don't +find your new card from this file, look from the home page +(http://www.opensound.com/ossfree). Then please contact +manufacturer of the card and ask if they have (or are willing to) +released technical details of the card. Do this before contacting me. I +can only answer 'no' if there are no programming information available. + +I have made decision to not accept code based on reverse engineering +to the driver. There are three main reasons: First I don't want to break +relationships to sound card manufacturers. The second reason is that +maintaining and supporting a driver without any specs will be a pain. +The third reason is that companies have freedom to refuse selling their +products to other than Windows users. + +Some companies don't give low level technical information about their +products to public or at least their require signing a NDA. It's not +possible to implement a freeware driver for them. However it's possible +that support for such cards become available in the commercial version +of this driver (see http://www.4Front-tech.com/oss.html for more info). + +There are some common audio chipsets that are not supported yet. For example +Sierra Aria and IBM Mwave. It's possible that these architectures +get some support in future but I can't make any promises. Just look +at the home page (http://www.opensound.com/ossfree/new_cards.html) +for latest info. + +Information about unsupported sound cards and chipsets is welcome as well +as free copies of sound cards, SDKs and operating systems. + +If you have any corrections and/or comments, please contact me. + +Hannu Savolainen +hannu@opensound.com + +Personal home page: http://www.compusonic.fi/~hannu +home page of OSS/Free: http://www.opensound.com/ossfree + +home page of commercial OSS +(Open Sound System) drivers: http://www.opensound.com/oss.html diff -urN linux-2.5.6-pre3/Documentation/sound/oss/README.awe linux-2.5.6/Documentation/sound/oss/README.awe --- linux-2.5.6-pre3/Documentation/sound/oss/README.awe Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/README.awe Thu Mar 7 18:24:41 2002 @@ -0,0 +1,218 @@ +================================================================ + AWE32 Sound Driver for Linux / FreeBSD + version 0.4.3; Nov. 1, 1998 + + Takashi Iwai +================================================================ + +* GENERAL NOTES + +This is a sound driver extension for SoundBlaster AWE32 and other +compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable +the wave synth operations. The driver is provided for Linux 1.2.x +and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC +Alpha systems. + +This driver was written by Takashi Iwai , +and provided "as is". The original source (awedrv-0.4.3.tar.gz) and +binary packages are available on the following URL: + http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ +Note that since the author is apart from this web site, the update is +not frequent now. + + +* NOTE TO LINUX USERS + +To enable this driver on linux-2.[01].x kernels, you need turn on +"AWE32 synth" options in sound menu when configure your linux kernel +and modules. The precise installation procedure is described in the +AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. + +If you're using PnP cards, the card must be initialized before loading +the sound driver. There're several options to do this: + - Initialize the card via ISA PnP tools, and load the sound module. + - Initialize the card on DOS, and load linux by loadlin.exe + - Use PnP kernel driver (for Linux-2.x.x) +The detailed instruction for the solution using isapnp tools is found +in many documents like above. A brief instruction is also included in +the installation document of this package. +For PnP driver project, please refer to the following URL: + http://www-jcr.lmh.ox.ac.uk/~pnp/ + + +* USING THE DRIVER + +The awedrv has several different playing modes to realize easy channel +allocation for MIDI songs. To hear the exact sound quality, you need +to obtain the extended sequencer program, drvmidi or playmidi-2.5. + +For playing MIDI files, you *MUST* load the soundfont file on the +driver previously by sfxload utility. Otherwise you'll here no sounds +at all! All the utilities and driver source packages are found in the +above URL. The sfxload program is included in the package +awesfx-0.4.3.tgz. Binary packages are available there, too. See the +instruction in each package for installation. + +Loading a soundfont file is very simple. Just execute the command + + % sfxload synthgm.sbk + +Then, sfxload transfers the file "synthgm.sbk" to the driver. +Both SF1 and SF2 formats are accepted. + +Now you can hear midi musics by a midi player. + + % drvmidi foo.mid + +If you run MIDI player after MOD player, you need to load soundfont +files again, since MOD player programs clear the previous loaded +samples by their own data. + +If you have only 512kb on the sound card, I recommend to use dynamic +sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is +available in most midi files. + + % sfxload synthgm + % drvmidi -L 2mbgmgs foo.mid + +This makes a big difference (believe me)! For more details, please +refer to the FAQ list which is available on the URL above. + +The current chorus, reverb and equalizer status can be changed by +aweset utility program (included in awesfx package). Note that +some awedrv-native programs (like drvmidi and xmp) will change the +current settings by themselves. The aweset program is effective +only for other programs like playmidi. + +Enjoy. + + +* COMPILE FLAGS + +Compile conditions are defined in awe_config.h. + +[Compatibility Conditions] +The following flags are defined automatically when using installation +shell script. + +- AWE_MODULE_SUPPORT + indicates your Linux kernel supports module for each sound card + (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels + as distributed in the RH5.0 package). + This flag is automatically set when you're using 2.1.x kernels. + You can pass the base address and memory size via the following + module options, + io = base I/O port address (eg. 0x620) + memsize = DRAM size in kilobytes (eg. 512) + As default, AWE driver probes these values automatically. + + +[Hardware Conditions] +You DON'T have to define the following two values. +Define them only when the driver couldn't detect the card properly. + +- AWE_DEFAULT_BASE_ADDR (default: not defined) + specifies the base port address of your AWE32 card. + 0 means to autodetect the address. + +- AWE_DEFAULT_MEM_SIZE (default: not defined) + specifies the memory size of your AWE32 card in kilobytes. + -1 means to autodetect its size. + + +[Sample Table Size] +From ver.0.4.0, sample tables are allocated dynamically (except +Linux-1.2.x system), so you need NOT to touch these parameters. +Linux-1.2.x users may need to increase these values to appropriate size +if the sound card is equipped with more DRAM. + +- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS + + +[Other Conditions] + +- AWE_ALWAYS_INIT_FM (default: not defined) + indicates the AWE driver always initialize FM passthrough even + without DRAM on board. Emu8000 chip has a restriction for playing + samples on DRAM that at least two channels must be occupied as + passthrough channels. + +- AWE_DEBUG_ON (default: defined) + turns on debugging messages if defined. + +- AWE_HAS_GUS_COMPATIBILITY (default: defined) + Enables GUS compatibility mode if defined, reading GUS patches and + GUS control commands. Define this option to use GMOD or other + GUS module players. + +- CONFIG_AWE32_MIDIEMU (default: defined) + Adds a MIDI emulation device by Emu8000 wavetable. The emulation + device can be accessed as an external MIDI, and sends the MIDI + control codes directly. XG and GS sysex/NRPN are accepted. + No MIDI input is supported. + +- CONFIG_AWE32_MIXER (default: not defined) + Adds a mixer device for AWE32 bass/treble equalizer control. + You can access this device using /dev/mixer?? (usually mixer01). + +- AWE_USE_NEW_VOLUME_CALC (default: defined) + Use the new method to calculate the volume change as compatible + with DOS/Win drivers. This option can be toggled via aweset + program, or drvmidi player. + +- AWE_CHECK_VTARGET (default: defined) + Check the current volume target value when searching for an + empty channel to allocate a new voice. This is experimentally + implemented in this version. (probably, this option doesn't + affect the sound quality severely...) + +- AWE_ALLOW_SAMPLE_SHARING (default: defined) + Allow sample sharing for differently loaded patches. + This function is available only together with awesfx-0.4.3p3. + Note that this is still an experimental option. + +- DEF_FM_CHORUS_DEPTH (default: 0x10) + The default strength to be sent to the chorus effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + +- DEF_FM_REVERB_DEPTH (default: 0x10) + The default strength to be sent to the reverb effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + + +* ACKNOWLEDGMENTS + +Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice +on programming of AWE32. Much code is brought from his AWE32-native +MOD player, ALMP. +The port of awedrv to FreeBSD is done by Randall Hopper +(rhh@ct.picker.com). +The new volume calculation routine was derived from Mark Weaver's +ADIP compatible routines. +I also thank linux-awe-ml members for their efforts +to reboot their system many times :-) + + +* TODO'S + +- Complete DOS/Win compatibility +- DSP-like output + + +* COPYRIGHT + +Copyright (C) 1996-1998 Takashi Iwai + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/README.modules linux-2.5.6/Documentation/sound/oss/README.modules --- linux-2.5.6-pre3/Documentation/sound/oss/README.modules Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/README.modules Thu Mar 7 18:24:41 2002 @@ -0,0 +1,105 @@ +Building a modular sound driver +================================ + + The following information is current as of linux-2.1.85. Check the other +readme files, especially README.OSS, for information not specific to +making sound modular. + + First, configure your kernel. This is an idea of what you should be +setting in the sound section: + + Sound card support + + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + + I have SoundBlaster. Select your card from the list. + + Generic OPL2/OPL3 FM synthesizer support + FM synthesizer (YM3812/OPL-3) support + + If you don't set these, you will probably find you can play .wav files +but not .midi. As the help for them says, set them unless you know your +card does not use one of these chips for FM support. + + Once you are configured, make zlilo, modules, modules_install; reboot. +Note that it is no longer necessary or possible to configure sound in the +drivers/sound dir. Now one simply configures and makes one's kernel and +modules in the usual way. + + Then, add to your /etc/modules.conf something like: + +alias char-major-14 sb +post-install sb /sbin/modprobe "-k" "adlib_card" +options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +options adlib_card io=0x388 # FM synthesizer + + Alternatively, if you have compiled in kernel level ISAPnP support: + +alias char-major-14 sb +post-install sb /sbin/modprobe "-k" "adlib_card" +options adlib_card io=0x388 + + The effect of this is that the sound driver and all necessary bits and +pieces autoload on demand, assuming you use kerneld (a sound choice) and +autoclean when not in use. Also, options for the device drivers are +set. They will not work without them. Change as appropriate for your card. +If you are not yet using the very cool kerneld, you will have to "modprobe +-k sb" yourself to get things going. Eventually things may be fixed so +that this kludgery is not necessary; for the time being, it seems to work +well. + + Replace 'sb' with the driver for your card, and give it the right +options. To find the filename of the driver, look in +/lib/modules//misc. Mine looks like: + +adlib_card.o # This is the generic OPLx driver +opl3.o # The OPL3 driver +sb.o # <> +sound.o # The sound driver +uart401.o # Used by sb, maybe other cards + + Whichever card you have, try feeding it the options that would be the +default if you were making the driver wired, not as modules. You can look +at the init_module() code for the card to see what args are expected. + + Note that at present there is no way to configure the io, irq and other +parameters for the modular drivers as one does for the wired drivers.. One +needs to pass the modules the necessary parameters as arguments, either +with /etc/modules.conf or with command-line args to modprobe, e.g. + +modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +modprobe -k adlib_card io=0x388 + + recommend using /etc/modules.conf. + +Persistent DMA Buffers: + +The sound modules normally allocate DMA buffers during open() and +deallocate them during close(). Linux can often have problems allocating +DMA buffers for ISA cards on machines with more than 16MB RAM. This is +because ISA DMA buffers must exist below the 16MB boundary and it is quite +possible that we can't find a large enough free block in this region after +the machine has been running for any amount of time. The way to avoid this +problem is to allocate the DMA buffers during module load and deallocate +them when the module is unloaded. For this to be effective we need to load +the sound modules right after the kernel boots, either manually or by an +init script, and keep them around until we shut down. This is a little +wasteful of RAM, but it guarantees that sound always works. + +To make the sound driver use persistent DMA buffers we need to pass the +sound.o module a "dmabuf=1" command-line argument. This is normally done +in /etc/modules.conf like so: + +options sound dmabuf=1 + +If you have 16MB or less RAM or a PCI sound card, this is wasteful and +unnecessary. It is possible that machine with 16MB or less RAM will find +this option useful, but if your machine is so memory-starved that it +cannot find a 64K block free, you will be wasting even more RAM by keeping +the sound modules loaded and the DMA buffers allocated when they are not +needed. The proper solution is to upgrade your RAM. But you do also have +this improper solution as well. Use it wisely. + + I'm afraid I know nothing about anything but my setup, being more of a +text-mode guy anyway. If you have options for other cards or other helpful +hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/README.ymfsb linux-2.5.6/Documentation/sound/oss/README.ymfsb --- linux-2.5.6-pre3/Documentation/sound/oss/README.ymfsb Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/README.ymfsb Thu Mar 7 18:24:41 2002 @@ -0,0 +1,107 @@ +Legacy audio driver for YMF7xx PCI cards. + + +FIRST OF ALL +============ + + This code references YAMAHA's sample codes and data sheets. + I respect and thank for all people they made open the informations + about YMF7xx cards. + + And this codes heavily based on Jeff Garzik 's + old VIA 82Cxxx driver (via82cxxx.c). I also respect him. + + +DISCLIMER +========= + + This driver is currently at early ALPHA stage. It may cause serious + damage to your computer when used. + PLEASE USE IT AT YOUR OWN RISK. + + +ABOUT THIS DRIVER +================= + + This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754 + cards. When enabled, your card acts as "SoundBlaster Pro" compatible card. + It can only play 22.05kHz / 8bit / Stereo samples, control external MIDI + port. + If you want to use your card as recent "16-bit" card, you should use + Alsa or OSS/Linux driver. Of course you can write native PCI driver for + your cards :) + + +USAGE +===== + + # modprobe ymfsb (options) + + +OPTIONS FOR MODULE +================== + + io : SB base address (0x220, 0x240, 0x260, 0x280) + synth_io : OPL3 base address (0x388, 0x398, 0x3a0, 0x3a8) + dma : DMA number (0,1,3) + master_volume: AC'97 PCM out Vol (0-100) + spdif_out : SPDIF-out flag (0:disable 1:enable) + + These options will change in future... + + +FREQUENCY +========= + + When playing sounds via this driver, you will hear its pitch is slightly + lower than original sounds. Since this driver recognizes your card acts + with 21.739kHz sample rates rather than 22.050kHz (I think it must be + hardware restriction). So many players become tone deafness. + To prevent this, you should express some options to your sound player + that specify correct sample frequency. For example, to play your MP3 file + correctly with mpg123, specify the frequency like following: + + % mpg123 -r 21739 foo.mp3 + + +SPDIF OUT +========= + + With installing modules with option 'spdif_out=1', you can enjoy your + sounds from SPDIF-out of your card (if it had). + Its Fs is fixed to 48kHz (It never means the sample frequency become + up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your + digital-in capable components has to be able to handle 48kHz Fs. + + +COPYING +======= + + 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, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +TODO +==== + * support for multiple cards + (set the different SB_IO,MPU_IO,OPL_IO for each cards) + + * support for OPL (dmfm) : There will be no requirements... :-< + + +AUTHOR +====== + + Daisuke Nagano + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/SoundPro linux-2.5.6/Documentation/sound/oss/SoundPro --- linux-2.5.6-pre3/Documentation/sound/oss/SoundPro Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/SoundPro Thu Mar 7 18:24:41 2002 @@ -0,0 +1,105 @@ +Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o) +------------------------------------------------------------------------------ + +( Be sure to read Documentation/sound/CMI8330 too ) + +Ion Badulescu, ionut@cs.columbia.edu +February 24, 1999 + +(derived from the OPL3-SA2 documentation by Scott Murray) + +The SoundPro CMI8330 (ISA) is a chip usually found on some Taiwanese +motherboards. The official name in the documentation is CMI8330, SoundPro +is the nickname and the big inscription on the chip itself. + +The chip emulates a WSS as well as a SB16, but it has certain differences +in the mixer section which require separate support. It also emulates an +MPU401 and an OPL3 synthesizer, so you probably want to enable support +for these, too. + +The chip identifies itself as an AD1848, but its mixer is significantly +more advanced than the original AD1848 one. If your system works with +either WSS or SB16 and you are having problems with some mixer controls +(no CD audio, no line-in, etc), you might want to give this driver a try. +Detection should work, but it hasn't been widely tested, so it might still +mis-identify the chip. You can still force soundpro=1 in the modprobe +parameters for ad1848. Please let me know if it happens to you, so I can +adjust the detection routine. + +The chip is capable of doing full-duplex, but since the driver sees it as an +AD1848, it cannot take advantage of this. Moreover, the full-duplex mode is +not achievable through the WSS interface, b/c it needs a dma16 line which is +assigned only to the SB16 subdevice (with isapnp). Windows documentation +says the user must use WSS Playback and SB16 Recording for full-duplex, so +it might be possible to do the same thing under Linux. You can try loading +up both ad1848 and sb then use one for playback and the other for +recording. I don't know if this works, b/c I haven't tested it. Anyway, if +you try it, be very careful: the SB16 mixer *mostly* works, but certain +settings can have unexpected effects. Use the WSS mixer for best results. + +There is also a PCI SoundPro chip. I have not seen this chip, so I have +no idea if the driver will work with it. I suspect it won't. + +As with PnP cards, some configuration is required. There are two ways +of doing this. The most common is to use the isapnptools package to +initialize the card, and use the kernel module form of the sound +subsystem and sound drivers. Alternatively, some BIOS's allow manual +configuration of installed PnP devices in a BIOS menu, which should +allow using the non-modular sound drivers, i.e. built into the kernel. +Since in this latter case you cannot use module parameters, you will +have to enable support for the SoundPro at compile time. + +The IRQ and DMA values can be any that are considered acceptable for a +WSS. Assuming you've got isapnp all happy, then you should be able to +do something like the following (which *must* match the isapnp/BIOS +configuration): + +modprobe ad1848 io=0x530 irq=11 dma=0 soundpro=1 +-and maybe- +modprobe sb io=0x220 irq=5 dma=1 dma16=5 + +-then- +modprobe mpu401 io=0x330 irq=9 +modprobe opl3 io=0x388 + +If all goes well and you see no error messages, you should be able to +start using the sound capabilities of your system. If you get an +error message while trying to insert the module(s), then make +sure that the values of the various arguments match what you specified +in your isapnp configuration file, and that there is no conflict with +another device for an I/O port or interrupt. Checking the contents of +/proc/ioports and /proc/interrupts can be useful to see if you're +butting heads with another device. + +If you do not see the chipset version message, and none of the other +messages present in the system log are helpful, try adding 'debug=1' +to the ad1848 parameters, email me the syslog results and I'll do +my best to help. + +Lastly, if you're using modules and want to set up automatic module +loading with kmod, the kernel module loader, here is the section I +currently use in my conf.modules file: + +# Sound +post-install sound modprobe -k ad1848; modprobe -k mpu401; modprobe -k opl3 +options ad1848 io=0x530 irq=11 dma=0 +options sb io=0x220 irq=5 dma=1 dma16=5 +options mpu401 io=0x330 irq=9 +options opl3 io=0x388 + +The above ensures that ad1848 will be loaded whenever the sound system +is being used. + +Good luck. + +Ion + +NOT REALLY TESTED: +- recording +- recording device selection +- full-duplex + +TODO: +- implement mixer support for surround, loud, digital CD switches. +- come up with a scheme which allows recording volumes for each subdevice. +This is a major OSS API change. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Soundblaster linux-2.5.6/Documentation/sound/oss/Soundblaster --- linux-2.5.6-pre3/Documentation/sound/oss/Soundblaster Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Soundblaster Thu Mar 7 18:24:41 2002 @@ -0,0 +1,53 @@ +modprobe sound +insmod uart401 +insmod sb ... + +This loads the driver for the Sound Blaster and assorted clones. Cards that +are covered by other drivers should not be using this driver. + +The Sound Blaster module takes the following arguments + +io I/O address of the Sound Blaster chip (0x220,0x240,0x260,0x280) +irq IRQ of the Sound Blaster chip (5,7,9,10) +dma 8-bit DMA channel for the Sound Blaster (0,1,3) +dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) +mpu_io I/O for MPU chip if present (0x300,0x330) + +sm_games=1 Set if you have a Logitech soundman games +acer=1 Set this to detect cards in some ACER notebooks +mwave_bug=1 Set if you are trying to use this driver with mwave (see on) +type Use this to specify a specific card type + +The following arguments are taken if ISAPnP support is compiled in + +isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) +multiple=0 Set to disable detection of multiple Soundblaster cards. + Consider it a bug if this option is needed, and send in a + report. +pnplegacy=1 Set this to be able to use a PnP card(s) along with a single + non-PnP (legacy) card. Above options for io, irq, etc. are + needed, and will apply only to the legacy card. +reverse=1 Reverses the order of the search in the PnP table. +uart401=1 Set to enable detection of mpu devices on some clones. +isapnpjump=n Jumps to slot n in the driver's PnP table. Use the source, + Luke. + +You may well want to load the opl3 driver for synth music on most SB and +clone SB devices + +insmod opl3 io=0x388 + +Using Mwave + +To make this driver work with Mwave you must set mwave_bug. You also need +to warm boot from DOS/Windows with the required firmware loaded under this +OS. IBM are being difficult about documenting how to load this firmware. + +Avance Logic ALS007 + +This card is supported; see the separate file ALS007 for full details. + +Avance Logic ALS100 + +This card is supported; setup should be as for a standard Sound Blaster 16. +The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Tropez+ linux-2.5.6/Documentation/sound/oss/Tropez+ --- linux-2.5.6-pre3/Documentation/sound/oss/Tropez+ Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Tropez+ Thu Mar 7 18:24:41 2002 @@ -0,0 +1,26 @@ +From: Paul Barton-Davis + +Here is the configuration I use with a Tropez+ and my modular +driver: + + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + +Please see drivers/sound/README.wavefront for more details. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/VIA-chipset linux-2.5.6/Documentation/sound/oss/VIA-chipset --- linux-2.5.6-pre3/Documentation/sound/oss/VIA-chipset Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/VIA-chipset Thu Mar 7 18:24:41 2002 @@ -0,0 +1,43 @@ +Running sound cards on VIA chipsets + +o There are problems with VIA chipsets and sound cards that appear to + lock the hardware solidly. Test programs under DOS have verified the + problem exists on at least some (but apparently not all) VIA boards + +o VIA have so far failed to bother to answer support mail on the subject + so if you are a VIA engineer feeling aggrieved as you read this + document go chase your own people. If there is a workaround please + let us know so we can implement it. + + +Certain patterns of ISA DMA access used for most PC sound cards cause the +VIA chipsets to lock up. From the collected reports this appears to cover a +wide range of boards. Some also lock up with sound cards under Win* as well. + +Linux implements a workaround providing your chipset is PCI and you compiled +with PCI Quirks enabled. If so you will see a message + "Activating ISA DMA bug workarounds" + +during booting. If you have a VIA PCI chipset that hangs when you use the +sound and is not generating this message even with PCI quirks enabled +please report the information to the linux-kernel list (see REPORTING-BUGS). + +If you are one of the tiny number of unfortunates with a 486 ISA/VLB VIA +chipset board you need to do the following to build a special kernel for +your board + + edit linux/include/asm-i386/dma.h + +change + +#define isa_dma_bridge_buggy (0) + +to + +#define isa_dma_bridge_buggy (1) + +and rebuild a kernel without PCI quirk support. + + +Other than this particular glitch the VIA [M]VP* chipsets appear to work +perfectly with Linux. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/VIBRA16 linux-2.5.6/Documentation/sound/oss/VIBRA16 --- linux-2.5.6-pre3/Documentation/sound/oss/VIBRA16 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/VIBRA16 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,80 @@ +Sound Blaster 16X Vibra addendum +-------------------------------- +by Marius Ilioaea + Stefan Laudat + +Sat Mar 6 23:55:27 EET 1999 + + Hello again, + + Playing with a SB Vibra 16x soundcard we found it very difficult +to setup because the kernel reported a lot of DMA errors and wouldn't +simply play any sound. + A good starting point is that the vibra16x chip full-duplex facility +is neither still exploited by the sb driver found in the linux kernel +(tried it with a 2.2.2-ac7), nor in the commercial OSS package (it reports +it as half-duplex soundcard). Oh, I almost forgot, the RedHat sndconfig +failed detecting it ;) + So, the big problem still remains, because the sb module wants a +8-bit and a 16-bit dma, which we could not allocate for vibra... it supports +only two 8-bit dma channels, the second one will be passed to the module +as a 16 bit channel, the kernel will yield about that but everything will +be okay, trust us. + The only inconvenient you may find is that you will have +some sound playing jitters if you have HDD dma support enabled - but this +will happen with almost all soundcards... + + A fully working isapnp.conf is just here: + + + +(READPORT 0x0203) +(ISOLATE PRESERVE) +(IDENTIFY *) +(VERBOSITY 2) +(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING +# SB 16 and OPL3 devices +(CONFIGURE CTL00f0/-1 (LD 0 +(INT 0 (IRQ 5 (MODE +E))) +(DMA 0 (CHANNEL 1)) +(DMA 1 (CHANNEL 3)) +(IO 0 (SIZE 16) (BASE 0x0220)) +(IO 2 (SIZE 4) (BASE 0x0388)) +(NAME "CTL00f0/-1[0]{Audio }") +(ACT Y) +)) + +# Joystick device - only if you need it :-/ + +(CONFIGURE CTL00f0/-1 (LD 1 +(IO 0 (SIZE 1) (BASE 0x0200)) +(NAME "CTL00f0/-1[1]{Game }") +(ACT Y) +)) +(WAITFORKEY) + + + + So, after a good kernel modules compilation and a 'depmod -a kernel_ver' +you may want to: + +modprobe sb io=0x220 irq=5 dma=1 dma16=3 + + Or, take the hard way: + +insmod souncore +insmod sound +insmod uart401 +insmod sb io=0x220 irq=5 dma=1 dma16=3 +# do you need MIDI? +insmod opl3=0x388 + + Just in case, the kernel sound support should be: + +CONFIG_SOUND=m +CONFIG_SOUND_OSS=m +CONFIG_SOUND_SB=m + + Enjoy your new noisy Linux box! ;) + + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/WaveArtist linux-2.5.6/Documentation/sound/oss/WaveArtist --- linux-2.5.6-pre3/Documentation/sound/oss/WaveArtist Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/WaveArtist Thu Mar 7 18:24:41 2002 @@ -0,0 +1,170 @@ + + (the following is from the armlinux CVS) + + WaveArtist mixer and volume levels can be accessed via these commands: + + nn30 read registers nn, where nn = 00 - 09 for mixer settings + 0a - 13 for channel volumes + mm31 write the volume setting in pairs, where mm = (nn - 10) / 2 + rr32 write the mixer settings in pairs, where rr = nn/2 + xx33 reset all settings to default + 0y34 select mono source, y=0 = left, y=1 = right + + bits + nn 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 00 | 0 | 0 0 1 1 | left line mixer gain | left aux1 mixer gain |lmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 01 | 0 | 0 1 0 1 | left aux2 mixer gain | right 2 left mic gain |mmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 05 | 0 | 1 1 0 1 | right aux2 mixer gain | left 2 right mic gain |test | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 09 | 1 | 0 1 0 1 |loopb|left linout|loop|ADCch|TxFch|OffCD|test |loopb|loopb|osamp| +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0a | 0 | left PCM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0b | 0 | right PCM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0c | 0 | left FM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0d | 0 | right FM channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0e | 0 | left wavetable channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 0f | 0 | right wavetable channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 10 | 0 | left PCM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 11 | 0 | right PCM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 12 | 0 | left FM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + 13 | 0 | right FM expansion channel volume | +----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ + + lmute: left mute + mmute: mono mute + dith: dithds + lrfg: + rmute: right mute + rbyps: right bypass + rrfg: + ADCch: + TxFch: + OffCD: + osamp: + + And the following diagram is derived from the description in the CVS archive: + + MIC L (mouthpiece) + +------+ + -->PreAmp>-\ + +--^---+ | + | | + r2b4-5 | +--------+ + /----*-------------------------------->5 | + | | | + | /----------------------------------->4 | + | | | | + | | /--------------------------------->3 1of5 | +---+ + | | | | mux >-->AMP>--> ADC L + | | | /------------------------------->2 | +-^-+ + | | | | | | | + Line | | | | +----+ +------+ +---+ /---->1 | r3b3-0 + ------------*->mute>--> Gain >--> | | | | + L | | | +----+ +------+ | | | *->0 | + | | | | | | +---^----+ + Aux2 | | | +----+ +------+ | | | | + ----------*--->mute>--> Gain >--> M | | r8b0-2 + L | | +----+ +------+ | | | + | | | | \------\ + Aux1 | | +----+ +------+ | | | + --------*----->mute>--> Gain >--> I | | + L | +----+ +------+ | | | + | | | | + | +----+ +------+ | | +---+ | + *------->mute>--> Gain >--> X >-->AMP>--* + | +----+ +------+ | | +-^-+ | + | | | | | + | +----+ +------+ | | r2b1-3 | + | /----->mute>--> Gain >--> E | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | | + | | /--->mute>--> Gain >--> R | | + | | | +----+ +------+ | | | + | | | | | | r9b8-9 + | | | +----+ +------+ | | | | + | | | /->mute>--> Gain >--> | | +---v---+ + | | | | +----+ +------+ +---+ /-*->0 | + DAC | | | | | | | + ------------*----------------------------------->? | +----+ + L | | | | | Mux >-->mute>--> L output + | | | | /->? | +--^-+ + | | | | | | | | + | | | /--------->? | r0b0 + | | | | | | +-------+ + | | | | | | + Mono | | | | | | +-------+ + ----------* | \---> | +----+ + | | | | | | Mix >-->mute>--> Mono output + | | | | *-> | +--^-+ + | | | | | +-------+ | + | | | | | r1b0 + DAC | | | | | +-------+ + ------------*-------------------------*--------->1 | +----+ + R | | | | | | Mux >-->mute>--> R output + | | | | +----+ +------+ +---+ *->0 | +--^-+ + | | | \->mute>--> Gain >--> | | +---^---+ | + | | | +----+ +------+ | | | | r5b0 + | | | | | | r6b0 + | | | +----+ +------+ | | | + | | \--->mute>--> Gain >--> M | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | | + | *----->mute>--> Gain >--> I | | + | | +----+ +------+ | | | + | | | | | + | | +----+ +------+ | | +---+ | + \------->mute>--> Gain >--> X >-->AMP>--* + | +----+ +------+ | | +-^-+ | + /--/ | | | | + Aux1 | +----+ +------+ | | r6b1-3 | + -------*------>mute>--> Gain >--> E | | + R | | +----+ +------+ | | | + | | | | | + Aux2 | | +----+ +------+ | | /------/ + ---------*---->mute>--> Gain >--> R | | + R | | | +----+ +------+ | | | + | | | | | | +--------+ + Line | | | +----+ +------+ | | | *->0 | + -----------*-->mute>--> Gain >--> | | | | + R | | | | +----+ +------+ +---+ \---->1 | + | | | | | | + | | | \-------------------------------->2 | +---+ + | | | | Mux >-->AMP>--> ADC R + | | \---------------------------------->3 | +-^-+ + | | | | | + | \------------------------------------>4 | r7b3-0 + | | | + \-----*-------------------------------->5 | + | +---^----+ + r6b4-5 | | + | | r8b3-5 + +--v---+ | + -->PreAmp>-/ + +------+ + MIC R (electret mic) diff -urN linux-2.5.6-pre3/Documentation/sound/oss/Wavefront linux-2.5.6/Documentation/sound/oss/Wavefront --- linux-2.5.6-pre3/Documentation/sound/oss/Wavefront Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/Wavefront Thu Mar 7 18:24:41 2002 @@ -0,0 +1,341 @@ + An OSS/Free Driver for WaveFront soundcards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + + VERSION 0.2.5 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later (the driver is included with kernels +2.1.109 and above) + +As of 7/22/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested (or even compiled) as a static +(non-modular) part of the kernel. Alan Cox's good work in modularizing +OSS/Free for Linux makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because its not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers + needed to make the rest of this work + DO NOT USE IF YOU'VE APPLIED THEM + BEFORE, OR HAVE 2.1.109 OR ABOVE + + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd + tar -zxvf + +PART TWO: apply the patches + + DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 + AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + soundcards you want support for) + + + make dep + make boot + . + . + . + + make modules + . + . + . + make modules_install + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/modules.conf. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] FM synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this in /var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the soundcard, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wait_usecs - loop timer used when waiting for + status conditions on the board. + The default is 150. + + debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis + + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/btaudio linux-2.5.6/Documentation/sound/oss/btaudio --- linux-2.5.6-pre3/Documentation/sound/oss/btaudio Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/btaudio Thu Mar 7 18:24:41 2002 @@ -0,0 +1,92 @@ + +Intro +===== + +people start bugging me about this with questions, looks like I +should write up some documentation for this beast. That way I +don't have to answer that much mails I hope. Yes, I'm lazy... + + +You might have noticed that the bt878 grabber cards have actually +_two_ PCI functions: + +$ lspci +[ ... ] +00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02) +00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02) +[ ... ] + +The first does video, it is backward compatible to the bt848. The second +does audio. btaudio is a driver for the second function. It's a sound +driver which can be used for recording sound (and _only_ recording, no +playback). As most TV cards come with a short cable which can be plugged +into your sound card's line-in you probably don't need this driver if all +you want to do is just watching TV... + + +Driver Status +============= + +Still somewhat experimental. The driver should work stable, i.e. it +should'nt crash your box. It might not work as expected, have bugs, +not being fully OSS API compilant, ... + +Latest versions are available from http://bytesex.org/bttv/, the +driver is in the bttv tarball. Kernel patches might be available too, +have a look at http://bytesex.org/bttv/listing.html. + +The chip knows two different modes. btaudio registers two dsp +devices, one for each mode. They can not be used at the same time. + + +Digital audio mode +================== + +The chip gives you 16 bit stereo sound. The sample rate depends on +the external source which feeds the bt878 with digital sound via I2S +interface. There is a insmod option (rate) to tell the driver which +sample rate the hardware uses (32000 is the default). + +One possible source for digital sound is the msp34xx audio processor +chip which provides digital sound via I2S with 32 kHz sample rate. My +Hauppauge board works this way. + +The Osprey-200 reportly gives you digital sound with 44100 Hz sample +rate. It is also possible that you get no sound at all. + + +analog mode (A/D) +================= + +You can tell the driver to use this mode with the insmod option "analog=1". +The chip has three analog inputs. Consequently you'll get a mixer device +to control these. + +The analog mode supports mono only. Both 8 + 16 bit. Both are _signed_ +int, which is uncommon for the 8 bit case. Sample rate range is 119 kHz +to 448 kHz. Yes, the number of digits is correct. The driver supports +downsampling by powers of two, so you can ask for more usual sample rates +like 44 kHz too. + +With my Hauppauge I get noisy sound on the second input (mapped to line2 +by the mixer device). Others get a useable signal on line1. + + +some examples +============= + +* read audio data from btaudio (dsp2), send to es1730 (dsp,dsp1): + $ sox -w -r 32000 -t ossdsp /dev/dsp2 -t ossdsp /dev/dsp + +* read audio data from btaudio, send to esound daemon (which might be + running on another host): + $ sox -c 2 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -r 32000 + $ sox -c 1 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -m -r 32000 + + +Have fun, + + Gerd + +-- +Gerd Knorr diff -urN linux-2.5.6-pre3/Documentation/sound/oss/cs46xx linux-2.5.6/Documentation/sound/oss/cs46xx --- linux-2.5.6-pre3/Documentation/sound/oss/cs46xx Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/cs46xx Thu Mar 7 18:24:41 2002 @@ -0,0 +1,138 @@ + +Documentation for the Cirrus Logic/Crystal SoundFusion cs46xx/cs4280 audio +controller chips (2001/05/11) + +The cs46xx audio driver supports the DSP line of Cirrus controllers. +Specifically, the cs4610, cs4612, cs4614, cs4622, cs4624, cs4630 and the cs4280 +products. This driver uses the generic ac97_codec driver for AC97 codec +support. + + +Features: + +Full Duplex Playback/Capture supported from 8k-48k. +16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. + +APM/PM - 2.2.x PM is enabled and functional. APM can also +be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro +definition. + +DMA playback buffer size is configurable from 16k (defaultorder=2) up to 2Meg +(defaultorder=11). DMA capture buffer size is fixed at a single 4k page as +two 2k fragments. + +MMAP seems to work well with QuakeIII, and test XMMS plugin. + +Myth2 works, but the polling logic is not fully correct, but is functional. + +The 2.4.4-ac6 gameport code in the cs461x joystick driver has been tested +with a Microsoft Sidewinder joystick (cs461x.o and sidewinder.o). This +audio driver must be loaded prior to the joystick driver to enable the +DSP task image supporting the joystick device. + + +Limitations: + +SPDIF is currently not supported. + +Primary codec support only. No secondary codec support is implemented. + + + +NOTES: + +Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, +and has been tested. +Module parameter hercules_egpio_disable set to 1, will force a 0 to EGPIODR +to disable the external amplifier. + +VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control +the external amplifier for the "back" speakers, since we do not +support the secondary codec then this external amp is not +turned on. The primary codec external amplifier is supported but +note that the AC97 EAPD bit is inverted logic (amp_voyetra()). + +DMA buffer size - there are issues with many of the Linux applications +concerning the optimal buffer size. Several applications request a +certain fragment size and number and then do not verify that the driver +has the ability to support the requested configuration. +SNDCTL_DSP_SETFRAGMENT ioctl is used to request a fragment size and +number of fragments. Some applications exit if an error is returned +on this particular ioctl. Therefore, in alignment with the other OSS audio +drivers, no error is returned when a SETFRAGs IOCTL is received, but the +values passed from the app are not used in any buffer calculation +(ossfragshift/ossmaxfrags are not used). +Use the "defaultorder=N" module parameter to change the buffer size if +you have an application that requires a specific number of fragments +or a specific buffer size (see below). + +Debug Interface +--------------- +There is an ioctl debug interface to allow runtime modification of the +debug print levels. This debug interface code can be disabled from the +compilation process with commenting the following define: +#define CSDEBUG_INTERFACE 1 +There is also a debug print methodolgy to select printf statements from +different areas of the driver. A debug print level is also used to allow +additional printfs to be active. Comment out the following line in the +driver to disable compilation of the CS_DBGOUT print statements: +#define CSDEBUG 1 + +Please see the defintions for cs_debuglevel and cs_debugmask for additional +information on the debug levels and sections. + +There is also a csdbg executable to allow runtime manipulation of these +parameters. for a copy email: twoller@crystal.cirrus.com + + + +MODULE_PARMS definitions +------------------------ +MODULE_PARM(defaultorder, "i"); +defaultorder=N +where N is a value from 1 to 12 +The buffer order determines the size of the dma buffer for the driver. +under Linux, a smaller buffer allows more responsiveness from many of the +applications (e.g. games). A larger buffer allows some of the apps (esound) +to not underrun the dma buffer as easily. As default, use 32k (order=3) +rather than 64k as some of the games work more responsively. +(2^N) * PAGE_SIZE = allocated buffer size + +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +cs_debuglevel=N +cs_debugmask=0xMMMMMMMM +where N is a value from 0 (no debug printfs), to 9 (maximum) +0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source). + +MODULE_PARM(hercules_egpio_disable, "i"); +hercules_egpio_disable=N +where N is a 0 (enable egpio), or a 1 (disable egpio support) + +MODULE_PARM(initdelay, "i"); +initdelay=N +This value is used to determine the millescond delay during the initialization +code prior to powering up the PLL. On laptops this value can be used to +assist with errors on resume, mostly with IBM laptops. Basically, if the +system is booted under battery power then the mdelay()/udelay() functions fail to +properly delay the required time. Also, if the system is booted under AC power +and then the power removed, the mdelay()/udelay() functions will not delay properly. + +MODULE_PARM(powerdown, "i"); +powerdown=N +where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown) + + +MODULE_PARM(external_amp, "i"); +external_amp=1 +if N is set to 1, then force enabling the EAPD support in the primary AC97 codec. +override the detection logic and force the external amp bit in the AC97 0x26 register +to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz +card has inverted logic, so there is a special function for these cards. + +MODULE_PARM(thinkpad, "i"); +thinkpad=1 +if N is set to 1, then force enabling the clkrun functionality. +Currently, when the part is being used, then clkrun is disabled for the entire system, +but re-enabled when the driver is released or there is no outstanding open count. + diff -urN linux-2.5.6-pre3/Documentation/sound/oss/es1370 linux-2.5.6/Documentation/sound/oss/es1370 --- linux-2.5.6-pre3/Documentation/sound/oss/es1370 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/es1370 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,70 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. +The second one goes to the mixer "synth" setting and supports +only a limited set of sampling rates (44100, 22050, 11025, 5512). +By setting lineout to 1 on the driver command line +(eg. insmod es1370 lineout=1) it is even possible on some +cards to convert the LINEIN jack into a second LINEOUT jack, thus +making it possible to output four independent audio channels! + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/oss/es1371 linux-2.5.6/Documentation/sound/oss/es1371 --- linux-2.5.6-pre3/Documentation/sound/oss/es1371 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/es1371 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,64 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/oss/mwave linux-2.5.6/Documentation/sound/oss/mwave --- linux-2.5.6-pre3/Documentation/sound/oss/mwave Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/mwave Thu Mar 7 18:24:41 2002 @@ -0,0 +1,185 @@ + How to try to survive an IBM Mwave under Linux SB drivers + + ++ IBM have now released documentation of sorts and Torsten is busy + trying to make the Mwave work. This is not however a trivial task. + +---------------------------------------------------------------------------- + +OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or +not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the +latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! + +I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. + +The steps, then: + + Boot to Linux. + Mount Windows 95 file system (assume mount point = /dos95). + mkdir /dos95/linux + mkdir /dos95/linux/boot + mkdir /dos95/linux/boot/parms + + Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. + + Reboot to Windows 95. + + Edit C:/msdos.sys and add or change the following: + + Logo=0 + BootGUI=0 + + Note that msdos.sys is a text file but it needs to be made 'unhidden', + readable and writable before it can be edited. This can be done with + DOS' "attrib" command. + + Edit config.sys to have multiple config menus. I have one for windows 95 and + five for Linux, like this: +------------ +[menu] +menuitem=W95, Windows 95 +menuitem=LINTP, Linux - ThinkPad +menuitem=LINTP3, Linux - ThinkPad Console +menuitem=LINDOC, Linux - Docked +menuitem=LINDOC3, Linux - Docked Console +menuitem=LIN1, Linux - Single User Mode +REM menudefault=W95,10 + +[W95] + +[LINTP] + +[LINDOC] + +[LINTP3] + +[LINDOC3] + +[LIN1] + +[COMMON] +FILES=30 +REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. +DOS=HIGH,UMB +DEVICE=C:\MWW\MANAGER\MWD50430.EXE +SHELL=c:\command.com /e:2048 +------------------- + +The important things are the SHELL and DEVICE statements. + + Then change autoexec.bat. Basically everything in there originally should be + done ONLY when Windows 95 is booted. Then you add new things specifically + for Linux. Mine is as follows + +--------------- +@ECHO OFF +if "%CONFIG%" == "W95" goto W95 + +REM +REM Linux stuff +REM +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\MWW\DLL; +CALL MWAVE START NOSHOW +c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par + +:W95 +REM +REM Windows 95 stuff +REM +c:\toolkit\guard +SET MSINPUT=C:\MSINPUT +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +REM The following is used by DOS games to recognize Sound Blaster hardware. +REM If hardware settings are changed, please change this line as well. +REM See the Mwave README file for instructions. +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; +SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE +SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB +win + +------------------------ + +Now build a file in c:\linux\boot\parms for each Linux config that you have. + +For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no +initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: + +----------------------- +# LOADLIN @param_file image=other_image root=/dev/other +# +# Linux Console in docking station +# +c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. +root=/dev/hda3 # device which gets mounted as root FS +ro # Other kernel arguments go here. +apm=off +doc=yes +3 +----------------------- + +The doc=yes parameter is an environment variable used by my init scripts, not +a kernel argument. + +However, the apm=off parameter IS a kernel argument! APM, at least in my setup, +causes the kernel to crash when loaded via loadlin (but NOT when loaded via +LILO). The APM stuff COULD be forced out of the kernel via the kernel compile +options. Instead, I got an unofficial patch to the APM drivers that allows them +to be dynamically deactivated via kernel arguments. Whatever you chose to +document, APM, it seems, MUST be off for setups like mine. + +Now make sure C:\MWW\MWCONFIG.REF looks like this: + +---------------------- +[NativeDOS] +Default=SB1.5 +SBInputSource=CD +SYNTH=FM +QSound=OFF +Reverb=OFF +Chorus=OFF +ReverbDepth=5 +ChorusDepth=5 +SBInputVolume=5 +SBMainVolume=10 +SBWaveVolume=10 +SBSynthVolume=10 +WaveTableVolume=10 +AudioPowerDriver=ON + +[FastCFG] +Show=No +HideOption=Off +----------------------------- + +OR the Default= line COULD be + +Default=SBPRO + +Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure +the sound modules and voilà - ThinkPad sound with Linux. + +Now the gotchas - you can either have CD sound OR Mixers but not both. That's a +problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why +this is! + +For some reason MPEG3 files, when played through mpg123, sound like they +are playing at 1/8th speed - not very useful! If you have ANY insight +on why this second thing might be happening, I would be grateful. + +=========================================================== + _/ _/_/_/_/ + _/_/ _/_/ _/ + _/ _/_/ _/_/_/_/ Martin John Bartlett + _/ _/ _/ _/ (martin@nitram.demon.co.uk) +_/ _/_/_/_/ + _/ +_/ _/ + _/_/ +=========================================================== diff -urN linux-2.5.6-pre3/Documentation/sound/oss/solo1 linux-2.5.6/Documentation/sound/oss/solo1 --- linux-2.5.6-pre3/Documentation/sound/oss/solo1 Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/solo1 Thu Mar 7 18:24:41 2002 @@ -0,0 +1,70 @@ +Recording +--------- + +Recording does not work on the author's card, but there +is at least one report of it working on later silicon. +The chip behaves differently than described in the data sheet, +likely due to a chip bug. Working around this would require +the help of ESS (for example by publishing an errata sheet), +but ESS has not done so so far. + +Also, the chip only supports 24 bit addresses for recording, +which means it cannot work on some Alpha mainboards. + + +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (or later, available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card has an OPL compatible FM synthesizer. + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/oss/sonicvibes linux-2.5.6/Documentation/sound/oss/sonicvibes --- linux-2.5.6-pre3/Documentation/sound/oss/sonicvibes Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/sonicvibes Thu Mar 7 18:24:41 2002 @@ -0,0 +1,81 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card both has an OPL compatible FM synthesizer as well as +a wavetable synthesizer. + +I haven't managed so far to get the OPL synth running. + +Using the wavetable synthesizer requires allocating +1-4MB of physically contiguous memory, which isn't possible +currently on Linux without ugly hacks like the bigphysarea +patch. Therefore, the driver doesn't support wavetable +synthesis. + + +No support from S3 +------------------ + +I do not get any support from S3. Therefore, the driver +still has many problems. For example, although the manual +states that the chip should be able to access the sample +buffer anywhere in 32bit address space, I haven't managed to +get it working with buffers above 16M. Therefore, the card +has the same disadvantages as ISA soundcards. + +Given that the card is also very noisy, and if you haven't +already bought it, you should strongly opt for one of the +comparatively priced Ensoniq products. + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/oss/ultrasound linux-2.5.6/Documentation/sound/oss/ultrasound --- linux-2.5.6-pre3/Documentation/sound/oss/ultrasound Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/ultrasound Thu Mar 7 18:24:41 2002 @@ -0,0 +1,30 @@ +modprobe sound +insmod ad1848 +insmod gus io=* irq=* dma=* ... + +This loads the driver for the Gravis Ultrasound family of sound cards. + +The gus module takes the following arguments + +io I/O address of the Ultrasound card (eg. io=0x220) +irq IRQ of the Sound Blaster card +dma DMA channel for the Sound Blaster +dma16 2nd DMA channel, only needed for full duplex operation +type 1 for PnP card +gus16 1 for using 16 bit sampling daughter board +no_wave_dma Set to disable DMA usage for wavetable (see note) +db16 ??? + + +no_wave_dma option + +This option defaults to a value of 0, which allows the Ultrasound wavetable +DSP to use DMA for for playback and downloading samples. This is the same +as the old behaviour. If set to 1, no DMA is needed for downloading samples, +and allows owners of a GUS MAX to make use of simultaneous digital audio +(/dev/dsp), MIDI, and wavetable playback. + + +If you have problems in recording with GUS MAX, you could try to use +just one 8 bit DMA channel. Recording will not work with one DMA +channel if it's a 16 bit one. diff -urN linux-2.5.6-pre3/Documentation/sound/oss/vwsnd linux-2.5.6/Documentation/sound/oss/vwsnd --- linux-2.5.6-pre3/Documentation/sound/oss/vwsnd Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/Documentation/sound/oss/vwsnd Thu Mar 7 18:24:41 2002 @@ -0,0 +1,293 @@ +vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual +Workstations' onboard audio. + +Copyright 1999 Silicon Graphics, Inc. All rights reserved. + + +At the time of this writing, March 1999, there are two models of +Visual Workstation, the 320 and the 540. This document only describes +those models. Future Visual Workstation models may have different +sound capabilities, and this driver will probably not work on those +boxes. + +The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio +codec chip. The AD1843 is accessed through the Cobalt I/O ASIC, also +known as Lithium. This driver programs both both chips. + +============================================================================== +QUICK CONFIGURATION + + # insmod soundcore + # insmod vwsnd + +============================================================================== +I/O CONNECTIONS + +On the Visual Workstation, only three of the AD1843 inputs are hooked +up. The analog line in jacks are connected to the AD1843's AUX1 +input. The CD audio lines are connected to the AD1843's AUX2 input. +The microphone jack is connected to the AD1843's MIC input. The mic +jack is mono, but the signal is delivered to both the left and right +MIC inputs. You can record in stereo from the mic input, but you will +get the same signal on both channels (within the limits of A/D +accuracy). Full scale on the Line input is +/- 2.0 V. Full scale on +the MIC input is 20 dB less, or +/- 0.2 V. + +The AD1843's LOUT1 outputs are connected to the Line Out jacks. The +AD1843's HPOUT outputs are connected to the speaker/headphone jack. +LOUT2 is not connected. Line out's maximum level is +/- 2.0 V peak to +peak. The speaker/headphone out's maximum is +/- 4.0 V peak to peak. + +The AD1843's PCM input channel and one of its output channels (DAC1) +are connected to Lithium. The other output channel (DAC2) is not +connected. + +============================================================================== +CAPABILITIES + +The AD1843 has PCM input and output (Pulse Code Modulation, also known +as wavetable). PCM input and output can be mono or stereo in any of +four formats. The formats are 16 bit signed and 8 bit unsigned, +u-Law, and A-Law format. Any sample rate from 4 KHz to 49 KHz is +available, in 1 Hz increments. + +The AD1843 includes an analog mixer that can mix all three input +signals (line, mic and CD) into the analog outputs. The mixer has a +separate gain control and mute switch for each input. + +There are two outputs, line out and speaker/headphone out. They +always produce the same signal, and the speaker always has 3 dB more +gain than the line out. The speaker/headphone output can be muted, +but this driver does not export that function. + +The hardware can sync audio to the video clock, but this driver does +not have a way to specify syncing to video. + +============================================================================== +PROGRAMMING + +This section explains the API supported by the driver. Also see the +Open Sound Programming Guide at http://www.opensound.com/pguide/ . +This section assumes familiarity with that document. + +The driver has two interfaces, an I/O interface and a mixer interface. +There is no MIDI or sequencer capability. + +============================================================================== +PROGRAMMING PCM I/O + +The I/O interface is usually accessed as /dev/audio or /dev/dsp. +Using the standard Open Sound System (OSS) ioctl calls, the sample +rate, number of channels, and sample format may be set within the +limitations described above. The driver supports triggering. It also +supports getting the input and output pointers with one-sample +accuracy. + +The SNDCTL_DSP_GETCAP ioctl returns these capabilities. + + DSP_CAP_DUPLEX - driver supports full duplex. + + DSP_CAP_TRIGGER - driver supports triggering. + + DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR + and SNDCTL_DSP_GETOPTR are accurate to a few samples. + +Memory mapping (mmap) is not implemented. + +The driver permits subdivided fragment sizes from 64 to 4096 bytes. +The number of fragments can be anything from 3 fragments to however +many fragments fit into 124 kilobytes. It is up to the user to +determine how few/small fragments can be used without introducing +glitches with a given workload. Linux is not realtime, so we can't +promise anything. (sigh...) + +When this driver is switched into or out of mu-Law or A-Law mode on +output, it may produce an audible click. This is unavoidable. To +prevent clicking, use signed 16-bit mode instead, and convert from +mu-Law or A-Law format in software. + +============================================================================== +PROGRAMMING THE MIXER INTERFACE + +The mixer interface is usually accessed as /dev/mixer. It is accessed +through ioctls. The mixer allows the application to control gain or +mute several audio signal paths, and also allows selection of the +recording source. + +Each of the constants described here can be read using the +MIXER_READ(SOUND_MIXER_xxx) ioctl. Those that are not read-only can +also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl. In most +cases, defines constants SOUND_MIXER_READ_xxx and +SOUND_MIXER_WRITE_xxx which work just as well. + +SOUND_MIXER_CAPS Read-only + +This is a mask of optional driver capabilities that are implemented. +This driver's only capability is SOUND_CAP_EXCL_INPUT, which means +that only one recording source can be active at a time. + +SOUND_MIXER_DEVMASK Read-only + +This is a mask of the sound channels. This driver's channels are PCM, +LINE, MIC, CD, and RECLEV. + +SOUND_MIXER_STEREODEVS Read-only + +This is a mask of which sound channels are capable of stereo. All +channels are capable of stereo. (But see caveat on MIC input in I/O +CONNECTIONS section above). + +SOUND_MIXER_OUTMASK Read-only + +This is a mask of channels that route inputs through to outputs. +Those are LINE, MIC, and CD. + +SOUND_MIXER_RECMASK Read-only + +This is a mask of channels that can be recording sources. Those are +PCM, LINE, MIC, CD. + +SOUND_MIXER_PCM Default: 0x5757 (0 dB) + +This is the gain control for PCM output. The left and right channel +gain are controlled independently. This gain control has 64 levels, +which range from -82.5 dB to +12.0 dB in 1.5 dB steps. Those 64 +levels are mapped onto 100 levels at the ioctl, see below. + +SOUND_MIXER_LINE Default: 0x4a4a (0 dB) + +This is the gain control for mixing the Line In source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_MIC Default: 0x4a4a (0 dB) + +This is the gain control for mixing the MIC source into the outputs. +The left and right channel gain are controlled independently. This +gain control has 32 levels, which range from -34.5 dB to +12.0 dB in +1.5 dB steps. Those 32 levels are mapped onto 100 levels at the +ioctl, see below. + +SOUND_MIXER_CD Default: 0x4a4a (0 dB) + +This is the gain control for mixing the CD audio source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_RECLEV Default: 0 (0 dB) + +This is the gain control for PCM input (RECording LEVel). The left +and right channel gain are controlled independently. This gain +control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB +steps. Those 16 levels are mapped onto 100 levels at the ioctl, see +below. + +SOUND_MIXER_RECSRC Default: SOUND_MASK_LINE + +This is a mask of currently selected PCM input sources (RECording +SouRCes). Because the AD1843 can only have a single recording source +at a time, only one bit at a time can be set in this mask. The +allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC, +or SOUND_MASK_CD. Selecting SOUND_MASK_PCM sets up internal +resampling which is useful for loopback testing and for hardware +sample rate conversion. But software sample rate conversion is +probably faster, so I don't know how useful that is. + +SOUND_MIXER_OUTSRC DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD + +This is a mask of sources that are currently passed through to the +outputs. Those sources whose bits are not set are muted. + +============================================================================== +GAIN CONTROL + +There are five gain controls listed above. Each has 16, 32, or 64 +steps. Each control has 1.5 dB of gain per step. Each control is +stereo. + +The OSS defines the argument to a channel gain ioctl as having two +components, left and right, each of which ranges from 0 to 100. The +two components are packed into the same word, with the left side gain +in the least significant byte, and the right side gain in the second +least significant byte. In C, we would say this. + + #include + + ... + + assert(leftgain >= 0 && leftgain <= 100); + assert(rightgain >= 0 && rightgain <= 100); + arg = leftgain | rightgain << 8; + +So each OSS gain control has 101 steps. But the hardware has 16, 32, +or 64 steps. The hardware steps are spread across the 101 OSS steps +nearly evenly. The conversion formulas are like this, given N equals +16, 32, or 64. + + int round = N/2 - 1; + OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1); + hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100; + +Here is a snippet of C code that will return the left and right gain +of any channel in dB. Pass it one of the predefined gain_desc_t +structures to access any of the five channels' gains. + + typedef struct gain_desc { + float min_gain; + float gain_step; + int nbits; + int chan; + } gain_desc_t; + + const gain_desc_t gain_pcm = { -82.5, 1.5, 6, SOUND_MIXER_PCM }; + const gain_desc_t gain_line = { -34.5, 1.5, 5, SOUND_MIXER_LINE }; + const gain_desc_t gain_mic = { -34.5, 1.5, 5, SOUND_MIXER_MIC }; + const gain_desc_t gain_cd = { -34.5, 1.5, 5, SOUND_MIXER_CD }; + const gain_desc_t gain_reclev = { 0.0, 1.5, 4, SOUND_MIXER_RECLEV }; + + int get_gain_dB(int fd, const gain_desc_t *gp, + float *left, float *right) + { + int word; + int lg, rg; + int mask = (1 << gp->nbits) - 1; + + if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0) + return -1; /* fail */ + lg = word & 0xFF; + rg = word >> 8 & 0xFF; + lg = (lg * mask + mask / 2) / 100; + rg = (rg * mask + mask / 2) / 100; + *left = gp->min_gain + gp->gain_step * lg; + *right = gp->min_gain + gp->gain_step * rg; + return 0; + } + +And here is the corresponding routine to set a channel's gain in dB. + + int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right) + { + float max_gain = + gp->min_gain + (1 << gp->nbits) * gp->gain_step; + float round = gp->gain_step / 2; + int mask = (1 << gp->nbits) - 1; + int word; + int lg, rg; + + if (left < gp->min_gain || right < gp->min_gain) + return EINVAL; + lg = (left - gp->min_gain + round) / gp->gain_step; + rg = (right - gp->min_gain + round) / gp->gain_step; + if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits)) + return EINVAL; + lg = (100 * lg + mask / 2) / mask; + rg = (100 * rg + mask / 2) / mask; + word = lg | rg << 8; + + return ioctl(fd, MIXER_WRITE(gp->chan), &word); + } + diff -urN linux-2.5.6-pre3/Documentation/sound/solo1 linux-2.5.6/Documentation/sound/solo1 --- linux-2.5.6-pre3/Documentation/sound/solo1 Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/Documentation/sound/solo1 Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -Recording ---------- - -Recording does not work on the author's card, but there -is at least one report of it working on later silicon. -The chip behaves differently than described in the data sheet, -likely due to a chip bug. Working around this would require -the help of ESS (for example by publishing an errata sheet), -but ESS has not done so so far. - -Also, the chip only supports 24 bit addresses for recording, -which means it cannot work on some Alpha mainboards. - - -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (or later, available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card has an OPL compatible FM synthesizer. - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/sonicvibes linux-2.5.6/Documentation/sound/sonicvibes --- linux-2.5.6-pre3/Documentation/sound/sonicvibes Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/Documentation/sound/sonicvibes Wed Dec 31 16:00:00 1969 @@ -1,81 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card both has an OPL compatible FM synthesizer as well as -a wavetable synthesizer. - -I haven't managed so far to get the OPL synth running. - -Using the wavetable synthesizer requires allocating -1-4MB of physically contiguous memory, which isn't possible -currently on Linux without ugly hacks like the bigphysarea -patch. Therefore, the driver doesn't support wavetable -synthesis. - - -No support from S3 ------------------- - -I do not get any support from S3. Therefore, the driver -still has many problems. For example, although the manual -states that the chip should be able to access the sample -buffer anywhere in 32bit address space, I haven't managed to -get it working with buffers above 16M. Therefore, the card -has the same disadvantages as ISA soundcards. - -Given that the card is also very noisy, and if you haven't -already bought it, you should strongly opt for one of the -comparatively priced Ensoniq products. - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff -urN linux-2.5.6-pre3/Documentation/sound/ultrasound linux-2.5.6/Documentation/sound/ultrasound --- linux-2.5.6-pre3/Documentation/sound/ultrasound Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/Documentation/sound/ultrasound Wed Dec 31 16:00:00 1969 @@ -1,30 +0,0 @@ -modprobe sound -insmod ad1848 -insmod gus io=* irq=* dma=* ... - -This loads the driver for the Gravis Ultrasound family of sound cards. - -The gus module takes the following arguments - -io I/O address of the Ultrasound card (eg. io=0x220) -irq IRQ of the Sound Blaster card -dma DMA channel for the Sound Blaster -dma16 2nd DMA channel, only needed for full duplex operation -type 1 for PnP card -gus16 1 for using 16 bit sampling daughter board -no_wave_dma Set to disable DMA usage for wavetable (see note) -db16 ??? - - -no_wave_dma option - -This option defaults to a value of 0, which allows the Ultrasound wavetable -DSP to use DMA for for playback and downloading samples. This is the same -as the old behaviour. If set to 1, no DMA is needed for downloading samples, -and allows owners of a GUS MAX to make use of simultaneous digital audio -(/dev/dsp), MIDI, and wavetable playback. - - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. diff -urN linux-2.5.6-pre3/Documentation/sound/vwsnd linux-2.5.6/Documentation/sound/vwsnd --- linux-2.5.6-pre3/Documentation/sound/vwsnd Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/Documentation/sound/vwsnd Wed Dec 31 16:00:00 1969 @@ -1,293 +0,0 @@ -vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual -Workstations' onboard audio. - -Copyright 1999 Silicon Graphics, Inc. All rights reserved. - - -At the time of this writing, March 1999, there are two models of -Visual Workstation, the 320 and the 540. This document only describes -those models. Future Visual Workstation models may have different -sound capabilities, and this driver will probably not work on those -boxes. - -The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio -codec chip. The AD1843 is accessed through the Cobalt I/O ASIC, also -known as Lithium. This driver programs both both chips. - -============================================================================== -QUICK CONFIGURATION - - # insmod soundcore - # insmod vwsnd - -============================================================================== -I/O CONNECTIONS - -On the Visual Workstation, only three of the AD1843 inputs are hooked -up. The analog line in jacks are connected to the AD1843's AUX1 -input. The CD audio lines are connected to the AD1843's AUX2 input. -The microphone jack is connected to the AD1843's MIC input. The mic -jack is mono, but the signal is delivered to both the left and right -MIC inputs. You can record in stereo from the mic input, but you will -get the same signal on both channels (within the limits of A/D -accuracy). Full scale on the Line input is +/- 2.0 V. Full scale on -the MIC input is 20 dB less, or +/- 0.2 V. - -The AD1843's LOUT1 outputs are connected to the Line Out jacks. The -AD1843's HPOUT outputs are connected to the speaker/headphone jack. -LOUT2 is not connected. Line out's maximum level is +/- 2.0 V peak to -peak. The speaker/headphone out's maximum is +/- 4.0 V peak to peak. - -The AD1843's PCM input channel and one of its output channels (DAC1) -are connected to Lithium. The other output channel (DAC2) is not -connected. - -============================================================================== -CAPABILITIES - -The AD1843 has PCM input and output (Pulse Code Modulation, also known -as wavetable). PCM input and output can be mono or stereo in any of -four formats. The formats are 16 bit signed and 8 bit unsigned, -u-Law, and A-Law format. Any sample rate from 4 KHz to 49 KHz is -available, in 1 Hz increments. - -The AD1843 includes an analog mixer that can mix all three input -signals (line, mic and CD) into the analog outputs. The mixer has a -separate gain control and mute switch for each input. - -There are two outputs, line out and speaker/headphone out. They -always produce the same signal, and the speaker always has 3 dB more -gain than the line out. The speaker/headphone output can be muted, -but this driver does not export that function. - -The hardware can sync audio to the video clock, but this driver does -not have a way to specify syncing to video. - -============================================================================== -PROGRAMMING - -This section explains the API supported by the driver. Also see the -Open Sound Programming Guide at http://www.opensound.com/pguide/ . -This section assumes familiarity with that document. - -The driver has two interfaces, an I/O interface and a mixer interface. -There is no MIDI or sequencer capability. - -============================================================================== -PROGRAMMING PCM I/O - -The I/O interface is usually accessed as /dev/audio or /dev/dsp. -Using the standard Open Sound System (OSS) ioctl calls, the sample -rate, number of channels, and sample format may be set within the -limitations described above. The driver supports triggering. It also -supports getting the input and output pointers with one-sample -accuracy. - -The SNDCTL_DSP_GETCAP ioctl returns these capabilities. - - DSP_CAP_DUPLEX - driver supports full duplex. - - DSP_CAP_TRIGGER - driver supports triggering. - - DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR - and SNDCTL_DSP_GETOPTR are accurate to a few samples. - -Memory mapping (mmap) is not implemented. - -The driver permits subdivided fragment sizes from 64 to 4096 bytes. -The number of fragments can be anything from 3 fragments to however -many fragments fit into 124 kilobytes. It is up to the user to -determine how few/small fragments can be used without introducing -glitches with a given workload. Linux is not realtime, so we can't -promise anything. (sigh...) - -When this driver is switched into or out of mu-Law or A-Law mode on -output, it may produce an audible click. This is unavoidable. To -prevent clicking, use signed 16-bit mode instead, and convert from -mu-Law or A-Law format in software. - -============================================================================== -PROGRAMMING THE MIXER INTERFACE - -The mixer interface is usually accessed as /dev/mixer. It is accessed -through ioctls. The mixer allows the application to control gain or -mute several audio signal paths, and also allows selection of the -recording source. - -Each of the constants described here can be read using the -MIXER_READ(SOUND_MIXER_xxx) ioctl. Those that are not read-only can -also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl. In most -cases, defines constants SOUND_MIXER_READ_xxx and -SOUND_MIXER_WRITE_xxx which work just as well. - -SOUND_MIXER_CAPS Read-only - -This is a mask of optional driver capabilities that are implemented. -This driver's only capability is SOUND_CAP_EXCL_INPUT, which means -that only one recording source can be active at a time. - -SOUND_MIXER_DEVMASK Read-only - -This is a mask of the sound channels. This driver's channels are PCM, -LINE, MIC, CD, and RECLEV. - -SOUND_MIXER_STEREODEVS Read-only - -This is a mask of which sound channels are capable of stereo. All -channels are capable of stereo. (But see caveat on MIC input in I/O -CONNECTIONS section above). - -SOUND_MIXER_OUTMASK Read-only - -This is a mask of channels that route inputs through to outputs. -Those are LINE, MIC, and CD. - -SOUND_MIXER_RECMASK Read-only - -This is a mask of channels that can be recording sources. Those are -PCM, LINE, MIC, CD. - -SOUND_MIXER_PCM Default: 0x5757 (0 dB) - -This is the gain control for PCM output. The left and right channel -gain are controlled independently. This gain control has 64 levels, -which range from -82.5 dB to +12.0 dB in 1.5 dB steps. Those 64 -levels are mapped onto 100 levels at the ioctl, see below. - -SOUND_MIXER_LINE Default: 0x4a4a (0 dB) - -This is the gain control for mixing the Line In source into the -outputs. The left and right channel gain are controlled -independently. This gain control has 32 levels, which range from --34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto -100 levels at the ioctl, see below. - -SOUND_MIXER_MIC Default: 0x4a4a (0 dB) - -This is the gain control for mixing the MIC source into the outputs. -The left and right channel gain are controlled independently. This -gain control has 32 levels, which range from -34.5 dB to +12.0 dB in -1.5 dB steps. Those 32 levels are mapped onto 100 levels at the -ioctl, see below. - -SOUND_MIXER_CD Default: 0x4a4a (0 dB) - -This is the gain control for mixing the CD audio source into the -outputs. The left and right channel gain are controlled -independently. This gain control has 32 levels, which range from --34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto -100 levels at the ioctl, see below. - -SOUND_MIXER_RECLEV Default: 0 (0 dB) - -This is the gain control for PCM input (RECording LEVel). The left -and right channel gain are controlled independently. This gain -control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB -steps. Those 16 levels are mapped onto 100 levels at the ioctl, see -below. - -SOUND_MIXER_RECSRC Default: SOUND_MASK_LINE - -This is a mask of currently selected PCM input sources (RECording -SouRCes). Because the AD1843 can only have a single recording source -at a time, only one bit at a time can be set in this mask. The -allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC, -or SOUND_MASK_CD. Selecting SOUND_MASK_PCM sets up internal -resampling which is useful for loopback testing and for hardware -sample rate conversion. But software sample rate conversion is -probably faster, so I don't know how useful that is. - -SOUND_MIXER_OUTSRC DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD - -This is a mask of sources that are currently passed through to the -outputs. Those sources whose bits are not set are muted. - -============================================================================== -GAIN CONTROL - -There are five gain controls listed above. Each has 16, 32, or 64 -steps. Each control has 1.5 dB of gain per step. Each control is -stereo. - -The OSS defines the argument to a channel gain ioctl as having two -components, left and right, each of which ranges from 0 to 100. The -two components are packed into the same word, with the left side gain -in the least significant byte, and the right side gain in the second -least significant byte. In C, we would say this. - - #include - - ... - - assert(leftgain >= 0 && leftgain <= 100); - assert(rightgain >= 0 && rightgain <= 100); - arg = leftgain | rightgain << 8; - -So each OSS gain control has 101 steps. But the hardware has 16, 32, -or 64 steps. The hardware steps are spread across the 101 OSS steps -nearly evenly. The conversion formulas are like this, given N equals -16, 32, or 64. - - int round = N/2 - 1; - OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1); - hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100; - -Here is a snippet of C code that will return the left and right gain -of any channel in dB. Pass it one of the predefined gain_desc_t -structures to access any of the five channels' gains. - - typedef struct gain_desc { - float min_gain; - float gain_step; - int nbits; - int chan; - } gain_desc_t; - - const gain_desc_t gain_pcm = { -82.5, 1.5, 6, SOUND_MIXER_PCM }; - const gain_desc_t gain_line = { -34.5, 1.5, 5, SOUND_MIXER_LINE }; - const gain_desc_t gain_mic = { -34.5, 1.5, 5, SOUND_MIXER_MIC }; - const gain_desc_t gain_cd = { -34.5, 1.5, 5, SOUND_MIXER_CD }; - const gain_desc_t gain_reclev = { 0.0, 1.5, 4, SOUND_MIXER_RECLEV }; - - int get_gain_dB(int fd, const gain_desc_t *gp, - float *left, float *right) - { - int word; - int lg, rg; - int mask = (1 << gp->nbits) - 1; - - if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0) - return -1; /* fail */ - lg = word & 0xFF; - rg = word >> 8 & 0xFF; - lg = (lg * mask + mask / 2) / 100; - rg = (rg * mask + mask / 2) / 100; - *left = gp->min_gain + gp->gain_step * lg; - *right = gp->min_gain + gp->gain_step * rg; - return 0; - } - -And here is the corresponding routine to set a channel's gain in dB. - - int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right) - { - float max_gain = - gp->min_gain + (1 << gp->nbits) * gp->gain_step; - float round = gp->gain_step / 2; - int mask = (1 << gp->nbits) - 1; - int word; - int lg, rg; - - if (left < gp->min_gain || right < gp->min_gain) - return EINVAL; - lg = (left - gp->min_gain + round) / gp->gain_step; - rg = (right - gp->min_gain + round) / gp->gain_step; - if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits)) - return EINVAL; - lg = (100 * lg + mask / 2) / mask; - rg = (100 * rg + mask / 2) / mask; - word = lg | rg << 8; - - return ioctl(fd, MIXER_WRITE(gp->chan), &word); - } - diff -urN linux-2.5.6-pre3/MAINTAINERS linux-2.5.6/MAINTAINERS --- linux-2.5.6-pre3/MAINTAINERS Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/MAINTAINERS Thu Mar 7 18:24:42 2002 @@ -1722,12 +1722,12 @@ S: Maintained USB SERIAL KEYSPAN DRIVER -P: Hugh Blemings -M: hugh@misc.nu +P: Greg Kroah-Hartman +M: greg@kroah.com L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +W: http://www.kroah.com/linux/ S: Maintained -W: http://misc.nu/hugh/keyspan/ USB SUBSYSTEM P: Greg Kroah-Hartman diff -urN linux-2.5.6-pre3/Makefile linux-2.5.6/Makefile --- linux-2.5.6-pre3/Makefile Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/Makefile Thu Mar 7 18:24:42 2002 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 6 -EXTRAVERSION =-pre3 +EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -162,6 +162,7 @@ DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o DRIVERS-$(CONFIG_NET_PCMCIA) += drivers/net/pcmcia/pcmcia_net.o DRIVERS-$(CONFIG_NET_WIRELESS) += drivers/net/wireless/wireless_net.o +DRIVERS-$(CONFIG_NET_TULIP) += drivers/net/tulip/tulip_net.o DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus_all.o diff -urN linux-2.5.6-pre3/arch/alpha/Makefile linux-2.5.6/arch/alpha/Makefile --- linux-2.5.6-pre3/arch/alpha/Makefile Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/alpha/Makefile Thu Mar 7 18:24:42 2002 @@ -45,22 +45,22 @@ CFLAGS := $(CFLAGS) -mcpu=pca56 mcpu_done := y endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_PYXIS),ny) - CFLAGS := $(CFLAGS) -mcpu=ev56 - mcpu_done := y - endif - ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS),ny) - ifeq ($(have_mcpu_pca56),y) - CFLAGS := $(CFLAGS) -mcpu=pca56 - else - CFLAGS := $(CFLAGS) -mcpu=ev56 - endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS)$(have_mcpu_pca56),nyy) + CFLAGS := $(CFLAGS) -mcpu=pca56 mcpu_done := y endif ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),ny) CFLAGS := $(CFLAGS) -mcpu=ev4 mcpu_done := y endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV56),ny) + CFLAGS := $(CFLAGS) -mcpu=ev56 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV5),ny) + CFLAGS := $(CFLAGS) -mcpu=ev5 + mcpu_done := y + endif ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV67)$(have_mcpu_ev67),nyy) CFLAGS := $(CFLAGS) -mcpu=ev67 mcpu_done := y diff -urN linux-2.5.6-pre3/arch/alpha/config.in linux-2.5.6/arch/alpha/config.in --- linux-2.5.6-pre3/arch/alpha/config.in Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/arch/alpha/config.in Thu Mar 7 18:24:42 2002 @@ -88,19 +88,43 @@ then define_bool CONFIG_ALPHA_EB64P y fi -if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ - -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_TAKARA" = "y" ] +if [ "$CONFIG_ALPHA_ALCOR" = "y" ] then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y + bool 'EV56 CPU (speed >= 366MHz)?' CONFIG_ALPHA_EV56 fi -if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +if [ "$CONFIG_ALPHA_EB164" = "y" ] +then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y +fi +if [ "$CONFIG_ALPHA_PC164" = "y" -o "$CONFIG_ALPHA_TAKARA" = "y" ] +then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y + define_bool CONFIG_ALPHA_CIA y +fi +if [ "$CONFIG_ALPHA_MIKASA" = "y" ] +then + bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO + if [ "$CONFIG_ALPHA_PRIMO" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y + else + define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_APECS y + fi +fi +if [ "$CONFIG_ALPHA_NORITAKE" = "y" ] then bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO if [ "$CONFIG_ALPHA_PRIMO" = "y" ] then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y + bool 'EV56 CPU (speed >= 333MHz)?' CONFIG_ALPHA_EV56 else define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_APECS y @@ -121,6 +145,7 @@ -o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ] then define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y define_bool CONFIG_ALPHA_CIA y define_bool CONFIG_ALPHA_PYXIS y fi @@ -145,10 +170,12 @@ then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_MCPCIA y + bool 'EV56 CPU (speed >= 400MHz)?' CONFIG_ALPHA_EV56 fi if [ "$CONFIG_ALPHA_RX164" = "y" ] then define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_EV56 y define_bool CONFIG_ALPHA_POLARIS y fi if [ "$CONFIG_ALPHA_JENSEN" = "y" ] diff -urN linux-2.5.6-pre3/arch/alpha/kernel/core_irongate.c linux-2.5.6/arch/alpha/kernel/core_irongate.c --- linux-2.5.6-pre3/arch/alpha/kernel/core_irongate.c Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/arch/alpha/kernel/core_irongate.c Thu Mar 7 18:24:42 2002 @@ -455,7 +455,7 @@ if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; irongate_remap_area_pte(pte, address, end - address, diff -urN linux-2.5.6-pre3/arch/alpha/kernel/process.c linux-2.5.6/arch/alpha/kernel/process.c --- linux-2.5.6-pre3/arch/alpha/kernel/process.c Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/alpha/kernel/process.c Thu Mar 7 18:24:42 2002 @@ -64,7 +64,6 @@ while (!need_resched()) barrier(); schedule(); - check_pgt_cache(); } } diff -urN linux-2.5.6-pre3/arch/alpha/mm/init.c linux-2.5.6/arch/alpha/mm/init.c --- linux-2.5.6-pre3/arch/alpha/mm/init.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/arch/alpha/mm/init.c Thu Mar 7 18:24:42 2002 @@ -42,12 +42,8 @@ static struct pcb_struct original_pcb; -#ifndef CONFIG_SMP -struct pgtable_cache_struct quicklists; -#endif - pgd_t * -get_pgd_slow(void) +pgd_alloc(struct mm_struct *mm) { pgd_t *ret, *init; @@ -69,28 +65,26 @@ return ret; } -int do_check_pgt_cache(int low, int high) +pte_t * +pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - int freed = 0; - if(pgtable_cache_size > high) { - do { - if(pgd_quicklist) { - free_pgd_slow(get_pgd_fast()); - freed++; - } - if(pmd_quicklist) { - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); - freed++; - } - if(pte_quicklist) { - pte_free_slow(pte_alloc_one_fast(NULL, 0)); - freed++; - } - } while(pgtable_cache_size > low); + pte_t *pte; + long timeout = 10; + + retry: + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pte) + clear_page(pte); + else if (--timeout >= 0) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + goto retry; } - return freed; + + return pte; } + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -145,7 +139,6 @@ printk("%ld reserved pages\n",reserved); printk("%ld pages shared\n",shared); printk("%ld pages swap cached\n",cached); - printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); } #endif @@ -260,7 +253,7 @@ unsigned long paddr = crb->map[i].pa; crb->map[i].va = vaddr; for (j = 0; j < crb->map[i].count; ++j) { - set_pte(pte_offset(pmd, vaddr), + set_pte(pte_offset_kernel(pmd, vaddr), mk_pte_phys(paddr, PAGE_KERNEL)); paddr += PAGE_SIZE; vaddr += PAGE_SIZE; diff -urN linux-2.5.6-pre3/arch/i386/defconfig linux-2.5.6/arch/i386/defconfig --- linux-2.5.6-pre3/arch/i386/defconfig Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/i386/defconfig Thu Mar 7 18:24:42 2002 @@ -435,12 +435,9 @@ # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set -# CONFIG_DE2104X is not set -# CONFIG_TULIP is not set -# CONFIG_DE4X5 is not set # CONFIG_DGRS is not set -# CONFIG_DM9102 is not set -CONFIG_EEPRO100=y +# CONFIG_EEPRO100 is not set +CONFIG_E100=y # CONFIG_LNE390 is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set @@ -459,7 +456,6 @@ # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set # CONFIG_VIA_RHINE_MMIO is not set -# CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set # @@ -473,6 +469,7 @@ # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set # CONFIG_FDDI is not set # CONFIG_PLIP is not set # CONFIG_PPP is not set @@ -495,6 +492,11 @@ # CONFIG_WAN is not set # +# "Tulip" family network device support +# +# CONFIG_NET_TULIP is not set + +# # PCMCIA network device support # CONFIG_NET_PCMCIA=y @@ -508,8 +510,6 @@ # CONFIG_PCMCIA_AXNET is not set # CONFIG_ARCNET_COM20020_CS is not set # CONFIG_PCMCIA_IBMTR is not set -# CONFIG_PCMCIA_XIRCOM is not set -# CONFIG_PCMCIA_XIRTULIP is not set CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y # CONFIG_AIRONET4500_CS is not set diff -urN linux-2.5.6-pre3/arch/i386/kernel/Makefile linux-2.5.6/arch/i386/kernel/Makefile --- linux-2.5.6-pre3/arch/i386/kernel/Makefile Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/arch/i386/kernel/Makefile Thu Mar 7 18:24:42 2002 @@ -14,7 +14,7 @@ O_TARGET := kernel.o -export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o +export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o time.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ diff -urN linux-2.5.6-pre3/arch/i386/kernel/apm.c linux-2.5.6/arch/i386/kernel/apm.c --- linux-2.5.6-pre3/arch/i386/kernel/apm.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/arch/i386/kernel/apm.c Thu Mar 7 18:24:42 2002 @@ -812,7 +812,7 @@ t1 = IDLE_LEAKY_MAX; - while (need_resched()) { + while (!need_resched()) { if (use_apm_idle) { unsigned int t; diff -urN linux-2.5.6-pre3/arch/i386/kernel/mpparse.c linux-2.5.6/arch/i386/kernel/mpparse.c --- linux-2.5.6-pre3/arch/i386/kernel/mpparse.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/arch/i386/kernel/mpparse.c Thu Mar 7 18:24:42 2002 @@ -37,6 +37,8 @@ int apic_version [MAX_APICS]; int mp_bus_id_to_type [MAX_MP_BUSSES]; int mp_bus_id_to_node [MAX_MP_BUSSES]; +int mp_bus_id_to_local [MAX_MP_BUSSES]; +int quad_local_to_mp_bus_id [NR_CPUS/4][4]; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; int mp_current_pci_id; @@ -241,13 +243,17 @@ static void __init MP_bus_info (struct mpc_config_bus *m) { char str[7]; + int quad; memcpy(str, m->mpc_bustype, 6); str[6] = 0; if (clustered_apic_mode) { - mp_bus_id_to_node[m->mpc_busid] = translation_table[mpc_record]->trans_quad; - printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, mp_bus_id_to_node[m->mpc_busid]); + quad = translation_table[mpc_record]->trans_quad; + mp_bus_id_to_node[m->mpc_busid] = quad; + mp_bus_id_to_local[m->mpc_busid] = translation_table[mpc_record]->trans_local; + quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid; + printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, quad); } else { Dprintk("Bus #%d is %s\n", m->mpc_busid, str); } @@ -324,13 +330,14 @@ static void __init MP_translation_info (struct mpc_config_translation *m) { - printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, - m->trans_quad, m->trans_global, m->trans_local); + printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, m->trans_quad, m->trans_global, m->trans_local); if (mpc_record >= MAX_MPC_ENTRY) printk("MAX_MPC_ENTRY exceeded!\n"); else translation_table[mpc_record] = m; /* stash this for later */ + if (m->trans_quad+1 > numnodes) + numnodes = m->trans_quad+1; } /* @@ -495,10 +502,6 @@ } ++mpc_record; } - if (clustered_apic_mode && nr_ioapics > 2) { - /* don't initialise IO apics on secondary quads */ - nr_ioapics = 2; - } if (!num_processors) printk(KERN_ERR "SMP mptable: no processors registered!\n"); return num_processors; diff -urN linux-2.5.6-pre3/arch/i386/kernel/pci-pc.c linux-2.5.6/arch/i386/kernel/pci-pc.c --- linux-2.5.6-pre3/arch/i386/kernel/pci-pc.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/arch/i386/kernel/pci-pc.c Thu Mar 7 18:24:42 2002 @@ -14,6 +14,7 @@ #include #include +#include #include "pci-i386.h" @@ -26,6 +27,16 @@ int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL; int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL; +#ifdef CONFIG_MULTIQUAD +#define BUS2QUAD(global) (mp_bus_id_to_node[global]) +#define BUS2LOCAL(global) (mp_bus_id_to_local[global]) +#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) +#else +#define BUS2QUAD(global) (0) +#define BUS2LOCAL(global) (global) +#define QUADLOCAL2BUS(quad,local) (local) +#endif + /* * This interrupt-safe spinlock protects all accesses to PCI * configuration space. @@ -39,10 +50,71 @@ #ifdef CONFIG_PCI_DIRECT +#ifdef CONFIG_MULTIQUAD +#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ + (0x80000000 | (BUS2LOCAL(bus) << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) + +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* CONFIG_MULTIQUAD */ +{ + unsigned long flags; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + *value = inb_quad(0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + *value = inw_quad(0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + *value = inl_quad(0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* CONFIG_MULTIQUAD */ +{ + unsigned long flags; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + outb_quad((u8)value, 0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + outw_quad((u16)value, 0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + outl_quad((u32)value, 0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +#else /* !CONFIG_MULTIQUAD */ #define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) -static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* !CONFIG_MULTIQUAD */ { unsigned long flags; @@ -70,7 +142,7 @@ return 0; } -static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* !CONFIG_MULTIQUAD */ { unsigned long flags; @@ -98,6 +170,8 @@ return 0; } +#endif /* CONFIG_MULTIQUAD */ + #undef PCI_CONF1_ADDRESS static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) @@ -1017,6 +1091,8 @@ */ int pxb, reg; u8 busno, suba, subb; + int quad = BUS2QUAD(d->bus->number); + printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name); reg = 0xd0; for(pxb=0; pxb<2; pxb++) { @@ -1025,9 +1101,9 @@ pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus(busno, pci_root_ops, NULL); /* Bus A */ + pci_scan_bus(QUADLOCAL2BUS(quad,busno), pci_root_ops, NULL); /* Bus A */ if (suba < subb) - pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */ + pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), pci_root_ops, NULL); /* Bus B */ } pcibios_last_bus = -1; } @@ -1199,6 +1275,8 @@ void __init pcibios_init(void) { + int quad; + if (!pci_root_ops) pcibios_config_init(); if (!pci_root_ops) { @@ -1208,6 +1286,14 @@ printk("PCI: Probing PCI hardware\n"); pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL); + if (clustered_apic_mode && (numnodes > 1)) { + for (quad = 1; quad < numnodes; ++quad) { + printk("Scanning PCI bus %d for quad %d\n", + QUADLOCAL2BUS(quad,0), quad); + pci_scan_bus(QUADLOCAL2BUS(quad,0), + pci_root_ops, NULL); + } + } pcibios_irq_init(); pcibios_fixup_peer_bridges(); diff -urN linux-2.5.6-pre3/arch/i386/kernel/smpboot.c linux-2.5.6/arch/i386/kernel/smpboot.c --- linux-2.5.6-pre3/arch/i386/kernel/smpboot.c Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/i386/kernel/smpboot.c Thu Mar 7 18:24:42 2002 @@ -1012,11 +1012,14 @@ { int apicid, cpu, bit; - if (clustered_apic_mode) { - /* remap the 1st quad's 256k range for cross-quad I/O */ - xquad_portio = ioremap (XQUAD_PORTIO_BASE, XQUAD_PORTIO_LEN); - printk("Cross quad port I/O vaddr 0x%08lx, len %08lx\n", - (u_long) xquad_portio, (u_long) XQUAD_PORTIO_LEN); + if (clustered_apic_mode && (numnodes > 1)) { + printk("Remapping cross-quad port I/O for %d quads\n", + numnodes); + printk("xquad_portio vaddr 0x%08lx, len %08lx\n", + (u_long) xquad_portio, + (u_long) numnodes * XQUAD_PORTIO_LEN); + xquad_portio = ioremap (XQUAD_PORTIO_BASE, + numnodes * XQUAD_PORTIO_LEN); } #ifdef CONFIG_MTRR diff -urN linux-2.5.6-pre3/arch/i386/kernel/time.c linux-2.5.6/arch/i386/kernel/time.c --- linux-2.5.6-pre3/arch/i386/kernel/time.c Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/i386/kernel/time.c Thu Mar 7 18:24:42 2002 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include diff -urN linux-2.5.6-pre3/arch/i386/kernel/traps.c linux-2.5.6/arch/i386/kernel/traps.c --- linux-2.5.6-pre3/arch/i386/kernel/traps.c Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/arch/i386/kernel/traps.c Thu Mar 7 18:24:42 2002 @@ -557,6 +557,8 @@ * allowing programs to debug themselves without the ptrace() * interface. */ + if ((regs->xcs & 3) == 0) + goto clear_TF; if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) goto clear_TF; } diff -urN linux-2.5.6-pre3/arch/mips64/sgi-ip27/ip27-rtc.c linux-2.5.6/arch/mips64/sgi-ip27/ip27-rtc.c --- linux-2.5.6-pre3/arch/mips64/sgi-ip27/ip27-rtc.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/arch/mips64/sgi-ip27/ip27-rtc.c Thu Mar 7 18:24:42 2002 @@ -201,7 +201,8 @@ KL_CONFIG_CH_CONS_INFO(nid)->memory_base + IOC3_BYTEBUS_DEV0; printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION); - misc_register(&rtc_dev); + if (misc_register(&rtc_dev)) + return -ENODEV; create_proc_read_entry ("rtc", 0, NULL, rtc_read_proc, NULL); save_flags(flags); diff -urN linux-2.5.6-pre3/arch/ppc/iSeries/rtc.c linux-2.5.6/arch/ppc/iSeries/rtc.c --- linux-2.5.6-pre3/arch/ppc/iSeries/rtc.c Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/arch/ppc/iSeries/rtc.c Thu Mar 7 18:24:42 2002 @@ -192,7 +192,8 @@ static int __init rtc_init(void) { - misc_register(&rtc_dev); + if (misc_register(&rtc_dev)) + return -ENODEV; create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); diff -urN linux-2.5.6-pre3/arch/x86_64/kernel/entry.S linux-2.5.6/arch/x86_64/kernel/entry.S --- linux-2.5.6-pre3/arch/x86_64/kernel/entry.S Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/arch/x86_64/kernel/entry.S Thu Mar 7 18:24:42 2002 @@ -102,8 +102,6 @@ * A newly forked process directly context switches into this. */ ENTRY(ret_from_fork) - movq %rbx, %rdi - call schedule_tail GET_THREAD_INFO(%rcx) bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) jc rff_trace diff -urN linux-2.5.6-pre3/drivers/acorn/char/mouse_ps2.c linux-2.5.6/drivers/acorn/char/mouse_ps2.c --- linux-2.5.6-pre3/drivers/acorn/char/mouse_ps2.c Thu Mar 7 18:24:24 2002 +++ linux-2.5.6/drivers/acorn/char/mouse_ps2.c Thu Mar 7 18:24:42 2002 @@ -271,7 +271,8 @@ iomd_writeb(0, IOMD_MSECTL); iomd_writeb(8, IOMD_MSECTL); - misc_register(&psaux_mouse); + if (misc_register(&psaux_mouse)) + return -ENODEV; queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); memset(queue, 0, sizeof(*queue)); queue->head = queue->tail = 0; diff -urN linux-2.5.6-pre3/drivers/acorn/scsi/ecoscsi.c linux-2.5.6/drivers/acorn/scsi/ecoscsi.c --- linux-2.5.6-pre3/drivers/acorn/scsi/ecoscsi.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/drivers/acorn/scsi/ecoscsi.c Thu Mar 7 18:24:42 2002 @@ -125,7 +125,11 @@ } NCR5380_init(instance, 0); - request_region (instance->io_port, instance->n_io_port, "ecoscsi"); + if (request_region (instance->io_port, instance->n_io_port, "ecoscsi") == NULL) + { + scsi_unregister(instance); + return 0; + } if (instance->irq != IRQ_NONE) if (request_irq(instance->irq, do_ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) { diff -urN linux-2.5.6-pre3/drivers/char/acquirewdt.c linux-2.5.6/drivers/char/acquirewdt.c --- linux-2.5.6-pre3/drivers/char/acquirewdt.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/drivers/char/acquirewdt.c Thu Mar 7 18:24:43 2002 @@ -207,7 +207,8 @@ printk("WDT driver for Acquire single board computer initialising.\n"); spin_lock_init(&acq_lock); - misc_register(&acq_miscdev); + if (misc_register(&acq_miscdev)) + return -ENODEV; request_region(WDT_STOP, 1, "Acquire WDT"); request_region(WDT_START, 1, "Acquire WDT"); register_reboot_notifier(&acq_notifier); diff -urN linux-2.5.6-pre3/drivers/ide/ide.c linux-2.5.6/drivers/ide/ide.c --- linux-2.5.6-pre3/drivers/ide/ide.c Thu Mar 7 18:24:27 2002 +++ linux-2.5.6/drivers/ide/ide.c Thu Mar 7 18:24:43 2002 @@ -1435,7 +1435,7 @@ && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time)) && 0 < (signed long)((jiffies + t) - WAKEUP(drive))) { - ide_stall_queue(best, min(t, 10 * WAIT_MIN_SLEEP)); + ide_stall_queue(best, min(t, 10L * WAIT_MIN_SLEEP)); goto repeat; } } while ((drive = drive->next) != best); diff -urN linux-2.5.6-pre3/drivers/isdn/act2000/act2000_isa.c linux-2.5.6/drivers/isdn/act2000/act2000_isa.c --- linux-2.5.6-pre3/drivers/isdn/act2000/act2000_isa.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/drivers/isdn/act2000/act2000_isa.c Thu Mar 7 18:24:44 2002 @@ -178,7 +178,8 @@ card->flags &= ~ACT2000_FLAGS_PVALID; } if (!check_region(portbase, ISA_REGION)) { - request_region(portbase, ACT2000_PORTLEN, card->regname); + if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) + return -EIO; card->port = portbase; card->flags |= ACT2000_FLAGS_PVALID; return 0; diff -urN linux-2.5.6-pre3/drivers/net/3c503.c linux-2.5.6/drivers/net/3c503.c --- linux-2.5.6-pre3/drivers/net/3c503.c Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/3c503.c Thu Mar 7 18:24:44 2002 @@ -692,9 +692,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_EL2_CARDS) "i"); -MODULE_PARM_DESC(io, "EtherLink II I/O base address(es)"); -MODULE_PARM_DESC(irq, "EtherLink II IRQ number(s) (assigned)"); -MODULE_PARM_DESC(xcvr, "EtherLink II tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(xcvr, "tranceiver(s) (0=internal, 1=external)"); +MODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -742,7 +744,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/3c505.c linux-2.5.6/drivers/net/3c505.c --- linux-2.5.6-pre3/drivers/net/3c505.c Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/drivers/net/3c505.c Thu Mar 7 18:24:45 2002 @@ -1350,87 +1350,6 @@ } } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) -{ - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; -} - -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - - /****************************************************** * * initialise Etherlink Plus board diff -urN linux-2.5.6-pre3/drivers/net/3c509.c linux-2.5.6/drivers/net/3c509.c --- linux-2.5.6-pre3/drivers/net/3c509.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/drivers/net/3c509.c Thu Mar 7 18:24:45 2002 @@ -1103,14 +1103,15 @@ MODULE_PARM(irq,"1-8i"); MODULE_PARM(xcvr,"1-8i"); MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM_DESC(debug, "EtherLink III debug level (0-6)"); -MODULE_PARM_DESC(irq, "EtherLink III IRQ number(s) (assigned)"); -MODULE_PARM_DESC(xcvr,"EtherLink III tranceiver(s) (0=internal, 1=external)"); -MODULE_PARM_DESC(max_interrupt_work, "EtherLink III maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "debug level (0-6)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(xcvr,"tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); #ifdef __ISAPNP__ MODULE_PARM(nopnp, "i"); -MODULE_PARM_DESC(nopnp, "EtherLink III disable ISA PnP support (0-1)"); +MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)"); #endif /* __ISAPNP__ */ +MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B) ISA/PnP ethernet driver"); int init_module(void) diff -urN linux-2.5.6-pre3/drivers/net/8139cp.c linux-2.5.6/drivers/net/8139cp.c --- linux-2.5.6-pre3/drivers/net/8139cp.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/8139cp.c Thu Mar 7 18:24:45 2002 @@ -2,6 +2,7 @@ /* Copyright 2001,2002 Jeff Garzik + Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] Copyright 2001 Manfred Spraul [natsemi.c] Copyright 1999-2001 by Donald Becker. [natsemi.c] @@ -29,8 +30,8 @@ * Consider Rx interrupt mitigation using TimerIntr * Implement 8139C+ statistics dump; maybe not... h/w stats can be reset only by software reset - * Rx checksumming * Tx checksumming + * Handle netif_rx return value * ETHTOOL_GREGS, ETHTOOL_[GS]WOL, * Investigate using skb->priority with h/w VLAN priority * Investigate using High Priority Tx Queue with skb->priority @@ -38,8 +39,8 @@ * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error * Implement Tx software interrupt mitigation via Tx descriptor bit - * Determine correct value for CP_{MIN,MAX}_MTU, instead of - using conservative guesses. + * The real minimum of CP_MIN_MTU is 4 bytes. However, + for this to be supported, one must(?) turn on packet padding. */ @@ -48,8 +49,10 @@ #define DRV_RELDATE "Feb 27, 2002" +#include #include #include +#include #include #include #include @@ -57,9 +60,15 @@ #include #include #include +#include +#include #include #include +#define CP_VLAN_TAG_USED 0 +#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \ + do { (tx_desc)->opts2 = 0; } while (0) + /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = KERN_INFO DRV_NAME " 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; @@ -110,8 +119,8 @@ #define TX_TIMEOUT (6*HZ) /* hardware minimum and maximum for a single frame's data payload */ -#define CP_MIN_MTU 60 /* FIXME: this is a guess */ -#define CP_MAX_MTU 1500 /* FIXME: this is a guess */ +#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */ +#define CP_MAX_MTU 4096 enum { /* NIC register offsets */ @@ -153,12 +162,17 @@ IPCS = (1 << 18), /* Calculate IP checksum */ UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ IPFail = (1 << 15), /* IP checksum failed */ UDPFail = (1 << 14), /* UDP/IP checksum failed */ TCPFail = (1 << 13), /* TCP/IP checksum failed */ NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ + RxProtoTCP = 2, + RxProtoUDP = 1, + RxProtoIP = 3, TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */ TxOWC = (1 << 22), /* Tx Out-of-window collision */ TxLinkFail = (1 << 21), /* Link failed during Tx of packet */ @@ -208,6 +222,7 @@ TxOn = (1 << 2), /* Tx mode enable */ /* C+ mode command register */ + RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */ RxChkSum = (1 << 5), /* Rx checksum offload enable */ PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */ CpRxOn = (1 << 1), /* Rx mode enable */ @@ -278,6 +293,10 @@ unsigned rx_buf_sz; dma_addr_t ring_dma; +#if CP_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif + u32 msg_enable; struct net_device_stats net_stats; @@ -335,14 +354,21 @@ cp->rx_buf_sz = PKT_BUF_SZ; } -static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb) +static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb, + struct cp_desc *desc) { skb->protocol = eth_type_trans (skb, cp->dev); cp->net_stats.rx_packets++; cp->net_stats.rx_bytes += skb->len; cp->dev->last_rx = jiffies; - netif_rx (skb); + +#if CP_VLAN_TAG_USED + if (cp->vlgrp && (desc->opts2 & RxVlanTagged)) { + vlan_hwaccel_rx(skb, cp->vlgrp, desc->opts2 & 0xffff); + } else +#endif + netif_rx(skb); } static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail, @@ -425,13 +451,26 @@ cp_rx_err_acct(cp, rx_tail, status, len); dev_kfree_skb_irq(copy_skb); } else - cp_rx_skb(cp, copy_skb); + cp_rx_skb(cp, copy_skb, &cp->rx_ring[rx_tail]); cp->frag_skb = NULL; } else { cp->frag_skb = copy_skb; } } +static inline unsigned int cp_rx_csum_ok (u32 status) +{ + unsigned int protocol = (status >> 16) & 0x3; + + if (likely((protocol == RxProtoTCP) && (!(status & TCPFail)))) + return 1; + else if ((protocol == RxProtoUDP) && (!(status & UDPFail))) + return 1; + else if ((protocol == RxProtoIP) && (!(status & IPFail))) + return 1; + return 0; +} + static void cp_rx (struct cp_private *cp) { unsigned rx_tail = cp->rx_tail; @@ -441,13 +480,15 @@ u32 status, len; dma_addr_t mapping; struct sk_buff *skb, *new_skb; + struct cp_desc *desc; unsigned buflen; skb = cp->rx_skb[rx_tail].skb; if (!skb) BUG(); - rmb(); - status = le32_to_cpu(cp->rx_ring[rx_tail].opts1); + + desc = &cp->rx_ring[rx_tail]; + status = le32_to_cpu(desc->opts1); if (status & DescOwn) break; @@ -480,7 +521,13 @@ pci_unmap_single(cp->pdev, mapping, buflen, PCI_DMA_FROMDEVICE); - skb->ip_summed = CHECKSUM_NONE; + + /* Handle checksum offloading for incoming packets. */ + if (cp_rx_csum_ok(status)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + skb_put(skb, len); mapping = @@ -489,15 +536,14 @@ buflen, PCI_DMA_FROMDEVICE); cp->rx_skb[rx_tail].skb = new_skb; - cp_rx_skb(cp, skb); + cp_rx_skb(cp, skb, desc); rx_next: if (rx_tail == (CP_RX_RING_SIZE - 1)) - cp->rx_ring[rx_tail].opts1 = - cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); + desc->opts1 = cpu_to_le32(DescOwn | RingEnd | + cp->rx_buf_sz); else - cp->rx_ring[rx_tail].opts1 = - cpu_to_le32(DescOwn | cp->rx_buf_sz); + desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz); cp->rx_ring[rx_tail].opts2 = 0; cp->rx_ring[rx_tail].addr_lo = cpu_to_le32(mapping); rx_tail = NEXT_RX(rx_tail); @@ -606,6 +652,9 @@ struct cp_private *cp = dev->priv; unsigned entry; u32 eor; +#if CP_VLAN_TAG_USED + u32 vlan_tag = 0; +#endif spin_lock_irq(&cp->lock); @@ -615,6 +664,11 @@ return 1; } +#if CP_VLAN_TAG_USED + if (cp->vlgrp && vlan_tx_tag_present(skb)) + vlan_tag = TxVlanTag | vlan_tx_tag_get(skb); +#endif + entry = cp->tx_head; eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; if (skb_shinfo(skb)->nr_frags == 0) { @@ -624,7 +678,7 @@ len = skb->len; mapping = pci_map_single(cp->pdev, skb->data, len, PCI_DMA_TODEVICE); eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(mapping); wmb(); @@ -677,7 +731,7 @@ ctrl |= LastFrag; txd = &cp->tx_ring[entry]; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(mapping); wmb(); @@ -691,7 +745,7 @@ } txd = &cp->tx_ring[first_entry]; - txd->opts2 = 0; + CP_VLAN_TX_TAG(txd, vlan_tag); txd->addr_lo = cpu_to_le32(first_mapping); wmb(); @@ -826,7 +880,7 @@ static inline void cp_start_hw (struct cp_private *cp) { cpw8(Cmd, RxOn | TxOn); - cpw16(CpCmd, PCIMulRW | CpRxOn | CpTxOn); + cpw16(CpCmd, PCIMulRW | RxChkSum | CpRxOn | CpTxOn); } static void cp_init_hw (struct cp_private *cp) @@ -1171,7 +1225,30 @@ return rc; } +#if CP_VLAN_TAG_USED +static int cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct cp_private *cp = dev->priv; + + spin_lock_irq(&cp->lock); + cp->vlgrp = grp; + cpw16(CpCmd, cpr16(CpCmd) | RxVlanOn); + spin_unlock_irq(&cp->lock); + return 0; +} + +static void cp_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct cp_private *cp = dev->priv; + + spin_lock_irq(&cp->lock); + cpw16(CpCmd, cpr16(CpCmd) & ~RxVlanOn); + if (cp->vlgrp) + cp->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irq(&cp->lock); +} +#endif /* Serial EEPROM section. */ @@ -1337,6 +1414,11 @@ #ifdef CP_TX_CHECKSUM dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; #endif +#if CP_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = cp_vlan_rx_register; + dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid; +#endif dev->irq = pdev->irq; diff -urN linux-2.5.6-pre3/drivers/net/Config.help linux-2.5.6/drivers/net/Config.help --- linux-2.5.6-pre3/drivers/net/Config.help Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/Config.help Thu Mar 7 18:24:45 2002 @@ -797,6 +797,14 @@ Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also . +CONFIG_TIGON3 + This driver supports Broadcom Tigon3 based gigabit Ethernet cards. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . This is + recommended. The module will be called tg3.o. + CONFIG_MYRI_SBUS This driver supports MyriCOM Sbus gigabit Ethernet cards. @@ -1325,71 +1333,6 @@ . The module will be called apricot.o. -CONFIG_DE4X5 - This is support for the DIGITAL series of PCI/EISA Ethernet cards. - These include the DE425, DE434, DE435, DE450 and DE500 models. If - you have a network card of this type, say Y and read the - Ethernet-HOWTO, available from - . More specific - information is contained in - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called de4x5.o. If you want to compile it as a - module, say M here and read as well - as . - -CONFIG_DE2104X - This driver is developed for the SMC EtherPower series Ethernet - cards and also works with cards based on the DECchip - 21040 (Tulip series) chips. Some LinkSys PCI cards are - of this type. (If your card is NOT SMC EtherPower 10/100 PCI - (smc9332dst), you can also try the driver for "Generic DECchip" - cards, above. However, most people with a network card of this type - will say Y here.) Do read the Ethernet-HOWTO, available from - . More specific - information is contained in - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called tulip.o. If you want to compile it as a - module, say M here and read as well - as . - -CONFIG_TULIP - This driver is developed for the SMC EtherPower series Ethernet - cards and also works with cards based on the DECchip - 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are - of this type. (If your card is NOT SMC EtherPower 10/100 PCI - (smc9332dst), you can also try the driver for "Generic DECchip" - cards, above. However, most people with a network card of this type - will say Y here.) Do read the Ethernet-HOWTO, available from - . More specific - information is contained in - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called tulip.o. If you want to compile it as a - module, say M here and read as well - as . - -CONFIG_TULIP_MWI - This configures your Tulip card specifically for the card and - system cache line size type you are using. - - This is experimental code, not yet tested on many boards. - - If unsure, say N. - -CONFIG_TULIP_MMIO - Use PCI shared memory for the NIC registers, rather than going through - the Tulip's PIO (programmed I/O ports). Faster, but could produce - obscure bugs if your mainboard has memory controller timing issues. - If in doubt, say N. - CONFIG_DGRS This is support for the Digi International RightSwitch series of PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 @@ -1474,18 +1417,6 @@ If unsure, say N. -CONFIG_DM9102 - This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from - Davicom (). If you have such a network - (Ethernet) card, say Y. Some information is contained in the file - . - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called dmfe.o. If you want to compile it as a - module, say M here and read as well - as . - CONFIG_ES3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -1517,12 +1448,6 @@ More specific information and updates are available from . -CONFIG_WINBOND_840 - This driver is for the Winbond W89c840 chip. It also works with - the TX9882 chip on the Compex RL100-ATX board. - More specific information and updates are available from - . - CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network (Ethernet) card, and this is the Linux driver for it. Note that the diff -urN linux-2.5.6-pre3/drivers/net/Config.in linux-2.5.6/drivers/net/Config.in --- linux-2.5.6-pre3/drivers/net/Config.in Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/Config.in Thu Mar 7 18:24:45 2002 @@ -148,25 +148,18 @@ fi if [ "$CONFIG_NET_PCI" = "y" ]; then dep_tristate ' AMD PCnet32 PCI support' CONFIG_PCNET32 $CONFIG_PCI - dep_tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI $CONFIG_EXPERIMENTAL + dep_tristate ' Adaptec Starfire/DuraLAN support' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI if [ "$CONFIG_ISA" = "y" -o "$CONFIG_EISA" = "y" ]; then dep_tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 $CONFIG_EXPERIMENTAL fi dep_tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT $CONFIG_ISA dep_tristate ' CS89x0 support' CONFIG_CS89x0 $CONFIG_ISA - dep_tristate ' Early DECchip Tulip (dc2104x) PCI support (EXPERIMENTAL)' CONFIG_DE2104X $CONFIG_PCI $CONFIG_EXPERIMENTAL - dep_tristate ' DECchip Tulip (dc2114x) PCI support' CONFIG_TULIP $CONFIG_PCI - if [ "$CONFIG_TULIP" = "y" -o "$CONFIG_TULIP" = "m" ]; then - dep_bool ' New bus configuration (EXPERIMENTAL)' CONFIG_TULIP_MWI $CONFIG_EXPERIMENTAL - bool ' Use PCI shared mem for NIC registers' CONFIG_TULIP_MMIO - fi if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then - tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS fi - dep_tristate ' Davicom DM910x/DM980x support' CONFIG_DM9102 $CONFIG_PCI - dep_tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100 $CONFIG_PCI + dep_tristate ' EtherExpressPro/100 support (eepro100, original Becker driver)' CONFIG_EEPRO100 $CONFIG_PCI + dep_tristate ' EtherExpressPro/100 support (e100, Alternate Intel driver)' CONFIG_E100 $CONFIG_PCI dep_tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 $CONFIG_EISA $CONFIG_EXPERIMENTAL dep_tristate ' Myson MTD-8xx PCI Ethernet support' CONFIG_FEALNX $CONFIG_PCI dep_tristate ' National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI @@ -190,7 +183,6 @@ fi dep_tristate ' VIA Rhine support' CONFIG_VIA_RHINE $CONFIG_PCI dep_mbool ' Use MMIO instead of PIO (EXPERIMENTAL)' CONFIG_VIA_RHINE_MMIO $CONFIG_VIA_RHINE $CONFIG_EXPERIMENTAL - dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI if [ "$CONFIG_OBSOLETE" = "y" ]; then dep_bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET $CONFIG_ISA fi @@ -237,6 +229,7 @@ dep_tristate 'Packet Engines Hamachi GNIC-II support' CONFIG_HAMACHI $CONFIG_PCI dep_tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN $CONFIG_PCI $CONFIG_EXPERIMENTAL dep_tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN $CONFIG_PCI +dep_tristate 'Broadcom Tigon3 support' CONFIG_TIGON3 $CONFIG_PCI endmenu @@ -326,6 +319,9 @@ source drivers/net/wan/Config.in +if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" -o "$CONFIG_CARDBUS" != "n" ]; then + source drivers/net/tulip/Config.in +fi if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then source drivers/net/pcmcia/Config.in fi diff -urN linux-2.5.6-pre3/drivers/net/Makefile linux-2.5.6/drivers/net/Makefile --- linux-2.5.6-pre3/drivers/net/Makefile Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/Makefile Thu Mar 7 18:24:45 2002 @@ -8,7 +8,7 @@ obj-n := obj- := -mod-subdirs := appletalk arcnet fc irda tokenring pcmcia wireless wan +mod-subdirs := appletalk arcnet fc irda tokenring tulip pcmcia wireless wan O_TARGET := net.o @@ -21,10 +21,9 @@ list-multi := rcpci.o rcpci-objs := rcpci45.o rclanmtl.o -ifeq ($(CONFIG_TULIP),y) - obj-y += tulip/tulip.o +ifeq ($(CONFIG_E100),y) + obj-y += e100/e100.o endif - ifeq ($(CONFIG_E1000),y) obj-y += e1000/e1000.o endif @@ -35,7 +34,8 @@ subdir-$(CONFIG_NET_PCMCIA) += pcmcia subdir-$(CONFIG_NET_WIRELESS) += wireless -subdir-$(CONFIG_TULIP) += tulip +subdir-$(CONFIG_NET_TULIP) += tulip +subdir-$(CONFIG_E100) += e100 subdir-$(CONFIG_E1000) += e1000 subdir-$(CONFIG_IRDA) += irda subdir-$(CONFIG_TR) += tokenring @@ -74,7 +74,6 @@ obj-$(CONFIG_TLAN) += tlan.o obj-$(CONFIG_EPIC100) += epic100.o mii.o obj-$(CONFIG_SIS900) += sis900.o -obj-$(CONFIG_DM9102) += dmfe.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_ACENIC) += acenic.o obj-$(CONFIG_VETH) += veth.o @@ -82,6 +81,7 @@ obj-$(CONFIG_NS83820) += ns83820.o obj-$(CONFIG_STNIC) += stnic.o 8390.o obj-$(CONFIG_FEALNX) += fealnx.o mii.o +obj-$(CONFIG_TIGON3) += tg3.o ifeq ($(CONFIG_SK98LIN),y) obj-y += sk98lin/sk98lin.o @@ -104,7 +104,7 @@ obj-$(CONFIG_AIRONET4500_PROC) += aironet4500_proc.o obj-$(CONFIG_AIRONET4500_CS) += aironet4500_proc.o -obj-$(CONFIG_WINBOND_840) += winbond-840.o mii.o +obj-$(CONFIG_WINBOND_840) += mii.o obj-$(CONFIG_SUNDANCE) += sundance.o obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o @@ -176,8 +176,6 @@ obj-$(CONFIG_DEPCA) += depca.o obj-$(CONFIG_EWRK3) += ewrk3.o obj-$(CONFIG_ATP) += atp.o -obj-$(CONFIG_DE4X5) += de4x5.o -obj-$(CONFIG_DE2104X) += de2104x.o obj-$(CONFIG_NI5010) += ni5010.o obj-$(CONFIG_NI52) += ni52.o obj-$(CONFIG_NI65) += ni65.o diff -urN linux-2.5.6-pre3/drivers/net/ac3200.c linux-2.5.6/drivers/net/ac3200.c --- linux-2.5.6-pre3/drivers/net/ac3200.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/drivers/net/ac3200.c Thu Mar 7 18:24:45 2002 @@ -346,9 +346,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_AC32_CARDS) "i"); -MODULE_PARM_DESC(io, "ac3200 I/O base adress(es)"); -MODULE_PARM_DESC(irq, "ac3200 IRQ number(s)"); -MODULE_PARM_DESC(mem, "ac3200 Memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base adress(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "Memory base address(es)"); +MODULE_DESCRIPTION("Ansel AC3200 EISA ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) @@ -395,7 +397,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/de2104x.c linux-2.5.6/drivers/net/de2104x.c --- linux-2.5.6-pre3/drivers/net/de2104x.c Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/de2104x.c Wed Dec 31 16:00:00 1969 @@ -1,2240 +0,0 @@ -/* de2104x.c: A Linux PCI Ethernet driver for Intel/Digital 21040/1 chips. */ -/* - Copyright 2001 Jeff Garzik - - Copyright 1994, 1995 Digital Equipment Corporation. [de4x5.c] - Written/copyright 1994-2001 by Donald Becker. [tulip.c] - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - See the file COPYING in this distribution for more information. - - TODO, in rough priority order: - * Support forcing media type with a module parameter, - like dl2k.c/sundance.c - * Constants (module parms?) for Rx work limit - * Complete reset on PciErr - * Jumbo frames / dev->change_mtu - * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error - * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error - * Implement Tx software interrupt mitigation via - Tx descriptor bit - - */ - -#define DRV_NAME "de2104x" -#define DRV_VERSION "0.5.4" -#define DRV_RELDATE "Jan 1, 2002" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* These identify the driver base version and may not be removed. */ -static char version[] __initdata = -KERN_INFO DRV_NAME " PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; - -MODULE_AUTHOR("Jeff Garzik "); -MODULE_DESCRIPTION("Intel/Digital 21040/1 series PCI Ethernet driver"); -MODULE_LICENSE("GPL"); - -static int debug = -1; -MODULE_PARM (debug, "i"); -MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); - -/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc_) || defined(__ia64__) \ - || defined(__sh__) || defined(__mips__) -static int rx_copybreak = 1518; -#else -static int rx_copybreak = 100; -#endif -MODULE_PARM (rx_copybreak, "i"); -MODULE_PARM_DESC (rx_copybreak, "de2104x Breakpoint at which Rx packets are copied"); - -#define PFX DRV_NAME ": " - -#define DE_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ - NETIF_MSG_PROBE | \ - NETIF_MSG_LINK | \ - NETIF_MSG_TIMER | \ - NETIF_MSG_IFDOWN | \ - NETIF_MSG_IFUP | \ - NETIF_MSG_RX_ERR | \ - NETIF_MSG_TX_ERR) - -#define DE_RX_RING_SIZE 64 -#define DE_TX_RING_SIZE 64 -#define DE_RING_BYTES \ - ((sizeof(struct de_desc) * DE_RX_RING_SIZE) + \ - (sizeof(struct de_desc) * DE_TX_RING_SIZE)) -#define NEXT_TX(N) (((N) + 1) & (DE_TX_RING_SIZE - 1)) -#define NEXT_RX(N) (((N) + 1) & (DE_RX_RING_SIZE - 1)) -#define TX_BUFFS_AVAIL(CP) \ - (((CP)->tx_tail <= (CP)->tx_head) ? \ - (CP)->tx_tail + (DE_TX_RING_SIZE - 1) - (CP)->tx_head : \ - (CP)->tx_tail - (CP)->tx_head - 1) - -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -#define RX_OFFSET 2 - -#define DE_SETUP_SKB ((struct sk_buff *) 1) -#define DE_DUMMY_SKB ((struct sk_buff *) 2) -#define DE_SETUP_FRAME_WORDS 96 -#define DE_EEPROM_WORDS 256 -#define DE_EEPROM_SIZE (DE_EEPROM_WORDS * sizeof(u16)) -#define DE_MAX_MEDIA 5 - -#define DE_MEDIA_TP_AUTO 0 -#define DE_MEDIA_BNC 1 -#define DE_MEDIA_AUI 2 -#define DE_MEDIA_TP 3 -#define DE_MEDIA_TP_FD 4 -#define DE_MEDIA_INVALID DE_MAX_MEDIA -#define DE_MEDIA_FIRST 0 -#define DE_MEDIA_LAST (DE_MAX_MEDIA - 1) -#define DE_AUI_BNC (SUPPORTED_AUI | SUPPORTED_BNC) - -#define DE_TIMER_LINK (60 * HZ) -#define DE_TIMER_NO_LINK (5 * HZ) - -#define DE_NUM_REGS 16 -#define DE_REGS_SIZE (DE_NUM_REGS * sizeof(u32)) -#define DE_REGS_VER 1 - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) - -#define DE_UNALIGNED_16(a) (u16)(get_unaligned((u16 *)(a))) - -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 - -enum { - /* NIC registers */ - BusMode = 0x00, - TxPoll = 0x08, - RxPoll = 0x10, - RxRingAddr = 0x18, - TxRingAddr = 0x20, - MacStatus = 0x28, - MacMode = 0x30, - IntrMask = 0x38, - RxMissed = 0x40, - ROMCmd = 0x48, - CSR11 = 0x58, - SIAStatus = 0x60, - CSR13 = 0x68, - CSR14 = 0x70, - CSR15 = 0x78, - PCIPM = 0x40, - - /* BusMode bits */ - CmdReset = (1 << 0), - CacheAlign16 = 0x00008000, - BurstLen4 = 0x00000400, - - /* Rx/TxPoll bits */ - NormalTxPoll = (1 << 0), - NormalRxPoll = (1 << 0), - - /* Tx/Rx descriptor status bits */ - DescOwn = (1 << 31), - RxError = (1 << 15), - RxErrLong = (1 << 7), - RxErrCRC = (1 << 1), - RxErrFIFO = (1 << 0), - RxErrRunt = (1 << 11), - RxErrFrame = (1 << 14), - RingEnd = (1 << 25), - FirstFrag = (1 << 29), - LastFrag = (1 << 30), - TxError = (1 << 15), - TxFIFOUnder = (1 << 1), - TxLinkFail = (1 << 2) | (1 << 10) | (1 << 11), - TxMaxCol = (1 << 8), - TxOWC = (1 << 9), - TxJabber = (1 << 14), - SetupFrame = (1 << 27), - TxSwInt = (1 << 31), - - /* MacStatus bits */ - IntrOK = (1 << 16), - IntrErr = (1 << 15), - RxIntr = (1 << 6), - RxEmpty = (1 << 7), - TxIntr = (1 << 0), - TxEmpty = (1 << 2), - PciErr = (1 << 13), - TxState = (1 << 22) | (1 << 21) | (1 << 20), - RxState = (1 << 19) | (1 << 18) | (1 << 17), - LinkFail = (1 << 12), - LinkPass = (1 << 4), - RxStopped = (1 << 8), - TxStopped = (1 << 1), - - /* MacMode bits */ - TxEnable = (1 << 13), - RxEnable = (1 << 1), - RxTx = TxEnable | RxEnable, - FullDuplex = (1 << 9), - AcceptAllMulticast = (1 << 7), - AcceptAllPhys = (1 << 6), - BOCnt = (1 << 5), - MacModeClear = (1<<12) | (1<<11) | (1<<10) | (1<<8) | (1<<3) | - RxTx | BOCnt | AcceptAllPhys | AcceptAllMulticast, - - /* ROMCmd bits */ - EE_SHIFT_CLK = 0x02, /* EEPROM shift clock. */ - EE_CS = 0x01, /* EEPROM chip select. */ - EE_DATA_WRITE = 0x04, /* Data from the Tulip to EEPROM. */ - EE_WRITE_0 = 0x01, - EE_WRITE_1 = 0x05, - EE_DATA_READ = 0x08, /* Data from the EEPROM chip. */ - EE_ENB = (0x4800 | EE_CS), - - /* The EEPROM commands include the alway-set leading bit. */ - EE_READ_CMD = 6, - - /* RxMissed bits */ - RxMissedOver = (1 << 16), - RxMissedMask = 0xffff, - - /* SROM-related bits */ - SROMC0InfoLeaf = 27, - MediaBlockMask = 0x3f, - MediaCustomCSRs = (1 << 6), - - /* PCIPM bits */ - PM_Sleep = (1 << 31), - PM_Snooze = (1 << 30), - PM_Mask = PM_Sleep | PM_Snooze, - - /* SIAStatus bits */ - NWayState = (1 << 14) | (1 << 13) | (1 << 12), - NWayRestart = (1 << 12), - NonselPortActive = (1 << 9), - LinkFailStatus = (1 << 2), - NetCxnErr = (1 << 1), -}; - -static const u32 de_intr_mask = - IntrOK | IntrErr | RxIntr | RxEmpty | TxIntr | TxEmpty | - LinkPass | LinkFail | PciErr; - -/* - * Set the programmable burst length to 4 longwords for all: - * DMA errors result without these values. Cache align 16 long. - */ -static const u32 de_bus_mode = CacheAlign16 | BurstLen4; - -struct de_srom_media_block { - u8 opts; - u16 csr13; - u16 csr14; - u16 csr15; -} __attribute__((packed)); - -struct de_srom_info_leaf { - u16 default_media; - u8 n_blocks; - u8 unused; -} __attribute__((packed)); - -struct de_desc { - u32 opts1; - u32 opts2; - u32 addr1; - u32 addr2; -}; - -struct media_info { - u16 type; /* DE_MEDIA_xxx */ - u16 csr13; - u16 csr14; - u16 csr15; -}; - -struct ring_info { - struct sk_buff *skb; - dma_addr_t mapping; -}; - -struct de_private { - unsigned tx_head; - unsigned tx_tail; - unsigned rx_tail; - - void *regs; - struct net_device *dev; - spinlock_t lock; - - struct de_desc *rx_ring; - struct de_desc *tx_ring; - struct ring_info tx_skb[DE_TX_RING_SIZE]; - struct ring_info rx_skb[DE_RX_RING_SIZE]; - unsigned rx_buf_sz; - dma_addr_t ring_dma; - - u32 msg_enable; - - struct net_device_stats net_stats; - - struct pci_dev *pdev; - u32 macmode; - - u16 setup_frame[DE_SETUP_FRAME_WORDS]; - - u32 media_type; - u32 media_supported; - u32 media_advertise; - struct media_info media[DE_MAX_MEDIA]; - struct timer_list media_timer; - - u8 *ee_data; - unsigned board_idx; - unsigned de21040 : 1; - unsigned media_lock : 1; -}; - - -static void de_set_rx_mode (struct net_device *dev); -static void de_tx (struct de_private *de); -static void de_clean_rings (struct de_private *de); -static void de_media_interrupt (struct de_private *de, u32 status); -static void de21040_media_timer (unsigned long data); -static void de21041_media_timer (unsigned long data); -static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); - - -static struct pci_device_id de_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { }, -}; -MODULE_DEVICE_TABLE(pci, de_pci_tbl); - -static const char * const media_name[DE_MAX_MEDIA] = { - "10baseT auto", - "BNC", - "AUI", - "10baseT-HD", - "10baseT-FD" -}; - -/* 21040 transceiver register settings: - * TP AUTO(unused), BNC(unused), AUI, TP, TP FD*/ -static u16 t21040_csr13[] = { 0, 0, 0x8F09, 0x8F01, 0x8F01, }; -static u16 t21040_csr14[] = { 0, 0, 0x0705, 0xFFFF, 0xFFFD, }; -static u16 t21040_csr15[] = { 0, 0, 0x0006, 0x0000, 0x0000, }; - -/* 21041 transceiver register settings: TP AUTO, BNC, AUI, TP, TP FD*/ -static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; -static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x6F3F, 0x6F3D, }; -static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; - - -static inline unsigned long -msec_to_jiffies(unsigned long ms) -{ - return (((ms)*HZ+999)/1000); -} - - -#define dr32(reg) readl(de->regs + (reg)) -#define dw32(reg,val) writel((val), de->regs + (reg)) - - -static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, - u32 status, u32 len) -{ - if (netif_msg_rx_err (de)) - printk (KERN_DEBUG - "%s: rx err, slot %d status 0x%x len %d\n", - de->dev->name, rx_tail, status, len); - - if ((status & 0x38000300) != 0x0300) { - /* Ingore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - if (netif_msg_rx_err(de)) - printk(KERN_WARNING "%s: Oversized Ethernet frame " - "spanned multiple buffers, status %8.8x!\n", - de->dev->name, status); - de->net_stats.rx_length_errors++; - } - } else if (status & RxError) { - /* There was a fatal error. */ - de->net_stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) de->net_stats.rx_length_errors++; - if (status & RxErrCRC) de->net_stats.rx_crc_errors++; - if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; - } -} - -static void de_rx (struct de_private *de) -{ - unsigned rx_tail = de->rx_tail; - unsigned rx_work = DE_RX_RING_SIZE; - unsigned drop = 0; - int rc; - - while (rx_work--) { - u32 status, len; - dma_addr_t mapping; - struct sk_buff *skb, *copy_skb; - unsigned copying_skb, buflen; - - skb = de->rx_skb[rx_tail].skb; - if (!skb) - BUG(); - rmb(); - status = le32_to_cpu(de->rx_ring[rx_tail].opts1); - if (status & DescOwn) - break; - - len = ((status >> 16) & 0x7ff) - 4; - mapping = de->rx_skb[rx_tail].mapping; - - if (unlikely(drop)) { - de->net_stats.rx_dropped++; - goto rx_next; - } - - if (unlikely((status & 0x38008300) != 0x0300)) { - de_rx_err_acct(de, rx_tail, status, len); - goto rx_next; - } - - copying_skb = (len <= rx_copybreak); - - if (unlikely(netif_msg_rx_status(de))) - printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d copying? %d\n", - de->dev->name, rx_tail, status, len, - copying_skb); - - buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; - copy_skb = dev_alloc_skb (buflen); - if (unlikely(!copy_skb)) { - de->net_stats.rx_dropped++; - drop = 1; - rx_work = 100; - goto rx_next; - } - copy_skb->dev = de->dev; - - if (!copying_skb) { - pci_unmap_single(de->pdev, mapping, - buflen, PCI_DMA_FROMDEVICE); - skb_put(skb, len); - - mapping = - de->rx_skb[rx_tail].mapping = - pci_map_single(de->pdev, copy_skb->tail, - buflen, PCI_DMA_FROMDEVICE); - de->rx_skb[rx_tail].skb = copy_skb; - } else { - pci_dma_sync_single(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); - skb_reserve(copy_skb, RX_OFFSET); - memcpy(skb_put(copy_skb, len), skb->tail, len); - - /* We'll reuse the original ring buffer. */ - skb = copy_skb; - } - - skb->protocol = eth_type_trans (skb, de->dev); - - de->net_stats.rx_packets++; - de->net_stats.rx_bytes += skb->len; - de->dev->last_rx = jiffies; - rc = netif_rx (skb); - if (rc == NET_RX_DROP) - drop = 1; - -rx_next: - de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); - if (rx_tail == (DE_RX_RING_SIZE - 1)) - de->rx_ring[rx_tail].opts2 = - cpu_to_le32(RingEnd | de->rx_buf_sz); - else - de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); - de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); - rx_tail = NEXT_RX(rx_tail); - } - - if (!rx_work) - printk(KERN_WARNING "%s: rx work limit reached\n", de->dev->name); - - de->rx_tail = rx_tail; -} - -static void de_interrupt (int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = dev_instance; - struct de_private *de = dev->priv; - u32 status; - - status = dr32(MacStatus); - if ((!(status & (IntrOK|IntrErr))) || (status == 0xFFFF)) - return; - - if (netif_msg_intr(de)) - printk(KERN_DEBUG "%s: intr, status %08x mode %08x desc %u/%u/%u\n", - dev->name, status, dr32(MacMode), de->rx_tail, de->tx_head, de->tx_tail); - - dw32(MacStatus, status); - - if (status & (RxIntr | RxEmpty)) { - de_rx(de); - if (status & RxEmpty) - dw32(RxPoll, NormalRxPoll); - } - - spin_lock(&de->lock); - - if (status & (TxIntr | TxEmpty)) - de_tx(de); - - if (status & (LinkPass | LinkFail)) - de_media_interrupt(de, status); - - spin_unlock(&de->lock); - - if (status & PciErr) { - u16 pci_status; - - pci_read_config_word(de->pdev, PCI_STATUS, &pci_status); - pci_write_config_word(de->pdev, PCI_STATUS, pci_status); - printk(KERN_ERR "%s: PCI bus error, status=%08x, PCI status=%04x\n", - dev->name, status, pci_status); - } -} - -static void de_tx (struct de_private *de) -{ - unsigned tx_head = de->tx_head; - unsigned tx_tail = de->tx_tail; - - while (tx_tail != tx_head) { - struct sk_buff *skb; - u32 status; - - rmb(); - status = le32_to_cpu(de->tx_ring[tx_tail].opts1); - if (status & DescOwn) - break; - - skb = de->tx_skb[tx_tail].skb; - if (!skb) - BUG(); - if (unlikely(skb == DE_DUMMY_SKB)) - goto next; - - if (unlikely(skb == DE_SETUP_SKB)) { - pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, - sizeof(de->setup_frame), PCI_DMA_TODEVICE); - goto next; - } - - pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, - skb->len, PCI_DMA_TODEVICE); - - if (status & LastFrag) { - if (status & TxError) { - if (netif_msg_tx_err(de)) - printk(KERN_DEBUG "%s: tx err, status 0x%x\n", - de->dev->name, status); - de->net_stats.tx_errors++; - if (status & TxOWC) - de->net_stats.tx_window_errors++; - if (status & TxMaxCol) - de->net_stats.tx_aborted_errors++; - if (status & TxLinkFail) - de->net_stats.tx_carrier_errors++; - if (status & TxFIFOUnder) - de->net_stats.tx_fifo_errors++; - } else { - de->net_stats.tx_packets++; - de->net_stats.tx_bytes += skb->len; - if (netif_msg_tx_done(de)) - printk(KERN_DEBUG "%s: tx done, slot %d\n", de->dev->name, tx_tail); - } - dev_kfree_skb_irq(skb); - } - -next: - de->tx_skb[tx_tail].skb = NULL; - - tx_tail = NEXT_TX(tx_tail); - } - - de->tx_tail = tx_tail; - - if (netif_queue_stopped(de->dev) && (TX_BUFFS_AVAIL(de) > (DE_TX_RING_SIZE / 4))) - netif_wake_queue(de->dev); -} - -static int de_start_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct de_private *de = dev->priv; - unsigned int entry, tx_free; - u32 mapping, len, flags = FirstFrag | LastFrag; - struct de_desc *txd; - - spin_lock_irq(&de->lock); - - tx_free = TX_BUFFS_AVAIL(de); - if (tx_free == 0) { - netif_stop_queue(dev); - spin_unlock_irq(&de->lock); - return 1; - } - tx_free--; - - entry = de->tx_head; - - txd = &de->tx_ring[entry]; - - len = skb->len; - mapping = pci_map_single(de->pdev, skb->data, len, PCI_DMA_TODEVICE); - if (entry == (DE_TX_RING_SIZE - 1)) - flags |= RingEnd; - if (!tx_free || (tx_free == (DE_TX_RING_SIZE / 2))) - flags |= TxSwInt; - flags |= len; - txd->opts2 = cpu_to_le32(flags); - txd->addr1 = cpu_to_le32(mapping); - - de->tx_skb[entry].skb = skb; - de->tx_skb[entry].mapping = mapping; - wmb(); - - txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - - de->tx_head = NEXT_TX(entry); - if (netif_msg_tx_queued(de)) - printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", - dev->name, entry, skb->len); - - if (tx_free == 0) - netif_stop_queue(dev); - - spin_unlock_irq(&de->lock); - - /* Trigger an immediate transmit demand. */ - dw32(TxPoll, NormalTxPoll); - dev->trans_start = jiffies; - - return 0; -} - -/* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling de->setup_frame. This is non-deterministic - when re-entered but still correct. */ - -#undef set_bit_le -#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) - -static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) -{ - struct de_private *de = dev->priv; - u16 hash_table[32]; - struct dev_mc_list *mclist; - int i; - u16 *eaddrs; - - memset(hash_table, 0, sizeof(hash_table)); - set_bit_le(255, hash_table); /* Broadcast entry */ - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; - - set_bit_le(index, hash_table); - - for (i = 0; i < 32; i++) { - *setup_frm++ = hash_table[i]; - *setup_frm++ = hash_table[i]; - } - setup_frm = &de->setup_frame[13*6]; - } - - /* Fill the final entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; -} - -static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) -{ - struct de_private *de = dev->priv; - struct dev_mc_list *mclist; - int i; - u16 *eaddrs; - - /* We have <= 14 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; - } - /* Fill the unused entries with the broadcast address. */ - memset(setup_frm, 0xff, (15-i)*12); - setup_frm = &de->setup_frame[15*6]; - - /* Fill the final entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; -} - - -static void __de_set_rx_mode (struct net_device *dev) -{ - struct de_private *de = dev->priv; - u32 macmode; - unsigned int entry; - u32 mapping; - struct de_desc *txd; - struct de_desc *dummy_txd = NULL; - - macmode = de->macmode & ~(AcceptAllMulticast | AcceptAllPhys); - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - macmode |= AcceptAllMulticast | AcceptAllPhys; - goto out; - } - - if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well -- accept all multicasts. */ - macmode |= AcceptAllMulticast; - goto out; - } - - /* Note that only the low-address shortword of setup_frame is valid! - The values are doubled for big-endian architectures. */ - if (dev->mc_count > 14) /* Must use a multicast hash table. */ - build_setup_frame_hash (de->setup_frame, dev); - else - build_setup_frame_perfect (de->setup_frame, dev); - - /* - * Now add this frame to the Tx list. - */ - - entry = de->tx_head; - - /* Avoid a chip errata by prefixing a dummy entry. */ - if (entry != 0) { - de->tx_skb[entry].skb = DE_DUMMY_SKB; - - dummy_txd = &de->tx_ring[entry]; - dummy_txd->opts2 = (entry == (DE_TX_RING_SIZE - 1)) ? - cpu_to_le32(RingEnd) : 0; - dummy_txd->addr1 = 0; - - /* Must set DescOwned later to avoid race with chip */ - - entry = NEXT_TX(entry); - } - - de->tx_skb[entry].skb = DE_SETUP_SKB; - de->tx_skb[entry].mapping = mapping = - pci_map_single (de->pdev, de->setup_frame, - sizeof (de->setup_frame), PCI_DMA_TODEVICE); - - /* Put the setup frame on the Tx list. */ - txd = &de->tx_ring[entry]; - if (entry == (DE_TX_RING_SIZE - 1)) - txd->opts2 = cpu_to_le32(SetupFrame | RingEnd | sizeof (de->setup_frame)); - else - txd->opts2 = cpu_to_le32(SetupFrame | sizeof (de->setup_frame)); - txd->addr1 = cpu_to_le32(mapping); - wmb(); - - txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - - if (dummy_txd) { - dummy_txd->opts1 = cpu_to_le32(DescOwn); - wmb(); - } - - de->tx_head = NEXT_TX(entry); - - if (TX_BUFFS_AVAIL(de) < 0) - BUG(); - if (TX_BUFFS_AVAIL(de) == 0) - netif_stop_queue(dev); - - /* Trigger an immediate transmit demand. */ - dw32(TxPoll, NormalTxPoll); - -out: - if (macmode != de->macmode) { - dw32 (MacMode, macmode); - de->macmode = macmode; - } -} - -static void de_set_rx_mode (struct net_device *dev) -{ - unsigned long flags; - struct de_private *de = dev->priv; - - spin_lock_irqsave (&de->lock, flags); - __de_set_rx_mode(dev); - spin_unlock_irqrestore (&de->lock, flags); -} - -static inline void de_rx_missed(struct de_private *de, u32 rx_missed) -{ - if (unlikely(rx_missed & RxMissedOver)) - de->net_stats.rx_missed_errors += RxMissedMask; - else - de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); -} - -static void __de_get_stats(struct de_private *de) -{ - u32 tmp = dr32(RxMissed); /* self-clearing */ - - de_rx_missed(de, tmp); -} - -static struct net_device_stats *de_get_stats(struct net_device *dev) -{ - struct de_private *de = dev->priv; - - /* The chip only need report frame silently dropped. */ - spin_lock_irq(&de->lock); - if (netif_running(dev) && netif_device_present(dev)) - __de_get_stats(de); - spin_unlock_irq(&de->lock); - - return &de->net_stats; -} - -static inline int de_is_running (struct de_private *de) -{ - return (dr32(MacStatus) & (RxState | TxState)) ? 1 : 0; -} - -static void de_stop_rxtx (struct de_private *de) -{ - u32 macmode; - unsigned int work = 1000; - - macmode = dr32(MacMode); - if (macmode & RxTx) { - dw32(MacMode, macmode & ~RxTx); - dr32(MacMode); - } - - while (--work > 0) { - if (!de_is_running(de)) - return; - cpu_relax(); - } - - printk(KERN_WARNING "%s: timeout expired stopping DMA\n", de->dev->name); -} - -static inline void de_start_rxtx (struct de_private *de) -{ - u32 macmode; - - macmode = dr32(MacMode); - if ((macmode & RxTx) != RxTx) { - dw32(MacMode, macmode | RxTx); - dr32(MacMode); - } -} - -static void de_stop_hw (struct de_private *de) -{ - - udelay(5); - dw32(IntrMask, 0); - - de_stop_rxtx(de); - - dw32(MacStatus, dr32(MacStatus)); - - udelay(10); - - de->rx_tail = 0; - de->tx_head = de->tx_tail = 0; -} - -static void de_link_up(struct de_private *de) -{ - if (!netif_carrier_ok(de->dev)) { - netif_carrier_on(de->dev); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link up, media %s\n", - de->dev->name, media_name[de->media_type]); - } -} - -static void de_link_down(struct de_private *de) -{ - if (netif_carrier_ok(de->dev)) { - netif_carrier_off(de->dev); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link down\n", de->dev->name); - } -} - -static void de_set_media (struct de_private *de) -{ - unsigned media = de->media_type; - - if (de_is_running(de)) - BUG(); - - if (de->de21040) - dw32(CSR11, FULL_DUPLEX_MAGIC); - dw32(CSR13, 0); /* Reset phy */ - dw32(CSR14, de->media[media].csr14); - dw32(CSR15, de->media[media].csr15); - dw32(CSR13, de->media[media].csr13); - - /* must delay 10ms before writing to other registers, - * especially CSR6 - */ - mdelay(10); - - if (media == DE_MEDIA_TP_FD) - de->macmode |= FullDuplex; - else - de->macmode &= ~FullDuplex; - - if (netif_msg_link(de)) { - printk(KERN_INFO "%s: set link %s\n" - KERN_INFO "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" - KERN_INFO "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", - de->dev->name, media_name[media], - de->dev->name, dr32(MacMode), dr32(SIAStatus), - dr32(CSR13), dr32(CSR14), dr32(CSR15), - de->dev->name, de->macmode, de->media[media].csr13, - de->media[media].csr14, de->media[media].csr15); - } -} - -static void de_next_media (struct de_private *de, u32 *media, - unsigned int n_media) -{ - unsigned int i; - - for (i = 0; i < n_media; i++) { - if (de_ok_to_advertise(de, media[i])) { - de->media_type = media[i]; - return; - } - } -} - -static void de21040_media_timer (unsigned long data) -{ - struct de_private *de = (struct de_private *) data; - struct net_device *dev = de->dev; - u32 status = dr32(SIAStatus); - unsigned int carrier; - unsigned long flags; - - carrier = (status & NetCxnErr) ? 0 : 1; - - if (carrier) { - if (de->media_type != DE_MEDIA_AUI && (status & LinkFailStatus)) - goto no_link_yet; - - de->media_timer.expires = jiffies + DE_TIMER_LINK; - add_timer(&de->media_timer); - if (!netif_carrier_ok(dev)) - de_link_up(de); - else - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: %s link ok, status %x\n", - dev->name, media_name[de->media_type], - status); - return; - } - - de_link_down(de); - - if (de->media_lock) - return; - - if (de->media_type == DE_MEDIA_AUI) { - u32 next_state = DE_MEDIA_TP; - de_next_media(de, &next_state, 1); - } else { - u32 next_state = DE_MEDIA_AUI; - de_next_media(de, &next_state, 1); - } - - spin_lock_irqsave(&de->lock, flags); - de_stop_rxtx(de); - spin_unlock_irqrestore(&de->lock, flags); - de_set_media(de); - de_start_rxtx(de); - -no_link_yet: - de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; - add_timer(&de->media_timer); - - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: no link, trying media %s, status %x\n", - dev->name, media_name[de->media_type], status); -} - -static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media) -{ - switch (new_media) { - case DE_MEDIA_TP_AUTO: - if (!(de->media_advertise & ADVERTISED_Autoneg)) - return 0; - if (!(de->media_advertise & (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full))) - return 0; - break; - case DE_MEDIA_BNC: - if (!(de->media_advertise & ADVERTISED_BNC)) - return 0; - break; - case DE_MEDIA_AUI: - if (!(de->media_advertise & ADVERTISED_AUI)) - return 0; - break; - case DE_MEDIA_TP: - if (!(de->media_advertise & ADVERTISED_10baseT_Half)) - return 0; - break; - case DE_MEDIA_TP_FD: - if (!(de->media_advertise & ADVERTISED_10baseT_Full)) - return 0; - break; - } - - return 1; -} - -static void de21041_media_timer (unsigned long data) -{ - struct de_private *de = (struct de_private *) data; - struct net_device *dev = de->dev; - u32 status = dr32(SIAStatus); - unsigned int carrier; - unsigned long flags; - - carrier = (status & NetCxnErr) ? 0 : 1; - - if (carrier) { - if ((de->media_type == DE_MEDIA_TP_AUTO || - de->media_type == DE_MEDIA_TP || - de->media_type == DE_MEDIA_TP_FD) && - (status & LinkFailStatus)) - goto no_link_yet; - - de->media_timer.expires = jiffies + DE_TIMER_LINK; - add_timer(&de->media_timer); - if (!netif_carrier_ok(dev)) - de_link_up(de); - else - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: %s link ok, mode %x status %x\n", - dev->name, media_name[de->media_type], - dr32(MacMode), status); - return; - } - - de_link_down(de); - - /* if media type locked, don't switch media */ - if (de->media_lock) - goto set_media; - - /* if activity detected, use that as hint for new media type */ - if (status & NonselPortActive) { - unsigned int have_media = 1; - - /* if AUI/BNC selected, then activity is on TP port */ - if (de->media_type == DE_MEDIA_AUI || - de->media_type == DE_MEDIA_BNC) { - if (de_ok_to_advertise(de, DE_MEDIA_TP_AUTO)) - de->media_type = DE_MEDIA_TP_AUTO; - else - have_media = 0; - } - - /* TP selected. If there is only TP and BNC, then it's BNC */ - else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_BNC) && - de_ok_to_advertise(de, DE_MEDIA_BNC)) - de->media_type = DE_MEDIA_BNC; - - /* TP selected. If there is only TP and AUI, then it's AUI */ - else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_AUI) && - de_ok_to_advertise(de, DE_MEDIA_AUI)) - de->media_type = DE_MEDIA_AUI; - - /* otherwise, ignore the hint */ - else - have_media = 0; - - if (have_media) - goto set_media; - } - - /* - * Absent or ambiguous activity hint, move to next advertised - * media state. If de->media_type is left unchanged, this - * simply resets the PHY and reloads the current media settings. - */ - if (de->media_type == DE_MEDIA_AUI) { - u32 next_states[] = { DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } else if (de->media_type == DE_MEDIA_BNC) { - u32 next_states[] = { DE_MEDIA_TP_AUTO, DE_MEDIA_AUI }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } else { - u32 next_states[] = { DE_MEDIA_AUI, DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; - de_next_media(de, next_states, ARRAY_SIZE(next_states)); - } - -set_media: - spin_lock_irqsave(&de->lock, flags); - de_stop_rxtx(de); - spin_unlock_irqrestore(&de->lock, flags); - de_set_media(de); - de_start_rxtx(de); - -no_link_yet: - de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; - add_timer(&de->media_timer); - - if (netif_msg_timer(de)) - printk(KERN_INFO "%s: no link, trying media %s, status %x\n", - dev->name, media_name[de->media_type], status); -} - -static void de_media_interrupt (struct de_private *de, u32 status) -{ - if (status & LinkPass) { - de_link_up(de); - mod_timer(&de->media_timer, jiffies + DE_TIMER_LINK); - return; - } - - if (!(status & LinkFail)) - BUG(); - - if (netif_carrier_ok(de->dev)) { - de_link_down(de); - mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); - } -} - -static int de_reset_mac (struct de_private *de) -{ - u32 status, tmp; - - /* - * Reset MAC. Copied from de4x5.c. - */ - - tmp = dr32 (BusMode); - if (tmp == 0xffffffff) - return -ENODEV; - mdelay (1); - - dw32 (BusMode, tmp | CmdReset); - mdelay (1); - - dw32 (BusMode, tmp); - mdelay (1); - - for (tmp = 0; tmp < 5; tmp++) { - dr32 (BusMode); - mdelay (1); - } - - mdelay (1); - - status = dr32(MacStatus); - if (status & (RxState | TxState)) - return -EBUSY; - if (status == 0xffffffff) - return -ENODEV; - return 0; -} - -static void de_adapter_wake (struct de_private *de) -{ - u32 pmctl; - - if (de->de21040) - return; - - pci_read_config_dword(de->pdev, PCIPM, &pmctl); - if (pmctl & PM_Mask) { - pmctl &= ~PM_Mask; - pci_write_config_dword(de->pdev, PCIPM, pmctl); - - /* de4x5.c delays, so we do too */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(msec_to_jiffies(10)); - } -} - -static void de_adapter_sleep (struct de_private *de) -{ - u32 pmctl; - - if (de->de21040) - return; - - pci_read_config_dword(de->pdev, PCIPM, &pmctl); - pmctl |= PM_Sleep; - pci_write_config_dword(de->pdev, PCIPM, pmctl); -} - -static int de_init_hw (struct de_private *de) -{ - struct net_device *dev = de->dev; - int rc; - - de_adapter_wake(de); - - de->macmode = dr32(MacMode) & ~MacModeClear; - - rc = de_reset_mac(de); - if (rc) - return rc; - - de_set_media(de); /* reset phy */ - - dw32(RxRingAddr, de->ring_dma); - dw32(TxRingAddr, de->ring_dma + (sizeof(struct de_desc) * DE_RX_RING_SIZE)); - - dw32(MacMode, RxTx | de->macmode); - - dr32(RxMissed); /* self-clearing */ - - dw32(IntrMask, de_intr_mask); - - de_set_rx_mode(dev); - - return 0; -} - -static int de_refill_rx (struct de_private *de) -{ - unsigned i; - - for (i = 0; i < DE_RX_RING_SIZE; i++) { - struct sk_buff *skb; - - skb = dev_alloc_skb(de->rx_buf_sz); - if (!skb) - goto err_out; - - skb->dev = de->dev; - - de->rx_skb[i].mapping = pci_map_single(de->pdev, - skb->tail, de->rx_buf_sz, PCI_DMA_FROMDEVICE); - de->rx_skb[i].skb = skb; - - de->rx_ring[i].opts1 = cpu_to_le32(DescOwn); - if (i == (DE_RX_RING_SIZE - 1)) - de->rx_ring[i].opts2 = - cpu_to_le32(RingEnd | de->rx_buf_sz); - else - de->rx_ring[i].opts2 = cpu_to_le32(de->rx_buf_sz); - de->rx_ring[i].addr1 = cpu_to_le32(de->rx_skb[i].mapping); - de->rx_ring[i].addr2 = 0; - } - - return 0; - -err_out: - de_clean_rings(de); - return -ENOMEM; -} - -static int de_init_rings (struct de_private *de) -{ - memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); - de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - - de->rx_tail = 0; - de->tx_head = de->tx_tail = 0; - - return de_refill_rx (de); -} - -static int de_alloc_rings (struct de_private *de) -{ - de->rx_ring = pci_alloc_consistent(de->pdev, DE_RING_BYTES, &de->ring_dma); - if (!de->rx_ring) - return -ENOMEM; - de->tx_ring = &de->rx_ring[DE_RX_RING_SIZE]; - return de_init_rings(de); -} - -static void de_clean_rings (struct de_private *de) -{ - unsigned i; - - memset(de->rx_ring, 0, sizeof(struct de_desc) * DE_RX_RING_SIZE); - de->rx_ring[DE_RX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - wmb(); - memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); - de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); - wmb(); - - for (i = 0; i < DE_RX_RING_SIZE; i++) { - if (de->rx_skb[i].skb) { - pci_unmap_single(de->pdev, de->rx_skb[i].mapping, - de->rx_buf_sz, PCI_DMA_FROMDEVICE); - dev_kfree_skb(de->rx_skb[i].skb); - } - } - - for (i = 0; i < DE_TX_RING_SIZE; i++) { - struct sk_buff *skb = de->tx_skb[i].skb; - if ((skb) && (skb != DE_DUMMY_SKB)) { - if (skb != DE_SETUP_SKB) { - dev_kfree_skb(skb); - de->net_stats.tx_dropped++; - pci_unmap_single(de->pdev, - de->tx_skb[i].mapping, - skb->len, PCI_DMA_TODEVICE); - } else { - pci_unmap_single(de->pdev, - de->tx_skb[i].mapping, - sizeof(de->setup_frame), - PCI_DMA_TODEVICE); - } - } - } - - memset(&de->rx_skb, 0, sizeof(struct ring_info) * DE_RX_RING_SIZE); - memset(&de->tx_skb, 0, sizeof(struct ring_info) * DE_TX_RING_SIZE); -} - -static void de_free_rings (struct de_private *de) -{ - de_clean_rings(de); - pci_free_consistent(de->pdev, DE_RING_BYTES, de->rx_ring, de->ring_dma); - de->rx_ring = NULL; - de->tx_ring = NULL; -} - -static int de_open (struct net_device *dev) -{ - struct de_private *de = dev->priv; - int rc; - unsigned long flags; - - if (netif_msg_ifup(de)) - printk(KERN_DEBUG "%s: enabling interface\n", dev->name); - - de->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - rc = de_alloc_rings(de); - if (rc) { - printk(KERN_ERR "%s: ring allocation failure, err=%d\n", - dev->name, rc); - return rc; - } - - rc = de_init_hw(de); - if (rc) { - printk(KERN_ERR "%s: h/w init failure, err=%d\n", - dev->name, rc); - goto err_out_free; - } - - rc = request_irq(dev->irq, de_interrupt, SA_SHIRQ, dev->name, dev); - if (rc) { - printk(KERN_ERR "%s: IRQ %d request failure, err=%d\n", - dev->name, dev->irq, rc); - goto err_out_hw; - } - - netif_start_queue(dev); - mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); - - return 0; - -err_out_hw: - spin_lock_irqsave(&de->lock, flags); - de_stop_hw(de); - spin_unlock_irqrestore(&de->lock, flags); - -err_out_free: - de_free_rings(de); - return rc; -} - -static int de_close (struct net_device *dev) -{ - struct de_private *de = dev->priv; - unsigned long flags; - - if (netif_msg_ifdown(de)) - printk(KERN_DEBUG "%s: disabling interface\n", dev->name); - - del_timer_sync(&de->media_timer); - - spin_lock_irqsave(&de->lock, flags); - de_stop_hw(de); - netif_stop_queue(dev); - netif_carrier_off(dev); - spin_unlock_irqrestore(&de->lock, flags); - - free_irq(dev->irq, dev); - - de_free_rings(de); - de_adapter_sleep(de); - pci_disable_device(de->pdev); - return 0; -} - -static void de_tx_timeout (struct net_device *dev) -{ - struct de_private *de = dev->priv; - - printk(KERN_DEBUG "%s: NIC status %08x mode %08x sia %08x desc %u/%u/%u\n", - dev->name, dr32(MacStatus), dr32(MacMode), dr32(SIAStatus), - de->rx_tail, de->tx_head, de->tx_tail); - - del_timer_sync(&de->media_timer); - - disable_irq(dev->irq); - spin_lock_irq(&de->lock); - - de_stop_hw(de); - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&de->lock); - enable_irq(dev->irq); - - /* Update the error counts. */ - __de_get_stats(de); - - synchronize_irq(); - de_clean_rings(de); - - de_init_hw(de); - - netif_wake_queue(dev); -} - -static int de_get_regs(struct de_private *de, u8 *buf) -{ - int i; - u32 *rbuf = (u32 *)buf; - - /* read all CSRs */ - for (i = 0; i < DE_NUM_REGS; i++) - rbuf[i] = dr32(i * 8); - - /* handle self-clearing RxMissed counter, CSR8 */ - de_rx_missed(de, rbuf[8]); - - return 0; -} - -static int de_ethtool_gset(struct de_private *de, struct ethtool_cmd *ecmd) -{ - ecmd->supported = de->media_supported; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = 0; - ecmd->advertising = de->media_advertise; - - switch (de->media_type) { - case DE_MEDIA_AUI: - ecmd->port = PORT_AUI; - ecmd->speed = 5; - break; - case DE_MEDIA_BNC: - ecmd->port = PORT_BNC; - ecmd->speed = 2; - break; - default: - ecmd->port = PORT_TP; - ecmd->speed = SPEED_10; - break; - } - - if (de->macmode & FullDuplex) - ecmd->duplex = DUPLEX_FULL; - else - ecmd->duplex = DUPLEX_HALF; - - if (de->media_lock) - ecmd->autoneg = AUTONEG_DISABLE; - else - ecmd->autoneg = AUTONEG_ENABLE; - - /* ignore maxtxpkt, maxrxpkt for now */ - - return 0; -} - -static int de_ethtool_sset(struct de_private *de, struct ethtool_cmd *ecmd) -{ - u32 new_media; - unsigned int media_lock; - - if (ecmd->speed != SPEED_10 && ecmd->speed != 5 && ecmd->speed != 2) - return -EINVAL; - if (de->de21040 && ecmd->speed == 2) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI && ecmd->port != PORT_BNC) - return -EINVAL; - if (de->de21040 && ecmd->port == PORT_BNC) - return -EINVAL; - if (ecmd->transceiver != XCVR_INTERNAL) - return -EINVAL; - if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) - return -EINVAL; - if (ecmd->advertising & ~de->media_supported) - return -EINVAL; - if (ecmd->autoneg == AUTONEG_ENABLE && - (!(ecmd->advertising & ADVERTISED_Autoneg))) - return -EINVAL; - - switch (ecmd->port) { - case PORT_AUI: - new_media = DE_MEDIA_AUI; - if (!(ecmd->advertising & ADVERTISED_AUI)) - return -EINVAL; - break; - case PORT_BNC: - new_media = DE_MEDIA_BNC; - if (!(ecmd->advertising & ADVERTISED_BNC)) - return -EINVAL; - break; - default: - if (ecmd->autoneg == AUTONEG_ENABLE) - new_media = DE_MEDIA_TP_AUTO; - else if (ecmd->duplex == DUPLEX_FULL) - new_media = DE_MEDIA_TP_FD; - else - new_media = DE_MEDIA_TP; - if (!(ecmd->advertising & ADVERTISED_TP)) - return -EINVAL; - if (!(ecmd->advertising & (ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half))) - return -EINVAL; - break; - } - - media_lock = (ecmd->autoneg == AUTONEG_ENABLE) ? 0 : 1; - - if ((new_media == de->media_type) && - (media_lock == de->media_lock) && - (ecmd->advertising == de->media_advertise)) - return 0; /* nothing to change */ - - de_link_down(de); - de_stop_rxtx(de); - - de->media_type = new_media; - de->media_lock = media_lock; - de->media_advertise = ecmd->advertising; - de_set_media(de); - - return 0; -} - -static int de_ethtool_ioctl (struct de_private *de, void *useraddr) -{ - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, de->pdev->slot_name); - info.eedump_len = DE_EEPROM_SIZE; - info.regdump_len = DE_REGS_SIZE; - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&de->lock); - de_ethtool_gset(de, &ecmd); - spin_unlock_irq(&de->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - struct ethtool_cmd ecmd; - int r; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&de->lock); - r = de_ethtool_sset(de, &ecmd); - spin_unlock_irq(&de->lock); - return r; - } - - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - u32 status; - - if (de->media_type != DE_MEDIA_TP_AUTO) - return -EINVAL; - if (netif_carrier_ok(de->dev)) - de_link_down(de); - - status = dr32(SIAStatus); - dw32(SIAStatus, (status & ~NWayState) | NWayRestart); - if (netif_msg_link(de)) - printk(KERN_INFO "%s: link nway restart, status %x,%x\n", - de->dev->name, status, dr32(SIAStatus)); - return 0; - } - - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = (netif_carrier_ok(de->dev)) ? 1 : 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = de->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - de->msg_enable = edata.data; - return 0; - } - - /* get registers */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 regbuf[DE_REGS_SIZE]; - int r; - - if (copy_from_user(®s, useraddr, sizeof(regs))) - return -EFAULT; - - if (regs.len > DE_REGS_SIZE) { - regs.len = DE_REGS_SIZE; - } - regs.version = (DE_REGS_VER << 2) | de->de21040; - if (copy_to_user(useraddr, ®s, sizeof(regs))) - return -EFAULT; - - useraddr += offsetof(struct ethtool_regs, data); - - spin_lock_irq(&de->lock); - r = de_get_regs(de, regbuf); - spin_unlock_irq(&de->lock); - - if (r) - return r; - if (copy_to_user(useraddr, regbuf, regs.len)) - return -EFAULT; - return 0; - } - - /* get SROM dump */ - case ETHTOOL_GEEPROM: { - struct ethtool_eeprom eeprom; - - if (!de->ee_data) - break; - if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) - return -EFAULT; - if ((eeprom.offset != 0) || (eeprom.magic != 0) || - (eeprom.len != DE_EEPROM_SIZE)) - return -EINVAL; - - useraddr += offsetof(struct ethtool_regs, data); - if (copy_to_user(useraddr, de->ee_data, DE_EEPROM_SIZE)) - return -EFAULT; - } - - default: - break; - } - - return -EOPNOTSUPP; -} - - -static int de_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct de_private *de = dev->priv; - int rc = 0; - - if (!netif_running(dev)) - return -EINVAL; - - switch (cmd) { - case SIOCETHTOOL: - return de_ethtool_ioctl(de, (void *) rq->ifr_data); - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - -static void __init de21040_get_mac_address (struct de_private *de) -{ - unsigned i; - - dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ - - for (i = 0; i < 6; i++) { - int value, boguscnt = 100000; - do - value = dr32(ROMCmd); - while (value < 0 && --boguscnt > 0); - de->dev->dev_addr[i] = value; - if (boguscnt <= 0) - printk(KERN_WARNING PFX "timeout reading 21040 MAC address byte %u\n", i); - } -} - -static void __init de21040_get_media_info(struct de_private *de) -{ - unsigned int i; - - de->media_type = DE_MEDIA_TP; - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full | - SUPPORTED_10baseT_Half | SUPPORTED_AUI; - de->media_advertise = de->media_supported; - - for (i = 0; i < DE_MAX_MEDIA; i++) { - switch (i) { - case DE_MEDIA_AUI: - case DE_MEDIA_TP: - case DE_MEDIA_TP_FD: - de->media[i].type = i; - de->media[i].csr13 = t21040_csr13[i]; - de->media[i].csr14 = t21040_csr14[i]; - de->media[i].csr15 = t21040_csr15[i]; - break; - default: - de->media[i].type = DE_MEDIA_INVALID; - break; - } - } -} - -/* Note: this routine returns extra data bits for size detection. */ -static unsigned __init tulip_read_eeprom(void *regs, int location, int addr_len) -{ - int i; - unsigned retval = 0; - void *ee_addr = regs + ROMCmd; - int read_cmd = location | (EE_READ_CMD << addr_len); - - writel(EE_ENB & ~EE_CS, ee_addr); - writel(EE_ENB, ee_addr); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - writel(EE_ENB | dataval, ee_addr); - readl(ee_addr); - writel(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - readl(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); - } - writel(EE_ENB, ee_addr); - readl(ee_addr); - - for (i = 16; i > 0; i--) { - writel(EE_ENB | EE_SHIFT_CLK, ee_addr); - readl(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); - writel(EE_ENB, ee_addr); - readl(ee_addr); - } - - /* Terminate the EEPROM access. */ - writel(EE_ENB & ~EE_CS, ee_addr); - return retval; -} - -static void __init de21041_get_srom_info (struct de_private *de) -{ - unsigned i, sa_offset = 0, ofs; - u8 ee_data[DE_EEPROM_SIZE + 6] = {}; - unsigned ee_addr_size = tulip_read_eeprom(de->regs, 0xff, 8) & 0x40000 ? 8 : 6; - struct de_srom_info_leaf *il; - void *bufp; - - /* download entire eeprom */ - for (i = 0; i < DE_EEPROM_WORDS; i++) - ((u16 *)ee_data)[i] = - le16_to_cpu(tulip_read_eeprom(de->regs, i, ee_addr_size)); - - /* DEC now has a specification but early board makers - just put the address in the first EEPROM locations. */ - /* This does memcmp(eedata, eedata+16, 8) */ - for (i = 0; i < 8; i ++) - if (ee_data[i] != ee_data[16+i]) - sa_offset = 20; - - /* store MAC address */ - for (i = 0; i < 6; i ++) - de->dev->dev_addr[i] = ee_data[i + sa_offset]; - - /* get offset of controller 0 info leaf. ignore 2nd byte. */ - ofs = ee_data[SROMC0InfoLeaf]; - if (ofs >= (sizeof(ee_data) - sizeof(struct de_srom_info_leaf) - sizeof(struct de_srom_media_block))) - goto bad_srom; - - /* get pointer to info leaf */ - il = (struct de_srom_info_leaf *) &ee_data[ofs]; - - /* paranoia checks */ - if (il->n_blocks == 0) - goto bad_srom; - if ((sizeof(ee_data) - ofs) < - (sizeof(struct de_srom_info_leaf) + (sizeof(struct de_srom_media_block) * il->n_blocks))) - goto bad_srom; - - /* get default media type */ - switch (DE_UNALIGNED_16(&il->default_media)) { - case 0x0001: de->media_type = DE_MEDIA_BNC; break; - case 0x0002: de->media_type = DE_MEDIA_AUI; break; - case 0x0204: de->media_type = DE_MEDIA_TP_FD; break; - default: de->media_type = DE_MEDIA_TP_AUTO; break; - } - - if (netif_msg_probe(de)) - printk(KERN_INFO "de%d: SROM leaf offset %u, default media %s\n", - de->board_idx, ofs, - media_name[de->media_type]); - - /* init SIA register values to defaults */ - for (i = 0; i < DE_MAX_MEDIA; i++) { - de->media[i].type = DE_MEDIA_INVALID; - de->media[i].csr13 = 0xffff; - de->media[i].csr14 = 0xffff; - de->media[i].csr15 = 0xffff; - } - - /* parse media blocks to see what medias are supported, - * and if any custom CSR values are provided - */ - bufp = ((void *)il) + sizeof(*il); - for (i = 0; i < il->n_blocks; i++) { - struct de_srom_media_block *ib = bufp; - unsigned idx; - - /* index based on media type in media block */ - switch(ib->opts & MediaBlockMask) { - case 0: /* 10baseT */ - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half - | SUPPORTED_Autoneg; - idx = DE_MEDIA_TP; - de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; - break; - case 1: /* BNC */ - de->media_supported |= SUPPORTED_BNC; - idx = DE_MEDIA_BNC; - break; - case 2: /* AUI */ - de->media_supported |= SUPPORTED_AUI; - idx = DE_MEDIA_AUI; - break; - case 4: /* 10baseT-FD */ - de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full - | SUPPORTED_Autoneg; - idx = DE_MEDIA_TP_FD; - de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; - break; - default: - goto bad_srom; - } - - de->media[idx].type = idx; - - if (netif_msg_probe(de)) - printk(KERN_INFO "de%d: media block #%u: %s", - de->board_idx, i, - media_name[de->media[idx].type]); - - bufp += sizeof (ib->opts); - - if (ib->opts & MediaCustomCSRs) { - de->media[idx].csr13 = DE_UNALIGNED_16(&ib->csr13); - de->media[idx].csr14 = DE_UNALIGNED_16(&ib->csr14); - de->media[idx].csr15 = DE_UNALIGNED_16(&ib->csr15); - bufp += sizeof(ib->csr13) + sizeof(ib->csr14) + - sizeof(ib->csr15); - - if (netif_msg_probe(de)) - printk(" (%x,%x,%x)\n", - de->media[idx].csr13, - de->media[idx].csr14, - de->media[idx].csr15); - - } else if (netif_msg_probe(de)) - printk("\n"); - - if (bufp > ((void *)&ee_data[DE_EEPROM_SIZE - 3])) - break; - } - - de->media_advertise = de->media_supported; - -fill_defaults: - /* fill in defaults, for cases where custom CSRs not used */ - for (i = 0; i < DE_MAX_MEDIA; i++) { - if (de->media[i].csr13 == 0xffff) - de->media[i].csr13 = t21041_csr13[i]; - if (de->media[i].csr14 == 0xffff) - de->media[i].csr14 = t21041_csr14[i]; - if (de->media[i].csr15 == 0xffff) - de->media[i].csr15 = t21041_csr15[i]; - } - - de->ee_data = kmalloc(DE_EEPROM_SIZE, GFP_KERNEL); - if (de->ee_data) - memcpy(de->ee_data, &ee_data[0], DE_EEPROM_SIZE); - - return; - -bad_srom: - /* for error cases, it's ok to assume we support all these */ - for (i = 0; i < DE_MAX_MEDIA; i++) - de->media[i].type = i; - de->media_supported = - SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | - SUPPORTED_AUI | - SUPPORTED_BNC; - goto fill_defaults; -} - -static int __init de_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct de_private *de; - int rc; - void *regs; - long pciaddr; - static int board_idx = -1; - - board_idx++; - -#ifndef MODULE - if (board_idx == 0) - printk("%s", version); -#endif - - /* allocate a new ethernet device structure, and fill in defaults */ - dev = alloc_etherdev(sizeof(struct de_private)); - if (!dev) - return -ENOMEM; - - SET_MODULE_OWNER(dev); - dev->open = de_open; - dev->stop = de_close; - dev->set_multicast_list = de_set_rx_mode; - dev->hard_start_xmit = de_start_xmit; - dev->get_stats = de_get_stats; - dev->do_ioctl = de_ioctl; - dev->tx_timeout = de_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->irq = pdev->irq; - - de = dev->priv; - de->de21040 = ent->driver_data == 0 ? 1 : 0; - de->pdev = pdev; - de->dev = dev; - de->msg_enable = (debug < 0 ? DE_DEF_MSG_ENABLE : debug); - de->board_idx = board_idx; - spin_lock_init (&de->lock); - init_timer(&de->media_timer); - if (de->de21040) - de->media_timer.function = de21040_media_timer; - else - de->media_timer.function = de21041_media_timer; - de->media_timer.data = (unsigned long) de; - - netif_carrier_off(dev); - netif_stop_queue(dev); - - /* wake up device, assign resources */ - rc = pci_enable_device(pdev); - if (rc) - goto err_out_free; - - /* reserve PCI resources to ensure driver atomicity */ - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto err_out_disable; - - /* check for invalid IRQ value */ - if (pdev->irq < 2) { - rc = -EIO; - printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", - pdev->irq, pdev->slot_name); - goto err_out_res; - } - - /* obtain and check validity of PCI I/O address */ - pciaddr = pci_resource_start(pdev, 1); - if (!pciaddr) { - rc = -EIO; - printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", - pdev->slot_name); - goto err_out_res; - } - if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) { - rc = -EIO; - printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n", - pci_resource_len(pdev, 1), pdev->slot_name); - goto err_out_res; - } - - /* remap CSR registers */ - regs = ioremap_nocache(pciaddr, DE_REGS_SIZE); - if (!regs) { - rc = -EIO; - printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", - pci_resource_len(pdev, 1), pciaddr, pdev->slot_name); - goto err_out_res; - } - dev->base_addr = (unsigned long) regs; - de->regs = regs; - - de_adapter_wake(de); - - /* make sure hardware is not running */ - rc = de_reset_mac(de); - if (rc) { - printk(KERN_ERR PFX "Cannot reset MAC, pci dev %s\n", - pdev->slot_name); - goto err_out_iomap; - } - - /* get MAC address, initialize default media type and - * get list of supported media - */ - if (de->de21040) { - de21040_get_mac_address(de); - de21040_get_media_info(de); - } else { - de21041_get_srom_info(de); - } - - /* register new network interface with kernel */ - rc = register_netdev(dev); - if (rc) - goto err_out_iomap; - - /* print info about board and interface just registered */ - printk (KERN_INFO "%s: %s at 0x%lx, " - "%02x:%02x:%02x:%02x:%02x:%02x, " - "IRQ %d\n", - dev->name, - de->de21040 ? "21040" : "21041", - dev->base_addr, - 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->irq); - - pci_set_drvdata(pdev, dev); - - /* enable busmastering */ - pci_set_master(pdev); - - /* put adapter to sleep */ - de_adapter_sleep(de); - - return 0; - -err_out_iomap: - if (de->ee_data) - kfree(de->ee_data); - iounmap(regs); -err_out_res: - pci_release_regions(pdev); -err_out_disable: - pci_disable_device(pdev); -err_out_free: - kfree(dev); - return rc; -} - -static void __exit de_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct de_private *de = dev->priv; - - if (!dev) - BUG(); - unregister_netdev(dev); - if (de->ee_data) - kfree(de->ee_data); - iounmap(de->regs); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - kfree(dev); -} - -#ifdef CONFIG_PM - -static int de_suspend (struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct de_private *de = dev->priv; - - rtnl_lock(); - if (netif_running (dev)) { - del_timer_sync(&de->media_timer); - - disable_irq(dev->irq); - spin_lock_irq(&de->lock); - - de_stop_hw(de); - netif_stop_queue(dev); - netif_device_detach(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&de->lock); - enable_irq(dev->irq); - - /* Update the error counts. */ - __de_get_stats(de); - - synchronize_irq(); - de_clean_rings(de); - - de_adapter_sleep(de); - pci_disable_device(pdev); - } else { - netif_device_detach(dev); - } - rtnl_unlock(); - return 0; -} - -static int de_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct de_private *de = dev->priv; - - rtnl_lock(); - if (netif_device_present(dev)) - goto out; - if (netif_running(dev)) { - pci_enable_device(pdev); - de_init_hw(de); - netif_device_attach(dev); - } else { - netif_device_attach(dev); - } -out: - rtnl_unlock(); - return 0; -} - -#endif /* CONFIG_PM */ - -static struct pci_driver de_driver = { - name: DRV_NAME, - id_table: de_pci_tbl, - probe: de_init_one, - remove: de_remove_one, -#ifdef CONFIG_PM - suspend: de_suspend, - resume: de_resume, -#endif -}; - -static int __init de_init (void) -{ -#ifdef MODULE - printk("%s", version); -#endif - return pci_module_init (&de_driver); -} - -static void __exit de_exit (void) -{ - pci_unregister_driver (&de_driver); -} - -module_init(de_init); -module_exit(de_exit); diff -urN linux-2.5.6-pre3/drivers/net/de4x5.c linux-2.5.6/drivers/net/de4x5.c --- linux-2.5.6-pre3/drivers/net/de4x5.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/drivers/net/de4x5.c Wed Dec 31 16:00:00 1969 @@ -1,5918 +0,0 @@ -/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 - ethernet driver for Linux. - - Copyright 1994, 1995 Digital Equipment Corporation. - - Testing resources for this driver have been made available - in part by NASA Ames Research Center (mjacob@nas.nasa.gov). - - The author may be reached at davies@maniac.ultranet.com. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - 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., - 675 Mass Ave, Cambridge, MA 02139, USA. - - Originally, this driver was written for the Digital Equipment - Corporation series of EtherWORKS ethernet cards: - - DE425 TP/COAX EISA - DE434 TP PCI - DE435 TP/COAX/AUI PCI - DE450 TP/COAX/AUI PCI - DE500 10/100 PCI Fasternet - - but it will now attempt to support all cards which conform to the - Digital Semiconductor SROM Specification. The driver currently - recognises the following chips: - - DC21040 (no SROM) - DC21041[A] - DC21140[A] - DC21142 - DC21143 - - So far the driver is known to work with the following cards: - - KINGSTON - Linksys - ZNYX342 - SMC8432 - SMC9332 (w/new SROM) - ZNYX31[45] - ZNYX346 10/100 4 port (can act as a 10/100 bridge!) - - The driver has been tested on a relatively busy network using the DE425, - DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred - 16M of data to a DECstation 5000/200 as follows: - - TCP UDP - TX RX TX RX - DE425 1030k 997k 1170k 1128k - DE434 1063k 995k 1170k 1125k - DE435 1063k 995k 1170k 1125k - DE500 1063k 998k 1170k 1125k in 10Mb/s mode - - All values are typical (in kBytes/sec) from a sample of 4 for each - measurement. Their error is +/-20k on a quiet (private) network and also - depend on what load the CPU has. - - ========================================================================= - This driver has been written substantially from scratch, although its - inheritance of style and stack interface from 'ewrk3.c' and in turn from - Donald Becker's 'lance.c' should be obvious. With the module autoload of - every usable DECchip board, I pinched Donald's 'next_module' field to - link my modules together. - - Upto 15 EISA cards can be supported under this driver, limited primarily - by the available IRQ lines. I have checked different configurations of - multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a - problem yet (provided you have at least depca.c v0.38) ... - - PCI support has been added to allow the driver to work with the DE434, - DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due - to the differences in the EISA and PCI CSR address offsets from the base - address. - - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). Loadable module support under PCI and EISA has been - achieved by letting the driver autoprobe as if it were compiled into the - kernel. Do make sure you're not sharing interrupts with anything that - cannot accommodate interrupt sharing! - - To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy de4x5.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) for fixed autoprobes (not recommended), edit the source code near - line 5594 to reflect the I/O address you're using, or assign these when - loading by: - - insmod de4x5 io=0xghh where g = bus number - hh = device number - - NB: autoprobing for modules is now supported by default. You may just - use: - - insmod de4x5 - - to load all available boards. For a specific board, still use - the 'io=?' above. - 3) compile de4x5.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the de4x5 configuration turned off and reboot. - 5) insmod de4x5 [io=0xghh] - 6) run the net startup bits for your new eth?? interface(s) manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - To unload a module, turn off the associated interface(s) - 'ifconfig eth?? down' then 'rmmod de4x5'. - - Automedia detection is included so that in principal you can disconnect - from, e.g. TP, reconnect to BNC and things will still work (after a - pause whilst the driver figures out where its media went). My tests - using ping showed that it appears to work.... - - By default, the driver will now autodetect any DECchip based card. - Should you have a need to restrict the driver to DIGITAL only cards, you - can compile with a DEC_ONLY define, or if loading as a module, use the - 'dec_only=1' parameter. - - I've changed the timing routines to use the kernel timer and scheduling - functions so that the hangs and other assorted problems that occurred - while autosensing the media should be gone. A bonus for the DC21040 - auto media sense algorithm is that it can now use one that is more in - line with the rest (the DC21040 chip doesn't have a hardware timer). - The downside is the 1 'jiffies' (10ms) resolution. - - IEEE 802.3u MII interface code has been added in anticipation that some - products may use it in the future. - - The SMC9332 card has a non-compliant SROM which needs fixing - I have - patched this driver to detect it because the SROM format used complies - to a previous DEC-STD format. - - I have removed the buffer copies needed for receive on Intels. I cannot - remove them for Alphas since the Tulip hardware only does longword - aligned DMA transfers and the Alphas get alignment traps with non - longword aligned data copies (which makes them really slow). No comment. - - I have added SROM decoding routines to make this driver work with any - card that supports the Digital Semiconductor SROM spec. This will help - all cards running the dc2114x series chips in particular. Cards using - the dc2104x chips should run correctly with the basic driver. I'm in - debt to for the testing and feedback that helped get - this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 - (with the latest SROM complying with the SROM spec V3: their first was - broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 - (quad 21041 MAC) cards also appear to work despite their incorrectly - wired IRQs. - - I have added a temporary fix for interrupt problems when some SCSI cards - share the same interrupt as the DECchip based cards. The problem occurs - because the SCSI card wants to grab the interrupt as a fast interrupt - (runs the service routine with interrupts turned off) vs. this card - which really needs to run the service routine with interrupts turned on. - This driver will now add the interrupt service routine as a fast - interrupt if it is bounced from the slow interrupt. THIS IS NOT A - RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time - until people sort out their compatibility issues and the kernel - interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST - INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not - run on the same interrupt. PCMCIA/CardBus is another can of worms... - - Finally, I think I have really fixed the module loading problem with - more than one DECchip based card. As a side effect, I don't mess with - the device structure any more which means that if more than 1 card in - 2.0.x is installed (4 in 2.1.x), the user will have to edit - linux/drivers/net/Space.c to make room for them. Hence, module loading - is the preferred way to use this driver, since it doesn't have this - limitation. - - Where SROM media detection is used and full duplex is specified in the - SROM, the feature is ignored unless lp->params.fdx is set at compile - time OR during a module load (insmod de4x5 args='eth??:fdx' [see - below]). This is because there is no way to automatically detect full - duplex links except through autonegotiation. When I include the - autonegotiation feature in the SROM autoconf code, this detection will - occur automatically for that case. - - Command line arguments are now allowed, similar to passing arguments - through LILO. This will allow a per adapter board set up of full duplex - and media. The only lexical constraints are: the board name (dev->name) - appears in the list before its parameters. The list of parameters ends - either at the end of the parameter list or with another board name. The - following parameters are allowed: - - fdx for full duplex - autosense to set the media/speed; with the following - sub-parameters: - TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO - - Case sensitivity is important for the sub-parameters. They *must* be - upper case. Examples: - - insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. - - For a compiled in driver, at or above line 548, place e.g. - #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" - - Yes, I know full duplex isn't permissible on BNC or AUI; they're just - examples. By default, full duplex is turned off and AUTO is the default - autosense setting. In reality, I expect only the full duplex option to - be used. Note the use of single quotes in the two examples above and the - lack of commas to separate items. ALSO, you must get the requested media - correct in relation to what the adapter SROM says it has. There's no way - to determine this in advance other than by trial and error and common - sense, e.g. call a BNC connectored port 'BNC', not '10Mb'. - - Changed the bus probing. EISA used to be done first, followed by PCI. - Most people probably don't even know what a de425 is today and the EISA - probe has messed up some SCSI cards in the past, so now PCI is always - probed first followed by EISA if a) the architecture allows EISA and - either b) there have been no PCI cards detected or c) an EISA probe is - forced by the user. To force a probe include "force_eisa" in your - insmod "args" line; for built-in kernels either change the driver to do - this automatically or include #define DE4X5_FORCE_EISA on or before - line 1040 in the driver. - - TO DO: - ------ - - Revision History - ---------------- - - Version Date Description - - 0.1 17-Nov-94 Initial writing. ALPHA code release. - 0.2 13-Jan-95 Added PCI support for DE435's. - 0.21 19-Jan-95 Added auto media detection. - 0.22 10-Feb-95 Fix interrupt handler call . - Fix recognition bug reported by . - Add request/release_region code. - Add loadable modules support for PCI. - Clean up loadable modules support. - 0.23 28-Feb-95 Added DC21041 and DC21140 support. - Fix missed frame counter value and initialisation. - Fixed EISA probe. - 0.24 11-Apr-95 Change delay routine to use . - Change TX_BUFFS_AVAIL macro. - Change media autodetection to allow manual setting. - Completed DE500 (DC21140) support. - 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm. - 0.242 10-May-95 Minor changes. - 0.30 12-Jun-95 Timer fix for DC21140. - Portability changes. - Add ALPHA changes from . - Add DE500 semi automatic autosense. - Add Link Fail interrupt TP failure detection. - Add timer based link change detection. - Plugged a memory leak in de4x5_queue_pkt(). - 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1. - 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a - suggestion by . - 0.33 8-Aug-95 Add shared interrupt support (not released yet). - 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs. - Fix de4x5_interrupt(). - Fix dc21140_autoconf() mess. - No shared interrupt support. - 0.332 11-Sep-95 Added MII management interface routines. - 0.40 5-Mar-96 Fix setup frame timeout . - Add kernel timer code (h/w is too flaky). - Add MII based PHY autosense. - Add new multicasting code. - Add new autosense algorithms for media/mode - selection using kernel scheduling/timing. - Re-formatted. - Made changes suggested by : - Change driver to detect all DECchip based cards - with DEC_ONLY restriction a special case. - Changed driver to autoprobe as a module. No irq - checking is done now - assume BIOS is good! - Added SMC9332 detection - 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card - only - Fix for multiple PCI cards reported by - Duh, put the SA_SHIRQ flag into request_interrupt(). - Fix SMC ethernet address in enet_det[]. - Print chip name instead of "UNKNOWN" during boot. - 0.42 26-Apr-96 Fix MII write TA bit error. - Fix bug in dc21040 and dc21041 autosense code. - Remove buffer copies on receive for Intels. - Change sk_buff handling during media disconnects to - eliminate DUP packets. - Add dynamic TX thresholding. - Change all chips to use perfect multicast filtering. - Fix alloc_device() bug - 0.43 21-Jun-96 Fix unconnected media TX retry bug. - Add Accton to the list of broken cards. - Fix TX under-run bug for non DC21140 chips. - Fix boot command probe bug in alloc_device() as - reported by and - . - Add cache locks to prevent a race condition as - reported by and - . - Upgraded alloc_device() code. - 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion - with - 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. - Fix EISA probe bugs reported by - and . - 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media - with a loopback packet. - 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported - by - 0.45 8-Dec-96 Include endian functions for PPC use, from work - by and . - 0.451 28-Dec-96 Added fix to allow autoprobe for modules after - suggestion from . - 0.5 30-Jan-97 Added SROM decoding functions. - Updated debug flags. - Fix sleep/wakeup calls for PCI cards, bug reported - by . - Added multi-MAC, one SROM feature from discussion - with . - Added full module autoprobe capability. - Added attempt to use an SMC9332 with broken SROM. - Added fix for ZYNX multi-mac cards that didn't - get their IRQs wired correctly. - 0.51 13-Feb-97 Added endian fixes for the SROM accesses from - - Fix init_connection() to remove extra device reset. - Fix MAC/PHY reset ordering in dc21140m_autoconf(). - Fix initialisation problem with lp->timeout in - typeX_infoblock() from . - Fix MII PHY reset problem from work done by - . - 0.52 26-Apr-97 Some changes may not credit the right people - - a disk crash meant I lost some mail. - Change RX interrupt routine to drop rather than - defer packets to avoid hang reported by - . - Fix srom_exec() to return for COMPACT and type 1 - infoblocks. - Added DC21142 and DC21143 functions. - Added byte counters from - Added SA_INTERRUPT temporary fix from - . - 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during - module load: bug reported by - - Fix multi-MAC, one SROM, to work with 2114x chips: - bug reported by . - Make above search independent of BIOS device scan - direction. - Completed DC2114[23] autosense functions. - 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by - and - . - Added argument list to set up each board from either - a module's command line or a compiled in #define. - Added generic MII PHY functionality to deal with - newer PHY chips. - Fix the mess in 2.1.67. - 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by - . - Fix bug in pci_probe() for 64 bit systems reported - by . - 0.533 9-Jan-98 Fix more 64 bit bugs reported by . - 0.534 24-Jan-98 Fix last (?) endian bug from - 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. - 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. - **Incompatible with 2.0.x from here.** - 0.540 5-Jul-98 Atomicize assertion of dev->interrupt for SMP - from - Add TP, AUI and BNC cases to 21140m_autoconf() for - case where a 21140 under SROM control uses, e.g. AUI - from problem report by - Add MII parallel detection to 2114x_autoconf() for - case where no autonegotiation partner exists from - problem report by . - Add ability to force connection type directly even - when using SROM control from problem report by - . - Updated the PCI interface to conform with the latest - version. I hope nothing is broken... - Add TX done interrupt modification from suggestion - by . - Fix is_anc_capable() bug reported by - . - Fix type[13]_infoblock() bug: during MII search, PHY - lp->rst not run because lp->ibn not initialised - - from report & fix by . - Fix probe bug with EISA & PCI cards present from - report by . - 0.541 24-Aug-98 Fix compiler problems associated with i386-string - ops from multiple bug reports and temporary fix - from . - Fix pci_probe() to correctly emulate the old - pcibios_find_class() function. - Add an_exception() for old ZYNX346 and fix compile - warning on PPC & SPARC, from . - Fix lastPCI to correctly work with compiled in - kernels and modules from bug report by - et al. - 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages - when media is unconnected. - Change dev->interrupt to lp->interrupt to ensure - alignment for Alpha's and avoid their unaligned - access traps. This flag is merely for log messages: - should do something more definitive though... - 0.543 30-Dec-98 Add SMP spin locking. - 0.544 8-May-99 Fix for buggy SROM in Motorola embedded boards using - a 21143 by . - Change PCI/EISA bus probing order. - 0.545 28-Nov-99 Further Moto SROM bug fix from - - Remove double checking for DEBUG_RX in de4x5_dbg_rx() - from report by - 0.546 22-Feb-01 Fixes Alpha XP1000 oops. The srom_search function - was causing a page fault when initializing the - variable 'pb', on a non de4x5 PCI device, in this - case a PCI bridge (DEC chip 21152). The value of - 'pb' is now only initialized if a de4x5 chip is - present. - - 0.547 08-Nov-01 Use library crc32 functions by - ========================================================================= -*/ - -static const char *version = "de4x5.c:V0.546 2001/02/22 davies@maniac.ultranet.com\n"; - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_PPC -#include -#endif /* CONFIG_PPC */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "de4x5.h" - -#define c_char const char -#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) - -/* -** MII Information -*/ -struct phy_table { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time - 802.3u is confusing here */ - struct { /* Non autonegotiation (parallel) speed det. */ - int reg; - int mask; - int value; - } spd; -}; - -struct mii_phy { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time */ - struct { /* Non autonegotiation (parallel) speed det. */ - int reg; - int mask; - int value; - } spd; - int addr; /* MII address for the PHY */ - u_char *gep; /* Start of GEP sequence block in SROM */ - u_char *rst; /* Start of reset sequence in SROM */ - u_int mc; /* Media Capabilities */ - u_int ana; /* NWay Advertisement */ - u_int fdx; /* Full DupleX capabilites for each media */ - u_int ttm; /* Transmit Threshold Mode for each media */ - u_int mci; /* 21142 MII Connector Interrupt info */ -}; - -#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ - -struct sia_phy { - u_char mc; /* Media Code */ - u_char ext; /* csr13-15 valid when set */ - int csr13; /* SIA Connectivity Register */ - int csr14; /* SIA TX/RX Register */ - int csr15; /* SIA General Register */ - int gepc; /* SIA GEP Control Information */ - int gep; /* SIA GEP Data */ -}; - -/* -** Define the know universe of PHY devices that can be -** recognised by this driver. -*/ -static struct phy_table phy_info[] = { - {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ - {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ - {0, 0x7810 , 1, {0x14, 0x0800, 0x0800}} /* Level One LTX970 */ -}; - -/* -** These GENERIC values assumes that the PHY devices follow 802.3u and -** allow parallel detection to set the link partner ability register. -** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. -*/ -#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ -#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ -#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ - -/* -** Define special SROM detection cases -*/ -static c_char enet_det[][ETH_ALEN] = { - {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} -}; - -#define SMC 1 -#define ACCTON 2 - -/* -** SROM Repair definitions. If a broken SROM is detected a card may -** use this information to help figure out what to do. This is a -** "stab in the dark" and so far for SMC9332's only. -*/ -static c_char srom_repair_info[][100] = { - {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ - 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, - 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, - 0x00,0x18,} -}; - - -#ifdef DE4X5_DEBUG -static int de4x5_debug = DE4X5_DEBUG; -#else -/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ -static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); -#endif - -/* -** Allow per adapter set up. For modules this is simply a command line -** parameter, e.g.: -** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. -** -** For a compiled in driver, place e.g. -** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" -** here -*/ -#ifdef DE4X5_PARM -static char *args = DE4X5_PARM; -#else -static char *args; -#endif - -struct parameters { - int fdx; - int autosense; -}; - -#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ - -#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ - -/* -** Ethernet PROM defines -*/ -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -/* -** Ethernet Info -*/ -#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */ -#define IEEE802_3_SZ 1518 /* Packet + CRC */ -#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */ -#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */ -#define MIN_DAT_SZ 1 /* Minimum ethernet data length */ -#define PKT_HDR_LEN 14 /* Addresses and data length info */ -#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1) -#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */ - - -/* -** EISA bus defines -*/ -#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ -#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */ - -#define MAX_EISA_SLOTS 16 -#define EISA_SLOT_INC 0x1000 -#define EISA_ALLOWED_IRQ_LIST {5, 9, 10, 11} - -#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} -#define DE4X5_NAME_LENGTH 8 - -/* -** Ethernet PROM defines for DC21040 -*/ -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -/* -** PCI Bus defines -*/ -#define PCI_MAX_BUS_NUM 8 -#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ -#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ -#define NO_MORE_PCI -2 /* PCI bus search all done */ - -/* -** Memory Alignment. Each descriptor is 4 longwords long. To force a -** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and -** DESC_ALIGN. ALIGN aligns the start address of the private memory area -** and hence the RX descriptor ring's first entry. -*/ -#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */ -#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */ -#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */ -#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */ -#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */ -#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */ - -#define ALIGN ALIGN32 /* Keep the DC21040 happy... */ -#define CACHE_ALIGN CAL_16LONG -#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */ -/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */ -#define DESC_ALIGN - -#ifndef DEC_ONLY /* See README.de4x5 for using this */ -static int dec_only; -#else -static int dec_only = 1; -#endif - -/* -** DE4X5 IRQ ENABLE/DISABLE -*/ -#define ENABLE_IRQs { \ - imr |= lp->irq_en;\ - outl(imr, DE4X5_IMR); /* Enable the IRQs */\ -} - -#define DISABLE_IRQs {\ - imr = inl(DE4X5_IMR);\ - imr &= ~lp->irq_en;\ - outl(imr, DE4X5_IMR); /* Disable the IRQs */\ -} - -#define UNMASK_IRQs {\ - imr |= lp->irq_mask;\ - outl(imr, DE4X5_IMR); /* Unmask the IRQs */\ -} - -#define MASK_IRQs {\ - imr = inl(DE4X5_IMR);\ - imr &= ~lp->irq_mask;\ - outl(imr, DE4X5_IMR); /* Mask the IRQs */\ -} - -/* -** DE4X5 START/STOP -*/ -#define START_DE4X5 {\ - omr = inl(DE4X5_OMR);\ - omr |= OMR_ST | OMR_SR;\ - outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\ -} - -#define STOP_DE4X5 {\ - omr = inl(DE4X5_OMR);\ - omr &= ~(OMR_ST|OMR_SR);\ - outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ -} - -/* -** DE4X5 SIA RESET -*/ -#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */ - -/* -** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) -*/ -#define DE4X5_AUTOSENSE_MS 250 - -/* -** SROM Structure -*/ -struct de4x5_srom { - char sub_vendor_id[2]; - char sub_system_id[2]; - char reserved[12]; - char id_block_crc; - char reserved2; - char version; - char num_controllers; - char ieee_addr[6]; - char info[100]; - short chksum; -}; -#define SUB_VENDOR_ID 0x500a - -/* -** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous -** and have sizes of both a power of 2 and a multiple of 4. -** A size of 256 bytes for each buffer could be chosen because over 90% of -** all packets in our network are <256 bytes long and 64 longword alignment -** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX -** descriptors are needed for machines with an ALPHA CPU. -*/ -#define NUM_RX_DESC 8 /* Number of RX descriptors */ -#define NUM_TX_DESC 32 /* Number of TX descriptors */ -#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */ - /* Multiple of 4 for DC21040 */ - /* Allows 512 byte alignment */ -struct de4x5_desc { - volatile s32 status; - u32 des1; - u32 buf; - u32 next; - DESC_ALIGN -}; - -/* -** The DE4X5 private structure -*/ -#define DE4X5_PKT_STAT_SZ 16 -#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase DE4X5_PKT_STAT_SZ */ - -struct pkt_stats { - u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ - u_int unicast; - u_int multicast; - u_int broadcast; - u_int excessive_collisions; - u_int tx_underruns; - u_int excessive_underruns; - u_int rx_runt_frames; - u_int rx_collision; - u_int rx_dribble; - u_int rx_overflow; -}; - -struct de4x5_private { - char adapter_name[80]; /* Adapter name */ - u_long interrupt; /* Aligned ISR flag */ - struct de4x5_desc *rx_ring; /* RX descriptor ring */ - struct de4x5_desc *tx_ring; /* TX descriptor ring */ - struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ - struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */ - int rx_new, rx_old; /* RX descriptor ring pointers */ - int tx_new, tx_old; /* TX descriptor ring pointers */ - char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ - char frame[64]; /* Min sized packet for loopback*/ - spinlock_t lock; /* Adapter specific spinlock */ - struct net_device_stats stats; /* Public stats */ - struct pkt_stats pktStats; /* Private stats counters */ - char rxRingSize; - char txRingSize; - int bus; /* EISA or PCI */ - int bus_num; /* PCI Bus number */ - int device; /* Device number on PCI bus */ - int state; /* Adapter OPENED or CLOSED */ - int chipset; /* DC21040, DC21041 or DC21140 */ - s32 irq_mask; /* Interrupt Mask (Enable) bits */ - s32 irq_en; /* Summary interrupt bits */ - int media; /* Media (eg TP), mode (eg 100B)*/ - int c_media; /* Remember the last media conn */ - int fdx; /* media full duplex flag */ - int linkOK; /* Link is OK */ - int autosense; /* Allow/disallow autosensing */ - int tx_enable; /* Enable descriptor polling */ - int setup_f; /* Setup frame filtering type */ - int local_state; /* State within a 'media' state */ - struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ - struct sia_phy sia; /* SIA PHY Information */ - int active; /* Index to active PHY device */ - int mii_cnt; /* Number of attached PHY's */ - int timeout; /* Scheduling counter */ - struct timer_list timer; /* Timer info for kernel */ - int tmp; /* Temporary global per card */ - struct { - void *priv; /* Original kmalloc'd mem addr */ - u_long lock; /* Lock the cache accesses */ - s32 csr0; /* Saved Bus Mode Register */ - s32 csr6; /* Saved Operating Mode Reg. */ - s32 csr7; /* Saved IRQ Mask Register */ - s32 gep; /* Saved General Purpose Reg. */ - s32 gepc; /* Control info for GEP */ - s32 csr13; /* Saved SIA Connectivity Reg. */ - s32 csr14; /* Saved SIA TX/RX Register */ - s32 csr15; /* Saved SIA General Register */ - int save_cnt; /* Flag if state already saved */ - struct sk_buff *skb; /* Save the (re-ordered) skb's */ - } cache; - struct de4x5_srom srom; /* A copy of the SROM */ - struct net_device *next_module; /* Link to the next module */ - int rx_ovf; /* Check for 'RX overflow' tag */ - int useSROM; /* For non-DEC card use SROM */ - int useMII; /* Infoblock using the MII */ - int asBitValid; /* Autosense bits in GEP? */ - int asPolarity; /* 0 => asserted high */ - int asBit; /* Autosense bit number in GEP */ - int defMedium; /* SROM default medium */ - int tcount; /* Last infoblock number */ - int infoblock_init; /* Initialised this infoblock? */ - int infoleaf_offset; /* SROM infoleaf for controller */ - s32 infoblock_csr6; /* csr6 value in SROM infoblock */ - int infoblock_media; /* infoblock media */ - int (*infoleaf_fn)(struct net_device *); /* Pointer to infoleaf function */ - u_char *rst; /* Pointer to Type 5 reset info */ - u_char ibn; /* Infoblock number */ - struct parameters params; /* Command line/ #defined params */ - struct pci_dev *pdev; /* Device cookie for DMA alloc */ - dma_addr_t dma_rings; /* DMA handle for rings */ - int dma_size; /* Size of the DMA area */ - char *rx_bufs; /* rx bufs on alpha, sparc, ... */ -}; - -/* -** Kludge to get around the fact that the CSR addresses have different -** offsets in the PCI and EISA boards. Also note that the ethernet address -** PROM is accessed differently. -*/ -static struct bus_type { - int bus; - int bus_num; - int device; - int chipset; - struct de4x5_srom srom; - int autosense; - int useSROM; -} bus; - -/* -** To get around certain poxy cards that don't provide an SROM -** for the second and more DECchip, I have to key off the first -** chip's address. I'll assume there's not a bad SROM iff: -** -** o the chipset is the same -** o the bus number is the same and > 0 -** o the sum of all the returned hw address bytes is 0 or 0x5fa -** -** Also have to save the irq for those cards whose hardware designers -** can't follow the PCI to PCI Bridge Architecture spec. -*/ -static struct { - int chipset; - int bus; - int irq; - u_char addr[ETH_ALEN]; -} last = {0,}; - -/* -** The transmit ring full condition is described by the tx_old and tx_new -** pointers by: -** tx_old = tx_new Empty ring -** tx_old = tx_new+1 Full ring -** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition) -*/ -#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ - lp->tx_old+lp->txRingSize-lp->tx_new-1:\ - lp->tx_old -lp->tx_new-1) - -#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) - -/* -** Public Functions -*/ -static int de4x5_open(struct net_device *dev); -static int de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev); -static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int de4x5_close(struct net_device *dev); -static struct net_device_stats *de4x5_get_stats(struct net_device *dev); -static void de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len); -static void set_multicast_list(struct net_device *dev); -static int de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -/* -** Private functions -*/ -static int de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev); -static int de4x5_init(struct net_device *dev); -static int de4x5_sw_reset(struct net_device *dev); -static int de4x5_rx(struct net_device *dev); -static int de4x5_tx(struct net_device *dev); -static int de4x5_ast(struct net_device *dev); -static int de4x5_txur(struct net_device *dev); -static int de4x5_rx_ovfc(struct net_device *dev); - -static int autoconf_media(struct net_device *dev); -static void create_packet(struct net_device *dev, char *frame, int len); -static void load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb); -static int dc21040_autoconf(struct net_device *dev); -static int dc21041_autoconf(struct net_device *dev); -static int dc21140m_autoconf(struct net_device *dev); -static int dc2114x_autoconf(struct net_device *dev); -static int srom_autoconf(struct net_device *dev); -static int de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, int (*fn)(struct net_device *, int), int (*asfn)(struct net_device *)); -static int dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct net_device *, int)); -static int test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); -static int test_for_100Mb(struct net_device *dev, int msec); -static int wait_for_link(struct net_device *dev); -static int test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec); -static int is_spd_100(struct net_device *dev); -static int is_100_up(struct net_device *dev); -static int is_10_up(struct net_device *dev); -static int is_anc_capable(struct net_device *dev); -static int ping_media(struct net_device *dev, int msec); -static struct sk_buff *de4x5_alloc_rx_buff(struct net_device *dev, int index, int len); -static void de4x5_free_rx_buffs(struct net_device *dev); -static void de4x5_free_tx_buffs(struct net_device *dev); -static void de4x5_save_skbs(struct net_device *dev); -static void de4x5_rst_desc_ring(struct net_device *dev); -static void de4x5_cache_state(struct net_device *dev, int flag); -static void de4x5_put_cache(struct net_device *dev, struct sk_buff *skb); -static void de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb); -static struct sk_buff *de4x5_get_cache(struct net_device *dev); -static void de4x5_setup_intr(struct net_device *dev); -static void de4x5_init_connection(struct net_device *dev); -static int de4x5_reset_phy(struct net_device *dev); -static void reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 sigr); -static int test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec); -static int test_tp(struct net_device *dev, s32 msec); -static int EISA_signature(char *name, s32 eisa_id); -static int PCI_signature(char *name, struct bus_type *lp); -static void DevicePresent(u_long iobase); -static void enet_addr_rst(u_long aprom_addr); -static int de4x5_bad_srom(struct bus_type *lp); -static short srom_rd(u_long address, u_char offset); -static void srom_latch(u_int command, u_long address); -static void srom_command(u_int command, u_long address); -static void srom_address(u_int command, u_long address, u_char offset); -static short srom_data(u_int command, u_long address); -/*static void srom_busy(u_int command, u_long address);*/ -static void sendto_srom(u_int command, u_long addr); -static int getfrom_srom(u_long addr); -static int srom_map_media(struct net_device *dev); -static int srom_infoleaf_info(struct net_device *dev); -static void srom_init(struct net_device *dev); -static void srom_exec(struct net_device *dev, u_char *p); -static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); -static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); -static int mii_rdata(u_long ioaddr); -static void mii_wdata(int data, int len, u_long ioaddr); -static void mii_ta(u_long rw, u_long ioaddr); -static int mii_swap(int data, int len); -static void mii_address(u_char addr, u_long ioaddr); -static void sendto_mii(u32 command, int data, u_long ioaddr); -static int getfrom_mii(u32 command, u_long ioaddr); -static int mii_get_oui(u_char phyaddr, u_long ioaddr); -static int mii_get_phy(struct net_device *dev); -static void SetMulticastFilter(struct net_device *dev); -static int get_hw_addr(struct net_device *dev); -static void srom_repair(struct net_device *dev, int card); -static int test_bad_enet(struct net_device *dev, int status); -static int an_exception(struct bus_type *lp); -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) -static void eisa_probe(struct net_device *dev, u_long iobase); -#endif -static void pci_probe(struct net_device *dev, u_long iobase); -static void srom_search(struct pci_dev *pdev); -static char *build_setup_frame(struct net_device *dev, int mode); -static void disable_ast(struct net_device *dev); -static void enable_ast(struct net_device *dev, u32 time_out); -static long de4x5_switch_mac_port(struct net_device *dev); -static int gep_rd(struct net_device *dev); -static void gep_wr(s32 data, struct net_device *dev); -static void timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec); -static void yawn(struct net_device *dev, int state); -static void link_modules(struct net_device *dev, struct net_device *tmp); -static void de4x5_parse_params(struct net_device *dev); -static void de4x5_dbg_open(struct net_device *dev); -static void de4x5_dbg_mii(struct net_device *dev, int k); -static void de4x5_dbg_media(struct net_device *dev); -static void de4x5_dbg_srom(struct de4x5_srom *p); -static void de4x5_dbg_rx(struct sk_buff *skb, int len); -static int de4x5_strncmp(char *a, char *b, int n); -static int dc21041_infoleaf(struct net_device *dev); -static int dc21140_infoleaf(struct net_device *dev); -static int dc21142_infoleaf(struct net_device *dev); -static int dc21143_infoleaf(struct net_device *dev); -static int type0_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type1_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type2_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type3_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type4_infoblock(struct net_device *dev, u_char count, u_char *p); -static int type5_infoblock(struct net_device *dev, u_char count, u_char *p); -static int compact_infoblock(struct net_device *dev, u_char count, u_char *p); - -#ifdef MODULE -int init_module(void); -void cleanup_module(void); -static struct net_device *unlink_modules(struct net_device *p); -static struct net_device *insert_device(struct net_device *dev, u_long iobase, - int (*init)(struct net_device *)); -static int count_adapters(void); -static int loading_module = 1; -MODULE_PARM(de4x5_debug, "i"); -MODULE_PARM(dec_only, "i"); -MODULE_PARM(args, "s"); -MODULE_PARM_DESC(de4x5_debug, "de4x5 debug mask"); -MODULE_PARM_DESC(dec_only, "de4x5 probe only for Digital boards (0-1)"); -MODULE_PARM_DESC(args, "de4x5 full duplex and media type settings; see de4x5.c for details"); -MODULE_LICENSE("GPL"); - -# else -static int loading_module; -#endif /* MODULE */ - -static char name[DE4X5_NAME_LENGTH + 1]; -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) -static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; -static int lastEISA; -# ifdef DE4X5_FORCE_EISA /* Force an EISA bus probe or not */ -static int forceEISA = 1; -# else -static int forceEISA; -# endif -#endif -static int num_de4x5s; -static int cfrv, useSROM; -static int lastPCI = -1; -static struct net_device *lastModule; -static struct pci_dev *pdev; - -/* -** List the SROM infoleaf functions and chipsets -*/ -struct InfoLeaf { - int chipset; - int (*fn)(struct net_device *); -}; -static struct InfoLeaf infoleaf_array[] = { - {DC21041, dc21041_infoleaf}, - {DC21140, dc21140_infoleaf}, - {DC21142, dc21142_infoleaf}, - {DC21143, dc21143_infoleaf} -}; -#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) - -/* -** List the SROM info block functions -*/ -static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { - type0_infoblock, - type1_infoblock, - type2_infoblock, - type3_infoblock, - type4_infoblock, - type5_infoblock, - compact_infoblock -}; - -#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) - -/* -** Miscellaneous defines... -*/ -#define RESET_DE4X5 {\ - int i;\ - i=inl(DE4X5_BMR);\ - mdelay(1);\ - outl(i | BMR_SWR, DE4X5_BMR);\ - mdelay(1);\ - outl(i, DE4X5_BMR);\ - mdelay(1);\ - for (i=0;i<5;i++) {inl(DE4X5_BMR); mdelay(1);}\ - mdelay(1);\ -} - -#define PHY_HARD_RESET {\ - outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ - mdelay(1); /* Assert for 1ms */\ - outl(0x00, DE4X5_GEP);\ - mdelay(2); /* Wait for 2ms */\ -} - - -/* -** Autoprobing in modules is allowed here. See the top of the file for -** more info. -*/ -int __init -de4x5_probe(struct net_device *dev) -{ - u_long iobase = dev->base_addr; - - pci_probe(dev, iobase); -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - if ((lastPCI == NO_MORE_PCI) && ((num_de4x5s == 0) || forceEISA)) { - eisa_probe(dev, iobase); - } -#endif - - return (dev->priv ? 0 : -ENODEV); -} - -static int __init -de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev) -{ - struct bus_type *lp = &bus; - int i, status=0; - char *tmp; - - /* Ensure we're not sleeping */ - if (lp->bus == EISA) { - outb(WAKEUP, PCI_CFPM); - } else { - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, WAKEUP); - } - mdelay(10); - - RESET_DE4X5; - - if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { - return -ENXIO; /* Hardware could not reset */ - } - - /* - ** Now find out what kind of DC21040/DC21041/DC21140 board we have. - */ - useSROM = FALSE; - if (lp->bus == PCI) { - PCI_signature(name, lp); - } else { - EISA_signature(name, EISA_ID0); - } - - if (*name == '\0') { /* Not found a board signature */ - return -ENXIO; - } - - dev->base_addr = iobase; - if (lp->bus == EISA) { - printk("%s: %s at 0x%04lx (EISA slot %ld)", - dev->name, name, iobase, ((iobase>>12)&0x0f)); - } else { /* PCI port address */ - printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name, - iobase, lp->bus_num, lp->device); - } - - printk(", h/w address "); - status = get_hw_addr(dev); - for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ - printk("%2.2x:", dev->dev_addr[i]); - } - printk("%2.2x,\n", dev->dev_addr[i]); - - if (status != 0) { - printk(" which has an Ethernet PROM CRC error.\n"); - return -ENXIO; - } else { - struct de4x5_private *lp; - - /* - ** Reserve a section of kernel memory for the adapter - ** private area and the TX/RX descriptor rings. - */ - dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN, - GFP_KERNEL); - if (dev->priv == NULL) { - return -ENOMEM; - } - - /* - ** Align to a longword boundary - */ - tmp = dev->priv; - dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN); - lp = (struct de4x5_private *)dev->priv; - memset(dev->priv, 0, sizeof(struct de4x5_private)); - lp->bus = bus.bus; - lp->bus_num = bus.bus_num; - lp->device = bus.device; - lp->chipset = bus.chipset; - lp->cache.priv = tmp; - lp->cache.gepc = GEP_INIT; - lp->asBit = GEP_SLNK; - lp->asPolarity = GEP_SLNK; - lp->asBitValid = TRUE; - lp->timeout = -1; - lp->useSROM = useSROM; - lp->pdev = pdev; - memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; - de4x5_parse_params(dev); - - /* - ** Choose correct autosensing in case someone messed up - */ - lp->autosense = lp->params.autosense; - if (lp->chipset != DC21140) { - if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { - lp->params.autosense = TP; - } - if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { - lp->params.autosense = BNC; - } - } - lp->fdx = lp->params.fdx; - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); - - lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); -#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) - lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + ALIGN; -#endif - lp->rx_ring = pci_alloc_consistent(pdev, lp->dma_size, &lp->dma_rings); - if (lp->rx_ring == NULL) { - kfree(lp->cache.priv); - lp->cache.priv = NULL; - return -ENOMEM; - } - - lp->tx_ring = lp->rx_ring + NUM_RX_DESC; - - /* - ** Set up the RX descriptor ring (Intels) - ** Allocate contiguous receive buffers, long word aligned (Alphas) - */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) - for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); - lp->rx_ring[i].buf = 0; - lp->rx_ring[i].next = 0; - lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ - } - -#else - { - dma_addr_t dma_rx_bufs; - - dma_rx_bufs = lp->dma_rings + (NUM_RX_DESC + NUM_TX_DESC) - * sizeof(struct de4x5_desc); - dma_rx_bufs = (dma_rx_bufs + ALIGN) & ~ALIGN; - lp->rx_bufs = (char *)(((long)(lp->rx_ring + NUM_RX_DESC - + NUM_TX_DESC) + ALIGN) & ~ALIGN); - for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); - lp->rx_ring[i].buf = - cpu_to_le32(dma_rx_bufs+i*RX_BUFF_SZ); - lp->rx_ring[i].next = 0; - lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ - } - - } -#endif - - barrier(); - - request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE), - lp->adapter_name); - - lp->rxRingSize = NUM_RX_DESC; - lp->txRingSize = NUM_TX_DESC; - - /* Write the end of list marker to the descriptor lists */ - lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); - lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); - - /* Tell the adapter where the TX/RX rings are located. */ - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - /* Initialise the IRQ mask and Enable/Disable */ - lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; - lp->irq_en = IMR_NIM | IMR_AIM; - - /* Create a loopback packet frame for later media probing */ - create_packet(dev, lp->frame, sizeof(lp->frame)); - - /* Check if the RX overflow bug needs testing for */ - i = cfrv & 0x000000fe; - if ((lp->chipset == DC21140) && (i == 0x20)) { - lp->rx_ovf = 1; - } - - /* Initialise the SROM pointers if possible */ - if (lp->useSROM) { - lp->state = INITIALISED; - if (srom_infoleaf_info(dev)) { - return -ENXIO; - } - srom_init(dev); - } - - lp->state = CLOSED; - - /* - ** Check for an MII interface - */ - if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { - mii_get_phy(dev); - } - -#ifndef __sparc_v9__ - printk(" and requires IRQ%d (provided by %s).\n", dev->irq, -#else - printk(" and requires IRQ%x (provided by %s).\n", dev->irq, -#endif - ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); - } - - if (de4x5_debug & DEBUG_VERSION) { - printk(version); - } - - /* The DE4X5-specific entries in the device structure. */ - dev->open = &de4x5_open; - dev->hard_start_xmit = &de4x5_queue_pkt; - dev->stop = &de4x5_close; - dev->get_stats = &de4x5_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &de4x5_ioctl; - - dev->mem_start = 0; - - /* Fill in the generic fields of the device structure. */ - ether_setup(dev); - - /* Let the adapter sleep to save power */ - yawn(dev, SLEEP); - - return status; -} - - -static int -de4x5_open(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, status = 0; - s32 omr; - - /* Allocate the RX buffers */ - for (i=0; irxRingSize; i++) { - if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { - de4x5_free_rx_buffs(dev); - return -EAGAIN; - } - } - - /* - ** Wake up the adapter - */ - yawn(dev, WAKEUP); - - /* - ** Re-initialize the DE4X5... - */ - status = de4x5_init(dev); - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; - lp->state = OPEN; - de4x5_dbg_open(dev); - - if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, - lp->adapter_name, dev)) { - printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); - if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, - lp->adapter_name, dev)) { - printk("\n Cannot get IRQ- reconfigure your hardware.\n"); - disable_ast(dev); - de4x5_free_rx_buffs(dev); - de4x5_free_tx_buffs(dev); - yawn(dev, SLEEP); - lp->state = CLOSED; - return -EAGAIN; - } else { - printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); - printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); - } - } - - lp->interrupt = UNMASK_INTERRUPTS; - dev->trans_start = jiffies; - - START_DE4X5; - - de4x5_setup_intr(dev); - - if (de4x5_debug & DEBUG_OPEN) { - printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); - printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); - printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); - printk("\tomr: 0x%08x\n", inl(DE4X5_OMR)); - printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); - printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); - printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); - printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); - } - - MOD_INC_USE_COUNT; - - return status; -} - -/* -** Initialize the DE4X5 operating conditions. NB: a chip problem with the -** DC21140 requires using perfect filtering mode for that chip. Since I can't -** see why I'd want > 14 multicast addresses, I have changed all chips to use -** the perfect filtering mode. Keep the DMA burst length at 8: there seems -** to be data corruption problems if it is larger (UDP errors seen from a -** ttcp source). -*/ -static int -de4x5_init(struct net_device *dev) -{ - /* Lock out other processes whilst setting up the hardware */ - netif_stop_queue(dev); - - de4x5_sw_reset(dev); - - /* Autoconfigure the connected port */ - autoconf_media(dev); - - return 0; -} - -static int -de4x5_sw_reset(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, j, status = 0; - s32 bmr, omr; - - /* Select the MII or SRL port now and RESET the MAC */ - if (!lp->useSROM) { - if (lp->phy[lp->active].id != 0) { - lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; - } else { - lp->infoblock_csr6 = OMR_SDP | OMR_TTM; - } - de4x5_switch_mac_port(dev); - } - - /* - ** Set the programmable burst length to 8 longwords for all the DC21140 - ** Fasternet chips and 4 longwords for all others: DMA errors result - ** without these values. Cache align 16 long. - */ - bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN; - bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); - outl(bmr, DE4X5_BMR); - - omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ - if (lp->chipset == DC21140) { - omr |= (OMR_SDP | OMR_SB); - } - lp->setup_f = PERFECT; - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - lp->rx_new = lp->rx_old = 0; - lp->tx_new = lp->tx_old = 0; - - for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = cpu_to_le32(R_OWN); - } - - for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = cpu_to_le32(0); - } - - barrier(); - - /* Build the setup frame depending on filtering mode */ - SetMulticastFilter(dev); - - load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, (struct sk_buff *)1); - outl(omr|OMR_ST, DE4X5_OMR); - - /* Poll for setup frame completion (adapter interrupts are disabled now) */ - sti(); /* Ensure timer interrupts */ - for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ - mdelay(1); - if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; - } - outl(omr, DE4X5_OMR); /* Stop everything! */ - - if (j == 0) { - printk("%s: Setup frame timed out, status %08x\n", dev->name, - inl(DE4X5_STS)); - status = -EIO; - } - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - lp->tx_old = lp->tx_new; - - return status; -} - -/* -** Writes a socket buffer address to the next available transmit descriptor. -*/ -static int -de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int status = 0; - u_long flags = 0; - - netif_stop_queue(dev); - if (lp->tx_enable == NO) { /* Cannot send for now */ - return -1; - } - - /* - ** Clean out the TX ring asynchronously to interrupts - sometimes the - ** interrupts are lost by delayed descriptor status updates relative to - ** the irq assertion, especially with a busy PCI bus. - */ - spin_lock_irqsave(&lp->lock, flags); - de4x5_tx(dev); - spin_unlock_irqrestore(&lp->lock, flags); - - /* Test if cache is already locked - requeue skb if so */ - if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) - return -1; - - /* Transmit descriptor ring full or stale skb */ - if (netif_queue_stopped(dev) || (u_long) lp->tx_skb[lp->tx_new] > 1) { - if (lp->interrupt) { - de4x5_putb_cache(dev, skb); /* Requeue the buffer */ - } else { - de4x5_put_cache(dev, skb); - } - if (de4x5_debug & DEBUG_TX) { - printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), netif_queue_stopped(dev), inl(DE4X5_IMR), inl(DE4X5_OMR), ((u_long) lp->tx_skb[lp->tx_new] > 1) ? "YES" : "NO"); - } - } else if (skb->len > 0) { - /* If we already have stuff queued locally, use that first */ - if (lp->cache.skb && !lp->interrupt) { - de4x5_put_cache(dev, skb); - skb = de4x5_get_cache(dev); - } - - while (skb && !netif_queue_stopped(dev) && - (u_long) lp->tx_skb[lp->tx_new] <= 1) { - spin_lock_irqsave(&lp->lock, flags); - netif_stop_queue(dev); - load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); - lp->stats.tx_bytes += skb->len; - outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - dev->trans_start = jiffies; - - if (TX_BUFFS_AVAIL) { - netif_start_queue(dev); /* Another pkt may be queued */ - } - skb = de4x5_get_cache(dev); - spin_unlock_irqrestore(&lp->lock, flags); - } - if (skb) de4x5_putb_cache(dev, skb); - } - - lp->cache.lock = 0; - - return status; -} - -/* -** The DE4X5 interrupt handler. -** -** I/O Read/Writes through intermediate PCI bridges are never 'posted', -** so that the asserted interrupt always has some real data to work with - -** if these I/O accesses are ever changed to memory accesses, ensure the -** STS write is read immediately to complete the transaction if the adapter -** is not on bus 0. Lost interrupts can still occur when the PCI bus load -** is high and descriptor status bits cannot be set before the associated -** interrupt is asserted and this routine entered. -*/ -static void -de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct de4x5_private *lp; - s32 imr, omr, sts, limit; - u_long iobase; - - if (dev == NULL) { - printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq); - return; - } - lp = (struct de4x5_private *)dev->priv; - spin_lock(&lp->lock); - iobase = dev->base_addr; - - DISABLE_IRQs; /* Ensure non re-entrancy */ - - if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - - synchronize_irq(); - - for (limit=0; limit<8; limit++) { - sts = inl(DE4X5_STS); /* Read IRQ status */ - outl(sts, DE4X5_STS); /* Reset the board interrupts */ - - if (!(sts & lp->irq_mask)) break;/* All done */ - - if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */ - de4x5_rx(dev); - - if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */ - de4x5_tx(dev); - - if (sts & STS_LNF) { /* TP Link has failed */ - lp->irq_mask &= ~IMR_LFM; - } - - if (sts & STS_UNF) { /* Transmit underrun */ - de4x5_txur(dev); - } - - if (sts & STS_SE) { /* Bus Error */ - STOP_DE4X5; - printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", - dev->name, sts); - spin_unlock(&lp->lock); - return; - } - } - - /* Load the TX ring with any locally stored packets */ - if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { - while (lp->cache.skb && !netif_queue_stopped(dev) && lp->tx_enable) { - de4x5_queue_pkt(de4x5_get_cache(dev), dev); - } - lp->cache.lock = 0; - } - - lp->interrupt = UNMASK_INTERRUPTS; - ENABLE_IRQs; - spin_unlock(&lp->lock); - - return; -} - -static int -de4x5_rx(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int entry; - s32 status; - - for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; - entry=lp->rx_new) { - status = (s32)le32_to_cpu(lp->rx_ring[entry].status); - - if (lp->rx_ovf) { - if (inl(DE4X5_MFC) & MFC_FOCM) { - de4x5_rx_ovfc(dev); - break; - } - } - - if (status & RD_FS) { /* Remember the start of frame */ - lp->rx_old = entry; - } - - if (status & RD_LS) { /* Valid frame status */ - if (lp->tx_enable) lp->linkOK++; - if (status & RD_ES) { /* There was an error. */ - lp->stats.rx_errors++; /* Update the error stats. */ - if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; - if (status & RD_CE) lp->stats.rx_crc_errors++; - if (status & RD_OF) lp->stats.rx_fifo_errors++; - if (status & RD_TL) lp->stats.rx_length_errors++; - if (status & RD_RF) lp->pktStats.rx_runt_frames++; - if (status & RD_CS) lp->pktStats.rx_collision++; - if (status & RD_DB) lp->pktStats.rx_dribble++; - if (status & RD_OF) lp->pktStats.rx_overflow++; - } else { /* A valid frame received */ - struct sk_buff *skb; - short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) - >> 16) - 4; - - if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { - printk("%s: Insufficient memory; nuking packet.\n", - dev->name); - lp->stats.rx_dropped++; - } else { - de4x5_dbg_rx(skb, pkt_len); - - /* Push up the protocol stack */ - skb->protocol=eth_type_trans(skb,dev); - de4x5_local_stats(dev, skb->data, pkt_len); - netif_rx(skb); - - /* Update stats */ - dev->last_rx = jiffies; - lp->stats.rx_packets++; - lp->stats.rx_bytes += pkt_len; - } - } - - /* Change buffer ownership for this frame, back to the adapter */ - for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { - lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); - barrier(); - } - lp->rx_ring[entry].status = cpu_to_le32(R_OWN); - barrier(); - } - - /* - ** Update entry information - */ - lp->rx_new = (++lp->rx_new) % lp->rxRingSize; - } - - return 0; -} - -static inline void -de4x5_free_tx_buff(struct de4x5_private *lp, int entry) -{ - pci_unmap_single(lp->pdev, le32_to_cpu(lp->tx_ring[entry].buf), - le32_to_cpu(lp->tx_ring[entry].des1) & TD_TBS1, - PCI_DMA_TODEVICE); - if ((u_long) lp->tx_skb[entry] > 1) - dev_kfree_skb_irq(lp->tx_skb[entry]); - lp->tx_skb[entry] = NULL; -} - -/* -** Buffer sent - check for TX buffer errors. -*/ -static int -de4x5_tx(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int entry; - s32 status; - - for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = (s32)le32_to_cpu(lp->tx_ring[entry].status); - if (status < 0) { /* Buffer not sent yet */ - break; - } else if (status != 0x7fffffff) { /* Not setup frame */ - if (status & TD_ES) { /* An error happened */ - lp->stats.tx_errors++; - if (status & TD_NC) lp->stats.tx_carrier_errors++; - if (status & TD_LC) lp->stats.tx_window_errors++; - if (status & TD_UF) lp->stats.tx_fifo_errors++; - if (status & TD_EC) lp->pktStats.excessive_collisions++; - if (status & TD_DE) lp->stats.tx_aborted_errors++; - - if (TX_PKT_PENDING) { - outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ - } - } else { /* Packet sent */ - lp->stats.tx_packets++; - if (lp->tx_enable) lp->linkOK++; - } - /* Update the collision counter */ - lp->stats.collisions += ((status & TD_EC) ? 16 : - ((status & TD_CC) >> 3)); - - /* Free the buffer. */ - if (lp->tx_skb[entry] != NULL) - de4x5_free_tx_buff(lp, entry); - } - - /* Update all the pointers */ - lp->tx_old = (++lp->tx_old) % lp->txRingSize; - } - - /* Any resources available? */ - if (TX_BUFFS_AVAIL && netif_queue_stopped(dev)) { - if (lp->interrupt) - netif_wake_queue(dev); - else - netif_start_queue(dev); - } - - return 0; -} - -static int -de4x5_ast(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - - disable_ast(dev); - - if (lp->useSROM) { - next_tick = srom_autoconf(dev); - } else if (lp->chipset == DC21140) { - next_tick = dc21140m_autoconf(dev); - } else if (lp->chipset == DC21041) { - next_tick = dc21041_autoconf(dev); - } else if (lp->chipset == DC21040) { - next_tick = dc21040_autoconf(dev); - } - lp->linkOK = 0; - enable_ast(dev, next_tick); - - return 0; -} - -static int -de4x5_txur(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int omr; - - omr = inl(DE4X5_OMR); - if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { - omr &= ~(OMR_ST|OMR_SR); - outl(omr, DE4X5_OMR); - while (inl(DE4X5_STS) & STS_TS); - if ((omr & OMR_TR) < OMR_TR) { - omr += 0x4000; - } else { - omr |= OMR_SF; - } - outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); - } - - return 0; -} - -static int -de4x5_rx_ovfc(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int omr; - - omr = inl(DE4X5_OMR); - outl(omr & ~OMR_SR, DE4X5_OMR); - while (inl(DE4X5_STS) & STS_RS); - - for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { - lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); - lp->rx_new = (++lp->rx_new % lp->rxRingSize); - } - - outl(omr, DE4X5_OMR); - - return 0; -} - -static int -de4x5_close(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 imr, omr; - - disable_ast(dev); - - netif_stop_queue(dev); - - if (de4x5_debug & DEBUG_CLOSE) { - printk("%s: Shutting down ethercard, status was %8.8x.\n", - dev->name, inl(DE4X5_STS)); - } - - /* - ** We stop the DE4X5 here... mask interrupts and stop TX & RX - */ - DISABLE_IRQs; - STOP_DE4X5; - - /* Free the associated irq */ - free_irq(dev->irq, dev); - lp->state = CLOSED; - - /* Free any socket buffers */ - de4x5_free_rx_buffs(dev); - de4x5_free_tx_buffs(dev); - - MOD_DEC_USE_COUNT; - - /* Put the adapter to sleep to save power */ - yawn(dev, SLEEP); - - return 0; -} - -static struct net_device_stats * -de4x5_get_stats(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); - - return &lp->stats; -} - -static void -de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=1; ipktStats.bins[i]++; - i = DE4X5_PKT_STAT_SZ; - } - } - if (buf[0] & 0x01) { /* Multicast/Broadcast */ - if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) && - (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { - lp->pktStats.unicast++; - } - - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); - } - - return; -} - -/* -** Removes the TD_IC flag from previous descriptor to improve TX performance. -** If the flag is changed on a descriptor that is being read by the hardware, -** I assume PCI transaction ordering will mean you are either successful or -** just miss asserting the change to the hardware. Anyway you're messing with -** a descriptor you don't own, but this shouldn't kill the chip provided -** the descriptor register is read only to the hardware. -*/ -static void -load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1); - dma_addr_t buf_dma = pci_map_single(lp->pdev, buf, flags & TD_TBS1, PCI_DMA_TODEVICE); - - lp->tx_ring[lp->tx_new].buf = cpu_to_le32(buf_dma); - lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); - lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); - lp->tx_skb[lp->tx_new] = skb; - lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC); - barrier(); - - lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); - barrier(); -} - -/* -** Set or clear the multicast filter for this adaptor. -*/ -static void -set_multicast_list(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - /* First, double check that the adapter is open */ - if (lp->state == OPEN) { - if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ - u32 omr; - omr = inl(DE4X5_OMR); - omr |= OMR_PR; - outl(omr, DE4X5_OMR); - } else { - SetMulticastFilter(dev); - load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | - SETUP_FRAME_LEN, (struct sk_buff *)1); - - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ - dev->trans_start = jiffies; - } - } - - return; -} - -/* -** Calculate the hash code and update the logical address filter -** from a list of ethernet multicast addresses. -** Little endian crc one liner from Matt Thomas, DEC. -*/ -static void -SetMulticastFilter(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - u_long iobase = dev->base_addr; - int i, j, bit, byte; - u16 hashcode; - u32 omr, crc; - char *pa; - unsigned char *addrs; - - omr = inl(DE4X5_OMR); - omr &= ~(OMR_PR | OMR_PM); - pa = build_setup_frame(dev, ALL); /* Build the basic frame */ - - if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) { - omr |= OMR_PM; /* Pass all multicasts */ - } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ - for (i=0;imc_count;i++) { /* for each address in the list */ - addrs=dmi->dmi_addr; - dmi=dmi->next; - if ((*addrs & 0x01) == 1) { /* multicast address? */ - crc = ether_crc_le(ETH_ALEN, addrs); - hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ - - byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ - bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ - - byte <<= 1; /* calc offset into setup frame */ - if (byte & 0x02) { - byte -= 1; - } - lp->setup_frame[byte] |= bit; - } - } - } else { /* Perfect filtering */ - for (j=0; jmc_count; j++) { - addrs=dmi->dmi_addr; - dmi=dmi->next; - for (i=0; ibus = EISA; - - if (ioaddr == 0) { /* Autoprobing */ - iobase = EISA_SLOT_INC; /* Get the first slot address */ - i = 1; - maxSlots = MAX_EISA_SLOTS; - } else { /* Probe a specific location */ - iobase = ioaddr; - i = (ioaddr >> 12); - maxSlots = i + 1; - } - - for (status = -ENODEV; (i> 8) & 0x00ffff00; - vendor = (u_short) cfid; - - /* Read the EISA Configuration Registers */ - irq = inb(EISA_REG0); - irq = de4x5_irq[(irq >> 1) & 0x03]; - - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Write the PCI Configuration Registers */ - outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); - outl(0x00006000, PCI_CFLT); - outl(iobase, PCI_CBIO); - - DevicePresent(EISA_APROM); - - dev->irq = irq; - if ((status = de4x5_hw_init(dev, iobase, NULL)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(lastModule, dev); - lastEISA = i; - return; - } - } - - if (ioaddr == 0) lastEISA = i; - - return; -} -#endif /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__) */ - -/* -** PCI bus I/O device probe -** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not -** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be -** enabled by the user first in the set up utility. Hence we just check for -** enabled features and silently ignore the card if they're not. -** -** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering -** bit. Here, check for I/O accesses and then set BM. If you put the card in -** a non BM slot, you're on your own (and complain to the PC vendor that your -** PC doesn't conform to the PCI standard)! -** -** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x -** kernels use the V0.535[n] drivers. -*/ -#define PCI_LAST_DEV 32 - -static void __init -pci_probe(struct net_device *dev, u_long ioaddr) -{ - u_char pb, pbus, dev_num, dnum, timer; - u_short vendor, index, status; - u_int irq = 0, device, class = DE4X5_CLASS_CODE; - u_long iobase = 0; /* Clear upper 32 bits in Alphas */ - struct bus_type *lp = &bus; - - if (lastPCI == NO_MORE_PCI) return; - - if (!pcibios_present()) { - lastPCI = NO_MORE_PCI; - return; /* No PCI bus in this machine! */ - } - - lp->bus = PCI; - lp->bus_num = 0; - - if ((ioaddr < 0x1000) && loading_module) { - pbus = (u_short)(ioaddr >> 8); - dnum = (u_short)(ioaddr & 0xff); - } else { - pbus = 0; - dnum = 0; - } - - for (index=lastPCI+1;(pdev = pci_find_class(class, pdev))!=NULL;index++) { - dev_num = PCI_SLOT(pdev->devfn); - pb = pdev->bus->number; - if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue; - - vendor = pdev->vendor; - device = pdev->device << 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; - - /* Search for an SROM on this bus */ - if (lp->bus_num != pb) { - lp->bus_num = pb; - srom_search(pdev); - } - - /* Get the chip configuration revision register */ - pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv); - - /* Set the device number information */ - lp->device = dev_num; - lp->bus_num = pb; - - /* Set the chipset information */ - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Get the board I/O address (64 bits on sparc64) */ - iobase = pci_resource_start(pdev, 0); - - /* Fetch the IRQ to be used */ - irq = pdev->irq; - if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; - - /* Check if I/O accesses and Bus Mastering are enabled */ - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); -#ifdef __powerpc__ - if (!(status & PCI_COMMAND_IO)) { - status |= PCI_COMMAND_IO; - pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); - } -#endif /* __powerpc__ */ - if (!(status & PCI_COMMAND_IO)) continue; - - if (!(status & PCI_COMMAND_MASTER)) { - status |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); - pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); - } - if (!(status & PCI_COMMAND_MASTER)) continue; - - /* Check the latency timer for values >= 0x60 */ - pcibios_read_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, &timer); - if (timer < 0x60) { - pcibios_write_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, 0x60); - } - - DevicePresent(DE4X5_APROM); - if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { - dev->irq = irq; - if ((status = de4x5_hw_init(dev, iobase, pdev)) == 0) { - num_de4x5s++; - lastPCI = index; - if (loading_module) link_modules(lastModule, dev); - return; - } - } else if (ioaddr != 0) { - printk("%s: region already allocated at 0x%04lx.\n", dev->name, - iobase); - } - } - - lastPCI = NO_MORE_PCI; - - return; -} - -/* -** This function searches the current bus (which is >0) for a DECchip with an -** SROM, so that in multiport cards that have one SROM shared between multiple -** DECchips, we can find the base SROM irrespective of the BIOS scan direction. -** For single port cards this is a time waster... -*/ -static void __init -srom_search(struct pci_dev *dev) -{ - u_char pb; - u_short vendor, status; - u_int irq = 0, device; - u_long iobase = 0; /* Clear upper 32 bits in Alphas */ - int i, j; - struct bus_type *lp = &bus; - struct list_head *walk = &dev->bus_list; - - for (walk = walk->next; walk != &dev->bus_list; walk = walk->next) { - struct pci_dev *this_dev = pci_dev_b(walk); - - /* Skip the pci_bus list entry */ - if (list_entry(walk, struct pci_bus, devices) == dev->bus) continue; - - vendor = this_dev->vendor; - device = this_dev->device << 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; - - /* Get the chip configuration revision register */ - pb = this_dev->bus->number; - pcibios_read_config_dword(pb, this_dev->devfn, PCI_REVISION_ID, &cfrv); - - /* Set the device number information */ - lp->device = PCI_SLOT(this_dev->devfn); - lp->bus_num = pb; - - /* Set the chipset information */ - if (is_DC2114x) { - device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); - } - lp->chipset = device; - - /* Get the board I/O address (64 bits on sparc64) */ - iobase = pci_resource_start(this_dev, 0); - - /* Fetch the IRQ to be used */ - irq = this_dev->irq; - if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; - - /* Check if I/O accesses are enabled */ - pcibios_read_config_word(pb, this_dev->devfn, PCI_COMMAND, &status); - if (!(status & PCI_COMMAND_IO)) continue; - - /* Search for a valid SROM attached to this DECchip */ - DevicePresent(DE4X5_APROM); - for (j=0, i=0; isrom + SROM_HWADD + i); - } - if ((j != 0) && (j != 0x5fa)) { - last.chipset = device; - last.bus = pb; - last.irq = irq; - for (i=0; isrom + SROM_HWADD + i); - } - return; - } - } - - return; -} - -static void __init -link_modules(struct net_device *dev, struct net_device *tmp) -{ - struct net_device *p=dev; - - if (p) { - while (((struct de4x5_private *)(p->priv))->next_module) { - p = ((struct de4x5_private *)(p->priv))->next_module; - } - - if (dev != tmp) { - ((struct de4x5_private *)(p->priv))->next_module = tmp; - } else { - ((struct de4x5_private *)(p->priv))->next_module = NULL; - } - } - - return; -} - -/* -** Auto configure the media here rather than setting the port at compile -** time. This routine is called by de4x5_init() and when a loss of media is -** detected (excessive collisions, loss of carrier, no carrier or link fail -** [TP] or no recent receive activity) to check whether the user has been -** sneaky and changed the port on us. -*/ -static int -autoconf_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS; - - lp->linkOK = 0; - lp->c_media = AUTO; /* Bogus last media */ - disable_ast(dev); - inl(DE4X5_MFC); /* Zero the lost frames counter */ - lp->media = INIT; - lp->tcount = 0; - - if (lp->useSROM) { - next_tick = srom_autoconf(dev); - } else if (lp->chipset == DC21040) { - next_tick = dc21040_autoconf(dev); - } else if (lp->chipset == DC21041) { - next_tick = dc21041_autoconf(dev); - } else if (lp->chipset == DC21140) { - next_tick = dc21140m_autoconf(dev); - } - - enable_ast(dev, next_tick); - - return (lp->media); -} - -/* -** Autoconfigure the media when using the DC21040. AUI cannot be distinguished -** from BNC as the port has a jumper to set thick or thin wire. When set for -** BNC, the BNC port will indicate activity if it's not terminated correctly. -** The only way to test for that is to place a loopback packet onto the -** network and watch for errors. Since we're messing with the interrupt mask -** register, disable the board interrupts and do not allow any more packets to -** be queued to the hardware. Re-enable everything only when the media is -** found. -** I may have to "age out" locally queued packets so that the higher layer -** timeouts don't effectively duplicate packets on the network. -*/ -static int -dc21040_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS; - s32 imr; - - switch (lp->media) { - case INIT: - DISABLE_IRQs; - lp->tx_enable = NO; - lp->timeout = -1; - de4x5_save_skbs(dev); - if ((lp->autosense == AUTO) || (lp->autosense == TP)) { - lp->media = TP; - } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { - lp->media = BNC_AUI; - } else if (lp->autosense == EXT_SIA) { - lp->media = EXT_SIA; - } else { - lp->media = NC; - } - lp->local_state = 0; - next_tick = dc21040_autoconf(dev); - break; - - case TP: - next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, - TP_SUSPECT, test_tp); - break; - - case TP_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); - break; - - case BNC: - case AUI: - case BNC_AUI: - next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, - BNC_AUI_SUSPECT, ping_media); - break; - - case BNC_AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); - break; - - case EXT_SIA: - next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, - NC, EXT_SIA_SUSPECT, ping_media); - break; - - case EXT_SIA_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); - break; - - case NC: - /* default to TP for all */ - reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = NO; - break; - } - - return next_tick; -} - -static int -dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, - int next_state, int suspect_state, - int (*fn)(struct net_device *, int)) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - int linkBad; - - switch (lp->local_state) { - case 0: - reset_init_sia(dev, csr13, csr14, csr15); - lp->local_state++; - next_tick = 500; - break; - - case 1: - if (!lp->tx_enable) { - linkBad = fn(dev, timeout); - if (linkBad < 0) { - next_tick = linkBad & ~TIMER_CB; - } else { - if (linkBad && (lp->autosense == AUTO)) { - lp->local_state = 0; - lp->media = next_state; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = suspect_state; - next_tick = 3000; - } - break; - } - - return next_tick; -} - -static int -de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, - int (*fn)(struct net_device *, int), - int (*asfn)(struct net_device *)) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int next_tick = DE4X5_AUTOSENSE_MS; - int linkBad; - - switch (lp->local_state) { - case 1: - if (lp->linkOK) { - lp->media = prev_state; - } else { - lp->local_state++; - next_tick = asfn(dev); - } - break; - - case 2: - linkBad = fn(dev, timeout); - if (linkBad < 0) { - next_tick = linkBad & ~TIMER_CB; - } else if (!linkBad) { - lp->local_state--; - lp->media = prev_state; - } else { - lp->media = INIT; - lp->tcount++; - } - } - - return next_tick; -} - -/* -** Autoconfigure the media when using the DC21041. AUI needs to be tested -** before BNC, because the BNC port will indicate activity if it's not -** terminated correctly. The only way to test for that is to place a loopback -** packet onto the network and watch for errors. Since we're messing with -** the interrupt mask register, disable the board interrupts and do not allow -** any more packets to be queued to the hardware. Re-enable everything only -** when the media is found. -*/ -static int -dc21041_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, irqs, irq_mask, imr, omr; - int next_tick = DE4X5_AUTOSENSE_MS; - - switch (lp->media) { - case INIT: - DISABLE_IRQs; - lp->tx_enable = NO; - lp->timeout = -1; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { - lp->media = TP; /* On chip auto negotiation is broken */ - } else if (lp->autosense == TP) { - lp->media = TP; - } else if (lp->autosense == BNC) { - lp->media = BNC; - } else if (lp->autosense == AUI) { - lp->media = AUI; - } else { - lp->media = NC; - } - lp->local_state = 0; - next_tick = dc21041_autoconf(dev); - break; - - case TP_NW: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FDX, DE4X5_OMR); - } - irqs = STS_LNF | STS_LNP; - irq_mask = IMR_LFM | IMR_LPM; - sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts & STS_LNP) { - lp->media = ANS; - } else { - lp->media = AUI; - } - next_tick = dc21041_autoconf(dev); - } - break; - - case ANS: - if (!lp->tx_enable) { - irqs = STS_LNP; - irq_mask = IMR_LPM; - sts = test_ans(dev, irqs, irq_mask, 3000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { - lp->media = TP; - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = ANS_SUSPECT; - next_tick = 3000; - } - break; - - case ANS_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); - break; - - case TP: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = STS_LNF | STS_LNP; - irq_mask = IMR_LFM | IMR_LPM; - sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { - if (inl(DE4X5_SISR) & SISR_NRA) { - lp->media = AUI; /* Non selected port activity */ - } else { - lp->media = BNC; - } - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = TP_SUSPECT; - next_tick = 3000; - } - break; - - case TP_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); - break; - - case AUI: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { - lp->media = BNC; - next_tick = dc21041_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = AUI_SUSPECT; - next_tick = 3000; - } - break; - - case AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); - break; - - case BNC: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - lp->local_state++; /* Ensure media connected */ - next_tick = dc21041_autoconf(dev); - } - break; - - case 1: - if (!lp->tx_enable) { - if ((sts = ping_media(dev, 3000)) < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts) { - lp->local_state = 0; - lp->media = NC; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = BNC_SUSPECT; - next_tick = 3000; - } - break; - } - break; - - case BNC_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); - break; - - case NC: - omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FDX, DE4X5_OMR); - reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = NO; - break; - } - - return next_tick; -} - -/* -** Some autonegotiation chips are broken in that they do not return the -** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement -** register, except at the first power up negotiation. -*/ -static int -dc21140m_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int ana, anlpa, cap, cr, slnk, sr; - int next_tick = DE4X5_AUTOSENSE_MS; - u_long imr, omr, iobase = dev->base_addr; - - switch(lp->media) { - case INIT: - if (lp->timeout < 0) { - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->linkOK = 0; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - } - if ((next_tick = de4x5_reset_phy(dev)) < 0) { - next_tick &= ~TIMER_CB; - } else { - if (lp->useSROM) { - if (srom_map_media(dev) < 0) { - lp->tcount++; - return next_tick; - } - srom_exec(dev, lp->phy[lp->active].gep); - if (lp->infoblock_media == ANS) { - ana = lp->phy[lp->active].ana | MII_ANA_CSMA; - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - } - } else { - lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ - SET_10Mb; - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if ((lp->autosense == AUTO) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } else if (lp->autosense == AUTO) { - lp->media = SPD_DET; - } else if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; - } else { - lp->media = NC; - } - } - lp->local_state = 0; - next_tick = dc21140m_autoconf(dev); - } - break; - - case ANS: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); - if (cr < 0) { - next_tick = cr & ~TIMER_CB; - } else { - if (cr) { - lp->local_state = 0; - lp->media = SPD_DET; - } else { - lp->local_state++; - } - next_tick = dc21140m_autoconf(dev); - } - break; - - case 1: - if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { - next_tick = sr & ~TIMER_CB; - } else { - lp->media = SPD_DET; - lp->local_state = 0; - if (sr) { /* Success! */ - lp->tmp = MII_SR_ASSC; - anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); - ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - if (!(anlpa & MII_ANLPA_RF) && - (cap = anlpa & MII_ANLPA_TAF & ana)) { - if (cap & MII_ANA_100M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); - lp->media = _100Mb; - } else if (cap & MII_ANA_10M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); - - lp->media = _10Mb; - } - } - } /* Auto Negotiation failed to finish */ - next_tick = dc21140m_autoconf(dev); - } /* Auto Negotiation failed to start */ - break; - } - break; - - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ - if (lp->timeout < 0) { - lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : - (~gep_rd(dev) & GEP_LNP)); - SET_100Mb_PDET; - } - if ((slnk = test_for_100Mb(dev, 6500)) < 0) { - next_tick = slnk & ~TIMER_CB; - } else { - if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; - } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { - lp->media = _10Mb; - } else { - lp->media = NC; - } - next_tick = dc21140m_autoconf(dev); - } - break; - - case _100Mb: /* Set 100Mb/s */ - next_tick = 3000; - if (!lp->tx_enable) { - SET_100Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case BNC: - case AUI: - case _10Mb: /* Set 10Mb/s */ - next_tick = 3000; - if (!lp->tx_enable) { - SET_10Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case NC: - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tx_enable = FALSE; - break; - } - - return next_tick; -} - -/* -** This routine may be merged into dc21140m_autoconf() sometime as I'm -** changing how I figure out the media - but trying to keep it backwards -** compatible with the de500-xa and de500-aa. -** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock -** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). -** This routine just has to figure out whether 10Mb/s or 100Mb/s is -** active. -** When autonegotiation is working, the ANS part searches the SROM for -** the highest common speed (TP) link that both can run and if that can -** be full duplex. That infoblock is executed and then the link speed set. -** -** Only _10Mb and _100Mb are tested here. -*/ -static int -dc2114x_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; - int next_tick = DE4X5_AUTOSENSE_MS; - - switch (lp->media) { - case INIT: - if (lp->timeout < 0) { - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->linkOK = 0; - lp->timeout = -1; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - if (lp->params.autosense & ~AUTO) { - srom_map_media(dev); /* Fixed media requested */ - if (lp->media != lp->params.autosense) { - lp->tcount++; - lp->media = INIT; - return next_tick; - } - lp->media = INIT; - } - } - if ((next_tick = de4x5_reset_phy(dev)) < 0) { - next_tick &= ~TIMER_CB; - } else { - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if (lp->autosense == TP) { - lp->media = TP; - } else if (lp->autosense == BNC) { - lp->media = BNC; - } else if (lp->autosense == AUI) { - lp->media = AUI; - } else { - lp->media = SPD_DET; - if ((lp->infoblock_media == ANS) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } - } - lp->local_state = 0; - next_tick = dc2114x_autoconf(dev); - } - break; - - case ANS: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); - if (cr < 0) { - next_tick = cr & ~TIMER_CB; - } else { - if (cr) { - lp->local_state = 0; - lp->media = SPD_DET; - } else { - lp->local_state++; - } - next_tick = dc2114x_autoconf(dev); - } - break; - - case 1: - if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { - next_tick = sr & ~TIMER_CB; - } else { - lp->media = SPD_DET; - lp->local_state = 0; - if (sr) { /* Success! */ - lp->tmp = MII_SR_ASSC; - anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); - ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - if (!(anlpa & MII_ANLPA_RF) && - (cap = anlpa & MII_ANLPA_TAF & ana)) { - if (cap & MII_ANA_100M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); - lp->media = _100Mb; - } else if (cap & MII_ANA_10M) { - lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); - lp->media = _10Mb; - } - } - } /* Auto Negotiation failed to finish */ - next_tick = dc2114x_autoconf(dev); - } /* Auto Negotiation failed to start */ - break; - } - break; - - case AUI: - if (!lp->tx_enable) { - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { - lp->media = BNC; - next_tick = dc2114x_autoconf(dev); - } else { - lp->local_state = 1; - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = AUI_SUSPECT; - next_tick = 3000; - } - break; - - case AUI_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); - break; - - case BNC: - switch (lp->local_state) { - case 0: - if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FDX, DE4X5_OMR); - } - irqs = 0; - irq_mask = 0; - sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); - if (sts < 0) { - next_tick = sts & ~TIMER_CB; - } else { - lp->local_state++; /* Ensure media connected */ - next_tick = dc2114x_autoconf(dev); - } - break; - - case 1: - if (!lp->tx_enable) { - if ((sts = ping_media(dev, 3000)) < 0) { - next_tick = sts & ~TIMER_CB; - } else { - if (sts) { - lp->local_state = 0; - lp->tcount++; - lp->media = INIT; - } else { - de4x5_init_connection(dev); - } - } - } else if (!lp->linkOK && (lp->autosense == AUTO)) { - lp->media = BNC_SUSPECT; - next_tick = 3000; - } - break; - } - break; - - case BNC_SUSPECT: - next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); - break; - - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ - if (srom_map_media(dev) < 0) { - lp->tcount++; - lp->media = INIT; - return next_tick; - } - if (lp->media == _100Mb) { - if ((slnk = test_for_100Mb(dev, 6500)) < 0) { - lp->media = SPD_DET; - return (slnk & ~TIMER_CB); - } - } else { - if (wait_for_link(dev) < 0) { - lp->media = SPD_DET; - return PDET_LINK_WAIT; - } - } - if (lp->media == ANS) { /* Do MII parallel detection */ - if (is_spd_100(dev)) { - lp->media = _100Mb; - } else { - lp->media = _10Mb; - } - next_tick = dc2114x_autoconf(dev); - } else if (((lp->media == _100Mb) && is_100_up(dev)) || - (((lp->media == _10Mb) || (lp->media == TP) || - (lp->media == BNC) || (lp->media == AUI)) && - is_10_up(dev))) { - next_tick = dc2114x_autoconf(dev); - } else { - lp->tcount++; - lp->media = INIT; - } - break; - - case _10Mb: - next_tick = 3000; - if (!lp->tx_enable) { - SET_10Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - case _100Mb: - next_tick = 3000; - if (!lp->tx_enable) { - SET_100Mb; - de4x5_init_connection(dev); - } else { - if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { - lp->media = INIT; - lp->tcount++; - next_tick = DE4X5_AUTOSENSE_MS; - } - } - } - break; - - default: - lp->tcount++; -printk("Huh?: media:%02x\n", lp->media); - lp->media = INIT; - break; - } - - return next_tick; -} - -static int -srom_autoconf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - return lp->infoleaf_fn(dev); -} - -/* -** This mapping keeps the original media codes and FDX flag unchanged. -** While it isn't strictly necessary, it helps me for the moment... -** The early return avoids a media state / SROM media space clash. -*/ -static int -srom_map_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - lp->fdx = 0; - if (lp->infoblock_media == lp->media) - return 0; - - switch(lp->infoblock_media) { - case SROM_10BASETF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_10BASET: - if (lp->params.fdx && !lp->fdx) return -1; - if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { - lp->media = _10Mb; - } else { - lp->media = TP; - } - break; - - case SROM_10BASE2: - lp->media = BNC; - break; - - case SROM_10BASE5: - lp->media = AUI; - break; - - case SROM_100BASETF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_100BASET: - if (lp->params.fdx && !lp->fdx) return -1; - lp->media = _100Mb; - break; - - case SROM_100BASET4: - lp->media = _100Mb; - break; - - case SROM_100BASEFF: - if (!lp->params.fdx) return -1; - lp->fdx = TRUE; - case SROM_100BASEF: - if (lp->params.fdx && !lp->fdx) return -1; - lp->media = _100Mb; - break; - - case ANS: - lp->media = ANS; - lp->fdx = lp->params.fdx; - break; - - default: - printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, - lp->infoblock_media); - return -1; - break; - } - - return 0; -} - -static void -de4x5_init_connection(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_long flags = 0; - - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; /* Stop scrolling media messages */ - } - - spin_lock_irqsave(&lp->lock, flags); - de4x5_rst_desc_ring(dev); - de4x5_setup_intr(dev); - lp->tx_enable = YES; - spin_unlock_irqrestore(&lp->lock, flags); - outl(POLL_DEMAND, DE4X5_TPD); - - netif_wake_queue(dev); - - return; -} - -/* -** General PHY reset function. Some MII devices don't reset correctly -** since their MII address pins can float at voltages that are dependent -** on the signal pin use. Do a double reset to ensure a reset. -*/ -static int -de4x5_reset_phy(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int next_tick = 0; - - if ((lp->useSROM) || (lp->phy[lp->active].id)) { - if (lp->timeout < 0) { - if (lp->useSROM) { - if (lp->phy[lp->active].rst) { - srom_exec(dev, lp->phy[lp->active].rst); - srom_exec(dev, lp->phy[lp->active].rst); - } else if (lp->rst) { /* Type 5 infoblock reset */ - srom_exec(dev, lp->rst); - srom_exec(dev, lp->rst); - } - } else { - PHY_HARD_RESET; - } - if (lp->useMII) { - mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); - } - } - if (lp->useMII) { - next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); - } - } else if (lp->chipset == DC21140) { - PHY_HARD_RESET; - } - - return next_tick; -} - -static int -test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, csr12; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ - reset_init_sia(dev, csr13, csr14, csr15); - } - - /* set up the interrupt mask */ - outl(irq_mask, DE4X5_IMR); - - /* clear all pending interrupts */ - sts = inl(DE4X5_STS); - outl(sts, DE4X5_STS); - - /* clear csr12 NRA and SRA bits */ - if ((lp->chipset == DC21041) || lp->useSROM) { - csr12 = inl(DE4X5_SISR); - outl(csr12, DE4X5_SISR); - } - } - - sts = inl(DE4X5_STS) & ~TIMER_CB; - - if (!(sts & irqs) && --lp->timeout) { - sts = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sts; -} - -static int -test_tp(struct net_device *dev, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int sisr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - } - - sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); - - if (sisr && --lp->timeout) { - sisr = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sisr; -} - -/* -** Samples the 100Mb Link State Signal. The sample interval is important -** because too fast a rate can give erroneous results and confuse the -** speed sense algorithm. -*/ -#define SAMPLE_INTERVAL 500 /* ms */ -#define SAMPLE_DELAY 2000 /* ms */ -static int -test_for_100Mb(struct net_device *dev, int msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); - - if (lp->timeout < 0) { - if ((msec/SAMPLE_INTERVAL) <= 0) return 0; - if (msec > SAMPLE_DELAY) { - lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; - gep = SAMPLE_DELAY | TIMER_CB; - return gep; - } else { - lp->timeout = msec/SAMPLE_INTERVAL; - } - } - - if (lp->phy[lp->active].id || lp->useSROM) { - gep = is_100_up(dev) | is_spd_100(dev); - } else { - gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); - } - if (!(gep & ret) && --lp->timeout) { - gep = SAMPLE_INTERVAL | TIMER_CB; - } else { - lp->timeout = -1; - } - - return gep; -} - -static int -wait_for_link(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - if (lp->timeout < 0) { - lp->timeout = 1; - } - - if (lp->timeout--) { - return TIMER_CB; - } else { - lp->timeout = -1; - } - - return 0; -} - -/* -** -** -*/ -static int -test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int test; - u_long iobase = dev->base_addr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - } - - if (pol) pol = ~0; - reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; - test = (reg ^ pol) & mask; - - if (test && --lp->timeout) { - reg = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return reg; -} - -static int -is_spd_100(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int spd; - - if (lp->useMII) { - spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); - spd = ~(spd ^ lp->phy[lp->active].spd.value); - spd &= lp->phy[lp->active].spd.mask; - } else if (!lp->useSROM) { /* de500-xa */ - spd = ((~gep_rd(dev)) & GEP_SLNK); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); - - spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid); - } - - return spd; -} - -static int -is_100_up(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->useMII) { - /* Double read for sticky bits & temporary drops */ - mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); - } else if (!lp->useSROM) { /* de500-xa */ - return ((~gep_rd(dev)) & GEP_SLNK); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); - - return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } -} - -static int -is_10_up(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->useMII) { - /* Double read for sticky bits & temporary drops */ - mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); - } else if (!lp->useSROM) { /* de500-xa */ - return ((~gep_rd(dev)) & GEP_LNP); - } else { - if ((lp->ibn == 2) || !lp->asBitValid) - return (((lp->chipset & ~0x00ff) == DC2114x) ? - (~inl(DE4X5_SISR)&SISR_LS10): - 0); - - return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } -} - -static int -is_anc_capable(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { - return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - return (inl(DE4X5_SISR) & SISR_LPN) >> 12; - } else { - return 0; - } -} - -/* -** Send a packet onto the media and watch for send errors that indicate the -** media is bad or unconnected. -*/ -static int -ping_media(struct net_device *dev, int msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int sisr; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - - lp->tmp = lp->tx_new; /* Remember the ring position */ - load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), (struct sk_buff *)1); - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); - } - - sisr = inl(DE4X5_SISR); - - if ((!(sisr & SISR_NCR)) && - ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && - (--lp->timeout)) { - sisr = 100 | TIMER_CB; - } else { - if ((!(sisr & SISR_NCR)) && - !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && - lp->timeout) { - sisr = 0; - } else { - sisr = 1; - } - lp->timeout = -1; - } - - return sisr; -} - -/* -** This function does 2 things: on Intels it kmalloc's another buffer to -** replace the one about to be passed up. On Alpha's it kmallocs a buffer -** into which the packet is copied. -*/ -static struct sk_buff * -de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p; - -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) - struct sk_buff *ret; - u_long i=0, tmp; - - p = dev_alloc_skb(IEEE802_3_SZ + ALIGN + 2); - if (!p) return NULL; - - p->dev = dev; - tmp = virt_to_bus(p->data); - i = ((tmp + ALIGN) & ~ALIGN) - tmp; - skb_reserve(p, i); - lp->rx_ring[index].buf = cpu_to_le32(tmp + i); - - ret = lp->rx_skb[index]; - lp->rx_skb[index] = p; - - if ((u_long) ret > 1) { - skb_put(ret, len); - } - - return ret; - -#else - if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ - - p = dev_alloc_skb(len + 2); - if (!p) return NULL; - - p->dev = dev; - skb_reserve(p, 2); /* Align */ - if (index < lp->rx_old) { /* Wrapped buffer */ - short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; - memcpy(skb_put(p,tlen),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,tlen); - memcpy(skb_put(p,len-tlen),lp->rx_bufs,len-tlen); - } else { /* Linear buffer */ - memcpy(skb_put(p,len),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,len); - } - - return p; -#endif -} - -static void -de4x5_free_rx_buffs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=0; irxRingSize; i++) { - if ((u_long) lp->rx_skb[i] > 1) { - dev_kfree_skb(lp->rx_skb[i]); - } - lp->rx_ring[i].status = 0; - lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */ - } - - return; -} - -static void -de4x5_free_tx_buffs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - for (i=0; itxRingSize; i++) { - if (lp->tx_skb[i]) - de4x5_free_tx_buff(lp, i); - lp->tx_ring[i].status = 0; - } - - /* Unload the locally queued packets */ - while (lp->cache.skb) { - dev_kfree_skb(de4x5_get_cache(dev)); - } - - return; -} - -/* -** When a user pulls a connection, the DECchip can end up in a -** 'running - waiting for end of transmission' state. This means that we -** have to perform a chip soft reset to ensure that we can synchronize -** the hardware and software and make any media probes using a loopback -** packet meaningful. -*/ -static void -de4x5_save_skbs(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 omr; - - if (!lp->cache.save_cnt) { - STOP_DE4X5; - de4x5_tx(dev); /* Flush any sent skb's */ - de4x5_free_tx_buffs(dev); - de4x5_cache_state(dev, DE4X5_SAVE_STATE); - de4x5_sw_reset(dev); - de4x5_cache_state(dev, DE4X5_RESTORE_STATE); - lp->cache.save_cnt++; - START_DE4X5; - } - - return; -} - -static void -de4x5_rst_desc_ring(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i; - s32 omr; - - if (lp->cache.save_cnt) { - STOP_DE4X5; - outl(lp->dma_rings, DE4X5_RRBA); - outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), - DE4X5_TRBA); - - lp->rx_new = lp->rx_old = 0; - lp->tx_new = lp->tx_old = 0; - - for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = cpu_to_le32(R_OWN); - } - - for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = cpu_to_le32(0); - } - - barrier(); - lp->cache.save_cnt--; - START_DE4X5; - } - - return; -} - -static void -de4x5_cache_state(struct net_device *dev, int flag) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - switch(flag) { - case DE4X5_SAVE_STATE: - lp->cache.csr0 = inl(DE4X5_BMR); - lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); - lp->cache.csr7 = inl(DE4X5_IMR); - break; - - case DE4X5_RESTORE_STATE: - outl(lp->cache.csr0, DE4X5_BMR); - outl(lp->cache.csr6, DE4X5_OMR); - outl(lp->cache.csr7, DE4X5_IMR); - if (lp->chipset == DC21140) { - gep_wr(lp->cache.gepc, dev); - gep_wr(lp->cache.gep, dev); - } else { - reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, - lp->cache.csr15); - } - break; - } - - return; -} - -static void -de4x5_put_cache(struct net_device *dev, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p; - - if (lp->cache.skb) { - for (p=lp->cache.skb; p->next; p=p->next); - p->next = skb; - } else { - lp->cache.skb = skb; - } - skb->next = NULL; - - return; -} - -static void -de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p = lp->cache.skb; - - lp->cache.skb = skb; - skb->next = p; - - return; -} - -static struct sk_buff * -de4x5_get_cache(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct sk_buff *p = lp->cache.skb; - - if (p) { - lp->cache.skb = p->next; - p->next = NULL; - } - - return p; -} - -/* -** Check the Auto Negotiation State. Return OK when a link pass interrupt -** is received and the auto-negotiation status is NWAY OK. -*/ -static int -test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 sts, ans; - - if (lp->timeout < 0) { - lp->timeout = msec/100; - outl(irq_mask, DE4X5_IMR); - - /* clear all pending interrupts */ - sts = inl(DE4X5_STS); - outl(sts, DE4X5_STS); - } - - ans = inl(DE4X5_SISR) & SISR_ANS; - sts = inl(DE4X5_STS) & ~TIMER_CB; - - if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { - sts = 100 | TIMER_CB; - } else { - lp->timeout = -1; - } - - return sts; -} - -static void -de4x5_setup_intr(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 imr, sts; - - if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */ - imr = 0; - UNMASK_IRQs; - sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */ - outl(sts, DE4X5_STS); - ENABLE_IRQs; - } - - return; -} - -/* -** -*/ -static void -reset_init_sia(struct net_device *dev, s32 csr13, s32 csr14, s32 csr15) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - RESET_SIA; - if (lp->useSROM) { - if (lp->ibn == 3) { - srom_exec(dev, lp->phy[lp->active].rst); - srom_exec(dev, lp->phy[lp->active].gep); - outl(1, DE4X5_SICR); - return; - } else { - csr15 = lp->cache.csr15; - csr14 = lp->cache.csr14; - csr13 = lp->cache.csr13; - outl(csr15 | lp->cache.gepc, DE4X5_SIGR); - outl(csr15 | lp->cache.gep, DE4X5_SIGR); - } - } else { - outl(csr15, DE4X5_SIGR); - } - outl(csr14, DE4X5_STRR); - outl(csr13, DE4X5_SICR); - - mdelay(10); - - return; -} - -/* -** Create a loopback ethernet packet -*/ -static void -create_packet(struct net_device *dev, char *frame, int len) -{ - int i; - char *buf = frame; - - for (i=0; idev_addr[i]; - } - for (i=0; idev_addr[i]; - } - - *buf++ = 0; /* Packet length (2 bytes) */ - *buf++ = 1; - - return; -} - -/* -** Look for a particular board name in the EISA configuration space -*/ -static int -EISA_signature(char *name, s32 eisa_id) -{ - static c_char *signatures[] = DE4X5_SIGNATURE; - char ManCode[DE4X5_STRLEN]; - union { - s32 ID; - char Id[4]; - } Eisa; - int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *); - - *name = '\0'; - Eisa.ID = inl(eisa_id); - - ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); - ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); - ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); - ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); - ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); - ManCode[5]='\0'; - - for (i=0;ichipset == DC21040) { - strcpy(name, "DE434/5"); - return status; - } else { /* Search for a DEC name in the SROM */ - int i = *((char *)&lp->srom + 19) * 3; - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } - name[8] = '\0'; - for (i=0; ichipset == DC21040) ? "DC21040" : - ((lp->chipset == DC21041) ? "DC21041" : - ((lp->chipset == DC21140) ? "DC21140" : - ((lp->chipset == DC21142) ? "DC21142" : - ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" - ))))))); - } - if (lp->chipset != DC21041) { - useSROM = TRUE; /* card is not recognisably DEC */ - } - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - useSROM = TRUE; - } - - return status; -} - -/* -** Set up the Ethernet PROM counter to the start of the Ethernet address on -** the DC21040, else read the SROM for the other chips. -** The SROM may not be present in a multi-MAC card, so first read the -** MAC address and check for a bad address. If there is a bad one then exit -** immediately with the prior srom contents intact (the h/w address will -** be fixed up later). -*/ -static void -DevicePresent(u_long aprom_addr) -{ - int i, j=0; - struct bus_type *lp = &bus; - - if (lp->chipset == DC21040) { - if (lp->bus == EISA) { - enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ - } else { - outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ - } - } else { /* Read new srom */ - u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); - for (i=0; i<(ETH_ALEN>>1); i++) { - tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); - *p = le16_to_cpu(tmp); - j += *p++; - } - if ((j == 0) || (j == 0x2fffd)) { - return; - } - - p=(short *)&lp->srom; - for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { - tmp = srom_rd(aprom_addr, i); - *p++ = le16_to_cpu(tmp); - } - de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); - } - - return; -} - -/* -** Since the write on the Enet PROM register doesn't seem to reset the PROM -** pointer correctly (at least on my DE425 EISA card), this routine should do -** it...from depca.c. -*/ -static void -enet_addr_rst(u_long aprom_addr) -{ - union { - struct { - u32 a; - u32 b; - } llsig; - char Sig[sizeof(u32) << 1]; - } dev; - short sigLength=0; - s8 data; - int i, j; - - dev.llsig.a = ETH_PROM_SIG; - dev.llsig.b = ETH_PROM_SIG; - sigLength = sizeof(u32) << 1; - - for (i=0,j=0;jbase_addr; - int broken, i, k, tmp, status = 0; - u_short j,chksum; - struct bus_type *lp = &bus; - - broken = de4x5_bad_srom(lp); - - for (i=0,k=0,j=0;j<3;j++) { - k <<= 1; - if (k > 0xffff) k-=0xffff; - - if (lp->bus == PCI) { - if (lp->chipset == DC21040) { - while ((tmp = inl(DE4X5_APROM)) < 0); - k += (u_char) tmp; - dev->dev_addr[i++] = (u_char) tmp; - while ((tmp = inl(DE4X5_APROM)) < 0); - k += (u_short) (tmp << 8); - dev->dev_addr[i++] = (u_char) tmp; - } else if (!broken) { - dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; - dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; - } else if ((broken == SMC) || (broken == ACCTON)) { - dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; - dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; - } - } else { - k += (u_char) (tmp = inb(EISA_APROM)); - dev->dev_addr[i++] = (u_char) tmp; - k += (u_short) ((tmp = inb(EISA_APROM)) << 8); - dev->dev_addr[i++] = (u_char) tmp; - } - - if (k > 0xffff) k-=0xffff; - } - if (k == 0xffff) k=0; - - if (lp->bus == PCI) { - if (lp->chipset == DC21040) { - while ((tmp = inl(DE4X5_APROM)) < 0); - chksum = (u_char) tmp; - while ((tmp = inl(DE4X5_APROM)) < 0); - chksum |= (u_short) (tmp << 8); - if ((k != chksum) && (dec_only)) status = -1; - } - } else { - chksum = (u_char) inb(EISA_APROM); - chksum |= (u_short) (inb(EISA_APROM) << 8); - if ((k != chksum) && (dec_only)) status = -1; - } - - /* If possible, try to fix a broken card - SMC only so far */ - srom_repair(dev, broken); - -#ifdef CONFIG_PPC - /* - ** If the address starts with 00 a0, we have to bit-reverse - ** each byte of the address. - */ - if ( (ppc_md.ppc_machine & _MACH_Pmac) && - (dev->dev_addr[0] == 0) && - (dev->dev_addr[1] == 0xa0) ) - { - for (i = 0; i < ETH_ALEN; ++i) - { - int x = dev->dev_addr[i]; - x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); - x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); - dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); - } - } -#endif /* CONFIG_PPC */ - - /* Test for a bad enet address */ - status = test_bad_enet(dev, status); - - return status; -} - -/* -** Test for enet addresses in the first 32 bytes. The built-in strncmp -** didn't seem to work here...? -*/ -static int -de4x5_bad_srom(struct bus_type *lp) -{ - int i, status = 0; - - for (i=0; isrom, (char *)&enet_det[i], 3) && - !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { - if (i == 0) { - status = SMC; - } else if (i == 1) { - status = ACCTON; - } - break; - } - } - - return status; -} - -static int -de4x5_strncmp(char *a, char *b, int n) -{ - int ret=0; - - for (;n && !ret;n--) { - ret = *a++ - *b++; - } - - return ret; -} - -static void -srom_repair(struct net_device *dev, int card) -{ - struct bus_type *lp = &bus; - - switch(card) { - case SMC: - memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); - memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); - memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); - useSROM = TRUE; - break; - } - - return; -} - -/* -** Assume that the irq's do not follow the PCI spec - this is seems -** to be true so far (2 for 2). -*/ -static int -test_bad_enet(struct net_device *dev, int status) -{ - struct bus_type *lp = &bus; - int i, tmp; - - for (tmp=0,i=0; idev_addr[i]; - if ((tmp == 0) || (tmp == 0x5fa)) { - if ((lp->chipset == last.chipset) && - (lp->bus_num == last.bus) && (lp->bus_num > 0)) { - for (i=0; idev_addr[i] = last.addr[i]; - for (i=ETH_ALEN-1; i>2; --i) { - dev->dev_addr[i] += 1; - if (dev->dev_addr[i] != 0) break; - } - for (i=0; idev_addr[i]; - if (!an_exception(lp)) { - dev->irq = last.irq; - } - - status = 0; - } - } else if (!status) { - last.chipset = lp->chipset; - last.bus = lp->bus_num; - last.irq = dev->irq; - for (i=0; idev_addr[i]; - } - - return status; -} - -/* -** List of board exceptions with correctly wired IRQs -*/ -static int -an_exception(struct bus_type *lp) -{ - if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && - (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { - return -1; - } - - return 0; -} - -/* -** SROM Read -*/ -static short -srom_rd(u_long addr, u_char offset) -{ - sendto_srom(SROM_RD | SROM_SR, addr); - - srom_latch(SROM_RD | SROM_SR | DT_CS, addr); - srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); - srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); - - return srom_data(SROM_RD | SROM_SR | DT_CS, addr); -} - -static void -srom_latch(u_int command, u_long addr) -{ - sendto_srom(command, addr); - sendto_srom(command | DT_CLK, addr); - sendto_srom(command, addr); - - return; -} - -static void -srom_command(u_int command, u_long addr) -{ - srom_latch(command, addr); - srom_latch(command, addr); - srom_latch((command & 0x0000ff00) | DT_CS, addr); - - return; -} - -static void -srom_address(u_int command, u_long addr, u_char offset) -{ - int i, a; - - a = offset << 2; - for (i=0; i<6; i++, a <<= 1) { - srom_latch(command | ((a & 0x80) ? DT_IN : 0), addr); - } - udelay(1); - - i = (getfrom_srom(addr) >> 3) & 0x01; - - return; -} - -static short -srom_data(u_int command, u_long addr) -{ - int i; - short word = 0; - s32 tmp; - - for (i=0; i<16; i++) { - sendto_srom(command | DT_CLK, addr); - tmp = getfrom_srom(addr); - sendto_srom(command, addr); - - word = (word << 1) | ((tmp >> 3) & 0x01); - } - - sendto_srom(command & 0x0000ff00, addr); - - return word; -} - -/* -static void -srom_busy(u_int command, u_long addr) -{ - sendto_srom((command & 0x0000ff00) | DT_CS, addr); - - while (!((getfrom_srom(addr) >> 3) & 0x01)) { - mdelay(1); - } - - sendto_srom(command & 0x0000ff00, addr); - - return; -} -*/ - -static void -sendto_srom(u_int command, u_long addr) -{ - outl(command, addr); - udelay(1); - - return; -} - -static int -getfrom_srom(u_long addr) -{ - s32 tmp; - - tmp = inl(addr); - udelay(1); - - return tmp; -} - -static int -srom_infoleaf_info(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i, count; - u_char *p; - - /* Find the infoleaf decoder function that matches this chipset */ - for (i=0; ichipset == infoleaf_array[i].chipset) break; - } - if (i == INFOLEAF_SIZE) { - lp->useSROM = FALSE; - printk("%s: Cannot find correct chipset for SROM decoding!\n", - dev->name); - return -ENXIO; - } - - lp->infoleaf_fn = infoleaf_array[i].fn; - - /* Find the information offset that this function should use */ - count = *((u_char *)&lp->srom + 19); - p = (u_char *)&lp->srom + 26; - - if (count > 1) { - for (i=count; i; --i, p+=3) { - if (lp->device == *p) break; - } - if (i == 0) { - lp->useSROM = FALSE; - printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", - dev->name, lp->device); - return -ENXIO; - } - } - - lp->infoleaf_offset = TWIDDLE(p+1); - - return 0; -} - -/* -** This routine loads any type 1 or 3 MII info into the mii device -** struct and executes any type 5 code to reset PHY devices for this -** controller. -** The info for the MII devices will be valid since the index used -** will follow the discovery process from MII address 1-31 then 0. -*/ -static void -srom_init(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - u_char count; - - p+=2; - if (lp->chipset == DC21140) { - lp->cache.gepc = (*p++ | GEP_CTRL); - gep_wr(lp->cache.gepc, dev); - } - - /* Block count */ - count = *p++; - - /* Jump the infoblocks to find types */ - for (;count; --count) { - if (*p < 128) { - p += COMPACT_LEN; - } else if (*(p+1) == 5) { - type5_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 4) { - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 3) { - type3_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 2) { - p += ((*p & BLOCK_LEN) + 1); - } else if (*(p+1) == 1) { - type1_infoblock(dev, 1, p); - p += ((*p & BLOCK_LEN) + 1); - } else { - p += ((*p & BLOCK_LEN) + 1); - } - } - - return; -} - -/* -** A generic routine that writes GEP control, data and reset information -** to the GEP register (21140) or csr15 GEP portion (2114[23]). -*/ -static void -srom_exec(struct net_device *dev, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char count = (p ? *p++ : 0); - u_short *w = (u_short *)p; - - if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; - - if (lp->chipset != DC21140) RESET_SIA; - - while (count--) { - gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? - *p++ : TWIDDLE(w++)), dev); - mdelay(2); /* 2ms per action */ - } - - if (lp->chipset != DC21140) { - outl(lp->cache.csr14, DE4X5_STRR); - outl(lp->cache.csr13, DE4X5_SICR); - } - - return; -} - -/* -** Basically this function is a NOP since it will never be called, -** unless I implement the DC21041 SROM functions. There's no need -** since the existing code will be satisfactory for all boards. -*/ -static int -dc21041_infoleaf(struct net_device *dev) -{ - return DE4X5_AUTOSENSE_MS; -} - -static int -dc21140_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* GEP control */ - lp->cache.gepc = (*p++ | GEP_CTRL); - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -static int -dc21142_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -static int -dc21143_infoleaf(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char count = 0; - u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; - int next_tick = DE4X5_AUTOSENSE_MS; - - /* Read the connection type */ - p+=2; - - /* Block count */ - count = *p++; - - /* Recursively figure out the info blocks */ - if (*p < 128) { - next_tick = dc_infoblock[COMPACT](dev, count, p); - } else { - next_tick = dc_infoblock[*(p+1)](dev, count, p); - } - if (lp->tcount == count) { - lp->media = NC; - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; - } - lp->media = INIT; - lp->tcount = 0; - lp->tx_enable = FALSE; - } - - return next_tick & ~TIMER_CB; -} - -/* -** The compact infoblock is only designed for DC21140[A] chips, so -** we'll reuse the dc21140m_autoconf function. Non MII media only. -*/ -static int -compact_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+COMPACT_LEN) < 128) { - return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); - } else { - return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = COMPACT; - lp->active = 0; - gep_wr(lp->cache.gepc, dev); - lp->infoblock_media = (*p++) & COMPACT_MC; - lp->cache.gep = *p++; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -/* -** This block describes non MII media for the DC21140[A] only. -*/ -static int -type0_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 0; - lp->active = 0; - gep_wr(lp->cache.gepc, dev); - p+=2; - lp->infoblock_media = (*p++) & BLOCK0_MC; - lp->cache.gep = *p++; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -/* These functions are under construction! */ - -static int -type1_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - p += 2; - if (lp->state == INITIALISED) { - lp->ibn = 1; - lp->active = *p++; - lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].mc = TWIDDLE(p); p += 2; - lp->phy[lp->active].ana = TWIDDLE(p); p += 2; - lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); - return 0; - } else if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 1; - lp->active = *p; - lp->infoblock_csr6 = OMR_MII_100; - lp->useMII = TRUE; - lp->infoblock_media = ANS; - - de4x5_switch_mac_port(dev); - } - - return dc21140m_autoconf(dev); -} - -static int -type2_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 2; - lp->active = 0; - p += 2; - lp->infoblock_media = (*p) & MEDIA_CODE; - - if ((*p++) & EXT_FIELD) { - lp->cache.csr13 = TWIDDLE(p); p += 2; - lp->cache.csr14 = TWIDDLE(p); p += 2; - lp->cache.csr15 = TWIDDLE(p); p += 2; - } else { - lp->cache.csr13 = CSR13; - lp->cache.csr14 = CSR14; - lp->cache.csr15 = CSR15; - } - lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; - lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); - lp->infoblock_csr6 = OMR_SIA; - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -static int -type3_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - p += 2; - if (lp->state == INITIALISED) { - lp->ibn = 3; - lp->active = *p++; - if (MOTO_SROM_BUG) lp->active = 0; - lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); - lp->phy[lp->active].mc = TWIDDLE(p); p += 2; - lp->phy[lp->active].ana = TWIDDLE(p); p += 2; - lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; - lp->phy[lp->active].mci = *p; - return 0; - } else if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 3; - lp->active = *p; - if (MOTO_SROM_BUG) lp->active = 0; - lp->infoblock_csr6 = OMR_MII_100; - lp->useMII = TRUE; - lp->infoblock_media = ANS; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -static int -type4_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - if ((lp->media == INIT) && (lp->timeout < 0)) { - lp->ibn = 4; - lp->active = 0; - p+=2; - lp->infoblock_media = (*p++) & MEDIA_CODE; - lp->cache.csr13 = CSR13; /* Hard coded defaults */ - lp->cache.csr14 = CSR14; - lp->cache.csr15 = CSR15; - lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; - lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; - csr6 = *p++; - flags = *p++; - - lp->asBitValid = (flags & 0x80) ? 0 : -1; - lp->defMedium = (flags & 0x40) ? -1 : 0; - lp->asBit = 1 << ((csr6 >> 1) & 0x07); - lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); - lp->useMII = FALSE; - - de4x5_switch_mac_port(dev); - } - - return dc2114x_autoconf(dev); -} - -/* -** This block type provides information for resetting external devices -** (chips) through the General Purpose Register. -*/ -static int -type5_infoblock(struct net_device *dev, u_char count, u_char *p) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char len = (*p & BLOCK_LEN)+1; - - /* Recursively figure out the info blocks */ - if (--count > lp->tcount) { - if (*(p+len) < 128) { - return dc_infoblock[COMPACT](dev, count, p+len); - } else { - return dc_infoblock[*(p+len+1)](dev, count, p+len); - } - } - - /* Must be initializing to run this code */ - if ((lp->state == INITIALISED) || (lp->media == INIT)) { - p+=2; - lp->rst = p; - srom_exec(dev, lp->rst); - } - - return DE4X5_AUTOSENSE_MS; -} - -/* -** MII Read/Write -*/ - -static int -mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) -{ - mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ - mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ - mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ - mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ - mii_address(phyreg, ioaddr); /* PHY Register to read */ - mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ - - return mii_rdata(ioaddr); /* Read data */ -} - -static void -mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) -{ - mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ - mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ - mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */ - mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ - mii_address(phyreg, ioaddr); /* PHY Register to write */ - mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */ - data = mii_swap(data, 16); /* Swap data bit ordering */ - mii_wdata(data, 16, ioaddr); /* Write data */ - - return; -} - -static int -mii_rdata(u_long ioaddr) -{ - int i; - s32 tmp = 0; - - for (i=0; i<16; i++) { - tmp <<= 1; - tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); - } - - return tmp; -} - -static void -mii_wdata(int data, int len, u_long ioaddr) -{ - int i; - - for (i=0; i>= 1; - } - - return; -} - -static void -mii_address(u_char addr, u_long ioaddr) -{ - int i; - - addr = mii_swap(addr, 5); - for (i=0; i<5; i++) { - sendto_mii(MII_MWR | MII_WR, addr, ioaddr); - addr >>= 1; - } - - return; -} - -static void -mii_ta(u_long rw, u_long ioaddr) -{ - if (rw == MII_STWR) { - sendto_mii(MII_MWR | MII_WR, 1, ioaddr); - sendto_mii(MII_MWR | MII_WR, 0, ioaddr); - } else { - getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */ - } - - return; -} - -static int -mii_swap(int data, int len) -{ - int i, tmp = 0; - - for (i=0; i>= 1; - } - - return tmp; -} - -static void -sendto_mii(u32 command, int data, u_long ioaddr) -{ - u32 j; - - j = (data & 1) << 17; - outl(command | j, ioaddr); - udelay(1); - outl(command | MII_MDC | j, ioaddr); - udelay(1); - - return; -} - -static int -getfrom_mii(u32 command, u_long ioaddr) -{ - outl(command, ioaddr); - udelay(1); - outl(command | MII_MDC, ioaddr); - udelay(1); - - return ((inl(ioaddr) >> 19) & 1); -} - -/* -** Here's 3 ways to calculate the OUI from the ID registers. -*/ -static int -mii_get_oui(u_char phyaddr, u_long ioaddr) -{ -/* - union { - u_short reg; - u_char breg[2]; - } a; - int i, r2, r3, ret=0;*/ - int r2, r3; - - /* Read r2 and r3 */ - r2 = mii_rd(MII_ID0, phyaddr, ioaddr); - r3 = mii_rd(MII_ID1, phyaddr, ioaddr); - /* SEEQ and Cypress way * / - / * Shuffle r2 and r3 * / - a.reg=0; - r3 = ((r3>>10)|(r2<<6))&0x0ff; - r2 = ((r2>>2)&0x3fff); - - / * Bit reverse r3 * / - for (i=0;i<8;i++) { - ret<<=1; - ret |= (r3&1); - r3>>=1; - } - - / * Bit reverse r2 * / - for (i=0;i<16;i++) { - a.reg<<=1; - a.reg |= (r2&1); - r2>>=1; - } - - / * Swap r2 bytes * / - i=a.breg[0]; - a.breg[0]=a.breg[1]; - a.breg[1]=i; - - return ((a.reg<<8)|ret); */ /* SEEQ and Cypress way */ -/* return ((r2<<6)|(u_int)(r3>>10)); */ /* NATIONAL and BROADCOM way */ - return r2; /* (I did it) My way */ -} - -/* -** The SROM spec forces us to search addresses [1-31 0]. Bummer. -*/ -static int -mii_get_phy(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); - int id; - - lp->active = 0; - lp->useMII = TRUE; - - /* Search the MII address space for possible PHY devices */ - for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { - lp->phy[lp->active].addr = i; - if (i==0) n++; /* Count cycles */ - while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ - id = mii_get_oui(i, DE4X5_MII); - if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ - for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); - if (k < DE4X5_MAX_PHY) { - memcpy((char *)&lp->phy[k], - (char *)&phy_info[j], sizeof(struct phy_table)); - lp->phy[k].addr = i; - lp->mii_cnt++; - lp->active++; - } else { - goto purgatory; /* Stop the search */ - } - break; - } - if ((j == limit) && (i < DE4X5_MAX_MII)) { - for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); - lp->phy[k].addr = i; - lp->phy[k].id = id; - lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ - lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ - lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ - lp->mii_cnt++; - lp->active++; - printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); - j = de4x5_debug; - de4x5_debug |= DEBUG_MII; - de4x5_dbg_mii(dev, k); - de4x5_debug = j; - printk("\n"); - } - } - purgatory: - lp->active = 0; - if (lp->phy[0].id) { /* Reset the PHY devices */ - for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ - mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); - while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); - - de4x5_dbg_mii(dev, k); - } - } - if (!lp->mii_cnt) lp->useMII = FALSE; - - return lp->mii_cnt; -} - -static char * -build_setup_frame(struct net_device *dev, int mode) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - char *pa = lp->setup_frame; - - /* Initialise the setup frame */ - if (mode == ALL) { - memset(lp->setup_frame, 0, SETUP_FRAME_LEN); - } - - if (lp->setup_f == HASH_PERF) { - for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; idev_addr[i]; /* Host address */ - if (i & 0x01) pa += 2; - } - *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; - } else { - for (i=0; idev_addr[i]; - if (i & 0x01) pa += 4; - } - for (i=0; ipriv; - - del_timer(&lp->timer); - - return; -} - -static long -de4x5_switch_mac_port(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - s32 omr; - - STOP_DE4X5; - - /* Assert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | - OMR_FDX)); - omr |= lp->infoblock_csr6; - if (omr & OMR_PS) omr |= OMR_HBD; - outl(omr, DE4X5_OMR); - - /* Soft Reset */ - RESET_DE4X5; - - /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ - if (lp->chipset == DC21140) { - gep_wr(lp->cache.gepc, dev); - gep_wr(lp->cache.gep, dev); - } else if ((lp->chipset & ~0x0ff) == DC2114x) { - reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); - } - - /* Restore CSR6 */ - outl(omr, DE4X5_OMR); - - /* Reset CSR8 */ - inl(DE4X5_MFC); - - return omr; -} - -static void -gep_wr(s32 data, struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->chipset == DC21140) { - outl(data, DE4X5_GEP); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); - } - - return; -} - -static int -gep_rd(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->chipset == DC21140) { - return inl(DE4X5_GEP); - } else if ((lp->chipset & ~0x00ff) == DC2114x) { - return (inl(DE4X5_SIGR) & 0x000fffff); - } - - return 0; -} - -static void -timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int dt; - - /* First, cancel any pending timer events */ - del_timer(&lp->timer); - - /* Convert msec to ticks */ - dt = (msec * HZ) / 1000; - if (dt==0) dt=1; - - /* Set up timer */ - lp->timer.expires = jiffies + dt; - lp->timer.function = fn; - lp->timer.data = data; - add_timer(&lp->timer); - - return; -} - -static void -yawn(struct net_device *dev, int state) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; - - if(lp->bus == EISA) { - switch(state) { - case WAKEUP: - outb(WAKEUP, PCI_CFPM); - mdelay(10); - break; - - case SNOOZE: - outb(SNOOZE, PCI_CFPM); - break; - - case SLEEP: - outl(0, DE4X5_SICR); - outb(SLEEP, PCI_CFPM); - break; - } - } else { - switch(state) { - case WAKEUP: - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, WAKEUP); - mdelay(10); - break; - - case SNOOZE: - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, SNOOZE); - break; - - case SLEEP: - outl(0, DE4X5_SICR); - pcibios_write_config_byte(lp->bus_num, lp->device << 3, - PCI_CFDA_PSM, SLEEP); - break; - } - } - - return; -} - -static void -de4x5_parse_params(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - char *p, *q, t; - - lp->params.fdx = 0; - lp->params.autosense = AUTO; - - if (args == NULL) return; - - if ((p = strstr(args, dev->name))) { - if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); - t = *q; - *q = '\0'; - -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - if (strstr(p, "force_eisa") || strstr(p, "FORCE_EISA")) forceEISA = 1; -#endif - if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; - - if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { - if (strstr(p, "TP")) { - lp->params.autosense = TP; - } else if (strstr(p, "TP_NW")) { - lp->params.autosense = TP_NW; - } else if (strstr(p, "BNC")) { - lp->params.autosense = BNC; - } else if (strstr(p, "AUI")) { - lp->params.autosense = AUI; - } else if (strstr(p, "BNC_AUI")) { - lp->params.autosense = BNC; - } else if (strstr(p, "10Mb")) { - lp->params.autosense = _10Mb; - } else if (strstr(p, "100Mb")) { - lp->params.autosense = _100Mb; - } else if (strstr(p, "AUTO")) { - lp->params.autosense = AUTO; - } - } - *q = t; - } - - return; -} - -static void -de4x5_dbg_open(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int i; - - if (de4x5_debug & DEBUG_OPEN) { - printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); - printk("\tphysical address: "); - for (i=0;i<6;i++) { - printk("%2.2x:",(short)dev->dev_addr[i]); - } - printk("\n"); - printk("Descriptor head addresses:\n"); - printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); - printk("Descriptor addresses:\nRX: "); - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status); - } - } - printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); - printk("TX: "); - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status); - } - } - printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); - printk("Descriptor buffers:\nRX: "); - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); - } - } - printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); - printk("TX: "); - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); - } - } - printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); - printk("Ring size: \nRX: %d\nTX: %d\n", - (short)lp->rxRingSize, - (short)lp->txRingSize); - } - - return; -} - -static void -de4x5_dbg_mii(struct net_device *dev, int k) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (de4x5_debug & DEBUG_MII) { - printk("\nMII device address: %d\n", lp->phy[k].addr); - printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); - printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); - printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); - printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); - if (lp->phy[k].id != BROADCOM_T4) { - printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); - printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); - } - printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); - if (lp->phy[k].id != BROADCOM_T4) { - printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); - printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); - } else { - printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); - } - } - - return; -} - -static void -de4x5_dbg_media(struct net_device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - if (lp->media != lp->c_media) { - if (de4x5_debug & DEBUG_MEDIA) { - printk("%s: media is %s%s\n", dev->name, - (lp->media == NC ? "unconnected, link down or incompatible connection" : - (lp->media == TP ? "TP" : - (lp->media == ANS ? "TP/Nway" : - (lp->media == BNC ? "BNC" : - (lp->media == AUI ? "AUI" : - (lp->media == BNC_AUI ? "BNC/AUI" : - (lp->media == EXT_SIA ? "EXT SIA" : - (lp->media == _100Mb ? "100Mb/s" : - (lp->media == _10Mb ? "10Mb/s" : - "???" - ))))))))), (lp->fdx?" full duplex.":".")); - } - lp->c_media = lp->media; - } - - return; -} - -static void -de4x5_dbg_srom(struct de4x5_srom *p) -{ - int i; - - if (de4x5_debug & DEBUG_SROM) { - printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); - printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); - printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); - printk("SROM version: %02x\n", (u_char)(p->version)); - printk("# controllers: %02x\n", (u_char)(p->num_controllers)); - - printk("Hardware Address: "); - for (i=0;iieee_addr+i)); - } - printk("%02x\n", (u_char)*(p->ieee_addr+i)); - printk("CRC checksum: %04x\n", (u_short)(p->chksum)); - for (i=0; i<64; i++) { - printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); - } - } - - return; -} - -static void -de4x5_dbg_rx(struct sk_buff *skb, int len) -{ - int i, j; - - if (de4x5_debug & DEBUG_RX) { - printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", - (u_char)skb->data[0], - (u_char)skb->data[1], - (u_char)skb->data[2], - (u_char)skb->data[3], - (u_char)skb->data[4], - (u_char)skb->data[5], - (u_char)skb->data[6], - (u_char)skb->data[7], - (u_char)skb->data[8], - (u_char)skb->data[9], - (u_char)skb->data[10], - (u_char)skb->data[11], - (u_char)skb->data[12], - (u_char)skb->data[13], - len); - for (j=0; len>0;j+=16, len-=16) { - printk(" %03x: ",j); - for (i=0; i<16 && idata[i+j]); - } - printk("\n"); - } - } - - return; -} - -/* -** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. In the normal course of events -** this function is only used for my testing. -*/ -static int -de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data; - u_long iobase = dev->base_addr; - int i, j, status = 0; - s32 omr; - union { - u8 addr[144]; - u16 sval[72]; - u32 lval[36]; - } tmp; - u_long flags = 0; - - switch(ioc->cmd) { - case DE4X5_GET_HWADDR: /* Get the hardware address */ - ioc->len = ETH_ALEN; - for (i=0; idev_addr[i]; - } - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - - case DE4X5_SET_HWADDR: /* Set the hardware address */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) return -EFAULT; - if (netif_queue_stopped(dev)) - return -EBUSY; - netif_stop_queue(dev); - for (i=0; idev_addr[i] = tmp.addr[i]; - } - build_setup_frame(dev, PHYS_ADDR_ONLY); - /* Set up the descriptor and give ownership to the card */ - load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | - SETUP_FRAME_LEN, (struct sk_buff *)1); - lp->tx_new = (++lp->tx_new) % lp->txRingSize; - outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ - netif_wake_queue(dev); /* Unlock the TX ring */ - break; - - case DE4X5_SET_PROM: /* Set Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr |= OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags |= IFF_PROMISC; - break; - - case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr &= ~OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags &= ~IFF_PROMISC; - break; - - case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - printk("%s: Boo!\n", dev->name); - break; - - case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr |= OMR_PM; - outl(omr, DE4X5_OMR); - break; - - case DE4X5_GET_STATS: /* Get the driver statistics */ - { - struct pkt_stats statbuf; - ioc->len = sizeof(statbuf); - spin_lock_irqsave(&lp->lock, flags); - memcpy(&statbuf, &lp->pktStats, ioc->len); - spin_unlock_irqrestore(&lp->lock, flags); - if (copy_to_user(ioc->data, &statbuf, ioc->len)) - return -EFAULT; - break; - } - case DE4X5_CLR_STATS: /* Zero out the driver statistics */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_irqsave(&lp->lock, flags); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - spin_unlock_irqrestore(&lp->lock, flags); - break; - - case DE4X5_GET_OMR: /* Get the OMR Register contents */ - tmp.addr[0] = inl(DE4X5_OMR); - if (copy_to_user(ioc->data, tmp.addr, 1)) return -EFAULT; - break; - - case DE4X5_SET_OMR: /* Set the OMR Register contents */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(tmp.addr, ioc->data, 1)) return -EFAULT; - outl(tmp.addr[0], DE4X5_OMR); - break; - - case DE4X5_GET_REG: /* Get the DE4X5 Registers */ - j = 0; - tmp.lval[0] = inl(DE4X5_STS); j+=4; - tmp.lval[1] = inl(DE4X5_BMR); j+=4; - tmp.lval[2] = inl(DE4X5_IMR); j+=4; - tmp.lval[3] = inl(DE4X5_OMR); j+=4; - tmp.lval[4] = inl(DE4X5_SISR); j+=4; - tmp.lval[5] = inl(DE4X5_SICR); j+=4; - tmp.lval[6] = inl(DE4X5_STRR); j+=4; - tmp.lval[7] = inl(DE4X5_SIGR); j+=4; - ioc->len = j; - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - -#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ -/* - case DE4X5_DUMP: - j = 0; - tmp.addr[j++] = dev->irq; - for (i=0; idev_addr[i]; - } - tmp.addr[j++] = lp->rxRingSize; - tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; - tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; - - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; - } - } - tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; - } - } - tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; - - for (i=0;irxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; - } - } - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; - for (i=0;itxRingSize-1;i++){ - if (i < 3) { - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; - } - } - tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; - - for (i=0;irxRingSize;i++){ - tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; - } - for (i=0;itxRingSize;i++){ - tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; - } - - tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4; - tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4; - tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; - tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; - tmp.lval[j>>2] = inl(DE4X5_STS); j+=4; - tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; - tmp.lval[j>>2] = lp->chipset; j+=4; - if (lp->chipset == DC21140) { - tmp.lval[j>>2] = gep_rd(dev); j+=4; - } else { - tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; - tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; - } - tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; - if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { - tmp.lval[j>>2] = lp->active; j+=4; - tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - if (lp->phy[lp->active].id != BROADCOM_T4) { - tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } - tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - if (lp->phy[lp->active].id != BROADCOM_T4) { - tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } else { - tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; - } - } - - tmp.addr[j++] = lp->txRingSize; - tmp.addr[j++] = netif_queue_stopped(dev); - - ioc->len = j; - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; - break; - -*/ - default: - return -EOPNOTSUPP; - } - - return status; -} - -#ifdef MODULE -/* -** Note now that module autoprobing is allowed under EISA and PCI. The -** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes -** to "do the right thing". -*/ -#define LP(a) ((struct de4x5_private *)(a)) -static struct net_device *mdev = NULL; -static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ -MODULE_PARM(io, "i"); -MODULE_PARM_DESC(io, "de4x5 I/O base address"); - -int -init_module(void) -{ - int i, num, status = -EIO; - struct net_device *p; - - num = count_adapters(); - - for (i=0; ipriv; - if (lp) { - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - if (lp->cache.priv) { /* Private area allocated? */ - kfree(lp->cache.priv); /* Free the private area */ - } - if (lp->rx_ring) { - pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, - lp->dma_rings); - } - } - kfree(p); - } else { - status = 0; /* At least one adapter will work */ - lastModule = p; - } - } - - return status; -} - -void -cleanup_module(void) -{ - while (mdev != NULL) { - mdev = unlink_modules(mdev); - } - - return; -} - -static struct net_device * -unlink_modules(struct net_device *p) -{ - struct net_device *next = NULL; - - if (p->priv) { /* Private areas allocated? */ - struct de4x5_private *lp = (struct de4x5_private *)p->priv; - - next = lp->next_module; - if (lp->rx_ring) { - pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, - lp->dma_rings); - } - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); /* Free the private area */ - } - unregister_netdev(p); - kfree(p); /* Free the device structure */ - - return next; -} - -static int -count_adapters(void) -{ - int i, j=0; - u_short vendor; - u_int class = DE4X5_CLASS_CODE; - u_int device; - -#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) - char name[DE4X5_STRLEN]; - u_long iobase = 0x1000; - - for (i=1; ivendor; - device = pdev->device << 8; - if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; - } - - return j; -} - -/* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ -static struct net_device * __init -insert_device(struct net_device *dev, u_long iobase, int (*init)(struct net_device *)) -{ - struct net_device *new; - - new = (struct net_device *)kmalloc(sizeof(struct net_device), GFP_KERNEL); - if (new == NULL) { - printk("de4x5.c: Device not initialised, insufficient memory\n"); - return NULL; - } else { - memset((char *)new, 0, sizeof(struct net_device)); - new->base_addr = iobase; /* assign the io address */ - new->init = init; /* initialisation routine */ - } - - return new; -} - -#endif /* MODULE */ - - -/* - * Local variables: - * - * Delete -DMODVERSIONS below if you didn't define this in your kernel - * - * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -DMODVERSIONS -include /linux/include/linux/modversions.h -c de4x5.c" - * End: - */ diff -urN linux-2.5.6-pre3/drivers/net/de4x5.h linux-2.5.6/drivers/net/de4x5.h --- linux-2.5.6-pre3/drivers/net/de4x5.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/de4x5.h Wed Dec 31 16:00:00 1969 @@ -1,1031 +0,0 @@ -/* - Copyright 1994 Digital Equipment Corporation. - - This software may be used and distributed according to the terms of the - GNU General Public License, incorporated herein by reference. - - The author may be reached as davies@wanton.lkg.dec.com or Digital - Equipment Corporation, 550 King Street, Littleton MA 01460. - - ========================================================================= -*/ - -/* -** DC21040 CSR<1..15> Register Address Map -*/ -#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */ -#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */ -#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */ -#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */ -#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */ -#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */ -#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */ -#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */ -#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */ -#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */ -#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */ -#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */ -#define DE4X5_MII iobase+(0x048 << lp->bus) /* MII Interface Register */ -#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */ -#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */ -#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/ -#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */ -#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */ -#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */ -#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */ -#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */ - -/* -** EISA Register Address Map -*/ -#define EISA_ID iobase+0x0c80 /* EISA ID Registers */ -#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */ -#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */ -#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */ -#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */ -#define EISA_CR iobase+0x0c84 /* EISA Control Register */ -#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */ -#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */ -#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */ -#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */ -#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */ - -/* -** PCI/EISA Configuration Registers Address Map -*/ -#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */ -#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */ -#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */ -#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */ -#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */ -#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */ -#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ -#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ -#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ -#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ -#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ - -/* -** EISA Configuration Register 0 bit definitions -*/ -#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */ -#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */ -#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */ -#define ER0_ISTS 0x10 /* Interrupt Status (X) */ -#define ER0_LI 0x08 /* Latch Interrupts */ -#define ER0_INTL 0x06 /* INTerrupt Level */ -#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */ - -/* -** EISA Configuration Register 1 bit definitions -*/ -#define ER1_IAM 0xe0 /* ISA Address Mode */ -#define ER1_IAE 0x10 /* ISA Addressing Enable */ -#define ER1_UPIN 0x0f /* User Pins */ - -/* -** EISA Configuration Register 2 bit definitions -*/ -#define ER2_BRS 0xc0 /* Boot ROM Size */ -#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */ - -/* -** EISA Configuration Register 3 bit definitions -*/ -#define ER3_BWE 0x40 /* Burst Write Enable */ -#define ER3_BRE 0x04 /* Burst Read Enable */ -#define ER3_LSR 0x02 /* Local Software Reset */ - -/* -** PCI Configuration ID Register (PCI_CFID). The Device IDs are left -** shifted 8 bits to allow detection of DC21142 and DC21143 variants with -** the configuration revision register step number. -*/ -#define CFID_DID 0xff00 /* Device ID */ -#define CFID_VID 0x00ff /* Vendor ID */ -#define DC21040_DID 0x0200 /* Unique Device ID # */ -#define DC21040_VID 0x1011 /* DC21040 Manufacturer */ -#define DC21041_DID 0x1400 /* Unique Device ID # */ -#define DC21041_VID 0x1011 /* DC21041 Manufacturer */ -#define DC21140_DID 0x0900 /* Unique Device ID # */ -#define DC21140_VID 0x1011 /* DC21140 Manufacturer */ -#define DC2114x_DID 0x1900 /* Unique Device ID # */ -#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ - -/* -** Chipset defines -*/ -#define DC21040 DC21040_DID -#define DC21041 DC21041_DID -#define DC21140 DC21140_DID -#define DC2114x DC2114x_DID -#define DC21142 (DC2114x_DID | 0x0010) -#define DC21143 (DC2114x_DID | 0x0030) -#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ - -#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) -#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) -#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) -#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) -#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) -#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) - -/* -** PCI Configuration Command/Status Register (PCI_CFCS) -*/ -#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */ -#define CFCS_SSE 0x40000000 /* Signal System Error (S) */ -#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */ -#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */ -#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ -#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ -#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ -#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ -#define CFCS_PER 0x00000040 /* Parity Error Response (C) */ -#define CFCS_MO 0x00000004 /* Master Operation (C) */ -#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ -#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */ - -/* -** PCI Configuration Revision Register (PCI_CFRV) -*/ -#define CFRV_BC 0xff000000 /* Base Class */ -#define CFRV_SC 0x00ff0000 /* Subclass */ -#define CFRV_RN 0x000000f0 /* Revision Number */ -#define CFRV_SN 0x0000000f /* Step Number */ -#define BASE_CLASS 0x02000000 /* Indicates Network Controller */ -#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ -#define STEP_NUMBER 0x00000020 /* Increments for future chips */ -#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ -#define CFRV_MASK 0xffff0000 /* Register mask */ - -/* -** PCI Configuration Latency Timer Register (PCI_CFLT) -*/ -#define CFLT_BC 0x0000ff00 /* Latency Timer bits */ - -/* -** PCI Configuration Base I/O Address Register (PCI_CBIO) -*/ -#define CBIO_MASK -128 /* Base I/O Address Mask */ -#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ - -/* -** PCI Configuration Card Information Structure Register (PCI_CCIS) -*/ -#define CCIS_ROMI 0xf0000000 /* ROM Image */ -#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ -#define CCIS_ASI 0x00000007 /* Address Space Indicator */ - -/* -** PCI Configuration Subsystem ID Register (PCI_SSID) -*/ -#define SSID_SSID 0xffff0000 /* Subsystem ID */ -#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ - -/* -** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) -*/ -#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ -#define CBER_ROME 0x00000001 /* ROM Enable */ - -/* -** PCI Configuration Interrupt Register (PCI_CFIT) -*/ -#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ -#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ -#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ -#define CFIT_IRQL 0x000000ff /* Interrupt Line */ - -/* -** PCI Configuration Power Management Area Register (PCI_CFPM) -*/ -#define SLEEP 0x80 /* Power Saving Sleep Mode */ -#define SNOOZE 0x40 /* Power Saving Snooze Mode */ -#define WAKEUP 0x00 /* Power Saving Wakeup */ - -#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ -#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ - -/* -** DC21040 Bus Mode Register (DE4X5_BMR) -*/ -#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ -#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ -#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ -#define BMR_DAS 0x00010000 /* Diagnostic Address Space */ -#define BMR_CAL 0x0000c000 /* Cache Alignment */ -#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ -#define BMR_BLE 0x00000080 /* Big/Little Endian */ -#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ -#define BMR_BAR 0x00000002 /* Bus ARbitration */ -#define BMR_SWR 0x00000001 /* Software Reset */ - - /* Timings here are for 10BASE-T/AUI only*/ -#define TAP_NOPOLL 0x00000000 /* No automatic polling */ -#define TAP_200US 0x00020000 /* TX automatic polling every 200us */ -#define TAP_800US 0x00040000 /* TX automatic polling every 800us */ -#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */ -#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */ -#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */ -#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */ -#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */ - -#define CAL_NOUSE 0x00000000 /* Not used */ -#define CAL_8LONG 0x00004000 /* 8-longword alignment */ -#define CAL_16LONG 0x00008000 /* 16-longword alignment */ -#define CAL_32LONG 0x0000c000 /* 32-longword alignment */ - -#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ -#define PBL_1 0x00000100 /* 1 longword DMA burst length */ -#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ -#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ -#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ -#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ -#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ - -#define DSL_0 0x00000000 /* 0 longword / descriptor */ -#define DSL_1 0x00000004 /* 1 longword / descriptor */ -#define DSL_2 0x00000008 /* 2 longwords / descriptor */ -#define DSL_4 0x00000010 /* 4 longwords / descriptor */ -#define DSL_8 0x00000020 /* 8 longwords / descriptor */ -#define DSL_16 0x00000040 /* 16 longwords / descriptor */ -#define DSL_32 0x00000080 /* 32 longwords / descriptor */ - -/* -** DC21040 Transmit Poll Demand Register (DE4X5_TPD) -*/ -#define TPD 0x00000001 /* Transmit Poll Demand */ - -/* -** DC21040 Receive Poll Demand Register (DE4X5_RPD) -*/ -#define RPD 0x00000001 /* Receive Poll Demand */ - -/* -** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) -*/ -#define RRBA 0xfffffffc /* RX Descriptor List Start Address */ - -/* -** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) -*/ -#define TRBA 0xfffffffc /* TX Descriptor List Start Address */ - -/* -** Status Register (DE4X5_STS) -*/ -#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ -#define STS_BE 0x03800000 /* Bus Error Bits */ -#define STS_TS 0x00700000 /* Transmit Process State */ -#define STS_RS 0x000e0000 /* Receive Process State */ -#define STS_NIS 0x00010000 /* Normal Interrupt Summary */ -#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ -#define STS_ER 0x00004000 /* Early Receive */ -#define STS_FBE 0x00002000 /* Fatal Bus Error */ -#define STS_SE 0x00002000 /* System Error */ -#define STS_LNF 0x00001000 /* Link Fail */ -#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ -#define STS_TM 0x00000800 /* Timer Expired (DC21041) */ -#define STS_ETI 0x00000400 /* Early Transmit Interrupt */ -#define STS_AT 0x00000400 /* AUI/TP Pin */ -#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ -#define STS_RPS 0x00000100 /* Receive Process Stopped */ -#define STS_RU 0x00000080 /* Receive Buffer Unavailable */ -#define STS_RI 0x00000040 /* Receive Interrupt */ -#define STS_UNF 0x00000020 /* Transmit Underflow */ -#define STS_LNP 0x00000010 /* Link Pass */ -#define STS_ANC 0x00000010 /* Autonegotiation Complete */ -#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ -#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ -#define STS_TPS 0x00000002 /* Transmit Process Stopped */ -#define STS_TI 0x00000001 /* Transmit Interrupt */ - -#define EB_PAR 0x00000000 /* Parity Error */ -#define EB_MA 0x00800000 /* Master Abort */ -#define EB_TA 0x01000000 /* Target Abort */ -#define EB_RES0 0x01800000 /* Reserved */ -#define EB_RES1 0x02000000 /* Reserved */ - -#define TS_STOP 0x00000000 /* Stopped */ -#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */ -#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */ -#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */ -#define TS_RES 0x00400000 /* Reserved */ -#define TS_SPKT 0x00500000 /* Setup Packet */ -#define TS_SUSP 0x00600000 /* Suspended */ -#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */ - -#define RS_STOP 0x00000000 /* Stopped */ -#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */ -#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */ -#define RS_WFRP 0x00060000 /* Wait for Receive Packet */ -#define RS_SUSP 0x00080000 /* Suspended */ -#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */ -#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */ -#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */ - -#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ - -/* -** Operation Mode Register (DE4X5_OMR) -*/ -#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ -#define OMR_RA 0x40000000 /* Receive All */ -#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ -#define OMR_SCR 0x01000000 /* Scrambler Mode */ -#define OMR_PCS 0x00800000 /* PCS Function */ -#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ -#define OMR_SF 0x00200000 /* Store and Forward */ -#define OMR_HBD 0x00080000 /* HeartBeat Disable */ -#define OMR_PS 0x00040000 /* Port Select */ -#define OMR_CA 0x00020000 /* Capture Effect Enable */ -#define OMR_BP 0x00010000 /* Back Pressure */ -#define OMR_TR 0x0000c000 /* Threshold Control Bits */ -#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ -#define OMR_FC 0x00001000 /* Force Collision Mode */ -#define OMR_OM 0x00000c00 /* Operating Mode */ -#define OMR_FDX 0x00000200 /* Full Duplex Mode */ -#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ -#define OMR_PM 0x00000080 /* Pass All Multicast */ -#define OMR_PR 0x00000040 /* Promiscuous Mode */ -#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */ -#define OMR_IF 0x00000010 /* Inverse Filtering */ -#define OMR_PB 0x00000008 /* Pass Bad Frames */ -#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */ -#define OMR_SR 0x00000002 /* Start/Stop Receive */ -#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ - -#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ -#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ -#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ -#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ - -#define OMR_DEF (OMR_SDP) -#define OMR_SIA (OMR_SDP | OMR_TTM) -#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) -#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) -#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) - -/* -** DC21040 Interrupt Mask Register (DE4X5_IMR) -*/ -#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ -#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ -#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ -#define IMR_ERM 0x00004000 /* Early Receive Mask */ -#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ -#define IMR_SEM 0x00002000 /* System Error Mask */ -#define IMR_LFM 0x00001000 /* Link Fail Mask */ -#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ -#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ -#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ -#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ -#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ -#define IMR_RSM 0x00000100 /* Receive Stopped Mask */ -#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ -#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ -#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ -#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ -#define IMR_LPM 0x00000010 /* Link Pass */ -#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ -#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ -#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */ -#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ - -/* -** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) -*/ -#define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ -#define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ -#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ -#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ -#define MFC_FOCM 0x1ffe0000 /* FIFO Overflow Counter Mask */ - -/* -** DC21040 Ethernet Address PROM (DE4X5_APROM) -*/ -#define APROM_DN 0x80000000 /* Data Not Valid */ -#define APROM_DT 0x000000ff /* Address Byte */ - -/* -** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) -*/ -#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ -#define BROM_RD 0x00004000 /* Read from Boot ROM */ -#define BROM_WR 0x00002000 /* Write to Boot ROM */ -#define BROM_BR 0x00001000 /* Select Boot ROM when set */ -#define BROM_SR 0x00000800 /* Select Serial ROM when set */ -#define BROM_REG 0x00000400 /* External Register Select */ -#define BROM_DT 0x000000ff /* Data Byte */ - -/* -** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) -*/ -#define MII_MDI 0x00080000 /* MII Management Data In */ -#define MII_MDO 0x00060000 /* MII Management Mode/Data Out */ -#define MII_MRD 0x00040000 /* MII Management Define Read Mode */ -#define MII_MWR 0x00000000 /* MII Management Define Write Mode */ -#define MII_MDT 0x00020000 /* MII Management Data Out */ -#define MII_MDC 0x00010000 /* MII Management Clock */ -#define MII_RD 0x00004000 /* Read from MII */ -#define MII_WR 0x00002000 /* Write to MII */ -#define MII_SEL 0x00000800 /* Select MII when RESET */ - -#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ -#define SROM_RD 0x00004000 /* Read from Boot ROM */ -#define SROM_WR 0x00002000 /* Write to Boot ROM */ -#define SROM_BR 0x00001000 /* Select Boot ROM when set */ -#define SROM_SR 0x00000800 /* Select Serial ROM when set */ -#define SROM_REG 0x00000400 /* External Register Select */ -#define SROM_DT 0x000000ff /* Data Byte */ - -#define DT_OUT 0x00000008 /* Serial Data Out */ -#define DT_IN 0x00000004 /* Serial Data In */ -#define DT_CLK 0x00000002 /* Serial ROM Clock */ -#define DT_CS 0x00000001 /* Serial ROM Chip Select */ - -#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ -#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ -#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ -#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ - -#define MII_CR 0x00 /* MII Management Control Register */ -#define MII_SR 0x01 /* MII Management Status Register */ -#define MII_ID0 0x02 /* PHY Identifier Register 0 */ -#define MII_ID1 0x03 /* PHY Identifier Register 1 */ -#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ -#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ -#define MII_ANE 0x06 /* Auto Negotiation Expansion */ -#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ - -#define DE4X5_MAX_MII 32 /* Maximum address of MII PHY devices */ - -/* -** MII Management Control Register -*/ -#define MII_CR_RST 0x8000 /* RESET the PHY chip */ -#define MII_CR_LPBK 0x4000 /* Loopback enable */ -#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ -#define MII_CR_10 0x0000 /* Set 10Mb/s */ -#define MII_CR_100 0x2000 /* Set 100Mb/s */ -#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ -#define MII_CR_PD 0x0800 /* Power Down */ -#define MII_CR_ISOL 0x0400 /* Isolate Mode */ -#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ -#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ -#define MII_CR_CTE 0x0080 /* Collision Test Enable */ - -/* -** MII Management Status Register -*/ -#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ -#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ -#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ -#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ -#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ -#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ -#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ -#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ -#define MII_SR_LKS 0x0004 /* Link Status */ -#define MII_SR_JABD 0x0002 /* Jabber Detect */ -#define MII_SR_XC 0x0001 /* Extended Capabilities */ - -/* -** MII Management Auto Negotiation Advertisement Register -*/ -#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ - -/* -** MII Management Auto Negotiation Remote End Register -*/ -#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ -#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ -#define MII_ANLPA_RF 0x2000 /* Remote Fault */ -#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ - -/* -** SROM Media Definitions (ABG SROM Section) -*/ -#define MEDIA_NWAY 0x0080 /* Nway (Auto Negotiation) on PHY */ -#define MEDIA_MII 0x0040 /* MII Present on the adapter */ -#define MEDIA_FIBRE 0x0008 /* Fibre Media present */ -#define MEDIA_AUI 0x0004 /* AUI Media present */ -#define MEDIA_TP 0x0002 /* TP Media present */ -#define MEDIA_BNC 0x0001 /* BNC Media present */ - -/* -** SROM Definitions (Digital Semiconductor Format) -*/ -#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ -#define SROM_SSID 0x0002 /* Sub-system ID offset */ -#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ -#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ -#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ -#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ -#define SROM_SFV 0x0012 /* SROM Format Version offset */ -#define SROM_CCNT 0x0013 /* Controller Count offset */ -#define SROM_HWADD 0x0014 /* Hardware Address offset */ -#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ -#define SROM_CRC 0x007e /* SROM CRC offset */ - -/* -** SROM Media Connection Definitions -*/ -#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ -#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ -#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ -#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ -#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ -#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ -#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ -#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ -#define SROM_100BT4 0x0006 /* 100BASE-T4 */ -#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ -#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ -#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ -#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ -#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ -#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ -#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ -#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ -#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ -#define SROM_PAO 0x8800 /* Powerup Autosense Only */ -#define SROM_NSMI 0xffff /* No Selected Media Information */ - -/* -** SROM Media Definitions -*/ -#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ -#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ -#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ -#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ -#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ -#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ -#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ -#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ -#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ - -#define BLOCK_LEN 0x7f /* Extended blocks length mask */ -#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ -#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ - -/* -** SROM Compact Format Block Masks -*/ -#define COMPACT_FI 0x80 /* Format Indicator */ -#define COMPACT_LEN 0x04 /* Length */ -#define COMPACT_MC 0x3f /* Media Code */ - -/* -** SROM Extended Format Block Type 0 Masks -*/ -#define BLOCK0_FI 0x80 /* Format Indicator */ -#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ -#define BLOCK0_MC 0x3f /* Media Code */ - -/* -** DC21040 Full Duplex Register (DE4X5_FDR) -*/ -#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ - -/* -** DC21041 General Purpose Timer Register (DE4X5_GPT) -*/ -#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */ -#define GPT_VAL 0x0000ffff /* Timer Value */ - -/* -** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) -*/ -/* Valid ONLY for DE500 hardware */ -#define GEP_LNP 0x00000080 /* Link Pass (input) */ -#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ -#define GEP_SDET 0x00000020 /* Signal Detect (input) */ -#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ -#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ -#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ -#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ -#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ -#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ -#define GEP_CTRL 0x00000100 /* GEP control bit */ - -/* -** SIA Register Defaults -*/ -#define CSR13 0x00000001 -#define CSR14 0x0003ff7f /* Autonegotiation disabled */ -#define CSR15 0x00000008 - -/* -** SIA Status Register (DE4X5_SISR) -*/ -#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ -#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ -#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ -#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ -#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ -#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ -#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ -#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ -#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ -#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ -#define SISR_DAO 0x00000080 /* PLL All One */ -#define SISR_DAZ 0x00000040 /* PLL All Zero */ -#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ -#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ -#define SISR_APS 0x00000008 /* Auto Polarity State */ -#define SISR_LKF 0x00000004 /* Link Fail Status */ -#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ -#define SISR_NCR 0x00000002 /* Network Connection Error */ -#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ -#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ - -#define ANS_NDIS 0x00000000 /* Nway disable */ -#define ANS_TDIS 0x00001000 /* Transmit Disable */ -#define ANS_ADET 0x00002000 /* Ability Detect */ -#define ANS_ACK 0x00003000 /* Acknowledge */ -#define ANS_CACK 0x00004000 /* Complete Acknowledge */ -#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ -#define ANS_LCHK 0x00006000 /* Link Check */ - -#define SISR_RST 0x00000301 /* CSR12 reset */ -#define SISR_ANR 0x00001301 /* Autonegotiation restart */ - -/* -** SIA Connectivity Register (DE4X5_SICR) -*/ -#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ -#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ -#define SICR_OE24 0x00004000 /* Output Enable 2 4 */ -#define SICR_OE13 0x00002000 /* Output Enable 1 3 */ -#define SICR_IE 0x00001000 /* Input Enable */ -#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */ -#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */ -#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/ -#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/ -#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */ -#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */ -#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/ -#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */ -#define SICR_ASE 0x00000080 /* APLL Start Enable*/ -#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ -#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ -#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ -#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ -#define SICR_CAC 0x00000004 /* CSR Auto Configuration */ -#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ -#define SICR_SRL 0x00000001 /* SIA Reset */ -#define SIA_RESET 0x00000000 /* SIA Reset Value */ - -/* -** SIA Transmit and Receive Register (DE4X5_STRR) -*/ -#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ -#define STRR_SPP 0x00004000 /* Set Polarity Plus */ -#define STRR_APE 0x00002000 /* Auto Polarity Enable */ -#define STRR_LTE 0x00001000 /* Link Test Enable */ -#define STRR_SQE 0x00000800 /* Signal Quality Enable */ -#define STRR_CLD 0x00000400 /* Collision Detect Enable */ -#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */ -#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */ -#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */ -#define STRR_HDE 0x00000040 /* Half Duplex Enable */ -#define STRR_CPEN 0x00000030 /* Compensation Enable */ -#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */ -#define STRR_DREN 0x00000004 /* Driver Enable */ -#define STRR_LBK 0x00000002 /* Loopback Enable */ -#define STRR_ECEN 0x00000001 /* Encoder Enable */ -#define STRR_RESET 0xffffffff /* Reset value for STRR */ - -/* -** SIA General Register (DE4X5_SIGR) -*/ -#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ -#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ -#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ -#define SIGR_CWE 0x08000000 /* Control Write Enable */ -#define SIGR_RME 0x04000000 /* Receive Match Enable */ -#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ -#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ -#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ -#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ -#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ -#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ -#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ -#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ -#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ -#define SIGR_FRL 0x00002000 /* Force Receiver Low */ -#define SIGR_DPST 0x00001000 /* PLL Self Test Start */ -#define SIGR_LSD 0x00000800 /* LED Stretch Disable */ -#define SIGR_FLF 0x00000400 /* Force Link Fail */ -#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */ -#define SIGR_TSCK 0x00000100 /* Test Clock */ -#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */ -#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */ -#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */ -#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */ -#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */ -#define SIGR_JCK 0x00000004 /* Jabber Clock */ -#define SIGR_HUJ 0x00000002 /* Host Unjab */ -#define SIGR_JBD 0x00000001 /* Jabber Disable */ -#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */ - -/* -** Receive Descriptor Bit Summary -*/ -#define R_OWN 0x80000000 /* Own Bit */ -#define RD_FF 0x40000000 /* Filtering Fail */ -#define RD_FL 0x3fff0000 /* Frame Length */ -#define RD_ES 0x00008000 /* Error Summary */ -#define RD_LE 0x00004000 /* Length Error */ -#define RD_DT 0x00003000 /* Data Type */ -#define RD_RF 0x00000800 /* Runt Frame */ -#define RD_MF 0x00000400 /* Multicast Frame */ -#define RD_FS 0x00000200 /* First Descriptor */ -#define RD_LS 0x00000100 /* Last Descriptor */ -#define RD_TL 0x00000080 /* Frame Too Long */ -#define RD_CS 0x00000040 /* Collision Seen */ -#define RD_FT 0x00000020 /* Frame Type */ -#define RD_RJ 0x00000010 /* Receive Watchdog */ -#define RD_RE 0x00000008 /* Report on MII Error */ -#define RD_DB 0x00000004 /* Dribbling Bit */ -#define RD_CE 0x00000002 /* CRC Error */ -#define RD_OF 0x00000001 /* Overflow */ - -#define RD_RER 0x02000000 /* Receive End Of Ring */ -#define RD_RCH 0x01000000 /* Second Address Chained */ -#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ -#define RD_RBS1 0x000007ff /* Buffer 1 Size */ - -/* -** Transmit Descriptor Bit Summary -*/ -#define T_OWN 0x80000000 /* Own Bit */ -#define TD_ES 0x00008000 /* Error Summary */ -#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */ -#define TD_LO 0x00000800 /* Loss Of Carrier */ -#define TD_NC 0x00000400 /* No Carrier */ -#define TD_LC 0x00000200 /* Late Collision */ -#define TD_EC 0x00000100 /* Excessive Collisions */ -#define TD_HF 0x00000080 /* Heartbeat Fail */ -#define TD_CC 0x00000078 /* Collision Counter */ -#define TD_LF 0x00000004 /* Link Fail */ -#define TD_UF 0x00000002 /* Underflow Error */ -#define TD_DE 0x00000001 /* Deferred */ - -#define TD_IC 0x80000000 /* Interrupt On Completion */ -#define TD_LS 0x40000000 /* Last Segment */ -#define TD_FS 0x20000000 /* First Segment */ -#define TD_FT1 0x10000000 /* Filtering Type */ -#define TD_SET 0x08000000 /* Setup Packet */ -#define TD_AC 0x04000000 /* Add CRC Disable */ -#define TD_TER 0x02000000 /* Transmit End Of Ring */ -#define TD_TCH 0x01000000 /* Second Address Chained */ -#define TD_DPD 0x00800000 /* Disabled Padding */ -#define TD_FT0 0x00400000 /* Filtering Type */ -#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ -#define TD_TBS1 0x000007ff /* Buffer 1 Size */ - -#define PERFECT_F 0x00000000 -#define HASH_F TD_FT0 -#define INVERSE_F TD_FT1 -#define HASH_O_F (TD_FT1 | TD_F0) - -/* -** Media / mode state machine definitions -** User selectable: -*/ -#define TP 0x0040 /* 10Base-T (now equiv to _10Mb) */ -#define TP_NW 0x0002 /* 10Base-T with Nway */ -#define BNC 0x0004 /* Thinwire */ -#define AUI 0x0008 /* Thickwire */ -#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ -#define _10Mb 0x0040 /* 10Mb/s Ethernet */ -#define _100Mb 0x0080 /* 100Mb/s Ethernet */ -#define AUTO 0x4000 /* Auto sense the media or speed */ - -/* -** Internal states -*/ -#define NC 0x0000 /* No Connection */ -#define ANS 0x0020 /* Intermediate AutoNegotiation State */ -#define SPD_DET 0x0100 /* Parallel speed detection */ -#define INIT 0x0200 /* Initial state */ -#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ -#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ -#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ -#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ -#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ -#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ -#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ -#define MII 0x1000 /* MII on the 21143 */ - -#define TIMER_CB 0x80000000 /* Timer callback detection */ - -/* -** DE4X5 DEBUG Options -*/ -#define DEBUG_NONE 0x0000 /* No DEBUG messages */ -#define DEBUG_VERSION 0x0001 /* Print version message */ -#define DEBUG_MEDIA 0x0002 /* Print media messages */ -#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ -#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ -#define DEBUG_SROM 0x0010 /* Print SROM messages */ -#define DEBUG_MII 0x0020 /* Print MII messages */ -#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ -#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ -#define DEBUG_PCICFG 0x0100 -#define DEBUG_ALL 0x01ff - -/* -** Miscellaneous -*/ -#define PCI 0 -#define EISA 1 - -#define HASH_TABLE_LEN 512 /* Bits */ -#define HASH_BITS 0x01ff /* 9 LS bits */ - -#define SETUP_FRAME_LEN 192 /* Bytes */ -#define IMPERF_PA_OFFSET 156 /* Bytes */ - -#define POLL_DEMAND 1 - -#define LOST_MEDIA_THRESHOLD 3 - -#define MASK_INTERRUPTS 1 -#define UNMASK_INTERRUPTS 0 - -#define DE4X5_STRLEN 8 - -#define DE4X5_INIT 0 /* Initialisation time */ -#define DE4X5_RUN 1 /* Run time */ - -#define DE4X5_SAVE_STATE 0 -#define DE4X5_RESTORE_STATE 1 - -/* -** Address Filtering Modes -*/ -#define PERFECT 0 /* 16 perfect physical addresses */ -#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ -#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ -#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ - -#define ALL 0 /* Clear out all the setup frame */ -#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ - -/* -** Booleans -*/ -#define NO 0 -#define FALSE 0 - -#define YES ~0 -#define TRUE ~0 - -/* -** Adapter state -*/ -#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ -#define CLOSED 1 /* Ready for opening */ -#define OPEN 2 /* Running */ - -/* -** Various wait times -*/ -#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ -#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ - -/* -** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since -** the vendors seem split 50-50 on how to calculate the OUI register values -** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. -*/ -#define NATIONAL_TX 0x2000 -#define BROADCOM_T4 0x03e0 -#define SEEQ_T4 0x0016 -#define CYPRESS_T4 0x0014 - -/* -** Speed Selection stuff -*/ -#define SET_10Mb {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ - if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ - outl(omr, DE4X5_OMR);\ - if (!lp->useSROM) lp->cache.gep = 0;\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ - lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -#define SET_100Mb {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - int fdx=0;\ - if (lp->phy[lp->active].id == NATIONAL_TX) {\ - mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ - 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ - sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ - if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ - if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - }\ - if (fdx) omr |= OMR_FDX;\ - outl(omr, DE4X5_OMR);\ - if (!lp->useSROM) lp->cache.gep = 0;\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -/* FIX ME so I don't jam 10Mb networks */ -#define SET_100Mb_PDET {\ - if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ - mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr, DE4X5_OMR);\ - } else if (lp->useSROM && !lp->useMII) {\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr, DE4X5_OMR);\ - } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ - lp->cache.gep = (GEP_FDXD | GEP_MODE);\ - gep_wr(lp->cache.gep, dev);\ - }\ -} - -/* -** Include the IOCTL stuff -*/ -#include - -#define DE4X5IOCTL SIOCDEVPRIVATE - -struct de4x5_ioctl { - unsigned short cmd; /* Command to run */ - unsigned short len; /* Length of the data buffer */ - unsigned char *data; /* Pointer to the data buffer */ -}; - -/* -** Recognised commands for the driver -*/ -#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ -#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ -#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ -#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ -#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ -#define DE4X5_GET_MCA 0x06 /* Get a multicast address */ -#define DE4X5_SET_MCA 0x07 /* Set a multicast address */ -#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */ -#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */ -#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */ -#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */ -#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ -#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ -#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - -#define MOTO_SROM_BUG ((lp->active == 8) && (((le32_to_cpu(get_unaligned(((s32 *)dev->dev_addr))))&0x00ffffff)==0x3e0008)) diff -urN linux-2.5.6-pre3/drivers/net/dmfe.c linux-2.5.6/drivers/net/dmfe.c --- linux-2.5.6-pre3/drivers/net/dmfe.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/drivers/net/dmfe.c Wed Dec 31 16:00:00 1969 @@ -1,2070 +0,0 @@ -/* - A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast - ethernet driver for Linux. - Copyright (C) 1997 Sten Wang - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - DAVICOM Web-Site: www.davicom.com.tw - - Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw - Maintainer: Tobias Ringstrom - - (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. - - Marcelo Tosatti : - Made it compile in 2.3 (device to net_device) - - Alan Cox : - Cleaned up for kernel merge. - Removed the back compatibility support - Reformatted, fixing spelling etc as I went - Removed IRQ 0-15 assumption - - Jeff Garzik : - Updated to use new PCI driver API. - Resource usage cleanups. - Report driver version to user. - - Tobias Ringstrom : - Cleaned up and added SMP safety. Thanks go to Jeff Garzik, - Andrew Morton and Frank Davis for the SMP safety fixes. - - Vojtech Pavlik : - Cleaned up pointer arithmetics. - Fixed a lot of 64bit issues. - Cleaned up printk()s a bit. - Fixed some obvious big endian problems. - - Tobias Ringstrom : - Use time_after for jiffies calculation. Added ethtool - support. Updated PCI resource allocation. Do not - forget to unmap PCI mapped skbs. - - TODO - - Implement pci_driver::suspend() and pci_driver::resume() - power management methods. - - Check on 64 bit boxes. - Check and fix on big endian boxes. - - Test and make sure PCI latency is now correct for all cases. -*/ - -#define DRV_NAME "dmfe" -#define DRV_VERSION "1.36.4" -#define DRV_RELDATE "2002-01-17" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -/* Board/System/Debug information/definition ---------------- */ -#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ -#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ -#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ -#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ - -#define DM9102_IO_SIZE 0x80 -#define DM9102A_IO_SIZE 0x100 -#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ -#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ -#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ -#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ -#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ -#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) -#define TX_BUF_ALLOC 0x600 -#define RX_ALLOC_SIZE 0x620 -#define DM910X_RESET 1 -#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ -#define CR6_DEFAULT 0x00080000 /* HD */ -#define CR7_DEFAULT 0x180c1 -#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ -#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ -#define MAX_PACKET_SIZE 1514 -#define DMFE_MAX_MULTICAST 14 -#define RX_COPY_SIZE 100 -#define MAX_CHECK_PACKET 0x8000 -#define DM9801_NOISE_FLOOR 8 -#define DM9802_NOISE_FLOOR 5 - -#define DMFE_10MHF 0 -#define DMFE_100MHF 1 -#define DMFE_10MFD 4 -#define DMFE_100MFD 5 -#define DMFE_AUTO 8 -#define DMFE_1M_HPNA 0x10 - -#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ -#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ -#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ -#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ -#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ -#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ - -#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ -#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ -#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ - -#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) - -#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); - - -/* CR9 definition: SROM/MII */ -#define CR9_SROM_READ 0x4800 -#define CR9_SRCS 0x1 -#define CR9_SRCLK 0x2 -#define CR9_CRDOUT 0x8 -#define SROM_DATA_0 0x0 -#define SROM_DATA_1 0x4 -#define PHY_DATA_1 0x20000 -#define PHY_DATA_0 0x00000 -#define MDCLKH 0x10000 - -#define PHY_POWER_DOWN 0x800 - -#define SROM_V41_CODE 0x14 - -#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); - -#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE -#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) - -/* Sten Check */ -#define DEVICE net_device - -/* Structure/enum declaration ------------------------------- */ -struct tx_desc { - u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ - char *tx_buf_ptr; /* Data for us */ - struct tx_desc *next_tx_desc; -} __attribute__(( aligned(32) )); - -struct rx_desc { - u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ - struct sk_buff *rx_skb_ptr; /* Data for us */ - struct rx_desc *next_rx_desc; -} __attribute__(( aligned(32) )); - -struct dmfe_board_info { - u32 chip_id; /* Chip vendor/Device ID */ - u32 chip_revision; /* Chip revision */ - struct DEVICE *next_dev; /* next device */ - struct pci_dev *pdev; /* PCI device */ - spinlock_t lock; - - long ioaddr; /* I/O base address */ - u32 cr0_data; - u32 cr5_data; - u32 cr6_data; - u32 cr7_data; - u32 cr15_data; - - /* pointer for memory physical address */ - dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ - dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ - dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ - dma_addr_t first_tx_desc_dma; - dma_addr_t first_rx_desc_dma; - - /* descriptor pointer */ - unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ - unsigned char *buf_pool_start; /* Tx buffer pool align dword */ - unsigned char *desc_pool_ptr; /* descriptor pool memory */ - struct tx_desc *first_tx_desc; - struct tx_desc *tx_insert_ptr; - struct tx_desc *tx_remove_ptr; - struct rx_desc *first_rx_desc; - struct rx_desc *rx_insert_ptr; - struct rx_desc *rx_ready_ptr; /* packet come pointer */ - unsigned long tx_packet_cnt; /* transmitted packet count */ - unsigned long tx_queue_cnt; /* wait to send packet count */ - unsigned long rx_avail_cnt; /* available rx descriptor count */ - unsigned long interval_rx_cnt; /* rx packet count a callback time */ - - u16 HPNA_command; /* For HPNA register 16 */ - u16 HPNA_timer; /* For HPNA remote device check */ - u16 dbug_cnt; - u16 NIC_capability; /* NIC media capability */ - u16 PHY_reg4; /* Saved Phyxcer register 4 value */ - - u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ - u8 chip_type; /* Keep DM9102A chip type */ - u8 media_mode; /* user specify media mode */ - u8 op_mode; /* real work media mode */ - u8 phy_addr; - u8 link_failed; /* Ever link failed */ - u8 wait_reset; /* Hardware failed, need to reset */ - u8 dm910x_chk_mode; /* Operating mode check */ - u8 first_in_callback; /* Flag to record state */ - struct timer_list timer; - - /* System defined statistic counter */ - struct net_device_stats stats; - - /* Driver defined statistic counter */ - unsigned long tx_fifo_underrun; - unsigned long tx_loss_carrier; - unsigned long tx_no_carrier; - unsigned long tx_late_collision; - unsigned long tx_excessive_collision; - unsigned long tx_jabber_timeout; - unsigned long reset_count; - unsigned long reset_cr8; - unsigned long reset_fatal; - unsigned long reset_TXtimeout; - - /* NIC SROM data */ - unsigned char srom[128]; -}; - -enum dmfe_offsets { - DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, - DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, - DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, - DCR15 = 0x78 -}; - -enum dmfe_CR6_bits { - CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, - CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, - CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 -}; - -/* Global variable declaration ----------------------------- */ -static int __devinitdata printed_version; -static char version[] __devinitdata = - KERN_INFO DRV_NAME ": Davicom DM9xxx net driver, version " - DRV_VERSION " (" DRV_RELDATE ")\n"; - -static int dmfe_debug; -static unsigned char dmfe_media_mode = DMFE_AUTO; -static u32 dmfe_cr6_user_set; - -/* For module input parameter */ -static int debug; -static u32 cr6set; -static unsigned char mode = 8; -static u8 chkmode = 1; -static u8 HPNA_mode; /* Default: Low Power/High Speed */ -static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ -static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ -static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ -static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control - 4: TX pause packet */ - - -/* function declaration ------------------------------------- */ -static int dmfe_open(struct DEVICE *); -static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *); -static int dmfe_stop(struct DEVICE *); -static struct net_device_stats * dmfe_get_stats(struct DEVICE *); -static void dmfe_set_filter_mode(struct DEVICE *); -static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int); -static u16 read_srom_word(long ,int); -static void dmfe_interrupt(int , void *, struct pt_regs *); -static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); -static void allocate_rx_buffer(struct dmfe_board_info *); -static void update_cr6(u32, unsigned long); -static void send_filter_frame(struct DEVICE * ,int); -static void dm9132_id_table(struct DEVICE * ,int); -static u16 phy_read(unsigned long, u8, u8, u32); -static void phy_write(unsigned long, u8, u8, u16, u32); -static void phy_write_1bit(unsigned long, u32); -static u16 phy_read_1bit(unsigned long); -static u8 dmfe_sense_speed(struct dmfe_board_info *); -static void dmfe_process_mode(struct dmfe_board_info *); -static void dmfe_timer(unsigned long); -static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); -static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); -static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); -static void dmfe_dynamic_reset(struct DEVICE *); -static void dmfe_free_rxbuffer(struct dmfe_board_info *); -static void dmfe_init_dm910x(struct DEVICE *); -static inline u32 cal_CRC(unsigned char *, unsigned int, u8); -static void dmfe_parse_srom(struct dmfe_board_info *); -static void dmfe_program_DM9801(struct dmfe_board_info *, int); -static void dmfe_program_DM9802(struct dmfe_board_info *); -static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); -static void dmfe_set_phyxcer(struct dmfe_board_info *); - -/* DM910X network baord routine ---------------------------- */ - -/* - * Search DM910X board ,allocate space and register it - */ - -static int __devinit dmfe_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct dmfe_board_info *db; /* board information structure */ - struct net_device *dev; - u32 dev_rev, pci_pmr; - int i, err; - - DMFE_DBUG(0, "dmfe_init_one()", 0); - - if (!printed_version++) - printk(version); - - /* Init network device */ - dev = alloc_etherdev(sizeof(*db)); - if (dev == NULL) - return -ENOMEM; - SET_MODULE_OWNER(dev); - - if (pci_set_dma_mask(pdev, 0xffffffff)) { - printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n"); - err = -ENODEV; - goto err_out_free; - } - - /* Enable Master/IO access, Disable memory access */ - err = pci_enable_device(pdev); - if (err) - goto err_out_free; - - if (!pci_resource_start(pdev, 0)) { - printk(KERN_ERR DRV_NAME ": I/O base is zero\n"); - err = -ENODEV; - goto err_out_disable; - } - - /* Read Chip revision */ - pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); - - if (pci_resource_len(pdev, 0) < (CHK_IO_SIZE(pdev, dev_rev)) ) { - printk(KERN_ERR DRV_NAME ": Allocated I/O size too small\n"); - err = -ENODEV; - goto err_out_disable; - } - -#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ - - /* Set Latency Timer 80h */ - /* FIXME: setting values > 32 breaks some SiS 559x stuff. - Need a PCI quirk.. */ - - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); -#endif - - if (pci_request_regions(pdev, DRV_NAME)) { - printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n"); - err = -ENODEV; - goto err_out_disable; - } - - /* Init system & device */ - db = dev->priv; - - /* Allocate Tx/Rx descriptor memory */ - db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); - db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); - - db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; - db->first_tx_desc_dma = db->desc_pool_dma_ptr; - db->buf_pool_start = db->buf_pool_ptr; - db->buf_pool_dma_start = db->buf_pool_dma_ptr; - - db->chip_id = ent->driver_data; - db->ioaddr = pci_resource_start(pdev, 0); - db->chip_revision = dev_rev; - - db->pdev = pdev; - - dev->base_addr = db->ioaddr; - dev->irq = pdev->irq; - pci_set_drvdata(pdev, dev); - dev->open = &dmfe_open; - dev->hard_start_xmit = &dmfe_start_xmit; - dev->stop = &dmfe_stop; - dev->get_stats = &dmfe_get_stats; - dev->set_multicast_list = &dmfe_set_filter_mode; - dev->do_ioctl = &dmfe_do_ioctl; - spin_lock_init(&db->lock); - - pci_read_config_dword(pdev, 0x50, &pci_pmr); - pci_pmr &= 0x70000; - if ( (pci_pmr == 0x10000) && (dev_rev == 0x02000031) ) - db->chip_type = 1; /* DM9102A E3 */ - else - db->chip_type = 0; - - /* read 64 word srom data */ - for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); - - /* Set Node address */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = db->srom[20 + i]; - - err = register_netdev (dev); - if (err) - goto err_out_res; - - printk(KERN_INFO "%s: Davicom DM%04lx at pci%s,", - dev->name, - ent->driver_data >> 16, - pdev->slot_name); - for (i = 0; i < 6; i++) - printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); - printk(", irq %d.\n", dev->irq); - - pci_set_master(pdev); - - return 0; - -err_out_res: - pci_release_regions(pdev); -err_out_disable: - pci_disable_device(pdev); -err_out_free: - pci_set_drvdata(pdev, NULL); - kfree(dev); - - return err; -} - - -static void __exit dmfe_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_remove_one()", 0); - - if (dev) { - pci_free_consistent(db->pdev, sizeof(struct tx_desc) * - DESC_ALL_CNT + 0x20, db->desc_pool_ptr, - db->desc_pool_dma_ptr); - pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, - db->buf_pool_ptr, db->buf_pool_dma_ptr); - unregister_netdev(dev); - pci_release_regions(pdev); - kfree(dev); /* free board information */ - pci_set_drvdata(pdev, NULL); - } - - DMFE_DBUG(0, "dmfe_remove_one() exit", 0); -} - - -/* - * Open the interface. - * The interface is opened whenever "ifconfig" actives it. - */ - -static int dmfe_open(struct DEVICE *dev) -{ - int ret; - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_open", 0); - - ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); - if (ret) - return ret; - - /* system variable init */ - db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; - db->tx_packet_cnt = 0; - db->tx_queue_cnt = 0; - db->rx_avail_cnt = 0; - db->link_failed = 1; - db->wait_reset = 0; - - db->first_in_callback = 0; - db->NIC_capability = 0xf; /* All capability*/ - db->PHY_reg4 = 0x1e0; - - /* CR6 operation mode decision */ - if ( !chkmode || (db->chip_id == PCI_DM9132_ID) || - (db->chip_revision >= 0x02000030) ) { - db->cr6_data |= DMFE_TXTH_256; - db->cr0_data = CR0_DEFAULT; - db->dm910x_chk_mode=4; /* Enter the normal mode */ - } else { - db->cr6_data |= CR6_SFT; /* Store & Forward mode */ - db->cr0_data = 0; - db->dm910x_chk_mode = 1; /* Enter the check mode */ - } - - /* Initilize DM910X board */ - dmfe_init_dm910x(dev); - - /* Active System Interface */ - netif_wake_queue(dev); - - /* set and active a timer process */ - init_timer(&db->timer); - db->timer.expires = DMFE_TIMER_WUT + HZ * 2; - db->timer.data = (unsigned long)dev; - db->timer.function = &dmfe_timer; - add_timer(&db->timer); - - return 0; -} - - -/* Initilize DM910X board - * Reset DM910X board - * Initilize TX/Rx descriptor chain structure - * Send the set-up frame - * Enable Tx/Rx machine - */ - -static void dmfe_init_dm910x(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long ioaddr = db->ioaddr; - - DMFE_DBUG(0, "dmfe_init_dm910x()", 0); - - /* Reset DM910x MAC controller */ - outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ - udelay(100); - outl(db->cr0_data, ioaddr + DCR0); - udelay(5); - - /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ - db->phy_addr = 1; - - /* Parser SROM and media mode */ - dmfe_parse_srom(db); - db->media_mode = dmfe_media_mode; - - /* RESET Phyxcer Chip by GPR port bit 7 */ - outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ - if (db->chip_id == PCI_DM9009_ID) { - outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ - mdelay(300); /* Delay 300 ms */ - } - outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ - - /* Process Phyxcer Media Mode */ - if ( !(db->media_mode & 0x10) ) /* Force 1M mode */ - dmfe_set_phyxcer(db); - - /* Media Mode Process */ - if ( !(db->media_mode & DMFE_AUTO) ) - db->op_mode = db->media_mode; /* Force Mode */ - - /* Initiliaze Transmit/Receive decriptor and CR3/4 */ - dmfe_descriptor_init(db, ioaddr); - - /* Init CR6 to program DM910x operation */ - update_cr6(db->cr6_data, ioaddr); - - /* Send setup frame */ - if (db->chip_id == PCI_DM9132_ID) - dm9132_id_table(dev, dev->mc_count); /* DM9132 */ - else - send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ - - /* Init CR7, interrupt active bit */ - db->cr7_data = CR7_DEFAULT; - outl(db->cr7_data, ioaddr + DCR7); - - /* Init CR15, Tx jabber and Rx watchdog timer */ - outl(db->cr15_data, ioaddr + DCR15); - - /* Enable DM910X Tx/Rx function */ - db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; - update_cr6(db->cr6_data, ioaddr); -} - - -/* - * Hardware start transmission. - * Send a packet to media from the upper layer. - */ - -static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - struct tx_desc *txptr; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_start_xmit", 0); - - /* Resource flag check */ - netif_stop_queue(dev); - - /* Too large packet check */ - if (skb->len > MAX_PACKET_SIZE) { - printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len); - dev_kfree_skb(skb); - return 0; - } - - spin_lock_irqsave(&db->lock, flags); - - /* No Tx resource check, it never happen nromally */ - if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { - spin_unlock_irqrestore(&db->lock, flags); - printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_queue_cnt); - return 1; - } - - /* Disable NIC interrupt */ - outl(0, dev->base_addr + DCR7); - - /* transmit this packet */ - txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); - txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); - - /* Point to next transmit free descriptor */ - db->tx_insert_ptr = txptr->next_tx_desc; - - /* Transmit Packet Process */ - if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) { - txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ - db->tx_packet_cnt++; /* Ready to send */ - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ - } else { - db->tx_queue_cnt++; /* queue TX packet */ - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - } - - /* Tx resource check */ - if ( db->tx_queue_cnt < TX_FREE_DESC_CNT ) - netif_wake_queue(dev); - - /* free this SKB */ - dev_kfree_skb(skb); - - /* Restore CR7 to enable interrupt */ - spin_unlock_irqrestore(&db->lock, flags); - outl(db->cr7_data, dev->base_addr + DCR7); - - return 0; -} - - -/* - * Stop the interface. - * The interface is stopped when it is brought. - */ - -static int dmfe_stop(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long ioaddr = dev->base_addr; - - DMFE_DBUG(0, "dmfe_stop", 0); - - /* disable system */ - netif_stop_queue(dev); - - /* deleted timer */ - del_timer_sync(&db->timer); - - /* Reset & stop DM910X board */ - outl(DM910X_RESET, ioaddr + DCR0); - udelay(5); - phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); - - /* free interrupt */ - free_irq(dev->irq, dev); - - /* free allocated rx buffer */ - dmfe_free_rxbuffer(db); - -#if 0 - /* show statistic counter */ - printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", - db->tx_fifo_underrun, db->tx_excessive_collision, - db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, - db->tx_jabber_timeout, db->reset_count, db->reset_cr8, - db->reset_fatal, db->reset_TXtimeout); -#endif - - return 0; -} - - -/* - * DM9102 insterrupt handler - * receive the packet to upper layer, free the transmitted packet - */ - -static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct DEVICE *dev = dev_id; - struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_interrupt()", 0); - - if (!dev) { - DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0); - return; - } - - spin_lock_irqsave(&db->lock, flags); - - /* Got DM910X status */ - db->cr5_data = inl(ioaddr + DCR5); - outl(db->cr5_data, ioaddr + DCR5); - if ( !(db->cr5_data & 0xc1) ) { - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Disable all interrupt in CR7 to solve the interrupt edge problem */ - outl(0, ioaddr + DCR7); - - /* Check system status */ - if (db->cr5_data & 0x2000) { - /* system bus error happen */ - DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); - db->reset_fatal++; - db->wait_reset = 1; /* Need to RESET */ - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Received the coming packet */ - if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) - dmfe_rx_packet(dev, db); - - /* reallocate rx descriptor buffer */ - if (db->rx_avail_cntcr5_data & 0x01) - dmfe_free_tx_pkt(dev, db); - - /* Mode Check */ - if (db->dm910x_chk_mode & 0x2) { - db->dm910x_chk_mode = 0x4; - db->cr6_data |= 0x100; - update_cr6(db->cr6_data, db->ioaddr); - } - - /* Restore CR7 to enable interrupt mask */ - outl(db->cr7_data, ioaddr + DCR7); - - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Free TX resource after TX complete - */ - -static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) -{ - struct tx_desc *txptr; - unsigned long ioaddr = dev->base_addr; - u32 tdes0; - - txptr = db->tx_remove_ptr; - while(db->tx_packet_cnt) { - tdes0 = le32_to_cpu(txptr->tdes0); - /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - if (tdes0 & 0x80000000) - break; - - /* A packet sent completed */ - db->tx_packet_cnt--; - db->stats.tx_packets++; - - /* Transmit statistic counter */ - if ( tdes0 != 0x7fffffff ) { - /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - db->stats.collisions += (tdes0 >> 3) & 0xf; - db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; - if (tdes0 & TDES0_ERR_MASK) { - db->stats.tx_errors++; - - if (tdes0 & 0x0002) { /* UnderRun */ - db->tx_fifo_underrun++; - if ( !(db->cr6_data & CR6_SFT) ) { - db->cr6_data = db->cr6_data | CR6_SFT; - update_cr6(db->cr6_data, db->ioaddr); - } - } - if (tdes0 & 0x0100) - db->tx_excessive_collision++; - if (tdes0 & 0x0200) - db->tx_late_collision++; - if (tdes0 & 0x0400) - db->tx_no_carrier++; - if (tdes0 & 0x0800) - db->tx_loss_carrier++; - if (tdes0 & 0x4000) - db->tx_jabber_timeout++; - } - } - - txptr = txptr->next_tx_desc; - }/* End of while */ - - /* Update TX remove pointer to next */ - db->tx_remove_ptr = txptr; - - /* Send the Tx packet in queue */ - if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) { - txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ - db->tx_packet_cnt++; /* Ready to send */ - db->tx_queue_cnt--; - outl(0x1, ioaddr + DCR1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ - } - - /* Resource available check */ - if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT ) - netif_wake_queue(dev); /* Active upper layer, send again */ -} - - -/* - * Receive the come packet and pass to upper layer - */ - -static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) -{ - struct rx_desc *rxptr; - struct sk_buff *skb; - int rxlen; - u32 rdes0; - - rxptr = db->rx_ready_ptr; - - while(db->rx_avail_cnt) { - rdes0 = le32_to_cpu(rxptr->rdes0); - if (rdes0 & 0x80000000) /* packet owner check */ - break; - - db->rx_avail_cnt--; - db->interval_rx_cnt++; - - pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - if ( (rdes0 & 0x300) != 0x300) { - /* A packet without First/Last flag */ - /* reuse this SKB */ - DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - /* A packet with First/Last flag */ - rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; - - /* error summary bit check */ - if (rdes0 & 0x8000) { - /* This is a error packet */ - //printk(DRV_NAME ": rdes0: %lx\n", rdes0); - db->stats.rx_errors++; - if (rdes0 & 1) - db->stats.rx_fifo_errors++; - if (rdes0 & 2) - db->stats.rx_crc_errors++; - if (rdes0 & 0x80) - db->stats.rx_length_errors++; - } - - if ( !(rdes0 & 0x8000) || - ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { - skb = rxptr->rx_skb_ptr; - - /* Received Packet CRC check need or not */ - if ( (db->dm910x_chk_mode & 1) && - (cal_CRC(skb->tail, rxlen, 1) != - (*(u32 *) (skb->tail+rxlen) ))) { /* FIXME (?) */ - /* Found a error received packet */ - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - db->dm910x_chk_mode = 3; - } else { - /* Good packet, send to upper layer */ - /* Shorst packet used new SKB */ - if ( (rxlen < RX_COPY_SIZE) && - ( (skb = dev_alloc_skb(rxlen + 2) ) - != NULL) ) { - /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; - skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; - skb_put(skb, rxlen); - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - db->stats.rx_packets++; - db->stats.rx_bytes += rxlen; - } - } else { - /* Reuse SKB buffer when the packet is error */ - DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); - dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } - } - - rxptr = rxptr->next_rx_desc; - } - - db->rx_ready_ptr = rxptr; -} - - -/* - * Get statistics from driver. - */ - -static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev) -{ - struct dmfe_board_info *db = (struct dmfe_board_info *)dev->priv; - - DMFE_DBUG(0, "dmfe_get_stats", 0); - return &db->stats; -} - - -/* - * Set DM910X multicast address - */ - -static void dmfe_set_filter_mode(struct DEVICE * dev) -{ - struct dmfe_board_info *db = dev->priv; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); - spin_lock_irqsave(&db->lock, flags); - - if (dev->flags & IFF_PROMISC) { - DMFE_DBUG(0, "Enable PROM Mode", 0); - db->cr6_data |= CR6_PM | CR6_PBF; - update_cr6(db->cr6_data, db->ioaddr); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) { - DMFE_DBUG(0, "Pass all multicast address", dev->mc_count); - db->cr6_data &= ~(CR6_PM | CR6_PBF); - db->cr6_data |= CR6_PAM; - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - DMFE_DBUG(0, "Set multicast address", dev->mc_count); - if (db->chip_id == PCI_DM9132_ID) - dm9132_id_table(dev, dev->mc_count); /* DM9132 */ - else - send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Process the ethtool ioctl command - */ - -static int dmfe_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct dmfe_board_info *db = dev->priv; - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - if (db->pdev) - strcpy(info.bus_info, db->pdev->slot_name); - else - sprintf(info.bus_info, "EISA 0x%lx %d", - dev->base_addr, dev->irq); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - return -EOPNOTSUPP; -} - - -/* - * Process the upper socket ioctl command - */ - -static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd) -{ - int retval = -EOPNOTSUPP; - DMFE_DBUG(0, "dmfe_do_ioctl()", 0); - - switch(cmd) { - case SIOCETHTOOL: - return dmfe_ethtool_ioctl(dev, (void*)ifr->ifr_data); - } - - return retval; -} - - -/* - * A periodic timer routine - * Dynamic media sense, allocate Rx buffer... - */ - -static void dmfe_timer(unsigned long data) -{ - u32 tmp_cr8; - unsigned char tmp_cr12; - struct DEVICE *dev = (struct DEVICE *) data; - struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; - unsigned long flags; - - DMFE_DBUG(0, "dmfe_timer()", 0); - spin_lock_irqsave(&db->lock, flags); - - /* Media mode process when Link OK before enter this route */ - if (db->first_in_callback == 0) { - db->first_in_callback = 1; - if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { - db->cr6_data &= ~0x40000; - update_cr6(db->cr6_data, db->ioaddr); - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); - db->cr6_data |= 0x40000; - update_cr6(db->cr6_data, db->ioaddr); - db->timer.expires = DMFE_TIMER_WUT + HZ * 2; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - } - - - /* Operating Mode Check */ - if ( (db->dm910x_chk_mode & 0x1) && - (db->stats.rx_packets > MAX_CHECK_PACKET) ) - db->dm910x_chk_mode = 0x4; - - /* Dynamic reset DM910X : system error or transmit time-out */ - tmp_cr8 = inl(db->ioaddr + DCR8); - if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { - db->reset_cr8++; - db->wait_reset = 1; - } - db->interval_rx_cnt = 0; - - /* TX polling kick monitor */ - if ( db->tx_packet_cnt && - time_after(jiffies, dev->trans_start + DMFE_TX_KICK) ) { - outl(0x1, dev->base_addr + DCR1); /* Tx polling again */ - - /* TX Timeout */ - if ( time_after(jiffies, dev->trans_start + DMFE_TX_TIMEOUT) ) { - db->reset_TXtimeout++; - db->wait_reset = 1; - printk(KERN_WARNING "%s: Tx timeout - resetting\n", - dev->name); - } - } - - if (db->wait_reset) { - DMFE_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); - db->reset_count++; - dmfe_dynamic_reset(dev); - db->first_in_callback = 0; - db->timer.expires = DMFE_TIMER_WUT; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); - return; - } - - /* Link status check, Dynamic media type change */ - if (db->chip_id == PCI_DM9132_ID) - tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ - else - tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ - - if ( ((db->chip_id == PCI_DM9102_ID) && - (db->chip_revision == 0x02000030)) || - ((db->chip_id == PCI_DM9132_ID) && - (db->chip_revision == 0x02000010)) ) { - /* DM9102A Chip */ - if (tmp_cr12 & 2) - tmp_cr12 = 0x0; /* Link failed */ - else - tmp_cr12 = 0x3; /* Link OK */ - } - - if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { - /* Link Failed */ - DMFE_DBUG(0, "Link Failed", tmp_cr12); - db->link_failed = 1; - - /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ - /* AUTO or force 1M Homerun/Longrun don't need */ - if ( !(db->media_mode & 0x38) ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); - - /* AUTO mode, if INT phyxcer link failed, select EXT device */ - if (db->media_mode & DMFE_AUTO) { - /* 10/100M link failed, used 1M Home-Net */ - db->cr6_data|=0x00040000; /* bit18=1, MII */ - db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ - update_cr6(db->cr6_data, db->ioaddr); - } - } else - if ((tmp_cr12 & 0x3) && db->link_failed) { - DMFE_DBUG(0, "Link link OK", tmp_cr12); - db->link_failed = 0; - - /* Auto Sense Speed */ - if ( (db->media_mode & DMFE_AUTO) && - dmfe_sense_speed(db) ) - db->link_failed = 1; - dmfe_process_mode(db); - /* SHOW_MEDIA_TYPE(db->op_mode); */ - } - - /* HPNA remote command check */ - if (db->HPNA_command & 0xf00) { - db->HPNA_timer--; - if (!db->HPNA_timer) - dmfe_HPNA_remote_cmd_chk(db); - } - - /* Timer active again */ - db->timer.expires = DMFE_TIMER_WUT; - add_timer(&db->timer); - spin_unlock_irqrestore(&db->lock, flags); -} - - -/* - * Dynamic reset the DM910X board - * Stop DM910X board - * Free Tx/Rx allocated memory - * Reset DM910X board - * Re-initilize DM910X board - */ - -static void dmfe_dynamic_reset(struct DEVICE *dev) -{ - struct dmfe_board_info *db = dev->priv; - - DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); - - /* Sopt MAC controller */ - db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ - update_cr6(db->cr6_data, dev->base_addr); - outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ - outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); - - /* Disable upper layer interface */ - netif_stop_queue(dev); - - /* Free Rx Allocate buffer */ - dmfe_free_rxbuffer(db); - - /* system variable init */ - db->tx_packet_cnt = 0; - db->tx_queue_cnt = 0; - db->rx_avail_cnt = 0; - db->link_failed = 1; - db->wait_reset = 0; - - /* Re-initilize DM910X board */ - dmfe_init_dm910x(dev); - - /* Restart upper layer interface */ - netif_wake_queue(dev); -} - - -/* - * free all allocated rx buffer - */ - -static void dmfe_free_rxbuffer(struct dmfe_board_info * db) -{ - DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); - - /* free allocated rx buffer */ - while (db->rx_avail_cnt) { - dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); - db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; - db->rx_avail_cnt--; - } -} - - -/* - * Reuse the SK buffer - */ - -static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) -{ - struct rx_desc *rxptr = db->rx_insert_ptr; - - if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { - rxptr->rx_skb_ptr = skb; - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); - wmb(); - rxptr->rdes0 = cpu_to_le32(0x80000000); - db->rx_avail_cnt++; - db->rx_insert_ptr = rxptr->next_rx_desc; - } else - DMFE_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); -} - - -/* - * Initialize transmit/Receive descriptor - * Using Chain structure, and allocate Tx/Rx buffer - */ - -static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) -{ - struct tx_desc *tmp_tx; - struct rx_desc *tmp_rx; - unsigned char *tmp_buf; - dma_addr_t tmp_tx_dma, tmp_rx_dma; - dma_addr_t tmp_buf_dma; - int i; - - DMFE_DBUG(0, "dmfe_descriptor_init()", 0); - - /* tx descriptor start pointer */ - db->tx_insert_ptr = db->first_tx_desc; - db->tx_remove_ptr = db->first_tx_desc; - outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ - - /* rx descriptor start pointer */ - db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; - db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; - db->rx_insert_ptr = db->first_rx_desc; - db->rx_ready_ptr = db->first_rx_desc; - outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ - - /* Init Transmit chain */ - tmp_buf = db->buf_pool_start; - tmp_buf_dma = db->buf_pool_dma_start; - tmp_tx_dma = db->first_tx_desc_dma; - for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { - tmp_tx->tx_buf_ptr = tmp_buf; - tmp_tx->tdes0 = cpu_to_le32(0); - tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ - tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); - tmp_tx_dma += sizeof(struct tx_desc); - tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); - tmp_tx->next_tx_desc = tmp_tx + 1; - tmp_buf = tmp_buf + TX_BUF_ALLOC; - tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; - } - (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); - tmp_tx->next_tx_desc = db->first_tx_desc; - - /* Init Receive descriptor chain */ - tmp_rx_dma=db->first_rx_desc_dma; - for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { - tmp_rx->rdes0 = cpu_to_le32(0); - tmp_rx->rdes1 = cpu_to_le32(0x01000600); - tmp_rx_dma += sizeof(struct rx_desc); - tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); - tmp_rx->next_rx_desc = tmp_rx + 1; - } - (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); - tmp_rx->next_rx_desc = db->first_rx_desc; - - /* pre-allocate Rx buffer */ - allocate_rx_buffer(db); -} - - -/* - * Update CR6 value - * Firstly stop DM910X , then written value and start - */ - -static void update_cr6(u32 cr6_data, unsigned long ioaddr) -{ - u32 cr6_tmp; - - cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ - outl(cr6_tmp, ioaddr + DCR6); - udelay(5); - outl(cr6_data, ioaddr + DCR6); - udelay(5); -} - - -/* - * Send a setup frame for DM9132 - * This setup frame initilize DM910X addres filter mode -*/ - -static void dm9132_id_table(struct DEVICE *dev, int mc_cnt) -{ - struct dev_mc_list *mcptr; - u16 * addrptr; - unsigned long ioaddr = dev->base_addr+0xc0; /* ID Table */ - u32 hash_val; - u16 i, hash_table[4]; - - DMFE_DBUG(0, "dm9132_id_table()", 0); - - /* Node address */ - addrptr = (u16 *) dev->dev_addr; - outw(addrptr[0], ioaddr); - ioaddr += 4; - outw(addrptr[1], ioaddr); - ioaddr += 4; - outw(addrptr[2], ioaddr); - ioaddr += 4; - - /* Clear Hash Table */ - for (i = 0; i < 4; i++) - hash_table[i] = 0x0; - - /* broadcast address */ - hash_table[3] = 0x8000; - - /* the multicast address in Hash Table : 64 bits */ - for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { - hash_val = cal_CRC( (char *) mcptr->dmi_addr, 6, 0) & 0x3f; - hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); - } - - /* Write the hash table to MAC MD table */ - for (i = 0; i < 4; i++, ioaddr += 4) - outw(hash_table[i], ioaddr); -} - - -/* - * Send a setup frame for DM9102/DM9102A - * This setup frame initilize DM910X addres filter mode - */ - -static void send_filter_frame(struct DEVICE *dev, int mc_cnt) -{ - struct dmfe_board_info *db = dev->priv; - struct dev_mc_list *mcptr; - struct tx_desc *txptr; - u16 * addrptr; - u32 * suptr; - int i; - - DMFE_DBUG(0, "send_filter_frame()", 0); - - txptr = db->tx_insert_ptr; - suptr = (u32 *) txptr->tx_buf_ptr; - - /* Node address */ - addrptr = (u16 *) dev->dev_addr; - *suptr++ = addrptr[0]; - *suptr++ = addrptr[1]; - *suptr++ = addrptr[2]; - - /* broadcast address */ - *suptr++ = 0xffff; - *suptr++ = 0xffff; - *suptr++ = 0xffff; - - /* fit the multicast address */ - for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { - addrptr = (u16 *) mcptr->dmi_addr; - *suptr++ = addrptr[0]; - *suptr++ = addrptr[1]; - *suptr++ = addrptr[2]; - } - - for (; i<14; i++) { - *suptr++ = 0xffff; - *suptr++ = 0xffff; - *suptr++ = 0xffff; - } - - /* prepare the setup frame */ - db->tx_insert_ptr = txptr->next_tx_desc; - txptr->tdes1 = cpu_to_le32(0x890000c0); - - /* Resource Check and Send the setup packet */ - if (!db->tx_packet_cnt) { - /* Resource Empty */ - db->tx_packet_cnt++; - txptr->tdes0 = cpu_to_le32(0x80000000); - update_cr6(db->cr6_data | 0x2000, dev->base_addr); - outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ - update_cr6(db->cr6_data, dev->base_addr); - dev->trans_start = jiffies; - } else - db->tx_queue_cnt++; /* Put in TX queue */ -} - - -/* - * Allocate rx buffer, - * As possible as allocate maxiumn Rx buffer - */ - -static void allocate_rx_buffer(struct dmfe_board_info *db) -{ - struct rx_desc *rxptr; - struct sk_buff *skb; - - rxptr = db->rx_insert_ptr; - - while(db->rx_avail_cnt < RX_DESC_CNT) { - if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) - break; - rxptr->rx_skb_ptr = skb; /* FIXME (?) */ - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); - wmb(); - rxptr->rdes0 = cpu_to_le32(0x80000000); - rxptr = rxptr->next_rx_desc; - db->rx_avail_cnt++; - } - - db->rx_insert_ptr = rxptr; -} - - -/* - * Read one word data from the serial ROM - */ - -static u16 read_srom_word(long ioaddr, int offset) -{ - int i; - u16 srom_data = 0; - long cr9_ioaddr = ioaddr + DCR9; - - outl(CR9_SROM_READ, cr9_ioaddr); - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - - /* Send the Read Command 110b */ - SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); - SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); - SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); - - /* Send the offset */ - for (i = 5; i >= 0; i--) { - srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; - SROM_CLK_WRITE(srom_data, cr9_ioaddr); - } - - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - - for (i = 16; i > 0; i--) { - outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); - udelay(5); - srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); - outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); - udelay(5); - } - - outl(CR9_SROM_READ, cr9_ioaddr); - return srom_data; -} - - -/* - * Auto sense the media mode - */ - -static u8 dmfe_sense_speed(struct dmfe_board_info * db) -{ - u8 ErrFlag = 0; - u16 phy_mode; - - /* CR6 bit18=0, select 10/100M */ - update_cr6( (db->cr6_data & ~0x40000), db->ioaddr); - - phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); - phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); - - if ( (phy_mode & 0x24) == 0x24 ) { - if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; - else /* DM9102/DM9102A */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; - /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ - switch (phy_mode) { - case 0x1000: db->op_mode = DMFE_10MHF; break; - case 0x2000: db->op_mode = DMFE_10MFD; break; - case 0x4000: db->op_mode = DMFE_100MHF; break; - case 0x8000: db->op_mode = DMFE_100MFD; break; - default: db->op_mode = DMFE_10MHF; - ErrFlag = 1; - break; - } - } else { - db->op_mode = DMFE_10MHF; - DMFE_DBUG(0, "Link Failed :", phy_mode); - ErrFlag = 1; - } - - return ErrFlag; -} - - -/* - * Set 10/100 phyxcer capability - * AUTO mode : phyxcer register4 is NIC capability - * Force mode: phyxcer register4 is the force media - */ - -static void dmfe_set_phyxcer(struct dmfe_board_info *db) -{ - u16 phy_reg; - - /* Select 10/100M phyxcer */ - db->cr6_data &= ~0x40000; - update_cr6(db->cr6_data, db->ioaddr); - - /* DM9009 Chip: Phyxcer reg18 bit12=0 */ - if (db->chip_id == PCI_DM9009_ID) { - phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000; - phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id); - } - - /* Phyxcer capability setting */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; - - if (db->media_mode & DMFE_AUTO) { - /* AUTO Mode */ - phy_reg |= db->PHY_reg4; - } else { - /* Force Mode */ - switch(db->media_mode) { - case DMFE_10MHF: phy_reg |= 0x20; break; - case DMFE_10MFD: phy_reg |= 0x40; break; - case DMFE_100MHF: phy_reg |= 0x80; break; - case DMFE_100MFD: phy_reg |= 0x100; break; - } - if (db->chip_id == PCI_DM9009_ID) phy_reg &= 0x61; - } - - /* Write new capability to Phyxcer Reg4 */ - if ( !(phy_reg & 0x01e0)) { - phy_reg|=db->PHY_reg4; - db->media_mode|=DMFE_AUTO; - } - phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); - - /* Restart Auto-Negotiation */ - if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id); - if ( !db->chip_type ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); -} - - -/* - * Process op-mode - * AUTO mode : PHY controller in Auto-negotiation Mode - * Force mode: PHY controller in force mode with HUB - * N-way force capability with SWITCH - */ - -static void dmfe_process_mode(struct dmfe_board_info *db) -{ - u16 phy_reg; - - /* Full Duplex Mode Check */ - if (db->op_mode & 0x4) - db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ - else - db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ - - /* Transciver Selection */ - if (db->op_mode & 0x10) /* 1M HomePNA */ - db->cr6_data |= 0x40000;/* External MII select */ - else - db->cr6_data &= ~0x40000;/* Internal 10/100 transciver */ - - update_cr6(db->cr6_data, db->ioaddr); - - /* 10/100M phyxcer force mode need */ - if ( !(db->media_mode & 0x18)) { - /* Forece Mode */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); - if ( !(phy_reg & 0x1) ) { - /* parter without N-Way capability */ - phy_reg = 0x0; - switch(db->op_mode) { - case DMFE_10MHF: phy_reg = 0x0; break; - case DMFE_10MFD: phy_reg = 0x100; break; - case DMFE_100MHF: phy_reg = 0x2000; break; - case DMFE_100MFD: phy_reg = 0x2100; break; - } - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); - if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) - mdelay(20); - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); - } - } -} - - -/* - * Write a word to Phy register - */ - -static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) -{ - u16 i; - unsigned long ioaddr; - - if (chip_id == PCI_DM9132_ID) { - ioaddr = iobase + 0x80 + offset * 4; - outw(phy_data, ioaddr); - } else { - /* DM9102/DM9102A Chip */ - ioaddr = iobase + DCR9; - - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send write command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - - /* written trasnition */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); - - /* Write a word data to PHY controller */ - for ( i = 0x8000; i > 0; i >>= 1) - phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); - } -} - - -/* - * Read a word data from phy register - */ - -static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) -{ - int i; - u16 phy_data; - unsigned long ioaddr; - - if (chip_id == PCI_DM9132_ID) { - /* DM9132 Chip */ - ioaddr = iobase + 0x80 + offset * 4; - phy_data = inw(ioaddr); - } else { - /* DM9102/DM9102A Chip */ - ioaddr = iobase + DCR9; - - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); - - /* Send read command(10) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); - - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - - /* Skip transition state */ - phy_read_1bit(ioaddr); - - /* read 16bit data */ - for (phy_data = 0, i = 0; i < 16; i++) { - phy_data <<= 1; - phy_data |= phy_read_1bit(ioaddr); - } - } - - return phy_data; -} - - -/* - * Write one bit data to Phy Controller - */ - -static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) -{ - outl(phy_data, ioaddr); /* MII Clock Low */ - udelay(1); - outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ - udelay(1); - outl(phy_data, ioaddr); /* MII Clock Low */ - udelay(1); -} - - -/* - * Read one bit phy data from PHY controller - */ - -static u16 phy_read_1bit(unsigned long ioaddr) -{ - u16 phy_data; - - outl(0x50000, ioaddr); - udelay(1); - phy_data = ( inl(ioaddr) >> 19 ) & 0x1; - outl(0x40000, ioaddr); - udelay(1); - - return phy_data; -} - - -/* - * Calculate the CRC valude of the Rx packet - * flag = 1 : return the reverse CRC (for the received packet CRC) - * 0 : return the normal CRC (for Hash Table index) - */ - -static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) -{ - u32 crc = crc32(~0, Data, Len); - if (flag) crc = ~crc; - return crc; -} - - -/* - * Parser SROM and media mode - */ - -static void dmfe_parse_srom(struct dmfe_board_info * db) -{ - char * srom = db->srom; - int dmfe_mode, tmp_reg; - - DMFE_DBUG(0, "dmfe_parse_srom() ", 0); - - /* Init CR15 */ - db->cr15_data = CR15_DEFAULT; - - /* Check SROM Version */ - if ( ( (int) srom[18] & 0xff) == SROM_V41_CODE) { - /* SROM V4.01 */ - /* Get NIC support media mode */ - db->NIC_capability = le16_to_cpup(srom + 34); - db->PHY_reg4 = 0; - for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { - switch( db->NIC_capability & tmp_reg ) { - case 0x1: db->PHY_reg4 |= 0x0020; break; - case 0x2: db->PHY_reg4 |= 0x0040; break; - case 0x4: db->PHY_reg4 |= 0x0080; break; - case 0x8: db->PHY_reg4 |= 0x0100; break; - } - } - - /* Media Mode Force or not check */ - dmfe_mode = le32_to_cpup(srom + 34) & le32_to_cpup(srom + 36); - switch(dmfe_mode) { - case 0x4: dmfe_media_mode = DMFE_100MHF; break; /* 100MHF */ - case 0x2: dmfe_media_mode = DMFE_10MFD; break; /* 10MFD */ - case 0x8: dmfe_media_mode = DMFE_100MFD; break; /* 100MFD */ - case 0x100: - case 0x200: dmfe_media_mode = DMFE_1M_HPNA; break;/* HomePNA */ - } - - /* Special Function setting */ - /* VLAN function */ - if ( (SF_mode & 0x1) || (srom[43] & 0x80) ) - db->cr15_data |= 0x40; - - /* Flow Control */ - if ( (SF_mode & 0x2) || (srom[40] & 0x1) ) - db->cr15_data |= 0x400; - - /* TX pause packet */ - if ( (SF_mode & 0x4) || (srom[40] & 0xe) ) - db->cr15_data |= 0x9800; - } - - /* Parse HPNA parameter */ - db->HPNA_command = 1; - - /* Accept remote command or not */ - if (HPNA_rx_cmd == 0) - db->HPNA_command |= 0x8000; - - /* Issue remote command & operation mode */ - if (HPNA_tx_cmd == 1) - switch(HPNA_mode) { /* Issue Remote Command */ - case 0: db->HPNA_command |= 0x0904; break; - case 1: db->HPNA_command |= 0x0a00; break; - case 2: db->HPNA_command |= 0x0506; break; - case 3: db->HPNA_command |= 0x0602; break; - } - else - switch(HPNA_mode) { /* Don't Issue */ - case 0: db->HPNA_command |= 0x0004; break; - case 1: db->HPNA_command |= 0x0000; break; - case 2: db->HPNA_command |= 0x0006; break; - case 3: db->HPNA_command |= 0x0002; break; - } - - /* Check DM9801 or DM9802 present or not */ - db->HPNA_present = 0; - update_cr6(db->cr6_data|0x40000, db->ioaddr); - tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); - if ( ( tmp_reg & 0xfff0 ) == 0xb900 ) { - /* DM9801 or DM9802 present */ - db->HPNA_timer = 8; - if ( phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) { - /* DM9801 HomeRun */ - db->HPNA_present = 1; - dmfe_program_DM9801(db, tmp_reg); - } else { - /* DM9802 LongRun */ - db->HPNA_present = 2; - dmfe_program_DM9802(db); - } - } - -} - - -/* - * Init HomeRun DM9801 - */ - -static void dmfe_program_DM9801(struct dmfe_board_info * db, int HPNA_rev) -{ - uint reg17, reg25; - - if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9801_NOISE_FLOOR; - switch(HPNA_rev) { - case 0xb900: /* DM9801 E3 */ - db->HPNA_command |= 0x1000; - reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id); - reg25 = ( (reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - break; - case 0xb901: /* DM9801 E4 */ - reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; - break; - case 0xb902: /* DM9801 E5 */ - case 0xb903: /* DM9801 E6 */ - default: - db->HPNA_command |= 0x1000; - reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; - reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); - reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; - break; - } - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id); - phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id); -} - - -/* - * Init HomeRun DM9802 - */ - -static void dmfe_program_DM9802(struct dmfe_board_info * db) -{ - uint phy_reg; - - if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9802_NOISE_FLOOR; - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); - phy_reg = ( phy_reg & 0xff00) + HPNA_NoiseFloor; - phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id); -} - - -/* - * Check remote HPNA power and speed status. If not correct, - * issue command again. -*/ - -static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) -{ - uint phy_reg; - - /* Got remote device status */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60; - switch(phy_reg) { - case 0x00: phy_reg = 0x0a00;break; /* LP/LS */ - case 0x20: phy_reg = 0x0900;break; /* LP/HS */ - case 0x40: phy_reg = 0x0600;break; /* HP/LS */ - case 0x60: phy_reg = 0x0500;break; /* HP/HS */ - } - - /* Check remote device status match our setting ot not */ - if ( phy_reg != (db->HPNA_command & 0x0f00) ) { - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); - db->HPNA_timer=8; - } else - db->HPNA_timer=600; /* Match, every 10 minutes, check */ -} - - - -static struct pci_device_id dmfe_pci_tbl[] __devinitdata = { - { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, - { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, - { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, - { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); - - -static struct pci_driver dmfe_driver = { - name: "dmfe", - id_table: dmfe_pci_tbl, - probe: dmfe_init_one, - remove: dmfe_remove_one, -}; - -MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); -MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(debug, "i"); -MODULE_PARM(mode, "i"); -MODULE_PARM(cr6set, "i"); -MODULE_PARM(chkmode, "i"); -MODULE_PARM(HPNA_mode, "i"); -MODULE_PARM(HPNA_rx_cmd, "i"); -MODULE_PARM(HPNA_tx_cmd, "i"); -MODULE_PARM(HPNA_NoiseFloor, "i"); -MODULE_PARM(SF_mode, "i"); -MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); -MODULE_PARM_DESC(mode, "Davicom DM9xxx: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); -MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function (bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); - -/* Description: - * when user used insmod to add module, system invoked init_module() - * to initilize and register. - */ - -static int __init dmfe_init_module(void) -{ - int rc; - - printk(version); - printed_version = 1; - - DMFE_DBUG(0, "init_module() ", debug); - - if (debug) - dmfe_debug = debug; /* set debug flag */ - if (cr6set) - dmfe_cr6_user_set = cr6set; - - switch(mode) { - case DMFE_10MHF: - case DMFE_100MHF: - case DMFE_10MFD: - case DMFE_100MFD: - case DMFE_1M_HPNA: - dmfe_media_mode = mode; - break; - default:dmfe_media_mode = DMFE_AUTO; - break; - } - - if (HPNA_mode > 4) - HPNA_mode = 0; /* Default: LP/HS */ - if (HPNA_rx_cmd > 1) - HPNA_rx_cmd = 0; /* Default: Ignored remote cmd */ - if (HPNA_tx_cmd > 1) - HPNA_tx_cmd = 0; /* Default: Don't issue remote cmd */ - if (HPNA_NoiseFloor > 15) - HPNA_NoiseFloor = 0; - - rc = pci_module_init(&dmfe_driver); - if (rc < 0) - return rc; - - return 0; -} - - -/* - * Description: - * when user used rmmod to delete module, system invoked clean_module() - * to un-register all registered services. - */ - -static void __exit dmfe_cleanup_module(void) -{ - DMFE_DBUG(0, "dmfe_clean_module() ", debug); - pci_unregister_driver(&dmfe_driver); -} - -module_init(dmfe_init_module); -module_exit(dmfe_cleanup_module); diff -urN linux-2.5.6-pre3/drivers/net/e100/Makefile linux-2.5.6/drivers/net/e100/Makefile --- linux-2.5.6-pre3/drivers/net/e100/Makefile Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/Makefile Thu Mar 7 18:24:45 2002 @@ -0,0 +1,16 @@ +# +# Makefile for the Intel's E100 ethernet driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := e100.o + +obj-y := e100_main.o e100_config.o e100_proc.o e100_phy.o \ + e100_eeprom.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -urN linux-2.5.6-pre3/drivers/net/e100/e100.h linux-2.5.6/drivers/net/e100/e100.h --- linux-2.5.6-pre3/drivers/net/e100/e100.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100.h Thu Mar 7 18:24:45 2002 @@ -0,0 +1,1033 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _E100_INC_ +#define _E100_INC_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SIOCETHTOOL +#include +#include +#endif + +#include +#include +#include +#include + +/* + * Configure parameters for buffers per controller. + * If the machine this is being used on is a faster machine (i.e. > 150MHz) + * and running on a 10MBS network then more queueing of data occurs. This + * may indicate the some of the numbers below should be adjusted. Here are + * some typical numbers: + * MAX_TCB 64 + * MAX_RFD 64 + * The default numbers give work well on most systems tests so no real + * adjustments really need to take place. Also, if the machine is connected + * to a 100MBS network the numbers described above can be lowered from the + * defaults as considerably less data will be queued. + */ + +#define MAX_TCB 64 /* number of transmit control blocks */ +#define MAX_TBD MAX_TCB +#define TX_FRAME_CNT 8 /* consecutive transmit frames per interrupt */ +/* TX_FRAME_CNT must be less than MAX_TCB */ +#define MAX_RFD 64 + +#define E100_DEFAULT_TCB MAX_TCB +#define E100_MIN_TCB 2*TX_FRAME_CNT + 3 /* make room for at least 2 interrupts */ + +#ifdef __ia64__ + /* We can't use too many DMAble buffers on IA64 machines with >4 GB mem */ +#define E100_MAX_TCB 64 +#else +#define E100_MAX_TCB 1024 +#endif /* __ia64__ */ + +#define E100_DEFAULT_RFD MAX_RFD +#define E100_MIN_RFD 8 + +#ifdef __ia64__ + /* We can't use too many DMAble buffers on IA64 machines with >4 GB mem */ +#define E100_MAX_RFD 64 +#else +#define E100_MAX_RFD 1024 +#endif /* __ia64__ */ + +#define E100_DEFAULT_XSUM true +#define E100_DEFAULT_BER ZLOCK_MAX_ERRORS +#define E100_DEFAULT_SPEED_DUPLEX 0 +#define E100_DEFAULT_FC 0 +#define E100_DEFAULT_IFS true +#define E100_DEFAULT_UCODE true + +#define TX_THRSHLD 8 + +/* sleep time is at least 50 ms, in jiffies */ +#define SLEEP_TIME ((HZ / 20) + 1) +#define CUS_TIMEOUT 1000 + +/* IFS parameters */ +#define MIN_NUMBER_OF_TRANSMITS_100 1000 +#define MIN_NUMBER_OF_TRANSMITS_10 100 + +#define E100_MAX_NIC 16 + +#define E100_MAX_BUSY_WAIT 50 /*Max udelays in wait_scb and wait_cus_idle */ + +/* CPUSAVER_BUNDLE_MAX: Sets the maximum number of frames that will be bundled. + * In some situations, such as the TCP windowing algorithm, it may be + * better to limit the growth of the bundle size than let it go as + * high as it can, because that could cause too much added latency. + * The default is six, because this is the number of packets in the + * default TCP window size. A value of 1 would make CPUSaver indicate + * an interrupt for every frame received. If you do not want to put + * a limit on the bundle size, set this value to xFFFF. + */ +#define E100_DEFAULT_CPUSAVER_BUNDLE_MAX 6 +#define E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY 0x600 +#define E100_DEFAULT_BUNDLE_SMALL_FR false +#define E100_DEFAULT_RX_CONGESTION_CONTROL true + +/* end of configurables */ + +/* ====================================================================== */ +/* hw */ +/* ====================================================================== */ + +/* timeout for command completion */ +#define E100_CMD_WAIT 100 /* iterations */ + +struct driver_stats { + struct net_device_stats net_stats; + + unsigned long tx_late_col; + unsigned long tx_ok_defrd; + unsigned long tx_one_retry; + unsigned long tx_mt_one_retry; + unsigned long rcv_cdt_frames; + unsigned long xmt_fc_pkts; + unsigned long rcv_fc_pkts; + unsigned long rcv_fc_unsupported; + unsigned long xmt_tco_pkts; + unsigned long rcv_tco_pkts; + unsigned long rx_intr_pkts; + unsigned long rx_tasklet_pkts; + unsigned long poll_intr_switch; +}; + +/* TODO: kill me when we can do C99 */ +#define false (0) +#define true (1) + +/* Changed for 82558 and 82559 enhancements */ +/* defines for 82558/9 flow control CSR values */ +#define DFLT_FC_THLD 0x00 /* Rx FIFO threshold of 0.5KB free */ +#define DFLT_FC_CMD 0x00 /* FC Command in CSR */ + +/* ====================================================================== */ +/* equates */ +/* ====================================================================== */ + +/* + * These are general purpose defines + */ + +/* Bit Mask definitions */ +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_28 0x10000000 + +#define BIT_0_2 0x0007 +#define BIT_0_3 0x000F +#define BIT_0_4 0x001F +#define BIT_0_5 0x003F +#define BIT_0_6 0x007F +#define BIT_0_7 0x00FF +#define BIT_0_8 0x01FF +#define BIT_0_13 0x3FFF +#define BIT_0_15 0xFFFF +#define BIT_1_2 0x0006 +#define BIT_1_3 0x000E +#define BIT_2_5 0x003C +#define BIT_3_4 0x0018 +#define BIT_4_5 0x0030 +#define BIT_4_6 0x0070 +#define BIT_4_7 0x00F0 +#define BIT_5_7 0x00E0 +#define BIT_5_12 0x1FE0 +#define BIT_5_15 0xFFE0 +#define BIT_6_7 0x00c0 +#define BIT_7_11 0x0F80 +#define BIT_8_10 0x0700 +#define BIT_9_13 0x3E00 +#define BIT_12_15 0xF000 +#define BIT_8_15 0xFF00 + +#define BIT_16_20 0x001F0000 +#define BIT_21_25 0x03E00000 +#define BIT_26_27 0x0C000000 + +/* Transmit Threshold related constants */ +#define DEFAULT_TX_PER_UNDERRUN 20000 + +#define MAX_MULTICAST_ADDRS 64 +#define MAX_FILTER 16 + +#define FULL_DUPLEX 2 +#define HALF_DUPLEX 1 + +/* + * These defines are specific to the 82557 + */ + +/* E100 PORT functions -- lower 4 bits */ +#define PORT_SOFTWARE_RESET 0 +#define PORT_SELFTEST 1 +#define PORT_SELECTIVE_RESET 2 +#define PORT_DUMP 3 + +/* SCB Status Word bit definitions */ +/* Interrupt status/ack fields */ +/* ER and FCP interrupts for 82558 masks */ +#define SCB_STATUS_ACK_MASK BIT_8_15 /* Status Mask */ +#define SCB_STATUS_ACK_CX BIT_15 /* CU Completed Action Cmd */ +#define SCB_STATUS_ACK_FR BIT_14 /* RU Received A Frame */ +#define SCB_STATUS_ACK_CNA BIT_13 /* CU Became Inactive (IDLE) */ +#define SCB_STATUS_ACK_RNR BIT_12 /* RU Became Not Ready */ +#define SCB_STATUS_ACK_MDI BIT_11 /* MDI read or write done */ +#define SCB_STATUS_ACK_SWI BIT_10 /* S/W generated interrupt */ +#define SCB_STATUS_ACK_ER BIT_9 /* Early Receive */ +#define SCB_STATUS_ACK_FCP BIT_8 /* Flow Control Pause */ + +/*- CUS Fields */ +#define SCB_CUS_MASK (BIT_6 | BIT_7) /* CUS 2-bit Mask */ +#define SCB_CUS_IDLE 0 /* CU Idle */ +#define SCB_CUS_SUSPEND BIT_6 /* CU Suspended */ +#define SCB_CUS_ACTIVE BIT_7 /* CU Active */ + +/*- RUS Fields */ +#define SCB_RUS_IDLE 0 /* RU Idle */ +#define SCB_RUS_MASK BIT_2_5 /* RUS 3-bit Mask */ +#define SCB_RUS_SUSPEND BIT_2 /* RU Suspended */ +#define SCB_RUS_NO_RESOURCES BIT_3 /* RU Out Of Resources */ +#define SCB_RUS_READY BIT_4 /* RU Ready */ +#define SCB_RUS_SUSP_NO_RBDS (BIT_2 | BIT_5) /* RU No More RBDs */ +#define SCB_RUS_NO_RBDS (BIT_3 | BIT_5) /* RU No More RBDs */ +#define SCB_RUS_READY_NO_RBDS (BIT_4 | BIT_5) /* RU Ready, No RBDs */ + +/* SCB Command Word bit definitions */ +/*- CUC fields */ +/* Changing mask to 4 bits */ +#define SCB_CUC_MASK BIT_4_7 /* CUC 4-bit Mask */ +#define SCB_CUC_NOOP 0 +#define SCB_CUC_START BIT_4 /* CU Start */ +#define SCB_CUC_RESUME BIT_5 /* CU Resume */ +/* Changed for 82558 enhancements */ +#define SCB_CUC_STATIC_RESUME (BIT_5 | BIT_7) /* 82558/9 Static Resume */ +#define SCB_CUC_DUMP_ADDR BIT_6 /* CU Dump Counters Address */ +#define SCB_CUC_DUMP_STAT (BIT_4 | BIT_6) /* CU Dump stat. counters */ +#define SCB_CUC_LOAD_BASE (BIT_5 | BIT_6) /* Load the CU base */ +/* Below was defined as BIT_4_7 */ +#define SCB_CUC_DUMP_RST_STAT BIT_4_6 /* CU Dump & reset statistics cntrs */ + +/*- RUC fields */ +#define SCB_RUC_MASK BIT_0_2 /* RUC 3-bit Mask */ +#define SCB_RUC_START BIT_0 /* RU Start */ +#define SCB_RUC_RESUME BIT_1 /* RU Resume */ +#define SCB_RUC_ABORT BIT_2 /* RU Abort */ +#define SCB_RUC_LOAD_HDS (BIT_0 | BIT_2) /* Load RFD Header Data Size */ +#define SCB_RUC_LOAD_BASE (BIT_1 | BIT_2) /* Load the RU base */ +#define SCB_RUC_RBD_RESUME BIT_0_2 /* RBD resume */ + +/* Interrupt fields (assuming byte addressing) */ +#define SCB_INT_MASK BIT_0 /* Mask interrupts */ +#define SCB_SOFT_INT BIT_1 /* Generate a S/W interrupt */ +/* Specific Interrupt Mask Bits (upper byte of SCB Command word) */ +#define SCB_FCP_INT_MASK BIT_2 /* Flow Control Pause */ +#define SCB_ER_INT_MASK BIT_3 /* Early Receive */ +#define SCB_RNR_INT_MASK BIT_4 /* RU Not Ready */ +#define SCB_CNA_INT_MASK BIT_5 /* CU Not Active */ +#define SCB_FR_INT_MASK BIT_6 /* Frame Received */ +#define SCB_CX_INT_MASK BIT_7 /* CU eXecution w/ I-bit done */ +#define SCB_BACHELOR_INT_MASK BIT_2_7 /* 82558 interrupt mask bits */ + +#define SCB_GCR2_EEPROM_ACCESS_SEMAPHORE BIT_7 + +/* EEPROM bit definitions */ +/*- EEPROM control register bits */ +#define EN_TRNF 0x10 /* Enable turnoff */ +#define EEDO 0x08 /* EEPROM data out */ +#define EEDI 0x04 /* EEPROM data in (set for writing data) */ +#define EECS 0x02 /* EEPROM chip select (1=hi, 0=lo) */ +#define EESK 0x01 /* EEPROM shift clock (1=hi, 0=lo) */ + +/*- EEPROM opcodes */ +#define EEPROM_READ_OPCODE 06 +#define EEPROM_WRITE_OPCODE 05 +#define EEPROM_ERASE_OPCODE 07 +#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable */ +#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable */ + +/*- EEPROM data locations */ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_COMPATIBILITY_WORD 3 +#define EEPROM_PWA_NO 8 +#define EEPROM_ID_WORD 0x0A + +#define EEPROM_SUM 0xbaba + +// Zero Locking Algorithm definitions: +#define ZLOCK_ZERO_MASK 0x00F0 +#define ZLOCK_MAX_READS 50 +#define ZLOCK_SET_ZERO 0x2010 +#define ZLOCK_MAX_SLEEP 300 * HZ +#define ZLOCK_MAX_ERRORS 300 + +/* E100 Action Commands */ +#define CB_IA_ADDRESS 1 +#define CB_CONFIGURE 2 +#define CB_MULTICAST 3 +#define CB_TRANSMIT 4 +#define CB_LOAD_MICROCODE 5 +#define CB_LOAD_FILTER 8 +#define CB_MAX_NONTX_CMD 9 +#define CB_IPCB_TRANSMIT 9 + +/* Pre-defined Filter Bits */ +#define CB_FILTER_EL 0x80000000 +#define CB_FILTER_FIX 0x40000000 +#define CB_FILTER_ARP 0x08000000 +#define CB_FILTER_IA_MATCH 0x02000000 + +/* Command Block (CB) Field Definitions */ +/*- CB Command Word */ +#define CB_EL_BIT BIT_15 /* CB EL Bit */ +#define CB_S_BIT BIT_14 /* CB Suspend Bit */ +#define CB_I_BIT BIT_13 /* CB Interrupt Bit */ +#define CB_TX_SF_BIT BIT_3 /* TX CB Flexible Mode */ +#define CB_CMD_MASK BIT_0_3 /* CB 4-bit CMD Mask */ +#define CB_CID_DEFAULT (0x1f << 8) /* CB 5-bit CID (max value) */ + +/*- CB Status Word */ +#define CB_STATUS_MASK BIT_12_15 /* CB Status Mask (4-bits) */ +#define CB_STATUS_COMPLETE BIT_15 /* CB Complete Bit */ +#define CB_STATUS_OK BIT_13 /* CB OK Bit */ +#define CB_STATUS_UNDERRUN BIT_12 /* CB A Bit */ +#define CB_STATUS_FAIL BIT_11 /* CB Fail (F) Bit */ + +/*misc command bits */ +#define CB_TX_EOF_BIT BIT_15 /* TX CB/TBD EOF Bit */ + +/* Config params */ +#define CB_CFIG_BYTE_COUNT 22 /* 22 config bytes */ +#define CB_CFIG_D102_BYTE_COUNT 10 + +/* Receive Frame Descriptor Fields */ + +/*- RFD Status Bits */ +#define RFD_RECEIVE_COLLISION BIT_0 /* Collision detected on Receive */ +#define RFD_IA_MATCH BIT_1 /* Indv Address Match Bit */ +#define RFD_RX_ERR BIT_4 /* RX_ERR pin on Phy was set */ +#define RFD_FRAME_TOO_SHORT BIT_7 /* Receive Frame Short */ +#define RFD_DMA_OVERRUN BIT_8 /* Receive DMA Overrun */ +#define RFD_NO_RESOURCES BIT_9 /* No Buffer Space */ +#define RFD_ALIGNMENT_ERROR BIT_10 /* Alignment Error */ +#define RFD_CRC_ERROR BIT_11 /* CRC Error */ +#define RFD_STATUS_OK BIT_13 /* RFD OK Bit */ +#define RFD_STATUS_COMPLETE BIT_15 /* RFD Complete Bit */ + +/*- RFD Command Bits*/ +#define RFD_EL_BIT BIT_15 /* RFD EL Bit */ +#define RFD_S_BIT BIT_14 /* RFD Suspend Bit */ +#define RFD_H_BIT BIT_4 /* Header RFD Bit */ +#define RFD_SF_BIT BIT_3 /* RFD Flexible Mode */ + +/*- RFD misc bits*/ +#define RFD_EOF_BIT BIT_15 /* RFD End-Of-Frame Bit */ +#define RFD_F_BIT BIT_14 /* RFD Buffer Fetch Bit */ +#define RFD_ACT_COUNT_MASK BIT_0_13 /* RFD Actual Count Mask */ + +/* Receive Buffer Descriptor Fields*/ +#define RBD_EOF_BIT BIT_15 /* RBD End-Of-Frame Bit */ +#define RBD_F_BIT BIT_14 /* RBD Buffer Fetch Bit */ +#define RBD_ACT_COUNT_MASK BIT_0_13 /* RBD Actual Count Mask */ + +#define SIZE_FIELD_MASK BIT_0_13 /* Size of the associated buffer */ +#define RBD_EL_BIT BIT_15 /* RBD EL Bit */ + +/* Self Test Results*/ +#define CB_SELFTEST_FAIL_BIT BIT_12 +#define CB_SELFTEST_DIAG_BIT BIT_5 +#define CB_SELFTEST_REGISTER_BIT BIT_3 +#define CB_SELFTEST_ROM_BIT BIT_2 + +#define CB_SELFTEST_ERROR_MASK ( \ + CB_SELFTEST_FAIL_BIT | CB_SELFTEST_DIAG_BIT | \ + CB_SELFTEST_REGISTER_BIT | CB_SELFTEST_ROM_BIT) + +/* adapter vendor & device ids */ +#define PCI_OHIO_BOARD 0x10f0 /* subdevice ID, Ohio dual port nic */ + +/* Values for PCI_REV_ID_REGISTER values */ +#define D101A4_REV_ID 4 /* 82558 A4 stepping */ +#define D101B0_REV_ID 5 /* 82558 B0 stepping */ +#define D101MA_REV_ID 8 /* 82559 A0 stepping */ +#define D101S_REV_ID 9 /* 82559S A-step */ +#define D102_REV_ID 12 +#define D102C_REV_ID 13 /* 82550 step C */ +#define D102E_REV_ID 15 + +/* ############Start of 82555 specific defines################## */ + +#define PHY_82555_LED_SWITCH_CONTROL 0x1b /* 82555 led switch control register */ + +/* 82555 led switch control reg. opcodes */ +#define PHY_82555_LED_NORMAL_CONTROL 0 // control back to the 8255X +#define PHY_82555_LED_DRIVER_CONTROL BIT_2 // the driver is in control +#define PHY_82555_LED_OFF BIT_2 // activity LED is off +#define PHY_82555_LED_ON_559 (BIT_0 | BIT_2) // activity LED is on for 559 and later +#define PHY_82555_LED_ON_PRE_559 (BIT_0 | BIT_1 | BIT_2) // activity LED is on for 558 and before + +// Describe the state of the phy led. +// needed for the function : 'e100_blink_timer' +enum led_state_e { + LED_OFF = 0, + LED_ON, +}; + +/* ############End of 82555 specific defines##################### */ + +#define RFD_PARSE_BIT BIT_3 +#define RFD_TCP_PACKET 0x00 +#define RFD_UDP_PACKET 0x01 +#define TCPUDP_CHECKSUM_BIT_VALID BIT_4 +#define TCPUDP_CHECKSUM_VALID BIT_5 +#define CHECKSUM_PROTOCOL_MASK 0x03 + +#define VLAN_SIZE 4 +#define CHKSUM_SIZE 2 +#define RFD_DATA_SIZE (ETH_FRAME_LEN + CHKSUM_SIZE + VLAN_SIZE) + +/* Bits for bdp->flags */ +#define DF_LINK_FC_CAP 0x00000001 /* Link is flow control capable */ +#define DF_CSUM_OFFLOAD 0x00000002 +#define DF_UCODE_LOADED 0x00000004 +#define USE_IPCB 0x00000008 /* set if using ipcb for transmits */ +#define IS_BACHELOR 0x00000010 /* set if 82558 or newer board */ +#define IS_ICH 0x00000020 +#define DF_SPEED_FORCED 0x00000040 /* set if speed is forced */ + +typedef struct net_device_stats net_dev_stats_t; + +/* needed macros */ +/* These macros use the bdp pointer. If you use them it better be defined */ +#define PREV_TCB_USED(X) ((X).tail ? (X).tail - 1 : bdp->params.TxDescriptors - 1) +#define NEXT_TCB_TOUSE(X) ((((X) + 1) >= bdp->params.TxDescriptors) ? 0 : (X) + 1) +#define TCB_TO_USE(X) ((X).tail) +#define TCBS_AVAIL(X) (NEXT_TCB_TOUSE( NEXT_TCB_TOUSE((X).tail)) != (X).head) + +#define RFD_POINTER(skb,bdp) ((rfd_t *) (((unsigned char *)((skb)->data))-((bdp)->rfd_size))) +#define SKB_RFD_STATUS(skb,bdp) ((RFD_POINTER((skb),(bdp)))->rfd_header.cb_status) +#define GET_SKB_DMA_ADDR(skb) ( *(dma_addr_t *)( (skb)->cb) ) +#define SET_SKB_DMA_ADDR(skb,dma_addr) ( *(dma_addr_t *)( (skb)->cb) = (dma_addr) ) + +/* ====================================================================== */ +/* 82557 */ +/* ====================================================================== */ + +/* Changed for 82558 enhancement */ +typedef struct _d101_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ +} d101_scb_ext __attribute__ ((__packed__)); + +/* Changed for 82559 enhancement */ +typedef struct _d101m_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ + u8 scb_gen_ctrl; /* General Control */ + u8 scb_gen_stat; /* General Status */ + u16 scb_reserved; /* Reserved */ + u32 scb_function_event; /* Cardbus Function Event */ + u32 scb_function_event_mask; /* Cardbus Function Mask */ + u32 scb_function_present_state; /* Cardbus Function state */ + u32 scb_force_event; /* Cardbus Force Event */ +} d101m_scb_ext __attribute__ ((__packed__)); + +/* Changed for 82550 enhancement */ +typedef struct _d102_scb_ext_t { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + u8 scb_early_rx_int; /* Early Rx DMA byte count */ + u8 scb_fc_thld; /* Flow Control threshold */ + u8 scb_fc_xon_xoff; /* Flow Control XON/XOFF values */ + u8 scb_pmdr; /* Power Mgmt. Driver Reg */ + u8 scb_gen_ctrl; /* General Control */ + u8 scb_gen_stat; /* General Status */ + u8 scb_gen_ctrl2; + u8 scb_reserved; /* Reserved */ + u32 scb_scheduling_reg; + u32 scb_reserved2; + u32 scb_function_event; /* Cardbus Function Event */ + u32 scb_function_event_mask; /* Cardbus Function Mask */ + u32 scb_function_present_state; /* Cardbus Function state */ + u32 scb_force_event; /* Cardbus Force Event */ +} d102_scb_ext __attribute__ ((__packed__)); + +/* + * 82557 status control block. this will be memory mapped & will hang of the + * the bdp, which hangs of the bdp. This is the brain of it. + */ +typedef struct _scb_t { + u16 scb_status; /* SCB Status register */ + u8 scb_cmd_low; /* SCB Command register (low byte) */ + u8 scb_cmd_hi; /* SCB Command register (high byte) */ + u32 scb_gen_ptr; /* SCB General pointer */ + u32 scb_port; /* PORT register */ + u16 scb_flsh_cntrl; /* Flash Control register */ + u16 scb_eprm_cntrl; /* EEPROM control register */ + u32 scb_mdi_cntrl; /* MDI Control Register */ + /* Changed for 82558 enhancement */ + union { + u32 scb_rx_dma_cnt; /* Rx DMA byte count */ + d101_scb_ext d101_scb; /* 82558/9 specific fields */ + d101m_scb_ext d101m_scb; /* 82559 specific fields */ + d102_scb_ext d102_scb; + } scb_ext; +} scb_t __attribute__ ((__packed__)); + +/* Self test + * This is used to dump results of the self test + */ +typedef struct _self_test_t { + u32 st_sign; /* Self Test Signature */ + u32 st_result; /* Self Test Results */ +} self_test_t __attribute__ ((__packed__)); + +/* + * Statistical Counters + */ +/* 82557 counters */ +typedef struct _basic_cntr_t { + u32 xmt_gd_frames; /* Good frames transmitted */ + u32 xmt_max_coll; /* Fatal frames -- had max collisions */ + u32 xmt_late_coll; /* Fatal frames -- had a late coll. */ + u32 xmt_uruns; /* Xmit underruns (fatal or re-transmit) */ + u32 xmt_lost_crs; /* Frames transmitted without CRS */ + u32 xmt_deferred; /* Deferred transmits */ + u32 xmt_sngl_coll; /* Transmits that had 1 and only 1 coll. */ + u32 xmt_mlt_coll; /* Transmits that had multiple coll. */ + u32 xmt_ttl_coll; /* Transmits that had 1+ collisions. */ + u32 rcv_gd_frames; /* Good frames received */ + u32 rcv_crc_errs; /* Aligned frames that had a CRC error */ + u32 rcv_algn_errs; /* Receives that had alignment errors */ + u32 rcv_rsrc_err; /* Good frame dropped cuz no resources */ + u32 rcv_oruns; /* Overrun errors - bus was busy */ + u32 rcv_err_coll; /* Received frms. that encountered coll. */ + u32 rcv_shrt_frames; /* Received frames that were to short */ +} basic_cntr_t; + +/* 82558 extended statistic counters */ +typedef struct _ext_cntr_t { + u32 xmt_fc_frames; + u32 rcv_fc_frames; + u32 rcv_fc_unsupported; +} ext_cntr_t; + +/* 82559 TCO statistic counters */ +typedef struct _tco_cntr_t { + u16 xmt_tco_frames; + u16 rcv_tco_frames; +} tco_cntr_t; + +/* Structures to access thet physical dump area */ +/* Use one of these types, according to the statisitcal counters mode, + to cast the pointer to the physical dump area and access the cmd_complete + DWORD. */ + +/* 557-mode : only basic counters + cmd_complete */ +typedef struct _err_cntr_557_t { + basic_cntr_t basic_stats; + u32 cmd_complete; +} err_cntr_557_t; + +/* 558-mode : basic + extended counters + cmd_complete */ +typedef struct _err_cntr_558_t { + basic_cntr_t basic_stats; + ext_cntr_t extended_stats; + u32 cmd_complete; +} err_cntr_558_t; + +/* 559-mode : basic + extended + TCO counters + cmd_complete */ +typedef struct _err_cntr_559_t { + basic_cntr_t basic_stats; + ext_cntr_t extended_stats; + tco_cntr_t tco_stats; + u32 cmd_complete; +} err_cntr_559_t; + +/* This typedef defines the struct needed to hold the largest number of counters */ +typedef err_cntr_559_t max_counters_t; + +/* Different statistical-counters mode the controller may be in */ +typedef enum _stat_mode_t { + E100_BASIC_STATS = 0, /* 82557 stats : 16 counters / 16 dw */ + E100_EXTENDED_STATS, /* 82558 stats : 19 counters / 19 dw */ + E100_TCO_STATS /* 82559 stats : 21 counters / 20 dw */ +} stat_mode_t; + +/* dump statistical counters complete codes */ +#define DUMP_STAT_COMPLETED 0xA005 +#define DUMP_RST_STAT_COMPLETED 0xA007 + +/* Command Block (CB) Generic Header Structure*/ +typedef struct _cb_header_t { + u16 cb_status; /* Command Block Status */ + u16 cb_cmd; /* Command Block Command */ + u32 cb_lnk_ptr; /* Link To Next CB */ +} cb_header_t __attribute__ ((__packed__)); + +//* Individual Address Command Block (IA_CB)*/ +typedef struct _ia_cb_t { + cb_header_t ia_cb_hdr; + u8 ia_addr[ETH_ALEN]; +} ia_cb_t __attribute__ ((__packed__)); + +/* Configure Command Block (CONFIG_CB)*/ +typedef struct _config_cb_t { + cb_header_t cfg_cbhdr; + u8 cfg_byte[CB_CFIG_BYTE_COUNT + CB_CFIG_D102_BYTE_COUNT]; +} config_cb_t __attribute__ ((__packed__)); + +/* MultiCast Command Block (MULTICAST_CB)*/ +typedef struct _multicast_cb_t { + cb_header_t mc_cbhdr; + u16 mc_count; /* Number of multicast addresses */ + u8 mc_addr[(ETH_ALEN * MAX_MULTICAST_ADDRS)]; +} mltcst_cb_t __attribute__ ((__packed__)); + +#define UCODE_MAX_DWORDS 134 +/* Load Microcode Command Block (LOAD_UCODE_CB)*/ +typedef struct _load_ucode_cb_t { + cb_header_t load_ucode_cbhdr; + u32 ucode_dword[UCODE_MAX_DWORDS]; +} load_ucode_cb_t __attribute__ ((__packed__)); + +/* Load Programmable Filter Data*/ +typedef struct _filter_cb_t { + cb_header_t filter_cb_hdr; + u32 filter_data[MAX_FILTER]; +} filter_cb_t __attribute__ ((__packed__)); + +/* NON_TRANSMIT_CB -- Generic Non-Transmit Command Block + */ +typedef struct _nxmit_cb_t { + union { + config_cb_t config; + ia_cb_t setup; + load_ucode_cb_t load_ucode; + mltcst_cb_t multicast; + filter_cb_t filter; + } ntcb; +} nxmit_cb_t __attribute__ ((__packed__)); + +/*Block for queuing for postponed execution of the non-transmit commands*/ +typedef struct _nxmit_cb_entry_t { + struct list_head list_elem; + nxmit_cb_t *non_tx_cmd; + dma_addr_t dma_addr; + unsigned long expiration_time; +} nxmit_cb_entry_t; + +/* States for postponed non tx commands execution */ +typedef enum _non_tx_cmd_state_t { + E100_NON_TX_IDLE = 0, /* No queued NON-TX commands */ + E100_WAIT_TX_FINISH, /* Wait for completion of the TX activities */ + E100_WAIT_NON_TX_FINISH /* Wait for completion of the non TX command */ +} non_tx_cmd_state_t; + +/* some defines for the ipcb */ +#define IPCB_IP_CHECKSUM_ENABLE BIT_4 +#define IPCB_TCPUDP_CHECKSUM_ENABLE BIT_5 +#define IPCB_TCP_PACKET BIT_6 +#define IPCB_LARGESEND_ENABLE BIT_7 +#define IPCB_HARDWAREPARSING_ENABLE BIT_0 +#define IPCB_INSERTVLAN_ENABLE BIT_1 +#define IPCB_IP_ACTIVATION_DEFAULT IPCB_HARDWAREPARSING_ENABLE + +/* Transmit Buffer Descriptor (TBD)*/ +typedef struct _tbd_t { + u32 tbd_buf_addr; /* Physical Transmit Buffer Address */ + u16 tbd_buf_cnt; /* Actual Count Of Bytes */ + u16 padd; +} tbd_t __attribute__ ((__packed__)); + +/* d102 specific fields */ +typedef struct _tcb_ipcb_t { + u16 schedule_low; + u8 ip_schedule; + u8 ip_activation_high; + u16 vlan; + u8 ip_header_offset; + u8 tcp_header_offset; + union { + u32 sec_rec_phys_addr; + u32 tbd_zero_address; + } tbd_sec_addr; + union { + u16 sec_rec_size; + u16 tbd_zero_size; + } tbd_sec_size; + u16 total_tcp_payload; +} tcb_ipcb_t __attribute__ ((__packed__)); + +#ifdef MAX_SKB_FRAGS +#define E100_ZEROCOPY +#endif + +#ifdef E100_ZEROCOPY +#define E100_TBD_ARRAY_SIZE (2+MAX_SKB_FRAGS) +#else +#define E100_TBD_ARRAY_SIZE 2 +#endif /*E100_ZEROCOPY */ + +/* Transmit Command Block (TCB)*/ +struct _tcb_t { + cb_header_t tcb_hdr; + u32 tcb_tbd_ptr; /* TBD address */ + u16 tcb_cnt; /* Data Bytes In TCB past header */ + u8 tcb_thrshld; /* TX Threshold for FIFO Extender */ + u8 tcb_tbd_num; + + union { + tcb_ipcb_t ipcb; /* d102 ipcb fields */ + tbd_t tbd_array[E100_TBD_ARRAY_SIZE]; + } tcbu; + + /* From here onward we can dump anything we want as long as the + * size of the total structure is a multiple of a paragraph + * boundary ( i.e. -16 bit aligned ). + */ + tbd_t *tbd_ptr; + +#ifdef E100_ZEROCOPY + u32 tcb_tbd_dflt_ptr; /* TBD address for non-segmented packet */ + u32 tcb_tbd_expand_ptr; /* TBD address for segmented packet */ +#endif /*E100_ZEROCOPY */ + + struct sk_buff *tcb_skb; /* the associated socket buffer */ + dma_addr_t tcb_phys; /* phys addr of the TCB */ +} __attribute__ ((__packed__)); + +#ifndef _TCB_T_ +#define _TCB_T_ +typedef struct _tcb_t tcb_t; +#endif + +/* Receive Frame Descriptor (RFD) - will be using the simple model*/ +struct _rfd_t { + /* 8255x */ + cb_header_t rfd_header; + u32 rfd_rbd_ptr; /* Receive Buffer Descriptor Addr */ + u16 rfd_act_cnt; /* Number Of Bytes Received */ + u16 rfd_sz; /* Number Of Bytes In RFD */ + /* D102 aka Gamla */ + u16 vlanid; + u8 rcvparserstatus; + u8 reserved; + u16 securitystatus; + u8 checksumstatus; + u8 zerocopystatus; + u8 pad[8]; /* data should be 16 byte aligned */ + u8 data[RFD_DATA_SIZE]; + +} __attribute__ ((__packed__)); + +#ifndef _RFD_T_ +#define _RFD_T_ +typedef struct _rfd_t rfd_t; +#endif + +/* Receive Buffer Descriptor (RBD)*/ +typedef struct _rbd_t { + u16 rbd_act_cnt; /* Number Of Bytes Received */ + u16 rbd_filler; + u32 rbd_lnk_addr; /* Link To Next RBD */ + u32 rbd_rcb_addr; /* Receive Buffer Address */ + u16 rbd_sz; /* Receive Buffer Size */ + u16 rbd_filler1; +} rbd_t __attribute__ ((__packed__)); + +/* + * This structure is used to maintain a FIFO access to a resource that is + * maintained as a circular queue. The resource to be maintained is pointed + * to by the "data" field in the structure below. In this driver the TCBs', + * TBDs' & RFDs' are maintained as a circular queue & are managed thru this + * structure. + */ +typedef struct _buf_pool_t { + unsigned int head; /* index to first used resource */ + unsigned int tail; /* index to last used resource */ + void *data; /* points to resource pool */ +} buf_pool_t; + +/*Rx skb holding structure*/ +struct rx_list_elem { + struct list_head list_elem; + dma_addr_t dma_addr; + struct sk_buff *skb; +}; + +enum next_cu_cmd_e { RESUME_NO_WAIT = 0, RESUME_WAIT, START_WAIT }; +enum zlock_state_e { ZLOCK_INITIAL, ZLOCK_READING, ZLOCK_SLEEPING }; +enum tx_queue_stop_type { LONG_STOP = 0, SHORT_STOP }; + +/* 64 bit aligned size */ +#define E100_SIZE_64A(X) ((sizeof(X) + 7) & ~0x7) + +typedef struct _bd_dma_able_t { + char selftest[E100_SIZE_64A(self_test_t)]; + char stats_counters[E100_SIZE_64A(max_counters_t)]; +} bd_dma_able_t; + +/* bit masks for bool parameters */ +#define PRM_XSUMRX 0x00000001 +#define PRM_UCODE 0x00000002 +#define PRM_FC 0x00000004 +#define PRM_IFS 0x00000008 +#define PRM_BUNDLE_SMALL 0x00000010 +#define PRM_RX_CONG 0x00000020 + +struct cfg_params { + int e100_speed_duplex; + int RxDescriptors; + int TxDescriptors; + int IntDelay; + int BundleMax; + int ber; + int PollingMaxWork; + u32 b_params; +}; + +struct e100_private { + u32 flags; /* board management flags */ + u32 tx_per_underrun; /* number of good tx frames per underrun */ + unsigned int tx_count; /* count of tx frames, so we can request an interrupt */ + u8 tx_thld; /* stores transmit threshold */ + u16 eeprom_size; + u32 pwa_no; /* PWA: xxxxxx-0xx */ + u8 perm_node_address[ETH_ALEN]; + struct list_head active_rx_list; /* list of rx buffers */ + struct list_head rx_struct_pool; /* pool of rx buffer struct headers */ + u16 rfd_size; /* size of the adapter's RFD struct */ + int skb_req; /* number of skbs neede by the adapter */ + u8 intr_mask; /* mask for interrupt status */ + + void *dma_able; /* dma allocated structs */ + dma_addr_t dma_able_phys; + self_test_t *selftest; /* pointer to self test area */ + dma_addr_t selftest_phys; /* phys addr of selftest */ + max_counters_t *stats_counters; /* pointer to stats table */ + dma_addr_t stat_cnt_phys; /* phys addr of stat counter area */ + + stat_mode_t stat_mode; /* statistics mode: extended, TCO, basic */ + scb_t *scb; /* memory mapped ptr to 82557 scb */ + + tcb_t *last_tcb; /* pointer to last tcb sent */ + buf_pool_t tcb_pool; /* adapter's TCB array */ + dma_addr_t tcb_phys; /* phys addr of start of TCBs */ + + u16 cur_line_speed; + u16 cur_dplx_mode; + + struct net_device *device; + struct pci_dev *pdev; + struct driver_stats drv_stats; + + u8 rev_id; /* adapter PCI revision ID */ + unsigned long device_type; /* device type from e100_vendor.h */ + + unsigned int phy_addr; /* address of PHY component */ + unsigned int PhyId; /* ID of PHY component */ + unsigned int PhyState; /* state for the fix squelch algorithm */ + unsigned int PhyDelay; /* delay for the fix squelch algorithm */ + + /* Lock defintions for the driver */ + spinlock_t bd_lock; /* board lock */ + spinlock_t bd_non_tx_lock; /* Non transmit command lock */ + spinlock_t config_lock; /* config block lock */ + spinlock_t mdi_access_lock; /* mdi lock */ + + struct timer_list watchdog_timer; /* watchdog timer id */ + + /* non-tx commands parameters */ + struct timer_list nontx_timer_id; /* non-tx timer id */ + struct list_head non_tx_cmd_list; + non_tx_cmd_state_t non_tx_command_state; + nxmit_cb_entry_t *same_cmd_entry[CB_MAX_NONTX_CMD]; + + enum next_cu_cmd_e next_cu_cmd; + + /* Zero Locking Algorithm data members */ + enum zlock_state_e zlock_state; + u8 zlock_read_data[16]; /* number of times each value 0-15 was read */ + u16 zlock_read_cnt; /* counts number of reads */ + ulong zlock_sleep_cnt; /* keeps track of "sleep" time */ + + u8 config[CB_CFIG_BYTE_COUNT + CB_CFIG_D102_BYTE_COUNT]; + + /* IFS params */ + u8 ifs_state; + u8 ifs_value; + + struct cfg_params params; /* adapter's command line parameters */ + + struct proc_dir_entry *proc_parent; + + rwlock_t isolate_lock; + int driver_isolated; + + u32 speed_duplex_caps; /* adapter's speed/duplex capabilities */ + + struct tasklet_struct polling_tasklet; + +#ifdef ETHTOOL_GWOL + /* WOL params for ethtool */ + u32 wolsupported; + u32 wolopts; + u16 ip_lbytes; +#endif +}; + +#define E100_AUTONEG 0 +#define E100_SPEED_10_HALF 1 +#define E100_SPEED_10_FULL 2 +#define E100_SPEED_100_HALF 3 +#define E100_SPEED_100_FULL 4 + +/********* function prototypes *************/ +extern void e100_isolate_driver(struct e100_private *bdp); +extern void e100_sw_reset(struct e100_private *bdp, u32 reset_cmd); +extern void e100_start_cu(struct e100_private *bdp, tcb_t *tcb); +extern void e100_free_non_tx_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *non_tx_cmd); +extern nxmit_cb_entry_t *e100_alloc_non_tx_cmd(struct e100_private *bdp); +extern unsigned char e100_exec_non_cu_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *cmd); +extern unsigned char e100_selftest(struct e100_private *bdp, u32 *st_timeout, + u32 *st_result); +extern unsigned char e100_get_link_state(struct e100_private *bdp); +extern unsigned char e100_wait_scb(struct e100_private *bdp); + +#endif diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_config.c linux-2.5.6/drivers/net/e100/e100_config.c --- linux-2.5.6-pre3/drivers/net/e100/e100_config.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_config.c Thu Mar 7 18:24:45 2002 @@ -0,0 +1,596 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_config.c * +* * +* Abstract: Functions for configuring the network adapter. * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ +#ifdef SIOCETHTOOL +#include +#endif + +#include "e100_config.h" + +static void e100_config_long_rx(struct e100_private *bdp, unsigned char enable); + +static const u8 def_config[] = { + CB_CFIG_BYTE_COUNT, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x32, 0x07, 0x01, + 0x00, 0x2e, 0x00, 0x60, 0x00, 0xf2, 0xc8, 0x00, + 0x40, 0xf2, 0x80, 0x3f, 0x05 +}; + +/** + * e100_config_init_82557 - config the 82557 adapter + * @bdp: atapter's private data struct + * + * This routine will initialize the 82557 configure block. + * All other init functions will only set values that are + * different from the 82557 default. + */ +static void __devinit +e100_config_init_82557(struct e100_private *bdp) +{ + /* initialize config block */ + memcpy(bdp->config, def_config, sizeof (def_config)); + bdp->config[0] = CB_CFIG_BYTE_COUNT; /* just in case */ + + e100_config_ifs(bdp); + + /* + * Enable extended statistical counters (82558 and up) and TCO counters + * (82559 and up) and set the statistical counters' mode in bdp + * + * stat. mode | TCO stat. bit (2) | Extended stat. bit (5) + * ------------------------------------------------------------------ + * Basic (557) | 0 | 1 + * ------------------------------------------------------------------ + * Extended (558) | 0 | 0 + * ------------------------------------------------------------------ + * TCO (559) | 1 | 1 + * ------------------------------------------------------------------ + * Reserved | 1 | 0 + * ------------------------------------------------------------------ + */ + bdp->config[6] &= ~CB_CFIG_TCO_STAT; + bdp->config[6] |= CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_BASIC_STATS; + + /* Setup for MII or 503 operation. The CRS+CDT bit should only be set */ + /* when operating in 503 mode. */ + if (bdp->phy_addr == 32) { + bdp->config[8] &= ~CB_CFIG_503_MII; + bdp->config[15] |= CB_CFIG_CRS_OR_CDT; + } else { + bdp->config[8] |= CB_CFIG_503_MII; + bdp->config[15] &= ~CB_CFIG_CRS_OR_CDT; + } + + e100_config_fc(bdp); + e100_config_force_dplx(bdp); + e100_config_promisc(bdp, false); + e100_config_mulcast_enbl(bdp, false); +} + +static void __devinit +e100_config_init_82558(struct e100_private *bdp) +{ + /* MWI enable. This should be turned on only if the adapter is a 82558/9 + * and if the PCI command reg. has enabled the MWI bit. */ + bdp->config[3] |= CB_CFIG_MWI_EN; + + bdp->config[6] &= ~CB_CFIG_EXT_TCB_DIS; + + if (bdp->rev_id >= D101MA_REV_ID) { + /* this is 82559 and up - enable TCO counters */ + bdp->config[6] |= CB_CFIG_TCO_STAT; + bdp->config[6] |= CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_TCO_STATS; + + if ((bdp->rev_id < D102_REV_ID) && + (bdp->params.b_params & PRM_XSUMRX) && + (bdp->pdev->device != 0x1209)) { + + bdp->flags |= DF_CSUM_OFFLOAD; + bdp->config[9] |= 1; + } + } else { + /* this is 82558 */ + bdp->config[6] &= ~CB_CFIG_TCO_STAT; + bdp->config[6] &= ~CB_CFIG_EXT_STAT_DIS; + bdp->stat_mode = E100_EXTENDED_STATS; + } + + e100_config_long_rx(bdp, true); +} + +static void __devinit +e100_config_init_82550(struct e100_private *bdp) +{ + /* The D102 chip allows for 32 config bytes. This value is + * supposed to be in Byte 0. Just add the extra bytes to + * what was already setup in the block. */ + bdp->config[0] += CB_CFIG_D102_BYTE_COUNT; + + /* now we need to enable the extended RFD. When this is + * enabled, the immediated receive data buffer starts at offset + * 32 from the RFD base address, instead of at offset 16. */ + bdp->config[7] |= CB_CFIG_EXTENDED_RFD; + + /* put the chip into D102 receive mode. This is neccessary + * for any parsing and offloading features. */ + bdp->config[22] = CB_CFIG_RECEIVE_GAMLA_MODE; + + /* set the flag if checksum offloading was enabled */ + if (bdp->params.b_params & PRM_XSUMRX) { + bdp->flags |= DF_CSUM_OFFLOAD; + } +} + +/* Initialize the adapter's configure block */ +void __devinit +e100_config_init(struct e100_private *bdp) +{ + e100_config_init_82557(bdp); + + if (bdp->flags & IS_BACHELOR) + e100_config_init_82558(bdp); + + if (bdp->rev_id >= D102_REV_ID) + e100_config_init_82550(bdp); +} + +/** + * e100_force_config - force a configure command + * @bdp: atapter's private data struct + * + * This routine will force a configure command to the adapter. + * The command will be executed in polled mode as interrupts + * are _disabled_ at this time. + * + * Returns: + * true: if the configure command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_force_config(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + bdp->config[0] = CB_CFIG_BYTE_COUNT; + if (bdp->rev_id >= D102_REV_ID) { + /* The D102 chip allows for 32 config bytes. This value is + supposed to be in Byte 0. Just add the extra bytes to + what was already setup in the block. */ + bdp->config[0] += CB_CFIG_D102_BYTE_COUNT; + } + + spin_unlock_bh(&(bdp->config_lock)); + + // although we call config outside the lock, there is no + // race condition because config byte count has maximum value + return e100_config(bdp); +} + +/** + * e100_config - issue a configure command + * @bdp: atapter's private data struct + * + * This routine will issue a configure command to the 82557. + * This command will be executed in polled mode as interrupts + * are _disabled_ at this time. + * + * Returns: + * true: if the configure command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_config(struct e100_private *bdp) +{ + cb_header_t *pntcb_hdr; + unsigned char res = true; + nxmit_cb_entry_t *cmd; + + if (bdp->config[0] == 0) { + goto exit; + } + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + res = false; + goto exit; + } + + pntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + pntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_CONFIGURE); + + spin_lock_bh(&bdp->config_lock); + + if (bdp->config[0] < CB_CFIG_MIN_PARAMS) { + bdp->config[0] = CB_CFIG_MIN_PARAMS; + } + + /* Copy the device's config block to the device's memory */ + memcpy(cmd->non_tx_cmd->ntcb.config.cfg_byte, bdp->config, + bdp->config[0]); + /* reset number of bytes to config next time */ + bdp->config[0] = 0; + + spin_unlock_bh(&bdp->config_lock); + + res = e100_exec_non_cu_cmd(bdp, cmd); + +exit: + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + return res; +} + +/** + * e100_config_fc - config flow-control state + * @bdp: atapter's private data struct + * + * This routine will enable or disable flow control support in the adapter's + * config block. Flow control will be enable only if requested using the command + * line option, and if the link is flow-contorl capable (both us and the link + * partner). + * + * Returns: + * true: if then option was indeed changed + * false: if no change was needed + */ +unsigned char +e100_config_fc(struct e100_private *bdp) +{ + unsigned char enable = false; + unsigned char changed = false; + + /* 82557 doesn't support fc. Don't touch this option */ + if (!(bdp->flags & IS_BACHELOR)) + return false; + + /* Enable fc if requested and if the link supports it */ + if ((bdp->params.b_params & PRM_FC) && (bdp->flags & DF_LINK_FC_CAP)) { + enable = true; + } + + spin_lock_bh(&(bdp->config_lock)); + + if (enable) { + + if (bdp->config[16] != DFLT_FC_DELAY_LSB) { + bdp->config[16] = DFLT_FC_DELAY_LSB; + E100_CONFIG(bdp, 16); + changed = true; + } + + if (bdp->config[17] != DFLT_FC_DELAY_LSB) { + bdp->config[17] = DFLT_FC_DELAY_MSB; + E100_CONFIG(bdp, 17); + changed = true; + } + + /* check if *all* fc config options were already set */ + if (((bdp->config[19] & CB_CFIG_FC_OPTS) != CB_CFIG_FC_OPTS) || + (bdp->config[19] & CB_CFIG_TX_FC_DIS)) { + + bdp->config[19] |= CB_CFIG_FC_OPTS; + bdp->config[19] &= ~CB_CFIG_TX_FC_DIS; + E100_CONFIG(bdp, 19); + changed = true; + } + + } else { + if (bdp->config[16] != DFLT_NO_FC_DELAY_LSB) { + bdp->config[16] = DFLT_NO_FC_DELAY_LSB; + E100_CONFIG(bdp, 16); + changed = true; + } + + if (bdp->config[17] != DFLT_NO_FC_DELAY_MSB) { + bdp->config[17] = DFLT_NO_FC_DELAY_MSB; + E100_CONFIG(bdp, 17); + changed = true; + } + + /* check if *any* fc config options was already set */ + if ((bdp->config[19] & CB_CFIG_FC_OPTS) || + !(bdp->config[19] & CB_CFIG_TX_FC_DIS)) { + + bdp->config[19] &= ~CB_CFIG_FC_OPTS; + bdp->config[19] |= CB_CFIG_TX_FC_DIS; + E100_CONFIG(bdp, 19); + changed = true; + } + } + + spin_unlock_bh(&(bdp->config_lock)); + + return changed; +} + +/** + * e100_config_promisc - configure promiscuous mode + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable promiscuous mode + * in the adapter's config block. + */ +void +e100_config_promisc(struct e100_private *bdp, unsigned char enable) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* if in promiscuous mode, save bad frames */ + if (enable) { + + if (!(bdp->config[6] & CB_CFIG_SAVE_BAD_FRAMES)) { + bdp->config[6] |= CB_CFIG_SAVE_BAD_FRAMES; + E100_CONFIG(bdp, 6); + } + + if (bdp->config[7] & (u8) BIT_0) { + bdp->config[7] &= (u8) (~BIT_0); + E100_CONFIG(bdp, 7); + } + + if (!(bdp->config[15] & CB_CFIG_PROMISCUOUS)) { + bdp->config[15] |= CB_CFIG_PROMISCUOUS; + E100_CONFIG(bdp, 15); + } + + } else { /* not in promiscuous mode */ + + if (bdp->config[6] & CB_CFIG_SAVE_BAD_FRAMES) { + bdp->config[6] &= ~CB_CFIG_SAVE_BAD_FRAMES; + E100_CONFIG(bdp, 6); + } + + if (!(bdp->config[7] & (u8) BIT_0)) { + bdp->config[7] |= (u8) (BIT_0); + E100_CONFIG(bdp, 7); + } + + if (bdp->config[15] & CB_CFIG_PROMISCUOUS) { + bdp->config[15] &= ~CB_CFIG_PROMISCUOUS; + E100_CONFIG(bdp, 15); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_mulcast_enbl - configure allmulti mode + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable reception of all multicast packets + * in the adapter's config block. + */ +void +e100_config_mulcast_enbl(struct e100_private *bdp, unsigned char enable) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* this flag is used to enable receiving all multicast packet */ + if (enable) { + if (!(bdp->config[21] & CB_CFIG_MULTICAST_ALL)) { + bdp->config[21] |= CB_CFIG_MULTICAST_ALL; + E100_CONFIG(bdp, 21); + } + + } else { + if (bdp->config[21] & CB_CFIG_MULTICAST_ALL) { + bdp->config[21] &= ~CB_CFIG_MULTICAST_ALL; + E100_CONFIG(bdp, 21); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_ifs - configure the IFS parameter + * @bdp: atapter's private data struct + * + * This routine will configure the adaptive IFS value + * in the adapter's config block. IFS values are only + * relevant in half duplex, so set to 0 in full duplex. + */ +void +e100_config_ifs(struct e100_private *bdp) +{ + u8 value = 0; + + spin_lock_bh(&(bdp->config_lock)); + + /* IFS value is only needed to be specified at half-duplex mode */ + if (bdp->cur_dplx_mode == HALF_DUPLEX) { + value = (u8) bdp->ifs_value; + } + + if (bdp->config[2] != value) { + bdp->config[2] = value; + E100_CONFIG(bdp, 2); + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_force_dplx - configure the forced full duplex mode + * @bdp: atapter's private data struct + * + * This routine will enable or disable force full duplex + * in the adapter's config block. If the PHY is 503, and + * the duplex is full, consider the adapter forced. + */ +void +e100_config_force_dplx(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + /* We must force full duplex on if we are using PHY 0, and we are */ + /* supposed to run in FDX mode. We do this because the e100 has only */ + /* one FDX# input pin, and that pin will be connected to PHY 1. */ + /* Changed the 'if' condition below to fix performance problem * at 10 + * full. The Phy was getting forced to full duplex while the MAC * was + * not, because the cur_dplx_mode was not being set to 2 by SetupPhy. * + * This is how the condition was, initially. * This has been changed so + * that the MAC gets forced to full duplex * simply if the user has + * forced full duplex. * * if (( bdp->phy_addr == 0 ) && ( + * bdp->cur_dplx_mode == 2 )) */ + /* The rest of the fix is in the PhyDetect code. */ + if ((bdp->params.e100_speed_duplex == E100_SPEED_10_FULL) || + (bdp->params.e100_speed_duplex == E100_SPEED_100_FULL) || + ((bdp->phy_addr == 32) && (bdp->cur_dplx_mode == FULL_DUPLEX))) { + if (!(bdp->config[19] & (u8) CB_CFIG_FORCE_FDX)) { + bdp->config[19] |= (u8) CB_CFIG_FORCE_FDX; + E100_CONFIG(bdp, 19); + } + + } else { + if (bdp->config[19] & (u8) CB_CFIG_FORCE_FDX) { + bdp->config[19] &= (u8) (~CB_CFIG_FORCE_FDX); + E100_CONFIG(bdp, 19); + } + } + + spin_unlock_bh(&(bdp->config_lock)); +} + +/** + * e100_config_long_rx + * @bdp: atapter's private data struct + * @enable: should we enable this option or not + * + * This routine will enable or disable reception of larger packets. + * This is needed by VLAN implementations. + */ +static void +e100_config_long_rx(struct e100_private *bdp, unsigned char enable) +{ + if (enable) { + if (!(bdp->config[18] & CB_CFIG_LONG_RX_OK)) { + bdp->config[18] |= CB_CFIG_LONG_RX_OK; + E100_CONFIG(bdp, 18); + } + + } else { + if ((bdp->config[18] & CB_CFIG_LONG_RX_OK)) { + bdp->config[18] &= ~CB_CFIG_LONG_RX_OK; + E100_CONFIG(bdp, 18); + } + } +} + +#ifdef ETHTOOL_GWOL +/** + * e100_config_wol + * @bdp: atapter's private data struct + * + * This sets configuration options for Wake On LAN functionality (WOL) in the + * config record. WOL options are retrieved from wolinfo_wolopts in @bdp + */ +void +e100_config_wol(struct e100_private *bdp) +{ + spin_lock_bh(&(bdp->config_lock)); + + if (bdp->wolopts & WAKE_PHY) { + bdp->config[9] |= CB_LINK_STATUS_WOL; + E100_CONFIG(bdp, 9); + } + + if (!(bdp->wolopts & WAKE_MAGIC)) { + bdp->config[19] |= CB_DISABLE_MAGPAK_WAKE; + E100_CONFIG(bdp, 19); + } + + spin_unlock_bh(&(bdp->config_lock)); +} +#endif + diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_config.h linux-2.5.6/drivers/net/e100/e100_config.h --- linux-2.5.6-pre3/drivers/net/e100/e100_config.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_config.h Thu Mar 7 18:24:45 2002 @@ -0,0 +1,206 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _E100_CONFIG_INC_ +#define _E100_CONFIG_INC_ + +#include "e100.h" + +#define E100_CONFIG(bdp, X) ((bdp)->config[0] = max_t(u8, (bdp)->config[0], (X)+1)) + +#define CB_CFIG_MIN_PARAMS 8 + +/* byte 0 bit definitions*/ +#define CB_CFIG_BYTE_COUNT_MASK BIT_0_5 /* Byte count occupies bit 5-0 */ + +/* byte 1 bit definitions*/ +#define CB_CFIG_RXFIFO_LIMIT_MASK BIT_0_4 /* RxFifo limit mask */ +#define CB_CFIG_TXFIFO_LIMIT_MASK BIT_4_7 /* TxFifo limit mask */ + +/* byte 2 bit definitions -- ADAPTIVE_IFS*/ + +/* word 3 bit definitions -- RESERVED*/ +/* Changed for 82558 enhancements */ +/* byte 3 bit definitions */ +#define CB_CFIG_MWI_EN BIT_0 /* Enable MWI on PCI bus */ +#define CB_CFIG_TYPE_EN BIT_1 /* Type Enable */ +#define CB_CFIG_READAL_EN BIT_2 /* Enable Read Align */ +#define CB_CFIG_TERMCL_EN BIT_3 /* Cache line write */ + +/* byte 4 bit definitions*/ +#define CB_CFIG_RX_MIN_DMA_MASK BIT_0_6 /* Rx minimum DMA count mask */ + +/* byte 5 bit definitions*/ +#define CB_CFIG_TX_MIN_DMA_MASK BIT_0_6 /* Tx minimum DMA count mask */ +#define CB_CFIG_DMBC_EN BIT_7 /* Enable Tx/Rx min. DMA counts */ + +/* Changed for 82558 enhancements */ +/* byte 6 bit definitions*/ +#define CB_CFIG_LATE_SCB BIT_0 /* Update SCB After New Tx Start */ +#define CB_CFIG_DIRECT_DMA_DIS BIT_1 /* Direct DMA mode */ +#define CB_CFIG_TNO_INT BIT_2 /* Tx Not OK Interrupt */ +#define CB_CFIG_TCO_STAT BIT_2 /* TCO statistics in 559 and above */ +#define CB_CFIG_CI_INT BIT_3 /* Command Complete Interrupt */ +#define CB_CFIG_EXT_TCB_DIS BIT_4 /* Extended TCB */ +#define CB_CFIG_EXT_STAT_DIS BIT_5 /* Extended Stats */ +#define CB_CFIG_SAVE_BAD_FRAMES BIT_7 /* Save Bad Frames Enabled */ + +/* byte 7 bit definitions*/ +#define CB_CFIG_DISC_SHORT_FRAMES BIT_0 /* Discard Short Frames */ +#define CB_CFIG_DYNTBD_EN BIT_7 /* Enable dynamic TBD */ +/* Enable extended RFD's on D102 */ +#define CB_CFIG_EXTENDED_RFD BIT_5 + +/* byte 8 bit definitions*/ +#define CB_CFIG_503_MII BIT_0 /* 503 vs. MII mode */ + +/* byte 9 bit definitions -- pre-defined all zeros*/ +#define CB_LINK_STATUS_WOL BIT_5 + +/* byte 10 bit definitions*/ +#define CB_CFIG_NO_SRCADR BIT_3 /* No Source Address Insertion */ +#define CB_CFIG_PREAMBLE_LEN BIT_4_5 /* Preamble Length */ +#define CB_CFIG_LOOPBACK_MODE BIT_6_7 /* Loopback Mode */ +#define CB_CFIG_LOOPBACK_NORMAL 0 +#define CB_CFIG_LOOPBACK_INTERNAL BIT_6 +#define CB_CFIG_LOOPBACK_EXTERNAL BIT_6_7 + +/* byte 11 bit definitions*/ +#define CB_CFIG_LINEAR_PRIORITY BIT_0_2 /* Linear Priority */ + +/* byte 12 bit definitions*/ +#define CB_CFIG_LINEAR_PRI_MODE BIT_0 /* Linear Priority mode */ +#define CB_CFIG_IFS_MASK BIT_4_7 /* Interframe Spacing mask */ + +/* byte 13 bit definitions -- pre-defined all zeros*/ + +/* byte 14 bit definitions -- pre-defined 0xf2*/ + +/* byte 15 bit definitions*/ +#define CB_CFIG_PROMISCUOUS BIT_0 /* Promiscuous Mode Enable */ +#define CB_CFIG_BROADCAST_DIS BIT_1 /* Broadcast Mode Disable */ +#define CB_CFIG_CRS_OR_CDT BIT_7 /* CRS Or CDT */ + +/* byte 16 bit definitions -- pre-defined all zeros*/ +#define DFLT_FC_DELAY_LSB 0x1f /* Delay for outgoing Pause frames */ +#define DFLT_NO_FC_DELAY_LSB 0x00 /* no flow control default value */ + +/* byte 17 bit definitions -- pre-defined 0x40*/ +#define DFLT_FC_DELAY_MSB 0x01 /* Delay for outgoing Pause frames */ +#define DFLT_NO_FC_DELAY_MSB 0x40 /* no flow control default value */ + +/* byte 18 bit definitions*/ +#define CB_CFIG_STRIPPING BIT_0 /* Padding Disabled */ +#define CB_CFIG_PADDING BIT_1 /* Padding Disabled */ +#define CB_CFIG_CRC_IN_MEM BIT_2 /* Transfer CRC To Memory */ + +/* byte 19 bit definitions*/ +#define CB_CFIG_TX_ADDR_WAKE BIT_0 /* Address Wakeup */ +#define CB_DISABLE_MAGPAK_WAKE BIT_1 /* Magic Packet Wakeup disable */ +/* Changed TX_FC_EN to TX_FC_DIS because 0 enables, 1 disables. Jul 8, 1999 */ +#define CB_CFIG_TX_FC_DIS BIT_2 /* Tx Flow Control Disable */ +#define CB_CFIG_FC_RESTOP BIT_3 /* Rx Flow Control Restop */ +#define CB_CFIG_FC_RESTART BIT_4 /* Rx Flow Control Restart */ +#define CB_CFIG_FC_REJECT BIT_5 /* Rx Flow Control Restart */ +#define CB_CFIG_FC_OPTS (CB_CFIG_FC_RESTOP | CB_CFIG_FC_RESTART | CB_CFIG_FC_REJECT) + +/* end 82558/9 specifics */ + +#define CB_CFIG_FORCE_FDX BIT_6 /* Force Full Duplex */ +#define CB_CFIG_FDX_ENABLE BIT_7 /* Full Duplex Enabled */ + +/* byte 20 bit definitions*/ +#define CB_CFIG_MULTI_IA BIT_6 /* Multiple IA Addr */ + +/* byte 21 bit definitions*/ +#define CB_CFIG_MULTICAST_ALL BIT_3 /* Multicast All */ + +/* byte 22 bit defines */ +#define CB_CFIG_RECEIVE_GAMLA_MODE BIT_0 /* D102 receive mode */ +#define CB_CFIG_VLAN_DROP_ENABLE BIT_1 /* vlan stripping */ + +#define CB_CFIG_LONG_RX_OK BIT_3 + +/* function prototypes */ +extern void e100_config_init(struct e100_private *bdp); +extern unsigned char e100_force_config(struct e100_private *bdp); +extern unsigned char e100_config(struct e100_private *bdp); +extern unsigned char e100_config_fc(struct e100_private *bdp); +extern void e100_config_promisc(struct e100_private *bdp, unsigned char enable); +extern void e100_config_brdcast_dsbl(struct e100_private *bdp); +extern void e100_config_mulcast_enbl(struct e100_private *bdp, + unsigned char enable); +extern void e100_config_ifs(struct e100_private *bdp); +extern void e100_config_force_dplx(struct e100_private *bdp); + +#endif /* _E100_CONFIG_INC_ */ diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_eeprom.c linux-2.5.6/drivers/net/e100/e100_eeprom.c --- linux-2.5.6-pre3/drivers/net/e100/e100_eeprom.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_eeprom.c Thu Mar 7 18:24:45 2002 @@ -0,0 +1,614 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_eeprom.c * +* * +* Abstract: This module contains routines to read and write to a * +* serial EEPROM * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ +#include "e100.h" + +#define CSR_EEPROM_CONTROL_FIELD(bdp) ((bdp)->scb->scb_eprm_cntrl) + +#define CSR_GENERAL_CONTROL2_FIELD(bdp) \ + ((bdp)->scb->scb_ext.d102_scb.scb_gen_ctrl2) + +#define EEPROM_STALL_TIME 4 +#define EEPROM_CHECKSUM ((u16) 0xBABA) +#define EEPROM_MAX_WORD_SIZE 256 + +void e100_eeprom_cleanup(struct e100_private *adapter); +u16 e100_eeprom_calculate_chksum(struct e100_private *adapter); +static void e100_eeprom_write_word(struct e100_private *adapter, u16 reg, + u16 data); +void e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data, + u16 size); +u16 e100_eeprom_size(struct e100_private *adapter); +u16 e100_eeprom_read(struct e100_private *adapter, u16 reg); + +static void shift_out_bits(struct e100_private *adapter, u16 data, u16 count); +static u16 shift_in_bits(struct e100_private *adapter); +static void raise_clock(struct e100_private *adapter, u16 *x); +static void lower_clock(struct e100_private *adapter, u16 *x); +static u16 eeprom_wait_cmd_done(struct e100_private *adapter); +static void eeprom_stand_by(struct e100_private *adapter); + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_set_semaphore +// +// Description: This function set (write 1) Gamla EEPROM semaphore bit (bit 23 word 0x1C in the CSR). +// +// Arguments: +// Adapter - Adapter context +// +// Returns: true if success +// else return false +// +//---------------------------------------------------------------------------------------- + +inline u8 +eeprom_set_semaphore(struct e100_private *adapter) +{ + u16 data = 0; + unsigned long expiration_time = jiffies + HZ / 100 + 1; + + while (time_before(jiffies, expiration_time)) { + // Get current value of General Control 2 + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + + // Set bit 23 word 0x1C in the CSR. + data |= SCB_GCR2_EEPROM_ACCESS_SEMAPHORE; + writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter)); + + barrier(); + + // Check to see if this bit set or not. + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + + if (data & SCB_GCR2_EEPROM_ACCESS_SEMAPHORE) { + return true; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + return false; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_reset_semaphore +// +// Description: This function reset (write 0) Gamla EEPROM semaphore bit +// (bit 23 word 0x1C in the CSR). +// +// Arguments: struct e100_private * adapter - Adapter context +//---------------------------------------------------------------------------------------- + +inline void +eeprom_reset_semaphore(struct e100_private *adapter) +{ + u16 data = 0; + + data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter)); + data &= ~(SCB_GCR2_EEPROM_ACCESS_SEMAPHORE); + writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter)); +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_size +// +// Description: This routine determines the size of the EEPROM. This value should be +// checked for validity - ie. is it too big or too small. The size returned +// is then passed to the read/write functions. +// +// Returns: +// Size of the eeprom, or zero if an error occured +//---------------------------------------------------------------------------------------- +u16 +e100_eeprom_size(struct e100_private *adapter) +{ + u16 x, size = 1; // must be one to accumulate a product + + // if we've already stored this data, read from memory + if (adapter->eeprom_size) { + return adapter->eeprom_size; + } + // otherwise, read from the eeprom + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return 0; + } + // enable the eeprom by setting EECS. + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + // write the read opcode + shift_out_bits(adapter, EEPROM_READ_OPCODE, 3); + + // experiment to discover the size of the eeprom. request register zero + // and wait for the eeprom to tell us it has accepted the entire address. + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + do { + size *= 2; // each bit of address doubles eeprom size + x |= EEDO; // set bit to detect "dummy zero" + x &= ~EEDI; // address consists of all zeros + + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); + raise_clock(adapter, &x); + lower_clock(adapter, &x); + + // check for "dummy zero" + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + if (size > EEPROM_MAX_WORD_SIZE) { + size = 0; + break; + } + } while (x & EEDO); + + // read in the value requested + (void) shift_in_bits(adapter); + e100_eeprom_cleanup(adapter); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } + + return size; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_address_size +// +// Description: determines the number of bits in an address for the eeprom acceptable +// values are 64, 128, and 256 +// Arguments: size of the eeprom +// Returns: bits in an address for that size eeprom +//---------------------------------------------------------------------------------------- + +static u16 +eeprom_address_size(u16 size) +{ + switch (size) { + case 64: + return 6; + case 128: + return 7; + case 256: + return 8; + } + + return 0; //fix compiler warning or error! +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_read +// +// Description: This routine serially reads one word out of the EEPROM. +// +// Arguments: +// adapter - our adapter context +// reg - EEPROM word to read. +// +// Returns: +// Contents of EEPROM word (reg). +//---------------------------------------------------------------------------------------- + +u16 +e100_eeprom_read(struct e100_private *adapter, u16 reg) +{ + u16 x, data, bits; + + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return 0; + } + // eeprom size is initialized to zero + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + bits = eeprom_address_size(adapter->eeprom_size); + + // select EEPROM, reset bits, set EECS + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + // write the read opcode and register number in that order + // The opcode is 3bits in length, reg is 'bits' bits long + shift_out_bits(adapter, EEPROM_READ_OPCODE, 3); + shift_out_bits(adapter, reg, bits); + + // Now read the data (16 bits) in from the selected EEPROM word + data = shift_in_bits(adapter); + + e100_eeprom_cleanup(adapter); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } + + return data; +} + +//---------------------------------------------------------------------------------------- +// Procedure: shift_out_bits +// +// Description: This routine shifts data bits out to the EEPROM. +// +// Arguments: +// data - data to send to the EEPROM. +// count - number of data bits to shift out. +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +static void +shift_out_bits(struct e100_private *adapter, u16 data, u16 count) +{ + u16 x, mask; + + mask = 1 << (count - 1); + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDO | EEDI); + + do { + x &= ~EEDI; + if (data & mask) + x |= EEDI; + + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); + raise_clock(adapter, &x); + lower_clock(adapter, &x); + mask = mask >> 1; + } while (mask); + + x &= ~EEDI; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); +} + +//---------------------------------------------------------------------------------------- +// Procedure: raise_clock +// +// Description: This routine raises the EEPROM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +void +raise_clock(struct e100_private *adapter, u16 *x) +{ + *x = *x | EESK; + writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} + +//---------------------------------------------------------------------------------------- +// Procedure: lower_clock +// +// Description: This routine lower's the EEPROM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//---------------------------------------------------------------------------------------- + +void +lower_clock(struct e100_private *adapter, u16 *x) +{ + *x = *x & ~EESK; + writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} + +//---------------------------------------------------------------------------------------- +// Procedure: shift_in_bits +// +// Description: This routine shifts data bits in from the EEPROM. +// +// Arguments: +// +// Returns: +// The contents of that particular EEPROM word +//---------------------------------------------------------------------------------------- + +static u16 +shift_in_bits(struct e100_private *adapter) +{ + u16 x, d, i; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDO | EEDI); + d = 0; + + for (i = 0; i < 16; i++) { + d <<= 1; + raise_clock(adapter, &x); + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~EEDI; + if (x & EEDO) + d |= 1; + + lower_clock(adapter, &x); + } + + return d; +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_cleanup +// +// Description: This routine returns the EEPROM to an idle state +//---------------------------------------------------------------------------------------- + +void +e100_eeprom_cleanup(struct e100_private *adapter) +{ + u16 x; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + + x &= ~(EECS | EEDI); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + raise_clock(adapter, &x); + lower_clock(adapter, &x); +} + +//********************************************************************************** +// Procedure: e100_eeprom_update_chksum +// +// Description: Calculates the checksum and writes it to the EEProm. +// It calculates the checksum accroding to the formula: +// Checksum = 0xBABA - (sum of first 63 words). +// +//----------------------------------------------------------------------------------- +u16 +e100_eeprom_calculate_chksum(struct e100_private *adapter) +{ + u16 idx, xsum_index, checksum = 0; + + // eeprom size is initialized to zero + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + xsum_index = adapter->eeprom_size - 1; + for (idx = 0; idx < xsum_index; idx++) + checksum += e100_eeprom_read(adapter, idx); + + checksum = EEPROM_CHECKSUM - checksum; + return checksum; +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_write_word +// +// Description: This routine writes a word to a specific EEPROM location without. +// taking EEPROM semaphore and updating checksum. +// Use e100_eeprom_write_block for the EEPROM update +// Arguments: reg - The EEPROM word that we are going to write to. +// data - The data (word) that we are going to write to the EEPROM. +//---------------------------------------------------------------------------------------- +static void +e100_eeprom_write_word(struct e100_private *adapter, u16 reg, u16 data) +{ + u16 x; + u16 bits; + + bits = eeprom_address_size(adapter->eeprom_size); + + /* select EEPROM, mask off ASIC and reset bits, set EECS */ + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EEDI | EEDO | EESK); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + wmb(); + udelay(EEPROM_STALL_TIME); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + + shift_out_bits(adapter, EEPROM_EWEN_OPCODE, 5); + shift_out_bits(adapter, reg, (u16) (bits - 2)); + if (!eeprom_wait_cmd_done(adapter)) + return; + + /* write the new word to the EEPROM & send the write opcode the EEPORM */ + shift_out_bits(adapter, EEPROM_WRITE_OPCODE, 3); + + /* select which word in the EEPROM that we are writing to */ + shift_out_bits(adapter, reg, bits); + + /* write the data to the selected EEPROM word */ + shift_out_bits(adapter, data, 16); + if (!eeprom_wait_cmd_done(adapter)) + return; + + shift_out_bits(adapter, EEPROM_EWDS_OPCODE, 5); + shift_out_bits(adapter, reg, (u16) (bits - 2)); + if (!eeprom_wait_cmd_done(adapter)) + return; + + e100_eeprom_cleanup(adapter); +} + +//---------------------------------------------------------------------------------------- +// Procedure: e100_eeprom_write_block +// +// Description: This routine writes a block of words starting from specified EEPROM +// location and updates checksum +// Arguments: reg - The EEPROM word that we are going to write to. +// data - The data (word) that we are going to write to the EEPROM. +//---------------------------------------------------------------------------------------- +void +e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data, + u16 size) +{ + u16 checksum; + u16 i; + + if (!adapter->eeprom_size) + adapter->eeprom_size = e100_eeprom_size(adapter); + + // Set EEPROM semaphore. + if (adapter->rev_id >= D102_REV_ID) { + if (!eeprom_set_semaphore(adapter)) + return; + } + + for (i = 0; i < size; i++) { + e100_eeprom_write_word(adapter, start + i, data[i]); + } + //Update checksum + checksum = e100_eeprom_calculate_chksum(adapter); + e100_eeprom_write_word(adapter, (adapter->eeprom_size - 1), checksum); + + // Clear EEPROM Semaphore. + if (adapter->rev_id >= D102_REV_ID) { + eeprom_reset_semaphore(adapter); + } +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_wait_cmd_done +// +// Description: This routine waits for the the EEPROM to finish its command. +// Specifically, it waits for EEDO (data out) to go high. +// Returns: true - If the command finished +// false - If the command never finished (EEDO stayed low) +//---------------------------------------------------------------------------------------- +static u16 +eeprom_wait_cmd_done(struct e100_private *adapter) +{ + u16 x; + unsigned long expiration_time = jiffies + HZ / 100 + 1; + + eeprom_stand_by(adapter); + + while (time_before(jiffies, expiration_time)) { + rmb(); + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + if (x & EEDO) + return true; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + return false; +} + +//---------------------------------------------------------------------------------------- +// Procedure: eeprom_stand_by +// +// Description: This routine lowers the EEPROM chip select (EECS) for a few microseconds. +//---------------------------------------------------------------------------------------- +static void +eeprom_stand_by(struct e100_private *adapter) +{ + u16 x; + + x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter)); + x &= ~(EECS | EESK); + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + wmb(); + udelay(EEPROM_STALL_TIME); + x |= EECS; + writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter)); + udelay(EEPROM_STALL_TIME); +} diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_main.c linux-2.5.6/drivers/net/e100/e100_main.c --- linux-2.5.6-pre3/drivers/net/e100/e100_main.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_main.c Thu Mar 7 18:24:45 2002 @@ -0,0 +1,3797 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_main.c * +* * +* Abstract: Functions for the driver entry points like load, * +* unload, open and close. All board specific calls made * +* by the network interface section of the driver. * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ + +#undef __NO_VERSION__ + +#include +#include +#include +#include +#include "e100.h" +#include "e100_ucode.h" +#include "e100_config.h" +#include "e100_phy.h" +#include "e100_vendor.h" + +#ifndef CONFIG_PROC_FS +#undef E100_CONFIG_PROC_FS +#endif + +#ifdef E100_CONFIG_PROC_FS +extern int e100_create_proc_subdir(struct e100_private *); +extern void e100_remove_proc_subdir(struct e100_private *); +#else +#define e100_create_proc_subdir(X) 0 +#define e100_remove_proc_subdir(X) do {} while(0) +#endif + +#ifdef SIOCETHTOOL +#define E100_ETHTOOL_IOCTL +#endif +#ifdef E100_ETHTOOL_IOCTL +static int e100_do_ethtool_ioctl(struct net_device *, struct ifreq *); +static void e100_get_speed_duplex_caps(struct e100_private *); +static int e100_ethtool_get_settings(struct net_device *, struct ifreq *); +static int e100_ethtool_set_settings(struct net_device *, struct ifreq *); +static void e100_set_speed_duplex(struct e100_private *); + +#ifdef ETHTOOL_GDRVINFO +static int e100_ethtool_get_drvinfo(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_GEEPROM +static int e100_ethtool_eeprom(struct net_device *, struct ifreq *); + +#define E100_EEPROM_MAGIC 0x1234 +#endif +#ifdef ETHTOOL_GLINK +static int e100_ethtool_glink(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_NWAY_RST +static int e100_ethtool_nway_rst(struct net_device *, struct ifreq *); +#endif +#ifdef ETHTOOL_GWOL +static int e100_ethtool_wol(struct net_device *, struct ifreq *); +static unsigned char e100_setup_filter(struct e100_private *bdp); +static void e100_do_wol(struct pci_dev *pcid, struct e100_private *bdp); +static u16 e100_get_ip_lbytes(struct net_device *dev); +extern void e100_config_wol(struct e100_private *bdp); +#endif +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef SIOCGMIIPHY +#define E100_MII_IOCTL +#endif +#ifdef E100_MII_IOCTL +#include +static int e100_mii_ioctl(struct net_device *, struct ifreq *, int); +#endif /*E100_MII_IOCTL */ + +static unsigned char e100_delayed_exec_non_cu_cmd(struct e100_private *, + nxmit_cb_entry_t *); +static void e100_free_nontx_list(struct e100_private *); +static void e100_non_tx_background(unsigned long); + +/* Global Data structures and variables */ +char e100_copyright[] __devinitdata = "Copyright (c) 2002 Intel Corporation"; + +#define E100_VERSION "2.0.20-pre1" +#define E100_FULL_DRIVER_NAME "Intel(R) PRO/100 Fast Ethernet Adapter - Loadable driver, ver " + +const char *e100_version = E100_VERSION; +const char *e100_full_driver_name = E100_FULL_DRIVER_NAME E100_VERSION; +char *e100_short_driver_name = "e100"; +static int e100nics = 0; + +/*********************************************************************/ +/*! This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + *********************************************************************/ + +#define E100_PARAM_INIT { [0 ... E100_MAX_NIC-1] = -1 } + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ +#define E100_PARAM(X, S) \ + static const int X[E100_MAX_NIC + 1] = E100_PARAM_INIT; \ + MODULE_PARM(X, "1-" __MODULE_STRING(E100_MAX_NIC) "i"); \ + MODULE_PARM_DESC(X, S); + +/* ====================================================================== */ +static u8 e100_D101M_checksum(struct e100_private *, struct sk_buff *); +static u8 e100_D102_check_checksum(rfd_t *); +static int e100_ioctl(struct net_device *, struct ifreq *, int); +static int e100_open(struct net_device *); +static int e100_close(struct net_device *); +static int e100_change_mtu(struct net_device *, int); +static int e100_xmit_frame(struct sk_buff *, struct net_device *); +static unsigned char e100_init(struct e100_private *); +static int e100_set_mac(struct net_device *, void *); +struct net_device_stats *e100_get_stats(struct net_device *); + +static void e100intr(int, void *, struct pt_regs *); +static void e100_print_brd_conf(struct e100_private *); +static void e100_set_multi(struct net_device *); + +char *e100_get_brand_msg(struct e100_private *); +static u8 e100_pci_setup(struct pci_dev *, struct e100_private *); +static u8 e100_sw_init(struct e100_private *); +static unsigned char e100_alloc_space(struct e100_private *); +static void e100_dealloc_space(struct e100_private *); +static int e100_alloc_tcb_pool(struct e100_private *); +static void e100_setup_tcb_pool(tcb_t *, unsigned int, struct e100_private *); +static void e100_free_tcb_pool(struct e100_private *); +static int e100_alloc_rfd_pool(struct e100_private *); +static void e100_free_rfd_pool(struct e100_private *); + +static void e100_rd_eaddr(struct e100_private *); +static void e100_rd_pwa_no(struct e100_private *); +extern u16 e100_eeprom_read(struct e100_private *, u16); +extern void e100_eeprom_write_block(struct e100_private *, u16, u16 *, u16); +extern u16 e100_eeprom_size(struct e100_private *); + +static unsigned char e100_clr_cntrs(struct e100_private *); +static unsigned char e100_load_microcode(struct e100_private *); +static unsigned char e100_hw_init(struct e100_private *, u32); +static unsigned char e100_setup_iaaddr(struct e100_private *, u8 *); +static unsigned char e100_update_stats(struct e100_private *bdp); + +static void e100_start_ru(struct e100_private *); +static void e100_dump_stats_cntrs(struct e100_private *); + +static void e100_check_options(int board, struct e100_private *bdp); +static void e100_set_int_option(int *, int, int, int, int, char *); +static void e100_set_bool_option(struct e100_private *bdp, int, u32, int, + char *); +unsigned char e100_wait_exec_cmplx(struct e100_private *, u32, u8); +void e100_exec_cmplx(struct e100_private *, u32, u8); + +/** + * e100_get_rx_struct - retrieve cell to hold skb buff from the pool + * @bdp: atapter's private data struct + * + * Returns the new cell to hold sk_buff or %NULL. + */ +static inline struct rx_list_elem * +e100_get_rx_struct(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct = NULL; + + if (!list_empty(&(bdp->rx_struct_pool))) { + rx_struct = list_entry(bdp->rx_struct_pool.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + } + + return rx_struct; +} + +/** + * e100_alloc_skb - allocate an skb for the adapter + * @bdp: atapter's private data struct + * + * Allocates skb with enough room for rfd, and data, and reserve non-data space. + * Returns the new cell with sk_buff or %NULL. + */ +static inline struct rx_list_elem * +e100_alloc_skb(struct e100_private *bdp) +{ + struct sk_buff *new_skb; + u32 skb_size = sizeof (rfd_t); + struct rx_list_elem *rx_struct; + + new_skb = (struct sk_buff *) dev_alloc_skb(skb_size); + if (new_skb) { + /* The IP data should be + DWORD aligned. since the ethernet header is 14 bytes long, + we need to reserve 2 extra bytes so that the TCP/IP headers + will be DWORD aligned. */ + skb_reserve(new_skb, 2); + if ((rx_struct = e100_get_rx_struct(bdp)) == NULL) + goto err; + rx_struct->skb = new_skb; + rx_struct->dma_addr = pci_map_single(bdp->pdev, new_skb->data, + sizeof (rfd_t), + PCI_DMA_FROMDEVICE); + if (!rx_struct->dma_addr) + goto err; + skb_reserve(new_skb, bdp->rfd_size); + return rx_struct; + } else { + return NULL; + } + +err: + dev_kfree_skb_irq(new_skb); + return NULL; +} + +/** + * e100_add_skb_to_end - add an skb to the end of our rfd list + * @bdp: atapter's private data struct + * @rx_struct: rx_list_elem with the new skb + * + * Adds a newly allocated skb to the end of our rfd list. + */ +inline void +e100_add_skb_to_end(struct e100_private *bdp, struct rx_list_elem *rx_struct) +{ + rfd_t *rfdn; /* The new rfd */ + rfd_t *rfd; /* The old rfd */ + struct rx_list_elem *rx_struct_last; + + (rx_struct->skb)->dev = bdp->device; + rfdn = RFD_POINTER(rx_struct->skb, bdp); + rfdn->rfd_header.cb_status = 0; + rfdn->rfd_header.cb_cmd = __constant_cpu_to_le16(RFD_EL_BIT); + rfdn->rfd_act_cnt = 0; + rfdn->rfd_sz = __constant_cpu_to_le16(RFD_DATA_SIZE); + + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, bdp->rfd_size, + PCI_DMA_TODEVICE); + + if (!list_empty(&(bdp->active_rx_list))) { + rx_struct_last = list_entry(bdp->active_rx_list.prev, + struct rx_list_elem, list_elem); + rfd = RFD_POINTER(rx_struct_last->skb, bdp); + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 4, PCI_DMA_FROMDEVICE); + put_unaligned(cpu_to_le32(rx_struct->dma_addr), + ((u32 *) (&(rfd->rfd_header.cb_lnk_ptr)))); + + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 8, PCI_DMA_TODEVICE); + rfd->rfd_header.cb_cmd &= + __constant_cpu_to_le16((u16) ~RFD_EL_BIT); + + pci_dma_sync_single(bdp->pdev, rx_struct_last->dma_addr, + 4, PCI_DMA_TODEVICE); + } + + list_add_tail(&(rx_struct->list_elem), &(bdp->active_rx_list)); +} + +static inline void +e100_alloc_skbs(struct e100_private *bdp) +{ + for (; bdp->skb_req > 0; bdp->skb_req--) { + struct rx_list_elem *rx_struct; + + if ((rx_struct = e100_alloc_skb(bdp)) == NULL) + return; + + e100_add_skb_to_end(bdp, rx_struct); + } +} + +void e100_tx_srv(struct e100_private *); +u32 e100_rx_srv(struct e100_private *, u32, int *); + +void e100_polling_tasklet(unsigned long); + +void e100_watchdog(struct net_device *); +void e100_refresh_txthld(struct e100_private *); +void e100_manage_adaptive_ifs(struct e100_private *); +void e100_clear_pools(struct e100_private *); +static void e100_clear_structs(struct net_device *); +static inline tcb_t *e100_prepare_xmit_buff(struct e100_private *, + struct sk_buff *); +static void e100_set_multi_exec(struct net_device *dev); + +MODULE_AUTHOR("Intel Corporation, "); +MODULE_DESCRIPTION(E100_FULL_DRIVER_NAME E100_VERSION); +MODULE_LICENSE("Dual BSD/GPL"); + +E100_PARAM(TxDescriptors, "Number of transmit descriptors"); +E100_PARAM(RxDescriptors, "Number of receive descriptors"); +E100_PARAM(XsumRX, "Disable or enable Receive Checksum offload"); +E100_PARAM(e100_speed_duplex, "Speed and Duplex settings"); +E100_PARAM(ucode, "Disable or enable microcode loading"); +E100_PARAM(ber, "Value for the BER correction algorithm"); +E100_PARAM(flow_control, "Disable or enable Ethernet PAUSE frames processing"); +E100_PARAM(IntDelay, "Value for CPU saver's interrupt delay"); +E100_PARAM(BundleSmallFr, "Disable or enable interrupt bundling of small frames"); +E100_PARAM(BundleMax, "Maximum number for CPU saver's packet bundling"); +E100_PARAM(IFS, "Disable or enable the adaptive IFS algorithm"); +E100_PARAM(RxCongestionControl, "Disable or enable switch to polling mode"); +E100_PARAM(PollingMaxWork, "Max number of receive packets processed on single " + "polling call"); + +/** + * e100_exec_cmd - issue a comand + * @bdp: atapter's private data struct + * @scb_cmd_low: the command that is to be issued + * + * This general routine will issue a command to the e100. + */ +static inline void +e100_exec_cmd(struct e100_private *bdp, u8 cmd_low) +{ + writeb(cmd_low, &(bdp->scb->scb_cmd_low)); +} + +/** + * e100_wait_scb - wait for SCB to clear + * @bdp: atapter's private data struct + * + * This routine checks to see if the e100 has accepted a command. + * It does so by checking the command field in the SCB, which will + * be zeroed by the e100 upon accepting a command. The loop waits + * for up to 1 millisecond for command acceptance. + * + * Returns: + * true if the SCB cleared within 1 millisecond. + * false if it didn't clear within 1 millisecond + */ +unsigned char +e100_wait_scb(struct e100_private *bdp) +{ + int i; + + /* loop on the scb for a few times */ + for (i = 0; i < 100; i++) { + if (!readb(&bdp->scb->scb_cmd_low)) + return true; + cpu_relax(); + } + + /* it didn't work. do it the slow way using udelay()s */ + for (i = 0; i < E100_MAX_BUSY_WAIT; i++) { + if (!readb(&bdp->scb->scb_cmd_low)) + return true; + cpu_relax(); + udelay(1); + } + + return false; +} + +/** + * e100_wait_exec_simple - issue a command + * @bdp: atapter's private data struct + * @scb_cmd_low: the command that is to be issued + * + * This general routine will issue a command to the e100 after waiting for + * the previous command to finish. + * + * Returns: + * true if the command was issued to the chip successfully + * false if the command was not issued to the chip + */ +inline unsigned char +e100_wait_exec_simple(struct e100_private *bdp, u8 scb_cmd_low) +{ + if (!e100_wait_scb(bdp)) { + printk(KERN_ERR "%s e100_wait_exec_simple: Wait failed\n", + bdp->device->name); + return false; + } + e100_exec_cmd(bdp, scb_cmd_low); + return true; +} + +void +e100_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd) +{ + writel(phys_addr, &(bdp->scb->scb_gen_ptr)); + readw(&(bdp->scb->scb_status)); /* flashes last write, read-safe */ + e100_exec_cmd(bdp, cmd); +} + +unsigned char +e100_wait_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd) +{ + if (!e100_wait_scb(bdp)) { + return false; + } + e100_exec_cmplx(bdp, phys_addr, cmd); + return true; +} + +inline u8 +e100_wait_cus_idle(struct e100_private *bdp) +{ + int i; + + /* loop on the scb for a few times */ + for (i = 0; i < 100; i++) { + if (((readw(&(bdp->scb->scb_status)) & SCB_CUS_MASK) != + SCB_CUS_ACTIVE)) { + return true; + } + cpu_relax(); + } + + for (i = 0; i < E100_MAX_BUSY_WAIT; i++) { + if (((readw(&(bdp->scb->scb_status)) & SCB_CUS_MASK) != + SCB_CUS_ACTIVE)) { + return true; + } + cpu_relax(); + udelay(1); + } + + return false; +} + +/** + * e100_dis_intr - disable interrupts + * @bdp: atapter's private data struct + * + * This routine disables interrupts at the hardware, by setting + * the M (mask) bit in the adapter's CSR SCB command word. + */ +static inline void +e100_dis_intr(struct e100_private *bdp) +{ + /* Disable interrupts on our PCI board by setting the mask bit */ + writeb(SCB_INT_MASK, &bdp->scb->scb_cmd_hi); +} + +/** + * e100_set_intr_mask - set interrupts + * @bdp: atapter's private data struct + * + * This routine sets interrupts at the hardware, by resetting + * the M (mask) bit in the adapter's CSR SCB command word + */ +static inline void +e100_set_intr_mask(struct e100_private *bdp) +{ + writeb(bdp->intr_mask, &bdp->scb->scb_cmd_hi); +} + +static inline void +e100_trigger_SWI(struct e100_private *bdp) +{ + /* Trigger interrupt on our PCI board by asserting SWI bit */ + writeb(SCB_SOFT_INT, &bdp->scb->scb_cmd_hi); +} + +static int __devinit +e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent) +{ + static int first_time = true; + struct net_device *dev = NULL; + struct e100_private *bdp = NULL; + int rc = 0; + + dev = alloc_etherdev(sizeof (struct e100_private)); + if (dev == NULL) { + printk(KERN_ERR "Not able to alloc etherdev struct\n"); + rc = -ENODEV; + goto out; + } + + SET_MODULE_OWNER(dev); + + if (first_time) { + first_time = false; + printk(KERN_NOTICE "%s\n", e100_full_driver_name); + printk(KERN_NOTICE "%s\n", e100_copyright); + printk(KERN_NOTICE "\n"); + } + + bdp = dev->priv; + bdp->pdev = pcid; + bdp->device = dev; + + pci_set_drvdata(pcid, dev); + + if ((rc = e100_alloc_space(bdp)) != 0) { + goto err_dev; + } + + bdp->flags = 0; + bdp->ifs_state = 0; + bdp->ifs_value = 0; + bdp->scb = 0; + + init_timer(&bdp->nontx_timer_id); + bdp->nontx_timer_id.data = (unsigned long) bdp; + bdp->nontx_timer_id.function = (void *) &e100_non_tx_background; + INIT_LIST_HEAD(&(bdp->non_tx_cmd_list)); + bdp->non_tx_command_state = E100_NON_TX_IDLE; + + init_timer(&bdp->watchdog_timer); + bdp->watchdog_timer.data = (unsigned long) dev; + bdp->watchdog_timer.function = (void *) &e100_watchdog; + + if ((rc = e100_pci_setup(pcid, bdp)) != 0) { + goto err_dealloc; + } + + if (((bdp->pdev->device > 0x1030) + && (bdp->pdev->device < 0x103F)) + || (bdp->pdev->device == 0x2449) + || (bdp->pdev->device == 0x2459) + || (bdp->pdev->device == 0x245D)) { + bdp->rev_id = D101MA_REV_ID; /* workaround for ICH3 */ + bdp->flags |= IS_ICH; + } + + if (bdp->rev_id == 0xff) + bdp->rev_id = 1; + + if ((u8) bdp->rev_id >= D101A4_REV_ID) + bdp->flags |= IS_BACHELOR; + + if ((u8) bdp->rev_id >= D102_REV_ID) { + bdp->flags |= USE_IPCB; + bdp->rfd_size = 32; + } else { + bdp->rfd_size = 16; + } + + e100_check_options(e100nics, bdp); + + if (!e100_init(bdp)) { + printk(KERN_ERR "Failed to initialize e100, instance #%d\n", + e100nics); + rc = -ENODEV; + goto err_pci; + } + + dev->irq = pcid->irq; + dev->open = &e100_open; + dev->hard_start_xmit = &e100_xmit_frame; + dev->stop = &e100_close; + dev->change_mtu = &e100_change_mtu; + dev->get_stats = &e100_get_stats; + dev->set_multicast_list = &e100_set_multi; + dev->set_mac_address = &e100_set_mac; + dev->do_ioctl = &e100_ioctl; +#ifdef E100_ZEROCOPY + if (bdp->flags & USE_IPCB) { + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + } +#endif + e100nics++; + +#ifdef E100_ETHTOOL_IOCTL + e100_get_speed_duplex_caps(bdp); +#endif /*E100_ETHTOOL_IOCTL */ + + if ((rc = register_netdev(dev)) != 0) { + goto err_pci; + } + + bdp->device_type = ent->driver_data; + printk(KERN_NOTICE + "%s: %s\n", bdp->device->name, e100_get_brand_msg(bdp)); + e100_print_brd_conf(bdp); + + if (e100_create_proc_subdir(bdp) < 0) { + printk(KERN_ERR "Failed to create proc directory for %s\n", + bdp->device->name); + } + +#ifdef ETHTOOL_GWOL + /* Disabling all WOLs as initialization */ + bdp->wolsupported = bdp->wolopts = 0; + if (bdp->rev_id >= D101MA_REV_ID) { + bdp->wolsupported = + WAKE_PHY | WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; + bdp->wolopts = WAKE_MAGIC; + } +#endif + + printk(KERN_NOTICE "\n"); + + goto out; + +err_pci: + iounmap(bdp->scb); + pci_release_regions(pcid); + pci_disable_device(pcid); +err_dealloc: + e100_dealloc_space(bdp); +err_dev: + pci_set_drvdata(pcid, NULL); + kfree(dev); +out: + return rc; +} + +/** + * e100_clear_structs - free resources + * @dev: adapter's net_device struct + * + * Free all device specific structs, unmap i/o address, etc. + */ +static void __devexit +e100_clear_structs(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + iounmap(bdp->scb); + pci_release_regions(bdp->pdev); + pci_disable_device(bdp->pdev); + + e100_dealloc_space(bdp); + pci_set_drvdata(bdp->pdev, NULL); + kfree(dev); +} + +static void __devexit +e100_remove1(struct pci_dev *pcid) +{ + struct net_device *dev; + struct e100_private *bdp; + + if (!(dev = (struct net_device *) pci_get_drvdata(pcid))) + return; + + bdp = dev->priv; + + unregister_netdev(dev); + + e100_remove_proc_subdir(bdp); + + e100_sw_reset(bdp, PORT_SELECTIVE_RESET); + + if (bdp->non_tx_command_state != E100_NON_TX_IDLE) { + del_timer_sync(&bdp->nontx_timer_id); + e100_free_nontx_list(bdp); + bdp->non_tx_command_state = E100_NON_TX_IDLE; + } + +#ifdef ETHTOOL_GWOL + /* Set up wol options and enable PME */ + e100_do_wol(pcid, bdp); +#endif + + e100_clear_structs(dev); + + --e100nics; +} + +MODULE_DEVICE_TABLE(pci, e100_id_table); + +static struct pci_driver e100_driver = { + name: "e100", + id_table: e100_id_table, + probe: e100_found1, + remove: __devexit_p(e100_remove1), + suspend: NULL, + resume: NULL, +}; + +static int __init +e100_init_module(void) +{ + return pci_module_init(&e100_driver); + +} + +static void __exit +e100_cleanup_module(void) +{ + pci_unregister_driver(&e100_driver); +} + +module_init(e100_init_module); +module_exit(e100_cleanup_module); + +/** + * e100_check_options - check command line options + * @board: board number + * @bdp: atapter's private data struct + * + * This routine does range checking on command-line options + */ +void __devinit +e100_check_options(int board, struct e100_private *bdp) +{ + int val; + + if (board >= E100_MAX_NIC) { + printk(KERN_NOTICE "No configuration available for board #%d\n", + board); + printk(KERN_NOTICE "Using defaults for all values\n"); + } + + val = (board < E100_MAX_NIC) ? TxDescriptors[board] : -1; + e100_set_int_option(&(bdp->params.TxDescriptors), val, E100_MIN_TCB, + E100_MAX_TCB, E100_DEFAULT_TCB, + "TxDescriptor count"); + + val = (board < E100_MAX_NIC) ? RxDescriptors[board] : -1; + e100_set_int_option(&(bdp->params.RxDescriptors), val, E100_MIN_RFD, + E100_MAX_RFD, E100_DEFAULT_RFD, + "RxDescriptor count"); + + val = (board < E100_MAX_NIC) ? e100_speed_duplex[board] : -1; + e100_set_int_option(&(bdp->params.e100_speed_duplex), val, 0, 4, + E100_DEFAULT_SPEED_DUPLEX, "speed/duplex mode"); + + val = (board < E100_MAX_NIC) ? ber[board] : -1; + e100_set_int_option(&(bdp->params.ber), val, 0, ZLOCK_MAX_ERRORS, + E100_DEFAULT_BER, "Bit Error Rate count"); + + val = (board < E100_MAX_NIC) ? XsumRX[board] : -1; + e100_set_bool_option(bdp, val, PRM_XSUMRX, E100_DEFAULT_XSUM, + "XsumRX value"); + + /* Default ucode value depended on controller revision */ + val = (board < E100_MAX_NIC) ? ucode[board] : -1; + if (bdp->rev_id >= D101MA_REV_ID) { + e100_set_bool_option(bdp, val, PRM_UCODE, E100_DEFAULT_UCODE, + "ucode value"); + } else { + e100_set_bool_option(bdp, val, PRM_UCODE, false, "ucode value"); + } + + val = (board < E100_MAX_NIC) ? flow_control[board] : -1; + e100_set_bool_option(bdp, val, PRM_FC, E100_DEFAULT_FC, + "flow control value"); + + val = (board < E100_MAX_NIC) ? IFS[board] : -1; + e100_set_bool_option(bdp, val, PRM_IFS, E100_DEFAULT_IFS, "IFS value"); + + val = (board < E100_MAX_NIC) ? BundleSmallFr[board] : -1; + e100_set_bool_option(bdp, val, PRM_BUNDLE_SMALL, + E100_DEFAULT_BUNDLE_SMALL_FR, + "CPU saver bundle small frames value"); + + val = (board < E100_MAX_NIC) ? IntDelay[board] : -1; + e100_set_int_option(&(bdp->params.IntDelay), val, 0x0, 0xFFFF, + E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY, + "CPU saver interrupt delay value"); + + val = (board < E100_MAX_NIC) ? BundleMax[board] : -1; + e100_set_int_option(&(bdp->params.BundleMax), val, 0x1, 0xFFFF, + E100_DEFAULT_CPUSAVER_BUNDLE_MAX, + "CPU saver bundle max value"); + + val = (board < E100_MAX_NIC) ? RxCongestionControl[board] : -1; + e100_set_bool_option(bdp, val, PRM_RX_CONG, + E100_DEFAULT_RX_CONGESTION_CONTROL, + "Rx Congestion Control value"); + + val = (board < E100_MAX_NIC) ? PollingMaxWork[board] : -1; + e100_set_int_option(&(bdp->params.PollingMaxWork), val, 1, E100_MAX_RFD, + RxDescriptors[board], "Polling Max Work value"); + + if (val <= 0) { + bdp->params.b_params &= ~PRM_RX_CONG; + } +} + +/** + * e100_set_int_option - check and set an integer option + * @option: a pointer to the relevant option field + * @val: the value specified + * @min: the minimum valid value + * @max: the maximum valid value + * @default_val: the default value + * @name: the name of the option + * + * This routine does range checking on a command-line option. + * If the option's value is '-1' use the specified default. + * Otherwise, if the value is invalid, change it to the default. + */ +void __devinit +e100_set_int_option(int *option, int val, int min, int max, int default_val, + char *name) +{ + if (val == -1) { /* no value specified. use default */ + *option = default_val; + + } else if ((val < min) || (val > max)) { + printk(KERN_NOTICE + "Invalid %s specified (%i). Valid range is %i-%i\n", + name, val, min, max); + printk(KERN_NOTICE "Using default %s of %i\n", name, + default_val); + *option = default_val; + } else { + printk(KERN_INFO "Using specified %s of %i\n", name, val); + *option = val; + } +} + +/** + * e100_set_bool_option - check and set a boolean option + * @bdp: atapter's private data struct + * @val: the value specified + * @mask: the mask for the relevant option + * @default_val: the default value + * @name: the name of the option + * + * This routine checks a boolean command-line option. + * If the option's value is '-1' use the specified default. + * Otherwise, if the value is invalid (not 0 or 1), + * change it to the default. + */ +void __devinit +e100_set_bool_option(struct e100_private *bdp, int val, u32 mask, + int default_val, char *name) +{ + if (val == -1) { + if (default_val) + bdp->params.b_params |= mask; + + } else if ((val != true) && (val != false)) { + printk(KERN_NOTICE + "Invalid %s specified (%i). Valid values are %i/%i\n", + name, val, false, true); + printk(KERN_NOTICE "Using default %s of %i\n", name, + default_val); + + if (default_val) + bdp->params.b_params |= mask; + } else { + printk(KERN_INFO "Using specified %s of %i\n", name, val); + if (val) + bdp->params.b_params |= mask; + } +} + +static int +e100_open(struct net_device *dev) +{ + struct e100_private *bdp; + int rc = 0; + + bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + rc = -EBUSY; + goto exit; + } + + /* setup the tcb pool */ + if (!e100_alloc_tcb_pool(bdp)) { + rc = -ENOMEM; + goto err_exit; + } + bdp->last_tcb = NULL; + + bdp->tcb_pool.head = 0; + bdp->tcb_pool.tail = 1; + + e100_setup_tcb_pool((tcb_t *) bdp->tcb_pool.data, + bdp->params.TxDescriptors, bdp); + + if (!e100_alloc_rfd_pool(bdp)) { + rc = -ENOMEM; + goto err_exit; + } + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) { + rc = -EAGAIN; + goto err_exit; + } + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) { + rc = -EAGAIN; + goto err_exit; + } + + mod_timer(&(bdp->watchdog_timer), jiffies + (2 * HZ)); + + netif_start_queue(dev); + + e100_start_ru(bdp); + if ((rc = request_irq(dev->irq, &e100intr, SA_SHIRQ, + e100_short_driver_name, dev)) != 0) { + del_timer_sync(&bdp->watchdog_timer); + goto err_exit; + } + if (bdp->params.b_params & PRM_RX_CONG) { + DECLARE_TASKLET(polling_tasklet, + e100_polling_tasklet, (unsigned long) bdp); + bdp->polling_tasklet = polling_tasklet; + } + bdp->intr_mask = 0; + e100_set_intr_mask(bdp); + + e100_force_config(bdp); + + goto exit; + +err_exit: + e100_clear_pools(bdp); +exit: + read_unlock(&(bdp->isolate_lock)); + return rc; +} + +static int +e100_close(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + bdp->intr_mask = SCB_INT_MASK; + e100_isolate_driver(bdp); + +#ifdef ETHTOOL_GWOL + bdp->ip_lbytes = e100_get_ip_lbytes(dev); +#endif + free_irq(dev->irq, dev); + e100_clear_pools(bdp); + + if (bdp->params.b_params & PRM_RX_CONG) { + tasklet_kill(&(bdp->polling_tasklet)); + } + + /* set the isolate flag to false, so e100_open can be called */ + bdp->driver_isolated = false; + + return 0; +} + +static int +e100_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static int +e100_xmit_frame(struct sk_buff *skb, struct net_device *dev) +{ + int rc = 0; + int notify_stop = false; + struct e100_private *bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + rc = -EBUSY; + goto exit2; + } + + if (!spin_trylock(&bdp->bd_non_tx_lock)) { + notify_stop = true; + rc = 1; + goto exit2; + } + + if (!TCBS_AVAIL(bdp->tcb_pool) || + (bdp->non_tx_command_state != E100_NON_TX_IDLE)) { + notify_stop = true; + rc = 1; + goto exit1; + } + + e100_prepare_xmit_buff(bdp, skb); + + bdp->drv_stats.net_stats.tx_bytes += skb->len; + + dev->trans_start = jiffies; + +exit1: + spin_unlock(&bdp->bd_non_tx_lock); +exit2: + read_unlock(&(bdp->isolate_lock)); + if (notify_stop) { + netif_stop_queue(dev); + } + + return rc; +} + +/** + * e100_get_stats - get driver statistics + * @dev: adapter's net_device struct + * + * This routine is called when the OS wants the adapter's stats returned. + * It returns the address of the net_device_stats stucture for the device. + * If the statistics are currently being updated, then they might be incorrect + * for a short while. However, since this cannot actually cause damage, no + * locking is used. + */ +struct net_device_stats * +e100_get_stats(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + bdp->drv_stats.net_stats.tx_errors = + bdp->drv_stats.net_stats.tx_carrier_errors + + bdp->drv_stats.net_stats.tx_aborted_errors; + + bdp->drv_stats.net_stats.rx_errors = + bdp->drv_stats.net_stats.rx_crc_errors + + bdp->drv_stats.net_stats.rx_frame_errors + + bdp->drv_stats.net_stats.rx_length_errors + + bdp->drv_stats.rcv_cdt_frames; + + return &(bdp->drv_stats.net_stats); +} + +/** + * e100_set_mac - set the MAC address + * @dev: adapter's net_device struct + * @addr: the new address + * + * This routine sets the ethernet address of the board + * Returns: + * 0 - if successful + * -1 - otherwise + */ +static int +e100_set_mac(struct net_device *dev, void *addr) +{ + struct e100_private *bdp; + int rc = -1; + struct sockaddr *p_sockaddr = (struct sockaddr *) addr; + + bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + + if (bdp->driver_isolated) { + goto exit; + } + if (e100_setup_iaaddr(bdp, (u8 *) (p_sockaddr->sa_data))) { + memcpy(&(dev->dev_addr[0]), p_sockaddr->sa_data, ETH_ALEN); + rc = 0; + } + +exit: + read_unlock(&(bdp->isolate_lock)); + return rc; +} + +static void +e100_set_multi_exec(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + mltcst_cb_t *mcast_buff; + cb_header_t *cb_hdr; + struct dev_mc_list *mc_list; + unsigned int i; + nxmit_cb_entry_t *cmd = e100_alloc_non_tx_cmd(bdp); + + if (cmd != NULL) { + mcast_buff = &((cmd->non_tx_cmd)->ntcb.multicast); + cb_hdr = &((cmd->non_tx_cmd)->ntcb.multicast.mc_cbhdr); + } else { + return; + } + + /* initialize the multi cast command */ + cb_hdr->cb_cmd = __constant_cpu_to_le16(CB_MULTICAST); + + /* now fill in the rest of the multicast command */ + *(u16 *) (&(mcast_buff->mc_count)) = cpu_to_le16(dev->mc_count * 6); + for (i = 0, mc_list = dev->mc_list; + (i < dev->mc_count) && (i < MAX_MULTICAST_ADDRS); + i++, mc_list = mc_list->next) { + /* copy into the command */ + memcpy(&(mcast_buff->mc_addr[i * ETH_ALEN]), + (u8 *) &(mc_list->dmi_addr), ETH_ALEN); + } + + if (!e100_exec_non_cu_cmd(bdp, cmd)) { + printk(KERN_WARNING "%s: Multicast setup failed\n", dev->name); + } +} + +/** + * e100_set_multi - set multicast status + * @dev: adapter's net_device struct + * + * This routine is called to add or remove multicast addresses, and/or to + * change the adapter's promiscuous state. + */ +static void +e100_set_multi(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + unsigned char promisc_enbl; + unsigned char mulcast_enbl; + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + promisc_enbl = (dev->flags & IFF_PROMISC); + mulcast_enbl = ((dev->flags & IFF_ALLMULTI) || + (dev->mc_count > MAX_MULTICAST_ADDRS)); + + e100_config_promisc(bdp, promisc_enbl); + e100_config_mulcast_enbl(bdp, mulcast_enbl); + + /* reconfigure the chip if something has changed in its config space */ + e100_config(bdp); + + if ((promisc_enbl) || (mulcast_enbl)) { + goto exit; /* no need for Multicast Cmd */ + } + + /* get the multicast CB */ + e100_set_multi_exec(dev); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +static int +e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + switch (cmd) { + +#ifdef E100_ETHTOOL_IOCTL + case SIOCETHTOOL: + return e100_do_ethtool_ioctl(dev, ifr); + break; +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef E100_MII_IOCTL + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + case SIOCGMIIREG: /* Read MII PHY register. */ + case SIOCSMIIREG: /* Write to MII PHY register. */ + return e100_mii_ioctl(dev, ifr, cmd); + break; +#endif /*E100_MII_IOCTL */ + + default: + return -EOPNOTSUPP; + } + return 0; + +} + +/** + * e100init - initialize the adapter + * @bdp: atapter's private data struct + * + * This routine is called when this driver is loaded. This is the initialization + * routine which allocates memory, configures the adapter and determines the + * system resources. + * + * Returns: + * true: if successful + * false: otherwise + */ +static unsigned char __devinit +e100_init(struct e100_private *bdp) +{ + e100_sw_init(bdp); + + if (!e100_selftest(bdp, NULL, NULL)) { + printk(KERN_ERR "selftest failed\n"); + return false; + } + + /* read the MAC address from the eprom */ + e100_rd_eaddr(bdp); + /* read NIC's part number */ + e100_rd_pwa_no(bdp); + + if (!e100_hw_init(bdp, PORT_SOFTWARE_RESET)) { + printk(KERN_ERR "hw init failed\n"); + return false; + } + e100_dis_intr(bdp); + + return true; +} + +/** + * e100_sw_init - initialize software structs + * @bdp: atapter's private data struct + * + * This routine initializes all software structures. Sets up the + * circular structures for the RFD's & TCB's. Allocates the per board + * structure for storing adapter information. The CSR is also memory + * mapped in this routine. + * + * Returns : + * true: if S/W was successfully initialized + * false: otherwise + */ +static unsigned char __devinit +e100_sw_init(struct e100_private *bdp) +{ + bdp->next_cu_cmd = START_WAIT; // init the next cu state + + /* + * Set the value for # of good xmits per underrun. the value assigned + * here is an intelligent suggested default. Nothing magical about it. + */ + bdp->tx_per_underrun = DEFAULT_TX_PER_UNDERRUN; + + /* get the default transmit threshold value */ + bdp->tx_thld = TX_THRSHLD; + + /* get the EPROM size */ + bdp->eeprom_size = e100_eeprom_size(bdp); + + /* Initialize our spinlocks */ + spin_lock_init(&(bdp->bd_lock)); + spin_lock_init(&(bdp->bd_non_tx_lock)); + spin_lock_init(&(bdp->config_lock)); + spin_lock_init(&(bdp->mdi_access_lock)); + bdp->isolate_lock = RW_LOCK_UNLOCKED; + bdp->driver_isolated = false; + + return 1; +} + +/** + * e100_hw_init - initialized tthe hardware + * @bdp: atapter's private data struct + * @reset_cmd: s/w reset or selective reset + * + * This routine performs a reset on the adapter, and configures the adapter. + * This includes configuring the 82557 LAN controller, validating and setting + * the node address, detecting and configuring the Phy chip on the adapter, + * and initializing all of the on chip counters. + * + * Returns: + * true - If the adapter was initialized + * false - If the adapter failed initialization + */ +unsigned char __devinit +e100_hw_init(struct e100_private *bdp, u32 reset_cmd) +{ + if (!e100_phy_init(bdp)) + return false; + + /* Issue a software reset to the e100 */ + e100_sw_reset(bdp, reset_cmd); + + /* Load the CU BASE (set to 0, because we use linear mode) */ + if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) + return false; + + if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) + return false; + + /* Load interrupt microcode */ + if (e100_load_microcode(bdp)) { + bdp->flags |= DF_UCODE_LOADED; + } + + e100_config_init(bdp); + if (!e100_config(bdp)) { + return false; + } + + if (!e100_setup_iaaddr(bdp, bdp->device->dev_addr)) + return false; + + /* Clear the internal counters */ + if (!e100_clr_cntrs(bdp)) + return false; + + /* Change for 82558 enhancement */ + /* If 82558/9 and if the user has enabled flow control, set up the + * Flow Control Reg. in the CSR */ + if ((bdp->flags & IS_BACHELOR) + && (bdp->params.b_params & PRM_FC)) { + writeb(DFLT_FC_THLD, &bdp->scb->scb_ext.d101_scb.scb_fc_thld); + writeb(DFLT_FC_CMD, + &bdp->scb->scb_ext.d101_scb.scb_fc_xon_xoff); + } + + return true; +} + +/** + * e100_setup_tcb_pool - setup TCB circular list + * @head: Pointer to head of the allocated TCBs + * @qlen: Number of elements in the queue + * @bdp: atapter's private data struct + * + * This routine arranges the contigiously allocated TCB's in a circular list. + * Also does the one time initialization of the TCBs. + */ +static void +e100_setup_tcb_pool(tcb_t *head, unsigned int qlen, struct e100_private *bdp) +{ + int ele_no; + tcb_t *pcurr_tcb; /* point to current tcb */ + u32 next_phys; /* the next phys addr */ + u16 txcommand = CB_S_BIT | CB_TX_SF_BIT; + + if (bdp->flags & USE_IPCB) { + txcommand |= CB_IPCB_TRANSMIT | CB_CID_DEFAULT; + } else if (bdp->flags & IS_BACHELOR) { + txcommand |= CB_TRANSMIT | CB_CID_DEFAULT; + } else { + txcommand |= CB_TRANSMIT; + } + + for (ele_no = 0, next_phys = bdp->tcb_phys, pcurr_tcb = head; + ele_no < qlen; ele_no++, pcurr_tcb++) { + + /* set the phys addr for this TCB, next_phys has not incr. yet */ + pcurr_tcb->tcb_phys = next_phys; + next_phys += sizeof (tcb_t); + + /* set the link to next tcb */ + if (ele_no == (qlen - 1)) + pcurr_tcb->tcb_hdr.cb_lnk_ptr = + cpu_to_le32(bdp->tcb_phys); + else + pcurr_tcb->tcb_hdr.cb_lnk_ptr = cpu_to_le32(next_phys); + + pcurr_tcb->tcb_hdr.cb_status = 0; + pcurr_tcb->tcb_hdr.cb_cmd = cpu_to_le16(txcommand); + pcurr_tcb->tcb_cnt = 0; + pcurr_tcb->tcb_thrshld = bdp->tx_thld; + if (ele_no < 2) { + pcurr_tcb->tcb_hdr.cb_status = + cpu_to_le16(CB_STATUS_COMPLETE); + } + pcurr_tcb->tcb_tbd_num = 1; + + if (bdp->flags & IS_BACHELOR) { + pcurr_tcb->tcb_tbd_ptr = + __constant_cpu_to_le32(0xFFFFFFFF); + } else { + pcurr_tcb->tcb_tbd_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x10); + } + +#ifdef E100_ZEROCOPY + if (bdp->flags & IS_BACHELOR) { + pcurr_tcb->tcb_tbd_expand_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x20); + } else { + pcurr_tcb->tcb_tbd_expand_ptr = + cpu_to_le32(pcurr_tcb->tcb_phys + 0x10); + } + pcurr_tcb->tcb_tbd_dflt_ptr = pcurr_tcb->tcb_tbd_ptr; +#endif + + if (bdp->flags & USE_IPCB) { + pcurr_tcb->tbd_ptr = &(pcurr_tcb->tcbu.tbd_array[1]); + pcurr_tcb->tcbu.ipcb.ip_activation_high = + IPCB_IP_ACTIVATION_DEFAULT; + pcurr_tcb->tcbu.ipcb.vlan = 0; + } else { + pcurr_tcb->tbd_ptr = &(pcurr_tcb->tcbu.tbd_array[0]); + } + + pcurr_tcb->tcb_skb = NULL; + } + + wmb(); +} + +/***************************************************************************/ +/***************************************************************************/ +/* Memory Management Routines */ +/***************************************************************************/ + +/** + * e100_alloc_space - allocate private driver data + * @bdp: atapter's private data struct + * + * This routine allocates memory for the driver. Memory allocated is for the + * selftest and statistics structures. + * + * Returns: + * 0: if the operation was successful + * %-ENOMEM: if memory allocation failed + */ +unsigned char __devinit +e100_alloc_space(struct e100_private *bdp) +{ + unsigned long off; + + /* allocate all the dma-able structures in one call: + * selftest results, adapter stats, and non-tx cb commands */ + if (!(bdp->dma_able = + pci_alloc_consistent(bdp->pdev, sizeof (bd_dma_able_t), + &(bdp->dma_able_phys)))) { + goto err; + } + + /* now assign the various pointers into the struct we've just allocated */ + off = offsetof(bd_dma_able_t, selftest); + + bdp->selftest = (self_test_t *) (bdp->dma_able + off); + bdp->selftest_phys = bdp->dma_able_phys + off; + + off = offsetof(bd_dma_able_t, stats_counters); + + bdp->stats_counters = (max_counters_t *) (bdp->dma_able + off); + bdp->stat_cnt_phys = bdp->dma_able_phys + off; + + return 0; + +err: + printk(KERN_ERR + "%s - Failed to allocate memory\n", e100_short_driver_name); + return -ENOMEM; +} + +/** + * e100_alloc_tcb_pool - allocate TCB circular list + * @bdp: atapter's private data struct + * + * This routine allocates memory for the circular list of transmit descriptors. + * + * Returns: + * 0: if allocation has failed. + * 1: Otherwise. + */ +int +e100_alloc_tcb_pool(struct e100_private *bdp) +{ + int stcb = sizeof (tcb_t) * bdp->params.TxDescriptors; + + /* allocate space for the TCBs */ + if (!(bdp->tcb_pool.data = + pci_alloc_consistent(bdp->pdev, stcb, &bdp->tcb_phys))) + return 0; + + memset(bdp->tcb_pool.data, 0x00, stcb); + + return 1; +} + +void +e100_free_tcb_pool(struct e100_private *bdp) +{ + pci_free_consistent(bdp->pdev, + sizeof (tcb_t) * bdp->params.TxDescriptors, + bdp->tcb_pool.data, bdp->tcb_phys); + bdp->tcb_phys = 0; +} + +static void +e100_dealloc_space(struct e100_private *bdp) +{ + if (bdp->dma_able) { + pci_free_consistent(bdp->pdev, sizeof (bd_dma_able_t), + bdp->dma_able, bdp->dma_able_phys); + } + + bdp->selftest_phys = 0; + bdp->stat_cnt_phys = 0; + bdp->dma_able_phys = 0; + bdp->dma_able = 0; +} + +static void +e100_free_rfd_pool(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct; + + while (!list_empty(&(bdp->active_rx_list))) { + + rx_struct = list_entry(bdp->active_rx_list.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + pci_unmap_single(bdp->pdev, rx_struct->dma_addr, + sizeof (rfd_t), PCI_DMA_TODEVICE); + dev_kfree_skb(rx_struct->skb); + kfree(rx_struct); + } + + while (!list_empty(&(bdp->rx_struct_pool))) { + rx_struct = list_entry(bdp->rx_struct_pool.next, + struct rx_list_elem, list_elem); + list_del(&(rx_struct->list_elem)); + kfree(rx_struct); + } +} + +/** + * e100_alloc_rfd_pool - allocate RFDs + * @bdp: atapter's private data struct + * + * Allocates initial pool of skb which holds both rfd and data, + * and return a pointer to the head of the list + */ +static int +e100_alloc_rfd_pool(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct; + int i; + + INIT_LIST_HEAD(&(bdp->active_rx_list)); + INIT_LIST_HEAD(&(bdp->rx_struct_pool)); + bdp->skb_req = bdp->params.RxDescriptors; + for (i = 0; i < bdp->skb_req; i++) { + rx_struct = kmalloc(sizeof (struct rx_list_elem), GFP_ATOMIC); + list_add(&(rx_struct->list_elem), &(bdp->rx_struct_pool)); + } + e100_alloc_skbs(bdp); + return !list_empty(&(bdp->active_rx_list)); + +} + +void +e100_clear_pools(struct e100_private *bdp) +{ + bdp->last_tcb = NULL; + e100_free_rfd_pool(bdp); + e100_free_tcb_pool(bdp); +} + +/*****************************************************************************/ +/*****************************************************************************/ +/* Run Time Functions */ +/*****************************************************************************/ + +/** + * e100_watchdog + * @dev: adapter's net_device struct + * + * This routine runs every 2 seconds and updates our statitics and link state, + * and refreshs txthld value. + */ +void +e100_watchdog(struct net_device *dev) +{ + struct e100_private *bdp = dev->priv; + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + if (!netif_running(dev)) { + goto exit; + } + spin_lock_bh(&(bdp->mdi_access_lock)); + + /* check if link state has changed */ + if (e100_phy_check(bdp)) { + if (netif_carrier_ok(dev)) { + printk(KERN_ERR + "e100: %s NIC Link is Up %d Mbps %s duplex\n", + bdp->device->name, bdp->cur_line_speed, + (bdp->cur_dplx_mode == HALF_DUPLEX) ? + "Half" : "Full"); + + e100_config_fc(bdp); + e100_config(bdp); + + } else { + printk(KERN_ERR "e100: %s NIC Link is Down\n", + bdp->device->name); + } + } + + // toggle the tx queue according to link status + // this also resolves a race condition between tx & non-cu cmd flows + if (netif_carrier_ok(dev)) { + if (netif_running(dev)) + netif_wake_queue(dev); + } else { + netif_stop_queue(dev); + } + + rmb(); + + if (e100_update_stats(bdp)) { + + /* Check if a change in the IFS parameter is needed, + and configure the device accordingly */ + if (bdp->params.b_params & PRM_IFS) + e100_manage_adaptive_ifs(bdp); + + /* Now adjust our dynamic tx threshold value */ + e100_refresh_txthld(bdp); + + /* Now if we are on a 557 and we havn't received any frames then we + * should issue a multicast command to reset the RU */ + if (bdp->rev_id < D101A4_REV_ID) { + if (!(bdp->stats_counters->basic_stats.rcv_gd_frames)) { + e100_set_multi(dev); + } + } + + /* Update the statistics needed by the upper interface */ + /* This should be the last statistic related command + * as it's async. now */ + e100_dump_stats_cntrs(bdp); + } + + wmb(); + + spin_unlock_bh(&(bdp->mdi_access_lock)); + + /* relaunch watchdog timer in 2 sec */ + mod_timer(&(bdp->watchdog_timer), jiffies + (2 * HZ)); + + if (list_empty(&bdp->active_rx_list)) + e100_trigger_SWI(bdp); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100_manage_adaptive_ifs + * @bdp: atapter's private data struct + * + * This routine manages the adaptive Inter-Frame Spacing algorithm + * using a state machine. + */ +void +e100_manage_adaptive_ifs(struct e100_private *bdp) +{ + static u16 state_table[9][4] = { // rows are states + {2, 0, 0, 0}, // state0 // column0: next state if increasing + {2, 0, 5, 30}, // state1 // column1: next state if decreasing + {5, 1, 5, 30}, // state2 // column2: IFS value for 100 mbit + {5, 3, 0, 0}, // state3 // column3: IFS value for 10 mbit + {5, 3, 10, 60}, // state4 + {8, 4, 10, 60}, // state5 + {8, 6, 0, 0}, // state6 + {8, 6, 20, 60}, // state7 + {8, 7, 20, 60} // state8 + }; + + u32 transmits = + le32_to_cpu(bdp->stats_counters->basic_stats.xmt_gd_frames); + u32 collisions = + le32_to_cpu(bdp->stats_counters->basic_stats.xmt_ttl_coll); + u32 state = bdp->ifs_state; + u32 old_value = bdp->ifs_value; + int next_col; + u32 min_transmits; + + if (bdp->cur_dplx_mode == FULL_DUPLEX) { + bdp->ifs_state = 0; + bdp->ifs_value = 0; + + } else { /* Half Duplex */ + /* Set speed specific parameters */ + if (bdp->cur_line_speed == 100) { + next_col = 2; + min_transmits = MIN_NUMBER_OF_TRANSMITS_100; + + } else { /* 10 Mbps */ + next_col = 3; + min_transmits = MIN_NUMBER_OF_TRANSMITS_10; + } + + if ((transmits / 32 < collisions) + && (transmits > min_transmits)) { + state = state_table[state][0]; /* increment */ + + } else if (transmits < min_transmits) { + state = state_table[state][1]; /* decrement */ + } + + bdp->ifs_value = state_table[state][next_col]; + bdp->ifs_state = state; + } + + /* If the IFS value has changed, configure the device */ + if (bdp->ifs_value != old_value) { + e100_config_ifs(bdp); + e100_config(bdp); + } +} + +void +e100_polling_tasklet(unsigned long ptr) +{ + struct e100_private *bdp = (struct e100_private *) ptr; + unsigned int rx_congestion = 0; + u32 skb_cnt; + + /* the device is closed, don't continue or else bad things may happen. */ + if (!netif_running(bdp->device)) { + return; + } + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + tasklet_schedule(&(bdp->polling_tasklet)); + goto exit; + } + + e100_alloc_skbs(bdp); + + skb_cnt = e100_rx_srv(bdp, bdp->params.PollingMaxWork, &rx_congestion); + + bdp->drv_stats.rx_tasklet_pkts += skb_cnt; + + if (rx_congestion || skb_cnt) { + tasklet_schedule(&(bdp->polling_tasklet)); + } else { + bdp->intr_mask &= ~SCB_INT_MASK; + + bdp->drv_stats.poll_intr_switch++; + } + + bdp->tx_count = 0; /* restart tx interrupt batch count */ + e100_tx_srv(bdp); + + e100_set_intr_mask(bdp); + +exit: + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100intr - interrupt handler + * @irq: the IRQ number + * @dev_inst: the net_device struct + * @regs: registers (unused) + * + * This routine is the ISR for the e100 board. It services + * the RX & TX queues & starts the RU if it has stopped due + * to no resources. + */ +void +e100intr(int irq, void *dev_inst, struct pt_regs *regs) +{ + struct net_device *dev; + struct e100_private *bdp; + u16 intr_status; + + dev = dev_inst; + bdp = dev->priv; + + intr_status = readw(&bdp->scb->scb_status); + if (!intr_status || (intr_status == 0xffff)) { + return; + } + + /* disable intr before we ack & after identifying the intr as ours */ + e100_dis_intr(bdp); + + writew(intr_status, &bdp->scb->scb_status); /* ack intrs */ + + /* the device is closed, don't continue or else bad things may happen. */ + if (!netif_running(dev)) { + e100_set_intr_mask(bdp); + return; + } + + read_lock(&(bdp->isolate_lock)); + if (bdp->driver_isolated) { + goto exit; + } + + /* SWI intr (triggered by watchdog) is signal to allocate new skb buffers */ + if (intr_status & SCB_STATUS_ACK_SWI) { + e100_alloc_skbs(bdp); + } + + /* do recv work if any */ + if (intr_status & + (SCB_STATUS_ACK_FR | SCB_STATUS_ACK_RNR | SCB_STATUS_ACK_SWI)) { + int rx_congestion; + + bdp->drv_stats.rx_intr_pkts += + e100_rx_srv(bdp, 0, &rx_congestion); + if ((bdp->params.b_params & PRM_RX_CONG) && rx_congestion) { + bdp->intr_mask |= SCB_INT_MASK; + tasklet_schedule(&(bdp->polling_tasklet)); + + bdp->drv_stats.poll_intr_switch++; + } + } + + /* clean up after tx'ed packets */ + if (intr_status & (SCB_STATUS_ACK_CNA | SCB_STATUS_ACK_CX)) { + bdp->tx_count = 0; /* restart tx interrupt batch count */ + e100_tx_srv(bdp); + } + +exit: + e100_set_intr_mask(bdp); + read_unlock(&(bdp->isolate_lock)); +} + +/** + * e100_tx_skb_free - free TX skbs resources + * @bdp: atapter's private data struct + * @tcb: associated tcb of the freed skb + * + * This routine frees resources of TX skbs. + */ +static void inline +e100_tx_skb_free(struct e100_private *bdp, tcb_t *tcb) +{ + if (tcb->tcb_skb) { +#ifdef E100_ZEROCOPY + int i; + tbd_t *tbd_arr = tcb->tbd_ptr; + int frags = skb_shinfo(tcb->tcb_skb)->nr_frags; + + for (i = 0; i <= frags; i++, tbd_arr++) { + pci_unmap_single(bdp->pdev, + le32_to_cpu(tbd_arr->tbd_buf_addr), + le16_to_cpu(tbd_arr->tbd_buf_cnt), + PCI_DMA_TODEVICE); + } +#else + pci_unmap_single(bdp->pdev, + le32_to_cpu((tcb->tbd_ptr)->tbd_buf_addr), + tcb->tcb_skb->len, PCI_DMA_TODEVICE); +#endif + dev_kfree_skb_irq(tcb->tcb_skb); + tcb->tcb_skb = NULL; + } +} + +/** + * e100_tx_srv - service TX queues + * @bdp: atapter's private data struct + * + * This routine services the TX queues. It reclaims the TCB's & TBD's & other + * resources used during the transmit of this buffer. It is called from the ISR. + * We don't need a tx_lock since we always access buffers which were already + * prepared. + */ +void +e100_tx_srv(struct e100_private *bdp) +{ + tcb_t *tcb; + int i; + + /* go over at most TxDescriptors buffers */ + for (i = 0; i < bdp->params.TxDescriptors; i++) { + tcb = bdp->tcb_pool.data; + tcb += bdp->tcb_pool.head; + + rmb(); + + /* if the buffer at 'head' is not complete, break */ + if (!(tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE))) + break; + + /* service next buffer, clear the out of resource condition */ + e100_tx_skb_free(bdp, tcb); + + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + + /* if we've caught up with 'tail', break */ + if (NEXT_TCB_TOUSE(bdp->tcb_pool.head) == bdp->tcb_pool.tail) { + break; + } + + bdp->tcb_pool.head = NEXT_TCB_TOUSE(bdp->tcb_pool.head); + } +} + +/** + * e100_rx_srv - service RX queue + * @bdp: atapter's private data struct + * @max_number_of_rfds: max number of RFDs to process + * @rx_congestion: flag pointer, to inform the calling function of congestion. + * + * This routine processes the RX interrupt & services the RX queues. + * For each successful RFD, it allocates a new msg block, links that + * into the RFD list, and sends the old msg upstream. + * The new RFD is then put at the end of the free list of RFD's. + * It returns the number of serviced RFDs. + */ +u32 +e100_rx_srv(struct e100_private *bdp, u32 max_number_of_rfds, + int *rx_congestion) +{ + rfd_t *rfd; /* new rfd, received rfd */ + int i; + u16 rfd_status; + struct sk_buff *skb; + struct net_device *dev; + unsigned int data_sz; + struct rx_list_elem *rx_struct; + u32 rfd_cnt = 0; + + if (rx_congestion) { + *rx_congestion = 0; + } + + dev = bdp->device; + + /* current design of rx is as following: + * 1. socket buffer (skb) used to pass network packet to upper layer + * 2. all HW host memory structures (like RFDs, RBDs and data buffers) + * are placed in a skb's data room + * 3. when rx process is complete, we change skb internal pointers to exclude + * from data area all unrelated things (RFD, RDB) and to leave + * just rx'ed packet netto + * 4. for each skb passed to upper layer, new one is allocated instead. + * 5. if no skb left, in 2 sec another atempt to allocate skbs will be made + * (watchdog trigger SWI intr and isr should allocate new skbs) + */ + for (i = 0; i < bdp->params.RxDescriptors; i++) { + if (max_number_of_rfds && (rfd_cnt >= max_number_of_rfds)) { + break; + } + if (list_empty(&(bdp->active_rx_list))) + break; + + rx_struct = list_entry(bdp->active_rx_list.next, + struct rx_list_elem, list_elem); + skb = rx_struct->skb; + + rfd = RFD_POINTER(skb, bdp); /* locate RFD within skb */ + + // sync only the RFD header + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + bdp->rfd_size, PCI_DMA_FROMDEVICE); + rfd_status = le16_to_cpu(rfd->rfd_header.cb_status); /* get RFD's status */ + if (!(rfd_status & RFD_STATUS_COMPLETE)) /* does not contains data yet - exit */ + break; + + /* to allow manipulation with current skb we need to unlink it */ + list_del(&(rx_struct->list_elem)); + + /* do not free & unmap badly recieved packet. + * move it to the end of skb list for reuse */ + if (!(rfd_status & RFD_STATUS_OK)) { + e100_add_skb_to_end(bdp, rx_struct); + continue; + } + + data_sz = min_t(u16, (le16_to_cpu(rfd->rfd_act_cnt) & 0x3fff), + (sizeof (rfd_t) - bdp->rfd_size)); + + /* now sync all the data */ + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + (data_sz + bdp->rfd_size), + PCI_DMA_FROMDEVICE); + + // we unmap using DMA_TODEVICE to avoid another memcpy from the + // bounce buffer + pci_unmap_single(bdp->pdev, rx_struct->dma_addr, + sizeof (rfd_t), PCI_DMA_TODEVICE); + + list_add(&(rx_struct->list_elem), &(bdp->rx_struct_pool)); + + /* end of dma access to rfd */ + bdp->skb_req++; /* incr number of requested skbs */ + e100_alloc_skbs(bdp); /* and get them */ + + /* set packet size, excluding checksum (2 last bytes) if it is present */ + if ((bdp->flags & DF_CSUM_OFFLOAD) + && (bdp->rev_id < D102_REV_ID)) + skb_put(skb, (int) data_sz - 2); + else + skb_put(skb, (int) data_sz); + + /* set the protocol */ + skb->protocol = eth_type_trans(skb, dev); + + /* set the checksum info */ + if (bdp->flags & DF_CSUM_OFFLOAD) { + if (bdp->rev_id >= D102_REV_ID) { + skb->ip_summed = e100_D102_check_checksum(rfd); + } else { + skb->ip_summed = e100_D101M_checksum(bdp, skb); + } + } else { + skb->ip_summed = CHECKSUM_NONE; + } + + switch (netif_rx(skb)) { + case NET_RX_BAD: + break; + + case NET_RX_DROP: + case NET_RX_CN_MOD: + case NET_RX_CN_HIGH: + if (bdp->params.b_params & PRM_RX_CONG) { + if (rx_congestion) { + *rx_congestion = 1; + } + } + /* FALL THROUGH TO STATISTICS UPDATE */ + default: + bdp->drv_stats.net_stats.rx_bytes += skb->len; + break; + } + + rfd_cnt++; + } /* end of rfd loop */ + + /* restart the RU if it has stopped */ + if ((readw(&bdp->scb->scb_status) & SCB_RUS_MASK) != SCB_RUS_READY) { + e100_start_ru(bdp); + } + + return rfd_cnt; +} + +void +e100_refresh_txthld(struct e100_private *bdp) +{ + basic_cntr_t *pstat = &(bdp->stats_counters->basic_stats); + + /* as long as tx_per_underrun is not 0, we can go about dynamically * + * adjusting the xmit threshold. we stop doing that & resort to defaults + * * once the adjustments become meaningless. the value is adjusted by * + * dumping the error counters & checking the # of xmit underrun errors * + * we've had. */ + if (bdp->tx_per_underrun) { + /* We are going to last values dumped from the dump statistics + * command */ + if (le32_to_cpu(pstat->xmt_gd_frames)) { + if (le32_to_cpu(pstat->xmt_uruns)) { + /* + * if we have had more than one underrun per "DEFAULT # + * OF XMITS ALLOWED PER UNDERRUN" good xmits, raise the + * THRESHOLD. + */ + if ((le32_to_cpu(pstat->xmt_gd_frames) / + le32_to_cpu(pstat->xmt_uruns)) < + bdp->tx_per_underrun) { + bdp->tx_thld += 3; + } + } + + /* + * if we've had less than one underrun per the DEFAULT number of + * of good xmits allowed, lower the THOLD but not less than 0 + */ + if (le32_to_cpu(pstat->xmt_gd_frames) > + bdp->tx_per_underrun) { + bdp->tx_thld--; + + if (bdp->tx_thld < 6) + bdp->tx_thld = 6; + + } + } + + /* end good xmits */ + /* + * * if our adjustments are becoming unresonable, stop adjusting & + * resort * to defaults & pray. A THOLD value > 190 means that the + * adapter will * wait for 190*8=1520 bytes in TX FIFO before it + * starts xmit. Since * MTU is 1514, it doesn't make any sense for + * further increase. */ + if (bdp->tx_thld >= 190) { + bdp->tx_per_underrun = 0; + bdp->tx_thld = 189; + } + } /* end underrun check */ +} + +/** + * e100_prepare_xmit_buff - prepare a buffer for transmission + * @bdp: atapter's private data struct + * @skb: skb to send + * + * This routine prepare a buffer for transmission. It checks + * the message length for the appropiate size. It picks up a + * free tcb from the TCB pool and sets up the corresponding + * TBD's. If the number of fragments are more than the number + * of TBD/TCB it copies all the fragments in a coalesce buffer. + * It returns a pointer to the prepared TCB. + */ +static inline tcb_t * +e100_prepare_xmit_buff(struct e100_private *bdp, struct sk_buff *skb) +{ + tcb_t *tcb, *prev_tcb; + + tcb = bdp->tcb_pool.data; + tcb += TCB_TO_USE(bdp->tcb_pool); + + tcb->tcb_hdr.cb_status = 0; + tcb->tcb_thrshld = bdp->tx_thld; + tcb->tcb_hdr.cb_cmd |= __constant_cpu_to_le16(CB_S_BIT); + + /* set the I bit on the modulo tcbs, so we will get an interrupt * to + * clean things up */ + if (!(++bdp->tx_count % TX_FRAME_CNT)) { + tcb->tcb_hdr.cb_cmd |= __constant_cpu_to_le16(CB_I_BIT); + } + + tcb->tcb_skb = skb; + +#ifdef E100_ZEROCOPY + if (skb->ip_summed == CHECKSUM_HW) { + const struct iphdr *ip = skb->nh.iph; + + if ((ip->protocol == IPPROTO_TCP) || + (ip->protocol == IPPROTO_UDP)) { + u16 *chksum; + + tcb->tcbu.ipcb.ip_activation_high = + IPCB_HARDWAREPARSING_ENABLE; + tcb->tcbu.ipcb.ip_schedule |= + IPCB_TCPUDP_CHECKSUM_ENABLE; + + if (ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp; + + tcp = (struct tcphdr *) ((u32 *) ip + ip->ihl); + chksum = &(tcp->check); + tcb->tcbu.ipcb.ip_schedule |= IPCB_TCP_PACKET; + } else { + struct udphdr *udp; + + udp = (struct udphdr *) ((u32 *) ip + ip->ihl); + chksum = &(udp->check); + } + + *chksum = csum_tcpudp_magic(ip->daddr, ip->saddr, + sizeof (struct tcphdr), + ip->protocol, 0); + } + } else { + if (bdp->flags & USE_IPCB) { + tcb->tcbu.ipcb.ip_activation_high = + IPCB_IP_ACTIVATION_DEFAULT; + tcb->tcbu.ipcb.ip_schedule &= ~IPCB_TCP_PACKET; + tcb->tcbu.ipcb.ip_schedule &= + ~IPCB_TCPUDP_CHECKSUM_ENABLE; + } + } + + if (!skb_shinfo(skb)->nr_frags) { + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = cpu_to_le16(skb->len); + tcb->tcb_tbd_num = 1; + tcb->tcb_tbd_ptr = tcb->tcb_tbd_dflt_ptr; + } else { + int i; + void *addr; + tbd_t *tbd_arr_ptr = &(tcb->tbd_ptr[1]); + skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; + + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = + cpu_to_le16(skb->len - skb->data_len); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; + i++, tbd_arr_ptr++, frag++) { + + addr = ((void *) page_address(frag->page) + + frag->page_offset); + + tbd_arr_ptr->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, + addr, frag->size, + PCI_DMA_TODEVICE)); + tbd_arr_ptr->tbd_buf_cnt = cpu_to_le16(frag->size); + } + tcb->tcb_tbd_num = skb_shinfo(skb)->nr_frags + 1; + tcb->tcb_tbd_ptr = tcb->tcb_tbd_expand_ptr; + } +#else + (tcb->tbd_ptr)->tbd_buf_addr = + cpu_to_le32(pci_map_single(bdp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE)); + (tcb->tbd_ptr)->tbd_buf_cnt = cpu_to_le16(skb->len); +#endif + + /* clear the S-BIT on the previous tcb */ + prev_tcb = bdp->tcb_pool.data; + prev_tcb += PREV_TCB_USED(bdp->tcb_pool); + prev_tcb->tcb_hdr.cb_cmd &= __constant_cpu_to_le16((u16) ~CB_S_BIT); + + bdp->tcb_pool.tail = NEXT_TCB_TOUSE(bdp->tcb_pool.tail); + + wmb(); + + e100_start_cu(bdp, tcb); + + return tcb; +} + +/* Changed for 82558 enhancement */ +/** + * e100_start_cu - start the adapter's CU + * @bdp: atapter's private data struct + * @tcb: TCB to be transmitted + * + * This routine issues a CU Start or CU Resume command to the 82558/9. + * This routine was added because the prepare_ext_xmit_buff takes advantage + * of the 82558/9's Dynamic TBD chaining feature and has to start the CU as + * soon as the first TBD is ready. + * + * e100_start_cu must be called while holding the tx_lock ! + */ +void +e100_start_cu(struct e100_private *bdp, tcb_t *tcb) +{ + unsigned long lock_flag; + + spin_lock_irqsave(&(bdp->bd_lock), lock_flag); + switch (bdp->next_cu_cmd) { + case RESUME_NO_WAIT: + /*last cu command was a CU_RESMUE if this is a 558 or newer we dont need to + * wait for command word to clear, we reach here only if we are bachlor + */ + e100_exec_cmd(bdp, SCB_CUC_RESUME); + break; + + case RESUME_WAIT: + if ((bdp->flags & IS_ICH) && + (bdp->cur_line_speed == 10) && + (bdp->cur_dplx_mode == HALF_DUPLEX)) { + e100_wait_exec_simple(bdp, SCB_CUC_NOOP); + udelay(1); + } + if ((e100_wait_exec_simple(bdp, SCB_CUC_RESUME)) && + (bdp->flags & IS_BACHELOR) && (!(bdp->flags & IS_ICH))) { + bdp->next_cu_cmd = RESUME_NO_WAIT; + } + break; + + case START_WAIT: + // The last command was a non_tx CU command + if (!e100_wait_cus_idle(bdp)) + printk("%s cu_start: timeout waiting for cu\n", + bdp->device->name); + + if (!e100_wait_exec_cmplx(bdp, (u32) (tcb->tcb_phys), + SCB_CUC_START)) { + printk("%s cu_start: timeout waiting for scb\n", + bdp->device->name); + e100_exec_cmplx(bdp, (u32) (tcb->tcb_phys), + SCB_CUC_START); + } + + bdp->next_cu_cmd = RESUME_WAIT; + + break; + } + + /* save the last tcb */ + bdp->last_tcb = tcb; + + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); +} + +/* ====================================================================== */ +/* hw */ +/* ====================================================================== */ + +/** + * e100_selftest - perform H/W self test + * @bdp: atapter's private data struct + * @st_timeout: address to return timeout value, if fails + * @st_result: address to return selftest result, if fails + * + * This routine will issue PORT Self-test command to test the e100. + * The self-test will fail if the adapter's master-enable bit is not + * set in the PCI Command Register, or if the adapter is not seated + * in a PCI master-enabled slot. we also disable interrupts when the + * command is completed. + * + * Returns: + * true: if adapter passes self_test + * false: otherwise + */ +unsigned char +e100_selftest(struct e100_private *bdp, u32 *st_timeout, u32 *st_result) +{ + u32 selftest_cmd; + + /* initialize the nic state before running test */ + e100_sw_reset(bdp, PORT_SOFTWARE_RESET); + /* Setup the address of the self_test area */ + selftest_cmd = bdp->selftest_phys; + + /* Setup SELF TEST Command Code in D3 - D0 */ + selftest_cmd |= PORT_SELFTEST; + + /* Initialize the self-test signature and results DWORDS */ + bdp->selftest->st_sign = 0; + bdp->selftest->st_result = 0xffffffff; + + /* Do the port command */ + writel(selftest_cmd, &bdp->scb->scb_port); + + /* Wait at least 10 milliseconds for the self-test to complete */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100 + 1); + + /* disable interrupts since the're now enabled */ + e100_dis_intr(bdp); + + rmb(); + + /* if The First Self Test DWORD Still Zero, We've timed out. If the + * second DWORD is not zero then we have an error. */ + if ((bdp->selftest->st_sign == 0) || (bdp->selftest->st_result != 0)) { + + if (st_timeout) + *st_timeout = !(le32_to_cpu(bdp->selftest->st_sign)); + + if (st_result) + *st_result = le32_to_cpu(bdp->selftest->st_result); + + return false; + } + + return true; +} + +/** + * e100_setup_iaaddr - issue IA setup sommand + * @bdp: atapter's private data struct + * @eaddr: new ethernet address + * + * This routine will issue the IA setup command. This command + * will notify the 82557 (e100) of what its individual (node) + * address is. This command will be executed in polled mode. + * + * Returns: + * true: if the IA setup command was successfully issued and completed + * false: otherwise + */ +unsigned char +e100_setup_iaaddr(struct e100_private *bdp, u8 *eaddr) +{ + unsigned int i; + cb_header_t *ntcb_hdr; + unsigned char res; + nxmit_cb_entry_t *cmd; + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + res = false; + goto exit; + } + + ntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + ntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_IA_ADDRESS); + + for (i = 0; i < ETH_ALEN; i++) { + (cmd->non_tx_cmd)->ntcb.setup.ia_addr[i] = eaddr[i]; + } + + res = e100_exec_non_cu_cmd(bdp, cmd); + if (!res) + printk(KERN_WARNING "%s IA setup failed\n", bdp->device->name); + +exit: + return res; +} + +/** + * e100_start_ru - start the RU if needed + * @bdp: atapter's private data struct + * + * This routine checks the status of the 82557's receive unit(RU), + * and starts the RU if it was not already active. However, + * before restarting the RU, the driver gives the RU the buffers + * it freed up during the servicing of the ISR. If there are + * no free buffers to give to the RU, (i.e. we have reached a + * no resource condition) the RU will not be started till the + * next ISR. + */ +void +e100_start_ru(struct e100_private *bdp) +{ + struct rx_list_elem *rx_struct = NULL; + int buffer_found = 0; + struct list_head *entry_ptr; + + list_for_each(entry_ptr, &(bdp->active_rx_list)) { + rx_struct = + list_entry(entry_ptr, struct rx_list_elem, list_elem); + pci_dma_sync_single(bdp->pdev, rx_struct->dma_addr, + bdp->rfd_size, PCI_DMA_FROMDEVICE); + if (!((SKB_RFD_STATUS(rx_struct->skb, bdp) & + __constant_cpu_to_le16(RFD_STATUS_COMPLETE)))) { + buffer_found = 1; + break; + } + } + + /* No available buffers */ + if (!buffer_found) { + return; + } + + spin_lock(&bdp->bd_lock); + + if (!e100_wait_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START)) { + printk("%s start_ru: wait_scb failed\n", bdp->device->name); + e100_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START); + } + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + spin_unlock(&bdp->bd_lock); +} + +/** + * e100_cmd_complete_location + * @bdp: atapter's private data struct + * + * This routine returns a pointer to the location of the command-complete + * DWord in the dump statistical counters area, according to the statistical + * counters mode (557 - basic, 558 - extended, or 559 - TCO mode). + * See e100_config_init() for the setting of the statistical counters mode. + */ +static u32 * +e100_cmd_complete_location(struct e100_private *bdp) +{ + u32 *cmd_complete; + max_counters_t *stats = bdp->stats_counters; + + switch (bdp->stat_mode) { + case E100_EXTENDED_STATS: + cmd_complete = + (u32 *) &(((err_cntr_558_t *) (stats))->cmd_complete); + break; + + case E100_TCO_STATS: + cmd_complete = + (u32 *) &(((err_cntr_559_t *) (stats))->cmd_complete); + break; + + case E100_BASIC_STATS: + default: + cmd_complete = + (u32 *) &(((err_cntr_557_t *) (stats))->cmd_complete); + break; + } + + return cmd_complete; +} + +/** + * e100_clr_cntrs - clear statistics counters + * @bdp: atapter's private data struct + * + * This routine will clear the adapter error statistic counters. + * + * Returns: + * true: if successfully cleared stat counters + * false: otherwise + */ +static unsigned char __devinit +e100_clr_cntrs(struct e100_private *bdp) +{ + volatile u32 *pcmd_complete; + + /* clear the dump counter complete word */ + pcmd_complete = e100_cmd_complete_location(bdp); + *pcmd_complete = 0; + wmb(); + + if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys, SCB_CUC_DUMP_ADDR)) + return false; + + /* wait 10 microseconds for the command to complete */ + udelay(10); + + if (!e100_wait_exec_simple(bdp, SCB_CUC_DUMP_RST_STAT)) + return false; + + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + + return true; +} + +static unsigned char +e100_update_stats(struct e100_private *bdp) +{ + u32 *pcmd_complete; + basic_cntr_t *pstat = &(bdp->stats_counters->basic_stats); + + // check if last dump command completed + pcmd_complete = e100_cmd_complete_location(bdp); + if (*pcmd_complete != le32_to_cpu(DUMP_RST_STAT_COMPLETED) && + *pcmd_complete != le32_to_cpu(DUMP_STAT_COMPLETED)) { + return false; + } + + /* increment the statistics */ + bdp->drv_stats.net_stats.rx_packets += + le32_to_cpu(pstat->rcv_gd_frames); + bdp->drv_stats.net_stats.tx_packets += + le32_to_cpu(pstat->xmt_gd_frames); + bdp->drv_stats.net_stats.rx_dropped += le32_to_cpu(pstat->rcv_rsrc_err); + bdp->drv_stats.net_stats.collisions += le32_to_cpu(pstat->xmt_ttl_coll); + bdp->drv_stats.net_stats.rx_length_errors += + le32_to_cpu(pstat->rcv_shrt_frames); + bdp->drv_stats.net_stats.rx_over_errors += + le32_to_cpu(pstat->rcv_rsrc_err); + bdp->drv_stats.net_stats.rx_crc_errors += + le32_to_cpu(pstat->rcv_crc_errs); + bdp->drv_stats.net_stats.rx_frame_errors += + le32_to_cpu(pstat->rcv_algn_errs); + bdp->drv_stats.net_stats.rx_fifo_errors += + le32_to_cpu(pstat->rcv_oruns); + bdp->drv_stats.net_stats.tx_aborted_errors += + le32_to_cpu(pstat->xmt_max_coll); + bdp->drv_stats.net_stats.tx_carrier_errors += + le32_to_cpu(pstat->xmt_lost_crs); + bdp->drv_stats.net_stats.tx_fifo_errors += + le32_to_cpu(pstat->xmt_uruns); + + bdp->drv_stats.tx_late_col += le32_to_cpu(pstat->xmt_late_coll); + bdp->drv_stats.tx_ok_defrd += le32_to_cpu(pstat->xmt_deferred); + bdp->drv_stats.tx_one_retry += le32_to_cpu(pstat->xmt_sngl_coll); + bdp->drv_stats.tx_mt_one_retry += le32_to_cpu(pstat->xmt_mlt_coll); + bdp->drv_stats.rcv_cdt_frames += le32_to_cpu(pstat->rcv_err_coll); + + if (bdp->stat_mode != E100_BASIC_STATS) { + ext_cntr_t *pex_stat = &bdp->stats_counters->extended_stats; + + bdp->drv_stats.xmt_fc_pkts += + le32_to_cpu(pex_stat->xmt_fc_frames); + bdp->drv_stats.rcv_fc_pkts += + le32_to_cpu(pex_stat->rcv_fc_frames); + bdp->drv_stats.rcv_fc_unsupported += + le32_to_cpu(pex_stat->rcv_fc_unsupported); + } + + if (bdp->stat_mode == E100_TCO_STATS) { + tco_cntr_t *ptco_stat = &bdp->stats_counters->tco_stats; + + bdp->drv_stats.xmt_tco_pkts += + le16_to_cpu(ptco_stat->xmt_tco_frames); + bdp->drv_stats.rcv_tco_pkts += + le16_to_cpu(ptco_stat->rcv_tco_frames); + } + + *pcmd_complete = 0; + return true; +} + +/** + * e100_dump_stat_cntrs + * @bdp: atapter's private data struct + * + * This routine will dump the board statistical counters without waiting + * for stat_dump to complete. Any access to this stats should verify the completion + * of the command + */ +void +e100_dump_stats_cntrs(struct e100_private *bdp) +{ + unsigned long lock_flag_bd; + + spin_lock_irqsave(&(bdp->bd_lock), lock_flag_bd); + + /* dump h/w stats counters */ + if (e100_wait_exec_simple(bdp, SCB_CUC_DUMP_RST_STAT)) { + if (bdp->next_cu_cmd == RESUME_NO_WAIT) { + bdp->next_cu_cmd = RESUME_WAIT; + } + } + + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag_bd); +} + +/** + * e100_exec_non_cu_cmd + * @bdp: atapter's private data struct + * @command: the non-cu command to execute + * + * This routine will submit a command block to be executed, + */ +unsigned char +e100_exec_non_cu_cmd(struct e100_private *bdp, nxmit_cb_entry_t *command) +{ + cb_header_t *ntcb_hdr; + unsigned long lock_flag; + unsigned long expiration_time; + unsigned char rc = false; + + ntcb_hdr = (cb_header_t *) command->non_tx_cmd; /* get hdr of non tcb cmd */ + + /* Set the Command Block to be the last command block */ + ntcb_hdr->cb_cmd |= __constant_cpu_to_le16(CB_EL_BIT); + ntcb_hdr->cb_status = 0; + ntcb_hdr->cb_lnk_ptr = 0; + + wmb(); + + if (in_interrupt()) + return e100_delayed_exec_non_cu_cmd(bdp, command); + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + if (bdp->non_tx_command_state != E100_NON_TX_IDLE) { + goto delayed_exec; + } + + if (bdp->last_tcb) { + rmb(); + if ((bdp->last_tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + goto delayed_exec; + } + + if ((readw(&bdp->scb->scb_status) & SCB_CUS_MASK) == SCB_CUS_ACTIVE) { + goto delayed_exec; + } + + spin_lock_irqsave(&bdp->bd_lock, lock_flag); + + if (!e100_wait_exec_cmplx(bdp, command->dma_addr, SCB_CUC_START)) { + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); + goto exit; + } + + bdp->next_cu_cmd = START_WAIT; + spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag); + + /* now wait for completion of non-cu CB up to 20 msec*/ + expiration_time = jiffies + HZ / 50 + 1; + while (time_before(jiffies, expiration_time)) { + rmb(); + if ((ntcb_hdr->cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE))) { + rc = true; + goto exit; + } + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_bh(&(bdp->bd_non_tx_lock)); + } + + /* didn't get a C bit assume command failed */ +exit: + e100_free_non_tx_cmd(bdp, command); + + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return rc; + +delayed_exec: + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return e100_delayed_exec_non_cu_cmd(bdp, command); +} + +/** + * e100_sw_reset + * @bdp: atapter's private data struct + * @reset_cmd: s/w reset or selective reset + * + * This routine will issue a software reset to the adapter. It + * will also disable interrupts, as the are enabled after reset. + */ +void +e100_sw_reset(struct e100_private *bdp, u32 reset_cmd) +{ + /* Do a selective reset first to avoid a potential PCI hang */ + writel(PORT_SELECTIVE_RESET, &bdp->scb->scb_port); + + /* wait for the reset to take effect */ + udelay(20); + if (reset_cmd == PORT_SOFTWARE_RESET) { + writel(PORT_SOFTWARE_RESET, &bdp->scb->scb_port); + + /* wait 20 micro seconds for the reset to take effect */ + udelay(20); + } + + /* Mask off our interrupt line -- its unmasked after reset */ + e100_dis_intr(bdp); +} + +/** + * e100_load_microcode - Download microsocde to controller. + * @bdp: atapter's private data struct + * + * This routine downloads microcode on to the controller. This + * microcode is available for the 82558/9, 82550. Currently the + * microcode handles interrupt bundling and TCO workaround. + * + * Returns: + * true: if successfull + * false: otherwise + */ +static unsigned char +e100_load_microcode(struct e100_private *bdp) +{ + static struct { + u8 rev_id; + u32 ucode[UCODE_MAX_DWORDS + 1]; + int timer_dword; + int bundle_dword; + int min_size_dword; + } ucode_opts[] = { + { D101A4_REV_ID, + D101_A_RCVBUNDLE_UCODE, + D101_CPUSAVER_TIMER_DWORD, + D101_CPUSAVER_BUNDLE_DWORD, + D101_CPUSAVER_MIN_SIZE_DWORD }, + { D101B0_REV_ID, + D101_B0_RCVBUNDLE_UCODE, + D101_CPUSAVER_TIMER_DWORD, + D101_CPUSAVER_BUNDLE_DWORD, + D101_CPUSAVER_MIN_SIZE_DWORD }, + { D101MA_REV_ID, + D101M_B_RCVBUNDLE_UCODE, + D101M_CPUSAVER_TIMER_DWORD, + D101M_CPUSAVER_BUNDLE_DWORD, + D101M_CPUSAVER_MIN_SIZE_DWORD }, + { D101S_REV_ID, + D101S_RCVBUNDLE_UCODE, + D101S_CPUSAVER_TIMER_DWORD, + D101S_CPUSAVER_BUNDLE_DWORD, + D101S_CPUSAVER_MIN_SIZE_DWORD }, + { D102_REV_ID, + D102_B_RCVBUNDLE_UCODE, + D102_B_CPUSAVER_TIMER_DWORD, + D102_B_CPUSAVER_BUNDLE_DWORD, + D102_B_CPUSAVER_MIN_SIZE_DWORD }, + { D102C_REV_ID, + D102_C_RCVBUNDLE_UCODE, + D102_C_CPUSAVER_TIMER_DWORD, + D102_C_CPUSAVER_BUNDLE_DWORD, + D102_C_CPUSAVER_MIN_SIZE_DWORD }, + { D102E_REV_ID, + D102_E_RCVBUNDLE_UCODE, + D102_E_CPUSAVER_TIMER_DWORD, + D102_E_CPUSAVER_BUNDLE_DWORD, + D102_E_CPUSAVER_MIN_SIZE_DWORD }, + { 0, {0}, 0, 0, 0} + }, *opts; + + opts = ucode_opts; + + /* User turned ucode loading off */ + if (!(bdp->params.b_params & PRM_UCODE)) + return false; + + /* These controllers do not need ucode */ + if (bdp->flags & IS_ICH) + return false; + + /* Search for ucode match against h/w rev_id */ + while (opts->rev_id) { + if (bdp->rev_id == opts->rev_id) { + int i; + u32 *ucode_dword; + load_ucode_cb_t *ucode_cmd_ptr; + nxmit_cb_entry_t *cmd = e100_alloc_non_tx_cmd(bdp); + + if (cmd != NULL) { + ucode_cmd_ptr = + (load_ucode_cb_t *) cmd->non_tx_cmd; + ucode_dword = ucode_cmd_ptr->ucode_dword; + } else { + return false; + } + + memcpy(ucode_dword, opts->ucode, sizeof (opts->ucode)); + + /* Insert user-tunable settings */ + ucode_dword[opts->timer_dword] &= 0xFFFF0000; + ucode_dword[opts->timer_dword] |= + (u16) bdp->params.IntDelay; + ucode_dword[opts->bundle_dword] &= 0xFFFF0000; + ucode_dword[opts->bundle_dword] |= + (u16) bdp->params.BundleMax; + ucode_dword[opts->min_size_dword] &= 0xFFFF0000; + ucode_dword[opts->min_size_dword] |= + (bdp->params.b_params & PRM_BUNDLE_SMALL) ? + 0xFFFF : 0xFF80; + + for (i = 0; i < UCODE_MAX_DWORDS; i++) + cpu_to_le32s(&(ucode_dword[i])); + + ucode_cmd_ptr->load_ucode_cbhdr.cb_cmd = + __constant_cpu_to_le16(CB_LOAD_MICROCODE); + + return e100_exec_non_cu_cmd(bdp, cmd); + } + opts++; + } + + return false; +} + +/***************************************************************************/ +/***************************************************************************/ +/* EEPROM Functions */ +/***************************************************************************/ + +/* Read PWA (printed wired assembly) number */ +void __devinit +e100_rd_pwa_no(struct e100_private *bdp) +{ + bdp->pwa_no = e100_eeprom_read(bdp, EEPROM_PWA_NO); + bdp->pwa_no <<= 16; + bdp->pwa_no |= e100_eeprom_read(bdp, EEPROM_PWA_NO + 1); +} + +/* Read the permanent ethernet address from the eprom. */ +void __devinit +e100_rd_eaddr(struct e100_private *bdp) +{ + int i; + u16 eeprom_word; + + for (i = 0; i < 6; i += 2) { + eeprom_word = + e100_eeprom_read(bdp, + EEPROM_NODE_ADDRESS_BYTE_0 + (i / 2)); + + bdp->device->dev_addr[i] = + bdp->perm_node_address[i] = (u8) eeprom_word; + bdp->device->dev_addr[i + 1] = + bdp->perm_node_address[i + 1] = (u8) (eeprom_word >> 8); + } +} + +/* Check the D102 RFD flags to see if the checksum passed */ +static unsigned char +e100_D102_check_checksum(rfd_t *rfd) +{ + if (((le16_to_cpu(rfd->rfd_header.cb_status)) & RFD_PARSE_BIT) + && (((rfd->rcvparserstatus & CHECKSUM_PROTOCOL_MASK) == + RFD_TCP_PACKET) + || ((rfd->rcvparserstatus & CHECKSUM_PROTOCOL_MASK) == + RFD_UDP_PACKET)) + && (rfd->checksumstatus & TCPUDP_CHECKSUM_BIT_VALID) + && (rfd->checksumstatus & TCPUDP_CHECKSUM_VALID)) { + return CHECKSUM_UNNECESSARY; + } + return CHECKSUM_NONE; +} + +/** + * e100_D101M_checksum + * @bdp: atapter's private data struct + * @skb: skb received + * + * Sets the skb->csum value from D101 csum found at the end of the Rx frame. The + * D101M sums all words in frame excluding the ethernet II header (14 bytes) so + * in case the packet is ethernet II and the protocol is IP, all is need is to + * assign this value to skb->csum. + */ +static unsigned char +e100_D101M_checksum(struct e100_private *bdp, struct sk_buff *skb) +{ + unsigned short proto = (skb->protocol); + + if (proto == __constant_htons(ETH_P_IP)) { + + skb->csum = get_unaligned((u16 *) (skb->tail)); + return CHECKSUM_HW; + } + return CHECKSUM_NONE; +} + +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ +/* Auxilary Functions */ +/***************************************************************************/ + +/* Print the board's configuration */ +void __devinit +e100_print_brd_conf(struct e100_private *bdp) +{ + if (netif_carrier_ok(bdp->device)) { + printk(KERN_NOTICE + " Mem:0x%08lx IRQ:%d Speed:%d Mbps Dx:%s\n", + (unsigned long) bdp->device->mem_start, + bdp->device->irq, bdp->cur_line_speed, + (bdp->cur_dplx_mode == FULL_DUPLEX) ? "Full" : "Half"); + } else { + printk(KERN_NOTICE + " Mem:0x%08lx IRQ:%d Speed:%d Mbps Dx:%s\n", + (unsigned long) bdp->device->mem_start, + bdp->device->irq, 0, "N/A"); + + /* Auto negotiation failed so we should display an error */ + printk(KERN_NOTICE " Failed to detect cable link\n"); + printk(KERN_NOTICE " Speed and duplex will be determined " + "at time of connection\n"); + } + + /* Print the string if checksum Offloading was enabled */ + if (bdp->flags & DF_CSUM_OFFLOAD) + printk(KERN_NOTICE " Hardware receive checksums enabled\n"); + else + printk(KERN_NOTICE " Hardware receive checksums disabled\n"); + + if ((bdp->flags & DF_UCODE_LOADED)) + printk(KERN_NOTICE " cpu cycle saver enabled\n"); +} + +/** + * e100_get_brand_msg + * @bdp: atapter's private data struct + * + * This routine checks if there is specified branding message for a given board + * type and returns a pointer to the string containing the branding message. + */ +char * +e100_get_brand_msg(struct e100_private *bdp) +{ + int i; + + for (i = 0; e100_vendor_info_array[i].idstr != NULL; i++) { + if (e100_vendor_info_array[i].device_type == bdp->device_type) { + return e100_vendor_info_array[i].idstr; + } + } + + return e100_vendor_info_array[E100_ALL_BOARDS].idstr; +} + +/** + * e100_pci_setup - setup the adapter's PCI information + * @pcid: adapter's pci_dev struct + * @bdp: atapter's private data struct + * + * This routine sets up all PCI information for the adapter. It enables the bus + * master bit (some BIOS don't do this), requests memory ans I/O regions, and + * calls ioremap() on the adapter's memory region. + * + * Returns: + * true: if successfull + * false: otherwise + */ +static unsigned char __devinit +e100_pci_setup(struct pci_dev *pcid, struct e100_private *bdp) +{ + struct net_device *dev = bdp->device; + int rc = 0; + + if ((rc = pci_enable_device(pcid)) != 0) { + goto err; + } + + /* dev and ven ID have already been checked so it is our device */ + pci_read_config_byte(pcid, PCI_REVISION_ID, (u8 *) &(bdp->rev_id)); + + /* address #0 is a memory region */ + dev->mem_start = pci_resource_start(pcid, 0); + dev->mem_end = dev->mem_start + sizeof (scb_t); + + /* address #1 is a IO region */ + dev->base_addr = pci_resource_start(pcid, 1); + + if ((rc = pci_request_regions(pcid, e100_short_driver_name)) != 0) { + goto err_disable; + } + + pci_enable_wake(pcid, 0, 0); + + /* if Bus Mastering is off, turn it on! */ + pci_set_master(pcid); + + /* address #0 is a memory mapping */ + bdp->scb = (scb_t *) ioremap_nocache(dev->mem_start, sizeof (scb_t)); + + if (!bdp->scb) { + printk(KERN_ERR "%s - %s: Failed to map PCI address 0x%lX\n", + e100_short_driver_name, dev->name, + pci_resource_start(pcid, 0)); + rc = -ENOMEM; + goto err_region; + } + + return 0; + +err_region: + pci_release_regions(pcid); +err_disable: + pci_disable_device(pcid); +err: + return rc; +} + +void +e100_isolate_driver(struct e100_private *bdp) +{ + write_lock_irq(&(bdp->isolate_lock)); + bdp->driver_isolated = true; + write_unlock_irq(&(bdp->isolate_lock)); + + del_timer_sync(&bdp->watchdog_timer); + + netif_stop_queue(bdp->device); + + bdp->last_tcb = NULL; + + e100_sw_reset(bdp, PORT_SELECTIVE_RESET); +} + +#ifdef E100_ETHTOOL_IOCTL +static int +e100_do_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) +{ + struct ethtool_cmd ecmd; + int rc = -EOPNOTSUPP; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd.cmd))) + return -EFAULT; + + switch (ecmd.cmd) { + case ETHTOOL_GSET: + rc = e100_ethtool_get_settings(dev, ifr); + break; + case ETHTOOL_SSET: + rc = e100_ethtool_set_settings(dev, ifr); + break; +#ifdef ETHTOOL_GDRVINFO + case ETHTOOL_GDRVINFO: + rc = e100_ethtool_get_drvinfo(dev, ifr); + break; +#endif +#ifdef ETHTOOL_NWAY_RST + case ETHTOOL_NWAY_RST: + rc = e100_ethtool_nway_rst(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GLINK + case ETHTOOL_GLINK: + rc = e100_ethtool_glink(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GEEPROM + case ETHTOOL_GEEPROM: + case ETHTOOL_SEEPROM: + rc = e100_ethtool_eeprom(dev, ifr); + break; +#endif +#ifdef ETHTOOL_GWOL + case ETHTOOL_GWOL: + case ETHTOOL_SWOL: + rc = e100_ethtool_wol(dev, ifr); + break; +#endif + default: + break; + } //switch + return rc; +} + +static int +e100_ethtool_get_settings(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_cmd ecmd; + u16 advert = 0; + + memset((void *) &ecmd, 0, sizeof (ecmd)); + + bdp = dev->priv; + + ecmd.supported = bdp->speed_duplex_caps; + + ecmd.port = + (bdp->speed_duplex_caps & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = bdp->phy_addr; + + ecmd.speed = bdp->cur_line_speed; + ecmd.duplex = + (bdp->cur_dplx_mode == HALF_DUPLEX) ? DUPLEX_HALF : DUPLEX_FULL; + + ecmd.advertising = ADVERTISED_TP; + + if (bdp->params.e100_speed_duplex == E100_AUTONEG) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.advertising |= ADVERTISED_Autoneg; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + } + + if (bdp->speed_duplex_caps & SUPPORTED_MII) { + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &advert); + spin_unlock_bh(&(bdp->mdi_access_lock)); + + if (advert & ADVERTISE_10HALF) + ecmd.advertising |= ADVERTISED_10baseT_Half; + if (advert & ADVERTISE_10FULL) + ecmd.advertising |= ADVERTISED_10baseT_Full; + if (advert & ADVERTISE_100HALF) + ecmd.advertising |= ADVERTISED_100baseT_Half; + if (advert & ADVERTISE_100FULL) + ecmd.advertising |= ADVERTISED_100baseT_Full; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.advertising &= ~ADVERTISED_Autoneg; + } + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + + return 0; +} + +static int +e100_ethtool_set_settings(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + int current_duplex; + int e100_new_speed_duplex; + int ethtool_new_speed_duplex; + int speed_duplex_change_required; + struct ethtool_cmd ecmd; + + if (!capable(CAP_NET_ADMIN)) { + return -EPERM; + } + + bdp = dev->priv; + if (netif_running(dev)) { + return -EBUSY; + } + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) { + return -EFAULT; + } + current_duplex = + (bdp->cur_dplx_mode == HALF_DUPLEX) ? DUPLEX_HALF : DUPLEX_FULL; + speed_duplex_change_required = (ecmd.speed != bdp->cur_line_speed) + || (ecmd.duplex != current_duplex); + + if ((ecmd.autoneg == AUTONEG_ENABLE) && speed_duplex_change_required) { + return -EINVAL; + } + + if ((ecmd.autoneg == AUTONEG_ENABLE) + && (bdp->speed_duplex_caps & SUPPORTED_Autoneg)) { + bdp->params.e100_speed_duplex = E100_AUTONEG; + e100_set_speed_duplex(bdp); + } else { + if (speed_duplex_change_required) { + if (ecmd.speed == SPEED_10) { + if (ecmd.duplex == DUPLEX_HALF) { + e100_new_speed_duplex = + E100_SPEED_10_HALF; + ethtool_new_speed_duplex = + SUPPORTED_10baseT_Half; + + } else { + e100_new_speed_duplex = + E100_SPEED_10_FULL; + ethtool_new_speed_duplex = + SUPPORTED_10baseT_Full; + } + + } else { + if (ecmd.duplex == DUPLEX_HALF) { + e100_new_speed_duplex = + E100_SPEED_100_HALF; + ethtool_new_speed_duplex = + SUPPORTED_100baseT_Half; + + } else { + e100_new_speed_duplex = + E100_SPEED_100_FULL; + ethtool_new_speed_duplex = + SUPPORTED_100baseT_Full; + } + } + + if (bdp->speed_duplex_caps & ethtool_new_speed_duplex) { + bdp->params.e100_speed_duplex = + e100_new_speed_duplex; + e100_set_speed_duplex(bdp); + } else { + return -EOPNOTSUPP; + } + } + } + + return 0; +} + +#ifdef ETHTOOL_GLINK +static int +e100_ethtool_glink(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_value info; + + memset((void *) &info, 0, sizeof (info)); + + bdp = dev->priv; + info.cmd = ETHTOOL_GLINK; + + spin_lock_bh(&(bdp->mdi_access_lock)); + info.data = e100_get_link_state(bdp); + spin_unlock_bh(&(bdp->mdi_access_lock)); + + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + return -EFAULT; + + return 0; +} +#endif + +#ifdef ETHTOOL_NWAY_RST +static int +e100_ethtool_nway_rst(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if ((bdp->speed_duplex_caps & SUPPORTED_Autoneg) && + (bdp->params.e100_speed_duplex == E100_AUTONEG)) { + e100_set_speed_duplex(bdp); + } else { + return -EFAULT; + } + return 0; +} +#endif + +#ifdef ETHTOOL_GDRVINFO +static int +e100_ethtool_get_drvinfo(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_drvinfo info; + + memset((void *) &info, 0, sizeof (info)); + + bdp = dev->priv; + + strncpy(info.driver, e100_short_driver_name, sizeof (info.driver) - 1); + strncpy(info.version, e100_version, sizeof (info.version) - 1); + strncpy(info.fw_version, e100_get_brand_msg(bdp), + sizeof (info.fw_version) - 1); + strncpy(info.bus_info, bdp->pdev->slot_name, + sizeof (info.bus_info) - 1); +#ifdef ETHTOOL_GEEPROM + info.eedump_len = (bdp->eeprom_size << 1); +#endif + + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + return -EFAULT; + + return 0; +} +#endif //ETHTOOL_GDRVINFO + +#ifdef ETHTOOL_GEEPROM +static int +e100_ethtool_eeprom(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_eeprom ecmd; + u16 eeprom_data[256]; + u16 *usr_eeprom_ptr; + u16 word_length, word_offset; + int i; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) + return -EFAULT; + + usr_eeprom_ptr = + (u16 *) (ifr->ifr_data + offsetof(struct ethtool_eeprom, data)); + + word_offset = (ecmd.offset >> 1); + if (word_offset >= bdp->eeprom_size) + return -EFAULT; + + word_length = + min_t(u32, (ecmd.len >> 1), (bdp->eeprom_size - word_offset)); + + if (ecmd.cmd == ETHTOOL_GEEPROM) { + for (i = word_offset; i < (word_length + word_offset); i++) + eeprom_data[i] = e100_eeprom_read(bdp, i); + + ecmd.len = (word_length << 1); + ecmd.magic = E100_EEPROM_MAGIC; + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + + if (copy_to_user(usr_eeprom_ptr, &(eeprom_data[word_offset]), + (ecmd.len << 1))) + return -EFAULT; + } else { + if (ecmd.magic != E100_EEPROM_MAGIC) + return -EFAULT; + + if (copy_from_user(&(eeprom_data[word_offset]), usr_eeprom_ptr, + (ecmd.len << 1))) + return -EFAULT; + + e100_eeprom_write_block(bdp, word_offset, + &(eeprom_data[word_offset]), + word_length); + + ecmd.len = (word_length << 1); + + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + } + return 0; +} +#endif + +static inline int __devinit +e100_10BaseT_adapter(struct e100_private *bdp) +{ + return ((bdp->pdev->device == 0x1229) && + (bdp->pdev->subsystem_vendor == 0x8086) && + (bdp->pdev->subsystem_device == 0x0003)); +} + +static void __devinit +e100_get_speed_duplex_caps(struct e100_private *bdp) +{ + u16 status; + + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + + bdp->speed_duplex_caps = 0; + + bdp->speed_duplex_caps |= + (status & BMSR_ANEGCAPABLE) ? SUPPORTED_Autoneg : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_10HALF) ? SUPPORTED_10baseT_Half : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_10FULL) ? SUPPORTED_10baseT_Full : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_100HALF) ? SUPPORTED_100baseT_Half : 0; + + bdp->speed_duplex_caps |= + (status & BMSR_100FULL) ? SUPPORTED_100baseT_Full : 0; + + if (IS_NC3133(bdp)) + bdp->speed_duplex_caps = + (SUPPORTED_FIBRE | SUPPORTED_100baseT_Full); + else + bdp->speed_duplex_caps |= SUPPORTED_TP; + + if ((status == 0xFFFF) && e100_10BaseT_adapter(bdp)) { + bdp->speed_duplex_caps = + (SUPPORTED_10baseT_Half | SUPPORTED_TP); + } else { + bdp->speed_duplex_caps |= SUPPORTED_MII; + } + +} + +static void +e100_set_speed_duplex(struct e100_private *bdp) +{ + e100_phy_set_speed_duplex(bdp, true); + e100_config_fc(bdp); /* re-config flow-control if necessary */ + e100_config(bdp); +} + +#ifdef ETHTOOL_GWOL +static unsigned char +e100_setup_filter(struct e100_private *bdp) +{ + cb_header_t *ntcb_hdr; + unsigned char res = false; + nxmit_cb_entry_t *cmd; + + if ((cmd = e100_alloc_non_tx_cmd(bdp)) == NULL) { + goto exit; + } + + ntcb_hdr = (cb_header_t *) cmd->non_tx_cmd; + ntcb_hdr->cb_cmd = __constant_cpu_to_le16(CB_LOAD_FILTER); + + /* Set EL and FIX bit */ + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] = + __constant_cpu_to_le32(CB_FILTER_EL | CB_FILTER_FIX); + + if (bdp->wolopts & WAKE_UCAST) { + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] |= + __constant_cpu_to_le32(CB_FILTER_IA_MATCH); + } + + if (bdp->wolopts & WAKE_ARP) { + /* Setup ARP bit and lower IP parts */ + /* bdp->ip_lbytes contains 2 lower bytes of IP address in network byte order */ + (cmd->non_tx_cmd)->ntcb.filter.filter_data[0] |= + cpu_to_le32(CB_FILTER_ARP | bdp->ip_lbytes); + } + + res = e100_exec_non_cu_cmd(bdp, cmd); + if (!res) + printk(KERN_WARNING "%s Filter setup failed\n", + bdp->device->name); + +exit: + return res; + +} + +static void +e100_do_wol(struct pci_dev *pcid, struct e100_private *bdp) +{ + int enable = 0; + u32 state = 0; + + if (bdp->wolopts) { + e100_config_wol(bdp); + + if (!e100_config(bdp)) { + printk("e100_config WOL options failed\n"); + goto exit; + } + + if (bdp->wolopts & (WAKE_UCAST | WAKE_ARP)) { + if (!e100_setup_filter(bdp)) { + printk("e100_config WOL options failed\n"); + goto exit; + } + state = 1; + pci_set_power_state(pcid, state); + } + enable = 1; + } +exit: + pci_enable_wake(pcid, state, enable); +} + +static u16 +e100_get_ip_lbytes(struct net_device *dev) +{ + struct in_ifaddr *ifa; + struct in_device *in_dev; + u32 res = 0; + + in_dev = (struct in_device *) dev->ip_ptr; + /* Check if any in_device bound to interface */ + if (in_dev) { + /* Check if any IP address is bound to interface */ + if ((ifa = in_dev->ifa_list) != NULL) { + res = __constant_ntohl(ifa->ifa_address); + res = __constant_htons(res & 0x0000ffff); + } + } + return res; +} + +static int +e100_ethtool_wol(struct net_device *dev, struct ifreq *ifr) +{ + struct e100_private *bdp; + struct ethtool_wolinfo wolinfo; + int res = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bdp = dev->priv; + + if (copy_from_user(&wolinfo, ifr->ifr_data, sizeof (wolinfo))) { + return -EFAULT; + } + + switch (wolinfo.cmd) { + case ETHTOOL_GWOL: + wolinfo.supported = bdp->wolsupported; + wolinfo.wolopts = bdp->wolopts; + if (copy_to_user(ifr->ifr_data, &wolinfo, sizeof (wolinfo))) + res = -EFAULT; + break; + + case ETHTOOL_SWOL: + /* If ALL requests are supported or request is DISABLE wol */ + if (((wolinfo.wolopts & bdp->wolsupported) == wolinfo.wolopts) + || (wolinfo.wolopts == 0)) { + bdp->wolopts = wolinfo.wolopts; + } else { + res = -EOPNOTSUPP; + } + break; + default: + break; + } + return res; +} + +#endif +#endif /*E100_ETHTOOL_IOCTL */ + +#ifdef E100_MII_IOCTL +static int +e100_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct e100_private *bdp; + struct mii_ioctl_data *data_ptr = + (struct mii_ioctl_data *) &(ifr->ifr_data); + + bdp = dev->priv; + + switch (cmd) { + case SIOCGMIIPHY: + data_ptr->phy_id = bdp->phy_addr & 0x1f; + break; + + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_read(bdp, data_ptr->reg_num & 0x1f, bdp->phy_addr, + &(data_ptr->val_out)); + spin_unlock_bh(&(bdp->mdi_access_lock)); + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (netif_running(dev)) { + return -EBUSY; + } + spin_lock_bh(&(bdp->mdi_access_lock)); + e100_mdi_write(bdp, data_ptr->reg_num & 0x1f, bdp->phy_addr, + data_ptr->val_in); + spin_unlock_bh(&(bdp->mdi_access_lock)); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} +#endif //E100_MII_IOCTL + +nxmit_cb_entry_t * +e100_alloc_non_tx_cmd(struct e100_private *bdp) +{ + nxmit_cb_entry_t *non_tx_cmd_elem; + + if (!(non_tx_cmd_elem = (nxmit_cb_entry_t *) + kmalloc(sizeof (nxmit_cb_entry_t), GFP_ATOMIC))) { + return NULL; + } + non_tx_cmd_elem->non_tx_cmd = + pci_alloc_consistent(bdp->pdev, sizeof (nxmit_cb_t), + &(non_tx_cmd_elem->dma_addr)); + if (non_tx_cmd_elem->non_tx_cmd == NULL) { + kfree(non_tx_cmd_elem); + return NULL; + } + return non_tx_cmd_elem; +} + +void +e100_free_non_tx_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *non_tx_cmd_elem) +{ + pci_free_consistent(bdp->pdev, sizeof (nxmit_cb_t), + non_tx_cmd_elem->non_tx_cmd, + non_tx_cmd_elem->dma_addr); + kfree(non_tx_cmd_elem); +} + +static void +e100_free_nontx_list(struct e100_private *bdp) +{ + nxmit_cb_entry_t *command; + int i; + + while (!list_empty(&bdp->non_tx_cmd_list)) { + command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + list_del(&(command->list_elem)); + e100_free_non_tx_cmd(bdp, command); + } + + for (i = 0; i < CB_MAX_NONTX_CMD; i++) { + bdp->same_cmd_entry[i] = NULL; + } +} + +static unsigned char +e100_delayed_exec_non_cu_cmd(struct e100_private *bdp, + nxmit_cb_entry_t *command) +{ + nxmit_cb_entry_t *same_command; + cb_header_t *ntcb_hdr; + u16 cmd; + + ntcb_hdr = (cb_header_t *) command->non_tx_cmd; + + cmd = CB_CMD_MASK & le16_to_cpu(ntcb_hdr->cb_cmd); + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + same_command = bdp->same_cmd_entry[cmd]; + + if (same_command != NULL) { + memcpy((void *) (same_command->non_tx_cmd), + (void *) (command->non_tx_cmd), sizeof (nxmit_cb_t)); + e100_free_non_tx_cmd(bdp, command); + } else { + list_add_tail(&(command->list_elem), &(bdp->non_tx_cmd_list)); + bdp->same_cmd_entry[cmd] = command; + } + + if (bdp->non_tx_command_state == E100_NON_TX_IDLE) { + bdp->non_tx_command_state = E100_WAIT_TX_FINISH; + mod_timer(&(bdp->nontx_timer_id), jiffies + 1); + } + + spin_unlock_bh(&(bdp->bd_non_tx_lock)); + return true; +} + +static void +e100_non_tx_background(unsigned long ptr) +{ + struct e100_private *bdp = (struct e100_private *) ptr; + nxmit_cb_entry_t *active_command; + int restart = true; + + spin_lock_bh(&(bdp->bd_non_tx_lock)); + + switch (bdp->non_tx_command_state) { + case E100_WAIT_TX_FINISH: + if (bdp->last_tcb != NULL) { + rmb(); + if ((bdp->last_tcb->tcb_hdr.cb_status & + __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + goto exit; + } + if ((readw(&bdp->scb->scb_status) & SCB_CUS_MASK) == + SCB_CUS_ACTIVE) { + goto exit; + } + break; + + case E100_WAIT_NON_TX_FINISH: + active_command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + rmb(); + + if (((((cb_header_t *) (active_command->non_tx_cmd))->cb_status + & __constant_cpu_to_le16(CB_STATUS_COMPLETE)) == 0) + && time_before(jiffies, active_command->expiration_time)) { + goto exit; + } else { + list_del(&(active_command->list_elem)); + e100_free_non_tx_cmd(bdp, active_command); + } + break; + + default: + break; + } //switch + + if (list_empty(&bdp->non_tx_cmd_list)) { + bdp->non_tx_command_state = E100_NON_TX_IDLE; + spin_lock_irq(&(bdp->bd_lock)); + bdp->next_cu_cmd = START_WAIT; + spin_unlock_irq(&(bdp->bd_lock)); + restart = false; + goto exit; + } else { + u16 cmd_type; + + bdp->non_tx_command_state = E100_WAIT_NON_TX_FINISH; + active_command = list_entry(bdp->non_tx_cmd_list.next, + nxmit_cb_entry_t, list_elem); + spin_lock_irq(&(bdp->bd_lock)); + e100_wait_exec_cmplx(bdp, active_command->dma_addr, + SCB_CUC_START); + spin_unlock_irq(&(bdp->bd_lock)); + active_command->expiration_time = jiffies + HZ; + cmd_type = CB_CMD_MASK & + le16_to_cpu(((cb_header_t *) + (active_command->non_tx_cmd))->cb_cmd); + bdp->same_cmd_entry[cmd_type] = NULL; + } + +exit: + if (restart) { + mod_timer(&(bdp->nontx_timer_id), jiffies + 1); + } else { + if (netif_running(bdp->device)) + netif_wake_queue(bdp->device); + } + spin_unlock_bh(&(bdp->bd_non_tx_lock)); +} diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_phy.c linux-2.5.6/drivers/net/e100/e100_phy.c --- linux-2.5.6-pre3/drivers/net/e100/e100_phy.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_phy.c Thu Mar 7 18:24:45 2002 @@ -0,0 +1,1133 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#include "e100_phy.h" + +void e100_handle_zlock(struct e100_private *bdp); + +/* + * Procedure: e100_mdi_write + * + * Description: This routine will write a value to the specified MII register + * of an external MDI compliant device (e.g. PHY 100). The + * command will execute in polled mode. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * reg_addr - The MII register that we are writing to + * phy_addr - The MDI address of the Phy component. + * data - The value that we are writing to the MII register. + * + * Returns: + * NOTHING + */ +void +e100_mdi_write(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 data) +{ + int e100_retry; + u32 temp_val; + + temp_val = (((u32) data) | (reg_addr << 16) | + (phy_addr << 21) | (MDI_WRITE << 26)); + writel(temp_val, &bdp->scb->scb_mdi_cntrl); + + /* wait 20usec before checking status */ + udelay(20); + + /* poll for the mdi write to complete */ + e100_retry = E100_CMD_WAIT; + while ((!(readl(&bdp->scb->scb_mdi_cntrl) & MDI_PHY_READY)) && + (e100_retry)) { + + udelay(20); + e100_retry--; + } +} + +/* + * Procedure: e100_mdi_read + * + * Description: This routine will read a value from the specified MII register + * of an external MDI compliant device (e.g. PHY 100), and return + * it to the calling routine. The command will execute in polled + * mode. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * reg_addr - The MII register that we are reading from + * phy_addr - The MDI address of the Phy component. + * + * Results: + * data - The value that we read from the MII register. + * + * Returns: + * NOTHING + */ +void +e100_mdi_read(struct e100_private *bdp, u32 reg_addr, u32 phy_addr, u16 *data) +{ + int e100_retry; + u32 temp_val; + + /* Issue the read command to the MDI control register. */ + temp_val = ((reg_addr << 16) | (phy_addr << 21) | (MDI_READ << 26)); + writel(temp_val, &bdp->scb->scb_mdi_cntrl); + + /* wait 20usec before checking status */ + udelay(20); + + /* poll for the mdi read to complete */ + e100_retry = E100_CMD_WAIT; + while ((!(readl(&bdp->scb->scb_mdi_cntrl) & MDI_PHY_READY)) && + (e100_retry)) { + + udelay(20); + e100_retry--; + } + + // return the lower word + *data = (u16) readl(&bdp->scb->scb_mdi_cntrl); +} + +static unsigned char __devinit +e100_phy_valid(struct e100_private *bdp, unsigned int phy_address) +{ + u16 ctrl_reg, stat_reg; + + /* Read the MDI control register */ + e100_mdi_read(bdp, MII_BMCR, phy_address, &ctrl_reg); + + /* Read the status register twice, bacause of sticky bits */ + e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, phy_address, &stat_reg); + + if ((ctrl_reg == 0xffff) || ((stat_reg == 0) && (ctrl_reg == 0))) + return false; + + return true; +} + +static void __devinit +e100_phy_address_detect(struct e100_private *bdp) +{ + unsigned int addr; + unsigned char valid_phy_found = false; + + if (IS_NC3133(bdp)) { + bdp->phy_addr = 0; + return; + } + + if (e100_phy_valid(bdp, PHY_DEFAULT_ADDRESS)) { + bdp->phy_addr = PHY_DEFAULT_ADDRESS; + valid_phy_found = true; + + } else { + for (addr = MIN_PHY_ADDR; addr <= MAX_PHY_ADDR; addr++) { + if (e100_phy_valid(bdp, addr)) { + bdp->phy_addr = addr; + valid_phy_found = true; + break; + } + } + } + + if (!valid_phy_found) { + bdp->phy_addr = PHY_ADDRESS_503; + } +} + +static void __devinit +e100_phy_id_detect(struct e100_private *bdp) +{ + u16 low_id_reg, high_id_reg; + + if (bdp->phy_addr == PHY_ADDRESS_503) { + bdp->PhyId = PHY_503; + return; + } + if (!(bdp->flags & IS_ICH)) { + if (bdp->rev_id >= D102_REV_ID) { + bdp->PhyId = PHY_82562ET; + return; + } + } + + /* Read phy id from the MII register */ + e100_mdi_read(bdp, MII_PHYSID1, bdp->phy_addr, &low_id_reg); + e100_mdi_read(bdp, MII_PHYSID2, bdp->phy_addr, &high_id_reg); + + bdp->PhyId = ((unsigned int) low_id_reg | + ((unsigned int) high_id_reg << 16)); +} + +static void __devinit +e100_phy_isolate(struct e100_private *bdp) +{ + unsigned int phy_address; + u16 ctrl_reg; + + /* Go over all phy addresses. Deisolate the selected one, and isolate + * all the rest */ + for (phy_address = 0; phy_address <= MAX_PHY_ADDR; phy_address++) { + if (phy_address != bdp->phy_addr) { + e100_mdi_write(bdp, MII_BMCR, phy_address, + BMCR_ISOLATE); + + } else { + e100_mdi_read(bdp, MII_BMCR, bdp->phy_addr, &ctrl_reg); + ctrl_reg &= ~BMCR_ISOLATE; + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, ctrl_reg); + } + + udelay(100); + } +} + +static unsigned char __devinit +e100_phy_specific_setup(struct e100_private *bdp) +{ + u16 misc_reg; + + if (bdp->phy_addr == PHY_ADDRESS_503) { + switch (bdp->params.e100_speed_duplex) { + case E100_AUTONEG: + /* The adapter can't autoneg. so set to 10/HALF */ + printk(KERN_INFO + "503 serial component detected which " + "cannot autonegotiate\n"); + printk(KERN_INFO + "speed/duplex forced to 10Mbps / Half duplex\n"); + bdp->params.e100_speed_duplex = E100_SPEED_10_HALF; + break; + + case E100_SPEED_100_HALF: + case E100_SPEED_100_FULL: + printk(KERN_ERR + "503 serial component detected which does not " + "support 100Mbps\n"); + printk(KERN_ERR + "Change the forced speed/duplex to a supported " + "setting\n"); + return false; + } + + return true; + } + + if (IS_NC3133(bdp)) { + u16 int_reg; + + /* enable 100BASE fiber interface */ + e100_mdi_write(bdp, MDI_NC3133_CONFIG_REG, bdp->phy_addr, + MDI_NC3133_100FX_ENABLE); + + if ((bdp->params.e100_speed_duplex != E100_AUTONEG) && + (bdp->params.e100_speed_duplex != E100_SPEED_100_FULL)) { + /* just inform user about 100 full */ + printk(KERN_ERR "NC3133 NIC can only run " + "at 100Mbps full duplex\n"); + } + + bdp->params.e100_speed_duplex = E100_SPEED_100_FULL; + + /* enable interrupts */ + e100_mdi_read(bdp, MDI_NC3133_INT_ENABLE_REG, + bdp->phy_addr, &int_reg); + int_reg |= MDI_NC3133_INT_ENABLE; + e100_mdi_write(bdp, MDI_NC3133_INT_ENABLE_REG, + bdp->phy_addr, int_reg); + } + + /* Handle the National TX */ + if ((bdp->PhyId & PHY_MODEL_REV_ID_MASK) == PHY_NSC_TX) { + e100_mdi_read(bdp, NSC_CONG_CONTROL_REG, + bdp->phy_addr, &misc_reg); + + misc_reg |= NSC_TX_CONG_TXREADY; + + /* disable the congestion control bit in the National Phy */ + misc_reg &= ~NSC_TX_CONG_ENABLE; + + e100_mdi_write(bdp, NSC_CONG_CONTROL_REG, + bdp->phy_addr, misc_reg); + } + + return true; +} + +/* + * Procedure: e100_phy_fix_squelch + * + * Description: + * Help find link on certain rare scenarios. + * NOTE: This routine must be called once per watchdog, + * and *after* setting the current link state. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void +e100_phy_fix_squelch(struct e100_private *bdp) +{ + if ((bdp->PhyId != PHY_82555_TX) || (bdp->flags & DF_SPEED_FORCED)) + return; + + if (netif_carrier_ok(bdp->device)) { + switch (bdp->PhyState) { + case 0: + break; + case 1: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, 0x0000); + break; + case 2: + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x3000); + break; + } + bdp->PhyState = 0; + bdp->PhyDelay = 0; + + } else if (!bdp->PhyDelay--) { + switch (bdp->PhyState) { + case 0: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, EXTENDED_SQUELCH_BIT); + bdp->PhyState = 1; + break; + case 1: + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, 0x0000); + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x2010); + bdp->PhyState = 2; + break; + case 2: + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0x3000); + bdp->PhyState = 0; + break; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, + BMCR_ANENABLE | BMCR_ANRESTART); + bdp->PhyDelay = 3; + } +} + +/* + * Procedure: e100_fix_polarity + * + * Description: + * Fix for 82555 auto-polarity toggle problem. With a short cable + * connecting an 82555 with an 840A link partner, if the medium is noisy, + * the 82555 sometime thinks that the polarity might be wrong and so + * toggles polarity. This happens repeatedly and results in a high bit + * error rate. + * NOTE: This happens only at 10 Mbps + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void __devinit +e100_fix_polarity(struct e100_private *bdp) +{ + u16 status; + u16 errors; + u16 misc_reg; + int speed; + + if ((bdp->PhyId != PHY_82555_TX) && (bdp->PhyId != PHY_82562ET) && + (bdp->PhyId != PHY_82562EM)) + return; + + /* If the user wants auto-polarity disabled, do only that and nothing * + * else. * e100_autopolarity == 0 means disable --- we do just the + * disabling * e100_autopolarity == 1 means enable --- we do nothing at + * all * e100_autopolarity >= 2 means we do the workaround code. */ + /* Change for 82558 enhancement */ + switch (E100_AUTOPOLARITY) { + case 0: + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, + (u16) (misc_reg | DISABLE_AUTO_POLARITY)); + break; + + case 1: + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, bdp->phy_addr, + (u16) (misc_reg & ~DISABLE_AUTO_POLARITY)); + break; + + case 2: + /* we do this only if link is up */ + if (!netif_carrier_ok(bdp->device)) { + break; + } + + e100_mdi_read(bdp, PHY_82555_CSR, bdp->phy_addr, &status); + speed = (status & PHY_82555_SPEED_BIT) ? 100 : 10; + + /* we need to do this only if speed is 10 */ + if (speed != 10) { + break; + } + + /* see if we have any end of frame errors */ + e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, + bdp->phy_addr, &errors); + + /* if non-zero, wait for 100 ms before reading again */ + if (errors) { + udelay(200); + e100_mdi_read(bdp, PHY_82555_EOF_COUNTER, + bdp->phy_addr, &errors); + + /* if non-zero again, we disable polarity */ + if (errors) { + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, + (u16) (misc_reg | + DISABLE_AUTO_POLARITY)); + } + } + + if (!errors) { + /* it is safe to read the polarity now */ + e100_mdi_read(bdp, PHY_82555_CSR, + bdp->phy_addr, &status); + + /* if polarity is normal, disable polarity */ + if (!(status & PHY_82555_POLARITY_BIT)) { + e100_mdi_read(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, &misc_reg); + e100_mdi_write(bdp, PHY_82555_SPECIAL_CONTROL, + bdp->phy_addr, + (u16) (misc_reg | + DISABLE_AUTO_POLARITY)); + } + } + break; + + default: + break; + } +} + +/* + * Procedure: e100_find_speed_duplex + * + * Description: This routine will figure out what line speed and duplex mode + * the PHY is currently using. + * + * Arguments: + * bdp - Ptr to this card's e100_bdconfig structure + * + * Returns: + * NOTHING + */ +static void +e100_find_speed_duplex(struct e100_private *bdp) +{ + unsigned int PhyId; + u16 stat_reg, misc_reg; + u16 ad_reg, lp_ad_reg; + + PhyId = bdp->PhyId & PHY_MODEL_REV_ID_MASK; + + /* First we should check to see if we have link */ + /* If we don't have a link no reason to print a speed and duplex */ + if (!e100_update_link_state(bdp)) { + return; + } + + if (bdp->flags & DF_SPEED_FORCED) { + return; + } + + /* On the 82559 and later controllers, speed/duplex is part of the * + * SCB. So, we save an mdi_read and get these from the SCB. * */ + if (bdp->rev_id >= D101MA_REV_ID) { + /* Read speed */ + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_1) + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Read duplex */ + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_2) + bdp->cur_dplx_mode = FULL_DUPLEX; + else + bdp->cur_dplx_mode = HALF_DUPLEX; + + return; + } + + /* If this is a Phy 100, then read bits 1 and 0 of extended register 0, + * to get the current speed and duplex settings. */ + if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || + (PhyId == PHY_82555_TX)) { + + /* Read Phy 100 extended register 0 */ + e100_mdi_read(bdp, EXTENDED_REG_0, bdp->phy_addr, &misc_reg); + + /* Get current speed setting */ + if (misc_reg & PHY_100_ER0_SPEED_INDIC) + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Get current duplex setting -- FDX enabled if bit is set */ + if (misc_reg & PHY_100_ER0_FDX_INDIC) + bdp->cur_dplx_mode = FULL_DUPLEX; + else + bdp->cur_dplx_mode = HALF_DUPLEX; + + return; + } + + /* See if link partner is capable of Auto-Negotiation (bit 0, reg 6) */ + e100_mdi_read(bdp, MII_EXPANSION, bdp->phy_addr, &misc_reg); + + /* See if Auto-Negotiation was complete (bit 5, reg 1) */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + /* If a True NWAY connection was made, then we can detect speed/dplx + * by ANDing our adapter's advertised abilities with our link partner's + * advertised ablilities, and then assuming that the highest common + * denominator was chosed by NWAY. */ + if ((misc_reg & EXPANSION_NWAY) && (stat_reg & BMSR_ANEGCOMPLETE)) { + + /* Read our advertisement register */ + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &ad_reg); + + /* Read our link partner's advertisement register */ + e100_mdi_read(bdp, MII_LPA, bdp->phy_addr, &lp_ad_reg); + + /* AND the two advertisement registers together, and get rid + * of any extraneous bits. */ + ad_reg &= (lp_ad_reg & NWAY_LP_ABILITY); + + /* Get speed setting */ + if (ad_reg & + (ADVERTISE_100HALF | ADVERTISE_100FULL | + ADVERTISE_100BASE4)) + + bdp->cur_line_speed = 100; + else + bdp->cur_line_speed = 10; + + /* Get duplex setting -- use priority resolution algorithm */ + if (ad_reg & ADVERTISE_100BASE4) { + bdp->cur_dplx_mode = HALF_DUPLEX; + } else if (ad_reg & ADVERTISE_100FULL) { + bdp->cur_dplx_mode = FULL_DUPLEX; + } else if (ad_reg & ADVERTISE_100HALF) { + bdp->cur_dplx_mode = HALF_DUPLEX; + } else if (ad_reg & ADVERTISE_10FULL) { + bdp->cur_dplx_mode = FULL_DUPLEX; + } else { + bdp->cur_dplx_mode = HALF_DUPLEX; + } + + return; + } + + /* If we are connected to a dumb (non-NWAY) repeater or hub, and the + * line speed was determined automatically by parallel detection, then + * we have no way of knowing exactly what speed the PHY is set to + * unless that PHY has a propietary register which indicates speed in + * this situation. The NSC TX PHY does have such a register. Also, + * since NWAY didn't establish the connection, the duplex setting + * should HALF duplex. */ + bdp->cur_dplx_mode = HALF_DUPLEX; + + if (PhyId == PHY_NSC_TX) { + /* Read register 25 to get the SPEED_10 bit */ + e100_mdi_read(bdp, NSC_SPEED_IND_REG, bdp->phy_addr, &misc_reg); + + /* If bit 6 was set then we're at 10Mbps */ + if (misc_reg & NSC_TX_SPD_INDC_SPEED) + bdp->cur_line_speed = 10; + else + bdp->cur_line_speed = 100; + + } else { + /* If we don't know the line speed, default to 10Mbps */ + bdp->cur_line_speed = 10; + } +} + +/* + * Procedure: e100_force_speed_duplex + * + * Description: This routine forces line speed and duplex mode of the + * adapter based on the values the user has set in e100.c. + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: void + * + */ +static void +e100_force_speed_duplex(struct e100_private *bdp) +{ + u16 control; + int neg_timeout = 2 * HZ; //2 sec in jiffies + + bdp->flags |= DF_SPEED_FORCED; + + spin_lock_bh(&(bdp->mdi_access_lock)); + + e100_mdi_read(bdp, MII_BMCR, bdp->phy_addr, &control); + control &= ~BMCR_ANENABLE; + + /* Check e100.c values */ + switch (bdp->params.e100_speed_duplex) { + case E100_SPEED_10_HALF: + control &= ~BMCR_SPEED100; + control &= ~BMCR_FULLDPLX; + bdp->cur_line_speed = 10; + bdp->cur_dplx_mode = HALF_DUPLEX; + break; + + case E100_SPEED_10_FULL: + control &= ~BMCR_SPEED100; + control |= BMCR_FULLDPLX; + bdp->cur_line_speed = 10; + bdp->cur_dplx_mode = FULL_DUPLEX; + break; + + case E100_SPEED_100_HALF: + control |= BMCR_SPEED100; + control &= ~BMCR_FULLDPLX; + bdp->cur_line_speed = 100; + bdp->cur_dplx_mode = HALF_DUPLEX; + break; + + case E100_SPEED_100_FULL: + control |= BMCR_SPEED100; + control |= BMCR_FULLDPLX; + bdp->cur_line_speed = 100; + bdp->cur_dplx_mode = FULL_DUPLEX; + break; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, control); + + /* loop must run at least once */ + do { + spin_unlock_bh(&(bdp->mdi_access_lock)); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&(bdp->mdi_access_lock)); + + if (e100_update_link_state(bdp)) { + break; + } + neg_timeout -= SLEEP_TIME; + + } while (neg_timeout > 0); + + spin_unlock_bh(&(bdp->mdi_access_lock)); +} + +/* + * Procedure: e100_set_fc + * + * Description: Checks the link's capability for flow control. + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: void + * + */ +static void +e100_set_fc(struct e100_private *bdp) +{ + u16 ad_reg; + u16 lp_ad_reg; + u16 exp_reg; + + /* no flow control for 82557, forced links or half duplex */ + if (!netif_carrier_ok(bdp->device) || (bdp->flags & DF_SPEED_FORCED) || + (bdp->cur_dplx_mode == HALF_DUPLEX) || + !(bdp->flags & IS_BACHELOR)) { + + bdp->flags &= ~DF_LINK_FC_CAP; + return; + } + + /* See if link partner is capable of Auto-Negotiation (bit 0, reg 6) */ + e100_mdi_read(bdp, MII_EXPANSION, bdp->phy_addr, &exp_reg); + + if (exp_reg & EXPANSION_NWAY) { + /* Read our advertisement register */ + e100_mdi_read(bdp, MII_ADVERTISE, bdp->phy_addr, &ad_reg); + + /* Read our link partner's advertisement register */ + e100_mdi_read(bdp, MII_LPA, bdp->phy_addr, &lp_ad_reg); + + ad_reg &= lp_ad_reg; /* AND the 2 ad registers */ + + if (ad_reg & NWAY_AD_FC_SUPPORTED) + bdp->flags |= DF_LINK_FC_CAP; + else + bdp->flags &= ~DF_LINK_FC_CAP; + + } else { + bdp->flags &= ~DF_LINK_FC_CAP; + } +} + +/* + * Procedure: e100_phy_check + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * Returns: true if link state was changed + * B_FLASE otherwise + * + */ +unsigned char +e100_phy_check(struct e100_private *bdp) +{ + unsigned char old_link; + unsigned char changed = false; + + old_link = netif_carrier_ok(bdp->device) ? 1 : 0; + e100_find_speed_duplex(bdp); + + if (!old_link && netif_carrier_ok(bdp->device)) { + e100_set_fc(bdp); + changed = true; + } + + if (old_link && !netif_carrier_ok(bdp->device)) { + /* reset the zero lock state */ + bdp->zlock_state = ZLOCK_INITIAL; + + // set auto lock for phy auto-negotiation on link up + if ((bdp->PhyId & PHY_MODEL_REV_ID_MASK) == PHY_82555_TX) + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0); + changed = true; + } + + e100_phy_fix_squelch(bdp); + e100_handle_zlock(bdp); + + return changed; +} + +/* + * Procedure: e100_auto_neg + * + * Description: This routine will start autonegotiation and wait + * for it to complete + * + * Arguments: + * bdp - pointer to this card's e100_bdconfig structure + * force_restart - defines if autoneg should be restarted even if it + * has been completed before + * Returns: + * NOTHING + */ +static void +e100_auto_neg(struct e100_private *bdp, unsigned char force_restart) +{ + u16 stat_reg; + unsigned int i; + + bdp->flags &= ~DF_SPEED_FORCED; + + spin_lock_bh(&(bdp->mdi_access_lock)); + + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + /* if we are capable of performing autoneg then we restart if needed */ + if ((stat_reg != 0xFFFF) && (stat_reg & BMSR_ANEGCAPABLE)) { + + if ((!force_restart) && + (stat_reg & BMSR_ANEGCOMPLETE)) { + goto exit; + } + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, + BMCR_ANENABLE | BMCR_ANRESTART); + + /* wait for autoneg to complete (up to 3 seconds) */ + for (i = 0; i < 60; i++) { + /* now re-read the value. Sticky so read twice */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &stat_reg); + + if (stat_reg & BMSR_ANEGCOMPLETE) + goto exit; + + spin_unlock_bh(&(bdp->mdi_access_lock)); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&(bdp->mdi_access_lock)); + + } + } + +exit: + e100_find_speed_duplex(bdp); + spin_unlock_bh(&(bdp->mdi_access_lock)); +} + +void +e100_phy_set_speed_duplex(struct e100_private *bdp, unsigned char force_restart) +{ + if (bdp->params.e100_speed_duplex == E100_AUTONEG) { + e100_auto_neg(bdp, force_restart); + + } else { + e100_force_speed_duplex(bdp); + } + + e100_set_fc(bdp); +} + +void __devexit +e100_phy_reset(struct e100_private *bdp) +{ + u16 ctrl_reg; + + ctrl_reg = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET; + + e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, ctrl_reg); + + udelay(100); +} + +unsigned char __devinit +e100_phy_init(struct e100_private *bdp) +{ + e100_phy_address_detect(bdp); + e100_phy_isolate(bdp); + e100_phy_id_detect(bdp); + + if (!e100_phy_specific_setup(bdp)) + return false; + + bdp->PhyState = 0; + bdp->PhyDelay = 0; + bdp->zlock_state = ZLOCK_INITIAL; + + e100_phy_set_speed_duplex(bdp, false); + e100_fix_polarity(bdp); + + return true; +} + +/* + * Procedure: e100_get_link_state + * + * Description: This routine checks the link status of the adapter + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * + * Returns: true - If a link is found + * false - If there is no link + * + */ +unsigned char +e100_get_link_state(struct e100_private *bdp) +{ + unsigned char link = false; + u16 status; + + /* Check link status */ + /* If the controller is a 82559 or later one, link status is available + * from the CSR. This avoids the mdi_read. */ + if (bdp->rev_id >= D101MA_REV_ID) { + if (readb(&bdp->scb->scb_ext.d101m_scb.scb_gen_stat) & BIT_0) { + link = true; + } else { + link = false; + } + + } else { + /* Read the status register twice because of sticky bits */ + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + e100_mdi_read(bdp, MII_BMSR, bdp->phy_addr, &status); + + if (status & BMSR_LSTATUS) { + link = true; + } else { + link = false; + } + } + + return link; +} + +/* + * Procedure: e100_update_link_state + * + * Description: This routine updates the link status of the adapter + * + * Arguments: bdp - Pointer to the e100_private structure for the board + * + * + * Returns: true - If a link is found + * false - If there is no link + * + */ +unsigned char +e100_update_link_state(struct e100_private *bdp) +{ + unsigned char link; + + link = e100_get_link_state(bdp); + + if (link) { + if (!netif_carrier_ok(bdp->device)) + netif_carrier_on(bdp->device); + } else { + if (netif_carrier_ok(bdp->device)) + netif_carrier_off(bdp->device); + } + + return link; +} + +/**************************************************************************\ + ** + ** PROC NAME: e100_handle_zlock + ** This function manages a state machine that controls + ** the driver's zero locking algorithm. + ** This function is called by e100_watchdog() every ~2 second. + ** States: + ** The current link handling state is stored in + ** bdp->zlock_state, and is one of: + ** ZLOCK_INITIAL, ZLOCK_READING, ZLOCK_SLEEPING + ** Detailed description of the states and the transitions + ** between states is found below. + ** Note that any time the link is down / there is a reset + ** state will be changed outside this function to ZLOCK_INITIAL + ** Algorithm: + ** 1. If link is up & 100 Mbps continue else stay in #1: + ** 2. Set 'auto lock' + ** 3. Read & Store 100 times 'Zero' locked in 1 sec interval + ** 4. If max zero read >= 0xB continue else goto 1 + ** 5. Set most popular 'Zero' read in #3 + ** 6. Sleep 5 minutes + ** 7. Read number of errors, if it is > 300 goto 2 else goto 6 + ** Data Structures (in DRIVER_DATA): + ** zlock_state - current state of the algorithm + ** zlock_read_cnt - counts number of reads (up to 100) + ** zlock_read_data[i] - counts number of times 'Zero' read was i, 0 <= i <= 15 + ** zlock_sleep_cnt - keeps track of "sleep" time (up to 300 secs = 5 minutes) + ** + ** Parameters: DRIVER_DATA *bdp + ** + ** bdp - Pointer to HSM's adapter data space + ** + ** Return Value: NONE + ** + ** See Also: e100_watchdog() + ** + \**************************************************************************/ +void +e100_handle_zlock(struct e100_private *bdp) +{ + u16 pos; + u16 eq_reg; + u16 err_cnt; + u8 mpz; /* Most Popular Zero */ + + switch (bdp->zlock_state) { + case ZLOCK_INITIAL: + + if (((u8) bdp->rev_id <= D102_REV_ID) || + !(bdp->cur_line_speed == 100) || + !netif_carrier_ok(bdp->device)) { + break; + } + + /* initialize hw and sw and start reading */ + e100_mdi_write(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, 0); + /* reset read counters: */ + bdp->zlock_read_cnt = 0; + for (pos = 0; pos < 16; pos++) + bdp->zlock_read_data[pos] = 0; + /* start reading in the next call back: */ + bdp->zlock_state = ZLOCK_READING; + + /* FALL THROUGH !! */ + + case ZLOCK_READING: + /* state: reading (100 times) zero locked in 1 sec interval + * prev states: ZLOCK_INITIAL + * next states: ZLOCK_INITIAL, ZLOCK_SLEEPING */ + + e100_mdi_read(bdp, PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, &eq_reg); + pos = (eq_reg & ZLOCK_ZERO_MASK) >> 4; + bdp->zlock_read_data[pos]++; + bdp->zlock_read_cnt++; + + if (bdp->zlock_read_cnt == ZLOCK_MAX_READS) { + /* check if we read a 'Zero' value of 0xB or greater */ + if ((bdp->zlock_read_data[0xB]) || + (bdp->zlock_read_data[0xC]) || + (bdp->zlock_read_data[0xD]) || + (bdp->zlock_read_data[0xE]) || + (bdp->zlock_read_data[0xF])) { + + /* we've read 'Zero' value of 0xB or greater, + * find most popular 'Zero' value and lock it */ + mpz = 0; + /* this loop finds the most popular 'Zero': */ + for (pos = 1; pos < 16; pos++) { + if (bdp->zlock_read_data[pos] > + bdp->zlock_read_data[mpz]) + + mpz = pos; + } + /* now lock the most popular 'Zero': */ + eq_reg = (ZLOCK_SET_ZERO | mpz); + e100_mdi_write(bdp, + PHY_82555_MDI_EQUALIZER_CSR, + bdp->phy_addr, eq_reg); + + /* sleep for 5 minutes: */ + bdp->zlock_sleep_cnt = jiffies; + bdp->zlock_state = ZLOCK_SLEEPING; + /* we will be reading the # of errors after 5 + * minutes, so we need to reset the error + * counters - these registers are self clearing + * on read, so read them */ + e100_mdi_read(bdp, PHY_82555_SYMBOL_ERR, + bdp->phy_addr, &err_cnt); + + } else { + /* we did not read a 'Zero' value of 0xB or + * above. go back to the start */ + bdp->zlock_state = ZLOCK_INITIAL; + } + + } + break; + + case ZLOCK_SLEEPING: + /* state: sleeping for 5 minutes + * prev states: ZLOCK_READING + * next states: ZLOCK_READING, ZLOCK_SLEEPING */ + + /* if 5 minutes have passed: */ + if ((jiffies - bdp->zlock_sleep_cnt) >= ZLOCK_MAX_SLEEP) { + /* read and sum up the number of errors: */ + e100_mdi_read(bdp, PHY_82555_SYMBOL_ERR, + bdp->phy_addr, &err_cnt); + /* if we've more than 300 errors (this number was + * calculated according to the spec max allowed errors + * (80 errors per 1 million frames) for 5 minutes in + * 100 Mbps (or the user specified max BER number) */ + if (err_cnt > bdp->params.ber) { + /* start again in the next callback: */ + bdp->zlock_state = ZLOCK_INITIAL; + } else { + /* we don't have more errors than allowed, + * sleep for 5 minutes */ + bdp->zlock_sleep_cnt = jiffies; + } + } + break; + + default: + break; + } +} diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_phy.h linux-2.5.6/drivers/net/e100/e100_phy.h --- linux-2.5.6-pre3/drivers/net/e100/e100_phy.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_phy.h Thu Mar 7 18:24:45 2002 @@ -0,0 +1,183 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _E100_PHY_INC_ +#define _E100_PHY_INC_ + +#include "e100.h" + +#include + +/* + * Auto-polarity enable/disable + * e100_autopolarity = 0 => disable auto-polarity + * e100_autopolarity = 1 => enable auto-polarity + * e100_autopolarity = 2 => let software determine + */ +#define E100_AUTOPOLARITY 2 + +#define IS_NC3133(bdp) (((bdp)->pdev->subsystem_vendor == 0x0E11) && \ + ((bdp)->pdev->subsystem_device == 0xB0E1)) + +#define PHY_503 0 +#define PHY_100_A 0x000003E0 +#define PHY_100_C 0x035002A8 +#define PHY_NSC_TX 0x5c002000 +#define PHY_82562ET 0x033002A8 +#define PHY_82562EM 0x032002A8 +#define PHY_82562EH 0x017002A8 +#define PHY_82555_TX 0x015002a8 /* added this for 82555 */ +#define PHY_OTHER 0xFFFF +#define MAX_PHY_ADDR 31 +#define MIN_PHY_ADDR 0 + +#define PHY_MODEL_REV_ID_MASK 0xFFF0FFFF + +#define PHY_DEFAULT_ADDRESS 1 +#define PHY_ADDRESS_503 32 + +/* MDI Control register bit definitions */ +#define MDI_PHY_READY BIT_28 /* PHY is ready for next MDI cycle */ + +#define MDI_NC3133_CONFIG_REG 0x19 +#define MDI_NC3133_100FX_ENABLE BIT_2 +#define MDI_NC3133_INT_ENABLE_REG 0x17 +#define MDI_NC3133_INT_ENABLE BIT_1 + +/* MDI Control register opcode definitions */ +#define MDI_WRITE 1 /* Phy Write */ +#define MDI_READ 2 /* Phy read */ + +/* MDI register set*/ +#define AUTO_NEG_NEXT_PAGE_REG 0x07 /* Auto-negotiation next page xmit */ +#define EXTENDED_REG_0 0x10 /* Extended reg 0 (Phy 100 modes) */ +#define EXTENDED_REG_1 0x14 /* Extended reg 1 (Phy 100 error indications) */ +#define NSC_CONG_CONTROL_REG 0x17 /* National (TX) congestion control */ +#define NSC_SPEED_IND_REG 0x19 /* National (TX) speed indication */ + +/* ############Start of 82555 specific defines################## */ + +/* Intel 82555 specific registers */ +#define PHY_82555_CSR 0x10 /* 82555 CSR */ +#define PHY_82555_SPECIAL_CONTROL 0x11 /* 82555 special control register */ + +#define PHY_82555_RCV_ERR 0x15 /* 82555 100BaseTx Receive Error + * Frame Counter */ +#define PHY_82555_SYMBOL_ERR 0x16 /* 82555 RCV Symbol Error Counter */ +#define PHY_82555_PREM_EOF_ERR 0x17 /* 82555 100BaseTx RCV Premature End + * of Frame Error Counter */ +#define PHY_82555_EOF_COUNTER 0x18 /* 82555 end of frame error counter */ +#define PHY_82555_MDI_EQUALIZER_CSR 0x1a /* 82555 specific equalizer reg. */ + +/* 82555 CSR bits */ +#define PHY_82555_SPEED_BIT BIT_1 +#define PHY_82555_POLARITY_BIT BIT_8 + +/* 82555 equalizer reg. opcodes */ +#define ENABLE_ZERO_FORCING 0x2010 /* write to ASD conf. reg. 0 */ +#define DISABLE_ZERO_FORCING 0x2000 /* write to ASD conf. reg. 0 */ + +/* 82555 special control reg. opcodes */ +#define DISABLE_AUTO_POLARITY 0x0010 +#define EXTENDED_SQUELCH_BIT BIT_2 + +/* ############End of 82555 specific defines##################### */ + +/* Auto-Negotiation advertisement register bit definitions*/ +#define NWAY_AD_FC_SUPPORTED 0x0400 /* Flow Control supported */ + +/* Auto-Negotiation link partner ability register bit definitions*/ +#define NWAY_LP_ABILITY 0x07e0 /* technologies supported */ + +/* PHY 100 Extended Register 0 bit definitions*/ +#define PHY_100_ER0_FDX_INDIC BIT_0 /* 1 = FDX, 0 = half duplex */ +#define PHY_100_ER0_SPEED_INDIC BIT_1 /* 1 = 100Mbps, 0= 10Mbps */ + +/* National Semiconductor TX phy congestion control register bit definitions*/ +#define NSC_TX_CONG_TXREADY BIT_10 /* Makes TxReady an input */ +#define NSC_TX_CONG_ENABLE BIT_8 /* Enables congestion control */ + +/* National Semiconductor TX phy speed indication register bit definitions*/ +#define NSC_TX_SPD_INDC_SPEED BIT_6 /* 0 = 100Mbps, 1=10Mbps */ + +/************* function prototypes ************/ +extern unsigned char e100_phy_init(struct e100_private *bdp); +extern unsigned char e100_update_link_state(struct e100_private *bdp); +extern unsigned char e100_phy_check(struct e100_private *bdp); +extern void e100_phy_set_speed_duplex(struct e100_private *bdp, + unsigned char force_restart); +extern void e100_phy_reset(struct e100_private *bdp); +extern void e100_mdi_write(struct e100_private *, u32, u32, u16); +extern void e100_mdi_read(struct e100_private *, u32, u32, u16 *); + +#endif diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_proc.c linux-2.5.6/drivers/net/e100/e100_proc.c --- linux-2.5.6-pre3/drivers/net/e100/e100_proc.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_proc.c Thu Mar 7 18:24:45 2002 @@ -0,0 +1,925 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/********************************************************************** +* * +* INTEL CORPORATION * +* * +* This software is supplied under the terms of the license included * +* above. All use of this driver must be in accordance with the terms * +* of that license. * +* * +* Module Name: e100_proc.c * +* * +* Abstract: Functions to handle the proc file system. * +* Create the proc directories and files and run read and * +* write requests from the user * +* * +* Environment: This file is intended to be specific to the Linux * +* operating system. * +* * +**********************************************************************/ + +#include + +#ifndef CONFIG_PROC_FS +#undef E100_CONFIG_PROC_FS +#endif + +#ifdef E100_CONFIG_PROC_FS +#include "e100.h" + +/***************************************************************************/ +/* /proc File System Interaface Support Functions */ +/***************************************************************************/ + +static struct proc_dir_entry *adapters_proc_dir = 0; + +/* externs from e100_main.c */ +extern const char *e100_short_driver_name; +extern const char *e100_version; +extern struct net_device_stats *e100_get_stats(struct net_device *dev); +extern char *e100_get_brand_msg(struct e100_private *bdp); +extern void e100_mdi_write(struct e100_private *, u32, u32, u16); + +static void e100_proc_cleanup(void); +static unsigned char e100_init_proc_dir(void); + +#define E100_EOU + +#define ADAPTERS_PROC_DIR "PRO_LAN_Adapters" +#define WRITE_BUF_MAX_LEN 20 +#define READ_BUF_MAX_LEN 256 +#define E100_PE_LEN 25 + +#define bdp_drv_off(off) (unsigned long)(offsetof(struct e100_private, drv_stats.off)) +#define bdp_prm_off(off) (unsigned long)(offsetof(struct e100_private, params.off)) + +typedef struct _e100_proc_entry { + char *name; + read_proc_t *read_proc; + write_proc_t *write_proc; + unsigned long offset; /* offset into bdp. ~0 means no value, pass NULL. */ +} e100_proc_entry; + +static int +generic_read(char *page, char **start, off_t off, int count, int *eof, int len) +{ + if (len <= off + count) + *eof = 1; + + *start = page + off; + len -= off; + if (len > count) + len = count; + + if (len < 0) + len = 0; + + return len; +} + +static int +read_ulong(char *page, char **start, off_t off, + int count, int *eof, unsigned long l) +{ + int len; + + len = sprintf(page, "%lu\n", l); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_gen_ulong(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long val = 0; + + if (data) + val = *((unsigned long *) data); + + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_hwaddr(char *page, char **start, off_t off, + int count, int *eof, unsigned char *hwaddr) +{ + int len; + + len = sprintf(page, "%02X:%02X:%02X:%02X:%02X:%02X\n", + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_descr(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + int len; + + len = sprintf(page, "%s\n", e100_get_brand_msg(bdp)); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_permanent_hwaddr(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned char *hwaddr = bdp->perm_node_address; + + return read_hwaddr(page, start, off, count, eof, hwaddr); +} + +static int +read_part_number(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + int len; + + len = sprintf(page, "%06lx-%03x\n", + (unsigned long) (bdp->pwa_no >> 8), + (unsigned int) (bdp->pwa_no & 0xFF)); + + return generic_read(page, start, off, count, eof, len); +} + +static void +set_led(struct e100_private *bdp, u16 led_mdi_op) +{ + spin_lock_bh(&bdp->mdi_access_lock); + + e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL, + bdp->phy_addr, led_mdi_op); + + spin_unlock_bh(&bdp->mdi_access_lock); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(SLEEP_TIME); + + spin_lock_bh(&bdp->mdi_access_lock); + + /* turn led ownership to the chip */ + e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL, + bdp->phy_addr, PHY_82555_LED_NORMAL_CONTROL); + + spin_unlock_bh(&bdp->mdi_access_lock); +} + +static int +write_blink_led_timer(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct e100_private *bdp = data; + char s_blink_op[WRITE_BUF_MAX_LEN + 1]; + char *res; + unsigned long i_blink_op; + + if (!buffer) + return -EINVAL; + + if (count > WRITE_BUF_MAX_LEN) { + count = WRITE_BUF_MAX_LEN; + } + copy_from_user(s_blink_op, buffer, count); + s_blink_op[count] = '\0'; + i_blink_op = simple_strtoul(s_blink_op, &res, 0); + if (res == s_blink_op) { + return -EINVAL; + } + + switch (i_blink_op) { + + case LED_OFF: + set_led(bdp, PHY_82555_LED_OFF); + break; + case LED_ON: + if (bdp->rev_id >= D101MA_REV_ID) + set_led(bdp, PHY_82555_LED_ON_559); + else + set_led(bdp, PHY_82555_LED_ON_PRE_559); + + break; + default: + return -EINVAL; + } + + return count; +} + +static e100_proc_entry e100_proc_list[] = { + {"Description", read_descr, 0, 0}, + {"Permanent_HWaddr", read_permanent_hwaddr, 0, 0}, + {"Part_Number", read_part_number, 0, 0}, + {"\n",}, + {"Rx_TCP_Checksum_Good", read_gen_ulong, 0, ~0}, + {"Rx_TCP_Checksum_Bad", read_gen_ulong, 0, ~0}, + {"Tx_TCP_Checksum_Good", read_gen_ulong, 0, ~0}, + {"Tx_TCP_Checksum_Bad", read_gen_ulong, 0, ~0}, + {"\n",}, + {"Tx_Abort_Late_Coll", read_gen_ulong, 0, bdp_drv_off(tx_late_col)}, + {"Tx_Deferred_Ok", read_gen_ulong, 0, bdp_drv_off(tx_ok_defrd)}, + {"Tx_Single_Coll_Ok", read_gen_ulong, 0, bdp_drv_off(tx_one_retry)}, + {"Tx_Multi_Coll_Ok", read_gen_ulong, 0, bdp_drv_off(tx_mt_one_retry)}, + {"Rx_Long_Length_Errors", read_gen_ulong, 0, ~0}, + {"\n",}, + {"Tx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(xmt_fc_pkts)}, + {"Rx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(rcv_fc_pkts)}, + {"Rx_Flow_Control_Unsup", read_gen_ulong, 0, bdp_drv_off(rcv_fc_unsupported)}, + {"\n",}, + {"Tx_TCO_Packets", read_gen_ulong, 0, bdp_drv_off(xmt_tco_pkts)}, + {"Rx_TCO_Packets", read_gen_ulong, 0, bdp_drv_off(rcv_tco_pkts)}, + {"\n",}, + {"Rx_Interrupt_Packets", read_gen_ulong, 0, bdp_drv_off(rx_intr_pkts)}, + {"Rx_Polling_Packets", read_gen_ulong, 0, bdp_drv_off(rx_tasklet_pkts)}, + {"Polling_Interrupt_Switch", read_gen_ulong, 0, bdp_drv_off(poll_intr_switch)}, + {"Identify_Adapter", 0, write_blink_led_timer, 0}, + {"", 0, 0, 0} +}; + +static int +read_info(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + e100_proc_entry *pe; + int tmp; + void *val; + int len = 0; + + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') { + len += sprintf(page + len, "\n"); + continue; + } + + if (pe->read_proc) { + if ((len + READ_BUF_MAX_LEN + E100_PE_LEN + 1) >= + PAGE_SIZE) + break; + + if (pe->offset != ~0) + val = ((char *) bdp) + pe->offset; + else + val = NULL; + + len += sprintf(page + len, "%-" + __MODULE_STRING(E100_PE_LEN) + "s ", pe->name); + len += pe->read_proc(page + len, start, 0, + READ_BUF_MAX_LEN + 1, &tmp, val); + } + } + + return generic_read(page, start, off, count, eof, len); +} + +#ifdef E100_EOU +/********************** + * parameter entries + **********************/ +static int +read_int_param(char *page, char *name, char *desc, int def, int min, int max) +{ + int len; + + len = sprintf(page, "Name: %s\n", name); + len += sprintf(page + len, "Description: %s\n", desc); + len += sprintf(page + len, "Default_Value: %d\n", def); + len += sprintf(page + len, "Type: Range\n"); + len += sprintf(page + len, "Min: %d\n", min); + len += sprintf(page + len, "Max: %d\n", max); + len += sprintf(page + len, "Step:1\n"); + len += sprintf(page + len, "Radix: dec\n"); + + return len; +} + +static int +read_bool_param(char *page, char *name, char *desc, int def) +{ + int len; + + len = sprintf(page, "Name: %s\n", name); + len += sprintf(page + len, "Description: %s\n", desc); + len += sprintf(page + len, "Default_Value: %d\n", def); + len += sprintf(page + len, "Type: Enum\n"); + len += sprintf(page + len, "0: Off\n"); + len += sprintf(page + len, "1: On\n"); + + return len; +} + +static int +read_speed_duplex_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "Name: Speed and Duplex\n"); + len += sprintf(page + len, "Description: Sets the adapter's " + "speed and duplex mode\n"); + len += sprintf(page + len, "Default_Value: 0\n"); + len += sprintf(page + len, "Type: Enum\n"); + len += sprintf(page + len, "0: Auto-Negotiate\n"); + len += sprintf(page + len, "1: 10 Mbps / Half Duplex\n"); + len += sprintf(page + len, "2: 10 Mbps / Full Duplex\n"); + len += sprintf(page + len, "3: 100 Mbps / Half Duplex\n"); + len += sprintf(page + len, "4: 100 Mbps / Full Duplex\n"); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_tx_desc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Transmit Descriptors", + "Sets the number of Tx descriptors " + "available for the adapter", + E100_DEFAULT_TCB, E100_MIN_TCB, E100_MAX_TCB); + return generic_read(page, start, off, count, eof, len); +} + +static int +read_rx_desc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Receive Descriptors", + "Sets the number of Rx descriptors " + "available for the adapter", + E100_DEFAULT_RFD, E100_MIN_RFD, E100_MAX_RFD); + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ber_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "Bit Error Rate", + "Sets the value for the BER correction algorithm", + E100_DEFAULT_BER, 0, ZLOCK_MAX_ERRORS); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_xsum_rx_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "RX Checksum", + "Setting this value to \"On\" enables " + "receive checksum", E100_DEFAULT_XSUM); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ucode_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Microcode", + "Setting this value to \"On\" enables " + "the adapter's microcode", E100_DEFAULT_UCODE); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_bundle_small_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Bundle Small Frames", + "Setting this value to \"On\" enables " + "interrupt bundling of small frames", + E100_DEFAULT_BUNDLE_SMALL_FR); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_fc_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Flow Control", + "Setting this value to \"On\" enables processing " + "flow-control packets", E100_DEFAULT_FC); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_rcv_cong_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "Receive Congestion Control", + "Setting this value to \"On\" enables switching " + "to polling mode on receive", + E100_DEFAULT_RX_CONGESTION_CONTROL); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_poll_max_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + + int len; + + len = read_int_param(page, "Maximum Polling Work", + "Sets the max number of RX packets processed" + " by single polling function call", + bdp->params.RxDescriptors, 1, E100_MAX_RFD); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_int_delay_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "CPU Saver Interrupt Delay", + "Sets the value for CPU saver's interrupt delay", + E100_DEFAULT_CPUSAVER_INTERRUPT_DELAY, 0x0, + 0xFFFF); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_bundle_max_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_int_param(page, "CPU Saver Maximum Bundle", + "Sets the value for CPU saver's maximum value", + E100_DEFAULT_CPUSAVER_BUNDLE_MAX, 0x1, 0xFFFF); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_ifs_def(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = read_bool_param(page, "IFS", + "Setting this value to \"On\" enables " + "the adaptive IFS algorithm", E100_DEFAULT_IFS); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_xsum_rx_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_XSUMRX) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_ucode_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_UCODE) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_fc_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_FC) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_ifs_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_IFS) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_bundle_small_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_BUNDLE_SMALL) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_rcv_cong_val(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct e100_private *bdp = data; + unsigned long val; + + val = (bdp->params.b_params & PRM_RX_CONG) ? 1 : 0; + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_gen_prm(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int val = 0; + + if (data) + val = *((int *) data); + + return read_ulong(page, start, off, count, eof, (unsigned long) val); +} + +static e100_proc_entry e100_proc_params[] = { + /* definitions */ + {"e100_speed_duplex.def", read_speed_duplex_def, 0, 0}, + {"RxDescriptors.def", read_rx_desc_def, 0, 0}, + {"TxDescriptors.def", read_tx_desc_def, 0, 0}, + {"XsumRX.def", read_xsum_rx_def, 0, 0}, + {"ucode.def", read_ucode_def, 0, 0}, + {"BundleSmallFr.def", read_bundle_small_def, 0, 0}, + {"IntDelay.def", read_int_delay_def, 0, 0}, + {"BundleMax.def", read_bundle_max_def, 0, 0}, + {"ber.def", read_ber_def, 0, 0}, + {"flow_control.def", read_fc_def, 0, 0}, + {"IFS.def", read_ifs_def, 0, 0}, + {"RxCongestionControl.def", read_rcv_cong_def, 0, 0}, + {"PollingMaxWork.def", read_poll_max_def, 0, 0}, + /* values */ + {"e100_speed_duplex.val", read_gen_prm, 0, bdp_prm_off(e100_speed_duplex)}, + {"RxDescriptors.val", read_gen_prm, 0, bdp_prm_off(RxDescriptors)}, + {"TxDescriptors.val", read_gen_prm, 0, bdp_prm_off(TxDescriptors)}, + {"XsumRX.val", read_xsum_rx_val, 0, 0}, + {"ucode.val", read_ucode_val, 0, 0}, + {"BundleSmallFr.val", read_bundle_small_val, 0, 0}, + {"IntDelay.val", read_gen_prm, 0, bdp_prm_off(IntDelay)}, + {"BundleMax.val", read_gen_prm, 0, bdp_prm_off(BundleMax)}, + {"ber.val", read_gen_prm, 0, bdp_prm_off(ber)}, + {"flow_control.val", read_fc_val, 0, 0}, + {"IFS.val", read_ifs_val, 0, 0}, + {"RxCongestionControl.val", read_rcv_cong_val, 0, 0}, + {"PollingMaxWork.val", read_gen_prm, 0, bdp_prm_off(PollingMaxWork)}, + {"", 0, 0, 0} +}; +#endif /* E100_EOU */ + +static struct proc_dir_entry * __devinit +create_proc_rw(char *name, void *data, struct proc_dir_entry *parent, + read_proc_t * read_proc, write_proc_t * write_proc) +{ + struct proc_dir_entry *pdep; + mode_t mode = S_IFREG; + + if (write_proc) { + mode |= S_IWUSR; + if (read_proc) { + mode |= S_IRUSR; + } + + } else if (read_proc) { + mode |= S_IRUGO; + } + + if (!(pdep = create_proc_entry(name, mode, parent))) + return NULL; + + pdep->read_proc = read_proc; + pdep->write_proc = write_proc; + pdep->data = data; + return pdep; +} + +#ifdef E100_EOU +static int __devinit +create_proc_param_subdir(struct e100_private *bdp, + struct proc_dir_entry *dev_dir) +{ + struct proc_dir_entry *param_dir; + e100_proc_entry *pe; + void *data; + + param_dir = create_proc_entry("LoadParameters", S_IFDIR, dev_dir); + if (!param_dir) + return -ENOMEM; + + for (pe = e100_proc_params; pe->name[0]; pe++) { + + data = ((char *) bdp) + pe->offset; + + if (!(create_proc_rw(pe->name, data, param_dir, + pe->read_proc, pe->write_proc))) { + return -ENOMEM; + } + } + + return 0; +} + +static void +remove_proc_param_subdir(struct proc_dir_entry *parent) +{ + struct proc_dir_entry *de; + e100_proc_entry *pe; + int len; + + len = strlen("LoadParameters"); + + for (de = parent->subdir; de; de = de->next) { + if ((de->namelen == len) && + (!memcmp(de->name, "LoadParameters", len))) + break; + } + + if (!de) + return; + + for (pe = e100_proc_params; pe->name[0]; pe++) { + remove_proc_entry(pe->name, de); + } + + remove_proc_entry("LoadParameters", parent); +} +#endif /* E100_EOU */ + +void +e100_remove_proc_subdir(struct e100_private *bdp) +{ + e100_proc_entry *pe; + char info[256]; + int len; + + /* If our root /proc dir was not created, there is nothing to remove */ + if (adapters_proc_dir == NULL) { + return; + } + + len = strlen(bdp->device->name); + strncpy(info, bdp->device->name, sizeof (info)); + strncat(info + len, ".info", sizeof (info) - len); + + if (bdp->proc_parent) { + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + remove_proc_entry(pe->name, bdp->proc_parent); + } + +#ifdef E100_EOU + remove_proc_param_subdir(bdp->proc_parent); +#endif + remove_proc_entry(bdp->device->name, adapters_proc_dir); + bdp->proc_parent = NULL; + } + + remove_proc_entry(info, adapters_proc_dir); + + /* try to remove the main /proc dir, if it's empty */ + e100_proc_cleanup(); +} + +int __devinit +e100_create_proc_subdir(struct e100_private *bdp) +{ + struct proc_dir_entry *dev_dir; + e100_proc_entry *pe; + char info[256]; + int len; + void *data; + + /* create the main /proc dir if needed */ + if (!adapters_proc_dir) { + if (!e100_init_proc_dir()) + return -ENOMEM; + } + + strncpy(info, bdp->device->name, sizeof (info)); + len = strlen(info); + strncat(info + len, ".info", sizeof (info) - len); + + /* info */ + if (!(create_proc_rw(info, bdp, adapters_proc_dir, read_info, 0))) { + e100_proc_cleanup(); + return -ENOMEM; + } + + dev_dir = create_proc_entry(bdp->device->name, S_IFDIR, + adapters_proc_dir); + bdp->proc_parent = dev_dir; + + if (!dev_dir) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } + + for (pe = e100_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + if (pe->offset != ~0) + data = ((char *) bdp) + pe->offset; + else + data = NULL; + + if (!(create_proc_rw(pe->name, data, dev_dir, + pe->read_proc, pe->write_proc))) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } + } + +#ifdef E100_EOU + if (create_proc_param_subdir(bdp, dev_dir)) { + e100_remove_proc_subdir(bdp); + return -ENOMEM; + } +#endif + + return 0; +} + +/**************************************************************************** + * Name: e100_init_proc_dir + * + * Description: This routine creates the top-level /proc directory for the + * driver in /proc/net + * + * Arguments: none + * + * Returns: true on success, false on fail + * + ***************************************************************************/ +static unsigned char +e100_init_proc_dir(void) +{ + int len; + + /* first check if adapters_proc_dir already exists */ + len = strlen(ADAPTERS_PROC_DIR); + for (adapters_proc_dir = proc_net->subdir; + adapters_proc_dir; adapters_proc_dir = adapters_proc_dir->next) { + + if ((adapters_proc_dir->namelen == len) && + (!memcmp(adapters_proc_dir->name, ADAPTERS_PROC_DIR, len))) + break; + } + + if (!adapters_proc_dir) + adapters_proc_dir = + create_proc_entry(ADAPTERS_PROC_DIR, S_IFDIR, proc_net); + + if (!adapters_proc_dir) + return false; + + return true; +} + +/**************************************************************************** + * Name: e100_proc_cleanup + * + * Description: This routine clears the top-level /proc directory, if empty. + * + * Arguments: none + * + * Returns: none + * + ***************************************************************************/ +static void +e100_proc_cleanup(void) +{ + struct proc_dir_entry *de; + + if (adapters_proc_dir == NULL) { + return; + } + + /* check if subdir list is empty before removing adapters_proc_dir */ + for (de = adapters_proc_dir->subdir; de; de = de->next) { + /* ignore . and .. */ + if (*(de->name) != '.') + break; + } + + if (de) + return; + + remove_proc_entry(ADAPTERS_PROC_DIR, proc_net); + adapters_proc_dir = NULL; +} + +#endif /* CONFIG_PROC_FS */ diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_ucode.h linux-2.5.6/drivers/net/e100/e100_ucode.h --- linux-2.5.6-pre3/drivers/net/e100/e100_ucode.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_ucode.h Thu Mar 7 18:24:45 2002 @@ -0,0 +1,411 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _E100_UCODE_H_ +#define _E100_UCODE_H_ + +/* +e100_ucode.h + +This file contains the loadable micro code arrays to implement receive +bundling on the D101 A-step, D101 B-step, D101M (B-step only), D101S, +D102 B-step, D102 B-step with TCO work around and D102 C-step. + +Each controller has its own specific micro code array. The array for one +controller is totally incompatible with any other controller, and if used +will most likely cause the controller to lock up and stop responding to +the driver. Each micro code array has its own parameter offsets (described +below), and they each have their own version number. +*/ + +/************************************************************************* +* CPUSaver parameters +* +* All CPUSaver parameters are 16-bit literals that are part of a +* "move immediate value" instruction. By changing the value of +* the literal in the instruction before the code is loaded, the +* driver can change algorithm. +* +* CPUSAVER_DWORD - This is the location of the instruction that loads +* the dead-man timer with its inital value. By writing a 16-bit +* value to the low word of this instruction, the driver can change +* the timer value. The current default is either x600 or x800; +* experiments show that the value probably should stay within the +* range of x200 - x1000. +* +* CPUSAVER_BUNDLE_MAX_DWORD - This is the location of the instruction +* that sets the maximum number of frames that will be bundled. In +* some situations, such as the TCP windowing algorithm, it may be +* better to limit the growth of the bundle size than let it go as +* high as it can, because that could cause too much added latency. +* The default is six, because this is the number of packets in the +* default TCP window size. A value of 1 would make CPUSaver indicate +* an interrupt for every frame received. If you do not want to put +* a limit on the bundle size, set this value to xFFFF. +* +* CPUSAVER_MIN_SIZE_DWORD - This is the location of the instruction +* that contains a bit-mask describing the minimum size frame that +* will be bundled. The default masks the lower 7 bits, which means +* that any frame less than 128 bytes in length will not be bundled, +* but will instead immediately generate an interrupt. This does +* not affect the current bundle in any way. Any frame that is 128 +* bytes or large will be bundled normally. This feature is meant +* to provide immediate indication of ACK frames in a TCP environment. +* Customers were seeing poor performance when a machine with CPUSaver +* enabled was sending but not receiving. The delay introduced when +* the ACKs were received was enough to reduce total throughput, because +* the sender would sit idle until the ACK was finally seen. +* +* The current default is 0xFF80, which masks out the lower 7 bits. +* This means that any frame which is x7F (127) bytes or smaller +* will cause an immediate interrupt. Because this value must be a +* bit mask, there are only a few valid values that can be used. To +* turn this feature off, the driver can write the value xFFFF to the +* lower word of this instruction (in the same way that the other +* parameters are used). Likewise, a value of 0xF800 (2047) would +* cause an interrupt to be generated for every frame, because all +* standard Ethernet frames are <= 2047 bytes in length. +*************************************************************************/ + +#ifndef UCODE_MAX_DWORDS +#define UCODE_MAX_DWORDS 134 +#endif + +/********************************************************/ +/* CPUSaver micro code for the D101A */ +/********************************************************/ + +/* Version 2.0 */ + +/* This value is the same for both A and B step of 558. */ + +#define D101_CPUSAVER_TIMER_DWORD 72 +#define D101_CPUSAVER_BUNDLE_DWORD UCODE_MAX_DWORDS +#define D101_CPUSAVER_MIN_SIZE_DWORD UCODE_MAX_DWORDS + +#define D101_A_RCVBUNDLE_UCODE \ +{\ +0x03B301BB, 0x0046FFFF, 0xFFFFFFFF, 0x051DFFFF, 0xFFFFFFFF, 0xFFFFFFFF, \ +0x000C0001, 0x00101212, 0x000C0008, 0x003801BC, \ +0x00000000, 0x00124818, 0x000C1000, 0x00220809, \ +0x00010200, 0x00124818, 0x000CFFFC, 0x003803B5, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B81D, 0x00130836, 0x000C0001, \ +0x0026081C, 0x0020C81B, 0x00130824, 0x00222819, \ +0x00101213, 0x00041000, 0x003A03B3, 0x00010200, \ +0x00101B13, 0x00238081, 0x00213049, 0x0038003B, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B83E, 0x00130826, 0x000C0001, \ +0x0026083B, 0x00010200, 0x00134824, 0x000C0001, \ +0x00101213, 0x00041000, 0x0038051E, 0x00101313, \ +0x00010400, 0x00380521, 0x00050600, 0x00100824, \ +0x00101310, 0x00041000, 0x00080600, 0x00101B10, \ +0x0038051E, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101B */ +/********************************************************/ + +/* Version 2.0 */ + +#define D101_B0_RCVBUNDLE_UCODE \ +{\ +0x03B401BC, 0x0047FFFF, 0xFFFFFFFF, 0x051EFFFF, 0xFFFFFFFF, 0xFFFFFFFF, \ +0x000C0001, 0x00101B92, 0x000C0008, 0x003801BD, \ +0x00000000, 0x00124818, 0x000C1000, 0x00220809, \ +0x00010200, 0x00124818, 0x000CFFFC, 0x003803B6, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B81D, 0x0013082F, 0x000C0001, \ +0x0026081C, 0x0020C81B, 0x00130837, 0x00222819, \ +0x00101B93, 0x00041000, 0x003A03B4, 0x00010200, \ +0x00101793, 0x00238082, 0x0021304A, 0x0038003C, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x0024B83E, 0x00130826, 0x000C0001, \ +0x0026083B, 0x00010200, 0x00134837, 0x000C0001, \ +0x00101B93, 0x00041000, 0x0038051F, 0x00101313, \ +0x00010400, 0x00380522, 0x00050600, 0x00100837, \ +0x00101310, 0x00041000, 0x00080600, 0x00101790, \ +0x0038051F, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101M (B-step only) */ +/********************************************************/ + +/* Version 2.10.1 */ + +/* Parameter values for the D101M B-step */ +#define D101M_CPUSAVER_TIMER_DWORD 78 +#define D101M_CPUSAVER_BUNDLE_DWORD 65 +#define D101M_CPUSAVER_MIN_SIZE_DWORD 126 + +#define D101M_B_RCVBUNDLE_UCODE \ +{\ +0x00550215, 0xFFFF0437, 0xFFFFFFFF, 0x06A70789, 0xFFFFFFFF, 0x0558FFFF, \ +0x000C0001, 0x00101312, 0x000C0008, 0x00380216, \ +0x0010009C, 0x00204056, 0x002380CC, 0x00380056, \ +0x0010009C, 0x00244C0B, 0x00000800, 0x00124818, \ +0x00380438, 0x00000000, 0x00140000, 0x00380555, \ +0x00308000, 0x00100662, 0x00100561, 0x000E0408, \ +0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ +0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ +0x000C007E, 0x00222C21, 0x000C0002, 0x00103093, \ +0x00380C7A, 0x00080000, 0x00103090, 0x00380C7A, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x00244C2D, 0x00010004, 0x00041000, \ +0x003A0437, 0x00044010, 0x0038078A, 0x00000000, \ +0x00100099, 0x00206C7A, 0x0010009C, 0x00244C48, \ +0x00130824, 0x000C0001, 0x00101213, 0x00260C75, \ +0x00041000, 0x00010004, 0x00130826, 0x000C0006, \ +0x002206A8, 0x0013C926, 0x00101313, 0x003806A8, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ +0x00101210, 0x00380C34, 0x00000000, 0x00000000, \ +0x0021155B, 0x00100099, 0x00206559, 0x0010009C, \ +0x00244559, 0x00130836, 0x000C0000, 0x00220C62, \ +0x000C0001, 0x00101B13, 0x00229C0E, 0x00210C0E, \ +0x00226C0E, 0x00216C0E, 0x0022FC0E, 0x00215C0E, \ +0x00214C0E, 0x00380555, 0x00010004, 0x00041000, \ +0x00278C67, 0x00040800, 0x00018100, 0x003A0437, \ +0x00130826, 0x000C0001, 0x00220559, 0x00101313, \ +0x00380559, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00130831, 0x0010090B, 0x00124813, \ +0x000CFF80, 0x002606AB, 0x00041000, 0x00010004, \ +0x003806A8, 0x00000000, 0x00000000, 0x00000000, \ +} + +/********************************************************/ +/* CPUSaver micro code for the D101S */ +/********************************************************/ + +/* Version 1.20.1 */ + +/* Parameter values for the D101S */ +#define D101S_CPUSAVER_TIMER_DWORD 78 +#define D101S_CPUSAVER_BUNDLE_DWORD 67 +#define D101S_CPUSAVER_MIN_SIZE_DWORD 128 + +#define D101S_RCVBUNDLE_UCODE \ +{\ +0x00550242, 0xFFFF047E, 0xFFFFFFFF, 0x06FF0818, 0xFFFFFFFF, 0x05A6FFFF, \ +0x000C0001, 0x00101312, 0x000C0008, 0x00380243, \ +0x0010009C, 0x00204056, 0x002380D0, 0x00380056, \ +0x0010009C, 0x00244F8B, 0x00000800, 0x00124818, \ +0x0038047F, 0x00000000, 0x00140000, 0x003805A3, \ +0x00308000, 0x00100610, 0x00100561, 0x000E0408, \ +0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ +0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ +0x000C007E, 0x00222FA1, 0x000C0002, 0x00103093, \ +0x00380F90, 0x00080000, 0x00103090, 0x00380F90, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0010009C, 0x00244FAD, 0x00010004, 0x00041000, \ +0x003A047E, 0x00044010, 0x00380819, 0x00000000, \ +0x00100099, 0x00206FFD, 0x0010009A, 0x0020AFFD, \ +0x0010009C, 0x00244FC8, 0x00130824, 0x000C0001, \ +0x00101213, 0x00260FF7, 0x00041000, 0x00010004, \ +0x00130826, 0x000C0006, 0x00220700, 0x0013C926, \ +0x00101313, 0x00380700, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ +0x00101210, 0x00380FB6, 0x00000000, 0x00000000, \ +0x002115A9, 0x00100099, 0x002065A7, 0x0010009A, \ +0x0020A5A7, 0x0010009C, 0x002445A7, 0x00130836, \ +0x000C0000, 0x00220FE4, 0x000C0001, 0x00101B13, \ +0x00229F8E, 0x00210F8E, 0x00226F8E, 0x00216F8E, \ +0x0022FF8E, 0x00215F8E, 0x00214F8E, 0x003805A3, \ +0x00010004, 0x00041000, 0x00278FE9, 0x00040800, \ +0x00018100, 0x003A047E, 0x00130826, 0x000C0001, \ +0x002205A7, 0x00101313, 0x003805A7, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00130831, \ +0x0010090B, 0x00124813, 0x000CFF80, 0x00260703, \ +0x00041000, 0x00010004, 0x00380700 \ +} + +/********************************************************/ +/* CPUSaver micro code for the D102 B-step */ +/********************************************************/ + +/* Version 2.0 */ +/* Parameter values for the D102 B-step */ +#define D102_B_CPUSAVER_TIMER_DWORD 82 +#define D102_B_CPUSAVER_BUNDLE_DWORD 106 +#define D102_B_CPUSAVER_MIN_SIZE_DWORD 70 + +#define D102_B_RCVBUNDLE_UCODE \ +{\ +0x006F0276, 0x0EF71FFF, 0x0ED30F86, 0x0D250ED9, 0x1FFF1FFF, 0x1FFF04D2, \ +0x00300001, 0x0140D871, 0x00300008, 0x00E00277, \ +0x01406C57, 0x00816073, 0x008700FA, 0x00E00070, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x01406CBA, 0x00807F9A, 0x00901F9A, 0x0024FFFF, \ +0x014B6F6F, 0x0030FFFE, 0x01407172, 0x01496FBA, \ +0x014B6F72, 0x00308000, 0x01406C52, 0x00912EFC, \ +0x00E00EF8, 0x00000000, 0x00000000, 0x00000000, \ +0x00906F8C, 0x00900F8C, 0x00E00F87, 0x00000000, \ +0x00906ED8, 0x01406C55, 0x00E00ED4, 0x00000000, \ +0x01406C51, 0x0080DFC2, 0x01406C52, 0x00815FC2, \ +0x01406C57, 0x00917FCC, 0x00E01FDD, 0x00000000, \ +0x00822D30, 0x01406C51, 0x0080CD26, 0x01406C52, \ +0x00814D26, 0x01406C57, 0x00916D26, 0x014C6FD7, \ +0x00300000, 0x00841FD2, 0x00300001, 0x0140D772, \ +0x00E012B3, 0x014C6F91, 0x0150710B, 0x01496F72, \ +0x0030FF80, 0x00940EDD, 0x00102000, 0x00038400, \ +0x00E00EDA, 0x00000000, 0x00000000, 0x00000000, \ +0x01406C57, 0x00917FE9, 0x00001000, 0x00E01FE9, \ +0x00200600, 0x0140D76F, 0x00138400, 0x01406FD8, \ +0x0140D96F, 0x00E01FDD, 0x00038400, 0x00102000, \ +0x00971FD7, 0x00101000, 0x00050200, 0x00E804D2, \ +0x014C6FD8, 0x00300001, 0x00840D26, 0x0140D872, \ +0x00E00D26, 0x014C6FD9, 0x00300001, 0x0140D972, \ +0x00941FBD, 0x00102000, 0x00038400, 0x014C6FD8, \ +0x00300006, 0x00840EDA, 0x014F71D8, 0x0140D872, \ +0x00E00EDA, 0x01496F50, 0x00E004D3, 0x00000000, \ +} + +/********************************************************/ +/* Micro code for the D102 C-step */ +/********************************************************/ + +/* Parameter values for the D102 C-step */ +#define D102_C_CPUSAVER_TIMER_DWORD 46 +#define D102_C_CPUSAVER_BUNDLE_DWORD 74 +#define D102_C_CPUSAVER_MIN_SIZE_DWORD 54 + +#define D102_C_RCVBUNDLE_UCODE \ +{ \ +0x00700279, 0x0E6604E2, 0x02BF0CAE, 0x1508150C, 0x15190E5B, 0x0E840F13, \ +0x00E014D8, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014DC, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014F4, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014E0, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014E7, 0x00000000, 0x00000000, 0x00000000, \ +0x00141000, 0x015D6F0D, 0x00E002C0, 0x00000000, \ +0x00200600, 0x00E0150D, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x0030FF80, 0x00940E6A, 0x00038200, 0x00102000, \ +0x00E00E67, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00906E65, 0x00800E60, 0x00E00E5D, 0x00000000, \ +0x00300006, 0x00E0151A, 0x00000000, 0x00000000, \ +0x00906F19, 0x00900F19, 0x00E00F14, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x01406CBA, 0x00807FDA, 0x00901FDA, 0x0024FFFF, \ +0x014B6F6F, 0x0030FFFE, 0x01407172, 0x01496FBA, \ +0x014B6F72, 0x00308000, 0x01406C52, 0x00912E89, \ +0x00E00E85, 0x00000000, 0x00000000, 0x00000000 \ +} + +/********************************************************/ +/* Micro code for the D102 E-step */ +/********************************************************/ + +/* Parameter values for the D102 E-step */ +#define D102_E_CPUSAVER_TIMER_DWORD 42 +#define D102_E_CPUSAVER_BUNDLE_DWORD 54 +#define D102_E_CPUSAVER_MIN_SIZE_DWORD 46 + +#define D102_E_RCVBUNDLE_UCODE \ +{\ +0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x1FFF1FFF, 0x1FFF1FFF, \ +0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014C1, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00E014C8, 0x00000000, 0x00000000, 0x00000000, \ +0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \ +0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \ +0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \ +0x00300006, 0x00E014FB, 0x00000000, 0x00000000 \ +} + +#endif /* _E100_UCODE_H_ */ diff -urN linux-2.5.6-pre3/drivers/net/e100/e100_vendor.h linux-2.5.6/drivers/net/e100/e100_vendor.h --- linux-2.5.6-pre3/drivers/net/e100/e100_vendor.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/e100/e100_vendor.h Thu Mar 7 18:24:45 2002 @@ -0,0 +1,348 @@ +/******************************************************************************* + +This software program is available to you under a choice of one of two +licenses. You may choose to be licensed under either the GNU General Public +License (GPL) Version 2, June 1991, available at +http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License, the +text of which follows: + +Recipient has requested a license and Intel Corporation ("Intel") is willing +to grant a license for the software entitled Linux Base Driver for the +Intel(R) PRO/100 Family of Adapters (e100) (the "Software") being provided +by Intel Corporation. The following definitions apply to this license: + +"Licensed Patents" means patent claims licensable by Intel Corporation which +are necessarily infringed by the use of sale of the Software alone or when +combined with the operating system referred to below. + +"Recipient" means the party to whom Intel delivers this Software. + +"Licensee" means Recipient and those third parties that receive a license to +any operating system available under the GNU Public License version 2.0 or +later. + +Copyright (c) 1999 - 2002 Intel Corporation. +All rights reserved. + +The license is provided to Recipient and Recipient's Licensees under the +following terms. + +Redistribution and use in source and binary forms of the Software, with or +without modification, are permitted provided that the following conditions +are met: + +Redistributions of source code of the Software may retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form of the Software may reproduce the above +copyright notice, this list of conditions and the following disclaimer in +the documentation and/or materials provided with the distribution. + +Neither the name of Intel Corporation nor the names of its contributors +shall be used to endorse or promote products derived from this Software +without specific prior written permission. + +Intel hereby grants Recipient and Licensees a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, offer +to sell, import and otherwise transfer the Software, if any, in source code +and object code form. This license shall include changes to the Software +that are error corrections or other minor changes to the Software that do +not add functionality or features when the Software is incorporated in any +version of an operating system that has been distributed under the GNU +General Public License 2.0 or later. This patent license shall apply to the +combination of the Software and any operating system licensed under the GNU +Public License version 2.0 or later if, at the time Intel provides the +Software to Recipient, such addition of the Software to the then publicly +available versions of such operating systems available under the GNU Public +License version 2.0 or later (whether in gold, beta or alpha form) causes +such combination to be covered by the Licensed Patents. The patent license +shall not apply to any other combinations which include the Software. NO +hardware per se is licensed hereunder. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef E100_VENDOR_ID_INFO +#define E100_VENDOR_ID_INFO +/* ====================================================================== */ +/* vendor_info */ +/* ====================================================================== */ + +struct e100_vendor_info { + unsigned long device_type; + char *idstr; +}; + +enum e100_device_type { + E100_BRD_100TX = 1, + E100_BRD_100T4, + E100_BRD_10T, + E100_BRD_100WFM, + E100_BRD_82557, + E100_BRD_82557_WOL, + E100_BRD_82558, + E100_BRD_82558_WOL, + E100_BRD_100, + E100_BRD_100M, + E100_BRD_AOL2, + E100_BRD_AOL, + E100_PROS_M, + E100_PROS_AM, + E100_PROS_AM_AOL, + E100_PROS_DT, + E100_PRO_DT, + E100_PROM_DT, + E100_PRO_SRV, + E100_PRO_SRVP, + E100_PROS_SRV, + E100_PRO_DUAL, + E100_PROS_DUAL, + E100_PROP_DUAL, + E100_PROP_WOL, + E100_PROS_MOB, + E100_PRO_CB, + E100_PRO_CB_M, + E100_PROSR_MOB, + E100_PROS_MC, + E100_PROSR_MC, + E100_PROP_MC, + E100_PROSP_MC, + E100_PROP_MOB, + E100_PROSP_MOB, + E100_PRO_MINI, + E100_PRO_NET, + E100_PROS_NET, + E100_PROVM_NET, + E100_PROVE_D, + E100_82559_LOM, + E100_82559_LOM_AOL, + E100_82559_LOM_AOL2, + E100_IBM_MDS, + E100_CMPQ_S, + E100_PROVE_DA, + E100_PROVM_DA, + E100_PROVE_LOM, + E100_PROVE_NET, + E100_82562, + E100_ALL_BOARDS, +}; + +struct e100_vendor_info e100_vendor_info_array[] = { + { E100_BRD_100TX, "Intel(R) PRO/100B PCI Adapter (TX)"}, + { E100_BRD_100T4, "Intel(R) PRO/100B PCI Adapter (T4)"}, + { E100_BRD_10T, "Intel(R) PRO/10+ PCI Adapter"}, + { E100_BRD_100WFM, "Intel(R) PRO/100 WfM PCI Adapter"}, + { E100_BRD_82557, "Intel(R) 82557-based Integrated Ethernet PCI (10/100)"}, + { E100_BRD_82557_WOL, "Intel(R) 82557-based Integrated Ethernet with Wake on LAN*"}, + { E100_BRD_82558, "Intel(R) 82558-based Integrated Ethernet"}, + { E100_BRD_82558_WOL, "Intel(R) 82558-based Integrated Ethernet with Wake on LAN*"}, + { E100_BRD_100, "Intel(R) PRO/100+ PCI Adapter"}, + { E100_BRD_100M, "Intel(R) PRO/100+ Management Adapter"}, + { E100_BRD_AOL2, "Intel(R) PRO/100+ Alert on LAN* 2 Management Adapter"}, + { E100_BRD_AOL, "Intel(R) PRO/100+ Alert on LAN* Management Adapter"}, + { E100_PROS_M, "Intel(R) PRO/100 S Management Adapter"}, + { E100_PROS_AM, "Intel(R) PRO/100 S Advanced Management Adapter"}, + { E100_PROS_AM_AOL, "Intel(R) PRO/100+ Management Adapter with Alert On LAN* GC"}, + { E100_PROS_DT, "Intel(R) PRO/100 S Desktop Adapter"}, + { E100_PRO_DT, "Intel(R) PRO/100 Desktop Adapter"}, + { E100_PROM_DT, "Intel(R) PRO/100 M Desktop Adapter"}, + { E100_PRO_SRV, "Intel(R) PRO/100+ Server Adapter"}, + { E100_PRO_SRVP, "Intel(R) PRO/100+ Server Adapter (PILA8470B)"}, + { E100_PROS_SRV, "Intel(R) PRO/100 S Server Adapter"}, + { E100_PRO_DUAL, "Intel(R) PRO/100 Dual Port Server Adapter"}, + { E100_PROS_DUAL, "Intel(R) PRO/100 S Dual Port Server Adapter"}, + { E100_PROP_DUAL, "Intel(R) PRO/100+ Dual Port Server Adapter"}, + { E100_PROP_WOL, "Intel(R) PRO/100+ Management Adapter with Alert On LAN* G Server"}, + { E100_PROS_MOB, "Intel(R) PRO/100 S Mobile Adapter"}, + { E100_PRO_CB, "Intel(R) PRO/100 CardBus II"}, + { E100_PRO_CB_M, "Intel(R) PRO/100 LAN+Modem56 CardBus II"}, + { E100_PROSR_MOB, "Intel(R) PRO/100 SR Mobile Adapter"}, + { E100_PROS_MC, "Intel(R) PRO/100 S Mobile Combo Adapter"}, + { E100_PROSR_MC, "Intel(R) PRO/100 SR Mobile Combo Adapter"}, + { E100_PROP_MC, "Intel(R) PRO/100 P Mobile Combo Adapter"}, + { E100_PROSP_MC, "Intel(R) PRO/100 SP Mobile Combo Adapter"}, + { E100_PROP_MOB, "Intel(R) PRO/100 P Mobile Adapter"}, + { E100_PROSP_MOB, "Intel(R) PRO/100 SP Mobile Adapter"}, + { E100_PRO_MINI, "Intel(R) PRO/100+ Mini PCI"}, + { E100_PRO_NET, "Intel(R) PRO/100 Network Connection" }, + { E100_PROS_NET, "Intel(R) PRO/100 S Network Connection" }, + { E100_PROVM_NET, "Intel(R) PRO/100 VM Network Connection"}, + { E100_PROVE_D, "Intel(R) PRO/100 VE Desktop Connection"}, + { E100_82559_LOM, "Intel(R) 82559 Fast Ethernet LAN on Motherboard"}, + { E100_82559_LOM_AOL, "Intel(R) 82559 Fast Ethernet LOM with Alert on LAN*" }, + { E100_82559_LOM_AOL2, "Intel(R) 82559 Fast Ethernet LOM with Alert on LAN* 2" }, + { E100_IBM_MDS, "IBM Mobile, Desktop & Server Adapters"}, + { E100_CMPQ_S, "Compaq Fast Ethernet Server Adapter" }, + { E100_PROVE_DA, "Intel(R) PRO/100 VE Desktop Adapter"}, + { E100_PROVM_DA, "Intel(R) PRO/100 VM Desktop Adapter"}, + { E100_PROVE_LOM, "Intel(R) PRO/100 VE Network ConnectionPLC LOM" }, + { E100_PROVE_NET, "Intel(R) PRO/100 VE Network Connection"}, + { E100_82562, "Intel(R)82562 based Fast Ethernet Connection"}, + { E100_ALL_BOARDS, "Intel(R) 8255x-based Ethernet Adapter"}, + {0,NULL} +}; + +static struct pci_device_id e100_id_table[] __devinitdata = { + {0x8086, 0x1229, 0x8086, 0x0001, 0, 0, E100_BRD_100TX}, + {0x8086, 0x1229, 0x8086, 0x0002, 0, 0, E100_BRD_100T4}, + {0x8086, 0x1229, 0x8086, 0x0003, 0, 0, E100_BRD_10T}, + {0x8086, 0x1229, 0x8086, 0x0004, 0, 0, E100_BRD_100WFM}, + {0x8086, 0x1229, 0x8086, 0x0005, 0, 0, E100_BRD_82557}, + {0x8086, 0x1229, 0x8086, 0x0006, 0, 0, E100_BRD_82557_WOL}, + {0x8086, 0x1229, 0x8086, 0x0002, 0, 0, E100_BRD_100T4}, + {0x8086, 0x1229, 0x8086, 0x0003, 0, 0, E100_BRD_10T}, + {0x8086, 0x1229, 0x8086, 0x0004, 0, 0, E100_BRD_100WFM}, + {0x8086, 0x1229, 0x8086, 0x0005, 0, 0, E100_BRD_82557}, + {0x8086, 0x1229, 0x8086, 0x0006, 0, 0, E100_BRD_82557_WOL}, + {0x8086, 0x1229, 0x8086, 0x0007, 0, 0, E100_BRD_82558}, + {0x8086, 0x1229, 0x8086, 0x0008, 0, 0, E100_BRD_82558_WOL}, + {0x8086, 0x1229, 0x8086, 0x0009, 0, 0, E100_BRD_100}, + {0x8086, 0x1229, 0x8086, 0x000A, 0, 0, E100_BRD_100M}, + {0x8086, 0x1229, 0x8086, 0x000B, 0, 0, E100_BRD_100}, + {0x8086, 0x1229, 0x8086, 0x000C, 0, 0, E100_BRD_100M}, + {0x8086, 0x1229, 0x8086, 0x000D, 0, 0, E100_BRD_AOL2}, + {0x8086, 0x1229, 0x8086, 0x000E, 0, 0, E100_BRD_AOL}, + {0x8086, 0x1229, 0x8086, 0x0010, 0, 0, E100_PROS_M}, + {0x8086, 0x1229, 0x8086, 0x0011, 0, 0, E100_PROS_M}, + {0x8086, 0x1229, 0x8086, 0x0012, 0, 0, E100_PROS_AM}, + {0x8086, 0x1229, 0x8086, 0x0013, 0, 0, E100_PROS_AM}, + {0x8086, 0x1229, 0x8086, 0x0030, 0, 0, E100_PROS_AM_AOL}, + {0x8086, 0x1229, 0x8086, 0x0040, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0041, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0042, 0, 0, E100_PRO_DT}, + {0x8086, 0x1229, 0x8086, 0x0050, 0, 0, E100_PROS_DT}, + {0x8086, 0x1229, 0x8086, 0x0070, 0, 0, E100_PROM_DT}, + {0x8086, 0x1229, 0x8086, 0x1009, 0, 0, E100_PRO_SRV}, + {0x8086, 0x1229, 0x8086, 0x100C, 0, 0, E100_PRO_SRVP}, + {0x8086, 0x1229, 0x8086, 0x1012, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1013, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1014, 0, 0, E100_PRO_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1015, 0, 0, E100_PROS_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1016, 0, 0, E100_PROS_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1017, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x1030, 0, 0, E100_PROP_WOL}, + {0x8086, 0x1229, 0x8086, 0x1040, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1041, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x1042, 0, 0, E100_PRO_SRV}, + {0x8086, 0x1229, 0x8086, 0x1050, 0, 0, E100_PROS_SRV}, + {0x8086, 0x1229, 0x8086, 0x10F0, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x10F0, 0, 0, E100_PROP_DUAL}, + {0x8086, 0x1229, 0x8086, 0x2009, 0, 0, E100_PROS_MOB}, + {0x8086, 0x1229, 0x8086, 0x200D, 0, 0, E100_PRO_CB}, + {0x8086, 0x1229, 0x8086, 0x200E, 0, 0, E100_PRO_CB_M}, + {0x8086, 0x1229, 0x8086, 0x200F, 0, 0, E100_PROSR_MOB}, + {0x8086, 0x1229, 0x8086, 0x2010, 0, 0, E100_PROS_MC}, + {0x8086, 0x1229, 0x8086, 0x2013, 0, 0, E100_PROSR_MC}, + {0x8086, 0x1229, 0x8086, 0x2016, 0, 0, E100_PROS_MOB}, + {0x8086, 0x1229, 0x8086, 0x2017, 0, 0, E100_PROS_MC}, + {0x8086, 0x1229, 0x8086, 0x2018, 0, 0, E100_PROSR_MOB}, + {0x8086, 0x1229, 0x8086, 0x2019, 0, 0, E100_PROSR_MC}, + {0x8086, 0x1229, 0x8086, 0x2101, 0, 0, E100_PROP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2102, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2103, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2104, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2105, 0, 0, E100_PROSP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2106, 0, 0, E100_PROP_MOB}, + {0x8086, 0x1229, 0x8086, 0x2107, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x2108, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x2200, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2201, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2202, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2203, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2204, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2205, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2206, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2207, 0, 0, E100_PROSP_MC}, + {0x8086, 0x1229, 0x8086, 0x2208, 0, 0, E100_PROP_MC}, + {0x8086, 0x1229, 0x8086, 0x2408, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x240F, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x2411, 0, 0, E100_PRO_MINI}, + {0x8086, 0x1229, 0x8086, 0x3400, 0, 0, E100_82559_LOM}, + {0x8086, 0x1229, 0x8086, 0x3000, 0, 0, E100_82559_LOM}, + {0x8086, 0x1229, 0x8086, 0x3001, 0, 0, E100_82559_LOM_AOL}, + {0x8086, 0x1229, 0x8086, 0x3002, 0, 0, E100_82559_LOM_AOL2}, + {0x8086, 0x1229, 0x8086, 0x3006, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3007, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3008, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x8086, 0x3010, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3011, 0, 0, E100_PROS_NET}, + {0x8086, 0x1229, 0x8086, 0x3012, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x005C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x305C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x405C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x605C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x505C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x105C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x805C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x705C, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x01F1, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x0232, 0, 0, E100_IBM_MDS}, + {0x8086, 0x1229, 0x1014, 0x0207, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x023F, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01BC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01CE, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01DC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01EB, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x01EC, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0202, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0205, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x1014, 0x0217, 0, 0, E100_PRO_NET}, + {0x8086, 0x1229, 0x0E11, 0xB01E, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB02F, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB04A, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0C6, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0C7, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0D7, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0DD, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0DE, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB0E1, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB134, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB13C, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB144, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB163, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, 0x0E11, 0xB164, 0, 0, E100_CMPQ_S}, + {0x8086, 0x1229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + + {0x8086, 0x2449, 0x1014, 0x0265, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x1014, 0x0267, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x1014, 0x026A, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, 0x8086, 0x3010, 0, 0, E100_PROVE_DA}, + {0x8086, 0x2449, 0x8086, 0x3011, 0, 0, E100_PROVM_DA}, + {0x8086, 0x2449, 0x8086, 0x3013, 0, 0, E100_PROVE_NET}, + {0x8086, 0x2449, 0x8086, 0x3014, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2449, 0x8086, 0x3016, 0, 0, E100_PROP_MC}, + {0x8086, 0x2449, 0x8086, 0x3017, 0, 0, E100_PROP_MOB}, + {0x8086, 0x2449, 0x8086, 0x3018, 0, 0, E100_PRO_NET}, + {0x8086, 0x2449, 0x0E11, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2449, 0x1014, PCI_ANY_ID, 0, 0, E100_PROVE_D}, + {0x8086, 0x2449, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + + {0x8086, 0x1209, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS}, + {0x8086, 0x1031, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x1032, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x1033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1034, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1038, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x1039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x103C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x103D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET}, + {0x8086, 0x103E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET}, + {0x8086, 0x2459, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562}, + {0x8086, 0x245D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562}, + {0,} /* This has to be the last entry*/ +}; + +#endif /* E100_VENDOR_ID_INFO */ diff -urN linux-2.5.6-pre3/drivers/net/e1000/e1000_main.c linux-2.5.6/drivers/net/e1000/e1000_main.c --- linux-2.5.6-pre3/drivers/net/e1000/e1000_main.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/e1000/e1000_main.c Thu Mar 7 18:24:45 2002 @@ -128,7 +128,7 @@ "IBM Mobile, Desktop & Server Adapters" }; -/* Local Function Prototypes */ +/* e1000_main.c Function Prototypes */ int e1000_up(struct e1000_adapter *adapter); void e1000_down(struct e1000_adapter *adapter); @@ -193,23 +193,6 @@ MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); MODULE_LICENSE("Dual BSD/GPL"); -#ifdef EXPORT_SYMTAB -EXPORT_SYMBOL(e1000_init_module); -EXPORT_SYMBOL(e1000_exit_module); -EXPORT_SYMBOL(e1000_probe); -EXPORT_SYMBOL(e1000_remove); -EXPORT_SYMBOL(e1000_open); -EXPORT_SYMBOL(e1000_close); -EXPORT_SYMBOL(e1000_xmit_frame); -EXPORT_SYMBOL(e1000_intr); -EXPORT_SYMBOL(e1000_set_multi); -EXPORT_SYMBOL(e1000_change_mtu); -EXPORT_SYMBOL(e1000_set_mac); -EXPORT_SYMBOL(e1000_get_stats); -EXPORT_SYMBOL(e1000_watchdog); -EXPORT_SYMBOL(e1000_ioctl); -#endif - /** * e1000_init_module - Driver Registration Routine * diff -urN linux-2.5.6-pre3/drivers/net/e2100.c linux-2.5.6/drivers/net/e2100.c --- linux-2.5.6-pre3/drivers/net/e2100.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/drivers/net/e2100.c Thu Mar 7 18:24:45 2002 @@ -388,10 +388,12 @@ MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_E21_CARDS) "i"); -MODULE_PARM_DESC(io, "E2100 I/O base address(es)"); -MODULE_PARM_DESC(irq, "E2100 IRQ number(s)"); -MODULE_PARM_DESC(mem, " E2100 memory base address(es)"); -MODULE_PARM_DESC(xcvr, "E2100 tranceiver(s) (0=internal, 1=external)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, " memory base address(es)"); +MODULE_PARM_DESC(xcvr, "tranceiver(s) (0=internal, 1=external)"); +MODULE_DESCRIPTION("Cabletron E2100 ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -440,7 +442,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/eepro100.c linux-2.5.6/drivers/net/eepro100.c --- linux-2.5.6-pre3/drivers/net/eepro100.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/eepro100.c Thu Mar 7 18:24:45 2002 @@ -130,17 +130,17 @@ MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(multicast_filter_limit, "i"); -MODULE_PARM_DESC(debug, "eepro100 debug level (0-6)"); -MODULE_PARM_DESC(options, "eepro100: Bits 0-3: tranceiver type, bit 4: full duplex, bit 5: 100Mbps"); -MODULE_PARM_DESC(full_duplex, "eepro100 full duplex setting(s) (1)"); -MODULE_PARM_DESC(congenb, "eepro100 Enable congestion control (1)"); -MODULE_PARM_DESC(txfifo, "eepro100 Tx FIFO threshold in 4 byte units, (0-15)"); -MODULE_PARM_DESC(rxfifo, "eepro100 Rx FIFO threshold in 4 byte units, (0-15)"); -MODULE_PARM_DESC(txdmaccount, "eepro100 Tx DMA burst length; 128 - disable (0-128)"); -MODULE_PARM_DESC(rxdmaccount, "eepro100 Rx DMA burst length; 128 - disable (0-128)"); -MODULE_PARM_DESC(rx_copybreak, "eepro100 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(max_interrupt_work, "eepro100 maximum events handled per interrupt"); -MODULE_PARM_DESC(multicast_filter_limit, "eepro100 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(debug, "debug level (0-6)"); +MODULE_PARM_DESC(options, "Bits 0-3: tranceiver type, bit 4: full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); +MODULE_PARM_DESC(congenb, "Enable congestion control (1)"); +MODULE_PARM_DESC(txfifo, "Tx FIFO threshold in 4 byte units, (0-15)"); +MODULE_PARM_DESC(rxfifo, "Rx FIFO threshold in 4 byte units, (0-15)"); +MODULE_PARM_DESC(txdmaccount, "Tx DMA burst length; 128 - disable (0-128)"); +MODULE_PARM_DESC(rxdmaccount, "Rx DMA burst length; 128 - disable (0-128)"); +MODULE_PARM_DESC(rx_copybreak, "copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); +MODULE_PARM_DESC(multicast_filter_limit, "maximum number of filtered multicast addresses"); #define RUN_AT(x) (jiffies + (x)) diff -urN linux-2.5.6-pre3/drivers/net/es3210.c linux-2.5.6/drivers/net/es3210.c --- linux-2.5.6-pre3/drivers/net/es3210.c Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/es3210.c Thu Mar 7 18:24:46 2002 @@ -383,9 +383,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_ES_CARDS) "i"); -MODULE_PARM_DESC(io, "ES3210 I/O base address(es)"); -MODULE_PARM_DESC(irq, "ES3210 IRQ number(s)"); -MODULE_PARM_DESC(mem, "ES3210 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("Racal-Interlan ES3210 EISA ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) @@ -429,5 +431,4 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -urN linux-2.5.6-pre3/drivers/net/hamradio/6pack.c linux-2.5.6/drivers/net/hamradio/6pack.c --- linux-2.5.6-pre3/drivers/net/hamradio/6pack.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/hamradio/6pack.c Thu Mar 7 18:24:46 2002 @@ -256,6 +256,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); + dev->last_rx = jiffies; sp->stats.rx_packets++; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/baycom_epp.c linux-2.5.6/drivers/net/hamradio/baycom_epp.c --- linux-2.5.6-pre3/drivers/net/hamradio/baycom_epp.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/net/hamradio/baycom_epp.c Thu Mar 7 18:24:46 2002 @@ -706,6 +706,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; bc->stats.rx_packets++; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/bpqether.c linux-2.5.6/drivers/net/hamradio/bpqether.c --- linux-2.5.6-pre3/drivers/net/hamradio/bpqether.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/net/hamradio/bpqether.c Thu Mar 7 18:24:46 2002 @@ -254,6 +254,7 @@ skb->pkt_type = PACKET_HOST; netif_rx(skb); + dev->last_rx = jiffies; return 0; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/hdlcdrv.c linux-2.5.6/drivers/net/hamradio/hdlcdrv.c --- linux-2.5.6-pre3/drivers/net/hamradio/hdlcdrv.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/hamradio/hdlcdrv.c Thu Mar 7 18:24:46 2002 @@ -223,6 +223,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; s->stats.rx_packets++; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/mkiss.c linux-2.5.6/drivers/net/hamradio/mkiss.c --- linux-2.5.6-pre3/drivers/net/hamradio/mkiss.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/drivers/net/hamradio/mkiss.c Thu Mar 7 18:24:46 2002 @@ -345,6 +345,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); + dev->last_rx = jiffies; tmp_ax->rx_packets++; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/scc.c linux-2.5.6/drivers/net/hamradio/scc.c --- linux-2.5.6-pre3/drivers/net/hamradio/scc.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/drivers/net/hamradio/scc.c Thu Mar 7 18:24:46 2002 @@ -1661,6 +1661,7 @@ skb->pkt_type = PACKET_HOST; netif_rx(skb); + dev->last_rx = jiffies; return; } diff -urN linux-2.5.6-pre3/drivers/net/hamradio/yam.c linux-2.5.6/drivers/net/hamradio/yam.c --- linux-2.5.6-pre3/drivers/net/hamradio/yam.c Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/hamradio/yam.c Thu Mar 7 18:24:46 2002 @@ -528,6 +528,7 @@ skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); + dev->last_rx = jiffies; ++yp->stats.rx_packets; } } diff -urN linux-2.5.6-pre3/drivers/net/hp-plus.c linux-2.5.6/drivers/net/hp-plus.c --- linux-2.5.6-pre3/drivers/net/hp-plus.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/drivers/net/hp-plus.c Thu Mar 7 18:24:46 2002 @@ -408,8 +408,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i"); -MODULE_PARM_DESC(io, "HP PC-LAN+ I/O port address(es)"); -MODULE_PARM_DESC(irq, "HP PC-LAN+ IRQ number(s); ignored if properly detected"); +MODULE_PARM_DESC(io, "I/O port address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected"); +MODULE_DESCRIPTION("HP PC-LAN+ ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -457,7 +459,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/hp.c linux-2.5.6/drivers/net/hp.c --- linux-2.5.6-pre3/drivers/net/hp.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/hp.c Thu Mar 7 18:24:46 2002 @@ -380,8 +380,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HP_CARDS) "i"); -MODULE_PARM_DESC(io, "HP PC-LAN I/O base address(es)"); -MODULE_PARM_DESC(irq, "HP PC-LAN IRQ number(s) (assigned)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_DESCRIPTION("HP PC-LAN ISA ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -429,7 +431,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/irda/ali-ircc.c linux-2.5.6/drivers/net/irda/ali-ircc.c --- linux-2.5.6-pre3/drivers/net/irda/ali-ircc.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/drivers/net/irda/ali-ircc.c Thu Mar 7 18:24:46 2002 @@ -1941,6 +1941,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } diff -urN linux-2.5.6-pre3/drivers/net/irda/irda-usb.c linux-2.5.6/drivers/net/irda/irda-usb.c --- linux-2.5.6-pre3/drivers/net/irda/irda-usb.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/irda/irda-usb.c Thu Mar 7 18:24:46 2002 @@ -839,6 +839,7 @@ new->mac.raw = new->data; new->protocol = htons(ETH_P_IRDA); netif_rx(new); + self->netdev->last_rx = jiffies; done: /* Note : at this point, the URB we've just received (urb) diff -urN linux-2.5.6-pre3/drivers/net/irda/nsc-ircc.c linux-2.5.6/drivers/net/irda/nsc-ircc.c --- linux-2.5.6-pre3/drivers/net/irda/nsc-ircc.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/net/irda/nsc-ircc.c Thu Mar 7 18:24:46 2002 @@ -1570,6 +1570,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } /* Restore bank register */ diff -urN linux-2.5.6-pre3/drivers/net/irda/smc-ircc.c linux-2.5.6/drivers/net/irda/smc-ircc.c --- linux-2.5.6-pre3/drivers/net/irda/smc-ircc.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/net/irda/smc-ircc.c Thu Mar 7 18:24:46 2002 @@ -957,6 +957,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } /* diff -urN linux-2.5.6-pre3/drivers/net/irda/toshoboe.c linux-2.5.6/drivers/net/irda/toshoboe.c --- linux-2.5.6-pre3/drivers/net/irda/toshoboe.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/net/irda/toshoboe.c Thu Mar 7 18:24:46 2002 @@ -433,8 +433,10 @@ self->rxs++; self->rxs %= RX_SLOTS; - if (skb) + if (skb) { netif_rx (skb); + self->netdev->last_rx = jiffies; + } } diff -urN linux-2.5.6-pre3/drivers/net/irda/vlsi_ir.c linux-2.5.6/drivers/net/irda/vlsi_ir.c --- linux-2.5.6-pre3/drivers/net/irda/vlsi_ir.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/irda/vlsi_ir.c Thu Mar 7 18:24:46 2002 @@ -637,6 +637,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + ndev->last_rx = jiffies; } else { idev->stats.rx_dropped++; diff -urN linux-2.5.6-pre3/drivers/net/irda/w83977af_ir.c linux-2.5.6/drivers/net/irda/w83977af_ir.c --- linux-2.5.6-pre3/drivers/net/irda/w83977af_ir.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/drivers/net/irda/w83977af_ir.c Thu Mar 7 18:24:46 2002 @@ -923,6 +923,7 @@ skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); + self->netdev->last_rx = jiffies; } } /* Restore set register */ diff -urN linux-2.5.6-pre3/drivers/net/lne390.c linux-2.5.6/drivers/net/lne390.c --- linux-2.5.6-pre3/drivers/net/lne390.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/drivers/net/lne390.c Thu Mar 7 18:24:46 2002 @@ -381,9 +381,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_LNE_CARDS) "i"); -MODULE_PARM_DESC(io, "LNE390 I/O base address(es)"); -MODULE_PARM_DESC(irq, "LNE390 IRQ number(s)"); -MODULE_PARM_DESC(mem, "LNE390 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("Mylex LNE390A/B EISA Ethernet driver"); MODULE_LICENSE("GPL"); int init_module(void) diff -urN linux-2.5.6-pre3/drivers/net/lp486e.c linux-2.5.6/drivers/net/lp486e.c --- linux-2.5.6-pre3/drivers/net/lp486e.c Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/lp486e.c Thu Mar 7 18:24:46 2002 @@ -676,6 +676,7 @@ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); + dev->last_rx = jiffies; lp->stats.rx_packets++; } else { #if 0 diff -urN linux-2.5.6-pre3/drivers/net/ne.c linux-2.5.6/drivers/net/ne.c --- linux-2.5.6-pre3/drivers/net/ne.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/net/ne.c Thu Mar 7 18:24:46 2002 @@ -737,9 +737,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); -MODULE_PARM_DESC(io, "NEx000 I/O base address(es),required"); -MODULE_PARM_DESC(irq, "NEx000 IRQ number(s)"); -MODULE_PARM_DESC(bad, "NEx000 accept bad clone(s)"); +MODULE_PARM_DESC(io, "I/O base address(es),required"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures"); +MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); /* This is set up so that no ISA autoprobe takes place. We can't guarantee that the ne2k probe is the last 8390 based probe to take place (as it @@ -791,7 +793,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); /* diff -urN linux-2.5.6-pre3/drivers/net/ne2k-pci.c linux-2.5.6/drivers/net/ne2k-pci.c --- linux-2.5.6-pre3/drivers/net/ne2k-pci.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/ne2k-pci.c Thu Mar 7 18:24:46 2002 @@ -82,9 +82,9 @@ MODULE_PARM(debug, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(debug, "PCI NE2000 debug level (1-2)"); -MODULE_PARM_DESC(options, "PCI NE2000: Bit 5: full duplex"); -MODULE_PARM_DESC(full_duplex, "PCI NE2000 full duplex setting(s) (1)"); +MODULE_PARM_DESC(debug, "debug level (1-2)"); +MODULE_PARM_DESC(options, "Bit 5: full duplex"); +MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); /* Some defines that people can play with if so inclined. */ diff -urN linux-2.5.6-pre3/drivers/net/ne3210.c linux-2.5.6/drivers/net/ne3210.c --- linux-2.5.6-pre3/drivers/net/ne3210.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/drivers/net/ne3210.c Thu Mar 7 18:24:46 2002 @@ -370,9 +370,11 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i"); -MODULE_PARM_DESC(io, "NE3210 I/O base address(es)"); -MODULE_PARM_DESC(irq, "NE3210 IRQ number(s)"); -MODULE_PARM_DESC(mem, "NE3210 memory base address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(mem, "memory base address(es)"); +MODULE_DESCRIPTION("NE3210 EISA Ethernet driver"); +MODULE_LICENSE("GPL"); int init_module(void) { @@ -415,7 +417,6 @@ } } } -MODULE_LICENSE("GPL"); #endif /* MODULE */ diff -urN linux-2.5.6-pre3/drivers/net/pcmcia/Config.in linux-2.5.6/drivers/net/pcmcia/Config.in --- linux-2.5.6-pre3/drivers/net/pcmcia/Config.in Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/pcmcia/Config.in Thu Mar 7 18:24:47 2002 @@ -20,11 +20,6 @@ dep_tristate ' IBM PCMCIA tokenring adapter support' CONFIG_PCMCIA_IBMTR $CONFIG_TR $CONFIG_PCMCIA fi - if [ "$CONFIG_CARDBUS" = "y" ]; then - tristate ' Xircom CardBus support (new driver)' CONFIG_PCMCIA_XIRCOM - tristate ' Xircom Tulip-like CardBus support (old driver)' CONFIG_PCMCIA_XIRTULIP - fi - 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 diff -urN linux-2.5.6-pre3/drivers/net/pcmcia/Makefile linux-2.5.6/drivers/net/pcmcia/Makefile --- linux-2.5.6-pre3/drivers/net/pcmcia/Makefile Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/net/pcmcia/Makefile Thu Mar 7 18:24:47 2002 @@ -29,11 +29,6 @@ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o -# Cardbus client drivers -obj-$(CONFIG_PCMCIA_XIRTULIP) += xircom_tulip_cb.o -obj-$(CONFIG_PCMCIA_XIRCOM) += xircom_cb.o - obj-$(CONFIG_PCMCIA_IBMTR) += ibmtr_cs.o include $(TOPDIR)/Rules.make - diff -urN linux-2.5.6-pre3/drivers/net/pcmcia/xircom_cb.c linux-2.5.6/drivers/net/pcmcia/xircom_cb.c --- linux-2.5.6-pre3/drivers/net/pcmcia/xircom_cb.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/pcmcia/xircom_cb.c Wed Dec 31 16:00:00 1969 @@ -1,1411 +0,0 @@ -/* - * xircom_cb: A driver for the (tulip-like) Xircom Cardbus ethernet cards - * - * This software is Copyright 2001 by the respective authors, and licensed under the GPL - * License. - * - * Written by Arjan van de Ven for Red Hat, Inc. - * Based on work by Jeff Garzik, Doug Ledford, Donald Becker and Ion Badulescu - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * - * $Id: xircom_cb.c,v 1.11 2001/06/05 09:50:57 fenrus Exp $ - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - - - -#ifdef DEBUG -#define enter() printk("Enter: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__) -#define leave() printk("Leave: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__) -#else -#define enter() do {} while (0) -#define leave() do {} while (0) -#endif - - -MODULE_DESCRIPTION("Xircom Cardbus ethernet driver"); -MODULE_AUTHOR("Arjan van de Ven "); -MODULE_LICENSE("GPL"); - - - -/* IO registers on the card, offsets */ -#define CSR0 0x00 -#define CSR1 0x08 -#define CSR2 0x10 -#define CSR3 0x18 -#define CSR4 0x20 -#define CSR5 0x28 -#define CSR6 0x30 -#define CSR7 0x38 -#define CSR8 0x40 -#define CSR9 0x48 -#define CSR10 0x50 -#define CSR11 0x58 -#define CSR12 0x60 -#define CSR13 0x68 -#define CSR14 0x70 -#define CSR15 0x78 -#define CSR16 0x80 - -/* PCI registers */ -#define PCI_POWERMGMT 0x40 - -/* Offsets of the buffers within the descriptor pages, in bytes */ - -#define NUMDESCRIPTORS 4 -#define RXTXBUFSIZE 8192 -#define MAX_PACKETSIZE 1536 - - -#define DescOwnedCard 0x80000000 -#define DescOwnedDriver 0x00000000 - -#define PromiscBit (1<<6) -#define CollisionBit (1<<8) -#define TxActiveBit (1<<13) -#define RxActiveBit (1<<1) -#define LastDescBit (1<<25) -#define LinkStatusBit (1<<27) - -#define PowerMgmtBits ( (1<<31)|(1<<30) ) - -static const unsigned int bufferoffsets[NUMDESCRIPTORS] = {128,2048,4096,6144}; - -/* note: this struct is assumed to be packed as this is the "hardware" layout */ -struct descriptor { - u32 status; - u32 control; - u32 address1; - u32 address2; -}; - - -struct xircom_private { - /* Send and receive buffers, kernel-addressable and dma addressable forms */ - - unsigned char *rx_buffer; - unsigned char *tx_buffer; - - struct descriptor *rx_desc; - struct descriptor *tx_desc; - - dma_addr_t rx_dma_handle; - dma_addr_t tx_dma_handle; - - struct sk_buff *tx_skb[NUMDESCRIPTORS]; - - unsigned long io_port; - - /* transmit_used is the rotating counter that indicates which transmit - descriptor has to be used next */ - unsigned int transmit_used; - - /* Spinlock to serialize register operations. - It must be helt while manipulating the following registers: - CSR0, CSR6, CSR7, CSR9, CSR10, CSR15 - */ - spinlock_t lock; - - - struct pci_dev *pdev; - struct net_device *dev; - struct net_device_stats stats; -}; - - -/* Function prototypes */ -static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id); -static void xircom_remove(struct pci_dev *pdev); -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int xircom_open(struct net_device *dev); -static int xircom_close(struct net_device *dev); -static void xircom_up(struct xircom_private *card); -static struct net_device_stats *xircom_get_stats(struct net_device *dev); - -static void investigate_rx_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset); -static unsigned int investigate_tx_descriptor(struct net_device *dev, struct xircom_private *card, unsigned int descnr, unsigned int bufferoffset); -static void read_mac_address(struct xircom_private *card); -static void tranceiver_voodoo(struct xircom_private *card); -static void initialize_card(struct xircom_private *card); -static inline void trigger_transmit(struct xircom_private *card); -static inline void trigger_receive(struct xircom_private *card); -static void setup_descriptors(struct xircom_private *card); -static inline void remove_descriptors(struct xircom_private *card); -static inline unsigned int link_status_changed(struct xircom_private *card); -static void activate_receiver(struct xircom_private *card); -static void deactivate_receiver(struct xircom_private *card); -static void activate_transmitter(struct xircom_private *card); -static void deactivate_transmitter(struct xircom_private *card); -static void enable_transmit_interrupt(struct xircom_private *card); -static void enable_receive_interrupt(struct xircom_private *card); -static void enable_link_interrupt(struct xircom_private *card); -static void disable_all_interrupts(struct xircom_private *card); -static inline unsigned int link_status(struct xircom_private *card); -static int mdio_read(struct xircom_private *card, int phy_id, int location); -static void mdio_write(struct xircom_private *card, int phy_id, int location, int value); - - - -static struct pci_device_id xircom_pci_table[] __devinitdata = { - {0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID,}, - {0,}, -}; -MODULE_DEVICE_TABLE(pci, xircom_pci_table); - -static struct pci_driver xircom_ops = { - name: "xircom_cb", - id_table: xircom_pci_table, - probe: xircom_probe, - remove: __devexit_p(xircom_remove), -}; - - -#ifdef DEBUG -static void print_binary(unsigned int number) -{ - int i,i2; - char buffer[64]; - memset(buffer,0,64); - i2=0; - for (i=31;i>=0;i--) { - if (number & (1<priv; - if (private==NULL) { - printk(KERN_ERR "xircom_probe: failed to allocate private device struct\n"); - return -ENODEV; - } - - /* Allocate the send/receive buffers */ - private->rx_buffer = pci_alloc_consistent(pdev,RXTXBUFSIZE,&private->rx_dma_handle); - if (private->rx_buffer == NULL) { - printk(KERN_ERR "xircom_probe: no memory for rx buffer \n"); - kfree(private); - return -ENODEV; - } - /* the descriptors are stored in the first bytes of the rx_buffer, hence the ugly cast */ - private->rx_desc = (struct descriptor *)private->rx_buffer; - - private->tx_buffer = pci_alloc_consistent(pdev,RXTXBUFSIZE,&private->tx_dma_handle); - if (private->tx_buffer == NULL) { - printk(KERN_ERR "xircom_probe: no memory for tx buffer \n"); - kfree(private->rx_buffer); - kfree(private); - return -ENODEV; - } - /* the descriptors are stored in the first bytes of the tx_buffer, hence the ugly cast */ - private->tx_desc = (struct descriptor *)private->tx_buffer; - - - printk(KERN_INFO "%s: Xircom cardbus revision %i at irq %i \n", dev->name, chip_rev, pdev->irq); - - private->dev = dev; - private->pdev = pdev; - private->io_port = pci_resource_start(pdev, 0); - private->lock = SPIN_LOCK_UNLOCKED; - dev->irq = pdev->irq; - dev->base_addr = private->io_port; - - - initialize_card(private); - read_mac_address(private); - setup_descriptors(private); - - dev->open = &xircom_open; - dev->hard_start_xmit = &xircom_start_xmit; - dev->stop = &xircom_close; - dev->get_stats = &xircom_get_stats; - dev->priv = private; - pci_set_drvdata(pdev,dev); - - - /* start the transmitter to get a heartbeat; don't do - that when there already is one though; Cisco's - really don't like that. */ - if (!link_status(private)) - tranceiver_voodoo(private); - - spin_lock_irqsave(&private->lock,flags); - activate_transmitter(private); - activate_receiver(private); - spin_unlock_irqrestore(&private->lock,flags); - - /* TODO: send 2 dummy packets here */ - - trigger_receive(private); - - leave(); - return 0; -} - - -/* - xircom_remove is called on module-unload or on device-eject. - it unregisters the irq, io-region and network device. - Interrupts and such are already stopped in the "ifconfig ethX down" - code. - */ -static void __devexit xircom_remove(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *card; - enter(); - - card=dev->priv; - - if (card->rx_buffer!=NULL) - pci_free_consistent(pdev,RXTXBUFSIZE,card->rx_buffer,card->rx_dma_handle); - card->rx_buffer = NULL; - card->rx_desc = NULL; - if (card->tx_buffer!=NULL) - pci_free_consistent(pdev,RXTXBUFSIZE,card->tx_buffer,card->tx_dma_handle); - card->tx_buffer = NULL; - card->tx_desc = NULL; - - release_region(dev->base_addr, 128); - unregister_netdev(dev); - kfree(dev); - pci_set_drvdata(pdev,NULL); - leave(); -} - -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = dev_instance; - struct xircom_private *card = dev->priv; - u32 status; - unsigned int xmit_free_count; - unsigned int i; - - enter(); - - - spin_lock(&card->lock); - status = inl(card->io_port+CSR5); - if (status==0xffffffff) {/* card has been ejected / powered down */ - spin_unlock(&card->lock); - return; - } - - /* Todo: check if there were any events at all; to speed up - returning if we're on a shared interrupt */ - - if (link_status_changed(card)) { - int newlink; - printk(KERN_DEBUG "xircom_cb: Link status has changed \n"); - newlink = link_status(card); - if (newlink) { - printk(KERN_INFO "xircom_cb: Link is %i mbit \n",newlink); - netif_carrier_on(dev); - } else { - printk(KERN_INFO "xircom_cb: Link is absent \n"); - netif_carrier_off(dev); - } - } - - /* Clear all remaining interrupt events */ - status |= 0xffffffff; /* FIXME: make this clear only the - real existing bits */ - outl(status,card->io_port+CSR5); - - xmit_free_count = 0; - - for (i=0;ilock); - leave(); -} - -static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xircom_private *card; - unsigned long flags; - unsigned int nextdescriptor; - unsigned int desc; - enter(); - - card = (struct xircom_private*)dev->priv; - - spin_lock_irqsave(&card->lock,flags); - - nextdescriptor = (card->transmit_used +1) % (NUMDESCRIPTORS); - desc = card->transmit_used; - - /* only send the packet if the descriptor is free */ - if (card->tx_desc[desc].status==0) { - /* Copy the packet data; zero the memory first as the card - sometimes sends more than you ask it to. */ - - memset(&card->tx_buffer[bufferoffsets[desc]],0,MAX_PACKETSIZE); - memcpy(&(card->tx_buffer[bufferoffsets[desc]]),skb->data,skb->len); - - - /* FIXME: The specification tells us that the length we send HAS to be a multiple of - 4 bytes. */ - - card->tx_desc[desc].control = skb->len; - if (desc == NUMDESCRIPTORS-1) - card->tx_desc[desc].control |= LastDescBit; /* bit 25: last descriptor of the ring */ - - card->tx_desc[desc].control |= 0xF0000000; - /* 0xF0... means want interrupts*/ - card->tx_skb[desc] = skb; - - wmb(); - /* This gives the descriptor to the card */ - card->tx_desc[desc].status = DescOwnedCard; - trigger_transmit(card); - if (((int)card->tx_desc[nextdescriptor].status)<0) { /* next descriptor is occupied... */ - netif_stop_queue(dev); - } - card->transmit_used = nextdescriptor; - spin_unlock_irqrestore(&card->lock,flags); - leave(); - return 0; - } - - - - /* Uh oh... no free descriptor... drop the packet */ - /* This should not happen in theory...*/ - netif_stop_queue(dev); - spin_unlock_irqrestore(&card->lock,flags); - trigger_transmit(card); - leave(); - - return -EIO; -} - - - - -static int xircom_open(struct net_device *dev) -{ - struct xircom_private *xp = (struct xircom_private *) dev->priv; - int retval; - enter(); - printk(KERN_INFO "Xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq); - retval = request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) { - printk(KERN_ERR "xircom_cb: Unable to aquire IRQ %i, aborting.\n",dev->irq); - leave(); - return retval; - } - - xircom_up(xp); - leave(); - return 0; -} - -static int xircom_close(struct net_device *dev) -{ - struct xircom_private *card; - unsigned long flags; - - enter(); - card = dev->priv; - netif_stop_queue(dev); /* we don't want to send new packets */ - - - spin_lock_irqsave(&card->lock,flags); - - disable_all_interrupts(card); -#if 0 - /* We can enable this again once we send dummy packets on ifconfig ethX up */ - deactivate_receiver(card); - deactivate_transmitter(card); -#endif - remove_descriptors(card); - - spin_unlock_irqrestore(&card->lock,flags); - - free_irq(dev->irq,dev); - - leave(); - - return 0; - -} - - - -static struct net_device_stats *xircom_get_stats(struct net_device *dev) -{ - struct xircom_private *card = (struct xircom_private *)dev->priv; - return &card->stats; -} - - - - -static void initialize_card(struct xircom_private *card) -{ - unsigned int val; - unsigned long flags; - enter(); - - - spin_lock_irqsave(&card->lock, flags); - - /* First: reset the card */ - val = inl(card->io_port + CSR0); - val |= 0x01; /* Software reset */ - outl(val, card->io_port + CSR0); - - udelay(100); /* give the card some time to reset */ - - val = inl(card->io_port + CSR0); - val &= ~0x01; /* disable Software reset */ - outl(val, card->io_port + CSR0); - - - val = 0; /* Value 0x00 is a safe and conservative value - for the PCI configuration settings */ - outl(val, card->io_port + CSR0); - - - disable_all_interrupts(card); - deactivate_receiver(card); - deactivate_transmitter(card); - - spin_unlock_irqrestore(&card->lock, flags); - - leave(); -} - -/* -trigger_transmit causes the card to check for frames to be transmitted. -This is accomplished by writing to the CSR1 port. The documentation -claims that the act of writing is sufficient and that the value is -ignored; I chose zero. -*/ -static inline void trigger_transmit(struct xircom_private *card) -{ - enter(); - outl(0, card->io_port + CSR1); - leave(); -} - -/* -trigger_receive causes the card to check for empty frames in the -descriptor list in which packets can be received. -This is accomplished by writing to the CSR2 port. The documentation -claims that the act of writing is sufficient and that the value is -ignored; I chose zero. -*/ -static inline void trigger_receive(struct xircom_private *card) -{ - enter(); - outl(0, card->io_port + CSR2); - leave(); -} - -/* -setup_descriptors initializes the send and receive buffers to be valid -descriptors and programs the addresses into the card. -*/ -static void setup_descriptors(struct xircom_private *card) -{ - unsigned int val; - u32 address; - unsigned int i; - enter(); - - - if (card->rx_buffer == NULL) - BUG(); - if (card->tx_buffer == NULL) - BUG(); - - /* Receive descriptors */ - memset(card->rx_desc, 0, 128); /* clear the descriptors */ - for (i=0;i 0x80000000 */ - card->rx_desc[i].status = DescOwnedCard; - /* Rx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ - card->rx_desc[i].control = MAX_PACKETSIZE; - if (i==NUMDESCRIPTORS-1) - card->rx_desc[i].control |= LastDescBit; /* bit 25 is "last descriptor" */ - - /* Rx Descr2: address of the buffer - we store the buffer at the 2nd half of the page */ - - address = card->rx_dma_handle; - - card->rx_desc[i].address1 = cpu_to_le32(address + bufferoffsets[i]); - /* Rx Desc3: address of 2nd buffer -> 0 */ - card->rx_desc[i].address2 = 0; - } - - wmb(); - /* Write the receive descriptor ring address to the card */ - address = card->rx_dma_handle; - val = cpu_to_le32(address); - outl(val, card->io_port + CSR3); /* Receive descr list address */ - - - /* transmit descriptors */ - memset(card->tx_desc, 0, 128); /* clear the descriptors */ - - for (i=0;i 0x00000000 */ - card->tx_desc[i].status = DescOwnedDriver; - /* Tx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ - card->tx_desc[i].control = MAX_PACKETSIZE; - if (i==NUMDESCRIPTORS-1) - card->tx_desc[i].control |= LastDescBit; /* bit 25 is "last descriptor" */ - - /* Tx Descr2: address of the buffer - we store the buffer at the 2nd half of the page */ - address = card->tx_dma_handle; - card->tx_desc[i].address1 = cpu_to_le32(address + bufferoffsets[i]); - /* Tx Desc3: address of 2nd buffer -> 0 */ - card->tx_desc[i].address2 = 0; - } - - wmb(); - /* wite the transmit descriptor ring to the card */ - address = card->tx_dma_handle; - val =cpu_to_le32(address); - outl(val, card->io_port + CSR4); /* xmit descr list address */ - - leave(); -} - -/* -remove_descriptors informs the card the descriptors are no longer -valid by setting the address in the card to 0x00. -*/ -static inline void remove_descriptors(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = 0; - outl(val, card->io_port + CSR3); /* Receive descriptor address */ - outl(val, card->io_port + CSR4); /* Send descriptor address */ - - leave(); -} - -/* -link_status_changed returns 1 if the card has indicated that -the link status has changed. The new link status has to be read from CSR12. - -This function also clears the status-bit. -*/ -static inline unsigned int link_status_changed(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR5); /* Status register */ - - if ((val & LinkStatusBit) == 0) { /* no change */ - leave(); - return 0; - } - - /* clear the event by writing a 1 to the bit in the - status register. */ - val = LinkStatusBit; - outl(val, card->io_port + CSR5); - - leave(); - return 1; -} - - -/* -transmit_active returns 1 if the transmitter on the card is -in a non-stopped state. -*/ -static inline int transmit_active(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR5); /* Status register */ - - if ((val & (7 << 20)) == 0) { /* transmitter disabled */ - leave(); - return 0; - } - - leave(); - return 1; -} - -/* -receive_active returns 1 if the receiver on the card is -in a non-stopped state. -*/ -static inline unsigned int receive_active(struct xircom_private *card) -{ - unsigned int val; - enter(); - - - val = inl(card->io_port + CSR5); /* Status register */ - - if ((val & (7 << 17)) == 0) { /* receiver disabled */ - leave(); - return 0; - } - - leave(); - return 1; -} - -/* -activate_receiver enables the receiver on the card. -Before being allowed to active the receiver, the receiver -must be completely de-activated. To achieve this, -this code actually disables the receiver first; then it waits for the -receiver to become inactive, then it activates the receiver and then -it waits for the receiver to be active. - -must be called with the lock held and interrupts disabled. -*/ -static void activate_receiver(struct xircom_private *card) -{ - unsigned int val; - int counter; - enter(); - - - val = inl(card->io_port + CSR6); /* Operation mode */ - - /* If the "active" bit (1) is set and the receiver is already - active, no need to do the expensive thing */ - if ((val& RxActiveBit) && (receive_active(card))) - return; - - - val = val & ~RxActiveBit; /* disable the receiver */ - outl(val, card->io_port + CSR6); - - counter = 10; - while (counter > 0) { - if (!receive_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); - } - - /* enable the receiver */ - val = inl(card->io_port + CSR6); /* Operation mode */ - val = val | RxActiveBit; /* enable the receiver */ - outl(val, card->io_port + CSR6); - - /* now wait for the card to activate again */ - counter = 10; - while (counter > 0) { - if (receive_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Receiver failed to re-activate\n"); - } - - leave(); -} - -/* -deactivate_receiver disables the receiver on the card. -To achieve this this code disables the receiver first; -then it waits for the receiver to become inactive. - -must be called with the lock held and interrupts disabled. -*/ -static void deactivate_receiver(struct xircom_private *card) -{ - unsigned int val; - int counter; - enter(); - - val = inl(card->io_port + CSR6); /* Operation mode */ - val = val & ~RxActiveBit; /* disable the receiver */ - outl(val, card->io_port + CSR6); - - counter = 10; - while (counter > 0) { - if (!receive_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); - } - - - leave(); -} - - -/* -activate_transmitter enables the transmitter on the card. -Before being allowed to active the transmitter, the transmitter -must be completely de-activated. To achieve this, -this code actually disables the transmitter first; then it waits for the -transmitter to become inactive, then it activates the transmitter and then -it waits for the transmitter to be active again. - -must be called with the lock held and interrupts disabled. -*/ -static void activate_transmitter(struct xircom_private *card) -{ - unsigned int val; - int counter; - enter(); - - - val = inl(card->io_port + CSR6); /* Operation mode */ - - /* If the "active" bit (13) is set and the receiver is already - active, no need to do the expensive thing */ - if ((val & TxActiveBit) && (transmit_active(card))) - return; - - val = val & ~TxActiveBit; /* disable the transmitter */ - outl(val, card->io_port + CSR6); - - counter = 10; - while (counter > 0) { - if (!transmit_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); - } - - /* enable the transmitter */ - val = inl(card->io_port + CSR6); /* Operation mode */ - val = val | TxActiveBit; /* enable the transmitter */ - outl(val, card->io_port + CSR6); - - /* now wait for the card to activate again */ - counter = 10; - while (counter > 0) { - if (transmit_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Transmitter failed to re-activate\n"); - } - - leave(); -} - -/* -deactivate_transmitter disables the transmitter on the card. -To achieve this this code disables the transmitter first; -then it waits for the transmitter to become inactive. - -must be called with the lock held and interrupts disabled. -*/ -static void deactivate_transmitter(struct xircom_private *card) -{ - unsigned int val; - int counter; - enter(); - - val = inl(card->io_port + CSR6); /* Operation mode */ - val = val & ~TxActiveBit; /* disable the transmitter */ - outl(val, card->io_port + CSR6); - - counter = 20; - while (counter > 0) { - if (!transmit_active(card)) - break; - /* wait a while */ - udelay(50); - counter--; - if (counter <= 0) - printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); - } - - - leave(); -} - - -/* -enable_transmit_interrupt enables the transmit interrupt - -must be called with the lock held and interrupts disabled. -*/ -static void enable_transmit_interrupt(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR7); /* Interrupt enable register */ - val |= 1; /* enable the transmit interrupt */ - outl(val, card->io_port + CSR7); - - leave(); -} - - -/* -enable_receive_interrupt enables the receive interrupt - -must be called with the lock held and interrupts disabled. -*/ -static void enable_receive_interrupt(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR7); /* Interrupt enable register */ - val = val | (1 << 6); /* enable the receive interrupt */ - outl(val, card->io_port + CSR7); - - leave(); -} - -/* -enable_link_interrupt enables the link status change interrupt - -must be called with the lock held and interrupts disabled. -*/ -static void enable_link_interrupt(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR7); /* Interrupt enable register */ - val = val | LinkStatusBit; /* enable the link status chage interrupt */ - outl(val, card->io_port + CSR7); - - leave(); -} - - - -/* -disable_all_interrupts disables all interrupts - -must be called with the lock held and interrupts disabled. -*/ -static void disable_all_interrupts(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = 0; /* disable all interrupts */ - outl(val, card->io_port + CSR7); - - leave(); -} - -/* -enable_common_interrupts enables several weird interrupts - -must be called with the lock held and interrupts disabled. -*/ -static void enable_common_interrupts(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR7); /* Interrupt enable register */ - val |= (1<<16); /* Normal Interrupt Summary */ - val |= (1<<15); /* Abnormal Interrupt Summary */ - val |= (1<<13); /* Fatal bus error */ - val |= (1<<8); /* Receive Process Stopped */ - val |= (1<<7); /* Receive Buffer Unavailable */ - val |= (1<<5); /* Transmit Underflow */ - val |= (1<<2); /* Transmit Buffer Unavailable */ - val |= (1<<1); /* Transmit Process Stopped */ - outl(val, card->io_port + CSR7); - - leave(); -} - -/* -enable_promisc starts promisc mode - -must be called with the lock held and interrupts disabled. -*/ -static inline void enable_promisc(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inl(card->io_port + CSR6); - val = val | PromiscBit; /* Bit 6 */ - outl(val, card->io_port + CSR6); - - printk(KERN_INFO "xircom_cb: enabling promiscuous mode \n"); - leave(); -} - - - - -/* -link_status() checks the the links status and will return 0 for no link, -10 for 10mbit link and 100 for.. guess what. - -Must be called in locked state with interrupts disabled -*/ -static inline unsigned int link_status(struct xircom_private *card) -{ - unsigned int val; - enter(); - - val = inb(card->io_port + CSR12); - - if (!(val&(1<<2))) /* bit 2 is 0 for 10mbit link, 1 for not an 10mbit link */ - return 10; - if (!(val&(1<<1))) /* bit 1 is 0 for 100mbit link, 1 for not an 100mbit link */ - return 100; - - /* If we get here -> no link at all */ - - leave(); - return 0; -} - - - -/* - -set_half_duplex() sets the card to half duplex mode. In order to do this, -set_half_duplex() has to deactivate the transmitter and receiver first. It -will re-enable the transmitter and receiver if those were active from the -beginning. - -Update: the above is not enough. It doesn't touch the MII, in fact it ensures -the main chipset and the MII are never in sync if a full-duplex connection -is negotiated. The proper fix is to tell the MII to force a half-duplex -connection. -Ion - -Must be called in locked state -*/ -static void set_half_duplex(struct xircom_private *card) -{ - unsigned int val; - int rx,tx,tmp; - enter(); - - rx=receive_active(card); - tx=transmit_active(card); - - deactivate_transmitter(card); - deactivate_receiver(card); - - val = inb(card->io_port + CSR6); - val &= ~(1<<9); - outb(val,card->io_port + CSR6); - - /* tell the MII not to advertise 10/100FDX */ - tmp = mdio_read(card, 0, 4); - printk("xircom_cb: capabilities changed from %#x to %#x\n", - tmp, tmp & ~0x140); - tmp &= ~0x140; - mdio_write(card, 0, 4, tmp); - /* restart autonegotiation */ - tmp = mdio_read(card, 0, 0); - mdio_write(card, 0, 0, tmp | 0x1200); - - if (rx) - activate_receiver(card); - if (tx) - activate_transmitter(card); - - leave(); -} - - -/* - read_mac_address() reads the MAC address from the NIC and stores it in the "dev" structure. - - This function will take the spinlock itself and can, as a result, not be called with the lock helt. - */ -static void read_mac_address(struct xircom_private *card) -{ - unsigned char j, tuple, link, data_id, data_count; - unsigned long flags; - int i; - - enter(); - - spin_lock_irqsave(&card->lock, flags); - - outl(1 << 12, card->io_port + CSR9); /* enable boot rom access */ - for (i = 0x100; i < 0x1f7; i += link + 2) { - outl(i, card->io_port + CSR10); - tuple = inl(card->io_port + CSR9) & 0xff; - outl(i + 1, card->io_port + CSR10); - link = inl(card->io_port + CSR9) & 0xff; - outl(i + 2, card->io_port + CSR10); - data_id = inl(card->io_port + CSR9) & 0xff; - outl(i + 3, card->io_port + CSR10); - data_count = inl(card->io_port + CSR9) & 0xff; - if ((tuple == 0x22) && (data_id == 0x04) && (data_count == 0x06)) { - /* - * This is it. We have the data we want. - */ - for (j = 0; j < 6; j++) { - outl(i + j + 4, card->io_port + CSR10); - card->dev->dev_addr[j] = inl(card->io_port + CSR9) & 0xff; - } - break; - } else if (link == 0) { - break; - } - } - spin_unlock_irqrestore(&card->lock, flags); -#ifdef DEBUG - for (i = 0; i < 6; i++) - printk("%c%2.2X", i ? ':' : ' ', card->dev->dev_addr[i]); - printk("\n"); -#endif - leave(); -} - - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ - -/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues or future 66Mhz PCI. */ -#define mdio_delay() inl(mdio_addr) - -/* Read and write the MII registers using software-generated serial - MDIO protocol. It is just different enough from the EEPROM protocol - to not share code. The maxium data clock rate is 2.5 Mhz. */ -#define MDIO_SHIFT_CLK 0x10000 -#define MDIO_DATA_WRITE0 0x00000 -#define MDIO_DATA_WRITE1 0x20000 -#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ -#define MDIO_ENB_IN 0x40000 -#define MDIO_DATA_READ 0x80000 - -static int mdio_read(struct xircom_private *card, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int retval = 0; - long mdio_addr = card->io_port + CSR9; - - /* Establish sync by sending at least 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct xircom_private *card, int phy_id, int location, int value) -{ - int i; - int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - long mdio_addr = card->io_port + CSR9; - - /* Establish sync by sending 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } -} - - -/* - tranceiver_voodoo() enables the external UTP plug thingy. - it's called voodoo as I stole this code and cannot cross-reference - it with the specification. - */ -static void tranceiver_voodoo(struct xircom_private *card) -{ - unsigned long flags; - u32 tmp32; - - enter(); - - /* disable all powermanagement */ - pci_read_config_dword(card->pdev, PCI_POWERMGMT,&tmp32); - tmp32 &= ~PowerMgmtBits; - pci_write_config_dword(card->pdev, PCI_POWERMGMT, tmp32); - - setup_descriptors(card); - - spin_lock_irqsave(&card->lock, flags); - - outl(0x0008, card->io_port + CSR15); - udelay(25); - outl(0xa8050000, card->io_port + CSR15); - udelay(25); - outl(0xa00f0000, card->io_port + CSR15); - udelay(25); - - spin_unlock_irqrestore(&card->lock, flags); - - netif_start_queue(card->dev); - leave(); -} - - -static void xircom_up(struct xircom_private *card) -{ - unsigned long flags; - int i; - u32 tmp32; - - enter(); - - /* disable all powermanagement */ - pci_read_config_dword(card->pdev, PCI_POWERMGMT,&tmp32); - tmp32 &= ~PowerMgmtBits; - pci_write_config_dword(card->pdev, PCI_POWERMGMT, tmp32); - - setup_descriptors(card); - - spin_lock_irqsave(&card->lock, flags); - - - enable_link_interrupt(card); - enable_transmit_interrupt(card); - enable_receive_interrupt(card); - enable_common_interrupts(card); - enable_promisc(card); - - /* The card can have received packets already, read them away now */ - for (i=0;idev,card,i,bufferoffsets[i]); - - - set_half_duplex(card); - spin_unlock_irqrestore(&card->lock, flags); - trigger_receive(card); - trigger_transmit(card); - netif_start_queue(card->dev); - leave(); -} - -static void investigate_rx_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset) -{ - int status; - - enter(); - status = card->rx_desc[descnr].status; - - if ((status > 0)) { /* packet received */ - - /* TODO: discard error packets */ - - short pkt_len = ((status >> 16) & 0x7ff) - 4; /* minus 4, we don't want the CRC */ - struct sk_buff *skb; - - if (pkt_len > 1518) { - printk(KERN_ERR "xircom_cb: Packet length %i is bogus \n",pkt_len); - pkt_len = 1518; - } - - skb = dev_alloc_skb(pkt_len + 2); - if (skb == NULL) { - card->stats.rx_dropped++; - goto out; - } - skb->dev = dev; - skb_reserve(skb, 2); - eth_copy_and_sum(skb, &card->rx_buffer[bufferoffset], pkt_len, 0); - skb_put(skb, pkt_len); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - card->stats.rx_packets++; - card->stats.rx_bytes += pkt_len; - - out: - /* give the buffer back to the card */ - card->rx_desc[descnr].status = DescOwnedCard; - trigger_receive(card); - } - - leave(); - -} - - -/* Returns 1 if the descriptor is free or became free */ -static unsigned int investigate_tx_descriptor(struct net_device *dev, struct xircom_private *card, unsigned int descnr, unsigned int bufferoffset) -{ - int status,retval = 0; - enter(); - - status = card->tx_desc[descnr].status; - - if (status == DescOwnedDriver) - return 1; -#if 0 - if (status & 0x8000) { /* Major error */ - printk(KERN_ERR "Major transmit error status %x \n", status); - card->tx_desc[descnr].status = 0; - netif_wake_queue (dev); - } -#endif - if (status > 0) { /* bit 31 is 0 when done */ - card->stats.tx_packets++; - if (card->tx_skb[descnr]!=NULL) { - card->stats.tx_bytes += card->tx_skb[descnr]->len; - dev_kfree_skb_irq(card->tx_skb[descnr]); - } - card->tx_skb[descnr] = NULL; - /* Bit 8 in the status field is 1 if there was a collision */ - if (status & CollisionBit) - card->stats.collisions++; - card->tx_desc[descnr].status = DescOwnedDriver; /* descriptor is free again */ - retval = 1; - } - - leave(); - return retval; -} - - -static int __init xircom_init(void) -{ - pci_register_driver(&xircom_ops); - return 0; -} - -static void __exit xircom_exit(void) -{ - pci_unregister_driver(&xircom_ops); -} - -module_init(xircom_init) -module_exit(xircom_exit) diff -urN linux-2.5.6-pre3/drivers/net/pcmcia/xircom_tulip_cb.c linux-2.5.6/drivers/net/pcmcia/xircom_tulip_cb.c --- linux-2.5.6-pre3/drivers/net/pcmcia/xircom_tulip_cb.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/pcmcia/xircom_tulip_cb.c Wed Dec 31 16:00:00 1969 @@ -1,1744 +0,0 @@ -/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. */ -/* - Written/copyright 1994-1999 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - ----------------------------------------------------------- - - Linux kernel-specific changes: - - LK1.0 (Ion Badulescu) - - Major cleanup - - Use 2.4 PCI API - - Support ethtool - - Rewrite perfect filter/hash code - - Use interrupts for media changes - - LK1.1 (Ion Badulescu) - - Disallow negotiation of unsupported full-duplex modes -*/ - -#define DRV_NAME "xircom_tulip_cb" -#define DRV_VERSION "0.91+LK1.1" -#define DRV_RELDATE "October 11, 2001" - -#define CARDBUS 1 - -/* A few user-configurable values. */ - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 25; - -#define MAX_UNITS 4 -/* Used to pass the full-duplex flag, etc. */ -static int full_duplex[MAX_UNITS]; -static int options[MAX_UNITS]; -static int mtu[MAX_UNITS]; /* Jumbo MTU for interfaces. */ - -/* Keep the ring sizes a power of two for efficiency. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 32 - -/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#ifdef __alpha__ -static int rx_copybreak = 1518; -#else -static int rx_copybreak = 100; -#endif - -/* - Set the bus performance register. - Typical: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords - Warning: many older 486 systems are broken and require setting 0x00A04800 - 8 longword cache alignment, 8 longword burst. - ToDo: Non-Intel setting could be better. -*/ - -#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) -static int csr0 = 0x01A00000 | 0xE000; -#elif defined(__powerpc__) -static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) -static int csr0 = 0x01B00080 | 0x8000; -#elif defined(__i386__) -static int csr0 = 0x01A00000 | 0x8000; -#else -#warning Processor architecture undefined! -static int csr0 = 0x00A00000 | 0x4800; -#endif - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4 * HZ) -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -#define PKT_SETUP_SZ 192 /* Size of the setup frame */ - -/* PCI registers */ -#define PCI_POWERMGMT 0x40 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include /* Processor type for cache alignment. */ -#include - - -/* These identify the driver base version and may not be removed. */ -static char version[] __devinitdata = -KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n" -KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n"; - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver"); -MODULE_LICENSE("GPL v2"); - -MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(csr0, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); - -#define RUN_AT(x) (jiffies + (x)) - -#define xircom_debug debug -#ifdef XIRCOM_DEBUG -static int xircom_debug = XIRCOM_DEBUG; -#else -static int xircom_debug = 1; -#endif - -/* - Theory of Operation - -I. Board Compatibility - -This device driver was forked from the driver for the DECchip "Tulip", -Digital's single-chip ethernet controllers for PCI. It supports Xircom's -almost-Tulip-compatible CBE-100 CardBus adapters. - -II. Board-specific settings - -PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS preferably should assign the -PCI INTA signal to an otherwise unused system IRQ line. - -III. Driver operation - -IIIa. Ring buffers - -The Xircom can use either ring buffers or lists of Tx and Rx descriptors. -This driver uses statically allocated rings of Rx and Tx descriptors, set at -compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs -for the Rx ring buffers at open() time and passes the skb->data field to the -Xircom as receive data buffers. When an incoming frame is less than -RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is -copied to the new skbuff. When the incoming frame is larger, the skbuff is -passed directly up the protocol stack and replaced by a newly allocated -skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. For small frames the copying cost is negligible (esp. considering -that we are pre-loading the cache with immediately useful header -information). For large frames the copying cost is non-trivial, and the -larger copy might flush the cache of useful data. A subtle aspect of this -choice is that the Xircom only receives into longword aligned buffers, thus -the IP header at offset 14 isn't longword aligned for further processing. -Copied frames are put into the new skbuff at an offset of "+2", thus copying -has the beneficial effect of aligning the IP header and preloading the -cache. - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'tp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - -IV. Notes - -IVb. References - -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html -http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") -http://www.national.com/pf/DP/DP83840A.html - -IVc. Errata - -*/ - -/* A full-duplex map for media types. */ -enum MediaIs { - MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, - MediaIs100=16}; -static const char media_cap[] = -{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; - -/* Offsets to the Command and Status Registers, "CSRs". All accesses - must be longword instructions and quadword aligned. */ -enum xircom_offsets { - CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, - CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, - CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, }; - -/* The bits in the CSR5 status registers, mostly interrupt sources. */ -enum status_bits { - LinkChange=0x08000000, - NormalIntr=0x10000, NormalIntrMask=0x00014045, - AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2, - ReservedIntrMask=0xe0001a18, - EarlyRxIntr=0x4000, BusErrorIntr=0x2000, - EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, - TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, -}; - -enum csr0_control_bits { - EnableMWI=0x01000000, EnableMRL=0x00800000, - EnableMRM=0x00200000, EqualBusPrio=0x02, - SoftwareReset=0x01, -}; - -enum csr6_control_bits { - ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40, - HashFilterBit=0x01, FullDuplexBit=0x0200, - TxThresh10=0x400000, TxStoreForw=0x200000, - TxThreshMask=0xc000, TxThreshShift=14, - EnableTx=0x2000, EnableRx=0x02, - ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000, - EnableTxRx=(EnableTx | EnableRx), -}; - - -enum tbl_flag { - HAS_MII=1, HAS_ACPI=2, -}; -static struct xircom_chip_table { - char *chip_name; - int valid_intrs; /* CSR7 interrupt enable settings */ - int flags; -} xircom_tbl[] = { - { "Xircom Cardbus Adapter", - LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr | - RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr, - HAS_MII | HAS_ACPI, }, - { NULL, }, -}; -/* This matches the table above. */ -enum chips { - X3201_3, -}; - - -/* The Xircom Rx and Tx buffer descriptors. */ -struct xircom_rx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; -}; - -struct xircom_tx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ -}; - -enum tx_desc0_status_bits { - Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800, - Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02, -}; -enum tx_desc1_status_bits { - Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000, - Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000, - Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000, - Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg), -}; -enum rx_desc0_status_bits { - Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000, - Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100, - Rx0HugeFrame=0x80, Rx0CRCError=0x02, - Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg), -}; -enum rx_desc1_status_bits { - Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000, -}; - -struct xircom_private { - struct xircom_rx_desc rx_ring[RX_RING_SIZE]; - struct xircom_tx_desc tx_ring[TX_RING_SIZE]; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; -#ifdef CARDBUS - /* The X3201-3 requires 4-byte aligned tx bufs */ - struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; -#endif - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */ - int chip_id; - struct net_device_stats stats; - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int speed100:1; - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int autoneg:1; - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int open:1; - unsigned int csr0; /* CSR0 setting. */ - unsigned int csr6; /* Current CSR6 control settings. */ - u16 to_advertise; /* NWay capabilities advertised. */ - u16 advertising[4]; - signed char phys[4], mii_cnt; /* MII device addresses. */ - int saved_if_port; - struct pci_dev *pdev; - spinlock_t lock; -}; - -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static void xircom_up(struct net_device *dev); -static void xircom_down(struct net_device *dev); -static int xircom_open(struct net_device *dev); -static void xircom_tx_timeout(struct net_device *dev); -static void xircom_init_ring(struct net_device *dev); -static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int xircom_rx(struct net_device *dev); -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int xircom_close(struct net_device *dev); -static struct net_device_stats *xircom_get_stats(struct net_device *dev); -static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void set_rx_mode(struct net_device *dev); -static void check_duplex(struct net_device *dev); - - -/* The Xircom cards are picky about when certain bits in CSR6 can be - manipulated. Keith Owens . */ -static void outl_CSR6(u32 newcsr6, long ioaddr) -{ - const int strict_bits = - TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit; - int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; - long flags; - save_flags(flags); - cli(); - /* mask out the reserved bits that always read 0 on the Xircom cards */ - newcsr6 &= ~ReservedZeroMask; - /* or in the reserved bits that always read 1 */ - newcsr6 |= ReservedOneMask; - currcsr6 = inl(ioaddr + CSR6); - if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || - ((currcsr6 & ~EnableTxRx) == 0)) { - outl(newcsr6, ioaddr + CSR6); /* safe */ - restore_flags(flags); - return; - } - /* make sure the transmitter and receiver are stopped first */ - currcsr6 &= ~EnableTxRx; - while (1) { - csr5 = inl(ioaddr + CSR5); - if (csr5 == 0xffffffff) - break; /* cannot read csr5, card removed? */ - csr5_22_20 = csr5 & 0x700000; - csr5_19_17 = csr5 & 0x0e0000; - if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && - (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) - break; /* both are stopped or suspended */ - if (!--attempts) { - printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts," - "csr5=0x%08x\n", csr5); - outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ - restore_flags(flags); - return; - } - outl(currcsr6, ioaddr + CSR6); - udelay(1); - } - /* now it is safe to change csr6 */ - outl(newcsr6, ioaddr + CSR6); - restore_flags(flags); -} - - -static void __devinit read_mac_address(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - int i, j; - unsigned char tuple, link, data_id, data_count; - - /* Xircom has its address stored in the CIS; - * we access it through the boot rom interface for now - * this might not work, as the CIS is not parsed but I - * (danilo) use the offset I found on my card's CIS !!! - * - * Doug Ledford: I changed this routine around so that it - * walks the CIS memory space, parsing the config items, and - * finds the proper lan_node_id tuple and uses the data - * stored there. - */ - outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */ - for (i = 0x100; i < 0x1f7; i += link+2) { - outl(i, ioaddr + CSR10); - tuple = inl(ioaddr + CSR9) & 0xff; - outl(i + 1, ioaddr + CSR10); - link = inl(ioaddr + CSR9) & 0xff; - outl(i + 2, ioaddr + CSR10); - data_id = inl(ioaddr + CSR9) & 0xff; - outl(i + 3, ioaddr + CSR10); - data_count = inl(ioaddr + CSR9) & 0xff; - if ( (tuple == 0x22) && - (data_id == 0x04) && (data_count == 0x06) ) { - /* - * This is it. We have the data we want. - */ - for (j = 0; j < 6; j++) { - outl(i + j + 4, ioaddr + CSR10); - dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; - } - break; - } else if (link == 0) { - break; - } - } -} - - -/* - * locate the MII interfaces and initialize them. - * we disable full-duplex modes here, - * because we don't know how to handle them. - */ -static void find_mii_transceivers(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int phy, phy_idx; - - if (media_cap[tp->default_port] & MediaIsMII) { - u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; - tp->to_advertise = media2advert[tp->default_port - 9]; - } else - tp->to_advertise = - /*ADVERTISE_100BASE4 | ADVERTISE_100FULL |*/ ADVERTISE_100HALF | - /*ADVERTISE_10FULL |*/ ADVERTISE_10HALF | ADVERTISE_CSMA; - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, - but takes much time. */ - for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if ((mii_status & (BMSR_100BASE4 | BMSR_100HALF | BMSR_10HALF)) == BMSR_100BASE4 || - ((mii_status & BMSR_100BASE4) == 0 && - (mii_status & (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF)) != 0)) { - int mii_reg0 = mdio_read(dev, phy, MII_BMCR); - int mii_advert = mdio_read(dev, phy, MII_ADVERTISE); - int reg4 = ((mii_status >> 6) & tp->to_advertise) | ADVERTISE_CSMA; - tp->phys[phy_idx] = phy; - tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver #%d " - "config %4.4x status %4.4x advertising %4.4x.\n", - dev->name, phy, mii_reg0, mii_status, mii_advert); - } - } - tp->mii_cnt = phy_idx; - if (phy_idx == 0) { - printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", - dev->name); - tp->phys[0] = 0; - } -} - - -/* - * To quote Arjan van de Ven: - * tranceiver_voodoo() enables the external UTP plug thingy. - * it's called voodoo as I stole this code and cannot cross-reference - * it with the specification. - * Actually it seems to go like this: - * - GPIO2 enables the MII itself so we can talk to it. The MII gets reset - * so any prior MII settings are lost. - * - GPIO0 enables the TP port so the MII can talk to the network. - * - a software reset will reset both GPIO pins. - * I also moved the software reset here, because doing it in xircom_up() - * required enabling the GPIO pins each time, which reset the MII each time. - * Thus we couldn't control the MII -- which sucks because we don't know - * how to handle full-duplex modes so we *must* disable them. - */ -static void transceiver_voodoo(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ - outl(SoftwareReset, ioaddr + CSR0); - udelay(2); - - /* Deassert reset. */ - outl(tp->csr0, ioaddr + CSR0); - - /* Reset the xcvr interface and turn on heartbeat. */ - outl(0x0008, ioaddr + CSR15); - udelay(5); /* The delays are Xircom-recommended to give the - * chipset time to reset the actual hardware - * on the PCMCIA card - */ - outl(0xa8050000, ioaddr + CSR15); - udelay(5); - outl(0xa00f0000, ioaddr + CSR15); - udelay(5); - - outl_CSR6(0, ioaddr); - //outl_CSR6(FullDuplexBit, ioaddr); -} - - -static int __devinit xircom_init_one(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *dev; - struct xircom_private *tp; - static int board_idx = -1; - int chip_idx = id->driver_data; - long ioaddr; - int i; - u8 chip_rev; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - //printk(KERN_INFO "xircom_init_one(%s)\n", pdev->slot_name); - - board_idx++; - - if (pci_enable_device(pdev)) - return -ENODEV; - - pci_set_master(pdev); - - ioaddr = pci_resource_start(pdev, 0); - dev = alloc_etherdev(sizeof(*tp)); - if (!dev) { - printk (KERN_ERR DRV_NAME "%d: cannot alloc etherdev, aborting\n", board_idx); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - - dev->base_addr = ioaddr; - dev->irq = pdev->irq; - - if (pci_request_regions(pdev, dev->name)) { - printk (KERN_ERR DRV_NAME " %d: cannot reserve PCI resources, aborting\n", board_idx); - goto err_out_free_netdev; - } - - /* Bring the chip out of sleep mode. - Caution: Snooze mode does not work with some boards! */ - if (xircom_tbl[chip_idx].flags & HAS_ACPI) - pci_write_config_dword(pdev, PCI_POWERMGMT, 0); - - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); - /* Clear the missed-packet counter. */ - (volatile int)inl(ioaddr + CSR8); - - tp = dev->priv; - - tp->lock = SPIN_LOCK_UNLOCKED; - tp->pdev = pdev; - tp->chip_id = chip_idx; - /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */ - /* XXX: is this necessary for Xircom? */ - tp->csr0 = csr0 & ~EnableMWI; - - pci_set_drvdata(pdev, dev); - - /* The lower four bits are the media type. */ - if (board_idx >= 0 && board_idx < MAX_UNITS) { - tp->default_port = options[board_idx] & 15; - if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) - tp->full_duplex = 1; - if (mtu[board_idx] > 0) - dev->mtu = mtu[board_idx]; - } - if (dev->mem_start) - tp->default_port = dev->mem_start; - if (tp->default_port) { - if (media_cap[tp->default_port] & MediaAlwaysFD) - tp->full_duplex = 1; - } - if (tp->full_duplex) - tp->autoneg = 0; - else - tp->autoneg = 1; - tp->speed100 = 1; - - /* The Xircom-specific entries in the device structure. */ - dev->open = &xircom_open; - dev->hard_start_xmit = &xircom_start_xmit; - dev->stop = &xircom_close; - dev->get_stats = &xircom_get_stats; - dev->do_ioctl = &xircom_ioctl; -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_rx_mode; -#endif - dev->tx_timeout = xircom_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - transceiver_voodoo(dev); - - read_mac_address(dev); - - if (register_netdev(dev)) - goto err_out_cleardev; - - pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); - printk(KERN_INFO "%s: %s rev %d at %#3lx,", - dev->name, xircom_tbl[chip_idx].chip_name, chip_rev, ioaddr); - for (i = 0; i < 6; i++) - printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]); - printk(", IRQ %d.\n", dev->irq); - - if (xircom_tbl[chip_idx].flags & HAS_MII) { - find_mii_transceivers(dev); - check_duplex(dev); - } - - return 0; - -err_out_cleardev: - pci_set_drvdata(pdev, NULL); - pci_release_regions(pdev); -err_out_free_netdev: - unregister_netdev(dev); - kfree(dev); - return -ENODEV; -} - - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ - -/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues or future 66Mhz PCI. */ -#define mdio_delay() inl(mdio_addr) - -/* Read and write the MII registers using software-generated serial - MDIO protocol. It is just different enough from the EEPROM protocol - to not share code. The maxium data clock rate is 2.5 Mhz. */ -#define MDIO_SHIFT_CLK 0x10000 -#define MDIO_DATA_WRITE0 0x00000 -#define MDIO_DATA_WRITE1 0x20000 -#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ -#define MDIO_ENB_IN 0x40000 -#define MDIO_DATA_READ 0x80000 - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int retval = 0; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - /* Establish sync by sending at least 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return (retval>>1) & 0xffff; -} - - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - int i; - int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - /* Establish sync by sending 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return; -} - - -static void -xircom_up(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - int i; - - /* Clear the tx ring */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0; - } - - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: xircom_up() irq %d.\n", dev->name, dev->irq); - - outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); - outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); - - tp->saved_if_port = dev->if_port; - if (dev->if_port == 0) - dev->if_port = tp->default_port; - - tp->csr6 = TxThresh10 /*| FullDuplexBit*/; /* XXX: why 10 and not 100? */ - - set_rx_mode(dev); - - /* Start the chip's Tx to process setup frame. */ - outl_CSR6(tp->csr6, ioaddr); - outl_CSR6(tp->csr6 | EnableTx, ioaddr); - - /* Acknowledge all outstanding interrupts sources */ - outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); - /* Enable interrupts by setting the interrupt mask. */ - outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); - /* Enable Rx */ - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - /* Rx poll demand */ - outl(0, ioaddr + CSR2); - - /* Tell the net layer we're ready */ - netif_start_queue (dev); - - if (xircom_debug > 2) { - printk(KERN_DEBUG "%s: Done xircom_up(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", - dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), - inl(ioaddr + CSR6)); - } -} - - -static int -xircom_open(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - - if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) - return -EAGAIN; - - xircom_init_ring(dev); - - xircom_up(dev); - tp->open = 1; - - return 0; -} - - -static void xircom_tx_timeout(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - } - -#if defined(way_too_many_messages) - if (xircom_debug > 3) { - int i; - for (i = 0; i < RX_RING_SIZE; i++) { - u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); - int j; - printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " - "%2.2x %2.2x %2.2x.\n", - i, (unsigned int)tp->rx_ring[i].status, - (unsigned int)tp->rx_ring[i].length, - (unsigned int)tp->rx_ring[i].buffer1, - (unsigned int)tp->rx_ring[i].buffer2, - buf[0], buf[1], buf[2]); - for (j = 0; buf[j] != 0xee && j < 1600; j++) - if (j < 100) printk(" %2.2x", buf[j]); - printk(" j=%d.\n", j); - } - printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); - } -#endif - - /* Stop and restart the chip's Tx/Rx processes . */ - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - netif_wake_queue (dev); - tp->stats.tx_errors++; -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void xircom_init_ring(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int i; - - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0; - tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); - tp->rx_skbuff[i] = NULL; - } - /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | Rx1RingWrap; - tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); - - for (i = 0; i < RX_RING_SIZE; i++) { - /* Note the receive buffer must be longword aligned. - dev_alloc_skb() provides 16 byte alignment. But do *not* - use skb_reserve() to align the IP header! */ - struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); - tp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[i].status = Rx0DescOwned; /* Owned by Xircom chip */ - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); - } - tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - /* The Tx buffer descriptor is filled in as needed, but we - do need to clear the ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0; - tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); -#ifdef CARDBUS - if (tp->chip_id == X3201_3) - tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); -#endif /* CARDBUS */ - } - tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); -} - - -static int -xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int entry; - u32 flag; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; - - tp->tx_skbuff[entry] = skb; -#ifdef CARDBUS - if (tp->chip_id == X3201_3) { - memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); - } else -#endif - tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); - - if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ - flag = Tx1WholePkt; /* No interrupt */ - } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { - flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ - } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { - flag = Tx1WholePkt; /* No Tx-done intr. */ - } else { - /* Leave room for set_rx_mode() to fill entries. */ - flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ - tp->tx_full = 1; - } - if (entry == TX_RING_SIZE - 1) - flag |= Tx1WholePkt | Tx1ComplIntr | Tx1RingWrap; - - tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = Tx0DescOwned; /* Pass ownership to the chip. */ - tp->cur_tx++; - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - /* Trigger an immediate transmit demand. */ - outl(0, dev->base_addr + CSR1); - - dev->trans_start = jiffies; - - return 0; -} - - -static void xircom_media_change(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - u16 reg0, reg1, reg4, reg5; - u32 csr6 = inl(ioaddr + CSR6), newcsr6; - - /* reset status first */ - mdio_read(dev, tp->phys[0], MII_BMCR); - mdio_read(dev, tp->phys[0], MII_BMSR); - - reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); - reg1 = mdio_read(dev, tp->phys[0], MII_BMSR); - - if (reg1 & BMSR_LSTATUS) { - /* link is up */ - if (reg0 & BMCR_ANENABLE) { - /* autonegotiation is enabled */ - reg4 = mdio_read(dev, tp->phys[0], MII_ADVERTISE); - reg5 = mdio_read(dev, tp->phys[0], MII_LPA); - if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) { - tp->speed100 = 1; - tp->full_duplex = 1; - } else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) { - tp->speed100 = 1; - tp->full_duplex = 0; - } else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) { - tp->speed100 = 0; - tp->full_duplex = 1; - } else { - tp->speed100 = 0; - tp->full_duplex = 0; - } - } else { - /* autonegotiation is disabled */ - if (reg0 & BMCR_SPEED100) - tp->speed100 = 1; - else - tp->speed100 = 0; - if (reg0 & BMCR_FULLDPLX) - tp->full_duplex = 1; - else - tp->full_duplex = 0; - } - printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n", - dev->name, - tp->speed100 ? "100" : "10", - tp->full_duplex ? "full" : "half"); - newcsr6 = csr6 & ~FullDuplexBit; - if (tp->full_duplex) - newcsr6 |= FullDuplexBit; - if (newcsr6 != csr6) - outl_CSR6(newcsr6, ioaddr + CSR6); - } else { - printk(KERN_DEBUG "%s: Link is down\n", dev->name); - } -} - - -static void check_duplex(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - u16 reg0; - - mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET); - udelay(500); - while (mdio_read(dev, tp->phys[0], MII_BMCR) & BMCR_RESET); - - reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); - mdio_write(dev, tp->phys[0], MII_ADVERTISE, tp->advertising[0]); - - if (tp->autoneg) { - reg0 &= ~(BMCR_SPEED100 | BMCR_FULLDPLX); - reg0 |= BMCR_ANENABLE | BMCR_ANRESTART; - } else { - reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART); - if (tp->speed100) - reg0 |= BMCR_SPEED100; - if (tp->full_duplex) - reg0 |= BMCR_FULLDPLX; - printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n", - dev->name, - tp->speed100 ? "100" : "10", - tp->full_duplex ? "full" : "half"); - } - mdio_write(dev, tp->phys[0], MII_BMCR, reg0); -} - - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = dev_instance; - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - int csr5, work_budget = max_interrupt_work; - - spin_lock (&tp->lock); - - do { - csr5 = inl(ioaddr + CSR5); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(csr5 & 0x0001ffff, ioaddr + CSR5); - - if (xircom_debug > 4) - printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", - dev->name, csr5, inl(dev->base_addr + CSR5)); - - if (csr5 == 0xffffffff) - break; /* all bits set, assume PCMCIA card removed */ - - if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) - break; - - if (csr5 & (RxIntr | RxNoBuf)) - work_budget -= xircom_rx(dev); - - if (csr5 & (TxNoBuf | TxDied | TxIntr)) { - unsigned int dirty_tx; - - for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; - dirty_tx++) { - int entry = dirty_tx % TX_RING_SIZE; - int status = tp->tx_ring[entry].status; - - if (status < 0) - break; /* It still hasn't been Txed */ - /* Check for Rx filter setup frames. */ - if (tp->tx_skbuff[entry] == NULL) - continue; - - if (status & Tx0DescError) { - /* There was an major error, log it. */ -#ifndef final_version - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, status); -#endif - tp->stats.tx_errors++; - if (status & Tx0ManyColl) { - tp->stats.tx_aborted_errors++; -#ifdef ETHER_STATS - tp->stats.collisions16++; -#endif - } - if (status & Tx0NoCarrier) tp->stats.tx_carrier_errors++; - if (status & Tx0LateColl) tp->stats.tx_window_errors++; - if (status & Tx0Underflow) tp->stats.tx_fifo_errors++; - } else { - tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; - tp->stats.collisions += (status >> 3) & 15; - tp->stats.tx_packets++; - } - - /* Free the original skb. */ - dev_kfree_skb_irq(tp->tx_skbuff[entry]); - tp->tx_skbuff[entry] = 0; - } - -#ifndef final_version - if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, tp->cur_tx, tp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (tp->tx_full && - tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) - /* The ring is no longer full */ - tp->tx_full = 0; - - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - tp->dirty_tx = dirty_tx; - if (csr5 & TxDied) { - if (xircom_debug > 2) - printk(KERN_WARNING "%s: The transmitter stopped." - " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", - dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - } - - /* Log errors. */ - if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ - if (csr5 & LinkChange) - xircom_media_change(dev); - if (csr5 & TxFIFOUnderflow) { - if ((tp->csr6 & TxThreshMask) != TxThreshMask) - tp->csr6 += (1 << TxThreshShift); /* Bump up the Tx threshold */ - else - tp->csr6 |= TxStoreForw; /* Store-n-forward. */ - /* Restart the transmit process. */ - outl_CSR6(tp->csr6 | EnableRx, ioaddr); - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - if (csr5 & RxDied) { /* Missed a Rx frame. */ - tp->stats.rx_errors++; - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); - } - /* Clear all error sources, included undocumented ones! */ - outl(0x0800f7ba, ioaddr + CSR5); - } - if (--work_budget < 0) { - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Too much work during an interrupt, " - "csr5=0x%8.8x.\n", dev->name, csr5); - /* Acknowledge all interrupt sources. */ - outl(0x8001ffff, ioaddr + CSR5); - break; - } - } while (1); - - if (xircom_debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", - dev->name, inl(ioaddr + CSR5)); - - spin_unlock (&tp->lock); -} - - -static int -xircom_rx(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - int entry = tp->cur_rx % RX_RING_SIZE; - int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; - int work_done = 0; - - if (xircom_debug > 4) - printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - /* If we own the next entry, it's a new packet. Send it up. */ - while (tp->rx_ring[entry].status >= 0) { - s32 status = tp->rx_ring[entry].status; - - if (xircom_debug > 5) - printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - if (--rx_work_limit < 0) - break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { - /* Ignore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - if (xircom_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame " - "spanned multiple buffers, status %8.8x!\n", - dev->name, status); - tp->stats.rx_length_errors++; - } - } else if (status & Rx0DescError) { - /* There was a fatal error. */ - if (xircom_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & (Rx0Runt | Rx0HugeFrame)) tp->stats.rx_length_errors++; - if (status & Rx0CRCError) tp->stats.rx_crc_errors++; - } - } else { - /* Omit the four octet CRC from the length. */ - short pkt_len = ((status >> 16) & 0x7ff) - 4; - struct sk_buff *skb; - -#ifndef final_version - if (pkt_len > 1518) { - printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", - dev->name, pkt_len, pkt_len); - pkt_len = 1518; - tp->stats.rx_length_errors++; - } -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if ! defined(__alpha__) - eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), - pkt_len, 0); - skb_put(skb, pkt_len); -#else - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#endif - work_done++; - } else { /* Pass up the skb already on the Rx ring. */ - skb_put(skb = tp->rx_skbuff[entry], pkt_len); - tp->rx_skbuff[entry] = NULL; - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - tp->stats.rx_packets++; - tp->stats.rx_bytes += pkt_len; - } - entry = (++tp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { - entry = tp->dirty_rx % RX_RING_SIZE; - if (tp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); - work_done++; - } - tp->rx_ring[entry].status = Rx0DescOwned; - } - - return work_done; -} - - -static void -xircom_down(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; - - /* Disable interrupts by clearing the interrupt mask. */ - outl(0, ioaddr + CSR7); - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); - - if (inl(ioaddr + CSR6) != 0xffffffff) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - dev->if_port = tp->saved_if_port; -} - - -static int -xircom_close(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; - int i; - - if (xircom_debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inl(ioaddr + CSR5)); - - netif_stop_queue(dev); - - if (netif_device_present(dev)) - xircom_down(dev); - - free_irq(dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = tp->rx_skbuff[i]; - tp->rx_skbuff[i] = 0; - tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */ - tp->rx_ring[i].length = 0; - tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ - if (skb) { - dev_kfree_skb(skb); - } - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i]); - tp->tx_skbuff[i] = 0; - } - - tp->open = 0; - return 0; -} - - -static struct net_device_stats *xircom_get_stats(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - long ioaddr = dev->base_addr; - - if (netif_device_present(dev)) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - return &tp->stats; -} - - -static int xircom_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct ethtool_cmd ecmd; - struct xircom_private *tp = dev->priv; - - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - - switch (ecmd.cmd) { - case ETHTOOL_GSET: - ecmd.supported = - SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_MII; - - ecmd.advertising = ADVERTISED_MII; - if (tp->advertising[0] & ADVERTISE_10HALF) - ecmd.advertising |= ADVERTISED_10baseT_Half; - if (tp->advertising[0] & ADVERTISE_10FULL) - ecmd.advertising |= ADVERTISED_10baseT_Full; - if (tp->advertising[0] & ADVERTISE_100HALF) - ecmd.advertising |= ADVERTISED_100baseT_Half; - if (tp->advertising[0] & ADVERTISE_100FULL) - ecmd.advertising |= ADVERTISED_100baseT_Full; - if (tp->autoneg) { - ecmd.advertising |= ADVERTISED_Autoneg; - ecmd.autoneg = AUTONEG_ENABLE; - } else - ecmd.autoneg = AUTONEG_DISABLE; - - ecmd.port = PORT_MII; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = tp->phys[0]; - ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10; - ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - ecmd.maxtxpkt = TX_RING_SIZE / 2; - ecmd.maxrxpkt = 0; - - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - - case ETHTOOL_SSET: { - u16 autoneg, speed100, full_duplex; - - autoneg = (ecmd.autoneg == AUTONEG_ENABLE); - speed100 = (ecmd.speed == SPEED_100); - full_duplex = (ecmd.duplex == DUPLEX_FULL); - - tp->autoneg = autoneg; - if (speed100 != tp->speed100 || - full_duplex != tp->full_duplex) { - tp->speed100 = speed100; - tp->full_duplex = full_duplex; - /* change advertising bits */ - tp->advertising[0] &= ~(ADVERTISE_10HALF | - ADVERTISE_10FULL | - ADVERTISE_100HALF | - ADVERTISE_100FULL | - ADVERTISE_100BASE4); - if (speed100) { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_100FULL; - else - tp->advertising[0] |= ADVERTISE_100HALF; - } else { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_10FULL; - else - tp->advertising[0] |= ADVERTISE_10HALF; - } - } - check_duplex(dev); - return 0; - } - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info; - memset(&info, 0, sizeof(info)); - info.cmd = ecmd.cmd; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - *info.fw_version = 0; - strcpy(info.bus_info, tp->pdev->slot_name); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - default: - return -EOPNOTSUPP; - } -} - - -/* Provide ioctl() calls to examine the MII xcvr state. */ -static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct xircom_private *tp = dev->priv; - u16 *data = (u16 *)&rq->ifr_data; - int phy = tp->phys[0] & 0x1f; - long flags; - - switch(cmd) { - case SIOCETHTOOL: - return xircom_ethtool_ioctl(dev, (void *) rq->ifr_data); - - /* Legacy mii-diag interface */ - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - if (tp->mii_cnt) - data[0] = phy; - else - return -ENODEV; - return 0; - case SIOCGMIIREG: /* Read MII PHY register. */ - save_flags(flags); - cli(); - data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); - restore_flags(flags); - return 0; - case SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - save_flags(flags); - cli(); - if (data[0] == tp->phys[0]) { - u16 value = data[2]; - switch (data[1]) { - case 0: - if (value & (BMCR_RESET | BMCR_ANENABLE)) - /* Autonegotiation. */ - tp->autoneg = 1; - else { - tp->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0; - tp->autoneg = 0; - } - break; - case 4: - tp->advertising[0] = value; - break; - } - check_duplex(dev); - } - mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); - restore_flags(flags); - return 0; - default: - return -EOPNOTSUPP; - } - - return -EOPNOTSUPP; -} - -/* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic - when re-entered but still correct. */ -static void set_rx_mode(struct net_device *dev) -{ - struct xircom_private *tp = dev->priv; - struct dev_mc_list *mclist; - long ioaddr = dev->base_addr; - int csr6 = inl(ioaddr + CSR6); - u16 *eaddrs, *setup_frm; - u32 tx_flags; - int i; - - tp->csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); - csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - tp->csr6 |= PromiscBit; - csr6 |= PromiscBit; - goto out; - } - - if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well -- accept all multicasts. */ - tp->csr6 |= AllMultiBit; - csr6 |= AllMultiBit; - goto out; - } - - tx_flags = Tx1WholePkt | Tx1SetupPkt | PKT_SETUP_SZ; - - /* Note that only the low-address shortword of setup_frame is valid! */ - setup_frm = tp->setup_frame; - mclist = dev->mc_list; - - /* Fill the first entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; - - if (dev->mc_count > 14) { /* Must use a multicast hash table. */ - u32 *hash_table = (u32 *)(tp->setup_frame + 4 * 12); - u32 hash, hash2; - - tx_flags |= Tx1HashSetup; - tp->csr6 |= HashFilterBit; - csr6 |= HashFilterBit; - - /* Fill the unused 3 entries with the broadcast address. - At least one entry *must* contain the broadcast address!!!*/ - for (i = 0; i < 3; i++) { - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - } - - /* Truly brain-damaged hash filter layout */ - /* XXX: not sure if I should take the last or the first 9 bits */ - for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { - u32 *hptr; - hash = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff; - if (hash < 384) { - hash2 = hash + ((hash >> 4) << 4) + - ((hash >> 5) << 5); - } else { - hash -= 384; - hash2 = 64 + hash + (hash >> 4) * 80; - } - hptr = &hash_table[hash2 & ~0x1f]; - *hptr |= cpu_to_le32(1 << (hash2 & 0x1f)); - } - } else { - /* We have <= 14 mcast addresses so we can use Xircom's - wonderful 16-address perfect filter. */ - for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; - *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; - } - /* Fill the unused entries with the broadcast address. - At least one entry *must* contain the broadcast address!!!*/ - for (; i < 15; i++) { - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - *setup_frm = 0xffff; setup_frm += 2; - } - } - - /* Now add this frame to the Tx list. */ - if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { - /* Same setup recently queued, we need not add it. */ - /* XXX: Huh? All it means is that the Tx list is full...*/ - } else { - unsigned long flags; - unsigned int entry; - int dummy = -1; - - save_flags(flags); cli(); - entry = tp->cur_tx++ % TX_RING_SIZE; - - if (entry != 0) { - /* Avoid a chip errata by prefixing a dummy entry. */ - tp->tx_skbuff[entry] = 0; - tp->tx_ring[entry].length = - (entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0; - tp->tx_ring[entry].buffer1 = 0; - /* race with chip, set Tx0DescOwned later */ - dummy = entry; - entry = tp->cur_tx++ % TX_RING_SIZE; - } - - tp->tx_skbuff[entry] = 0; - /* Put the setup frame on the Tx list. */ - if (entry == TX_RING_SIZE - 1) - tx_flags |= Tx1RingWrap; /* Wrap ring. */ - tp->tx_ring[entry].length = tx_flags; - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = Tx0DescOwned; - if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { - tp->tx_full = 1; - netif_stop_queue (dev); - } - if (dummy >= 0) - tp->tx_ring[dummy].status = Tx0DescOwned; - restore_flags(flags); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - } - -out: - outl_CSR6(csr6, ioaddr); -} - - -static struct pci_device_id xircom_pci_table[] __devinitdata = { - { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, - {0}, -}; -MODULE_DEVICE_TABLE(pci, xircom_pci_table); - - -#ifdef CONFIG_PM -static int xircom_suspend(struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; - printk(KERN_INFO "xircom_suspend(%s)\n", dev->name); - if (tp->open) - xircom_down(dev); - return 0; -} - - -static int xircom_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; - printk(KERN_INFO "xircom_resume(%s)\n", dev->name); - - /* Bring the chip out of sleep mode. - Caution: Snooze mode does not work with some boards! */ - if (xircom_tbl[tp->chip_id].flags & HAS_ACPI) - pci_write_config_dword(tp->pdev, PCI_POWERMGMT, 0); - - transceiver_voodoo(dev); - if (xircom_tbl[tp->chip_id].flags & HAS_MII) - check_duplex(dev); - - if (tp->open) - xircom_up(dev); - return 0; -} -#endif /* CONFIG_PM */ - - -static void __devexit xircom_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - printk(KERN_INFO "xircom_remove_one(%s)\n", dev->name); - unregister_netdev(dev); - pci_release_regions(pdev); - kfree(dev); - pci_set_drvdata(pdev, NULL); -} - - -static struct pci_driver xircom_driver = { - name: DRV_NAME, - id_table: xircom_pci_table, - probe: xircom_init_one, - remove: __devexit_p(xircom_remove_one), -#ifdef CONFIG_PM - suspend: xircom_suspend, - resume: xircom_resume -#endif /* CONFIG_PM */ -}; - - -static int __init xircom_init(void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_module_init(&xircom_driver); -} - - -static void __exit xircom_exit(void) -{ - pci_unregister_driver(&xircom_driver); -} - -module_init(xircom_init) -module_exit(xircom_exit) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff -urN linux-2.5.6-pre3/drivers/net/pcnet32.c linux-2.5.6/drivers/net/pcnet32.c --- linux-2.5.6-pre3/drivers/net/pcnet32.c Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/pcnet32.c Thu Mar 7 18:24:47 2002 @@ -22,8 +22,9 @@ *************************************************************************/ #define DRV_NAME "pcnet32" -#define DRV_VERSION "1.25kf" -#define DRV_RELDATE "17.11.2001" +#define DRV_VERSION "1.27a" +#define DRV_RELDATE "10.02.2002" +#define PFX DRV_NAME ": " static const char *version = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n"; @@ -54,8 +55,6 @@ #include #include -static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0}; - /* * PCI device identifiers for "new style" Linux PCI Device Drivers */ @@ -65,13 +64,26 @@ { 0, } }; +MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl); + +int cards_found __initdata; + +/* + * VLB I/O addresses + */ +static unsigned int pcnet32_portlist[] __initdata = + { 0x300, 0x320, 0x340, 0x360, 0 }; + + + static int pcnet32_debug = 1; static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ +static int pcnet32vlb; /* check for VLB cards ? */ static struct net_device *pcnet32_dev; -static const int max_interrupt_work = 80; -static const int rx_copybreak = 200; +static int max_interrupt_work = 80; +static int rx_copybreak = 200; #define PCNET32_PORT_AUI 0x00 #define PCNET32_PORT_10BT 0x01 @@ -94,21 +106,21 @@ PCNET32_PORT_AUI, /* 1 BNC/AUI */ PCNET32_PORT_AUI, /* 2 AUI/BNC */ PCNET32_PORT_ASEL, /* 3 not supported */ - PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ + PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ PCNET32_PORT_ASEL, /* 5 not supported */ PCNET32_PORT_ASEL, /* 6 not supported */ PCNET32_PORT_ASEL, /* 7 not supported */ PCNET32_PORT_ASEL, /* 8 not supported */ PCNET32_PORT_MII, /* 9 MII 10baseT */ - PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ + PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ PCNET32_PORT_MII, /* 11 MII (autosel) */ PCNET32_PORT_10BT, /* 12 10BaseT */ - PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ + PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */ PCNET32_PORT_ASEL /* 15 not supported */ }; -#define MAX_UNITS 8 +#define MAX_UNITS 8 /* More are supported, limit only on options */ static int options[MAX_UNITS]; static int full_duplex[MAX_UNITS]; @@ -187,7 +199,19 @@ * v1.25kf Added No Interrupt on successful Tx for some Tx's * v1.26 Converted to pci_alloc_consistent, Jamey Hicks / George France * - * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker + * - Fixed a few bugs, related to running the controller in 32bit mode. + * 23 Oct, 2000. Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker + * v1.27 improved CSR/PROM address detection, lots of cleanups, + * new pcnet32vlb module option, HP-PARISC support, + * added module parameter descriptions, + * initial ethtool support - Helge Deller + * v1.27a Sun Feb 10 2002 Go Taniguchi + * use alloc_etherdev and register_netdev + * fix pci probe not increment cards_found + * FD auto negotiate error workaround for xSeries250 + * clean up and using new mii module */ @@ -201,13 +225,13 @@ #define PCNET32_LOG_RX_BUFFERS 5 #endif -#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -#define TX_RING_LEN_BITS ((PCNET32_LOG_TX_BUFFERS) << 12) - -#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) +#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((PCNET32_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) #define PKT_BUF_SZ 1544 @@ -222,7 +246,7 @@ #define PCNET32_DWIO_RESET 0x18 #define PCNET32_DWIO_BDP 0x1C -#define PCNET32_TOTAL_SIZE 0x20 +#define PCNET32_TOTAL_SIZE 0x20 /* The PCNET32 Rx and Tx ring descriptors. */ struct pcnet32_rx_head { @@ -270,37 +294,36 @@ */ struct pcnet32_private { /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */ - struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; - struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; - struct pcnet32_init_block init_block; - dma_addr_t dma_addr; /* DMA address of beginning of this object, returned by pci_alloc_consistent */ - struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ - const char *name; + struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; + struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; + struct pcnet32_init_block init_block; + dma_addr_t dma_addr; /* DMA address of beginning of this object, + returned by pci_alloc_consistent */ + struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ + const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - struct sk_buff *rx_skbuff[RX_RING_SIZE]; - dma_addr_t tx_dma_addr[TX_RING_SIZE]; - dma_addr_t rx_dma_addr[RX_RING_SIZE]; + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + dma_addr_t tx_dma_addr[TX_RING_SIZE]; + dma_addr_t rx_dma_addr[RX_RING_SIZE]; struct pcnet32_access a; - spinlock_t lock; /* Guard lock */ - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + spinlock_t lock; /* Guard lock */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ struct net_device_stats stats; - char tx_full; - int options; - int shared_irq:1, /* shared irq possible */ - ltint:1, -#ifdef DO_DXSUFLO - dxsuflo:1, /* disable transmit stop on uflo */ -#endif - mii:1; /* mii port available */ - struct net_device *next; + char tx_full; + int options; + int shared_irq:1, /* shared irq possible */ + ltint:1, /* enable TxDone-intr inhibitor */ + dxsuflo:1, /* disable transmit stop on uflo */ + mii:1; /* mii port available */ + struct net_device *next; struct mii_if_info mii_if; }; -static int pcnet32_probe_vlbus(int cards_found); +static void pcnet32_probe_vlbus(void); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned char, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -319,15 +342,6 @@ PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, }; -struct pcnet32_pci_id_info { - const char *name; - u16 vendor_id, device_id, svid, sdid, flags; - int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); -}; - - -MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl); static u16 pcnet32_wio_read_csr (unsigned long addr, int index) { @@ -375,13 +389,13 @@ } static struct pcnet32_access pcnet32_wio = { - pcnet32_wio_read_csr, - pcnet32_wio_write_csr, - pcnet32_wio_read_bcr, - pcnet32_wio_write_bcr, - pcnet32_wio_read_rap, - pcnet32_wio_write_rap, - pcnet32_wio_reset + read_csr: pcnet32_wio_read_csr, + write_csr: pcnet32_wio_write_csr, + read_bcr: pcnet32_wio_read_bcr, + write_bcr: pcnet32_wio_write_bcr, + read_rap: pcnet32_wio_read_rap, + write_rap: pcnet32_wio_write_rap, + reset: pcnet32_wio_reset }; static u16 pcnet32_dwio_read_csr (unsigned long addr, int index) @@ -430,82 +444,61 @@ } static struct pcnet32_access pcnet32_dwio = { - pcnet32_dwio_read_csr, - pcnet32_dwio_write_csr, - pcnet32_dwio_read_bcr, - pcnet32_dwio_write_bcr, - pcnet32_dwio_read_rap, - pcnet32_dwio_write_rap, - pcnet32_dwio_reset - + read_csr: pcnet32_dwio_read_csr, + write_csr: pcnet32_dwio_write_csr, + read_bcr: pcnet32_dwio_read_bcr, + write_bcr: pcnet32_dwio_write_bcr, + read_rap: pcnet32_dwio_read_rap, + write_rap: pcnet32_dwio_write_rap, + reset: pcnet32_dwio_reset }; - -/* only probes for non-PCI devices, the rest are handled by pci_register_driver via pcnet32_probe_pci*/ -static int __init pcnet32_probe_vlbus(int cards_found) + +/* only probes for non-PCI devices, the rest are handled by + * pci_register_driver via pcnet32_probe_pci */ + +static void __devinit +pcnet32_probe_vlbus(void) { - unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; - unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; - int *port; + unsigned int *port, ioaddr; - printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); -#ifndef __powerpc__ - if (ioaddr > 0x1ff) { - if (check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) - return pcnet32_probe1(ioaddr, irq_line, 0, 0, NULL); - else - return -ENODEV; - } else -#endif - if (ioaddr != 0) - return -ENXIO; - - /* now look for PCnet32 VLB cards */ - for (port = pcnet32_portlist; *port; port++) { - unsigned long ioaddr = *port; - - if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) { + /* search for PCnet32 VLB cards at known addresses */ + for (port = pcnet32_portlist; (ioaddr = *port); port++) { + if (!check_region(ioaddr, PCNET32_TOTAL_SIZE)) { /* check if there is really a pcnet chip on that ioaddr */ - if ((inb(ioaddr + 14) == 0x57) && - (inb(ioaddr + 15) == 0x57) && - (pcnet32_probe1(ioaddr, 0, 0, 0, NULL) == 0)) - cards_found++; + if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) + pcnet32_probe1(ioaddr, 0, 0, NULL); } } - return cards_found ? 0: -ENODEV; } - static int __devinit pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int card_idx; - long ioaddr; - int err = 0; - - printk(KERN_INFO "pcnet32_probe_pci: found device %#08x.%#08x\n", ent->vendor, ent->device); + unsigned long ioaddr; + int err; - if ((err = pci_enable_device(pdev)) < 0) { - printk(KERN_ERR "pcnet32.c: failed to enable device -- err=%d\n", err); + err = pci_enable_device(pdev); + if (err < 0) { + printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err); return err; } pci_set_master(pdev); ioaddr = pci_resource_start (pdev, 0); - printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0)); if (!ioaddr) { - printk (KERN_ERR "no PCI IO resources, aborting\n"); + printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n"); return -ENODEV; } - + if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) { - printk(KERN_ERR "pcnet32.c: architecture does not support 32bit PCI busmaster DMA\n"); + printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n"); return -ENODEV; } - return pcnet32_probe1(ioaddr, pdev->irq, 1, card_idx, pdev); + return pcnet32_probe1(ioaddr, pdev->irq, 1, pdev); } @@ -514,41 +507,44 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, + struct pci_dev *pdev) { struct pcnet32_private *lp; struct resource *res; dma_addr_t lp_dma_addr; - int i,media,fdx = 0, mii = 0, fset = 0; -#ifdef DO_DXSUFLO - int dxsuflo = 0; -#endif - int ltint = 0; + int i, media; + int fdx, mii, fset, dxsuflo, ltint; int chip_version; char *chipname; struct net_device *dev; struct pcnet32_access *a = NULL; + u8 promaddr[6]; /* reset the chip */ pcnet32_dwio_reset(ioaddr); pcnet32_wio_reset(ioaddr); /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) { a = &pcnet32_wio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { a = &pcnet32_dwio; } else return -ENODEV; } - chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16); + chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16); if (pcnet32_debug > 2) printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) return -ENODEV; + + /* initialize variables */ + fdx = mii = fset = dxsuflo = ltint = 0; chip_version = (chip_version >> 12) & 0xffff; + switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; /* PCI */ @@ -587,23 +583,24 @@ * mode by which the card should operate */ /* switch to home wiring mode */ - media = a->read_bcr (ioaddr, 49); + media = a->read_bcr(ioaddr, 49); #if 0 if (pcnet32_debug > 2) - printk(KERN_DEBUG "pcnet32: pcnet32 media value %#x.\n", media); + printk(KERN_DEBUG PFX "media value %#x.\n", media); media &= ~3; media |= 1; #endif if (pcnet32_debug > 2) - printk(KERN_DEBUG "pcnet32: pcnet32 media reset to %#x.\n", media); - a->write_bcr (ioaddr, 49, media); + printk(KERN_DEBUG PFX "media reset to %#x.\n", media); + a->write_bcr(ioaddr, 49, media); break; case 0x2627: chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; break; default: - printk(KERN_INFO "pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version); + printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n", + chip_version); return -ENODEV; } @@ -618,17 +615,15 @@ { a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); -#ifdef DO_DXSUFLO dxsuflo = 1; -#endif ltint = 1; } - dev = init_etherdev(NULL, 0); - if(dev==NULL) + dev = alloc_etherdev(0); + if(!dev) return -ENOMEM; - printk(KERN_INFO "%s: %s at %#3lx,", dev->name, chipname, ioaddr); + printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr); /* In most chips, after a chip reset, the ethernet address is read from the * station address PROM at the base address and programmed into the @@ -644,31 +639,28 @@ dev->dev_addr[2*i] = val & 0x0ff; dev->dev_addr[2*i+1] = (val >> 8) & 0x0ff; } - { - u8 promaddr[6]; - for (i = 0; i < 6; i++) { - promaddr[i] = inb(ioaddr + i); - } - if( memcmp( promaddr, dev->dev_addr, 6) ) - { - printk(" warning PROM address does not match CSR address\n"); -#if defined(__i386__) - printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); - memcpy(dev->dev_addr, promaddr, 6); -#elif defined(__powerpc__) - if (!is_valid_ether_addr(dev->dev_addr) - && is_valid_ether_addr(promaddr)) { - printk("\n" KERN_WARNING "%s: using PROM address:", - dev->name); - memcpy(dev->dev_addr, promaddr, 6); - } + + /* read PROM address and compare with CSR address */ + for (i = 0; i < 6; i++) + promaddr[i] = inb(ioaddr + i); + + if( memcmp( promaddr, dev->dev_addr, 6) + || !is_valid_ether_addr(dev->dev_addr) ) { +#ifndef __powerpc__ + if( is_valid_ether_addr(promaddr) ){ +#else + if( !is_valid_ether_addr(dev->dev_addr) + && is_valid_ether_addr(promaddr)) { #endif - } + printk(" warning: CSR address invalid,\n"); + printk(KERN_INFO " using instead PROM address of"); + memcpy(dev->dev_addr, promaddr, 6); + } } + /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ if( !is_valid_ether_addr(dev->dev_addr) ) - for (i = 0; i < 6; i++) - dev->dev_addr[i]=0; + memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] ); @@ -698,7 +690,7 @@ dev->base_addr = ioaddr; res = request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); - if (res == NULL) + if (!res) return -EBUSY; /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ @@ -710,7 +702,6 @@ memset(lp, 0, sizeof(*lp)); lp->dma_addr = lp_dma_addr; lp->pci_dev = pdev; - printk("\n" KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x", lp, lp_dma_addr); spin_lock_init(&lp->lock); @@ -718,24 +709,23 @@ lp->name = chipname; lp->shared_irq = shared; lp->mii_if.full_duplex = fdx; -#ifdef DO_DXSUFLO lp->dxsuflo = dxsuflo; -#endif lp->ltint = ltint; lp->mii = mii; - if (options[card_idx] > sizeof (options_mapping)) + if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping))) lp->options = PCNET32_PORT_ASEL; else - lp->options = options_mapping[options[card_idx]]; + lp->options = options_mapping[options[cards_found]]; lp->mii_if.dev = dev; lp->mii_if.mdio_read = mdio_read; lp->mii_if.mdio_write = mdio_write; - if (fdx && !(lp->options & PCNET32_PORT_ASEL) && full_duplex[card_idx]) + if (fdx && !(lp->options & PCNET32_PORT_ASEL) && + ((cards_found>=MAX_UNITS) || full_duplex[cards_found])) lp->options |= PCNET32_PORT_FD; - if (a == NULL) { - printk(KERN_ERR "pcnet32: No access methods\n"); + if (!a) { + printk(KERN_ERR PFX "No access methods\n"); pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); release_resource(res); return -ENODEV; @@ -790,8 +780,6 @@ } } - if (pcnet32_debug > 0) - printk(KERN_INFO "%s", version); /* The PCNET32-specific entries in the device structure. */ dev->open = &pcnet32_open; @@ -807,11 +795,13 @@ pcnet32_dev = dev; /* Fill in the generic fields of the device structure. */ - ether_setup(dev); + register_netdev(dev); + printk(KERN_INFO "%s: registered as %s\n",dev->name, lp->name); + cards_found++; return 0; } - + static int pcnet32_open(struct net_device *dev) { @@ -856,6 +846,9 @@ val |= 1; if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI)) val |= 2; + } else if (lp->options & PCNET32_PORT_ASEL) { + /* workaround for xSeries250 */ + val |= 3; } lp->a.write_bcr (ioaddr, 9, val); } @@ -888,6 +881,7 @@ lp->a.write_csr (ioaddr, 3, val); } #endif + if (lp->ltint) { /* Enable TxDone-intr inhibitor */ val = lp->a.read_csr (ioaddr, 5); val |= (1<<14); @@ -922,7 +916,7 @@ if (pcnet32_debug > 2) printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n", dev->name, i, (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block)), - lp->a.read_csr (ioaddr, 0)); + lp->a.read_csr(ioaddr, 0)); MOD_INC_USE_COUNT; @@ -1032,7 +1026,7 @@ /* Transmitter timeout, serious problems. */ printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, lp->a.read_csr (ioaddr, 0)); + dev->name, lp->a.read_csr(ioaddr, 0)); lp->a.write_csr (ioaddr, 0, 0x0004); lp->stats.tx_errors++; if (pcnet32_debug > 2) { @@ -1068,7 +1062,7 @@ if (pcnet32_debug > 3) { printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", - dev->name, lp->a.read_csr (ioaddr, 0)); + dev->name, lp->a.read_csr(ioaddr, 0)); } spin_lock_irqsave(&lp->lock, flags); @@ -1135,8 +1129,9 @@ int boguscnt = max_interrupt_work; int must_restart; - if (dev == NULL) { - printk (KERN_DEBUG "pcnet32_interrupt(): irq %d for unknown device.\n", irq); + if (!dev) { + printk (KERN_DEBUG "%s(): irq %d for unknown device\n", + __FUNCTION__, irq); return; } @@ -1207,7 +1202,8 @@ /* We must free the original skb */ if (lp->tx_skbuff[entry]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); + pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], + lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(lp->tx_skbuff[entry]); lp->tx_skbuff[entry] = 0; lp->tx_dma_addr[entry] = 0; @@ -1215,13 +1211,12 @@ dirty_tx++; } -#ifndef final_version if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dirty_tx, lp->cur_tx, lp->tx_full); + printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); dirty_tx += TX_RING_SIZE; } -#endif + if (lp->tx_full && netif_queue_stopped(dev) && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { @@ -1262,7 +1257,7 @@ /* Clear any other interrupt, and set interrupt enable. */ lp->a.write_csr (ioaddr, 0, 0x7940); - lp->a.write_rap(ioaddr,rap); + lp->a.write_rap (ioaddr,rap); if (pcnet32_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n", @@ -1315,7 +1310,9 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = + pci_map_single(lp->pci_dev, newskb->tail, + newskb->len, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1349,6 +1346,7 @@ lp->stats.rx_bytes += skb->len; skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); + dev->last_rx = jiffies; lp->stats.rx_packets++; } } @@ -1445,13 +1443,13 @@ /* set all multicast bits */ if (dev->flags & IFF_ALLMULTI){ - ib->filter [0] = 0xffffffff; - ib->filter [1] = 0xffffffff; + ib->filter[0] = 0xffffffff; + ib->filter[1] = 0xffffffff; return; } /* clear the multicast filter */ - ib->filter [0] = 0; - ib->filter [1] = 0; + ib->filter[0] = 0; + ib->filter[1] = 0; /* Add addresses */ for (i = 0; i < dev->mc_count; i++){ @@ -1648,20 +1646,28 @@ } return -EOPNOTSUPP; } - + static struct pci_driver pcnet32_driver = { - name: DRV_NAME, - probe: pcnet32_probe_pci, - remove: NULL, - id_table: pcnet32_pci_tbl, + name: DRV_NAME, + probe: pcnet32_probe_pci, + id_table: pcnet32_pci_tbl, }; MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, DRV_NAME " debug level (0-6)"); MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt"); MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames"); MODULE_PARM(tx_start_pt, "i"); +MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)"); +MODULE_PARM(pcnet32vlb, "i"); +MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)"); + MODULE_AUTHOR("Thomas Bogendoerfer"); MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards"); MODULE_LICENSE("GPL"); @@ -1672,36 +1678,25 @@ static int __init pcnet32_init_module(void) { - int cards_found = 0; - int err; + printk(KERN_INFO "%s", version); if (debug > 0) pcnet32_debug = debug; + if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) tx_start = tx_start_pt; - - pcnet32_dev = NULL; + /* find the PCI devices */ -#define USE_PCI_REGISTER_DRIVER -#ifdef USE_PCI_REGISTER_DRIVER - if ((err = pci_module_init(&pcnet32_driver)) < 0 ) - return err; -#else - { - struct pci_device_id *devid = pcnet32_pci_tbl; - for (devid = pcnet32_pci_tbl; devid != NULL && devid->vendor != 0; devid++) { - struct pci_dev *pdev = pci_find_subsys(devid->vendor, devid->device, devid->subvendor, devid->subdevice, NULL); - if (pdev != NULL) { - if (pcnet32_probe_pci(pdev, devid) >= 0) { - cards_found++; - } - } - } - } -#endif - return 0; - /* find any remaining VLbus devices */ - return pcnet32_probe_vlbus(cards_found); + pci_module_init(&pcnet32_driver); + + /* should we find any remaining VLbus devices ? */ + if (pcnet32vlb) + pcnet32_probe_vlbus(); + + if (cards_found) + printk(KERN_INFO PFX "%d cards_found.\n", cards_found); + + return cards_found ? 0 : -ENODEV; } static void __exit pcnet32_cleanup_module(void) @@ -1710,13 +1705,13 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (pcnet32_dev) { - struct pcnet32_private *lp = pcnet32_dev->priv; + struct pcnet32_private *lp = pcnet32_dev->priv; next_dev = lp->next; unregister_netdev(pcnet32_dev); release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); - if (lp->pci_dev != NULL) + if (lp->pci_dev) pci_unregister_driver(&pcnet32_driver); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); kfree(pcnet32_dev); pcnet32_dev = next_dev; } diff -urN linux-2.5.6-pre3/drivers/net/saa9730.c linux-2.5.6/drivers/net/saa9730.c --- linux-2.5.6-pre3/drivers/net/saa9730.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/net/saa9730.c Thu Mar 7 18:24:47 2002 @@ -685,6 +685,7 @@ len, 0); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); + dev->last_rx = jiffies; } } else { /* We got an error packet. */ diff -urN linux-2.5.6-pre3/drivers/net/sk98lin/skge.c linux-2.5.6/drivers/net/sk98lin/skge.c --- linux-2.5.6-pre3/drivers/net/sk98lin/skge.c Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/drivers/net/sk98lin/skge.c Thu Mar 7 18:24:47 2002 @@ -541,6 +541,48 @@ boards_found++; + /* More then one port found */ + if ((pAC->GIni.GIMacsFound == 2 ) && (pAC->RlmtNets == 2)) { + if ((dev = init_etherdev(NULL, sizeof(DEV_NET))) == 0) { + printk(KERN_ERR "Unable to allocate etherdev " + "structure!\n"); + break; + } + + pAC->dev[1] = dev; + pNet = dev->priv; + pNet->PortNr = 1; + pNet->NetNr = 1; + pNet->pAC = pAC; + pNet->Mtu = 1500; + pNet->Up = 0; + + dev->open = &SkGeOpen; + dev->stop = &SkGeClose; + dev->hard_start_xmit = &SkGeXmit; + dev->get_stats = &SkGeStats; + dev->set_multicast_list = &SkGeSetRxMode; + dev->set_mac_address = &SkGeSetMacAddr; + dev->do_ioctl = &SkGeIoctl; + dev->change_mtu = &SkGeChangeMtu; + + pProcFile = create_proc_entry(dev->name, + S_IFREG | 0444, pSkRootDir); + pProcFile->read_proc = proc_read; + pProcFile->write_proc = NULL; + pProcFile->nlink = 1; + pProcFile->size = sizeof(dev->name+1); + pProcFile->data = (void*)pProcFile; + + memcpy((caddr_t) &dev->dev_addr, + (caddr_t) &pAC->Addr.Net[1].CurrentMacAddress, 6); + + printk("%s: %s\n", dev->name, pAC->DeviceStr); + printk(" PrefPort:B RlmtMode:Dual Check Link State\n"); + + } + + /* * This is bollocks, but we need to tell the net-init * code that it shall go for the next device. diff -urN linux-2.5.6-pre3/drivers/net/smc-ultra.c linux-2.5.6/drivers/net/smc-ultra.c --- linux-2.5.6-pre3/drivers/net/smc-ultra.c Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/drivers/net/smc-ultra.c Thu Mar 7 18:24:47 2002 @@ -501,8 +501,10 @@ MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); -MODULE_PARM_DESC(io, "SMC Ultra I/O base address(es)"); -MODULE_PARM_DESC(irq, "SMC Ultra IRQ number(s) (assigned)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; @@ -557,7 +559,6 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -urN linux-2.5.6-pre3/drivers/net/smc-ultra32.c linux-2.5.6/drivers/net/smc-ultra32.c --- linux-2.5.6-pre3/drivers/net/smc-ultra32.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/drivers/net/smc-ultra32.c Thu Mar 7 18:24:47 2002 @@ -379,6 +379,9 @@ #define MAX_ULTRA32_CARDS 4 /* Max number of Ultra cards per module */ static struct net_device dev_ultra[MAX_ULTRA32_CARDS]; +MODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver"); +MODULE_LICENSE("GPL"); + int init_module(void) { int this_dev, found = 0; @@ -415,5 +418,4 @@ } } #endif /* MODULE */ -MODULE_LICENSE("GPL"); diff -urN linux-2.5.6-pre3/drivers/net/starfire.c linux-2.5.6/drivers/net/starfire.c --- linux-2.5.6-pre3/drivers/net/starfire.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/starfire.c Thu Mar 7 18:24:47 2002 @@ -96,13 +96,19 @@ LK1.3.5 (jgarzik) - ethtool NWAY_RST, GLINK, [GS]MSGLVL support + LK1.3.6: + - Sparc64 support and fixes (Ion Badulescu) + - Better stats and error handling (Ion Badulescu) + - Use new pci_set_mwi() PCI API function (jgarzik) + TODO: - implement tx_timeout() properly + - VLAN support */ #define DRV_NAME "starfire" -#define DRV_VERSION "1.03+LK1.3.5" -#define DRV_RELDATE "November 17, 2001" +#define DRV_VERSION "1.03+LK1.3.6" +#define DRV_RELDATE "March 7, 2002" #include #include @@ -128,8 +134,11 @@ * for this driver to really use the firmware. Note that Rx/Tx * hardware TCP checksumming is not possible without the firmware. * - * I'm currently [Feb 2001] talking to Adaptec about this redistribution - * issue. Stay tuned... + * If Adaptec could allow redistribution of the firmware (even in binary + * format), life would become a lot easier. Unfortunately, I've lost my + * Adaptec contacts, so progress on this front is rather unlikely to + * occur. If anybody from Adaptec reads this and can help with this matter, + * please let me know... */ #undef HAS_FIRMWARE /* @@ -609,7 +618,10 @@ long ioaddr; int drv_flags, io_size; int boguscnt; +#ifndef HAVE_PCI_SET_MWI + u16 cmd; u8 cache; +#endif /* when built into the kernel, we only print version if device is found */ #ifndef MODULE @@ -644,14 +656,25 @@ goto err_out_free_netdev; } - ioaddr = (long) ioremap (ioaddr, io_size); + /* ioremap is borken in Linux-2.2.x/sparc64 */ +#if !defined(CONFIG_SPARC64) || LINUX_VERSION_CODE > 0x20300 + ioaddr = (long) ioremap(ioaddr, io_size); if (!ioaddr) { printk (KERN_ERR DRV_NAME " %d: cannot remap 0x%x @ 0x%lx, aborting\n", card_idx, io_size, ioaddr); goto err_out_free_res; } +#endif /* !CONFIG_SPARC64 || Linux 2.3.0+ */ - pci_set_master (pdev); + pci_set_master(pdev); + +#ifdef HAVE_PCI_SET_MWI + pci_set_mwi(pdev); +#else + /* enable MWI -- it vastly improves Rx performance on sparc64 */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_INVALIDATE; + pci_write_config_word(pdev, PCI_COMMAND, cmd); /* set PCI cache size */ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); @@ -662,6 +685,7 @@ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, SMP_CACHE_BYTES >> 2); } +#endif #ifdef ZEROCOPY /* Starfire can do SG and TCP/UDP checksumming */ @@ -670,7 +694,7 @@ /* Serial EEPROM reads are hidden by the hardware. */ for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20-i); + dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20 - i); #if ! defined(final_version) /* Dump the EEPROM contents during development. */ if (debug > 4) @@ -932,7 +956,7 @@ /* Fill both the unused Tx SA register and the Rx perfect filter. */ for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + 5-i); + writeb(dev->dev_addr[i], ioaddr + StationAddr + 5 - i); for (i = 0; i < 16; i++) { u16 *eaddrs = (u16 *)dev->dev_addr; long setup_frm = ioaddr + PerfFilterTable + i * 16; @@ -979,9 +1003,9 @@ #ifdef HAS_FIRMWARE /* Load Rx/Tx firmware into the frame processors */ for (i = 0; i < FIRMWARE_RX_SIZE * 2; i++) - writel(cpu_to_le32(firmware_rx[i]), ioaddr + RxGfpMem + i * 4); + writel(firmware_rx[i], ioaddr + RxGfpMem + i * 4); for (i = 0; i < FIRMWARE_TX_SIZE * 2; i++) - writel(cpu_to_le32(firmware_tx[i]), ioaddr + TxGfpMem + i * 4); + writel(firmware_tx[i], ioaddr + TxGfpMem + i * 4); /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */ writel(0x003F, ioaddr + GenCtrl); #else /* not HAS_FIRMWARE */ @@ -1156,8 +1180,8 @@ np->tx_ring[entry].first_addr = cpu_to_le32(np->tx_info[entry].first_mapping); #ifdef ZEROCOPY - np->tx_ring[entry].first_len = cpu_to_le32(skb_first_frag_len(skb)); - np->tx_ring[entry].total_len = cpu_to_le32(skb->len); + np->tx_ring[entry].first_len = cpu_to_le16(skb_first_frag_len(skb)); + np->tx_ring[entry].total_len = cpu_to_le16(skb->len); /* Add "| TxDescIntr" to generate Tx-done interrupts. */ np->tx_ring[entry].status = cpu_to_le32(TxDescID | TxCRCEn); np->tx_ring[entry].nbufs = cpu_to_le32(skb_shinfo(skb)->nr_frags + 1); @@ -1170,8 +1194,10 @@ np->tx_ring[entry].status |= cpu_to_le32(TxRingWrap | TxDescIntr); #ifdef ZEROCOPY - if (skb->ip_summed == CHECKSUM_HW) + if (skb->ip_summed == CHECKSUM_HW) { np->tx_ring[entry].status |= cpu_to_le32(TxCalTCP); + np->stats.tx_compressed++; + } #endif /* ZEROCOPY */ if (debug > 5) { @@ -1449,6 +1475,7 @@ #if defined(full_rx_status) || defined(csum_rx_status) if (le32_to_cpu(np->rx_done_q[np->rx_done].status2) & 0x01000000) { skb->ip_summed = CHECKSUM_UNNECESSARY; + np->stats.rx_compressed++; } /* * This feature doesn't seem to be working, at least @@ -1580,12 +1607,17 @@ printk(KERN_NOTICE "%s: Increasing Tx FIFO threshold to %d bytes\n", dev->name, np->tx_threshold * 16); } - if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrPCIPad)) && debug) + if (intr_status & IntrRxGFPDead) { + np->stats.rx_fifo_errors++; + np->stats.rx_errors++; + } + if (intr_status & (IntrNoTxCsum | IntrDMAErr)) { + np->stats.tx_fifo_errors++; + np->stats.tx_errors++; + } + if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrRxGFPDead | IntrNoTxCsum | IntrPCIPad)) && debug) printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); - /* Hmmmmm, it's not clear how to recover from DMA faults. */ - if (intr_status & IntrDMAErr) - np->stats.tx_fifo_errors++; } diff -urN linux-2.5.6-pre3/drivers/net/tg3.c linux-2.5.6/drivers/net/tg3.c --- linux-2.5.6-pre3/drivers/net/tg3.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tg3.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,5925 @@ +/* $Id: tg3.c,v 1.43.2.74 2002/03/06 22:22:29 davem Exp $ + * tg3.c: Broadcom Tigon3 ethernet driver. + * + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com) + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifndef PCI_DMA_BUS_IS_PHYS +#define PCI_DMA_BUS_IS_PHYS 1 +#endif + +/* Either I can't figure out how they secretly implemented it (ie. RXD flags + * for mini ring, where it should go in NIC sram, and how many entries the NIC + * firmware expects) or it isn't really fully implemented. Perhaps Broadcom + * wants people to pay for a "performance enhanced" version of their firmware + + * binary-only driver that has the mini ring actually implemented. + * These kids today... -DaveM + */ +#define TG3_MINI_RING_WORKS 0 + +#define TG3_VLAN_TAG_USED 0 + +#include "tg3.h" + +#define DRV_MODULE_NAME "tg3" +#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_VERSION "0.96" +#define DRV_MODULE_RELDATE "Mar 6, 2002" + +#define TG3_DEF_MAC_MODE 0 +#define TG3_DEF_RX_MODE 0 +#define TG3_DEF_TX_MODE 0 +#define TG3_DEF_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +/* length of time before we decide the hardware is borked, + * and dev->tx_timeout() should be called to fix the problem + */ +#define TG3_TX_TIMEOUT (5 * HZ) + +/* hardware minimum and maximum for a single frame's data payload */ +#define TG3_MIN_MTU 60 +#define TG3_MAX_MTU 9000 + +/* These numbers seem to be hard coded in the NIC firmware somehow. + * You can't change the ring sizes, but you can change where you place + * them in the NIC onboard memory. + */ +#define TG3_RX_RING_SIZE 512 +#define TG3_RX_RING_PENDING 200 +#if TG3_MINI_RING_WORKS +#define TG3_RX_MINI_RING_SIZE 256 /* ??? */ +#define TG3_RX_MINI_RING_PENDING 100 +#endif +#define TG3_RX_JUMBO_RING_SIZE 256 +#define TG3_RX_JUMBO_RING_PENDING 100 +#define TG3_RX_RCB_RING_SIZE 1024 +#define TG3_TX_RING_SIZE 512 + +#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_RING_SIZE) +#if TG3_MINI_RING_WORKS +#define TG3_RX_MINI_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_MINI_RING_SIZE) +#endif +#define TG3_RX_JUMBO_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_JUMBO_RING_SIZE) +#define TG3_RX_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * \ + TG3_RX_RCB_RING_SIZE) +#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * \ + TG3_TX_RING_SIZE) +#define TX_BUFFS_AVAIL(TP) \ + (((TP)->tx_cons <= (TP)->tx_prod) ? \ + (TP)->tx_cons + (TG3_TX_RING_SIZE - 1) - (TP)->tx_prod : \ + (TP)->tx_cons - (TP)->tx_prod - 1) +#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1)) + +#define RX_PKT_BUF_SZ (1536 + tp->rx_offset + 64) +#if TG3_MINI_RING_WORKS +#define RX_MINI_PKT_BUF_SZ (256 + tp->rx_offset + 64) +#endif +#define RX_JUMBO_PKT_BUF_SZ (9046 + tp->rx_offset + 64) + +/* minimum number of free TX descriptors required to wake up TX process */ +#define TG3_TX_WAKEUP_THRESH (TG3_TX_RING_SIZE / 4) + +static char version[] __devinitdata = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + +MODULE_AUTHOR("David S. Miller (davem@redhat.com) and Jeff Garzik (jgarzik@mandrakesoft.com)"); +MODULE_DESCRIPTION("Broadcom Tigon3 ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(tg3_debug, "i"); +MODULE_PARM_DESC(tg3_debug, "Tigon3 bitmapped debugging message enable value"); + +static int tg3_debug = -1; /* -1 == use TG3_DEF_MSG_ENABLE as value */ + +static struct pci_device_id tg3_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_SYSKONNECT, 0x4400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, tg3_pci_tbl); + +static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val) +{ + if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) { + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off); + pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val); + spin_unlock_irqrestore(&tp->indirect_lock, flags); + } else { + writel(val, tp->regs + off); + } +} + +#define tw32(reg,val) tg3_write_indirect_reg32(tp,(reg),(val)) +#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tp->regs + (reg)) +#define tw16(reg,val) writew(((val) & 0xffff), tp->regs + (reg)) +#define tw8(reg,val) writeb(((val) & 0xff), tp->regs + (reg)) +#define tr32(reg) readl(tp->regs + (reg)) +#define tr16(reg) readw(tp->regs + (reg)) +#define tr8(reg) readb(tp->regs + (reg)) + +static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + spin_unlock_irqrestore(&tp->indirect_lock, flags); +} + +static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->indirect_lock, flags); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + spin_unlock_irqrestore(&tp->indirect_lock, flags); +} + +static void tg3_disable_ints(struct tg3 *tp) +{ + tw32(TG3PCI_MISC_HOST_CTRL, + (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); +} + +static void tg3_enable_ints(struct tg3 *tp) +{ + tw32(TG3PCI_MISC_HOST_CTRL, + (tp->misc_host_ctrl & ~MISC_HOST_CTRL_MASK_PCI_INT)); + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000000); + + if (tp->hw_status->status & SD_STATUS_UPDATED) + tw32(GRC_LOCAL_CTRL, + tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); +} + +#define PHY_BUSY_LOOPS 5000 + +static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) +{ + u32 frame_val; + int loops, ret; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, + (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); + udelay(40); + } + + *val = 0xffffffff; + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (MI_COM_CMD_READ | MI_COM_START); + + tw32(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + frame_val = tr32(MAC_MI_COM); + + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + udelay(10); + } + + ret = -EBUSY; + if (loops > 0) { + *val = frame_val & MI_COM_DATA_MASK; + ret = 0; + } + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + } + + return ret; +} + +static int tg3_writephy(struct tg3 *tp, int reg, u32 val) +{ + u32 frame_val; + int loops, ret; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, + (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); + udelay(40); + } + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (val & MI_COM_DATA_MASK); + frame_val |= (MI_COM_CMD_WRITE | MI_COM_START); + + tw32(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + frame_val = tr32(MAC_MI_COM); + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + udelay(10); + } + + ret = -EBUSY; + if (loops > 0) + ret = 0; + + if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + } + + return ret; +} + +/* This will reset the tigon3 PHY if there is no valid + * link unless the FORCE argument is non-zero. + */ +static int tg3_phy_reset(struct tg3 *tp, int force) +{ + u32 phy_status, phy_control; + int err, limit; + + err = tg3_readphy(tp, MII_BMSR, &phy_status); + err |= tg3_readphy(tp, MII_BMSR, &phy_status); + if (err != 0) + return -EBUSY; + + /* If we have link, and not forcing a reset, then nothing + * to do. + */ + if ((phy_status & BMSR_LSTATUS) != 0 && (force == 0)) + return 0; + + /* OK, reset it, and poll the BMCR_RESET bit until it + * clears or we time out. + */ + phy_control = BMCR_RESET; + err = tg3_writephy(tp, MII_BMCR, phy_control); + if (err != 0) + return -EBUSY; + + limit = 5000; + while (limit--) { + err = tg3_readphy(tp, MII_BMCR, &phy_control); + if (err != 0) + return -EBUSY; + + if ((phy_control & BMCR_RESET) == 0) { + udelay(40); + return 0; + } + udelay(10); + } + + return -EBUSY; +} + +static int tg3_setup_phy(struct tg3 *); +static int tg3_halt(struct tg3 *); + +static int tg3_set_power_state(struct tg3 *tp, int state) +{ + u32 misc_host_ctrl; + u16 power_control, power_caps; + int pm = tp->pm_cap; + + /* Make sure register accesses (indirect or otherwise) + * will function correctly. + */ + pci_write_config_dword(tp->pdev, + TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + pci_read_config_word(tp->pdev, + pm + PCI_PM_CTRL, + &power_control); + power_control |= PCI_PM_CTRL_PME_STATUS; + power_control &= ~(PCI_PM_CTRL_STATE_MASK); + switch (state) { + case 0: + power_control |= 0; + pci_write_config_word(tp->pdev, + pm + PCI_PM_CTRL, + power_control); + tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); + return 0; + + case 1: + power_control |= 1; + break; + + case 2: + power_control |= 2; + break; + + case 3: + power_control |= 3; + break; + + default: + printk(KERN_WARNING "%s: Invalid power state (%d) requested.\n", + tp->dev->name, state); + return -EINVAL; + }; + + power_control |= PCI_PM_CTRL_PME_ENABLE; + + misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL); + tw32(TG3PCI_MISC_HOST_CTRL, + misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT); + + if (tp->link_config.phy_is_low_power == 0) { + tp->link_config.phy_is_low_power = 1; + tp->link_config.orig_speed = tp->link_config.speed; + tp->link_config.orig_duplex = tp->link_config.duplex; + tp->link_config.orig_autoneg = tp->link_config.autoneg; + } + + tp->link_config.speed = SPEED_10; + tp->link_config.autoneg = AUTONEG_ENABLE; + tg3_setup_phy(tp); + + tg3_halt(tp); + + pci_read_config_word(tp->pdev, pm + PCI_PM_PMC, &power_caps); + + if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) { + u32 mac_mode; + + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a); + + mac_mode = MAC_MODE_PORT_MODE_MII | + MAC_MODE_LINK_POLARITY; + + if (((power_caps & PCI_PM_CAP_PME_D3cold) && + (tp->tg3_flags & TG3_FLAG_WOL_ENABLE))) + mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE; + + tw32(MAC_MODE, mac_mode); + tw32(MAC_RX_MODE, RX_MODE_ENABLE); + } + + if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) { + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK)); + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_44MHZ_CORE)); + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK | + CLOCK_CTRL_44MHZ_CORE)); + } else { + tw32(TG3PCI_CLOCK_CTRL, + (CLOCK_CTRL_RXCLK_DISABLE | + CLOCK_CTRL_TXCLK_DISABLE | + CLOCK_CTRL_ALTCLK | + CLOCK_CTRL_PWRDOWN_PLL133)); + } + + udelay(40); + + if ((power_caps & PCI_PM_CAP_PME_D3cold) && + (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)) { + /* Move to auxilliary power. */ + tw32(GRC_LOCAL_CTRL, + (GRC_LCLCTRL_GPIO_OE0 | + GRC_LCLCTRL_GPIO_OE1 | + GRC_LCLCTRL_GPIO_OE2 | + GRC_LCLCTRL_GPIO_OUTPUT0 | + GRC_LCLCTRL_GPIO_OUTPUT1)); + } + + /* Finally, set the new power state. */ + pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control); + + return 0; +} + +static void tg3_link_report(struct tg3 *tp) +{ + if (!netif_carrier_ok(tp->dev)) { + printk("%s: Link is down.\n", tp->dev->name); + } else { + printk("%s: Link is up at %d Mbps, %s duplex.\n", + tp->dev->name, + (tp->link_config.active_speed == SPEED_1000 ? + 1000 : + (tp->link_config.active_speed == SPEED_100 ? + 100 : 10)), + (tp->link_config.active_duplex == DUPLEX_FULL ? + "full" : "half")); + + printk("%s: Flow control is %s for TX and %s for RX.\n", + tp->dev->name, + (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "on" : "off", + (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "on" : "off"); + } +} + +static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv) +{ + u32 new_tg3_flags = 0; + + if (local_adv & ADVERTISE_PAUSE_CAP) { + if (local_adv & ADVERTISE_PAUSE_ASYM) { + if (remote_adv & LPA_PAUSE_CAP) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE | + TG3_FLAG_TX_PAUSE); + else if (remote_adv & LPA_PAUSE_ASYM) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE); + } else { + if (remote_adv & LPA_PAUSE_CAP) + new_tg3_flags |= + (TG3_FLAG_RX_PAUSE | + TG3_FLAG_TX_PAUSE); + } + } else if (local_adv & ADVERTISE_PAUSE_ASYM) { + if ((remote_adv & LPA_PAUSE_CAP) && + (remote_adv & LPA_PAUSE_ASYM)) + new_tg3_flags |= TG3_FLAG_TX_PAUSE; + } + + tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE); + tp->tg3_flags |= new_tg3_flags; + + if (new_tg3_flags & TG3_FLAG_RX_PAUSE) + tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE; + else + tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; + + if (new_tg3_flags & TG3_FLAG_TX_PAUSE) + tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; + else + tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; +} + +static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) +{ + switch (val & MII_TG3_AUX_STAT_SPDMASK) { + case MII_TG3_AUX_STAT_10HALF: + *speed = SPEED_10; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_10FULL: + *speed = SPEED_10; + *duplex = DUPLEX_FULL; + break; + + case MII_TG3_AUX_STAT_100HALF: + *speed = SPEED_100; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_100FULL: + *speed = SPEED_100; + *duplex = DUPLEX_FULL; + break; + + case MII_TG3_AUX_STAT_1000HALF: + *speed = SPEED_1000; + *duplex = DUPLEX_HALF; + break; + + case MII_TG3_AUX_STAT_1000FULL: + *speed = SPEED_1000; + *duplex = DUPLEX_FULL; + break; + + default: + *speed = SPEED_INVALID; + *duplex = DUPLEX_INVALID; + break; + }; +} + +static int tg3_phy_copper_begin(struct tg3 *tp, int wait_for_link) +{ + u32 new_adv; + int i; + + if (tp->link_config.phy_is_low_power) { + /* Entering low power mode. Disable gigabit and + * 100baseT advertisements. + */ + tg3_writephy(tp, MII_TG3_CTRL, 0); + + new_adv = (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB) + new_adv |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + + tg3_writephy(tp, MII_ADVERTISE, new_adv); + } else if (tp->link_config.speed == SPEED_INVALID) { + tp->link_config.advertising = + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_MII); + + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + tp->link_config.advertising &= + ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full); + + new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + if (tp->link_config.advertising & ADVERTISED_10baseT_Half) + new_adv |= ADVERTISE_10HALF; + if (tp->link_config.advertising & ADVERTISED_10baseT_Full) + new_adv |= ADVERTISE_10FULL; + if (tp->link_config.advertising & ADVERTISED_100baseT_Half) + new_adv |= ADVERTISE_100HALF; + if (tp->link_config.advertising & ADVERTISED_100baseT_Full) + new_adv |= ADVERTISE_100FULL; + tg3_writephy(tp, MII_ADVERTISE, new_adv); + + if (tp->link_config.advertising & + (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) { + new_adv = 0; + if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) + new_adv |= MII_TG3_CTRL_ADV_1000_HALF; + if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) + new_adv |= MII_TG3_CTRL_ADV_1000_FULL; + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + new_adv |= (MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + tg3_writephy(tp, MII_TG3_CTRL, new_adv); + } else { + tg3_writephy(tp, MII_TG3_CTRL, 0); + } + } else { + /* Asking for a specific link mode. */ + if (tp->link_config.speed == SPEED_1000) { + new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; + tg3_writephy(tp, MII_ADVERTISE, new_adv); + + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv = MII_TG3_CTRL_ADV_1000_FULL; + else + new_adv = MII_TG3_CTRL_ADV_1000_HALF; + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + new_adv |= (MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + tg3_writephy(tp, MII_TG3_CTRL, new_adv); + } else { + tg3_writephy(tp, MII_TG3_CTRL, 0); + + new_adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP; + if (tp->link_config.speed == SPEED_100) { + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv |= ADVERTISE_100FULL; + else + new_adv |= ADVERTISE_100HALF; + } else { + if (tp->link_config.duplex == DUPLEX_FULL) + new_adv |= ADVERTISE_10FULL; + else + new_adv |= ADVERTISE_10HALF; + } + tg3_writephy(tp, MII_ADVERTISE, new_adv); + } + } + + if (tp->link_config.autoneg == AUTONEG_DISABLE && + tp->link_config.speed != SPEED_INVALID) { + u32 bmcr, orig_bmcr; + + tp->link_config.active_speed = tp->link_config.speed; + tp->link_config.active_duplex = tp->link_config.duplex; + + bmcr = 0; + switch (tp->link_config.speed) { + default: + case SPEED_10: + break; + + case SPEED_100: + bmcr |= BMCR_SPEED100; + break; + + case SPEED_1000: + bmcr |= TG3_BMCR_SPEED1000; + break; + }; + + if (tp->link_config.duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + + tg3_readphy(tp, MII_BMCR, &orig_bmcr); + if (bmcr != orig_bmcr) { + tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK); + for (i = 0; i < 15000; i++) { + u32 tmp; + + udelay(10); + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (!(tmp & BMSR_LSTATUS)) { + udelay(40); + break; + } + } + tg3_writephy(tp, MII_BMCR, bmcr); + udelay(40); + } + } else { + tg3_writephy(tp, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + } + + if (wait_for_link) { + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + for (i = 0; i < 300000; i++) { + u32 tmp; + + udelay(10); + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (!(tmp & BMSR_LSTATUS)) + continue; + + tg3_readphy(tp, MII_TG3_AUX_STAT, &tmp); + tg3_aux_stat_to_speed_duplex(tp, tmp, + &tp->link_config.active_speed, + &tp->link_config.active_duplex); + } + if (tp->link_config.active_speed == SPEED_INVALID) + return -EINVAL; + } + + return 0; +} + +static int tg3_init_5401phy_dsp(struct tg3 *tp) +{ + int err; + + /* Turn off tap power management. */ + err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c20); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0013); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1204); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0132); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8006); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0232); + + err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0a20); + + udelay(40); + + return err; +} + +static int tg3_setup_copper_phy(struct tg3 *tp) +{ + int current_link_up; + u32 bmsr, dummy; + u16 current_speed; + u8 current_duplex; + int i, err; + + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + + tp->mi_mode = MAC_MI_MODE_BASE; + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + + if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) + bmsr = 0; + + if (!(bmsr & BMSR_LSTATUS)) { + err = tg3_init_5401phy_dsp(tp); + if (err) + return err; + + tg3_readphy(tp, MII_BMSR, &bmsr); + for (i = 0; i < 1000; i++) { + udelay(10); + tg3_readphy(tp, MII_BMSR, &bmsr); + if (bmsr & BMSR_LSTATUS) { + udelay(40); + break; + } + } + + if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && + !(bmsr & BMSR_LSTATUS) && + tp->link_config.active_speed == SPEED_1000) { + err = tg3_phy_reset(tp, 1); + if (!err) + err = tg3_init_5401phy_dsp(tp); + if (err) + return err; + } + } + } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { + /* 5701 {A0,B0} CRC bug workaround */ + tg3_writephy(tp, 0x15, 0x0a75); + tg3_writephy(tp, 0x1c, 0x8c68); + tg3_writephy(tp, 0x1c, 0x8d68); + tg3_writephy(tp, 0x1c, 0x8c68); + } + + /* Clear pending interrupts... */ + tg3_readphy(tp, MII_TG3_ISTAT, &dummy); + tg3_readphy(tp, MII_TG3_ISTAT, &dummy); + + if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) + tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG); + else + tg3_writephy(tp, MII_TG3_IMASK, ~0); + + if (tp->led_mode == led_mode_three_link) + tg3_writephy(tp, MII_TG3_EXT_CTRL, + MII_TG3_EXT_CTRL_LNK3_LED_MODE); + else + tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); + + current_link_up = 0; + current_speed = SPEED_INVALID; + current_duplex = DUPLEX_INVALID; + + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); + + if (bmsr & BMSR_LSTATUS) { + u32 aux_stat, bmcr; + + tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); + for (i = 0; i < 2000; i++) { + udelay(10); + tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat); + if (aux_stat) + break; + } + + tg3_aux_stat_to_speed_duplex(tp, aux_stat, + ¤t_speed, + ¤t_duplex); + tg3_readphy(tp, MII_BMCR, &bmcr); + tg3_readphy(tp, MII_BMCR, &bmcr); + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + if (bmcr & BMCR_ANENABLE) { + u32 gig_ctrl; + + current_link_up = 1; + + /* Force autoneg restart if we are exiting + * low power mode. + */ + tg3_readphy(tp, MII_TG3_CTRL, &gig_ctrl); + if (!(gig_ctrl & (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL))) { + current_link_up = 0; + } + } else { + current_link_up = 0; + } + } else { + if (!(bmcr & BMCR_ANENABLE) && + tp->link_config.speed == current_speed && + tp->link_config.duplex == current_duplex) { + current_link_up = 1; + } else { + current_link_up = 0; + } + } + + tp->link_config.active_speed = current_speed; + tp->link_config.active_duplex = current_duplex; + } + + if (current_link_up == 1 && + (tp->link_config.active_duplex == DUPLEX_FULL) && + (tp->link_config.autoneg == AUTONEG_ENABLE)) { + u32 local_adv, remote_adv; + + tg3_readphy(tp, MII_ADVERTISE, &local_adv); + local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + + tg3_readphy(tp, MII_LPA, &remote_adv); + remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM); + + /* If we are not advertising full pause capability, + * something is wrong. Bring the link down and reconfigure. + */ + if (local_adv != ADVERTISE_PAUSE_CAP) { + current_link_up = 0; + } else { + tg3_setup_flow_control(tp, local_adv, remote_adv); + } + } + + if (current_link_up == 0) { + u32 tmp; + + tg3_phy_copper_begin(tp, 0); + + tg3_readphy(tp, MII_BMSR, &tmp); + tg3_readphy(tp, MII_BMSR, &tmp); + if (tmp & BMSR_LSTATUS) + current_link_up = 1; + } + + tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK; + if (current_link_up == 1) { + if (tp->link_config.active_speed == SPEED_100 || + tp->link_config.active_speed == SPEED_10) + tp->mac_mode |= MAC_MODE_PORT_MODE_MII; + else + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + } else + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + + tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; + if (tp->link_config.active_duplex == DUPLEX_HALF) + tp->mac_mode |= MAC_MODE_HALF_DUPLEX; + + tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { + if (current_link_up == 1) + tp->mac_mode |= MAC_MODE_LINK_POLARITY; + tw32(MAC_LED_CTRL, LED_CTRL_PHY_MODE_1); + } else { + if ((tp->led_mode == led_mode_link10) || + (current_link_up == 1 && + tp->link_config.active_speed == SPEED_10)) + tp->mac_mode |= MAC_MODE_LINK_POLARITY; + } + tw32(MAC_MODE, tp->mac_mode); + + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { + /* Polled via timer. */ + tw32(MAC_EVENT, 0); + } else { + tw32(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 && + current_link_up == 1 && + tp->link_config.active_speed == SPEED_1000 && + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) || + (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) { + udelay(120); + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC2); + } + + if (current_link_up != netif_carrier_ok(tp->dev)) { + if (current_link_up) + netif_carrier_on(tp->dev); + else + netif_carrier_off(tp->dev); + tg3_link_report(tp); + } + + return 0; +} + +struct tg3_fiber_aneginfo { + int state; +#define ANEG_STATE_UNKNOWN 0 +#define ANEG_STATE_AN_ENABLE 1 +#define ANEG_STATE_RESTART_INIT 2 +#define ANEG_STATE_RESTART 3 +#define ANEG_STATE_DISABLE_LINK_OK 4 +#define ANEG_STATE_ABILITY_DETECT_INIT 5 +#define ANEG_STATE_ABILITY_DETECT 6 +#define ANEG_STATE_ACK_DETECT_INIT 7 +#define ANEG_STATE_ACK_DETECT 8 +#define ANEG_STATE_COMPLETE_ACK_INIT 9 +#define ANEG_STATE_COMPLETE_ACK 10 +#define ANEG_STATE_IDLE_DETECT_INIT 11 +#define ANEG_STATE_IDLE_DETECT 12 +#define ANEG_STATE_LINK_OK 13 +#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14 +#define ANEG_STATE_NEXT_PAGE_WAIT 15 + + u32 flags; +#define MR_AN_ENABLE 0x00000001 +#define MR_RESTART_AN 0x00000002 +#define MR_AN_COMPLETE 0x00000004 +#define MR_PAGE_RX 0x00000008 +#define MR_NP_LOADED 0x00000010 +#define MR_TOGGLE_TX 0x00000020 +#define MR_LP_ADV_FULL_DUPLEX 0x00000040 +#define MR_LP_ADV_HALF_DUPLEX 0x00000080 +#define MR_LP_ADV_SYM_PAUSE 0x00000100 +#define MR_LP_ADV_ASYM_PAUSE 0x00000200 +#define MR_LP_ADV_REMOTE_FAULT1 0x00000400 +#define MR_LP_ADV_REMOTE_FAULT2 0x00000800 +#define MR_LP_ADV_NEXT_PAGE 0x00001000 +#define MR_TOGGLE_RX 0x00002000 +#define MR_NP_RX 0x00004000 + +#define MR_LINK_OK 0x80000000 + + unsigned long link_time, cur_time; + + u32 ability_match_cfg; + int ability_match_count; + + char ability_match, idle_match, ack_match; + + u32 txconfig, rxconfig; +#define ANEG_CFG_NP 0x00000080 +#define ANEG_CFG_ACK 0x00000040 +#define ANEG_CFG_RF2 0x00000020 +#define ANEG_CFG_RF1 0x00000010 +#define ANEG_CFG_PS2 0x00000001 +#define ANEG_CFG_PS1 0x00008000 +#define ANEG_CFG_HD 0x00004000 +#define ANEG_CFG_FD 0x00002000 +#define ANEG_CFG_INVAL 0x00001f06 + +}; +#define ANEG_OK 0 +#define ANEG_DONE 1 +#define ANEG_TIMER_ENAB 2 +#define ANEG_FAILED -1 + + +static int tg3_fiber_aneg_smachine(struct tg3 *tp, + struct tg3_fiber_aneginfo *ap) +{ + unsigned long delta; + u32 rx_cfg_reg; + int ret; + + if (ap->state == ANEG_STATE_UNKNOWN) { + ap->rxconfig = 0; + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + } + ap->cur_time++; + + if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) { + rx_cfg_reg = tr32(MAC_RX_AUTO_NEG); + + if (rx_cfg_reg != ap->ability_match_cfg) { + ap->ability_match_cfg = rx_cfg_reg; + ap->ability_match = 0; + ap->ability_match_count = 0; + } else { + if (++ap->ability_match_count > 1) + ap->ability_match = 1; + } + if (rx_cfg_reg & ANEG_CFG_ACK) + ap->ack_match = 1; + else + ap->ack_match = 0; + + ap->idle_match = 0; + } else { + ap->idle_match = 1; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->ack_match = 0; + + rx_cfg_reg = 0; + } + + ap->rxconfig = rx_cfg_reg; + ret = ANEG_OK; + + switch(ap->state) { + case ANEG_STATE_UNKNOWN: + if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN)) + ap->state = ANEG_STATE_AN_ENABLE; + + /* fallthru */ + case ANEG_STATE_AN_ENABLE: + ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX); + if (ap->flags & MR_AN_ENABLE) { + ap->link_time = 0; + ap->cur_time = 0; + ap->ability_match_cfg = 0; + ap->ability_match_count = 0; + ap->ability_match = 0; + ap->idle_match = 0; + ap->ack_match = 0; + + ap->state = ANEG_STATE_RESTART_INIT; + } else { + ap->state = ANEG_STATE_DISABLE_LINK_OK; + } + break; + + case ANEG_STATE_RESTART_INIT: + ap->link_time = ap->cur_time; + ap->flags &= ~(MR_NP_LOADED); + ap->txconfig = 0; + tw32(MAC_TX_AUTO_NEG, 0); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ret = ANEG_TIMER_ENAB; + ap->state = ANEG_STATE_RESTART; + + /* fallthru */ + case ANEG_STATE_RESTART: + delta = ap->cur_time - ap->link_time; + if (delta > 100000) + ap->state = ANEG_STATE_ABILITY_DETECT_INIT; + else + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_DISABLE_LINK_OK: + ret = ANEG_DONE; + break; + + case ANEG_STATE_ABILITY_DETECT_INIT: + ap->flags &= ~(MR_TOGGLE_TX); + ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1); + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_ABILITY_DETECT; + break; + + case ANEG_STATE_ABILITY_DETECT: + if (ap->ability_match != 0 && ap->rxconfig != 0) + ap->state = ANEG_STATE_ACK_DETECT_INIT; + break; + + case ANEG_STATE_ACK_DETECT_INIT: + ap->txconfig |= ANEG_CFG_ACK; + tw32(MAC_TX_AUTO_NEG, ap->txconfig); + tp->mac_mode |= MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_ACK_DETECT; + + /* fallthru */ + case ANEG_STATE_ACK_DETECT: + if (ap->ack_match != 0) { + if ((ap->rxconfig & ~ANEG_CFG_ACK) == + (ap->ability_match_cfg & ~ANEG_CFG_ACK)) + ap->state = ANEG_STATE_COMPLETE_ACK_INIT; + else + ap->state = ANEG_STATE_AN_ENABLE; + } else if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + } + break; + + case ANEG_STATE_COMPLETE_ACK_INIT: + if (ap->rxconfig & ANEG_CFG_INVAL) { + ret = ANEG_FAILED; + break; + } + ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX | + MR_LP_ADV_HALF_DUPLEX | + MR_LP_ADV_SYM_PAUSE | + MR_LP_ADV_ASYM_PAUSE | + MR_LP_ADV_REMOTE_FAULT1 | + MR_LP_ADV_REMOTE_FAULT2 | + MR_LP_ADV_NEXT_PAGE | + MR_TOGGLE_RX | + MR_NP_RX); + if (ap->rxconfig & ANEG_CFG_FD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_HD) + ap->flags |= MR_LP_ADV_FULL_DUPLEX; + if (ap->rxconfig & ANEG_CFG_PS1) + ap->flags |= MR_LP_ADV_SYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_PS2) + ap->flags |= MR_LP_ADV_ASYM_PAUSE; + if (ap->rxconfig & ANEG_CFG_RF1) + ap->flags |= MR_LP_ADV_REMOTE_FAULT1; + if (ap->rxconfig & ANEG_CFG_RF2) + ap->flags |= MR_LP_ADV_REMOTE_FAULT2; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_LP_ADV_NEXT_PAGE; + + ap->link_time = ap->cur_time; + + ap->flags ^= (MR_TOGGLE_TX); + if (ap->rxconfig & 0x0008) + ap->flags |= MR_TOGGLE_RX; + if (ap->rxconfig & ANEG_CFG_NP) + ap->flags |= MR_NP_RX; + ap->flags |= MR_PAGE_RX; + + ap->state = ANEG_STATE_COMPLETE_ACK; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_COMPLETE_ACK: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > 100000) { + if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) { + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + } else { + if ((ap->txconfig & 0x0080) == 0 && + !(ap->flags & MR_NP_RX)) + ap->state = ANEG_STATE_IDLE_DETECT_INIT; + else + ret = ANEG_FAILED; + } + } + break; + + case ANEG_STATE_IDLE_DETECT_INIT: + ap->link_time = ap->cur_time; + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + ap->state = ANEG_STATE_IDLE_DETECT; + ret = ANEG_TIMER_ENAB; + break; + + case ANEG_STATE_IDLE_DETECT: + if (ap->ability_match != 0 && + ap->rxconfig == 0) { + ap->state = ANEG_STATE_AN_ENABLE; + break; + } + delta = ap->cur_time - ap->link_time; + if (delta > 100000) { + /* XXX another gem from the Broadcom driver :( */ + ap->state = ANEG_STATE_LINK_OK; + } + break; + + case ANEG_STATE_LINK_OK: + ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK); + ret = ANEG_DONE; + break; + + case ANEG_STATE_NEXT_PAGE_WAIT_INIT: + /* ??? unimplemented */ + break; + + case ANEG_STATE_NEXT_PAGE_WAIT: + /* ??? unimplemented */ + break; + + default: + ret = ANEG_FAILED; + break; + }; + + return ret; +} + +static int tg3_setup_fiber_phy(struct tg3 *tp) +{ + int current_link_up; + int i; + + tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); + tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; + tw32(MAC_MODE, tp->mac_mode); + udelay(40); + + /* Reset when initting first time or we have a link. */ + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) { + /* Set PLL lock range. */ + tg3_writephy(tp, 0x16, 0x8007); + + /* SW reset */ + tg3_writephy(tp, 0x00, 0x8000); + + /* Wait for reset to complete. */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 500; i++) + udelay(10); + + /* Config mode; select PMA/Ch 1 regs. */ + tg3_writephy(tp, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + tg3_writephy(tp, 0x11, 0x0a10); + + tg3_writephy(tp, 0x18, 0x00a0); + tg3_writephy(tp, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + tg3_writephy(tp, 0x13, 0x0400); + udelay(40); + tg3_writephy(tp, 0x13, 0x0000); + + tg3_writephy(tp, 0x11, 0x0a50); + udelay(40); + tg3_writephy(tp, 0x11, 0x0a10); + + /* Wait for signal to stabilize */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 15000; i++) + udelay(10); + + /* Deselect the channel register so we can read the PHYID + * later. + */ + tg3_writephy(tp, 0x10, 0x8011); + } + + /* Enable link change interrupt. */ + tw32(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); + + current_link_up = 0; + if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) { + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= (MR_AN_ENABLE); + + for (i = 0; i < 6; i++) { + u32 tmp; + + tw32(MAC_TX_AUTO_NEG, 0); + + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(20); + + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + while (aninfo.cur_time < 95000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || + status == ANEG_FAILED) + break; + + udelay(1); + } + if (status == ANEG_DONE || + status == ANEG_FAILED) + break; + } + + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32(MAC_MODE, tp->mac_mode); + + if (status == ANEG_DONE && + (aninfo.flags & MR_AN_COMPLETE) && + (aninfo.flags & MR_LINK_OK) && + (aninfo.flags & MR_LP_ADV_FULL_DUPLEX)) { + u32 local_adv, remote_adv; + + local_adv = ADVERTISE_PAUSE_CAP; + remote_adv = 0; + if (aninfo.flags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_PAUSE_CAP; + if (aninfo.flags & MR_LP_ADV_ASYM_PAUSE) + remote_adv |= LPA_PAUSE_ASYM; + + tg3_setup_flow_control(tp, local_adv, remote_adv); + + current_link_up = 1; + } + for (i = 0; i < 60; i++) { + udelay(20); + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + + udelay(20); + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + } else { + /* Forcing 1000FD link up. */ + current_link_up = 1; + } + } + + tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; + tw32(MAC_MODE, tp->mac_mode); + + tp->hw_status->status = + (SD_STATUS_UPDATED | + (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); + + for (i = 0; i < 100; i++) { + tw32(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(5); + + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) + current_link_up = 0; + + if (current_link_up == 1) { + tp->link_config.active_speed = SPEED_1000; + tp->link_config.active_duplex = DUPLEX_FULL; + } else { + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + } + + if (current_link_up != netif_carrier_ok(tp->dev)) { + if (current_link_up) + netif_carrier_on(tp->dev); + else + netif_carrier_off(tp->dev); + tg3_link_report(tp); + } + + if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) { + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_LINK_POLARITY); + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { + udelay(1); + tw32(MAC_MODE, tp->mac_mode); + } + } + + return 0; +} + +static int tg3_setup_phy(struct tg3 *tp) +{ + int err; + + if (tp->phy_id == PHY_ID_SERDES) { + err = tg3_setup_fiber_phy(tp); + } else { + err = tg3_setup_copper_phy(tp); + } + + if (tp->link_config.active_speed == SPEED_1000 && + tp->link_config.active_duplex == DUPLEX_HALF) + tw32(MAC_TX_LENGTHS, + ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (0xff << TX_LENGTHS_SLOT_TIME_SHIFT))); + else + tw32(MAC_TX_LENGTHS, + ((2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); + + return err; +} + +/* Tigon3 never reports partial packet sends. So we do not + * need special logic to handle SKBs that have not had all + * of their frags sent yet, like SunGEM does. + */ +static void tg3_tx(struct tg3 *tp) +{ + u32 hw_idx = tp->hw_status->idx[0].tx_consumer; + u32 sw_idx = tp->tx_cons; + + while (sw_idx != hw_idx) { + struct ring_info *ri = &tp->tx_buffers[sw_idx]; + struct sk_buff *skb = ri->skb; + int i; + + if (unlikely(skb == NULL)) + BUG(); + + pci_unmap_single(tp->pdev, ri->mapping, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE); + + ri->skb = NULL; + + sw_idx = NEXT_TX(sw_idx); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (unlikely(sw_idx == hw_idx)) + BUG(); + + ri = &tp->tx_buffers[sw_idx]; + if (unlikely(ri->skb != NULL)) + BUG(); + + pci_unmap_page(tp->pdev, + ri->mapping, + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); + + sw_idx = NEXT_TX(sw_idx); + } + + dev_kfree_skb_irq(skb); + } + + tp->tx_cons = sw_idx; + + if (netif_queue_stopped(tp->dev) && + (TX_BUFFS_AVAIL(tp) > TG3_TX_WAKEUP_THRESH)) + netif_wake_queue(tp->dev); +} + +/* Returns size of skb allocated or < 0 on error. + * + * We only need to fill in the address because the other members + * of the RX descriptor are invariant, see tg3_init_rings. + * + * Note the purposeful assymetry of cpu vs. chip accesses. For + * posting buffers we only dirty the first cache line of the RX + * descriptor (containing the address). Whereas for the RX status + * buffers the cpu only reads the last cacheline of the RX descriptor + * (to fetch the error flags, vlan tag, checksum, and opaque cookie). + */ +static int tg3_alloc_rx_skb(struct tg3 *tp, u32 opaque_key, + int src_idx, u32 dest_idx_unmasked) +{ + struct tg3_rx_buffer_desc *desc; + struct ring_info *map, *src_map; + struct sk_buff *skb; + dma_addr_t mapping; + int skb_size, dest_idx; + + src_map = NULL; + switch (opaque_key) { + case RXD_OPAQUE_RING_STD: + dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; + desc = &tp->rx_std[dest_idx]; + map = &tp->rx_std_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_std_buffers[src_idx]; + skb_size = RX_PKT_BUF_SZ; + break; + + case RXD_OPAQUE_RING_JUMBO: + dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; + desc = &tp->rx_jumbo[dest_idx]; + map = &tp->rx_jumbo_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_jumbo_buffers[src_idx]; + skb_size = RX_JUMBO_PKT_BUF_SZ; + break; +#if TG3_MINI_RING_WORKS + case RXD_OPAQUE_RING_MINI: + dest_idx = dest_idx_unmasked % TG3_RX_MINI_RING_SIZE; + desc = &tp->rx_mini[dest_idx]; + map = &tp->rx_mini_buffers[dest_idx]; + if (src_idx >= 0) + src_map = &tp->rx_mini_buffers[src_idx]; + skb_size = RX_MINI_PKT_BUF_SZ; + break; +#endif + default: + return -EINVAL; + }; + + /* Do not overwrite any of the map or rp information + * until we are sure we can commit to a new buffer. + * + * Callers depend upon this behavior and assume that + * we leave everything unchanged if we fail. + */ + skb = dev_alloc_skb(skb_size); + if (skb == NULL) + return -ENOMEM; + + skb->dev = tp->dev; + skb_reserve(skb, tp->rx_offset); + + mapping = pci_map_single(tp->pdev, skb->data, + skb_size - tp->rx_offset, + PCI_DMA_FROMDEVICE); + + map->skb = skb; + map->mapping = mapping; + + if (src_map != NULL) + src_map->skb = NULL; + + desc->addr_hi = ((u64)mapping >> 32); + desc->addr_lo = ((u64)mapping & 0xffffffff); + + return skb_size; +} + +/* We only need to move over in the address because the other + * members of the RX descriptor are invariant. See notes above + * tg3_alloc_rx_skb for full details. + */ +static void tg3_recycle_rx(struct tg3 *tp, u32 opaque_key, + int src_idx, int dest_idx_unmasked) +{ + struct tg3_rx_buffer_desc *src_desc, *dest_desc; + struct ring_info *src_map, *dest_map; + int dest_idx; + + switch (opaque_key) { + case RXD_OPAQUE_RING_STD: + dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE; + dest_desc = &tp->rx_std[dest_idx]; + dest_map = &tp->rx_std_buffers[dest_idx]; + src_desc = &tp->rx_std[src_idx]; + src_map = &tp->rx_std_buffers[src_idx]; + break; + + case RXD_OPAQUE_RING_JUMBO: + dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE; + dest_desc = &tp->rx_jumbo[dest_idx]; + dest_map = &tp->rx_jumbo_buffers[dest_idx]; + src_desc = &tp->rx_jumbo[src_idx]; + src_map = &tp->rx_jumbo_buffers[src_idx]; + break; +#if TG3_MINI_RING_WORKS + case RXD_OPAQUE_RING_MINI: + dest_idx = dest_idx_unmasked % TG3_RX_MINI_RING_SIZE; + dest_desc = &tp->rx_mini[dest_idx]; + dest_map = &tp->rx_mini_buffers[dest_idx]; + src_desc = &tp->rx_mini[src_idx]; + src_map = &tp->rx_mini_buffers[src_idx]; + break; +#endif + default: + return; + }; + + dest_map->skb = src_map->skb; + dest_map->mapping = src_map->mapping; + dest_desc->addr_hi = src_desc->addr_hi; + dest_desc->addr_lo = src_desc->addr_lo; + + src_map->skb = NULL; +} + +#if TG3_VLAN_TAG_USED +static int tg3_vlan_rx(struct tg3 *tp, struct sk_buff *skb, u16 vlan_tag) +{ + return vlan_hwaccel_rx(skb, tp->vlgrp, vlan_tag); +} +#endif + +/* The RX ring scheme is composed of multiple rings which post fresh + * buffers to the chip, and one special ring the chip uses to report + * status back to the host. + * + * The special ring reports the status of received packets to the + * host. The chip does not write into the original descriptor the + * RX buffer was obtained from. The chip simply takes the original + * descriptor as provided by the host, updates the status and length + * field, then writes this into the next status ring entry. + * + * Each ring the host uses to post buffers to the chip is described + * by a TG3_BDINFO entry in the chips SRAM area. When a packet arrives, + * it is first placed into the on-chip ram. When the packet's length + * is known, it walks down the TG3_BDINFO entries to select the ring. + * Each TG3_BDINFO specifies a MAXLEN field and the first TG3_BDINFO + * which is within the range of the new packet's length is chosen. + * + * The "seperate ring for rx status" scheme may sound queer, but it makes + * sense from a cache coherency perspective. If only the host writes + * to the buffer post rings, and only the chip writes to the rx status + * rings, then cache lines never move beyond shared-modified state. + * If both the host and chip were to write into the same ring, cache line + * eviction could occur since both entities want it in an exclusive state. + */ +static void tg3_rx(struct tg3 *tp) +{ + u32 work_mask; + u32 rx_rcb_ptr = tp->rx_rcb_ptr; + u16 hw_idx, sw_idx; + + hw_idx = tp->hw_status->idx[0].rx_producer; + sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE; + work_mask = 0; + while (sw_idx != hw_idx) { + struct tg3_rx_buffer_desc *desc = &tp->rx_rcb[sw_idx]; + unsigned int len; + struct sk_buff *skb; + dma_addr_t dma_addr; + u32 opaque_key, desc_idx, *post_ptr; + + desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK; + opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK; + if (opaque_key == RXD_OPAQUE_RING_STD) { + dma_addr = tp->rx_std_buffers[desc_idx].mapping; + skb = tp->rx_std_buffers[desc_idx].skb; + post_ptr = &tp->rx_std_ptr; + } else if (opaque_key == RXD_OPAQUE_RING_JUMBO) { + dma_addr = tp->rx_jumbo_buffers[desc_idx].mapping; + skb = tp->rx_jumbo_buffers[desc_idx].skb; + post_ptr = &tp->rx_jumbo_ptr; + } +#if TG3_MINI_RING_WORKS + else if (opaque_key == RXD_OPAQUE_RING_MINI) { + dma_addr = tp->rx_mini_buffers[desc_idx].mapping; + skb = tp->rx_mini_buffers[desc_idx].skb; + post_ptr = &tp->rx_mini_ptr; + } +#endif + else { + goto next_pkt_nopost; + } + + work_mask |= opaque_key; + + if ((desc->err_vlan & RXD_ERR_MASK) != 0 && + (desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) { + drop_it: + tg3_recycle_rx(tp, opaque_key, + desc_idx, *post_ptr); + drop_it_no_recycle: + /* Other statistics kept track of by card. */ + tp->net_stats.rx_dropped++; + goto next_pkt; + } + + len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */ + + /* Kill the copy case if we ever get the mini ring working. */ + if (len > RX_COPY_THRESHOLD) { + int skb_size; + + skb_size = tg3_alloc_rx_skb(tp, opaque_key, + desc_idx, *post_ptr); + if (skb_size < 0) + goto drop_it; + + pci_unmap_single(tp->pdev, dma_addr, + skb_size - tp->rx_offset, + PCI_DMA_FROMDEVICE); + + skb_put(skb, len); + } else { + struct sk_buff *copy_skb; + + tg3_recycle_rx(tp, opaque_key, + desc_idx, *post_ptr); + + copy_skb = dev_alloc_skb(len + 2); + if (copy_skb == NULL) + goto drop_it_no_recycle; + + copy_skb->dev = tp->dev; + skb_reserve(copy_skb, 2); + skb_put(copy_skb, len); + pci_dma_sync_single(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + memcpy(copy_skb->data, skb->data, len); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + if (!(tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) && + (desc->type_flags & RXD_FLAG_TCPUDP_CSUM)) { + skb->csum = htons((desc->ip_tcp_csum & RXD_TCPCSUM_MASK) + >> RXD_TCPCSUM_SHIFT); + skb->ip_summed = CHECKSUM_HW; + } else { + skb->ip_summed = CHECKSUM_NONE; + } + + skb->protocol = eth_type_trans(skb, tp->dev); +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && + desc->type_flags & RXD_FLAG_VLAN) { + tg3_vlan_rx(tp, skb, + desc->err_vlan & RXD_VLAN_MASK); + } else +#endif + netif_rx(skb); + + tp->dev->last_rx = jiffies; + +next_pkt: + (*post_ptr)++; +next_pkt_nopost: + rx_rcb_ptr++; + sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE; + } + + /* ACK the status ring. */ + tp->rx_rcb_ptr = rx_rcb_ptr; + tw32_mailbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, + (rx_rcb_ptr % TG3_RX_RCB_RING_SIZE)); + + /* Refill RX ring(s). */ + if (work_mask & RXD_OPAQUE_RING_STD) { + sw_idx = tp->rx_std_ptr % TG3_RX_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } + if (work_mask & RXD_OPAQUE_RING_JUMBO) { + sw_idx = tp->rx_jumbo_ptr % TG3_RX_JUMBO_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } +#if TG3_MINI_RING_WORKS + if (work_mask & RXD_OPAQUE_RING_MINI) { + sw_idx = tp->rx_mini_ptr % TG3_RX_MINI_RING_SIZE; + tw32_mailbox(MAILBOX_RCV_MINI_PROD_IDX + TG3_64BIT_REG_LOW, + sw_idx); + } +#endif +} + +#define RATE_SAMPLE_INTERVAL (1 * HZ) + +#define PKT_RATE_LOW 22000 +#define PKT_RATE_HIGH 61000 + +static void tg3_rate_sample(struct tg3 *tp, unsigned long ticks) +{ + u32 delta, rx_now, tx_now; + int new_vals; + + rx_now = tp->hw_stats->rx_ucast_packets.low; + tx_now = tp->hw_stats->COS_out_packets[0].low; + + delta = (rx_now - tp->last_rx_count); + delta += (tx_now - tp->last_tx_count); + delta /= (ticks / RATE_SAMPLE_INTERVAL); + + tp->last_rx_count = rx_now; + tp->last_tx_count = tx_now; + + new_vals = 0; + if (delta < PKT_RATE_LOW) { + if (tp->coalesce_config.rx_max_coalesced_frames != + LOW_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + LOW_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + LOW_RXCOL_TICKS; + new_vals = 1; + } + } else if (delta < PKT_RATE_HIGH) { + if (tp->coalesce_config.rx_max_coalesced_frames != + DEFAULT_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + DEFAULT_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + DEFAULT_RXCOL_TICKS; + new_vals = 1; + } + } else { + if (tp->coalesce_config.rx_max_coalesced_frames != + HIGH_RXMAX_FRAMES) { + tp->coalesce_config.rx_max_coalesced_frames = + HIGH_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks = + HIGH_RXCOL_TICKS; + new_vals = 1; + } + } + + if (new_vals) { + tw32(HOSTCC_RXCOL_TICKS, + tp->coalesce_config.rx_coalesce_ticks); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + } + + tp->last_rate_sample = jiffies; +} + +static void tg3_interrupt_main_work(struct tg3 *tp) +{ + struct tg3_hw_status *sblk = tp->hw_status; + int did_pkts; + + if (!(tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG)) { + if (sblk->status & SD_STATUS_LINK_CHG) { + sblk->status = SD_STATUS_UPDATED | + (sblk->status & ~SD_STATUS_LINK_CHG); + tg3_setup_phy(tp); + } + } + + did_pkts = 0; + if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) { + tg3_rx(tp); + did_pkts = 1; + } + + if (sblk->idx[0].tx_consumer != tp->tx_cons) { + tg3_tx(tp); + did_pkts = 1; + } + + if (did_pkts) { + unsigned long ticks = jiffies - tp->last_rate_sample; + + if (ticks >= RATE_SAMPLE_INTERVAL) + tg3_rate_sample(tp, ticks); + } +} + +static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct tg3 *tp = dev->priv; + struct tg3_hw_status *sblk = tp->hw_status; + + spin_lock(&tp->lock); + + while (sblk->status & SD_STATUS_UPDATED) { + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000001); + sblk->status &= ~SD_STATUS_UPDATED; + + tg3_interrupt_main_work(tp); + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000000); + tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); + } + + spin_unlock(&tp->lock); +} + +static void tg3_interrupt_tagged(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct tg3 *tp = dev->priv; + struct tg3_hw_status *sblk = tp->hw_status; + + spin_lock(&tp->lock); + + if (sblk->status & SD_STATUS_UPDATED) { + u32 oldtag; + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000001); + oldtag = sblk->status_tag; + + while (1) { + u32 newtag; + + sblk->status &= ~SD_STATUS_UPDATED; + barrier(); + + tg3_interrupt_main_work(tp); + + newtag = sblk->status_tag; + if (newtag == oldtag) { + tw32_mailbox(MAILBOX_INTERRUPT_0 + + TG3_64BIT_REG_LOW, + newtag << 24); + break; + } + oldtag = newtag; + } + } + + spin_unlock(&tp->lock); +} + +static void tg3_init_rings(struct tg3 *); +static int tg3_init_hw(struct tg3 *); + +static void tg3_tx_timeout(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + printk(KERN_ERR "%s: transmit timed out, resetting\n", + dev->name); + + spin_lock_irq(&tp->lock); + + tg3_halt(tp); + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + netif_wake_queue(dev); +} + +#if !PCI_DMA_BUS_IS_PHYS +static void tg3_set_txd_addr(struct tg3 *tp, int entry, dma_addr_t mapping) +{ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry]; + + txd->addr_hi = ((u64) mapping >> 32); + txd->addr_lo = ((u64) mapping & 0xffffffff); + } else { + unsigned long txd; + + txd = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + txd += (entry * TXD_SIZE); + + writel(((u64) mapping >> 32), + txd + TXD_ADDR + TG3_64BIT_REG_HIGH); + + writel(((u64) mapping & 0xffffffff), + txd + TXD_ADDR + TG3_64BIT_REG_LOW); + } +} +#endif + +static void tg3_set_txd(struct tg3 *, int, dma_addr_t, int, u32, int); + +static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, + u32 guilty_entry, int guilty_len, + u32 last_plus_one, u32 *start) +{ + dma_addr_t new_addr; + u32 entry = *start; + int i; + +#if !PCI_DMA_BUS_IS_PHYS + /* IOMMU, just map the guilty area again which is guarenteed to + * use different addresses. + */ + + i = 0; + while (entry != guilty_entry) { + entry = NEXT_TX(entry); + i++; + } + if (i == 0) { + new_addr = pci_map_single(tp->pdev, skb->data, guilty_len, + PCI_DMA_TODEVICE); + } else { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; + + new_addr = pci_map_page(tp->pdev, + frag->page, frag->page_offset, + guilty_len, PCI_DMA_TODEVICE); + } + pci_unmap_single(tp->pdev, tp->tx_buffers[guilty_entry].mapping, + guilty_len, PCI_DMA_TODEVICE); + tg3_set_txd_addr(tp, guilty_entry, new_addr); + tp->tx_buffers[guilty_entry].mapping = new_addr; + *start = last_plus_one; +#else + /* Oh well, no IOMMU, have to allocate a whole new SKB. */ + struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC); + + if (!new_skb) { + dev_kfree_skb(skb); + return -1; + } + + /* NOTE: Broadcom's driver botches this case up really bad. + * This is especially true if any of the frag pages + * are in highmem. It will instantly oops in that case. + */ + + /* New SKB is guarenteed to be linear. */ + entry = *start; + new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len, + PCI_DMA_TODEVICE); + tg3_set_txd(tp, entry, new_addr, new_skb->len, + (skb->ip_summed == CHECKSUM_HW) ? + TXD_FLAG_TCPUDP_CSUM : 0, 1); + *start = NEXT_TX(entry); + + /* Now clean up the sw ring entries. */ + i = 0; + while (entry != last_plus_one) { + int len; + + if (i == 0) + len = skb->len - skb->data_len; + else + len = skb_shinfo(skb)->frags[i-1].size; + pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping, + len, PCI_DMA_TODEVICE); + if (i == 0) { + tp->tx_buffers[entry].skb = new_skb; + tp->tx_buffers[entry].mapping = new_addr; + } else { + tp->tx_buffers[entry].skb = NULL; + } + entry = NEXT_TX(entry); + } + + dev_kfree_skb(skb); +#endif + + return 0; +} + +static void tg3_set_txd(struct tg3 *tp, int entry, + dma_addr_t mapping, int len, u32 flags, + int is_end) +{ +#if TG3_VLAN_TAG_USED + u16 vlan_tag = 0; +#endif + + if (is_end) + flags |= TXD_FLAG_END; +#if TG3_VLAN_TAG_USED + if (flags & TXD_FLAG_VLAN) { + vlan_tag = flags >> 16; + flags &= 0xffff; + } +#endif + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry]; + + txd->addr_hi = ((u64) mapping >> 32); + txd->addr_lo = ((u64) mapping & 0xffffffff); + txd->len_flags = (len << TXD_LEN_SHIFT) | flags; +#if TG3_VLAN_TAG_USED + txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT; +#endif + } else { + unsigned long txd; + + txd = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + txd += (entry * TXD_SIZE); + + /* Save some PIOs */ + if (((u64) tp->tx_buffers[entry].mapping >> 32) != + ((u64) mapping >> 32)) + writel(((u64) mapping >> 32), + txd + TXD_ADDR + TG3_64BIT_REG_HIGH); + + writel(((u64) mapping & 0xffffffff), + txd + TXD_ADDR + TG3_64BIT_REG_LOW); + writel(len << TXD_LEN_SHIFT | flags, txd + TXD_LEN_FLAGS); +#if TG3_VLAN_TAG_USED + writel(vlan_tag << TXD_VLAN_TAG_SHIFT, txd + TXD_VLAN_TAG); +#endif + } +} + +static inline int tg3_4g_overflow_test(dma_addr_t mapping, int len) +{ + u32 base = (u32) mapping & 0xffffffff; + + return ((base > 0xffffdcc0) && + ((u64) mapping >> 32) == 0 && + (base + len + 8 < base)); +} + +static int tg3_start_xmit_4gbug(struct sk_buff *skb, struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + dma_addr_t mapping; + unsigned int i; + u32 len, entry, base_flags; + int would_hit_hwbug; + + len = (skb->len - skb->data_len); + + spin_lock_irq(&tp->lock); + + /* This is a hard error, log it. */ + if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) { + netif_stop_queue(dev); + spin_unlock_irq(&tp->lock); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + entry = tp->tx_prod; + base_flags = 0; + if (skb->ip_summed == CHECKSUM_HW) + base_flags |= TXD_FLAG_TCPUDP_CSUM; +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) + base_flags |= (TXD_FLAG_VLAN | + (vlan_tx_tag_get(skb) << 16)); +#endif + + /* Queue skb data, a.k.a. the main skb fragment. */ + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = skb; + tp->tx_buffers[entry].mapping = mapping; + + would_hit_hwbug = tg3_4g_overflow_test(mapping, len); + + tg3_set_txd(tp, entry, mapping, len, base_flags, + (skb_shinfo(skb)->nr_frags == 0)); + + entry = NEXT_TX(entry); + + /* Now loop through additional data fragments, and queue them. */ + if (skb_shinfo(skb)->nr_frags > 0) { + unsigned int i, last; + + last = skb_shinfo(skb)->nr_frags - 1; + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + len = frag->size; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = mapping; + + would_hit_hwbug |= + tg3_4g_overflow_test(mapping, len); + + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)); + + entry = NEXT_TX(entry); + } + } + + if (would_hit_hwbug) { + u32 last_plus_one = entry; + u32 start; + unsigned int len = 0; + + entry = entry - 1 - skb_shinfo(skb)->nr_frags; + entry &= (TG3_TX_RING_SIZE - 1); + start = entry; + i = 0; + while (entry != last_plus_one) { + dma_addr_t mapping = tp->tx_buffers[entry].mapping; + + if (i == 0) + len = skb->len - skb->data_len; + else + len = skb_shinfo(skb)->frags[i-1].size; + + if (tg3_4g_overflow_test(mapping, len)) + break; + + i++; + entry = NEXT_TX(entry); + + } + + /* If the workaround fails due to memory/mapping + * failure, silently drop this packet. + */ + if (tigon3_4gb_hwbug_workaround(tp, skb, + entry, len, + last_plus_one, + &start)) + goto out_unlock; + + entry = start; + } + + /* Packets are ready, update Tx producer idx local and on card. */ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } else { + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } + + tp->tx_prod = entry; + if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + +out_unlock: + spin_unlock_irq(&tp->lock); + + dev->trans_start = jiffies; + + return 0; +} + +static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + dma_addr_t mapping; + u32 len, entry, base_flags; + + len = (skb->len - skb->data_len); + + spin_lock_irq(&tp->lock); + + /* This is a hard error, log it. */ + if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) { + netif_stop_queue(dev); + spin_unlock_irq(&tp->lock); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + entry = tp->tx_prod; + base_flags = 0; + if (skb->ip_summed == CHECKSUM_HW) + base_flags |= TXD_FLAG_TCPUDP_CSUM; +#if TG3_VLAN_TAG_USED + if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) + base_flags |= (TXD_FLAG_VLAN | + (vlan_tx_tag_get(skb) << 16)); +#endif + + /* Queue skb data, a.k.a. the main skb fragment. */ + mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = skb; + tp->tx_buffers[entry].mapping = mapping; + + tg3_set_txd(tp, entry, mapping, len, base_flags, + (skb_shinfo(skb)->nr_frags == 0)); + + entry = NEXT_TX(entry); + + /* Now loop through additional data fragments, and queue them. */ + if (skb_shinfo(skb)->nr_frags > 0) { + unsigned int i, last; + + last = skb_shinfo(skb)->nr_frags - 1; + for (i = 0; i <= last; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + + len = frag->size; + mapping = pci_map_page(tp->pdev, + frag->page, + frag->page_offset, + len, PCI_DMA_TODEVICE); + + tp->tx_buffers[entry].skb = NULL; + tp->tx_buffers[entry].mapping = mapping; + + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)); + + entry = NEXT_TX(entry); + } + } + + /* Packets are ready, update Tx producer idx local and on card. + * We know this is not a 5700 (by virtue of not being a chip + * requiring the 4GB overflow workaround) so we can safely omit + * the double-write bug tests. + */ + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } else { + tw32_mailbox((MAILBOX_SNDNIC_PROD_IDX_0 + + TG3_64BIT_REG_LOW), entry); + } + + tp->tx_prod = entry; + if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + + spin_unlock_irq(&tp->lock); + + dev->trans_start = jiffies; + + return 0; +} + +static int tg3_change_mtu(struct net_device *dev, int new_mtu) +{ + struct tg3 *tp = dev->priv; + + if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU) + return -EINVAL; + + if (!netif_running(dev)) { + /* We'll just catch it later when the + * device is up'd. + */ + dev->mtu = new_mtu; + return 0; + } + + spin_lock_irq(&tp->lock); + + tg3_halt(tp); + + dev->mtu = new_mtu; + + if (new_mtu > ETH_DATA_LEN) + tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; + else + tp->tg3_flags &= ~TG3_FLAG_JUMBO_ENABLE; + + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +/* Free up pending packets in all rx/tx rings. + * + * The chip has been shut down and the driver detached from + * the networking, so no interrupts or new tx packets will + * end up in the driver. tp->lock is not held and we are not + * in an interrupt context and thus may sleep. + */ +static void tg3_free_rings(struct tg3 *tp) +{ + struct ring_info *rxp; + int i; + + for (i = 0; i < TG3_RX_RING_SIZE; i++) { + rxp = &tp->rx_std_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_SIZE; i++) { + rxp = &tp->rx_mini_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_MINI_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } +#endif + for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { + rxp = &tp->rx_jumbo_buffers[i]; + + if (rxp->skb == NULL) + continue; + pci_unmap_single(tp->pdev, rxp->mapping, + RX_JUMBO_PKT_BUF_SZ - tp->rx_offset, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxp->skb); + rxp->skb = NULL; + } + + for (i = 0; i < TG3_TX_RING_SIZE; ) { + struct ring_info *txp; + struct sk_buff *skb; + int j; + + txp = &tp->tx_buffers[i]; + skb = txp->skb; + + if (skb == NULL) { + i++; + continue; + } + + pci_unmap_single(tp->pdev, txp->mapping, + (skb->len - skb->data_len), + PCI_DMA_TODEVICE); + txp->skb = NULL; + + i++; + + for (j = 0; j < skb_shinfo(skb)->nr_frags; j++) { + txp = &tp->tx_buffers[i & (TG3_TX_RING_SIZE - 1)]; + pci_unmap_page(tp->pdev, txp->mapping, + skb_shinfo(skb)->frags[j].size, + PCI_DMA_TODEVICE); + i++; + } + + dev_kfree_skb_any(skb); + } +} + +/* Initialize tx/rx rings for packet processing. + * + * The chip has been shut down and the driver detached from + * the networking, so no interrupts or new tx packets will + * end up in the driver. tp->lock is not held and we are not + * in an interrupt context and thus may sleep. + */ +static void tg3_init_rings(struct tg3 *tp) +{ + unsigned long start, end; + u32 i; + + /* Free up all the SKBs. */ + tg3_free_rings(tp); + + /* Zero out all descriptors. */ + memset(tp->rx_std, 0, TG3_RX_RING_BYTES); +#if TG3_MINI_RING_WORKS + memset(tp->rx_mini, 0, TG3_RX_MINI_RING_BYTES); +#endif + memset(tp->rx_jumbo, 0, TG3_RX_JUMBO_RING_BYTES); + memset(tp->rx_rcb, 0, TG3_RX_RCB_RING_BYTES); + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + memset(tp->tx_ring, 0, TG3_TX_RING_BYTES); + } else { + start = (tp->regs + + NIC_SRAM_WIN_BASE + + NIC_SRAM_TX_BUFFER_DESC); + end = start + TG3_TX_RING_BYTES; + while (start < end) { + writel(0, start); + start += 4; + } + } + + /* Initialize invariants of the rings, we only set this + * stuff once. This works because the card does not + * write into the rx buffer posting rings. + */ + for (i = 0; i < TG3_RX_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_std[i]; + rxd->idx_len = (RX_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT); + rxd->opaque = (RXD_OPAQUE_RING_STD | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_mini[i]; + rxd->idx_len = (RX_MINI_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT) | + RXD_FLAG_MINI; + rxd->opaque = (RXD_OPAQUE_RING_MINI | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } +#endif + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) { + struct tg3_rx_buffer_desc *rxd; + + rxd = &tp->rx_jumbo[i]; + rxd->idx_len = (RX_JUMBO_PKT_BUF_SZ - tp->rx_offset - 64) + << RXD_LEN_SHIFT; + rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT) | + RXD_FLAG_JUMBO; + rxd->opaque = (RXD_OPAQUE_RING_JUMBO | + (i << RXD_OPAQUE_INDEX_SHIFT)); + } + } + + /* Now allocate fresh SKBs for each rx ring. */ + for (i = 0; i < TG3_RX_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_STD, + -1, i) < 0) + break; + } + +#if TG3_MINI_RING_WORKS + for (i = 0; i < TG3_RX_MINI_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_MINI, + -1, i) < 0) + break; + } +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + for (i = 0; i < TG3_RX_JUMBO_RING_PENDING; i++) { + if (tg3_alloc_rx_skb(tp, RXD_OPAQUE_RING_JUMBO, + -1, i) < 0) + break; + } + } +} + +/* + * Must not be invoked with interrupt sources disabled and + * the hardware shutdown down. + */ +static void tg3_free_consistent(struct tg3 *tp) +{ + if (tp->rx_std_buffers) { + kfree(tp->rx_std_buffers); + tp->rx_std_buffers = NULL; + } + if (tp->rx_std) { + pci_free_consistent(tp->pdev, TG3_RX_RING_BYTES, + tp->rx_std, tp->rx_std_mapping); + tp->rx_std = NULL; + } +#if TG3_MINI_RING_WORKS + if (tp->rx_mini) { + pci_free_consistent(tp->pdev, TG3_RX_MINI_RING_BYTES, + tp->rx_mini, tp->rx_mini_mapping); + tp->rx_mini = NULL; + } +#endif + if (tp->rx_jumbo) { + pci_free_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, + tp->rx_jumbo, tp->rx_jumbo_mapping); + tp->rx_jumbo = NULL; + } + if (tp->rx_rcb) { + pci_free_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES, + tp->rx_rcb, tp->rx_rcb_mapping); + tp->rx_rcb = NULL; + } + if (tp->tx_ring) { + pci_free_consistent(tp->pdev, TG3_TX_RING_BYTES, + tp->tx_ring, tp->tx_desc_mapping); + tp->tx_ring = NULL; + } + if (tp->hw_status) { + pci_free_consistent(tp->pdev, TG3_HW_STATUS_SIZE, + tp->hw_status, tp->status_mapping); + tp->hw_status = NULL; + } + if (tp->hw_stats) { + pci_free_consistent(tp->pdev, sizeof(struct tg3_hw_stats), + tp->hw_stats, tp->stats_mapping); + tp->hw_stats = NULL; + } +} + +/* + * Must not be invoked with interrupt sources disabled and + * the hardware shutdown down. Can sleep. + */ +static int tg3_alloc_consistent(struct tg3 *tp) +{ + tp->rx_std_buffers = kmalloc(sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + +#if TG3_MINI_RING_WORKS + TG3_RX_MINI_RING_SIZE + +#endif + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE), + GFP_KERNEL); + if (!tp->rx_std_buffers) + return -ENOMEM; + +#if TG3_MINI_RING_WORKS + memset(tp->rx_std_buffers, 0, + (sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + + TG3_RX_MINI_RING_SIZE + + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE))); +#else + memset(tp->rx_std_buffers, 0, + (sizeof(struct ring_info) * + (TG3_RX_RING_SIZE + + TG3_RX_JUMBO_RING_SIZE + + TG3_TX_RING_SIZE))); +#endif + +#if TG3_MINI_RING_WORKS + tp->rx_mini_buffers = &tp->rx_std_buffers[TG3_RX_RING_SIZE]; + tp->rx_jumbo_buffers = &tp->rx_mini_buffers[TG3_RX_MINI_RING_SIZE]; +#else + tp->rx_jumbo_buffers = &tp->rx_std_buffers[TG3_RX_RING_SIZE]; +#endif + tp->tx_buffers = &tp->rx_jumbo_buffers[TG3_RX_JUMBO_RING_SIZE]; + + tp->rx_std = pci_alloc_consistent(tp->pdev, TG3_RX_RING_BYTES, + &tp->rx_std_mapping); + if (!tp->rx_std) + goto err_out; + +#if TG3_MINI_RING_WORKS + tp->rx_mini = pci_alloc_consistent(tp->pdev, TG3_RX_MINI_RING_BYTES, + &tp->rx_mini_mapping); + + if (!tp->rx_mini) + goto err_out; +#endif + + tp->rx_jumbo = pci_alloc_consistent(tp->pdev, TG3_RX_JUMBO_RING_BYTES, + &tp->rx_jumbo_mapping); + + if (!tp->rx_jumbo) + goto err_out; + + tp->rx_rcb = pci_alloc_consistent(tp->pdev, TG3_RX_RCB_RING_BYTES, + &tp->rx_rcb_mapping); + if (!tp->rx_rcb) + goto err_out; + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tp->tx_ring = pci_alloc_consistent(tp->pdev, TG3_TX_RING_BYTES, + &tp->tx_desc_mapping); + if (!tp->tx_ring) + goto err_out; + } else { + tp->tx_ring = NULL; + tp->tx_desc_mapping = 0; + } + + tp->hw_status = pci_alloc_consistent(tp->pdev, + TG3_HW_STATUS_SIZE, + &tp->status_mapping); + if (!tp->hw_status) + goto err_out; + + tp->hw_stats = pci_alloc_consistent(tp->pdev, + sizeof(struct tg3_hw_stats), + &tp->stats_mapping); + if (!tp->hw_stats) + goto err_out; + + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); + + return 0; + +err_out: + tg3_free_consistent(tp); + return -ENOMEM; +} + +#define MAX_WAIT_CNT 10000 + +/* To stop a block, clear the enable bit and poll till it + * clears. tp->lock is held. + */ +static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, u32 enable_bit) +{ + unsigned int i; + u32 val; + + val = tr32(ofs); + val &= ~enable_bit; + tw32(ofs, val); + + for (i = 0; i < MAX_WAIT_CNT; i++) { + val = tr32(ofs); + + if ((val & enable_bit) == 0) + break; + udelay(100); + } + + if (i == MAX_WAIT_CNT) { + printk(KERN_ERR PFX "tg3_stop_block timed out, " + "ofs=%lx enable_bit=%x\n", + ofs, enable_bit); + return -ENODEV; + } + + return 0; +} + +/* tp->lock is held. */ +static int tg3_abort_hw(struct tg3 *tp) +{ + int i, err; + + tg3_disable_ints(tp); + + tp->rx_mode &= ~RX_MODE_ENABLE; + tw32(MAC_RX_MODE, tp->rx_mode); + + err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE); + + err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); + err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDC_MODE, SNDBDC_MODE_ENABLE); + if (err) + goto out; + + tp->mac_mode &= ~MAC_MODE_TDE_ENABLE; + tw32(MAC_MODE, tp->mac_mode); + + tp->tx_mode &= ~TX_MODE_ENABLE; + tw32(MAC_TX_MODE, tp->tx_mode); + for (i = 0; i < MAX_WAIT_CNT; i++) { + udelay(100); + if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE)) + break; + } + if (i >= MAX_WAIT_CNT) { + printk(KERN_ERR PFX "tg3_abort_hw timed out for %s, " + "TX_MODE_ENABLE will not clear MAC_TX_MODE=%08x\n", + tp->dev->name, tr32(MAC_TX_MODE)); + return -ENODEV; + } + + err = tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE); + err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE); + + tw32(FTQ_RESET, 0xffffffff); + tw32(FTQ_RESET, 0x00000000); + + err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE); + err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE); + if (err) + goto out; + + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + +out: + return err; +} + +/* tp->lock is held. */ +static void tg3_chip_reset(struct tg3 *tp) +{ + u32 val; + + /* Force NVRAM to settle. + * This deals with a chip bug which can result in EEPROM + * corruption. + */ + if (tp->tg3_flags & TG3_FLAG_NVRAM) { + int i; + + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 100000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(10); + } + } + + tw32(GRC_MISC_CFG, GRC_MISC_CFG_CORECLK_RESET); + udelay(40); + udelay(40); + udelay(40); + + /* Re-enable indirect register accesses. */ + pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + /* Set MAX PCI retry to zero. */ + pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, + (PCISTATE_ROM_ENABLE | + PCISTATE_ROM_RETRY_ENABLE)); + + pci_restore_state(tp->pdev, tp->pci_cfg_state); + + /* Make sure PCI-X relaxed ordering bit is clear. */ + pci_read_config_dword(tp->pdev, TG3PCI_X_CAPS, &val); + val &= ~PCIX_CAPS_RELAXED_ORDERING; + pci_write_config_dword(tp->pdev, TG3PCI_X_CAPS, val); + + tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); + + tw32(TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl); +} + +/* tp->lock is held. */ +static int tg3_halt(struct tg3 *tp) +{ + u32 val; + int i; + + tg3_abort_hw(tp); + tg3_chip_reset(tp); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC1); + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + + if (i >= 100000) { + printk(KERN_ERR PFX "tg3_halt timed out for %s, " + "firmware will not restart magic=%08x\n", + tp->dev->name, val); + return -ENODEV; + } + + return 0; +} + +#define TG3_FW_RELEASE_MAJOR 0x0 +#define TG3_FW_RELASE_MINOR 0x0 +#define TG3_FW_RELEASE_FIX 0x0 +#define TG3_FW_START_ADDR 0x08000000 +#define TG3_FW_TEXT_ADDR 0x08000000 +#define TG3_FW_TEXT_LEN 0x9c0 +#define TG3_FW_RODATA_ADDR 0x080009c0 +#define TG3_FW_RODATA_LEN 0x60 +#define TG3_FW_DATA_ADDR 0x08000a40 +#define TG3_FW_DATA_LEN 0x20 +#define TG3_FW_SBSS_ADDR 0x08000a60 +#define TG3_FW_SBSS_LEN 0xc +#define TG3_FW_BSS_ADDR 0x08000a70 +#define TG3_FW_BSS_LEN 0x10 + +static u32 t3FwText[(TG3_FW_TEXT_LEN / sizeof(u32)) + 1] = { + 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0800, + 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100000, 0x0e000018, 0x00000000, + 0x0000000d, 0x3c1d0800, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100034, + 0x0e00021c, 0x00000000, 0x0000000d, 0x00000000, 0x00000000, 0x00000000, + 0x27bdffe0, 0x3c1cc000, 0xafbf0018, 0xaf80680c, 0x0e00004c, 0x241b2105, + 0x97850000, 0x97870002, 0x9782002c, 0x9783002e, 0x3c040800, 0x248409c0, + 0xafa00014, 0x00021400, 0x00621825, 0x00052c00, 0xafa30010, 0x8f860010, + 0x00e52825, 0x0e000060, 0x24070102, 0x3c02ac00, 0x34420100, 0x3c03ac01, + 0x34630100, 0xaf820490, 0x3c02ffff, 0xaf820494, 0xaf830498, 0xaf82049c, + 0x24020001, 0xaf825ce0, 0x0e00003f, 0xaf825d00, 0x0e000140, 0x00000000, + 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x2402ffff, 0xaf825404, 0x8f835400, + 0x34630400, 0xaf835400, 0xaf825404, 0x3c020800, 0x24420034, 0xaf82541c, + 0x03e00008, 0xaf805400, 0x00000000, 0x00000000, 0x3c020800, 0x34423000, + 0x3c030800, 0x34633000, 0x3c040800, 0x348437ff, 0x3c010800, 0xac220a64, + 0x24020040, 0x3c010800, 0xac220a68, 0x3c010800, 0xac200a60, 0xac600000, + 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, + 0x00804821, 0x8faa0010, 0x3c020800, 0x8c420a60, 0x3c040800, 0x8c840a68, + 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010800, 0xac230a60, 0x14400003, + 0x00004021, 0x3c010800, 0xac200a60, 0x3c020800, 0x8c420a60, 0x3c030800, + 0x8c630a64, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, + 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, 0x8c420a60, + 0x3c030800, 0x8c630a64, 0x8f84680c, 0x00021140, 0x00431021, 0xac440008, + 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0x02000008, 0x00000000, 0x0a0001e3, 0x3c0a0001, 0x0a0001e3, 0x3c0a0002, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x3c0a0007, 0x0a0001e3, 0x3c0a0008, 0x0a0001e3, 0x3c0a0009, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000b, + 0x0a0001e3, 0x3c0a000c, 0x0a0001e3, 0x3c0a000d, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000e, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, + 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a0013, 0x0a0001e3, 0x3c0a0014, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x27bdffe0, 0x00001821, 0x00001021, 0xafbf0018, 0xafb10014, 0xafb00010, + 0x3c010800, 0x00220821, 0xac200a70, 0x3c010800, 0x00220821, 0xac200a74, + 0x3c010800, 0x00220821, 0xac200a78, 0x24630001, 0x1860fff5, 0x2442000c, + 0x24110001, 0x8f906810, 0x32020004, 0x14400005, 0x24040001, 0x3c020800, + 0x8c420a78, 0x18400003, 0x00002021, 0x0e000182, 0x00000000, 0x32020001, + 0x10400003, 0x00000000, 0x0e000169, 0x00000000, 0x0a000153, 0xaf915028, + 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c050800, + 0x8ca50a70, 0x3c060800, 0x8cc60a80, 0x3c070800, 0x8ce70a78, 0x27bdffe0, + 0x3c040800, 0x248409d0, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, + 0x0e00017b, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x24020001, + 0x8f836810, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, 0xaf836810, + 0x27bdffd8, 0xafbf0024, 0x1080002e, 0xafb00020, 0x8f825cec, 0xafa20018, + 0x8f825cec, 0x3c100800, 0x26100a78, 0xafa2001c, 0x34028000, 0xaf825cec, + 0x8e020000, 0x18400016, 0x00000000, 0x3c020800, 0x94420a74, 0x8fa3001c, + 0x000221c0, 0xac830004, 0x8fa2001c, 0x3c010800, 0x0e000201, 0xac220a74, + 0x10400005, 0x00000000, 0x8e020000, 0x24420001, 0x0a0001df, 0xae020000, + 0x3c020800, 0x8c420a70, 0x00021c02, 0x000321c0, 0x0a0001c5, 0xafa2001c, + 0x0e000201, 0x00000000, 0x1040001f, 0x00000000, 0x8e020000, 0x8fa3001c, + 0x24420001, 0x3c010800, 0xac230a70, 0x3c010800, 0xac230a74, 0x0a0001df, + 0xae020000, 0x3c100800, 0x26100a78, 0x8e020000, 0x18400028, 0x00000000, + 0x0e000201, 0x00000000, 0x14400024, 0x00000000, 0x8e020000, 0x3c030800, + 0x8c630a70, 0x2442ffff, 0xafa3001c, 0x18400006, 0xae020000, 0x00031402, + 0x000221c0, 0x8c820004, 0x3c010800, 0xac220a70, 0x97a2001e, 0x2442ff00, + 0x2c420300, 0x1440000b, 0x24024000, 0x3c040800, 0x248409dc, 0xafa00010, + 0xafa00014, 0x8fa6001c, 0x24050008, 0x0e000060, 0x00003821, 0x0a0001df, + 0x00000000, 0xaf825cf8, 0x3c020800, 0x8c420a40, 0x8fa3001c, 0x24420001, + 0xaf835cf8, 0x3c010800, 0xac220a40, 0x8fbf0024, 0x8fb00020, 0x03e00008, + 0x27bd0028, 0x27bdffe0, 0x3c040800, 0x248409e8, 0x00002821, 0x00003021, + 0x00003821, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x8fbf0018, + 0x03e00008, 0x27bd0020, 0x8f82680c, 0x8f85680c, 0x00021827, 0x0003182b, + 0x00031823, 0x00431024, 0x00441021, 0x00a2282b, 0x10a00006, 0x00000000, + 0x00401821, 0x8f82680c, 0x0043102b, 0x1440fffd, 0x00000000, 0x03e00008, + 0x00000000, 0x3c040800, 0x8c840000, 0x3c030800, 0x8c630a40, 0x0064102b, + 0x54400002, 0x00831023, 0x00641023, 0x2c420008, 0x03e00008, 0x38420001, + 0x27bdffe0, 0x00802821, 0x3c040800, 0x24840a00, 0x00003021, 0x00003821, + 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x0a000216, 0x00000000, + 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x27bdffe0, 0x3c1cc000, + 0xafbf0018, 0x0e00004c, 0xaf80680c, 0x3c040800, 0x24840a10, 0x03802821, + 0x00003021, 0x00003821, 0xafa00010, 0x0e000060, 0xafa00014, 0x2402ffff, + 0xaf825404, 0x3c0200aa, 0x0e000234, 0xaf825434, 0x8fbf0018, 0x03e00008, + 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe8, 0xafb00010, + 0x24100001, 0xafbf0014, 0x3c01c003, 0xac200000, 0x8f826810, 0x30422000, + 0x10400003, 0x00000000, 0x0e000246, 0x00000000, 0x0a00023a, 0xaf905428, + 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdfff8, 0x8f845d0c, + 0x3c0200ff, 0x3c030800, 0x8c630a50, 0x3442fff8, 0x00821024, 0x1043001e, + 0x3c0500ff, 0x34a5fff8, 0x3c06c003, 0x3c074000, 0x00851824, 0x8c620010, + 0x3c010800, 0xac230a50, 0x30420008, 0x10400005, 0x00871025, 0x8cc20000, + 0x24420001, 0xacc20000, 0x00871025, 0xaf825d0c, 0x8fa20000, 0x24420001, + 0xafa20000, 0x8fa20000, 0x8fa20000, 0x24420001, 0xafa20000, 0x8fa20000, + 0x8f845d0c, 0x3c030800, 0x8c630a50, 0x00851024, 0x1443ffe8, 0x00851824, + 0x27bd0008, 0x03e00008, 0x00000000, 0x00000000, 0x00000000 +}; + +static u32 t3FwRodata[(TG3_FW_RODATA_LEN / sizeof(u32)) + 1] = { + 0x35373031, 0x726c7341, 0x00000000, 0x00000000, 0x53774576, 0x656e7430, + 0x00000000, 0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, 0x45766e74, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, + 0x00000000, 0x00000000, 0x4d61696e, 0x43707542, 0x00000000, 0x00000000, + 0x00000000 +}; + +#if 0 /* All zeros, dont eat up space with it. */ +u32 t3FwData[(TG3_FW_DATA_LEN / sizeof(u32)) + 1] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; +#endif + +#define RX_CPU_SCRATCH_BASE 0x30000 +#define RX_CPU_SCRATCH_SIZE 0x04000 +#define TX_CPU_SCRATCH_BASE 0x34000 +#define TX_CPU_SCRATCH_SIZE 0x04000 + +/* tp->lock is held. */ +static int tg3_reset_cpu(struct tg3 *tp, u32 offset) +{ + int i; + + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + if (offset == RX_CPU_BASE) { + for (i = 0; i < 10000; i++) + if (!(tr32(offset + CPU_MODE) & CPU_MODE_RESET)) + break; + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + udelay(10); + } else { + for (i = 0; i < 10000; i++) { + if (!(tr32(offset + CPU_MODE) & CPU_MODE_RESET)) + break; + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_RESET); + udelay(10); + } + } + + if (i >= 10000) { + printk(KERN_ERR PFX "tg3_reset_cpu timed out for %s, " + "and %s CPU\n", + tp->dev->name, + (offset == RX_CPU_BASE ? "RX" : "TX")); + return -ENODEV; + } + return 0; +} + +/* tp->lock is held. */ +static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_base, + int cpu_scratch_size) +{ + int err, i; + + err = tg3_reset_cpu(tp, cpu_base); + if (err) + return err; + + for (i = 0; i < cpu_scratch_size; i += sizeof(u32)) + tg3_write_indirect_reg32(tp, cpu_scratch_base + i, 0); + tw32(cpu_base + CPU_STATE, 0xffffffff); + tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT); + for (i = 0; i < (TG3_FW_TEXT_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_TEXT_ADDR & 0xffff) + + (i * sizeof(u32))), + t3FwText[i]); + for (i = 0; i < (TG3_FW_RODATA_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_RODATA_ADDR & 0xffff) + + (i * sizeof(u32))), + t3FwRodata[i]); + for (i = 0; i < (TG3_FW_DATA_LEN / sizeof(u32)); i++) + tg3_write_indirect_reg32(tp, (cpu_scratch_base + + (TG3_FW_DATA_ADDR & 0xffff) + + (i * sizeof(u32))), + 0); + + return 0; +} + +/* tp->lock is held. */ +static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) +{ + int err, i; + + err = tg3_load_firmware_cpu(tp, RX_CPU_BASE, + RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE); + if (err) + return err; + + err = tg3_load_firmware_cpu(tp, TX_CPU_BASE, + TX_CPU_SCRATCH_BASE, TX_CPU_SCRATCH_SIZE); + if (err) + return err; + + /* Now startup only the RX cpu. */ + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + for (i = 0; i < 5; i++) { + if (tr32(RX_CPU_BASE + CPU_PC) == TG3_FW_TEXT_ADDR) + break; + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT); + tw32(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + udelay(1000); + } + if (i >= 5) { + printk(KERN_ERR PFX "tg3_load_firmware fails for %s " + "to set RX CPU PC, is %08x should be %08x\n", + tp->dev->name, tr32(RX_CPU_BASE + CPU_PC), + TG3_FW_TEXT_ADDR); + return -ENODEV; + } + tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); + tw32(RX_CPU_BASE + CPU_MODE, 0x00000000); + + return 0; +} + +/* tp->lock is held. */ +static void __tg3_set_mac_addr(struct tg3 *tp) +{ + u32 addr_high, addr_low; + int i; + + addr_high = ((tp->dev->dev_addr[0] << 8) | + tp->dev->dev_addr[1]); + addr_low = ((tp->dev->dev_addr[2] << 24) | + (tp->dev->dev_addr[3] << 16) | + (tp->dev->dev_addr[4] << 8) | + (tp->dev->dev_addr[5] << 0)); + for (i = 0; i < 4; i++) { + tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high); + tw32(MAC_ADDR_0_LOW + (i * 8), addr_low); + } + + addr_high = (tp->dev->dev_addr[0] + + tp->dev->dev_addr[1] + + tp->dev->dev_addr[2] + + tp->dev->dev_addr[3] + + tp->dev->dev_addr[4] + + tp->dev->dev_addr[5]) & + TX_BACKOFF_SEED_MASK; + tw32(MAC_TX_BACKOFF_SEED, addr_high); +} + +static int tg3_set_mac_addr(struct net_device *dev, void *p) +{ + struct tg3 *tp = dev->priv; + struct sockaddr *addr = p; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + spin_lock_irq(&tp->lock); + __tg3_set_mac_addr(tp); + spin_unlock_irq(&tp->lock); + + return 0; +} + +/* tp->lock is held. */ +static void tg3_set_bdinfo(struct tg3 *tp, u32 bdinfo_addr, + dma_addr_t mapping, u32 maxlen_flags, + u32 nic_addr) +{ + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_HOST_ADDR + + TG3_64BIT_REG_HIGH), + ((u64) mapping >> 32)); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_HOST_ADDR + + TG3_64BIT_REG_LOW), + ((u64) mapping & 0xffffffff)); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_MAXLEN_FLAGS), + maxlen_flags); + tg3_write_mem(tp, + (bdinfo_addr + + TG3_BDINFO_NIC_ADDR), + nic_addr); +} + +static void __tg3_set_rx_mode(struct net_device *); + +/* tp->lock is held. */ +static int tg3_reset_hw(struct tg3 *tp) +{ + u32 val; + int i, err; + + tg3_disable_ints(tp); + + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { + err = tg3_abort_hw(tp); + if (err) + return err; + } + + tg3_chip_reset(tp); + + tw32(GRC_MODE, tp->grc_mode); + tg3_write_mem(tp, + NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC1); + if (tp->phy_id == PHY_ID_SERDES) { + tp->mac_mode = MAC_MODE_PORT_MODE_TBI; + tw32(MAC_MODE, tp->mac_mode); + } else + tw32(MAC_MODE, 0); + + /* Wait for firmware initialization to complete. */ + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + if (i >= 100000) { + printk(KERN_ERR PFX "tg3_reset_hw timed out for %s, " + "firmware will not restart magic=%08x\n", + tp->dev->name, val); + return -ENODEV; + } + + /* This works around an issue with Athlon chipsets on + * B3 tigon3 silicon. This bit has no effect on any + * other revision. + */ + val = tr32(TG3PCI_CLOCK_CTRL); + val |= CLOCK_CTRL_DELAY_PCI_GRANT; + tw32(TG3PCI_CLOCK_CTRL, val); + + /* Clear statistics/status block in chip, and status block in ram. */ + for (i = NIC_SRAM_STATS_BLK; + i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE; + i += sizeof(u32)) + tg3_write_mem(tp, i, 0); + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + + /* This value is determined during the probe time DMA + * engine test, tg3_test_dma. + */ + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + + tp->grc_mode &= ~(GRC_MODE_HOST_SENDBDS | + GRC_MODE_4X_NIC_SEND_RINGS | + GRC_MODE_NO_TX_PHDR_CSUM | + GRC_MODE_NO_RX_PHDR_CSUM); + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) + tp->grc_mode |= GRC_MODE_HOST_SENDBDS; + else + tp->grc_mode |= GRC_MODE_4X_NIC_SEND_RINGS; + if (tp->tg3_flags & TG3_FLAG_NO_TX_PSEUDO_CSUM) + tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM; + if (tp->tg3_flags & TG3_FLAG_NO_RX_PSEUDO_CSUM) + tp->grc_mode |= GRC_MODE_NO_RX_PHDR_CSUM; + + tw32(GRC_MODE, + tp->grc_mode | + (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP)); + + /* Setup the timer prescalar register. Clock is always 66Mhz. */ + tw32(GRC_MISC_CFG, + (65 << GRC_MISC_CFG_PRESCALAR_SHIFT)); + + /* Initialize MBUF/DESC pool. */ + tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE); + tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE); + tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE); + tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE); + + if (!(tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE)) { + tw32(BUFMGR_MB_RDMA_LOW_WATER, + tp->bufmgr_config.mbuf_read_dma_low_water); + tw32(BUFMGR_MB_MACRX_LOW_WATER, + tp->bufmgr_config.mbuf_mac_rx_low_water); + tw32(BUFMGR_MB_HIGH_WATER, + tp->bufmgr_config.mbuf_high_water); + } else { + tw32(BUFMGR_MB_RDMA_LOW_WATER, + tp->bufmgr_config.mbuf_read_dma_low_water_jumbo); + tw32(BUFMGR_MB_MACRX_LOW_WATER, + tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo); + tw32(BUFMGR_MB_HIGH_WATER, + tp->bufmgr_config.mbuf_high_water_jumbo); + } + tw32(BUFMGR_DMA_LOW_WATER, + tp->bufmgr_config.dma_low_water); + tw32(BUFMGR_DMA_HIGH_WATER, + tp->bufmgr_config.dma_high_water); + + tw32(BUFMGR_MODE, BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE); + for (i = 0; i < 2000; i++) { + if (tr32(BUFMGR_MODE) & BUFMGR_MODE_ENABLE) + break; + udelay(10); + } + if (i >= 2000) { + printk(KERN_ERR PFX "tg3_reset_hw cannot enable BUFMGR for %s.\n", + tp->dev->name); + return -ENODEV; + } + + tw32(FTQ_RESET, 0xffffffff); + tw32(FTQ_RESET, 0x00000000); + for (i = 0; i < 2000; i++) { + if (tr32(FTQ_RESET) == 0x00000000) + break; + udelay(10); + } + if (i >= 2000) { + printk(KERN_ERR PFX "tg3_reset_hw cannot reset FTQ for %s.\n", + tp->dev->name); + return -ENODEV; + } + + /* Initialize TG3_BDINFO's at: + * RCVDBDI_STD_BD: standard eth size rx ring + * RCVDBDI_JUMBO_BD: jumbo frame rx ring + * RCVDBDI_MINI_BD: small frame rx ring (??? does not work) + * + * like so: + * TG3_BDINFO_HOST_ADDR: high/low parts of DMA address of ring + * TG3_BDINFO_MAXLEN_FLAGS: (rx max buffer size << 16) | + * ring attribute flags + * TG3_BDINFO_NIC_ADDR: location of descriptors in nic SRAM + * + * Standard receive ring @ NIC_SRAM_RX_BUFFER_DESC, 512 entries. + * Jumbo receive ring @ NIC_SRAM_RX_JUMBO_BUFFER_DESC, 256 entries. + * + * ??? No space allocated for mini receive ring? :( + * + * The size of each ring is fixed in the firmware, but the location is + * configurable. + */ + tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_std_mapping >> 32)); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_std_mapping & 0xffffffff)); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_STD_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_BUFFER_DESC); + +#if TG3_MINI_RING_WORKS + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_mini_mapping >> 32)); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_mini_mapping & 0xffffffff)); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_MINI_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_MINI_BUFFER_DESC); +#else + tw32(RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) { + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->rx_jumbo_mapping >> 32)); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->rx_jumbo_mapping & 0xffffffff)); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, + RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT); + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR, + NIC_SRAM_RX_JUMBO_BUFFER_DESC); + } else { + tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); + } + + /* Setup replenish thresholds. */ + tw32(RCVBDI_STD_THRESH, TG3_RX_RING_PENDING / 8); +#if TG3_MINI_RING_WORKS + tw32(RCVBDI_MINI_THRESH, TG3_RX_MINI_RING_PENDING / 8); +#endif + tw32(RCVBDI_JUMBO_THRESH, TG3_RX_JUMBO_RING_PENDING / 8); + + /* Clear out send RCB ring in SRAM. */ + for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE) + tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); + + tp->tx_prod = 0; + tp->tx_cons = 0; + tw32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0); + tw32_mailbox(MAILBOX_SNDNIC_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0); + + if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { + tg3_set_bdinfo(tp, NIC_SRAM_SEND_RCB, + tp->tx_desc_mapping, + (TG3_TX_RING_SIZE << + BDINFO_FLAGS_MAXLEN_SHIFT), + NIC_SRAM_TX_BUFFER_DESC); + } else { + tg3_set_bdinfo(tp, NIC_SRAM_SEND_RCB, + 0, + BDINFO_FLAGS_DISABLED, + NIC_SRAM_TX_BUFFER_DESC); + } + + for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) { + tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); + } + + tp->rx_rcb_ptr = 0; + tw32_mailbox(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, 0); + + tg3_set_bdinfo(tp, NIC_SRAM_RCV_RET_RCB, + tp->rx_rcb_mapping, + (TG3_RX_RCB_RING_SIZE << + BDINFO_FLAGS_MAXLEN_SHIFT), + 0); + + tp->rx_std_ptr = TG3_RX_RING_PENDING; + tw32_mailbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_std_ptr); +#if TG3_MINI_RING_WORKS + tp->rx_mini_ptr = TG3_RX_MINI_RING_PENDING; + tw32_mailbox(MAILBOX_RCV_MINI_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_mini_ptr); +#endif + + if (tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE) + tp->rx_jumbo_ptr = TG3_RX_JUMBO_RING_PENDING; + else + tp->rx_jumbo_ptr = 0; + tw32_mailbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, + tp->rx_jumbo_ptr); + + /* Initialize MAC address and backoff seed. */ + __tg3_set_mac_addr(tp); + + /* MTU + ethernet header + FCS + optional VLAN tag */ + tw32(MAC_RX_MTU_SIZE, tp->dev->mtu + ETH_HLEN + 8); + + /* The slot time is changed by tg3_setup_phy if we + * run at gigabit with half duplex. + */ + tw32(MAC_TX_LENGTHS, + (2 << TX_LENGTHS_IPG_CRS_SHIFT) | + (6 << TX_LENGTHS_IPG_SHIFT) | + (32 << TX_LENGTHS_SLOT_TIME_SHIFT)); + + /* Receive rules. */ + tw32(MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS); + tw32(RCVLPC_CONFIG, 0x0181); + + /* Receive/send statistics. */ + tw32(RCVLPC_STATS_ENABLE, 0xffffff); + tw32(RCVLPC_STATSCTRL, RCVLPC_STATSCTRL_ENABLE); + tw32(SNDDATAI_STATSENAB, 0xffffff); + tw32(SNDDATAI_STATSCTRL, + (SNDDATAI_SCTRL_ENABLE | + SNDDATAI_SCTRL_FASTUPD)); + + /* Setup host coalescing engine. */ + tw32(HOSTCC_MODE, 0); + for (i = 0; i < 2000; i++) { + if (!(tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE)) + break; + udelay(10); + } + + tw32(HOSTCC_RXCOL_TICKS, + tp->coalesce_config.rx_coalesce_ticks); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + tw32(HOSTCC_RXCOAL_TICK_INT, + tp->coalesce_config.rx_coalesce_ticks_during_int); + tw32(HOSTCC_RXCOAL_MAXF_INT, + tp->coalesce_config.rx_max_coalesced_frames_during_int); + tw32(HOSTCC_TXCOL_TICKS, + tp->coalesce_config.tx_coalesce_ticks); + tw32(HOSTCC_TXMAX_FRAMES, + tp->coalesce_config.tx_max_coalesced_frames); + tw32(HOSTCC_TXCOAL_TICK_INT, + tp->coalesce_config.tx_coalesce_ticks_during_int); + tw32(HOSTCC_TXCOAL_MAXF_INT, + tp->coalesce_config.tx_max_coalesced_frames_during_int); + tw32(HOSTCC_STAT_COAL_TICKS, + tp->coalesce_config.stats_coalesce_ticks); + + /* Status/statistics block address. */ + tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->stats_mapping >> 32)); + tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->stats_mapping & 0xffffffff)); + tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, + ((u64) tp->status_mapping >> 32)); + tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, + ((u64) tp->status_mapping & 0xffffffff)); + tw32(HOSTCC_STATS_BLK_NIC_ADDR, NIC_SRAM_STATS_BLK); + tw32(HOSTCC_STATUS_BLK_NIC_ADDR, NIC_SRAM_STATUS_BLK); + + tw32(HOSTCC_MODE, HOSTCC_MODE_ENABLE | tp->coalesce_mode); + + tw32(RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE); + tw32(RCVLPC_MODE, RCVLPC_MODE_ENABLE); + tw32(RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE); + + tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE | + MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE; + tw32(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR); + + tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_GPIO_OE1 | + GRC_LCLCTRL_GPIO_OUTPUT1 | GRC_LCLCTRL_AUTO_SEEPROM; + tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); + + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0); + + tw32(DMAC_MODE, DMAC_MODE_ENABLE); + + tw32(WDMAC_MODE, (WDMAC_MODE_ENABLE | WDMAC_MODE_TGTABORT_ENAB | + WDMAC_MODE_MSTABORT_ENAB | WDMAC_MODE_PARITYERR_ENAB | + WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB | + WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB | + WDMAC_MODE_LNGREAD_ENAB)); + tw32(RDMAC_MODE, (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB | + RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB | + RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB | + RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | + RDMAC_MODE_LNGREAD_ENAB)); + + tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE); + tw32(MBFREE_MODE, MBFREE_MODE_ENABLE); + tw32(SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); + tw32(SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE); + tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB); + tw32(RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ); + tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); + tw32(SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE); + tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { + err = tg3_load_5701_a0_firmware_fix(tp); + if (err) + return err; + } + + tp->tx_mode = TX_MODE_ENABLE; + tw32(MAC_TX_MODE, tp->tx_mode); + tp->rx_mode = RX_MODE_ENABLE; + tw32(MAC_RX_MODE, tp->rx_mode); + + if (tp->link_config.phy_is_low_power) { + tp->link_config.phy_is_low_power = 0; + tp->link_config.speed = tp->link_config.orig_speed; + tp->link_config.duplex = tp->link_config.orig_duplex; + tp->link_config.autoneg = tp->link_config.orig_autoneg; + } + + tp->mi_mode = MAC_MI_MODE_BASE; + tw32(MAC_MI_MODE, tp->mi_mode); + tw32(MAC_LED_CTRL, 0); + tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); + tw32(MAC_RX_MODE, RX_MODE_RESET); + udelay(10); + tw32(MAC_RX_MODE, tp->rx_mode); + + err = tg3_setup_phy(tp); + if (err) + return err; + + if (tp->phy_id != PHY_ID_SERDES) { + u32 tmp; + + /* Clear CRC stats. */ + tg3_readphy(tp, 0x1e, &tmp); + tg3_writephy(tp, 0x1e, tmp | 0x8000); + tg3_readphy(tp, 0x14, &tmp); + } + + __tg3_set_rx_mode(tp->dev); + + /* Initialize receive rules. */ + tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK); +#if 0 + tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); + tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); +#endif + tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); + tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); + tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); + tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); + tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); + tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); + tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); + tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); + tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); + tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); + tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); + tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); + + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) + tg3_enable_ints(tp); + + return 0; +} + +/* Called at device open time to get the chip ready for + * packet processing. Invoked with tp->lock held. + */ +static int tg3_init_hw(struct tg3 *tp) +{ + int err; + + /* Force the chip into D0. */ + err = tg3_set_power_state(tp, 0); + if (err) + goto out; + + tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); + + err = tg3_reset_hw(tp); + +out: + return err; +} + +static void tg3_timer(unsigned long __opaque) +{ + struct tg3 *tp = (struct tg3 *) __opaque; + + spin_lock_irq(&tp->lock); + + if (!(tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS)) { + /* All of this garbage is because on the 5700 the + * mailbox/status_block protocol the chip uses with + * the cpu is race prone. + */ + if (tp->hw_status->status & SD_STATUS_UPDATED) { + tw32(GRC_LOCAL_CTRL, + tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); + } else { + tw32(HOSTCC_MODE, + (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW)); + } + + if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { + tg3_halt(tp); + tg3_init_rings(tp); + tg3_init_hw(tp); + } + } + + /* This part only runs once per second. */ + if (!--tp->timer_counter) { + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { + u32 mac_stat; + int phy_event; + + mac_stat = tr32(MAC_STATUS); + + phy_event = 0; + if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) { + if (mac_stat & MAC_STATUS_MI_INTERRUPT) + phy_event = 1; + } else if (mac_stat & MAC_STATUS_LNKSTATE_CHANGED) + phy_event = 1; + + if (phy_event) + tg3_setup_phy(tp); + } + + tp->timer_counter = tp->timer_multiplier; + } + + spin_unlock_irq(&tp->lock); + + tp->timer.expires = jiffies + tp->timer_offset; + add_timer(&tp->timer); +} + +static int tg3_open(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + int err; + + spin_lock_irq(&tp->lock); + + tg3_disable_ints(tp); + tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE; + + spin_unlock_irq(&tp->lock); + + /* If you move this call, make sure TG3_FLAG_HOST_TXDS in + * tp->tg3_flags is accurate at that new place. + */ + err = tg3_alloc_consistent(tp); + if (err) + return err; + + if (tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS) + err = request_irq(dev->irq, tg3_interrupt_tagged, + SA_SHIRQ, dev->name, dev); + else + err = request_irq(dev->irq, tg3_interrupt, + SA_SHIRQ, dev->name, dev); + + if (err) { + tg3_free_consistent(tp); + return err; + } + + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + + err = tg3_init_hw(tp); + if (err) { + tg3_halt(tp); + tg3_free_rings(tp); + } else { + if (tp->tg3_flags & TG3_FLAG_TAGGED_IRQ_STATUS) { + tp->timer_offset = HZ; + tp->timer_counter = tp->timer_multiplier = 1; + } else { + tp->timer_offset = HZ / 10; + tp->timer_counter = tp->timer_multiplier = 10; + } + + init_timer(&tp->timer); + tp->timer.expires = jiffies + tp->timer_offset; + tp->timer.data = (unsigned long) tp; + tp->timer.function = tg3_timer; + add_timer(&tp->timer); + + tp->last_rate_sample = jiffies; + tp->last_rx_count = 0; + tp->last_tx_count = 0; + + tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; + } + + spin_unlock_irq(&tp->lock); + + if (err) { + free_irq(dev->irq, dev); + tg3_free_consistent(tp); + return err; + } + + netif_start_queue(dev); + + spin_lock_irq(&tp->lock); + + tg3_enable_ints(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +#if 0 +/*static*/ void tg3_dump_state(struct tg3 *tp) +{ + u32 val32, val32_2, val32_3, val32_4, val32_5; + u16 val16; + int i; + + pci_read_config_word(tp->pdev, PCI_STATUS, &val16); + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, &val32); + printk("DEBUG: PCI status [%04x] TG3PCI state[%08x]\n", + val16, val32); + + /* MAC block */ + printk("DEBUG: MAC_MODE[%08x] MAC_STATUS[%08x]\n", + tr32(MAC_MODE), tr32(MAC_STATUS)); + printk(" MAC_EVENT[%08x] MAC_LED_CTRL[%08x]\n", + tr32(MAC_EVENT), tr32(MAC_LED_CTRL)); + printk("DEBUG: MAC_TX_MODE[%08x] MAC_TX_STATUS[%08x]\n", + tr32(MAC_TX_MODE), tr32(MAC_TX_STATUS)); + printk(" MAC_RX_MODE[%08x] MAC_RX_STATUS[%08x]\n", + tr32(MAC_RX_MODE), tr32(MAC_RX_STATUS)); + + /* Send data initiator control block */ + printk("DEBUG: SNDDATAI_MODE[%08x] SNDDATAI_STATUS[%08x]\n", + tr32(SNDDATAI_MODE), tr32(SNDDATAI_STATUS)); + printk(" SNDDATAI_STATSCTRL[%08x]\n", + tr32(SNDDATAI_STATSCTRL)); + + /* Send data completion control block */ + printk("DEBUG: SNDDATAC_MODE[%08x]\n", tr32(SNDDATAC_MODE)); + + /* Send BD ring selector block */ + printk("DEBUG: SNDBDS_MODE[%08x] SNDBDS_STATUS[%08x]\n", + tr32(SNDBDS_MODE), tr32(SNDBDS_STATUS)); + + /* Send BD initiator control block */ + printk("DEBUG: SNDBDI_MODE[%08x] SNDBDI_STATUS[%08x]\n", + tr32(SNDBDI_MODE), tr32(SNDBDI_STATUS)); + + /* Send BD completion control block */ + printk("DEBUG: SNDBDC_MODE[%08x]\n", tr32(SNDBDC_MODE)); + + /* Receive list placement control block */ + printk("DEBUG: RCVLPC_MODE[%08x] RCVLPC_STATUS[%08x]\n", + tr32(RCVLPC_MODE), tr32(RCVLPC_STATUS)); + printk(" RCVLPC_STATSCTRL[%08x]\n", + tr32(RCVLPC_STATSCTRL)); + + /* Receive data and receive BD initiator control block */ + printk("DEBUG: RCVDBDI_MODE[%08x] RCVDBDI_STATUS[%08x]\n", + tr32(RCVDBDI_MODE), tr32(RCVDBDI_STATUS)); + + /* Receive data completion control block */ + printk("DEBUG: RCVDCC_MODE[%08x]\n", + tr32(RCVDCC_MODE)); + + /* Receive BD initiator control block */ + printk("DEBUG: RCVBDI_MODE[%08x] RCVBDI_STATUS[%08x]\n", + tr32(RCVBDI_MODE), tr32(RCVBDI_STATUS)); + + /* Receive BD completion control block */ + printk("DEBUG: RCVCC_MODE[%08x] RCVCC_STATUS[%08x]\n", + tr32(RCVCC_MODE), tr32(RCVCC_STATUS)); + + /* Receive list selector control block */ + printk("DEBUG: RCVLSC_MODE[%08x] RCVLSC_STATUS[%08x]\n", + tr32(RCVLSC_MODE), tr32(RCVLSC_STATUS)); + + /* Mbuf cluster free block */ + printk("DEBUG: MBFREE_MODE[%08x] MBFREE_STATUS[%08x]\n", + tr32(MBFREE_MODE), tr32(MBFREE_STATUS)); + + /* Host coalescing control block */ + printk("DEBUG: HOSTCC_MODE[%08x] HOSTCC_STATUS[%08x]\n", + tr32(HOSTCC_MODE), tr32(HOSTCC_STATUS)); + printk("DEBUG: HOSTCC_STATS_BLK_HOST_ADDR[%08x%08x]\n", + tr32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH), + tr32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW)); + printk("DEBUG: HOSTCC_STATUS_BLK_HOST_ADDR[%08x%08x]\n", + tr32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH), + tr32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW)); + printk("DEBUG: HOSTCC_STATS_BLK_NIC_ADDR[%08x]\n", + tr32(HOSTCC_STATS_BLK_NIC_ADDR)); + printk("DEBUG: HOSTCC_STATUS_BLK_NIC_ADDR[%08x]\n", + tr32(HOSTCC_STATUS_BLK_NIC_ADDR)); + + /* Memory arbiter control block */ + printk("DEBUG: MEMARB_MODE[%08x] MEMARB_STATUS[%08x]\n", + tr32(MEMARB_MODE), tr32(MEMARB_STATUS)); + + /* Buffer manager control block */ + printk("DEBUG: BUFMGR_MODE[%08x] BUFMGR_STATUS[%08x]\n", + tr32(BUFMGR_MODE), tr32(BUFMGR_STATUS)); + printk("DEBUG: BUFMGR_MB_POOL_ADDR[%08x] BUFMGR_MB_POOL_SIZE[%08x]\n", + tr32(BUFMGR_MB_POOL_ADDR), tr32(BUFMGR_MB_POOL_SIZE)); + printk("DEBUG: BUFMGR_DMA_DESC_POOL_ADDR[%08x] " + "BUFMGR_DMA_DESC_POOL_SIZE[%08x]\n", + tr32(BUFMGR_DMA_DESC_POOL_ADDR), + tr32(BUFMGR_DMA_DESC_POOL_SIZE)); + + /* Read DMA control block */ + printk("DEBUG: RDMAC_MODE[%08x] RDMAC_STATUS[%08x]\n", + tr32(RDMAC_MODE), tr32(RDMAC_STATUS)); + + /* Write DMA control block */ + printk("DEBUG: WDMAC_MODE[%08x] WDMAC_STATUS[%08x]\n", + tr32(WDMAC_MODE), tr32(WDMAC_STATUS)); + + /* DMA completion block */ + printk("DEBUG: DMAC_MODE[%08x]\n", + tr32(DMAC_MODE)); + + /* GRC block */ + printk("DEBUG: GRC_MODE[%08x] GRC_MISC_CFG[%08x]\n", + tr32(GRC_MODE), tr32(GRC_MISC_CFG)); + printk("DEBUG: GRC_LOCAL_CTRL[%08x]\n", + tr32(GRC_LOCAL_CTRL)); + + /* TG3_BDINFOs */ + printk("DEBUG: RCVDBDI_JUMBO_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_JUMBO_BD + 0x0), + tr32(RCVDBDI_JUMBO_BD + 0x4), + tr32(RCVDBDI_JUMBO_BD + 0x8), + tr32(RCVDBDI_JUMBO_BD + 0xc)); + printk("DEBUG: RCVDBDI_STD_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_STD_BD + 0x0), + tr32(RCVDBDI_STD_BD + 0x4), + tr32(RCVDBDI_STD_BD + 0x8), + tr32(RCVDBDI_STD_BD + 0xc)); + printk("DEBUG: RCVDBDI_MINI_BD[%08x%08x:%08x:%08x]\n", + tr32(RCVDBDI_MINI_BD + 0x0), + tr32(RCVDBDI_MINI_BD + 0x4), + tr32(RCVDBDI_MINI_BD + 0x8), + tr32(RCVDBDI_MINI_BD + 0xc)); + + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_SEND_RCB + 0xc, &val32_4); + printk("DEBUG: SRAM_SEND_RCB_0[%08x%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4); + + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_RCV_RET_RCB + 0xc, &val32_4); + printk("DEBUG: SRAM_RCV_RET_RCB_0[%08x%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4); + + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x0, &val32); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x4, &val32_2); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x8, &val32_3); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0xc, &val32_4); + tg3_read_mem(tp, NIC_SRAM_STATUS_BLK + 0x10, &val32_5); + printk("DEBUG: SRAM_STATUS_BLK[%08x:%08x:%08x:%08x:%08x]\n", + val32, val32_2, val32_3, val32_4, val32_5); + + /* SW status block */ + printk("DEBUG: Host status block [%08x:%08x:(%04x:%04x:%04x):(%04x:%04x)]\n", + tp->hw_status->status, + tp->hw_status->status_tag, + tp->hw_status->rx_jumbo_consumer, + tp->hw_status->rx_consumer, + tp->hw_status->rx_mini_consumer, + tp->hw_status->idx[0].rx_producer, + tp->hw_status->idx[0].tx_consumer); + + /* SW statistics block */ + printk("DEBUG: Host statistics block [%08x:%08x:%08x:%08x]\n", + ((u32 *)tp->hw_stats)[0], + ((u32 *)tp->hw_stats)[1], + ((u32 *)tp->hw_stats)[2], + ((u32 *)tp->hw_stats)[3]); + + /* Mailboxes */ + printk("DEBUG: SNDHOST_PROD[%08x%08x] SNDNIC_PROD[%08x%08x]\n", + tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x0), + tr32(MAILBOX_SNDHOST_PROD_IDX_0 + 0x4), + tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x0), + tr32(MAILBOX_SNDNIC_PROD_IDX_0 + 0x4)); + + /* NIC side send descriptors. */ + for (i = 0; i < 6; i++) { + unsigned long txd; + + txd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_TX_BUFFER_DESC + + (i * sizeof(struct tg3_tx_buffer_desc)); + printk("DEBUG: NIC TXD(%d)[%08x:%08x:%08x:%08x]\n", + i, + readl(txd + 0x0), readl(txd + 0x4), + readl(txd + 0x8), readl(txd + 0xc)); + } + + /* NIC side RX descriptors. */ + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_STD(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_STD(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +#if TG3_MINI_RING_WORKS + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_MINI_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_MINI(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_MINI(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +#endif + + for (i = 0; i < 6; i++) { + unsigned long rxd; + + rxd = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_RX_JUMBO_BUFFER_DESC + + (i * sizeof(struct tg3_rx_buffer_desc)); + printk("DEBUG: NIC RXD_JUMBO(%d)[0][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + rxd += (4 * sizeof(u32)); + printk("DEBUG: NIC RXD_JUMBO(%d)[1][%08x:%08x:%08x:%08x]\n", + i, + readl(rxd + 0x0), readl(rxd + 0x4), + readl(rxd + 0x8), readl(rxd + 0xc)); + } +} +#endif + +static int tg3_close(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + netif_stop_queue(dev); + + del_timer_sync(&tp->timer); + + spin_lock_irq(&tp->lock); +#if 0 + tg3_dump_state(tp); +#endif + + tg3_disable_ints(tp); + + tg3_halt(tp); + tg3_free_rings(tp); + tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE; + + spin_unlock_irq(&tp->lock); + + free_irq(dev->irq, dev); + + tg3_free_consistent(tp); + + return 0; +} + +static inline unsigned long get_stat64(tg3_stat64_t *val) +{ + unsigned long ret; + +#if (BITS_PER_LONG == 32) + if (val->high != 0) + ret = ~0UL; + else + ret = val->low; +#else + ret = ((u64)val->high << 32) | ((u64)val->low); +#endif + return ret; +} + +static unsigned long calc_crc_errors(struct tg3 *tp) +{ + struct tg3_hw_stats *hw_stats = tp->hw_stats; + + if (tp->phy_id != PHY_ID_SERDES && + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) { + unsigned long flags; + u32 val; + + spin_lock_irqsave(&tp->lock, flags); + tg3_readphy(tp, 0x1e, &val); + tg3_writephy(tp, 0x1e, val | 0x8000); + tg3_readphy(tp, 0x14, &val); + spin_unlock_irqrestore(&tp->lock, flags); + + tp->phy_crc_errors += val; + + return tp->phy_crc_errors; + } + + return get_stat64(&hw_stats->rx_fcs_errors); +} + +static struct net_device_stats *tg3_get_stats(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + struct net_device_stats *stats = &tp->net_stats; + struct tg3_hw_stats *hw_stats = tp->hw_stats; + + /* XXX Fix this... this is wrong because + * XXX it means every open/close we lose the stats. + */ + if (!hw_stats) + return stats; + + stats->rx_packets = + get_stat64(&hw_stats->rx_ucast_packets) + + get_stat64(&hw_stats->rx_mcast_packets) + + get_stat64(&hw_stats->rx_bcast_packets); + + stats->tx_packets = + get_stat64(&hw_stats->COS_out_packets[0]); + + stats->rx_bytes = get_stat64(&hw_stats->rx_octets); + stats->tx_bytes = get_stat64(&hw_stats->tx_octets); + + stats->rx_errors = get_stat64(&hw_stats->rx_errors); + stats->tx_errors = + get_stat64(&hw_stats->tx_errors) + + get_stat64(&hw_stats->tx_mac_errors) + + get_stat64(&hw_stats->tx_carrier_sense_errors) + + get_stat64(&hw_stats->tx_discards); + + stats->multicast = get_stat64(&hw_stats->rx_mcast_packets); + stats->collisions = get_stat64(&hw_stats->tx_collisions); + + stats->rx_length_errors = + get_stat64(&hw_stats->rx_frame_too_long_errors) + + get_stat64(&hw_stats->rx_undersize_packets); + + stats->rx_over_errors = get_stat64(&hw_stats->rxbds_empty); + stats->rx_frame_errors = get_stat64(&hw_stats->rx_align_errors); + stats->tx_aborted_errors = get_stat64(&hw_stats->tx_discards); + stats->tx_carrier_errors = + get_stat64(&hw_stats->tx_carrier_sense_errors); + + stats->rx_crc_errors = calc_crc_errors(tp); + + return stats; +} + +static inline u32 calc_crc(unsigned char *buf, int len) +{ + u32 reg; + u32 tmp; + int j, k; + + reg = 0xffffffff; + + for (j = 0; j < len; j++) { + reg ^= buf[j]; + + for (k = 0; k < 8; k++) { + tmp = reg & 0x01; + + reg >>= 1; + + if (tmp) { + reg ^= 0xedb88320; + } + } + } + + return ~reg; +} + +static void tg3_set_multi(struct tg3 *tp, unsigned int accept_all) +{ + /* accept or reject all multicast frames */ + tw32 (MAC_HASH_REG_0, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_1, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_2, accept_all ? 0xffffffff : 0); + tw32 (MAC_HASH_REG_3, accept_all ? 0xffffffff : 0); +} + +static void __tg3_set_rx_mode(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + u32 rx_mode; + + rx_mode = tp->rx_mode & ~RX_MODE_PROMISC; + + if (dev->flags & IFF_PROMISC) { + /* Promiscuous mode. */ + rx_mode |= RX_MODE_PROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + /* Accept all multicast. */ + tg3_set_multi (tp, 1); + } else if (dev->mc_count < 1) { + /* Reject all multicast. */ + tg3_set_multi (tp, 0); + } else { + /* Accept one or more multicast(s). */ + struct dev_mc_list *mclist; + unsigned int i; + u32 mc_filter[4] = { 0, }; + u32 regidx; + u32 bit; + u32 crc; + + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + + crc = calc_crc (mclist->dmi_addr, ETH_ALEN); + bit = ~crc & 0x7f; + regidx = (bit & 0x60) >> 5; + bit &= 0x1f; + mc_filter[regidx] |= (1 << bit); + } + + tw32 (MAC_HASH_REG_0, mc_filter[0]); + tw32 (MAC_HASH_REG_1, mc_filter[1]); + tw32 (MAC_HASH_REG_2, mc_filter[2]); + tw32 (MAC_HASH_REG_3, mc_filter[3]); + } + + if (rx_mode != tp->rx_mode) { + tp->rx_mode = rx_mode; + tw32 (MAC_RX_MODE, rx_mode); + } +} + +static void tg3_set_rx_mode(struct net_device *dev) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + __tg3_set_rx_mode(dev); + spin_unlock_irq(&tp->lock); +} + +static u8 *tg3_get_regs(struct tg3 *tp) +{ + u8 *orig_p = kmalloc((32 * 1024), GFP_KERNEL); + u8 *p; + int i; + + if (orig_p == NULL) + return NULL; + + memset(orig_p, 0, (32 * 1024)); + + spin_lock_irq(&tp->lock); + +#define __GET_REG32(reg) (*((u32 *)(p))++ = tr32(reg)) +#define GET_REG32_LOOP(base,len) \ +do { p = orig_p + (base); \ + for (i = 0; i < len; i += 4) \ + __GET_REG32((base) + i); \ +} while (0) +#define GET_REG32_1(reg) \ +do { p = orig_p + (reg); \ + __GET_REG32((reg)); \ +} while (0) + + GET_REG32_LOOP(TG3PCI_VENDOR, 0xb0); + GET_REG32_LOOP(MAILBOX_INTERRUPT_0, 0x200); + GET_REG32_LOOP(MAC_MODE, 0x4f0); + GET_REG32_LOOP(SNDDATAI_MODE, 0xe0); + GET_REG32_1(SNDDATAC_MODE); + GET_REG32_LOOP(SNDBDS_MODE, 0x80); + GET_REG32_LOOP(SNDBDI_MODE, 0x48); + GET_REG32_1(SNDBDC_MODE); + GET_REG32_LOOP(RCVLPC_MODE, 0x20); + GET_REG32_LOOP(RCVLPC_SELLST_BASE, 0x15c); + GET_REG32_LOOP(RCVDBDI_MODE, 0x0c); + GET_REG32_LOOP(RCVDBDI_JUMBO_BD, 0x3c); + GET_REG32_LOOP(RCVDBDI_BD_PROD_IDX_0, 0x44); + GET_REG32_1(RCVDCC_MODE); + GET_REG32_LOOP(RCVBDI_MODE, 0x20); + GET_REG32_LOOP(RCVCC_MODE, 0x14); + GET_REG32_LOOP(RCVLSC_MODE, 0x08); + GET_REG32_1(MBFREE_MODE); + GET_REG32_LOOP(HOSTCC_MODE, 0x100); + GET_REG32_LOOP(MEMARB_MODE, 0x10); + GET_REG32_LOOP(BUFMGR_MODE, 0x58); + GET_REG32_LOOP(RDMAC_MODE, 0x08); + GET_REG32_LOOP(WDMAC_MODE, 0x08); + GET_REG32_LOOP(RX_CPU_BASE, 0x280); + GET_REG32_LOOP(TX_CPU_BASE, 0x280); + GET_REG32_LOOP(GRCMBOX_INTERRUPT_0, 0x110); + GET_REG32_LOOP(FTQ_RESET, 0x120); + GET_REG32_LOOP(MSGINT_MODE, 0x0c); + GET_REG32_1(DMAC_MODE); + GET_REG32_LOOP(GRC_MODE, 0x4c); + GET_REG32_LOOP(NVRAM_CMD, 0x24); + +#undef __GET_REG32 +#undef GET_REG32_LOOP +#undef GET_REG32_1 + + spin_unlock_irq(&tp->lock); + + return orig_p; +} + +static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) +{ + struct tg3 *tp = dev->priv; + struct pci_dev *pci_dev = tp->pdev; + u32 ethcmd; + + if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO:{ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_MODULE_NAME); + strcpy (info.version, DRV_MODULE_VERSION); + strcpy (info.bus_info, pci_dev->slot_name); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + case ETHTOOL_GSET: { + struct ethtool_cmd cmd = { ETHTOOL_GSET }; + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + tp->link_config.phy_is_low_power) + return -EAGAIN; + cmd.supported = (SUPPORTED_Autoneg); + + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) + cmd.supported |= (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); + + if (tp->phy_id != PHY_ID_SERDES) + cmd.supported |= (SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_MII); + else + cmd.supported |= SUPPORTED_FIBRE; + + cmd.advertising = tp->link_config.advertising; + cmd.speed = tp->link_config.active_speed; + cmd.duplex = tp->link_config.active_duplex; + cmd.port = 0; + cmd.phy_address = PHY_ADDR; + cmd.transceiver = 0; + cmd.autoneg = tp->link_config.autoneg; + cmd.maxtxpkt = tp->coalesce_config.tx_max_coalesced_frames; + cmd.maxrxpkt = tp->coalesce_config.rx_max_coalesced_frames; + if (copy_to_user(useraddr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: { + struct ethtool_cmd cmd; + + if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || + tp->link_config.phy_is_low_power) + return -EAGAIN; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + + /* Fiber PHY only supports 1000 full/half */ + if (cmd.autoneg == AUTONEG_ENABLE) { + if (tp->phy_id == PHY_ID_SERDES && + (cmd.advertising & + (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full))) + return -EINVAL; + if ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) && + (cmd.advertising & + (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full))) + return -EINVAL; + } else { + if (tp->phy_id == PHY_ID_SERDES && + (cmd.speed == SPEED_10 || + cmd.speed == SPEED_100)) + return -EINVAL; + if ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) && + (cmd.speed == SPEED_10 || + cmd.speed == SPEED_100)) + return -EINVAL; + } + + spin_lock_irq(&tp->lock); + + tp->link_config.autoneg = cmd.autoneg; + if (cmd.autoneg == AUTONEG_ENABLE) { + tp->link_config.advertising = cmd.advertising; + tp->link_config.speed = SPEED_INVALID; + tp->link_config.duplex = DUPLEX_INVALID; + } else { + tp->link_config.speed = cmd.speed; + tp->link_config.duplex = cmd.duplex; + } + + if (cmd.maxtxpkt || cmd.maxrxpkt) { + tp->coalesce_config.tx_max_coalesced_frames = + cmd.maxtxpkt; + tp->coalesce_config.rx_max_coalesced_frames = + cmd.maxrxpkt; + + /* Coalescing config bits can be updated without + * a full chip reset. + */ + tw32(HOSTCC_TXMAX_FRAMES, + tp->coalesce_config.tx_max_coalesced_frames); + tw32(HOSTCC_RXMAX_FRAMES, + tp->coalesce_config.rx_max_coalesced_frames); + } + tg3_setup_phy(tp); + spin_unlock_irq(&tp->lock); + + return 0; + } + + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + u8 *regbuf; + int ret; + + if (copy_from_user(®s, useraddr, sizeof(regs))) + return -EFAULT; + if (regs.len > (32 * 1024)) + regs.len = (32 * 1024); + regs.version = 0; + if (copy_to_user(useraddr, ®s, sizeof(regs))) + return -EFAULT; + + regbuf = tg3_get_regs(tp); + if (!regbuf) + return -ENOMEM; + + useraddr += offsetof(struct ethtool_regs, data); + ret = 0; + if (copy_to_user(useraddr, regbuf, regs.len)) + ret = -EFAULT; + kfree(regbuf); + return ret; + } + case ETHTOOL_GWOL: { + struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; + + wol.supported = WAKE_MAGIC; + wol.wolopts = 0; + if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) + wol.wolopts = WAKE_MAGIC; + memset(&wol.sopass, 0, sizeof(wol.sopass)); + if (copy_to_user(useraddr, &wol, sizeof(wol))) + return -EFAULT; + return 0; + } + case ETHTOOL_SWOL: { + struct ethtool_wolinfo wol; + + if (copy_from_user(&wol, useraddr, sizeof(wol))) + return -EFAULT; + if (wol.wolopts & ~WAKE_MAGIC) + return -EINVAL; + spin_lock_irq(&tp->lock); + if (wol.wolopts & WAKE_MAGIC) + tp->tg3_flags |= TG3_FLAG_WOL_ENABLE; + else + tp->tg3_flags &= ~TG3_FLAG_WOL_ENABLE; + spin_unlock_irq(&tp->lock); + + return 0; + } + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = { ETHTOOL_GMSGLVL }; + edata.data = tp->msg_enable; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + tp->msg_enable = edata.data; + return 0; + } + case ETHTOOL_NWAY_RST: { + u32 bmcr; + int r; + + spin_lock_irq(&tp->lock); + tg3_readphy(tp, MII_BMCR, &bmcr); + tg3_readphy(tp, MII_BMCR, &bmcr); + r = -EINVAL; + if (bmcr & BMCR_ANENABLE) { + tg3_writephy(tp, MII_BMCR, + bmcr | BMCR_ANRESTART); + r = 0; + } + spin_unlock_irq(&tp->lock); + + return r; + } + case ETHTOOL_GLINK: { + struct ethtool_value edata = { ETHTOOL_GLINK }; + edata.data = netif_carrier_ok(tp->dev) ? 1 : 0; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + } + }; + + return -EOPNOTSUPP; +} + +static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; + struct tg3 *tp = dev->priv; + int err; + + switch(cmd) { + case SIOCETHTOOL: + return tg3_ethtool_ioctl(dev, (void *) ifr->ifr_data); + case SIOCGMIIPHY: + data->phy_id = PHY_ADDR; + + /* fallthru */ + case SIOCGMIIREG: { + u32 mii_regval; + + spin_lock_irq(&tp->lock); + err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval); + spin_unlock_irq(&tp->lock); + + data->val_out = mii_regval; + + return err; + } + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irq(&tp->lock); + err = tg3_writephy(tp, data->reg_num & 0x1f, data->val_in); + spin_unlock_irq(&tp->lock); + + return err; + + default: + /* do nothing */ + break; + } + return -EOPNOTSUPP; +} + +#if TG3_VLAN_TAG_USED +static void tg3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + tp->vlgrp = grp; + spin_unlock_irq(&tp->lock); +} + +static void tg3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct tg3 *tp = dev->priv; + + spin_lock_irq(&tp->lock); + if (tp->vlgrp) + tp->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irq(&tp->lock); +} +#endif + +/* Chips other than 5700/5701 use the NVRAM for fetching info. */ +static void __devinit tg3_nvram_init(struct tg3 *tp) +{ + int j; + + tw32(GRC_EEPROM_ADDR, + (EEPROM_ADDR_FSM_RESET | + (EEPROM_DEFAULT_CLOCK_PERIOD << + EEPROM_ADDR_CLKPERD_SHIFT))); + + /* XXX schedule_timeout() ... */ + for (j = 0; j < 100; j++) + udelay(10); + + /* Enable seeprom accesses. */ + tw32(GRC_LOCAL_CTRL, + tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM); + udelay(100); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { + u32 nvcfg1 = tr32(NVRAM_CFG1); + + tp->tg3_flags |= TG3_FLAG_NVRAM; + if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { + if (nvcfg1 & NVRAM_CFG1_BUFFERED_MODE) + tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; + } else { + nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; + tw32(NVRAM_CFG1, nvcfg1); + } + + } else { + tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); + } +} + +static int __devinit tg3_nvram_read_using_eeprom(struct tg3 *tp, + u32 offset, u32 *val) +{ + u32 tmp; + int i; + + if (offset > EEPROM_ADDR_ADDR_MASK || + (offset % 4) != 0) + return -EINVAL; + + tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK | + EEPROM_ADDR_DEVID_MASK | + EEPROM_ADDR_READ); + tw32(GRC_EEPROM_ADDR, + tmp | + (0 << EEPROM_ADDR_DEVID_SHIFT) | + ((offset << EEPROM_ADDR_ADDR_SHIFT) & + EEPROM_ADDR_ADDR_MASK) | + EEPROM_ADDR_READ | EEPROM_ADDR_START); + + for (i = 0; i < 10000; i++) { + tmp = tr32(GRC_EEPROM_ADDR); + + if (tmp & EEPROM_ADDR_COMPLETE) + break; + udelay(100); + } + if (!(tmp & EEPROM_ADDR_COMPLETE)) + return -EBUSY; + + *val = tr32(GRC_EEPROM_DATA); + return 0; +} + +static int __devinit tg3_nvram_read(struct tg3 *tp, + u32 offset, u32 *val) +{ + int i, saw_done_clear; + + if (!(tp->tg3_flags & TG3_FLAG_NVRAM)) + return tg3_nvram_read_using_eeprom(tp, offset, val); + + if (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED) + offset = ((offset / NVRAM_BUFFERED_PAGE_SIZE) << + NVRAM_BUFFERED_PAGE_POS) + + (offset % NVRAM_BUFFERED_PAGE_SIZE); + + if (offset > NVRAM_ADDR_MSK) + return -EINVAL; + + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 1000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(20); + } + + tw32(NVRAM_ADDR, offset); + tw32(NVRAM_CMD, + NVRAM_CMD_RD | NVRAM_CMD_GO | + NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); + + /* Wait for done bit to clear then set again. */ + saw_done_clear = 0; + for (i = 0; i < 1000; i++) { + udelay(10); + if (!saw_done_clear && + !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + saw_done_clear = 1; + else if (saw_done_clear && + (tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + break; + } + if (i >= 1000) { + tw32(NVRAM_SWARB, SWARB_REQ_CLR1); + return -EBUSY; + } + + *val = swab32(tr32(NVRAM_RDDATA)); + tw32(NVRAM_SWARB, 0x20); + + return 0; +} + +struct subsys_tbl_ent { + u16 subsys_vendor, subsys_devid; + u32 phy_id; +}; + +static struct subsys_tbl_ent subsys_id_to_phy_id[] = { + /* Broadcom boards. */ + { 0x14e4, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */ + { 0x14e4, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */ + { 0x14e4, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */ + { 0x14e4, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */ + { 0x14e4, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */ + { 0x14e4, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */ + { 0x14e4, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */ + { 0x14e4, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ + { 0x14e4, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ + { 0x14e4, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */ + { 0x14e4, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */ + + /* 3com boards. */ + { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */ + { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */ + /* { PCI_VENDOR_ID_3COM, 0x1002, PHY_ID_XXX }, 3C996CT */ + /* { PCI_VENDOR_ID_3COM, 0x1003, PHY_ID_XXX }, 3C997T */ + { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */ + /* { PCI_VENDOR_ID_3COM, 0x1005, PHY_ID_XXX }, 3C997SZ */ + { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */ + { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */ + + /* DELL boards. */ + { PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */ + { PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */ + { PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */ + { PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */ + + /* Compaq boards. */ + { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */ + { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */ + { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */ + { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */ + { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 } /* NC7780_2 */ +}; + +static int __devinit tg3_phy_probe(struct tg3 *tp) +{ + u32 eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; + u32 hw_phy_id, hw_phy_id_masked; + enum phy_led_mode eeprom_led_mode; + u32 val; + int i, eeprom_signature_found, err; + + tp->phy_id = PHY_ID_INVALID; + for (i = 0; i < ARRAY_SIZE(subsys_id_to_phy_id); i++) { + if ((subsys_id_to_phy_id[i].subsys_vendor == + tp->pdev->subsystem_vendor) && + (subsys_id_to_phy_id[i].subsys_devid == + tp->pdev->subsystem_device)) { + tp->phy_id = subsys_id_to_phy_id[i].phy_id; + break; + } + } + + eeprom_phy_id = PHY_ID_INVALID; + eeprom_led_mode = led_mode_auto; + eeprom_signature_found = 0; + tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); + if (val == NIC_SRAM_DATA_SIG_MAGIC) { + u32 nic_cfg; + + tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); + + eeprom_signature_found = 1; + + if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == + NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) { + eeprom_phy_id = PHY_ID_SERDES; + } else { + u32 nic_phy_id; + + tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &nic_phy_id); + if (nic_phy_id != 0) { + u32 id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; + u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; + + eeprom_phy_id = (id1 >> 16) << 10; + eeprom_phy_id |= (id2 & 0xfc00) << 16; + eeprom_phy_id |= (id2 & 0x03ff) << 0; + } + } + + switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) { + case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD: + eeprom_led_mode = led_mode_three_link; + break; + + case NIC_SRAM_DATA_CFG_LED_LINK_SPD: + eeprom_led_mode = led_mode_link10; + break; + + default: + eeprom_led_mode = led_mode_auto; + break; + }; + } + + err = tg3_phy_reset(tp, 0); + if (err) + return err; + + /* Now read the physical PHY_ID from the chip and verify + * that it is sane. If it doesn't look good, we fall back + * to either the hard-coded table based PHY_ID and failing + * that the value found in the eeprom area. + */ + err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); + err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); + + hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; + hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; + hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; + + hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; + + if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { + tp->phy_id = hw_phy_id; + } else { + /* phy_id currently holds the value found in the + * subsys_id_to_phy_id[] table or PHY_ID_INVALID + * if a match was not found there. + */ + if (tp->phy_id == PHY_ID_INVALID) { + if (!eeprom_signature_found || + !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK)) + return -ENODEV; + tp->phy_id = eeprom_phy_id; + } + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x2aaa); + } + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + tp->tg3_flags |= TG3_FLAG_PHY_RESET_ON_INIT; + + if (tp->tg3_flags & TG3_FLAG_PHY_RESET_ON_INIT) { + u32 mii_tg3_ctrl; + + err = tg3_phy_reset(tp, 1); + if (err) + return err; + + /* These chips, when reset, only advertise 10Mb capabilities. + * Fix that. + */ + err = tg3_writephy(tp, MII_ADVERTISE, + (ADVERTISE_CSMA | + ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL)); + mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL | + MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + mii_tg3_ctrl = 0; + + err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl); + err |= tg3_writephy(tp, MII_BMCR, + (BMCR_ANRESTART | BMCR_ANENABLE)); + } + + if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { + err = tg3_init_5401phy_dsp(tp); + } + + /* Determine the PHY led mode. */ + if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL) { + tp->led_mode = led_mode_link10; + } else { + tp->led_mode = led_mode_three_link; + if (eeprom_signature_found && + eeprom_led_mode != led_mode_auto) + tp->led_mode = eeprom_led_mode; + } + + if (tp->phy_id == PHY_ID_SERDES) + tp->link_config.advertising = + (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | + ADVERTISED_FIBRE); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + tp->link_config.advertising &= + ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full); + + return err; +} + +static void __devinit tg3_read_partno(struct tg3 *tp) +{ + unsigned char vpd_data[256]; + int i; + + for (i = 0; i < 256; i += 4) { + u32 tmp; + + if (tg3_nvram_read(tp, 0x100 + i, &tmp)) + goto out_not_found; + + vpd_data[i + 0] = ((tmp >> 0) & 0xff); + vpd_data[i + 1] = ((tmp >> 8) & 0xff); + vpd_data[i + 2] = ((tmp >> 16) & 0xff); + vpd_data[i + 3] = ((tmp >> 24) & 0xff); + } + + /* Now parse and find the part number. */ + for (i = 0; i < 256; ) { + unsigned char val = vpd_data[i]; + int block_end; + + if (val == 0x82 || val == 0x91) { + i = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + continue; + } + + if (val != 0x90) + goto out_not_found; + + block_end = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + i += 3; + while (i < block_end) { + if (vpd_data[i + 0] == 'P' && + vpd_data[i + 1] == 'N') { + int partno_len = vpd_data[i + 2]; + + if (partno_len > 24) + goto out_not_found; + + memcpy(tp->board_part_number, + &vpd_data[i + 3], + partno_len); + + /* Success. */ + return; + } + } + + /* Part number not found. */ + goto out_not_found; + } + +out_not_found: + strcpy(tp->board_part_number, "none"); +} + +static int __devinit tg3_get_invariants(struct tg3 *tp) +{ + u32 misc_ctrl_reg; + u32 cacheline_sz_reg; + u32 pci_state_reg, grc_misc_cfg; + u16 pci_cmd; + int err; + + /* Force memory write invalidate off. If we leave it on, + * then on 5700_BX chips we have to enable a workaround. + * The workaround is to set the TG3PCI_DMA_RW_CTRL boundry + * to match the cacheline size. The Broadcom driver have this + * workaround but turns MWI off all the times so never uses + * it. This seems to suggest that the workaround is insufficient. + */ + pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); + pci_cmd &= ~PCI_COMMAND_INVALIDATE; + pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); + + pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + &misc_ctrl_reg); + + tp->pci_chip_rev_id = (misc_ctrl_reg >> + MISC_HOST_CTRL_CHIPREV_SHIFT); + + pci_read_config_dword(tp->pdev, TG3PCI_CACHELINESZ, + &cacheline_sz_reg); + + tp->pci_cacheline_sz = (cacheline_sz_reg >> 24) & 0xff; + tp->pci_lat_timer = (cacheline_sz_reg >> 16) & 0xff; + tp->pci_hdr_type = (cacheline_sz_reg >> 8) & 0xff; + tp->pci_bist = (cacheline_sz_reg >> 0) & 0xff; + + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, + &pci_state_reg); + + if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) { + tp->tg3_flags |= TG3_FLAG_PCIX_MODE; + + /* If this is a 5700 BX chipset, and we are in PCI-X + * mode, enable register write workaround. + * + * The workaround is to use indirect register accesses + * for all chip writes not to mailbox registers. + */ + if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) { + u32 pm_reg; + u16 pci_cmd; + + tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; + + /* The chip can have it's power management PCI config + * space registers clobbered due to this bug. + * So explicitly force the chip into D0 here. + */ + pci_read_config_dword(tp->pdev, TG3PCI_PM_CTRL_STAT, + &pm_reg); + pm_reg &= ~PCI_PM_CTRL_STATE_MASK; + pm_reg |= PCI_PM_CTRL_PME_ENABLE | 0 /* D0 */; + pci_write_config_dword(tp->pdev, TG3PCI_PM_CTRL_STAT, + pm_reg); + + /* Also, force SERR#/PERR# in PCI command. */ + pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); + pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); + } + } + if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED; + if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_32BIT; + + /* Force the chip into D0. */ + err = tg3_set_power_state(tp, 0); + if (err) + return err; + + /* 5700 B0 chips do not support checksumming correctly due + * to hardware bugs. + */ + if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0) + tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS; + + /* Regardless of whether checksums work or not, we configure + * the StrongARM chips to not compute the pseudo header checksums + * in either direction. Because of the way Linux checksum support + * works we do not need the chips to do this, and taking the load + * off of the TX/RX onboard StrongARM cpus means that they will not be + * the bottleneck. Whoever wrote Broadcom's driver did not + * understand the situation at all. He could have bothered + * to read Jes's Acenic driver because the logic (and this part of + * the Tigon2 hardware/firmware) is pretty much identical. + */ + tp->tg3_flags |= TG3_FLAG_NO_TX_PSEUDO_CSUM; + tp->tg3_flags |= TG3_FLAG_NO_RX_PSEUDO_CSUM; + + /* Derive initial jumbo mode from MTU assigned in + * ether_setup() via the alloc_etherdev() call + */ + if (tp->dev->mtu > ETH_DATA_LEN) + tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; + + /* Determine WakeOnLan speed to use. */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B2) { + tp->tg3_flags &= ~(TG3_FLAG_WOL_SPEED_100MB); + } else { + tp->tg3_flags |= TG3_FLAG_WOL_SPEED_100MB; + } + + /* Only 5701 and later support tagged irq status mode. */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700) { + tp->tg3_flags |= TG3_FLAG_TAGGED_IRQ_STATUS; + tp->misc_host_ctrl |= MISC_HOST_CTRL_TAGGED_STATUS; + + /* ??? Due to a glitch Broadcom's driver ALWAYS sets + * ??? these bits in coalesce_mode. Because MM_GetConfig + * ??? always sets pDevice->UseTaggedStatus correctly + * ??? the following test at tigon3.c:LM_GetAdapterInfo() + * ??? + * ??? pDevice->UseTaggedStatus && + * ??? (pDevice->ChipRevId == T3_CHIP_ID_5700_C0 || + * ??? T3_CHIP_REV(pDevice->ChipRevId) == T3_CHIP_REV_5700_AX || + * ??? T3_CHIP_REV(pDevice->ChipRevId) == T3_CHIP_REV_5700_BX) + * ??? + * ??? will never pass and thus pDevice->CoalesceMode will never + * ??? get set to zero. For now I'll mirror what I believe is + * ??? the intention of their driver. + * ??? + * ??? Update: This is fixed in Broadcom's 2.2.3 and later + * ??? drivers. All the current 2.0.x drivers still + * ??? have the bug. + */ + tp->coalesce_mode = (HOSTCC_MODE_CLRTICK_RXBD | + HOSTCC_MODE_CLRTICK_TXBD); + } else { + tp->coalesce_mode = 0; + + /* If not using tagged status, set the *_during_int + * coalesce default config values to zero. + */ + tp->coalesce_config.rx_coalesce_ticks_during_int = 0; + tp->coalesce_config.rx_max_coalesced_frames_during_int = 0; + tp->coalesce_config.tx_coalesce_ticks_during_int = 0; + tp->coalesce_config.tx_max_coalesced_frames_during_int = 0; + } + + if (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_AX && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) + tp->coalesce_mode |= HOSTCC_MODE_32BYTE; + + /* Initialize misc host control in PCI block. */ + tp->misc_host_ctrl |= (misc_ctrl_reg & + MISC_HOST_CTRL_CHIPREV); + pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + /* Initialize MAC MI mode, polling disabled. */ + tw32(MAC_MI_MODE, tp->mi_mode); + udelay(40); + + /* Initialize data/descriptor byte/word swapping. */ + tw32(GRC_MODE, tp->grc_mode); + + /* Clear these out for sanity. */ + tw32(TG3PCI_CLOCK_CTRL, 0); + tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); + + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, + &pci_state_reg); + if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0 && + (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) == 0) { + u32 chiprevid = GET_CHIP_REV_ID(tp->misc_host_ctrl); + + if (chiprevid == CHIPREV_ID_5701_A0 || + chiprevid == CHIPREV_ID_5701_B0 || + chiprevid == CHIPREV_ID_5701_B2 || + chiprevid == CHIPREV_ID_5701_B5) { + unsigned long sram_base; + + /* Write some dummy words into the SRAM status block + * area, see if it reads back correctly. If the return + * value is bad, force enable the PCIX workaround. + */ + sram_base = tp->regs + NIC_SRAM_WIN_BASE + NIC_SRAM_STATS_BLK; + + writel(0x00000000, sram_base); + writel(0x00000000, sram_base + 4); + writel(0xffffffff, sram_base + 4); + if (readl(sram_base) != 0x00000000) + tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG; + } + } + + udelay(50); + tg3_nvram_init(tp); + + /* Determine if TX descriptors will reside in + * main memory or in the chip SRAM. + */ + if (tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) + tp->tg3_flags |= TG3_FLAG_HOST_TXDS; + + /* Quick sanity check. Make sure we see an expected + * value here. + */ + grc_misc_cfg = tr32(GRC_MISC_CFG); + grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; + if (grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5700 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5701 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5702FE && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5703 && + grc_misc_cfg != GRC_MISC_CFG_BOARD_ID_5703S) + return -ENODEV; + + /* ROFL, you should see Broadcom's driver code implementing + * this, stuff like "if (a || b)" where a and b are always + * mutually exclusive. DaveM finds like 6 bugs today, hello! + */ + if (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5702FE) + tp->tg3_flags |= TG3_FLAG_10_100_ONLY; + + err = tg3_phy_probe(tp); + + tg3_read_partno(tp); + + if (tp->phy_id == PHY_ID_SERDES) { + tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; + + /* And override led_mode in case Dell ever makes + * a fibre board. + */ + tp->led_mode = led_mode_three_link; + } else { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) + tp->tg3_flags |= TG3_FLAG_USE_MI_INTERRUPT; + else + tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; + } + + /* 5700 {AX,BX} chips have a broken status block link + * change bit implementation, so we must use the + * status register in those cases. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) + tp->tg3_flags |= TG3_FLAG_USE_LINKCHG_REG; + else + tp->tg3_flags &= ~TG3_FLAG_USE_LINKCHG_REG; + + /* The led_mode is set during tg3_phy_probe, here we might + * have to force the link status polling mechanism based + * upon subsystem IDs. + */ + if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL && + tp->phy_id != PHY_ID_SERDES) { + tp->tg3_flags |= (TG3_FLAG_USE_MI_INTERRUPT | + TG3_FLAG_USE_LINKCHG_REG); + } + + /* 5700 BX chips need to have their TX producer index mailboxes + * written twice to workaround a bug. + */ + if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) + tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG; + else + tp->tg3_flags &= ~TG3_FLAG_TXD_MBOX_HWBUG; + + /* 5700 chips can get confused if TX buffers straddle the + * 4GB address boundary in some cases. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { + /* ROFL! Latest Broadcom driver disables NETIF_F_HIGHDMA + * in this case instead of fixing their workaround code. + * + * Like, hey, there is this skb_copy() thing guys, + * use it. Oh I can't stop laughing... + */ + tp->dev->hard_start_xmit = tg3_start_xmit_4gbug; + } else { + tp->dev->hard_start_xmit = tg3_start_xmit; + } + + tp->rx_offset = 2; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 && + (tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) + tp->rx_offset = 0; + + return err; +} + +static int __devinit tg3_get_device_address(struct tg3 *tp) +{ + struct net_device *dev = tp->dev; + u32 hi, lo; + + /* First try to get it from MAC address mailbox. */ + tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); + if ((hi >> 16) == 0x484b) { + dev->dev_addr[0] = (hi >> 8) & 0xff; + dev->dev_addr[1] = (hi >> 0) & 0xff; + + tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo); + dev->dev_addr[2] = (lo >> 24) & 0xff; + dev->dev_addr[3] = (lo >> 16) & 0xff; + dev->dev_addr[4] = (lo >> 8) & 0xff; + dev->dev_addr[5] = (lo >> 0) & 0xff; + } + /* Next, try NVRAM. */ + else if (!tg3_nvram_read(tp, 0x7c, &hi) && + !tg3_nvram_read(tp, 0x80, &lo)) { + dev->dev_addr[0] = ((hi >> 16) & 0xff); + dev->dev_addr[1] = ((hi >> 24) & 0xff); + dev->dev_addr[2] = ((lo >> 0) & 0xff); + dev->dev_addr[3] = ((lo >> 8) & 0xff); + dev->dev_addr[4] = ((lo >> 16) & 0xff); + dev->dev_addr[5] = ((lo >> 24) & 0xff); + } + /* Finally just fetch it out of the MAC control regs. */ + else { + hi = tr32(MAC_ADDR_0_HIGH); + lo = tr32(MAC_ADDR_0_LOW); + + dev->dev_addr[5] = lo & 0xff; + dev->dev_addr[4] = (lo >> 8) & 0xff; + dev->dev_addr[3] = (lo >> 16) & 0xff; + dev->dev_addr[2] = (lo >> 24) & 0xff; + dev->dev_addr[1] = hi & 0xff; + dev->dev_addr[0] = (hi >> 8) & 0xff; + } + + if (!is_valid_ether_addr(&dev->dev_addr[0])) + return -EINVAL; + + return 0; +} + +static int __devinit tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma, int size, int to_device) +{ + struct tg3_internal_buffer_desc test_desc; + u32 sram_dma_descs; + int i, ret; + + sram_dma_descs = NIC_SRAM_DMA_DESC_POOL_BASE; + + tw32(FTQ_RCVBD_COMP_FIFO_ENQDEQ, 0); + tw32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ, 0); + tw32(RDMAC_STATUS, 0); + tw32(WDMAC_STATUS, 0); + + tw32(BUFMGR_MODE, 0); + tw32(FTQ_RESET, 0); + + /* pci_alloc_consistent gives only non-DAC addresses */ + test_desc.addr_hi = 0; + test_desc.addr_lo = buf_dma & 0xffffffff; + test_desc.nic_mbuf = 0x00002100; + test_desc.len = size; + if (to_device) { + test_desc.cqid_sqid = (13 << 8) | 2; + tw32(RDMAC_MODE, RDMAC_MODE_RESET); + tw32(RDMAC_MODE, RDMAC_MODE_ENABLE); + } else { + test_desc.cqid_sqid = (16 << 8) | 7; + tw32(WDMAC_MODE, WDMAC_MODE_RESET); + tw32(WDMAC_MODE, WDMAC_MODE_ENABLE); + } + test_desc.flags = 0x00000004; + + for (i = 0; i < (sizeof(test_desc) / sizeof(u32)); i++) { + u32 val; + + val = *(((u32 *)&test_desc) + i); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, + sram_dma_descs + (i * sizeof(u32))); + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); + } + pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + + if (to_device) { + tw32(FTQ_DMA_HIGH_READ_FIFO_ENQDEQ, sram_dma_descs); + } else { + tw32(FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ, sram_dma_descs); + } + + ret = -ENODEV; + for (i = 0; i < 40; i++) { + u32 val; + + if (to_device) + val = tr32(FTQ_RCVBD_COMP_FIFO_ENQDEQ); + else + val = tr32(FTQ_RCVDATA_COMP_FIFO_ENQDEQ); + if ((val & 0xffff) == sram_dma_descs) { + ret = 0; + break; + } + + udelay(100); + } + + return ret; +} + +#define TEST_BUFFER_SIZE 0x400 + +static int __devinit tg3_test_dma(struct tg3 *tp) +{ + dma_addr_t buf_dma; + u32 *buf; + int ret; + + buf = pci_alloc_consistent(tp->pdev, TEST_BUFFER_SIZE, &buf_dma); + if (!buf) { + ret = -ENOMEM; + goto out_nofree; + } + + tw32(TG3PCI_CLOCK_CTRL, 0); + + if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) { + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + } else { + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + + /* Wheee, some more chip bugs... */ + if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1 || + tp->pci_chip_rev_id == CHIPREV_ID_5703_A2) + tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; + } + + /* We don't do this on x86 because it seems to hurt performace. + * It does help things on other platforms though. + */ +#ifndef CONFIG_X86 + { + u8 byte; + int cacheline_size; + pci_read_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE, &byte); + + if (byte == 0) + cacheline_size = 1024; + else + cacheline_size = (int) byte * 4; + + tp->dma_rwctrl &= ~(DMA_RWCTRL_READ_BNDRY_MASK | + DMA_RWCTRL_WRITE_BNDRY_MASK); + + switch (cacheline_size) { + case 16: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_16 | + DMA_RWCTRL_WRITE_BNDRY_16); + break; + + case 32: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_32 | + DMA_RWCTRL_WRITE_BNDRY_32); + break; + + case 64: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_64 | + DMA_RWCTRL_WRITE_BNDRY_64); + break; + + case 128: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_128 | + DMA_RWCTRL_WRITE_BNDRY_128); + break; + + case 256: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_256 | + DMA_RWCTRL_WRITE_BNDRY_256); + break; + + case 512: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_512 | + DMA_RWCTRL_WRITE_BNDRY_512); + break; + + case 1024: + tp->dma_rwctrl |= + (DMA_RWCTRL_READ_BNDRY_1024 | + DMA_RWCTRL_WRITE_BNDRY_1024); + break; + }; + } +#endif + + /* Remove this if it causes problems for some boards. */ + tp->dma_rwctrl |= DMA_RWCTRL_USE_MEM_READ_MULT; + + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + + ret = 0; + while (1) { + u32 *p, i; + + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) + p[i] = i; + + /* Send the buffer to the chip. */ + ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 1); + if (ret) + break; + + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) + p[i] = 0; + + /* Now read it back. */ + ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, 0); + if (ret) + break; + + /* Verify it. */ + p = buf; + for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { + if (p[i] == i) + continue; + + if ((tp->dma_rwctrl & DMA_RWCTRL_WRITE_BNDRY_MASK) == + DMA_RWCTRL_WRITE_BNDRY_DISAB) { + tp->dma_rwctrl |= DMA_RWCTRL_WRITE_BNDRY_16; + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + break; + } else { + ret = -ENODEV; + goto out; + } + } + + if (i == (TEST_BUFFER_SIZE / sizeof(u32))) { + /* Success. */ + ret = 0; + break; + } + } + +out: + pci_free_consistent(tp->pdev, TEST_BUFFER_SIZE, buf, buf_dma); +out_nofree: + return ret; +} + +static void __devinit tg3_init_link_config(struct tg3 *tp) +{ + tp->link_config.advertising = + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_MII); + tp->link_config.speed = SPEED_INVALID; + tp->link_config.duplex = DUPLEX_INVALID; + tp->link_config.autoneg = AUTONEG_ENABLE; + netif_carrier_off(tp->dev); + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; + tp->link_config.phy_is_low_power = 0; + tp->link_config.orig_speed = SPEED_INVALID; + tp->link_config.orig_duplex = DUPLEX_INVALID; + tp->link_config.orig_autoneg = AUTONEG_INVALID; +} + +static void __devinit tg3_init_coalesce_config(struct tg3 *tp) +{ + tp->coalesce_config.rx_coalesce_ticks = DEFAULT_RXCOL_TICKS; + tp->coalesce_config.rx_max_coalesced_frames = DEFAULT_RXMAX_FRAMES; + tp->coalesce_config.rx_coalesce_ticks_during_int = + DEFAULT_RXCOAL_TICK_INT; + tp->coalesce_config.rx_max_coalesced_frames_during_int = + DEFAULT_RXCOAL_MAXF_INT; + tp->coalesce_config.tx_coalesce_ticks = DEFAULT_TXCOL_TICKS; + tp->coalesce_config.tx_max_coalesced_frames = DEFAULT_TXMAX_FRAMES; + tp->coalesce_config.tx_coalesce_ticks_during_int = + DEFAULT_TXCOAL_TICK_INT; + tp->coalesce_config.tx_max_coalesced_frames_during_int = + DEFAULT_TXCOAL_MAXF_INT; + tp->coalesce_config.stats_coalesce_ticks = + DEFAULT_STAT_COAL_TICKS; +} + +static void __devinit tg3_init_bufmgr_config(struct tg3 *tp) +{ + tp->bufmgr_config.mbuf_read_dma_low_water = + DEFAULT_MB_RDMA_LOW_WATER; + tp->bufmgr_config.mbuf_mac_rx_low_water = + DEFAULT_MB_MACRX_LOW_WATER; + tp->bufmgr_config.mbuf_high_water = + DEFAULT_MB_HIGH_WATER; + + tp->bufmgr_config.mbuf_read_dma_low_water_jumbo = + DEFAULT_MB_RDMA_LOW_WATER_JUMBO; + tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo = + DEFAULT_MB_MACRX_LOW_WATER_JUMBO; + tp->bufmgr_config.mbuf_high_water_jumbo = + DEFAULT_MB_HIGH_WATER_JUMBO; + + tp->bufmgr_config.dma_low_water = DEFAULT_DMA_LOW_WATER; + tp->bufmgr_config.dma_high_water = DEFAULT_DMA_HIGH_WATER; +} + +static char * __devinit tg3_phy_string(struct tg3 *tp) +{ + switch (tp->phy_id & PHY_ID_MASK) { + case PHY_ID_BCM5400: return "5400"; + case PHY_ID_BCM5401: return "5401"; + case PHY_ID_BCM5411: return "5411"; + case PHY_ID_BCM5701: return "5701"; + case PHY_ID_BCM5703: return "5703"; + case PHY_ID_BCM8002: return "8002"; + case PHY_ID_SERDES: return "serdes"; + default: return "unknown"; + }; +} + +static int __devinit tg3_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int tg3_version_printed = 0; + unsigned long tg3reg_base, tg3reg_len; + struct net_device *dev; + struct tg3 *tp; + int i, err, pci_using_dac, pm_cap; + + if (tg3_version_printed++ == 0) + printk(KERN_INFO "%s", version); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device, " + "aborting.\n"); + return err; + } + + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + printk(KERN_ERR PFX "Cannot find proper PCI device " + "base address, aborting.\n"); + err = -ENODEV; + goto err_out_disable_pdev; + } + + err = pci_request_regions(pdev, DRV_MODULE_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources, " + "aborting.\n"); + goto err_out_disable_pdev; + } + + pci_set_master(pdev); + + /* Find power-management capability. */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap == 0) { + printk(KERN_ERR PFX "Cannot find PowerManagement capability, " + "aborting.\n"); + goto err_out_free_res; + } + + /* Configure DMA attributes. */ + if (!pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) { + pci_using_dac = 1; + } else { + err = pci_set_dma_mask(pdev, (u64) 0xffffffff); + if (err) { + printk(KERN_ERR PFX "No usable DMA configuration, " + "aborting.\n"); + goto err_out_free_res; + } + pci_using_dac = 0; + } + + tg3reg_base = pci_resource_start(pdev, 0); + tg3reg_len = pci_resource_len(pdev, 0); + + dev = alloc_etherdev(sizeof(*tp)); + if (!dev) { + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + err = -ENOMEM; + goto err_out_free_res; + } + + SET_MODULE_OWNER(dev); + + if (pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; +#if TG3_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = tg3_vlan_rx_register; + dev->vlan_rx_kill_vid = tg3_vlan_rx_kill_vid; +#endif + + tp = dev->priv; + tp->pdev = pdev; + tp->dev = dev; + tp->pm_cap = pm_cap; + tp->mac_mode = TG3_DEF_MAC_MODE; + tp->rx_mode = TG3_DEF_RX_MODE; + tp->tx_mode = TG3_DEF_TX_MODE; + tp->mi_mode = MAC_MI_MODE_BASE; + if (tg3_debug > 0) + tp->msg_enable = tg3_debug; + else + tp->msg_enable = TG3_DEF_MSG_ENABLE; + + /* The word/byte swap controls here control register access byte + * swapping. DMA data byte swapping is controlled in the GRC_MODE + * setting below. + */ + tp->misc_host_ctrl = + MISC_HOST_CTRL_MASK_PCI_INT | + MISC_HOST_CTRL_WORD_SWAP | + MISC_HOST_CTRL_INDIR_ACCESS | + MISC_HOST_CTRL_PCISTATE_RW; + + /* The NONFRM (non-frame) byte/word swap controls take effect + * on descriptor entries, anything which isn't packet data. + * + * The StrongARM chips on the board (one for tx, one for rx) + * are running in big-endian mode. + */ + tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA | + GRC_MODE_WSWAP_NONFRM_DATA); +#ifdef __BIG_ENDIAN + tp->grc_mode |= GRC_MODE_BSWAP_NONFRM_DATA; +#endif + spin_lock_init(&tp->lock); + spin_lock_init(&tp->indirect_lock); + + tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len); + if (tp->regs == 0UL) { + printk(KERN_ERR PFX "Cannot map device registers, " + "aborting.\n"); + err = -ENOMEM; + goto err_out_free_dev; + } + + tg3_init_link_config(tp); + + tg3_init_coalesce_config(tp); + + tg3_init_bufmgr_config(tp); + + dev->open = tg3_open; + dev->stop = tg3_close; + dev->get_stats = tg3_get_stats; + dev->set_multicast_list = tg3_set_rx_mode; + dev->set_mac_address = tg3_set_mac_addr; + dev->do_ioctl = tg3_ioctl; + dev->tx_timeout = tg3_tx_timeout; + dev->watchdog_timeo = TG3_TX_TIMEOUT; + dev->change_mtu = tg3_change_mtu; + dev->irq = pdev->irq; + + err = tg3_get_invariants(tp); + if (err) { + printk(KERN_ERR PFX "Problem fetching invariants of chip, " + "aborting.\n"); + goto err_out_iounmap; + } + + err = tg3_get_device_address(tp); + if (err) { + printk(KERN_ERR PFX "Could not obtain valid ethernet address, " + "aborting.\n"); + goto err_out_iounmap; + } + + err = tg3_test_dma(tp); + if (err) { + printk(KERN_ERR PFX "DMA engine test failed, aborting.\n"); + goto err_out_iounmap; + } + + /* Tigon3 can do ipv4 only... and some chips have buggy + * checksumming. + */ + if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + + err = register_netdev(dev); + if (err) { + printk(KERN_ERR PFX "Cannot register net device, " + "aborting.\n"); + goto err_out_iounmap; + } + + pci_set_drvdata(pdev, dev); + + /* Now that we have fully setup the chip, save away a snapshot + * of the PCI config space. We need to restore this after + * GRC_MISC_CFG core clock resets and some resume events. + */ + pci_save_state(tp->pdev, tp->pci_cfg_state); + + printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] (PCI%s:%s:%s) %sBaseT Ethernet ", + dev->name, + tp->board_part_number, + tp->pci_chip_rev_id, + tg3_phy_string(tp), + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "X" : ""), + ((tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) ? + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "133MHz" : "66MHz") : + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "100MHz" : "33MHz")), + ((tp->tg3_flags & TG3_FLAG_PCI_32BIT) ? "32-bit" : "64-bit"), + (tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100" : "10/100/1000"); + + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], + i == 5 ? '\n' : ':'); + + return 0; + +err_out_iounmap: + iounmap((void *) tp->regs); + +err_out_free_dev: + kfree(dev); + +err_out_free_res: + pci_release_regions(pdev); + +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void __devexit tg3_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + unregister_netdev(dev); + iounmap((void *) ((struct tg3 *)(dev->priv))->regs); + kfree(dev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + } +} + +static int tg3_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tg3 *tp = dev->priv; + int err; + + if (!netif_running(dev)) + return 0; + + spin_lock_irq(&tp->lock); + tg3_disable_ints(tp); + spin_unlock_irq(&tp->lock); + + netif_device_detach(dev); + + spin_lock_irq(&tp->lock); + tg3_halt(tp); + spin_unlock_irq(&tp->lock); + + err = tg3_set_power_state(tp, state); + if (err) { + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + tg3_init_hw(tp); + + spin_unlock_irq(&tp->lock); + + netif_device_attach(dev); + } + + return err; +} + +static int tg3_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tg3 *tp = dev->priv; + int err; + + if (!netif_running(dev)) + return 0; + + err = tg3_set_power_state(tp, 0); + if (err) + return err; + + netif_device_attach(dev); + + spin_lock_irq(&tp->lock); + + tg3_init_rings(tp); + tg3_init_hw(tp); + tg3_enable_ints(tp); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +static struct pci_driver tg3_driver = { + name: DRV_MODULE_NAME, + id_table: tg3_pci_tbl, + probe: tg3_init_one, + remove: __devexit_p(tg3_remove_one), + suspend: tg3_suspend, + resume: tg3_resume +}; + +static int __init tg3_init(void) +{ + return pci_module_init(&tg3_driver); +} + +static void __exit tg3_cleanup(void) +{ + pci_unregister_driver(&tg3_driver); +} + +module_init(tg3_init); +module_exit(tg3_cleanup); diff -urN linux-2.5.6-pre3/drivers/net/tg3.h linux-2.5.6/drivers/net/tg3.h --- linux-2.5.6-pre3/drivers/net/tg3.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tg3.h Thu Mar 7 18:24:48 2002 @@ -0,0 +1,1851 @@ +/* $Id: tg3.h,v 1.37.2.30 2002/03/05 10:08:39 davem Exp $ + * tg3.h: Definitions for Broadcom Tigon3 ethernet driver. + * + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001 Jeff Garzik (jgarzik@mandrakesoft.com) + */ + +#ifndef _T3_H +#define _T3_H + +#define TG3_64BIT_REG_HIGH 0x00UL +#define TG3_64BIT_REG_LOW 0x04UL + +/* Descriptor block info. */ +#define TG3_BDINFO_HOST_ADDR 0x0UL /* 64-bit */ +#define TG3_BDINFO_MAXLEN_FLAGS 0x8UL /* 32-bit */ +#define BDINFO_FLAGS_USE_EXT_RECV 0x00000001 /* ext rx_buffer_desc */ +#define BDINFO_FLAGS_DISABLED 0x00000002 +#define BDINFO_FLAGS_MAXLEN_MASK 0xffff0000 +#define BDINFO_FLAGS_MAXLEN_SHIFT 16 +#define TG3_BDINFO_NIC_ADDR 0xcUL /* 32-bit */ +#define TG3_BDINFO_SIZE 0x10UL + +#define RX_COPY_THRESHOLD 256 + +#define RX_STD_MAX_SIZE 1536 +#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */ +#if TG3_MINI_RING_WORKS +#define RX_MINI_MAX_SIZE 256 +#endif + +/* First 256 bytes are a mirror of PCI config space. */ +#define TG3PCI_VENDOR 0x00000000 +#define TG3PCI_VENDOR_BROADCOM 0x14e4 +#define TG3PCI_DEVICE 0x00000002 +#define TG3PCI_DEVICE_TIGON3_1 0x1644 /* BCM5700 */ +#define TG3PCI_DEVICE_TIGON3_2 0x1645 /* BCM5701 */ +#define TG3PCI_DEVICE_TIGON3_3 0x1646 /* BCM5702 */ +#define TG3PCI_DEVICE_TIGON3_4 0x1647 /* BCM5703 */ +#define TG3PCI_COMMAND 0x00000004 +#define TG3PCI_STATUS 0x00000006 +#define TG3PCI_CCREVID 0x00000008 +#define TG3PCI_CACHELINESZ 0x0000000c +#define TG3PCI_LATTIMER 0x0000000d +#define TG3PCI_HEADERTYPE 0x0000000e +#define TG3PCI_BIST 0x0000000f +#define TG3PCI_BASE0_LOW 0x00000010 +#define TG3PCI_BASE0_HIGH 0x00000014 +/* 0x18 --> 0x2c unused */ +#define TG3PCI_SUBSYSVENID 0x0000002c +#define TG3PCI_SUBSYSID 0x0000002e +#define TG3PCI_ROMADDR 0x00000030 +#define TG3PCI_CAPLIST 0x00000034 +/* 0x35 --> 0x3c unused */ +#define TG3PCI_IRQ_LINE 0x0000003c +#define TG3PCI_IRQ_PIN 0x0000003d +#define TG3PCI_MIN_GNT 0x0000003e +#define TG3PCI_MAX_LAT 0x0000003f +#define TG3PCI_X_CAPS 0x00000040 +#define PCIX_CAPS_RELAXED_ORDERING 0x00020000 +#define TG3PCI_PM_CAP_PTR 0x00000041 +#define TG3PCI_X_COMMAND 0x00000042 +#define TG3PCI_X_STATUS 0x00000044 +#define TG3PCI_PM_CAP_ID 0x00000048 +#define TG3PCI_VPD_CAP_PTR 0x00000049 +#define TG3PCI_PM_CAPS 0x0000004a +#define TG3PCI_PM_CTRL_STAT 0x0000004c +#define TG3PCI_BR_SUPP_EXT 0x0000004e +#define TG3PCI_PM_DATA 0x0000004f +#define TG3PCI_VPD_CAP_ID 0x00000050 +#define TG3PCI_MSI_CAP_PTR 0x00000051 +#define TG3PCI_VPD_ADDR_FLAG 0x00000052 +#define VPD_ADDR_FLAG_WRITE 0x00008000 +#define TG3PCI_VPD_DATA 0x00000054 +#define TG3PCI_MSI_CAP_ID 0x00000058 +#define TG3PCI_NXT_CAP_PTR 0x00000059 +#define TG3PCI_MSI_CTRL 0x0000005a +#define TG3PCI_MSI_ADDR_LOW 0x0000005c +#define TG3PCI_MSI_ADDR_HIGH 0x00000060 +#define TG3PCI_MSI_DATA 0x00000064 +/* 0x66 --> 0x68 unused */ +#define TG3PCI_MISC_HOST_CTRL 0x00000068 +#define MISC_HOST_CTRL_CLEAR_INT 0x00000001 +#define MISC_HOST_CTRL_MASK_PCI_INT 0x00000002 +#define MISC_HOST_CTRL_BYTE_SWAP 0x00000004 +#define MISC_HOST_CTRL_WORD_SWAP 0x00000008 +#define MISC_HOST_CTRL_PCISTATE_RW 0x00000010 +#define MISC_HOST_CTRL_CLKREG_RW 0x00000020 +#define MISC_HOST_CTRL_REGWORD_SWAP 0x00000040 +#define MISC_HOST_CTRL_INDIR_ACCESS 0x00000080 +#define MISC_HOST_CTRL_IRQ_MASK_MODE 0x00000100 +#define MISC_HOST_CTRL_TAGGED_STATUS 0x00000200 +#define MISC_HOST_CTRL_CHIPREV 0xffff0000 +#define MISC_HOST_CTRL_CHIPREV_SHIFT 16 +#define GET_CHIP_REV_ID(MISC_HOST_CTRL) \ + (((MISC_HOST_CTRL) & MISC_HOST_CTRL_CHIPREV) >> \ + MISC_HOST_CTRL_CHIPREV_SHIFT) +#define CHIPREV_ID_5700_A0 0x7000 +#define CHIPREV_ID_5700_A1 0x7001 +#define CHIPREV_ID_5700_B0 0x7100 +#define CHIPREV_ID_5700_B1 0x7101 +#define CHIPREV_ID_5700_C0 0x7200 +#define CHIPREV_ID_5701_A0 0x0000 +#define CHIPREV_ID_5701_B0 0x0100 +#define CHIPREV_ID_5701_B2 0x0102 +#define CHIPREV_ID_5701_B5 0x0105 +#define CHIPREV_ID_5703_A0 0x1000 +#define CHIPREV_ID_5703_A1 0x1001 +#define CHIPREV_ID_5703_A2 0x1002 +#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) +#define ASIC_REV_5700 0x07 +#define ASIC_REV_5701 0x00 +#define ASIC_REV_5703 0x01 +#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) +#define CHIPREV_5700_AX 0x70 +#define CHIPREV_5700_BX 0x71 +#define CHIPREV_5700_CX 0x72 +#define CHIPREV_5701_AX 0x00 +#define GET_METAL_REV(CHIP_REV_ID) ((CHIP_REV_ID) & 0xff) +#define METAL_REV_A0 0x00 +#define METAL_REV_A1 0x01 +#define METAL_REV_B0 0x00 +#define METAL_REV_B1 0x01 +#define METAL_REV_B2 0x02 +#define TG3PCI_DMA_RW_CTRL 0x0000006c +#define DMA_RWCTRL_MIN_DMA 0x000000ff +#define DMA_RWCTRL_MIN_DMA_SHIFT 0 +#define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700 +#define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000 +#define DMA_RWCTRL_READ_BNDRY_16 0x00000100 +#define DMA_RWCTRL_READ_BNDRY_32 0x00000200 +#define DMA_RWCTRL_READ_BNDRY_64 0x00000300 +#define DMA_RWCTRL_READ_BNDRY_128 0x00000400 +#define DMA_RWCTRL_READ_BNDRY_256 0x00000500 +#define DMA_RWCTRL_READ_BNDRY_512 0x00000600 +#define DMA_RWCTRL_READ_BNDRY_1024 0x00000700 +#define DMA_RWCTRL_WRITE_BNDRY_MASK 0x00003800 +#define DMA_RWCTRL_WRITE_BNDRY_DISAB 0x00000000 +#define DMA_RWCTRL_WRITE_BNDRY_16 0x00000800 +#define DMA_RWCTRL_WRITE_BNDRY_32 0x00001000 +#define DMA_RWCTRL_WRITE_BNDRY_64 0x00001800 +#define DMA_RWCTRL_WRITE_BNDRY_128 0x00002000 +#define DMA_RWCTRL_WRITE_BNDRY_256 0x00002800 +#define DMA_RWCTRL_WRITE_BNDRY_512 0x00003000 +#define DMA_RWCTRL_WRITE_BNDRY_1024 0x00003800 +#define DMA_RWCTRL_ONE_DMA 0x00004000 +#define DMA_RWCTRL_READ_WATER 0x00070000 +#define DMA_RWCTRL_READ_WATER_SHIFT 16 +#define DMA_RWCTRL_WRITE_WATER 0x00380000 +#define DMA_RWCTRL_WRITE_WATER_SHIFT 19 +#define DMA_RWCTRL_USE_MEM_READ_MULT 0x00400000 +#define DMA_RWCTRL_ASSERT_ALL_BE 0x00800000 +#define DMA_RWCTRL_PCI_READ_CMD 0x0f000000 +#define DMA_RWCTRL_PCI_READ_CMD_SHIFT 24 +#define DMA_RWCTRL_PCI_WRITE_CMD 0xf0000000 +#define DMA_RWCTRL_PCI_WRITE_CMD_SHIFT 28 +#define TG3PCI_PCISTATE 0x00000070 +#define PCISTATE_FORCE_RESET 0x00000001 +#define PCISTATE_INT_NOT_ACTIVE 0x00000002 +#define PCISTATE_CONV_PCI_MODE 0x00000004 +#define PCISTATE_BUS_SPEED_HIGH 0x00000008 +#define PCISTATE_BUS_32BIT 0x00000010 +#define PCISTATE_ROM_ENABLE 0x00000020 +#define PCISTATE_ROM_RETRY_ENABLE 0x00000040 +#define PCISTATE_FLAT_VIEW 0x00000100 +#define TG3PCI_CLOCK_CTRL 0x00000074 +#define CLOCK_CTRL_CORECLK_DISABLE 0x00000200 +#define CLOCK_CTRL_RXCLK_DISABLE 0x00000400 +#define CLOCK_CTRL_TXCLK_DISABLE 0x00000800 +#define CLOCK_CTRL_ALTCLK 0x00001000 +#define CLOCK_CTRL_PWRDOWN_PLL133 0x00008000 +#define CLOCK_CTRL_44MHZ_CORE 0x00040000 +#define CLOCK_CTRL_DELAY_PCI_GRANT 0x80000000 +#define TG3PCI_REG_BASE_ADDR 0x00000078 +#define TG3PCI_MEM_WIN_BASE_ADDR 0x0000007c +#define TG3PCI_REG_DATA 0x00000080 +#define TG3PCI_MEM_WIN_DATA 0x00000084 +#define TG3PCI_MODE_CTRL 0x00000088 +#define TG3PCI_MISC_CFG 0x0000008c +#define TG3PCI_MISC_LOCAL_CTRL 0x00000090 +/* 0x94 --> 0x98 unused */ +#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */ +#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */ +#define TG3PCI_SND_PROD_IDX 0x000000a8 /* 64-bit */ +/* 0xb0 --> 0x100 unused */ + +/* 0x100 --> 0x200 unused */ + +/* Mailbox registers */ +#define MAILBOX_INTERRUPT_0 0x00000200 /* 64-bit */ +#define MAILBOX_INTERRUPT_1 0x00000208 /* 64-bit */ +#define MAILBOX_INTERRUPT_2 0x00000210 /* 64-bit */ +#define MAILBOX_INTERRUPT_3 0x00000218 /* 64-bit */ +#define MAILBOX_GENERAL_0 0x00000220 /* 64-bit */ +#define MAILBOX_GENERAL_1 0x00000228 /* 64-bit */ +#define MAILBOX_GENERAL_2 0x00000230 /* 64-bit */ +#define MAILBOX_GENERAL_3 0x00000238 /* 64-bit */ +#define MAILBOX_GENERAL_4 0x00000240 /* 64-bit */ +#define MAILBOX_GENERAL_5 0x00000248 /* 64-bit */ +#define MAILBOX_GENERAL_6 0x00000250 /* 64-bit */ +#define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */ +#define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */ +#define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */ +#define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */ +#define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_2 0x00000290 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_3 0x00000298 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_4 0x000002a0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_5 0x000002a8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_6 0x000002b0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_7 0x000002b8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_8 0x000002c0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_9 0x000002c8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_10 0x000002d0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_11 0x000002d8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_12 0x000002e0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_13 0x000002e8 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_14 0x000002f0 /* 64-bit */ +#define MAILBOX_RCVRET_CON_IDX_15 0x000002f8 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_0 0x00000300 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_1 0x00000308 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_2 0x00000310 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_3 0x00000318 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_4 0x00000320 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_5 0x00000328 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_6 0x00000330 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_7 0x00000338 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_8 0x00000340 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_9 0x00000348 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_10 0x00000350 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_11 0x00000358 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_12 0x00000360 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_13 0x00000368 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_14 0x00000370 /* 64-bit */ +#define MAILBOX_SNDHOST_PROD_IDX_15 0x00000378 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_0 0x00000380 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_1 0x00000388 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_2 0x00000390 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_3 0x00000398 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_4 0x000003a0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_5 0x000003a8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_6 0x000003b0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_7 0x000003b8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_8 0x000003c0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_9 0x000003c8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_10 0x000003d0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_11 0x000003d8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_12 0x000003e0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_13 0x000003e8 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_14 0x000003f0 /* 64-bit */ +#define MAILBOX_SNDNIC_PROD_IDX_15 0x000003f8 /* 64-bit */ + +/* MAC control registers */ +#define MAC_MODE 0x00000400 +#define MAC_MODE_RESET 0x00000001 +#define MAC_MODE_HALF_DUPLEX 0x00000002 +#define MAC_MODE_PORT_MODE_MASK 0x0000000c +#define MAC_MODE_PORT_MODE_TBI 0x0000000c +#define MAC_MODE_PORT_MODE_GMII 0x00000008 +#define MAC_MODE_PORT_MODE_MII 0x00000004 +#define MAC_MODE_PORT_MODE_NONE 0x00000000 +#define MAC_MODE_PORT_INT_LPBACK 0x00000010 +#define MAC_MODE_TAGGED_MAC_CTRL 0x00000080 +#define MAC_MODE_TX_BURSTING 0x00000100 +#define MAC_MODE_MAX_DEFER 0x00000200 +#define MAC_MODE_LINK_POLARITY 0x00000400 +#define MAC_MODE_RXSTAT_ENABLE 0x00000800 +#define MAC_MODE_RXSTAT_CLEAR 0x00001000 +#define MAC_MODE_RXSTAT_FLUSH 0x00002000 +#define MAC_MODE_TXSTAT_ENABLE 0x00004000 +#define MAC_MODE_TXSTAT_CLEAR 0x00008000 +#define MAC_MODE_TXSTAT_FLUSH 0x00010000 +#define MAC_MODE_SEND_CONFIGS 0x00020000 +#define MAC_MODE_MAGIC_PKT_ENABLE 0x00040000 +#define MAC_MODE_ACPI_ENABLE 0x00080000 +#define MAC_MODE_MIP_ENABLE 0x00100000 +#define MAC_MODE_TDE_ENABLE 0x00200000 +#define MAC_MODE_RDE_ENABLE 0x00400000 +#define MAC_MODE_FHDE_ENABLE 0x00800000 +#define MAC_STATUS 0x00000404 +#define MAC_STATUS_PCS_SYNCED 0x00000001 +#define MAC_STATUS_SIGNAL_DET 0x00000002 +#define MAC_STATUS_RCVD_CFG 0x00000004 +#define MAC_STATUS_CFG_CHANGED 0x00000008 +#define MAC_STATUS_SYNC_CHANGED 0x00000010 +#define MAC_STATUS_PORT_DEC_ERR 0x00000400 +#define MAC_STATUS_LNKSTATE_CHANGED 0x00001000 +#define MAC_STATUS_MI_COMPLETION 0x00400000 +#define MAC_STATUS_MI_INTERRUPT 0x00800000 +#define MAC_STATUS_AP_ERROR 0x01000000 +#define MAC_STATUS_ODI_ERROR 0x02000000 +#define MAC_STATUS_RXSTAT_OVERRUN 0x04000000 +#define MAC_STATUS_TXSTAT_OVERRUN 0x08000000 +#define MAC_EVENT 0x00000408 +#define MAC_EVENT_PORT_DECODE_ERR 0x00000400 +#define MAC_EVENT_LNKSTATE_CHANGED 0x00001000 +#define MAC_EVENT_MI_COMPLETION 0x00400000 +#define MAC_EVENT_MI_INTERRUPT 0x00800000 +#define MAC_EVENT_AP_ERROR 0x01000000 +#define MAC_EVENT_ODI_ERROR 0x02000000 +#define MAC_EVENT_RXSTAT_OVERRUN 0x04000000 +#define MAC_EVENT_TXSTAT_OVERRUN 0x08000000 +#define MAC_LED_CTRL 0x0000040c +#define LED_CTRL_LNKLED_OVERRIDE 0x00000001 +#define LED_CTRL_1000MBPS_ON 0x00000002 +#define LED_CTRL_100MBPS_ON 0x00000004 +#define LED_CTRL_10MBPS_ON 0x00000008 +#define LED_CTRL_TRAFFIC_OVERRIDE 0x00000010 +#define LED_CTRL_TRAFFIC_BLINK 0x00000020 +#define LED_CTRL_TRAFFIC_LED 0x00000040 +#define LED_CTRL_1000MBPS_STATUS 0x00000080 +#define LED_CTRL_100MBPS_STATUS 0x00000100 +#define LED_CTRL_10MBPS_STATUS 0x00000200 +#define LED_CTRL_TRAFFIC_STATUS 0x00000400 +#define LED_CTRL_MAC_MODE 0x00000000 +#define LED_CTRL_PHY_MODE_1 0x00000800 +#define LED_CTRL_PHY_MODE_2 0x00001000 +#define LED_CTRL_BLINK_RATE_MASK 0x7ff80000 +#define LED_CTRL_BLINK_RATE_SHIFT 19 +#define LED_CTRL_BLINK_PER_OVERRIDE 0x00080000 +#define LED_CTRL_BLINK_RATE_OVERRIDE 0x80000000 +#define MAC_ADDR_0_HIGH 0x00000410 /* upper 2 bytes */ +#define MAC_ADDR_0_LOW 0x00000414 /* lower 4 bytes */ +#define MAC_ADDR_1_HIGH 0x00000418 /* upper 2 bytes */ +#define MAC_ADDR_1_LOW 0x0000041c /* lower 4 bytes */ +#define MAC_ADDR_2_HIGH 0x00000420 /* upper 2 bytes */ +#define MAC_ADDR_2_LOW 0x00000424 /* lower 4 bytes */ +#define MAC_ADDR_3_HIGH 0x00000428 /* upper 2 bytes */ +#define MAC_ADDR_3_LOW 0x0000042c /* lower 4 bytes */ +#define MAC_ACPI_MBUF_PTR 0x00000430 +#define MAC_ACPI_LEN_OFFSET 0x00000434 +#define ACPI_LENOFF_LEN_MASK 0x0000ffff +#define ACPI_LENOFF_LEN_SHIFT 0 +#define ACPI_LENOFF_OFF_MASK 0x0fff0000 +#define ACPI_LENOFF_OFF_SHIFT 16 +#define MAC_TX_BACKOFF_SEED 0x00000438 +#define TX_BACKOFF_SEED_MASK 0x000003ff +#define MAC_RX_MTU_SIZE 0x0000043c +#define RX_MTU_SIZE_MASK 0x0000ffff +#define MAC_PCS_TEST 0x00000440 +#define PCS_TEST_PATTERN_MASK 0x000fffff +#define PCS_TEST_PATTERN_SHIFT 0 +#define PCS_TEST_ENABLE 0x00100000 +#define MAC_TX_AUTO_NEG 0x00000444 +#define TX_AUTO_NEG_MASK 0x0000ffff +#define TX_AUTO_NEG_SHIFT 0 +#define MAC_RX_AUTO_NEG 0x00000448 +#define RX_AUTO_NEG_MASK 0x0000ffff +#define RX_AUTO_NEG_SHIFT 0 +#define MAC_MI_COM 0x0000044c +#define MI_COM_CMD_MASK 0x0c000000 +#define MI_COM_CMD_WRITE 0x04000000 +#define MI_COM_CMD_READ 0x08000000 +#define MI_COM_READ_FAILED 0x10000000 +#define MI_COM_START 0x20000000 +#define MI_COM_BUSY 0x20000000 +#define MI_COM_PHY_ADDR_MASK 0x03e00000 +#define MI_COM_PHY_ADDR_SHIFT 21 +#define MI_COM_REG_ADDR_MASK 0x001f0000 +#define MI_COM_REG_ADDR_SHIFT 16 +#define MI_COM_DATA_MASK 0x0000ffff +#define MAC_MI_STAT 0x00000450 +#define MAC_MI_STAT_LNKSTAT_ATTN_ENAB 0x00000001 +#define MAC_MI_MODE 0x00000454 +#define MAC_MI_MODE_CLK_10MHZ 0x00000001 +#define MAC_MI_MODE_SHORT_PREAMBLE 0x00000002 +#define MAC_MI_MODE_AUTO_POLL 0x00000010 +#define MAC_MI_MODE_CORE_CLK_62MHZ 0x00008000 +#define MAC_MI_MODE_BASE 0x000c0000 /* XXX magic values XXX */ +#define MAC_AUTO_POLL_STATUS 0x00000458 +#define MAC_AUTO_POLL_ERROR 0x00000001 +#define MAC_TX_MODE 0x0000045c +#define TX_MODE_RESET 0x00000001 +#define TX_MODE_ENABLE 0x00000002 +#define TX_MODE_FLOW_CTRL_ENABLE 0x00000010 +#define TX_MODE_BIG_BCKOFF_ENABLE 0x00000020 +#define TX_MODE_LONG_PAUSE_ENABLE 0x00000040 +#define MAC_TX_STATUS 0x00000460 +#define TX_STATUS_XOFFED 0x00000001 +#define TX_STATUS_SENT_XOFF 0x00000002 +#define TX_STATUS_SENT_XON 0x00000004 +#define TX_STATUS_LINK_UP 0x00000008 +#define TX_STATUS_ODI_UNDERRUN 0x00000010 +#define TX_STATUS_ODI_OVERRUN 0x00000020 +#define MAC_TX_LENGTHS 0x00000464 +#define TX_LENGTHS_SLOT_TIME_MASK 0x000000ff +#define TX_LENGTHS_SLOT_TIME_SHIFT 0 +#define TX_LENGTHS_IPG_MASK 0x00000f00 +#define TX_LENGTHS_IPG_SHIFT 8 +#define TX_LENGTHS_IPG_CRS_MASK 0x00003000 +#define TX_LENGTHS_IPG_CRS_SHIFT 12 +#define MAC_RX_MODE 0x00000468 +#define RX_MODE_RESET 0x00000001 +#define RX_MODE_ENABLE 0x00000002 +#define RX_MODE_FLOW_CTRL_ENABLE 0x00000004 +#define RX_MODE_KEEP_MAC_CTRL 0x00000008 +#define RX_MODE_KEEP_PAUSE 0x00000010 +#define RX_MODE_ACCEPT_OVERSIZED 0x00000020 +#define RX_MODE_ACCEPT_RUNTS 0x00000040 +#define RX_MODE_LEN_CHECK 0x00000080 +#define RX_MODE_PROMISC 0x00000100 +#define RX_MODE_NO_CRC_CHECK 0x00000200 +#define RX_MODE_KEEP_VLAN_TAG 0x00000400 +#define MAC_RX_STATUS 0x0000046c +#define RX_STATUS_REMOTE_TX_XOFFED 0x00000001 +#define RX_STATUS_XOFF_RCVD 0x00000002 +#define RX_STATUS_XON_RCVD 0x00000004 +#define MAC_HASH_REG_0 0x00000470 +#define MAC_HASH_REG_1 0x00000474 +#define MAC_HASH_REG_2 0x00000478 +#define MAC_HASH_REG_3 0x0000047c +#define MAC_RCV_RULE_0 0x00000480 +#define MAC_RCV_VALUE_0 0x00000484 +#define MAC_RCV_RULE_1 0x00000488 +#define MAC_RCV_VALUE_1 0x0000048c +#define MAC_RCV_RULE_2 0x00000490 +#define MAC_RCV_VALUE_2 0x00000494 +#define MAC_RCV_RULE_3 0x00000498 +#define MAC_RCV_VALUE_3 0x0000049c +#define MAC_RCV_RULE_4 0x000004a0 +#define MAC_RCV_VALUE_4 0x000004a4 +#define MAC_RCV_RULE_5 0x000004a8 +#define MAC_RCV_VALUE_5 0x000004ac +#define MAC_RCV_RULE_6 0x000004b0 +#define MAC_RCV_VALUE_6 0x000004b4 +#define MAC_RCV_RULE_7 0x000004b8 +#define MAC_RCV_VALUE_7 0x000004bc +#define MAC_RCV_RULE_8 0x000004c0 +#define MAC_RCV_VALUE_8 0x000004c4 +#define MAC_RCV_RULE_9 0x000004c8 +#define MAC_RCV_VALUE_9 0x000004cc +#define MAC_RCV_RULE_10 0x000004d0 +#define MAC_RCV_VALUE_10 0x000004d4 +#define MAC_RCV_RULE_11 0x000004d8 +#define MAC_RCV_VALUE_11 0x000004dc +#define MAC_RCV_RULE_12 0x000004e0 +#define MAC_RCV_VALUE_12 0x000004e4 +#define MAC_RCV_RULE_13 0x000004e8 +#define MAC_RCV_VALUE_13 0x000004ec +#define MAC_RCV_RULE_14 0x000004f0 +#define MAC_RCV_VALUE_14 0x000004f4 +#define MAC_RCV_RULE_15 0x000004f8 +#define MAC_RCV_VALUE_15 0x000004fc +#define RCV_RULE_DISABLE_MASK 0x7fffffff +#define MAC_RCV_RULE_CFG 0x00000500 +#define RCV_RULE_CFG_DEFAULT_CLASS 0x00000008 +/* 0x504 --> 0x590 unused */ +#define MAC_SERDES_CFG 0x00000590 +#define MAC_SERDES_STAT 0x00000594 +/* 0x598 --> 0x600 unused */ +#define MAC_TX_MAC_STATE_BASE 0x00000600 /* 16 bytes */ +#define MAC_RX_MAC_STATE_BASE 0x00000610 /* 20 bytes */ +/* 0x624 --> 0x800 unused */ +#define MAC_RX_STATS_BASE 0x00000800 /* 26 32-bit words */ +/* 0x868 --> 0x880 unused */ +#define MAC_TX_STATS_BASE 0x00000880 /* 28 32-bit words */ +/* 0x8f0 --> 0xc00 unused */ + +/* Send data initiator control registers */ +#define SNDDATAI_MODE 0x00000c00 +#define SNDDATAI_MODE_RESET 0x00000001 +#define SNDDATAI_MODE_ENABLE 0x00000002 +#define SNDDATAI_MODE_STAT_OFLOW_ENAB 0x00000004 +#define SNDDATAI_STATUS 0x00000c04 +#define SNDDATAI_STATUS_STAT_OFLOW 0x00000004 +#define SNDDATAI_STATSCTRL 0x00000c08 +#define SNDDATAI_SCTRL_ENABLE 0x00000001 +#define SNDDATAI_SCTRL_FASTUPD 0x00000002 +#define SNDDATAI_SCTRL_CLEAR 0x00000004 +#define SNDDATAI_SCTRL_FLUSH 0x00000008 +#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010 +#define SNDDATAI_STATSENAB 0x00000c0c +#define SNDDATAI_STATSINCMASK 0x00000c10 +/* 0xc14 --> 0xc80 unused */ +#define SNDDATAI_COS_CNT_0 0x00000c80 +#define SNDDATAI_COS_CNT_1 0x00000c84 +#define SNDDATAI_COS_CNT_2 0x00000c88 +#define SNDDATAI_COS_CNT_3 0x00000c8c +#define SNDDATAI_COS_CNT_4 0x00000c90 +#define SNDDATAI_COS_CNT_5 0x00000c94 +#define SNDDATAI_COS_CNT_6 0x00000c98 +#define SNDDATAI_COS_CNT_7 0x00000c9c +#define SNDDATAI_COS_CNT_8 0x00000ca0 +#define SNDDATAI_COS_CNT_9 0x00000ca4 +#define SNDDATAI_COS_CNT_10 0x00000ca8 +#define SNDDATAI_COS_CNT_11 0x00000cac +#define SNDDATAI_COS_CNT_12 0x00000cb0 +#define SNDDATAI_COS_CNT_13 0x00000cb4 +#define SNDDATAI_COS_CNT_14 0x00000cb8 +#define SNDDATAI_COS_CNT_15 0x00000cbc +#define SNDDATAI_DMA_RDQ_FULL_CNT 0x00000cc0 +#define SNDDATAI_DMA_PRIO_RDQ_FULL_CNT 0x00000cc4 +#define SNDDATAI_SDCQ_FULL_CNT 0x00000cc8 +#define SNDDATAI_NICRNG_SSND_PIDX_CNT 0x00000ccc +#define SNDDATAI_STATS_UPDATED_CNT 0x00000cd0 +#define SNDDATAI_INTERRUPTS_CNT 0x00000cd4 +#define SNDDATAI_AVOID_INTERRUPTS_CNT 0x00000cd8 +#define SNDDATAI_SND_THRESH_HIT_CNT 0x00000cdc +/* 0xce0 --> 0x1000 unused */ + +/* Send data completion control registers */ +#define SNDDATAC_MODE 0x00001000 +#define SNDDATAC_MODE_RESET 0x00000001 +#define SNDDATAC_MODE_ENABLE 0x00000002 +/* 0x1004 --> 0x1400 unused */ + +/* Send BD ring selector */ +#define SNDBDS_MODE 0x00001400 +#define SNDBDS_MODE_RESET 0x00000001 +#define SNDBDS_MODE_ENABLE 0x00000002 +#define SNDBDS_MODE_ATTN_ENABLE 0x00000004 +#define SNDBDS_STATUS 0x00001404 +#define SNDBDS_STATUS_ERROR_ATTN 0x00000004 +#define SNDBDS_HWDIAG 0x00001408 +/* 0x140c --> 0x1440 */ +#define SNDBDS_SEL_CON_IDX_0 0x00001440 +#define SNDBDS_SEL_CON_IDX_1 0x00001444 +#define SNDBDS_SEL_CON_IDX_2 0x00001448 +#define SNDBDS_SEL_CON_IDX_3 0x0000144c +#define SNDBDS_SEL_CON_IDX_4 0x00001450 +#define SNDBDS_SEL_CON_IDX_5 0x00001454 +#define SNDBDS_SEL_CON_IDX_6 0x00001458 +#define SNDBDS_SEL_CON_IDX_7 0x0000145c +#define SNDBDS_SEL_CON_IDX_8 0x00001460 +#define SNDBDS_SEL_CON_IDX_9 0x00001464 +#define SNDBDS_SEL_CON_IDX_10 0x00001468 +#define SNDBDS_SEL_CON_IDX_11 0x0000146c +#define SNDBDS_SEL_CON_IDX_12 0x00001470 +#define SNDBDS_SEL_CON_IDX_13 0x00001474 +#define SNDBDS_SEL_CON_IDX_14 0x00001478 +#define SNDBDS_SEL_CON_IDX_15 0x0000147c +/* 0x1480 --> 0x1800 unused */ + +/* Send BD initiator control registers */ +#define SNDBDI_MODE 0x00001800 +#define SNDBDI_MODE_RESET 0x00000001 +#define SNDBDI_MODE_ENABLE 0x00000002 +#define SNDBDI_MODE_ATTN_ENABLE 0x00000004 +#define SNDBDI_STATUS 0x00001804 +#define SNDBDI_STATUS_ERROR_ATTN 0x00000004 +#define SNDBDI_IN_PROD_IDX_0 0x00001808 +#define SNDBDI_IN_PROD_IDX_1 0x0000180c +#define SNDBDI_IN_PROD_IDX_2 0x00001810 +#define SNDBDI_IN_PROD_IDX_3 0x00001814 +#define SNDBDI_IN_PROD_IDX_4 0x00001818 +#define SNDBDI_IN_PROD_IDX_5 0x0000181c +#define SNDBDI_IN_PROD_IDX_6 0x00001820 +#define SNDBDI_IN_PROD_IDX_7 0x00001824 +#define SNDBDI_IN_PROD_IDX_8 0x00001828 +#define SNDBDI_IN_PROD_IDX_9 0x0000182c +#define SNDBDI_IN_PROD_IDX_10 0x00001830 +#define SNDBDI_IN_PROD_IDX_11 0x00001834 +#define SNDBDI_IN_PROD_IDX_12 0x00001838 +#define SNDBDI_IN_PROD_IDX_13 0x0000183c +#define SNDBDI_IN_PROD_IDX_14 0x00001840 +#define SNDBDI_IN_PROD_IDX_15 0x00001844 +/* 0x1848 --> 0x1c00 unused */ + +/* Send BD completion control registers */ +#define SNDBDC_MODE 0x00001c00 +#define SNDBDC_MODE_RESET 0x00000001 +#define SNDBDC_MODE_ENABLE 0x00000002 +#define SNDBDC_MODE_ATTN_ENABLE 0x00000004 +/* 0x1c04 --> 0x2000 unused */ + +/* Receive list placement control registers */ +#define RCVLPC_MODE 0x00002000 +#define RCVLPC_MODE_RESET 0x00000001 +#define RCVLPC_MODE_ENABLE 0x00000002 +#define RCVLPC_MODE_CLASS0_ATTN_ENAB 0x00000004 +#define RCVLPC_MODE_MAPOOR_AATTN_ENAB 0x00000008 +#define RCVLPC_MODE_STAT_OFLOW_ENAB 0x00000010 +#define RCVLPC_STATUS 0x00002004 +#define RCVLPC_STATUS_CLASS0 0x00000004 +#define RCVLPC_STATUS_MAPOOR 0x00000008 +#define RCVLPC_STATUS_STAT_OFLOW 0x00000010 +#define RCVLPC_LOCK 0x00002008 +#define RCVLPC_LOCK_REQ_MASK 0x0000ffff +#define RCVLPC_LOCK_REQ_SHIFT 0 +#define RCVLPC_LOCK_GRANT_MASK 0xffff0000 +#define RCVLPC_LOCK_GRANT_SHIFT 16 +#define RCVLPC_NON_EMPTY_BITS 0x0000200c +#define RCVLPC_NON_EMPTY_BITS_MASK 0x0000ffff +#define RCVLPC_CONFIG 0x00002010 +#define RCVLPC_STATSCTRL 0x00002014 +#define RCVLPC_STATSCTRL_ENABLE 0x00000001 +#define RCVLPC_STATSCTRL_FASTUPD 0x00000002 +#define RCVLPC_STATS_ENABLE 0x00002018 +#define RCVLPC_STATS_INCMASK 0x0000201c +/* 0x2020 --> 0x2100 unused */ +#define RCVLPC_SELLST_BASE 0x00002100 /* 16 16-byte entries */ +#define SELLST_TAIL 0x00000004 +#define SELLST_CONT 0x00000008 +#define SELLST_UNUSED 0x0000000c +#define RCVLPC_COS_CNTL_BASE 0x00002200 /* 16 4-byte entries */ +#define RCVLPC_DROP_FILTER_CNT 0x00002240 +#define RCVLPC_DMA_WQ_FULL_CNT 0x00002244 +#define RCVLPC_DMA_HIPRIO_WQ_FULL_CNT 0x00002248 +#define RCVLPC_NO_RCV_BD_CNT 0x0000224c +#define RCVLPC_IN_DISCARDS_CNT 0x00002250 +#define RCVLPC_IN_ERRORS_CNT 0x00002254 +#define RCVLPC_RCV_THRESH_HIT_CNT 0x00002258 +/* 0x225c --> 0x2400 unused */ + +/* Receive Data and Receive BD Initiator Control */ +#define RCVDBDI_MODE 0x00002400 +#define RCVDBDI_MODE_RESET 0x00000001 +#define RCVDBDI_MODE_ENABLE 0x00000002 +#define RCVDBDI_MODE_JUMBOBD_NEEDED 0x00000004 +#define RCVDBDI_MODE_FRM_TOO_BIG 0x00000008 +#define RCVDBDI_MODE_INV_RING_SZ 0x00000010 +#define RCVDBDI_STATUS 0x00002404 +#define RCVDBDI_STATUS_JUMBOBD_NEEDED 0x00000004 +#define RCVDBDI_STATUS_FRM_TOO_BIG 0x00000008 +#define RCVDBDI_STATUS_INV_RING_SZ 0x00000010 +#define RCVDBDI_SPLIT_FRAME_MINSZ 0x00002408 +/* 0x240c --> 0x2440 unused */ +#define RCVDBDI_JUMBO_BD 0x00002440 /* TG3_BDINFO_... */ +#define RCVDBDI_STD_BD 0x00002450 /* TG3_BDINFO_... */ +#define RCVDBDI_MINI_BD 0x00002460 /* TG3_BDINFO_... */ +#define RCVDBDI_JUMBO_CON_IDX 0x00002470 +#define RCVDBDI_STD_CON_IDX 0x00002474 +#define RCVDBDI_MINI_CON_IDX 0x00002478 +/* 0x247c --> 0x2480 unused */ +#define RCVDBDI_BD_PROD_IDX_0 0x00002480 +#define RCVDBDI_BD_PROD_IDX_1 0x00002484 +#define RCVDBDI_BD_PROD_IDX_2 0x00002488 +#define RCVDBDI_BD_PROD_IDX_3 0x0000248c +#define RCVDBDI_BD_PROD_IDX_4 0x00002490 +#define RCVDBDI_BD_PROD_IDX_5 0x00002494 +#define RCVDBDI_BD_PROD_IDX_6 0x00002498 +#define RCVDBDI_BD_PROD_IDX_7 0x0000249c +#define RCVDBDI_BD_PROD_IDX_8 0x000024a0 +#define RCVDBDI_BD_PROD_IDX_9 0x000024a4 +#define RCVDBDI_BD_PROD_IDX_10 0x000024a8 +#define RCVDBDI_BD_PROD_IDX_11 0x000024ac +#define RCVDBDI_BD_PROD_IDX_12 0x000024b0 +#define RCVDBDI_BD_PROD_IDX_13 0x000024b4 +#define RCVDBDI_BD_PROD_IDX_14 0x000024b8 +#define RCVDBDI_BD_PROD_IDX_15 0x000024bc +#define RCVDBDI_HWDIAG 0x000024c0 +/* 0x24c4 --> 0x2800 unused */ + +/* Receive Data Completion Control */ +#define RCVDCC_MODE 0x00002800 +#define RCVDCC_MODE_RESET 0x00000001 +#define RCVDCC_MODE_ENABLE 0x00000002 +#define RCVDCC_MODE_ATTN_ENABLE 0x00000004 +/* 0x2804 --> 0x2c00 unused */ + +/* Receive BD Initiator Control Registers */ +#define RCVBDI_MODE 0x00002c00 +#define RCVBDI_MODE_RESET 0x00000001 +#define RCVBDI_MODE_ENABLE 0x00000002 +#define RCVBDI_MODE_RCB_ATTN_ENAB 0x00000004 +#define RCVBDI_STATUS 0x00002c04 +#define RCVBDI_STATUS_RCB_ATTN 0x00000004 +#define RCVBDI_JUMBO_PROD_IDX 0x00002c08 +#define RCVBDI_STD_PROD_IDX 0x00002c0c +#define RCVBDI_MINI_PROD_IDX 0x00002c10 +#define RCVBDI_MINI_THRESH 0x00002c14 +#define RCVBDI_STD_THRESH 0x00002c18 +#define RCVBDI_JUMBO_THRESH 0x00002c1c +/* 0x2c20 --> 0x3000 unused */ + +/* Receive BD Completion Control Registers */ +#define RCVCC_MODE 0x00003000 +#define RCVCC_MODE_RESET 0x00000001 +#define RCVCC_MODE_ENABLE 0x00000002 +#define RCVCC_MODE_ATTN_ENABLE 0x00000004 +#define RCVCC_STATUS 0x00003004 +#define RCVCC_STATUS_ERROR_ATTN 0x00000004 +#define RCVCC_JUMP_PROD_IDX 0x00003008 +#define RCVCC_STD_PROD_IDX 0x0000300c +#define RCVCC_MINI_PROD_IDX 0x00003010 +/* 0x3014 --> 0x3400 unused */ + +/* Receive list selector control registers */ +#define RCVLSC_MODE 0x00003400 +#define RCVLSC_MODE_RESET 0x00000001 +#define RCVLSC_MODE_ENABLE 0x00000002 +#define RCVLSC_MODE_ATTN_ENABLE 0x00000004 +#define RCVLSC_STATUS 0x00003404 +#define RCVLSC_STATUS_ERROR_ATTN 0x00000004 +/* 0x3408 --> 0x3800 unused */ + +/* Mbuf cluster free registers */ +#define MBFREE_MODE 0x00003800 +#define MBFREE_MODE_RESET 0x00000001 +#define MBFREE_MODE_ENABLE 0x00000002 +#define MBFREE_STATUS 0x00003804 +/* 0x3808 --> 0x3c00 unused */ + +/* Host coalescing control registers */ +#define HOSTCC_MODE 0x00003c00 +#define HOSTCC_MODE_RESET 0x00000001 +#define HOSTCC_MODE_ENABLE 0x00000002 +#define HOSTCC_MODE_ATTN 0x00000004 +#define HOSTCC_MODE_NOW 0x00000008 +#define HOSTCC_MODE_FULL_STATUS 0x00000000 +#define HOSTCC_MODE_64BYTE 0x00000080 +#define HOSTCC_MODE_32BYTE 0x00000100 +#define HOSTCC_MODE_CLRTICK_RXBD 0x00000200 +#define HOSTCC_MODE_CLRTICK_TXBD 0x00000400 +#define HOSTCC_MODE_NOINT_ON_NOW 0x00000800 +#define HOSTCC_MODE_NOINT_ON_FORCE 0x00001000 +#define HOSTCC_STATUS 0x00003c04 +#define HOSTCC_STATUS_ERROR_ATTN 0x00000004 +#define HOSTCC_RXCOL_TICKS 0x00003c08 +#define LOW_RXCOL_TICKS 0x00000032 +#define DEFAULT_RXCOL_TICKS 0x00000048 +#define HIGH_RXCOL_TICKS 0x00000096 +#define HOSTCC_TXCOL_TICKS 0x00003c0c +#define DEFAULT_TXCOL_TICKS 0x0000012c +#define HOSTCC_RXMAX_FRAMES 0x00003c10 +#define LOW_RXMAX_FRAMES 0x00000005 +#define DEFAULT_RXMAX_FRAMES 0x00000008 +#define HIGH_RXMAX_FRAMES 0x00000012 +#define HOSTCC_TXMAX_FRAMES 0x00003c14 +#define DEFAULT_TXMAX_FRAMES 0x0000004b +#define HOSTCC_RXCOAL_TICK_INT 0x00003c18 +#define DEFAULT_RXCOAL_TICK_INT 0x00000019 +#define HOSTCC_TXCOAL_TICK_INT 0x00003c1c +#define DEFAULT_TXCOAL_TICK_INT 0x00000019 +#define HOSTCC_RXCOAL_MAXF_INT 0x00003c20 +#define DEFAULT_RXCOAL_MAXF_INT 0x00000005 +#define HOSTCC_TXCOAL_MAXF_INT 0x00003c24 +#define DEFAULT_TXCOAL_MAXF_INT 0x00000005 +#define HOSTCC_STAT_COAL_TICKS 0x00003c28 +#define DEFAULT_STAT_COAL_TICKS 0x000f4240 +/* 0x3c2c --> 0x3c30 unused */ +#define HOSTCC_STATS_BLK_HOST_ADDR 0x00003c30 /* 64-bit */ +#define HOSTCC_STATUS_BLK_HOST_ADDR 0x00003c38 /* 64-bit */ +#define HOSTCC_STATS_BLK_NIC_ADDR 0x00003c40 +#define HOSTCC_STATUS_BLK_NIC_ADDR 0x00003c44 +#define HOSTCC_FLOW_ATTN 0x00003c48 +/* 0x3c4c --> 0x3c50 unused */ +#define HOSTCC_JUMBO_CON_IDX 0x00003c50 +#define HOSTCC_STD_CON_IDX 0x00003c54 +#define HOSTCC_MINI_CON_IDX 0x00003c58 +/* 0x3c5c --> 0x3c80 unused */ +#define HOSTCC_RET_PROD_IDX_0 0x00003c80 +#define HOSTCC_RET_PROD_IDX_1 0x00003c84 +#define HOSTCC_RET_PROD_IDX_2 0x00003c88 +#define HOSTCC_RET_PROD_IDX_3 0x00003c8c +#define HOSTCC_RET_PROD_IDX_4 0x00003c90 +#define HOSTCC_RET_PROD_IDX_5 0x00003c94 +#define HOSTCC_RET_PROD_IDX_6 0x00003c98 +#define HOSTCC_RET_PROD_IDX_7 0x00003c9c +#define HOSTCC_RET_PROD_IDX_8 0x00003ca0 +#define HOSTCC_RET_PROD_IDX_9 0x00003ca4 +#define HOSTCC_RET_PROD_IDX_10 0x00003ca8 +#define HOSTCC_RET_PROD_IDX_11 0x00003cac +#define HOSTCC_RET_PROD_IDX_12 0x00003cb0 +#define HOSTCC_RET_PROD_IDX_13 0x00003cb4 +#define HOSTCC_RET_PROD_IDX_14 0x00003cb8 +#define HOSTCC_RET_PROD_IDX_15 0x00003cbc +#define HOSTCC_SND_CON_IDX_0 0x00003cc0 +#define HOSTCC_SND_CON_IDX_1 0x00003cc4 +#define HOSTCC_SND_CON_IDX_2 0x00003cc8 +#define HOSTCC_SND_CON_IDX_3 0x00003ccc +#define HOSTCC_SND_CON_IDX_4 0x00003cd0 +#define HOSTCC_SND_CON_IDX_5 0x00003cd4 +#define HOSTCC_SND_CON_IDX_6 0x00003cd8 +#define HOSTCC_SND_CON_IDX_7 0x00003cdc +#define HOSTCC_SND_CON_IDX_8 0x00003ce0 +#define HOSTCC_SND_CON_IDX_9 0x00003ce4 +#define HOSTCC_SND_CON_IDX_10 0x00003ce8 +#define HOSTCC_SND_CON_IDX_11 0x00003cec +#define HOSTCC_SND_CON_IDX_12 0x00003cf0 +#define HOSTCC_SND_CON_IDX_13 0x00003cf4 +#define HOSTCC_SND_CON_IDX_14 0x00003cf8 +#define HOSTCC_SND_CON_IDX_15 0x00003cfc +/* 0x3d00 --> 0x4000 unused */ + +/* Memory arbiter control registers */ +#define MEMARB_MODE 0x00004000 +#define MEMARB_MODE_RESET 0x00000001 +#define MEMARB_MODE_ENABLE 0x00000002 +#define MEMARB_STATUS 0x00004004 +#define MEMARB_TRAP_ADDR_LOW 0x00004008 +#define MEMARB_TRAP_ADDR_HIGH 0x0000400c +/* 0x4010 --> 0x4400 unused */ + +/* Buffer manager control registers */ +#define BUFMGR_MODE 0x00004400 +#define BUFMGR_MODE_RESET 0x00000001 +#define BUFMGR_MODE_ENABLE 0x00000002 +#define BUFMGR_MODE_ATTN_ENABLE 0x00000004 +#define BUFMGR_MODE_BM_TEST 0x00000008 +#define BUFMGR_MODE_MBLOW_ATTN_ENAB 0x00000010 +#define BUFMGR_STATUS 0x00004404 +#define BUFMGR_STATUS_ERROR 0x00000004 +#define BUFMGR_STATUS_MBLOW 0x00000010 +#define BUFMGR_MB_POOL_ADDR 0x00004408 +#define BUFMGR_MB_POOL_SIZE 0x0000440c +#define BUFMGR_MB_RDMA_LOW_WATER 0x00004410 +#define DEFAULT_MB_RDMA_LOW_WATER 0x00000040 +#define DEFAULT_MB_RDMA_LOW_WATER_JUMBO 0x00000130 +#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414 +#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020 +#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098 +#define BUFMGR_MB_HIGH_WATER 0x00004418 +#define DEFAULT_MB_HIGH_WATER 0x00000060 +#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c +#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c +#define BUFMGR_MB_ALLOC_BIT 0x10000000 +#define BUFMGR_RX_MB_ALLOC_RESP 0x00004420 +#define BUFMGR_TX_MB_ALLOC_REQ 0x00004424 +#define BUFMGR_TX_MB_ALLOC_RESP 0x00004428 +#define BUFMGR_DMA_DESC_POOL_ADDR 0x0000442c +#define BUFMGR_DMA_DESC_POOL_SIZE 0x00004430 +#define BUFMGR_DMA_LOW_WATER 0x00004434 +#define DEFAULT_DMA_LOW_WATER 0x00000005 +#define BUFMGR_DMA_HIGH_WATER 0x00004438 +#define DEFAULT_DMA_HIGH_WATER 0x0000000a +#define BUFMGR_RX_DMA_ALLOC_REQ 0x0000443c +#define BUFMGR_RX_DMA_ALLOC_RESP 0x00004440 +#define BUFMGR_TX_DMA_ALLOC_REQ 0x00004444 +#define BUFMGR_TX_DMA_ALLOC_RESP 0x00004448 +#define BUFMGR_HWDIAG_0 0x0000444c +#define BUFMGR_HWDIAG_1 0x00004450 +#define BUFMGR_HWDIAG_2 0x00004454 +/* 0x4458 --> 0x4800 unused */ + +/* Read DMA control registers */ +#define RDMAC_MODE 0x00004800 +#define RDMAC_MODE_RESET 0x00000001 +#define RDMAC_MODE_ENABLE 0x00000002 +#define RDMAC_MODE_TGTABORT_ENAB 0x00000004 +#define RDMAC_MODE_MSTABORT_ENAB 0x00000008 +#define RDMAC_MODE_PARITYERR_ENAB 0x00000010 +#define RDMAC_MODE_ADDROFLOW_ENAB 0x00000020 +#define RDMAC_MODE_FIFOOFLOW_ENAB 0x00000040 +#define RDMAC_MODE_FIFOURUN_ENAB 0x00000080 +#define RDMAC_MODE_FIFOOREAD_ENAB 0x00000100 +#define RDMAC_MODE_LNGREAD_ENAB 0x00000200 +#define RDMAC_STATUS 0x00004804 +#define RDMAC_STATUS_TGTABORT 0x00000004 +#define RDMAC_STATUS_MSTABORT 0x00000008 +#define RDMAC_STATUS_PARITYERR 0x00000010 +#define RDMAC_STATUS_ADDROFLOW 0x00000020 +#define RDMAC_STATUS_FIFOOFLOW 0x00000040 +#define RDMAC_STATUS_FIFOURUN 0x00000080 +#define RDMAC_STATUS_FIFOOREAD 0x00000100 +#define RDMAC_STATUS_LNGREAD 0x00000200 +/* 0x4808 --> 0x4c00 unused */ + +/* Write DMA control registers */ +#define WDMAC_MODE 0x00004c00 +#define WDMAC_MODE_RESET 0x00000001 +#define WDMAC_MODE_ENABLE 0x00000002 +#define WDMAC_MODE_TGTABORT_ENAB 0x00000004 +#define WDMAC_MODE_MSTABORT_ENAB 0x00000008 +#define WDMAC_MODE_PARITYERR_ENAB 0x00000010 +#define WDMAC_MODE_ADDROFLOW_ENAB 0x00000020 +#define WDMAC_MODE_FIFOOFLOW_ENAB 0x00000040 +#define WDMAC_MODE_FIFOURUN_ENAB 0x00000080 +#define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100 +#define WDMAC_MODE_LNGREAD_ENAB 0x00000200 +#define WDMAC_STATUS 0x00004c04 +#define WDMAC_STATUS_TGTABORT 0x00000004 +#define WDMAC_STATUS_MSTABORT 0x00000008 +#define WDMAC_STATUS_PARITYERR 0x00000010 +#define WDMAC_STATUS_ADDROFLOW 0x00000020 +#define WDMAC_STATUS_FIFOOFLOW 0x00000040 +#define WDMAC_STATUS_FIFOURUN 0x00000080 +#define WDMAC_STATUS_FIFOOREAD 0x00000100 +#define WDMAC_STATUS_LNGREAD 0x00000200 +/* 0x4c08 --> 0x5000 unused */ + +/* Per-cpu register offsets (arm9) */ +#define CPU_MODE 0x00000000 +#define CPU_MODE_RESET 0x00000001 +#define CPU_MODE_HALT 0x00000400 +#define CPU_STATE 0x00000004 +#define CPU_EVTMASK 0x00000008 +/* 0xc --> 0x1c reserved */ +#define CPU_PC 0x0000001c +#define CPU_INSN 0x00000020 +#define CPU_SPAD_UFLOW 0x00000024 +#define CPU_WDOG_CLEAR 0x00000028 +#define CPU_WDOG_VECTOR 0x0000002c +#define CPU_WDOG_PC 0x00000030 +#define CPU_HW_BP 0x00000034 +/* 0x38 --> 0x44 unused */ +#define CPU_WDOG_SAVED_STATE 0x00000044 +#define CPU_LAST_BRANCH_ADDR 0x00000048 +#define CPU_SPAD_UFLOW_SET 0x0000004c +/* 0x50 --> 0x200 unused */ +#define CPU_R0 0x00000200 +#define CPU_R1 0x00000204 +#define CPU_R2 0x00000208 +#define CPU_R3 0x0000020c +#define CPU_R4 0x00000210 +#define CPU_R5 0x00000214 +#define CPU_R6 0x00000218 +#define CPU_R7 0x0000021c +#define CPU_R8 0x00000220 +#define CPU_R9 0x00000224 +#define CPU_R10 0x00000228 +#define CPU_R11 0x0000022c +#define CPU_R12 0x00000230 +#define CPU_R13 0x00000234 +#define CPU_R14 0x00000238 +#define CPU_R15 0x0000023c +#define CPU_R16 0x00000240 +#define CPU_R17 0x00000244 +#define CPU_R18 0x00000248 +#define CPU_R19 0x0000024c +#define CPU_R20 0x00000250 +#define CPU_R21 0x00000254 +#define CPU_R22 0x00000258 +#define CPU_R23 0x0000025c +#define CPU_R24 0x00000260 +#define CPU_R25 0x00000264 +#define CPU_R26 0x00000268 +#define CPU_R27 0x0000026c +#define CPU_R28 0x00000270 +#define CPU_R29 0x00000274 +#define CPU_R30 0x00000278 +#define CPU_R31 0x0000027c +/* 0x280 --> 0x400 unused */ + +#define RX_CPU_BASE 0x00005000 +#define TX_CPU_BASE 0x00005400 + +/* Mailboxes */ +#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */ +#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */ +#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */ +#define GRCMBOX_INTERRUPT_3 0x00005818 /* 64-bit */ +#define GRCMBOX_GENERAL_0 0x00005820 /* 64-bit */ +#define GRCMBOX_GENERAL_1 0x00005828 /* 64-bit */ +#define GRCMBOX_GENERAL_2 0x00005830 /* 64-bit */ +#define GRCMBOX_GENERAL_3 0x00005838 /* 64-bit */ +#define GRCMBOX_GENERAL_4 0x00005840 /* 64-bit */ +#define GRCMBOX_GENERAL_5 0x00005848 /* 64-bit */ +#define GRCMBOX_GENERAL_6 0x00005850 /* 64-bit */ +#define GRCMBOX_GENERAL_7 0x00005858 /* 64-bit */ +#define GRCMBOX_RELOAD_STAT 0x00005860 /* 64-bit */ +#define GRCMBOX_RCVSTD_PROD_IDX 0x00005868 /* 64-bit */ +#define GRCMBOX_RCVJUMBO_PROD_IDX 0x00005870 /* 64-bit */ +#define GRCMBOX_RCVMINI_PROD_IDX 0x00005878 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_0 0x00005880 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_1 0x00005888 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_2 0x00005890 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_3 0x00005898 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_4 0x000058a0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_5 0x000058a8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_6 0x000058b0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_7 0x000058b8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_8 0x000058c0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_9 0x000058c8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_10 0x000058d0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_11 0x000058d8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_12 0x000058e0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_13 0x000058e8 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_14 0x000058f0 /* 64-bit */ +#define GRCMBOX_RCVRET_CON_IDX_15 0x000058f8 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_0 0x00005900 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_1 0x00005908 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_2 0x00005910 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_3 0x00005918 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_4 0x00005920 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_5 0x00005928 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_6 0x00005930 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_7 0x00005938 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_8 0x00005940 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_9 0x00005948 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_10 0x00005950 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_11 0x00005958 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_12 0x00005960 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_13 0x00005968 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_14 0x00005970 /* 64-bit */ +#define GRCMBOX_SNDHOST_PROD_IDX_15 0x00005978 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_0 0x00005980 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_1 0x00005988 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_2 0x00005990 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_3 0x00005998 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_4 0x000059a0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_5 0x000059a8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_6 0x000059b0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_7 0x000059b8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_8 0x000059c0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_9 0x000059c8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_10 0x000059d0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_11 0x000059d8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_12 0x000059e0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_13 0x000059e8 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_14 0x000059f0 /* 64-bit */ +#define GRCMBOX_SNDNIC_PROD_IDX_15 0x000059f8 /* 64-bit */ +#define GRCMBOX_HIGH_PRIO_EV_VECTOR 0x00005a00 +#define GRCMBOX_HIGH_PRIO_EV_MASK 0x00005a04 +#define GRCMBOX_LOW_PRIO_EV_VEC 0x00005a08 +#define GRCMBOX_LOW_PRIO_EV_MASK 0x00005a0c +/* 0x5a10 --> 0x5c00 */ + +/* Flow Through queues */ +#define FTQ_RESET 0x00005c00 +/* 0x5c04 --> 0x5c10 unused */ +#define FTQ_DMA_NORM_READ_CTL 0x00005c10 +#define FTQ_DMA_NORM_READ_FULL_CNT 0x00005c14 +#define FTQ_DMA_NORM_READ_FIFO_ENQDEQ 0x00005c18 +#define FTQ_DMA_NORM_READ_WRITE_PEEK 0x00005c1c +#define FTQ_DMA_HIGH_READ_CTL 0x00005c20 +#define FTQ_DMA_HIGH_READ_FULL_CNT 0x00005c24 +#define FTQ_DMA_HIGH_READ_FIFO_ENQDEQ 0x00005c28 +#define FTQ_DMA_HIGH_READ_WRITE_PEEK 0x00005c2c +#define FTQ_DMA_COMP_DISC_CTL 0x00005c30 +#define FTQ_DMA_COMP_DISC_FULL_CNT 0x00005c34 +#define FTQ_DMA_COMP_DISC_FIFO_ENQDEQ 0x00005c38 +#define FTQ_DMA_COMP_DISC_WRITE_PEEK 0x00005c3c +#define FTQ_SEND_BD_COMP_CTL 0x00005c40 +#define FTQ_SEND_BD_COMP_FULL_CNT 0x00005c44 +#define FTQ_SEND_BD_COMP_FIFO_ENQDEQ 0x00005c48 +#define FTQ_SEND_BD_COMP_WRITE_PEEK 0x00005c4c +#define FTQ_SEND_DATA_INIT_CTL 0x00005c50 +#define FTQ_SEND_DATA_INIT_FULL_CNT 0x00005c54 +#define FTQ_SEND_DATA_INIT_FIFO_ENQDEQ 0x00005c58 +#define FTQ_SEND_DATA_INIT_WRITE_PEEK 0x00005c5c +#define FTQ_DMA_NORM_WRITE_CTL 0x00005c60 +#define FTQ_DMA_NORM_WRITE_FULL_CNT 0x00005c64 +#define FTQ_DMA_NORM_WRITE_FIFO_ENQDEQ 0x00005c68 +#define FTQ_DMA_NORM_WRITE_WRITE_PEEK 0x00005c6c +#define FTQ_DMA_HIGH_WRITE_CTL 0x00005c70 +#define FTQ_DMA_HIGH_WRITE_FULL_CNT 0x00005c74 +#define FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ 0x00005c78 +#define FTQ_DMA_HIGH_WRITE_WRITE_PEEK 0x00005c7c +#define FTQ_SWTYPE1_CTL 0x00005c80 +#define FTQ_SWTYPE1_FULL_CNT 0x00005c84 +#define FTQ_SWTYPE1_FIFO_ENQDEQ 0x00005c88 +#define FTQ_SWTYPE1_WRITE_PEEK 0x00005c8c +#define FTQ_SEND_DATA_COMP_CTL 0x00005c90 +#define FTQ_SEND_DATA_COMP_FULL_CNT 0x00005c94 +#define FTQ_SEND_DATA_COMP_FIFO_ENQDEQ 0x00005c98 +#define FTQ_SEND_DATA_COMP_WRITE_PEEK 0x00005c9c +#define FTQ_HOST_COAL_CTL 0x00005ca0 +#define FTQ_HOST_COAL_FULL_CNT 0x00005ca4 +#define FTQ_HOST_COAL_FIFO_ENQDEQ 0x00005ca8 +#define FTQ_HOST_COAL_WRITE_PEEK 0x00005cac +#define FTQ_MAC_TX_CTL 0x00005cb0 +#define FTQ_MAC_TX_FULL_CNT 0x00005cb4 +#define FTQ_MAC_TX_FIFO_ENQDEQ 0x00005cb8 +#define FTQ_MAC_TX_WRITE_PEEK 0x00005cbc +#define FTQ_MB_FREE_CTL 0x00005cc0 +#define FTQ_MB_FREE_FULL_CNT 0x00005cc4 +#define FTQ_MB_FREE_FIFO_ENQDEQ 0x00005cc8 +#define FTQ_MB_FREE_WRITE_PEEK 0x00005ccc +#define FTQ_RCVBD_COMP_CTL 0x00005cd0 +#define FTQ_RCVBD_COMP_FULL_CNT 0x00005cd4 +#define FTQ_RCVBD_COMP_FIFO_ENQDEQ 0x00005cd8 +#define FTQ_RCVBD_COMP_WRITE_PEEK 0x00005cdc +#define FTQ_RCVLST_PLMT_CTL 0x00005ce0 +#define FTQ_RCVLST_PLMT_FULL_CNT 0x00005ce4 +#define FTQ_RCVLST_PLMT_FIFO_ENQDEQ 0x00005ce8 +#define FTQ_RCVLST_PLMT_WRITE_PEEK 0x00005cec +#define FTQ_RCVDATA_INI_CTL 0x00005cf0 +#define FTQ_RCVDATA_INI_FULL_CNT 0x00005cf4 +#define FTQ_RCVDATA_INI_FIFO_ENQDEQ 0x00005cf8 +#define FTQ_RCVDATA_INI_WRITE_PEEK 0x00005cfc +#define FTQ_RCVDATA_COMP_CTL 0x00005d00 +#define FTQ_RCVDATA_COMP_FULL_CNT 0x00005d04 +#define FTQ_RCVDATA_COMP_FIFO_ENQDEQ 0x00005d08 +#define FTQ_RCVDATA_COMP_WRITE_PEEK 0x00005d0c +#define FTQ_SWTYPE2_CTL 0x00005d10 +#define FTQ_SWTYPE2_FULL_CNT 0x00005d14 +#define FTQ_SWTYPE2_FIFO_ENQDEQ 0x00005d18 +#define FTQ_SWTYPE2_WRITE_PEEK 0x00005d1c +/* 0x5d20 --> 0x6000 unused */ + +/* Message signaled interrupt registers */ +#define MSGINT_MODE 0x00006000 +#define MSGINT_MODE_RESET 0x00000001 +#define MSGINT_MODE_ENABLE 0x00000002 +#define MSGINT_STATUS 0x00006004 +#define MSGINT_FIFO 0x00006008 +/* 0x600c --> 0x6400 unused */ + +/* DMA completion registers */ +#define DMAC_MODE 0x00006400 +#define DMAC_MODE_RESET 0x00000001 +#define DMAC_MODE_ENABLE 0x00000002 +/* 0x6404 --> 0x6800 unused */ + +/* GRC registers */ +#define GRC_MODE 0x00006800 +#define GRC_MODE_UPD_ON_COAL 0x00000001 +#define GRC_MODE_BSWAP_NONFRM_DATA 0x00000002 +#define GRC_MODE_WSWAP_NONFRM_DATA 0x00000004 +#define GRC_MODE_BSWAP_DATA 0x00000010 +#define GRC_MODE_WSWAP_DATA 0x00000020 +#define GRC_MODE_SPLITHDR 0x00000100 +#define GRC_MODE_NOFRM_CRACKING 0x00000200 +#define GRC_MODE_INCL_CRC 0x00000400 +#define GRC_MODE_ALLOW_BAD_FRMS 0x00000800 +#define GRC_MODE_NOIRQ_ON_SENDS 0x00002000 +#define GRC_MODE_NOIRQ_ON_RCV 0x00004000 +#define GRC_MODE_FORCE_PCI32BIT 0x00008000 +#define GRC_MODE_HOST_STACKUP 0x00010000 +#define GRC_MODE_HOST_SENDBDS 0x00020000 +#define GRC_MODE_NO_TX_PHDR_CSUM 0x00100000 +#define GRC_MODE_NO_RX_PHDR_CSUM 0x00800000 +#define GRC_MODE_IRQ_ON_TX_CPU_ATTN 0x01000000 +#define GRC_MODE_IRQ_ON_RX_CPU_ATTN 0x02000000 +#define GRC_MODE_IRQ_ON_MAC_ATTN 0x04000000 +#define GRC_MODE_IRQ_ON_DMA_ATTN 0x08000000 +#define GRC_MODE_IRQ_ON_FLOW_ATTN 0x10000000 +#define GRC_MODE_4X_NIC_SEND_RINGS 0x20000000 +#define GRC_MODE_MCAST_FRM_ENABLE 0x40000000 +#define GRC_MISC_CFG 0x00006804 +#define GRC_MISC_CFG_CORECLK_RESET 0x00000001 +#define GRC_MISC_CFG_PRESCALAR_MASK 0x000000fe +#define GRC_MISC_CFG_PRESCALAR_SHIFT 1 +#define GRC_MISC_CFG_BOARD_ID_MASK 0x0001e000 +#define GRC_MISC_CFG_BOARD_ID_5700 0x0001e000 +#define GRC_MISC_CFG_BOARD_ID_5701 0x00000000 +#define GRC_MISC_CFG_BOARD_ID_5702FE 0x00004000 +#define GRC_MISC_CFG_BOARD_ID_5703 0x00000000 +#define GRC_MISC_CFG_BOARD_ID_5703S 0x00002000 +#define GRC_LOCAL_CTRL 0x00006808 +#define GRC_LCLCTRL_INT_ACTIVE 0x00000001 +#define GRC_LCLCTRL_CLEARINT 0x00000002 +#define GRC_LCLCTRL_SETINT 0x00000004 +#define GRC_LCLCTRL_INT_ON_ATTN 0x00000008 +#define GRC_LCLCTRL_GPIO_INPUT0 0x00000100 +#define GRC_LCLCTRL_GPIO_INPUT1 0x00000200 +#define GRC_LCLCTRL_GPIO_INPUT2 0x00000400 +#define GRC_LCLCTRL_GPIO_OE0 0x00000800 +#define GRC_LCLCTRL_GPIO_OE1 0x00001000 +#define GRC_LCLCTRL_GPIO_OE2 0x00002000 +#define GRC_LCLCTRL_GPIO_OUTPUT0 0x00004000 +#define GRC_LCLCTRL_GPIO_OUTPUT1 0x00008000 +#define GRC_LCLCTRL_GPIO_OUTPUT2 0x00010000 +#define GRC_LCLCTRL_EXTMEM_ENABLE 0x00020000 +#define GRC_LCLCTRL_MEMSZ_MASK 0x001c0000 +#define GRC_LCLCTRL_MEMSZ_256K 0x00000000 +#define GRC_LCLCTRL_MEMSZ_512K 0x00040000 +#define GRC_LCLCTRL_MEMSZ_1M 0x00080000 +#define GRC_LCLCTRL_MEMSZ_2M 0x000c0000 +#define GRC_LCLCTRL_MEMSZ_4M 0x00100000 +#define GRC_LCLCTRL_MEMSZ_8M 0x00140000 +#define GRC_LCLCTRL_MEMSZ_16M 0x00180000 +#define GRC_LCLCTRL_BANK_SELECT 0x00200000 +#define GRC_LCLCTRL_SSRAM_TYPE 0x00400000 +#define GRC_LCLCTRL_AUTO_SEEPROM 0x01000000 +#define GRC_TIMER 0x0000680c +#define GRC_RX_CPU_EVENT 0x00006810 +#define GRC_RX_TIMER_REF 0x00006814 +#define GRC_RX_CPU_SEM 0x00006818 +#define GRC_REMOTE_RX_CPU_ATTN 0x0000681c +#define GRC_TX_CPU_EVENT 0x00006820 +#define GRC_TX_TIMER_REF 0x00006824 +#define GRC_TX_CPU_SEM 0x00006828 +#define GRC_REMOTE_TX_CPU_ATTN 0x0000682c +#define GRC_MEM_POWER_UP 0x00006830 /* 64-bit */ +#define GRC_EEPROM_ADDR 0x00006838 +#define EEPROM_ADDR_WRITE 0x00000000 +#define EEPROM_ADDR_READ 0x80000000 +#define EEPROM_ADDR_COMPLETE 0x40000000 +#define EEPROM_ADDR_FSM_RESET 0x20000000 +#define EEPROM_ADDR_DEVID_MASK 0x1c000000 +#define EEPROM_ADDR_DEVID_SHIFT 26 +#define EEPROM_ADDR_START 0x02000000 +#define EEPROM_ADDR_CLKPERD_SHIFT 16 +#define EEPROM_ADDR_ADDR_MASK 0x0000ffff +#define EEPROM_ADDR_ADDR_SHIFT 0 +#define EEPROM_DEFAULT_CLOCK_PERIOD 0x60 +#define EEPROM_CHIP_SIZE (64 * 1024) +#define GRC_EEPROM_DATA 0x0000683c +#define GRC_EEPROM_CTRL 0x00006840 +#define GRC_MDI_CTRL 0x00006844 +#define GRC_SEEPROM_DELAY 0x00006848 +/* 0x684c --> 0x6c00 unused */ + +/* 0x6c00 --> 0x7000 unused */ + +/* NVRAM Control registers */ +#define NVRAM_CMD 0x00007000 +#define NVRAM_CMD_RESET 0x00000001 +#define NVRAM_CMD_DONE 0x00000008 +#define NVRAM_CMD_GO 0x00000010 +#define NVRAM_CMD_WR 0x00000020 +#define NVRAM_CMD_RD 0x00000000 +#define NVRAM_CMD_ERASE 0x00000040 +#define NVRAM_CMD_FIRST 0x00000080 +#define NVRAM_CMD_LAST 0x00000100 +#define NVRAM_STAT 0x00007004 +#define NVRAM_WRDATA 0x00007008 +#define NVRAM_ADDR 0x0000700c +#define NVRAM_ADDR_MSK 0x00ffffff +#define NVRAM_RDDATA 0x00007010 +#define NVRAM_CFG1 0x00007014 +#define NVRAM_CFG1_FLASHIF_ENAB 0x00000001 +#define NVRAM_CFG1_BUFFERED_MODE 0x00000002 +#define NVRAM_CFG1_PASS_THRU 0x00000004 +#define NVRAM_CFG1_BIT_BANG 0x00000008 +#define NVRAM_CFG1_COMPAT_BYPASS 0x80000000 +#define NVRAM_CFG2 0x00007018 +#define NVRAM_CFG3 0x0000701c +#define NVRAM_SWARB 0x00007020 +#define SWARB_REQ_SET0 0x00000001 +#define SWARB_REQ_SET1 0x00000002 +#define SWARB_REQ_SET2 0x00000004 +#define SWARB_REQ_SET3 0x00000008 +#define SWARB_REQ_CLR0 0x00000010 +#define SWARB_REQ_CLR1 0x00000020 +#define SWARB_REQ_CLR2 0x00000040 +#define SWARB_REQ_CLR3 0x00000080 +#define SWARB_GNT0 0x00000100 +#define SWARB_GNT1 0x00000200 +#define SWARB_GNT2 0x00000400 +#define SWARB_GNT3 0x00000800 +#define SWARB_REQ0 0x00001000 +#define SWARB_REQ1 0x00002000 +#define SWARB_REQ2 0x00004000 +#define SWARB_REQ3 0x00008000 +#define NVRAM_BUFFERED_PAGE_SIZE 264 +#define NVRAM_BUFFERED_PAGE_POS 9 +/* 0x7024 --> 0x7400 unused */ + +/* 0x7400 --> 0x8000 unused */ + +/* 32K Window into NIC internal memory */ +#define NIC_SRAM_WIN_BASE 0x00008000 + +/* Offsets into first 32k of NIC internal memory. */ +#define NIC_SRAM_PAGE_ZERO 0x00000000 +#define NIC_SRAM_SEND_RCB 0x00000100 /* 16 * TG3_BDINFO_... */ +#define NIC_SRAM_RCV_RET_RCB 0x00000200 /* 16 * TG3_BDINFO_... */ +#define NIC_SRAM_STATS_BLK 0x00000300 +#define NIC_SRAM_STATUS_BLK 0x00000b00 + +#define NIC_SRAM_FIRMWARE_MBOX 0x00000b50 +#define NIC_SRAM_FIRMWARE_MBOX_MAGIC1 0x4B657654 +#define NIC_SRAM_FIRMWARE_MBOX_MAGIC2 0x4861764b /* !dma on linkchg */ + +#define NIC_SRAM_DATA_SIG 0x00000b54 +#define NIC_SRAM_DATA_SIG_MAGIC 0x4b657654 /* ascii for 'KevT' */ + +#define NIC_SRAM_DATA_CFG 0x00000b58 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_MASK 0x0000000c +#define NIC_SRAM_DATA_CFG_PHY_TYPE_UNKNOWN 0x00000000 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_COPPER 0x00000004 +#define NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER 0x00000008 +#define NIC_SRAM_DATA_CFG_LED_MODE_MASK 0x00000030 +#define NIC_SRAM_DATA_CFG_LED_MODE_UNKNOWN 0x00000000 +#define NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD 0x00000010 +#define NIC_SRAM_DATA_CFG_LED_LINK_SPD 0x00000020 + +#define NIC_SRAM_DATA_PHY_ID 0x00000b74 +#define NIC_SRAM_DATA_PHY_ID1_MASK 0xffff0000 +#define NIC_SRAM_DATA_PHY_ID2_MASK 0x0000ffff + +#define NIC_SRAM_FW_CMD_MBOX 0x00000b78 +#define FWCMD_NICDRV_ALIVE 0x00000001 +#define FWCMD_NICDRV_PAUSE_FW 0x00000002 +#define FWCMD_NICDRV_IPV4ADDR_CHG 0x00000003 +#define FWCMD_NICDRV_IPV6ADDR_CHG 0x00000004 +#define FWCMD_NICDRV_FIX_DMAR 0x00000005 +#define FWCMD_NICDRV_FIX_DMAW 0x00000006 +#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c +#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80 +#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00 +#define NIC_SRAM_FW_DRV_STATE_MBOX 0x00000c04 +#define DRV_STATE_START 0x00000001 +#define DRV_STATE_UNLOAD 0x00000002 +#define DRV_STATE_WOL 0x00000003 +#define DRV_STATE_SUSPEND 0x00000004 + +#define NIC_SRAM_FW_RESET_TYPE_MBOX 0x00000c08 + +#define NIC_SRAM_MAC_ADDR_HIGH_MBOX 0x00000c14 +#define NIC_SRAM_MAC_ADDR_LOW_MBOX 0x00000c18 + +#if TG3_MINI_RING_WORKS +#define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 +#endif + +#define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000 +#define NIC_SRAM_DMA_DESC_POOL_SIZE 0x00002000 +#define NIC_SRAM_TX_BUFFER_DESC 0x00004000 /* 512 entries */ +#define NIC_SRAM_RX_BUFFER_DESC 0x00006000 /* 256 entries */ +#define NIC_SRAM_RX_JUMBO_BUFFER_DESC 0x00007000 /* 256 entries */ +#define NIC_SRAM_MBUF_POOL_BASE 0x00008000 +#define NIC_SRAM_MBUF_POOL_SIZE 0x00018000 + +/* Currently this is fixed. */ +#define PHY_ADDR 0x01 + +/* Tigon3 specific PHY MII registers. */ +#define TG3_BMCR_SPEED1000 0x0040 + +#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */ +#define MII_TG3_CTRL_ADV_1000_HALF 0x0100 +#define MII_TG3_CTRL_ADV_1000_FULL 0x0200 +#define MII_TG3_CTRL_AS_MASTER 0x0800 +#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000 + +#define MII_TG3_EXT_CTRL 0x10 /* Extended control register */ +#define MII_TG3_EXT_CTRL_LNK3_LED_MODE 0x0002 +#define MII_TG3_EXT_CTRL_TBI 0x8000 + +#define MII_TG3_EXT_STAT 0x11 /* Extended status register */ +#define MII_TG3_EXT_STAT_LPASS 0x0100 + +#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */ + +#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */ + +#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */ + +#define MII_TG3_AUX_STAT 0x19 /* auxilliary status register */ +#define MII_TG3_AUX_STAT_LPASS 0x0004 +#define MII_TG3_AUX_STAT_SPDMASK 0x0700 +#define MII_TG3_AUX_STAT_10HALF 0x0100 +#define MII_TG3_AUX_STAT_10FULL 0x0200 +#define MII_TG3_AUX_STAT_100HALF 0x0300 +#define MII_TG3_AUX_STAT_100_4 0x0400 +#define MII_TG3_AUX_STAT_100FULL 0x0500 +#define MII_TG3_AUX_STAT_1000HALF 0x0600 +#define MII_TG3_AUX_STAT_1000FULL 0x0700 + +#define MII_TG3_ISTAT 0x1a /* IRQ status register */ +#define MII_TG3_IMASK 0x1b /* IRQ mask register */ + +/* ISTAT/IMASK event bits */ +#define MII_TG3_INT_LINKCHG 0x0002 +#define MII_TG3_INT_SPEEDCHG 0x0004 +#define MII_TG3_INT_DUPLEXCHG 0x0008 +#define MII_TG3_INT_ANEG_PAGE_RX 0x0400 + +/* XXX Add this to mii.h */ +#ifndef ADVERTISE_PAUSE +#define ADVERTISE_PAUSE_CAP 0x0400 +#endif +#ifndef ADVERTISE_PAUSE_ASYM +#define ADVERTISE_PAUSE_ASYM 0x0800 +#endif +#ifndef LPA_PAUSE +#define LPA_PAUSE_CAP 0x0400 +#endif +#ifndef LPA_PAUSE_ASYM +#define LPA_PAUSE_ASYM 0x0800 +#endif + +/* There are two ways to manage the TX descriptors on the tigon3. + * Either the descriptors are in host DMA'able memory, or they + * exist only in the cards on-chip SRAM. All 16 send bds are under + * the same mode, they may not be configured individually. + * + * The mode we use is controlled by TG3_FLAG_HOST_TXDS in tp->tg3_flags. + * + * To use host memory TX descriptors: + * 1) Set GRC_MODE_HOST_SENDBDS in GRC_MODE register. + * Make sure GRC_MODE_4X_NIC_SEND_RINGS is clear. + * 2) Allocate DMA'able memory. + * 3) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM: + * a) Set TG3_BDINFO_HOST_ADDR to DMA address of memory + * obtained in step 2 + * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC. + * c) Set len field of TG3_BDINFO_MAXLEN_FLAGS to number + * of TX descriptors. Leave flags field clear. + * 4) Access TX descriptors via host memory. The chip + * will refetch into local SRAM as needed when producer + * index mailboxes are updated. + * + * To use on-chip TX descriptors: + * 1) Set GRC_MODE_4X_NIC_SEND_RINGS in GRC_MODE register. + * Make sure GRC_MODE_HOST_SENDBDS is clear. + * 2) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM: + * a) Set TG3_BDINFO_HOST_ADDR to zero. + * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC + * c) TG3_BDINFO_MAXLEN_FLAGS is don't care. + * 3) Access TX descriptors directly in on-chip SRAM + * using normal {read,write}l(). (and not using + * pointer dereferencing of ioremap()'d memory like + * the broken Broadcom driver does) + * + * Note that BDINFO_FLAGS_DISABLED should be set in the flags field of + * TG3_BDINFO_MAXLEN_FLAGS of all unused SEND_RCB indices. + */ +struct tg3_tx_buffer_desc { + u32 addr_hi; + u32 addr_lo; + + u32 len_flags; +#define TXD_FLAG_TCPUDP_CSUM 0x0001 +#define TXD_FLAG_IP_CSUM 0x0002 +#define TXD_FLAG_END 0x0004 +#define TXD_FLAG_IP_FRAG 0x0008 +#define TXD_FLAG_IP_FRAG_END 0x0010 +#define TXD_FLAG_VLAN 0x0040 +#define TXD_FLAG_COAL_NOW 0x0080 +#define TXD_FLAG_CPU_PRE_DMA 0x0100 +#define TXD_FLAG_CPU_POST_DMA 0x0200 +#define TXD_FLAG_ADD_SRC_ADDR 0x1000 +#define TXD_FLAG_CHOOSE_SRC_ADDR 0x6000 +#define TXD_FLAG_NO_CRC 0x8000 +#define TXD_LEN_SHIFT 16 + + u32 vlan_tag; +#define TXD_VLAN_TAG_SHIFT 0 +}; + +#define TXD_ADDR 0x00UL /* 64-bit */ +#define TXD_LEN_FLAGS 0x08UL /* 32-bit (upper 16-bits are len) */ +#define TXD_VLAN_TAG 0x0cUL /* 32-bit (upper 16-bits are tag) */ +#define TXD_SIZE 0x10UL + +struct tg3_rx_buffer_desc { + u32 addr_hi; + u32 addr_lo; + + u32 idx_len; +#define RXD_IDX_MASK 0xffff0000 +#define RXD_IDX_SHIFT 16 +#define RXD_LEN_MASK 0x0000ffff +#define RXD_LEN_SHIFT 0 + + u32 type_flags; +#define RXD_TYPE_SHIFT 16 +#define RXD_FLAGS_SHIFT 0 + +#define RXD_FLAG_END 0x0004 +#if TG3_MINI_RING_WORKS +#define RXD_FLAG_MINI 0x0800 +#endif +#define RXD_FLAG_JUMBO 0x0020 +#define RXD_FLAG_VLAN 0x0040 +#define RXD_FLAG_ERROR 0x0400 +#define RXD_FLAG_IP_CSUM 0x1000 +#define RXD_FLAG_TCPUDP_CSUM 0x2000 +#define RXD_FLAG_IS_TCP 0x4000 + + u32 ip_tcp_csum; +#define RXD_IPCSUM_MASK 0xffff0000 +#define RXD_IPCSUM_SHIFT 16 +#define RXD_TCPCSUM_MASK 0x0000ffff +#define RXD_TCPCSUM_SHIFT 0 + + u32 err_vlan; + +#define RXD_VLAN_MASK 0x0000ffff + +#define RXD_ERR_BAD_CRC 0x00010000 +#define RXD_ERR_COLLISION 0x00020000 +#define RXD_ERR_LINK_LOST 0x00040000 +#define RXD_ERR_PHY_DECODE 0x00080000 +#define RXD_ERR_ODD_NIBBLE_RCVD_MII 0x00100000 +#define RXD_ERR_MAC_ABRT 0x00200000 +#define RXD_ERR_TOO_SMALL 0x00400000 +#define RXD_ERR_NO_RESOURCES 0x00800000 +#define RXD_ERR_HUGE_FRAME 0x01000000 +#define RXD_ERR_MASK 0xffff0000 + + u32 reserved; + u32 opaque; +#define RXD_OPAQUE_INDEX_MASK 0x0000ffff +#define RXD_OPAQUE_INDEX_SHIFT 0 +#define RXD_OPAQUE_RING_STD 0x00010000 +#define RXD_OPAQUE_RING_JUMBO 0x00020000 +#if TG3_MINI_RING_WORKS +#define RXD_OPAQUE_RING_MINI 0x00040000 +#endif +#define RXD_OPAQUE_RING_MASK 0x00070000 +}; + +struct tg3_ext_rx_buffer_desc { + struct { + u32 addr_hi; + u32 addr_lo; + } addrlist[3]; + u32 len2_len1; + u32 resv_len3; + struct tg3_rx_buffer_desc std; +}; + +/* We only use this when testing out the DMA engine + * at probe time. This is the internal format of buffer + * descriptors used by the chip at NIC_SRAM_DMA_DESCS. + */ +struct tg3_internal_buffer_desc { + u32 addr_hi; + u32 addr_lo; + u32 nic_mbuf; + /* XXX FIX THIS */ +#ifdef __BIG_ENDIAN + u16 cqid_sqid; + u16 len; +#else + u16 len; + u16 cqid_sqid; +#endif + u32 flags; + u32 __cookie1; + u32 __cookie2; + u32 __cookie3; +}; + +#define TG3_HW_STATUS_SIZE 0x80 +struct tg3_hw_status { + u32 status; +#define SD_STATUS_UPDATED 0x00000001 +#define SD_STATUS_LINK_CHG 0x00000002 +#define SD_STATUS_ERROR 0x00000004 + + u32 status_tag; + +#ifdef __BIG_ENDIAN + u16 rx_consumer; + u16 rx_jumbo_consumer; +#else + u16 rx_jumbo_consumer; + u16 rx_consumer; +#endif + +#ifdef __BIG_ENDIAN + u16 reserved; + u16 rx_mini_consumer; +#else + u16 rx_mini_consumer; + u16 reserved; +#endif + struct { +#ifdef __BIG_ENDIAN + u16 tx_consumer; + u16 rx_producer; +#else + u16 rx_producer; + u16 tx_consumer; +#endif + } idx[16]; +}; + +typedef struct { + u32 high, low; +} tg3_stat64_t; + +struct tg3_hw_stats { + u8 __reserved0[0x400-0x300]; + + /* Statistics maintained by Receive MAC. */ + tg3_stat64_t rx_octets; + u64 __reserved1; + tg3_stat64_t rx_fragments; + tg3_stat64_t rx_ucast_packets; + tg3_stat64_t rx_mcast_packets; + tg3_stat64_t rx_bcast_packets; + tg3_stat64_t rx_fcs_errors; + tg3_stat64_t rx_align_errors; + tg3_stat64_t rx_xon_pause_rcvd; + tg3_stat64_t rx_xoff_pause_rcvd; + tg3_stat64_t rx_mac_ctrl_rcvd; + tg3_stat64_t rx_xoff_entered; + tg3_stat64_t rx_frame_too_long_errors; + tg3_stat64_t rx_jabbers; + tg3_stat64_t rx_undersize_packets; + tg3_stat64_t rx_in_length_errors; + tg3_stat64_t rx_out_length_errors; + tg3_stat64_t rx_64_or_less_octet_packets; + tg3_stat64_t rx_65_to_127_octet_packets; + tg3_stat64_t rx_128_to_255_octet_packets; + tg3_stat64_t rx_256_to_511_octet_packets; + tg3_stat64_t rx_512_to_1023_octet_packets; + tg3_stat64_t rx_1024_to_1522_octet_packets; + tg3_stat64_t rx_1523_to_2047_octet_packets; + tg3_stat64_t rx_2048_to_4095_octet_packets; + tg3_stat64_t rx_4096_to_8191_octet_packets; + tg3_stat64_t rx_8192_to_9022_octet_packets; + + u64 __unused0[37]; + + /* Statistics maintained by Transmit MAC. */ + tg3_stat64_t tx_octets; + u64 __reserved2; + tg3_stat64_t tx_collisions; + tg3_stat64_t tx_xon_sent; + tg3_stat64_t tx_xoff_sent; + tg3_stat64_t tx_flow_control; + tg3_stat64_t tx_mac_errors; + tg3_stat64_t tx_single_collisions; + tg3_stat64_t tx_mult_collisions; + tg3_stat64_t tx_deferred; + u64 __reserved3; + tg3_stat64_t tx_excessive_collisions; + tg3_stat64_t tx_late_collisions; + tg3_stat64_t tx_collide_2times; + tg3_stat64_t tx_collide_3times; + tg3_stat64_t tx_collide_4times; + tg3_stat64_t tx_collide_5times; + tg3_stat64_t tx_collide_6times; + tg3_stat64_t tx_collide_7times; + tg3_stat64_t tx_collide_8times; + tg3_stat64_t tx_collide_9times; + tg3_stat64_t tx_collide_10times; + tg3_stat64_t tx_collide_11times; + tg3_stat64_t tx_collide_12times; + tg3_stat64_t tx_collide_13times; + tg3_stat64_t tx_collide_14times; + tg3_stat64_t tx_collide_15times; + tg3_stat64_t tx_ucast_packets; + tg3_stat64_t tx_mcast_packets; + tg3_stat64_t tx_bcast_packets; + tg3_stat64_t tx_carrier_sense_errors; + tg3_stat64_t tx_discards; + tg3_stat64_t tx_errors; + + u64 __unused1[31]; + + /* Statistics maintained by Receive List Placement. */ + tg3_stat64_t COS_rx_packets[16]; + tg3_stat64_t COS_rx_filter_dropped; + tg3_stat64_t dma_writeq_full; + tg3_stat64_t dma_write_prioq_full; + tg3_stat64_t rxbds_empty; + tg3_stat64_t rx_discards; + tg3_stat64_t rx_errors; + tg3_stat64_t rx_threshold_hit; + + u64 __unused2[9]; + + /* Statistics maintained by Send Data Initiator. */ + tg3_stat64_t COS_out_packets[16]; + tg3_stat64_t dma_readq_full; + tg3_stat64_t dma_read_prioq_full; + tg3_stat64_t tx_comp_queue_full; + + /* Statistics maintained by Host Coalescing. */ + tg3_stat64_t ring_set_send_prod_index; + tg3_stat64_t ring_status_update; + tg3_stat64_t nic_irqs; + tg3_stat64_t nic_avoided_irqs; + tg3_stat64_t nic_tx_threshold_hit; + + u8 __reserved4[0xb00-0x9c0]; +}; + +enum phy_led_mode { + led_mode_auto, + led_mode_three_link, + led_mode_link10 +}; + +/* 'mapping' is superfluous as the chip does not write into + * the tx/rx post rings so we could just fetch it from there. + * But the cache behavior is better how we are doing it now. + */ +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct tg3_config_info { + u32 flags; +}; + +struct tg3_link_config { + /* Describes what we're trying to get. */ + u32 advertising; + u16 speed; + u8 duplex; + u8 autoneg; + + /* Describes what we actually have. */ + u16 active_speed; + u8 active_duplex; +#define SPEED_INVALID 0xffff +#define DUPLEX_INVALID 0xff +#define AUTONEG_INVALID 0xff + + /* When we go in and out of low power mode we need + * to swap with this state. + */ + int phy_is_low_power; + u16 orig_speed; + u8 orig_duplex; + u8 orig_autoneg; +}; + +struct tg3_coalesce_config { + u32 rx_coalesce_ticks; + u32 rx_max_coalesced_frames; + u32 rx_coalesce_ticks_during_int; + u32 rx_max_coalesced_frames_during_int; + + u32 tx_coalesce_ticks; + u32 tx_max_coalesced_frames; + u32 tx_coalesce_ticks_during_int; + u32 tx_max_coalesced_frames_during_int; + + u32 stats_coalesce_ticks; +}; + +struct tg3_bufmgr_config { + u32 mbuf_read_dma_low_water; + u32 mbuf_mac_rx_low_water; + u32 mbuf_high_water; + + u32 mbuf_read_dma_low_water_jumbo; + u32 mbuf_mac_rx_low_water_jumbo; + u32 mbuf_high_water_jumbo; + + u32 dma_low_water; + u32 dma_high_water; +}; + +struct tg3 { + spinlock_t lock; + u32 tx_prod; + u32 tx_cons; + u32 rx_rcb_ptr; + u32 rx_std_ptr; + u32 rx_jumbo_ptr; +#if TG3_MINI_RING_WORKS + u32 rx_mini_ptr; +#endif + spinlock_t indirect_lock; + + struct net_device_stats net_stats; + unsigned long phy_crc_errors; + + /* Adaptive coalescing engine. */ + unsigned long last_rate_sample; + u32 last_rx_count; + u32 last_tx_count; + + u32 rx_offset; + u32 tg3_flags; +#define TG3_FLAG_HOST_TXDS 0x00000001 +#define TG3_FLAG_TXD_MBOX_HWBUG 0x00000002 +#define TG3_FLAG_BROKEN_CHECKSUMS 0x00000004 +#define TG3_FLAG_USE_LINKCHG_REG 0x00000008 +#define TG3_FLAG_USE_MI_INTERRUPT 0x00000010 +#define TG3_FLAG_PHY_RESET_ON_INIT 0x00000100 +#define TG3_FLAG_PCIX_TARGET_HWBUG 0x00000200 +#define TG3_FLAG_TAGGED_IRQ_STATUS 0x00000400 +#define TG3_FLAG_WOL_SPEED_100MB 0x00000800 +#define TG3_FLAG_WOL_ENABLE 0x00001000 +#define TG3_FLAG_NVRAM 0x00002000 +#define TG3_FLAG_NVRAM_BUFFERED 0x00004000 +#define TG3_FLAG_RX_PAUSE 0x00008000 +#define TG3_FLAG_TX_PAUSE 0x00010000 +#define TG3_FLAG_PCIX_MODE 0x00020000 +#define TG3_FLAG_PCI_HIGH_SPEED 0x00040000 +#define TG3_FLAG_PCI_32BIT 0x00080000 +#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000 +#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000 +#define TG3_FLAG_AUTONEG_DISABLE 0x00400000 +#define TG3_FLAG_JUMBO_ENABLE 0x00800000 +#define TG3_FLAG_10_100_ONLY 0x01000000 +#define TG3_FLAG_INIT_COMPLETE 0x80000000 + + u32 msg_enable; + + struct timer_list timer; + u16 timer_counter; + u16 timer_multiplier; + u32 timer_offset; + + struct tg3_link_config link_config; + struct tg3_coalesce_config coalesce_config; + struct tg3_bufmgr_config bufmgr_config; + + /* cache h/w values, often passed straight to h/w */ + u32 rx_mode; + u32 tx_mode; + u32 mac_mode; + u32 mi_mode; + u32 misc_host_ctrl; + u32 grc_mode; + u32 grc_local_ctrl; + u32 dma_rwctrl; + u32 coalesce_mode; + + /* PCI block */ + u16 pci_chip_rev_id; + u8 pci_cacheline_sz; + u8 pci_lat_timer; + u8 pci_hdr_type; + u8 pci_bist; + u32 pci_cfg_state[64 / sizeof(u32)]; + + int pm_cap; + + /* PHY info */ + u32 phy_id; +#define PHY_ID_MASK 0xfffffff0 +#define PHY_ID_BCM5400 0x60008040 +#define PHY_ID_BCM5401 0x60008050 +#define PHY_ID_BCM5411 0x60008070 +#define PHY_ID_BCM5701 0x60008110 +#define PHY_ID_BCM5703 0x60008160 +#define PHY_ID_BCM8002 0x60010140 +#define PHY_ID_SERDES 0xfeedbee0 +#define PHY_ID_INVALID 0xffffffff +#define PHY_ID_REV_MASK 0x0000000f +#define PHY_REV_BCM5401_B0 0x1 +#define PHY_REV_BCM5401_B2 0x3 +#define PHY_REV_BCM5401_C0 0x6 + + enum phy_led_mode led_mode; + + char board_part_number[24]; + + /* This macro assumes the passed PHY ID is already masked + * with PHY_ID_MASK. + */ +#define KNOWN_PHY_ID(X) \ + ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \ + (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ + (X) == PHY_ID_BCM5703 || \ + (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) + + unsigned long regs; + struct pci_dev *pdev; + struct net_device *dev; +#if TG3_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif + + struct tg3_rx_buffer_desc *rx_std; + struct ring_info *rx_std_buffers; + dma_addr_t rx_std_mapping; +#if TG3_MINI_RING_WORKS + struct tg3_rx_buffer_desc *rx_mini; + struct ring_info *rx_mini_buffers; + dma_addr_t rx_mini_mapping; +#endif + struct tg3_rx_buffer_desc *rx_jumbo; + struct ring_info *rx_jumbo_buffers; + dma_addr_t rx_jumbo_mapping; + + struct tg3_rx_buffer_desc *rx_rcb; + dma_addr_t rx_rcb_mapping; + + /* TX descs are only used if TG3_FLAG_HOST_TXDS is set. */ + struct tg3_tx_buffer_desc *tx_ring; + struct ring_info *tx_buffers; + dma_addr_t tx_desc_mapping; + + struct tg3_hw_status *hw_status; + dma_addr_t status_mapping; + + struct tg3_hw_stats *hw_stats; + dma_addr_t stats_mapping; +}; + +#endif /* !(_T3_H) */ diff -urN linux-2.5.6-pre3/drivers/net/tokenring/lanstreamer.c linux-2.5.6/drivers/net/tokenring/lanstreamer.c --- linux-2.5.6-pre3/drivers/net/tokenring/lanstreamer.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/tokenring/lanstreamer.c Thu Mar 7 18:24:48 2002 @@ -60,6 +60,11 @@ * malloc free checks, reviewed code. * 03/13/00 - Added spinlocks for smp * 03/08/01 - Added support for module_init() and module_exit() + * 08/15/01 - Added ioctl() functionality for debugging, changed netif_*_queue + * calls and other incorrectness - Kent Yoder + * 11/05/01 - Restructured the interrupt function, added delays, reduced the + * the number of TX descriptors to 1, which together can prevent + * the card from locking up the box - * * To Do: * @@ -84,6 +89,14 @@ #define STREAMER_NETWORK_MONITOR 0 +/* #define CONFIG_PROC_FS */ + +/* + * Allow or disallow ioctl's for debugging + */ + +#define STREAMER_IOCTL 0 + #include #include @@ -105,6 +118,7 @@ #include #include #include +#include #include #include @@ -121,7 +135,8 @@ * Official releases will only have an a.b.c version number format. */ -static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan"; +static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan\n" + " v0.5.1 03/04/02 - Kent Yoder"; static struct pci_device_id streamer_pci_tbl[] __initdata = { { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, PCI_ANY_ID, PCI_ANY_ID,}, @@ -175,6 +190,10 @@ MODULE_PARM(message_level, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i"); +#if STREAMER_IOCTL +static int streamer_ioctl(struct net_device *, struct ifreq *, int); +#endif + static int streamer_reset(struct net_device *dev); static int streamer_open(struct net_device *dev); static int streamer_xmit(struct sk_buff *skb, struct net_device *dev); @@ -206,6 +225,8 @@ __u32 mmio_start, mmio_end, mmio_flags, mmio_len; int rc=0; static int card_no=-1; + u16 pcr; + u8 cls = 0; #if STREAMER_DEBUG printk("lanstreamer::streamer_init_one, entry pdev %p\n",pdev); @@ -281,7 +302,11 @@ dev->hard_start_xmit = &streamer_xmit; dev->change_mtu = &streamer_change_mtu; dev->stop = &streamer_close; +#if STREAMER_IOCTL + dev->do_ioctl = &streamer_ioctl; +#else dev->do_ioctl = NULL; +#endif dev->set_multicast_list = &streamer_set_rx_mode; dev->get_stats = &streamer_get_stats; dev->set_mac_address = &streamer_set_mac_address; @@ -303,6 +328,27 @@ spin_lock_init(&streamer_priv->streamer_lock); + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cls); + cls <<= 2; + if (cls != SMP_CACHE_BYTES) { + printk(KERN_INFO " PCI cache line size set incorrectly " + "(%i bytes) by BIOS/FW, ", cls); + if (cls > SMP_CACHE_BYTES) + printk("expecting %i\n", SMP_CACHE_BYTES); + else { + printk("correcting to %i\n", SMP_CACHE_BYTES); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, + SMP_CACHE_BYTES >> 2); + } + } + + pci_read_config_word (pdev, PCI_COMMAND, &pcr); + + pcr |= (PCI_COMMAND_INVALIDATE | PCI_COMMAND_SERR); + + pci_write_config_word (pdev, PCI_COMMAND, pcr); + pci_read_config_word (pdev, PCI_COMMAND, &pcr); + printk("%s \n", version); printk("%s: %s. I/O at %hx, MMIO at %p, using irq %d\n",dev->name, streamer_priv->streamer_card_name, @@ -403,6 +449,7 @@ printk("GPR: %x\n", readw(streamer_mmio + GPR)); printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK)); #endif + writew(readw(streamer_mmio + BCTL) | (BCTL_RX_FIFO_8 | BCTL_TX_FIFO_8), streamer_mmio + BCTL ); if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */ writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE, @@ -558,8 +605,6 @@ do { int i; - save_flags(flags); - cli(); for (i = 0; i < SRB_COMMAND_SIZE; i += 2) { writew(0, streamer_mmio + LAPDINC); } @@ -599,11 +644,12 @@ } printk("\n"); #endif - + spin_lock_irqsave(&streamer_priv->streamer_lock, flags); streamer_priv->srb_queued = 1; /* signal solo that SRB command has been issued */ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags); while (streamer_priv->srb_queued) { interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ); @@ -617,7 +663,6 @@ break; } } - restore_flags(flags); #if STREAMER_DEBUG printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK)); @@ -767,9 +812,12 @@ streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0; streamer_priv->streamer_tx_ring[i].buffer = 0; streamer_priv->streamer_tx_ring[i].buflen = 0; + streamer_priv->streamer_tx_ring[i].rsvd1 = 0; + streamer_priv->streamer_tx_ring[i].rsvd2 = 0; + streamer_priv->streamer_tx_ring[i].rsvd3 = 0; } streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward = - virt_to_bus(&streamer_priv->streamer_tx_ring[0]);; + virt_to_bus(&streamer_priv->streamer_tx_ring[0]); streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE; streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */ @@ -941,37 +989,30 @@ __u8 *streamer_mmio = streamer_priv->streamer_mmio; __u16 sisr; __u16 misr; - __u16 sisrmask; + u8 max_intr = MAX_INTR; - sisrmask = SISR_MI; - writew(~sisrmask, streamer_mmio + SISR_MASK_RUM); + spin_lock(&streamer_priv->streamer_lock); sisr = readw(streamer_mmio + SISR); - writew(~sisr, streamer_mmio + SISR_RUM); - misr = readw(streamer_mmio + MISR_RUM); - writew(~misr, streamer_mmio + MISR_RUM); - if (!sisr) - { /* Interrupt isn't for us */ - writew(~misr,streamer_mmio+MISR_RUM); - return; - } + while((sisr & (SISR_MI | SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | + SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR)) + && (max_intr > 0)) { - spin_lock(&streamer_priv->streamer_lock); + if(sisr & SISR_PAR_ERR) { + writew(~SISR_PAR_ERR, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); + } - if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY)) - || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) { - if (sisr & SISR_SRB_REPLY) { - if (streamer_priv->srb_queued == 1) { - wake_up_interruptible(&streamer_priv->srb_wait); - } else if (streamer_priv->srb_queued == 2) { - streamer_srb_bh(dev); - } - streamer_priv->srb_queued = 0; + else if(sisr & SISR_SERR_ERR) { + writew(~SISR_SERR_ERR, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } - /* SISR_SRB_REPLY */ + + else if(sisr & SISR_MI) { + misr = readw(streamer_mmio + MISR_RUM); + if (misr & MISR_TX2_EOF) { - while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) - { + while(streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) { streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1); streamer_priv->free_tx_ring_entries++; streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len; @@ -981,6 +1022,9 @@ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd1 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd2 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd3 = 0; } netif_wake_queue(dev); } @@ -989,7 +1033,30 @@ streamer_rx(dev); } /* MISR_RX_EOF */ - if (sisr & SISR_ADAPTER_CHECK) { + + if (misr & MISR_RX_NOBUF) { + /* According to the documentation, we don't have to do anything, + * but trapping it keeps it out of /var/log/messages. + */ + } /* SISR_RX_NOBUF */ + + writew(~misr, streamer_mmio + MISR_RUM); + (void)readw(streamer_mmio + MISR_RUM); + } + + else if (sisr & SISR_SRB_REPLY) { + if (streamer_priv->srb_queued == 1) { + wake_up_interruptible(&streamer_priv->srb_wait); + } else if (streamer_priv->srb_queued == 2) { + streamer_srb_bh(dev); + } + streamer_priv->srb_queued = 0; + + writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); + } + + else if (sisr & SISR_ADAPTER_CHECK) { printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name); writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA); printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n", @@ -1001,38 +1068,37 @@ } /* SISR_ADAPTER_CHECK */ - if (sisr & SISR_ASB_FREE) { + else if (sisr & SISR_ASB_FREE) { /* Wake up anything that is waiting for the asb response */ if (streamer_priv->asb_queued) { streamer_asb_bh(dev); } + writew(~SISR_ASB_FREE, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_ASB_FREE */ - if (sisr & SISR_ARB_CMD) { + else if (sisr & SISR_ARB_CMD) { streamer_arb_cmd(dev); + writew(~SISR_ARB_CMD, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_ARB_CMD */ - if (sisr & SISR_TRB_REPLY) { + else if (sisr & SISR_TRB_REPLY) { /* Wake up anything that is waiting for the trb response */ if (streamer_priv->trb_queued) { wake_up_interruptible(&streamer_priv-> trb_wait); } streamer_priv->trb_queued = 0; + writew(~SISR_TRB_REPLY, streamer_mmio + SISR_RUM); + (void)readw(streamer_mmio + SISR_RUM); } /* SISR_TRB_REPLY */ - if (misr & MISR_RX_NOBUF) { - /* According to the documentation, we don't have to do anything, but trapping it keeps it out of - /var/log/messages. */ - } /* SISR_RX_NOBUF */ - } else { - printk(KERN_WARNING "%s: Unexpected interrupt: %x\n", - dev->name, sisr); - printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name, - readw(streamer_mmio + SISR_MASK)); - } /* One if the interrupts we want */ - writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); + sisr = readw(streamer_mmio + SISR); + max_intr--; + } /* while() */ + spin_unlock(&streamer_priv->streamer_lock) ; } @@ -1044,13 +1110,16 @@ unsigned long flags ; spin_lock_irqsave(&streamer_priv->streamer_lock, flags); - netif_stop_queue(dev); if (streamer_priv->free_tx_ring_entries) { streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0; - streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00020000 | skb->len; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data); + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd1 = skb->len; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd2 = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd3 = 0; streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len; + streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb; streamer_priv->free_tx_ring_entries--; #if STREAMER_DEBUG_PACKETS @@ -1067,12 +1136,13 @@ #endif writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA); + (void)readl(streamer_mmio + TX2LFDA); streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1); - netif_wake_queue(dev); spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags); return 0; } else { + netif_stop_queue(dev); spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags); return 1; } @@ -1092,12 +1162,13 @@ writew(htons(SRB_CLOSE_ADAPTER << 8),streamer_mmio+LAPDINC); writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC); - save_flags(flags); - cli(); + spin_lock_irqsave(&streamer_priv->streamer_lock, flags); streamer_priv->srb_queued = 1; writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags); + while (streamer_priv->srb_queued) { interruptible_sleep_on_timeout(&streamer_priv->srb_wait, @@ -1114,7 +1185,6 @@ } } - restore_flags(flags); streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1); for (i = 0; i < STREAMER_RX_RING_SIZE; i++) { @@ -1808,6 +1878,64 @@ #endif #endif +#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +static int streamer_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int i; + struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv; + u8 *streamer_mmio = streamer_priv->streamer_mmio; + + switch(cmd) { + case IOCTL_SISR_MASK: + writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); + break; + case IOCTL_SPIN_LOCK_TEST: + printk(KERN_INFO "spin_lock() called.\n"); + spin_lock(&streamer_priv->streamer_lock); + spin_unlock(&streamer_priv->streamer_lock); + printk(KERN_INFO "spin_unlock() finished.\n"); + break; + case IOCTL_PRINT_BDAS: + printk(KERN_INFO "bdas: RXBDA: %x RXLBDA: %x TX2FDA: %x TX2LFDA: %x\n", + readw(streamer_mmio + RXBDA), + readw(streamer_mmio + RXLBDA), + readw(streamer_mmio + TX2FDA), + readw(streamer_mmio + TX2LFDA)); + break; + case IOCTL_PRINT_REGISTERS: + printk(KERN_INFO "registers:\n"); + printk(KERN_INFO "SISR: %04x MISR: %04x LISR: %04x BCTL: %04x BMCTL: %04x\nmask %04x mask %04x\n", + readw(streamer_mmio + SISR), + readw(streamer_mmio + MISR_RUM), + readw(streamer_mmio + LISR), + readw(streamer_mmio + BCTL), + readw(streamer_mmio + BMCTL_SUM), + readw(streamer_mmio + SISR_MASK), + readw(streamer_mmio + MISR_MASK)); + break; + case IOCTL_PRINT_RX_BUFS: + printk(KERN_INFO "Print rx bufs:\n"); + for(i=0; istreamer_rx_ring[i].status); + break; + case IOCTL_PRINT_TX_BUFS: + printk(KERN_INFO "Print tx bufs:\n"); + for(i=0; istreamer_tx_ring[i].status); + break; + case IOCTL_RX_CMD: + streamer_rx(dev); + printk(KERN_INFO "Sent rx command.\n"); + break; + default: + printk(KERN_INFO "Bad ioctl!\n"); + } + return 0; +} +#endif + static struct pci_driver streamer_pci_driver = { name: "lanstreamer", id_table: streamer_pci_tbl, diff -urN linux-2.5.6-pre3/drivers/net/tokenring/lanstreamer.h linux-2.5.6/drivers/net/tokenring/lanstreamer.h --- linux-2.5.6-pre3/drivers/net/tokenring/lanstreamer.h Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/tokenring/lanstreamer.h Thu Mar 7 18:24:48 2002 @@ -56,11 +56,36 @@ * * 12/10/99 - Alpha Release 0.1.0 * First release to the public + * 08/15/01 - Added ioctl() definitions and others - Kent Yoder * */ +#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#include +#define IOCTL_PRINT_RX_BUFS SIOCDEVPRIVATE +#define IOCTL_PRINT_TX_BUFS SIOCDEVPRIVATE+1 +#define IOCTL_RX_CMD SIOCDEVPRIVATE+2 +#define IOCTL_TX_CMD SIOCDEVPRIVATE+3 +#define IOCTL_PRINT_REGISTERS SIOCDEVPRIVATE+4 +#define IOCTL_PRINT_BDAS SIOCDEVPRIVATE+5 +#define IOCTL_SPIN_LOCK_TEST SIOCDEVPRIVATE+6 +#define IOCTL_SISR_MASK SIOCDEVPRIVATE+7 +#endif + +/* MAX_INTR - the maximum number of times we can loop + * inside the interrupt function before returning + * control to the OS (maximum value is 256) + */ +#define MAX_INTR 5 + +#define CLS 0x0C +#define MLR 0x86 +#define LTR 0x0D + #define BCTL 0x60 #define BCTL_SOFTRESET (1<<15) +#define BCTL_RX_FIFO_8 (1<<1) +#define BCTL_TX_FIFO_8 (1<<3) #define GPR 0x4a #define GPR_AUTOSENSE (1<<2) @@ -89,6 +114,7 @@ #define SISR_MASK_RUM 0x58 #define SISR_MI (1<<15) +#define SISR_SERR_ERR (1<<14) #define SISR_TIMER (1<<11) #define SISR_LAP_PAR_ERR (1<<10) #define SISR_LAP_ACC_ERR (1<<9) @@ -218,7 +244,13 @@ /* Streamer defaults for buffers */ #define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */ -#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */ +/* Setting the number of TX descriptors to 1 is a workaround for an + * undocumented hardware problem with the lanstreamer board. Setting + * this to something higher may slightly increase the throughput you + * can get from the card, but at the risk of locking up the box. - + * + */ +#define STREAMER_TX_RING_SIZE 1 /* should be a power of 2 */ #define PKT_BUF_SZ 4096 /* Default packet size */ diff -urN linux-2.5.6-pre3/drivers/net/tulip/ChangeLog linux-2.5.6/drivers/net/tulip/ChangeLog --- linux-2.5.6-pre3/drivers/net/tulip/ChangeLog Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/tulip/ChangeLog Thu Mar 7 18:24:48 2002 @@ -1,3 +1,10 @@ +2002-03-07 Jeff Garzik + + * tulip_core (tulip_mwi_config): Use new PCI API functions + for enabling and disabled Memory-Write-Invalidate + PCI transaction. + Fix bugs in tulip MWI config also. + 2002-01-28 Stefan Rompf , Jeff Garzik diff -urN linux-2.5.6-pre3/drivers/net/tulip/Config.help linux-2.5.6/drivers/net/tulip/Config.help --- linux-2.5.6-pre3/drivers/net/tulip/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/Config.help Thu Mar 7 18:24:48 2002 @@ -0,0 +1,110 @@ +CONFIG_PCMCIA_XIRCOM + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xircom_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. + +CONFIG_PCMCIA_XIRTULIP + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xircom_tulip_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. + +CONFIG_DE2104X + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (smc9332dst), you can also try the driver for "Generic DECchip" + cards, above. However, most people with a network card of this type + will say Y here.) Do read the Ethernet-HOWTO, available from + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tulip.o. If you want to compile it as a + module, say M here and read as well + as . + +CONFIG_TULIP + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (smc9332dst), you can also try the driver for "Generic DECchip" + cards, above. However, most people with a network card of this type + will say Y here.) Do read the Ethernet-HOWTO, available from + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tulip.o. If you want to compile it as a + module, say M here and read as well + as . + +CONFIG_TULIP_MWI + This configures your Tulip card specifically for the card and + system cache line size type you are using. + + This is experimental code, not yet tested on many boards. + + If unsure, say N. + +CONFIG_TULIP_MMIO + Use PCI shared memory for the NIC registers, rather than going through + the Tulip's PIO (programmed I/O ports). Faster, but could produce + obscure bugs if your mainboard has memory controller timing issues. + If in doubt, say N. + +CONFIG_NET_TULIP + This selects the "Tulip" family of EISA/PCI network cards. + +CONFIG_DE4X5 + This is support for the DIGITAL series of PCI/EISA Ethernet cards. + These include the DE425, DE434, DE435, DE450 and DE500 models. If + you have a network card of this type, say Y and read the + Ethernet-HOWTO, available from + . More specific + information is contained in + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called de4x5.o. If you want to compile it as a + module, say M here and read as well + as . + +CONFIG_WINBOND_840 + This driver is for the Winbond W89c840 chip. It also works with + the TX9882 chip on the Compex RL100-ATX board. + More specific information and updates are available from + . + +CONFIG_DM9102 + This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from + Davicom (). If you have such a network + (Ethernet) card, say Y. Some information is contained in the file + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called dmfe.o. If you want to compile it as a + module, say M here and read as well + as . + diff -urN linux-2.5.6-pre3/drivers/net/tulip/Config.in linux-2.5.6/drivers/net/tulip/Config.in --- linux-2.5.6-pre3/drivers/net/tulip/Config.in Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/Config.in Thu Mar 7 18:24:48 2002 @@ -0,0 +1,27 @@ +# +# Tulip family network device configuration +# + +mainmenu_option next_comment +comment '"Tulip" family network device support' + +bool '"Tulip" family network device support' CONFIG_NET_TULIP +if [ "$CONFIG_NET_TULIP" = "y" ]; then + dep_tristate ' Early DECchip Tulip (dc2104x) PCI support (EXPERIMENTAL)' CONFIG_DE2104X $CONFIG_PCI $CONFIG_EXPERIMENTAL + dep_tristate ' DECchip Tulip (dc2114x) PCI support' CONFIG_TULIP $CONFIG_PCI + if [ "$CONFIG_TULIP" = "y" -o "$CONFIG_TULIP" = "m" ]; then + dep_bool ' New bus configuration (EXPERIMENTAL)' CONFIG_TULIP_MWI $CONFIG_EXPERIMENTAL + bool ' Use PCI shared mem for NIC registers' CONFIG_TULIP_MMIO + fi + if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then + tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 + fi + dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI + dep_tristate ' Davicom DM910x/DM980x support' CONFIG_DM9102 $CONFIG_PCI + if [ "$CONFIG_CARDBUS" = "y" ]; then + tristate ' Xircom CardBus support (new driver)' CONFIG_PCMCIA_XIRCOM + tristate ' Xircom Tulip-like CardBus support (old driver)' CONFIG_PCMCIA_XIRTULIP + fi +fi + +endmenu diff -urN linux-2.5.6-pre3/drivers/net/tulip/Makefile linux-2.5.6/drivers/net/tulip/Makefile --- linux-2.5.6-pre3/drivers/net/tulip/Makefile Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/drivers/net/tulip/Makefile Thu Mar 7 18:24:48 2002 @@ -1,17 +1,36 @@ # -# Makefile for the Tulip ethernet driver +# drivers/net/tulip/Makefile # -# 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). +# Makefile for the Linux "Tulip" family network device drivers. # -# Note 2! The CFLAGS definitions are now in the main makefile... -O_TARGET := tulip.o +O_TARGET := tulip_net.o -obj-y := eeprom.o interrupt.o media.o \ - timer.o tulip_core.o \ - 21142.o pnic.o pnic2.o -obj-m := $(O_TARGET) +obj-y := +obj-m := +obj-n := +obj- := + +# Things that need to export symbols +export-objs := + +obj-$(CONFIG_PCMCIA_XIRTULIP) += xircom_tulip_cb.o +obj-$(CONFIG_PCMCIA_XIRCOM) += xircom_cb.o +obj-$(CONFIG_DM9102) += dmfe.o +obj-$(CONFIG_WINBOND_840) += winbond-840.o +obj-$(CONFIG_DE2104X) += de2104x.o +obj-$(CONFIG_TULIP) += tulip.o +obj-$(CONFIG_DE4X5) += de4x5.o + +# Declare multi-part drivers. +list-multi := tulip.o + +tulip-objs := eeprom.o interrupt.o media.o \ + timer.o tulip_core.o \ + 21142.o pnic.o pnic2.o include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. +tulip.o: $(tulip-objs) + $(LD) -r -o $@ $(tulip-objs) diff -urN linux-2.5.6-pre3/drivers/net/tulip/de2104x.c linux-2.5.6/drivers/net/tulip/de2104x.c --- linux-2.5.6-pre3/drivers/net/tulip/de2104x.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/de2104x.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,2240 @@ +/* de2104x.c: A Linux PCI Ethernet driver for Intel/Digital 21040/1 chips. */ +/* + Copyright 2001 Jeff Garzik + + Copyright 1994, 1995 Digital Equipment Corporation. [de4x5.c] + Written/copyright 1994-2001 by Donald Becker. [tulip.c] + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + See the file COPYING in this distribution for more information. + + TODO, in rough priority order: + * Support forcing media type with a module parameter, + like dl2k.c/sundance.c + * Constants (module parms?) for Rx work limit + * Complete reset on PciErr + * Jumbo frames / dev->change_mtu + * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error + * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error + * Implement Tx software interrupt mitigation via + Tx descriptor bit + + */ + +#define DRV_NAME "de2104x" +#define DRV_VERSION "0.5.4" +#define DRV_RELDATE "Jan 1, 2002" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These identify the driver base version and may not be removed. */ +static char version[] __initdata = +KERN_INFO DRV_NAME " PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; + +MODULE_AUTHOR("Jeff Garzik "); +MODULE_DESCRIPTION("Intel/Digital 21040/1 series PCI Ethernet driver"); +MODULE_LICENSE("GPL"); + +static int debug = -1; +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ + || defined(__sparc_) || defined(__ia64__) \ + || defined(__sh__) || defined(__mips__) +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif +MODULE_PARM (rx_copybreak, "i"); +MODULE_PARM_DESC (rx_copybreak, "de2104x Breakpoint at which Rx packets are copied"); + +#define PFX DRV_NAME ": " + +#define DE_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) + +#define DE_RX_RING_SIZE 64 +#define DE_TX_RING_SIZE 64 +#define DE_RING_BYTES \ + ((sizeof(struct de_desc) * DE_RX_RING_SIZE) + \ + (sizeof(struct de_desc) * DE_TX_RING_SIZE)) +#define NEXT_TX(N) (((N) + 1) & (DE_TX_RING_SIZE - 1)) +#define NEXT_RX(N) (((N) + 1) & (DE_RX_RING_SIZE - 1)) +#define TX_BUFFS_AVAIL(CP) \ + (((CP)->tx_tail <= (CP)->tx_head) ? \ + (CP)->tx_tail + (DE_TX_RING_SIZE - 1) - (CP)->tx_head : \ + (CP)->tx_tail - (CP)->tx_head - 1) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define RX_OFFSET 2 + +#define DE_SETUP_SKB ((struct sk_buff *) 1) +#define DE_DUMMY_SKB ((struct sk_buff *) 2) +#define DE_SETUP_FRAME_WORDS 96 +#define DE_EEPROM_WORDS 256 +#define DE_EEPROM_SIZE (DE_EEPROM_WORDS * sizeof(u16)) +#define DE_MAX_MEDIA 5 + +#define DE_MEDIA_TP_AUTO 0 +#define DE_MEDIA_BNC 1 +#define DE_MEDIA_AUI 2 +#define DE_MEDIA_TP 3 +#define DE_MEDIA_TP_FD 4 +#define DE_MEDIA_INVALID DE_MAX_MEDIA +#define DE_MEDIA_FIRST 0 +#define DE_MEDIA_LAST (DE_MAX_MEDIA - 1) +#define DE_AUI_BNC (SUPPORTED_AUI | SUPPORTED_BNC) + +#define DE_TIMER_LINK (60 * HZ) +#define DE_TIMER_NO_LINK (5 * HZ) + +#define DE_NUM_REGS 16 +#define DE_REGS_SIZE (DE_NUM_REGS * sizeof(u32)) +#define DE_REGS_VER 1 + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + +#define DE_UNALIGNED_16(a) (u16)(get_unaligned((u16 *)(a))) + +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +enum { + /* NIC registers */ + BusMode = 0x00, + TxPoll = 0x08, + RxPoll = 0x10, + RxRingAddr = 0x18, + TxRingAddr = 0x20, + MacStatus = 0x28, + MacMode = 0x30, + IntrMask = 0x38, + RxMissed = 0x40, + ROMCmd = 0x48, + CSR11 = 0x58, + SIAStatus = 0x60, + CSR13 = 0x68, + CSR14 = 0x70, + CSR15 = 0x78, + PCIPM = 0x40, + + /* BusMode bits */ + CmdReset = (1 << 0), + CacheAlign16 = 0x00008000, + BurstLen4 = 0x00000400, + + /* Rx/TxPoll bits */ + NormalTxPoll = (1 << 0), + NormalRxPoll = (1 << 0), + + /* Tx/Rx descriptor status bits */ + DescOwn = (1 << 31), + RxError = (1 << 15), + RxErrLong = (1 << 7), + RxErrCRC = (1 << 1), + RxErrFIFO = (1 << 0), + RxErrRunt = (1 << 11), + RxErrFrame = (1 << 14), + RingEnd = (1 << 25), + FirstFrag = (1 << 29), + LastFrag = (1 << 30), + TxError = (1 << 15), + TxFIFOUnder = (1 << 1), + TxLinkFail = (1 << 2) | (1 << 10) | (1 << 11), + TxMaxCol = (1 << 8), + TxOWC = (1 << 9), + TxJabber = (1 << 14), + SetupFrame = (1 << 27), + TxSwInt = (1 << 31), + + /* MacStatus bits */ + IntrOK = (1 << 16), + IntrErr = (1 << 15), + RxIntr = (1 << 6), + RxEmpty = (1 << 7), + TxIntr = (1 << 0), + TxEmpty = (1 << 2), + PciErr = (1 << 13), + TxState = (1 << 22) | (1 << 21) | (1 << 20), + RxState = (1 << 19) | (1 << 18) | (1 << 17), + LinkFail = (1 << 12), + LinkPass = (1 << 4), + RxStopped = (1 << 8), + TxStopped = (1 << 1), + + /* MacMode bits */ + TxEnable = (1 << 13), + RxEnable = (1 << 1), + RxTx = TxEnable | RxEnable, + FullDuplex = (1 << 9), + AcceptAllMulticast = (1 << 7), + AcceptAllPhys = (1 << 6), + BOCnt = (1 << 5), + MacModeClear = (1<<12) | (1<<11) | (1<<10) | (1<<8) | (1<<3) | + RxTx | BOCnt | AcceptAllPhys | AcceptAllMulticast, + + /* ROMCmd bits */ + EE_SHIFT_CLK = 0x02, /* EEPROM shift clock. */ + EE_CS = 0x01, /* EEPROM chip select. */ + EE_DATA_WRITE = 0x04, /* Data from the Tulip to EEPROM. */ + EE_WRITE_0 = 0x01, + EE_WRITE_1 = 0x05, + EE_DATA_READ = 0x08, /* Data from the EEPROM chip. */ + EE_ENB = (0x4800 | EE_CS), + + /* The EEPROM commands include the alway-set leading bit. */ + EE_READ_CMD = 6, + + /* RxMissed bits */ + RxMissedOver = (1 << 16), + RxMissedMask = 0xffff, + + /* SROM-related bits */ + SROMC0InfoLeaf = 27, + MediaBlockMask = 0x3f, + MediaCustomCSRs = (1 << 6), + + /* PCIPM bits */ + PM_Sleep = (1 << 31), + PM_Snooze = (1 << 30), + PM_Mask = PM_Sleep | PM_Snooze, + + /* SIAStatus bits */ + NWayState = (1 << 14) | (1 << 13) | (1 << 12), + NWayRestart = (1 << 12), + NonselPortActive = (1 << 9), + LinkFailStatus = (1 << 2), + NetCxnErr = (1 << 1), +}; + +static const u32 de_intr_mask = + IntrOK | IntrErr | RxIntr | RxEmpty | TxIntr | TxEmpty | + LinkPass | LinkFail | PciErr; + +/* + * Set the programmable burst length to 4 longwords for all: + * DMA errors result without these values. Cache align 16 long. + */ +static const u32 de_bus_mode = CacheAlign16 | BurstLen4; + +struct de_srom_media_block { + u8 opts; + u16 csr13; + u16 csr14; + u16 csr15; +} __attribute__((packed)); + +struct de_srom_info_leaf { + u16 default_media; + u8 n_blocks; + u8 unused; +} __attribute__((packed)); + +struct de_desc { + u32 opts1; + u32 opts2; + u32 addr1; + u32 addr2; +}; + +struct media_info { + u16 type; /* DE_MEDIA_xxx */ + u16 csr13; + u16 csr14; + u16 csr15; +}; + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct de_private { + unsigned tx_head; + unsigned tx_tail; + unsigned rx_tail; + + void *regs; + struct net_device *dev; + spinlock_t lock; + + struct de_desc *rx_ring; + struct de_desc *tx_ring; + struct ring_info tx_skb[DE_TX_RING_SIZE]; + struct ring_info rx_skb[DE_RX_RING_SIZE]; + unsigned rx_buf_sz; + dma_addr_t ring_dma; + + u32 msg_enable; + + struct net_device_stats net_stats; + + struct pci_dev *pdev; + u32 macmode; + + u16 setup_frame[DE_SETUP_FRAME_WORDS]; + + u32 media_type; + u32 media_supported; + u32 media_advertise; + struct media_info media[DE_MAX_MEDIA]; + struct timer_list media_timer; + + u8 *ee_data; + unsigned board_idx; + unsigned de21040 : 1; + unsigned media_lock : 1; +}; + + +static void de_set_rx_mode (struct net_device *dev); +static void de_tx (struct de_private *de); +static void de_clean_rings (struct de_private *de); +static void de_media_interrupt (struct de_private *de, u32 status); +static void de21040_media_timer (unsigned long data); +static void de21041_media_timer (unsigned long data); +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); + + +static struct pci_device_id de_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { }, +}; +MODULE_DEVICE_TABLE(pci, de_pci_tbl); + +static const char * const media_name[DE_MAX_MEDIA] = { + "10baseT auto", + "BNC", + "AUI", + "10baseT-HD", + "10baseT-FD" +}; + +/* 21040 transceiver register settings: + * TP AUTO(unused), BNC(unused), AUI, TP, TP FD*/ +static u16 t21040_csr13[] = { 0, 0, 0x8F09, 0x8F01, 0x8F01, }; +static u16 t21040_csr14[] = { 0, 0, 0x0705, 0xFFFF, 0xFFFD, }; +static u16 t21040_csr15[] = { 0, 0, 0x0006, 0x0000, 0x0000, }; + +/* 21041 transceiver register settings: TP AUTO, BNC, AUI, TP, TP FD*/ +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x6F3F, 0x6F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + + +static inline unsigned long +msec_to_jiffies(unsigned long ms) +{ + return (((ms)*HZ+999)/1000); +} + + +#define dr32(reg) readl(de->regs + (reg)) +#define dw32(reg,val) writel((val), de->regs + (reg)) + + +static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, + u32 status, u32 len) +{ + if (netif_msg_rx_err (de)) + printk (KERN_DEBUG + "%s: rx err, slot %d status 0x%x len %d\n", + de->dev->name, rx_tail, status, len); + + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (netif_msg_rx_err(de)) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + de->dev->name, status); + de->net_stats.rx_length_errors++; + } + } else if (status & RxError) { + /* There was a fatal error. */ + de->net_stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) de->net_stats.rx_length_errors++; + if (status & RxErrCRC) de->net_stats.rx_crc_errors++; + if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; + } +} + +static void de_rx (struct de_private *de) +{ + unsigned rx_tail = de->rx_tail; + unsigned rx_work = DE_RX_RING_SIZE; + unsigned drop = 0; + int rc; + + while (rx_work--) { + u32 status, len; + dma_addr_t mapping; + struct sk_buff *skb, *copy_skb; + unsigned copying_skb, buflen; + + skb = de->rx_skb[rx_tail].skb; + if (!skb) + BUG(); + rmb(); + status = le32_to_cpu(de->rx_ring[rx_tail].opts1); + if (status & DescOwn) + break; + + len = ((status >> 16) & 0x7ff) - 4; + mapping = de->rx_skb[rx_tail].mapping; + + if (unlikely(drop)) { + de->net_stats.rx_dropped++; + goto rx_next; + } + + if (unlikely((status & 0x38008300) != 0x0300)) { + de_rx_err_acct(de, rx_tail, status, len); + goto rx_next; + } + + copying_skb = (len <= rx_copybreak); + + if (unlikely(netif_msg_rx_status(de))) + printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d copying? %d\n", + de->dev->name, rx_tail, status, len, + copying_skb); + + buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; + copy_skb = dev_alloc_skb (buflen); + if (unlikely(!copy_skb)) { + de->net_stats.rx_dropped++; + drop = 1; + rx_work = 100; + goto rx_next; + } + copy_skb->dev = de->dev; + + if (!copying_skb) { + pci_unmap_single(de->pdev, mapping, + buflen, PCI_DMA_FROMDEVICE); + skb_put(skb, len); + + mapping = + de->rx_skb[rx_tail].mapping = + pci_map_single(de->pdev, copy_skb->tail, + buflen, PCI_DMA_FROMDEVICE); + de->rx_skb[rx_tail].skb = copy_skb; + } else { + pci_dma_sync_single(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + skb_reserve(copy_skb, RX_OFFSET); + memcpy(skb_put(copy_skb, len), skb->tail, len); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + skb->protocol = eth_type_trans (skb, de->dev); + + de->net_stats.rx_packets++; + de->net_stats.rx_bytes += skb->len; + de->dev->last_rx = jiffies; + rc = netif_rx (skb); + if (rc == NET_RX_DROP) + drop = 1; + +rx_next: + de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); + if (rx_tail == (DE_RX_RING_SIZE - 1)) + de->rx_ring[rx_tail].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); + rx_tail = NEXT_RX(rx_tail); + } + + if (!rx_work) + printk(KERN_WARNING "%s: rx work limit reached\n", de->dev->name); + + de->rx_tail = rx_tail; +} + +static void de_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = dev_instance; + struct de_private *de = dev->priv; + u32 status; + + status = dr32(MacStatus); + if ((!(status & (IntrOK|IntrErr))) || (status == 0xFFFF)) + return; + + if (netif_msg_intr(de)) + printk(KERN_DEBUG "%s: intr, status %08x mode %08x desc %u/%u/%u\n", + dev->name, status, dr32(MacMode), de->rx_tail, de->tx_head, de->tx_tail); + + dw32(MacStatus, status); + + if (status & (RxIntr | RxEmpty)) { + de_rx(de); + if (status & RxEmpty) + dw32(RxPoll, NormalRxPoll); + } + + spin_lock(&de->lock); + + if (status & (TxIntr | TxEmpty)) + de_tx(de); + + if (status & (LinkPass | LinkFail)) + de_media_interrupt(de, status); + + spin_unlock(&de->lock); + + if (status & PciErr) { + u16 pci_status; + + pci_read_config_word(de->pdev, PCI_STATUS, &pci_status); + pci_write_config_word(de->pdev, PCI_STATUS, pci_status); + printk(KERN_ERR "%s: PCI bus error, status=%08x, PCI status=%04x\n", + dev->name, status, pci_status); + } +} + +static void de_tx (struct de_private *de) +{ + unsigned tx_head = de->tx_head; + unsigned tx_tail = de->tx_tail; + + while (tx_tail != tx_head) { + struct sk_buff *skb; + u32 status; + + rmb(); + status = le32_to_cpu(de->tx_ring[tx_tail].opts1); + if (status & DescOwn) + break; + + skb = de->tx_skb[tx_tail].skb; + if (!skb) + BUG(); + if (unlikely(skb == DE_DUMMY_SKB)) + goto next; + + if (unlikely(skb == DE_SETUP_SKB)) { + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + sizeof(de->setup_frame), PCI_DMA_TODEVICE); + goto next; + } + + pci_unmap_single(de->pdev, de->tx_skb[tx_tail].mapping, + skb->len, PCI_DMA_TODEVICE); + + if (status & LastFrag) { + if (status & TxError) { + if (netif_msg_tx_err(de)) + printk(KERN_DEBUG "%s: tx err, status 0x%x\n", + de->dev->name, status); + de->net_stats.tx_errors++; + if (status & TxOWC) + de->net_stats.tx_window_errors++; + if (status & TxMaxCol) + de->net_stats.tx_aborted_errors++; + if (status & TxLinkFail) + de->net_stats.tx_carrier_errors++; + if (status & TxFIFOUnder) + de->net_stats.tx_fifo_errors++; + } else { + de->net_stats.tx_packets++; + de->net_stats.tx_bytes += skb->len; + if (netif_msg_tx_done(de)) + printk(KERN_DEBUG "%s: tx done, slot %d\n", de->dev->name, tx_tail); + } + dev_kfree_skb_irq(skb); + } + +next: + de->tx_skb[tx_tail].skb = NULL; + + tx_tail = NEXT_TX(tx_tail); + } + + de->tx_tail = tx_tail; + + if (netif_queue_stopped(de->dev) && (TX_BUFFS_AVAIL(de) > (DE_TX_RING_SIZE / 4))) + netif_wake_queue(de->dev); +} + +static int de_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct de_private *de = dev->priv; + unsigned int entry, tx_free; + u32 mapping, len, flags = FirstFrag | LastFrag; + struct de_desc *txd; + + spin_lock_irq(&de->lock); + + tx_free = TX_BUFFS_AVAIL(de); + if (tx_free == 0) { + netif_stop_queue(dev); + spin_unlock_irq(&de->lock); + return 1; + } + tx_free--; + + entry = de->tx_head; + + txd = &de->tx_ring[entry]; + + len = skb->len; + mapping = pci_map_single(de->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (entry == (DE_TX_RING_SIZE - 1)) + flags |= RingEnd; + if (!tx_free || (tx_free == (DE_TX_RING_SIZE / 2))) + flags |= TxSwInt; + flags |= len; + txd->opts2 = cpu_to_le32(flags); + txd->addr1 = cpu_to_le32(mapping); + + de->tx_skb[entry].skb = skb; + de->tx_skb[entry].mapping = mapping; + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + de->tx_head = NEXT_TX(entry); + if (netif_msg_tx_queued(de)) + printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", + dev->name, entry, skb->len); + + if (tx_free == 0) + netif_stop_queue(dev); + + spin_unlock_irq(&de->lock); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + dev->trans_start = jiffies; + + return 0; +} + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling de->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +#undef set_bit_le +#define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0) + +static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = dev->priv; + u16 hash_table[32]; + struct dev_mc_list *mclist; + int i; + u16 *eaddrs; + + memset(hash_table, 0, sizeof(hash_table)); + set_bit_le(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int index = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + + set_bit_le(index, hash_table); + + for (i = 0; i < 32; i++) { + *setup_frm++ = hash_table[i]; + *setup_frm++ = hash_table[i]; + } + setup_frm = &de->setup_frame[13*6]; + } + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + +static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev) +{ + struct de_private *de = dev->priv; + struct dev_mc_list *mclist; + int i; + u16 *eaddrs; + + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &de->setup_frame[15*6]; + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +} + + +static void __de_set_rx_mode (struct net_device *dev) +{ + struct de_private *de = dev->priv; + u32 macmode; + unsigned int entry; + u32 mapping; + struct de_desc *txd; + struct de_desc *dummy_txd = NULL; + + macmode = de->macmode & ~(AcceptAllMulticast | AcceptAllPhys); + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + macmode |= AcceptAllMulticast | AcceptAllPhys; + goto out; + } + + if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + macmode |= AcceptAllMulticast; + goto out; + } + + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ + if (dev->mc_count > 14) /* Must use a multicast hash table. */ + build_setup_frame_hash (de->setup_frame, dev); + else + build_setup_frame_perfect (de->setup_frame, dev); + + /* + * Now add this frame to the Tx list. + */ + + entry = de->tx_head; + + /* Avoid a chip errata by prefixing a dummy entry. */ + if (entry != 0) { + de->tx_skb[entry].skb = DE_DUMMY_SKB; + + dummy_txd = &de->tx_ring[entry]; + dummy_txd->opts2 = (entry == (DE_TX_RING_SIZE - 1)) ? + cpu_to_le32(RingEnd) : 0; + dummy_txd->addr1 = 0; + + /* Must set DescOwned later to avoid race with chip */ + + entry = NEXT_TX(entry); + } + + de->tx_skb[entry].skb = DE_SETUP_SKB; + de->tx_skb[entry].mapping = mapping = + pci_map_single (de->pdev, de->setup_frame, + sizeof (de->setup_frame), PCI_DMA_TODEVICE); + + /* Put the setup frame on the Tx list. */ + txd = &de->tx_ring[entry]; + if (entry == (DE_TX_RING_SIZE - 1)) + txd->opts2 = cpu_to_le32(SetupFrame | RingEnd | sizeof (de->setup_frame)); + else + txd->opts2 = cpu_to_le32(SetupFrame | sizeof (de->setup_frame)); + txd->addr1 = cpu_to_le32(mapping); + wmb(); + + txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + + if (dummy_txd) { + dummy_txd->opts1 = cpu_to_le32(DescOwn); + wmb(); + } + + de->tx_head = NEXT_TX(entry); + + if (TX_BUFFS_AVAIL(de) < 0) + BUG(); + if (TX_BUFFS_AVAIL(de) == 0) + netif_stop_queue(dev); + + /* Trigger an immediate transmit demand. */ + dw32(TxPoll, NormalTxPoll); + +out: + if (macmode != de->macmode) { + dw32 (MacMode, macmode); + de->macmode = macmode; + } +} + +static void de_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct de_private *de = dev->priv; + + spin_lock_irqsave (&de->lock, flags); + __de_set_rx_mode(dev); + spin_unlock_irqrestore (&de->lock, flags); +} + +static inline void de_rx_missed(struct de_private *de, u32 rx_missed) +{ + if (unlikely(rx_missed & RxMissedOver)) + de->net_stats.rx_missed_errors += RxMissedMask; + else + de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); +} + +static void __de_get_stats(struct de_private *de) +{ + u32 tmp = dr32(RxMissed); /* self-clearing */ + + de_rx_missed(de, tmp); +} + +static struct net_device_stats *de_get_stats(struct net_device *dev) +{ + struct de_private *de = dev->priv; + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&de->lock); + if (netif_running(dev) && netif_device_present(dev)) + __de_get_stats(de); + spin_unlock_irq(&de->lock); + + return &de->net_stats; +} + +static inline int de_is_running (struct de_private *de) +{ + return (dr32(MacStatus) & (RxState | TxState)) ? 1 : 0; +} + +static void de_stop_rxtx (struct de_private *de) +{ + u32 macmode; + unsigned int work = 1000; + + macmode = dr32(MacMode); + if (macmode & RxTx) { + dw32(MacMode, macmode & ~RxTx); + dr32(MacMode); + } + + while (--work > 0) { + if (!de_is_running(de)) + return; + cpu_relax(); + } + + printk(KERN_WARNING "%s: timeout expired stopping DMA\n", de->dev->name); +} + +static inline void de_start_rxtx (struct de_private *de) +{ + u32 macmode; + + macmode = dr32(MacMode); + if ((macmode & RxTx) != RxTx) { + dw32(MacMode, macmode | RxTx); + dr32(MacMode); + } +} + +static void de_stop_hw (struct de_private *de) +{ + + udelay(5); + dw32(IntrMask, 0); + + de_stop_rxtx(de); + + dw32(MacStatus, dr32(MacStatus)); + + udelay(10); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; +} + +static void de_link_up(struct de_private *de) +{ + if (!netif_carrier_ok(de->dev)) { + netif_carrier_on(de->dev); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link up, media %s\n", + de->dev->name, media_name[de->media_type]); + } +} + +static void de_link_down(struct de_private *de) +{ + if (netif_carrier_ok(de->dev)) { + netif_carrier_off(de->dev); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link down\n", de->dev->name); + } +} + +static void de_set_media (struct de_private *de) +{ + unsigned media = de->media_type; + + if (de_is_running(de)) + BUG(); + + if (de->de21040) + dw32(CSR11, FULL_DUPLEX_MAGIC); + dw32(CSR13, 0); /* Reset phy */ + dw32(CSR14, de->media[media].csr14); + dw32(CSR15, de->media[media].csr15); + dw32(CSR13, de->media[media].csr13); + + /* must delay 10ms before writing to other registers, + * especially CSR6 + */ + mdelay(10); + + if (media == DE_MEDIA_TP_FD) + de->macmode |= FullDuplex; + else + de->macmode &= ~FullDuplex; + + if (netif_msg_link(de)) { + printk(KERN_INFO "%s: set link %s\n" + KERN_INFO "%s: mode 0x%x, sia 0x%x,0x%x,0x%x,0x%x\n" + KERN_INFO "%s: set mode 0x%x, set sia 0x%x,0x%x,0x%x\n", + de->dev->name, media_name[media], + de->dev->name, dr32(MacMode), dr32(SIAStatus), + dr32(CSR13), dr32(CSR14), dr32(CSR15), + de->dev->name, de->macmode, de->media[media].csr13, + de->media[media].csr14, de->media[media].csr15); + } +} + +static void de_next_media (struct de_private *de, u32 *media, + unsigned int n_media) +{ + unsigned int i; + + for (i = 0; i < n_media; i++) { + if (de_ok_to_advertise(de, media[i])) { + de->media_type = media[i]; + return; + } + } +} + +static void de21040_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if (de->media_type != DE_MEDIA_AUI && (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: %s link ok, status %x\n", + dev->name, media_name[de->media_type], + status); + return; + } + + de_link_down(de); + + if (de->media_lock) + return; + + if (de->media_type == DE_MEDIA_AUI) { + u32 next_state = DE_MEDIA_TP; + de_next_media(de, &next_state, 1); + } else { + u32 next_state = DE_MEDIA_AUI; + de_next_media(de, &next_state, 1); + } + + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: no link, trying media %s, status %x\n", + dev->name, media_name[de->media_type], status); +} + +static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media) +{ + switch (new_media) { + case DE_MEDIA_TP_AUTO: + if (!(de->media_advertise & ADVERTISED_Autoneg)) + return 0; + if (!(de->media_advertise & (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full))) + return 0; + break; + case DE_MEDIA_BNC: + if (!(de->media_advertise & ADVERTISED_BNC)) + return 0; + break; + case DE_MEDIA_AUI: + if (!(de->media_advertise & ADVERTISED_AUI)) + return 0; + break; + case DE_MEDIA_TP: + if (!(de->media_advertise & ADVERTISED_10baseT_Half)) + return 0; + break; + case DE_MEDIA_TP_FD: + if (!(de->media_advertise & ADVERTISED_10baseT_Full)) + return 0; + break; + } + + return 1; +} + +static void de21041_media_timer (unsigned long data) +{ + struct de_private *de = (struct de_private *) data; + struct net_device *dev = de->dev; + u32 status = dr32(SIAStatus); + unsigned int carrier; + unsigned long flags; + + carrier = (status & NetCxnErr) ? 0 : 1; + + if (carrier) { + if ((de->media_type == DE_MEDIA_TP_AUTO || + de->media_type == DE_MEDIA_TP || + de->media_type == DE_MEDIA_TP_FD) && + (status & LinkFailStatus)) + goto no_link_yet; + + de->media_timer.expires = jiffies + DE_TIMER_LINK; + add_timer(&de->media_timer); + if (!netif_carrier_ok(dev)) + de_link_up(de); + else + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: %s link ok, mode %x status %x\n", + dev->name, media_name[de->media_type], + dr32(MacMode), status); + return; + } + + de_link_down(de); + + /* if media type locked, don't switch media */ + if (de->media_lock) + goto set_media; + + /* if activity detected, use that as hint for new media type */ + if (status & NonselPortActive) { + unsigned int have_media = 1; + + /* if AUI/BNC selected, then activity is on TP port */ + if (de->media_type == DE_MEDIA_AUI || + de->media_type == DE_MEDIA_BNC) { + if (de_ok_to_advertise(de, DE_MEDIA_TP_AUTO)) + de->media_type = DE_MEDIA_TP_AUTO; + else + have_media = 0; + } + + /* TP selected. If there is only TP and BNC, then it's BNC */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_BNC) && + de_ok_to_advertise(de, DE_MEDIA_BNC)) + de->media_type = DE_MEDIA_BNC; + + /* TP selected. If there is only TP and AUI, then it's AUI */ + else if (((de->media_supported & DE_AUI_BNC) == SUPPORTED_AUI) && + de_ok_to_advertise(de, DE_MEDIA_AUI)) + de->media_type = DE_MEDIA_AUI; + + /* otherwise, ignore the hint */ + else + have_media = 0; + + if (have_media) + goto set_media; + } + + /* + * Absent or ambiguous activity hint, move to next advertised + * media state. If de->media_type is left unchanged, this + * simply resets the PHY and reloads the current media settings. + */ + if (de->media_type == DE_MEDIA_AUI) { + u32 next_states[] = { DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else if (de->media_type == DE_MEDIA_BNC) { + u32 next_states[] = { DE_MEDIA_TP_AUTO, DE_MEDIA_AUI }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } else { + u32 next_states[] = { DE_MEDIA_AUI, DE_MEDIA_BNC, DE_MEDIA_TP_AUTO }; + de_next_media(de, next_states, ARRAY_SIZE(next_states)); + } + +set_media: + spin_lock_irqsave(&de->lock, flags); + de_stop_rxtx(de); + spin_unlock_irqrestore(&de->lock, flags); + de_set_media(de); + de_start_rxtx(de); + +no_link_yet: + de->media_timer.expires = jiffies + DE_TIMER_NO_LINK; + add_timer(&de->media_timer); + + if (netif_msg_timer(de)) + printk(KERN_INFO "%s: no link, trying media %s, status %x\n", + dev->name, media_name[de->media_type], status); +} + +static void de_media_interrupt (struct de_private *de, u32 status) +{ + if (status & LinkPass) { + de_link_up(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_LINK); + return; + } + + if (!(status & LinkFail)) + BUG(); + + if (netif_carrier_ok(de->dev)) { + de_link_down(de); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + } +} + +static int de_reset_mac (struct de_private *de) +{ + u32 status, tmp; + + /* + * Reset MAC. Copied from de4x5.c. + */ + + tmp = dr32 (BusMode); + if (tmp == 0xffffffff) + return -ENODEV; + mdelay (1); + + dw32 (BusMode, tmp | CmdReset); + mdelay (1); + + dw32 (BusMode, tmp); + mdelay (1); + + for (tmp = 0; tmp < 5; tmp++) { + dr32 (BusMode); + mdelay (1); + } + + mdelay (1); + + status = dr32(MacStatus); + if (status & (RxState | TxState)) + return -EBUSY; + if (status == 0xffffffff) + return -ENODEV; + return 0; +} + +static void de_adapter_wake (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + if (pmctl & PM_Mask) { + pmctl &= ~PM_Mask; + pci_write_config_dword(de->pdev, PCIPM, pmctl); + + /* de4x5.c delays, so we do too */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(msec_to_jiffies(10)); + } +} + +static void de_adapter_sleep (struct de_private *de) +{ + u32 pmctl; + + if (de->de21040) + return; + + pci_read_config_dword(de->pdev, PCIPM, &pmctl); + pmctl |= PM_Sleep; + pci_write_config_dword(de->pdev, PCIPM, pmctl); +} + +static int de_init_hw (struct de_private *de) +{ + struct net_device *dev = de->dev; + int rc; + + de_adapter_wake(de); + + de->macmode = dr32(MacMode) & ~MacModeClear; + + rc = de_reset_mac(de); + if (rc) + return rc; + + de_set_media(de); /* reset phy */ + + dw32(RxRingAddr, de->ring_dma); + dw32(TxRingAddr, de->ring_dma + (sizeof(struct de_desc) * DE_RX_RING_SIZE)); + + dw32(MacMode, RxTx | de->macmode); + + dr32(RxMissed); /* self-clearing */ + + dw32(IntrMask, de_intr_mask); + + de_set_rx_mode(dev); + + return 0; +} + +static int de_refill_rx (struct de_private *de) +{ + unsigned i; + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + struct sk_buff *skb; + + skb = dev_alloc_skb(de->rx_buf_sz); + if (!skb) + goto err_out; + + skb->dev = de->dev; + + de->rx_skb[i].mapping = pci_map_single(de->pdev, + skb->tail, de->rx_buf_sz, PCI_DMA_FROMDEVICE); + de->rx_skb[i].skb = skb; + + de->rx_ring[i].opts1 = cpu_to_le32(DescOwn); + if (i == (DE_RX_RING_SIZE - 1)) + de->rx_ring[i].opts2 = + cpu_to_le32(RingEnd | de->rx_buf_sz); + else + de->rx_ring[i].opts2 = cpu_to_le32(de->rx_buf_sz); + de->rx_ring[i].addr1 = cpu_to_le32(de->rx_skb[i].mapping); + de->rx_ring[i].addr2 = 0; + } + + return 0; + +err_out: + de_clean_rings(de); + return -ENOMEM; +} + +static int de_init_rings (struct de_private *de) +{ + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + + de->rx_tail = 0; + de->tx_head = de->tx_tail = 0; + + return de_refill_rx (de); +} + +static int de_alloc_rings (struct de_private *de) +{ + de->rx_ring = pci_alloc_consistent(de->pdev, DE_RING_BYTES, &de->ring_dma); + if (!de->rx_ring) + return -ENOMEM; + de->tx_ring = &de->rx_ring[DE_RX_RING_SIZE]; + return de_init_rings(de); +} + +static void de_clean_rings (struct de_private *de) +{ + unsigned i; + + memset(de->rx_ring, 0, sizeof(struct de_desc) * DE_RX_RING_SIZE); + de->rx_ring[DE_RX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + memset(de->tx_ring, 0, sizeof(struct de_desc) * DE_TX_RING_SIZE); + de->tx_ring[DE_TX_RING_SIZE - 1].opts2 = cpu_to_le32(RingEnd); + wmb(); + + for (i = 0; i < DE_RX_RING_SIZE; i++) { + if (de->rx_skb[i].skb) { + pci_unmap_single(de->pdev, de->rx_skb[i].mapping, + de->rx_buf_sz, PCI_DMA_FROMDEVICE); + dev_kfree_skb(de->rx_skb[i].skb); + } + } + + for (i = 0; i < DE_TX_RING_SIZE; i++) { + struct sk_buff *skb = de->tx_skb[i].skb; + if ((skb) && (skb != DE_DUMMY_SKB)) { + if (skb != DE_SETUP_SKB) { + dev_kfree_skb(skb); + de->net_stats.tx_dropped++; + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + skb->len, PCI_DMA_TODEVICE); + } else { + pci_unmap_single(de->pdev, + de->tx_skb[i].mapping, + sizeof(de->setup_frame), + PCI_DMA_TODEVICE); + } + } + } + + memset(&de->rx_skb, 0, sizeof(struct ring_info) * DE_RX_RING_SIZE); + memset(&de->tx_skb, 0, sizeof(struct ring_info) * DE_TX_RING_SIZE); +} + +static void de_free_rings (struct de_private *de) +{ + de_clean_rings(de); + pci_free_consistent(de->pdev, DE_RING_BYTES, de->rx_ring, de->ring_dma); + de->rx_ring = NULL; + de->tx_ring = NULL; +} + +static int de_open (struct net_device *dev) +{ + struct de_private *de = dev->priv; + int rc; + unsigned long flags; + + if (netif_msg_ifup(de)) + printk(KERN_DEBUG "%s: enabling interface\n", dev->name); + + de->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + rc = de_alloc_rings(de); + if (rc) { + printk(KERN_ERR "%s: ring allocation failure, err=%d\n", + dev->name, rc); + return rc; + } + + rc = de_init_hw(de); + if (rc) { + printk(KERN_ERR "%s: h/w init failure, err=%d\n", + dev->name, rc); + goto err_out_free; + } + + rc = request_irq(dev->irq, de_interrupt, SA_SHIRQ, dev->name, dev); + if (rc) { + printk(KERN_ERR "%s: IRQ %d request failure, err=%d\n", + dev->name, dev->irq, rc); + goto err_out_hw; + } + + netif_start_queue(dev); + mod_timer(&de->media_timer, jiffies + DE_TIMER_NO_LINK); + + return 0; + +err_out_hw: + spin_lock_irqsave(&de->lock, flags); + de_stop_hw(de); + spin_unlock_irqrestore(&de->lock, flags); + +err_out_free: + de_free_rings(de); + return rc; +} + +static int de_close (struct net_device *dev) +{ + struct de_private *de = dev->priv; + unsigned long flags; + + if (netif_msg_ifdown(de)) + printk(KERN_DEBUG "%s: disabling interface\n", dev->name); + + del_timer_sync(&de->media_timer); + + spin_lock_irqsave(&de->lock, flags); + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + spin_unlock_irqrestore(&de->lock, flags); + + free_irq(dev->irq, dev); + + de_free_rings(de); + de_adapter_sleep(de); + pci_disable_device(de->pdev); + return 0; +} + +static void de_tx_timeout (struct net_device *dev) +{ + struct de_private *de = dev->priv; + + printk(KERN_DEBUG "%s: NIC status %08x mode %08x sia %08x desc %u/%u/%u\n", + dev->name, dr32(MacStatus), dr32(MacMode), dr32(SIAStatus), + de->rx_tail, de->tx_head, de->tx_tail); + + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(); + de_clean_rings(de); + + de_init_hw(de); + + netif_wake_queue(dev); +} + +static int de_get_regs(struct de_private *de, u8 *buf) +{ + int i; + u32 *rbuf = (u32 *)buf; + + /* read all CSRs */ + for (i = 0; i < DE_NUM_REGS; i++) + rbuf[i] = dr32(i * 8); + + /* handle self-clearing RxMissed counter, CSR8 */ + de_rx_missed(de, rbuf[8]); + + return 0; +} + +static int de_ethtool_gset(struct de_private *de, struct ethtool_cmd *ecmd) +{ + ecmd->supported = de->media_supported; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = 0; + ecmd->advertising = de->media_advertise; + + switch (de->media_type) { + case DE_MEDIA_AUI: + ecmd->port = PORT_AUI; + ecmd->speed = 5; + break; + case DE_MEDIA_BNC: + ecmd->port = PORT_BNC; + ecmd->speed = 2; + break; + default: + ecmd->port = PORT_TP; + ecmd->speed = SPEED_10; + break; + } + + if (de->macmode & FullDuplex) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + + if (de->media_lock) + ecmd->autoneg = AUTONEG_DISABLE; + else + ecmd->autoneg = AUTONEG_ENABLE; + + /* ignore maxtxpkt, maxrxpkt for now */ + + return 0; +} + +static int de_ethtool_sset(struct de_private *de, struct ethtool_cmd *ecmd) +{ + u32 new_media; + unsigned int media_lock; + + if (ecmd->speed != SPEED_10 && ecmd->speed != 5 && ecmd->speed != 2) + return -EINVAL; + if (de->de21040 && ecmd->speed == 2) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI && ecmd->port != PORT_BNC) + return -EINVAL; + if (de->de21040 && ecmd->port == PORT_BNC) + return -EINVAL; + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) + return -EINVAL; + if (ecmd->advertising & ~de->media_supported) + return -EINVAL; + if (ecmd->autoneg == AUTONEG_ENABLE && + (!(ecmd->advertising & ADVERTISED_Autoneg))) + return -EINVAL; + + switch (ecmd->port) { + case PORT_AUI: + new_media = DE_MEDIA_AUI; + if (!(ecmd->advertising & ADVERTISED_AUI)) + return -EINVAL; + break; + case PORT_BNC: + new_media = DE_MEDIA_BNC; + if (!(ecmd->advertising & ADVERTISED_BNC)) + return -EINVAL; + break; + default: + if (ecmd->autoneg == AUTONEG_ENABLE) + new_media = DE_MEDIA_TP_AUTO; + else if (ecmd->duplex == DUPLEX_FULL) + new_media = DE_MEDIA_TP_FD; + else + new_media = DE_MEDIA_TP; + if (!(ecmd->advertising & ADVERTISED_TP)) + return -EINVAL; + if (!(ecmd->advertising & (ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half))) + return -EINVAL; + break; + } + + media_lock = (ecmd->autoneg == AUTONEG_ENABLE) ? 0 : 1; + + if ((new_media == de->media_type) && + (media_lock == de->media_lock) && + (ecmd->advertising == de->media_advertise)) + return 0; /* nothing to change */ + + de_link_down(de); + de_stop_rxtx(de); + + de->media_type = new_media; + de->media_lock = media_lock; + de->media_advertise = ecmd->advertising; + de_set_media(de); + + return 0; +} + +static int de_ethtool_ioctl (struct de_private *de, void *useraddr) +{ + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + strcpy (info.bus_info, de->pdev->slot_name); + info.eedump_len = DE_EEPROM_SIZE; + info.regdump_len = DE_REGS_SIZE; + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&de->lock); + de_ethtool_gset(de, &ecmd); + spin_unlock_irq(&de->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + int r; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&de->lock); + r = de_ethtool_sset(de, &ecmd); + spin_unlock_irq(&de->lock); + return r; + } + + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + u32 status; + + if (de->media_type != DE_MEDIA_TP_AUTO) + return -EINVAL; + if (netif_carrier_ok(de->dev)) + de_link_down(de); + + status = dr32(SIAStatus); + dw32(SIAStatus, (status & ~NWayState) | NWayRestart); + if (netif_msg_link(de)) + printk(KERN_INFO "%s: link nway restart, status %x,%x\n", + de->dev->name, status, dr32(SIAStatus)); + return 0; + } + + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = (netif_carrier_ok(de->dev)) ? 1 : 0; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = de->msg_enable; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + de->msg_enable = edata.data; + return 0; + } + + /* get registers */ + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + u8 regbuf[DE_REGS_SIZE]; + int r; + + if (copy_from_user(®s, useraddr, sizeof(regs))) + return -EFAULT; + + if (regs.len > DE_REGS_SIZE) { + regs.len = DE_REGS_SIZE; + } + regs.version = (DE_REGS_VER << 2) | de->de21040; + if (copy_to_user(useraddr, ®s, sizeof(regs))) + return -EFAULT; + + useraddr += offsetof(struct ethtool_regs, data); + + spin_lock_irq(&de->lock); + r = de_get_regs(de, regbuf); + spin_unlock_irq(&de->lock); + + if (r) + return r; + if (copy_to_user(useraddr, regbuf, regs.len)) + return -EFAULT; + return 0; + } + + /* get SROM dump */ + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom; + + if (!de->ee_data) + break; + if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) + return -EFAULT; + if ((eeprom.offset != 0) || (eeprom.magic != 0) || + (eeprom.len != DE_EEPROM_SIZE)) + return -EINVAL; + + useraddr += offsetof(struct ethtool_regs, data); + if (copy_to_user(useraddr, de->ee_data, DE_EEPROM_SIZE)) + return -EFAULT; + } + + default: + break; + } + + return -EOPNOTSUPP; +} + + +static int de_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct de_private *de = dev->priv; + int rc = 0; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCETHTOOL: + return de_ethtool_ioctl(de, (void *) rq->ifr_data); + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +static void __init de21040_get_mac_address (struct de_private *de) +{ + unsigned i; + + dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ + + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = dr32(ROMCmd); + while (value < 0 && --boguscnt > 0); + de->dev->dev_addr[i] = value; + if (boguscnt <= 0) + printk(KERN_WARNING PFX "timeout reading 21040 MAC address byte %u\n", i); + } +} + +static void __init de21040_get_media_info(struct de_private *de) +{ + unsigned int i; + + de->media_type = DE_MEDIA_TP; + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half | SUPPORTED_AUI; + de->media_advertise = de->media_supported; + + for (i = 0; i < DE_MAX_MEDIA; i++) { + switch (i) { + case DE_MEDIA_AUI: + case DE_MEDIA_TP: + case DE_MEDIA_TP_FD: + de->media[i].type = i; + de->media[i].csr13 = t21040_csr13[i]; + de->media[i].csr14 = t21040_csr14[i]; + de->media[i].csr15 = t21040_csr15[i]; + break; + default: + de->media[i].type = DE_MEDIA_INVALID; + break; + } + } +} + +/* Note: this routine returns extra data bits for size detection. */ +static unsigned __init tulip_read_eeprom(void *regs, int location, int addr_len) +{ + int i; + unsigned retval = 0; + void *ee_addr = regs + ROMCmd; + int read_cmd = location | (EE_READ_CMD << addr_len); + + writel(EE_ENB & ~EE_CS, ee_addr); + writel(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + writel(EE_ENB | dataval, ee_addr); + readl(ee_addr); + writel(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + } + writel(EE_ENB, ee_addr); + readl(ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ENB | EE_SHIFT_CLK, ee_addr); + readl(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DATA_READ) ? 1 : 0); + writel(EE_ENB, ee_addr); + readl(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +static void __init de21041_get_srom_info (struct de_private *de) +{ + unsigned i, sa_offset = 0, ofs; + u8 ee_data[DE_EEPROM_SIZE + 6] = {}; + unsigned ee_addr_size = tulip_read_eeprom(de->regs, 0xff, 8) & 0x40000 ? 8 : 6; + struct de_srom_info_leaf *il; + void *bufp; + + /* download entire eeprom */ + for (i = 0; i < DE_EEPROM_WORDS; i++) + ((u16 *)ee_data)[i] = + le16_to_cpu(tulip_read_eeprom(de->regs, i, ee_addr_size)); + + /* DEC now has a specification but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + + /* store MAC address */ + for (i = 0; i < 6; i ++) + de->dev->dev_addr[i] = ee_data[i + sa_offset]; + + /* get offset of controller 0 info leaf. ignore 2nd byte. */ + ofs = ee_data[SROMC0InfoLeaf]; + if (ofs >= (sizeof(ee_data) - sizeof(struct de_srom_info_leaf) - sizeof(struct de_srom_media_block))) + goto bad_srom; + + /* get pointer to info leaf */ + il = (struct de_srom_info_leaf *) &ee_data[ofs]; + + /* paranoia checks */ + if (il->n_blocks == 0) + goto bad_srom; + if ((sizeof(ee_data) - ofs) < + (sizeof(struct de_srom_info_leaf) + (sizeof(struct de_srom_media_block) * il->n_blocks))) + goto bad_srom; + + /* get default media type */ + switch (DE_UNALIGNED_16(&il->default_media)) { + case 0x0001: de->media_type = DE_MEDIA_BNC; break; + case 0x0002: de->media_type = DE_MEDIA_AUI; break; + case 0x0204: de->media_type = DE_MEDIA_TP_FD; break; + default: de->media_type = DE_MEDIA_TP_AUTO; break; + } + + if (netif_msg_probe(de)) + printk(KERN_INFO "de%d: SROM leaf offset %u, default media %s\n", + de->board_idx, ofs, + media_name[de->media_type]); + + /* init SIA register values to defaults */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + de->media[i].type = DE_MEDIA_INVALID; + de->media[i].csr13 = 0xffff; + de->media[i].csr14 = 0xffff; + de->media[i].csr15 = 0xffff; + } + + /* parse media blocks to see what medias are supported, + * and if any custom CSR values are provided + */ + bufp = ((void *)il) + sizeof(*il); + for (i = 0; i < il->n_blocks; i++) { + struct de_srom_media_block *ib = bufp; + unsigned idx; + + /* index based on media type in media block */ + switch(ib->opts & MediaBlockMask) { + case 0: /* 10baseT */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + case 1: /* BNC */ + de->media_supported |= SUPPORTED_BNC; + idx = DE_MEDIA_BNC; + break; + case 2: /* AUI */ + de->media_supported |= SUPPORTED_AUI; + idx = DE_MEDIA_AUI; + break; + case 4: /* 10baseT-FD */ + de->media_supported |= SUPPORTED_TP | SUPPORTED_10baseT_Full + | SUPPORTED_Autoneg; + idx = DE_MEDIA_TP_FD; + de->media[DE_MEDIA_TP_AUTO].type = DE_MEDIA_TP_AUTO; + break; + default: + goto bad_srom; + } + + de->media[idx].type = idx; + + if (netif_msg_probe(de)) + printk(KERN_INFO "de%d: media block #%u: %s", + de->board_idx, i, + media_name[de->media[idx].type]); + + bufp += sizeof (ib->opts); + + if (ib->opts & MediaCustomCSRs) { + de->media[idx].csr13 = DE_UNALIGNED_16(&ib->csr13); + de->media[idx].csr14 = DE_UNALIGNED_16(&ib->csr14); + de->media[idx].csr15 = DE_UNALIGNED_16(&ib->csr15); + bufp += sizeof(ib->csr13) + sizeof(ib->csr14) + + sizeof(ib->csr15); + + if (netif_msg_probe(de)) + printk(" (%x,%x,%x)\n", + de->media[idx].csr13, + de->media[idx].csr14, + de->media[idx].csr15); + + } else if (netif_msg_probe(de)) + printk("\n"); + + if (bufp > ((void *)&ee_data[DE_EEPROM_SIZE - 3])) + break; + } + + de->media_advertise = de->media_supported; + +fill_defaults: + /* fill in defaults, for cases where custom CSRs not used */ + for (i = 0; i < DE_MAX_MEDIA; i++) { + if (de->media[i].csr13 == 0xffff) + de->media[i].csr13 = t21041_csr13[i]; + if (de->media[i].csr14 == 0xffff) + de->media[i].csr14 = t21041_csr14[i]; + if (de->media[i].csr15 == 0xffff) + de->media[i].csr15 = t21041_csr15[i]; + } + + de->ee_data = kmalloc(DE_EEPROM_SIZE, GFP_KERNEL); + if (de->ee_data) + memcpy(de->ee_data, &ee_data[0], DE_EEPROM_SIZE); + + return; + +bad_srom: + /* for error cases, it's ok to assume we support all these */ + for (i = 0; i < DE_MAX_MEDIA; i++) + de->media[i].type = i; + de->media_supported = + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_AUI | + SUPPORTED_BNC; + goto fill_defaults; +} + +static int __init de_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct de_private *de; + int rc; + void *regs; + long pciaddr; + static int board_idx = -1; + + board_idx++; + +#ifndef MODULE + if (board_idx == 0) + printk("%s", version); +#endif + + /* allocate a new ethernet device structure, and fill in defaults */ + dev = alloc_etherdev(sizeof(struct de_private)); + if (!dev) + return -ENOMEM; + + SET_MODULE_OWNER(dev); + dev->open = de_open; + dev->stop = de_close; + dev->set_multicast_list = de_set_rx_mode; + dev->hard_start_xmit = de_start_xmit; + dev->get_stats = de_get_stats; + dev->do_ioctl = de_ioctl; + dev->tx_timeout = de_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->irq = pdev->irq; + + de = dev->priv; + de->de21040 = ent->driver_data == 0 ? 1 : 0; + de->pdev = pdev; + de->dev = dev; + de->msg_enable = (debug < 0 ? DE_DEF_MSG_ENABLE : debug); + de->board_idx = board_idx; + spin_lock_init (&de->lock); + init_timer(&de->media_timer); + if (de->de21040) + de->media_timer.function = de21040_media_timer; + else + de->media_timer.function = de21041_media_timer; + de->media_timer.data = (unsigned long) de; + + netif_carrier_off(dev); + netif_stop_queue(dev); + + /* wake up device, assign resources */ + rc = pci_enable_device(pdev); + if (rc) + goto err_out_free; + + /* reserve PCI resources to ensure driver atomicity */ + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + + /* check for invalid IRQ value */ + if (pdev->irq < 2) { + rc = -EIO; + printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", + pdev->irq, pdev->slot_name); + goto err_out_res; + } + + /* obtain and check validity of PCI I/O address */ + pciaddr = pci_resource_start(pdev, 1); + if (!pciaddr) { + rc = -EIO; + printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", + pdev->slot_name); + goto err_out_res; + } + if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) { + rc = -EIO; + printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n", + pci_resource_len(pdev, 1), pdev->slot_name); + goto err_out_res; + } + + /* remap CSR registers */ + regs = ioremap_nocache(pciaddr, DE_REGS_SIZE); + if (!regs) { + rc = -EIO; + printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", + pci_resource_len(pdev, 1), pciaddr, pdev->slot_name); + goto err_out_res; + } + dev->base_addr = (unsigned long) regs; + de->regs = regs; + + de_adapter_wake(de); + + /* make sure hardware is not running */ + rc = de_reset_mac(de); + if (rc) { + printk(KERN_ERR PFX "Cannot reset MAC, pci dev %s\n", + pdev->slot_name); + goto err_out_iomap; + } + + /* get MAC address, initialize default media type and + * get list of supported media + */ + if (de->de21040) { + de21040_get_mac_address(de); + de21040_get_media_info(de); + } else { + de21041_get_srom_info(de); + } + + /* register new network interface with kernel */ + rc = register_netdev(dev); + if (rc) + goto err_out_iomap; + + /* print info about board and interface just registered */ + printk (KERN_INFO "%s: %s at 0x%lx, " + "%02x:%02x:%02x:%02x:%02x:%02x, " + "IRQ %d\n", + dev->name, + de->de21040 ? "21040" : "21041", + dev->base_addr, + 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->irq); + + pci_set_drvdata(pdev, dev); + + /* enable busmastering */ + pci_set_master(pdev); + + /* put adapter to sleep */ + de_adapter_sleep(de); + + return 0; + +err_out_iomap: + if (de->ee_data) + kfree(de->ee_data); + iounmap(regs); +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + kfree(dev); + return rc; +} + +static void __exit de_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct de_private *de = dev->priv; + + if (!dev) + BUG(); + unregister_netdev(dev); + if (de->ee_data) + kfree(de->ee_data); + iounmap(de->regs); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + kfree(dev); +} + +#ifdef CONFIG_PM + +static int de_suspend (struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = dev->priv; + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&de->media_timer); + + disable_irq(dev->irq); + spin_lock_irq(&de->lock); + + de_stop_hw(de); + netif_stop_queue(dev); + netif_device_detach(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&de->lock); + enable_irq(dev->irq); + + /* Update the error counts. */ + __de_get_stats(de); + + synchronize_irq(); + de_clean_rings(de); + + de_adapter_sleep(de); + pci_disable_device(pdev); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + +static int de_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct de_private *de = dev->priv; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; + if (netif_running(dev)) { + pci_enable_device(pdev); + de_init_hw(de); + netif_device_attach(dev); + } else { + netif_device_attach(dev); + } +out: + rtnl_unlock(); + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver de_driver = { + name: DRV_NAME, + id_table: de_pci_tbl, + probe: de_init_one, + remove: de_remove_one, +#ifdef CONFIG_PM + suspend: de_suspend, + resume: de_resume, +#endif +}; + +static int __init de_init (void) +{ +#ifdef MODULE + printk("%s", version); +#endif + return pci_module_init (&de_driver); +} + +static void __exit de_exit (void) +{ + pci_unregister_driver (&de_driver); +} + +module_init(de_init); +module_exit(de_exit); diff -urN linux-2.5.6-pre3/drivers/net/tulip/de4x5.c linux-2.5.6/drivers/net/tulip/de4x5.c --- linux-2.5.6-pre3/drivers/net/tulip/de4x5.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/de4x5.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,5918 @@ +/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 + ethernet driver for Linux. + + Copyright 1994, 1995 Digital Equipment Corporation. + + Testing resources for this driver have been made available + in part by NASA Ames Research Center (mjacob@nas.nasa.gov). + + The author may be reached at davies@maniac.ultranet.com. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 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., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: + + DE425 TP/COAX EISA + DE434 TP PCI + DE435 TP/COAX/AUI PCI + DE450 TP/COAX/AUI PCI + DE500 10/100 PCI Fasternet + + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 + + So far the driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + + The driver has been tested on a relatively busy network using the DE425, + DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred + 16M of data to a DECstation 5000/200 as follows: + + TCP UDP + TX RX TX RX + DE425 1030k 997k 1170k 1128k + DE434 1063k 995k 1170k 1125k + DE435 1063k 995k 1170k 1125k + DE500 1063k 998k 1170k 1125k in 10Mb/s mode + + All values are typical (in kBytes/sec) from a sample of 4 for each + measurement. Their error is +/-20k on a quiet (private) network and also + depend on what load the CPU has. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'ewrk3.c' and in turn from + Donald Becker's 'lance.c' should be obvious. With the module autoload of + every usable DECchip board, I pinched Donald's 'next_module' field to + link my modules together. + + Upto 15 EISA cards can be supported under this driver, limited primarily + by the available IRQ lines. I have checked different configurations of + multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a + problem yet (provided you have at least depca.c v0.38) ... + + PCI support has been added to allow the driver to work with the DE434, + DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due + to the differences in the EISA and PCI CSR address offsets from the base + address. + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). Loadable module support under PCI and EISA has been + achieved by letting the driver autoprobe as if it were compiled into the + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! + + To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy de4x5.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) for fixed autoprobes (not recommended), edit the source code near + line 5594 to reflect the I/O address you're using, or assign these when + loading by: + + insmod de4x5 io=0xghh where g = bus number + hh = device number + + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. + 3) compile de4x5.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the de4x5 configuration turned off and reboot. + 5) insmod de4x5 [io=0xghh] + 6) run the net startup bits for your new eth?? interface(s) manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + To unload a module, turn off the associated interface(s) + 'ifconfig eth?? down' then 'rmmod de4x5'. + + Automedia detection is included so that in principal you can disconnect + from, e.g. TP, reconnect to BNC and things will still work (after a + pause whilst the driver figures out where its media went). My tests + using ping showed that it appears to work.... + + By default, the driver will now autodetect any DECchip based card. + Should you have a need to restrict the driver to DIGITAL only cards, you + can compile with a DEC_ONLY define, or if loading as a module, use the + 'dec_only=1' parameter. + + I've changed the timing routines to use the kernel timer and scheduling + functions so that the hangs and other assorted problems that occurred + while autosensing the media should be gone. A bonus for the DC21040 + auto media sense algorithm is that it can now use one that is more in + line with the rest (the DC21040 chip doesn't have a hardware timer). + The downside is the 1 'jiffies' (10ms) resolution. + + IEEE 802.3u MII interface code has been added in anticipation that some + products may use it in the future. + + The SMC9332 card has a non-compliant SROM which needs fixing - I have + patched this driver to detect it because the SROM format used complies + to a previous DEC-STD format. + + I have removed the buffer copies needed for receive on Intels. I cannot + remove them for Alphas since the Tulip hardware only does longword + aligned DMA transfers and the Alphas get alignment traps with non + longword aligned data copies (which makes them really slow). No comment. + + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguments are now allowed, similar to passing arguments + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, at or above line 548, place e.g. + #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. ALSO, you must get the requested media + correct in relation to what the adapter SROM says it has. There's no way + to determine this in advance other than by trial and error and common + sense, e.g. call a BNC connectored port 'BNC', not '10Mb'. + + Changed the bus probing. EISA used to be done first, followed by PCI. + Most people probably don't even know what a de425 is today and the EISA + probe has messed up some SCSI cards in the past, so now PCI is always + probed first followed by EISA if a) the architecture allows EISA and + either b) there have been no PCI cards detected or c) an EISA probe is + forced by the user. To force a probe include "force_eisa" in your + insmod "args" line; for built-in kernels either change the driver to do + this automatically or include #define DE4X5_FORCE_EISA on or before + line 1040 in the driver. + + TO DO: + ------ + + Revision History + ---------------- + + Version Date Description + + 0.1 17-Nov-94 Initial writing. ALPHA code release. + 0.2 13-Jan-95 Added PCI support for DE435's. + 0.21 19-Jan-95 Added auto media detection. + 0.22 10-Feb-95 Fix interrupt handler call . + Fix recognition bug reported by . + Add request/release_region code. + Add loadable modules support for PCI. + Clean up loadable modules support. + 0.23 28-Feb-95 Added DC21041 and DC21140 support. + Fix missed frame counter value and initialisation. + Fixed EISA probe. + 0.24 11-Apr-95 Change delay routine to use . + Change TX_BUFFS_AVAIL macro. + Change media autodetection to allow manual setting. + Completed DE500 (DC21140) support. + 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm. + 0.242 10-May-95 Minor changes. + 0.30 12-Jun-95 Timer fix for DC21140. + Portability changes. + Add ALPHA changes from . + Add DE500 semi automatic autosense. + Add Link Fail interrupt TP failure detection. + Add timer based link change detection. + Plugged a memory leak in de4x5_queue_pkt(). + 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1. + 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a + suggestion by . + 0.33 8-Aug-95 Add shared interrupt support (not released yet). + 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs. + Fix de4x5_interrupt(). + Fix dc21140_autoconf() mess. + No shared interrupt support. + 0.332 11-Sep-95 Added MII management interface routines. + 0.40 5-Mar-96 Fix setup frame timeout . + Add kernel timer code (h/w is too flaky). + Add MII based PHY autosense. + Add new multicasting code. + Add new autosense algorithms for media/mode + selection using kernel scheduling/timing. + Re-formatted. + Made changes suggested by : + Change driver to detect all DECchip based cards + with DEC_ONLY restriction a special case. + Changed driver to autoprobe as a module. No irq + checking is done now - assume BIOS is good! + Added SMC9332 detection + 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card + only + Fix for multiple PCI cards reported by + Duh, put the SA_SHIRQ flag into request_interrupt(). + Fix SMC ethernet address in enet_det[]. + Print chip name instead of "UNKNOWN" during boot. + 0.42 26-Apr-96 Fix MII write TA bit error. + Fix bug in dc21040 and dc21041 autosense code. + Remove buffer copies on receive for Intels. + Change sk_buff handling during media disconnects to + eliminate DUP packets. + Add dynamic TX thresholding. + Change all chips to use perfect multicast filtering. + Fix alloc_device() bug + 0.43 21-Jun-96 Fix unconnected media TX retry bug. + Add Accton to the list of broken cards. + Fix TX under-run bug for non DC21140 chips. + Fix boot command probe bug in alloc_device() as + reported by and + . + Add cache locks to prevent a race condition as + reported by and + . + Upgraded alloc_device() code. + 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion + with + 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. + Fix EISA probe bugs reported by + and . + 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media + with a loopback packet. + 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported + by + 0.45 8-Dec-96 Include endian functions for PPC use, from work + by and . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from . + 0.5 30-Jan-97 Added SROM decoding functions. + Updated debug flags. + Fix sleep/wakeup calls for PCI cards, bug reported + by . + Added multi-MAC, one SROM feature from discussion + with . + Added full module autoprobe capability. + Added attempt to use an SMC9332 with broken SROM. + Added fix for ZYNX multi-mac cards that didn't + get their IRQs wired correctly. + 0.51 13-Feb-97 Added endian fixes for the SROM accesses from + + Fix init_connection() to remove extra device reset. + Fix MAC/PHY reset ordering in dc21140m_autoconf(). + Fix initialisation problem with lp->timeout in + typeX_infoblock() from . + Fix MII PHY reset problem from work done by + . + 0.52 26-Apr-97 Some changes may not credit the right people - + a disk crash meant I lost some mail. + Change RX interrupt routine to drop rather than + defer packets to avoid hang reported by + . + Fix srom_exec() to return for COMPACT and type 1 + infoblocks. + Added DC21142 and DC21143 functions. + Added byte counters from + Added SA_INTERRUPT temporary fix from + . + 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during + module load: bug reported by + + Fix multi-MAC, one SROM, to work with 2114x chips: + bug reported by . + Make above search independent of BIOS device scan + direction. + Completed DC2114[23] autosense functions. + 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by + and + . + Added argument list to set up each board from either + a module's command line or a compiled in #define. + Added generic MII PHY functionality to deal with + newer PHY chips. + Fix the mess in 2.1.67. + 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by + . + Fix bug in pci_probe() for 64 bit systems reported + by . + 0.533 9-Jan-98 Fix more 64 bit bugs reported by . + 0.534 24-Jan-98 Fix last (?) endian bug from + 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. + 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. + **Incompatible with 2.0.x from here.** + 0.540 5-Jul-98 Atomicize assertion of dev->interrupt for SMP + from + Add TP, AUI and BNC cases to 21140m_autoconf() for + case where a 21140 under SROM control uses, e.g. AUI + from problem report by + Add MII parallel detection to 2114x_autoconf() for + case where no autonegotiation partner exists from + problem report by . + Add ability to force connection type directly even + when using SROM control from problem report by + . + Updated the PCI interface to conform with the latest + version. I hope nothing is broken... + Add TX done interrupt modification from suggestion + by . + Fix is_anc_capable() bug reported by + . + Fix type[13]_infoblock() bug: during MII search, PHY + lp->rst not run because lp->ibn not initialised - + from report & fix by . + Fix probe bug with EISA & PCI cards present from + report by . + 0.541 24-Aug-98 Fix compiler problems associated with i386-string + ops from multiple bug reports and temporary fix + from . + Fix pci_probe() to correctly emulate the old + pcibios_find_class() function. + Add an_exception() for old ZYNX346 and fix compile + warning on PPC & SPARC, from . + Fix lastPCI to correctly work with compiled in + kernels and modules from bug report by + et al. + 0.542 15-Sep-98 Fix dc2114x_autoconf() to stop multiple messages + when media is unconnected. + Change dev->interrupt to lp->interrupt to ensure + alignment for Alpha's and avoid their unaligned + access traps. This flag is merely for log messages: + should do something more definitive though... + 0.543 30-Dec-98 Add SMP spin locking. + 0.544 8-May-99 Fix for buggy SROM in Motorola embedded boards using + a 21143 by . + Change PCI/EISA bus probing order. + 0.545 28-Nov-99 Further Moto SROM bug fix from + + Remove double checking for DEBUG_RX in de4x5_dbg_rx() + from report by + 0.546 22-Feb-01 Fixes Alpha XP1000 oops. The srom_search function + was causing a page fault when initializing the + variable 'pb', on a non de4x5 PCI device, in this + case a PCI bridge (DEC chip 21152). The value of + 'pb' is now only initialized if a de4x5 chip is + present. + + 0.547 08-Nov-01 Use library crc32 functions by + ========================================================================= +*/ + +static const char *version = "de4x5.c:V0.546 2001/02/22 davies@maniac.ultranet.com\n"; + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC +#include +#endif /* CONFIG_PPC */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "de4x5.h" + +#define c_char const char +#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) + +/* +** MII Information +*/ +struct phy_table { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time - 802.3u is confusing here */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; +}; + +struct mii_phy { + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time */ + struct { /* Non autonegotiation (parallel) speed det. */ + int reg; + int mask; + int value; + } spd; + int addr; /* MII address for the PHY */ + u_char *gep; /* Start of GEP sequence block in SROM */ + u_char *rst; /* Start of reset sequence in SROM */ + u_int mc; /* Media Capabilities */ + u_int ana; /* NWay Advertisement */ + u_int fdx; /* Full DupleX capabilites for each media */ + u_int ttm; /* Transmit Threshold Mode for each media */ + u_int mci; /* 21142 MII Connector Interrupt info */ +}; + +#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ + +struct sia_phy { + u_char mc; /* Media Code */ + u_char ext; /* csr13-15 valid when set */ + int csr13; /* SIA Connectivity Register */ + int csr14; /* SIA TX/RX Register */ + int csr15; /* SIA General Register */ + int gepc; /* SIA GEP Control Information */ + int gep; /* SIA GEP Data */ +}; + +/* +** Define the know universe of PHY devices that can be +** recognised by this driver. +*/ +static struct phy_table phy_info[] = { + {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ + {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ + {0, 0x7810 , 1, {0x14, 0x0800, 0x0800}} /* Level One LTX970 */ +}; + +/* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ + +/* +** Define special SROM detection cases +*/ +static c_char enet_det[][ETH_ALEN] = { + {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} +}; + +#define SMC 1 +#define ACCTON 2 + +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { + {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ + 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, + 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, + 0x00,0x18,} +}; + + +#ifdef DE4X5_DEBUG +static int de4x5_debug = DE4X5_DEBUG; +#else +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); +#endif + +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.: +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" +** here +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; +#else +static char *args; +#endif + +struct parameters { + int fdx; + int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ + +#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** Ethernet Info +*/ +#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */ +#define IEEE802_3_SZ 1518 /* Packet + CRC */ +#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */ +#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */ +#define MIN_DAT_SZ 1 /* Minimum ethernet data length */ +#define PKT_HDR_LEN 14 /* Addresses and data length info */ +#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1) +#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */ + + +/* +** EISA bus defines +*/ +#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */ + +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#define EISA_ALLOWED_IRQ_LIST {5, 9, 10, 11} + +#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} +#define DE4X5_NAME_LENGTH 8 + +/* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* +** PCI Bus defines +*/ +#define PCI_MAX_BUS_NUM 8 +#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ +#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ +#define NO_MORE_PCI -2 /* PCI bus search all done */ + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry. +*/ +#define DE4X5_ALIGN4 ((u_long)4 - 1) /* 1 longword align */ +#define DE4X5_ALIGN8 ((u_long)8 - 1) /* 2 longword align */ +#define DE4X5_ALIGN16 ((u_long)16 - 1) /* 4 longword align */ +#define DE4X5_ALIGN32 ((u_long)32 - 1) /* 8 longword align */ +#define DE4X5_ALIGN64 ((u_long)64 - 1) /* 16 longword align */ +#define DE4X5_ALIGN128 ((u_long)128 - 1) /* 32 longword align */ + +#define DE4X5_ALIGN DE4X5_ALIGN32 /* Keep the DC21040 happy... */ +#define DE4X5_CACHE_ALIGN CAL_16LONG +#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */ +/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */ +#define DESC_ALIGN + +#ifndef DEC_ONLY /* See README.de4x5 for using this */ +static int dec_only; +#else +static int dec_only = 1; +#endif + +/* +** DE4X5 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ + imr |= lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Enable the IRQs */\ +} + +#define DISABLE_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_en;\ + outl(imr, DE4X5_IMR); /* Disable the IRQs */\ +} + +#define UNMASK_IRQs {\ + imr |= lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Unmask the IRQs */\ +} + +#define MASK_IRQs {\ + imr = inl(DE4X5_IMR);\ + imr &= ~lp->irq_mask;\ + outl(imr, DE4X5_IMR); /* Mask the IRQs */\ +} + +/* +** DE4X5 START/STOP +*/ +#define START_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr |= OMR_ST | OMR_SR;\ + outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5 {\ + omr = inl(DE4X5_OMR);\ + omr &= ~(OMR_ST|OMR_SR);\ + outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ +} + +/* +** DE4X5 SIA RESET +*/ +#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */ + +/* +** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) +*/ +#define DE4X5_AUTOSENSE_MS 250 + +/* +** SROM Structure +*/ +struct de4x5_srom { + char sub_vendor_id[2]; + char sub_system_id[2]; + char reserved[12]; + char id_block_crc; + char reserved2; + char version; + char num_controllers; + char ieee_addr[6]; + char info[100]; + short chksum; +}; +#define SUB_VENDOR_ID 0x500a + +/* +** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous +** and have sizes of both a power of 2 and a multiple of 4. +** A size of 256 bytes for each buffer could be chosen because over 90% of +** all packets in our network are <256 bytes long and 64 longword alignment +** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX +** descriptors are needed for machines with an ALPHA CPU. +*/ +#define NUM_RX_DESC 8 /* Number of RX descriptors */ +#define NUM_TX_DESC 32 /* Number of TX descriptors */ +#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */ + /* Multiple of 4 for DC21040 */ + /* Allows 512 byte alignment */ +struct de4x5_desc { + volatile s32 status; + u32 des1; + u32 buf; + u32 next; + DESC_ALIGN +}; + +/* +** The DE4X5 private structure +*/ +#define DE4X5_PKT_STAT_SZ 16 +#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase DE4X5_PKT_STAT_SZ */ + +struct pkt_stats { + u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ + u_int unicast; + u_int multicast; + u_int broadcast; + u_int excessive_collisions; + u_int tx_underruns; + u_int excessive_underruns; + u_int rx_runt_frames; + u_int rx_collision; + u_int rx_dribble; + u_int rx_overflow; +}; + +struct de4x5_private { + char adapter_name[80]; /* Adapter name */ + u_long interrupt; /* Aligned ISR flag */ + struct de4x5_desc *rx_ring; /* RX descriptor ring */ + struct de4x5_desc *tx_ring; /* TX descriptor ring */ + struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ + struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */ + int rx_new, rx_old; /* RX descriptor ring pointers */ + int tx_new, tx_old; /* TX descriptor ring pointers */ + char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ + char frame[64]; /* Min sized packet for loopback*/ + spinlock_t lock; /* Adapter specific spinlock */ + struct net_device_stats stats; /* Public stats */ + struct pkt_stats pktStats; /* Private stats counters */ + char rxRingSize; + char txRingSize; + int bus; /* EISA or PCI */ + int bus_num; /* PCI Bus number */ + int device; /* Device number on PCI bus */ + int state; /* Adapter OPENED or CLOSED */ + int chipset; /* DC21040, DC21041 or DC21140 */ + s32 irq_mask; /* Interrupt Mask (Enable) bits */ + s32 irq_en; /* Summary interrupt bits */ + int media; /* Media (eg TP), mode (eg 100B)*/ + int c_media; /* Remember the last media conn */ + int fdx; /* media full duplex flag */ + int linkOK; /* Link is OK */ + int autosense; /* Allow/disallow autosensing */ + int tx_enable; /* Enable descriptor polling */ + int setup_f; /* Setup frame filtering type */ + int local_state; /* State within a 'media' state */ + struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ + struct sia_phy sia; /* SIA PHY Information */ + int active; /* Index to active PHY device */ + int mii_cnt; /* Number of attached PHY's */ + int timeout; /* Scheduling counter */ + struct timer_list timer; /* Timer info for kernel */ + int tmp; /* Temporary global per card */ + struct { + void *priv; /* Original kmalloc'd mem addr */ + u_long lock; /* Lock the cache accesses */ + s32 csr0; /* Saved Bus Mode Register */ + s32 csr6; /* Saved Operating Mode Reg. */ + s32 csr7; /* Saved IRQ Mask Register */ + s32 gep; /* Saved General Purpose Reg. */ + s32 gepc; /* Control info for GEP */ + s32 csr13; /* Saved SIA Connectivity Reg. */ + s32 csr14; /* Saved SIA TX/RX Register */ + s32 csr15; /* Saved SIA General Register */ + int save_cnt; /* Flag if state already saved */ + struct sk_buff *skb; /* Save the (re-ordered) skb's */ + } cache; + struct de4x5_srom srom; /* A copy of the SROM */ + struct net_device *next_module; /* Link to the next module */ + int rx_ovf; /* Check for 'RX overflow' tag */ + int useSROM; /* For non-DEC card use SROM */ + int useMII; /* Infoblock using the MII */ + int asBitValid; /* Autosense bits in GEP? */ + int asPolarity; /* 0 => asserted high */ + int asBit; /* Autosense bit number in GEP */ + int defMedium; /* SROM default medium */ + int tcount; /* Last infoblock number */ + int infoblock_init; /* Initialised this infoblock? */ + int infoleaf_offset; /* SROM infoleaf for controller */ + s32 infoblock_csr6; /* csr6 value in SROM infoblock */ + int infoblock_media; /* infoblock media */ + int (*infoleaf_fn)(struct net_device *); /* Pointer to infoleaf function */ + u_char *rst; /* Pointer to Type 5 reset info */ + u_char ibn; /* Infoblock number */ + struct parameters params; /* Command line/ #defined params */ + struct pci_dev *pdev; /* Device cookie for DMA alloc */ + dma_addr_t dma_rings; /* DMA handle for rings */ + int dma_size; /* Size of the DMA area */ + char *rx_bufs; /* rx bufs on alpha, sparc, ... */ +}; + +/* +** Kludge to get around the fact that the CSR addresses have different +** offsets in the PCI and EISA boards. Also note that the ethernet address +** PROM is accessed differently. +*/ +static struct bus_type { + int bus; + int bus_num; + int device; + int chipset; + struct de4x5_srom srom; + int autosense; + int useSROM; +} bus; + +/* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +** o the chipset is the same +** o the bus number is the same and > 0 +** o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { + int chipset; + int bus; + int irq; + u_char addr[ETH_ALEN]; +} last = {0,}; + +/* +** The transmit ring full condition is described by the tx_old and tx_new +** pointers by: +** tx_old = tx_new Empty ring +** tx_old = tx_new+1 Full ring +** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition) +*/ +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ + lp->tx_old+lp->txRingSize-lp->tx_new-1:\ + lp->tx_old -lp->tx_new-1) + +#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) + +/* +** Public Functions +*/ +static int de4x5_open(struct net_device *dev); +static int de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev); +static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int de4x5_close(struct net_device *dev); +static struct net_device_stats *de4x5_get_stats(struct net_device *dev); +static void de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len); +static void set_multicast_list(struct net_device *dev); +static int de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev); +static int de4x5_init(struct net_device *dev); +static int de4x5_sw_reset(struct net_device *dev); +static int de4x5_rx(struct net_device *dev); +static int de4x5_tx(struct net_device *dev); +static int de4x5_ast(struct net_device *dev); +static int de4x5_txur(struct net_device *dev); +static int de4x5_rx_ovfc(struct net_device *dev); + +static int autoconf_media(struct net_device *dev); +static void create_packet(struct net_device *dev, char *frame, int len); +static void load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb); +static int dc21040_autoconf(struct net_device *dev); +static int dc21041_autoconf(struct net_device *dev); +static int dc21140m_autoconf(struct net_device *dev); +static int dc2114x_autoconf(struct net_device *dev); +static int srom_autoconf(struct net_device *dev); +static int de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, int (*fn)(struct net_device *, int), int (*asfn)(struct net_device *)); +static int dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct net_device *, int)); +static int test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); +static int test_for_100Mb(struct net_device *dev, int msec); +static int wait_for_link(struct net_device *dev); +static int test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec); +static int is_spd_100(struct net_device *dev); +static int is_100_up(struct net_device *dev); +static int is_10_up(struct net_device *dev); +static int is_anc_capable(struct net_device *dev); +static int ping_media(struct net_device *dev, int msec); +static struct sk_buff *de4x5_alloc_rx_buff(struct net_device *dev, int index, int len); +static void de4x5_free_rx_buffs(struct net_device *dev); +static void de4x5_free_tx_buffs(struct net_device *dev); +static void de4x5_save_skbs(struct net_device *dev); +static void de4x5_rst_desc_ring(struct net_device *dev); +static void de4x5_cache_state(struct net_device *dev, int flag); +static void de4x5_put_cache(struct net_device *dev, struct sk_buff *skb); +static void de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb); +static struct sk_buff *de4x5_get_cache(struct net_device *dev); +static void de4x5_setup_intr(struct net_device *dev); +static void de4x5_init_connection(struct net_device *dev); +static int de4x5_reset_phy(struct net_device *dev); +static void reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 sigr); +static int test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec); +static int test_tp(struct net_device *dev, s32 msec); +static int EISA_signature(char *name, s32 eisa_id); +static int PCI_signature(char *name, struct bus_type *lp); +static void DevicePresent(u_long iobase); +static void enet_addr_rst(u_long aprom_addr); +static int de4x5_bad_srom(struct bus_type *lp); +static short srom_rd(u_long address, u_char offset); +static void srom_latch(u_int command, u_long address); +static void srom_command(u_int command, u_long address); +static void srom_address(u_int command, u_long address, u_char offset); +static short srom_data(u_int command, u_long address); +/*static void srom_busy(u_int command, u_long address);*/ +static void sendto_srom(u_int command, u_long addr); +static int getfrom_srom(u_long addr); +static int srom_map_media(struct net_device *dev); +static int srom_infoleaf_info(struct net_device *dev); +static void srom_init(struct net_device *dev); +static void srom_exec(struct net_device *dev, u_char *p); +static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); +static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); +static int mii_rdata(u_long ioaddr); +static void mii_wdata(int data, int len, u_long ioaddr); +static void mii_ta(u_long rw, u_long ioaddr); +static int mii_swap(int data, int len); +static void mii_address(u_char addr, u_long ioaddr); +static void sendto_mii(u32 command, int data, u_long ioaddr); +static int getfrom_mii(u32 command, u_long ioaddr); +static int mii_get_oui(u_char phyaddr, u_long ioaddr); +static int mii_get_phy(struct net_device *dev); +static void SetMulticastFilter(struct net_device *dev); +static int get_hw_addr(struct net_device *dev); +static void srom_repair(struct net_device *dev, int card); +static int test_bad_enet(struct net_device *dev, int status); +static int an_exception(struct bus_type *lp); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static void eisa_probe(struct net_device *dev, u_long iobase); +#endif +static void pci_probe(struct net_device *dev, u_long iobase); +static void srom_search(struct pci_dev *pdev); +static char *build_setup_frame(struct net_device *dev, int mode); +static void disable_ast(struct net_device *dev); +static void enable_ast(struct net_device *dev, u32 time_out); +static long de4x5_switch_mac_port(struct net_device *dev); +static int gep_rd(struct net_device *dev); +static void gep_wr(s32 data, struct net_device *dev); +static void timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec); +static void yawn(struct net_device *dev, int state); +static void link_modules(struct net_device *dev, struct net_device *tmp); +static void de4x5_parse_params(struct net_device *dev); +static void de4x5_dbg_open(struct net_device *dev); +static void de4x5_dbg_mii(struct net_device *dev, int k); +static void de4x5_dbg_media(struct net_device *dev); +static void de4x5_dbg_srom(struct de4x5_srom *p); +static void de4x5_dbg_rx(struct sk_buff *skb, int len); +static int de4x5_strncmp(char *a, char *b, int n); +static int dc21041_infoleaf(struct net_device *dev); +static int dc21140_infoleaf(struct net_device *dev); +static int dc21142_infoleaf(struct net_device *dev); +static int dc21143_infoleaf(struct net_device *dev); +static int type0_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type1_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type2_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type3_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type4_infoblock(struct net_device *dev, u_char count, u_char *p); +static int type5_infoblock(struct net_device *dev, u_char count, u_char *p); +static int compact_infoblock(struct net_device *dev, u_char count, u_char *p); + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +static struct net_device *unlink_modules(struct net_device *p); +static struct net_device *insert_device(struct net_device *dev, u_long iobase, + int (*init)(struct net_device *)); +static int count_adapters(void); +static int loading_module = 1; +MODULE_PARM(de4x5_debug, "i"); +MODULE_PARM(dec_only, "i"); +MODULE_PARM(args, "s"); +MODULE_PARM_DESC(de4x5_debug, "de4x5 debug mask"); +MODULE_PARM_DESC(dec_only, "de4x5 probe only for Digital boards (0-1)"); +MODULE_PARM_DESC(args, "de4x5 full duplex and media type settings; see de4x5.c for details"); +MODULE_LICENSE("GPL"); + +# else +static int loading_module; +#endif /* MODULE */ + +static char name[DE4X5_NAME_LENGTH + 1]; +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; +static int lastEISA; +# ifdef DE4X5_FORCE_EISA /* Force an EISA bus probe or not */ +static int forceEISA = 1; +# else +static int forceEISA; +# endif +#endif +static int num_de4x5s; +static int cfrv, useSROM; +static int lastPCI = -1; +static struct net_device *lastModule; +static struct pci_dev *pdev; + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { + int chipset; + int (*fn)(struct net_device *); +}; +static struct InfoLeaf infoleaf_array[] = { + {DC21041, dc21041_infoleaf}, + {DC21140, dc21140_infoleaf}, + {DC21142, dc21142_infoleaf}, + {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { + type0_infoblock, + type1_infoblock, + type2_infoblock, + type3_infoblock, + type4_infoblock, + type5_infoblock, + compact_infoblock +}; + +#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) + +/* +** Miscellaneous defines... +*/ +#define RESET_DE4X5 {\ + int i;\ + i=inl(DE4X5_BMR);\ + mdelay(1);\ + outl(i | BMR_SWR, DE4X5_BMR);\ + mdelay(1);\ + outl(i, DE4X5_BMR);\ + mdelay(1);\ + for (i=0;i<5;i++) {inl(DE4X5_BMR); mdelay(1);}\ + mdelay(1);\ +} + +#define PHY_HARD_RESET {\ + outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ + mdelay(1); /* Assert for 1ms */\ + outl(0x00, DE4X5_GEP);\ + mdelay(2); /* Wait for 2ms */\ +} + + +/* +** Autoprobing in modules is allowed here. See the top of the file for +** more info. +*/ +int __init +de4x5_probe(struct net_device *dev) +{ + u_long iobase = dev->base_addr; + + pci_probe(dev, iobase); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + if ((lastPCI == NO_MORE_PCI) && ((num_de4x5s == 0) || forceEISA)) { + eisa_probe(dev, iobase); + } +#endif + + return (dev->priv ? 0 : -ENODEV); +} + +static int __init +de4x5_hw_init(struct net_device *dev, u_long iobase, struct pci_dev *pdev) +{ + struct bus_type *lp = &bus; + int i, status=0; + char *tmp; + + /* Ensure we're not sleeping */ + if (lp->bus == EISA) { + outb(WAKEUP, PCI_CFPM); + } else { + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + } + mdelay(10); + + RESET_DE4X5; + + if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { + return -ENXIO; /* Hardware could not reset */ + } + + /* + ** Now find out what kind of DC21040/DC21041/DC21140 board we have. + */ + useSROM = FALSE; + if (lp->bus == PCI) { + PCI_signature(name, lp); + } else { + EISA_signature(name, EISA_ID0); + } + + if (*name == '\0') { /* Not found a board signature */ + return -ENXIO; + } + + dev->base_addr = iobase; + if (lp->bus == EISA) { + printk("%s: %s at 0x%04lx (EISA slot %ld)", + dev->name, name, iobase, ((iobase>>12)&0x0f)); + } else { /* PCI port address */ + printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name, + iobase, lp->bus_num, lp->device); + } + + printk(", h/w address "); + status = get_hw_addr(dev); + for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x,\n", dev->dev_addr[i]); + + if (status != 0) { + printk(" which has an Ethernet PROM CRC error.\n"); + return -ENXIO; + } else { + struct de4x5_private *lp; + + /* + ** Reserve a section of kernel memory for the adapter + ** private area and the TX/RX descriptor rings. + */ + dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + DE4X5_ALIGN, + GFP_KERNEL); + if (dev->priv == NULL) { + return -ENOMEM; + } + + /* + ** Align to a longword boundary + */ + tmp = dev->priv; + dev->priv = (void *)(((u_long)dev->priv + DE4X5_ALIGN) & ~DE4X5_ALIGN); + lp = (struct de4x5_private *)dev->priv; + memset(dev->priv, 0, sizeof(struct de4x5_private)); + lp->bus = bus.bus; + lp->bus_num = bus.bus_num; + lp->device = bus.device; + lp->chipset = bus.chipset; + lp->cache.priv = tmp; + lp->cache.gepc = GEP_INIT; + lp->asBit = GEP_SLNK; + lp->asPolarity = GEP_SLNK; + lp->asBitValid = TRUE; + lp->timeout = -1; + lp->useSROM = useSROM; + lp->pdev = pdev; + memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); + lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + de4x5_parse_params(dev); + + /* + ** Choose correct autosensing in case someone messed up + */ + lp->autosense = lp->params.autosense; + if (lp->chipset != DC21140) { + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; + } + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; + } + } + lp->fdx = lp->params.fdx; + sprintf(lp->adapter_name,"%s (%s)", name, dev->name); + + lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); +#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) + lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN; +#endif + lp->rx_ring = pci_alloc_consistent(pdev, lp->dma_size, &lp->dma_rings); + if (lp->rx_ring == NULL) { + kfree(lp->cache.priv); + lp->cache.priv = NULL; + return -ENOMEM; + } + + lp->tx_ring = lp->rx_ring + NUM_RX_DESC; + + /* + ** Set up the RX descriptor ring (Intels) + ** Allocate contiguous receive buffers, long word aligned (Alphas) + */ +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = 0; + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + +#else + { + dma_addr_t dma_rx_bufs; + + dma_rx_bufs = lp->dma_rings + (NUM_RX_DESC + NUM_TX_DESC) + * sizeof(struct de4x5_desc); + dma_rx_bufs = (dma_rx_bufs + DE4X5_ALIGN) & ~DE4X5_ALIGN; + lp->rx_bufs = (char *)(((long)(lp->rx_ring + NUM_RX_DESC + + NUM_TX_DESC) + DE4X5_ALIGN) & ~DE4X5_ALIGN); + for (i=0; irx_ring[i].status = 0; + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = + cpu_to_le32(dma_rx_bufs+i*RX_BUFF_SZ); + lp->rx_ring[i].next = 0; + lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ + } + + } +#endif + + barrier(); + + request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE), + lp->adapter_name); + + lp->rxRingSize = NUM_RX_DESC; + lp->txRingSize = NUM_TX_DESC; + + /* Write the end of list marker to the descriptor lists */ + lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); + + /* Tell the adapter where the TX/RX rings are located. */ + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + /* Initialise the IRQ mask and Enable/Disable */ + lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; + lp->irq_en = IMR_NIM | IMR_AIM; + + /* Create a loopback packet frame for later media probing */ + create_packet(dev, lp->frame, sizeof(lp->frame)); + + /* Check if the RX overflow bug needs testing for */ + i = cfrv & 0x000000fe; + if ((lp->chipset == DC21140) && (i == 0x20)) { + lp->rx_ovf = 1; + } + + /* Initialise the SROM pointers if possible */ + if (lp->useSROM) { + lp->state = INITIALISED; + if (srom_infoleaf_info(dev)) { + return -ENXIO; + } + srom_init(dev); + } + + lp->state = CLOSED; + + /* + ** Check for an MII interface + */ + if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { + mii_get_phy(dev); + } + +#ifndef __sparc_v9__ + printk(" and requires IRQ%d (provided by %s).\n", dev->irq, +#else + printk(" and requires IRQ%x (provided by %s).\n", dev->irq, +#endif + ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); + } + + if (de4x5_debug & DEBUG_VERSION) { + printk(version); + } + + /* The DE4X5-specific entries in the device structure. */ + dev->open = &de4x5_open; + dev->hard_start_xmit = &de4x5_queue_pkt; + dev->stop = &de4x5_close; + dev->get_stats = &de4x5_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &de4x5_ioctl; + + dev->mem_start = 0; + + /* Fill in the generic fields of the device structure. */ + ether_setup(dev); + + /* Let the adapter sleep to save power */ + yawn(dev, SLEEP); + + return status; +} + + +static int +de4x5_open(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + s32 omr; + + /* Allocate the RX buffers */ + for (i=0; irxRingSize; i++) { + if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { + de4x5_free_rx_buffs(dev); + return -EAGAIN; + } + } + + /* + ** Wake up the adapter + */ + yawn(dev, WAKEUP); + + /* + ** Re-initialize the DE4X5... + */ + status = de4x5_init(dev); + lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + lp->state = OPEN; + de4x5_dbg_open(dev); + + if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, + lp->adapter_name, dev)) { + printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); + if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, + lp->adapter_name, dev)) { + printk("\n Cannot get IRQ- reconfigure your hardware.\n"); + disable_ast(dev); + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + yawn(dev, SLEEP); + lp->state = CLOSED; + return -EAGAIN; + } else { + printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); + printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); + } + } + + lp->interrupt = UNMASK_INTERRUPTS; + dev->trans_start = jiffies; + + START_DE4X5; + + de4x5_setup_intr(dev); + + if (de4x5_debug & DEBUG_OPEN) { + printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); + printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); + printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); + printk("\tomr: 0x%08x\n", inl(DE4X5_OMR)); + printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); + printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); + printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); + printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); + } + + MOD_INC_USE_COUNT; + + return status; +} + +/* +** Initialize the DE4X5 operating conditions. NB: a chip problem with the +** DC21140 requires using perfect filtering mode for that chip. Since I can't +** see why I'd want > 14 multicast addresses, I have changed all chips to use +** the perfect filtering mode. Keep the DMA burst length at 8: there seems +** to be data corruption problems if it is larger (UDP errors seen from a +** ttcp source). +*/ +static int +de4x5_init(struct net_device *dev) +{ + /* Lock out other processes whilst setting up the hardware */ + netif_stop_queue(dev); + + de4x5_sw_reset(dev); + + /* Autoconfigure the connected port */ + autoconf_media(dev); + + return 0; +} + +static int +de4x5_sw_reset(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 bmr, omr; + + /* Select the MII or SRL port now and RESET the MAC */ + if (!lp->useSROM) { + if (lp->phy[lp->active].id != 0) { + lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; + } else { + lp->infoblock_csr6 = OMR_SDP | OMR_TTM; + } + de4x5_switch_mac_port(dev); + } + + /* + ** Set the programmable burst length to 8 longwords for all the DC21140 + ** Fasternet chips and 4 longwords for all others: DMA errors result + ** without these values. Cache align 16 long. + */ + bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | DE4X5_CACHE_ALIGN; + bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); + outl(bmr, DE4X5_BMR); + + omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ + if (lp->chipset == DC21140) { + omr |= (OMR_SDP | OMR_SB); + } + lp->setup_f = PERFECT; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + + /* Build the setup frame depending on filtering mode */ + SetMulticastFilter(dev); + + load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, (struct sk_buff *)1); + outl(omr|OMR_ST, DE4X5_OMR); + + /* Poll for setup frame completion (adapter interrupts are disabled now) */ + sti(); /* Ensure timer interrupts */ + for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ + mdelay(1); + if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; + } + outl(omr, DE4X5_OMR); /* Stop everything! */ + + if (j == 0) { + printk("%s: Setup frame timed out, status %08x\n", dev->name, + inl(DE4X5_STS)); + status = -EIO; + } + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + lp->tx_old = lp->tx_new; + + return status; +} + +/* +** Writes a socket buffer address to the next available transmit descriptor. +*/ +static int +de4x5_queue_pkt(struct sk_buff *skb, struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int status = 0; + u_long flags = 0; + + netif_stop_queue(dev); + if (lp->tx_enable == NO) { /* Cannot send for now */ + return -1; + } + + /* + ** Clean out the TX ring asynchronously to interrupts - sometimes the + ** interrupts are lost by delayed descriptor status updates relative to + ** the irq assertion, especially with a busy PCI bus. + */ + spin_lock_irqsave(&lp->lock, flags); + de4x5_tx(dev); + spin_unlock_irqrestore(&lp->lock, flags); + + /* Test if cache is already locked - requeue skb if so */ + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) + return -1; + + /* Transmit descriptor ring full or stale skb */ + if (netif_queue_stopped(dev) || (u_long) lp->tx_skb[lp->tx_new] > 1) { + if (lp->interrupt) { + de4x5_putb_cache(dev, skb); /* Requeue the buffer */ + } else { + de4x5_put_cache(dev, skb); + } + if (de4x5_debug & DEBUG_TX) { + printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), netif_queue_stopped(dev), inl(DE4X5_IMR), inl(DE4X5_OMR), ((u_long) lp->tx_skb[lp->tx_new] > 1) ? "YES" : "NO"); + } + } else if (skb->len > 0) { + /* If we already have stuff queued locally, use that first */ + if (lp->cache.skb && !lp->interrupt) { + de4x5_put_cache(dev, skb); + skb = de4x5_get_cache(dev); + } + + while (skb && !netif_queue_stopped(dev) && + (u_long) lp->tx_skb[lp->tx_new] <= 1) { + spin_lock_irqsave(&lp->lock, flags); + netif_stop_queue(dev); + load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); + lp->stats.tx_bytes += skb->len; + outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + dev->trans_start = jiffies; + + if (TX_BUFFS_AVAIL) { + netif_start_queue(dev); /* Another pkt may be queued */ + } + skb = de4x5_get_cache(dev); + spin_unlock_irqrestore(&lp->lock, flags); + } + if (skb) de4x5_putb_cache(dev, skb); + } + + lp->cache.lock = 0; + + return status; +} + +/* +** The DE4X5 interrupt handler. +** +** I/O Read/Writes through intermediate PCI bridges are never 'posted', +** so that the asserted interrupt always has some real data to work with - +** if these I/O accesses are ever changed to memory accesses, ensure the +** STS write is read immediately to complete the transaction if the adapter +** is not on bus 0. Lost interrupts can still occur when the PCI bus load +** is high and descriptor status bits cannot be set before the associated +** interrupt is asserted and this routine entered. +*/ +static void +de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct de4x5_private *lp; + s32 imr, omr, sts, limit; + u_long iobase; + + if (dev == NULL) { + printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq); + return; + } + lp = (struct de4x5_private *)dev->priv; + spin_lock(&lp->lock); + iobase = dev->base_addr; + + DISABLE_IRQs; /* Ensure non re-entrancy */ + + if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + synchronize_irq(); + + for (limit=0; limit<8; limit++) { + sts = inl(DE4X5_STS); /* Read IRQ status */ + outl(sts, DE4X5_STS); /* Reset the board interrupts */ + + if (!(sts & lp->irq_mask)) break;/* All done */ + + if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */ + de4x5_rx(dev); + + if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */ + de4x5_tx(dev); + + if (sts & STS_LNF) { /* TP Link has failed */ + lp->irq_mask &= ~IMR_LFM; + } + + if (sts & STS_UNF) { /* Transmit underrun */ + de4x5_txur(dev); + } + + if (sts & STS_SE) { /* Bus Error */ + STOP_DE4X5; + printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", + dev->name, sts); + spin_unlock(&lp->lock); + return; + } + } + + /* Load the TX ring with any locally stored packets */ + if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { + while (lp->cache.skb && !netif_queue_stopped(dev) && lp->tx_enable) { + de4x5_queue_pkt(de4x5_get_cache(dev), dev); + } + lp->cache.lock = 0; + } + + lp->interrupt = UNMASK_INTERRUPTS; + ENABLE_IRQs; + spin_unlock(&lp->lock); + + return; +} + +static int +de4x5_rx(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; + entry=lp->rx_new) { + status = (s32)le32_to_cpu(lp->rx_ring[entry].status); + + if (lp->rx_ovf) { + if (inl(DE4X5_MFC) & MFC_FOCM) { + de4x5_rx_ovfc(dev); + break; + } + } + + if (status & RD_FS) { /* Remember the start of frame */ + lp->rx_old = entry; + } + + if (status & RD_LS) { /* Valid frame status */ + if (lp->tx_enable) lp->linkOK++; + if (status & RD_ES) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; + if (status & RD_CE) lp->stats.rx_crc_errors++; + if (status & RD_OF) lp->stats.rx_fifo_errors++; + if (status & RD_TL) lp->stats.rx_length_errors++; + if (status & RD_RF) lp->pktStats.rx_runt_frames++; + if (status & RD_CS) lp->pktStats.rx_collision++; + if (status & RD_DB) lp->pktStats.rx_dribble++; + if (status & RD_OF) lp->pktStats.rx_overflow++; + } else { /* A valid frame received */ + struct sk_buff *skb; + short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) + >> 16) - 4; + + if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { + printk("%s: Insufficient memory; nuking packet.\n", + dev->name); + lp->stats.rx_dropped++; + } else { + de4x5_dbg_rx(skb, pkt_len); + + /* Push up the protocol stack */ + skb->protocol=eth_type_trans(skb,dev); + de4x5_local_stats(dev, skb->data, pkt_len); + netif_rx(skb); + + /* Update stats */ + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; + } + } + + /* Change buffer ownership for this frame, back to the adapter */ + for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { + lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); + barrier(); + } + lp->rx_ring[entry].status = cpu_to_le32(R_OWN); + barrier(); + } + + /* + ** Update entry information + */ + lp->rx_new = (++lp->rx_new) % lp->rxRingSize; + } + + return 0; +} + +static inline void +de4x5_free_tx_buff(struct de4x5_private *lp, int entry) +{ + pci_unmap_single(lp->pdev, le32_to_cpu(lp->tx_ring[entry].buf), + le32_to_cpu(lp->tx_ring[entry].des1) & TD_TBS1, + PCI_DMA_TODEVICE); + if ((u_long) lp->tx_skb[entry] > 1) + dev_kfree_skb_irq(lp->tx_skb[entry]); + lp->tx_skb[entry] = NULL; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +de4x5_tx(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int entry; + s32 status; + + for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { + status = (s32)le32_to_cpu(lp->tx_ring[entry].status); + if (status < 0) { /* Buffer not sent yet */ + break; + } else if (status != 0x7fffffff) { /* Not setup frame */ + if (status & TD_ES) { /* An error happened */ + lp->stats.tx_errors++; + if (status & TD_NC) lp->stats.tx_carrier_errors++; + if (status & TD_LC) lp->stats.tx_window_errors++; + if (status & TD_UF) lp->stats.tx_fifo_errors++; + if (status & TD_EC) lp->pktStats.excessive_collisions++; + if (status & TD_DE) lp->stats.tx_aborted_errors++; + + if (TX_PKT_PENDING) { + outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ + } + } else { /* Packet sent */ + lp->stats.tx_packets++; + if (lp->tx_enable) lp->linkOK++; + } + /* Update the collision counter */ + lp->stats.collisions += ((status & TD_EC) ? 16 : + ((status & TD_CC) >> 3)); + + /* Free the buffer. */ + if (lp->tx_skb[entry] != NULL) + de4x5_free_tx_buff(lp, entry); + } + + /* Update all the pointers */ + lp->tx_old = (++lp->tx_old) % lp->txRingSize; + } + + /* Any resources available? */ + if (TX_BUFFS_AVAIL && netif_queue_stopped(dev)) { + if (lp->interrupt) + netif_wake_queue(dev); + else + netif_start_queue(dev); + } + + return 0; +} + +static int +de4x5_ast(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + + disable_ast(dev); + + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21140) { + next_tick = dc21140m_autoconf(dev); + } else if (lp->chipset == DC21041) { + next_tick = dc21041_autoconf(dev); + } else if (lp->chipset == DC21040) { + next_tick = dc21040_autoconf(dev); + } + lp->linkOK = 0; + enable_ast(dev, next_tick); + + return 0; +} + +static int +de4x5_txur(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { + omr &= ~(OMR_ST|OMR_SR); + outl(omr, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_TS); + if ((omr & OMR_TR) < OMR_TR) { + omr += 0x4000; + } else { + omr |= OMR_SF; + } + outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); + } + + return 0; +} + +static int +de4x5_rx_ovfc(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int omr; + + omr = inl(DE4X5_OMR); + outl(omr & ~OMR_SR, DE4X5_OMR); + while (inl(DE4X5_STS) & STS_RS); + + for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { + lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); + lp->rx_new = (++lp->rx_new % lp->rxRingSize); + } + + outl(omr, DE4X5_OMR); + + return 0; +} + +static int +de4x5_close(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 imr, omr; + + disable_ast(dev); + + netif_stop_queue(dev); + + if (de4x5_debug & DEBUG_CLOSE) { + printk("%s: Shutting down ethercard, status was %8.8x.\n", + dev->name, inl(DE4X5_STS)); + } + + /* + ** We stop the DE4X5 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + STOP_DE4X5; + + /* Free the associated irq */ + free_irq(dev->irq, dev); + lp->state = CLOSED; + + /* Free any socket buffers */ + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + + MOD_DEC_USE_COUNT; + + /* Put the adapter to sleep to save power */ + yawn(dev, SLEEP); + + return 0; +} + +static struct net_device_stats * +de4x5_get_stats(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); + + return &lp->stats; +} + +static void +de4x5_local_stats(struct net_device *dev, char *buf, int pkt_len) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=1; ipktStats.bins[i]++; + i = DE4X5_PKT_STAT_SZ; + } + } + if (buf[0] & 0x01) { /* Multicast/Broadcast */ + if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) && + (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { + lp->pktStats.unicast++; + } + + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); + } + + return; +} + +/* +** Removes the TD_IC flag from previous descriptor to improve TX performance. +** If the flag is changed on a descriptor that is being read by the hardware, +** I assume PCI transaction ordering will mean you are either successful or +** just miss asserting the change to the hardware. Anyway you're messing with +** a descriptor you don't own, but this shouldn't kill the chip provided +** the descriptor register is read only to the hardware. +*/ +static void +load_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1); + dma_addr_t buf_dma = pci_map_single(lp->pdev, buf, flags & TD_TBS1, PCI_DMA_TODEVICE); + + lp->tx_ring[lp->tx_new].buf = cpu_to_le32(buf_dma); + lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); + lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); + lp->tx_skb[lp->tx_new] = skb; + lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC); + barrier(); + + lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); + barrier(); +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + /* First, double check that the adapter is open */ + if (lp->state == OPEN) { + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + u32 omr; + omr = inl(DE4X5_OMR); + omr |= OMR_PR; + outl(omr, DE4X5_OMR); + } else { + SetMulticastFilter(dev); + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + dev->trans_start = jiffies; + } + } + + return; +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +*/ +static void +SetMulticastFilter(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct dev_mc_list *dmi=dev->mc_list; + u_long iobase = dev->base_addr; + int i, j, bit, byte; + u16 hashcode; + u32 omr, crc; + char *pa; + unsigned char *addrs; + + omr = inl(DE4X5_OMR); + omr &= ~(OMR_PR | OMR_PM); + pa = build_setup_frame(dev, ALL); /* Build the basic frame */ + + if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) { + omr |= OMR_PM; /* Pass all multicasts */ + } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */ + for (i=0;imc_count;i++) { /* for each address in the list */ + addrs=dmi->dmi_addr; + dmi=dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + crc = ether_crc_le(ETH_ALEN, addrs); + hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ + + byte <<= 1; /* calc offset into setup frame */ + if (byte & 0x02) { + byte -= 1; + } + lp->setup_frame[byte] |= bit; + } + } + } else { /* Perfect filtering */ + for (j=0; jmc_count; j++) { + addrs=dmi->dmi_addr; + dmi=dmi->next; + for (i=0; ibus = EISA; + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EISA_SLOT_INC; /* Get the first slot address */ + i = 1; + maxSlots = MAX_EISA_SLOTS; + } else { /* Probe a specific location */ + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + } + + for (status = -ENODEV; (i> 8) & 0x00ffff00; + vendor = (u_short) cfid; + + /* Read the EISA Configuration Registers */ + irq = inb(EISA_REG0); + irq = de4x5_irq[(irq >> 1) & 0x03]; + + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Write the PCI Configuration Registers */ + outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); + outl(0x00006000, PCI_CFLT); + outl(iobase, PCI_CBIO); + + DevicePresent(EISA_APROM); + + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase, NULL)) == 0) { + num_de4x5s++; + if (loading_module) link_modules(lastModule, dev); + lastEISA = i; + return; + } + } + + if (ioaddr == 0) lastEISA = i; + + return; +} +#endif /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__) */ + +/* +** PCI bus I/O device probe +** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not +** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be +** enabled by the user first in the set up utility. Hence we just check for +** enabled features and silently ignore the card if they're not. +** +** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering +** bit. Here, check for I/O accesses and then set BM. If you put the card in +** a non BM slot, you're on your own (and complain to the PC vendor that your +** PC doesn't conform to the PCI standard)! +** +** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x +** kernels use the V0.535[n] drivers. +*/ +#define PCI_LAST_DEV 32 + +static void __init +pci_probe(struct net_device *dev, u_long ioaddr) +{ + u_char pb, pbus, dev_num, dnum, timer; + u_short vendor, index, status; + u_int irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + struct bus_type *lp = &bus; + + if (lastPCI == NO_MORE_PCI) return; + + if (!pcibios_present()) { + lastPCI = NO_MORE_PCI; + return; /* No PCI bus in this machine! */ + } + + lp->bus = PCI; + lp->bus_num = 0; + + if ((ioaddr < 0x1000) && loading_module) { + pbus = (u_short)(ioaddr >> 8); + dnum = (u_short)(ioaddr & 0xff); + } else { + pbus = 0; + dnum = 0; + } + + for (index=lastPCI+1;(pdev = pci_find_class(class, pdev))!=NULL;index++) { + dev_num = PCI_SLOT(pdev->devfn); + pb = pdev->bus->number; + if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue; + + vendor = pdev->vendor; + device = pdev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; + + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { + lp->bus_num = pb; + srom_search(pdev); + } + + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(pdev, 0); + + /* Fetch the IRQ to be used */ + irq = pdev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses and Bus Mastering are enabled */ + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); +#ifdef __powerpc__ + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); + } +#endif /* __powerpc__ */ + if (!(status & PCI_COMMAND_IO)) continue; + + if (!(status & PCI_COMMAND_MASTER)) { + status |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status); + pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status); + } + if (!(status & PCI_COMMAND_MASTER)) continue; + + /* Check the latency timer for values >= 0x60 */ + pcibios_read_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pcibios_write_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, 0x60); + } + + DevicePresent(DE4X5_APROM); + if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase, pdev)) == 0) { + num_de4x5s++; + lastPCI = index; + if (loading_module) link_modules(lastModule, dev); + return; + } + } else if (ioaddr != 0) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, + iobase); + } + } + + lastPCI = NO_MORE_PCI; + + return; +} + +/* +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... +*/ +static void __init +srom_search(struct pci_dev *dev) +{ + u_char pb; + u_short vendor, status; + u_int irq = 0, device; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + int i, j; + struct bus_type *lp = &bus; + struct list_head *walk = &dev->bus_list; + + for (walk = walk->next; walk != &dev->bus_list; walk = walk->next) { + struct pci_dev *this_dev = pci_dev_b(walk); + + /* Skip the pci_bus list entry */ + if (list_entry(walk, struct pci_bus, devices) == dev->bus) continue; + + vendor = this_dev->vendor; + device = this_dev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; + + /* Get the chip configuration revision register */ + pb = this_dev->bus->number; + pcibios_read_config_dword(pb, this_dev->devfn, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = PCI_SLOT(this_dev->devfn); + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) { + device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143); + } + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pci_resource_start(this_dev, 0); + + /* Fetch the IRQ to be used */ + irq = this_dev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses are enabled */ + pcibios_read_config_word(pb, this_dev->devfn, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) continue; + + /* Search for a valid SROM attached to this DECchip */ + DevicePresent(DE4X5_APROM); + for (j=0, i=0; isrom + SROM_HWADD + i); + } + if ((j != 0) && (j != 0x5fa)) { + last.chipset = device; + last.bus = pb; + last.irq = irq; + for (i=0; isrom + SROM_HWADD + i); + } + return; + } + } + + return; +} + +static void __init +link_modules(struct net_device *dev, struct net_device *tmp) +{ + struct net_device *p=dev; + + if (p) { + while (((struct de4x5_private *)(p->priv))->next_module) { + p = ((struct de4x5_private *)(p->priv))->next_module; + } + + if (dev != tmp) { + ((struct de4x5_private *)(p->priv))->next_module = tmp; + } else { + ((struct de4x5_private *)(p->priv))->next_module = NULL; + } + } + + return; +} + +/* +** Auto configure the media here rather than setting the port at compile +** time. This routine is called by de4x5_init() and when a loss of media is +** detected (excessive collisions, loss of carrier, no carrier or link fail +** [TP] or no recent receive activity) to check whether the user has been +** sneaky and changed the port on us. +*/ +static int +autoconf_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = DE4X5_AUTOSENSE_MS; + + lp->linkOK = 0; + lp->c_media = AUTO; /* Bogus last media */ + disable_ast(dev); + inl(DE4X5_MFC); /* Zero the lost frames counter */ + lp->media = INIT; + lp->tcount = 0; + + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21040) { + next_tick = dc21040_autoconf(dev); + } else if (lp->chipset == DC21041) { + next_tick = dc21041_autoconf(dev); + } else if (lp->chipset == DC21140) { + next_tick = dc21140m_autoconf(dev); + } + + enable_ast(dev, next_tick); + + return (lp->media); +} + +/* +** Autoconfigure the media when using the DC21040. AUI cannot be distinguished +** from BNC as the port has a jumper to set thick or thin wire. When set for +** BNC, the BNC port will indicate activity if it's not terminated correctly. +** The only way to test for that is to place a loopback packet onto the +** network and watch for errors. Since we're messing with the interrupt mask +** register, disable the board interrupts and do not allow any more packets to +** be queued to the hardware. Re-enable everything only when the media is +** found. +** I may have to "age out" locally queued packets so that the higher layer +** timeouts don't effectively duplicate packets on the network. +*/ +static int +dc21040_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = DE4X5_AUTOSENSE_MS; + s32 imr; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = NO; + lp->timeout = -1; + de4x5_save_skbs(dev); + if ((lp->autosense == AUTO) || (lp->autosense == TP)) { + lp->media = TP; + } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { + lp->media = BNC_AUI; + } else if (lp->autosense == EXT_SIA) { + lp->media = EXT_SIA; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21040_autoconf(dev); + break; + + case TP: + next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, + TP_SUSPECT, test_tp); + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); + break; + + case BNC: + case AUI: + case BNC_AUI: + next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, + BNC_AUI_SUSPECT, ping_media); + break; + + case BNC_AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); + break; + + case EXT_SIA: + next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, + NC, EXT_SIA_SUSPECT, ping_media); + break; + + case EXT_SIA_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); + break; + + case NC: + /* default to TP for all */ + reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = NO; + break; + } + + return next_tick; +} + +static int +dc21040_state(struct net_device *dev, int csr13, int csr14, int csr15, int timeout, + int next_state, int suspect_state, + int (*fn)(struct net_device *, int)) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 0: + reset_init_sia(dev, csr13, csr14, csr15); + lp->local_state++; + next_tick = 500; + break; + + case 1: + if (!lp->tx_enable) { + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else { + if (linkBad && (lp->autosense == AUTO)) { + lp->local_state = 0; + lp->media = next_state; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = suspect_state; + next_tick = 3000; + } + break; + } + + return next_tick; +} + +static int +de4x5_suspect_state(struct net_device *dev, int timeout, int prev_state, + int (*fn)(struct net_device *, int), + int (*asfn)(struct net_device *)) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int next_tick = DE4X5_AUTOSENSE_MS; + int linkBad; + + switch (lp->local_state) { + case 1: + if (lp->linkOK) { + lp->media = prev_state; + } else { + lp->local_state++; + next_tick = asfn(dev); + } + break; + + case 2: + linkBad = fn(dev, timeout); + if (linkBad < 0) { + next_tick = linkBad & ~TIMER_CB; + } else if (!linkBad) { + lp->local_state--; + lp->media = prev_state; + } else { + lp->media = INIT; + lp->tcount++; + } + } + + return next_tick; +} + +/* +** Autoconfigure the media when using the DC21041. AUI needs to be tested +** before BNC, because the BNC port will indicate activity if it's not +** terminated correctly. The only way to test for that is to place a loopback +** packet onto the network and watch for errors. Since we're messing with +** the interrupt mask register, disable the board interrupts and do not allow +** any more packets to be queued to the hardware. Re-enable everything only +** when the media is found. +*/ +static int +dc21041_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, irqs, irq_mask, imr, omr; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + DISABLE_IRQs; + lp->tx_enable = NO; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { + lp->media = TP; /* On chip auto negotiation is broken */ + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = NC; + } + lp->local_state = 0; + next_tick = dc21041_autoconf(dev); + break; + + case TP_NW: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts & STS_LNP) { + lp->media = ANS; + } else { + lp->media = AUI; + } + next_tick = dc21041_autoconf(dev); + } + break; + + case ANS: + if (!lp->tx_enable) { + irqs = STS_LNP; + irq_mask = IMR_LPM; + sts = test_ans(dev, irqs, irq_mask, 3000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + lp->media = TP; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = ANS_SUSPECT; + next_tick = 3000; + } + break; + + case ANS_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); + break; + + case TP: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = STS_LNF | STS_LNP; + irq_mask = IMR_LFM | IMR_LPM; + sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { + if (inl(DE4X5_SISR) & SISR_NRA) { + lp->media = AUI; /* Non selected port activity */ + } else { + lp->media = BNC; + } + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = TP_SUSPECT; + next_tick = 3000; + } + break; + + case TP_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc21041_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc21041_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->media = NC; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); + break; + + case NC: + omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ + outl(omr | OMR_FDX, DE4X5_OMR); + reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = NO; + break; + } + + return next_tick; +} + +/* +** Some autonegotiation chips are broken in that they do not return the +** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement +** register, except at the first power up negotiation. +*/ +static int +dc21140m_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int ana, anlpa, cap, cr, slnk, sr; + int next_tick = DE4X5_AUTOSENSE_MS; + u_long imr, omr, iobase = dev->base_addr; + + switch(lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->useSROM) { + if (srom_map_media(dev) < 0) { + lp->tcount++; + return next_tick; + } + srom_exec(dev, lp->phy[lp->active].gep); + if (lp->infoblock_media == ANS) { + ana = lp->phy[lp->active].ana | MII_ANA_CSMA; + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + } + } else { + lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ + SET_10Mb; + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if ((lp->autosense == AUTO) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } else if (lp->autosense == AUTO) { + lp->media = SPD_DET; + } else if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else { + lp->media = NC; + } + } + lp->local_state = 0; + next_tick = dc21140m_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc21140m_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (lp->timeout < 0) { + lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : + (~gep_rd(dev) & GEP_LNP)); + SET_100Mb_PDET; + } + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + next_tick = slnk & ~TIMER_CB; + } else { + if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { + lp->media = _10Mb; + } else { + lp->media = NC; + } + next_tick = dc21140m_autoconf(dev); + } + break; + + case _100Mb: /* Set 100Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case BNC: + case AUI: + case _10Mb: /* Set 10Mb/s */ + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case NC: + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tx_enable = FALSE; + break; + } + + return next_tick; +} + +/* +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. +*/ +static int +dc2114x_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; + int next_tick = DE4X5_AUTOSENSE_MS; + + switch (lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if (lp->params.autosense & ~AUTO) { + srom_map_media(dev); /* Fixed media requested */ + if (lp->media != lp->params.autosense) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + lp->media = INIT; + } + } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } + } + lp->local_state = 0; + next_tick = dc2114x_autoconf(dev); + } + break; + + case ANS: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc2114x_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc2114x_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->tcount++; + lp->media = INIT; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (srom_map_media(dev) < 0) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + if (lp->media == _100Mb) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + lp->media = SPD_DET; + return (slnk & ~TIMER_CB); + } + } else { + if (wait_for_link(dev) < 0) { + lp->media = SPD_DET; + return PDET_LINK_WAIT; + } + } + if (lp->media == ANS) { /* Do MII parallel detection */ + if (is_spd_100(dev)) { + lp->media = _100Mb; + } else { + lp->media = _10Mb; + } + next_tick = dc2114x_autoconf(dev); + } else if (((lp->media == _100Mb) && is_100_up(dev)) || + (((lp->media == _10Mb) || (lp->media == TP) || + (lp->media == BNC) || (lp->media == AUI)) && + is_10_up(dev))) { + next_tick = dc2114x_autoconf(dev); + } else { + lp->tcount++; + lp->media = INIT; + } + break; + + case _10Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case _100Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + default: + lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); + lp->media = INIT; + break; + } + + return next_tick; +} + +static int +srom_autoconf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + if (lp->infoblock_media == lp->media) + return 0; + + switch(lp->infoblock_media) { + case SROM_10BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_10BASET: + if (lp->params.fdx && !lp->fdx) return -1; + if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASET: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASEF: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + lp->fdx = lp->params.fdx; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + return -1; + break; + } + + return 0; +} + +static void +de4x5_init_connection(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_long flags = 0; + + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; /* Stop scrolling media messages */ + } + + spin_lock_irqsave(&lp->lock, flags); + de4x5_rst_desc_ring(dev); + de4x5_setup_intr(dev); + lp->tx_enable = YES; + spin_unlock_irqrestore(&lp->lock, flags); + outl(POLL_DEMAND, DE4X5_TPD); + + netif_wake_queue(dev); + + return; +} + +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ +static int +de4x5_reset_phy(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int next_tick = 0; + + if ((lp->useSROM) || (lp->phy[lp->active].id)) { + if (lp->timeout < 0) { + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); + } + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; + } + + return next_tick; +} + +static int +test_media(struct net_device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, csr12; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ + reset_init_sia(dev, csr13, csr14, csr15); + } + + /* set up the interrupt mask */ + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + + /* clear csr12 NRA and SRA bits */ + if ((lp->chipset == DC21041) || lp->useSROM) { + csr12 = inl(DE4X5_SISR); + outl(csr12, DE4X5_SISR); + } + } + + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static int +test_tp(struct net_device *dev, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); + + if (sisr && --lp->timeout) { + sisr = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sisr; +} + +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500 /* ms */ +#define SAMPLE_DELAY 2000 /* ms */ +static int +test_for_100Mb(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + + if (lp->timeout < 0) { + if ((msec/SAMPLE_INTERVAL) <= 0) return 0; + if (msec > SAMPLE_DELAY) { + lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; + gep = SAMPLE_DELAY | TIMER_CB; + return gep; + } else { + lp->timeout = msec/SAMPLE_INTERVAL; + } + } + + if (lp->phy[lp->active].id || lp->useSROM) { + gep = is_100_up(dev) | is_spd_100(dev); + } else { + gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); + } + if (!(gep & ret) && --lp->timeout) { + gep = SAMPLE_INTERVAL | TIMER_CB; + } else { + lp->timeout = -1; + } + + return gep; +} + +static int +wait_for_link(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->timeout < 0) { + lp->timeout = 1; + } + + if (lp->timeout--) { + return TIMER_CB; + } else { + lp->timeout = -1; + } + + return 0; +} + +/* +** +** +*/ +static int +test_mii_reg(struct net_device *dev, int reg, int mask, int pol, long msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int test; + u_long iobase = dev->base_addr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + } + + if (pol) pol = ~0; + reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; + test = (reg ^ pol) & mask; + + if (test && --lp->timeout) { + reg = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return reg; +} + +static int +is_spd_100(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int spd; + + if (lp->useMII) { + spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); + spd = ~(spd ^ lp->phy[lp->active].spd.value); + spd &= lp->phy[lp->active].spd.mask; + } else if (!lp->useSROM) { /* de500-xa */ + spd = ((~gep_rd(dev)) & GEP_SLNK); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } + + return spd; +} + +static int +is_100_up(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_SLNK); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } +} + +static int +is_10_up(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->useMII) { + /* Double read for sticky bits & temporary drops */ + mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_LNP); + } else { + if ((lp->ibn == 2) || !lp->asBitValid) + return (((lp->chipset & ~0x00ff) == DC2114x) ? + (~inl(DE4X5_SISR)&SISR_LS10): + 0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } +} + +static int +is_anc_capable(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SISR) & SISR_LPN) >> 12; + } else { + return 0; + } +} + +/* +** Send a packet onto the media and watch for send errors that indicate the +** media is bad or unconnected. +*/ +static int +ping_media(struct net_device *dev, int msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int sisr; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + + lp->tmp = lp->tx_new; /* Remember the ring position */ + load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), (struct sk_buff *)1); + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); + } + + sisr = inl(DE4X5_SISR); + + if ((!(sisr & SISR_NCR)) && + ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && + (--lp->timeout)) { + sisr = 100 | TIMER_CB; + } else { + if ((!(sisr & SISR_NCR)) && + !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && + lp->timeout) { + sisr = 0; + } else { + sisr = 1; + } + lp->timeout = -1; + } + + return sisr; +} + +/* +** This function does 2 things: on Intels it kmalloc's another buffer to +** replace the one about to be passed up. On Alpha's it kmallocs a buffer +** into which the packet is copied. +*/ +static struct sk_buff * +de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p; + +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) + struct sk_buff *ret; + u_long i=0, tmp; + + p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2); + if (!p) return NULL; + + p->dev = dev; + tmp = virt_to_bus(p->data); + i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp; + skb_reserve(p, i); + lp->rx_ring[index].buf = cpu_to_le32(tmp + i); + + ret = lp->rx_skb[index]; + lp->rx_skb[index] = p; + + if ((u_long) ret > 1) { + skb_put(ret, len); + } + + return ret; + +#else + if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ + + p = dev_alloc_skb(len + 2); + if (!p) return NULL; + + p->dev = dev; + skb_reserve(p, 2); /* Align */ + if (index < lp->rx_old) { /* Wrapped buffer */ + short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; + memcpy(skb_put(p,tlen),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,tlen); + memcpy(skb_put(p,len-tlen),lp->rx_bufs,len-tlen); + } else { /* Linear buffer */ + memcpy(skb_put(p,len),lp->rx_bufs + lp->rx_old * RX_BUFF_SZ,len); + } + + return p; +#endif +} + +static void +de4x5_free_rx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=0; irxRingSize; i++) { + if ((u_long) lp->rx_skb[i] > 1) { + dev_kfree_skb(lp->rx_skb[i]); + } + lp->rx_ring[i].status = 0; + lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */ + } + + return; +} + +static void +de4x5_free_tx_buffs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + for (i=0; itxRingSize; i++) { + if (lp->tx_skb[i]) + de4x5_free_tx_buff(lp, i); + lp->tx_ring[i].status = 0; + } + + /* Unload the locally queued packets */ + while (lp->cache.skb) { + dev_kfree_skb(de4x5_get_cache(dev)); + } + + return; +} + +/* +** When a user pulls a connection, the DECchip can end up in a +** 'running - waiting for end of transmission' state. This means that we +** have to perform a chip soft reset to ensure that we can synchronize +** the hardware and software and make any media probes using a loopback +** packet meaningful. +*/ +static void +de4x5_save_skbs(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 omr; + + if (!lp->cache.save_cnt) { + STOP_DE4X5; + de4x5_tx(dev); /* Flush any sent skb's */ + de4x5_free_tx_buffs(dev); + de4x5_cache_state(dev, DE4X5_SAVE_STATE); + de4x5_sw_reset(dev); + de4x5_cache_state(dev, DE4X5_RESTORE_STATE); + lp->cache.save_cnt++; + START_DE4X5; + } + + return; +} + +static void +de4x5_rst_desc_ring(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i; + s32 omr; + + if (lp->cache.save_cnt) { + STOP_DE4X5; + outl(lp->dma_rings, DE4X5_RRBA); + outl(lp->dma_rings + NUM_RX_DESC * sizeof(struct de4x5_desc), + DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); + lp->cache.save_cnt--; + START_DE4X5; + } + + return; +} + +static void +de4x5_cache_state(struct net_device *dev, int flag) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + switch(flag) { + case DE4X5_SAVE_STATE: + lp->cache.csr0 = inl(DE4X5_BMR); + lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); + lp->cache.csr7 = inl(DE4X5_IMR); + break; + + case DE4X5_RESTORE_STATE: + outl(lp->cache.csr0, DE4X5_BMR); + outl(lp->cache.csr6, DE4X5_OMR); + outl(lp->cache.csr7, DE4X5_IMR); + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, + lp->cache.csr15); + } + break; + } + + return; +} + +static void +de4x5_put_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p; + + if (lp->cache.skb) { + for (p=lp->cache.skb; p->next; p=p->next); + p->next = skb; + } else { + lp->cache.skb = skb; + } + skb->next = NULL; + + return; +} + +static void +de4x5_putb_cache(struct net_device *dev, struct sk_buff *skb) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p = lp->cache.skb; + + lp->cache.skb = skb; + skb->next = p; + + return; +} + +static struct sk_buff * +de4x5_get_cache(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct sk_buff *p = lp->cache.skb; + + if (p) { + lp->cache.skb = p->next; + p->next = NULL; + } + + return p; +} + +/* +** Check the Auto Negotiation State. Return OK when a link pass interrupt +** is received and the auto-negotiation status is NWAY OK. +*/ +static int +test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 sts, ans; + + if (lp->timeout < 0) { + lp->timeout = msec/100; + outl(irq_mask, DE4X5_IMR); + + /* clear all pending interrupts */ + sts = inl(DE4X5_STS); + outl(sts, DE4X5_STS); + } + + ans = inl(DE4X5_SISR) & SISR_ANS; + sts = inl(DE4X5_STS) & ~TIMER_CB; + + if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { + sts = 100 | TIMER_CB; + } else { + lp->timeout = -1; + } + + return sts; +} + +static void +de4x5_setup_intr(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 imr, sts; + + if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */ + imr = 0; + UNMASK_IRQs; + sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */ + outl(sts, DE4X5_STS); + ENABLE_IRQs; + } + + return; +} + +/* +** +*/ +static void +reset_init_sia(struct net_device *dev, s32 csr13, s32 csr14, s32 csr15) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + RESET_SIA; + if (lp->useSROM) { + if (lp->ibn == 3) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].gep); + outl(1, DE4X5_SICR); + return; + } else { + csr15 = lp->cache.csr15; + csr14 = lp->cache.csr14; + csr13 = lp->cache.csr13; + outl(csr15 | lp->cache.gepc, DE4X5_SIGR); + outl(csr15 | lp->cache.gep, DE4X5_SIGR); + } + } else { + outl(csr15, DE4X5_SIGR); + } + outl(csr14, DE4X5_STRR); + outl(csr13, DE4X5_SICR); + + mdelay(10); + + return; +} + +/* +** Create a loopback ethernet packet +*/ +static void +create_packet(struct net_device *dev, char *frame, int len) +{ + int i; + char *buf = frame; + + for (i=0; idev_addr[i]; + } + for (i=0; idev_addr[i]; + } + + *buf++ = 0; /* Packet length (2 bytes) */ + *buf++ = 1; + + return; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int +EISA_signature(char *name, s32 eisa_id) +{ + static c_char *signatures[] = DE4X5_SIGNATURE; + char ManCode[DE4X5_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *); + + *name = '\0'; + Eisa.ID = inl(eisa_id); + + ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); + ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); + ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); + ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); + ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); + ManCode[5]='\0'; + + for (i=0;ichipset == DC21040) { + strcpy(name, "DE434/5"); + return status; + } else { /* Search for a DEC name in the SROM */ + int i = *((char *)&lp->srom + 19) * 3; + strncpy(name, (char *)&lp->srom + 26 + i, 8); + } + name[8] = '\0'; + for (i=0; ichipset == DC21040) ? "DC21040" : + ((lp->chipset == DC21041) ? "DC21041" : + ((lp->chipset == DC21140) ? "DC21140" : + ((lp->chipset == DC21142) ? "DC21142" : + ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" + ))))))); + } + if (lp->chipset != DC21041) { + useSROM = TRUE; /* card is not recognisably DEC */ + } + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + useSROM = TRUE; + } + + return status; +} + +/* +** Set up the Ethernet PROM counter to the start of the Ethernet address on +** the DC21040, else read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). +*/ +static void +DevicePresent(u_long aprom_addr) +{ + int i, j=0; + struct bus_type *lp = &bus; + + if (lp->chipset == DC21040) { + if (lp->bus == EISA) { + enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } else { + outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } + } else { /* Read new srom */ + u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); + for (i=0; i<(ETH_ALEN>>1); i++) { + tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); + *p = le16_to_cpu(tmp); + j += *p++; + } + if ((j == 0) || (j == 0x2fffd)) { + return; + } + + p=(short *)&lp->srom; + for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { + tmp = srom_rd(aprom_addr, i); + *p++ = le16_to_cpu(tmp); + } + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + } + + return; +} + +/* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } dev; + short sigLength=0; + s8 data; + int i, j; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i=0,j=0;jbase_addr; + int broken, i, k, tmp, status = 0; + u_short j,chksum; + struct bus_type *lp = &bus; + + broken = de4x5_bad_srom(lp); + + for (i=0,k=0,j=0;j<3;j++) { + k <<= 1; + if (k > 0xffff) k-=0xffff; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_char) tmp; + dev->dev_addr[i++] = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + k += (u_short) (tmp << 8); + dev->dev_addr[i++] = (u_char) tmp; + } else if (!broken) { + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; + } else if ((broken == SMC) || (broken == ACCTON)) { + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; + } + } else { + k += (u_char) (tmp = inb(EISA_APROM)); + dev->dev_addr[i++] = (u_char) tmp; + k += (u_short) ((tmp = inb(EISA_APROM)) << 8); + dev->dev_addr[i++] = (u_char) tmp; + } + + if (k > 0xffff) k-=0xffff; + } + if (k == 0xffff) k=0; + + if (lp->bus == PCI) { + if (lp->chipset == DC21040) { + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum = (u_char) tmp; + while ((tmp = inl(DE4X5_APROM)) < 0); + chksum |= (u_short) (tmp << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + } else { + chksum = (u_char) inb(EISA_APROM); + chksum |= (u_short) (inb(EISA_APROM) << 8); + if ((k != chksum) && (dec_only)) status = -1; + } + + /* If possible, try to fix a broken card - SMC only so far */ + srom_repair(dev, broken); + +#ifdef CONFIG_PPC + /* + ** If the address starts with 00 a0, we have to bit-reverse + ** each byte of the address. + */ + if ( (ppc_md.ppc_machine & _MACH_Pmac) && + (dev->dev_addr[0] == 0) && + (dev->dev_addr[1] == 0xa0) ) + { + for (i = 0; i < ETH_ALEN; ++i) + { + int x = dev->dev_addr[i]; + x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); + x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); + dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); + } + } +#endif /* CONFIG_PPC */ + + /* Test for a bad enet address */ + status = test_bad_enet(dev, status); + + return status; +} + +/* +** Test for enet addresses in the first 32 bytes. The built-in strncmp +** didn't seem to work here...? +*/ +static int +de4x5_bad_srom(struct bus_type *lp) +{ + int i, status = 0; + + for (i=0; isrom, (char *)&enet_det[i], 3) && + !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { + if (i == 0) { + status = SMC; + } else if (i == 1) { + status = ACCTON; + } + break; + } + } + + return status; +} + +static int +de4x5_strncmp(char *a, char *b, int n) +{ + int ret=0; + + for (;n && !ret;n--) { + ret = *a++ - *b++; + } + + return ret; +} + +static void +srom_repair(struct net_device *dev, int card) +{ + struct bus_type *lp = &bus; + + switch(card) { + case SMC: + memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); + memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); + memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); + useSROM = TRUE; + break; + } + + return; +} + +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ +static int +test_bad_enet(struct net_device *dev, int status) +{ + struct bus_type *lp = &bus; + int i, tmp; + + for (tmp=0,i=0; idev_addr[i]; + if ((tmp == 0) || (tmp == 0x5fa)) { + if ((lp->chipset == last.chipset) && + (lp->bus_num == last.bus) && (lp->bus_num > 0)) { + for (i=0; idev_addr[i] = last.addr[i]; + for (i=ETH_ALEN-1; i>2; --i) { + dev->dev_addr[i] += 1; + if (dev->dev_addr[i] != 0) break; + } + for (i=0; idev_addr[i]; + if (!an_exception(lp)) { + dev->irq = last.irq; + } + + status = 0; + } + } else if (!status) { + last.chipset = lp->chipset; + last.bus = lp->bus_num; + last.irq = dev->irq; + for (i=0; idev_addr[i]; + } + + return status; +} + +/* +** List of board exceptions with correctly wired IRQs +*/ +static int +an_exception(struct bus_type *lp) +{ + if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && + (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { + return -1; + } + + return 0; +} + +/* +** SROM Read +*/ +static short +srom_rd(u_long addr, u_char offset) +{ + sendto_srom(SROM_RD | SROM_SR, addr); + + srom_latch(SROM_RD | SROM_SR | DT_CS, addr); + srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); + srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); + + return srom_data(SROM_RD | SROM_SR | DT_CS, addr); +} + +static void +srom_latch(u_int command, u_long addr) +{ + sendto_srom(command, addr); + sendto_srom(command | DT_CLK, addr); + sendto_srom(command, addr); + + return; +} + +static void +srom_command(u_int command, u_long addr) +{ + srom_latch(command, addr); + srom_latch(command, addr); + srom_latch((command & 0x0000ff00) | DT_CS, addr); + + return; +} + +static void +srom_address(u_int command, u_long addr, u_char offset) +{ + int i, a; + + a = offset << 2; + for (i=0; i<6; i++, a <<= 1) { + srom_latch(command | ((a & 0x80) ? DT_IN : 0), addr); + } + udelay(1); + + i = (getfrom_srom(addr) >> 3) & 0x01; + + return; +} + +static short +srom_data(u_int command, u_long addr) +{ + int i; + short word = 0; + s32 tmp; + + for (i=0; i<16; i++) { + sendto_srom(command | DT_CLK, addr); + tmp = getfrom_srom(addr); + sendto_srom(command, addr); + + word = (word << 1) | ((tmp >> 3) & 0x01); + } + + sendto_srom(command & 0x0000ff00, addr); + + return word; +} + +/* +static void +srom_busy(u_int command, u_long addr) +{ + sendto_srom((command & 0x0000ff00) | DT_CS, addr); + + while (!((getfrom_srom(addr) >> 3) & 0x01)) { + mdelay(1); + } + + sendto_srom(command & 0x0000ff00, addr); + + return; +} +*/ + +static void +sendto_srom(u_int command, u_long addr) +{ + outl(command, addr); + udelay(1); + + return; +} + +static int +getfrom_srom(u_long addr) +{ + s32 tmp; + + tmp = inl(addr); + udelay(1); + + return tmp; +} + +static int +srom_infoleaf_info(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i, count; + u_char *p; + + /* Find the infoleaf decoder function that matches this chipset */ + for (i=0; ichipset == infoleaf_array[i].chipset) break; + } + if (i == INFOLEAF_SIZE) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct chipset for SROM decoding!\n", + dev->name); + return -ENXIO; + } + + lp->infoleaf_fn = infoleaf_array[i].fn; + + /* Find the information offset that this function should use */ + count = *((u_char *)&lp->srom + 19); + p = (u_char *)&lp->srom + 26; + + if (count > 1) { + for (i=count; i; --i, p+=3) { + if (lp->device == *p) break; + } + if (i == 0) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", + dev->name, lp->device); + return -ENXIO; + } + } + + lp->infoleaf_offset = TWIDDLE(p+1); + + return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + u_char count; + + p+=2; + if (lp->chipset == DC21140) { + lp->cache.gepc = (*p++ | GEP_CTRL); + gep_wr(lp->cache.gepc, dev); + } + + /* Block count */ + count = *p++; + + /* Jump the infoblocks to find types */ + for (;count; --count) { + if (*p < 128) { + p += COMPACT_LEN; + } else if (*(p+1) == 5) { + type5_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 4) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 3) { + type3_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 2) { + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 1) { + type1_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else { + p += ((*p & BLOCK_LEN) + 1); + } + } + + return; +} + +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ +static void +srom_exec(struct net_device *dev, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char count = (p ? *p++ : 0); + u_short *w = (u_short *)p; + + if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + + if (lp->chipset != DC21140) RESET_SIA; + + while (count--) { + gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? + *p++ : TWIDDLE(w++)), dev); + mdelay(2); /* 2ms per action */ + } + + if (lp->chipset != DC21140) { + outl(lp->cache.csr14, DE4X5_STRR); + outl(lp->cache.csr13, DE4X5_SICR); + } + + return; +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int +dc21041_infoleaf(struct net_device *dev) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21140_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* GEP control */ + lp->cache.gepc = (*p++ | GEP_CTRL); + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21142_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21143_infoleaf(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int +compact_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+COMPACT_LEN) < 128) { + return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); + } else { + return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = COMPACT; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + lp->infoblock_media = (*p++) & COMPACT_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. +*/ +static int +type0_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 0; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int +type1_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 1; + lp->active = *p++; + lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 1; + lp->active = *p; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type2_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 2; + lp->active = 0; + p += 2; + lp->infoblock_media = (*p) & MEDIA_CODE; + + if ((*p++) & EXT_FIELD) { + lp->cache.csr13 = TWIDDLE(p); p += 2; + lp->cache.csr14 = TWIDDLE(p); p += 2; + lp->cache.csr15 = TWIDDLE(p); p += 2; + } else { + lp->cache.csr13 = CSR13; + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + } + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); + lp->infoblock_csr6 = OMR_SIA; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type3_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 3; + lp->active = *p++; + if (MOTO_SROM_BUG) lp->active = 0; + lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; + lp->phy[lp->active].mci = *p; + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 3; + lp->active = *p; + if (MOTO_SROM_BUG) lp->active = 0; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type4_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 4; + lp->active = 0; + p+=2; + lp->infoblock_media = (*p++) & MEDIA_CODE; + lp->cache.csr13 = CSR13; /* Hard coded defaults */ + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int +type5_infoblock(struct net_device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + /* Must be initializing to run this code */ + if ((lp->state == INITIALISED) || (lp->media == INIT)) { + p+=2; + lp->rst = p; + srom_exec(dev, lp->rst); + } + + return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ + +static int +mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to read */ + mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ + + return mii_rdata(ioaddr); /* Read data */ +} + +static void +mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */ + mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ + mii_address(phyreg, ioaddr); /* PHY Register to write */ + mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */ + data = mii_swap(data, 16); /* Swap data bit ordering */ + mii_wdata(data, 16, ioaddr); /* Write data */ + + return; +} + +static int +mii_rdata(u_long ioaddr) +{ + int i; + s32 tmp = 0; + + for (i=0; i<16; i++) { + tmp <<= 1; + tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); + } + + return tmp; +} + +static void +mii_wdata(int data, int len, u_long ioaddr) +{ + int i; + + for (i=0; i>= 1; + } + + return; +} + +static void +mii_address(u_char addr, u_long ioaddr) +{ + int i; + + addr = mii_swap(addr, 5); + for (i=0; i<5; i++) { + sendto_mii(MII_MWR | MII_WR, addr, ioaddr); + addr >>= 1; + } + + return; +} + +static void +mii_ta(u_long rw, u_long ioaddr) +{ + if (rw == MII_STWR) { + sendto_mii(MII_MWR | MII_WR, 1, ioaddr); + sendto_mii(MII_MWR | MII_WR, 0, ioaddr); + } else { + getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */ + } + + return; +} + +static int +mii_swap(int data, int len) +{ + int i, tmp = 0; + + for (i=0; i>= 1; + } + + return tmp; +} + +static void +sendto_mii(u32 command, int data, u_long ioaddr) +{ + u32 j; + + j = (data & 1) << 17; + outl(command | j, ioaddr); + udelay(1); + outl(command | MII_MDC | j, ioaddr); + udelay(1); + + return; +} + +static int +getfrom_mii(u32 command, u_long ioaddr) +{ + outl(command, ioaddr); + udelay(1); + outl(command | MII_MDC, ioaddr); + udelay(1); + + return ((inl(ioaddr) >> 19) & 1); +} + +/* +** Here's 3 ways to calculate the OUI from the ID registers. +*/ +static int +mii_get_oui(u_char phyaddr, u_long ioaddr) +{ +/* + union { + u_short reg; + u_char breg[2]; + } a; + int i, r2, r3, ret=0;*/ + int r2, r3; + + /* Read r2 and r3 */ + r2 = mii_rd(MII_ID0, phyaddr, ioaddr); + r3 = mii_rd(MII_ID1, phyaddr, ioaddr); + /* SEEQ and Cypress way * / + / * Shuffle r2 and r3 * / + a.reg=0; + r3 = ((r3>>10)|(r2<<6))&0x0ff; + r2 = ((r2>>2)&0x3fff); + + / * Bit reverse r3 * / + for (i=0;i<8;i++) { + ret<<=1; + ret |= (r3&1); + r3>>=1; + } + + / * Bit reverse r2 * / + for (i=0;i<16;i++) { + a.reg<<=1; + a.reg |= (r2&1); + r2>>=1; + } + + / * Swap r2 bytes * / + i=a.breg[0]; + a.breg[0]=a.breg[1]; + a.breg[1]=i; + + return ((a.reg<<8)|ret); */ /* SEEQ and Cypress way */ +/* return ((r2<<6)|(u_int)(r3>>10)); */ /* NATIONAL and BROADCOM way */ + return r2; /* (I did it) My way */ +} + +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ +static int +mii_get_phy(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); + int id; + + lp->active = 0; + lp->useMII = TRUE; + + /* Search the MII address space for possible PHY devices */ + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { + lp->phy[lp->active].addr = i; + if (i==0) n++; /* Count cycles */ + while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ + id = mii_get_oui(i, DE4X5_MII); + if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ + for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); + if (k < DE4X5_MAX_PHY) { + memcpy((char *)&lp->phy[k], + (char *)&phy_info[j], sizeof(struct phy_table)); + lp->phy[k].addr = i; + lp->mii_cnt++; + lp->active++; + } else { + goto purgatory; /* Stop the search */ + } + break; + } + if ((j == limit) && (i < DE4X5_MAX_MII)) { + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); + lp->phy[k].addr = i; + lp->phy[k].id = id; + lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ + lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ + lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ + lp->mii_cnt++; + lp->active++; + printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); + j = de4x5_debug; + de4x5_debug |= DEBUG_MII; + de4x5_dbg_mii(dev, k); + de4x5_debug = j; + printk("\n"); + } + } + purgatory: + lp->active = 0; + if (lp->phy[0].id) { /* Reset the PHY devices */ + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ + mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); + while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); + + de4x5_dbg_mii(dev, k); + } + } + if (!lp->mii_cnt) lp->useMII = FALSE; + + return lp->mii_cnt; +} + +static char * +build_setup_frame(struct net_device *dev, int mode) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + char *pa = lp->setup_frame; + + /* Initialise the setup frame */ + if (mode == ALL) { + memset(lp->setup_frame, 0, SETUP_FRAME_LEN); + } + + if (lp->setup_f == HASH_PERF) { + for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; idev_addr[i]; /* Host address */ + if (i & 0x01) pa += 2; + } + *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; + } else { + for (i=0; idev_addr[i]; + if (i & 0x01) pa += 4; + } + for (i=0; ipriv; + + del_timer(&lp->timer); + + return; +} + +static long +de4x5_switch_mac_port(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 omr; + + STOP_DE4X5; + + /* Assert the OMR_PS bit in CSR6 */ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | + OMR_FDX)); + omr |= lp->infoblock_csr6; + if (omr & OMR_PS) omr |= OMR_HBD; + outl(omr, DE4X5_OMR); + + /* Soft Reset */ + RESET_DE4X5; + + /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ + if (lp->chipset == DC21140) { + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else if ((lp->chipset & ~0x0ff) == DC2114x) { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); + } + + /* Restore CSR6 */ + outl(omr, DE4X5_OMR); + + /* Reset CSR8 */ + inl(DE4X5_MFC); + + return omr; +} + +static void +gep_wr(s32 data, struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + outl(data, DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); + } + + return; +} + +static int +gep_rd(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + return inl(DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SIGR) & 0x000fffff); + } + + return 0; +} + +static void +timeout(struct net_device *dev, void (*fn)(u_long data), u_long data, u_long msec) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int dt; + + /* First, cancel any pending timer events */ + del_timer(&lp->timer); + + /* Convert msec to ticks */ + dt = (msec * HZ) / 1000; + if (dt==0) dt=1; + + /* Set up timer */ + lp->timer.expires = jiffies + dt; + lp->timer.function = fn; + lp->timer.data = data; + add_timer(&lp->timer); + + return; +} + +static void +yawn(struct net_device *dev, int state) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + + if(lp->bus == EISA) { + switch(state) { + case WAKEUP: + outb(WAKEUP, PCI_CFPM); + mdelay(10); + break; + + case SNOOZE: + outb(SNOOZE, PCI_CFPM); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + outb(SLEEP, PCI_CFPM); + break; + } + } else { + switch(state) { + case WAKEUP: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + mdelay(10); + break; + + case SNOOZE: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SNOOZE); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SLEEP); + break; + } + } + + return; +} + +static void +de4x5_parse_params(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + char *p, *q, t; + + lp->params.fdx = 0; + lp->params.autosense = AUTO; + + if (args == NULL) return; + + if ((p = strstr(args, dev->name))) { + if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); + t = *q; + *q = '\0'; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + if (strstr(p, "force_eisa") || strstr(p, "FORCE_EISA")) forceEISA = 1; +#endif + if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + + if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { + if (strstr(p, "TP")) { + lp->params.autosense = TP; + } else if (strstr(p, "TP_NW")) { + lp->params.autosense = TP_NW; + } else if (strstr(p, "BNC")) { + lp->params.autosense = BNC; + } else if (strstr(p, "AUI")) { + lp->params.autosense = AUI; + } else if (strstr(p, "BNC_AUI")) { + lp->params.autosense = BNC; + } else if (strstr(p, "10Mb")) { + lp->params.autosense = _10Mb; + } else if (strstr(p, "100Mb")) { + lp->params.autosense = _100Mb; + } else if (strstr(p, "AUTO")) { + lp->params.autosense = AUTO; + } + } + *q = t; + } + + return; +} + +static void +de4x5_dbg_open(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i; + + if (de4x5_debug & DEBUG_OPEN) { + printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); + printk("\tphysical address: "); + for (i=0;i<6;i++) { + printk("%2.2x:",(short)dev->dev_addr[i]); + } + printk("\n"); + printk("Descriptor head addresses:\n"); + printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); + printk("Descriptor addresses:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status); + } + } + printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status); + } + } + printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); + printk("Descriptor buffers:\nRX: "); + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); + } + } + printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); + printk("TX: "); + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); + } + } + printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); + printk("Ring size: \nRX: %d\nTX: %d\n", + (short)lp->rxRingSize, + (short)lp->txRingSize); + } + + return; +} + +static void +de4x5_dbg_mii(struct net_device *dev, int k) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (de4x5_debug & DEBUG_MII) { + printk("\nMII device address: %d\n", lp->phy[k].addr); + printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); + printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); + printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); + printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); + } + printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); + if (lp->phy[k].id != BROADCOM_T4) { + printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); + printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); + } else { + printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); + } + } + + return; +} + +static void +de4x5_dbg_media(struct net_device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->media != lp->c_media) { + if (de4x5_debug & DEBUG_MEDIA) { + printk("%s: media is %s%s\n", dev->name, + (lp->media == NC ? "unconnected, link down or incompatible connection" : + (lp->media == TP ? "TP" : + (lp->media == ANS ? "TP/Nway" : + (lp->media == BNC ? "BNC" : + (lp->media == AUI ? "AUI" : + (lp->media == BNC_AUI ? "BNC/AUI" : + (lp->media == EXT_SIA ? "EXT SIA" : + (lp->media == _100Mb ? "100Mb/s" : + (lp->media == _10Mb ? "10Mb/s" : + "???" + ))))))))), (lp->fdx?" full duplex.":".")); + } + lp->c_media = lp->media; + } + + return; +} + +static void +de4x5_dbg_srom(struct de4x5_srom *p) +{ + int i; + + if (de4x5_debug & DEBUG_SROM) { + printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); + printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); + printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); + printk("SROM version: %02x\n", (u_char)(p->version)); + printk("# controllers: %02x\n", (u_char)(p->num_controllers)); + + printk("Hardware Address: "); + for (i=0;iieee_addr+i)); + } + printk("%02x\n", (u_char)*(p->ieee_addr+i)); + printk("CRC checksum: %04x\n", (u_short)(p->chksum)); + for (i=0; i<64; i++) { + printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); + } + } + + return; +} + +static void +de4x5_dbg_rx(struct sk_buff *skb, int len) +{ + int i, j; + + if (de4x5_debug & DEBUG_RX) { + printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", + (u_char)skb->data[0], + (u_char)skb->data[1], + (u_char)skb->data[2], + (u_char)skb->data[3], + (u_char)skb->data[4], + (u_char)skb->data[5], + (u_char)skb->data[6], + (u_char)skb->data[7], + (u_char)skb->data[8], + (u_char)skb->data[9], + (u_char)skb->data[10], + (u_char)skb->data[11], + (u_char)skb->data[12], + (u_char)skb->data[13], + len); + for (j=0; len>0;j+=16, len-=16) { + printk(" %03x: ",j); + for (i=0; i<16 && idata[i+j]); + } + printk("\n"); + } + } + + return; +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. +*/ +static int +de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data; + u_long iobase = dev->base_addr; + int i, j, status = 0; + s32 omr; + union { + u8 addr[144]; + u16 sval[72]; + u32 lval[36]; + } tmp; + u_long flags = 0; + + switch(ioc->cmd) { + case DE4X5_GET_HWADDR: /* Get the hardware address */ + ioc->len = ETH_ALEN; + for (i=0; idev_addr[i]; + } + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + + case DE4X5_SET_HWADDR: /* Set the hardware address */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) return -EFAULT; + if (netif_queue_stopped(dev)) + return -EBUSY; + netif_stop_queue(dev); + for (i=0; idev_addr[i] = tmp.addr[i]; + } + build_setup_frame(dev, PHYS_ADDR_ONLY); + /* Set up the descriptor and give ownership to the card */ + load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | + SETUP_FRAME_LEN, (struct sk_buff *)1); + lp->tx_new = (++lp->tx_new) % lp->txRingSize; + outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ + netif_wake_queue(dev); /* Unlock the TX ring */ + break; + + case DE4X5_SET_PROM: /* Set Promiscuous Mode */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr |= OMR_PR; + outl(omr, DE4X5_OMR); + dev->flags |= IFF_PROMISC; + break; + + case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr &= ~OMR_PR; + outl(omr, DE4X5_OMR); + dev->flags &= ~IFF_PROMISC; + break; + + case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + printk("%s: Boo!\n", dev->name); + break; + + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + omr = inl(DE4X5_OMR); + omr |= OMR_PM; + outl(omr, DE4X5_OMR); + break; + + case DE4X5_GET_STATS: /* Get the driver statistics */ + { + struct pkt_stats statbuf; + ioc->len = sizeof(statbuf); + spin_lock_irqsave(&lp->lock, flags); + memcpy(&statbuf, &lp->pktStats, ioc->len); + spin_unlock_irqrestore(&lp->lock, flags); + if (copy_to_user(ioc->data, &statbuf, ioc->len)) + return -EFAULT; + break; + } + case DE4X5_CLR_STATS: /* Zero out the driver statistics */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + spin_lock_irqsave(&lp->lock, flags); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + spin_unlock_irqrestore(&lp->lock, flags); + break; + + case DE4X5_GET_OMR: /* Get the OMR Register contents */ + tmp.addr[0] = inl(DE4X5_OMR); + if (copy_to_user(ioc->data, tmp.addr, 1)) return -EFAULT; + break; + + case DE4X5_SET_OMR: /* Set the OMR Register contents */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (copy_from_user(tmp.addr, ioc->data, 1)) return -EFAULT; + outl(tmp.addr[0], DE4X5_OMR); + break; + + case DE4X5_GET_REG: /* Get the DE4X5 Registers */ + j = 0; + tmp.lval[0] = inl(DE4X5_STS); j+=4; + tmp.lval[1] = inl(DE4X5_BMR); j+=4; + tmp.lval[2] = inl(DE4X5_IMR); j+=4; + tmp.lval[3] = inl(DE4X5_OMR); j+=4; + tmp.lval[4] = inl(DE4X5_SISR); j+=4; + tmp.lval[5] = inl(DE4X5_SICR); j+=4; + tmp.lval[6] = inl(DE4X5_STRR); j+=4; + tmp.lval[7] = inl(DE4X5_SIGR); j+=4; + ioc->len = j; + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + +#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* + case DE4X5_DUMP: + j = 0; + tmp.addr[j++] = dev->irq; + for (i=0; idev_addr[i]; + } + tmp.addr[j++] = lp->rxRingSize; + tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; + tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + } + } + tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; + + for (i=0;irxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; + for (i=0;itxRingSize-1;i++){ + if (i < 3) { + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + } + } + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; + + for (i=0;irxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; + } + for (i=0;itxRingSize;i++){ + tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; + } + + tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4; + tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STS); j+=4; + tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; + tmp.lval[j>>2] = lp->chipset; j+=4; + if (lp->chipset == DC21140) { + tmp.lval[j>>2] = gep_rd(dev); j+=4; + } else { + tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; + tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; + } + tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + tmp.lval[j>>2] = lp->active; j+=4; + tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + if (lp->phy[lp->active].id != BROADCOM_T4) { + tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } else { + tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; + } + } + + tmp.addr[j++] = lp->txRingSize; + tmp.addr[j++] = netif_queue_stopped(dev); + + ioc->len = j; + if (copy_to_user(ioc->data, tmp.addr, ioc->len)) return -EFAULT; + break; + +*/ + default: + return -EOPNOTSUPP; + } + + return status; +} + +#ifdef MODULE +/* +** Note now that module autoprobing is allowed under EISA and PCI. The +** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes +** to "do the right thing". +*/ +#define LP(a) ((struct de4x5_private *)(a)) +static struct net_device *mdev = NULL; +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "de4x5 I/O base address"); + +int +init_module(void) +{ + int i, num, status = -EIO; + struct net_device *p; + + num = count_adapters(); + + for (i=0; ipriv; + if (lp) { + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); + if (lp->cache.priv) { /* Private area allocated? */ + kfree(lp->cache.priv); /* Free the private area */ + } + if (lp->rx_ring) { + pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, + lp->dma_rings); + } + } + kfree(p); + } else { + status = 0; /* At least one adapter will work */ + lastModule = p; + } + } + + return status; +} + +void +cleanup_module(void) +{ + while (mdev != NULL) { + mdev = unlink_modules(mdev); + } + + return; +} + +static struct net_device * +unlink_modules(struct net_device *p) +{ + struct net_device *next = NULL; + + if (p->priv) { /* Private areas allocated? */ + struct de4x5_private *lp = (struct de4x5_private *)p->priv; + + next = lp->next_module; + if (lp->rx_ring) { + pci_free_consistent(lp->pdev, lp->dma_size, lp->rx_ring, + lp->dma_rings); + } + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); + kfree(lp->cache.priv); /* Free the private area */ + } + unregister_netdev(p); + kfree(p); /* Free the device structure */ + + return next; +} + +static int +count_adapters(void) +{ + int i, j=0; + u_short vendor; + u_int class = DE4X5_CLASS_CODE; + u_int device; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + char name[DE4X5_STRLEN]; + u_long iobase = 0x1000; + + for (i=1; ivendor; + device = pdev->device << 8; + if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; + } + + return j; +} + +/* +** If at end of eth device list and can't use current entry, malloc +** one up. If memory could not be allocated, print an error message. +*/ +static struct net_device * __init +insert_device(struct net_device *dev, u_long iobase, int (*init)(struct net_device *)) +{ + struct net_device *new; + + new = (struct net_device *)kmalloc(sizeof(struct net_device), GFP_KERNEL); + if (new == NULL) { + printk("de4x5.c: Device not initialised, insufficient memory\n"); + return NULL; + } else { + memset((char *)new, 0, sizeof(struct net_device)); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + } + + return new; +} + +#endif /* MODULE */ + + +/* + * Local variables: + * + * Delete -DMODVERSIONS below if you didn't define this in your kernel + * + * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -DMODVERSIONS -include /linux/include/linux/modversions.h -c de4x5.c" + * End: + */ diff -urN linux-2.5.6-pre3/drivers/net/tulip/de4x5.h linux-2.5.6/drivers/net/tulip/de4x5.h --- linux-2.5.6-pre3/drivers/net/tulip/de4x5.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/de4x5.h Thu Mar 7 18:24:48 2002 @@ -0,0 +1,1031 @@ +/* + Copyright 1994 Digital Equipment Corporation. + + This software may be used and distributed according to the terms of the + GNU General Public License, incorporated herein by reference. + + The author may be reached as davies@wanton.lkg.dec.com or Digital + Equipment Corporation, 550 King Street, Littleton MA 01460. + + ========================================================================= +*/ + +/* +** DC21040 CSR<1..15> Register Address Map +*/ +#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */ +#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */ +#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */ +#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */ +#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */ +#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */ +#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */ +#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */ +#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */ +#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */ +#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */ +#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */ +#define DE4X5_MII iobase+(0x048 << lp->bus) /* MII Interface Register */ +#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */ +#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */ +#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/ +#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */ +#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */ +#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */ +#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */ +#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */ + +/* +** EISA Register Address Map +*/ +#define EISA_ID iobase+0x0c80 /* EISA ID Registers */ +#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */ +#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */ +#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */ +#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */ +#define EISA_CR iobase+0x0c84 /* EISA Control Register */ +#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */ +#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */ +#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */ +#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */ +#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */ + +/* +** PCI/EISA Configuration Registers Address Map +*/ +#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */ +#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */ +#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */ +#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */ +#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */ +#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */ +#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ +#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ +#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ +#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ +#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ + +/* +** EISA Configuration Register 0 bit definitions +*/ +#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */ +#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */ +#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */ +#define ER0_ISTS 0x10 /* Interrupt Status (X) */ +#define ER0_LI 0x08 /* Latch Interrupts */ +#define ER0_INTL 0x06 /* INTerrupt Level */ +#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */ + +/* +** EISA Configuration Register 1 bit definitions +*/ +#define ER1_IAM 0xe0 /* ISA Address Mode */ +#define ER1_IAE 0x10 /* ISA Addressing Enable */ +#define ER1_UPIN 0x0f /* User Pins */ + +/* +** EISA Configuration Register 2 bit definitions +*/ +#define ER2_BRS 0xc0 /* Boot ROM Size */ +#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */ + +/* +** EISA Configuration Register 3 bit definitions +*/ +#define ER3_BWE 0x40 /* Burst Write Enable */ +#define ER3_BRE 0x04 /* Burst Read Enable */ +#define ER3_LSR 0x02 /* Local Software Reset */ + +/* +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. +*/ +#define CFID_DID 0xff00 /* Device ID */ +#define CFID_VID 0x00ff /* Vendor ID */ +#define DC21040_DID 0x0200 /* Unique Device ID # */ +#define DC21040_VID 0x1011 /* DC21040 Manufacturer */ +#define DC21041_DID 0x1400 /* Unique Device ID # */ +#define DC21041_VID 0x1011 /* DC21041 Manufacturer */ +#define DC21140_DID 0x0900 /* Unique Device ID # */ +#define DC21140_VID 0x1011 /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900 /* Unique Device ID # */ +#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ + +/* +** Chipset defines +*/ +#define DC21040 DC21040_DID +#define DC21041 DC21041_DID +#define DC21140 DC21140_DID +#define DC2114x DC2114x_DID +#define DC21142 (DC2114x_DID | 0x0010) +#define DC21143 (DC2114x_DID | 0x0030) +#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ + +#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) +#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) +#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) + +/* +** PCI Configuration Command/Status Register (PCI_CFCS) +*/ +#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */ +#define CFCS_SSE 0x40000000 /* Signal System Error (S) */ +#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */ +#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */ +#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ +#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ +#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ +#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ +#define CFCS_PER 0x00000040 /* Parity Error Response (C) */ +#define CFCS_MO 0x00000004 /* Master Operation (C) */ +#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ +#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */ + +/* +** PCI Configuration Revision Register (PCI_CFRV) +*/ +#define CFRV_BC 0xff000000 /* Base Class */ +#define CFRV_SC 0x00ff0000 /* Subclass */ +#define CFRV_RN 0x000000f0 /* Revision Number */ +#define CFRV_SN 0x0000000f /* Step Number */ +#define BASE_CLASS 0x02000000 /* Indicates Network Controller */ +#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ +#define STEP_NUMBER 0x00000020 /* Increments for future chips */ +#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ +#define CFRV_MASK 0xffff0000 /* Register mask */ + +/* +** PCI Configuration Latency Timer Register (PCI_CFLT) +*/ +#define CFLT_BC 0x0000ff00 /* Latency Timer bits */ + +/* +** PCI Configuration Base I/O Address Register (PCI_CBIO) +*/ +#define CBIO_MASK -128 /* Base I/O Address Mask */ +#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ + +/* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI 0xf0000000 /* ROM Image */ +#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ +#define CCIS_ASI 0x00000007 /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID 0xffff0000 /* Subsystem ID */ +#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ + +/* +** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) +*/ +#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ +#define CBER_ROME 0x00000001 /* ROM Enable */ + +/* +** PCI Configuration Interrupt Register (PCI_CFIT) +*/ +#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ +#define CFIT_IRQL 0x000000ff /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ +#define SNOOZE 0x40 /* Power Saving Snooze Mode */ +#define WAKEUP 0x00 /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ + +/* +** DC21040 Bus Mode Register (DE4X5_BMR) +*/ +#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ +#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ +#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ +#define BMR_DAS 0x00010000 /* Diagnostic Address Space */ +#define BMR_CAL 0x0000c000 /* Cache Alignment */ +#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ +#define BMR_BLE 0x00000080 /* Big/Little Endian */ +#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ +#define BMR_BAR 0x00000002 /* Bus ARbitration */ +#define BMR_SWR 0x00000001 /* Software Reset */ + + /* Timings here are for 10BASE-T/AUI only*/ +#define TAP_NOPOLL 0x00000000 /* No automatic polling */ +#define TAP_200US 0x00020000 /* TX automatic polling every 200us */ +#define TAP_800US 0x00040000 /* TX automatic polling every 800us */ +#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */ +#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */ +#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */ +#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */ +#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */ + +#define CAL_NOUSE 0x00000000 /* Not used */ +#define CAL_8LONG 0x00004000 /* 8-longword alignment */ +#define CAL_16LONG 0x00008000 /* 16-longword alignment */ +#define CAL_32LONG 0x0000c000 /* 32-longword alignment */ + +#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ +#define PBL_1 0x00000100 /* 1 longword DMA burst length */ +#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ +#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ +#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ +#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ +#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ + +#define DSL_0 0x00000000 /* 0 longword / descriptor */ +#define DSL_1 0x00000004 /* 1 longword / descriptor */ +#define DSL_2 0x00000008 /* 2 longwords / descriptor */ +#define DSL_4 0x00000010 /* 4 longwords / descriptor */ +#define DSL_8 0x00000020 /* 8 longwords / descriptor */ +#define DSL_16 0x00000040 /* 16 longwords / descriptor */ +#define DSL_32 0x00000080 /* 32 longwords / descriptor */ + +/* +** DC21040 Transmit Poll Demand Register (DE4X5_TPD) +*/ +#define TPD 0x00000001 /* Transmit Poll Demand */ + +/* +** DC21040 Receive Poll Demand Register (DE4X5_RPD) +*/ +#define RPD 0x00000001 /* Receive Poll Demand */ + +/* +** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) +*/ +#define RRBA 0xfffffffc /* RX Descriptor List Start Address */ + +/* +** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) +*/ +#define TRBA 0xfffffffc /* TX Descriptor List Start Address */ + +/* +** Status Register (DE4X5_STS) +*/ +#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ +#define STS_BE 0x03800000 /* Bus Error Bits */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ +#define STS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define STS_ER 0x00004000 /* Early Receive */ +#define STS_FBE 0x00002000 /* Fatal Bus Error */ +#define STS_SE 0x00002000 /* System Error */ +#define STS_LNF 0x00001000 /* Link Fail */ +#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ +#define STS_TM 0x00000800 /* Timer Expired (DC21041) */ +#define STS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define STS_AT 0x00000400 /* AUI/TP Pin */ +#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ +#define STS_RPS 0x00000100 /* Receive Process Stopped */ +#define STS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define STS_RI 0x00000040 /* Receive Interrupt */ +#define STS_UNF 0x00000020 /* Transmit Underflow */ +#define STS_LNP 0x00000010 /* Link Pass */ +#define STS_ANC 0x00000010 /* Autonegotiation Complete */ +#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ +#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define STS_TPS 0x00000002 /* Transmit Process Stopped */ +#define STS_TI 0x00000001 /* Transmit Interrupt */ + +#define EB_PAR 0x00000000 /* Parity Error */ +#define EB_MA 0x00800000 /* Master Abort */ +#define EB_TA 0x01000000 /* Target Abort */ +#define EB_RES0 0x01800000 /* Reserved */ +#define EB_RES1 0x02000000 /* Reserved */ + +#define TS_STOP 0x00000000 /* Stopped */ +#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */ +#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */ +#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */ +#define TS_RES 0x00400000 /* Reserved */ +#define TS_SPKT 0x00500000 /* Setup Packet */ +#define TS_SUSP 0x00600000 /* Suspended */ +#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */ + +#define RS_STOP 0x00000000 /* Stopped */ +#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */ +#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */ +#define RS_WFRP 0x00060000 /* Wait for Receive Packet */ +#define RS_SUSP 0x00080000 /* Suspended */ +#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */ +#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */ +#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */ + +#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ + +/* +** Operation Mode Register (DE4X5_OMR) +*/ +#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ +#define OMR_RA 0x40000000 /* Receive All */ +#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ +#define OMR_SCR 0x01000000 /* Scrambler Mode */ +#define OMR_PCS 0x00800000 /* PCS Function */ +#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ +#define OMR_SF 0x00200000 /* Store and Forward */ +#define OMR_HBD 0x00080000 /* HeartBeat Disable */ +#define OMR_PS 0x00040000 /* Port Select */ +#define OMR_CA 0x00020000 /* Capture Effect Enable */ +#define OMR_BP 0x00010000 /* Back Pressure */ +#define OMR_TR 0x0000c000 /* Threshold Control Bits */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_FC 0x00001000 /* Force Collision Mode */ +#define OMR_OM 0x00000c00 /* Operating Mode */ +#define OMR_FDX 0x00000200 /* Full Duplex Mode */ +#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ +#define OMR_PM 0x00000080 /* Pass All Multicast */ +#define OMR_PR 0x00000040 /* Promiscuous Mode */ +#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */ +#define OMR_IF 0x00000010 /* Inverse Filtering */ +#define OMR_PB 0x00000008 /* Pass Bad Frames */ +#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ +#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ + +#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ +#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ +#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ +#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ + +#define OMR_DEF (OMR_SDP) +#define OMR_SIA (OMR_SDP | OMR_TTM) +#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) + +/* +** DC21040 Interrupt Mask Register (DE4X5_IMR) +*/ +#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ +#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ +#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ +#define IMR_ERM 0x00004000 /* Early Receive Mask */ +#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ +#define IMR_SEM 0x00002000 /* System Error Mask */ +#define IMR_LFM 0x00001000 /* Link Fail Mask */ +#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ +#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ +#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ +#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ +#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ +#define IMR_RSM 0x00000100 /* Receive Stopped Mask */ +#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ +#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ +#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ +#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ +#define IMR_LPM 0x00000010 /* Link Pass */ +#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ +#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ +#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */ +#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ + +/* +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +*/ +#define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ +#define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ +#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ +#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ +#define MFC_FOCM 0x1ffe0000 /* FIFO Overflow Counter Mask */ + +/* +** DC21040 Ethernet Address PROM (DE4X5_APROM) +*/ +#define APROM_DN 0x80000000 /* Data Not Valid */ +#define APROM_DT 0x000000ff /* Address Byte */ + +/* +** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) +*/ +#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define BROM_RD 0x00004000 /* Read from Boot ROM */ +#define BROM_WR 0x00002000 /* Write to Boot ROM */ +#define BROM_BR 0x00001000 /* Select Boot ROM when set */ +#define BROM_SR 0x00000800 /* Select Serial ROM when set */ +#define BROM_REG 0x00000400 /* External Register Select */ +#define BROM_DT 0x000000ff /* Data Byte */ + +/* +** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) +*/ +#define MII_MDI 0x00080000 /* MII Management Data In */ +#define MII_MDO 0x00060000 /* MII Management Mode/Data Out */ +#define MII_MRD 0x00040000 /* MII Management Define Read Mode */ +#define MII_MWR 0x00000000 /* MII Management Define Write Mode */ +#define MII_MDT 0x00020000 /* MII Management Data Out */ +#define MII_MDC 0x00010000 /* MII Management Clock */ +#define MII_RD 0x00004000 /* Read from MII */ +#define MII_WR 0x00002000 /* Write to MII */ +#define MII_SEL 0x00000800 /* Select MII when RESET */ + +#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */ +#define SROM_RD 0x00004000 /* Read from Boot ROM */ +#define SROM_WR 0x00002000 /* Write to Boot ROM */ +#define SROM_BR 0x00001000 /* Select Boot ROM when set */ +#define SROM_SR 0x00000800 /* Select Serial ROM when set */ +#define SROM_REG 0x00000400 /* External Register Select */ +#define SROM_DT 0x000000ff /* Data Byte */ + +#define DT_OUT 0x00000008 /* Serial Data Out */ +#define DT_IN 0x00000004 /* Serial Data In */ +#define DT_CLK 0x00000002 /* Serial ROM Clock */ +#define DT_CS 0x00000001 /* Serial ROM Chip Select */ + +#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ +#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ +#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ + +#define MII_CR 0x00 /* MII Management Control Register */ +#define MII_SR 0x01 /* MII Management Status Register */ +#define MII_ID0 0x02 /* PHY Identifier Register 0 */ +#define MII_ID1 0x03 /* PHY Identifier Register 1 */ +#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ +#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ +#define MII_ANE 0x06 /* Auto Negotiation Expansion */ +#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ + +#define DE4X5_MAX_MII 32 /* Maximum address of MII PHY devices */ + +/* +** MII Management Control Register +*/ +#define MII_CR_RST 0x8000 /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000 /* Loopback enable */ +#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_10 0x0000 /* Set 10Mb/s */ +#define MII_CR_100 0x2000 /* Set 100Mb/s */ +#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ +#define MII_CR_PD 0x0800 /* Power Down */ +#define MII_CR_ISOL 0x0400 /* Isolate Mode */ +#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ +#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ +#define MII_CR_CTE 0x0080 /* Collision Test Enable */ + +/* +** MII Management Status Register +*/ +#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ +#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ +#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ +#define MII_SR_LKS 0x0004 /* Link Status */ +#define MII_SR_JABD 0x0002 /* Jabber Detect */ +#define MII_SR_XC 0x0001 /* Extended Capabilities */ + +/* +** MII Management Auto Negotiation Advertisement Register +*/ +#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** MII Management Auto Negotiation Remote End Register +*/ +#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ +#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* +** SROM Media Definitions (ABG SROM Section) +*/ +#define MEDIA_NWAY 0x0080 /* Nway (Auto Negotiation) on PHY */ +#define MEDIA_MII 0x0040 /* MII Present on the adapter */ +#define MEDIA_FIBRE 0x0008 /* Fibre Media present */ +#define MEDIA_AUI 0x0004 /* AUI Media present */ +#define MEDIA_TP 0x0002 /* TP Media present */ +#define MEDIA_BNC 0x0001 /* BNC Media present */ + +/* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ +#define SROM_SSID 0x0002 /* Sub-system ID offset */ +#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ +#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ +#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ +#define SROM_SFV 0x0012 /* SROM Format Version offset */ +#define SROM_CCNT 0x0013 /* Controller Count offset */ +#define SROM_HWADD 0x0014 /* Hardware Address offset */ +#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ +#define SROM_CRC 0x007e /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ +#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ +#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ +#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ +#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ +#define SROM_100BT4 0x0006 /* 100BASE-T4 */ +#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ +#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ +#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ +#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ +#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ +#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ +#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ +#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ +#define SROM_PAO 0x8800 /* Powerup Autosense Only */ +#define SROM_NSMI 0xffff /* No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ +#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ +#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ +#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ +#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ + +#define BLOCK_LEN 0x7f /* Extended blocks length mask */ +#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ +#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI 0x80 /* Format Indicator */ +#define COMPACT_LEN 0x04 /* Length */ +#define COMPACT_MC 0x3f /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI 0x80 /* Format Indicator */ +#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ +#define BLOCK0_MC 0x3f /* Media Code */ + +/* +** DC21040 Full Duplex Register (DE4X5_FDR) +*/ +#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ + +/* +** DC21041 General Purpose Timer Register (DE4X5_GPT) +*/ +#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */ +#define GPT_VAL 0x0000ffff /* Timer Value */ + +/* +** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) +*/ +/* Valid ONLY for DE500 hardware */ +#define GEP_LNP 0x00000080 /* Link Pass (input) */ +#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ +#define GEP_SDET 0x00000020 /* Signal Detect (input) */ +#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ +#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ +#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ +#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ +#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ +#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ +#define GEP_CTRL 0x00000100 /* GEP control bit */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f /* Autonegotiation disabled */ +#define CSR15 0x00000008 + +/* +** SIA Status Register (DE4X5_SISR) +*/ +#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ +#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ +#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ +#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ +#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ +#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ +#define SISR_DAO 0x00000080 /* PLL All One */ +#define SISR_DAZ 0x00000040 /* PLL All Zero */ +#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ +#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ +#define SISR_APS 0x00000008 /* Auto Polarity State */ +#define SISR_LKF 0x00000004 /* Link Fail Status */ +#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ +#define SISR_NCR 0x00000002 /* Network Connection Error */ +#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ +#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ + +#define ANS_NDIS 0x00000000 /* Nway disable */ +#define ANS_TDIS 0x00001000 /* Transmit Disable */ +#define ANS_ADET 0x00002000 /* Ability Detect */ +#define ANS_ACK 0x00003000 /* Acknowledge */ +#define ANS_CACK 0x00004000 /* Complete Acknowledge */ +#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ +#define ANS_LCHK 0x00006000 /* Link Check */ + +#define SISR_RST 0x00000301 /* CSR12 reset */ +#define SISR_ANR 0x00001301 /* Autonegotiation restart */ + +/* +** SIA Connectivity Register (DE4X5_SICR) +*/ +#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ +#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ +#define SICR_OE24 0x00004000 /* Output Enable 2 4 */ +#define SICR_OE13 0x00002000 /* Output Enable 1 3 */ +#define SICR_IE 0x00001000 /* Input Enable */ +#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */ +#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */ +#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/ +#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */ +#define SICR_ASE 0x00000080 /* APLL Start Enable*/ +#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ +#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ +#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ +#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ +#define SICR_CAC 0x00000004 /* CSR Auto Configuration */ +#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ +#define SICR_SRL 0x00000001 /* SIA Reset */ +#define SIA_RESET 0x00000000 /* SIA Reset Value */ + +/* +** SIA Transmit and Receive Register (DE4X5_STRR) +*/ +#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ +#define STRR_SPP 0x00004000 /* Set Polarity Plus */ +#define STRR_APE 0x00002000 /* Auto Polarity Enable */ +#define STRR_LTE 0x00001000 /* Link Test Enable */ +#define STRR_SQE 0x00000800 /* Signal Quality Enable */ +#define STRR_CLD 0x00000400 /* Collision Detect Enable */ +#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */ +#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */ +#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */ +#define STRR_HDE 0x00000040 /* Half Duplex Enable */ +#define STRR_CPEN 0x00000030 /* Compensation Enable */ +#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */ +#define STRR_DREN 0x00000004 /* Driver Enable */ +#define STRR_LBK 0x00000002 /* Loopback Enable */ +#define STRR_ECEN 0x00000001 /* Encoder Enable */ +#define STRR_RESET 0xffffffff /* Reset value for STRR */ + +/* +** SIA General Register (DE4X5_SIGR) +*/ +#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ +#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ +#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ +#define SIGR_CWE 0x08000000 /* Control Write Enable */ +#define SIGR_RME 0x04000000 /* Receive Match Enable */ +#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ +#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ +#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ +#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ +#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ +#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ +#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ +#define SIGR_FRL 0x00002000 /* Force Receiver Low */ +#define SIGR_DPST 0x00001000 /* PLL Self Test Start */ +#define SIGR_LSD 0x00000800 /* LED Stretch Disable */ +#define SIGR_FLF 0x00000400 /* Force Link Fail */ +#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */ +#define SIGR_TSCK 0x00000100 /* Test Clock */ +#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */ +#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */ +#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */ +#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */ +#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */ +#define SIGR_JCK 0x00000004 /* Jabber Clock */ +#define SIGR_HUJ 0x00000002 /* Host Unjab */ +#define SIGR_JBD 0x00000001 /* Jabber Disable */ +#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */ + +/* +** Receive Descriptor Bit Summary +*/ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ +#define RD_ES 0x00008000 /* Error Summary */ +#define RD_LE 0x00004000 /* Length Error */ +#define RD_DT 0x00003000 /* Data Type */ +#define RD_RF 0x00000800 /* Runt Frame */ +#define RD_MF 0x00000400 /* Multicast Frame */ +#define RD_FS 0x00000200 /* First Descriptor */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_TL 0x00000080 /* Frame Too Long */ +#define RD_CS 0x00000040 /* Collision Seen */ +#define RD_FT 0x00000020 /* Frame Type */ +#define RD_RJ 0x00000010 /* Receive Watchdog */ +#define RD_RE 0x00000008 /* Report on MII Error */ +#define RD_DB 0x00000004 /* Dribbling Bit */ +#define RD_CE 0x00000002 /* CRC Error */ +#define RD_OF 0x00000001 /* Overflow */ + +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_RCH 0x01000000 /* Second Address Chained */ +#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ +#define RD_RBS1 0x000007ff /* Buffer 1 Size */ + +/* +** Transmit Descriptor Bit Summary +*/ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_ES 0x00008000 /* Error Summary */ +#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */ +#define TD_LO 0x00000800 /* Loss Of Carrier */ +#define TD_NC 0x00000400 /* No Carrier */ +#define TD_LC 0x00000200 /* Late Collision */ +#define TD_EC 0x00000100 /* Excessive Collisions */ +#define TD_HF 0x00000080 /* Heartbeat Fail */ +#define TD_CC 0x00000078 /* Collision Counter */ +#define TD_LF 0x00000004 /* Link Fail */ +#define TD_UF 0x00000002 /* Underflow Error */ +#define TD_DE 0x00000001 /* Deferred */ + +#define TD_IC 0x80000000 /* Interrupt On Completion */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_FT1 0x10000000 /* Filtering Type */ +#define TD_SET 0x08000000 /* Setup Packet */ +#define TD_AC 0x04000000 /* Add CRC Disable */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define TD_TCH 0x01000000 /* Second Address Chained */ +#define TD_DPD 0x00800000 /* Disabled Padding */ +#define TD_FT0 0x00400000 /* Filtering Type */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ + +#define PERFECT_F 0x00000000 +#define HASH_F TD_FT0 +#define INVERSE_F TD_FT1 +#define HASH_O_F (TD_FT1 | TD_F0) + +/* +** Media / mode state machine definitions +** User selectable: +*/ +#define TP 0x0040 /* 10Base-T (now equiv to _10Mb) */ +#define TP_NW 0x0002 /* 10Base-T with Nway */ +#define BNC 0x0004 /* Thinwire */ +#define AUI 0x0008 /* Thickwire */ +#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ +#define _10Mb 0x0040 /* 10Mb/s Ethernet */ +#define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define AUTO 0x4000 /* Auto sense the media or speed */ + +/* +** Internal states +*/ +#define NC 0x0000 /* No Connection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ +#define SPD_DET 0x0100 /* Parallel speed detection */ +#define INIT 0x0200 /* Initial state */ +#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ +#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ +#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ +#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ +#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ +#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ +#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ +#define MII 0x1000 /* MII on the 21143 */ + +#define TIMER_CB 0x80000000 /* Timer callback detection */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE 0x0000 /* No DEBUG messages */ +#define DEBUG_VERSION 0x0001 /* Print version message */ +#define DEBUG_MEDIA 0x0002 /* Print media messages */ +#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ +#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM 0x0010 /* Print SROM messages */ +#define DEBUG_MII 0x0020 /* Print MII messages */ +#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ +#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ +#define DEBUG_PCICFG 0x0100 +#define DEBUG_ALL 0x01ff + +/* +** Miscellaneous +*/ +#define PCI 0 +#define EISA 1 + +#define HASH_TABLE_LEN 512 /* Bits */ +#define HASH_BITS 0x01ff /* 9 LS bits */ + +#define SETUP_FRAME_LEN 192 /* Bytes */ +#define IMPERF_PA_OFFSET 156 /* Bytes */ + +#define POLL_DEMAND 1 + +#define LOST_MEDIA_THRESHOLD 3 + +#define MASK_INTERRUPTS 1 +#define UNMASK_INTERRUPTS 0 + +#define DE4X5_STRLEN 8 + +#define DE4X5_INIT 0 /* Initialisation time */ +#define DE4X5_RUN 1 /* Run time */ + +#define DE4X5_SAVE_STATE 0 +#define DE4X5_RESTORE_STATE 1 + +/* +** Address Filtering Modes +*/ +#define PERFECT 0 /* 16 perfect physical addresses */ +#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ +#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ + +#define ALL 0 /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ + +/* +** Booleans +*/ +#define NO 0 +#define FALSE 0 + +#define YES ~0 +#define TRUE ~0 + +/* +** Adapter state +*/ +#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ +#define CLOSED 1 /* Ready for opening */ +#define OPEN 2 /* Running */ + +/* +** Various wait times +*/ +#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ +#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ + +/* +** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since +** the vendors seem split 50-50 on how to calculate the OUI register values +** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. +*/ +#define NATIONAL_TX 0x2000 +#define BROADCOM_T4 0x03e0 +#define SEEQ_T4 0x0016 +#define CYPRESS_T4 0x0014 + +/* +** Speed Selection stuff +*/ +#define SET_10Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +#define SET_100Mb {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + int fdx=0;\ + if (lp->phy[lp->active].id == NATIONAL_TX) {\ + mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ + 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ + sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ + if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ + if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ + mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + }\ + if (fdx) omr |= OMR_FDX;\ + outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* FIX ME so I don't jam 10Mb networks */ +#define SET_100Mb_PDET {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ + lp->cache.gep = (GEP_FDXD | GEP_MODE);\ + gep_wr(lp->cache.gep, dev);\ + }\ +} + +/* +** Include the IOCTL stuff +*/ +#include + +#define DE4X5IOCTL SIOCDEVPRIVATE + +struct de4x5_ioctl { + unsigned short cmd; /* Command to run */ + unsigned short len; /* Length of the data buffer */ + unsigned char *data; /* Pointer to the data buffer */ +}; + +/* +** Recognised commands for the driver +*/ +#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ +#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ +#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ +#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ +#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ +#define DE4X5_GET_MCA 0x06 /* Get a multicast address */ +#define DE4X5_SET_MCA 0x07 /* Set a multicast address */ +#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */ +#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */ +#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */ +#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */ +#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ +#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ +#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#define MOTO_SROM_BUG ((lp->active == 8) && (((le32_to_cpu(get_unaligned(((s32 *)dev->dev_addr))))&0x00ffffff)==0x3e0008)) diff -urN linux-2.5.6-pre3/drivers/net/tulip/dmfe.c linux-2.5.6/drivers/net/tulip/dmfe.c --- linux-2.5.6-pre3/drivers/net/tulip/dmfe.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/dmfe.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,2070 @@ +/* + A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast + ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + DAVICOM Web-Site: www.davicom.com.tw + + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw + Maintainer: Tobias Ringstrom + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + + Marcelo Tosatti : + Made it compile in 2.3 (device to net_device) + + Alan Cox : + Cleaned up for kernel merge. + Removed the back compatibility support + Reformatted, fixing spelling etc as I went + Removed IRQ 0-15 assumption + + Jeff Garzik : + Updated to use new PCI driver API. + Resource usage cleanups. + Report driver version to user. + + Tobias Ringstrom : + Cleaned up and added SMP safety. Thanks go to Jeff Garzik, + Andrew Morton and Frank Davis for the SMP safety fixes. + + Vojtech Pavlik : + Cleaned up pointer arithmetics. + Fixed a lot of 64bit issues. + Cleaned up printk()s a bit. + Fixed some obvious big endian problems. + + Tobias Ringstrom : + Use time_after for jiffies calculation. Added ethtool + support. Updated PCI resource allocation. Do not + forget to unmap PCI mapped skbs. + + TODO + + Implement pci_driver::suspend() and pci_driver::resume() + power management methods. + + Check on 64 bit boxes. + Check and fix on big endian boxes. + + Test and make sure PCI latency is now correct for all cases. +*/ + +#define DRV_NAME "dmfe" +#define DRV_VERSION "1.36.4" +#define DRV_RELDATE "2002-01-17" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ +#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ +#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ +#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ + +#define DM9102_IO_SIZE 0x80 +#define DM9102A_IO_SIZE 0x100 +#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define DM910X_RESET 1 +#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ +#define CR6_DEFAULT 0x00080000 /* HD */ +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define DMFE_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 +#define DM9801_NOISE_FLOOR 8 +#define DM9802_NOISE_FLOOR 5 + +#define DMFE_10MHF 0 +#define DMFE_100MHF 1 +#define DMFE_10MFD 4 +#define DMFE_100MFD 5 +#define DMFE_AUTO 8 +#define DMFE_1M_HPNA 0x10 + +#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ +#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ +#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ +#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ +#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ +#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ + +#define DMFE_TIMER_WUT (jiffies + HZ * 1)/* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ +#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ + +#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) + +#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); + +#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) + +/* Sten Check */ +#define DEVICE net_device + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +} __attribute__(( aligned(32) )); + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + struct sk_buff *rx_skb_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +} __attribute__(( aligned(32) )); + +struct dmfe_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + u32 chip_revision; /* Chip revision */ + struct DEVICE *next_dev; /* next device */ + struct pci_dev *pdev; /* PCI device */ + spinlock_t lock; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_insert_ptr; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + unsigned long tx_queue_cnt; /* wait to send packet count */ + unsigned long rx_avail_cnt; /* available rx descriptor count */ + unsigned long interval_rx_cnt; /* rx packet count a callback time */ + + u16 HPNA_command; /* For HPNA register 16 */ + u16 HPNA_timer; /* For HPNA remote device check */ + u16 dbug_cnt; + u16 NIC_capability; /* NIC media capability */ + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ + u8 chip_type; /* Keep DM9102A chip type */ + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 link_failed; /* Ever link failed */ + u8 wait_reset; /* Hardware failed, need to reset */ + u8 dm910x_chk_mode; /* Operating mode check */ + u8 first_in_callback; /* Flag to record state */ + struct timer_list timer; + + /* System defined statistic counter */ + struct net_device_stats stats; + + /* Driver defined statistic counter */ + unsigned long tx_fifo_underrun; + unsigned long tx_loss_carrier; + unsigned long tx_no_carrier; + unsigned long tx_late_collision; + unsigned long tx_excessive_collision; + unsigned long tx_jabber_timeout; + unsigned long reset_count; + unsigned long reset_cr8; + unsigned long reset_fatal; + unsigned long reset_TXtimeout; + + /* NIC SROM data */ + unsigned char srom[128]; +}; + +enum dmfe_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum dmfe_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static int __devinitdata printed_version; +static char version[] __devinitdata = + KERN_INFO DRV_NAME ": Davicom DM9xxx net driver, version " + DRV_VERSION " (" DRV_RELDATE ")\n"; + +static int dmfe_debug; +static unsigned char dmfe_media_mode = DMFE_AUTO; +static u32 dmfe_cr6_user_set; + +/* For module input parameter */ +static int debug; +static u32 cr6set; +static unsigned char mode = 8; +static u8 chkmode = 1; +static u8 HPNA_mode; /* Default: Low Power/High Speed */ +static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ +static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ +static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ +static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control + 4: TX pause packet */ + + +/* function declaration ------------------------------------- */ +static int dmfe_open(struct DEVICE *); +static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *); +static int dmfe_stop(struct DEVICE *); +static struct net_device_stats * dmfe_get_stats(struct DEVICE *); +static void dmfe_set_filter_mode(struct DEVICE *); +static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int); +static u16 read_srom_word(long ,int); +static void dmfe_interrupt(int , void *, struct pt_regs *); +static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); +static void allocate_rx_buffer(struct dmfe_board_info *); +static void update_cr6(u32, unsigned long); +static void send_filter_frame(struct DEVICE * ,int); +static void dm9132_id_table(struct DEVICE * ,int); +static u16 phy_read(unsigned long, u8, u8, u32); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_write_1bit(unsigned long, u32); +static u16 phy_read_1bit(unsigned long); +static u8 dmfe_sense_speed(struct dmfe_board_info *); +static void dmfe_process_mode(struct dmfe_board_info *); +static void dmfe_timer(unsigned long); +static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); +static void dmfe_dynamic_reset(struct DEVICE *); +static void dmfe_free_rxbuffer(struct dmfe_board_info *); +static void dmfe_init_dm910x(struct DEVICE *); +static inline u32 cal_CRC(unsigned char *, unsigned int, u8); +static void dmfe_parse_srom(struct dmfe_board_info *); +static void dmfe_program_DM9801(struct dmfe_board_info *, int); +static void dmfe_program_DM9802(struct dmfe_board_info *); +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); +static void dmfe_set_phyxcer(struct dmfe_board_info *); + +/* DM910X network baord routine ---------------------------- */ + +/* + * Search DM910X board ,allocate space and register it + */ + +static int __devinit dmfe_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct dmfe_board_info *db; /* board information structure */ + struct net_device *dev; + u32 dev_rev, pci_pmr; + int i, err; + + DMFE_DBUG(0, "dmfe_init_one()", 0); + + if (!printed_version++) + printk(version); + + /* Init network device */ + dev = alloc_etherdev(sizeof(*db)); + if (dev == NULL) + return -ENOMEM; + SET_MODULE_OWNER(dev); + + if (pci_set_dma_mask(pdev, 0xffffffff)) { + printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n"); + err = -ENODEV; + goto err_out_free; + } + + /* Enable Master/IO access, Disable memory access */ + err = pci_enable_device(pdev); + if (err) + goto err_out_free; + + if (!pci_resource_start(pdev, 0)) { + printk(KERN_ERR DRV_NAME ": I/O base is zero\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Read Chip revision */ + pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); + + if (pci_resource_len(pdev, 0) < (CHK_IO_SIZE(pdev, dev_rev)) ) { + printk(KERN_ERR DRV_NAME ": Allocated I/O size too small\n"); + err = -ENODEV; + goto err_out_disable; + } + +#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ + + /* Set Latency Timer 80h */ + /* FIXME: setting values > 32 breaks some SiS 559x stuff. + Need a PCI quirk.. */ + + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); +#endif + + if (pci_request_regions(pdev, DRV_NAME)) { + printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n"); + err = -ENODEV; + goto err_out_disable; + } + + /* Init system & device */ + db = dev->priv; + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + + db->chip_id = ent->driver_data; + db->ioaddr = pci_resource_start(pdev, 0); + db->chip_revision = dev_rev; + + db->pdev = pdev; + + dev->base_addr = db->ioaddr; + dev->irq = pdev->irq; + pci_set_drvdata(pdev, dev); + dev->open = &dmfe_open; + dev->hard_start_xmit = &dmfe_start_xmit; + dev->stop = &dmfe_stop; + dev->get_stats = &dmfe_get_stats; + dev->set_multicast_list = &dmfe_set_filter_mode; + dev->do_ioctl = &dmfe_do_ioctl; + spin_lock_init(&db->lock); + + pci_read_config_dword(pdev, 0x50, &pci_pmr); + pci_pmr &= 0x70000; + if ( (pci_pmr == 0x10000) && (dev_rev == 0x02000031) ) + db->chip_type = 1; /* DM9102A E3 */ + else + db->chip_type = 0; + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + err = register_netdev (dev); + if (err) + goto err_out_res; + + printk(KERN_INFO "%s: Davicom DM%04lx at pci%s,", + dev->name, + ent->driver_data >> 16, + pdev->slot_name); + for (i = 0; i < 6; i++) + printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + printk(", irq %d.\n", dev->irq); + + pci_set_master(pdev); + + return 0; + +err_out_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + pci_set_drvdata(pdev, NULL); + kfree(dev); + + return err; +} + + +static void __exit dmfe_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_remove_one()", 0); + + if (dev) { + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, db->desc_pool_ptr, + db->desc_pool_dma_ptr); + pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, + db->buf_pool_ptr, db->buf_pool_dma_ptr); + unregister_netdev(dev); + pci_release_regions(pdev); + kfree(dev); /* free board information */ + pci_set_drvdata(pdev, NULL); + } + + DMFE_DBUG(0, "dmfe_remove_one() exit", 0); +} + + +/* + * Open the interface. + * The interface is opened whenever "ifconfig" actives it. + */ + +static int dmfe_open(struct DEVICE *dev) +{ + int ret; + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_open", 0); + + ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); + if (ret) + return ret; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + db->wait_reset = 0; + + db->first_in_callback = 0; + db->NIC_capability = 0xf; /* All capability*/ + db->PHY_reg4 = 0x1e0; + + /* CR6 operation mode decision */ + if ( !chkmode || (db->chip_id == PCI_DM9132_ID) || + (db->chip_revision >= 0x02000030) ) { + db->cr6_data |= DMFE_TXTH_256; + db->cr0_data = CR0_DEFAULT; + db->dm910x_chk_mode=4; /* Enter the normal mode */ + } else { + db->cr6_data |= CR6_SFT; /* Store & Forward mode */ + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ + } + + /* Initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Active System Interface */ + netif_wake_queue(dev); + + /* set and active a timer process */ + init_timer(&db->timer); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + db->timer.data = (unsigned long)dev; + db->timer.function = &dmfe_timer; + add_timer(&db->timer); + + return 0; +} + + +/* Initilize DM910X board + * Reset DM910X board + * Initilize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void dmfe_init_dm910x(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = db->ioaddr; + + DMFE_DBUG(0, "dmfe_init_dm910x()", 0); + + /* Reset DM910x MAC controller */ + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, ioaddr + DCR0); + udelay(5); + + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + + /* Parser SROM and media mode */ + dmfe_parse_srom(db); + db->media_mode = dmfe_media_mode; + + /* RESET Phyxcer Chip by GPR port bit 7 */ + outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ + if (db->chip_id == PCI_DM9009_ID) { + outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ + mdelay(300); /* Delay 300 ms */ + } + outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ + + /* Process Phyxcer Media Mode */ + if ( !(db->media_mode & 0x10) ) /* Force 1M mode */ + dmfe_set_phyxcer(db); + + /* Media Mode Process */ + if ( !(db->media_mode & DMFE_AUTO) ) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initiliaze Transmit/Receive decriptor and CR3/4 */ + dmfe_descriptor_init(db, ioaddr); + + /* Init CR6 to program DM910x operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, ioaddr + DCR15); + + /* Enable DM910X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; + update_cr6(db->cr6_data, ioaddr); +} + + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + struct tx_desc *txptr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_start_xmit", 0); + + /* Resource flag check */ + netif_stop_queue(dev); + + /* Too large packet check */ + if (skb->len > MAX_PACKET_SIZE) { + printk(KERN_ERR DRV_NAME ": big packet = %d\n", (u16)skb->len); + dev_kfree_skb(skb); + return 0; + } + + spin_lock_irqsave(&db->lock, flags); + + /* No Tx resource check, it never happen nromally */ + if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { + spin_unlock_irqrestore(&db->lock, flags); + printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_queue_cnt); + return 1; + } + + /* Disable NIC interrupt */ + outl(0, dev->base_addr + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } else { + db->tx_queue_cnt++; /* queue TX packet */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + } + + /* Tx resource check */ + if ( db->tx_queue_cnt < TX_FREE_DESC_CNT ) + netif_wake_queue(dev); + + /* free this SKB */ + dev_kfree_skb(skb); + + /* Restore CR7 to enable interrupt */ + spin_unlock_irqrestore(&db->lock, flags); + outl(db->cr7_data, dev->base_addr + DCR7); + + return 0; +} + + +/* + * Stop the interface. + * The interface is stopped when it is brought. + */ + +static int dmfe_stop(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long ioaddr = dev->base_addr; + + DMFE_DBUG(0, "dmfe_stop", 0); + + /* disable system */ + netif_stop_queue(dev); + + /* deleted timer */ + del_timer_sync(&db->timer); + + /* Reset & stop DM910X board */ + outl(DM910X_RESET, ioaddr + DCR0); + udelay(5); + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* free interrupt */ + free_irq(dev->irq, dev); + + /* free allocated rx buffer */ + dmfe_free_rxbuffer(db); + +#if 0 + /* show statistic counter */ + printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", + db->tx_fifo_underrun, db->tx_excessive_collision, + db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, + db->tx_jabber_timeout, db->reset_count, db->reset_cr8, + db->reset_fatal, db->reset_TXtimeout); +#endif + + return 0; +} + + +/* + * DM9102 insterrupt handler + * receive the packet to upper layer, free the transmitted packet + */ + +static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct DEVICE *dev = dev_id; + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + unsigned long ioaddr = dev->base_addr; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_interrupt()", 0); + + if (!dev) { + DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0); + return; + } + + spin_lock_irqsave(&db->lock, flags); + + /* Got DM910X status */ + db->cr5_data = inl(ioaddr + DCR5); + outl(db->cr5_data, ioaddr + DCR5); + if ( !(db->cr5_data & 0xc1) ) { + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Disable all interrupt in CR7 to solve the interrupt edge problem */ + outl(0, ioaddr + DCR7); + + /* Check system status */ + if (db->cr5_data & 0x2000) { + /* system bus error happen */ + DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data); + db->reset_fatal++; + db->wait_reset = 1; /* Need to RESET */ + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Received the coming packet */ + if ( (db->cr5_data & 0x40) && db->rx_avail_cnt ) + dmfe_rx_packet(dev, db); + + /* reallocate rx descriptor buffer */ + if (db->rx_avail_cntcr5_data & 0x01) + dmfe_free_tx_pkt(dev, db); + + /* Mode Check */ + if (db->dm910x_chk_mode & 0x2) { + db->dm910x_chk_mode = 0x4; + db->cr6_data |= 0x100; + update_cr6(db->cr6_data, db->ioaddr); + } + + /* Restore CR7 to enable interrupt mask */ + outl(db->cr7_data, ioaddr + DCR7); + + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Free TX resource after TX complete + */ + +static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct tx_desc *txptr; + unsigned long ioaddr = dev->base_addr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while(db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + db->stats.tx_packets++; + + /* Transmit statistic counter */ + if ( tdes0 != 0x7fffffff ) { + /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ + db->stats.collisions += (tdes0 >> 3) & 0xf; + db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + if (tdes0 & TDES0_ERR_MASK) { + db->stats.tx_errors++; + + if (tdes0 & 0x0002) { /* UnderRun */ + db->tx_fifo_underrun++; + if ( !(db->cr6_data & CR6_SFT) ) { + db->cr6_data = db->cr6_data | CR6_SFT; + update_cr6(db->cr6_data, db->ioaddr); + } + } + if (tdes0 & 0x0100) + db->tx_excessive_collision++; + if (tdes0 & 0x0200) + db->tx_late_collision++; + if (tdes0 & 0x0400) + db->tx_no_carrier++; + if (tdes0 & 0x0800) + db->tx_loss_carrier++; + if (tdes0 & 0x4000) + db->tx_jabber_timeout++; + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; + + /* Send the Tx packet in queue */ + if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + db->tx_queue_cnt--; + outl(0x1, ioaddr + DCR1); /* Issue Tx polling */ + dev->trans_start = jiffies; /* saved time stamp */ + } + + /* Resource available check */ + if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT ) + netif_wake_queue(dev); /* Active upper layer, send again */ +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + int rxlen; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + while(db->rx_avail_cnt) { + rdes0 = le32_to_cpu(rxptr->rdes0); + if (rdes0 & 0x80000000) /* packet owner check */ + break; + + db->rx_avail_cnt--; + db->interval_rx_cnt++; + + pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + if ( (rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this SKB */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + /* A packet with First/Last flag */ + rxlen = ( (rdes0 >> 16) & 0x3fff) - 4; + + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + //printk(DRV_NAME ": rdes0: %lx\n", rdes0); + db->stats.rx_errors++; + if (rdes0 & 1) + db->stats.rx_fifo_errors++; + if (rdes0 & 2) + db->stats.rx_crc_errors++; + if (rdes0 & 0x80) + db->stats.rx_length_errors++; + } + + if ( !(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen>6)) ) { + skb = rxptr->rx_skb_ptr; + + /* Received Packet CRC check need or not */ + if ( (db->dm910x_chk_mode & 1) && + (cal_CRC(skb->tail, rxlen, 1) != + (*(u32 *) (skb->tail+rxlen) ))) { /* FIXME (?) */ + /* Found a error received packet */ + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + db->dm910x_chk_mode = 3; + } else { + /* Good packet, send to upper layer */ + /* Shorst packet used new SKB */ + if ( (rxlen < RX_COPY_SIZE) && + ( (skb = dev_alloc_skb(rxlen + 2) ) + != NULL) ) { + /* size less than COPY_SIZE, allocate a rxlen SKB */ + skb->dev = dev; + skb_reserve(skb, 2); /* 16byte align */ + memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } else { + skb->dev = dev; + skb_put(skb, rxlen); + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + db->stats.rx_packets++; + db->stats.rx_bytes += rxlen; + } + } else { + /* Reuse SKB buffer when the packet is error */ + DMFE_DBUG(0, "Reuse SK buffer, rdes0", rdes0); + dmfe_reuse_skb(db, rxptr->rx_skb_ptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; +} + + +/* + * Get statistics from driver. + */ + +static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev) +{ + struct dmfe_board_info *db = (struct dmfe_board_info *)dev->priv; + + DMFE_DBUG(0, "dmfe_get_stats", 0); + return &db->stats; +} + + +/* + * Set DM910X multicast address + */ + +static void dmfe_set_filter_mode(struct DEVICE * dev) +{ + struct dmfe_board_info *db = dev->priv; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_set_filter_mode()", 0); + spin_lock_irqsave(&db->lock, flags); + + if (dev->flags & IFF_PROMISC) { + DMFE_DBUG(0, "Enable PROM Mode", 0); + db->cr6_data |= CR6_PM | CR6_PBF; + update_cr6(db->cr6_data, db->ioaddr); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) { + DMFE_DBUG(0, "Pass all multicast address", dev->mc_count); + db->cr6_data &= ~(CR6_PM | CR6_PBF); + db->cr6_data |= CR6_PAM; + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + DMFE_DBUG(0, "Set multicast address", dev->mc_count); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Process the ethtool ioctl command + */ + +static int dmfe_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct dmfe_board_info *db = dev->priv; + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + u32 ethcmd; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + if (db->pdev) + strcpy(info.bus_info, db->pdev->slot_name); + else + sprintf(info.bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + return -EOPNOTSUPP; +} + + +/* + * Process the upper socket ioctl command + */ + +static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd) +{ + int retval = -EOPNOTSUPP; + DMFE_DBUG(0, "dmfe_do_ioctl()", 0); + + switch(cmd) { + case SIOCETHTOOL: + return dmfe_ethtool_ioctl(dev, (void*)ifr->ifr_data); + } + + return retval; +} + + +/* + * A periodic timer routine + * Dynamic media sense, allocate Rx buffer... + */ + +static void dmfe_timer(unsigned long data) +{ + u32 tmp_cr8; + unsigned char tmp_cr12; + struct DEVICE *dev = (struct DEVICE *) data; + struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; + unsigned long flags; + + DMFE_DBUG(0, "dmfe_timer()", 0); + spin_lock_irqsave(&db->lock, flags); + + /* Media mode process when Link OK before enter this route */ + if (db->first_in_callback == 0) { + db->first_in_callback = 1; + if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + db->cr6_data |= 0x40000; + update_cr6(db->cr6_data, db->ioaddr); + db->timer.expires = DMFE_TIMER_WUT + HZ * 2; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + } + + + /* Operating Mode Check */ + if ( (db->dm910x_chk_mode & 0x1) && + (db->stats.rx_packets > MAX_CHECK_PACKET) ) + db->dm910x_chk_mode = 0x4; + + /* Dynamic reset DM910X : system error or transmit time-out */ + tmp_cr8 = inl(db->ioaddr + DCR8); + if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) { + db->reset_cr8++; + db->wait_reset = 1; + } + db->interval_rx_cnt = 0; + + /* TX polling kick monitor */ + if ( db->tx_packet_cnt && + time_after(jiffies, dev->trans_start + DMFE_TX_KICK) ) { + outl(0x1, dev->base_addr + DCR1); /* Tx polling again */ + + /* TX Timeout */ + if ( time_after(jiffies, dev->trans_start + DMFE_TX_TIMEOUT) ) { + db->reset_TXtimeout++; + db->wait_reset = 1; + printk(KERN_WARNING "%s: Tx timeout - resetting\n", + dev->name); + } + } + + if (db->wait_reset) { + DMFE_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt); + db->reset_count++; + dmfe_dynamic_reset(dev); + db->first_in_callback = 0; + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); + return; + } + + /* Link status check, Dynamic media type change */ + if (db->chip_id == PCI_DM9132_ID) + tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ + else + tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ + + if ( ((db->chip_id == PCI_DM9102_ID) && + (db->chip_revision == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && + (db->chip_revision == 0x02000010)) ) { + /* DM9102A Chip */ + if (tmp_cr12 & 2) + tmp_cr12 = 0x0; /* Link failed */ + else + tmp_cr12 = 0x3; /* Link OK */ + } + + if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { + /* Link Failed */ + DMFE_DBUG(0, "Link Failed", tmp_cr12); + db->link_failed = 1; + + /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ + /* AUTO or force 1M Homerun/Longrun don't need */ + if ( !(db->media_mode & 0x38) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + + /* AUTO mode, if INT phyxcer link failed, select EXT device */ + if (db->media_mode & DMFE_AUTO) { + /* 10/100M link failed, used 1M Home-Net */ + db->cr6_data|=0x00040000; /* bit18=1, MII */ + db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ + update_cr6(db->cr6_data, db->ioaddr); + } + } else + if ((tmp_cr12 & 0x3) && db->link_failed) { + DMFE_DBUG(0, "Link link OK", tmp_cr12); + db->link_failed = 0; + + /* Auto Sense Speed */ + if ( (db->media_mode & DMFE_AUTO) && + dmfe_sense_speed(db) ) + db->link_failed = 1; + dmfe_process_mode(db); + /* SHOW_MEDIA_TYPE(db->op_mode); */ + } + + /* HPNA remote command check */ + if (db->HPNA_command & 0xf00) { + db->HPNA_timer--; + if (!db->HPNA_timer) + dmfe_HPNA_remote_cmd_chk(db); + } + + /* Timer active again */ + db->timer.expires = DMFE_TIMER_WUT; + add_timer(&db->timer); + spin_unlock_irqrestore(&db->lock, flags); +} + + +/* + * Dynamic reset the DM910X board + * Stop DM910X board + * Free Tx/Rx allocated memory + * Reset DM910X board + * Re-initilize DM910X board + */ + +static void dmfe_dynamic_reset(struct DEVICE *dev) +{ + struct dmfe_board_info *db = dev->priv; + + DMFE_DBUG(0, "dmfe_dynamic_reset()", 0); + + /* Sopt MAC controller */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->base_addr); + outl(0, dev->base_addr + DCR7); /* Disable Interrupt */ + outl(inl(dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Disable upper layer interface */ + netif_stop_queue(dev); + + /* Free Rx Allocate buffer */ + dmfe_free_rxbuffer(db); + + /* system variable init */ + db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; + db->rx_avail_cnt = 0; + db->link_failed = 1; + db->wait_reset = 0; + + /* Re-initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Restart upper layer interface */ + netif_wake_queue(dev); +} + + +/* + * free all allocated rx buffer + */ + +static void dmfe_free_rxbuffer(struct dmfe_board_info * db) +{ + DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); + + /* free allocated rx buffer */ + while (db->rx_avail_cnt) { + dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr); + db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc; + db->rx_avail_cnt--; + } +} + + +/* + * Reuse the SK buffer + */ + +static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) +{ + struct rx_desc *rxptr = db->rx_insert_ptr; + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { + rxptr->rx_skb_ptr = skb; + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + db->rx_avail_cnt++; + db->rx_insert_ptr = rxptr->next_rx_desc; + } else + DMFE_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt); +} + + +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + + DMFE_DBUG(0, "dmfe_descriptor_init()", 0); + + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_insert_ptr = db->first_rx_desc; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ + + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma=db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + + +/* + * Update CR6 value + * Firstly stop DM910X , then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + u32 cr6_tmp; + + cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ + outl(cr6_tmp, ioaddr + DCR6); + udelay(5); + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + + +/* + * Send a setup frame for DM9132 + * This setup frame initilize DM910X addres filter mode +*/ + +static void dm9132_id_table(struct DEVICE *dev, int mc_cnt) +{ + struct dev_mc_list *mcptr; + u16 * addrptr; + unsigned long ioaddr = dev->base_addr+0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; + + DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + for (i = 0; i < 4; i++) + hash_table[i] = 0x0; + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + hash_val = cal_CRC( (char *) mcptr->dmi_addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) + outw(hash_table[i], ioaddr); +} + + +/* + * Send a setup frame for DM9102/DM9102A + * This setup frame initilize DM910X addres filter mode + */ + +static void send_filter_frame(struct DEVICE *dev, int mc_cnt) +{ + struct dmfe_board_info *db = dev->priv; + struct dev_mc_list *mcptr; + struct tx_desc *txptr; + u16 * addrptr; + u32 * suptr; + int i; + + DMFE_DBUG(0, "send_filter_frame()", 0); + + txptr = db->tx_insert_ptr; + suptr = (u32 *) txptr->tx_buf_ptr; + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + + /* broadcast address */ + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + + /* fit the multicast address */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + addrptr = (u16 *) mcptr->dmi_addr; + *suptr++ = addrptr[0]; + *suptr++ = addrptr[1]; + *suptr++ = addrptr[2]; + } + + for (; i<14; i++) { + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + } + + /* prepare the setup frame */ + db->tx_insert_ptr = txptr->next_tx_desc; + txptr->tdes1 = cpu_to_le32(0x890000c0); + + /* Resource Check and Send the setup packet */ + if (!db->tx_packet_cnt) { + /* Resource Empty */ + db->tx_packet_cnt++; + txptr->tdes0 = cpu_to_le32(0x80000000); + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */ + update_cr6(db->cr6_data, dev->base_addr); + dev->trans_start = jiffies; + } else + db->tx_queue_cnt++; /* Put in TX queue */ +} + + +/* + * Allocate rx buffer, + * As possible as allocate maxiumn Rx buffer + */ + +static void allocate_rx_buffer(struct dmfe_board_info *db) +{ + struct rx_desc *rxptr; + struct sk_buff *skb; + + rxptr = db->rx_insert_ptr; + + while(db->rx_avail_cnt < RX_DESC_CNT) { + if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) + break; + rxptr->rx_skb_ptr = skb; /* FIXME (?) */ + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + wmb(); + rxptr->rdes0 = cpu_to_le32(0x80000000); + rxptr = rxptr->next_rx_desc; + db->rx_avail_cnt++; + } + + db->rx_insert_ptr = rxptr; +} + + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + + +/* + * Auto sense the media mode + */ + +static u8 dmfe_sense_speed(struct dmfe_board_info * db) +{ + u8 ErrFlag = 0; + u16 phy_mode; + + /* CR6 bit18=0, select 10/100M */ + update_cr6( (db->cr6_data & ~0x40000), db->ioaddr); + + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); + + if ( (phy_mode & 0x24) == 0x24 ) { + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; + /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ + switch (phy_mode) { + case 0x1000: db->op_mode = DMFE_10MHF; break; + case 0x2000: db->op_mode = DMFE_10MFD; break; + case 0x4000: db->op_mode = DMFE_100MHF; break; + case 0x8000: db->op_mode = DMFE_100MFD; break; + default: db->op_mode = DMFE_10MHF; + ErrFlag = 1; + break; + } + } else { + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Link Failed :", phy_mode); + ErrFlag = 1; + } + + return ErrFlag; +} + + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void dmfe_set_phyxcer(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Select 10/100M phyxcer */ + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, db->ioaddr); + + /* DM9009 Chip: Phyxcer reg18 bit12=0 */ + if (db->chip_id == PCI_DM9009_ID) { + phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000; + phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id); + } + + /* Phyxcer capability setting */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & DMFE_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch(db->media_mode) { + case DMFE_10MHF: phy_reg |= 0x20; break; + case DMFE_10MFD: phy_reg |= 0x40; break; + case DMFE_100MHF: phy_reg |= 0x80; break; + case DMFE_100MFD: phy_reg |= 0x100; break; + } + if (db->chip_id == PCI_DM9009_ID) phy_reg &= 0x61; + } + + /* Write new capability to Phyxcer Reg4 */ + if ( !(phy_reg & 0x01e0)) { + phy_reg|=db->PHY_reg4; + db->media_mode|=DMFE_AUTO; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1800, db->chip_id); + if ( !db->chip_type ) + phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); +} + + +/* + * Process op-mode + * AUTO mode : PHY controller in Auto-negotiation Mode + * Force mode: PHY controller in force mode with HUB + * N-way force capability with SWITCH + */ + +static void dmfe_process_mode(struct dmfe_board_info *db) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ + else + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + + /* Transciver Selection */ + if (db->op_mode & 0x10) /* 1M HomePNA */ + db->cr6_data |= 0x40000;/* External MII select */ + else + db->cr6_data &= ~0x40000;/* Internal 10/100 transciver */ + + update_cr6(db->cr6_data, db->ioaddr); + + /* 10/100M phyxcer force mode need */ + if ( !(db->media_mode & 0x18)) { + /* Forece Mode */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 6, db->chip_id); + if ( !(phy_reg & 0x1) ) { + /* parter without N-Way capability */ + phy_reg = 0x0; + switch(db->op_mode) { + case DMFE_10MHF: phy_reg = 0x0; break; + case DMFE_10MFD: phy_reg = 0x100; break; + case DMFE_100MHF: phy_reg = 0x2000; break; + case DMFE_100MFD: phy_reg = 0x2100; break; + } + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) + mdelay(20); + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + } + } +} + + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for ( i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + } +} + + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(ioaddr); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } + } + + return phy_data; +} + + +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) +{ + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); +} + + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr) +{ + u16 phy_data; + + outl(0x50000, ioaddr); + udelay(1); + phy_data = ( inl(ioaddr) >> 19 ) & 0x1; + outl(0x40000, ioaddr); + udelay(1); + + return phy_data; +} + + +/* + * Calculate the CRC valude of the Rx packet + * flag = 1 : return the reverse CRC (for the received packet CRC) + * 0 : return the normal CRC (for Hash Table index) + */ + +static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) +{ + u32 crc = crc32(~0, Data, Len); + if (flag) crc = ~crc; + return crc; +} + + +/* + * Parser SROM and media mode + */ + +static void dmfe_parse_srom(struct dmfe_board_info * db) +{ + char * srom = db->srom; + int dmfe_mode, tmp_reg; + + DMFE_DBUG(0, "dmfe_parse_srom() ", 0); + + /* Init CR15 */ + db->cr15_data = CR15_DEFAULT; + + /* Check SROM Version */ + if ( ( (int) srom[18] & 0xff) == SROM_V41_CODE) { + /* SROM V4.01 */ + /* Get NIC support media mode */ + db->NIC_capability = le16_to_cpup(srom + 34); + db->PHY_reg4 = 0; + for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { + switch( db->NIC_capability & tmp_reg ) { + case 0x1: db->PHY_reg4 |= 0x0020; break; + case 0x2: db->PHY_reg4 |= 0x0040; break; + case 0x4: db->PHY_reg4 |= 0x0080; break; + case 0x8: db->PHY_reg4 |= 0x0100; break; + } + } + + /* Media Mode Force or not check */ + dmfe_mode = le32_to_cpup(srom + 34) & le32_to_cpup(srom + 36); + switch(dmfe_mode) { + case 0x4: dmfe_media_mode = DMFE_100MHF; break; /* 100MHF */ + case 0x2: dmfe_media_mode = DMFE_10MFD; break; /* 10MFD */ + case 0x8: dmfe_media_mode = DMFE_100MFD; break; /* 100MFD */ + case 0x100: + case 0x200: dmfe_media_mode = DMFE_1M_HPNA; break;/* HomePNA */ + } + + /* Special Function setting */ + /* VLAN function */ + if ( (SF_mode & 0x1) || (srom[43] & 0x80) ) + db->cr15_data |= 0x40; + + /* Flow Control */ + if ( (SF_mode & 0x2) || (srom[40] & 0x1) ) + db->cr15_data |= 0x400; + + /* TX pause packet */ + if ( (SF_mode & 0x4) || (srom[40] & 0xe) ) + db->cr15_data |= 0x9800; + } + + /* Parse HPNA parameter */ + db->HPNA_command = 1; + + /* Accept remote command or not */ + if (HPNA_rx_cmd == 0) + db->HPNA_command |= 0x8000; + + /* Issue remote command & operation mode */ + if (HPNA_tx_cmd == 1) + switch(HPNA_mode) { /* Issue Remote Command */ + case 0: db->HPNA_command |= 0x0904; break; + case 1: db->HPNA_command |= 0x0a00; break; + case 2: db->HPNA_command |= 0x0506; break; + case 3: db->HPNA_command |= 0x0602; break; + } + else + switch(HPNA_mode) { /* Don't Issue */ + case 0: db->HPNA_command |= 0x0004; break; + case 1: db->HPNA_command |= 0x0000; break; + case 2: db->HPNA_command |= 0x0006; break; + case 3: db->HPNA_command |= 0x0002; break; + } + + /* Check DM9801 or DM9802 present or not */ + db->HPNA_present = 0; + update_cr6(db->cr6_data|0x40000, db->ioaddr); + tmp_reg = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); + if ( ( tmp_reg & 0xfff0 ) == 0xb900 ) { + /* DM9801 or DM9802 present */ + db->HPNA_timer = 8; + if ( phy_read(db->ioaddr, db->phy_addr, 31, db->chip_id) == 0x4404) { + /* DM9801 HomeRun */ + db->HPNA_present = 1; + dmfe_program_DM9801(db, tmp_reg); + } else { + /* DM9802 LongRun */ + db->HPNA_present = 2; + dmfe_program_DM9802(db); + } + } + +} + + +/* + * Init HomeRun DM9801 + */ + +static void dmfe_program_DM9801(struct dmfe_board_info * db, int HPNA_rev) +{ + uint reg17, reg25; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9801_NOISE_FLOOR; + switch(HPNA_rev) { + case 0xb900: /* DM9801 E3 */ + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 24, db->chip_id); + reg25 = ( (reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + break; + case 0xb901: /* DM9801 E4 */ + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; + break; + case 0xb902: /* DM9801 E5 */ + case 0xb903: /* DM9801 E6 */ + default: + db->HPNA_command |= 0x1000; + reg25 = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; + reg17 = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; + break; + } + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 17, reg17, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 25, reg25, db->chip_id); +} + + +/* + * Init HomeRun DM9802 + */ + +static void dmfe_program_DM9802(struct dmfe_board_info * db) +{ + uint phy_reg; + + if ( !HPNA_NoiseFloor ) HPNA_NoiseFloor = DM9802_NOISE_FLOOR; + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_reg = phy_read(db->ioaddr, db->phy_addr, 25, db->chip_id); + phy_reg = ( phy_reg & 0xff00) + HPNA_NoiseFloor; + phy_write(db->ioaddr, db->phy_addr, 25, phy_reg, db->chip_id); +} + + +/* + * Check remote HPNA power and speed status. If not correct, + * issue command again. +*/ + +static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) +{ + uint phy_reg; + + /* Got remote device status */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0x60; + switch(phy_reg) { + case 0x00: phy_reg = 0x0a00;break; /* LP/LS */ + case 0x20: phy_reg = 0x0900;break; /* LP/HS */ + case 0x40: phy_reg = 0x0600;break; /* HP/LS */ + case 0x60: phy_reg = 0x0500;break; /* HP/HS */ + } + + /* Check remote device status match our setting ot not */ + if ( phy_reg != (db->HPNA_command & 0x0f00) ) { + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + db->HPNA_timer=8; + } else + db->HPNA_timer=600; /* Match, every 10 minutes, check */ +} + + + +static struct pci_device_id dmfe_pci_tbl[] __devinitdata = { + { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, + { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); + + +static struct pci_driver dmfe_driver = { + name: "dmfe", + id_table: dmfe_pci_tbl, + probe: dmfe_init_one, + remove: __devexit_p(dmfe_remove_one), +}; + +MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); +MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(debug, "i"); +MODULE_PARM(mode, "i"); +MODULE_PARM(cr6set, "i"); +MODULE_PARM(chkmode, "i"); +MODULE_PARM(HPNA_mode, "i"); +MODULE_PARM(HPNA_rx_cmd, "i"); +MODULE_PARM(HPNA_tx_cmd, "i"); +MODULE_PARM(HPNA_NoiseFloor, "i"); +MODULE_PARM(SF_mode, "i"); +MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); +MODULE_PARM_DESC(mode, "Davicom DM9xxx: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); +MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function (bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); + +/* Description: + * when user used insmod to add module, system invoked init_module() + * to initilize and register. + */ + +static int __init dmfe_init_module(void) +{ + int rc; + + printk(version); + printed_version = 1; + + DMFE_DBUG(0, "init_module() ", debug); + + if (debug) + dmfe_debug = debug; /* set debug flag */ + if (cr6set) + dmfe_cr6_user_set = cr6set; + + switch(mode) { + case DMFE_10MHF: + case DMFE_100MHF: + case DMFE_10MFD: + case DMFE_100MFD: + case DMFE_1M_HPNA: + dmfe_media_mode = mode; + break; + default:dmfe_media_mode = DMFE_AUTO; + break; + } + + if (HPNA_mode > 4) + HPNA_mode = 0; /* Default: LP/HS */ + if (HPNA_rx_cmd > 1) + HPNA_rx_cmd = 0; /* Default: Ignored remote cmd */ + if (HPNA_tx_cmd > 1) + HPNA_tx_cmd = 0; /* Default: Don't issue remote cmd */ + if (HPNA_NoiseFloor > 15) + HPNA_NoiseFloor = 0; + + rc = pci_module_init(&dmfe_driver); + if (rc < 0) + return rc; + + return 0; +} + + +/* + * Description: + * when user used rmmod to delete module, system invoked clean_module() + * to un-register all registered services. + */ + +static void __exit dmfe_cleanup_module(void) +{ + DMFE_DBUG(0, "dmfe_clean_module() ", debug); + pci_unregister_driver(&dmfe_driver); +} + +module_init(dmfe_init_module); +module_exit(dmfe_cleanup_module); diff -urN linux-2.5.6-pre3/drivers/net/tulip/tulip_core.c linux-2.5.6/drivers/net/tulip/tulip_core.c --- linux-2.5.6-pre3/drivers/net/tulip/tulip_core.c Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/tulip/tulip_core.c Thu Mar 7 18:24:48 2002 @@ -15,8 +15,8 @@ */ #define DRV_NAME "tulip" -#define DRV_VERSION "1.1.11" -#define DRV_RELDATE "Feb 08, 2002" +#define DRV_VERSION "1.1.12" +#define DRV_RELDATE "Mar 07, 2002" #include #include @@ -1164,7 +1164,7 @@ { struct tulip_private *tp = dev->priv; u8 cache; - u16 pci_command, new_command; + u16 pci_command; u32 csr0; if (tulip_debug > 3) @@ -1172,24 +1172,6 @@ tp->csr0 = csr0 = 0; - /* check for sane cache line size. from acenic.c. */ - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); - if ((cache << 2) != SMP_CACHE_BYTES) { - printk(KERN_WARNING "%s: PCI cache line size set incorrectly " - "(%i bytes) by BIOS/FW, correcting to %i\n", - pdev->slot_name, (cache << 2), SMP_CACHE_BYTES); - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, - SMP_CACHE_BYTES >> 2); - udelay(5); - } - - /* read cache line size again, hardware may not have accepted - * our cache line size change - */ - pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); - if (!cache) - goto out; - /* if we have any cache line size at all, we can do MRM */ csr0 |= MRM; @@ -1200,15 +1182,19 @@ /* set or disable MWI in the standard PCI command bit. * Check for the case where mwi is desired but not available */ + if (csr0 & MWI) pci_set_mwi(pdev); + else pci_clear_mwi(pdev); + + /* read result from hardware (in case bit refused to enable) */ pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if (csr0 & MWI) new_command = pci_command | PCI_COMMAND_INVALIDATE; - else new_command = pci_command & ~PCI_COMMAND_INVALIDATE; - if (new_command != pci_command) { - pci_write_config_word(pdev, PCI_COMMAND, new_command); - udelay(5); - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE))) - csr0 &= ~MWI; + if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE))) + csr0 &= ~MWI; + + /* if cache line size hardwired to zero, no MWI */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache); + if ((csr0 & MWI) && (cache == 0)) { + csr0 &= ~MWI; + pci_clear_mwi(pdev); } /* assign per-cacheline-size cache alignment and @@ -1225,20 +1211,29 @@ csr0 |= MRL | (3 << CALShift) | (32 << BurstLenShift); break; default: - goto out; + cache = 0; + break; } - tp->csr0 = csr0; - goto out; + /* if we have a good cache line size, we by now have a good + * csr0, so save it and exit + */ + if (cache) + goto out; + /* we don't have a good csr0 or cache line size, disable MWI */ if (csr0 & MWI) { - pci_command &= ~PCI_COMMAND_INVALIDATE; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); + pci_clear_mwi(pdev); csr0 &= ~MWI; } - tp->csr0 = csr0 | (8 << BurstLenShift) | (1 << CALShift); + + /* sane defaults for burst length and cache alignment + * originally from de4x5 driver + */ + csr0 |= (8 << BurstLenShift) | (1 << CALShift); out: + tp->csr0 = csr0; if (tulip_debug > 2) printk(KERN_DEBUG "%s: MWI config cacheline=%d, csr0=%08x\n", pdev->slot_name, cache, csr0); diff -urN linux-2.5.6-pre3/drivers/net/tulip/winbond-840.c linux-2.5.6/drivers/net/tulip/winbond-840.c --- linux-2.5.6-pre3/drivers/net/tulip/winbond-840.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/winbond-840.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,1757 @@ +/* winbond-840.c: A Linux PCI network adapter device driver. */ +/* + Written 1998-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support and updates available at + http://www.scyld.com/network/drivers.html + + Do not remove the copyright infomation. + Do not change the version information unless an improvement has been made. + Merely removing my name, as Compex has done in the past, does not count + as an improvement. + + Changelog: + * ported to 2.4 + ??? + * spin lock update, memory barriers, new style dma mappings + limit each tx buffer to < 1024 bytes + remove DescIntr from Rx descriptors (that's an Tx flag) + remove next pointer from Tx descriptors + synchronize tx_q_bytes + software reset in tx_timeout + Copyright (C) 2000 Manfred Spraul + * further cleanups + power management. + support for big endian descriptors + Copyright (C) 2001 Manfred Spraul + * ethtool support (jgarzik) + * Replace some MII-related magic numbers with constants (jgarzik) + + TODO: + * enable pci_power_off + * Wake-On-LAN +*/ + +#define DRV_NAME "winbond-840" +#define DRV_VERSION "1.01-d" +#define DRV_RELDATE "Nov-17-2001" + + +/* Automatically extracted configuration info: +probe-func: winbond840_probe +config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 + +c-help-name: Winbond W89c840 PCI Ethernet support +c-help-symbol: CONFIG_WINBOND_840 +c-help: This driver is for the Winbond W89c840 chip. It also works with +c-help: the TX9882 chip on the Compex RL100-ATX board. +c-help: More specific information and updates are available from +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. + These may be modified when a driver module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int max_interrupt_work = 20; +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The '840 uses a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak; + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<2^N> into a bit mask. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ +#define TX_QUEUE_LEN_RESTART 5 +#define RX_RING_SIZE 32 + +#define TX_BUFLIMIT (1024-128) + +/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. + To avoid overflowing we don't queue again until we have room for a + full-size packet. + */ +#define TX_FIFO_SIZE (2048) +#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) + + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c:v" DRV_VERSION " (2.4 port) " DRV_RELDATE " Donald Becker \n" +KERN_INFO " http://www.scyld.com/network/drivers.html\n"; + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)"); +MODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex"); +MODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)"); + +/* + Theory of Operation + +I. Board Compatibility + +This driver is for the Winbond w89c840 chip. + +II. Board-specific settings + +None. + +III. Driver operation + +This chip is very similar to the Digital 21*4* "Tulip" family. The first +twelve registers and the descriptor format are nearly identical. Read a +Tulip manual for operational details. + +A significant difference is that the multicast filter and station address are +stored in registers rather than loaded through a pseudo-transmit packet. + +Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a +full-sized packet we must use both data buffers in a descriptor. Thus the +driver uses ring mode where descriptors are implicitly sequential in memory, +rather than using the second descriptor address as a chain pointer to +subsequent descriptors. + +IV. Notes + +If you are going to almost clone a Tulip, why not go all the way and avoid +the need for a new driver? + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html +http://www.winbond.com.tw/ + +IVc. Errata + +A horrible bug exists in the transmit FIFO. Apparently the chip doesn't +correctly detect a full FIFO, and queuing more than 2048 bytes may result in +silent data corruption. + +Test with 'ping -s 10000' on a fast computer. + +*/ + + + +/* + PCI probe table. +*/ +enum pci_id_flags_bits { + /* Set PCI command register bits before calling probe1(). */ + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + /* Read and map the single following PCI BAR. */ + PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, + PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, +}; +enum chip_capability_flags { + CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,}; +#ifdef USE_IO_OPS +#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) +#else +#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) +#endif + +static struct pci_device_id w840_pci_tbl[] __devinitdata = { + { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, + { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, w840_pci_tbl); + +struct pci_id_info { + const char *name; + struct match_info { + int pci, pci_mask, subsystem, subsystem_mask; + int revision, revision_mask; /* Only 8 bits. */ + } id; + enum pci_id_flags_bits pci_flags; + int io_size; /* Needed for I/O region check or ioremap(). */ + int drv_flags; /* Driver use, intended as capability flags. */ +}; +static struct pci_id_info pci_id_tbl[] = { + {"Winbond W89c840", /* Sometime a Level-One switch card. */ + { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, + {"Winbond W89c840", { 0x08401050, 0xffffffff, }, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, + {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, + W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, + {0,}, /* 0 terminated list. */ +}; + +/* This driver was written to use PCI memory space, however some x86 systems + work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space + accesses instead of memory space. */ + +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the Command and Status Registers, "CSRs". + While similar to the Tulip, these registers are longword aligned. + Note: It's not useful to define symbolic names for every register bit in + the device. The name can only partially document the semantics and make + the driver longer and more difficult to read. +*/ +enum w840_offsets { + PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, + RxRingPtr=0x0C, TxRingPtr=0x10, + IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, + RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, + CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ + MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, + CurTxDescAddr=0x4C, CurTxBufAddr=0x50, +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { + NormalIntr=0x10000, AbnormalIntr=0x8000, + IntrPCIErr=0x2000, TimerInt=0x800, + IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, + TxFIFOUnderflow=0x20, RxErrIntr=0x10, + TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { + AcceptErr=0x80, AcceptRunt=0x40, + AcceptBroadcast=0x20, AcceptMulticast=0x10, + AcceptAllPhys=0x08, AcceptMyPhys=0x02, +}; + +enum mii_reg_bits { + MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, + MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct w840_rx_desc { + s32 status; + s32 length; + u32 buffer1; + u32 buffer2; +}; + +struct w840_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, + DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, + DescIntr=0x80000000, +}; + +#define PRIV_ALIGN 15 /* Required alignment mask */ +#define MII_CNT 1 /* winbond only supports one MII */ +struct netdev_private { + struct w840_rx_desc *rx_ring; + dma_addr_t rx_addr[RX_RING_SIZE]; + struct w840_tx_desc *tx_ring; + dma_addr_t tx_addr[TX_RING_SIZE]; + dma_addr_t ring_dma_addr; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for later free(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct net_device_stats stats; + struct timer_list timer; /* Media monitoring timer. */ + /* Frequently used values: keep some adjacent for cache effect. */ + spinlock_t lock; + int chip_id, drv_flags; + struct pci_dev *pci_dev; + int csr6; + struct w840_rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + unsigned int cur_tx, dirty_tx; + unsigned int tx_q_bytes; + unsigned int tx_full; /* The Tx queue is full. */ + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + unsigned char phys[MII_CNT]; /* MII device addresses, but only the first is used */ + u32 mii; + struct mii_if_info mii_if; +}; + +static int eeprom_read(long ioaddr, int location); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int netdev_open(struct net_device *dev); +static int update_link(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void init_rxtx_rings(struct net_device *dev); +static void free_rxtx_rings(struct netdev_private *np); +static void init_registers(struct net_device *dev); +static void tx_timeout(struct net_device *dev); +static int alloc_ringdesc(struct net_device *dev); +static void free_ringdesc(struct netdev_private *np); +static int start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int netdev_rx(struct net_device *dev); +static u32 __set_rx_mode(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int netdev_close(struct net_device *dev); + + + +static int __devinit w840_probe1 (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + struct netdev_private *np; + static int find_cnt; + int chip_idx = ent->driver_data; + int irq; + int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; + long ioaddr; + + i = pci_enable_device(pdev); + if (i) return i; + + pci_set_master(pdev); + + irq = pdev->irq; + + if (pci_set_dma_mask(pdev,0xFFFFffff)) { + printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n", + pdev->slot_name); + return -EIO; + } + dev = alloc_etherdev(sizeof(*np)); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); + + if (pci_request_regions(pdev, DRV_NAME)) + goto err_out_netdev; + +#ifdef USE_IO_OPS + ioaddr = pci_resource_start(pdev, 0); +#else + ioaddr = pci_resource_start(pdev, 1); + ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size); + if (!ioaddr) + goto err_out_free_res; +#endif + + for (i = 0; i < 3; i++) + ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); + + /* Reset the chip to erase previous misconfiguration. + No hold time required! */ + writel(0x00000001, ioaddr + PCIBusCfg); + + dev->base_addr = ioaddr; + dev->irq = irq; + + np = dev->priv; + np->pci_dev = pdev; + np->chip_id = chip_idx; + np->drv_flags = pci_id_tbl[chip_idx].drv_flags; + spin_lock_init(&np->lock); + np->mii_if.dev = dev; + np->mii_if.mdio_read = mdio_read; + np->mii_if.mdio_write = mdio_write; + + pci_set_drvdata(pdev, dev); + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->mii_if.full_duplex = 1; + if (option & 15) + printk(KERN_INFO "%s: ignoring user supplied media type %d", + dev->name, option & 15); + } + if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) + np->mii_if.full_duplex = 1; + + if (np->mii_if.full_duplex) + np->mii_if.duplex_lock = 1; + + /* The chip-specific entries in the device structure. */ + dev->open = &netdev_open; + dev->hard_start_xmit = &start_tx; + dev->stop = &netdev_close; + dev->get_stats = &get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &netdev_ioctl; + dev->tx_timeout = &tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + i = register_netdev(dev); + if (i) + goto err_out_cleardev; + + printk(KERN_INFO "%s: %s at 0x%lx, ", + dev->name, pci_id_tbl[chip_idx].name, ioaddr); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + + if (np->drv_flags & CanHaveMII) { + int phy, phy_idx = 0; + for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { + int mii_status = mdio_read(dev, phy, MII_BMSR); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); + np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+ + mdio_read(dev, phy, MII_PHYSID2); + printk(KERN_INFO "%s: MII PHY %8.8xh found at address %d, status " + "0x%4.4x advertising %4.4x.\n", + dev->name, np->mii, phy, mii_status, np->mii_if.advertising); + } + } + np->mii_cnt = phy_idx; + np->mii_if.phy_id = np->phys[0]; + if (phy_idx == 0) { + printk(KERN_WARNING "%s: MII PHY not found -- this device may " + "not operate correctly.\n", dev->name); + } + } + + find_cnt++; + return 0; + +err_out_cleardev: + pci_set_drvdata(pdev, NULL); +#ifndef USE_IO_OPS + iounmap((void *)ioaddr); +err_out_free_res: +#endif + pci_release_regions(pdev); +err_out_netdev: + kfree (dev); + return -ENODEV; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are + often serial bit streams generated by the host processor. + The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need + a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that + made udelay() unreliable. + The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is + depricated. +*/ +#define eeprom_delay(ee_addr) readl(ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, + EE_ChipSelect=0x801, EE_DataIn=0x08, +}; + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(long addr, int location) +{ + int i; + int retval = 0; + long ee_addr = addr + EECtrl; + int read_cmd = location | EE_ReadCmd; + writel(EE_ChipSelect, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + writel(dataval, ee_addr); + eeprom_delay(ee_addr); + writel(dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + writel(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); + writel(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(0, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back 33Mhz PCI cycles. */ +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with older tranceivers, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 1; + +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + writel(MDIO_WRITE1, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + long mdio_addr = dev->base_addr + MIICtrl; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i, retval = 0; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 20; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + struct netdev_private *np = dev->priv; + long mdio_addr = dev->base_addr + MIICtrl; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (location == 4 && phy_id == np->phys[0]) + np->mii_if.advertising = value; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return; +} + + +static int netdev_open(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int i; + + writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ + + netif_device_detach(dev); + i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (i) + goto out_err; + + if (debug > 1) + printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", + dev->name, dev->irq); + + if((i=alloc_ringdesc(dev))) + goto out_err; + + spin_lock_irq(&np->lock); + netif_device_attach(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_start_queue(dev); + if (debug > 2) + printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); + + /* Set the timer to check for link beat. */ + init_timer(&np->timer); + np->timer.expires = jiffies + 1*HZ; + np->timer.data = (unsigned long)dev; + np->timer.function = &netdev_timer; /* timer handler */ + add_timer(&np->timer); + return 0; +out_err: + netif_device_attach(dev); + return i; +} + +#define MII_DAVICOM_DM9101 0x0181b800 + +static int update_link(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int duplex, fasteth, result, mii_reg; + + /* BSMR */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + + if (mii_reg == 0xffff) + return np->csr6; + /* reread: the link status bit is sticky */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); + if (!(mii_reg & 0x4)) { + if (netif_carrier_ok(dev)) { + if (debug) + printk(KERN_INFO "%s: MII #%d reports no link. Disabling watchdog.\n", + dev->name, np->phys[0]); + netif_carrier_off(dev); + } + return np->csr6; + } + if (!netif_carrier_ok(dev)) { + if (debug) + printk(KERN_INFO "%s: MII #%d link is back. Enabling watchdog.\n", + dev->name, np->phys[0]); + netif_carrier_on(dev); + } + + if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) { + /* If the link partner doesn't support autonegotiation + * the MII detects it's abilities with the "parallel detection". + * Some MIIs update the LPA register to the result of the parallel + * detection, some don't. + * The Davicom PHY [at least 0181b800] doesn't. + * Instead bit 9 and 13 of the BMCR are updated to the result + * of the negotiation.. + */ + mii_reg = mdio_read(dev, np->phys[0], MII_BMCR); + duplex = mii_reg & BMCR_FULLDPLX; + fasteth = mii_reg & BMCR_SPEED100; + } else { + int negotiated; + mii_reg = mdio_read(dev, np->phys[0], MII_LPA); + negotiated = mii_reg & np->mii_if.advertising; + + duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL); + fasteth = negotiated & 0x380; + } + duplex |= np->mii_if.duplex_lock; + /* remove fastether and fullduplex */ + result = np->csr6 & ~0x20000200; + if (duplex) + result |= 0x200; + if (fasteth) + result |= 0x20000000; + if (result != np->csr6 && debug) + printk(KERN_INFO "%s: Setting %dMBit-%s-duplex based on MII#%d\n", + dev->name, fasteth ? 100 : 10, + duplex ? "full" : "half", np->phys[0]); + return result; +} + +#define RXTX_TIMEOUT 2000 +static inline void update_csr6(struct net_device *dev, int new) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int limit = RXTX_TIMEOUT; + + if (!netif_device_present(dev)) + new = 0; + if (new==np->csr6) + return; + /* stop both Tx and Rx processes */ + writel(np->csr6 & ~0x2002, ioaddr + NetworkConfig); + /* wait until they have really stopped */ + for (;;) { + int csr5 = readl(ioaddr + IntrStatus); + int t; + + t = (csr5 >> 17) & 0x07; + if (t==0||t==1) { + /* rx stopped */ + t = (csr5 >> 20) & 0x07; + if (t==0||t==1) + break; + } + + limit--; + if(!limit) { + printk(KERN_INFO "%s: couldn't stop rxtx, IntrStatus %xh.\n", + dev->name, csr5); + break; + } + udelay(1); + } + np->csr6 = new; + /* and restart them with the new configuration */ + writel(np->csr6, ioaddr + NetworkConfig); + if (new & 0x200) + np->mii_if.full_duplex = 1; +} + +static void netdev_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + if (debug > 2) + printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " + "config %8.8x.\n", + dev->name, (int)readl(ioaddr + IntrStatus), + (int)readl(ioaddr + NetworkConfig)); + spin_lock_irq(&np->lock); + update_csr6(dev, update_link(dev)); + spin_unlock_irq(&np->lock); + np->timer.expires = jiffies + 10*HZ; + add_timer(&np->timer); +} + +static void init_rxtx_rings(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int i; + + np->rx_head_desc = &np->rx_ring[0]; + np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; + + /* Initial all Rx descriptors. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].length = np->rx_buf_sz; + np->rx_ring[i].status = 0; + np->rx_skbuff[i] = 0; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].length |= DescEndRing; + + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_addr[i] = pci_map_single(np->pci_dev,skb->tail, + skb->len,PCI_DMA_FROMDEVICE); + + np->rx_ring[i].buffer1 = np->rx_addr[i]; + np->rx_ring[i].status = DescOwn; + } + + np->cur_rx = 0; + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* Initialize the Tx descriptors */ + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = 0; + np->tx_ring[i].status = 0; + } + np->tx_full = 0; + np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; + + writel(np->ring_dma_addr, dev->base_addr + RxRingPtr); + writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, + dev->base_addr + TxRingPtr); + +} + +static void free_rxtx_rings(struct netdev_private* np) +{ + int i; + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].status = 0; + if (np->rx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->rx_addr[i], + np->rx_skbuff[i]->len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) { + pci_unmap_single(np->pci_dev, + np->tx_addr[i], + np->tx_skbuff[i]->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(np->tx_skbuff[i]); + } + np->tx_skbuff[i] = 0; + } +} + +static void init_registers(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int i; + + for (i = 0; i < 6; i++) + writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ +#ifdef __BIG_ENDIAN + i = (1<<20); /* Big-endian descriptors */ +#else + i = 0; +#endif + i |= (0x04<<2); /* skip length 4 u32 */ + i |= 0x02; /* give Rx priority */ + + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 0000 align to cache 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords */ + +#if defined (__i386__) && !defined(MODULE) + /* When not a module we can work around broken '486 PCI boards. */ + if (boot_cpu_data.x86 <= 4) { + i |= 0x4800; + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to 8 longwords.\n", dev->name); + } else { + i |= 0xE000; + } +#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) + i |= 0xE000; +#elif defined(__sparc__) + i |= 0x4800; +#else +#warning Processor architecture undefined + i |= 0x4800; +#endif + writel(i, ioaddr + PCIBusCfg); + + np->csr6 = 0; + /* 128 byte Tx threshold; + Transmit on; Receive on; */ + update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev)); + + /* Clear and Enable interrupts by setting the interrupt mask. */ + writel(0x1A0F5, ioaddr + IntrStatus); + writel(0x1A0F5, ioaddr + IntrEnable); + + writel(0, ioaddr + RxStartDemand); +} + +static void tx_timeout(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," + " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); + + { + int i; + printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)np->rx_ring[i].status); + printk("\n"KERN_DEBUG" Tx ring %p: ", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", np->tx_ring[i].status); + printk("\n"); + } + printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n", + np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes); + printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C)); + + disable_irq(dev->irq); + spin_lock_irq(&np->lock); + /* + * Under high load dirty_tx and the internal tx descriptor pointer + * come out of sync, thus perform a software reset and reinitialize + * everything. + */ + + writel(1, dev->base_addr+PCIBusCfg); + udelay(1); + + free_rxtx_rings(np); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + enable_irq(dev->irq); + + netif_wake_queue(dev); + dev->trans_start = jiffies; + np->stats.tx_errors++; + return; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static int alloc_ringdesc(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + np->rx_ring = pci_alloc_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + &np->ring_dma_addr); + if(!np->rx_ring) + return -ENOMEM; + init_rxtx_rings(dev); + return 0; +} + +static void free_ringdesc(struct netdev_private *np) +{ + pci_free_consistent(np->pci_dev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + np->rx_ring, np->ring_dma_addr); + +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + unsigned entry; + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = np->cur_tx % TX_RING_SIZE; + + np->tx_addr[entry] = pci_map_single(np->pci_dev, + skb->data,skb->len, PCI_DMA_TODEVICE); + np->tx_skbuff[entry] = skb; + + np->tx_ring[entry].buffer1 = np->tx_addr[entry]; + if (skb->len < TX_BUFLIMIT) { + np->tx_ring[entry].length = DescWholePkt | skb->len; + } else { + int len = skb->len - TX_BUFLIMIT; + + np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT; + np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT; + } + if(entry == TX_RING_SIZE-1) + np->tx_ring[entry].length |= DescEndRing; + + /* Now acquire the irq spinlock. + * The difficult race is the the ordering between + * increasing np->cur_tx and setting DescOwn: + * - if np->cur_tx is increased first the interrupt + * handler could consider the packet as transmitted + * since DescOwn is cleared. + * - If DescOwn is set first the NIC could report the + * packet as sent, but the interrupt handler would ignore it + * since the np->cur_tx was not yet increased. + */ + spin_lock_irq(&np->lock); + np->cur_tx++; + + wmb(); /* flush length, buffer1, buffer2 */ + np->tx_ring[entry].status = DescOwn; + wmb(); /* flush status and kick the hardware */ + writel(0, dev->base_addr + TxStartDemand); + np->tx_q_bytes += skb->len; + /* Work around horrible bug in the chip by marking the queue as full + when we do not have FIFO room for a maximum sized packet. */ + if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN || + ((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) { + netif_stop_queue(dev); + wmb(); + np->tx_full = 1; + } + spin_unlock_irq(&np->lock); + + dev->trans_start = jiffies; + + if (debug > 4) { + printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", + dev->name, np->cur_tx, entry); + } + return 0; +} + +static void netdev_tx_done(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { + int entry = np->dirty_tx % TX_RING_SIZE; + int tx_status = np->tx_ring[entry].status; + + if (tx_status < 0) + break; + if (tx_status & 0x8000) { /* There was an error, log it. */ +#ifndef final_version + if (debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, tx_status); +#endif + np->stats.tx_errors++; + if (tx_status & 0x0104) np->stats.tx_aborted_errors++; + if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; + if (tx_status & 0x0200) np->stats.tx_window_errors++; + if (tx_status & 0x0002) np->stats.tx_fifo_errors++; + if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0) + np->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS + if (tx_status & 0x0100) np->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if (tx_status & 0x0001) np->stats.tx_deferred++; +#endif +#ifndef final_version + if (debug > 3) + printk(KERN_DEBUG "%s: Transmit slot %d ok, Tx status %8.8x.\n", + dev->name, entry, tx_status); +#endif + np->stats.tx_bytes += np->tx_skbuff[entry]->len; + np->stats.collisions += (tx_status >> 3) & 15; + np->stats.tx_packets++; + } + /* Free the original skb. */ + pci_unmap_single(np->pci_dev,np->tx_addr[entry], + np->tx_skbuff[entry]->len, + PCI_DMA_TODEVICE); + np->tx_q_bytes -= np->tx_skbuff[entry]->len; + dev_kfree_skb_irq(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = 0; + } + if (np->tx_full && + np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART && + np->tx_q_bytes < TX_BUG_FIFO_LIMIT) { + /* The ring is no longer full, clear tbusy. */ + np->tx_full = 0; + wmb(); + netif_wake_queue(dev); + } +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int work_limit = max_interrupt_work; + + if (!netif_device_present(dev)) + return; + do { + u32 intr_status = readl(ioaddr + IntrStatus); + + /* Acknowledge all of the current interrupt sources ASAP. */ + writel(intr_status & 0x001ffff, ioaddr + IntrStatus); + + if (debug > 4) + printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", + dev->name, intr_status); + + if ((intr_status & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (intr_status & (IntrRxDone | RxNoBuf)) + netdev_rx(dev); + if (intr_status & RxNoBuf) + writel(0, ioaddr + RxStartDemand); + + if (intr_status & (TxIdle | IntrTxDone) && + np->cur_tx != np->dirty_tx) { + spin_lock(&np->lock); + netdev_tx_done(dev); + spin_unlock(&np->lock); + } + + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr | + TimerInt | IntrTxStopped)) + netdev_error(dev, intr_status); + + if (--work_limit < 0) { + printk(KERN_WARNING "%s: Too much work at interrupt, " + "status=0x%4.4x.\n", dev->name, intr_status); + /* Set the timer to re-enable the other interrupts after + 10*82usec ticks. */ + spin_lock(&np->lock); + if (netif_device_present(dev)) { + writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable); + writel(10, ioaddr + GPTimer); + } + spin_unlock(&np->lock); + break; + } + } while (1); + + if (debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, (int)readl(ioaddr + IntrStatus)); +} + +/* This routine is logically part of the interrupt handler, but separated + for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int entry = np->cur_rx % RX_RING_SIZE; + int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + + if (debug > 4) { + printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", + entry, np->rx_ring[entry].status); + } + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while (--work_limit >= 0) { + struct w840_rx_desc *desc = np->rx_head_desc; + s32 status = desc->status; + + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", + status); + if (status < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, entry %#x status %4.4x!\n", + dev->name, np->cur_rx, status); + np->stats.rx_length_errors++; + } + } else if (status & 0x8000) { + /* There was a fatal error. */ + if (debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + np->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) np->stats.rx_length_errors++; + if (status & 0x004C) np->stats.rx_frame_errors++; + if (status & 0x0002) np->stats.rx_crc_errors++; + } + } else { + struct sk_buff *skb; + /* Omit the four octet CRC from the length. */ + int pkt_len = ((status >> 16) & 0x7ff) - 4; + +#ifndef final_version + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" + " status %x.\n", pkt_len, status); +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + /* Call copy + cksum if available. */ +#if HAS_IP_COPYSUM + eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, + pkt_len); +#endif + } else { + pci_unmap_single(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + skb_put(skb = np->rx_skbuff[entry], pkt_len); + np->rx_skbuff[entry] = NULL; + } +#ifndef final_version /* Remove after testing. */ + /* You will want this info for the initial debug. */ + if (debug > 5) + printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" + "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " + "%d.%d.%d.%d.\n", + skb->data[0], skb->data[1], skb->data[2], skb->data[3], + skb->data[4], skb->data[5], skb->data[6], skb->data[7], + skb->data[8], skb->data[9], skb->data[10], + skb->data[11], skb->data[12], skb->data[13], + skb->data[14], skb->data[15], skb->data[16], + skb->data[17]); +#endif + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += pkt_len; + } + entry = (++np->cur_rx) % RX_RING_SIZE; + np->rx_head_desc = &np->rx_ring[entry]; + } + + /* Refill the Rx ring buffers. */ + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { + struct sk_buff *skb; + entry = np->dirty_rx % RX_RING_SIZE; + if (np->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[entry] = skb; + if (skb == NULL) + break; /* Better luck next round. */ + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_addr[entry] = pci_map_single(np->pci_dev, + skb->tail, + skb->len, PCI_DMA_FROMDEVICE); + np->rx_ring[entry].buffer1 = np->rx_addr[entry]; + } + wmb(); + np->rx_ring[entry].status = DescOwn; + } + + return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + if (debug > 2) + printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n", + dev->name, intr_status); + if (intr_status == 0xffffffff) + return; + spin_lock(&np->lock); + if (intr_status & TxFIFOUnderflow) { + int new; + /* Bump up the Tx threshold */ +#if 0 + /* This causes lots of dropped packets, + * and under high load even tx_timeouts + */ + new = np->csr6 + 0x4000; +#else + new = (np->csr6 >> 14)&0x7f; + if (new < 64) + new *= 2; + else + new = 127; /* load full packet before starting */ + new = (np->csr6 & ~(0x7F << 14)) | (new<<14); +#endif + printk(KERN_DEBUG "%s: Tx underflow, new csr6 %8.8x.\n", + dev->name, new); + update_csr6(dev, new); + } + if (intr_status & IntrRxDied) { /* Missed a Rx frame. */ + np->stats.rx_errors++; + } + if (intr_status & TimerInt) { + /* Re-enable other interrupts. */ + if (netif_device_present(dev)) + writel(0x1A0F5, ioaddr + IntrEnable); + } + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + writel(0, ioaddr + RxStartDemand); + spin_unlock(&np->lock); +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + /* The chip only need report frame silently dropped. */ + spin_lock_irq(&np->lock); + if (netif_running(dev) && netif_device_present(dev)) + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + spin_unlock_irq(&np->lock); + + return &np->stats; +} + + +static u32 __set_rx_mode(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys + | AcceptMyPhys; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } else { + struct dev_mc_list *mclist; + int i; + memset(mc_filter, 0, sizeof(mc_filter)); + 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) ^ 0x3F, + mc_filter); + } + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + } + writel(mc_filter[0], ioaddr + MulticastFilter0); + writel(mc_filter[1], ioaddr + MulticastFilter1); + return rx_mode; +} + +static void set_rx_mode(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + u32 rx_mode = __set_rx_mode(dev); + spin_lock_irq(&np->lock); + update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode); + spin_unlock_irq(&np->lock); +} + +static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct netdev_private *np = dev->priv; + u32 ethcmd; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + strcpy(info.bus_info, np->pci_dev->slot_name); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii_if, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = mii_ethtool_sset(&np->mii_if, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&np->mii_if); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&np->mii_if); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + } + + return -EOPNOTSUPP; +} + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; + struct netdev_private *np = dev->priv; + + switch(cmd) { + case SIOCETHTOOL: + return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; + /* Fall Through */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + spin_lock_irq(&np->lock); + data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); + spin_unlock_irq(&np->lock); + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_irq(&np->lock); + mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); + spin_unlock_irq(&np->lock); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int netdev_close(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = dev->priv; + + netif_stop_queue(dev); + + if (debug > 1) { + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x " + "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), + (int)readl(ioaddr + NetworkConfig)); + printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", + dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); + } + + /* Stop the chip's Tx and Rx processes. */ + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + writel(0x0000, ioaddr + IntrEnable); + spin_unlock_irq(&np->lock); + + free_irq(dev->irq, dev); + wmb(); + netif_device_attach(dev); + + if (readl(ioaddr + NetworkConfig) != 0xffffffff) + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + +#ifdef __i386__ + if (debug > 2) { + int i; + + printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", + (int)np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" #%d desc. %4.4x %4.4x %8.8x.\n", + i, np->tx_ring[i].length, + np->tx_ring[i].status, np->tx_ring[i].buffer1); + printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", + (int)np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) { + printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", + i, np->rx_ring[i].length, + np->rx_ring[i].status, np->rx_ring[i].buffer1); + } + } +#endif /* __i386__ debugging only */ + + del_timer_sync(&np->timer); + + free_rxtx_rings(np); + free_ringdesc(np); + + return 0; +} + +static void __devexit w840_remove1 (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + if (dev) { + unregister_netdev(dev); + pci_release_regions(pdev); +#ifndef USE_IO_OPS + iounmap((char *)(dev->base_addr)); +#endif + kfree(dev); + } + + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM + +/* + * suspend/resume synchronization: + * - open, close, do_ioctl: + * rtnl_lock, & netif_device_detach after the rtnl_unlock. + * - get_stats: + * spin_lock_irq(np->lock), doesn't touch hw if not present + * - hard_start_xmit: + * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); + * - tx_timeout: + * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); + * - set_multicast_list + * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); + * - interrupt handler + * doesn't touch hw if not present, synchronize_irq waits for + * running instances of the interrupt handler. + * + * Disabling hw requires clearing csr6 & IntrEnable. + * update_csr6 & all function that write IntrEnable check netif_device_present + * before settings any bits. + * + * Detach must occur under spin_unlock_irq(), interrupts from a detached + * device would cause an irq storm. + */ +static int w840_suspend (struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + rtnl_lock(); + if (netif_running (dev)) { + del_timer_sync(&np->timer); + + spin_lock_irq(&np->lock); + netif_device_detach(dev); + update_csr6(dev, 0); + writel(0, ioaddr + IntrEnable); + netif_stop_queue(dev); + spin_unlock_irq(&np->lock); + + spin_unlock_wait(&dev->xmit_lock); + synchronize_irq(); + + np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + + /* no more hardware accesses behind this line. */ + + if (np->csr6) BUG(); + if (readl(ioaddr + IntrEnable)) BUG(); + + /* pci_power_off(pdev, -1); */ + + free_rxtx_rings(np); + } else { + netif_device_detach(dev); + } + rtnl_unlock(); + return 0; +} + + +static int w840_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct netdev_private *np = dev->priv; + + rtnl_lock(); + if (netif_device_present(dev)) + goto out; /* device not suspended */ + if (netif_running(dev)) { + pci_enable_device(pdev); + /* pci_power_on(pdev); */ + + spin_lock_irq(&np->lock); + writel(1, dev->base_addr+PCIBusCfg); + readl(dev->base_addr+PCIBusCfg); + udelay(1); + netif_device_attach(dev); + init_rxtx_rings(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + + netif_wake_queue(dev); + + np->timer.expires = jiffies + 1*HZ; + add_timer(&np->timer); + } else { + netif_device_attach(dev); + } +out: + rtnl_unlock(); + return 0; +} +#endif + +static struct pci_driver w840_driver = { + name: DRV_NAME, + id_table: w840_pci_tbl, + probe: w840_probe1, + remove: __devexit_p(w840_remove1), +#ifdef CONFIG_PM + suspend: w840_suspend, + resume: w840_resume, +#endif +}; + +static int __init w840_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_module_init(&w840_driver); +} + +static void __exit w840_exit(void) +{ + pci_unregister_driver(&w840_driver); +} + +module_init(w840_init); +module_exit(w840_exit); diff -urN linux-2.5.6-pre3/drivers/net/tulip/xircom_cb.c linux-2.5.6/drivers/net/tulip/xircom_cb.c --- linux-2.5.6-pre3/drivers/net/tulip/xircom_cb.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/xircom_cb.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,1245 @@ +/* + * xircom_cb: A driver for the (tulip-like) Xircom Cardbus ethernet cards + * + * This software is (C) by the respective authors, and licensed under the GPL + * License. + * + * Written by Arjan van de Ven for Red Hat, Inc. + * Based on work by Jeff Garzik, Doug Ledford and Donald Becker + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * + * $Id: xircom_cb.c,v 1.33 2001/03/19 14:02:07 arjanv Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#ifdef DEBUG +#define enter(x) printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__) +#define leave(x) printk("Leave: %s, %s line %i\n",x,__FILE__,__LINE__) +#else +#define enter(x) do {} while (0) +#define leave(x) do {} while (0) +#endif + + +MODULE_DESCRIPTION("Xircom Cardbus ethernet driver"); +MODULE_AUTHOR("Arjan van de Ven "); +MODULE_LICENSE("GPL"); + + + +/* IO registers on the card, offsets */ +#define CSR0 0x00 +#define CSR1 0x08 +#define CSR2 0x10 +#define CSR3 0x18 +#define CSR4 0x20 +#define CSR5 0x28 +#define CSR6 0x30 +#define CSR7 0x38 +#define CSR8 0x40 +#define CSR9 0x48 +#define CSR10 0x50 +#define CSR11 0x58 +#define CSR12 0x60 +#define CSR13 0x68 +#define CSR14 0x70 +#define CSR15 0x78 +#define CSR16 0x80 + +/* PCI registers */ +#define PCI_POWERMGMT 0x40 + +/* Offsets of the buffers within the descriptor pages, in bytes */ + +#define NUMDESCRIPTORS 4 + +static int bufferoffsets[NUMDESCRIPTORS] = {128,2048,4096,6144}; + + +struct xircom_private { + /* Send and receive buffers, kernel-addressable and dma addressable forms */ + + unsigned int *rx_buffer; + unsigned int *tx_buffer; + + dma_addr_t rx_dma_handle; + dma_addr_t tx_dma_handle; + + struct sk_buff *tx_skb[4]; + + unsigned long io_port; + int open; + + /* transmit_used is the rotating counter that indicates which transmit + descriptor has to be used next */ + int transmit_used; + + /* Spinlock to serialize register operations. + It must be helt while manipulating the following registers: + CSR0, CSR6, CSR7, CSR9, CSR10, CSR15 + */ + spinlock_t lock; + + + struct pci_dev *pdev; + struct net_device *dev; + struct net_device_stats stats; +}; + + +/* Function prototypes */ +static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void xircom_remove(struct pci_dev *pdev); +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int xircom_open(struct net_device *dev); +static int xircom_close(struct net_device *dev); +static void xircom_up(struct xircom_private *card); +static struct net_device_stats *xircom_get_stats(struct net_device *dev); + +static void investigate_read_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset); +static void investigate_write_descriptor(struct net_device *dev, struct xircom_private *card, int descnr, unsigned int bufferoffset); +static void read_mac_address(struct xircom_private *card); +static void tranceiver_voodoo(struct xircom_private *card); +static void initialize_card(struct xircom_private *card); +static void trigger_transmit(struct xircom_private *card); +static void trigger_receive(struct xircom_private *card); +static void setup_descriptors(struct xircom_private *card); +static void remove_descriptors(struct xircom_private *card); +static int link_status_changed(struct xircom_private *card); +static void activate_receiver(struct xircom_private *card); +static void deactivate_receiver(struct xircom_private *card); +static void activate_transmitter(struct xircom_private *card); +static void deactivate_transmitter(struct xircom_private *card); +static void enable_transmit_interrupt(struct xircom_private *card); +static void enable_receive_interrupt(struct xircom_private *card); +static void enable_link_interrupt(struct xircom_private *card); +static void disable_all_interrupts(struct xircom_private *card); +static int link_status(struct xircom_private *card); + + + +static struct pci_device_id xircom_pci_table[] __devinitdata = { + {0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; +MODULE_DEVICE_TABLE(pci, xircom_pci_table); + +static struct pci_driver xircom_ops = { + name: "xircom_cb", + id_table: xircom_pci_table, + probe: xircom_probe, + remove: xircom_remove, + suspend:NULL, + resume:NULL +}; + + +#ifdef DEBUG +static void print_binary(unsigned int number) +{ + int i,i2; + char buffer[64]; + memset(buffer,0,64); + i2=0; + for (i=31;i>=0;i--) { + if (number & (1<rx_buffer = pci_alloc_consistent(pdev,8192,&private->rx_dma_handle); + + if (private->rx_buffer == NULL) { + printk(KERN_ERR "xircom_probe: no memory for rx buffer \n"); + kfree(private); + return -ENODEV; + } + private->tx_buffer = pci_alloc_consistent(pdev,8192,&private->tx_dma_handle); + if (private->tx_buffer == NULL) { + printk(KERN_ERR "xircom_probe: no memory for tx buffer \n"); + kfree(private->rx_buffer); + kfree(private); + return -ENODEV; + } + dev = init_etherdev(dev, 0); + if (dev == NULL) { + printk(KERN_ERR "xircom_probe: failed to allocate etherdev\n"); + kfree(private->rx_buffer); + kfree(private->tx_buffer); + kfree(private); + return -ENODEV; + } + SET_MODULE_OWNER(dev); + printk(KERN_INFO "%s: Xircom cardbus revision %i at irq %i \n", dev->name, chip_rev, pdev->irq); + + private->dev = dev; + private->pdev = pdev; + private->io_port = pci_resource_start(pdev, 0); + private->lock = SPIN_LOCK_UNLOCKED; + dev->irq = pdev->irq; + dev->base_addr = private->io_port; + + + initialize_card(private); + read_mac_address(private); + setup_descriptors(private); + + dev->open = &xircom_open; + dev->hard_start_xmit = &xircom_start_xmit; + dev->stop = &xircom_close; + dev->get_stats = &xircom_get_stats; + dev->priv = private; + pdev->driver_data = dev; + + + /* start the transmitter to get a heartbeat */ + /* TODO: send 2 dummy packets here */ + tranceiver_voodoo(private); + + spin_lock_irqsave(&private->lock,flags); + activate_transmitter(private); + activate_receiver(private); + spin_unlock_irqrestore(&private->lock,flags); + + trigger_receive(private); + + leave("xircom_probe"); + return 0; +} + + +/* + xircom_remove is called on module-unload or on device-eject. + it unregisters the irq, io-region and network device. + Interrupts and such are already stopped in the "ifconfig ethX down" + code. + */ +static void __devexit xircom_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct xircom_private *card; + enter("xircom_remove"); + if (dev!=NULL) { + card=dev->priv; + if (card!=NULL) { + if (card->rx_buffer!=NULL) + pci_free_consistent(pdev,8192,card->rx_buffer,card->rx_dma_handle); + card->rx_buffer = NULL; + if (card->tx_buffer!=NULL) + pci_free_consistent(pdev,8192,card->tx_buffer,card->tx_dma_handle); + card->tx_buffer = NULL; + } + kfree(card); + } + release_region(dev->base_addr, 128); + unregister_netdev(dev); + kfree(dev); + leave("xircom_remove"); +} + +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct xircom_private *card = (struct xircom_private *) dev->priv; + unsigned int status; + int i; + + enter("xircom_interrupt\n"); + + spin_lock(&card->lock); + status = inl(card->io_port+CSR5); + +#if DEBUG + print_binary(status); + printk("tx status 0x%08x 0x%08x \n",card->tx_buffer[0],card->tx_buffer[4]); + printk("rx status 0x%08x 0x%08x \n",card->rx_buffer[0],card->rx_buffer[4]); +#endif + + if (link_status_changed(card)) { + int newlink; + printk(KERN_DEBUG "xircom_cb: Link status has changed \n"); + newlink = link_status(card); + printk(KERN_INFO "xircom_cb: Link is %i mbit \n",newlink); + if (newlink) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + + } + + /* Clear all remaining interrupts */ + status |= 0xffffffff; /* FIXME: make this clear only the + real existing bits */ + outl(status,card->io_port+CSR5); + + + for (i=0;ilock); + leave("xircom_interrupt"); +} + +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + int nextdescriptor; + int desc; + enter("xircom_start_xmit"); + + card = (struct xircom_private*)dev->priv; + spin_lock_irqsave(&card->lock,flags); + + /* First see if we can free some descriptors */ + for (desc=0;desctransmit_used +1) % (NUMDESCRIPTORS); + desc = card->transmit_used; + + /* only send the packet if the descriptor is free */ + if (card->tx_buffer[4*desc]==0) { + /* Copy the packet data; zero the memory first as the card + sometimes sends more than you ask it to. */ + + memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536); + memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len); + + + /* FIXME: The specification tells us that the length we send HAS to be a multiple of + 4 bytes. */ + + card->tx_buffer[4*desc+1] = skb->len; + if (desc == NUMDESCRIPTORS-1) + card->tx_buffer[4*desc+1] |= (1<<25); /* bit 25: last descriptor of the ring */ + + card->tx_buffer[4*desc+1] |= 0xF0000000; + /* 0xF0... means want interrupts*/ + card->tx_skb[desc] = skb; + + wmb(); + /* This gives the descriptor to the card */ + card->tx_buffer[4*desc] = 0x80000000; + trigger_transmit(card); + if (((int)card->tx_buffer[nextdescriptor*4])<0) { /* next descriptor is occupied... */ + netif_stop_queue(dev); + } + card->transmit_used = nextdescriptor; + leave("xircom-start_xmit - sent"); + spin_unlock_irqrestore(&card->lock,flags); + return 0; + } + + + + /* Uh oh... no free descriptor... drop the packet */ + netif_stop_queue(dev); + spin_unlock_irqrestore(&card->lock,flags); + trigger_transmit(card); + + return -EIO; +} + + + + +static int xircom_open(struct net_device *dev) +{ + struct xircom_private *xp = (struct xircom_private *) dev->priv; + int retval; + enter("xircom_open"); + printk(KERN_INFO "xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq); + retval = request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) { + leave("xircom_open - No IRQ"); + return retval; + } + + xircom_up(xp); + xp->open = 1; + leave("xircom_open"); + return 0; +} + +static int xircom_close(struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + + enter("xircom_close"); + card = dev->priv; + netif_stop_queue(dev); /* we don't want new packets */ + + + spin_lock_irqsave(&card->lock,flags); + + disable_all_interrupts(card); +#if 0 + /* We can enable this again once we send dummy packets on ifconfig ethX up */ + deactivate_receiver(card); + deactivate_transmitter(card); +#endif + remove_descriptors(card); + + spin_unlock_irqrestore(&card->lock,flags); + + card->open = 0; + free_irq(dev->irq,dev); + + leave("xircom_close"); + + return 0; + +} + + + +static struct net_device_stats *xircom_get_stats(struct net_device *dev) +{ + struct xircom_private *card = (struct xircom_private *)dev->priv; + return &card->stats; +} + + + + +static void initialize_card(struct xircom_private *card) +{ + unsigned int val; + unsigned long flags; + enter("initialize_card"); + + + spin_lock_irqsave(&card->lock, flags); + + /* First: reset the card */ + val = inl(card->io_port + CSR0); + val |= 0x01; /* Software reset */ + outl(val, card->io_port + CSR0); + + udelay(100); /* give the card some time to reset */ + + val = inl(card->io_port + CSR0); + val &= ~0x01; /* disable Software reset */ + outl(val, card->io_port + CSR0); + + + val = 0; /* Value 0x00 is a safe and conservative value + for the PCI configuration settings */ + outl(val, card->io_port + CSR0); + + + disable_all_interrupts(card); + deactivate_receiver(card); + deactivate_transmitter(card); + + spin_unlock_irqrestore(&card->lock, flags); + + leave("initialize_card"); +} + +/* +trigger_transmit causes the card to check for frames to be transmitted. +This is accomplished by writing to the CSR1 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static void trigger_transmit(struct xircom_private *card) +{ + unsigned int val; + enter("trigger_transmit"); + + val = 0; + outl(val, card->io_port + CSR1); + + leave("trigger_transmit"); +} + +/* +trigger_receive causes the card to check for empty frames in the +descriptor list in which packets can be received. +This is accomplished by writing to the CSR2 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static void trigger_receive(struct xircom_private *card) +{ + unsigned int val; + enter("trigger_receive"); + + val = 0; + outl(val, card->io_port + CSR2); + + leave("trigger_receive"); +} + +/* +setup_descriptors initializes the send and receive buffers to be valid +descriptors and programs the addresses into the card. +*/ +static void setup_descriptors(struct xircom_private *card) +{ + unsigned int val; + unsigned int address; + int i; + enter("setup_descriptors"); + + + if (card->rx_buffer == NULL) + BUG(); + if (card->tx_buffer == NULL) + BUG(); + + /* Receive descriptors */ + memset(card->rx_buffer, 0, 128); /* clear the descriptors */ + for (i=0;i 0x80000000 */ + card->rx_buffer[i*4 + 0] = 0x80000000; + /* Rx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->rx_buffer[i*4 + 1] = 1536; + if (i==NUMDESCRIPTORS-1) + card->rx_buffer[i*4 + 1] |= (1 << 25); /* bit 25 is "last descriptor" */ + + /* Rx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + + address = (unsigned long) card->rx_dma_handle; + card->rx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Rx Desc3: address of 2nd buffer -> 0 */ + card->rx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* Write the receive descriptor ring address to the card */ + address = (unsigned long) card->rx_dma_handle; + val = cpu_to_le32(address); + outl(val, card->io_port + CSR3); /* Receive descr list address */ + + + /* transmit descriptors */ + memset(card->tx_buffer, 0, 128); /* clear the descriptors */ + + for (i=0;i 0x00000000 */ + card->tx_buffer[i*4 + 0] = 0x00000000; + /* Tx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->tx_buffer[i*4 + 1] = 1536; + if (i==NUMDESCRIPTORS-1) + card->tx_buffer[i*4 + 1] |= (1 << 25); /* bit 25 is "last descriptor" */ + + /* Tx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + address = (unsigned long) card->tx_dma_handle; + card->tx_buffer[i*4 + 2] = cpu_to_le32(address + bufferoffsets[i]); + /* Tx Desc3: address of 2nd buffer -> 0 */ + card->tx_buffer[i*4 + 3] = 0; + } + + wmb(); + /* wite the transmit descriptor ring to the card */ + address = (unsigned long) card->tx_dma_handle; + val =cpu_to_le32(address); + outl(val, card->io_port + CSR4); /* xmit descr list address */ + + leave("setup_descriptors"); +} + +/* +remove_descriptors informs the card the descriptors are no longer +valid by setting the address in the card to 0x00. +*/ +static void remove_descriptors(struct xircom_private *card) +{ + unsigned int val; + enter("remove_descriptors"); + + val = 0; + outl(val, card->io_port + CSR3); /* Receive descriptor address */ + outl(val, card->io_port + CSR4); /* Send descriptor address */ + + leave("remove_descriptors"); +} + +/* +link_status_changed returns 1 if the card has indicated that +the link status has changed. The new link status has to be read from CSR12. + +This function also clears the status-bit. +*/ +static int link_status_changed(struct xircom_private *card) +{ + unsigned int val; + enter("link_status_changed"); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (1 << 27)) == 0) { /* no change */ + leave("link_status_changed - nochange"); + return 0; + } + + /* clear the event by writing a 1 to the bit in the + status register. */ + val = (1 << 27); + outl(val, card->io_port + CSR5); + + leave("link_status_changed - changed"); + return 1; +} + + +/* +transmit_active returns 1 if the transmitter on the card is +in a non-stopped state. +*/ +static int transmit_active(struct xircom_private *card) +{ + unsigned int val; + enter("transmit_active"); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 20)) == 0) { /* transmitter disabled */ + leave("transmit_active - inactive"); + return 0; + } + + leave("transmit_active - active"); + return 1; +} + +/* +receive_active returns 1 if the receiver on the card is +in a non-stopped state. +*/ +static int receive_active(struct xircom_private *card) +{ + unsigned int val; + enter("receive_active"); + + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 17)) == 0) { /* receiver disabled */ + leave("receive_active - inactive"); + return 0; + } + + leave("receive_active - active"); + return 1; +} + +/* +activate_receiver enables the receiver on the card. +Before being allowed to active the receiver, the receiver +must be completely de-activated. To achieve this, +this code actually disables the receiver first; then it waits for the +receiver to become inactive, then it activates the receiver and then +it waits for the receiver to be active. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter("activate_receiver"); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&2) && (receive_active(card))) + return; + + + val = val & ~2; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); + } + + /* enable the receiver */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | 2; /* enable the receiver */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to re-activate\n"); + } + + leave("activate_receiver"); +} + +/* +deactivate_receiver disables the receiver on the card. +To achieve this this code disables the receiver first; +then it waits for the receiver to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter("deactivate_receiver"); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); + } + + + leave("deactivate_receiver"); +} + + +/* +activate_transmitter enables the transmitter on the card. +Before being allowed to active the transmitter, the transmitter +must be completely de-activated. To achieve this, +this code actually disables the transmitter first; then it waits for the +transmitter to become inactive, then it activates the transmitter and then +it waits for the transmitter to be active again. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter("activate_transmitter"); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit is set and the receiver is already + active, no need to do the expensive thing */ + if ((val&(1<<13)) && (transmit_active(card))) + return; + + val = val & ~(1 << 13); /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); + } + + /* enable the transmitter */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | (1 << 13); /* enable the transmitter */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to re-activate\n"); + } + + leave("activate_transmitter"); +} + +/* +deactivate_transmitter disables the transmitter on the card. +To achieve this this code disables the transmitter first; +then it waits for the transmitter to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter("deactivate_transmitter"); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~2; /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 20; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); + } + + + leave("deactivate_transmitter"); +} + + +/* +enable_transmit_interrupt enables the transmit interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_transmit_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter("enable_transmit_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= 1; /* enable the transmit interrupt */ + outl(val, card->io_port + CSR7); + + leave("enable_transmit_interrupt"); +} + + +/* +enable_receive_interrupt enables the receive interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_receive_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter("enable_receive_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 6); /* enable the receive interrupt */ + outl(val, card->io_port + CSR7); + + leave("enable_receive_interrupt"); +} + +/* +enable_link_interrupt enables the link status change interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_link_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter("enable_link_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 27); /* enable the link status chage interrupt */ + outl(val, card->io_port + CSR7); + + leave("enable_link_interrupt"); +} + + + +/* +disable_all_interrupts disables all interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void disable_all_interrupts(struct xircom_private *card) +{ + unsigned int val; + enter("enable_all_interrupts"); + + val = 0; /* disable all interrupts */ + outl(val, card->io_port + CSR7); + + leave("disable_all_interrupts"); +} + +/* +enable_common_interrupts enables several weird interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void enable_common_interrupts(struct xircom_private *card) +{ + unsigned int val; + enter("enable_link_interrupt"); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= (1<<16); /* Normal Interrupt Summary */ + val |= (1<<15); /* Abnormal Interrupt Summary */ + val |= (1<<13); /* Fatal bus error */ + val |= (1<<8); /* Receive Process Stopped */ + val |= (1<<7); /* Receive Buffer Unavailable */ + val |= (1<<5); /* Transmit Underflow */ + val |= (1<<2); /* Transmit Buffer Unavailable */ + val |= (1<<1); /* Transmit Process Stopped */ + outl(val, card->io_port + CSR7); + + leave("enable_link_interrupt"); +} + +/* +enable_promisc starts promisc mode + +must be called with the lock held and interrupts disabled. +*/ +static int enable_promisc(struct xircom_private *card) +{ + unsigned int val; + enter("enable_promisc"); + + val = inl(card->io_port + CSR6); + val = val | (1 << 6); + outl(val, card->io_port + CSR6); + + leave("enable_promisc"); + return 1; +} + + + + +/* +link_status() checks the the links status and will return 0 for no link, 10 for 10mbit link and 100 for.. guess what. + +Must be called in locked state with interrupts disabled +*/ +static int link_status(struct xircom_private *card) +{ + unsigned int val; + enter("link_status"); + + val = inb(card->io_port + CSR12); + + if (!(val&(1<<2))) /* bit 2 is 0 for 10mbit link, 1 for not an 10mbit link */ + return 10; + if (!(val&(1<<1))) /* bit 1 is 0 for 100mbit link, 1 for not an 100mbit link */ + return 100; + + /* If we get here -> no link at all */ + + leave("link_status"); + return 0; +} + + + + + +/* + read_mac_address() reads the MAC address from the NIC and stores it in the "dev" structure. + + This function will take the spinlock itself and can, as a result, not be called with the lock helt. + */ +static void read_mac_address(struct xircom_private *card) +{ + unsigned char j, tuple, link, data_id, data_count; + unsigned long flags; + int i; + + enter("read_mac_address"); + + spin_lock_irqsave(&card->lock, flags); + + outl(1 << 12, card->io_port + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link + 2) { + outl(i, card->io_port + CSR10); + tuple = inl(card->io_port + CSR9) & 0xff; + outl(i + 1, card->io_port + CSR10); + link = inl(card->io_port + CSR9) & 0xff; + outl(i + 2, card->io_port + CSR10); + data_id = inl(card->io_port + CSR9) & 0xff; + outl(i + 3, card->io_port + CSR10); + data_count = inl(card->io_port + CSR9) & 0xff; + if ((tuple == 0x22) && (data_id == 0x04) && (data_count == 0x06)) { + /* + * This is it. We have the data we want. + */ + for (j = 0; j < 6; j++) { + outl(i + j + 4, card->io_port + CSR10); + card->dev->dev_addr[j] = inl(card->io_port + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); +#ifdef DEBUG + for (i = 0; i < 6; i++) + printk("%c%2.2X", i ? ':' : ' ', card->dev->dev_addr[i]); + printk("\n"); +#endif + leave("read_mac_address"); +} + + +/* + tranceiver_voodoo() enables the external UTP plug thingy. + it's called voodoo as I stole this code and cannot cross-reference + it with the specification. + */ +static void tranceiver_voodoo(struct xircom_private *card) +{ + unsigned long flags; + + enter("tranceiver_voodoo"); + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + outl(0x0008, card->io_port + CSR15); + udelay(25); + outl(0xa8050000, card->io_port + CSR15); + udelay(25); + outl(0xa00f0000, card->io_port + CSR15); + udelay(25); + + spin_unlock_irqrestore(&card->lock, flags); + + netif_start_queue(card->dev); + leave("tranceiver_voodoo"); +} + + +static void xircom_up(struct xircom_private *card) +{ + unsigned long flags; + int i; + + enter("xircom_up"); + + /* disable all powermanagement */ + pci_write_config_dword(card->pdev, PCI_POWERMGMT, 0x0000); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + + enable_link_interrupt(card); + enable_transmit_interrupt(card); + enable_receive_interrupt(card); + enable_common_interrupts(card); + enable_promisc(card); + + /* The card can have received packets already, read them away now */ + for (i=0;idev,card,i,bufferoffsets[i]); + + + spin_unlock_irqrestore(&card->lock, flags); + trigger_receive(card); + trigger_transmit(card); + netif_start_queue(card->dev); + leave("xircom_up"); +} + +/* Bufferoffset is in BYTES */ +static void investigate_read_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset) +{ + int status; + + enter("investigate_read_descriptor"); + status = card->rx_buffer[4*descnr]; + + if ((status > 0)) { /* packet received */ + + /* TODO: discard error packets */ + + short pkt_len = ((status >> 16) & 0x7ff) - 4; /* minus 4, we don't want the CRC */ + struct sk_buff *skb; + + if (pkt_len > 1518) { + printk(KERN_ERR "xircom_cb: Packet length %i is bogus \n",pkt_len); + pkt_len = 1518; + } + + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + card->stats.rx_dropped++; + goto out; + } + skb->dev = dev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, (unsigned char*)&card->rx_buffer[bufferoffset / 4], pkt_len, 0); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + card->stats.rx_packets++; + card->stats.rx_bytes += pkt_len; + + out: + /* give the buffer back to the card */ + card->rx_buffer[4*descnr] = 0x80000000; + trigger_receive(card); + } + + leave("investigate_read_descriptor"); + +} + + +/* Bufferoffset is in BYTES */ +static void investigate_write_descriptor(struct net_device *dev, struct xircom_private *card, int descnr, unsigned int bufferoffset) +{ + int status; + + enter("investigate_write_descriptor"); + + status = card->tx_buffer[4*descnr]; +#if 0 + if (status & 0x8000) { /* Major error */ + printk(KERN_ERR "Major transmit error status %x \n", status); + card->tx_buffer[4*descnr] = 0; + netif_wake_queue (dev); + } +#endif + if (status > 0) { /* bit 31 is 0 when done */ + if (card->tx_skb[descnr]!=NULL) { + card->stats.tx_bytes += card->tx_skb[descnr]->len; + dev_kfree_skb_irq(card->tx_skb[descnr]); + } + card->tx_skb[descnr] = NULL; + /* Bit 8 in the status field is 1 if there was a collision */ + if (status&(1<<8)) + card->stats.collisions++; + card->tx_buffer[4*descnr] = 0; /* descriptor is free again */ + netif_wake_queue (dev); + card->stats.tx_packets++; + } + + leave("investigate_write_descriptor"); + +} + + +static int __init xircom_init(void) +{ + pci_register_driver(&xircom_ops); + return 0; +} + +static void __exit xircom_exit(void) +{ + pci_unregister_driver(&xircom_ops); +} + +module_init(xircom_init) +module_exit(xircom_exit) + diff -urN linux-2.5.6-pre3/drivers/net/tulip/xircom_tulip_cb.c linux-2.5.6/drivers/net/tulip/xircom_tulip_cb.c --- linux-2.5.6-pre3/drivers/net/tulip/xircom_tulip_cb.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/tulip/xircom_tulip_cb.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,1744 @@ +/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. */ +/* + Written/copyright 1994-1999 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + ----------------------------------------------------------- + + Linux kernel-specific changes: + + LK1.0 (Ion Badulescu) + - Major cleanup + - Use 2.4 PCI API + - Support ethtool + - Rewrite perfect filter/hash code + - Use interrupts for media changes + + LK1.1 (Ion Badulescu) + - Disallow negotiation of unsupported full-duplex modes +*/ + +#define DRV_NAME "xircom_tulip_cb" +#define DRV_VERSION "0.91+LK1.1" +#define DRV_RELDATE "October 11, 2001" + +#define CARDBUS 1 + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 4 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS]; +static int options[MAX_UNITS]; +static int mtu[MAX_UNITS]; /* Jumbo MTU for interfaces. */ + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__powerpc__) +static int csr0 = 0x01B00000 | 0x8000; +#elif defined(__sparc__) +static int csr0 = 0x01B00080 | 0x8000; +#elif defined(__i386__) +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4 * HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define PKT_SETUP_SZ 192 /* Size of the setup frame */ + +/* PCI registers */ +#define PCI_POWERMGMT 0x40 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* Processor type for cache alignment. */ +#include + + +/* These identify the driver base version and may not be removed. */ +static char version[] __devinitdata = +KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n" +KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n"; + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver"); +MODULE_LICENSE("GPL v2"); + +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +#define RUN_AT(x) (jiffies + (x)) + +#define xircom_debug debug +#ifdef XIRCOM_DEBUG +static int xircom_debug = XIRCOM_DEBUG; +#else +static int xircom_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver was forked from the driver for the DECchip "Tulip", +Digital's single-chip ethernet controllers for PCI. It supports Xircom's +almost-Tulip-compatible CBE-100 CardBus adapters. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. + +III. Driver operation + +IIIa. Ring buffers + +The Xircom can use either ring buffers or lists of Tx and Rx descriptors. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Xircom as receive data buffers. When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Xircom only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'tp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840A.html + +IVc. Errata + +*/ + +/* A full-duplex map for media types. */ +enum MediaIs { + MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum xircom_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + LinkChange=0x08000000, + NormalIntr=0x10000, NormalIntrMask=0x00014045, + AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2, + ReservedIntrMask=0xe0001a18, + EarlyRxIntr=0x4000, BusErrorIntr=0x2000, + EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +enum csr0_control_bits { + EnableMWI=0x01000000, EnableMRL=0x00800000, + EnableMRM=0x00200000, EqualBusPrio=0x02, + SoftwareReset=0x01, +}; + +enum csr6_control_bits { + ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40, + HashFilterBit=0x01, FullDuplexBit=0x0200, + TxThresh10=0x400000, TxStoreForw=0x200000, + TxThreshMask=0xc000, TxThreshShift=14, + EnableTx=0x2000, EnableRx=0x02, + ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000, + EnableTxRx=(EnableTx | EnableRx), +}; + + +enum tbl_flag { + HAS_MII=1, HAS_ACPI=2, +}; +static struct xircom_chip_table { + char *chip_name; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; +} xircom_tbl[] = { + { "Xircom Cardbus Adapter", + LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr | + RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr, + HAS_MII | HAS_ACPI, }, + { NULL, }, +}; +/* This matches the table above. */ +enum chips { + X3201_3, +}; + + +/* The Xircom Rx and Tx buffer descriptors. */ +struct xircom_rx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +struct xircom_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +enum tx_desc0_status_bits { + Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800, + Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02, +}; +enum tx_desc1_status_bits { + Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000, + Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000, + Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000, + Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg), +}; +enum rx_desc0_status_bits { + Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000, + Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100, + Rx0HugeFrame=0x80, Rx0CRCError=0x02, + Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg), +}; +enum rx_desc1_status_bits { + Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000, +}; + +struct xircom_private { + struct xircom_rx_desc rx_ring[RX_RING_SIZE]; + struct xircom_tx_desc tx_ring[TX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; +#ifdef CARDBUS + /* The X3201-3 requires 4-byte aligned tx bufs */ + struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; +#endif + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + struct net_device_stats stats; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int speed100:1; + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int autoneg:1; + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int open:1; + unsigned int csr0; /* CSR0 setting. */ + unsigned int csr6; /* Current CSR6 control settings. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ + int saved_if_port; + struct pci_dev *pdev; + spinlock_t lock; +}; + +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static void xircom_up(struct net_device *dev); +static void xircom_down(struct net_device *dev); +static int xircom_open(struct net_device *dev); +static void xircom_tx_timeout(struct net_device *dev); +static void xircom_init_ring(struct net_device *dev); +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int xircom_rx(struct net_device *dev); +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int xircom_close(struct net_device *dev); +static struct net_device_stats *xircom_get_stats(struct net_device *dev); +static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +static void check_duplex(struct net_device *dev); + + +/* The Xircom cards are picky about when certain bits in CSR6 can be + manipulated. Keith Owens . */ +static void outl_CSR6(u32 newcsr6, long ioaddr) +{ + const int strict_bits = + TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit; + int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; + long flags; + save_flags(flags); + cli(); + /* mask out the reserved bits that always read 0 on the Xircom cards */ + newcsr6 &= ~ReservedZeroMask; + /* or in the reserved bits that always read 1 */ + newcsr6 |= ReservedOneMask; + currcsr6 = inl(ioaddr + CSR6); + if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || + ((currcsr6 & ~EnableTxRx) == 0)) { + outl(newcsr6, ioaddr + CSR6); /* safe */ + restore_flags(flags); + return; + } + /* make sure the transmitter and receiver are stopped first */ + currcsr6 &= ~EnableTxRx; + while (1) { + csr5 = inl(ioaddr + CSR5); + if (csr5 == 0xffffffff) + break; /* cannot read csr5, card removed? */ + csr5_22_20 = csr5 & 0x700000; + csr5_19_17 = csr5 & 0x0e0000; + if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && + (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) + break; /* both are stopped or suspended */ + if (!--attempts) { + printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts," + "csr5=0x%08x\n", csr5); + outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ + restore_flags(flags); + return; + } + outl(currcsr6, ioaddr + CSR6); + udelay(1); + } + /* now it is safe to change csr6 */ + outl(newcsr6, ioaddr + CSR6); + restore_flags(flags); +} + + +static void __devinit read_mac_address(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + int i, j; + unsigned char tuple, link, data_id, data_count; + + /* Xircom has its address stored in the CIS; + * we access it through the boot rom interface for now + * this might not work, as the CIS is not parsed but I + * (danilo) use the offset I found on my card's CIS !!! + * + * Doug Ledford: I changed this routine around so that it + * walks the CIS memory space, parsing the config items, and + * finds the proper lan_node_id tuple and uses the data + * stored there. + */ + outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link+2) { + outl(i, ioaddr + CSR10); + tuple = inl(ioaddr + CSR9) & 0xff; + outl(i + 1, ioaddr + CSR10); + link = inl(ioaddr + CSR9) & 0xff; + outl(i + 2, ioaddr + CSR10); + data_id = inl(ioaddr + CSR9) & 0xff; + outl(i + 3, ioaddr + CSR10); + data_count = inl(ioaddr + CSR9) & 0xff; + if ( (tuple == 0x22) && + (data_id == 0x04) && (data_count == 0x06) ) { + /* + * This is it. We have the data we want. + */ + for (j = 0; j < 6; j++) { + outl(i + j + 4, ioaddr + CSR10); + dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } +} + + +/* + * locate the MII interfaces and initialize them. + * we disable full-duplex modes here, + * because we don't know how to handle them. + */ +static void find_mii_transceivers(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int phy, phy_idx; + + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = + /*ADVERTISE_100BASE4 | ADVERTISE_100FULL |*/ ADVERTISE_100HALF | + /*ADVERTISE_10FULL |*/ ADVERTISE_10HALF | ADVERTISE_CSMA; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, MII_BMSR); + if ((mii_status & (BMSR_100BASE4 | BMSR_100HALF | BMSR_10HALF)) == BMSR_100BASE4 || + ((mii_status & BMSR_100BASE4) == 0 && + (mii_status & (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF)) != 0)) { + int mii_reg0 = mdio_read(dev, phy, MII_BMCR); + int mii_advert = mdio_read(dev, phy, MII_ADVERTISE); + int reg4 = ((mii_status >> 6) & tp->to_advertise) | ADVERTISE_CSMA; + tp->phys[phy_idx] = phy; + tp->advertising[phy_idx++] = reg4; + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + } + } + tp->mii_cnt = phy_idx; + if (phy_idx == 0) { + printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + tp->phys[0] = 0; + } +} + + +/* + * To quote Arjan van de Ven: + * tranceiver_voodoo() enables the external UTP plug thingy. + * it's called voodoo as I stole this code and cannot cross-reference + * it with the specification. + * Actually it seems to go like this: + * - GPIO2 enables the MII itself so we can talk to it. The MII gets reset + * so any prior MII settings are lost. + * - GPIO0 enables the TP port so the MII can talk to the network. + * - a software reset will reset both GPIO pins. + * I also moved the software reset here, because doing it in xircom_up() + * required enabling the GPIO pins each time, which reset the MII each time. + * Thus we couldn't control the MII -- which sucks because we don't know + * how to handle full-duplex modes so we *must* disable them. + */ +static void transceiver_voodoo(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(SoftwareReset, ioaddr + CSR0); + udelay(2); + + /* Deassert reset. */ + outl(tp->csr0, ioaddr + CSR0); + + /* Reset the xcvr interface and turn on heartbeat. */ + outl(0x0008, ioaddr + CSR15); + udelay(5); /* The delays are Xircom-recommended to give the + * chipset time to reset the actual hardware + * on the PCMCIA card + */ + outl(0xa8050000, ioaddr + CSR15); + udelay(5); + outl(0xa00f0000, ioaddr + CSR15); + udelay(5); + + outl_CSR6(0, ioaddr); + //outl_CSR6(FullDuplexBit, ioaddr); +} + + +static int __devinit xircom_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *dev; + struct xircom_private *tp; + static int board_idx = -1; + int chip_idx = id->driver_data; + long ioaddr; + int i; + u8 chip_rev; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + //printk(KERN_INFO "xircom_init_one(%s)\n", pdev->slot_name); + + board_idx++; + + if (pci_enable_device(pdev)) + return -ENODEV; + + pci_set_master(pdev); + + ioaddr = pci_resource_start(pdev, 0); + dev = alloc_etherdev(sizeof(*tp)); + if (!dev) { + printk (KERN_ERR DRV_NAME "%d: cannot alloc etherdev, aborting\n", board_idx); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + + dev->base_addr = ioaddr; + dev->irq = pdev->irq; + + if (pci_request_regions(pdev, dev->name)) { + printk (KERN_ERR DRV_NAME " %d: cannot reserve PCI resources, aborting\n", board_idx); + goto err_out_free_netdev; + } + + /* Bring the chip out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (xircom_tbl[chip_idx].flags & HAS_ACPI) + pci_write_config_dword(pdev, PCI_POWERMGMT, 0); + + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); + /* Clear the missed-packet counter. */ + (volatile int)inl(ioaddr + CSR8); + + tp = dev->priv; + + tp->lock = SPIN_LOCK_UNLOCKED; + tp->pdev = pdev; + tp->chip_id = chip_idx; + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */ + /* XXX: is this necessary for Xircom? */ + tp->csr0 = csr0 & ~EnableMWI; + + pci_set_drvdata(pdev, dev); + + /* The lower four bits are the media type. */ + if (board_idx >= 0 && board_idx < MAX_UNITS) { + tp->default_port = options[board_idx] & 15; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->autoneg = 0; + else + tp->autoneg = 1; + tp->speed100 = 1; + + /* The Xircom-specific entries in the device structure. */ + dev->open = &xircom_open; + dev->hard_start_xmit = &xircom_start_xmit; + dev->stop = &xircom_close; + dev->get_stats = &xircom_get_stats; + dev->do_ioctl = &xircom_ioctl; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_rx_mode; +#endif + dev->tx_timeout = xircom_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + transceiver_voodoo(dev); + + read_mac_address(dev); + + if (register_netdev(dev)) + goto err_out_cleardev; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); + printk(KERN_INFO "%s: %s rev %d at %#3lx,", + dev->name, xircom_tbl[chip_idx].chip_name, chip_rev, ioaddr); + for (i = 0; i < 6; i++) + printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]); + printk(", IRQ %d.\n", dev->irq); + + if (xircom_tbl[chip_idx].flags & HAS_MII) { + find_mii_transceivers(dev); + check_duplex(dev); + } + + return 0; + +err_out_cleardev: + pci_set_drvdata(pdev, NULL); + pci_release_regions(pdev); +err_out_free_netdev: + unregister_netdev(dev); + kfree(dev); + return -ENODEV; +} + + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return; +} + + +static void +xircom_up(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + int i; + + /* Clear the tx ring */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0; + } + + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: xircom_up() irq %d.\n", dev->name, dev->irq); + + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + tp->saved_if_port = dev->if_port; + if (dev->if_port == 0) + dev->if_port = tp->default_port; + + tp->csr6 = TxThresh10 /*| FullDuplexBit*/; /* XXX: why 10 and not 100? */ + + set_rx_mode(dev); + + /* Start the chip's Tx to process setup frame. */ + outl_CSR6(tp->csr6, ioaddr); + outl_CSR6(tp->csr6 | EnableTx, ioaddr); + + /* Acknowledge all outstanding interrupts sources */ + outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + /* Enable interrupts by setting the interrupt mask. */ + outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + /* Enable Rx */ + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + /* Rx poll demand */ + outl(0, ioaddr + CSR2); + + /* Tell the net layer we're ready */ + netif_start_queue (dev); + + if (xircom_debug > 2) { + printk(KERN_DEBUG "%s: Done xircom_up(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR6)); + } +} + + +static int +xircom_open(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + + if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + xircom_init_ring(dev); + + xircom_up(dev); + tp->open = 1; + + return 0; +} + + +static void xircom_tx_timeout(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } + +#if defined(way_too_many_messages) + if (xircom_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " + "%2.2x %2.2x %2.2x.\n", + i, (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) printk(" %2.2x", buf[j]); + printk(" j=%d.\n", j); + } + printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); + } +#endif + + /* Stop and restart the chip's Tx/Rx processes . */ + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + netif_wake_queue (dev); + tp->stats.tx_errors++; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void xircom_init_ring(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0; + tp->rx_ring[i].length = PKT_BUF_SZ; + tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_skbuff[i] = NULL; + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = PKT_BUF_SZ | Rx1RingWrap; + tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = Rx0DescOwned; /* Owned by Xircom chip */ + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); +#ifdef CARDBUS + if (tp->chip_id == X3201_3) + tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); +#endif /* CARDBUS */ + } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); +} + + +static int +xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int entry; + u32 flag; + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; +#ifdef CARDBUS + if (tp->chip_id == X3201_3) { + memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); + } else +#endif + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = Tx1WholePkt; /* No interrupt */ + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = Tx1WholePkt; /* No Tx-done intr. */ + } else { + /* Leave room for set_rx_mode() to fill entries. */ + flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */ + tp->tx_full = 1; + } + if (entry == TX_RING_SIZE - 1) + flag |= Tx1WholePkt | Tx1ComplIntr | Tx1RingWrap; + + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = Tx0DescOwned; /* Pass ownership to the chip. */ + tp->cur_tx++; + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); + + dev->trans_start = jiffies; + + return 0; +} + + +static void xircom_media_change(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + u16 reg0, reg1, reg4, reg5; + u32 csr6 = inl(ioaddr + CSR6), newcsr6; + + /* reset status first */ + mdio_read(dev, tp->phys[0], MII_BMCR); + mdio_read(dev, tp->phys[0], MII_BMSR); + + reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); + reg1 = mdio_read(dev, tp->phys[0], MII_BMSR); + + if (reg1 & BMSR_LSTATUS) { + /* link is up */ + if (reg0 & BMCR_ANENABLE) { + /* autonegotiation is enabled */ + reg4 = mdio_read(dev, tp->phys[0], MII_ADVERTISE); + reg5 = mdio_read(dev, tp->phys[0], MII_LPA); + if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) { + tp->speed100 = 1; + tp->full_duplex = 1; + } else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) { + tp->speed100 = 1; + tp->full_duplex = 0; + } else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) { + tp->speed100 = 0; + tp->full_duplex = 1; + } else { + tp->speed100 = 0; + tp->full_duplex = 0; + } + } else { + /* autonegotiation is disabled */ + if (reg0 & BMCR_SPEED100) + tp->speed100 = 1; + else + tp->speed100 = 0; + if (reg0 & BMCR_FULLDPLX) + tp->full_duplex = 1; + else + tp->full_duplex = 0; + } + printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n", + dev->name, + tp->speed100 ? "100" : "10", + tp->full_duplex ? "full" : "half"); + newcsr6 = csr6 & ~FullDuplexBit; + if (tp->full_duplex) + newcsr6 |= FullDuplexBit; + if (newcsr6 != csr6) + outl_CSR6(newcsr6, ioaddr + CSR6); + } else { + printk(KERN_DEBUG "%s: Link is down\n", dev->name); + } +} + + +static void check_duplex(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + u16 reg0; + + mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET); + udelay(500); + while (mdio_read(dev, tp->phys[0], MII_BMCR) & BMCR_RESET); + + reg0 = mdio_read(dev, tp->phys[0], MII_BMCR); + mdio_write(dev, tp->phys[0], MII_ADVERTISE, tp->advertising[0]); + + if (tp->autoneg) { + reg0 &= ~(BMCR_SPEED100 | BMCR_FULLDPLX); + reg0 |= BMCR_ANENABLE | BMCR_ANRESTART; + } else { + reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART); + if (tp->speed100) + reg0 |= BMCR_SPEED100; + if (tp->full_duplex) + reg0 |= BMCR_FULLDPLX; + printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n", + dev->name, + tp->speed100 ? "100" : "10", + tp->full_duplex ? "full" : "half"); + } + mdio_write(dev, tp->phys[0], MII_BMCR, reg0); +} + + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = dev_instance; + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + int csr5, work_budget = max_interrupt_work; + + spin_lock (&tp->lock); + + do { + csr5 = inl(ioaddr + CSR5); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(csr5 & 0x0001ffff, ioaddr + CSR5); + + if (xircom_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); + + if (csr5 == 0xffffffff) + break; /* all bits set, assume PCMCIA card removed */ + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= xircom_rx(dev); + + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = tp->tx_ring[entry].status; + + if (status < 0) + break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (tp->tx_skbuff[entry] == NULL) + continue; + + if (status & Tx0DescError) { + /* There was an major error, log it. */ +#ifndef final_version + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif + tp->stats.tx_errors++; + if (status & Tx0ManyColl) { + tp->stats.tx_aborted_errors++; +#ifdef ETHER_STATS + tp->stats.collisions16++; +#endif + } + if (status & Tx0NoCarrier) tp->stats.tx_carrier_errors++; + if (status & Tx0LateColl) tp->stats.tx_window_errors++; + if (status & Tx0Underflow) tp->stats.tx_fifo_errors++; + } else { + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_skbuff[entry]); + tp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->tx_full && + tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) + /* The ring is no longer full */ + tp->tx_full = 0; + + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (xircom_debug > 2) + printk(KERN_WARNING "%s: The transmitter stopped." + " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 & LinkChange) + xircom_media_change(dev); + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & TxThreshMask) != TxThreshMask) + tp->csr6 += (1 << TxThreshShift); /* Bump up the Tx threshold */ + else + tp->csr6 |= TxStoreForw; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl_CSR6(tp->csr6 | EnableRx, ioaddr); + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + outl_CSR6(tp->csr6 | EnableTxRx, ioaddr); + } + /* Clear all error sources, included undocumented ones! */ + outl(0x0800f7ba, ioaddr + CSR5); + } + if (--work_budget < 0) { + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Too much work during an interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); + break; + } + } while (1); + + if (xircom_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + spin_unlock (&tp->lock); +} + + +static int +xircom_rx(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; + + if (xircom_debug > 4) + printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + /* If we own the next entry, it's a new packet. Send it up. */ + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + + if (xircom_debug > 5) + printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + if (--rx_work_limit < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ignore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (xircom_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & Rx0DescError) { + /* There was a fatal error. */ + if (xircom_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & (Rx0Runt | Rx0HugeFrame)) tp->stats.rx_length_errors++; + if (status & Rx0CRCError) tp->stats.rx_crc_errors++; + } + } else { + /* Omit the four octet CRC from the length. */ + short pkt_len = ((status >> 16) & 0x7ff) - 4; + struct sk_buff *skb; + +#ifndef final_version + if (pkt_len > 1518) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); +#endif + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + skb_put(skb = tp->rx_skbuff[entry], pkt_len); + tp->rx_skbuff[entry] = NULL; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); + work_done++; + } + tp->rx_ring[entry].status = Rx0DescOwned; + } + + return work_done; +} + + +static void +xircom_down(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct xircom_private *tp = dev->priv; + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + CSR7); + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr); + + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + dev->if_port = tp->saved_if_port; +} + + +static int +xircom_close(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct xircom_private *tp = dev->priv; + int i; + + if (xircom_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + + netif_stop_queue(dev); + + if (netif_device_present(dev)) + xircom_down(dev); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { + dev_kfree_skb(skb); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i]); + tp->tx_skbuff[i] = 0; + } + + tp->open = 0; + return 0; +} + + +static struct net_device_stats *xircom_get_stats(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + long ioaddr = dev->base_addr; + + if (netif_device_present(dev)) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + return &tp->stats; +} + + +static int xircom_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct ethtool_cmd ecmd; + struct xircom_private *tp = dev->priv; + + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + + switch (ecmd.cmd) { + case ETHTOOL_GSET: + ecmd.supported = + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_MII; + + ecmd.advertising = ADVERTISED_MII; + if (tp->advertising[0] & ADVERTISE_10HALF) + ecmd.advertising |= ADVERTISED_10baseT_Half; + if (tp->advertising[0] & ADVERTISE_10FULL) + ecmd.advertising |= ADVERTISED_10baseT_Full; + if (tp->advertising[0] & ADVERTISE_100HALF) + ecmd.advertising |= ADVERTISED_100baseT_Half; + if (tp->advertising[0] & ADVERTISE_100FULL) + ecmd.advertising |= ADVERTISED_100baseT_Full; + if (tp->autoneg) { + ecmd.advertising |= ADVERTISED_Autoneg; + ecmd.autoneg = AUTONEG_ENABLE; + } else + ecmd.autoneg = AUTONEG_DISABLE; + + ecmd.port = PORT_MII; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = tp->phys[0]; + ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10; + ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd.maxtxpkt = TX_RING_SIZE / 2; + ecmd.maxrxpkt = 0; + + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + + case ETHTOOL_SSET: { + u16 autoneg, speed100, full_duplex; + + autoneg = (ecmd.autoneg == AUTONEG_ENABLE); + speed100 = (ecmd.speed == SPEED_100); + full_duplex = (ecmd.duplex == DUPLEX_FULL); + + tp->autoneg = autoneg; + if (speed100 != tp->speed100 || + full_duplex != tp->full_duplex) { + tp->speed100 = speed100; + tp->full_duplex = full_duplex; + /* change advertising bits */ + tp->advertising[0] &= ~(ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL | + ADVERTISE_100BASE4); + if (speed100) { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_100FULL; + else + tp->advertising[0] |= ADVERTISE_100HALF; + } else { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_10FULL; + else + tp->advertising[0] |= ADVERTISE_10HALF; + } + } + check_duplex(dev); + return 0; + } + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info; + memset(&info, 0, sizeof(info)); + info.cmd = ecmd.cmd; + strcpy(info.driver, DRV_NAME); + strcpy(info.version, DRV_VERSION); + *info.fw_version = 0; + strcpy(info.bus_info, tp->pdev->slot_name); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + default: + return -EOPNOTSUPP; + } +} + + +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct xircom_private *tp = dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCETHTOOL: + return xircom_ethtool_ioctl(dev, (void *) rq->ifr_data); + + /* Legacy mii-diag interface */ + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + if (tp->mii_cnt) + data[0] = phy; + else + return -ENODEV; + return 0; + case SIOCGMIIREG: /* Read MII PHY register. */ + save_flags(flags); + cli(); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + return 0; + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + save_flags(flags); + cli(); + if (data[0] == tp->phys[0]) { + u16 value = data[2]; + switch (data[1]) { + case 0: + if (value & (BMCR_RESET | BMCR_ANENABLE)) + /* Autonegotiation. */ + tp->autoneg = 1; + else { + tp->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0; + tp->autoneg = 0; + } + break; + case 4: + tp->advertising[0] = value; + break; + } + check_duplex(dev); + } + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ +static void set_rx_mode(struct net_device *dev) +{ + struct xircom_private *tp = dev->priv; + struct dev_mc_list *mclist; + long ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6); + u16 *eaddrs, *setup_frm; + u32 tx_flags; + int i; + + tp->csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); + csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit); + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + tp->csr6 |= PromiscBit; + csr6 |= PromiscBit; + goto out; + } + + if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= AllMultiBit; + csr6 |= AllMultiBit; + goto out; + } + + tx_flags = Tx1WholePkt | Tx1SetupPkt | PKT_SETUP_SZ; + + /* Note that only the low-address shortword of setup_frame is valid! */ + setup_frm = tp->setup_frame; + mclist = dev->mc_list; + + /* Fill the first entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; + + if (dev->mc_count > 14) { /* Must use a multicast hash table. */ + u32 *hash_table = (u32 *)(tp->setup_frame + 4 * 12); + u32 hash, hash2; + + tx_flags |= Tx1HashSetup; + tp->csr6 |= HashFilterBit; + csr6 |= HashFilterBit; + + /* Fill the unused 3 entries with the broadcast address. + At least one entry *must* contain the broadcast address!!!*/ + for (i = 0; i < 3; i++) { + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + } + + /* Truly brain-damaged hash filter layout */ + /* XXX: not sure if I should take the last or the first 9 bits */ + for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { + u32 *hptr; + hash = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff; + if (hash < 384) { + hash2 = hash + ((hash >> 4) << 4) + + ((hash >> 5) << 5); + } else { + hash -= 384; + hash2 = 64 + hash + (hash >> 4) * 80; + } + hptr = &hash_table[hash2 & ~0x1f]; + *hptr |= cpu_to_le32(1 << (hash2 & 0x1f)); + } + } else { + /* We have <= 14 mcast addresses so we can use Xircom's + wonderful 16-address perfect filter. */ + for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2; + *setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2; + } + /* Fill the unused entries with the broadcast address. + At least one entry *must* contain the broadcast address!!!*/ + for (; i < 15; i++) { + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + *setup_frm = 0xffff; setup_frm += 2; + } + } + + /* Now add this frame to the Tx list. */ + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + /* XXX: Huh? All it means is that the Tx list is full...*/ + } else { + unsigned long flags; + unsigned int entry; + int dummy = -1; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0; + tp->tx_ring[entry].buffer1 = 0; + /* race with chip, set Tx0DescOwned later */ + dummy = entry; + entry = tp->cur_tx++ % TX_RING_SIZE; + } + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE - 1) + tx_flags |= Tx1RingWrap; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = Tx0DescOwned; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + tp->tx_full = 1; + netif_stop_queue (dev); + } + if (dummy >= 0) + tp->tx_ring[dummy].status = Tx0DescOwned; + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + +out: + outl_CSR6(csr6, ioaddr); +} + + +static struct pci_device_id xircom_pci_table[] __devinitdata = { + { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, + {0}, +}; +MODULE_DEVICE_TABLE(pci, xircom_pci_table); + + +#ifdef CONFIG_PM +static int xircom_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct xircom_private *tp = dev->priv; + printk(KERN_INFO "xircom_suspend(%s)\n", dev->name); + if (tp->open) + xircom_down(dev); + return 0; +} + + +static int xircom_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct xircom_private *tp = dev->priv; + printk(KERN_INFO "xircom_resume(%s)\n", dev->name); + + /* Bring the chip out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (xircom_tbl[tp->chip_id].flags & HAS_ACPI) + pci_write_config_dword(tp->pdev, PCI_POWERMGMT, 0); + + transceiver_voodoo(dev); + if (xircom_tbl[tp->chip_id].flags & HAS_MII) + check_duplex(dev); + + if (tp->open) + xircom_up(dev); + return 0; +} +#endif /* CONFIG_PM */ + + +static void __devexit xircom_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + printk(KERN_INFO "xircom_remove_one(%s)\n", dev->name); + unregister_netdev(dev); + pci_release_regions(pdev); + kfree(dev); + pci_set_drvdata(pdev, NULL); +} + + +static struct pci_driver xircom_driver = { + name: DRV_NAME, + id_table: xircom_pci_table, + probe: xircom_init_one, + remove: __devexit_p(xircom_remove_one), +#ifdef CONFIG_PM + suspend: xircom_suspend, + resume: xircom_resume +#endif /* CONFIG_PM */ +}; + + +static int __init xircom_init(void) +{ +/* when a module, this is printed whether or not devices are found in probe */ +#ifdef MODULE + printk(version); +#endif + return pci_module_init(&xircom_driver); +} + + +static void __exit xircom_exit(void) +{ + pci_unregister_driver(&xircom_driver); +} + +module_init(xircom_init) +module_exit(xircom_exit) + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -urN linux-2.5.6-pre3/drivers/net/wan/Config.help linux-2.5.6/drivers/net/wan/Config.help --- linux-2.5.6-pre3/drivers/net/wan/Config.help Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/drivers/net/wan/Config.help Thu Mar 7 18:24:48 2002 @@ -41,14 +41,15 @@ This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards from FarSite Communications Ltd. Synchronous communication is supported on all ports at speeds up to - 8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC. + 8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC, + Frame Relay or X.25/LAPB. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want) say M here and read . The module will be called farsync.o and if you want the module to be automatically loaded when the interface is referenced then you - should add "alias syncX farsync" to /etc/modules.conf for each + should add "alias hdlcX farsync" to /etc/modules.conf for each interface, where X is 0, 1, 2, ... CONFIG_DLCI diff -urN linux-2.5.6-pre3/drivers/net/wan/Config.in linux-2.5.6/drivers/net/wan/Config.in --- linux-2.5.6-pre3/drivers/net/wan/Config.in Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/drivers/net/wan/Config.in Thu Mar 7 18:24:48 2002 @@ -39,9 +39,6 @@ dep_tristate ' Etinc PCISYNC serial board support (EXPERIMENTAL)' CONFIG_DSCC4 m -# FarSite Communications' cards - - tristate ' FarSync T-Series X.21 (and V.35/V.24) cards' CONFIG_FARSYNC # # Lan Media's board. Currently 1000, 1200, 5200, 5245 @@ -55,8 +52,13 @@ tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP - tristate ' Generic HDLC driver' CONFIG_HDLC +# Generic HDLC + + tristate ' Generic HDLC layer' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then + bool ' Raw HDLC support' CONFIG_HDLC_RAW + bool ' Cisco HDLC support' CONFIG_HDLC_CISCO + bool ' Frame Relay support' CONFIG_HDLC_FR bool ' Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then bool ' X.25 protocol support' CONFIG_HDLC_X25 @@ -65,6 +67,11 @@ fi dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC + dep_tristate ' FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC + bool ' Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT + bool ' Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER + bool ' Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN + bool ' Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS fi tristate ' Frame relay DLCI support' CONFIG_DLCI diff -urN linux-2.5.6-pre3/drivers/net/wan/Makefile linux-2.5.6/drivers/net/wan/Makefile --- linux-2.5.6-pre3/drivers/net/wan/Makefile Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/wan/Makefile Thu Mar 7 18:24:48 2002 @@ -9,7 +9,7 @@ O_TARGET := wan.o -export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o +export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o list-multi = wanpipe.o cyclomx.o wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y) @@ -22,6 +22,11 @@ cyclomx-objs = cycx_main.o $(cyclomx-y) cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o +hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o +hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o +hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o +hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o +hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o @@ -56,12 +61,17 @@ obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o obj-$(CONFIG_HDLC) += hdlc.o -obj-$(CONFIG_HDLC_PPP) += syncppp.o +ifeq ($(CONFIG_HDLC_PPP),y) + obj-$(CONFIG_HDLC) += syncppp.o +endif obj-$(CONFIG_N2) += n2.o obj-$(CONFIG_C101) += c101.o include $(TOPDIR)/Rules.make +hdlc.o: hdlc_generic.o $(hdlc-y) + $(LD) -r -o $@ hdlc_generic.o $(hdlc-y) + wanpipe.o: $(wanpipe-objs) $(LD) -r -o $@ $(wanpipe-objs) diff -urN linux-2.5.6-pre3/drivers/net/wan/c101.c linux-2.5.6/drivers/net/wan/c101.c --- linux-2.5.6-pre3/drivers/net/wan/c101.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/wan/c101.c Thu Mar 7 18:24:48 2002 @@ -1,7 +1,7 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000 Krzysztof Halasa + * Copyright (C) 2000-2001 Krzysztof Halasa * * 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 @@ -15,6 +15,7 @@ * Moxa C101 User's Manual */ +#include #include #include #include @@ -29,10 +30,8 @@ #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4"; +static const char* version = "Moxa C101 driver version: 1.09"; static const char* devname = "C101"; #define C101_PAGE 0x1D00 @@ -51,12 +50,12 @@ typedef struct card_s { hdlc_device hdlc; /* HDLC device struct - must be first */ spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock speed */ - int line; /* loopback only */ u8 *win0base; /* ISA window base address */ u32 phy_winbase; /* ISA physical base address */ u16 buff_offset; /* offset of first buffer of first channel */ + sync_serial_settings settings; + unsigned short encoding; + unsigned short parity; u8 rxs, txs, tmc; /* SCA registers */ u8 irq; /* IRQ (3-15) */ u8 ring_buffers; /* number of buffers in a ring */ @@ -72,6 +71,9 @@ typedef card_t port_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) #define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) @@ -105,13 +107,13 @@ #include "hd6457x.c" -static int c101_set_clock(port_t *port, int value) +static int c101_set_iface(port_t *port) { u8 msci = get_msci(port); u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { + switch(port->settings.clock_type) { case CLOCK_EXT: rxs |= CLK_LINE_RX; /* RXC input */ txs |= CLK_LINE_TX; /* TXC input */ @@ -140,76 +142,76 @@ port->txs = txs; sca_out(rxs, msci + RXS, port); sca_out(txs, msci + TXS, port); - port->clkmode = value; + sca_set_port(port); return 0; } -static int c101_open(hdlc_device *hdlc) +static int c101_open(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); + int result = hdlc_open(hdlc); + if (result) + return result; MOD_INC_USE_COUNT; writeb(1, port->win0base + C101_DTR); sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ sca_open(hdlc); - c101_set_clock(port, port->clkmode); - return 0; + return c101_set_iface(port); } -static void c101_close(hdlc_device *hdlc) +static int c101_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); sca_close(hdlc); writeb(0, port->win0base + C101_DTR); sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + union line_settings *line = &ifr->ifr_settings->ifs_line; + const size_t size = sizeof(sync_serial_settings); + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(cmd) { - case HDLCSCLOCK: - result = c101_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; - - case HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; - - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; - -#ifdef DEBUG_RINGS - case HDLCRUN: +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { sca_dump_rings(hdlc); return 0; -#endif /* DEBUG_RINGS */ + } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings->type) { + case IF_GET_IFACE: + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &port->settings, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&port->settings, &line->sync, size)) + return -EFAULT; + /* FIXME - put sanity checks here */ + return c101_set_iface(port); default: - return -EINVAL; + return hdlc_ioctl(dev, ifr, cmd); } - - ifr->ifr_ifru.ifru_ivalue = value; - return result; } @@ -231,6 +233,7 @@ static int c101_run(unsigned long irq, unsigned long winbase) { + struct net_device *dev; card_t *card; int result; @@ -284,15 +287,19 @@ sca_init(card, 0); + dev = hdlc_to_dev(&card->hdlc); + spin_lock_init(&card->lock); - hdlc_to_dev(&card->hdlc)->irq = irq; - hdlc_to_dev(&card->hdlc)->mem_start = winbase; - hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; - hdlc_to_dev(&card->hdlc)->tx_queue_len = 50; - card->hdlc.ioctl = c101_ioctl; - card->hdlc.open = c101_open; - card->hdlc.close = c101_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; + dev->tx_queue_len = 50; + dev->do_ioctl = c101_ioctl; + dev->open = c101_open; + dev->stop = c101_close; + card->hdlc.attach = sca_attach; card->hdlc.xmit = sca_xmit; + card->settings.clock_type = CLOCK_EXT; result = register_hdlc_device(&card->hdlc); if (result) { @@ -319,7 +326,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s\n", version); + printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); do { unsigned long irq, ram; diff -urN linux-2.5.6-pre3/drivers/net/wan/dscc4.c linux-2.5.6/drivers/net/wan/dscc4.c --- linux-2.5.6-pre3/drivers/net/wan/dscc4.c Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/drivers/net/wan/dscc4.c Thu Mar 7 18:24:48 2002 @@ -72,7 +72,7 @@ * the documentation/chipset releases. An on-line errata would be welcome. * * TODO: - * - syncppp oopses. X25 untested. + * - test X25. * - use polling at high irq/s, * - performance analysis, * - endianness. @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ #include /* Version */ -static const char version[] = "$Id: dscc4.c,v 1.157 2002/01/28 01:54:19 romieu Exp $\n"; +static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n"; static int debug; static int quartz; @@ -168,9 +169,9 @@ #define BRR_DIVIDER_MAX 64*0x00008000 #define dev_per_card 4 -#define SOURCE_ID(flags) ((flags >> 28 ) & 0x03) -#define TO_SIZE(state) ((state >> 16) & 0x1fff) -#define TO_STATE(len) cpu_to_le32((len & TxSizeMax) << 16) +#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03) +#define TO_SIZE(state) (((state) >> 16) & 0x1fff) +#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16) #define RX_MAX(len) ((((len) >> 5) + 1)<< 5) #define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET @@ -343,6 +344,11 @@ static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *); #endif +static inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev) +{ + return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev); +} + static inline void dscc4_patch_register(u32 ioaddr, u32 mask, u32 value) { u32 state; @@ -479,52 +485,49 @@ static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv) { - int cur, ret = 0; - s16 i; + int cur = dpriv->iqtx_current%IRQ_RING_SIZE; + s16 i = 0; - cur = dpriv->iqtx_current%IRQ_RING_SIZE; - for (i = 0; i >= 0; i++) { + do { if (!(dpriv->flags & (NeedIDR | NeedIDT)) || (dpriv->iqtx[cur] & Xpr)) break; smp_rmb(); - } - if (i < 0) { - printk(KERN_ERR "%s: %s timeout\n", "dscc4", "XPR"); - ret = -1; - } - return ret; + } while (i++ >= 0); + + return i; } static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur, struct RxFD *rx_fd, struct net_device *dev) { + struct net_device_stats *stats = &dev_to_hdlc(dev)->stats; struct pci_dev *pdev = dpriv->pci_priv->pdev; struct sk_buff *skb; int pkt_len; skb = dpriv->rx_skbuff[cur]; - pkt_len = TO_SIZE(rx_fd->state2) - 1; - pci_dma_sync_single(pdev, rx_fd->data, pkt_len + 1, PCI_DMA_FROMDEVICE); - if((skb->data[pkt_len] & FrameOk) == FrameOk) { + pkt_len = TO_SIZE(rx_fd->state2); + pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE); + if((skb->data[--pkt_len] & FrameOk) == FrameOk) { pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE); - dev_to_hdlc(dev)->stats.rx_packets++; - dev_to_hdlc(dev)->stats.rx_bytes += pkt_len; + stats->rx_packets++; + stats->rx_bytes += pkt_len; skb->tail += pkt_len; skb->len = pkt_len; - if (netif_running(hdlc_to_dev(&dpriv->hdlc))) + if (netif_running(dev)) skb->protocol = htons(ETH_P_HDLC); netif_rx(skb); try_get_rx_skb(dpriv, cur, dev); } else { if(skb->data[pkt_len] & FrameRdo) - dev_to_hdlc(dev)->stats.rx_fifo_errors++; + stats->rx_fifo_errors++; else if(!(skb->data[pkt_len] | ~FrameCrc)) - dev_to_hdlc(dev)->stats.rx_crc_errors++; + stats->rx_crc_errors++; else if(!(skb->data[pkt_len] | ~(FrameVfr | FrameRab))) - dev_to_hdlc(dev)->stats.rx_length_errors++; + stats->rx_length_errors++; else - dev_to_hdlc(dev)->stats.rx_errors++; + stats->rx_errors++; } rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; @@ -612,7 +615,7 @@ * SCC 0-3 private rx/tx irq structures * IQRX/TXi needs to be set soon. Learned it the hard way... */ - for(i = 0; i < dev_per_card; i++) { + for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; dpriv->iqtx = (u32 *) pci_alloc_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma); @@ -620,7 +623,7 @@ goto err_out_free_iqtx; writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4); } - for(i = 0; i < dev_per_card; i++) { + for (i = 0; i < dev_per_card; i++) { dpriv = priv->root + i; dpriv->iqrx = (u32 *) pci_alloc_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma); @@ -740,7 +743,6 @@ dpriv->dev_id = i; dpriv->pci_priv = ppriv; spin_lock_init(&dpriv->lock); - d->priv = dpriv; hdlc->xmit = dscc4_start_xmit; hdlc->attach = dscc4_hdlc_attach; @@ -778,7 +780,7 @@ struct dscc4_dev_priv *dpriv; struct dscc4_pci_priv *ppriv; - dpriv = dev->priv; + dpriv = dscc4_priv(dev); if (netif_queue_stopped(dev) && ((jiffies - dev->trans_start) > TX_TIMEOUT)) { ppriv = dpriv->pci_priv; @@ -844,7 +846,7 @@ static int dscc4_open(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); hdlc_device *hdlc = &dpriv->hdlc; struct dscc4_pci_priv *ppriv; u32 ioaddr; @@ -893,8 +895,10 @@ * WARNING, a really missing XPR usually means a hardware * reset is needed. Suggestions anyone ? */ - if (dscc4_xpr_ack(dpriv)) + if (dscc4_xpr_ack(dpriv) < 0) { + printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR"); goto err_free_ring; + } netif_start_queue(dev); @@ -925,7 +929,7 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct dscc4_pci_priv *ppriv; struct TxFD *tx_fd; int cur, next; @@ -935,7 +939,6 @@ next = dpriv->tx_current%TX_RING_SIZE; dpriv->tx_skbuff[next] = skb; tx_fd = dpriv->tx_fd + next; - printk(KERN_DEBUG "%s: %d sent\n", dev->name, skb->len); tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len); tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); @@ -972,7 +975,7 @@ static int dscc4_close(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); u32 ioaddr = dev->base_addr; int dev_id; hdlc_device *hdlc = dev_to_hdlc(dev); @@ -1011,7 +1014,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); u32 brr; *state &= ~Ccr0ClockMask; @@ -1062,37 +1065,29 @@ static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct dscc4_dev_priv *dpriv = dev->priv; - struct if_settings *if_s = &ifr->ifr_settings; + union line_settings *line = &ifr->ifr_settings->ifs_line; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); const size_t size = sizeof(dpriv->settings); int ret = 0; if (dev->flags & IFF_UP) return -EBUSY; - if (cmd != SIOCDEVICE) + if (cmd != SIOCWANDEV) return -EOPNOTSUPP; - switch(ifr->ifr_settings.type) { + switch(ifr->ifr_settings->type) { case IF_GET_IFACE: - if_s->type = IF_IFACE_SYNC_SERIAL; - if (if_s->data_length == 0) - return 0; - if (if_s->data_length < size) - return -ENOMEM; - if (copy_to_user(if_s->data, &dpriv->settings, size)) + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &dpriv->settings, size)) return -EFAULT; - if_s->data_length = size; break; case IF_IFACE_SYNC_SERIAL: if(!capable(CAP_NET_ADMIN)) return -EPERM; - if (if_s->data_length != size) - return -ENOMEM; - - if (copy_from_user(&dpriv->settings, if_s->data, size)) + if (copy_from_user(&dpriv->settings, &line->sync, size)) return -EFAULT; ret = dscc4_set_iface(dev); break; @@ -1133,7 +1128,7 @@ static int dscc4_clock_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); sync_serial_settings *settings = &dpriv->settings; u32 bps, state; u32 ioaddr; @@ -1160,7 +1155,7 @@ static int dscc4_encoding_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct thingie encoding[] = { { ENCODING_NRZ, 0x00000000 }, { ENCODING_NRZI, 0x00200000 }, @@ -1184,7 +1179,7 @@ static int dscc4_loopback_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); sync_serial_settings *settings = &dpriv->settings; u32 ioaddr, state; @@ -1203,7 +1198,7 @@ static int dscc4_crc_setting(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct thingie crc[] = { { PARITY_CRC16_PR0_CCITT, 0x00000010 }, { PARITY_CRC16_PR1_CCITT, 0x00000000 }, @@ -1516,7 +1511,6 @@ } } else { /* ! SccEvt */ #ifdef DEBUG_PARANOIA - int i; static struct { u32 mask; const char *irq_name; @@ -1528,21 +1522,21 @@ { 0x00000008, "PLLA"}, { 0x00000004, "CDSC"}, { 0, NULL} - }; + }, *evt; #endif /* DEBUG_PARANOIA */ state &= 0x00ffffff; #ifdef DEBUG_PARANOIA - for (i = 0; evts[i].irq_name; i++) { - if (state & evts[i].mask) { + for (evt = evts; evt->irq_name; evt++) { + if (state & evt->mask) { printk(KERN_DEBUG "%s: %s\n", dev->name, - evts[i].irq_name); - if (!(state &= ~evts[i].mask)) + evt->irq_name); + if (!(state &= ~evt->mask)) goto try; } } #endif /* DEBUG_PARANOIA */ /* - * Receive Data Overflow (FIXME: untested) + * Receive Data Overflow (FIXME: fscked) */ if (state & Rdo) { u32 ioaddr, scc_offset, scc_addr; @@ -1633,7 +1627,7 @@ static int dscc4_init_ring(struct net_device *dev) { - struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv; + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); struct TxFD *tx_fd; struct RxFD *rx_fd; int i; @@ -1654,7 +1648,7 @@ dpriv->tx_dirty = 0; /* the dma core of the dscc4 will be locked on the first desc */ - for(i = 0; i < TX_RING_SIZE; ) { + for (i = 0; i < TX_RING_SIZE; ) { reset_TxFD(tx_fd); /* FIXME: NULL should be ok - to be tried */ tx_fd->data = dpriv->tx_fd_dma; @@ -1689,7 +1683,7 @@ skb->len, PCI_DMA_TODEVICE); dpriv->tx_skbuff[0] = skb; } - for (i = 0; i < RX_RING_SIZE;) { + for (i = 0; i < RX_RING_SIZE; ) { /* size set by the host. Multiple of 4 bytes please */ rx_fd->state1 = HiDesc; /* Hi, no Hold */ rx_fd->state2 = 0x00000000; @@ -1753,7 +1747,8 @@ static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding, unsigned short parity) { - struct dscc4_dev_priv *dpriv = hdlc_to_dev(hdlc)->priv; + struct net_device *dev = hdlc_to_dev(hdlc); + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI && @@ -1782,7 +1777,7 @@ MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl); static struct pci_driver dscc4_driver = { - name: "dscc4", + name: DRV_NAME, id_table: dscc4_pci_tbl, probe: dscc4_init_one, remove: dscc4_remove_one, diff -urN linux-2.5.6-pre3/drivers/net/wan/farsync.c linux-2.5.6/drivers/net/wan/farsync.c --- linux-2.5.6-pre3/drivers/net/wan/farsync.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/wan/farsync.c Thu Mar 7 18:24:48 2002 @@ -1,5 +1,5 @@ /* - * FarSync X21 driver for Linux (2.4.x kernel version) + * FarSync X21 driver for Linux (generic HDLC version) * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * @@ -18,11 +18,10 @@ #include #include #include -#include #include -#include #include -#include +#include +#include #include "farsync.h" @@ -32,6 +31,7 @@ */ MODULE_AUTHOR("R.J.Dunlop "); MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd."); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; @@ -331,26 +331,15 @@ /* Per port (line or channel) information */ struct fst_port_info { - void *if_ptr; /* Some drivers describe this as a - * general purpose pointer. However if - * using syncPPP it has a very specific - * purpose: it must be the first item in - * the structure pointed to by dev->priv - * and must in turn point to the - * associated ppp_device structure. - */ + hdlc_device hdlc; /* HDLC device struct - must be first */ struct fst_card_info *card; /* Card we're associated with */ int index; /* Port index on the card */ - int proto; /* Protocol we are running */ int hwif; /* Line hardware (lineInterface copy) */ int run; /* Port is running */ int rxpos; /* Next Rx buffer to use */ int txpos; /* Next Tx buffer to use */ int txipos; /* Next Tx buffer to check for free */ int txcnt; /* Count of Tx buffers in use */ - struct net_device *dev; /* Kernel network device entry */ - struct net_device_stats stats; /* Standard statistics */ - struct ppp_device pppdev; /* Link to syncPPP */ }; /* Per card information @@ -370,6 +359,11 @@ struct fst_port_info ports[ FST_MAX_PORTS ]; }; +/* Convert an HDLC device pointer into a port info pointer and similar */ +#define hdlc_to_port(H) ((struct fst_port_info *)(H)) +#define dev_to_port(D) hdlc_to_port(dev_to_hdlc(D)) +#define port_to_dev(P) hdlc_to_dev(&(P)->hdlc) + /* * Shared memory window access macros @@ -632,25 +626,18 @@ if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD )) { - if ( ! netif_carrier_ok ( port->dev )) + if ( ! netif_carrier_ok ( port_to_dev ( port ))) { dbg ( DBG_INTR,"DCD active\n"); - - /* Poke sPPP to renegotiate */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - { - sppp_reopen ( port->dev ); - } - - netif_carrier_on ( port->dev ); + netif_carrier_on ( port_to_dev ( port )); } } else { - if ( netif_carrier_ok ( port->dev )) + if ( netif_carrier_ok ( port_to_dev ( port ))) { dbg ( DBG_INTR,"DCD lost\n"); - netif_carrier_off ( port->dev ); + netif_carrier_off ( port_to_dev ( port )); } } } @@ -693,24 +680,24 @@ len ); if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 ) { - port->stats.rx_errors++; + port->hdlc.stats.rx_errors++; /* Update error stats and discard buffer */ if ( dmabits & RX_OFLO ) { - port->stats.rx_fifo_errors++; + port->hdlc.stats.rx_fifo_errors++; } if ( dmabits & RX_CRC ) { - port->stats.rx_crc_errors++; + port->hdlc.stats.rx_crc_errors++; } if ( dmabits & RX_FRAM ) { - port->stats.rx_frame_errors++; + port->hdlc.stats.rx_frame_errors++; } if ( dmabits == ( RX_STP | RX_ENP )) { - port->stats.rx_length_errors++; + port->hdlc.stats.rx_length_errors++; } /* Discard buffer descriptors until we see the end of packet @@ -747,7 +734,7 @@ { dbg ( DBG_RX,"intr_rx: can't allocate buffer\n"); - port->stats.rx_dropped++; + port->hdlc.stats.rx_dropped++; /* Return descriptor to card */ FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); @@ -771,29 +758,16 @@ port->rxpos = rxp; /* Update stats */ - port->stats.rx_packets++; - port->stats.rx_bytes += len; + port->hdlc.stats.rx_packets++; + port->hdlc.stats.rx_bytes += len; /* Push upstream */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - { - /* Mark for further processing by sPPP module */ - skb->protocol = htons ( ETH_P_WAN_PPP ); - } - else - { - /* DEC customer specific protocol (since nothing defined for - * marking raw data), at least one other driver uses this value - * for this purpose. - */ - skb->protocol = htons ( ETH_P_CUST ); - skb->pkt_type = PACKET_HOST; - } skb->mac.raw = skb->data; - skb->dev = port->dev; + skb->dev = hdlc_to_dev ( &port->hdlc ); + skb->protocol = htons ( ETH_P_HDLC ); netif_rx ( skb ); - port->dev->last_rx = jiffies; + port_to_dev ( port )->last_rx = jiffies; } @@ -844,7 +818,7 @@ case CTLB_CHG: case CTLC_CHG: case CTLD_CHG: - if ( port->run && port->dev != NULL ) + if ( port->run ) fst_intr_ctlchg ( card, port ); break; @@ -863,9 +837,8 @@ * always load up the entire packet for DMA. */ dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 ); - - port->stats.tx_errors++; - port->stats.tx_fifo_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_fifo_errors++; break; case INIT_CPLT: @@ -890,7 +863,7 @@ for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ ) { - if ( port->dev == NULL || ! port->run ) + if ( ! port->run ) continue; /* Check for rx completions */ @@ -907,7 +880,7 @@ --port->txcnt; if ( ++port->txipos >= NUM_TX_BUFFER ) port->txipos = 0; - netif_wake_queue ( port->dev ); + netif_wake_queue ( port_to_dev ( port )); } } @@ -969,145 +942,28 @@ static int -fst_change_mtu ( struct net_device *dev, int new_mtu ) -{ - if ( new_mtu < 128 || new_mtu > FST_MAX_MTU ) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - -/* Sooner or later you can't avoid a forward declaration */ -static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ); - -static int -switch_proto ( struct fst_port_info *port, int new_proto ) -{ - int err; - int orig_mtu; - struct net_device *dev; - - dev = port->dev; - - /* Turn off sPPP module ? */ - if (( new_proto != FST_HDLC && new_proto != FST_PPP ) - && ( port->proto == FST_HDLC || port->proto == FST_PPP )) - { - sppp_close ( port->pppdev.dev ); - sppp_detach ( port->pppdev.dev ); - - /* Reset some fields overwritten by sPPP */ - dev->hard_header = NULL; - dev->rebuild_header = NULL; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->type = ARPHRD_MYTYPE; - dev->addr_len = 0; - dev->hard_header_len = 0; - dev->do_ioctl = fst_ioctl; - dev->change_mtu = fst_change_mtu; - dev->flags = IFF_POINTOPOINT|IFF_NOARP; - - return 0; - } - - /* Turn on sPPP ? */ - if (( new_proto == FST_HDLC || new_proto == FST_PPP ) - && ( port->proto != FST_HDLC && port->proto != FST_PPP )) - { - orig_mtu = dev->mtu; - - /* Attach to sync PPP module */ - port->pppdev.dev = dev; - sppp_attach ( &port->pppdev ); - - if ( orig_mtu < dev->mtu ) - dev->change_mtu ( dev, orig_mtu ); - - /* Claw back the ioctl routine. We promise to be good and call - * the sync PPP routines once we've eliminated our functions. - */ - dev->do_ioctl = fst_ioctl; - - /* Set the mode */ - if ( new_proto == FST_HDLC ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, - SPPPIOCCISCO ); - } - else - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, - SPPPIOCPPP ); - } - - /* Open the device */ - if ( err == 0 ) - { - (void) sppp_open ( port->dev ); - } - - return err; - } - - /* Switch sPPP mode to that desired */ - err = 0; - if ( new_proto == FST_HDLC && port->pppdev.dev != NULL ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO ); - } - else if ( new_proto == FST_PPP && port->pppdev.dev != NULL ) - { - err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP ); - } - - /* Anything else is switching from one raw mode to another which is - * basically a NOP - */ - - return err; -} - - -static int set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port, struct fstioc_info *info ) { int err; - /* Set things according to the user set valid flags */ + /* Set things according to the user set valid flags. + * Several of the old options have been invalidated/replaced by the + * generic HDLC package. + */ err = 0; if ( info->valid & FSTVAL_PROTO ) - { - if ( port->proto != info->proto ) - { - err = switch_proto ( port, info->proto ); - if ( err == 0 ) - port->proto = info->proto; - } - } + err = -EINVAL; if ( info->valid & FSTVAL_CABLE ) - { - FST_WRB ( card, portConfig[port->index].lineInterface, - info->lineInterface ); - port->hwif = info->lineInterface; - } + err = -EINVAL; if ( info->valid & FSTVAL_SPEED ) - { - FST_WRL ( card, portConfig[port->index].lineSpeed, - info->lineSpeed ); - FST_WRB ( card, portConfig[port->index].internalClock, - info->internalClock ); - } + err = -EINVAL; + if ( info->valid & FSTVAL_MODE ) - { FST_WRW ( card, cardMode, info->cardMode ); - } #if FST_DEBUG if ( info->valid & FSTVAL_DEBUG ) - { fst_debug_mask = info->debug; - } #endif return err; @@ -1125,7 +981,7 @@ info->nports = card->nports; info->type = card->type; info->state = card->state; - info->proto = port->proto; + info->proto = FST_GEN_HDLC; info->index = i; #if FST_DEBUG info->debug = fst_debug_mask; @@ -1154,6 +1010,102 @@ static int +fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) +{ + union line_settings *line = &ifr->ifr_settings->ifs_line; + sync_serial_settings sync; + int i; + + if ( copy_from_user ( &sync, &line->sync, sizeof ( sync ))) + return -EFAULT; + + if ( sync.loopback ) + return -EINVAL; + + i = port->index; + + switch ( ifr->ifr_settings->type ) + { + case IF_IFACE_V35: + FST_WRW ( card, portConfig[i].lineInterface, V35 ); + port->hwif = V35; + break; + + case IF_IFACE_V24: + FST_WRW ( card, portConfig[i].lineInterface, V24 ); + port->hwif = V24; + break; + + case IF_IFACE_X21: + FST_WRW ( card, portConfig[i].lineInterface, X21 ); + port->hwif = X21; + break; + + case IF_IFACE_SYNC_SERIAL: + break; + + default: + return -EINVAL; + } + + switch ( sync.clock_type ) + { + case CLOCK_EXT: + FST_WRB ( card, portConfig[i].internalClock, EXTCLK ); + break; + + case CLOCK_INT: + FST_WRB ( card, portConfig[i].internalClock, INTCLK ); + break; + + default: + return -EINVAL; + } + FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate ); + return 0; +} + +static int +fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) +{ + union line_settings *line = &ifr->ifr_settings->ifs_line; + sync_serial_settings sync; + int i; + + /* First check what line type is set, we'll default to reporting X.21 + * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be + * changed + */ + switch ( port->hwif ) + { + case V35: + ifr->ifr_settings->type = IF_IFACE_V35; + break; + case V24: + ifr->ifr_settings->type = IF_IFACE_V24; + break; + case X21: + default: + ifr->ifr_settings->type = IF_IFACE_X21; + break; + } + + i = port->index; + sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed ); + /* Lucky card and linux use same encoding here */ + sync.clock_type = FST_RDB ( card, portConfig[i].internalClock ); + sync.loopback = 0; + + if ( copy_to_user (&line->sync, &sync, sizeof ( sync ))) + return -EFAULT; + + return 0; +} + + +static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) { struct fst_card_info *card; @@ -1164,7 +1116,7 @@ dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data ); - port = dev->priv; + port = dev_to_port ( dev ); card = port->card; if ( !capable ( CAP_NET_ADMIN )) @@ -1201,7 +1153,7 @@ * when going over the top */ if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE - || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) + || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) { return -ENXIO; } @@ -1261,6 +1213,9 @@ case FSTSETCONF: + /* Most of the setting have been moved to the generic ioctls + * this just covers debug and board ident mode now + */ if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info ))) { return -EFAULT; @@ -1268,12 +1223,25 @@ return set_conf_from_info ( card, port, &info ); + case SIOCWANDEV: + switch ( ifr->ifr_settings->type ) + { + case IF_GET_IFACE: + return fst_get_iface ( card, port, ifr ); + + case IF_IFACE_SYNC_SERIAL: + case IF_IFACE_V35: + case IF_IFACE_V24: + case IF_IFACE_X21: + return fst_set_iface ( card, port, ifr ); + + default: + return hdlc_ioctl ( dev, ifr, cmd ); + } + default: - /* Not one of ours. Pass it through to sPPP package */ - if ( port->proto == FST_HDLC || port->proto == FST_PPP ) - return sppp_do_ioctl ( dev, ifr, cmd ); - else - return -EINVAL; + /* Not one of ours. Pass through to HDLC package */ + return hdlc_ioctl ( dev, ifr, cmd ); } } @@ -1306,9 +1274,9 @@ signals = FST_RDL ( port->card, v24DebouncedSts[port->index]); if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD )) - netif_carrier_on ( port->dev ); + netif_carrier_on ( port_to_dev ( port )); else - netif_carrier_off ( port->dev ); + netif_carrier_off ( port_to_dev ( port )); } } @@ -1335,64 +1303,15 @@ static int fst_open ( struct net_device *dev ) { - struct fst_card_info *card; - struct fst_port_info *port; - int orig_mtu; int err; - MOD_INC_USE_COUNT; - - port = dev->priv; - card = port->card; - - switch ( port->proto ) - { - case FST_HDLC: - case FST_PPP: - - orig_mtu = dev->mtu; - - /* Attach to sync PPP module */ - port->pppdev.dev = dev; - sppp_attach ( &port->pppdev ); - - if ( orig_mtu < dev->mtu ) - dev->change_mtu ( dev, orig_mtu ); - - /* Claw back the ioctl routine. We promise to be good and call - * the sync PPP routines once we've eliminated our functions. - */ - dev->do_ioctl = fst_ioctl; - - err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC - ? SPPPIOCCISCO : SPPPIOCPPP ); - if ( err ) - { - sppp_detach ( dev ); - MOD_DEC_USE_COUNT; - return err; - } - - err = sppp_open ( dev ); - if ( err ) - { - sppp_detach ( dev ); - MOD_DEC_USE_COUNT; - return err; - } - break; - - case FST_MONITOR: - case FST_RAW: - break; - - default: - dbg ( DBG_OPEN,"open: Unknown proto %d\n", port->proto ); - break; - } + err = hdlc_open ( dev_to_hdlc ( dev )); + if ( err ) + return err; - fst_openport ( port ); + MOD_INC_USE_COUNT; + fst_openport ( dev_to_port ( dev )); netif_wake_queue ( dev ); return 0; } @@ -1400,34 +1319,22 @@ static int fst_close ( struct net_device *dev ) { - struct fst_port_info *port; - - port = dev->priv; - netif_stop_queue ( dev ); - switch ( port->proto ) - { - case FST_HDLC: - case FST_PPP: - sppp_close ( dev ); - sppp_detach ( dev ); - break; - - case FST_MONITOR: - case FST_RAW: - break; - - default: - dbg ( DBG_OPEN,"close: Unknown proto %d\n", port->proto ); - break; - } - - fst_closeport ( port ); - + fst_closeport ( dev_to_port ( dev )); + hdlc_close ( dev_to_hdlc ( dev )); MOD_DEC_USE_COUNT; return 0; } +static int +fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity ) +{ + /* Setting currently fixed in FarSync card so we check and forget */ + if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT ) + return -EINVAL; + return 0; +} + static void fst_tx_timeout ( struct net_device *dev ) @@ -1436,10 +1343,10 @@ dbg ( DBG_INTR | DBG_TX,"tx_timeout\n"); - port = dev->priv; + port = dev_to_port ( dev ); - port->stats.tx_errors++; - port->stats.tx_aborted_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_aborted_errors++; if ( port->txcnt > 0 ) fst_issue_cmd ( port, ABORTTX ); @@ -1459,22 +1366,15 @@ int pi; int txp; - port = dev->priv; + port = dev_to_port ( dev ); card = port->card; - /* Drop packet if in monitor (rx only) mode */ - if ( port->proto == FST_MONITOR ) - { - dev_kfree_skb ( skb ); - return 0; - } - /* Drop packet with error if we don't have carrier */ if ( ! netif_carrier_ok ( dev )) { dev_kfree_skb ( skb ); - port->stats.tx_errors++; - port->stats.tx_carrier_errors++; + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_carrier_errors++; return 0; } @@ -1484,7 +1384,7 @@ dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len, LEN_TX_BUFFER ); dev_kfree_skb ( skb ); - port->stats.tx_errors++; + port->hdlc.stats.tx_errors++; return 0; } @@ -1498,7 +1398,7 @@ spin_unlock_irqrestore ( &card->card_lock, flags ); dbg ( DBG_TX,"Out of Tx buffers\n"); dev_kfree_skb ( skb ); - port->stats.tx_errors++; + port->hdlc.stats.tx_errors++; return 0; } if ( ++port->txpos >= NUM_TX_BUFFER ) @@ -1518,8 +1418,8 @@ FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len )); FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP ); - port->stats.tx_packets++; - port->stats.tx_bytes += skb->len; + port->hdlc.stats.tx_packets++; + port->hdlc.stats.tx_bytes += skb->len; dev_kfree_skb ( skb ); @@ -1528,18 +1428,6 @@ } -static struct net_device_stats * -fst_get_stats ( struct net_device *dev ) -{ - struct fst_port_info *port; - - if (( port = dev->priv ) != NULL ) - return &port->stats; - else - return NULL; -} - - /* * Card setup having checked hardware resources. * Should be pretty bizarre if we get an error here (kernel memory @@ -1566,34 +1454,13 @@ */ for ( i = 0 ; i < card->nports ; i++ ) { - card->ports[i].if_ptr = &card->ports[i].pppdev; card->ports[i].card = card; card->ports[i].index = i; - card->ports[i].proto = FST_HDLC; card->ports[i].run = 0; - dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL ); - if ( dev == NULL ) - { - printk_err ("Cannot allocate net_device for port %d\n", - i ); - /* No point going any further */ - card->nports = i; - break; - } - memset ( dev, 0, sizeof ( struct net_device )); - card->ports[i].dev = dev; - - if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 ) - { - printk_err ("Cannot allocate i/f name for port %d\n", - i ); - kfree ( dev ); - card->nports = i; - break; - } + dev = hdlc_to_dev ( &card->ports[i].hdlc ); - /* Fill in remainder of the net device info */ + /* Fill in the net device info */ /* Since this is a PCI setup this is purely * informational. Give them the buffer addresses * and basic card I/O. @@ -1609,26 +1476,19 @@ dev->base_addr = card->pci_conf; dev->irq = card->irq; - dev->get_stats = fst_get_stats; - dev->mtu = FST_DEF_MTU; - dev->change_mtu = fst_change_mtu; - dev->priv = &card->ports[i]; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->type = ARPHRD_MYTYPE; - dev->addr_len = 0; - dev->open = fst_open; - dev->stop = fst_close; - dev->hard_start_xmit = fst_start_xmit; - dev->do_ioctl = fst_ioctl; - dev->watchdog_timeo = FST_TX_TIMEOUT; - dev->tx_timeout = fst_tx_timeout; - dev->flags = IFF_POINTOPOINT|IFF_NOARP; - - if (( err = register_netdev ( dev )) < 0 ) - { - printk_err ("Cannot register %s (errno %d)\n", - dev->name, -err ); - kfree ( dev ); + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->open = fst_open; + dev->stop = fst_close; + dev->do_ioctl = fst_ioctl; + dev->watchdog_timeo = FST_TX_TIMEOUT; + dev->tx_timeout = fst_tx_timeout; + card->ports[i].hdlc.attach = fst_attach; + card->ports[i].hdlc.xmit = fst_start_xmit; + + if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 ) + { + printk_err ("Cannot register HDLC device for port %d" + " (errno %d)\n", i, -err ); card->nports = i; break; } @@ -1637,8 +1497,8 @@ spin_lock_init ( &card->card_lock ); printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n", - card->ports[0].dev->name, - card->ports[card->nports-1].dev->name, + hdlc_to_dev(&card->ports[0].hdlc)->name, + hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name, type_strings[card->type], card->irq, card->nports ); } @@ -1789,8 +1649,7 @@ for ( i = 0 ; i < card->nports ; i++ ) { - unregister_netdev ( card->ports[i].dev ); - kfree ( card->ports[i].dev ); + unregister_hdlc_device ( &card->ports[i].hdlc ); } fst_disable_intr ( card ); @@ -1830,4 +1689,3 @@ module_init ( fst_init ); module_exit ( fst_cleanup_module ); -MODULE_LICENSE("GPL"); diff -urN linux-2.5.6-pre3/drivers/net/wan/farsync.h linux-2.5.6/drivers/net/wan/farsync.h --- linux-2.5.6-pre3/drivers/net/wan/farsync.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/drivers/net/wan/farsync.h Thu Mar 7 18:24:48 2002 @@ -32,13 +32,8 @@ * A short common prefix is useful for routines within the driver to avoid * conflict with other similar drivers and I chosen to use "fst_" for this * purpose (FarSite T-series). - * - * Finally the device driver needs a short network interface name. Since - * "hdlc" is already in use I've chosen the even less informative "sync" - * for the present. */ #define FST_NAME "fst" /* In debug/info etc */ -#define FST_NDEV_NAME "sync" /* For net interface */ #define FST_DEV_NAME "farsync" /* For misc interfaces */ diff -urN linux-2.5.6-pre3/drivers/net/wan/hd64570.h linux-2.5.6/drivers/net/wan/hd64570.h --- linux-2.5.6-pre3/drivers/net/wan/hd64570.h Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/drivers/net/wan/hd64570.h Thu Mar 7 18:24:48 2002 @@ -152,7 +152,7 @@ u32 bp; /* Buffer Pointer (24 bits) */ u16 len; /* Data Length */ u8 stat; /* Status */ - u8 unused2; + u8 unused; /* pads to 2-byte boundary */ }__attribute__ ((packed)) pkt_desc; @@ -202,7 +202,11 @@ #define MD0_CRC_ITU_0 0x06 #define MD0_CRC_ITU 0x07 -#define MD2_NRZI 0x20 /* NRZI mode */ +#define MD2_NRZ 0x00 +#define MD2_NRZI 0x20 +#define MD2_MANCHESTER 0x80 +#define MD2_FM_MARK 0xA0 +#define MD2_FM_SPACE 0xC0 #define MD2_LOOPBACK 0x03 /* Local data Loopback */ #define CTL_NORTS 0x01 diff -urN linux-2.5.6-pre3/drivers/net/wan/hd6457x.c linux-2.5.6/drivers/net/wan/hd6457x.c --- linux-2.5.6-pre3/drivers/net/wan/hd6457x.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/wan/hd6457x.c Thu Mar 7 18:24:48 2002 @@ -42,13 +42,7 @@ #error Either hd64570.h or hd64572.h must be included #endif - -static card_t *first_card; -static card_t **new_card = &first_card; - - -/* Maximum events to handle at each interrupt - should I increase it? */ -#define INTR_WORK 4 +static char sca_version[]="1.09"; #define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET) @@ -63,11 +57,19 @@ #define sca_ina(reg, card) sca_inw(reg, card) #define writea(value, ptr) writew(value, ptr) +#else /* HD64572 */ +#define sca_outa(value, reg, card) sca_outl(value, reg, card) +#define sca_ina(reg, card) sca_inl(reg, card) +#define writea(value, ptr) writel(value, ptr) +#endif + static inline int sca_intr_status(card_t *card) { + u8 result = 0; + +#ifdef __HD64570_H /* HD64570 */ u8 isr0 = sca_in(ISR0, card); u8 isr1 = sca_in(ISR1, card); - u8 result = 0; if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0); if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0); @@ -76,19 +78,8 @@ if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0); if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1); - return result; -} - #else /* HD64572 */ -#define sca_outa(value, reg, card) sca_outl(value, reg, card) -#define sca_ina(reg, card) sca_inl(reg, card) -#define writea(value, ptr) writel(value, ptr) - - -static inline int sca_intr_status(card_t *card) -{ u32 isr0 = sca_inl(ISR0, card); - u8 result = 0; if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0); if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0); @@ -97,11 +88,17 @@ if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0); if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1); - return result; -} - #endif /* HD64570 vs HD64572 */ + if (!(result & SCA_INTR_DMAC_TX(0))) + if (sca_in(DSR_TX(0), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(0); + if (!(result & SCA_INTR_DMAC_TX(1))) + if (sca_in(DSR_TX(1), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(1); + + return result; +} @@ -250,8 +247,7 @@ -static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, - u8 rxin) +static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin) { struct sk_buff *skb; u16 len; @@ -289,13 +285,16 @@ openwin(card, 0); #endif skb_put(skb, len); -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len); debug_frame(skb); #endif port->hdlc.stats.rx_packets++; port->hdlc.stats.rx_bytes += skb->len; - hdlc_netif_rx(&port->hdlc, skb); + skb->mac.raw = skb->data; + skb->dev = hdlc_to_dev(&port->hdlc); + skb->protocol = htons(ETH_P_HDLC); + netif_rx(skb); } @@ -320,14 +319,9 @@ pkt_desc *desc; u32 cda = sca_ina(dmac + CDAL, card); - if (cda == desc_off) + if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* No frame received */ -#ifdef __HD64572_H - if (cda == desc_off + 8) - break; /* SCA-II updates CDA in 2 steps */ -#endif - desc = desc_address(port, port->rxin, 0); stat = readb(&desc->stat); if (!(stat & ST_RX_EOM)) @@ -371,20 +365,17 @@ DSR_TX(phy_node(port)), card); while (1) { - u32 desc_off = desc_offset(port, port->txlast, 1); pkt_desc *desc; - u16 len; - if (sca_ina(dmac + CDAL, card) == desc_off) + u32 desc_off = desc_offset(port, port->txlast, 1); + u32 cda = sca_ina(dmac + CDAL, card); + if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* Transmitter is/will_be sending this frame */ desc = desc_address(port, port->txlast, 1); - len = readw(&desc->len); - port->hdlc.stats.tx_packets++; - port->hdlc.stats.tx_bytes += len; + port->hdlc.stats.tx_bytes += readw(&desc->len); writeb(0, &desc->stat); /* Free descriptor */ - port->txlast = (port->txlast + 1) % port_to_card(port)->ring_buffers; } @@ -398,7 +389,8 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) { card_t *card = dev_id; - int boguscnt = INTR_WORK; +/* Maximum events to handle at each interrupt - should I increase it? */ + int boguscnt = 4; int i; u8 stat; @@ -421,9 +413,11 @@ } if (--boguscnt < 0) { +#if 0 printk(KERN_ERR "%s: too much work at " "interrupt\n", hdlc_to_name(&port->hdlc)); +#endif goto exit; } } @@ -437,47 +431,22 @@ -static inline int sca_set_loopback(port_t *port, int line) +static void sca_set_port(port_t *port) { card_t* card = port_to_card(port); u8 msci = get_msci(port); u8 md2 = sca_in(msci + MD2, card); - - switch(line) { - case LINE_DEFAULT: - md2 &= ~MD2_LOOPBACK; - port->line &= ~LINE_LOOPBACK; - break; - - case LINE_LOOPBACK: - md2 |= MD2_LOOPBACK; - port->line |= LINE_LOOPBACK; - break; - - default: - return -EINVAL; - } - - sca_out(md2, msci + MD2, card); - return 0; -} - - - -static void sca_set_clock(port_t *port) -{ - card_t *card = port_to_card(port); - u8 msci = get_msci(port); unsigned int tmc, br = 10, brv = 1024; - if (port->clkrate > 0) { + + if (port->settings.clock_rate > 0) { /* Try lower br for better accuracy*/ do { br--; brv >>= 1; /* brv = 2^9 = 512 max in specs */ /* Baud Rate = CLOCK_BASE / TMC / 2^BR */ - tmc = CLOCK_BASE / (brv * port->clkrate); + tmc = CLOCK_BASE / (brv * port->settings.clock_rate); }while(br > 1 && tmc <= 128); if (tmc < 1) { @@ -487,11 +456,11 @@ } else if (tmc > 255) tmc = 256; /* tmc=0 means 256 - low baud rates */ - port->clkrate = CLOCK_BASE / (brv * tmc); + port->settings.clock_rate = CLOCK_BASE / (brv * tmc); } else { br = 9; /* Minimum clock rate */ tmc = 256; /* 8bit = 0 */ - port->clkrate = CLOCK_BASE / (256 * 512); + port->settings.clock_rate = CLOCK_BASE / (256 * 512); } port->rxs = (port->rxs & ~CLK_BRG_MASK) | br; @@ -509,27 +478,59 @@ /* Set BRG bits */ sca_out(port->rxs, msci + RXS, card); sca_out(port->txs, msci + TXS, card); + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + else + md2 &= ~MD2_LOOPBACK; + + sca_out(md2, msci + MD2, card); + } -static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi) +static void sca_open(hdlc_device *hdlc) { + port_t *port = hdlc_to_port(hdlc); card_t* card = port_to_card(port); u8 msci = get_msci(port); - u8 md2 = (nrzi ? MD2_NRZI : 0) | - ((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0); - u8 ctl = (idle ? CTL_IDLE : 0); -#ifdef __HD64572_H - ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */ -#endif + u8 md0, md2; + + switch(port->encoding) { + case ENCODING_NRZ: md2 = MD2_NRZ; break; + case ENCODING_NRZI: md2 = MD2_NRZI; break; + case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break; + case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break; + default: md2 = MD2_MANCHESTER; + } + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + + switch(port->parity) { + case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break; + case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break; +#ifdef __HD64570_H + case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break; +#else + case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break; +#endif + case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break; + default: md0 = MD0_HDLC | MD0_CRC_NONE; + } sca_out(CMD_RESET, msci + CMD, card); - sca_out(MD0_HDLC | crc, msci + MD0, card); + sca_out(md0, msci + MD0, card); sca_out(0x00, msci + MD1, card); /* no address field check */ sca_out(md2, msci + MD2, card); sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */ - sca_out(ctl, msci + CTL, card); +#ifdef __HD64570_H + sca_out(CTL_IDLE, msci + CTL, card); +#else + /* Skip the rest of underrun frame */ + sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card); +#endif #ifdef __HD64570_H /* Allow at least 8 bytes before requesting RX DMA operation */ @@ -539,24 +540,28 @@ sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */ #else sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */ - /* Setting than to larger value may cause Illegal Access */ - sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */ - sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ - sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x3C, msci + TFS, card); /* +1 = TX start */ + sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */ + sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ #endif +/* We're using the following interrupts: + - TXINT (DMAC completed all transmisions, underflow or CTS change) + - all DMA interrupts +*/ #ifdef __HD64570_H /* MSCI TX INT IRQ enable */ sca_out(IE0_TXINT, msci + IE0, card); - sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */ + sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */ sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08), IER0, card); /* DMA IRQ enable */ sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F), IER1, card); #else - /* MSCI TX INT and underrrun IRQ enable */ + /* MSCI TX INT IRQ enable */ sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card); /* DMA & MSCI IRQ enable */ sca_outl(sca_in(IER0, card) | @@ -573,11 +578,52 @@ sca_out(port->txs, msci + TXS, card); sca_out(CMD_TX_ENABLE, msci + CMD, card); sca_out(CMD_RX_ENABLE, msci + CMD, card); + + netif_start_queue(hdlc_to_dev(hdlc)); +} + + + +static void sca_close(hdlc_device *hdlc) +{ + port_t *port = hdlc_to_port(hdlc); + + /* reset channel */ + netif_stop_queue(hdlc_to_dev(hdlc)); + sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); } -#ifdef DEBUG_RINGS +static int sca_attach(hdlc_device *hdlc, unsigned short encoding, + unsigned short parity) +{ + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI && + encoding != ENCODING_FM_MARK && + encoding != ENCODING_FM_SPACE && + encoding != ENCODING_MANCHESTER) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC16_PR0 && + parity != PARITY_CRC16_PR1 && +#ifdef __HD64570_H + parity != PARITY_CRC16_PR0_CCITT && +#else + parity != PARITY_CRC32_PR1_CCITT && +#endif + parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + + hdlc_to_port(hdlc)->encoding = encoding; + hdlc_to_port(hdlc)->parity = parity; + return 0; +} + + + +#ifdef CONFIG_HDLC_DEBUG_RINGS static void sca_dump_rings(hdlc_device *hdlc) { port_t *port = hdlc_to_port(hdlc); @@ -644,34 +690,14 @@ openwin(card, page); /* Restore original page */ #endif } -#endif /* DEBUG_RINGS */ - - - -static void sca_open(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0); - netif_start_queue(hdlc_to_dev(hdlc)); -} - - -static void sca_close(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - /* reset channel */ - netif_stop_queue(hdlc_to_dev(hdlc)); - sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); -} +#endif /* CONFIG_HDLC_DEBUG_RINGS */ -static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb) +static int sca_xmit(struct sk_buff *skb, struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - struct net_device *dev = hdlc_to_dev(hdlc); card_t *card = port_to_card(port); pkt_desc *desc; u32 buff, len; @@ -685,7 +711,7 @@ desc = desc_address(port, port->txin + 1, 1); if (readb(&desc->stat)) { /* allow 1 packet gap */ /* should never happen - previous xmit should stop queue */ -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); #endif netif_stop_queue(dev); @@ -693,7 +719,7 @@ return 1; /* request packet to be queued */ } -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len); debug_frame(skb); #endif diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc.c linux-2.5.6/drivers/net/wan/hdlc.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/drivers/net/wan/hdlc.c Wed Dec 31 16:00:00 1969 @@ -1,1453 +0,0 @@ -/* - * Generic HDLC support routines for Linux - * - * Copyright (C) 1999, 2000 Krzysztof Halasa - * - * 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. - * - * Current status: - * - this is work in progress - * - not heavily tested on SMP - * - currently supported: - * * raw IP-in-HDLC - * * Cisco HDLC - * * Frame Relay with ANSI or CCITT LMI (both user and network side) - * * PPP (using syncppp.c) - * * X.25 - * - * Use sethdlc utility to set line parameters, protocol and PVCs - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* #define DEBUG_PKT */ -/* #define DEBUG_HARD_HEADER */ -/* #define DEBUG_FECN */ -/* #define DEBUG_BECN */ - -static const char* version = "HDLC support module revision 1.02 for Linux 2.4"; - - -#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ -#define CISCO_UNICAST 0x0F /* Cisco unicast address */ -#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ -#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ -#define CISCO_ADDR_REQ 0 /* Cisco address request */ -#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ -#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - -/******************************************************** - * - * Cisco HDLC support - * - *******************************************************/ - -static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, - unsigned int len) -{ - hdlc_header *data; -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); -#endif - - skb_push(skb, sizeof(hdlc_header)); - data = (hdlc_header*)skb->data; - if (type == CISCO_KEEPALIVE) - data->address = CISCO_MULTICAST; - else - data->address = CISCO_UNICAST; - data->control = 0; - data->protocol = htons(type); - - return sizeof(hdlc_header); -} - - - -static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, - u32 par1, u32 par2) -{ - struct sk_buff *skb; - cisco_packet *data; - - skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet)); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n", - hdlc_to_name(hdlc)); - return; - } - skb_reserve(skb, 4); - cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, - NULL, NULL, 0); - data = (cisco_packet*)skb->tail; - - data->type = htonl(type); - data->par1 = htonl(par1); - data->par2 = htonl(par2); - data->rel = 0xFFFF; - data->time = htonl(jiffies * 1000 / HZ); - - skb_put(skb, sizeof(cisco_packet)); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - hdlc_header *data = (hdlc_header*)skb->data; - cisco_packet *cisco_data; - struct in_device *in_dev; - u32 addr, mask; - - if (skb->lenaddress != CISCO_MULTICAST && - data->address != CISCO_UNICAST) - goto rx_error; - - skb_pull(skb, sizeof(hdlc_header)); - - switch(ntohs(data->protocol)) { - case ETH_P_IP: - case ETH_P_IPX: - case ETH_P_IPV6: - skb->protocol = data->protocol; - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - - case CISCO_SYS_INFO: - /* Packet is not needed, drop it. */ - dev_kfree_skb_any(skb); - return; - - case CISCO_KEEPALIVE: - if (skb->len != CISCO_PACKET_LEN && - skb->len != CISCO_BIG_PACKET_LEN) { - printk(KERN_INFO "%s: Invalid length of Cisco " - "control packet (%d bytes)\n", - hdlc_to_name(hdlc), skb->len); - goto rx_error; - } - - cisco_data = (cisco_packet*)skb->data; - - switch(ntohl (cisco_data->type)) { - case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ - in_dev = hdlc_to_dev(hdlc)->ip_ptr; - addr = 0; - mask = ~0; /* is the mask correct? */ - - if (in_dev != NULL) { - struct in_ifaddr **ifap = &in_dev->ifa_list; - - while (*ifap != NULL) { - if (strcmp(hdlc_to_name(hdlc), - (*ifap)->ifa_label) == 0) { - addr = (*ifap)->ifa_local; - mask = (*ifap)->ifa_mask; - break; - } - ifap = &(*ifap)->ifa_next; - } - - cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, - addr, mask); - } - dev_kfree_skb_any(skb); - return; - - case CISCO_ADDR_REPLY: - printk(KERN_INFO "%s: Unexpected Cisco IP address " - "reply\n", hdlc_to_name(hdlc)); - goto rx_error; - - case CISCO_KEEPALIVE_REQ: - hdlc->lmi.rxseq = ntohl(cisco_data->par1); - if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) { - hdlc->lmi.last_poll = jiffies; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) { - u32 sec, min, hrs, days; - sec = ntohl(cisco_data->time) / 1000; - min = sec / 60; sec -= min * 60; - hrs = min / 60; min -= hrs * 60; - days = hrs / 24; hrs -= days * 24; - printk(KERN_INFO "%s: Link up (peer " - "uptime %ud%uh%um%us)\n", - hdlc_to_name(hdlc), days, hrs, - min, sec); - } - hdlc->lmi.state |= LINK_STATE_RELIABLE; - } - - dev_kfree_skb_any(skb); - return; - } /* switch(keepalive type) */ - } /* switch(protocol) */ - - printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), - data->protocol); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void cisco_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) { - hdlc->lmi.state &= ~LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); - } - - cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq, - hdlc->lmi.rxseq); - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - - hdlc->timer.function = cisco_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -/****************************************************************** - * - * generic Frame Relay routines - * - *****************************************************************/ - - -static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, unsigned int len) -{ - u16 head_len; - - if (!daddr) - daddr = dev->broadcast; - -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); -#endif - - switch(type) { - case ETH_P_IP: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IP; - break; - - case ETH_P_IPV6: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IPV6; - break; - - case LMI_PROTO: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = LMI_PROTO; - break; - - default: - head_len = 10; - skb_push(skb, head_len); - skb->data[3] = FR_PAD; - skb->data[4] = NLPID_SNAP; - skb->data[5] = FR_PAD; - skb->data[6] = FR_PAD; - skb->data[7] = FR_PAD; - skb->data[8] = type>>8; - skb->data[9] = (u8)type; - } - - memcpy(skb->data, daddr, 2); - skb->data[2] = FR_UI; - - return head_len; -} - - - -static inline void fr_log_dlci_active(pvc_device *pvc) -{ - printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), - pvc->state & PVC_STATE_ACTIVE ? "" : "in", - pvc->state & PVC_STATE_NEW ? " new" : ""); -} - - - -static inline u8 fr_lmi_nextseq(u8 x) -{ - x++; - return x ? x : 1; -} - - - -static void fr_lmi_send(hdlc_device *hdlc, int fullrep) -{ - struct sk_buff *skb; - pvc_device *pvc = hdlc->first_pvc; - int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; - int stat_len = 3; - u8 *data; - int i = 0; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - len += hdlc->pvc_count * (2 + stat_len); - if (len > HDLC_MAX_MTU) { - printk(KERN_WARNING "%s: Too many PVCs while sending " - "LMI full report\n", hdlc_to_name(hdlc)); - return; - } - } - - skb = dev_alloc_skb(len); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", - hdlc_to_name(hdlc)); - return; - } - memset(skb->data, 0, len); - skb_reserve(skb, 4); - fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); - data = skb->tail; - data[i++] = LMI_CALLREF; - data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; - if (mode_is(hdlc, MODE_FR_ANSI)) - data[i++] = LMI_ANSI_LOCKSHIFT; - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : - LMI_REPTYPE; - data[i++] = LMI_REPT_LEN; - data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; - - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; - data[i++] = LMI_INTEG_LEN; - data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); - data[i++] = hdlc->lmi.rxseq; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - while (pvc) { - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT:LMI_PVCSTAT; - data[i++] = stat_len; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (pvc->netdev.flags & IFF_UP) && - !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { - pvc->state |= PVC_STATE_NEW; - fr_log_dlci_active(pvc); - } - - dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), - data+i, pvc->state); - i += stat_len; - pvc = pvc->next; - } - } - - skb_put(skb, i); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void fr_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - int i, cnt = 0, reliable; - u32 list; - - if (mode_is(hdlc, MODE_DCE)) - reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); - else { - hdlc->lmi.last_errors <<= 1; /* Shift the list */ - if (hdlc->lmi.state & LINK_STATE_REQUEST) { - printk(KERN_INFO "%s: No LMI status reply received\n", - hdlc_to_name(hdlc)); - hdlc->lmi.last_errors |= 1; - } - - for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393; - i++, list >>= 1) - cnt += (list & 1); /* errors count */ - - reliable = (cnt < hdlc->lmi.N392); - } - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != - (reliable ? LINK_STATE_RELIABLE : 0)) { - pvc_device *pvc = hdlc->first_pvc; - - while (pvc) {/* Deactivate all PVCs */ - pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); - pvc = pvc->next; - } - - hdlc->lmi.state ^= LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), - reliable ? "" : "un"); - - if (reliable) { - hdlc->lmi.N391cnt = 0; /* Request full status */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - } - - if (mode_is(hdlc, MODE_DCE)) - hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; - else { - if (hdlc->lmi.N391cnt) - hdlc->lmi.N391cnt--; - - fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); - - hdlc->lmi.state |= LINK_STATE_REQUEST; - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - } - - hdlc->timer.function = fr_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) -{ - int stat_len; - pvc_device *pvc; - int reptype = -1, error; - u8 rxseq, txseq; - int i; - - if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? - LMI_ANSI_LENGTH : LMI_LENGTH)) { - printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); - return 1; - } - - if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? - LMI_STATUS : LMI_STATUS_ENQUIRY)) { - printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", - hdlc_to_name(hdlc), skb->data[2], - mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); - return 1; - } - - i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; - - if (skb->data[i] != - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { - printk(KERN_INFO "%s: Not a report type=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - reptype = skb->data[i++]; - - if (skb->data[i]!= - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { - printk(KERN_INFO "%s: Unsupported status element=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ - rxseq = skb->data[i++]; /* Should confirm our sequence */ - - txseq = hdlc->lmi.txseq; - - if (mode_is(hdlc, MODE_DCE)) { - if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { - printk(KERN_INFO "%s: Unsupported report type=%x\n", - hdlc_to_name(hdlc), reptype); - return 1; - } - } - - error = 0; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) - error = 1; - - if (rxseq == 0 || rxseq != txseq) { - hdlc->lmi.N391cnt = 0; /* Ask for full report next time */ - error = 1; - } - - if (mode_is(hdlc, MODE_DCE)) { - if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) { -/* Stop sending full report - the last one has been confirmed by DTE */ - hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; - pvc = hdlc->first_pvc; - while (pvc) { - if (pvc->state & PVC_STATE_NEW) { - pvc->state &= ~PVC_STATE_NEW; - pvc->state |= PVC_STATE_ACTIVE; - fr_log_dlci_active(pvc); - -/* Tell DTE that new PVC is now active */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - pvc = pvc->next; - } - } - - if (hdlc->lmi.state & LINK_STATE_CHANGED) { - reptype = LMI_FULLREP; - hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; - hdlc->lmi.state &= ~LINK_STATE_CHANGED; - } - - fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); - return 0; - } - - /* DTE */ - - if (reptype != LMI_FULLREP || error) - return 0; - - stat_len = 3; - pvc = hdlc->first_pvc; - - while (pvc) { - pvc->newstate = 0; - pvc = pvc->next; - } - - while (skb->len >= i + 2 + stat_len) { - u16 dlci; - u8 state = 0; - - if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { - printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - if (skb->data[i] != stat_len) { - printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - dlci = status_to_dlci(hdlc, skb->data+i, &state); - pvc = find_pvc(hdlc, dlci); - - if (pvc) - pvc->newstate = state; - else if (state == PVC_STATE_NEW) - printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", - hdlc_to_name(hdlc), dlci); - - i += stat_len; - } - - pvc = hdlc->first_pvc; - - while (pvc) { - if (pvc->newstate == PVC_STATE_NEW) - pvc->newstate = PVC_STATE_ACTIVE; - - pvc->newstate |= (pvc->state & - ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); - if (pvc->state != pvc->newstate) { - pvc->state = pvc->newstate; - fr_log_dlci_active(pvc); - } - pvc = pvc->next; - } - - /* Next full report after N391 polls */ - hdlc->lmi.N391cnt = hdlc->lmi.N391; - - return 0; -} - - - -static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - fr_hdr *fh = (fr_hdr*)skb->data; - u8 *data = skb->data; - u16 dlci; - pvc_device *pvc; - - if (skb->len<4 || fh->ea1 || data[2] != FR_UI) - goto rx_error; - - dlci = q922_to_dlci(skb->data); - - if (dlci == LMI_DLCI) { - if (data[3] == LMI_PROTO) { - if (fr_lmi_recv(hdlc, skb)) - goto rx_error; - else { - /* No request pending */ - hdlc->lmi.state &= ~LINK_STATE_REQUEST; - hdlc->lmi.last_poll = jiffies; - dev_kfree_skb_any(skb); - return; - } - } - - printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", - hdlc_to_name(hdlc)); - goto rx_error; - } - - pvc = find_pvc(hdlc, dlci); - if (!pvc) { -#ifdef DEBUG_PKT - printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; - } - - if ((pvc->netdev.flags & IFF_UP) == 0) { -#ifdef DEBUG_PKT - printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; - } - - pvc->stats.rx_packets++; /* PVC traffic */ - pvc->stats.rx_bytes += skb->len; - - if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), - fh->fecn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_FECN; - } - - if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), - fh->becn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_BECN; - } - - if (pvc->state & PVC_STATE_BECN) - pvc->stats.rx_compressed++; - - if (data[3] == NLPID_IP) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - - if (data[3] == NLPID_IPV6) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IPV6); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD && - data[6] == FR_PAD && data[7] == FR_PAD && - ((data[8]<<8) | data[9]) == ETH_P_ARP) { - skb_pull(skb, 10); - skb->protocol = htons(ETH_P_ARP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - printk(KERN_INFO "%s: Unusupported protocol %x\n", - hdlc_to_name(hdlc), data[3]); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void fr_cisco_open(hdlc_device *hdlc) -{ - hdlc->lmi.state = LINK_STATE_CHANGED; - hdlc->lmi.txseq = hdlc->lmi.rxseq = 0; - hdlc->lmi.last_errors = 0xFFFFFFFF; - hdlc->lmi.N391cnt = 0; - - init_timer(&hdlc->timer); - hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */ - hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer; - hdlc->timer.data = (unsigned long)hdlc; - add_timer(&hdlc->timer); -} - - - -static void fr_cisco_close(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - - del_timer_sync(&hdlc->timer); - - while(pvc) { /* NULL in Cisco mode */ - dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ - pvc = pvc->next; - } -} - - - -/****************************************************************** - * - * generic HDLC routines - * - *****************************************************************/ - - - -static int hdlc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -/******************************************************** - * - * PVC device routines - * - *******************************************************/ - -static int pvc_open(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - int result = 0; - - if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) - return -EIO; /* Master must be UP in order to activate PVC */ - - memset(&(pvc->stats), 0, sizeof(struct net_device_stats)); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc) - result = pvc->master->open_pvc(pvc); - if (result) - return result; - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_close(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc) - pvc->master->close_pvc(pvc); - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - - if (pvc->state & PVC_STATE_ACTIVE) { - skb->dev = hdlc_to_dev(pvc->master); - pvc->stats.tx_bytes += skb->len; - pvc->stats.tx_packets++; - if (pvc->state & PVC_STATE_FECN) - pvc->stats.tx_compressed++; /* TX Congestion counter */ - dev_queue_xmit(skb); - } else { - pvc->stats.tx_dropped++; - dev_kfree_skb(skb); - } - - return 0; -} - - - -static struct net_device_stats *pvc_get_stats(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - return &pvc->stats; -} - - - -static int pvc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -static void destroy_pvc_list(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - while(pvc) { - pvc_device *next = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - pvc = next; - } - - hdlc->first_pvc = NULL; /* All PVCs destroyed */ - hdlc->pvc_count = 0; - hdlc->lmi.state |= LINK_STATE_CHANGED; -} - - - -/******************************************************** - * - * X.25 protocol support routines - * - *******************************************************/ - -#ifdef CONFIG_HDLC_X25 -/* These functions are callbacks called by LAPB layer */ - -void x25_connect_disconnect(void *token, int reason, int code) -{ - hdlc_device *hdlc = token; - struct sk_buff *skb; - unsigned char *ptr; - - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); - return; - } - - ptr = skb_put(skb, 1); - *ptr = code; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); -} - -void x25_connected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 1); -} - -void x25_disconnected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 2); -} - - -int x25_data_indication(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - unsigned char *ptr; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - return netif_rx(skb); -} - - -void x25_data_transmit(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - hdlc->xmit(hdlc, skb); /* Ignore return value :-( */ -} -#endif /* CONFIG_HDLC_X25 */ - - -/******************************************************** - * - * HDLC device routines - * - *******************************************************/ - -static int hdlc_open(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - int result; - - if (hdlc->mode == MODE_NONE) - return -ENOSYS; - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_open(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_attach(&hdlc->pppdev); - /* sppp_attach nukes them. We don't need syncppp's ioctl */ - dev->do_ioctl = hdlc_ioctl; - hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO; - dev->type = ARPHRD_PPP; - result = sppp_open(dev); - if (result) { - sppp_detach(dev); - return result; - } - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) { - struct lapb_register_struct cb; - - cb.connect_confirmation = x25_connected; - cb.connect_indication = x25_connected; - cb.disconnect_confirmation = x25_disconnected; - cb.disconnect_indication = x25_disconnected; - cb.data_indication = x25_data_indication; - cb.data_transmit = x25_data_transmit; - - result = lapb_register(hdlc, &cb); - if (result != LAPB_OK) - return result; - } -#endif - result = hdlc->open(hdlc); - if (result) { - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - } - - return result; -} - - - -static int hdlc_close(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - hdlc->close(hdlc); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - return 0; -} - - - -static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - -#ifdef CONFIG_HDLC_X25 - if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) { - int result; - - - /* X.25 to LAPB */ - switch (skb->data[0]) { - case 0: /* Data to be transmitted */ - skb_pull(skb, 1); - if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) - dev_kfree_skb(skb); - return 0; - - case 1: - if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { - if (result == LAPB_CONNECTED) { - /* Send connect confirm. msg to level 3 */ - x25_connected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB connect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - case 2: - if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) { - if (result == LAPB_NOTCONNECTED) { - /* Send disconnect confirm. msg to level 3 */ - x25_disconnected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB disconnect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - default: - /* to be defined */ - break; - } - - dev_kfree_skb(skb); - return 0; - } /* MODE_X25 */ -#endif /* CONFIG_HDLC_X25 */ - - return hdlc->xmit(hdlc, skb); -} - - - -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb) -{ -/* skb contains raw HDLC frame, in both hard- and software modes */ - skb->mac.raw = skb->data; - - switch(hdlc->mode & MODE_MASK) { - case MODE_HDLC: - skb->protocol = htons(ETH_P_IP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - - case MODE_FR: - fr_netif(hdlc, skb); - return; - - case MODE_CISCO: - cisco_netif(hdlc, skb); - return; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#if 0 - sppp_input(hdlc_to_dev(hdlc), skb); -#else - skb->protocol = htons(ETH_P_WAN_PPP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); -#endif - return; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - skb->dev = hdlc_to_dev(hdlc); - if (lapb_data_received(hdlc, skb) == LAPB_OK) - return; - break; -#endif - } - - hdlc->stats.rx_errors++; - dev_kfree_skb_any(skb); -} - - - -static struct net_device_stats *hdlc_get_stats(struct net_device *dev) -{ - return &dev_to_hdlc(dev)->stats; -} - - - -static int hdlc_set_mode(hdlc_device *hdlc, int mode) -{ - int result = -1; /* Default to soft modes */ - struct net_device *dev = hdlc_to_dev(hdlc); - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dev->flags & IFF_UP) - return -EBUSY; - - dev->addr_len = 0; - dev->hard_header = NULL; - hdlc->mode = MODE_NONE; - - if (!(mode & MODE_SOFT)) - switch(mode & MODE_MASK) { - case MODE_HDLC: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - break; - - case MODE_CISCO: /* By card */ -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: -#endif - case MODE_FR: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, mode) : -ENOSYS; - break; - - default: - return -EINVAL; - } - - if (result) { - mode |= MODE_SOFT; /* Try "host software" protocol */ - - switch(mode & MODE_MASK) { - case MODE_CISCO: - dev->hard_header = cisco_hard_header; - break; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: - break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - break; -#endif - - case MODE_FR: - dev->hard_header = fr_hard_header; - dev->addr_len = 2; - *(u16*)dev->dev_addr = htons(LMI_DLCI); - dlci_to_q922(dev->broadcast, LMI_DLCI); - break; - - default: - return -EINVAL; - } - - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - } - - if (result) - return result; - - hdlc->mode = mode; - switch(mode & MODE_MASK) { -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: dev->type = ARPHRD_PPP; break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: dev->type = ARPHRD_X25; break; -#endif - case MODE_FR: dev->type = ARPHRD_FRAD; break; - case MODE_CISCO: dev->type = ARPHRD_CISCO; break; - default: dev->type = ARPHRD_RAWHDLC; - } - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - destroy_pvc_list(hdlc); - return 0; -} - - - -static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci) -{ - pvc_device **pvc_p = &hdlc->first_pvc; - pvc_device *pvc; - int result, create = 1; /* Create or delete PVC */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dlci<0) { - dlci = -dlci; - create = 0; - } - - if(dlci <= 0 || dlci >= 1024) - return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */ - - if(!mode_is(hdlc, MODE_FR)) - return -EINVAL; /* Only meaningfull on FR */ - - while(*pvc_p) { - if (netdev_dlci(&(*pvc_p)->netdev) == dlci) - break; - pvc_p = &(*pvc_p)->next; - } - - if (create) { /* Create PVC */ - if (*pvc_p != NULL) - return -EEXIST; - - pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); - if (!pvc) { - printk(KERN_WARNING "%s: Memory squeeze on " - "hdlc_fr_pvc()\n", hdlc_to_name(hdlc)); - return -ENOBUFS; - } - memset(pvc, 0, sizeof(pvc_device)); - - pvc->netdev.hard_start_xmit = pvc_xmit; - pvc->netdev.get_stats = pvc_get_stats; - pvc->netdev.open = pvc_open; - pvc->netdev.stop = pvc_close; - pvc->netdev.change_mtu = pvc_change_mtu; - pvc->netdev.mtu = HDLC_MAX_MTU; - - pvc->netdev.type = ARPHRD_DLCI; - pvc->netdev.hard_header_len = 16; - pvc->netdev.hard_header = fr_hard_header; - pvc->netdev.tx_queue_len = 0; - pvc->netdev.flags = IFF_POINTOPOINT; - - pvc->master = hdlc; - *(u16*)pvc->netdev.dev_addr = htons(dlci); - dlci_to_q922(pvc->netdev.broadcast, dlci); - pvc->netdev.addr_len = 2; - pvc->netdev.irq = hdlc_to_dev(hdlc)->irq; - - result = dev_alloc_name(&pvc->netdev, "pvc%d"); - if (result < 0) { - kfree(pvc); - *pvc_p = NULL; - return result; - } - - if (register_netdevice(&pvc->netdev) != 0) { - kfree(pvc); - *pvc_p = NULL; - return -EIO; - } - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) { - result = hdlc->create_pvc(pvc); - if (result) { - unregister_netdevice(&pvc->netdev); - kfree(pvc); - *pvc_p = NULL; - return result; - } - } - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count++; - return 0; - } - - if (*pvc_p == NULL) /* Delete PVC */ - return -ENOENT; - - pvc = *pvc_p; - - if (pvc->netdev.flags & IFF_UP) - return -EBUSY; /* PVC in use */ - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc) - hdlc->destroy_pvc(pvc); - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count--; - *pvc_p = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - return 0; -} - - - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - switch(cmd) { - case HDLCGMODE: - ifr->ifr_ifru.ifru_ivalue = hdlc->mode; - return 0; - - case HDLCSMODE: - return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue); - - case HDLCPVC: - return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue); - - default: - if (hdlc->ioctl != NULL) - return hdlc->ioctl(hdlc, ifr, cmd); - } - - return -EINVAL; -} - - - -static int hdlc_init(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - dev->get_stats = hdlc_get_stats; - dev->open = hdlc_open; - dev->stop = hdlc_close; - dev->hard_start_xmit = hdlc_xmit; - dev->do_ioctl = hdlc_ioctl; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - - dev->type = ARPHRD_RAWHDLC; - dev->hard_header_len = 16; - - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - - return 0; -} - - - -int register_hdlc_device(hdlc_device *hdlc) -{ - int result; - struct net_device *dev = hdlc_to_dev(hdlc); - - dev->init = hdlc_init; - dev->priv = &hdlc->syncppp_ptr; - hdlc->syncppp_ptr = &hdlc->pppdev; - hdlc->pppdev.dev = dev; - hdlc->mode = MODE_NONE; - hdlc->lmi.T391 = 10; /* polling verification timer */ - hdlc->lmi.T392 = 15; /* link integrity verification polling timer */ - hdlc->lmi.N391 = 6; /* full status polling counter */ - hdlc->lmi.N392 = 3; /* error threshold */ - hdlc->lmi.N393 = 4; /* monitored events count */ - - result = dev_alloc_name(dev, "hdlc%d"); - if (result<0) - return result; - - result = register_netdev(dev); - if (result != 0) - return -EIO; - - MOD_INC_USE_COUNT; - return 0; -} - - - -void unregister_hdlc_device(hdlc_device *hdlc) -{ - destroy_pvc_list(hdlc); - unregister_netdev(hdlc_to_dev(hdlc)); - MOD_DEC_USE_COUNT; -} - -MODULE_AUTHOR("Krzysztof Halasa "); -MODULE_DESCRIPTION("HDLC support module"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(hdlc_netif_rx); -EXPORT_SYMBOL(register_hdlc_device); -EXPORT_SYMBOL(unregister_hdlc_device); - -static int __init hdlc_module_init(void) -{ - printk(KERN_INFO "%s\n", version); - return 0; -} - - -module_init(hdlc_module_init); diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_cisco.c linux-2.5.6/drivers/net/wan/hdlc_cisco.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_cisco.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_cisco.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,293 @@ +/* + * Generic HDLC support routines for Linux + * Cisco HDLC support + * + * Copyright (C) 2000 - 2001 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ +#define CISCO_UNICAST 0x0F /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + + +static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, + u16 type, void *daddr, void *saddr, + unsigned int len) +{ + hdlc_header *data; +#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER + printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); +#endif + + skb_push(skb, sizeof(hdlc_header)); + data = (hdlc_header*)skb->data; + if (type == CISCO_KEEPALIVE) + data->address = CISCO_MULTICAST; + else + data->address = CISCO_UNICAST; + data->control = 0; + data->protocol = htons(type); + + return sizeof(hdlc_header); +} + + + +static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, + u32 par1, u32 par2) +{ + struct sk_buff *skb; + cisco_packet *data; + + skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet)); + if (!skb) { + printk(KERN_WARNING + "%s: Memory squeeze on cisco_keepalive_send()\n", + hdlc_to_name(hdlc)); + return; + } + skb_reserve(skb, 4); + cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, + NULL, NULL, 0); + data = (cisco_packet*)skb->tail; + + data->type = htonl(type); + data->par1 = htonl(par1); + data->par2 = htonl(par2); + data->rel = 0xFFFF; + data->time = htonl(jiffies * 1000 / HZ); + + skb_put(skb, sizeof(cisco_packet)); + skb->priority = TC_PRIO_CONTROL; + skb->dev = hdlc_to_dev(hdlc); + + dev_queue_xmit(skb); +} + + + +static void cisco_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + hdlc_header *data = (hdlc_header*)skb->data; + cisco_packet *cisco_data; + struct in_device *in_dev; + u32 addr, mask; + + if (skb->len < sizeof(hdlc_header)) + goto rx_error; + + if (data->address != CISCO_MULTICAST && + data->address != CISCO_UNICAST) + goto rx_error; + + skb_pull(skb, sizeof(hdlc_header)); + + switch(ntohs(data->protocol)) { + case ETH_P_IP: + case ETH_P_IPX: + case ETH_P_IPV6: + skb->protocol = data->protocol; + skb->dev = hdlc_to_dev(hdlc); + netif_rx(skb); + return; + + case CISCO_SYS_INFO: + /* Packet is not needed, drop it. */ + dev_kfree_skb_any(skb); + return; + + case CISCO_KEEPALIVE: + if (skb->len != CISCO_PACKET_LEN && + skb->len != CISCO_BIG_PACKET_LEN) { + printk(KERN_INFO "%s: Invalid length of Cisco " + "control packet (%d bytes)\n", + hdlc_to_name(hdlc), skb->len); + goto rx_error; + } + + cisco_data = (cisco_packet*)skb->data; + + switch(ntohl (cisco_data->type)) { + case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ + in_dev = hdlc_to_dev(hdlc)->ip_ptr; + addr = 0; + mask = ~0; /* is the mask correct? */ + + if (in_dev != NULL) { + struct in_ifaddr **ifap = &in_dev->ifa_list; + + while (*ifap != NULL) { + if (strcmp(hdlc_to_name(hdlc), + (*ifap)->ifa_label) == 0) { + addr = (*ifap)->ifa_local; + mask = (*ifap)->ifa_mask; + break; + } + ifap = &(*ifap)->ifa_next; + } + + cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, + addr, mask); + } + dev_kfree_skb_any(skb); + return; + + case CISCO_ADDR_REPLY: + printk(KERN_INFO "%s: Unexpected Cisco IP address " + "reply\n", hdlc_to_name(hdlc)); + goto rx_error; + + case CISCO_KEEPALIVE_REQ: + hdlc->state.cisco.rxseq = ntohl(cisco_data->par1); + if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) { + hdlc->state.cisco.last_poll = jiffies; + if (!hdlc->state.cisco.up) { + u32 sec, min, hrs, days; + sec = ntohl(cisco_data->time) / 1000; + min = sec / 60; sec -= min * 60; + hrs = min / 60; min -= hrs * 60; + days = hrs / 24; hrs -= days * 24; + printk(KERN_INFO "%s: Link up (peer " + "uptime %ud%uh%um%us)\n", + hdlc_to_name(hdlc), days, hrs, + min, sec); + } + hdlc->state.cisco.up = 1; + } + + dev_kfree_skb_any(skb); + return; + } /* switch(keepalive type) */ + } /* switch(protocol) */ + + printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), + data->protocol); + dev_kfree_skb_any(skb); + return; + + rx_error: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static void cisco_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + + if (hdlc->state.cisco.up && + jiffies - hdlc->state.cisco.last_poll >= + hdlc->state.cisco.settings.timeout * HZ) { + hdlc->state.cisco.up = 0; + printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); + } + + cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, + ++hdlc->state.cisco.txseq, + hdlc->state.cisco.rxseq); + hdlc->state.cisco.timer.expires = jiffies + + hdlc->state.cisco.settings.interval * HZ; + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = arg; + add_timer(&hdlc->state.cisco.timer); +} + + + +static int cisco_open(hdlc_device *hdlc) +{ + hdlc->state.cisco.last_poll = 0; + hdlc->state.cisco.up = 0; + hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0; + + init_timer(&hdlc->state.cisco.timer); + hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/ + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.cisco.timer); + return 0; +} + + + +static void cisco_close(hdlc_device *hdlc) +{ + del_timer_sync(&hdlc->state.cisco.timer); +} + + + +int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + cisco_proto *cisco_s = &ifr->ifr_settings->ifs_hdlc.cisco; + const size_t size = sizeof(cisco_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_CISCO; + if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_CISCO: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.cisco.settings, cisco_s, size)) + return -EFAULT; + + /* FIXME - put sanity checks here */ + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = cisco_open; + hdlc->stop = cisco_close; + hdlc->netif_rx = cisco_rx; + hdlc->proto = IF_PROTO_CISCO; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = cisco_hard_header; + dev->type = ARPHRD_CISCO; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_fr.c linux-2.5.6/drivers/net/wan/hdlc_fr.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_fr.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_fr.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,842 @@ +/* + * Generic HDLC support routines for Linux + * Frame Relay support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) +{ + pvc_device *pvc=hdlc->state.fr.first_pvc; + + while (pvc) { + if (netdev_dlci(&pvc->netdev) == dlci) + return pvc; + pvc = pvc->next; + } + + return NULL; +} + + + +__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, + int *active, int *new) +{ + *new = (status[2] & 0x08); + *active = (!*new && (status[2] & 0x02)); + + return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); +} + + +__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, + int active, int new) +{ + status[0] = (dlci>>4) & 0x3F; + status[1] = ((dlci<<3) & 0x78) | 0x80; + status[2] = 0x80; + + if (new) + status[2] |= 0x08; + else if (active) + status[2] |= 0x02; +} + + + +static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, + u16 type, void *daddr, void *saddr, unsigned int len) +{ + u16 head_len; + + if (!daddr) + daddr = dev->broadcast; + +#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER + printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); +#endif + + switch(type) { + case ETH_P_IP: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IP; + break; + + case ETH_P_IPV6: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IPV6; + break; + + case LMI_PROTO: + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = LMI_PROTO; + break; + + default: + head_len = 10; + skb_push(skb, head_len); + skb->data[3] = FR_PAD; + skb->data[4] = NLPID_SNAP; + skb->data[5] = FR_PAD; + skb->data[6] = FR_PAD; + skb->data[7] = FR_PAD; + skb->data[8] = type>>8; + skb->data[9] = (u8)type; + } + + memcpy(skb->data, daddr, 2); + skb->data[2] = FR_UI; + + return head_len; +} + + + +static int pvc_open(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) + return -EIO; /* Master must be UP in order to activate PVC */ + + if (pvc->master->state.fr.settings.lmi != LMI_NONE) + pvc->state.active = 0; + else + pvc->state.active = 1; + + pvc->state.new = 0; + pvc->master->state.fr.changed = 1; + return 0; +} + + + +static int pvc_close(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + pvc->state.active = pvc->state.new = 0; + pvc->master->state.fr.changed = 1; + return 0; +} + + + +static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + if (pvc->state.active) { + skb->dev = hdlc_to_dev(pvc->master); + pvc->stats.tx_bytes += skb->len; + pvc->stats.tx_packets++; + if (pvc->state.fecn) + pvc->stats.tx_compressed++; /* TX Congestion counter */ + dev_queue_xmit(skb); + } else { + pvc->stats.tx_dropped++; + dev_kfree_skb(skb); + } + + return 0; +} + + + +static struct net_device_stats *pvc_get_stats(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + return &pvc->stats; +} + + + +static int pvc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static inline void fr_log_dlci_active(pvc_device *pvc) +{ + printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), + pvc->state.active ? "" : "in", + pvc->state.new ? " new" : ""); +} + + + +static inline u8 fr_lmi_nextseq(u8 x) +{ + x++; + return x ? x : 1; +} + + + +static void fr_lmi_send(hdlc_device *hdlc, int fullrep) +{ + struct sk_buff *skb; + pvc_device *pvc = hdlc->state.fr.first_pvc; + int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH + : LMI_LENGTH; + int stat_len = 3; + u8 *data; + int i = 0; + + if (hdlc->state.fr.settings.dce && fullrep) { + len += hdlc->state.fr.pvc_count * (2 + stat_len); + if (len > HDLC_MAX_MTU) { + printk(KERN_WARNING "%s: Too many PVCs while sending " + "LMI full report\n", hdlc_to_name(hdlc)); + return; + } + } + + skb = dev_alloc_skb(len); + if (!skb) { + printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", + hdlc_to_name(hdlc)); + return; + } + memset(skb->data, 0, len); + skb_reserve(skb, 4); + fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); + data = skb->tail; + data[i++] = LMI_CALLREF; + data[i++] = hdlc->state.fr.settings.dce + ? LMI_STATUS : LMI_STATUS_ENQUIRY; + if (hdlc->state.fr.settings.lmi == LMI_ANSI) + data[i++] = LMI_ANSI_LOCKSHIFT; + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE; + data[i++] = LMI_REPT_LEN; + data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; + + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE; + data[i++] = LMI_INTEG_LEN; + data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); + data[i++] = hdlc->state.fr.rxseq; + + if (hdlc->state.fr.settings.dce && fullrep) { + while (pvc) { + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; + data[i++] = stat_len; + + if (hdlc->state.fr.reliable && + (pvc->netdev.flags & IFF_UP) && + !pvc->state.active && + !pvc->state.new) { + pvc->state.new = 1; + fr_log_dlci_active(pvc); + } + + dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), + data + i, + pvc->state.active, pvc->state.new); + i += stat_len; + pvc = pvc->next; + } + } + + skb_put(skb, i); + skb->priority = TC_PRIO_CONTROL; + skb->dev = hdlc_to_dev(hdlc); + + dev_queue_xmit(skb); +} + + + +static void fr_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + int i, cnt = 0, reliable; + u32 list; + + if (hdlc->state.fr.settings.dce) + reliable = (jiffies - hdlc->state.fr.last_poll < + hdlc->state.fr.settings.t392 * HZ); + else { + hdlc->state.fr.last_errors <<= 1; /* Shift the list */ + if (hdlc->state.fr.request) { + if (hdlc->state.fr.reliable) + printk(KERN_INFO "%s: No LMI status reply " + "received\n", hdlc_to_name(hdlc)); + hdlc->state.fr.last_errors |= 1; + } + + list = hdlc->state.fr.last_errors; + for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1) + cnt += (list & 1); /* errors count */ + + reliable = (cnt < hdlc->state.fr.settings.n392); + } + + if (hdlc->state.fr.reliable != reliable) { + pvc_device *pvc = hdlc->state.fr.first_pvc; + + hdlc->state.fr.reliable = reliable; + printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), + reliable ? "" : "un"); + + if (reliable) { + hdlc->state.fr.n391cnt = 0; /* Request full status */ + hdlc->state.fr.changed = 1; + } else { + while (pvc) { /* Deactivate all PVCs */ + pvc->state.new = pvc->state.active = 0; + pvc = pvc->next; + } + } + } + + if (hdlc->state.fr.settings.dce) + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t392 * HZ; + else { + if (hdlc->state.fr.n391cnt) + hdlc->state.fr.n391cnt--; + + fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0); + + hdlc->state.fr.request = 1; + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t391 * HZ; + } + + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = arg; + add_timer(&hdlc->state.fr.timer); +} + + + +static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) +{ + int stat_len; + pvc_device *pvc; + int reptype = -1, error; + u8 rxseq, txseq; + int i; + + if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI) + ? LMI_ANSI_LENGTH : LMI_LENGTH)) { + printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); + return 1; + } + + if (skb->data[5] != (!hdlc->state.fr.settings.dce ? + LMI_STATUS : LMI_STATUS_ENQUIRY)) { + printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", + hdlc_to_name(hdlc), skb->data[2], + hdlc->state.fr.settings.dce ? "enquiry" : "reply"); + return 1; + } + + i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6; + + if (skb->data[i] != + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { + printk(KERN_INFO "%s: Not a report type=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + reptype = skb->data[i++]; + + if (skb->data[i]!= + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE)) { + printk(KERN_INFO "%s: Unsupported status element=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ + rxseq = skb->data[i++]; /* Should confirm our sequence */ + + txseq = hdlc->state.fr.txseq; + + if (hdlc->state.fr.settings.dce) { + if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { + printk(KERN_INFO "%s: Unsupported report type=%x\n", + hdlc_to_name(hdlc), reptype); + return 1; + } + } + + error = 0; + if (!hdlc->state.fr.reliable) + error = 1; + + if (rxseq == 0 || rxseq != txseq) { + hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */ + error = 1; + } + + if (hdlc->state.fr.settings.dce) { + if (hdlc->state.fr.fullrep_sent && !error) { +/* Stop sending full report - the last one has been confirmed by DTE */ + hdlc->state.fr.fullrep_sent = 0; + pvc = hdlc->state.fr.first_pvc; + while (pvc) { + if (pvc->state.new) { + pvc->state.new = 0; + pvc->state.active = 1; + fr_log_dlci_active(pvc); + +/* Tell DTE that new PVC is now active */ + hdlc->state.fr.changed = 1; + } + pvc = pvc->next; + } + } + + if (hdlc->state.fr.changed) { + reptype = LMI_FULLREP; + hdlc->state.fr.fullrep_sent = 1; + hdlc->state.fr.changed = 0; + } + + fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); + return 0; + } + + /* DTE */ + + if (reptype != LMI_FULLREP || error) + return 0; + + stat_len = 3; + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + pvc->state.deleted = pvc->state.active; /* mark active PVCs */ + pvc = pvc->next; + } + + while (skb->len >= i + 2 + stat_len) { + u16 dlci; + int active, new; + + if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { + printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + if (skb->data[i] != stat_len) { + printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); + pvc = find_pvc(hdlc, dlci); + + active |= new; + if (pvc) { + if (active && !pvc->state.active && + (pvc->netdev.flags & IFF_UP)) { + pvc->state.active = active; + fr_log_dlci_active(pvc); + } + pvc->state.deleted = 0; + } + else if (new) + printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", + hdlc_to_name(hdlc), dlci); + + i += stat_len; + } + + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + if (pvc->state.deleted) { + pvc->state.active = pvc->state.new = 0; + fr_log_dlci_active(pvc); + pvc->state.deleted = 0; + } + pvc = pvc->next; + } + + /* Next full report after N391 polls */ + hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391; + + return 0; +} + + + +static void fr_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + fr_hdr *fh = (fr_hdr*)skb->data; + u8 *data = skb->data; + u16 dlci; + pvc_device *pvc; + + if (skb->len<4 || fh->ea1 || data[2] != FR_UI) + goto rx_error; + + dlci = q922_to_dlci(skb->data); + + if (dlci == LMI_DLCI) { + if (hdlc->state.fr.settings.lmi == LMI_NONE) + goto rx_error; /* LMI packet with no LMI? */ + + if (data[3] == LMI_PROTO) { + if (fr_lmi_recv(hdlc, skb)) + goto rx_error; + else { + /* No request pending */ + hdlc->state.fr.request = 0; + hdlc->state.fr.last_poll = jiffies; + dev_kfree_skb_any(skb); + return; + } + } + + printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", + hdlc_to_name(hdlc)); + goto rx_error; + } + + pvc = find_pvc(hdlc, dlci); + if (!pvc) { +#ifdef CONFIG_HDLC_DEBUG_PKT + printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", + hdlc_to_name(hdlc), dlci); +#endif + goto rx_error; + } + + if ((pvc->netdev.flags & IFF_UP) == 0) { +#ifdef CONFIG_HDLC_DEBUG_PKT + printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", + hdlc_to_name(hdlc), dlci); +#endif + goto rx_error; + } + + pvc->stats.rx_packets++; /* PVC traffic */ + pvc->stats.rx_bytes += skb->len; + + if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) { +#ifdef CONFIG_HDLC_DEBUG_ECN + printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), + fh->fecn ? "N" : "FF"); +#endif + pvc->state.fecn ^= 1; + } + + if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) { +#ifdef CONFIG_HDLC_DEBUG_ECN + printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), + fh->becn ? "N" : "FF"); +#endif + pvc->state.becn ^= 1; + } + + if (pvc->state.becn) + pvc->stats.rx_compressed++; + + skb->dev = &pvc->netdev; + + if (data[3] == NLPID_IP) { + skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + skb->protocol = htons(ETH_P_IP); + netif_rx(skb); + return; + } + + + if (data[3] == NLPID_IPV6) { + skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + skb->protocol = htons(ETH_P_IPV6); + netif_rx(skb); + return; + } + + if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { + u16 oui = ntohl(*(u16*)(data + 6)); + u16 pid = ntohl(*(u16*)(data + 8)); + skb_pull(skb, 10); + + switch ((((u32)oui) << 16) | pid) { + case ETH_P_ARP: /* routed frame with SNAP */ + case ETH_P_IPX: + skb->protocol = htons(pid); + break; + + default: + printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " + "PID=%x\n", hdlc_to_name(hdlc), oui, pid); + dev_kfree_skb_any(skb); + return; + } + + netif_rx(skb); + return; + } + + printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", + hdlc_to_name(hdlc), data[3]); + dev_kfree_skb_any(skb); + return; + + rx_error: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static int fr_open(hdlc_device *hdlc) +{ + if (hdlc->state.fr.settings.lmi != LMI_NONE) { + hdlc->state.fr.last_poll = 0; + hdlc->state.fr.reliable = 0; + hdlc->state.fr.changed = 1; + hdlc->state.fr.request = 0; + hdlc->state.fr.fullrep_sent = 0; + hdlc->state.fr.last_errors = 0xFFFFFFFF; + hdlc->state.fr.n391cnt = 0; + hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0; + + init_timer(&hdlc->state.fr.timer); + /* First poll after 1 s */ + hdlc->state.fr.timer.expires = jiffies + HZ; + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.fr.timer); + } else + hdlc->state.fr.reliable = 1; + + return 0; +} + + + +static void fr_close(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + + if (hdlc->state.fr.settings.lmi != LMI_NONE) + del_timer_sync(&hdlc->state.fr.timer); + + while(pvc) { + dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ + pvc = pvc->next; + } +} + + + +static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create) +{ + pvc_device **pvc_p = &hdlc->state.fr.first_pvc; + pvc_device *pvc; + int result; + + if(dlci <= 0 || dlci >= 1024) + return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ + + while(*pvc_p) { + if (netdev_dlci(&(*pvc_p)->netdev) == dlci) + break; + pvc_p = &(*pvc_p)->next; + } + + if (create) { /* Create PVC */ + if (*pvc_p != NULL) + return -EEXIST; + + pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); + if (!pvc) { + printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", + hdlc_to_name(hdlc)); + return -ENOBUFS; + } + memset(pvc, 0, sizeof(pvc_device)); + + pvc->netdev.hard_start_xmit = pvc_xmit; + pvc->netdev.get_stats = pvc_get_stats; + pvc->netdev.open = pvc_open; + pvc->netdev.stop = pvc_close; + pvc->netdev.change_mtu = pvc_change_mtu; + pvc->netdev.mtu = HDLC_MAX_MTU; + + pvc->netdev.type = ARPHRD_DLCI; + pvc->netdev.hard_header_len = 16; + pvc->netdev.hard_header = fr_hard_header; + pvc->netdev.tx_queue_len = 0; + pvc->netdev.flags = IFF_POINTOPOINT; + + pvc->master = hdlc; + *(u16*)pvc->netdev.dev_addr = htons(dlci); + dlci_to_q922(pvc->netdev.broadcast, dlci); + pvc->netdev.addr_len = 2; + + result = dev_alloc_name(&pvc->netdev, "pvc%d"); + if (result < 0) { + kfree(pvc); + *pvc_p = NULL; + return result; + } + + if (register_netdevice(&pvc->netdev) != 0) { + kfree(pvc); + *pvc_p = NULL; + return -EIO; + } + + hdlc->state.fr.changed = 1; + hdlc->state.fr.pvc_count++; + return 0; + } + + if (*pvc_p == NULL) /* Delete PVC */ + return -ENOENT; + + pvc = *pvc_p; + + if (pvc->netdev.flags & IFF_UP) + return -EBUSY; /* PVC in use */ + + hdlc->state.fr.changed = 1; + hdlc->state.fr.pvc_count--; + *pvc_p = pvc->next; + unregister_netdevice(&pvc->netdev); + kfree(pvc); + return 0; +} + + + +static void fr_destroy(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + while(pvc) { + pvc_device *next = pvc->next; + unregister_netdevice(&pvc->netdev); + kfree(pvc); + pvc = next; + } + + hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ + hdlc->state.fr.pvc_count = 0; + hdlc->state.fr.changed = 1; +} + + + +int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + fr_proto *fr_s = &ifr->ifr_settings->ifs_hdlc.fr; + const size_t size = sizeof(fr_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + fr_proto_pvc pvc; + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_FR; + if (copy_to_user(fr_s, &hdlc->state.fr.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_FR: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.fr.settings, fr_s, size)) + return -EFAULT; + + /* FIXME - put sanity checks here */ + if (hdlc->proto != IF_PROTO_FR) { + hdlc_detach(hdlc); + hdlc->state.fr.first_pvc = NULL; + hdlc->state.fr.pvc_count = 0; + } + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = fr_open; + hdlc->stop = fr_close; + hdlc->netif_rx = fr_rx; + hdlc->detach = fr_destroy; + hdlc->proto = IF_PROTO_FR; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = fr_hard_header; + dev->type = ARPHRD_FRAD; + dev->addr_len = 2; + *(u16*)dev->dev_addr = htons(LMI_DLCI); + dlci_to_q922(dev->broadcast, LMI_DLCI); + return 0; + + case IF_PROTO_FR_ADD_PVC: + case IF_PROTO_FR_DEL_PVC: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&pvc, &ifr->ifr_settings->ifs_hdlc.fr_pvc, + sizeof(fr_proto_pvc))) + return -EFAULT; + + return fr_pvc(hdlc, pvc.dlci, + ifr->ifr_settings->type == IF_PROTO_FR_ADD_PVC); + } + + return -EINVAL; +} diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_generic.c linux-2.5.6/drivers/net/wan/hdlc_generic.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_generic.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_generic.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,188 @@ +/* + * Generic HDLC support routines for Linux + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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. + * + * Current status: + * - this is work in progress + * - not heavily tested on SMP + * - currently supported: + * * raw IP-in-HDLC + * * Cisco HDLC + * * Frame Relay with ANSI or CCITT LMI (both user and network side) + * * PPP + * * X.25 + * + * Use sethdlc utility to set line parameters, protocol and PVCs + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char* version = "HDLC support module revision 1.08"; + + +static int hdlc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static struct net_device_stats *hdlc_get_stats(struct net_device *dev) +{ + return &dev_to_hdlc(dev)->stats; +} + + + +static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *p) +{ + dev_to_hdlc(dev)->netif_rx(skb); + return 0; +} + + + +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + unsigned int proto; + + if (cmd != SIOCWANDEV) + return -EINVAL; + + switch(ifr->ifr_settings->type) { + case IF_PROTO_HDLC: + case IF_PROTO_PPP: + case IF_PROTO_CISCO: + case IF_PROTO_FR: + case IF_PROTO_X25: + proto = ifr->ifr_settings->type; + break; + + default: + proto = hdlc->proto; + } + + switch(proto) { +#ifdef CONFIG_HDLC_RAW + case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_PPP + case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_CISCO + case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_FR + case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr); +#endif +#ifdef CONFIG_HDLC_X25 + case IF_PROTO_X25: return hdlc_x25_ioctl(hdlc, ifr); +#endif + default: return -ENOSYS; + } +} + + + +int register_hdlc_device(hdlc_device *hdlc) +{ + int result; + struct net_device *dev = hdlc_to_dev(hdlc); + + dev->get_stats = hdlc_get_stats; + dev->change_mtu = hdlc_change_mtu; + dev->mtu = HDLC_MAX_MTU; + + dev->type = ARPHRD_RAWHDLC; + dev->hard_header_len = 16; + + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + + hdlc->proto = -1; + hdlc->detach = NULL; + + result = dev_alloc_name(dev, "hdlc%d"); + if (result<0) + return result; + + result = register_netdev(dev); + if (result != 0) + return -EIO; + + MOD_INC_USE_COUNT; + return 0; +} + + + +void unregister_hdlc_device(hdlc_device *hdlc) +{ + hdlc_detach(hdlc); + + unregister_netdev(hdlc_to_dev(hdlc)); + MOD_DEC_USE_COUNT; +} + + + +MODULE_AUTHOR("Krzysztof Halasa "); +MODULE_DESCRIPTION("HDLC support module"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(hdlc_ioctl); +EXPORT_SYMBOL(register_hdlc_device); +EXPORT_SYMBOL(unregister_hdlc_device); + +struct packet_type hdlc_packet_type= +{ + __constant_htons(ETH_P_HDLC), + NULL, + hdlc_rcv, + NULL, + NULL +}; + + +static int __init hdlc_module_init(void) +{ + printk(KERN_INFO "%s\n", version); + dev_add_pack(&hdlc_packet_type); + return 0; +} + + + +static void __exit hdlc_module_exit(void) +{ + dev_remove_pack(&hdlc_packet_type); +} + + +module_init(hdlc_module_init); +module_exit(hdlc_module_exit); diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_ppp.c linux-2.5.6/drivers/net/wan/hdlc_ppp.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_ppp.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_ppp.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,119 @@ +/* + * Generic HDLC support routines for Linux + * Point-to-point protocol support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int ppp_open(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + void *old_ioctl; + int result; + + dev->priv = &hdlc->state.ppp.syncppp_ptr; + hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev; + hdlc->state.ppp.pppdev.dev = dev; + + old_ioctl = dev->do_ioctl; + hdlc->state.ppp.old_change_mtu = dev->change_mtu; + sppp_attach(&hdlc->state.ppp.pppdev); + /* sppp_attach nukes them. We don't need syncppp's ioctl */ + dev->do_ioctl = old_ioctl; + hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO; + dev->type = ARPHRD_PPP; + result = sppp_open(dev); + if (result) { + sppp_detach(dev); + return result; + } + + return 0; +} + + + +static void ppp_close(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + + sppp_close(dev); + sppp_detach(dev); + dev->rebuild_header = NULL; + dev->change_mtu = hdlc->state.ppp.old_change_mtu; + dev->mtu = HDLC_MAX_MTU; + dev->hard_header_len = 16; +} + + + +static void ppp_rx(struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_WAN_PPP); + netif_rx(skb); +} + + + +int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_PPP; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_PPP: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + /* no settable parameters */ + + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = ppp_open; + hdlc->stop = ppp_close; + hdlc->netif_rx = ppp_rx; + hdlc->proto = IF_PROTO_PPP; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_PPP; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_raw.c linux-2.5.6/drivers/net/wan/hdlc_raw.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_raw.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_raw.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,84 @@ +/* + * Generic HDLC support routines for Linux + * HDLC support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void raw_rx(struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_IP); + netif_rx(skb); +} + + + +int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + raw_hdlc_proto *raw_s = &ifr->ifr_settings->ifs_hdlc.raw_hdlc; + const size_t size = sizeof(raw_hdlc_proto); + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_HDLC: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&hdlc->state.raw_hdlc.settings, raw_s, size)) + return -EFAULT; + + + /* FIXME - put sanity checks here */ + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, hdlc->state.raw_hdlc.settings.encoding, + hdlc->state.raw_hdlc.settings.parity); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = NULL; + hdlc->stop = NULL; + hdlc->netif_rx = raw_rx; + hdlc->proto = IF_PROTO_HDLC; + dev->hard_start_xmit = hdlc->xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_RAWHDLC; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -urN linux-2.5.6-pre3/drivers/net/wan/hdlc_x25.c linux-2.5.6/drivers/net/wan/hdlc_x25.c --- linux-2.5.6-pre3/drivers/net/wan/hdlc_x25.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/drivers/net/wan/hdlc_x25.c Thu Mar 7 18:24:48 2002 @@ -0,0 +1,219 @@ +/* + * Generic HDLC support routines for Linux + * X.25 support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* These functions are callbacks called by LAPB layer */ + +static void x25_connect_disconnect(void *token, int reason, int code) +{ + hdlc_device *hdlc = token; + struct sk_buff *skb; + unsigned char *ptr; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); + return; + } + + ptr = skb_put(skb, 1); + *ptr = code; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + + + +static void x25_connected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 1); +} + + + +static void x25_disconnected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 2); +} + + + +static int x25_data_indication(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + unsigned char *ptr; + + ptr = skb_push(skb, 1); + *ptr = 0; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + return netif_rx(skb); +} + + + +static void x25_data_transmit(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */ +} + + + +static int x25_xmit(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + int result; + + + /* X.25 to LAPB */ + switch (skb->data[0]) { + case 0: /* Data to be transmitted */ + skb_pull(skb, 1); + if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) + dev_kfree_skb(skb); + return 0; + + case 1: + if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { + if (result == LAPB_CONNECTED) + /* Send connect confirm. msg to level 3 */ + x25_connected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB connect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + case 2: + if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) { + if (result == LAPB_NOTCONNECTED) + /* Send disconnect confirm. msg to level 3 */ + x25_disconnected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB disconnect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + default: /* to be defined */ + break; + } + + dev_kfree_skb(skb); + return 0; +} + + + +static int x25_open(hdlc_device *hdlc) +{ + struct lapb_register_struct cb; + int result; + + cb.connect_confirmation = x25_connected; + cb.connect_indication = x25_connected; + cb.disconnect_confirmation = x25_disconnected; + cb.disconnect_indication = x25_disconnected; + cb.data_indication = x25_data_indication; + cb.data_transmit = x25_data_transmit; + + result = lapb_register(hdlc, &cb); + if (result != LAPB_OK) + return result; + return 0; +} + + + +static void x25_close(hdlc_device *hdlc) +{ + lapb_unregister(hdlc); +} + + + +static void x25_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + + if (lapb_data_received(hdlc, skb) == LAPB_OK) + return; + hdlc->stats.rx_errors++; + dev_kfree_skb_any(skb); +} + + + +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings->type) { + case IF_GET_PROTO: + ifr->ifr_settings->type = IF_PROTO_X25; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_X25: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + hdlc_detach(hdlc); + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) { + hdlc->proto = -1; + return result; + } + + hdlc->open = x25_open; + hdlc->stop = x25_close; + hdlc->netif_rx = x25_rx; + hdlc->proto = IF_PROTO_X25; + dev->hard_start_xmit = x25_xmit; + dev->hard_header = NULL; + dev->type = ARPHRD_X25; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} diff -urN linux-2.5.6-pre3/drivers/net/wan/n2.c linux-2.5.6/drivers/net/wan/n2.c --- linux-2.5.6-pre3/drivers/net/wan/n2.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/net/wan/n2.c Thu Mar 7 18:24:48 2002 @@ -1,7 +1,7 @@ /* * SDL Inc. RISCom/N2 synchronous serial card driver for Linux * - * Copyright (C) 1998-2000 Krzysztof Halasa + * Copyright (C) 1998-2001 Krzysztof Halasa * * 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 @@ -17,6 +17,7 @@ * SDL Inc. PPP/HDLC/CISCO driver */ +#include #include #include #include @@ -33,10 +34,8 @@ #include #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4"; +static const char* version = "SDL RISCom/N2 driver version: 1.09"; static const char* devname = "RISCom/N2"; #define USE_WINDOWSIZE 16384 @@ -87,9 +86,9 @@ hdlc_device hdlc; /* HDLC device struct - must be first */ struct card_s *card; spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock rate */ - int line; /* loopback only */ + sync_serial_settings settings; + unsigned short encoding; + unsigned short parity; u8 rxs, txs, tmc; /* SCA registers */ u8 valid; /* port enabled */ u8 phy_node; /* physical port # - 0 or 1 */ @@ -116,6 +115,9 @@ }card_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #define sca_reg(reg, card) (0x8000 | (card)->io | \ ((reg) & 0x0F) | (((reg) & 0xF0) << 6)) @@ -157,7 +159,7 @@ -static int n2_set_clock(port_t *port, int value) +static int n2_set_iface(port_t *port) { card_t *card = port->card; int io = card->io; @@ -166,7 +168,7 @@ u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { + switch(port->settings.clock_type) { case CLOCK_EXT: mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0; rxs |= CLK_LINE_RX; /* RXC input */ @@ -200,17 +202,22 @@ port->txs = txs; sca_out(rxs, msci + RXS, card); sca_out(txs, msci + TXS, card); - port->clkmode = value; + sca_set_port(port); return 0; } -static int n2_open(hdlc_device *hdlc) +static int n2_open(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; - u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); + u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0); + + int result = hdlc_open(hdlc); + if (result) + return result; MOD_INC_USE_COUNT; mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */ @@ -219,14 +226,14 @@ outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */ outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */ sca_open(hdlc); - n2_set_clock(port, port->clkmode); - return 0; + return n2_set_iface(port); } -static void n2_close(hdlc_device *hdlc) +static int n2_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); @@ -234,52 +241,48 @@ sca_close(hdlc); mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */ outb(mcr, io + N2_MCR); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + union line_settings *line = &ifr->ifr_settings->ifs_line; + const size_t size = sizeof(sync_serial_settings); + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(cmd) { - case HDLCSCLOCK: - result = n2_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; - - case HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; - - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; - -#ifdef DEBUG_RINGS - case HDLCRUN: +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { sca_dump_rings(hdlc); return 0; -#endif /* DEBUG_RINGS */ + } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings->type) { + case IF_GET_IFACE: + ifr->ifr_settings->type = IF_IFACE_SYNC_SERIAL; + if (copy_to_user(&line->sync, &port->settings, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&port->settings, &line->sync, size)) + return -EFAULT; + /* FIXME - put sanity checks here */ + return n2_set_iface(port); default: - return -EINVAL; + return hdlc_ioctl(dev, ifr, cmd); } - - ifr->ifr_ifru.ifru_ivalue = value; - return result; } @@ -465,6 +468,7 @@ sca_init(card, 0); for (cnt = 0; cnt < 2; cnt++) { port_t *port = &card->ports[cnt]; + struct net_device *dev = hdlc_to_dev(&port->hdlc); if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1)) continue; @@ -476,14 +480,16 @@ port->log_node = 1; spin_lock_init(&port->lock); - hdlc_to_dev(&port->hdlc)->irq = irq; - hdlc_to_dev(&port->hdlc)->mem_start = winbase; - hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1; - hdlc_to_dev(&port->hdlc)->tx_queue_len = 50; - port->hdlc.ioctl = n2_ioctl; - port->hdlc.open = n2_open; - port->hdlc.close = n2_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + USE_WINDOWSIZE-1; + dev->tx_queue_len = 50; + dev->do_ioctl = n2_ioctl; + dev->open = n2_open; + dev->stop = n2_close; + port->hdlc.attach = sca_attach; port->hdlc.xmit = sca_xmit; + port->settings.clock_type = CLOCK_EXT; if (register_hdlc_device(&port->hdlc)) { printk(KERN_WARNING "n2: unable to register hdlc " @@ -515,7 +521,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s\n", version); + printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); do { unsigned long io, irq, ram; diff -urN linux-2.5.6-pre3/drivers/net/wd.c linux-2.5.6/drivers/net/wd.c --- linux-2.5.6-pre3/drivers/net/wd.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/drivers/net/wd.c Thu Mar 7 18:24:48 2002 @@ -450,10 +450,11 @@ MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); MODULE_PARM(mem_end, "1-" __MODULE_STRING(MAX_WD_CARDS) "i"); -MODULE_PARM_DESC(io, "WD80x3 I/O base address(es)"); -MODULE_PARM_DESC(irq, "WD80x3 IRQ number(s) (ignored for PureData boards)"); -MODULE_PARM_DESC(mem, "WD80x3 memory base address(es)(ignored for PureData boards)"); -MODULE_PARM_DESC(mem_end, "WD80x3 memory end address(es)"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); +MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); +MODULE_PARM_DESC(mem_end, "memory end address(es)"); +MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); MODULE_LICENSE("GPL"); /* This is set up so that only a single autoprobe takes place per call. diff -urN linux-2.5.6-pre3/drivers/net/winbond-840.c linux-2.5.6/drivers/net/winbond-840.c --- linux-2.5.6-pre3/drivers/net/winbond-840.c Thu Mar 7 18:24:28 2002 +++ linux-2.5.6/drivers/net/winbond-840.c Wed Dec 31 16:00:00 1969 @@ -1,1757 +0,0 @@ -/* winbond-840.c: A Linux PCI network adapter device driver. */ -/* - Written 1998-2001 by Donald Becker. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Support and updates available at - http://www.scyld.com/network/drivers.html - - Do not remove the copyright infomation. - Do not change the version information unless an improvement has been made. - Merely removing my name, as Compex has done in the past, does not count - as an improvement. - - Changelog: - * ported to 2.4 - ??? - * spin lock update, memory barriers, new style dma mappings - limit each tx buffer to < 1024 bytes - remove DescIntr from Rx descriptors (that's an Tx flag) - remove next pointer from Tx descriptors - synchronize tx_q_bytes - software reset in tx_timeout - Copyright (C) 2000 Manfred Spraul - * further cleanups - power management. - support for big endian descriptors - Copyright (C) 2001 Manfred Spraul - * ethtool support (jgarzik) - * Replace some MII-related magic numbers with constants (jgarzik) - - TODO: - * enable pci_power_off - * Wake-On-LAN -*/ - -#define DRV_NAME "winbond-840" -#define DRV_VERSION "1.01-d" -#define DRV_RELDATE "Nov-17-2001" - - -/* Automatically extracted configuration info: -probe-func: winbond840_probe -config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 - -c-help-name: Winbond W89c840 PCI Ethernet support -c-help-symbol: CONFIG_WINBOND_840 -c-help: This driver is for the Winbond W89c840 chip. It also works with -c-help: the TX9882 chip on the Compex RL100-ATX board. -c-help: More specific information and updates are available from -c-help: http://www.scyld.com/network/drivers.html -*/ - -/* The user-configurable values. - These may be modified when a driver module is loaded.*/ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -static int max_interrupt_work = 20; -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The '840 uses a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1518 effectively disables this feature. */ -static int rx_copybreak; - -/* Used to pass the media type, etc. - Both 'options[]' and 'full_duplex[]' should exist for driver - interoperability. - The media type is usually passed in 'options[]'. -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for compile efficiency. - The compiler will convert '%'<2^N> into a bit mask. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ -#define TX_QUEUE_LEN_RESTART 5 -#define RX_RING_SIZE 32 - -#define TX_BUFLIMIT (1024-128) - -/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. - To avoid overflowing we don't queue again until we have room for a - full-size packet. - */ -#define TX_FIFO_SIZE (2048) -#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) - - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (2*HZ) - -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ - -#ifndef __KERNEL__ -#define __KERNEL__ -#endif -#if !defined(__OPTIMIZE__) -#warning You must compile this file with the correct options! -#warning See the last lines of the source file. -#error You must compile this driver with "-O". -#endif - -/* Include files, designed to support most kernel versions 2.0.0 and later. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* Processor type for cache alignment. */ -#include -#include -#include - -/* These identify the driver base version and may not be removed. */ -static char version[] __devinitdata = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " (2.4 port) " DRV_RELDATE " Donald Becker \n" -KERN_INFO " http://www.scyld.com/network/drivers.html\n"; - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(debug, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(multicast_filter_limit, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt"); -MODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)"); -MODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses"); -MODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)"); - -/* - Theory of Operation - -I. Board Compatibility - -This driver is for the Winbond w89c840 chip. - -II. Board-specific settings - -None. - -III. Driver operation - -This chip is very similar to the Digital 21*4* "Tulip" family. The first -twelve registers and the descriptor format are nearly identical. Read a -Tulip manual for operational details. - -A significant difference is that the multicast filter and station address are -stored in registers rather than loaded through a pseudo-transmit packet. - -Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a -full-sized packet we must use both data buffers in a descriptor. Thus the -driver uses ring mode where descriptors are implicitly sequential in memory, -rather than using the second descriptor address as a chain pointer to -subsequent descriptors. - -IV. Notes - -If you are going to almost clone a Tulip, why not go all the way and avoid -the need for a new driver? - -IVb. References - -http://www.scyld.com/expert/100mbps.html -http://www.scyld.com/expert/NWay.html -http://www.winbond.com.tw/ - -IVc. Errata - -A horrible bug exists in the transmit FIFO. Apparently the chip doesn't -correctly detect a full FIFO, and queuing more than 2048 bytes may result in -silent data corruption. - -Test with 'ping -s 10000' on a fast computer. - -*/ - - - -/* - PCI probe table. -*/ -enum pci_id_flags_bits { - /* Set PCI command register bits before calling probe1(). */ - PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, - /* Read and map the single following PCI BAR. */ - PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, - PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, -}; -enum chip_capability_flags { - CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,}; -#ifdef USE_IO_OPS -#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) -#else -#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) -#endif - -static struct pci_device_id w840_pci_tbl[] __devinitdata = { - { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, - { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, w840_pci_tbl); - -struct pci_id_info { - const char *name; - struct match_info { - int pci, pci_mask, subsystem, subsystem_mask; - int revision, revision_mask; /* Only 8 bits. */ - } id; - enum pci_id_flags_bits pci_flags; - int io_size; /* Needed for I/O region check or ioremap(). */ - int drv_flags; /* Driver use, intended as capability flags. */ -}; -static struct pci_id_info pci_id_tbl[] = { - {"Winbond W89c840", /* Sometime a Level-One switch card. */ - { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, - {"Winbond W89c840", { 0x08401050, 0xffffffff, }, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, - {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, - W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, - {0,}, /* 0 terminated list. */ -}; - -/* This driver was written to use PCI memory space, however some x86 systems - work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space - accesses instead of memory space. */ - -#ifdef USE_IO_OPS -#undef readb -#undef readw -#undef readl -#undef writeb -#undef writew -#undef writel -#define readb inb -#define readw inw -#define readl inl -#define writeb outb -#define writew outw -#define writel outl -#endif - -/* Offsets to the Command and Status Registers, "CSRs". - While similar to the Tulip, these registers are longword aligned. - Note: It's not useful to define symbolic names for every register bit in - the device. The name can only partially document the semantics and make - the driver longer and more difficult to read. -*/ -enum w840_offsets { - PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, - RxRingPtr=0x0C, TxRingPtr=0x10, - IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, - RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, - CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ - MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, - CurTxDescAddr=0x4C, CurTxBufAddr=0x50, -}; - -/* Bits in the interrupt status/enable registers. */ -/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ -enum intr_status_bits { - NormalIntr=0x10000, AbnormalIntr=0x8000, - IntrPCIErr=0x2000, TimerInt=0x800, - IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, - TxFIFOUnderflow=0x20, RxErrIntr=0x10, - TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, -}; - -/* Bits in the NetworkConfig register. */ -enum rx_mode_bits { - AcceptErr=0x80, AcceptRunt=0x40, - AcceptBroadcast=0x20, AcceptMulticast=0x10, - AcceptAllPhys=0x08, AcceptMyPhys=0x02, -}; - -enum mii_reg_bits { - MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, - MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, -}; - -/* The Tulip Rx and Tx buffer descriptors. */ -struct w840_rx_desc { - s32 status; - s32 length; - u32 buffer1; - u32 buffer2; -}; - -struct w840_tx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; -}; - -/* Bits in network_desc.status */ -enum desc_status_bits { - DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, - DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, - DescIntr=0x80000000, -}; - -#define PRIV_ALIGN 15 /* Required alignment mask */ -#define MII_CNT 1 /* winbond only supports one MII */ -struct netdev_private { - struct w840_rx_desc *rx_ring; - dma_addr_t rx_addr[RX_RING_SIZE]; - struct w840_tx_desc *tx_ring; - dma_addr_t tx_addr[TX_RING_SIZE]; - dma_addr_t ring_dma_addr; - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - /* The saved address of a sent-in-place packet/buffer, for later free(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - struct net_device_stats stats; - struct timer_list timer; /* Media monitoring timer. */ - /* Frequently used values: keep some adjacent for cache effect. */ - spinlock_t lock; - int chip_id, drv_flags; - struct pci_dev *pci_dev; - int csr6; - struct w840_rx_desc *rx_head_desc; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - unsigned int cur_tx, dirty_tx; - unsigned int tx_q_bytes; - unsigned int tx_full; /* The Tx queue is full. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - unsigned char phys[MII_CNT]; /* MII device addresses, but only the first is used */ - u32 mii; - struct mii_if_info mii_if; -}; - -static int eeprom_read(long ioaddr, int location); -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static int netdev_open(struct net_device *dev); -static int update_link(struct net_device *dev); -static void netdev_timer(unsigned long data); -static void init_rxtx_rings(struct net_device *dev); -static void free_rxtx_rings(struct netdev_private *np); -static void init_registers(struct net_device *dev); -static void tx_timeout(struct net_device *dev); -static int alloc_ringdesc(struct net_device *dev); -static void free_ringdesc(struct netdev_private *np); -static int start_tx(struct sk_buff *skb, struct net_device *dev); -static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); -static void netdev_error(struct net_device *dev, int intr_status); -static int netdev_rx(struct net_device *dev); -static u32 __set_rx_mode(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static struct net_device_stats *get_stats(struct net_device *dev); -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int netdev_close(struct net_device *dev); - - - -static int __devinit w840_probe1 (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct netdev_private *np; - static int find_cnt; - int chip_idx = ent->driver_data; - int irq; - int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; - long ioaddr; - - i = pci_enable_device(pdev); - if (i) return i; - - pci_set_master(pdev); - - irq = pdev->irq; - - if (pci_set_dma_mask(pdev,0xFFFFffff)) { - printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n", - pdev->slot_name); - return -EIO; - } - dev = alloc_etherdev(sizeof(*np)); - if (!dev) - return -ENOMEM; - SET_MODULE_OWNER(dev); - - if (pci_request_regions(pdev, DRV_NAME)) - goto err_out_netdev; - -#ifdef USE_IO_OPS - ioaddr = pci_resource_start(pdev, 0); -#else - ioaddr = pci_resource_start(pdev, 1); - ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size); - if (!ioaddr) - goto err_out_free_res; -#endif - - for (i = 0; i < 3; i++) - ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); - - /* Reset the chip to erase previous misconfiguration. - No hold time required! */ - writel(0x00000001, ioaddr + PCIBusCfg); - - dev->base_addr = ioaddr; - dev->irq = irq; - - np = dev->priv; - np->pci_dev = pdev; - np->chip_id = chip_idx; - np->drv_flags = pci_id_tbl[chip_idx].drv_flags; - spin_lock_init(&np->lock); - np->mii_if.dev = dev; - np->mii_if.mdio_read = mdio_read; - np->mii_if.mdio_write = mdio_write; - - pci_set_drvdata(pdev, dev); - - if (dev->mem_start) - option = dev->mem_start; - - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x200) - np->mii_if.full_duplex = 1; - if (option & 15) - printk(KERN_INFO "%s: ignoring user supplied media type %d", - dev->name, option & 15); - } - if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) - np->mii_if.full_duplex = 1; - - if (np->mii_if.full_duplex) - np->mii_if.duplex_lock = 1; - - /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; - dev->tx_timeout = &tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - i = register_netdev(dev); - if (i) - goto err_out_cleardev; - - printk(KERN_INFO "%s: %s at 0x%lx, ", - dev->name, pci_id_tbl[chip_idx].name, ioaddr); - for (i = 0; i < 5; i++) - printk("%2.2x:", dev->dev_addr[i]); - printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); - - if (np->drv_flags & CanHaveMII) { - int phy, phy_idx = 0; - for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if (mii_status != 0xffff && mii_status != 0x0000) { - np->phys[phy_idx++] = phy; - np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); - np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+ - mdio_read(dev, phy, MII_PHYSID2); - printk(KERN_INFO "%s: MII PHY %8.8xh found at address %d, status " - "0x%4.4x advertising %4.4x.\n", - dev->name, np->mii, phy, mii_status, np->mii_if.advertising); - } - } - np->mii_cnt = phy_idx; - np->mii_if.phy_id = np->phys[0]; - if (phy_idx == 0) { - printk(KERN_WARNING "%s: MII PHY not found -- this device may " - "not operate correctly.\n", dev->name); - } - } - - find_cnt++; - return 0; - -err_out_cleardev: - pci_set_drvdata(pdev, NULL); -#ifndef USE_IO_OPS - iounmap((void *)ioaddr); -err_out_free_res: -#endif - pci_release_regions(pdev); -err_out_netdev: - kfree (dev); - return -ENODEV; -} - - -/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are - often serial bit streams generated by the host processor. - The example below is for the common 93c46 EEPROM, 64 16 bit words. */ - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need - a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that - made udelay() unreliable. - The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is - depricated. -*/ -#define eeprom_delay(ee_addr) readl(ee_addr) - -enum EEPROM_Ctrl_Bits { - EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, - EE_ChipSelect=0x801, EE_DataIn=0x08, -}; - -/* The EEPROM commands include the alway-set leading bit. */ -enum EEPROM_Cmds { - EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), -}; - -static int eeprom_read(long addr, int location) -{ - int i; - int retval = 0; - long ee_addr = addr + EECtrl; - int read_cmd = location | EE_ReadCmd; - writel(EE_ChipSelect, ee_addr); - - /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; - writel(dataval, ee_addr); - eeprom_delay(ee_addr); - writel(dataval | EE_ShiftClk, ee_addr); - eeprom_delay(ee_addr); - } - writel(EE_ChipSelect, ee_addr); - eeprom_delay(ee_addr); - - for (i = 16; i > 0; i--) { - writel(EE_ChipSelect | EE_ShiftClk, ee_addr); - eeprom_delay(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); - writel(EE_ChipSelect, ee_addr); - eeprom_delay(ee_addr); - } - - /* Terminate the EEPROM access. */ - writel(0, ee_addr); - return retval; -} - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. - - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back 33Mhz PCI cycles. */ -#define mdio_delay(mdio_addr) readl(mdio_addr) - -/* Set iff a MII transceiver on any interface requires mdio preamble. - This only set with older tranceivers, so the extra - code size of a per-interface flag is not worthwhile. */ -static char mii_preamble_required = 1; - -#define MDIO_WRITE0 (MDIO_EnbOutput) -#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) - -/* Generate the preamble required for initial synchronization and - a few older transceivers. */ -static void mdio_sync(long mdio_addr) -{ - int bits = 32; - - /* Establish sync by sending at least 32 logic ones. */ - while (--bits >= 0) { - writel(MDIO_WRITE1, mdio_addr); - mdio_delay(mdio_addr); - writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } -} - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - long mdio_addr = dev->base_addr + MIICtrl; - int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int i, retval = 0; - - if (mii_preamble_required) - mdio_sync(mdio_addr); - - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - - writel(dataval, mdio_addr); - mdio_delay(mdio_addr); - writel(dataval | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 20; i > 0; i--) { - writel(MDIO_EnbIn, mdio_addr); - mdio_delay(mdio_addr); - retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); - writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - struct netdev_private *np = dev->priv; - long mdio_addr = dev->base_addr + MIICtrl; - int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - int i; - - if (location == 4 && phy_id == np->phys[0]) - np->mii_if.advertising = value; - - if (mii_preamble_required) - mdio_sync(mdio_addr); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - - writel(dataval, mdio_addr); - mdio_delay(mdio_addr); - writel(dataval | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - writel(MDIO_EnbIn, mdio_addr); - mdio_delay(mdio_addr); - writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); - mdio_delay(mdio_addr); - } - return; -} - - -static int netdev_open(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int i; - - writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ - - netif_device_detach(dev); - i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); - if (i) - goto out_err; - - if (debug > 1) - printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", - dev->name, dev->irq); - - if((i=alloc_ringdesc(dev))) - goto out_err; - - spin_lock_irq(&np->lock); - netif_device_attach(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - - netif_start_queue(dev); - if (debug > 2) - printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); - - /* Set the timer to check for link beat. */ - init_timer(&np->timer); - np->timer.expires = jiffies + 1*HZ; - np->timer.data = (unsigned long)dev; - np->timer.function = &netdev_timer; /* timer handler */ - add_timer(&np->timer); - return 0; -out_err: - netif_device_attach(dev); - return i; -} - -#define MII_DAVICOM_DM9101 0x0181b800 - -static int update_link(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int duplex, fasteth, result, mii_reg; - - /* BSMR */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); - - if (mii_reg == 0xffff) - return np->csr6; - /* reread: the link status bit is sticky */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMSR); - if (!(mii_reg & 0x4)) { - if (netif_carrier_ok(dev)) { - if (debug) - printk(KERN_INFO "%s: MII #%d reports no link. Disabling watchdog.\n", - dev->name, np->phys[0]); - netif_carrier_off(dev); - } - return np->csr6; - } - if (!netif_carrier_ok(dev)) { - if (debug) - printk(KERN_INFO "%s: MII #%d link is back. Enabling watchdog.\n", - dev->name, np->phys[0]); - netif_carrier_on(dev); - } - - if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) { - /* If the link partner doesn't support autonegotiation - * the MII detects it's abilities with the "parallel detection". - * Some MIIs update the LPA register to the result of the parallel - * detection, some don't. - * The Davicom PHY [at least 0181b800] doesn't. - * Instead bit 9 and 13 of the BMCR are updated to the result - * of the negotiation.. - */ - mii_reg = mdio_read(dev, np->phys[0], MII_BMCR); - duplex = mii_reg & BMCR_FULLDPLX; - fasteth = mii_reg & BMCR_SPEED100; - } else { - int negotiated; - mii_reg = mdio_read(dev, np->phys[0], MII_LPA); - negotiated = mii_reg & np->mii_if.advertising; - - duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL); - fasteth = negotiated & 0x380; - } - duplex |= np->mii_if.duplex_lock; - /* remove fastether and fullduplex */ - result = np->csr6 & ~0x20000200; - if (duplex) - result |= 0x200; - if (fasteth) - result |= 0x20000000; - if (result != np->csr6 && debug) - printk(KERN_INFO "%s: Setting %dMBit-%s-duplex based on MII#%d\n", - dev->name, fasteth ? 100 : 10, - duplex ? "full" : "half", np->phys[0]); - return result; -} - -#define RXTX_TIMEOUT 2000 -static inline void update_csr6(struct net_device *dev, int new) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int limit = RXTX_TIMEOUT; - - if (!netif_device_present(dev)) - new = 0; - if (new==np->csr6) - return; - /* stop both Tx and Rx processes */ - writel(np->csr6 & ~0x2002, ioaddr + NetworkConfig); - /* wait until they have really stopped */ - for (;;) { - int csr5 = readl(ioaddr + IntrStatus); - int t; - - t = (csr5 >> 17) & 0x07; - if (t==0||t==1) { - /* rx stopped */ - t = (csr5 >> 20) & 0x07; - if (t==0||t==1) - break; - } - - limit--; - if(!limit) { - printk(KERN_INFO "%s: couldn't stop rxtx, IntrStatus %xh.\n", - dev->name, csr5); - break; - } - udelay(1); - } - np->csr6 = new; - /* and restart them with the new configuration */ - writel(np->csr6, ioaddr + NetworkConfig); - if (new & 0x200) - np->mii_if.full_duplex = 1; -} - -static void netdev_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - if (debug > 2) - printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " - "config %8.8x.\n", - dev->name, (int)readl(ioaddr + IntrStatus), - (int)readl(ioaddr + NetworkConfig)); - spin_lock_irq(&np->lock); - update_csr6(dev, update_link(dev)); - spin_unlock_irq(&np->lock); - np->timer.expires = jiffies + 10*HZ; - add_timer(&np->timer); -} - -static void init_rxtx_rings(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int i; - - np->rx_head_desc = &np->rx_ring[0]; - np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; - - /* Initial all Rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].length = np->rx_buf_sz; - np->rx_ring[i].status = 0; - np->rx_skbuff[i] = 0; - } - /* Mark the last entry as wrapping the ring. */ - np->rx_ring[i-1].length |= DescEndRing; - - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); - np->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - np->rx_addr[i] = pci_map_single(np->pci_dev,skb->tail, - skb->len,PCI_DMA_FROMDEVICE); - - np->rx_ring[i].buffer1 = np->rx_addr[i]; - np->rx_ring[i].status = DescOwn; - } - - np->cur_rx = 0; - np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - /* Initialize the Tx descriptors */ - for (i = 0; i < TX_RING_SIZE; i++) { - np->tx_skbuff[i] = 0; - np->tx_ring[i].status = 0; - } - np->tx_full = 0; - np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; - - writel(np->ring_dma_addr, dev->base_addr + RxRingPtr); - writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, - dev->base_addr + TxRingPtr); - -} - -static void free_rxtx_rings(struct netdev_private* np) -{ - int i; - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].status = 0; - if (np->rx_skbuff[i]) { - pci_unmap_single(np->pci_dev, - np->rx_addr[i], - np->rx_skbuff[i]->len, - PCI_DMA_FROMDEVICE); - dev_kfree_skb(np->rx_skbuff[i]); - } - np->rx_skbuff[i] = 0; - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (np->tx_skbuff[i]) { - pci_unmap_single(np->pci_dev, - np->tx_addr[i], - np->tx_skbuff[i]->len, - PCI_DMA_TODEVICE); - dev_kfree_skb(np->tx_skbuff[i]); - } - np->tx_skbuff[i] = 0; - } -} - -static void init_registers(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int i; - - for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + i); - - /* Initialize other registers. */ -#ifdef __BIG_ENDIAN - i = (1<<20); /* Big-endian descriptors */ -#else - i = 0; -#endif - i |= (0x04<<2); /* skip length 4 u32 */ - i |= 0x02; /* give Rx priority */ - - /* Configure the PCI bus bursts and FIFO thresholds. - 486: Set 8 longword cache alignment, 8 longword burst. - 586: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 0000 align to cache 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords */ - -#if defined (__i386__) && !defined(MODULE) - /* When not a module we can work around broken '486 PCI boards. */ - if (boot_cpu_data.x86 <= 4) { - i |= 0x4800; - printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " - "alignment to 8 longwords.\n", dev->name); - } else { - i |= 0xE000; - } -#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) - i |= 0xE000; -#elif defined(__sparc__) - i |= 0x4800; -#else -#warning Processor architecture undefined - i |= 0x4800; -#endif - writel(i, ioaddr + PCIBusCfg); - - np->csr6 = 0; - /* 128 byte Tx threshold; - Transmit on; Receive on; */ - update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev)); - - /* Clear and Enable interrupts by setting the interrupt mask. */ - writel(0x1A0F5, ioaddr + IntrStatus); - writel(0x1A0F5, ioaddr + IntrEnable); - - writel(0, ioaddr + RxStartDemand); -} - -static void tx_timeout(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," - " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); - - { - int i; - printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)np->rx_ring[i].status); - printk("\n"KERN_DEBUG" Tx ring %p: ", np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", np->tx_ring[i].status); - printk("\n"); - } - printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n", - np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes); - printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C)); - - disable_irq(dev->irq); - spin_lock_irq(&np->lock); - /* - * Under high load dirty_tx and the internal tx descriptor pointer - * come out of sync, thus perform a software reset and reinitialize - * everything. - */ - - writel(1, dev->base_addr+PCIBusCfg); - udelay(1); - - free_rxtx_rings(np); - init_rxtx_rings(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - enable_irq(dev->irq); - - netif_wake_queue(dev); - dev->trans_start = jiffies; - np->stats.tx_errors++; - return; -} - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static int alloc_ringdesc(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - - np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - np->rx_ring = pci_alloc_consistent(np->pci_dev, - sizeof(struct w840_rx_desc)*RX_RING_SIZE + - sizeof(struct w840_tx_desc)*TX_RING_SIZE, - &np->ring_dma_addr); - if(!np->rx_ring) - return -ENOMEM; - init_rxtx_rings(dev); - return 0; -} - -static void free_ringdesc(struct netdev_private *np) -{ - pci_free_consistent(np->pci_dev, - sizeof(struct w840_rx_desc)*RX_RING_SIZE + - sizeof(struct w840_tx_desc)*TX_RING_SIZE, - np->rx_ring, np->ring_dma_addr); - -} - -static int start_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - unsigned entry; - - /* Caution: the write order is important here, set the field - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = np->cur_tx % TX_RING_SIZE; - - np->tx_addr[entry] = pci_map_single(np->pci_dev, - skb->data,skb->len, PCI_DMA_TODEVICE); - np->tx_skbuff[entry] = skb; - - np->tx_ring[entry].buffer1 = np->tx_addr[entry]; - if (skb->len < TX_BUFLIMIT) { - np->tx_ring[entry].length = DescWholePkt | skb->len; - } else { - int len = skb->len - TX_BUFLIMIT; - - np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT; - np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT; - } - if(entry == TX_RING_SIZE-1) - np->tx_ring[entry].length |= DescEndRing; - - /* Now acquire the irq spinlock. - * The difficult race is the the ordering between - * increasing np->cur_tx and setting DescOwn: - * - if np->cur_tx is increased first the interrupt - * handler could consider the packet as transmitted - * since DescOwn is cleared. - * - If DescOwn is set first the NIC could report the - * packet as sent, but the interrupt handler would ignore it - * since the np->cur_tx was not yet increased. - */ - spin_lock_irq(&np->lock); - np->cur_tx++; - - wmb(); /* flush length, buffer1, buffer2 */ - np->tx_ring[entry].status = DescOwn; - wmb(); /* flush status and kick the hardware */ - writel(0, dev->base_addr + TxStartDemand); - np->tx_q_bytes += skb->len; - /* Work around horrible bug in the chip by marking the queue as full - when we do not have FIFO room for a maximum sized packet. */ - if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN || - ((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) { - netif_stop_queue(dev); - wmb(); - np->tx_full = 1; - } - spin_unlock_irq(&np->lock); - - dev->trans_start = jiffies; - - if (debug > 4) { - printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", - dev->name, np->cur_tx, entry); - } - return 0; -} - -static void netdev_tx_done(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { - int entry = np->dirty_tx % TX_RING_SIZE; - int tx_status = np->tx_ring[entry].status; - - if (tx_status < 0) - break; - if (tx_status & 0x8000) { /* There was an error, log it. */ -#ifndef final_version - if (debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, tx_status); -#endif - np->stats.tx_errors++; - if (tx_status & 0x0104) np->stats.tx_aborted_errors++; - if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; - if (tx_status & 0x0200) np->stats.tx_window_errors++; - if (tx_status & 0x0002) np->stats.tx_fifo_errors++; - if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0) - np->stats.tx_heartbeat_errors++; -#ifdef ETHER_STATS - if (tx_status & 0x0100) np->stats.collisions16++; -#endif - } else { -#ifdef ETHER_STATS - if (tx_status & 0x0001) np->stats.tx_deferred++; -#endif -#ifndef final_version - if (debug > 3) - printk(KERN_DEBUG "%s: Transmit slot %d ok, Tx status %8.8x.\n", - dev->name, entry, tx_status); -#endif - np->stats.tx_bytes += np->tx_skbuff[entry]->len; - np->stats.collisions += (tx_status >> 3) & 15; - np->stats.tx_packets++; - } - /* Free the original skb. */ - pci_unmap_single(np->pci_dev,np->tx_addr[entry], - np->tx_skbuff[entry]->len, - PCI_DMA_TODEVICE); - np->tx_q_bytes -= np->tx_skbuff[entry]->len; - dev_kfree_skb_irq(np->tx_skbuff[entry]); - np->tx_skbuff[entry] = 0; - } - if (np->tx_full && - np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART && - np->tx_q_bytes < TX_BUG_FIFO_LIMIT) { - /* The ring is no longer full, clear tbusy. */ - np->tx_full = 0; - wmb(); - netif_wake_queue(dev); - } -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) -{ - struct net_device *dev = (struct net_device *)dev_instance; - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - int work_limit = max_interrupt_work; - - if (!netif_device_present(dev)) - return; - do { - u32 intr_status = readl(ioaddr + IntrStatus); - - /* Acknowledge all of the current interrupt sources ASAP. */ - writel(intr_status & 0x001ffff, ioaddr + IntrStatus); - - if (debug > 4) - printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", - dev->name, intr_status); - - if ((intr_status & (NormalIntr|AbnormalIntr)) == 0) - break; - - if (intr_status & (IntrRxDone | RxNoBuf)) - netdev_rx(dev); - if (intr_status & RxNoBuf) - writel(0, ioaddr + RxStartDemand); - - if (intr_status & (TxIdle | IntrTxDone) && - np->cur_tx != np->dirty_tx) { - spin_lock(&np->lock); - netdev_tx_done(dev); - spin_unlock(&np->lock); - } - - /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr | - TimerInt | IntrTxStopped)) - netdev_error(dev, intr_status); - - if (--work_limit < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, " - "status=0x%4.4x.\n", dev->name, intr_status); - /* Set the timer to re-enable the other interrupts after - 10*82usec ticks. */ - spin_lock(&np->lock); - if (netif_device_present(dev)) { - writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable); - writel(10, ioaddr + GPTimer); - } - spin_unlock(&np->lock); - break; - } - } while (1); - - if (debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", - dev->name, (int)readl(ioaddr + IntrStatus)); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int netdev_rx(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - int entry = np->cur_rx % RX_RING_SIZE; - int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx; - - if (debug > 4) { - printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", - entry, np->rx_ring[entry].status); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (--work_limit >= 0) { - struct w840_rx_desc *desc = np->rx_head_desc; - s32 status = desc->status; - - if (debug > 4) - printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", - status); - if (status < 0) - break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { - /* Ingore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, entry %#x status %4.4x!\n", - dev->name, np->cur_rx, status); - np->stats.rx_length_errors++; - } - } else if (status & 0x8000) { - /* There was a fatal error. */ - if (debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - np->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) np->stats.rx_length_errors++; - if (status & 0x004C) np->stats.rx_frame_errors++; - if (status & 0x0002) np->stats.rx_crc_errors++; - } - } else { - struct sk_buff *skb; - /* Omit the four octet CRC from the length. */ - int pkt_len = ((status >> 16) & 0x7ff) - 4; - -#ifndef final_version - if (debug > 4) - printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" - " status %x.\n", pkt_len, status); -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(np->pci_dev,np->rx_addr[entry], - np->rx_skbuff[entry]->len, - PCI_DMA_FROMDEVICE); - /* Call copy + cksum if available. */ -#if HAS_IP_COPYSUM - eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); - skb_put(skb, pkt_len); -#else - memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, - pkt_len); -#endif - } else { - pci_unmap_single(np->pci_dev,np->rx_addr[entry], - np->rx_skbuff[entry]->len, - PCI_DMA_FROMDEVICE); - skb_put(skb = np->rx_skbuff[entry], pkt_len); - np->rx_skbuff[entry] = NULL; - } -#ifndef final_version /* Remove after testing. */ - /* You will want this info for the initial debug. */ - if (debug > 5) - printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" - "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " - "%d.%d.%d.%d.\n", - skb->data[0], skb->data[1], skb->data[2], skb->data[3], - skb->data[4], skb->data[5], skb->data[6], skb->data[7], - skb->data[8], skb->data[9], skb->data[10], - skb->data[11], skb->data[12], skb->data[13], - skb->data[14], skb->data[15], skb->data[16], - skb->data[17]); -#endif - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - np->stats.rx_packets++; - np->stats.rx_bytes += pkt_len; - } - entry = (++np->cur_rx) % RX_RING_SIZE; - np->rx_head_desc = &np->rx_ring[entry]; - } - - /* Refill the Rx ring buffers. */ - for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { - struct sk_buff *skb; - entry = np->dirty_rx % RX_RING_SIZE; - if (np->rx_skbuff[entry] == NULL) { - skb = dev_alloc_skb(np->rx_buf_sz); - np->rx_skbuff[entry] = skb; - if (skb == NULL) - break; /* Better luck next round. */ - skb->dev = dev; /* Mark as being used by this device. */ - np->rx_addr[entry] = pci_map_single(np->pci_dev, - skb->tail, - skb->len, PCI_DMA_FROMDEVICE); - np->rx_ring[entry].buffer1 = np->rx_addr[entry]; - } - wmb(); - np->rx_ring[entry].status = DescOwn; - } - - return 0; -} - -static void netdev_error(struct net_device *dev, int intr_status) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - if (debug > 2) - printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n", - dev->name, intr_status); - if (intr_status == 0xffffffff) - return; - spin_lock(&np->lock); - if (intr_status & TxFIFOUnderflow) { - int new; - /* Bump up the Tx threshold */ -#if 0 - /* This causes lots of dropped packets, - * and under high load even tx_timeouts - */ - new = np->csr6 + 0x4000; -#else - new = (np->csr6 >> 14)&0x7f; - if (new < 64) - new *= 2; - else - new = 127; /* load full packet before starting */ - new = (np->csr6 & ~(0x7F << 14)) | (new<<14); -#endif - printk(KERN_DEBUG "%s: Tx underflow, new csr6 %8.8x.\n", - dev->name, new); - update_csr6(dev, new); - } - if (intr_status & IntrRxDied) { /* Missed a Rx frame. */ - np->stats.rx_errors++; - } - if (intr_status & TimerInt) { - /* Re-enable other interrupts. */ - if (netif_device_present(dev)) - writel(0x1A0F5, ioaddr + IntrEnable); - } - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - writel(0, ioaddr + RxStartDemand); - spin_unlock(&np->lock); -} - -static struct net_device_stats *get_stats(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - /* The chip only need report frame silently dropped. */ - spin_lock_irq(&np->lock); - if (netif_running(dev) && netif_device_present(dev)) - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - spin_unlock_irq(&np->lock); - - return &np->stats; -} - - -static u32 __set_rx_mode(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - u32 mc_filter[2]; /* Multicast hash filter */ - u32 rx_mode; - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - /* Unconditionally log net taps. */ - printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys - | AcceptMyPhys; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - /* Too many to match, or accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - } else { - struct dev_mc_list *mclist; - int i; - memset(mc_filter, 0, sizeof(mc_filter)); - 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) ^ 0x3F, - mc_filter); - } - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - } - writel(mc_filter[0], ioaddr + MulticastFilter0); - writel(mc_filter[1], ioaddr + MulticastFilter1); - return rx_mode; -} - -static void set_rx_mode(struct net_device *dev) -{ - struct netdev_private *np = dev->priv; - u32 rx_mode = __set_rx_mode(dev); - spin_lock_irq(&np->lock); - update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode); - spin_unlock_irq(&np->lock); -} - -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct netdev_private *np = dev->priv; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, np->pci_dev->slot_name); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&np->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&np->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - } - - return -EOPNOTSUPP; -} - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; - struct netdev_private *np = dev->priv; - - switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; - /* Fall Through */ - - case SIOCGMIIREG: /* Read MII PHY register. */ - spin_lock_irq(&np->lock); - data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); - spin_unlock_irq(&np->lock); - return 0; - - case SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - spin_lock_irq(&np->lock); - mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); - spin_unlock_irq(&np->lock); - return 0; - default: - return -EOPNOTSUPP; - } -} - -static int netdev_close(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; - - netif_stop_queue(dev); - - if (debug > 1) { - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x " - "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), - (int)readl(ioaddr + NetworkConfig)); - printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", - dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); - } - - /* Stop the chip's Tx and Rx processes. */ - spin_lock_irq(&np->lock); - netif_device_detach(dev); - update_csr6(dev, 0); - writel(0x0000, ioaddr + IntrEnable); - spin_unlock_irq(&np->lock); - - free_irq(dev->irq, dev); - wmb(); - netif_device_attach(dev); - - if (readl(ioaddr + NetworkConfig) != 0xffffffff) - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - -#ifdef __i386__ - if (debug > 2) { - int i; - - printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", - (int)np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" #%d desc. %4.4x %4.4x %8.8x.\n", - i, np->tx_ring[i].length, - np->tx_ring[i].status, np->tx_ring[i].buffer1); - printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", - (int)np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", - i, np->rx_ring[i].length, - np->rx_ring[i].status, np->rx_ring[i].buffer1); - } - } -#endif /* __i386__ debugging only */ - - del_timer_sync(&np->timer); - - free_rxtx_rings(np); - free_ringdesc(np); - - return 0; -} - -static void __devexit w840_remove1 (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - if (dev) { - unregister_netdev(dev); - pci_release_regions(pdev); -#ifndef USE_IO_OPS - iounmap((char *)(dev->base_addr)); -#endif - kfree(dev); - } - - pci_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM - -/* - * suspend/resume synchronization: - * - open, close, do_ioctl: - * rtnl_lock, & netif_device_detach after the rtnl_unlock. - * - get_stats: - * spin_lock_irq(np->lock), doesn't touch hw if not present - * - hard_start_xmit: - * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); - * - tx_timeout: - * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); - * - set_multicast_list - * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); - * - interrupt handler - * doesn't touch hw if not present, synchronize_irq waits for - * running instances of the interrupt handler. - * - * Disabling hw requires clearing csr6 & IntrEnable. - * update_csr6 & all function that write IntrEnable check netif_device_present - * before settings any bits. - * - * Detach must occur under spin_unlock_irq(), interrupts from a detached - * device would cause an irq storm. - */ -static int w840_suspend (struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct netdev_private *np = dev->priv; - long ioaddr = dev->base_addr; - - rtnl_lock(); - if (netif_running (dev)) { - del_timer_sync(&np->timer); - - spin_lock_irq(&np->lock); - netif_device_detach(dev); - update_csr6(dev, 0); - writel(0, ioaddr + IntrEnable); - netif_stop_queue(dev); - spin_unlock_irq(&np->lock); - - spin_unlock_wait(&dev->xmit_lock); - synchronize_irq(); - - np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; - - /* no more hardware accesses behind this line. */ - - if (np->csr6) BUG(); - if (readl(ioaddr + IntrEnable)) BUG(); - - /* pci_power_off(pdev, -1); */ - - free_rxtx_rings(np); - } else { - netif_device_detach(dev); - } - rtnl_unlock(); - return 0; -} - - -static int w840_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct netdev_private *np = dev->priv; - - rtnl_lock(); - if (netif_device_present(dev)) - goto out; /* device not suspended */ - if (netif_running(dev)) { - pci_enable_device(pdev); - /* pci_power_on(pdev); */ - - spin_lock_irq(&np->lock); - writel(1, dev->base_addr+PCIBusCfg); - readl(dev->base_addr+PCIBusCfg); - udelay(1); - netif_device_attach(dev); - init_rxtx_rings(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - - netif_wake_queue(dev); - - np->timer.expires = jiffies + 1*HZ; - add_timer(&np->timer); - } else { - netif_device_attach(dev); - } -out: - rtnl_unlock(); - return 0; -} -#endif - -static struct pci_driver w840_driver = { - name: DRV_NAME, - id_table: w840_pci_tbl, - probe: w840_probe1, - remove: __devexit_p(w840_remove1), -#ifdef CONFIG_PM - suspend: w840_suspend, - resume: w840_resume, -#endif -}; - -static int __init w840_init(void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_module_init(&w840_driver); -} - -static void __exit w840_exit(void) -{ - pci_unregister_driver(&w840_driver); -} - -module_init(w840_init); -module_exit(w840_exit); diff -urN linux-2.5.6-pre3/drivers/net/wireless/orinoco.c linux-2.5.6/drivers/net/wireless/orinoco.c --- linux-2.5.6-pre3/drivers/net/wireless/orinoco.c Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/drivers/net/wireless/orinoco.c Thu Mar 7 18:24:49 2002 @@ -1253,6 +1253,7 @@ /* Pass the packet to the networking stack */ netif_rx(skb); + dev->last_rx = jiffies; stats->rx_packets++; stats->rx_bytes += length; diff -urN linux-2.5.6-pre3/drivers/pci/pci.c linux-2.5.6/drivers/pci/pci.c --- linux-2.5.6-pre3/drivers/pci/pci.c Thu Mar 7 18:24:29 2002 +++ linux-2.5.6/drivers/pci/pci.c Thu Mar 7 18:24:49 2002 @@ -23,6 +23,7 @@ #include /* for hotplug_path */ #include #include +#include #include #include /* isa_dma_bridge_buggy */ @@ -848,6 +849,100 @@ pcibios_set_master(dev); } +/** + * pdev_set_mwi - arch helper function for pcibios_set_mwi + * @dev: the PCI device for which MWI is enabled + * + * Helper function for implementation the arch-specific pcibios_set_mwi + * function. Originally copied from drivers/net/acenic.c. + * Copyright 1998-2001 by Jes Sorensen, . + * + * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. + */ +int +pdev_set_mwi(struct pci_dev *dev) +{ + int rc = 0; + u8 cache_size; + + /* + * Looks like this is necessary to deal with on all architectures, + * even this %$#%$# N440BX Intel based thing doesn't get it right. + * Ie. having two NICs in the machine, one will have the cache + * line set at boot time, the other will not. + */ + pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cache_size); + cache_size <<= 2; + if (cache_size != SMP_CACHE_BYTES) { + printk(KERN_WARNING "PCI: %s PCI cache line size set incorrectly " + "(%i bytes) by BIOS/FW, ", + dev->slot_name, cache_size); + if (cache_size > SMP_CACHE_BYTES) { + printk("expecting %i\n", SMP_CACHE_BYTES); + rc = -EINVAL; + } else { + printk("correcting to %i\n", SMP_CACHE_BYTES); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, + SMP_CACHE_BYTES >> 2); + } + } + + return rc; +} + +/** + * pci_set_mwi - enables memory-write-validate PCI transaction + * @dev: the PCI device for which MWI is enabled + * + * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND, + * and then calls @pcibios_set_mwi to do the needed arch specific + * operations or a generic mwi-prep function. + * + * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. + */ +int +pci_set_mwi(struct pci_dev *dev) +{ + int rc; + u16 cmd; + +#ifdef HAVE_ARCH_PCI_MWI + rc = pcibios_set_mwi(dev); +#else + rc = pdev_set_mwi(dev); +#endif + + if (rc) + return rc; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (! (cmd & PCI_COMMAND_INVALIDATE)) { + DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", dev->slot_name); + cmd |= PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + + return 0; +} + +/** + * pci_clear_mwi - disables Memory-Write-Invalidate for device dev + * @dev: the PCI device to disable + * + * Disables PCI Memory-Write-Invalidate transaction on the device + */ +void +pci_clear_mwi(struct pci_dev *dev) +{ + u16 cmd; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_INVALIDATE) { + cmd &= ~PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } +} + int pci_set_dma_mask(struct pci_dev *dev, u64 mask) { @@ -2002,6 +2097,9 @@ EXPORT_SYMBOL(pci_find_slot); EXPORT_SYMBOL(pci_find_subsys); EXPORT_SYMBOL(pci_set_master); +EXPORT_SYMBOL(pci_set_mwi); +EXPORT_SYMBOL(pci_clear_mwi); +EXPORT_SYMBOL(pdev_set_mwi); EXPORT_SYMBOL(pci_set_dma_mask); EXPORT_SYMBOL(pci_dac_set_dma_mask); EXPORT_SYMBOL(pci_assign_resource); @@ -2019,11 +2117,13 @@ EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_do_scan_bus); EXPORT_SYMBOL(pci_scan_slot); +#ifdef CONFIG_PROC_FS EXPORT_SYMBOL(pci_proc_attach_device); EXPORT_SYMBOL(pci_proc_detach_device); EXPORT_SYMBOL(pci_proc_attach_bus); EXPORT_SYMBOL(pci_proc_detach_bus); #endif +#endif EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); diff -urN linux-2.5.6-pre3/drivers/pci/pci.ids linux-2.5.6/drivers/pci/pci.ids --- linux-2.5.6-pre3/drivers/pci/pci.ids Thu Mar 7 18:24:29 2002 +++ linux-2.5.6/drivers/pci/pci.ids Thu Mar 7 18:24:49 2002 @@ -4183,12 +4183,18 @@ 14e3 AMTELCO 14e4 BROADCOM Corporation 1644 NetXtreme BCM5700 Gigabit Ethernet + 1014 0277 Broadcom Vigil B5700 1000Base-T + 1028 00d1 Broadcom BCM5700 + 1028 0106 Broadcom BCM5700 + 1028 0109 Broadcom BCM5700 + 1028 010a Broadcom BCM5700 10b7 1000 3C996-T 1000BaseTX 10b7 1001 3C996B-T 1000BaseTX 10b7 1002 3C996C-T 1000BaseTX 10b7 1003 3C997-T 1000BaseTX 10b7 1004 3C996-SX 1000BaseSX 10b7 1005 3C997-SX 1000BaseSX + 10b7 1008 3C942 Gigabit LOM (31X31) 14e4 0002 NetXtreme 1000BaseSX 14e4 0003 NetXtreme 1000BaseSX 14e4 0004 NetXtreme 1000BaseTX @@ -4197,6 +4203,7 @@ 0e11 007c NC7770 1000BaseTX 0e11 007d NC6770 1000BaseSX 0e11 0085 NC7780 1000BaseTX + 1028 0121 Broadcom BCM5701 10b7 1004 3C996-SX 1000BaseSX 10b7 1006 3C996B-T 1000BaseTX 10b7 1007 3C1000-T 1000BaseTX @@ -4207,7 +4214,16 @@ 14e4 0007 NetXtreme BCM5701 1000BaseSX 14e4 0008 NetXtreme BCM5701 1000BaseTX 14e4 8008 NetXtreme BCM5701 1000BaseTX + 1646 NetXtreme BCM5702 Gigabit Ethernet + 14e4 8009 Broadcom BCM5702 1647 NetXtreme BCM5703 Gigabit Ethernet + 0e11 009a NC7770 + 0e11 0099 NC7780 + 14e4 0009 Broadcom BCM5703 + 14e4 8009 Broadcom BCM5703 + 164d NetXtreme BCM5702FE Gigabit Ethernet + 16a6 NetXtreme BCM5702X Gigabit Ethernet + 16a7 NetXtreme BCM5703X Gigabit Ethernet 5820 BCM5820 Crypto Accelerator 14e5 Pixelfusion Ltd 14e6 SHINING Technology Inc @@ -4647,6 +4663,8 @@ 0400 FarSync T2P (2 port X.21/V.35/V.24) 0440 FarSync T4P (4 port X.21/V.35/V.24) 1668 Action Tec Electronics Inc +173b Altima (nee BroadCom) + 03e8 AC1000 Gigabit Ethernet 1813 Ambient Technologies Inc 1a08 Sierra semiconductor 0000 SC15064 diff -urN linux-2.5.6-pre3/drivers/pnp/pnpbios_core.c linux-2.5.6/drivers/pnp/pnpbios_core.c --- linux-2.5.6-pre3/drivers/pnp/pnpbios_core.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/drivers/pnp/pnpbios_core.c Thu Mar 7 18:24:49 2002 @@ -1189,7 +1189,7 @@ subsys_initcall(pnpbios_init); -void __init pnpbios_init(void) +int __init pnpbios_init(void) { union pnp_bios_expansion_header *check; u8 sum; @@ -1200,7 +1200,7 @@ if(pnpbios_disabled) { printk(KERN_INFO "PnPBIOS: Disabled.\n"); - return; + return 0; } if ( is_sony_vaio_laptop ) @@ -1253,6 +1253,7 @@ if(kernel_thread(pnp_dock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)>0) unloading = 0; #endif + return 0; } #ifdef MODULE diff -urN linux-2.5.6-pre3/drivers/s390/net/ctctty.c linux-2.5.6/drivers/s390/net/ctctty.c --- linux-2.5.6-pre3/drivers/s390/net/ctctty.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/drivers/s390/net/ctctty.c Thu Mar 7 18:24:50 2002 @@ -286,7 +286,7 @@ if (!info->netdev) { if (skb) - kfree(skb); + kfree_skb(skb); return 0; } if (info->flags & CTC_ASYNC_TX_LINESTAT) { diff -urN linux-2.5.6-pre3/drivers/scsi/sr.c linux-2.5.6/drivers/scsi/sr.c --- linux-2.5.6-pre3/drivers/scsi/sr.c Thu Mar 7 18:24:31 2002 +++ linux-2.5.6/drivers/scsi/sr.c Thu Mar 7 18:24:50 2002 @@ -99,11 +99,13 @@ static void sr_release(struct cdrom_device_info *cdi) { - if (scsi_CDs[minor(cdi->dev)].device->sector_size > 2048) + Scsi_CD *SCp = cdi->handle; + + if (SCp->device->sector_size > 2048) sr_set_blocklength(minor(cdi->dev), 2048); - scsi_CDs[minor(cdi->dev)].device->access_count--; - if (scsi_CDs[minor(cdi->dev)].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_CDs[minor(cdi->dev)].device->host->hostt->module); + SCp->device->access_count--; + if (SCp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(SCp->device->host->hostt->module); if (sr_template.module) __MOD_DEC_USE_COUNT(sr_template.module); } @@ -144,28 +146,27 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot) { + Scsi_CD *SCp = cdi->handle; int retval; if (CDSL_CURRENT != slot) { /* no changer support */ return -EINVAL; } - retval = scsi_ioctl(scsi_CDs[minor(cdi->dev)].device, - SCSI_IOCTL_TEST_UNIT_READY, 0); + retval = scsi_ioctl(SCp->device, SCSI_IOCTL_TEST_UNIT_READY, 0); if (retval) { /* Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, * and we will figure it out later once the drive is * available again. */ - - scsi_CDs[minor(cdi->dev)].device->changed = 1; + SCp->device->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ }; - retval = scsi_CDs[minor(cdi->dev)].device->changed; - scsi_CDs[minor(cdi->dev)].device->changed = 0; + retval = SCp->device->changed; + SCp->device->changed = 0; /* If the disk changed, the capacity will now be different, * so we force a re-read of this information */ if (retval) { @@ -179,9 +180,8 @@ * be trying to use something that is too small if the disc * has changed. */ - scsi_CDs[minor(cdi->dev)].needs_sector_size = 1; - - scsi_CDs[minor(cdi->dev)].device->sector_size = 2048; + SCp->needs_sector_size = 1; + SCp->device->sector_size = 2048; } return retval; } @@ -198,6 +198,7 @@ int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 0; int device_nr = DEVICE_NR(SCpnt->request.rq_dev); + Scsi_CD *SCp = &scsi_CDs[device_nr]; #ifdef DEBUG printk("sr.c done: %x %p\n", result, SCpnt->request.bh->b_data); @@ -222,7 +223,7 @@ block_sectors = bio_sectors(SCpnt->request.bio); if (block_sectors < 4) block_sectors = 4; - if (scsi_CDs[device_nr].device->sector_size == 2048) + if (SCp->device->sector_size == 2048) error_sector <<= 2; error_sector &= ~(block_sectors - 1); good_sectors = error_sector - SCpnt->request.sector; @@ -235,7 +236,7 @@ * 75 2K sectors, we decrease the saved size value. */ if ((error_sector >> 1) < sr_sizes[device_nr] && - scsi_CDs[device_nr].capacity - error_sector < 4 * 75) + SCp->capacity - error_sector < 4 * 75) sr_sizes[device_nr] = error_sector >> 1; } @@ -250,32 +251,34 @@ static request_queue_t *sr_find_queue(kdev_t dev) { - /* - * No such device - */ - if (minor(dev) >= sr_template.dev_max || !scsi_CDs[minor(dev)].device) - return NULL; + Scsi_CD *SCp; - return &scsi_CDs[minor(dev)].device->request_queue; + if (minor(dev) >= sr_template.dev_max) + return NULL; + SCp = &scsi_CDs[minor(dev)]; + if (!SCp->device) + return NULL; + return &SCp->device->request_queue; } static int sr_init_command(Scsi_Cmnd * SCpnt) { int dev, devm, block=0, this_count, s_size; + Scsi_CD *SCp; devm = minor(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); + SCp = &scsi_CDs[dev]; SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %d, block = %d\n", devm, block)); - if (dev >= sr_template.nr_dev || - !scsi_CDs[dev].device || - !scsi_CDs[dev].device->online) { + if (dev >= sr_template.nr_dev || !SCp->device || !SCp->device->online) { SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", SCpnt->request.nr_sectors)); SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt)); return 0; } - if (scsi_CDs[dev].device->changed) { + + if (SCp->device->changed) { /* * quietly refuse to do anything to a changed disc until the * changed bit has been reset @@ -292,7 +295,7 @@ * we do lazy blocksize switching (when reading XA sectors, * see CDROMREADMODE2 ioctl) */ - s_size = scsi_CDs[dev].device->sector_size; + s_size = SCp->device->sector_size; if (s_size > 2048) { if (!in_interrupt()) sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev), 2048); @@ -306,7 +309,7 @@ } if (rq_data_dir(&SCpnt->request) == WRITE) { - if (!scsi_CDs[dev].device->writeable) + if (!SCp->device->writeable) return 0; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = SCSI_DATA_WRITE; @@ -355,7 +358,7 @@ * host adapter, it's safe to assume that we can at least transfer * this many bytes between each connect / disconnect. */ - SCpnt->transfersize = scsi_CDs[dev].device->sector_size; + SCpnt->transfersize = SCp->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; @@ -397,22 +400,23 @@ static int sr_open(struct cdrom_device_info *cdi, int purpose) { + Scsi_CD *SCp = cdi->handle; + check_disk_change(cdi->dev); - if (minor(cdi->dev) >= sr_template.dev_max - || !scsi_CDs[minor(cdi->dev)].device) { + if (minor(cdi->dev) >= sr_template.dev_max || !SCp->device) { return -ENXIO; /* No such device */ } /* * If the device is in error recovery, wait until it is done. * If the device is offline, then disallow any access to it. */ - if (!scsi_block_when_processing_errors(scsi_CDs[minor(cdi->dev)].device)) { + if (!scsi_block_when_processing_errors(SCp->device)) { return -ENXIO; } - scsi_CDs[minor(cdi->dev)].device->access_count++; - if (scsi_CDs[minor(cdi->dev)].device->host->hostt->module) - __MOD_INC_USE_COUNT(scsi_CDs[minor(cdi->dev)].device->host->hostt->module); + SCp->device->access_count++; + if (SCp->device->host->hostt->module) + __MOD_INC_USE_COUNT(SCp->device->host->hostt->module); if (sr_template.module) __MOD_INC_USE_COUNT(sr_template.module); @@ -421,7 +425,7 @@ * this is the case, and try again. */ - if (scsi_CDs[minor(cdi->dev)].needs_sector_size) + if (SCp->needs_sector_size) get_sectorsize(minor(cdi->dev)); return 0; @@ -472,31 +476,25 @@ { unsigned char cmd[10]; unsigned char *buffer; - int the_result, retries; + int the_result, retries = 3; int sector_size; - Scsi_Request *SRpnt; + Scsi_Request *SRpnt = NULL; + Scsi_CD *SCp; request_queue_t *queue; - buffer = (unsigned char *) kmalloc(512, GFP_DMA); - SRpnt = scsi_allocate_request(scsi_CDs[i].device); - - if(buffer == NULL || SRpnt == NULL) - { - scsi_CDs[i].capacity = 0x1fffff; - sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; - if(buffer) - kfree(buffer); - if(SRpnt) - scsi_release_request(SRpnt); - return; - } + SCp = &scsi_CDs[i]; + + buffer = kmalloc(512, GFP_DMA); + if (!buffer) + goto Enomem; + SRpnt = scsi_allocate_request(SCp->device); + if (!SRpnt) + goto Enomem; - retries = 3; do { cmd[0] = READ_CAPACITY; - cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun << 5) & 0xe0) : 0; memset((void *) &cmd[2], 0, 8); SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ SRpnt->sr_cmd_len = 0; @@ -519,15 +517,15 @@ SRpnt = NULL; if (the_result) { - scsi_CDs[i].capacity = 0x1fffff; + SCp->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; + SCp->needs_sector_size = 1; } else { #if 0 if (cdrom_get_last_written(mkdev(MAJOR_NR, i), &scsi_CDs[i].capacity)) #endif - scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) | + SCp->capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); @@ -546,33 +544,45 @@ sector_size = 2048; /* fall through */ case 2048: - scsi_CDs[i].capacity *= 4; + SCp->capacity *= 4; /* fall through */ case 512: break; default: printk("sr%d: unsupported sector size %d.\n", i, sector_size); - scsi_CDs[i].capacity = 0; - scsi_CDs[i].needs_sector_size = 1; + SCp->capacity = 0; + SCp->needs_sector_size = 1; } - scsi_CDs[i].device->sector_size = sector_size; + SCp->device->sector_size = sector_size; /* * Add this so that we have the ability to correctly gauge * what the device is capable of. */ - scsi_CDs[i].needs_sector_size = 0; - sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); + SCp->needs_sector_size = 0; + sr_sizes[i] = SCp->capacity >> (BLOCK_SIZE_BITS - 9); } - queue = &scsi_CDs[i].device->request_queue; + + queue = &SCp->device->request_queue; blk_queue_hardsect_size(queue, sector_size); +out: kfree(buffer); + return; + +Enomem: + SCp->capacity = 0x1fffff; + sector_size = 2048; /* A guess, just in case */ + SCp->needs_sector_size = 1; + if (SRpnt) + scsi_release_request(SRpnt); + goto out; } void get_capabilities(int i) { + Scsi_CD *SCp; unsigned char cmd[6]; unsigned char *buffer; int rc, n; @@ -589,15 +599,16 @@ "" }; - buffer = (unsigned char *) kmalloc(512, GFP_DMA); + SCp = &scsi_CDs[i]; + buffer = kmalloc(512, GFP_DMA); if (!buffer) { printk(KERN_ERR "sr: out of memory.\n"); return; } cmd[0] = MODE_SENSE; - cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun << 5) & 0xe0) : 0; cmd[2] = 0x2a; cmd[4] = 128; cmd[3] = cmd[5] = 0; @@ -605,8 +616,8 @@ if (rc) { /* failed, drive doesn't have capabilities mode page */ - scsi_CDs[i].cdi.speed = 1; - scsi_CDs[i].cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | + SCp->cdi.speed = 1; + SCp->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | CDC_DVD | CDC_DVD_RAM | CDC_SELECT_DISC | CDC_SELECT_SPEED); kfree(buffer); @@ -614,13 +625,13 @@ return; } n = buffer[3] + 4; - scsi_CDs[i].cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176; - scsi_CDs[i].readcd_known = 1; - scsi_CDs[i].readcd_cdda = buffer[n + 5] & 0x01; + SCp->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176; + SCp->readcd_known = 1; + SCp->readcd_cdda = buffer[n + 5] & 0x01; /* print some capability bits */ printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n", i, ((buffer[n + 14] << 8) + buffer[n + 15]) / 176, - scsi_CDs[i].cdi.speed, + SCp->cdi.speed, buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */ buffer[n + 3] & 0x20 ? "dvd-ram " : "", buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */ @@ -629,38 +640,38 @@ loadmech[buffer[n + 6] >> 5]); if ((buffer[n + 6] >> 5) == 0) /* caddy drives can't close tray... */ - scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY; + SCp->cdi.mask |= CDC_CLOSE_TRAY; if ((buffer[n + 2] & 0x8) == 0) /* not a DVD drive */ - scsi_CDs[i].cdi.mask |= CDC_DVD; + SCp->cdi.mask |= CDC_DVD; if ((buffer[n + 3] & 0x20) == 0) { /* can't write DVD-RAM media */ - scsi_CDs[i].cdi.mask |= CDC_DVD_RAM; + SCp->cdi.mask |= CDC_DVD_RAM; } else { - scsi_CDs[i].device->writeable = 1; + SCp->device->writeable = 1; } if ((buffer[n + 3] & 0x10) == 0) /* can't write DVD-R media */ - scsi_CDs[i].cdi.mask |= CDC_DVD_R; + SCp->cdi.mask |= CDC_DVD_R; if ((buffer[n + 3] & 0x2) == 0) /* can't write CD-RW media */ - scsi_CDs[i].cdi.mask |= CDC_CD_RW; + SCp->cdi.mask |= CDC_CD_RW; if ((buffer[n + 3] & 0x1) == 0) /* can't write CD-R media */ - scsi_CDs[i].cdi.mask |= CDC_CD_R; + SCp->cdi.mask |= CDC_CD_R; if ((buffer[n + 6] & 0x8) == 0) /* can't eject */ - scsi_CDs[i].cdi.mask |= CDC_OPEN_TRAY; + SCp->cdi.mask |= CDC_OPEN_TRAY; if ((buffer[n + 6] >> 5) == mechtype_individual_changer || (buffer[n + 6] >> 5) == mechtype_cartridge_changer) - scsi_CDs[i].cdi.capacity = - cdrom_number_of_slots(&(scsi_CDs[i].cdi)); - if (scsi_CDs[i].cdi.capacity <= 1) + SCp->cdi.capacity = + cdrom_number_of_slots(&SCp->cdi); + if (SCp->cdi.capacity <= 1) /* not a changer */ - scsi_CDs[i].cdi.mask |= CDC_SELECT_DISC; + SCp->cdi.mask |= CDC_SELECT_DISC; /*else I don't think it can close its tray - scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY; */ + SCp->cdi.mask |= CDC_CLOSE_TRAY; */ kfree(buffer); } @@ -671,8 +682,9 @@ */ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) { - Scsi_Device *device = scsi_CDs[minor(cdi->dev)].device; - + Scsi_CD *SCp = cdi->handle; + Scsi_Device *device = SCp->device; + /* set the LUN */ if (device->scsi_level <= SCSI_2) cgc->cmd[1] |= device->lun << 5; @@ -743,47 +755,47 @@ blk_size[MAJOR_NR] = sr_sizes; for (i = 0; i < sr_template.nr_dev; ++i) { + Scsi_CD *SCp = &scsi_CDs[i]; /* If we have already seen this, then skip it. Comes up * with loadable modules. */ - if (scsi_CDs[i].capacity) + if (SCp->capacity) continue; - scsi_CDs[i].capacity = 0x1fffff; - scsi_CDs[i].device->sector_size = 2048; /* A guess, just in case */ - scsi_CDs[i].needs_sector_size = 1; - scsi_CDs[i].device->changed = 1; /* force recheck CD type */ + SCp->capacity = 0x1fffff; + SCp->device->sector_size = 2048;/* A guess, just in case */ + SCp->needs_sector_size = 1; + SCp->device->changed = 1; /* force recheck CD type */ #if 0 /* seems better to leave this for later */ get_sectorsize(i); - printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size); + printk("Scd sectorsize = %d bytes.\n", SCp->sector_size); #endif - scsi_CDs[i].use = 1; + SCp->use = 1; - scsi_CDs[i].device->ten = 1; - scsi_CDs[i].device->remap = 1; - scsi_CDs[i].readcd_known = 0; - scsi_CDs[i].readcd_cdda = 0; - sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); - - scsi_CDs[i].cdi.ops = &sr_dops; - scsi_CDs[i].cdi.handle = &scsi_CDs[i]; - scsi_CDs[i].cdi.dev = mk_kdev(MAJOR_NR, i); - scsi_CDs[i].cdi.mask = 0; - scsi_CDs[i].cdi.capacity = 1; + SCp->device->ten = 1; + SCp->device->remap = 1; + SCp->readcd_known = 0; + SCp->readcd_cdda = 0; + sr_sizes[i] = SCp->capacity >> (BLOCK_SIZE_BITS - 9); + + SCp->cdi.ops = &sr_dops; + SCp->cdi.handle = SCp; + SCp->cdi.dev = mk_kdev(MAJOR_NR, i); + SCp->cdi.mask = 0; + SCp->cdi.capacity = 1; /* * FIXME: someone needs to handle a get_capabilities * failure properly ?? */ get_capabilities(i); - sr_vendor_init(i); + sr_vendor_init(SCp); sprintf(name, "sr%d", i); - strcpy(scsi_CDs[i].cdi.name, name); - scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", + strcpy(SCp->cdi.name, name); + SCp->cdi.de = devfs_register(SCp->device->de, "cd", DEVFS_FL_DEFAULT, MAJOR_NR, i, S_IFBLK | S_IRUGO | S_IWUGO, &sr_bdops, NULL); - register_cdrom(&scsi_CDs[i].cdi); + register_cdrom(&SCp->cdi); } } diff -urN linux-2.5.6-pre3/drivers/scsi/sr.h linux-2.5.6/drivers/scsi/sr.h --- linux-2.5.6-pre3/drivers/scsi/sr.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/drivers/scsi/sr.h Thu Mar 7 18:24:50 2002 @@ -53,7 +53,7 @@ int sr_is_xa(int minor); /* sr_vendor.c */ -void sr_vendor_init(int minor); +void sr_vendor_init(Scsi_CD *); int sr_cd_check(struct cdrom_device_info *); int sr_set_blocklength(int minor, int blocklength); diff -urN linux-2.5.6-pre3/drivers/scsi/sr_ioctl.c linux-2.5.6/drivers/scsi/sr_ioctl.c --- linux-2.5.6-pre3/drivers/scsi/sr_ioctl.c Thu Mar 7 18:24:31 2002 +++ linux-2.5.6/drivers/scsi/sr_ioctl.c Thu Mar 7 18:24:51 2002 @@ -84,7 +84,7 @@ char *bounce_buffer; SDev = scsi_CDs[target].device; - SRpnt = scsi_allocate_request(scsi_CDs[target].device); + SRpnt = scsi_allocate_request(SDev); if (!SRpnt) { printk("Unable to allocate SCSI request in sr_do_ioctl"); return -ENOMEM; @@ -124,7 +124,7 @@ if (driver_byte(result) != 0) { switch (SRpnt->sr_sense_buffer[2] & 0xf) { case UNIT_ATTENTION: - scsi_CDs[target].device->changed = 1; + SDev->changed = 1; if (!quiet) printk(KERN_INFO "sr%d: disc change detected.\n", target); if (retries++ < 10) @@ -192,22 +192,25 @@ static int test_unit_ready(int minor) { + Scsi_CD *SCp; u_char sr_cmd[10]; + SCp = &scsi_CDs[minor]; sr_cmd[0] = GPCMD_TEST_UNIT_READY; - sr_cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; return sr_do_ioctl(minor, sr_cmd, NULL, 0, 1, SCSI_DATA_NONE, NULL); } int sr_tray_move(struct cdrom_device_info *cdi, int pos) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; sr_cmd[0] = GPCMD_START_STOP_UNIT; - sr_cmd[1] = (scsi_CDs[minor(cdi->dev)].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ; @@ -216,9 +219,10 @@ int sr_lock_door(struct cdrom_device_info *cdi, int lock) { - return scsi_ioctl(scsi_CDs[minor(cdi->dev)].device, - lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK, - 0); + Scsi_CD *SCp = cdi->handle; + + return scsi_ioctl(SCp->device, lock ? SCSI_IOCTL_DOORLOCK : + SCSI_IOCTL_DOORUNLOCK, 0); } int sr_drive_status(struct cdrom_device_info *cdi, int slot) @@ -235,6 +239,7 @@ int sr_disk_status(struct cdrom_device_info *cdi) { + Scsi_CD *SCp = cdi->handle; struct cdrom_tochdr toc_h; struct cdrom_tocentry toc_e; int i, rc, have_datatracks = 0; @@ -256,7 +261,7 @@ if (!have_datatracks) return CDS_AUDIO; - if (scsi_CDs[minor(cdi->dev)].xa_flag) + if (SCp->xa_flag) return CDS_XA_2_1; else return CDS_DATA_1; @@ -265,22 +270,24 @@ int sr_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_info) { - ms_info->addr.lba = scsi_CDs[minor(cdi->dev)].ms_offset; - ms_info->xa_flag = scsi_CDs[minor(cdi->dev)].xa_flag || - (scsi_CDs[minor(cdi->dev)].ms_offset > 0); + Scsi_CD *SCp = cdi->handle; + + ms_info->addr.lba = SCp->ms_offset; + ms_info->xa_flag = SCp->xa_flag || SCp->ms_offset > 0; return 0; } int sr_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; char buffer[32]; int result; sr_cmd[0] = GPCMD_READ_SUBCHANNEL; - sr_cmd[1] = (scsi_CDs[minor(cdi->dev)].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = 0x40; /* I do want the subchannel info */ sr_cmd[3] = 0x02; /* Give me medium catalog number info */ sr_cmd[4] = sr_cmd[5] = 0; @@ -305,6 +312,7 @@ int sr_select_speed(struct cdrom_device_info *cdi, int speed) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[MAX_COMMAND_SIZE]; if (speed == 0) @@ -314,8 +322,8 @@ memset(sr_cmd, 0, MAX_COMMAND_SIZE); sr_cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */ - sr_cmd[1] = (scsi_CDs[minor(cdi->dev)].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[minor(cdi->dev)].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */ sr_cmd[3] = speed & 0xff; /* LSB */ @@ -332,6 +340,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg) { + Scsi_CD *SCp = cdi->handle; u_char sr_cmd[10]; int result, target = minor(cdi->dev); unsigned char buffer[32]; @@ -344,8 +353,8 @@ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[target].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; sr_cmd[8] = 12; /* LSB of length */ @@ -362,8 +371,8 @@ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - ((scsi_CDs[target].device->lun) << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + ((SCp->device->lun) << 5) : 0; sr_cmd[1] |= (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; sr_cmd[6] = tocentry->cdte_track; @@ -389,8 +398,8 @@ struct cdrom_ti* ti = (struct cdrom_ti*)arg; sr_cmd[0] = GPCMD_PLAYAUDIO_TI; - sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? - (scsi_CDs[target].device->lun << 5) : 0; + sr_cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; sr_cmd[4] = ti->cdti_trk0; sr_cmd[5] = ti->cdti_ind0; sr_cmd[7] = ti->cdti_trk1; @@ -432,6 +441,7 @@ int sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize) { unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_CD *SCp = &scsi_CDs[minor]; #ifdef DEBUG printk("sr%d: sr_read_cd lba=%d format=%d blksize=%d\n", @@ -440,8 +450,8 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_CD; /* READ_CD */ - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= ((format & 7) << 2); cmd[2] = (unsigned char) (lba >> 24) & 0xff; cmd[3] = (unsigned char) (lba >> 16) & 0xff; @@ -472,19 +482,20 @@ int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest) { unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ + Scsi_CD *SCp = &scsi_CDs[minor]; int rc; /* we try the READ CD command first... */ - if (scsi_CDs[minor].readcd_known) { + if (SCp->readcd_known) { rc = sr_read_cd(minor, dest, lba, 0, blksize); if (-EDRIVE_CANT_DO_THIS != rc) return rc; - scsi_CDs[minor].readcd_known = 0; + SCp->readcd_known = 0; printk("CDROM does'nt support READ CD (0xbe) command\n"); /* fall & retry the other way */ } /* ... if this fails, we switch the blocksize using MODE SELECT */ - if (blksize != scsi_CDs[minor].device->sector_size) { + if (blksize != SCp->device->sector_size) { if (0 != (rc = sr_set_blocklength(minor, blksize))) return rc; } @@ -494,8 +505,8 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_10; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[2] = (unsigned char) (lba >> 24) & 0xff; cmd[3] = (unsigned char) (lba >> 16) & 0xff; cmd[4] = (unsigned char) (lba >> 8) & 0xff; @@ -514,6 +525,7 @@ int sr_is_xa(int minor) { unsigned char *raw_sector; + Scsi_CD *SCp = &scsi_CDs[minor]; int is_xa; if (!xa_test) @@ -522,7 +534,7 @@ raw_sector = (unsigned char *) kmalloc(2048, GFP_DMA | GFP_KERNEL); if (!raw_sector) return -ENOMEM; - if (0 == sr_read_sector(minor, scsi_CDs[minor].ms_offset + 16, + if (0 == sr_read_sector(minor, SCp->ms_offset + 16, CD_FRAMESIZE_RAW1, raw_sector)) { is_xa = (raw_sector[3] == 0x02) ? 1 : 0; } else { @@ -539,18 +551,16 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg) { - int target; - - target = minor(cdi->dev); + Scsi_CD *SCp = cdi->handle; switch (cmd) { case BLKGETSIZE: - return put_user(scsi_CDs[target].capacity, (unsigned long *) arg); + return put_user(SCp->capacity, (unsigned long *) arg); case BLKGETSIZE64: - return put_user((u64)scsi_CDs[target].capacity << 9, (u64 *)arg); + return put_user((u64)SCp->capacity << 9, (u64 *)arg); default: - return scsi_ioctl(scsi_CDs[target].device, cmd, (void *) arg); + return scsi_ioctl(SCp->device, cmd, (void *)arg); } } diff -urN linux-2.5.6-pre3/drivers/scsi/sr_vendor.c linux-2.5.6/drivers/scsi/sr_vendor.c --- linux-2.5.6-pre3/drivers/scsi/sr_vendor.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/scsi/sr_vendor.c Thu Mar 7 18:24:51 2002 @@ -58,27 +58,25 @@ #define VENDOR_TOSHIBA 3 #define VENDOR_WRITER 4 /* pre-scsi3 writers */ -#define VENDOR_ID (scsi_CDs[minor].vendor) - -void sr_vendor_init(int minor) +void sr_vendor_init(Scsi_CD *SCp) { #ifndef CONFIG_BLK_DEV_SR_VENDOR - VENDOR_ID = VENDOR_SCSI3; + SCp->vendor = VENDOR_SCSI3; #else - char *vendor = scsi_CDs[minor].device->vendor; - char *model = scsi_CDs[minor].device->model; - + char *vendor = SCp->device->vendor; + char *model = SCp->device->model; + /* default */ - VENDOR_ID = VENDOR_SCSI3; - if (scsi_CDs[minor].readcd_known) + SCp->vendor = VENDOR_SCSI3; + if (SCp->readcd_known) /* this is true for scsi3/mmc drives - no more checks */ return; - if (scsi_CDs[minor].device->type == TYPE_WORM) { - VENDOR_ID = VENDOR_WRITER; + if (SCp->device->type == TYPE_WORM) { + SCp->vendor = VENDOR_WRITER; } else if (!strncmp(vendor, "NEC", 3)) { - VENDOR_ID = VENDOR_NEC; + SCp->vendor = VENDOR_NEC; if (!strncmp(model, "CD-ROM DRIVE:25", 15) || !strncmp(model, "CD-ROM DRIVE:36", 15) || !strncmp(model, "CD-ROM DRIVE:83", 15) || @@ -90,10 +88,10 @@ #endif ) /* these can't handle multisession, may hang */ - scsi_CDs[minor].cdi.mask |= CDC_MULTI_SESSION; + SCp->cdi.mask |= CDC_MULTI_SESSION; } else if (!strncmp(vendor, "TOSHIBA", 7)) { - VENDOR_ID = VENDOR_TOSHIBA; + SCp->vendor = VENDOR_TOSHIBA; } #endif @@ -108,10 +106,11 @@ unsigned char *buffer; /* the buffer for the ioctl */ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ struct ccs_modesel_head *modesel; + Scsi_CD *SCp = &scsi_CDs[minor]; int rc, density = 0; #ifdef CONFIG_BLK_DEV_SR_VENDOR - if (VENDOR_ID == VENDOR_TOSHIBA) + if (SCp->vendor == VENDOR_TOSHIBA) density = (blocklength > 2048) ? 0x81 : 0x83; #endif @@ -124,8 +123,8 @@ #endif memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= (1 << 4); cmd[4] = 12; modesel = (struct ccs_modesel_head *) buffer; @@ -135,7 +134,7 @@ modesel->block_length_med = (blocklength >> 8) & 0xff; modesel->block_length_lo = blocklength & 0xff; if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0, SCSI_DATA_WRITE, NULL))) { - scsi_CDs[minor].device->sector_size = blocklength; + SCp->device->sector_size = blocklength; } #ifdef DEBUG else @@ -153,13 +152,14 @@ int sr_cd_check(struct cdrom_device_info *cdi) { + Scsi_CD *SCp = cdi->handle; unsigned long sector; unsigned char *buffer; /* the buffer for the ioctl */ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */ int rc, no_multi, minor; minor = minor(cdi->dev); - if (scsi_CDs[minor].cdi.mask & CDC_MULTI_SESSION) + if (SCp->cdi.mask & CDC_MULTI_SESSION) return 0; buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA); @@ -170,13 +170,13 @@ no_multi = 0; /* flag: the drive can't handle multisession */ rc = 0; - switch (VENDOR_ID) { + switch (SCp->vendor) { case VENDOR_SCSI3: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[8] = 12; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 12, 1, SCSI_DATA_READ, NULL); @@ -201,8 +201,8 @@ unsigned long min, sec, frame; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xde; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= 0x03; cmd[2] = 0xb0; rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 1, SCSI_DATA_READ, NULL); @@ -228,8 +228,8 @@ * where starts the last session ?) */ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xc7; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[1] |= 0x03; rc = sr_do_ioctl(minor, cmd, buffer, 4, 1, SCSI_DATA_READ, NULL); if (rc == -EINVAL) { @@ -253,8 +253,8 @@ case VENDOR_WRITER: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[8] = 0x04; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 1, SCSI_DATA_READ, NULL); @@ -267,8 +267,8 @@ break; } cmd[0] = READ_TOC; /* Read TOC */ - cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? - (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] = (SCp->device->scsi_level <= SCSI_2) ? + (SCp->device->lun << 5) : 0; cmd[6] = rc & 0x7f; /* number of last session */ cmd[8] = 0x0c; cmd[9] = 0x40; @@ -285,17 +285,17 @@ /* should not happen */ printk(KERN_WARNING "sr%d: unknown vendor code (%i), not initialized ?\n", - minor, VENDOR_ID); + minor, SCp->vendor); sector = 0; no_multi = 1; break; } - scsi_CDs[minor].ms_offset = sector; - scsi_CDs[minor].xa_flag = 0; + SCp->ms_offset = sector; + SCp->xa_flag = 0; if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(minor)) - scsi_CDs[minor].xa_flag = 1; + SCp->xa_flag = 1; - if (2048 != scsi_CDs[minor].device->sector_size) { + if (2048 != SCp->device->sector_size) { sr_set_blocklength(minor, 2048); } if (no_multi) diff -urN linux-2.5.6-pre3/drivers/usb/Config.in linux-2.5.6/drivers/usb/Config.in --- linux-2.5.6-pre3/drivers/usb/Config.in Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/drivers/usb/Config.in Thu Mar 7 18:24:51 2002 @@ -8,7 +8,7 @@ if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then bool ' USB verbose debug messages' CONFIG_USB_DEBUG -comment 'Miscellaneous USB options' + comment 'Miscellaneous USB options' bool ' USB device filesystem' CONFIG_USB_DEVICEFS if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH @@ -16,90 +16,90 @@ define_bool CONFIG_USB_BANDWIDTH n fi bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT -fi -comment 'USB Host Controller Drivers' -source drivers/usb/hcd/Config.in -if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then - dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB -fi -if [ "$CONFIG_USB_UHCI" != "y" ]; then - dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB -else - define_bool CONFIG_USB_UHCI_ALT n -fi -dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB + comment 'USB Host Controller Drivers' + source drivers/usb/hcd/Config.in + if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + fi + if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB + else + define_bool CONFIG_USB_UHCI_ALT n + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB -comment 'USB Device Class drivers' -dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND -dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL -if [ "$CONFIG_SCSI" = "n" ]; then - comment ' SCSI support is needed for USB Storage' -fi -dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI - dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE - dep_mbool ' Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE - dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE - dep_mbool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE - dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL - dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL -dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB -dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB - -comment 'USB Human Interface Devices (HID)' -if [ "$CONFIG_INPUT" = "n" ]; then - comment ' Input core support is needed for USB HID' -else - dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT - dep_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $CONFIG_USB_HID - if [ "$CONFIG_USB_HID" != "y" ]; then - dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT - dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT + comment 'USB Device Class drivers' + dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL + if [ "$CONFIG_SCSI" = "n" ]; then + comment ' SCSI support is needed for USB Storage' + fi + dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE + dep_mbool ' Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE + dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE + dep_mbool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE + dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + + comment 'USB Human Interface Devices (HID)' + if [ "$CONFIG_INPUT" = "n" ]; then + comment ' Input core support is needed for USB HID' + else + dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT + dep_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $CONFIG_USB_HID + if [ "$CONFIG_USB_HID" != "y" ]; then + dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT + dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT + fi + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT fi - dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT -fi -comment 'USB Imaging devices' -dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB -dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL -dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB -dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI -dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL - -comment 'USB Multimedia devices' -if [ "$CONFIG_VIDEO_DEV" = "n" ]; then - comment ' Video4Linux support is needed for USB Multimedia device support' -else - dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV - dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL - dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL - dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB - dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV -fi + comment 'USB Imaging devices' + dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB + dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB + dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI + dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL + + comment 'USB Multimedia devices' + if [ "$CONFIG_VIDEO_DEV" = "n" ]; then + comment ' Video4Linux support is needed for USB Multimedia device support' + else + dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL + dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL + dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB + dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV + fi -comment 'USB Network adaptors' -if [ "$CONFIG_NET" = "n" ]; then - comment ' Networking support is needed for USB Networking device support' -else - dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL -fi + comment 'USB Network adaptors' + if [ "$CONFIG_NET" = "n" ]; then + comment ' Networking support is needed for USB Networking device support' + else + dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + fi -comment 'USB port drivers' -dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT -source drivers/usb/serial/Config.in - -comment 'USB Miscellaneous drivers' -dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL -dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL + comment 'USB port drivers' + dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT + source drivers/usb/serial/Config.in + + comment 'USB Miscellaneous drivers' + dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL +fi endmenu diff -urN linux-2.5.6-pre3/drivers/usb/hcd/ehci-hcd.c linux-2.5.6/drivers/usb/hcd/ehci-hcd.c --- linux-2.5.6-pre3/drivers/usb/hcd/ehci-hcd.c Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd/ehci-hcd.c Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2001 by David Brownell + * Copyright (c) 2000-2002 by David Brownell * * 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 @@ -31,10 +31,6 @@ #include #include -#ifndef CONFIG_USB_DEBUG - #define CONFIG_USB_DEBUG /* this is still experimental! */ -#endif - #ifdef CONFIG_USB_DEBUG #define DEBUG #else @@ -73,19 +69,25 @@ * ... * * HISTORY: + * + * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift + * more checking to generic hcd framework (db). Make it work with + * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt). * 2002-01-14 Minor cleanup; version synch. * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers. * 2002-01-04 Control/Bulk queuing behaves. + * * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. + * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "$Revision: 0.26 $" +#define DRIVER_VERSION "$Revision: 0.27 $" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" // #define EHCI_VERBOSE_DEBUG -// #define have_iso +// #define have_split_iso #ifdef CONFIG_DEBUG_SLAB # define EHCI_SLAB_FLAGS (SLAB_POISON) @@ -187,6 +189,9 @@ dbg_hcs_params (ehci, "ehci_start"); dbg_hcc_params (ehci, "ehci_start"); + /* cache this readonly data; minimize PCI reads */ + ehci->hcs_params = readl (&ehci->caps->hcs_params); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -204,7 +209,7 @@ ehci->async = 0; ehci->reclaim = 0; - ehci->next_frame = -1; + ehci->next_uframe = -1; /* controller state: unknown --> reset */ @@ -310,7 +315,7 @@ // root hub is shut down separately (first, when possible) scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); ehci_mem_cleanup (ehci); @@ -332,14 +337,12 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: suspend to %d", hcd->bus_name, state); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: This assumes what's probably a D3 level suspend... @@ -375,14 +378,12 @@ static int ehci_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: resume", hcd->bus_name); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: if controller didn't retain state, // return and let generic code clean it up @@ -426,7 +427,7 @@ if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); // FIXME: when nothing is connected to the root hub, @@ -440,20 +441,20 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status = readl (&ehci->regs->status); - int bh = 0; + int bh; - /* clear (just) interrupts */ status &= INTR_MASK; + if (!status) /* irq sharing? */ + return; + + /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ - - if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ - return; + bh = 0; #ifdef EHCI_VERBOSE_DEBUG /* unrequested/ignored: Port Change Detect, Frame List Rollover */ - if (status & INTR_MASK) - dbg_status (ehci, "irq", status); + dbg_status (ehci, "irq", status); #endif /* INT, ERR, and IAA interrupt rates can be throttled */ @@ -520,17 +521,15 @@ return intr_submit (ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: -#ifdef have_iso if (urb->dev->speed == USB_SPEED_HIGH) - return itd_submit (ehci, urb); + return itd_submit (ehci, urb, mem_flags); +#ifdef have_split_iso else - return sitd_submit (ehci, urb); + return sitd_submit (ehci, urb, mem_flags); #else - // FIXME highspeed iso stuff is written but never run/tested. - // and the split iso support isn't even written yet. - dbg ("no iso support yet"); + dbg ("no split iso support yet"); return -ENOSYS; -#endif /* have_iso */ +#endif /* have_split_iso */ } return 0; diff -urN linux-2.5.6-pre3/drivers/usb/hcd/ehci-hub.c linux-2.5.6/drivers/usb/hcd/ehci-hub.c --- linux-2.5.6-pre3/drivers/usb/hcd/ehci-hub.c Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd/ehci-hub.c Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * 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 @@ -68,8 +68,7 @@ /* init status to no-changes */ buf [0] = 0; - temp = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (temp); + ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; @@ -107,8 +106,7 @@ struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); + int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; @@ -124,10 +122,10 @@ memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ - if (HCS_PPC (params)) /* per-port power control */ - temp |= 0x0001; - if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */ - temp |= 0x0080; + if (HCS_PPC (ehci->hcs_params)) + temp |= 0x0001; /* per-port power control */ + if (HCS_INDICATOR (ehci->hcs_params)) + temp |= 0x0080; /* per-port indicators (LEDs) */ desc->wHubCharacteristics = cpu_to_le16 (temp); } @@ -142,8 +140,7 @@ u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); + int ports = HCS_N_PORTS (ehci->hcs_params); u32 temp; unsigned long flags; int retval = 0; @@ -189,7 +186,7 @@ /* ? */ break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp & ~PORT_POWER, &ehci->regs->port_status [wIndex]); break; @@ -300,7 +297,7 @@ &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; @@ -312,6 +309,13 @@ hcd->bus_name, wIndex + 1); temp |= PORT_OWNER; } else { + /* Philips 1562 wants CMD_RUN to reset */ + if (!HCD_IS_RUNNING(ehci->hcd.state)) { + u32 cmd = readl (&ehci->regs->command); + cmd |= CMD_RUN; + writel (cmd, &ehci->regs->command); + ehci->hcd.state = USB_STATE_RUNNING; + } vdbg ("%s port %d reset", hcd->bus_name, wIndex + 1); temp |= PORT_RESET; diff -urN linux-2.5.6-pre3/drivers/usb/hcd/ehci-q.c linux-2.5.6/drivers/usb/hcd/ehci-q.c --- linux-2.5.6-pre3/drivers/usb/hcd/ehci-q.c Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd/ehci-q.c Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * 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 @@ -643,12 +643,19 @@ if (usb_pipecontrol (urb->pipe)) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); } else if (usb_pipebulk (urb->pipe)) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + } else { + u32 temp; + temp = usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe)); + info1 |= (temp & 0x3ff) << 16; /* maxpacket */ + /* HS intr can be "high bandwidth" */ + temp = 1 + ((temp >> 11) & 0x03); + info2 |= temp << 30; /* mult */ + } break; default: #ifdef DEBUG diff -urN linux-2.5.6-pre3/drivers/usb/hcd/ehci-sched.c linux-2.5.6/drivers/usb/hcd/ehci-sched.c --- linux-2.5.6-pre3/drivers/usb/hcd/ehci-sched.c Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd/ehci-sched.c Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * 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 @@ -55,12 +55,12 @@ return &periodic->qh->qh_next; case Q_TYPE_FSTN: return &periodic->fstn->fstn_next; -#ifdef have_iso case Q_TYPE_ITD: return &periodic->itd->itd_next; +#ifdef have_split_iso case Q_TYPE_SITD: return &periodic->sitd->sitd_next; -#endif /* have_iso */ +#endif /* have_split_iso */ } dbg ("BAD shadow %p tag %d", periodic->ptr, tag); // BUG (); @@ -109,9 +109,6 @@ u32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; -#ifdef have_iso - u32 temp = 0; -#endif while (q->ptr) { switch (Q_NEXT_TYPE (*hw_p)) { @@ -130,15 +127,13 @@ } q = &q->fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: - temp = le32_to_cpu (q->itd->transaction [uframe]); - temp >>= 16; - temp &= 0x0fff; - if (temp) - usecs += HS_USECS_ISO (temp); + /* NOTE the "one uframe per itd" policy */ + if (q->itd->hw_transaction [uframe] != 0) + usecs += q->itd->usecs; q = &q->itd->itd_next; break; +#ifdef have_split_iso case Q_TYPE_SITD: temp = q->sitd->hw_fullspeed_ep & __constant_cpu_to_le32 (1 << 31); @@ -163,7 +158,7 @@ } q = &q->sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: BUG (); } @@ -178,6 +173,45 @@ /*-------------------------------------------------------------------------*/ +static void enable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + while (readl (&ehci->regs->status) & STS_PSS) + udelay (20); + + cmd = readl (&ehci->regs->command) | CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... PSS happens later */ + ehci->hcd.state = USB_STATE_RUNNING; + + /* make sure tasklet scans these */ + ehci->next_uframe = readl (&ehci->regs->frame_index) + % (ehci->periodic_size << 3); +} + +static void disable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + while (!(readl (&ehci->regs->status) & STS_PSS)) + udelay (20); + + cmd = readl (&ehci->regs->command) & ~CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... */ + + ehci->next_uframe = -1; +} + +/*-------------------------------------------------------------------------*/ + static void intr_deschedule ( struct ehci_hcd *ehci, unsigned frame, @@ -199,21 +233,9 @@ ehci->periodic_urbs--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) { - u32 cmd = readl (&ehci->regs->command); - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - while (!(readl (&ehci->regs->status) & STS_PSS)) - udelay (20); - - cmd &= ~CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... */ - - ehci->next_frame = -1; - } else + if (!ehci->periodic_urbs) + disable_periodic (ehci); + else vdbg ("periodic schedule still enabled"); spin_unlock_irqrestore (&ehci->lock, flags); @@ -242,7 +264,7 @@ ) { unsigned epnum, period; unsigned temp; - unsigned short mult, usecs; + unsigned short usecs; unsigned long flags; struct ehci_qh *qh; struct hcd_dev *dev; @@ -255,12 +277,7 @@ epnum |= 0x10; } else temp = urb->dev->epmaxpacketout [epnum]; - mult = 1; - if (urb->dev->speed == USB_SPEED_HIGH) { - /* high speed "high bandwidth" is coded in ep maxpacket */ - mult += (temp >> 11) & 0x03; - temp &= 0x03ff; - } else { + if (urb->dev->speed != USB_SPEED_HIGH) { dbg ("no intr/tt scheduling yet"); status = -ENOSYS; goto done; @@ -279,21 +296,12 @@ usecs = HS_USECS (urb->transfer_buffer_length); - /* - * force a power-of-two (frames) sized polling interval - * - * NOTE: endpoint->bInterval for highspeed is measured in uframes, - * while for full/low speeds it's in frames. Here we "know" that - * urb->interval doesn't give acccess to high interrupt rates. - */ - period = ehci->periodic_size; - temp = period; - if (unlikely (urb->interval < 1)) - urb->interval = 1; - while (temp > urb->interval) - temp >>= 1; - period = urb->interval = temp; - + /* FIXME handle HS periods of less than 1 frame. */ + if (urb->interval < 8) + period = 1; + else + period = urb->interval >> 8; + spin_lock_irqsave (&ehci->lock, flags); /* get the qh (must be empty and idle) */ @@ -335,7 +343,6 @@ unsigned frame = urb->interval; qh->hw_next = EHCI_LIST_END; - qh->hw_info2 |= cpu_to_le32 (mult << 30); qh->usecs = usecs; urb->hcpriv = qh_put (qh); @@ -378,7 +385,7 @@ /* stuff into the periodic schedule */ qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting frame %d.%d", + vdbg ("qh %p usecs %d period %d starting %d.%d", qh, qh->usecs, period, frame, uframe); do { if (unlikely (ehci->pshadow [frame].ptr != 0)) { @@ -397,23 +404,8 @@ usb_claim_bandwidth (urb->dev, urb, usecs, 0); /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) { - u32 cmd; - - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - while (readl (&ehci->regs->status) & STS_PSS) - udelay (20); - - cmd = readl (&ehci->regs->command) | CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... PSS happens later */ - ehci->hcd.state = USB_STATE_RUNNING; - - /* make sure tasklet scans these */ - ehci->next_frame = ehci_get_frame (&ehci->hcd); - } + if (!ehci->periodic_urbs++) + enable_periodic (ehci); break; } while (frame); @@ -489,53 +481,56 @@ /*-------------------------------------------------------------------------*/ -#ifdef have_iso - -static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd) +static void +itd_free_list (struct ehci_hcd *ehci, struct urb *urb) { - pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + struct ehci_itd *first_itd = urb->hcpriv; + + pci_unmap_single (ehci->hcd.pdev, + first_itd->buf_dma, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + while (!list_empty (&first_itd->itd_list)) { + struct ehci_itd *itd; + + itd = list_entry ( + first_itd->itd_list.next, + struct ehci_itd, itd_list); + list_del (&itd->itd_list); + pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + } + pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma); + urb->hcpriv = 0; } -/* - * Create itd and allocate into uframes within specified frame. - * Caller must update the resulting uframe links. - */ -static struct ehci_itd * -itd_make ( +static int +itd_fill ( struct ehci_hcd *ehci, + struct ehci_itd *itd, struct urb *urb, unsigned index, // urb->iso_frame_desc [index] - unsigned frame, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags + dma_addr_t dma // mapped transfer buffer ) { - struct ehci_itd *itd; u64 temp; u32 buf1; - unsigned epnum, maxp, multi, usecs; + unsigned i, epnum, maxp, multi; unsigned length; - unsigned i, bufnum; - - /* allocate itd, start to fill it */ - itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma); - if (!itd) - return itd; itd->hw_next = EHCI_LIST_END; itd->urb = urb; itd->index = index; - INIT_LIST_HEAD (&itd->itd_list); - itd->uframe = (frame * 8) % ehci->periodic_size; - /* tell itd about the buffer its transfers will consume */ + /* tell itd about its transfer buffer, max 2 pages */ length = urb->iso_frame_desc [index].length; dma += urb->iso_frame_desc [index].offset; temp = dma & ~0x0fff; - for (i = 0; i < 7; i++) { + for (i = 0; i < 2; i++) { itd->hw_bufp [i] = cpu_to_le32 ((u32) temp); itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); - temp += 0x0fff; + temp += 0x1000; } + itd->buf_dma = dma; /* * this might be a "high bandwidth" highspeed endpoint, @@ -544,282 +539,407 @@ epnum = usb_pipeendpoint (urb->pipe); if (usb_pipein (urb->pipe)) { maxp = urb->dev->epmaxpacketin [epnum]; - buf1 = (1 << 11) | maxp; + buf1 = (1 << 11); } else { maxp = urb->dev->epmaxpacketout [epnum]; - buf1 = maxp; + buf1 = 0; } + buf1 |= (maxp & 0x03ff); multi = 1; - multi += (temp >> 11) & 0x03; + multi += (maxp >> 11) & 0x03; maxp &= 0x03ff; + maxp *= multi; + + /* transfer can't fit in any uframe? */ + if (length < 0 || maxp < length) { + dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)", + length, maxp, urb, index, + urb->iso_frame_desc [index].length); + return -ENOSPC; + } + itd->usecs = HS_USECS_ISO (length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); itd->hw_bufp [1] |= cpu_to_le32 (buf1); itd->hw_bufp [2] |= cpu_to_le32 (multi); - /* schedule as many uframes as needed */ - maxp *= multi; - usecs = HS_USECS_ISO (maxp); - bufnum = 0; - for (i = 0; i < 8; i++) { - unsigned t, offset, scratch; + /* figure hw_transaction[] value (it's scheduled later) */ + itd->transaction = EHCI_ISOC_ACTIVE; + itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */ + if ((index + 1) == urb->number_of_packets) + itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */ + itd->transaction |= length << 16; + cpu_to_le32s (&itd->transaction); - if (length <= 0) { - itd->hw_transaction [i] = 0; - continue; - } + return 0; +} - /* don't commit more than 80% periodic == 100 usec */ - if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100) - continue; +static int +itd_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + int mem_flags +) { + int frame_index; + struct ehci_itd *first_itd, *itd; + int status; + dma_addr_t buf_dma, itd_dma; - /* we'll use this uframe; figure hw_transaction */ - t = EHCI_ISOC_ACTIVE; - t |= bufnum << 12; // which buffer? - offset = temp & 0x0fff; // offset therein - t |= offset; - if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page" - bufnum++; - if (length <= maxp) { - // interrupt only needed at end-of-urb - if ((index + 1) == urb->number_of_packets) - t |= EHCI_ITD_IOC; - scratch = length; - } else - scratch = maxp; - t |= scratch << 16; - t = cpu_to_le32 (t); - - itd->hw_transaction [i] = itd->transaction [i] = t; - length -= scratch; - } - if (length > 0) { - dbg ("iso frame too big, urb %p [%d], %d extra (of %d)", - urb, index, length, urb->iso_frame_desc [index].length); - itd_free (ehci, itd); - itd = 0; + /* set up one dma mapping for this urb */ + buf_dma = pci_map_single (ehci->hcd.pdev, + urb->transfer_buffer, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + if (buf_dma == 0) + return -ENOMEM; + + /* allocate/init ITDs */ + for (frame_index = 0, first_itd = 0; + frame_index < urb->number_of_packets; + frame_index++) { + itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); + if (!itd) { + status = -ENOMEM; + goto fail; + } + memset (itd, 0, sizeof *itd); + itd->itd_dma = itd_dma; + + status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + if (status != 0) + goto fail; + + if (first_itd) + list_add_tail (&itd->itd_list, + &first_itd->itd_list); + else { + INIT_LIST_HEAD (&itd->itd_list); + urb->hcpriv = first_itd = itd; + } } - return itd; + urb->error_count = 0; + return 0; + +fail: + if (urb->hcpriv) + itd_free_list (ehci, urb); + return status; } +/*-------------------------------------------------------------------------*/ + static inline void itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) { - u32 ptr; + /* always prepend ITD/SITD ... only QH tree is order-sensitive */ + itd->itd_next = ehci->pshadow [frame]; + itd->hw_next = ehci->periodic [frame]; + ehci->pshadow [frame].itd = itd; + ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; +} - ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd - if (ehci->pshadow [frame].ptr) { - if (!itd->itd_next.ptr) { - itd->itd_next = ehci->pshadow [frame]; - itd->hw_next = ehci->periodic [frame]; - } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d itd link goof", frame); - BUG (); +/* + * return zero on success, else -errno + * - start holds first uframe to start scheduling into + * - max is the first uframe it's NOT (!) OK to start scheduling into + * math to be done modulo "mod" (ehci->periodic_size << 3) + */ +static int get_iso_range ( + struct ehci_hcd *ehci, + struct urb *urb, + unsigned *start, + unsigned *max, + unsigned mod +) { + struct list_head *lh; + struct hcd_dev *dev = urb->dev->hcpriv; + int last = -1; + unsigned now, span, end; + + span = urb->interval * urb->number_of_packets; + + /* first see if we know when the next transfer SHOULD happen */ + list_for_each (lh, &dev->urb_list) { + struct urb *u; + struct ehci_itd *itd; + unsigned s; + + u = list_entry (lh, struct urb, urb_list); + if (u == urb || u->pipe != urb->pipe) + continue; + if (u->interval != urb->interval) { /* must not change! */ + dbg ("urb %p interval %d ... != %p interval %d", + u, u->interval, urb, urb->interval); + return -EINVAL; + } + + /* URB for this endpoint... covers through when? */ + itd = urb->hcpriv; + s = itd->uframe + u->interval * u->number_of_packets; + if (last < 0) + last = s; + else { + /* + * So far we can only queue two ISO URBs... + * + * FIXME do interval math, figure out whether + * this URB is "before" or not ... also, handle + * the case where the URB might have completed, + * but hasn't yet been processed. + */ + dbg ("NYET: queue >2 URBs per ISO endpoint"); + return -EDOM; } } - ehci->pshadow [frame].itd = itd; - ehci->periodic [frame] = ptr; -} -#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + /* calculate the legal range [start,max) */ + now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ + if (!ehci->periodic_urbs) + now += 8; /* startup delay */ + now %= mod; + end = now + mod; + if (last < 0) { + *start = now + ehci->i_thresh + /* paranoia */ 1; + *max = end - span; + if (*max < *start + 1) + *max = *start + 1; + } else { + *start = last % mod; + *max = (last + 1) % mod; + } -static unsigned long -itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags) + /* explicit start frame? */ + if (!(urb->transfer_flags & USB_ISO_ASAP)) { + unsigned temp; + + /* sanity check: must be in range */ + urb->start_frame %= ehci->periodic_size; + temp = urb->start_frame << 3; + if (temp < *start) + temp += mod; + if (temp > *max) + return -EDOM; + + /* use that explicit start frame */ + *start = urb->start_frame << 3; + temp += 8; + if (temp < *max) + *max = temp; + } + + // FIXME minimize wraparound to "now" ... insist max+span + // (and start+span) remains a few frames short of "end" + + *max %= ehci->periodic_size; + if ((*start + span) < end) + return 0; + return -EFBIG; +} + +static int +itd_schedule (struct ehci_hcd *ehci, struct urb *urb) { - struct urb *urb = itd->urb; + unsigned start, max, i; + int status; + unsigned mod = ehci->periodic_size << 3; - /* if not unlinking: */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - int i; - struct usb_iso_packet_descriptor *desc; - struct ehci_itd *first_itd = urb->hcpriv; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc [i].status = -EINPROGRESS; + urb->iso_frame_desc [i].actual_length = 0; + } - /* update status for this frame's transfers */ - desc = &urb->iso_frame_desc [itd->index]; - desc->status = 0; - desc->actual_length = 0; - for (i = 0; i < 8; i++) { - u32 t = itd->hw_transaction [i]; - if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) { - if (t & EHCI_ISOC_ACTIVE) - desc->status = -EXDEV; - else if (t & EHCI_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) - ? -ENOSR /* couldn't read */ - : -ECOMM; /* couldn't write */ - else if (t & EHCI_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & EHCI_ISOC_XACTERR) */ - desc->status = -EPROTO; + if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0) + return status; + + do { + unsigned uframe; + unsigned usecs; + struct ehci_itd *itd; + + /* check schedule: enough space? */ + itd = urb->hcpriv; + uframe = start; + for (i = 0, uframe = start; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + /* can't commit more than 80% periodic == 100 usec */ + if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) + > (100 - itd->usecs)) { + itd = 0; break; } - desc->actual_length += EHCI_ITD_LENGTH (t); + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); + } + if (!itd) + continue; + + /* that's where we'll schedule this! */ + itd = urb->hcpriv; + urb->start_frame = start >> 3; + vdbg ("ISO urb %p (%d packets period %d) starting %d.%d", + urb, urb->number_of_packets, urb->interval, + urb->start_frame, start & 0x7); + for (i = 0, uframe = start, usecs = 0; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + itd->uframe = uframe; + itd->hw_transaction [uframe & 0x07] = itd->transaction; + itd_link (ehci, (uframe >> 3) % ehci->periodic_size, + itd); + usecs += itd->usecs; + + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); } - /* handle completion now? */ - if ((itd->index + 1) != urb->number_of_packets) - return flags; + /* update bandwidth utilization records (for usbfs) */ + /* FIXME usbcore expects per-frame average, which isn't + * most accurate model... this provides the total claim, + * and expects the average to be computed only display. + */ + usb_claim_bandwidth (urb->dev, urb, usecs, 1); - i = usb_pipein (urb->pipe); - if (i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_urbs++) + enable_periodic (ehci); - /* call completion with no locks; it can unlink ... */ - spin_unlock_irqrestore (&ehci->lock, flags); - urb->complete (urb); - spin_lock_irqsave (&ehci->lock, flags); - - /* re-activate this URB? or unlink? */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - if (!i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); + return 0; - itd = urb->hcpriv; - do { - for (i = 0; i < 8; i++) - itd->hw_transaction [i] - = itd->transaction [i]; - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != urb->hcpriv); - return flags; - } + } while ((start = ++start % mod) != max); + + /* no room in the schedule */ + dbg ("urb %p, CAN'T SCHEDULE", urb); + return -ENOSPC; +} + +/*-------------------------------------------------------------------------*/ + +#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) - /* unlink done only on the last itd */ - } else if ((itd->index + 1) != urb->number_of_packets) +static unsigned long +itd_complete ( + struct ehci_hcd *ehci, + struct ehci_itd *itd, + unsigned uframe, + unsigned long flags +) { + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + + /* update status for this uframe's transfers */ + desc = &urb->iso_frame_desc [itd->index]; + + t = itd->hw_transaction [uframe]; + itd->hw_transaction [uframe] = 0; + if (t & EHCI_ISOC_ACTIVE) + desc->status = -EXDEV; + else if (t & ISO_ERRS) { + urb->error_count++; + if (t & EHCI_ISOC_BUF_ERR) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* couldn't read */ + : -ECOMM; /* couldn't write */ + else if (t & EHCI_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & EHCI_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & EHCI_ISOC_BABBLE)) + desc->actual_length += EHCI_ITD_LENGTH (t); + } else { + desc->status = 0; + desc->actual_length += EHCI_ITD_LENGTH (t); + } + + vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d", + itd, urb, itd->index + 1, urb->number_of_packets, + t, desc->status, desc->actual_length); + + /* handle completion now? */ + if ((itd->index + 1) != urb->number_of_packets) return flags; - /* we're unlinking ... */ + /* + * For now, always give the urb back to the driver ... expect it + * to submit a new urb (or resubmit this), and to have another + * already queued when un-interrupted transfers are needed. + * No, that's not what OHCI or UHCI are now doing. + * + * FIXME Revisit the ISO URB model. It's cleaner not to have all + * the special case magic, but it'd be faster to reuse existing + * ITD/DMA setup and schedule state. Easy to dma_sync/complete(), + * then either reschedule or, if unlinking, free and giveback(). + * But we can't overcommit like the full and low speed HCs do, and + * there's no clean way to report an error when rescheduling... + * + * NOTE that for now we don't accelerate ISO unlinks; they just + * happen according to the current schedule. Means a delay of + * up to about a second (max). + */ + itd_free_list (ehci, urb); + if (urb->status == -EINPROGRESS) + urb->status = 0; - /* decouple urb from the hcd */ spin_unlock_irqrestore (&ehci->lock, flags); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - itd = urb->hcpriv; - urb->hcpriv = 0; - ehci_urb_done (ehci, itd->buf_dma, urb); + usb_hcd_giveback_urb (&ehci->hcd, urb); spin_lock_irqsave (&ehci->lock, flags); - /* take itds out of the hc's periodic schedule */ - list_entry (itd->itd_list.prev, struct ehci_itd, itd_list) - ->itd_list.next = 0; - do { - struct ehci_itd *next; - - if (itd->itd_list.next) - next = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - else - next = 0; + /* defer stopping schedule; completion can submit */ + ehci->periodic_urbs--; + if (!ehci->periodic_urbs) + disable_periodic (ehci); - // FIXME: hc WILL (!) lap us here, if we get behind - // by 128 msec (or less, with smaller periodic_size). - // Reading/caching these itds will cause trouble... - - periodic_unlink (ehci, itd->uframe, itd); - itd_free (ehci, itd); - itd = next; - } while (itd); return flags; } /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { - struct ehci_itd *first_itd = 0, *itd; - unsigned frame_index; - dma_addr_t dma; - unsigned long flags; + int status; + unsigned long flags; - dbg ("itd_submit"); - - /* set up one dma mapping for this urb */ - dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (dma == 0) - return -ENOMEM; + dbg ("itd_submit urb %p", urb); + /* NOTE DMA mapping assumes this ... */ + if (urb->iso_frame_desc [0].offset != 0) + return -EINVAL; + /* - * Schedule as needed. This is VERY optimistic about free - * bandwidth! But the API assumes drivers can pick frames - * intelligently (how?), so there's no other good option. - * - * FIXME this doesn't handle urb->next rings, or try to - * use the iso periodicity. + * NOTE doing this for now, anticipating periodic URB models + * get updated to be "explicit resubmit". */ - if (urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ehci_get_frame (&ehci->hcd); - urb->start_frame++; + if (urb->next) { + dbg ("use explicit resubmit for ISO"); + return -EINVAL; } - urb->start_frame %= ehci->periodic_size; - /* create and populate itds (doing uframe scheduling) */ - spin_lock_irqsave (&ehci->lock, flags); - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - itd = itd_make (ehci, urb, frame_index, - urb->start_frame + frame_index, - dma, SLAB_ATOMIC); - if (itd) { - if (first_itd) - list_add_tail (&itd->itd_list, - &first_itd->itd_list); - else - first_itd = itd; - } else { - spin_unlock_irqrestore (&ehci->lock, flags); - if (first_itd) { - while (!list_empty (&first_itd->itd_list)) { - itd = list_entry ( - first_itd->itd_list.next, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - itd_free (ehci, itd); - } - itd_free (ehci, first_itd); - } - pci_unmap_single (ehci->hcd.pdev, - dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - return -ENOMEM; - } - } - - /* stuff into the schedule */ - itd = first_itd; - do { - unsigned i; - - for (i = 0; i < 8; i++) { - if (!itd->hw_transaction [i]) - continue; - itd_link (ehci, itd->uframe + i, itd); - } - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != first_itd); - urb->hcpriv = first_itd; + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction (ehci, urb, mem_flags); + if (status < 0) + return status; + /* schedule ... need to lock */ + spin_lock_irqsave (&ehci->lock, flags); + status = itd_schedule (ehci, urb); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + if (status < 0) + itd_free_list (ehci, urb); + + return status; } +#ifdef have_split_iso + /*-------------------------------------------------------------------------*/ /* @@ -827,7 +947,7 @@ * the TTs in USB 2.0 hubs. */ -static inline void +static void sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) { pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); @@ -861,7 +981,7 @@ } -static inline void +static void sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) { u32 ptr; @@ -894,12 +1014,11 @@ /*-------------------------------------------------------------------------*/ -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { // struct ehci_sitd *first_sitd = 0; unsigned frame_index; dma_addr_t dma; - int mem_flags; dbg ("NYI -- sitd_submit"); @@ -908,8 +1027,6 @@ // FIXME: setup one big dma mapping dma = 0; - mem_flags = SLAB_ATOMIC; - for (frame_index = 0; frame_index < urb->number_of_packets; frame_index++) { @@ -941,53 +1058,62 @@ return -ENOSYS; } - -#endif /* have_iso */ +#endif /* have_split_iso */ /*-------------------------------------------------------------------------*/ static void scan_periodic (struct ehci_hcd *ehci) { - unsigned frame; - unsigned clock; + unsigned frame, clock, now_uframe, mod; unsigned long flags; + mod = ehci->periodic_size << 3; spin_lock_irqsave (&ehci->lock, flags); /* * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. * Touches as few pages as possible: cache-friendly. - * It's safe to scan entries more than once, though. + * Don't scan ISO entries more than once, though. */ - if (HCD_IS_RUNNING (ehci->hcd.state)) { - frame = ehci->next_frame; - clock = ehci_get_frame (&ehci->hcd); + frame = ehci->next_uframe >> 3; + if (HCD_IS_RUNNING (ehci->hcd.state)) + now_uframe = readl (&ehci->regs->frame_index); + else + now_uframe = (frame << 3) - 1; + now_uframe %= mod; + clock = now_uframe >> 3; - /* when shutting down, scan everything for thoroughness */ - } else { - frame = 0; - clock = ehci->periodic_size - 1; - } for (;;) { - union ehci_shadow q; - u32 type; + union ehci_shadow q, *q_p; + u32 type, *hw_p; + unsigned uframes; restart: - q.ptr = ehci->pshadow [frame].ptr; - type = Q_NEXT_TYPE (ehci->periodic [frame]); + /* scan schedule to _before_ current frame index */ + if (frame == clock) + uframes = now_uframe & 0x07; + else + uframes = 8; + + q_p = &ehci->pshadow [frame]; + hw_p = &ehci->periodic [frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE (*hw_p); /* scan each element in frame's queue for completions */ while (q.ptr != 0) { int last; + unsigned uf; union ehci_shadow temp; switch (type) { case Q_TYPE_QH: last = (q.qh->hw_next == EHCI_LIST_END); + temp = q.qh->qh_next; + type = Q_NEXT_TYPE (q.qh->hw_next); flags = intr_complete (ehci, frame, qh_put (q.qh), flags); - type = Q_NEXT_TYPE (q.qh->hw_next); - temp = q.qh->qh_next; qh_unput (ehci, q.qh); q = temp; break; @@ -1000,22 +1126,49 @@ dbg ("ignoring completions from FSTNs"); } type = Q_NEXT_TYPE (q.fstn->hw_next); - temp = q.fstn->fstn_next; + q = q.fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: last = (q.itd->hw_next == EHCI_LIST_END); - flags = itd_complete (ehci, q.itd, flags); - type = Q_NEXT_TYPE (q.itd->hw_next); - q = q.itd->itd_next; + + /* Unlink each (S)ITD we see, since the ISO + * URB model forces constant rescheduling. + * That complicates sharing uframes in ITDs, + * and means we need to skip uframes the HC + * hasn't yet processed. + */ + for (uf = 0; uf < uframes; uf++) { + if (q.itd->hw_transaction [uf] != 0) { + temp = q; + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE (*hw_p); + + /* might free q.itd ... */ + flags = itd_complete (ehci, + temp.itd, uf, flags); + break; + } + } + /* we might skip this ITD's uframe ... */ + if (uf == uframes) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE (q.itd->hw_next); + } + + q = *q_p; break; +#ifdef have_split_iso case Q_TYPE_SITD: last = (q.sitd->hw_next == EHCI_LIST_END); flags = sitd_complete (ehci, q.sitd, flags); type = Q_NEXT_TYPE (q.sitd->hw_next); + + // FIXME unlink SITD after split completes q = q.sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: dbg ("corrupt type %d frame %d shadow %p", type, frame, q.ptr); @@ -1044,13 +1197,16 @@ if (!HCD_IS_RUNNING (ehci->hcd.state)) break; - ehci->next_frame = clock; - now = ehci_get_frame (&ehci->hcd); - if (clock == now) + ehci->next_uframe = now_uframe; + now = readl (&ehci->regs->frame_index) % mod; + if (now_uframe == now) break; - clock = now; - } else if (++frame >= ehci->periodic_size) - frame = 0; + + /* rescan the rest of this frame, then ... */ + now_uframe = now; + clock = now_uframe >> 3; + } else + frame = (frame + 1) % ehci->periodic_size; } spin_unlock_irqrestore (&ehci->lock, flags); - } +} diff -urN linux-2.5.6-pre3/drivers/usb/hcd/ehci.h linux-2.5.6/drivers/usb/hcd/ehci.h --- linux-2.5.6-pre3/drivers/usb/hcd/ehci.h Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/drivers/usb/hcd/ehci.h Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * 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 @@ -49,7 +49,7 @@ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ - int next_frame; /* scan periodic, start here */ + int next_uframe; /* scan periodic, start here */ unsigned periodic_urbs; /* how many urbs scheduled? */ /* deferred work from IRQ, etc */ @@ -62,6 +62,7 @@ struct usb_hcd hcd; struct ehci_caps *caps; struct ehci_regs *regs; + u32 hcs_params; /* cached register copy */ /* per-HC memory pools (could be per-PCI-bus, but ...) */ struct pci_pool *qh_pool; /* qh per active urb */ @@ -324,13 +325,14 @@ union ehci_shadow itd_next; /* ptr to periodic q entry */ struct urb *urb; - unsigned index; /* in urb->iso_frame_desc */ struct list_head itd_list; /* list of urb frames' itds */ dma_addr_t buf_dma; /* frame's buffer address */ - unsigned uframe; /* in periodic schedule */ - u32 transaction [8]; /* copy of hw_transaction */ - + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ diff -urN linux-2.5.6-pre3/drivers/usb/hcd.c linux-2.5.6/drivers/usb/hcd.c --- linux-2.5.6-pre3/drivers/usb/hcd.c Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd.c Thu Mar 7 18:24:51 2002 @@ -224,12 +224,12 @@ * helper routine for returning string descriptors in UTF-16LE * input can actually be ISO-8859-1; ASCII is its 7-bit subset */ -static int ascii2utf (char *ascii, u8 *utf, int utfmax) +static int ascii2utf (char *s, u8 *utf, int utfmax) { int retval; - for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *ascii++ & 0x7f; + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; *utf++ = 0; } return retval; @@ -248,8 +248,7 @@ */ static int rh_string ( int id, - struct pci_dev *pci_desc, - char *type, + struct usb_hcd *hcd, u8 *data, int len ) { @@ -263,15 +262,16 @@ // serial number } else if (id == 1) { - strcpy (buf, pci_desc->slot_name); + strcpy (buf, hcd->bus_name); // product description } else if (id == 2) { - strcpy (buf, pci_desc->name); + strcpy (buf, hcd->product_desc); // id 3 == vendor description } else if (id == 3) { - sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, type); + sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, + hcd->description); // unsupported IDs --> "protocol stall" } else @@ -338,9 +338,7 @@ break; case USB_DT_STRING << 8: urb->actual_length = rh_string ( - wValue & 0xff, - hcd->pdev, - (char *) hcd->description, + wValue & 0xff, hcd, ubuf, wLength); break; default: @@ -1004,6 +1002,7 @@ hcd->self.hcpriv = (void *) hcd; hcd->bus = &hcd->self; hcd->bus_name = dev->slot_name; + hcd->product_desc = dev->name; INIT_LIST_HEAD (&hcd->dev_list); @@ -1266,7 +1265,7 @@ struct usb_hcd *hcd; struct hcd_dev *dev; unsigned long flags; - int pipe; + int pipe, temp; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; @@ -1286,6 +1285,7 @@ if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state)) return -ESHUTDOWN; pipe = urb->pipe; + temp = usb_pipetype (urb->pipe); if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; @@ -1298,7 +1298,7 @@ /* enforce simple/standard policy */ allowed = USB_ASYNC_UNLINK; // affects later unlinks allowed |= USB_NO_FSBR; // only affects UHCI - switch (usb_pipetype (pipe)) { + switch (temp) { case PIPE_CONTROL: allowed |= USB_DISABLE_SPD; break; @@ -1317,15 +1317,55 @@ /* warn if submitter gave bogus flags */ if (urb->transfer_flags != orig_flags) - warn ("BOGUS urb flags, %x --> %x", + err ("BOGUS urb flags, %x --> %x", orig_flags, urb->transfer_flags); } #endif /* - * FIXME: alloc periodic bandwidth here, for interrupt and iso? - * Need to look at the ring submit mechanism for iso tds ... they - * aren't actually "periodic" in 2.4 kernels. + * Force periodic transfer intervals to be legal values that are + * a power of two (so HCDs don't need to). * + * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC + * supports different values... this uses EHCI/UHCI defaults (and + * EHCI can use smaller non-default values). + */ + switch (temp) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + /* too small? */ + if (urb->interval <= 0) + return -EINVAL; + /* too big? */ + switch (urb->dev->speed) { + case USB_SPEED_HIGH: /* units are microframes */ + // NOTE usb handles 2^15 + if (urb->interval > (1024 * 8)) + urb->interval = 1024 * 8; + temp = 1024 * 8; + break; + case USB_SPEED_FULL: /* units are frames/msec */ + case USB_SPEED_LOW: + if (temp == PIPE_INTERRUPT) { + if (urb->interval > 255) + return -EINVAL; + // NOTE ohci only handles up to 32 + temp = 128; + } else { + if (urb->interval > 1024) + urb->interval = 1024; + // NOTE usb and ohci handle up to 2^15 + temp = 1024; + } + default: + return -EINVAL; + } + /* power of two? */ + while (temp > urb->interval) + temp >>= 1; + urb->interval = temp; + } + + /* * FIXME: make urb timeouts be generic, keeping the HCD cores * as simple as possible. */ @@ -1589,6 +1629,9 @@ struct usb_hcd *hcd = __hcd; int start = hcd->state; + if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ + return; + hcd->driver->irq (hcd); if (hcd->state != start && hcd->state == USB_STATE_HALT) hc_died (hcd); @@ -1642,7 +1685,8 @@ // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev) if (urb->status) - dbg ("giveback urb %p status %d", urb, urb->status); + dbg ("giveback urb %p status %d len %d", + urb, urb->status, urb->actual_length); /* if no error, make sure urb->next progresses */ else if (urb->next) { diff -urN linux-2.5.6-pre3/drivers/usb/hcd.h linux-2.5.6/drivers/usb/hcd.h --- linux-2.5.6-pre3/drivers/usb/hcd.h Thu Mar 7 18:24:32 2002 +++ linux-2.5.6/drivers/usb/hcd.h Thu Mar 7 18:24:51 2002 @@ -39,7 +39,7 @@ struct usb_bus self; /* hcd is-a bus */ const char *bus_name; - + const char *product_desc; /* product/vendor string */ const char *description; /* "ehci-hcd" etc */ struct timer_list rh_timer; /* drives root hub */ diff -urN linux-2.5.6-pre3/drivers/usb/pegasus.c linux-2.5.6/drivers/usb/pegasus.c --- linux-2.5.6-pre3/drivers/usb/pegasus.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/usb/pegasus.c Thu Mar 7 18:24:51 2002 @@ -1,7 +1,7 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (c) 1999-2001 Petko Manolov (pmanolov@lnxw.com) +** Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) ** ** ** ChangeLog: @@ -21,6 +21,8 @@ ** TODO: suppressing HCD warnings spewage on disconnect. ** v0.4.13 Ethernet address is now set at probe(), not at open() ** time as this seems to break dhcpd. +** v0.5.0 branch to 2.5.x kernels +** v0.5.1 ethtool support added */ /* @@ -46,20 +48,25 @@ #include #include #include +#include +#include #include #include #include +#include #include "pegasus.h" /* * Version Information */ -#define DRIVER_VERSION "v0.4.23 (2002/02/01)" -#define DRIVER_AUTHOR "Petko Manolov " +#define DRIVER_VERSION "v0.5.1 (2002/03/06)" +#define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" #define PEGASUS_USE_INTR #define PEGASUS_WRITE_EEPROM +#define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ + BMSR_100FULL | BMSR_ANEGCAPABLE) static int loopback = 0; static int mii_mode = 0; @@ -461,8 +468,8 @@ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK ) { __u16 auxmode; - read_mii_word( pegasus, 0, 0x1b, &auxmode ); - write_mii_word( pegasus, 0, 0x1b, auxmode | 4 ); + read_mii_word( pegasus, 0, MII_TPISTATUS, &auxmode ); + write_mii_word( pegasus, 0, MII_TPISTATUS, auxmode | 4 ); } return 0; @@ -481,16 +488,16 @@ if ( !(bmsr & 0x20) && !loopback ) warn( "%s: link NOT established (0x%x) - check the cable.", dev->name, bmsr ); - if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) + if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) ) return 2; if ( !(linkpart & 1) ) warn( "link partner stat %x", linkpart ); data[0] = 0xc9; data[1] = 0; - if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) ) + if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL) ) data[1] |= 0x20; /* set full duplex */ - if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) ) + if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF) ) data[1] |= 0x10; /* set 100 Mbps */ if ( mii_mode ) data[1] = 0; @@ -710,15 +717,26 @@ } +static void set_carrier(struct net_device *net) +{ + pegasus_t *pegasus; + short tmp; + + pegasus = net->priv; + read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp); + if (tmp & BMSR_LSTATUS) + netif_carrier_on(net); + else + netif_carrier_off(net); + +} + + static int pegasus_open(struct net_device *net) { pegasus_t *pegasus = (pegasus_t *)net->priv; int res; - if ( (res = enable_net_traffic(net, pegasus->usb)) ) { - err("can't enable_net_traffic() - %d", res); - return -EIO; - } FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, @@ -735,6 +753,11 @@ #endif netif_start_queue( net ); pegasus->flags |= PEGASUS_RUNNING; + if ( (res = enable_net_traffic(net, pegasus->usb)) ) { + err("can't enable_net_traffic() - %d", res); + return -EIO; + } + set_carrier(net); return 0; } @@ -760,24 +783,103 @@ } +static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr) +{ + pegasus_t *pegasus; + int cmd; + char tmp[128]; + + pegasus = net->priv; + if (get_user(cmd, (int *)uaddr)) + return -EFAULT; + switch (cmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum, + pegasus->usb->devnum); + strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd; + short lpa, bmcr; + + if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) + return -EFAULT; + ecmd.supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_MII); + ecmd.port = PORT_TP; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = pegasus->phy; + read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); + read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = lpa & (LPA_100HALF|LPA_100FULL) ? + SPEED_100 : SPEED_10; + if (ecmd.speed == SPEED_100) + ecmd.duplex = lpa & LPA_100FULL ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd.duplex = lpa & LPA_10FULL ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = bmcr & BMCR_SPEED100 ? + SPEED_100 : SPEED_10; + ecmd.duplex = bmcr & BMCR_FULLDPLX ? + DUPLEX_FULL : DUPLEX_HALF; + } + if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SSET: { + return -EOPNOTSUPP; + } + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = netif_carrier_ok(net); + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + default: + return -EOPNOTSUPP; + } +} + + static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) { __u16 *data = (__u16 *)&rq->ifr_data; pegasus_t *pegasus = net->priv; switch(cmd) { - case SIOCDEVPRIVATE: - data[0] = pegasus->phy; - case SIOCDEVPRIVATE+1: - read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); - return 0; - case SIOCDEVPRIVATE+2: - if ( !capable(CAP_NET_ADMIN) ) - return -EPERM; - write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); - return 0; - default: - return -EOPNOTSUPP; + case SIOCETHTOOL: + return pegasus_ethtool_ioctl(net, rq->ifr_data); + case SIOCDEVPRIVATE: + data[0] = pegasus->phy; + case SIOCDEVPRIVATE+1: + read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if ( !capable(CAP_NET_ADMIN) ) + return -EPERM; + write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; } } diff -urN linux-2.5.6-pre3/drivers/usb/pegasus.h linux-2.5.6/drivers/usb/pegasus.h --- linux-2.5.6-pre3/drivers/usb/pegasus.h Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/usb/pegasus.h Thu Mar 7 18:24:51 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999,2000 Petko Manolov - Petkan (pmanolov@lnxw.com) + * Copyright (c) 1999-2002 Petko Manolov - Petkan (petkan@users.sourceforge.net) * * 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 @@ -31,14 +31,6 @@ #define EPROM_WR_ENABLE 0x10 #define EPROM_LOAD 0x20 -#define MII_BMCR 0x00 -#define MII_BMSR 0x01 -#define BMSR_MEDIA 0x7808 -#define MII_ANLPA 0x05 -#define ANLPA_100TX_FD 0x0100 -#define ANLPA_100TX_HD 0x0080 -#define ANLPA_10T_FD 0x0040 -#define ANLPA_10T_HD 0x0020 #define PHY_DONE 0x80 #define PHY_READ 0x40 #define PHY_WRITE 0x20 @@ -184,6 +176,8 @@ PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval. board)", VENDOR_ADMTEK, 0x0986, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "AN986A USB MAC", VENDOR_ADMTEK, 1986, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, @@ -254,6 +248,8 @@ DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201, + DEFAULT_GPIO_RESET | PEGASUS_II) PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, diff -urN linux-2.5.6-pre3/drivers/usb/printer.c linux-2.5.6/drivers/usb/printer.c --- linux-2.5.6-pre3/drivers/usb/printer.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/drivers/usb/printer.c Thu Mar 7 18:24:51 2002 @@ -91,7 +91,7 @@ struct usb_device *dev; /* USB device */ devfs_handle_t devfs; /* devfs device */ struct semaphore sem; /* locks this struct, especially "dev" */ - struct urb readurb, writeurb; /* The urbs */ + struct urb *readurb, *writeurb; /* The urbs */ wait_queue_head_t wait; /* Zzzzz ... */ int readcount; /* Counter for reads */ int ifnum; /* Interface number */ @@ -253,15 +253,15 @@ usblp->used = 1; file->private_data = usblp; - usblp->writeurb.transfer_buffer_length = 0; - usblp->writeurb.status = 0; + usblp->writeurb->transfer_buffer_length = 0; + usblp->writeurb->status = 0; usblp->wcomplete = 1; /* we begin writeable */ usblp->rcomplete = 0; if (usblp->bidir) { usblp->readcount = 0; - usblp->readurb.dev = usblp->dev; - if (usb_submit_urb(&usblp->readurb, GFP_KERNEL) < 0) { + usblp->readurb->dev = usblp->dev; + if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) { retval = -EIO; usblp->used = 0; file->private_data = NULL; @@ -278,8 +278,10 @@ usblp_table [usblp->minor] = NULL; info ("usblp%d: removed", usblp->minor); - kfree (usblp->writeurb.transfer_buffer); + kfree (usblp->writeurb->transfer_buffer); kfree (usblp->device_id_string); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree (usblp); } @@ -292,8 +294,8 @@ usblp->used = 0; if (usblp->dev) { if (usblp->bidir) - usb_unlink_urb(&usblp->readurb); - usb_unlink_urb(&usblp->writeurb); + usb_unlink_urb(usblp->readurb); + usb_unlink_urb(usblp->writeurb); up(&usblp->sem); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); @@ -306,8 +308,8 @@ { struct usblp *usblp = file->private_data; poll_wait(file, &usblp->wait, wait); - return ((!usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) - | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); + return ((!usblp->bidir || usblp->readurb->status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) + | (usblp->writeurb->status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); } static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -423,12 +425,12 @@ return -ENODEV; } - if (usblp->writeurb.status != 0) { + if (usblp->writeurb->status != 0) { if (usblp->quirks & USBLP_QUIRK_BIDIR) { if (!usblp->wcomplete) err("usblp%d: error %d writing to printer", - usblp->minor, usblp->writeurb.status); - err = usblp->writeurb.status; + usblp->minor, usblp->writeurb->status); + err = usblp->writeurb->status; } else err = usblp_check_status(usblp, err); up (&usblp->sem); @@ -440,23 +442,23 @@ continue; } - writecount += usblp->writeurb.transfer_buffer_length; - usblp->writeurb.transfer_buffer_length = 0; + writecount += usblp->writeurb->transfer_buffer_length; + usblp->writeurb->transfer_buffer_length = 0; if (writecount == count) { up (&usblp->sem); break; } - usblp->writeurb.transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ? - (count - writecount) : USBLP_BUF_SIZE; + usblp->writeurb->transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ? + (count - writecount) : USBLP_BUF_SIZE; - if (copy_from_user(usblp->writeurb.transfer_buffer, buffer + writecount, - usblp->writeurb.transfer_buffer_length)) return -EFAULT; + if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount, + usblp->writeurb->transfer_buffer_length)) return -EFAULT; - usblp->writeurb.dev = usblp->dev; + usblp->writeurb->dev = usblp->dev; usblp->wcomplete = 0; - if (usb_submit_urb(&usblp->writeurb, GFP_KERNEL)) { + if (usb_submit_urb(usblp->writeurb, GFP_KERNEL)) { count = -EIO; up (&usblp->sem); break; @@ -516,29 +518,29 @@ goto done; } - if (usblp->readurb.status) { + if (usblp->readurb->status) { err("usblp%d: error %d reading from printer", - usblp->minor, usblp->readurb.status); - usblp->readurb.dev = usblp->dev; + usblp->minor, usblp->readurb->status); + usblp->readurb->dev = usblp->dev; usblp->readcount = 0; - usb_submit_urb(&usblp->readurb, GFP_KERNEL); + usb_submit_urb(usblp->readurb, GFP_KERNEL); count = -EIO; goto done; } - count = count < usblp->readurb.actual_length - usblp->readcount ? - count : usblp->readurb.actual_length - usblp->readcount; + count = count < usblp->readurb->actual_length - usblp->readcount ? + count : usblp->readurb->actual_length - usblp->readcount; - if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount, count)) { + if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) { count = -EFAULT; goto done; } - if ((usblp->readcount += count) == usblp->readurb.actual_length) { + if ((usblp->readcount += count) == usblp->readurb->actual_length) { usblp->readcount = 0; - usblp->readurb.dev = usblp->dev; + usblp->readurb->dev = usblp->dev; usblp->rcomplete = 0; - if (usb_submit_urb(&usblp->readurb, GFP_KERNEL)) { + if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) { count = -EIO; goto done; } @@ -668,24 +670,42 @@ init_waitqueue_head(&usblp->wait); + usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL); + if (!usblp->writeurb) { + err("out of memory"); + kfree(usblp); + return NULL; + } + usblp->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!usblp->readurb) { + err("out of memory"); + usb_free_urb(usblp->writeurb); + kfree(usblp); + return NULL; + } + if (!(buf = kmalloc(USBLP_BUF_SIZE * (bidir ? 2 : 1), GFP_KERNEL))) { err("out of memory"); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree(usblp); return NULL; } if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) { err("out of memory"); + usb_free_urb(usblp->writeurb); + usb_free_urb(usblp->readurb); kfree(usblp); kfree(buf); return NULL; } - FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + FILL_BULK_URB(usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf, 0, usblp_bulk_write, usblp); if (bidir) - FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + FILL_BULK_URB(usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk_read, usblp); /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */ @@ -737,9 +757,9 @@ lock_kernel(); usblp->dev = NULL; - usb_unlink_urb(&usblp->writeurb); + usb_unlink_urb(usblp->writeurb); if (usblp->bidir) - usb_unlink_urb(&usblp->readurb); + usb_unlink_urb(usblp->readurb); if (!usblp->used) usblp_cleanup (usblp); diff -urN linux-2.5.6-pre3/drivers/usb/serial/ir-usb.c linux-2.5.6/drivers/usb/serial/ir-usb.c --- linux-2.5.6-pre3/drivers/usb/serial/ir-usb.c Thu Mar 7 18:24:33 2002 +++ linux-2.5.6/drivers/usb/serial/ir-usb.c Thu Mar 7 18:24:52 2002 @@ -1,8 +1,8 @@ /* * USB IR Dongle driver * - * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com) + * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com) * * 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 @@ -21,6 +21,11 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * 2002_Mar_07 greg kh + * moved some needed structures and #define values from the + * net/irda/irda-usb.h file into our file, as we don't want to depend on + * that codebase compiling correctly :) + * * 2002_Jan_14 gb * Added module parameter to force specific number of XBOFs. * Added ir_xbof_change(). @@ -56,7 +61,6 @@ #include #include #include -#include #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; @@ -73,6 +77,33 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB IR Dongle driver" +/* USB IrDA class spec information */ +#define USB_CLASS_IRDA 0x02 +#define USB_DT_IRDA 0x21 +#define IU_REQ_GET_CLASS_DESC 0x06 +#define SPEED_2400 0x01 +#define SPEED_9600 0x02 +#define SPEED_19200 0x03 +#define SPEED_38400 0x04 +#define SPEED_57600 0x05 +#define SPEED_115200 0x06 +#define SPEED_576000 0x07 +#define SPEED_1152000 0x08 +#define SPEED_4000000 0x09 + +struct irda_class_desc { + u8 bLength; + u8 bDescriptorType; + u16 bcdSpecRevision; + u8 bmDataSize; + u8 bmWindowSize; + u8 bmMinTurnaroundTime; + u16 wBaudRate; + u8 bmAdditionalBOFs; + u8 bIrdaRateSniff; + u8 bMaxUnicastList; +} __attribute__ ((packed)); + /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size = 0; @@ -158,7 +189,7 @@ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), IU_REQ_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, ifnum, desc, sizeof(*desc), MSECS_TO_JIFFIES(500)); + 0, ifnum, desc, sizeof(*desc), HZ); dbg("%s - ret=%d", __FUNCTION__, ret); if (ret < sizeof(*desc)) { diff -urN linux-2.5.6-pre3/drivers/video/matrox/matroxfb_DAC1064.c linux-2.5.6/drivers/video/matrox/matroxfb_DAC1064.c --- linux-2.5.6-pre3/drivers/video/matrox/matroxfb_DAC1064.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/drivers/video/matrox/matroxfb_DAC1064.c Thu Mar 7 18:24:53 2002 @@ -473,9 +473,12 @@ static int m1064_compute(void* outdev, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)outdev) +#ifdef CONFIG_FB_MATROX_G450 if (ACCESS_FBINFO(devflags.g450dac)) { matroxfb_g450_setclk(PMINFO m->pixclock, M_PIXEL_PLL_C); - } else { + } else +#endif + { int i; int tmout; CRITFLAGS diff -urN linux-2.5.6-pre3/drivers/video/vesafb.c linux-2.5.6/drivers/video/vesafb.c --- linux-2.5.6-pre3/drivers/video/vesafb.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/drivers/video/vesafb.c Thu Mar 7 18:24:53 2002 @@ -550,7 +550,7 @@ ypan = pmi_setpal = 0; /* not available or some DOS TSR ... */ if (ypan || pmi_setpal) { - pmi_base = (unsigned short*)bus_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off); + pmi_base = (unsigned short*)phys_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off); pmi_start = (void*)((char*)pmi_base + pmi_base[1]); pmi_pal = (void*)((char*)pmi_base + pmi_base[2]); printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal); diff -urN linux-2.5.6-pre3/fs/cramfs/README linux-2.5.6/fs/cramfs/README --- linux-2.5.6-pre3/fs/cramfs/README Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/fs/cramfs/README Thu Mar 7 18:24:53 2002 @@ -6,8 +6,8 @@ swapped around (though it does care that directory entries (inodes) in a given directory are contiguous, as this is used by readdir). -All data is in host-endian format; neither mkcramfs nor the kernel -ever do swabbing. (See section `Block Size' below.) +All data is currently in host-endian format; neither mkcramfs nor the +kernel ever do swabbing. (See section `Block Size' below.) : @@ -29,6 +29,10 @@ lines); put another way, the same order as `find -type d -exec ls -AU1 {} \;'. +Beginning in 2.4.7, directory entries are sorted. This optimization +allows cramfs_lookup to return more quickly when a filename does not +exist, speeds up user-space directory sorts, etc. + : One for each file that's either a symlink or a regular file of non-zero st_size. @@ -63,17 +67,15 @@ This kernel supports cramfs holes (i.e. [efficient representation of] blocks in uncompressed data consisting entirely of NUL bytes), but by default mkcramfs doesn't test for & create holes, since cramfs in -kernels up to at least 2.3.39 didn't support holes. Compile mkcramfs -with -DDO_HOLES if you want it to create files that can have holes in -them. +kernels up to at least 2.3.39 didn't support holes. Run mkcramfs +with -z if you want it to create files that can have holes in them. Tools ----- -If you're hacking on cramfs, you might find useful some tools for -testing cramfs at , including a -rudimentary fsck for cramfs. +The cramfs user-space tools, including mkcramfs and cramfsck, are +located at . Future Development @@ -103,8 +105,8 @@ PAGE_CACHE_SIZE (4096)' to `#include '. The disadvantage is that the generated cramfs cannot always be shared between different kernels, not even necessarily kernels of the same architecture if -PAGE_CACHE_SIZE is subject to change between kernel versions. - +PAGE_CACHE_SIZE is subject to change between kernel versions +(currently possible with arm and ia64). The remaining options try to make cramfs more sharable. diff -urN linux-2.5.6-pre3/fs/cramfs/inode.c linux-2.5.6/fs/cramfs/inode.c --- linux-2.5.6-pre3/fs/cramfs/inode.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/fs/cramfs/inode.c Thu Mar 7 18:24:53 2002 @@ -117,7 +117,7 @@ unsigned i, blocknr, buffer, unread; unsigned long devsize; unsigned int major, minor; - + char *data; if (!len) @@ -187,7 +187,7 @@ } return read_buffers[buffer] + offset; } - + static int cramfs_fill_super(struct super_block *sb, void *data, int silent) { @@ -266,7 +266,7 @@ buf->f_bavail = 0; buf->f_files = sb->CRAMFS_SB_FILES; buf->f_ffree = 0; - buf->f_namelen = 255; + buf->f_namelen = CRAMFS_MAXPATHLEN; return 0; } @@ -476,4 +476,3 @@ module_init(init_cramfs_fs) module_exit(exit_cramfs_fs) MODULE_LICENSE("GPL"); - diff -urN linux-2.5.6-pre3/fs/exec.c linux-2.5.6/fs/exec.c --- linux-2.5.6-pre3/fs/exec.c Thu Mar 7 18:24:34 2002 +++ linux-2.5.6/fs/exec.c Thu Mar 7 18:24:53 2002 @@ -509,23 +509,49 @@ /* * An execve() will automatically "de-thread" the process. - * Note: we don't have to hold the tasklist_lock to test - * whether we migth need to do this. If we're not part of - * a thread group, there is no way we can become one - * dynamically. And if we are, we only need to protect the - * unlink - even if we race with the last other thread exit, - * at worst the list_del_init() might end up being a no-op. + * - if a master thread (PID==TGID) is doing this, then all subsidiary threads + * will be killed (otherwise there will end up being two independent thread + * groups with the same TGID). + * - if a subsidary thread is doing this, then it just leaves the thread group */ -static inline void de_thread(struct task_struct *tsk) +static void de_thread(struct task_struct *tsk) { - if (!list_empty(&tsk->thread_group)) { - write_lock_irq(&tasklist_lock); + struct task_struct *sub; + struct list_head *head, *ptr; + struct siginfo info; + int pause; + + write_lock_irq(&tasklist_lock); + + if (tsk->tgid != tsk->pid) { + /* subsidiary thread - just escapes the group */ + list_del_init(&tsk->thread_group); + tsk->tgid = tsk->pid; + pause = 0; + } + else { + /* master thread - kill all subsidiary threads */ + info.si_signo = SIGKILL; + info.si_errno = 0; + info.si_code = SI_DETHREAD; + info.si_pid = current->pid; + info.si_uid = current->uid; + + head = tsk->thread_group.next; list_del_init(&tsk->thread_group); - write_unlock_irq(&tasklist_lock); + + list_for_each(ptr,head) { + sub = list_entry(ptr,struct task_struct,thread_group); + send_sig_info(SIGKILL,&info,sub); + } + + pause = 1; } - /* Minor oddity: this might stay the same. */ - tsk->tgid = tsk->pid; + write_unlock_irq(&tasklist_lock); + + /* give the subsidiary threads a chance to clean themselves up */ + if (pause) yield(); } int flush_old_exec(struct linux_binprm * bprm) @@ -566,7 +592,8 @@ flush_thread(); - de_thread(current); + if (!list_empty(¤t->thread_group)) + de_thread(current); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || permission(bprm->file->f_dentry->d_inode,MAY_READ)) diff -urN linux-2.5.6-pre3/fs/ext2/ext2.h linux-2.5.6/fs/ext2/ext2.h --- linux-2.5.6-pre3/fs/ext2/ext2.h Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/fs/ext2/ext2.h Thu Mar 7 18:24:54 2002 @@ -63,7 +63,6 @@ /* fsync.c */ extern int ext2_sync_file (struct file *, struct dentry *, int); -extern int ext2_fsync_inode (struct inode *, int); /* ialloc.c */ extern struct inode * ext2_new_inode (struct inode *, int); diff -urN linux-2.5.6-pre3/fs/ext2/fsync.c linux-2.5.6/fs/ext2/fsync.c --- linux-2.5.6-pre3/fs/ext2/fsync.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/fs/ext2/fsync.c Thu Mar 7 18:24:54 2002 @@ -35,11 +35,6 @@ int ext2_sync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; - return ext2_fsync_inode(inode, datasync); -} - -int ext2_fsync_inode(struct inode *inode, int datasync) -{ int err; err = fsync_inode_buffers(inode); diff -urN linux-2.5.6-pre3/fs/inode.c linux-2.5.6/fs/inode.c --- linux-2.5.6-pre3/fs/inode.c Thu Mar 7 18:24:35 2002 +++ linux-2.5.6/fs/inode.c Thu Mar 7 18:24:54 2002 @@ -291,15 +291,15 @@ static inline void sync_one(struct inode *inode, int sync) { - if (inode->i_state & I_LOCK) { + while (inode->i_state & I_LOCK) { __iget(inode); spin_unlock(&inode_lock); __wait_on_inode(inode); iput(inode); spin_lock(&inode_lock); - } else { - __sync_one(inode, sync); } + + __sync_one(inode, sync); } static inline void sync_list(struct list_head *head) diff -urN linux-2.5.6-pre3/fs/jfs/jfs_dtree.c linux-2.5.6/fs/jfs/jfs_dtree.c --- linux-2.5.6-pre3/fs/jfs/jfs_dtree.c Thu Mar 7 18:24:35 2002 +++ linux-2.5.6/fs/jfs/jfs_dtree.c Thu Mar 7 18:24:54 2002 @@ -798,7 +798,7 @@ * insert entry for new key */ if (DO_INDEX(ip)) { - if (JFS_IP(ip)->next_index == -1) { + if (JFS_IP(ip)->next_index == DIREND) { DT_PUTPAGE(mp); return EMLINK; } @@ -1927,22 +1927,6 @@ release_metapage(mp); } /* - * Update directory index table for entries now in right page - */ - if ((rp->header.flag & BT_LEAF) && DO_INDEX(ip)) { - metapage_t *mp = 0; - ldtentry_t *ldtentry; - - stbl = DT_GETSTBL(rp); - for (n = 0; n < rp->header.nextindex; n++) { - ldtentry = (ldtentry_t *) & rp->slot[stbl[n]]; - modify_index(tid, ip, le32_to_cpu(ldtentry->index), - rbn, n, &mp); - } - if (mp) - release_metapage(mp); - } - /* * insert the new entry into the new right/child page * (skip index in the new right page will not change) */ @@ -2885,7 +2869,7 @@ int do_index = 0; uint loop_count = 0; - if (filp->f_pos == -1) + if (filp->f_pos == DIREND) return 0; if (DO_INDEX(ip)) { @@ -2904,25 +2888,25 @@ dir_table_slot_t dirtab_slot; if (dtEmpty(ip)) { - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } repeat: rc = get_index(ip, dir_index, &dirtab_slot); if (rc) { - filp->f_pos = -1; + filp->f_pos = DIREND; return rc; } if (dirtab_slot.flag == DIR_INDEX_FREE) { if (loop_count++ > JFS_IP(ip)->next_index) { jERROR(1, ("jfs_readdir detected " "infinite loop!\n")); - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } dir_index = le32_to_cpu(dirtab_slot.addr2); if (dir_index == -1) { - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } goto repeat; @@ -2931,13 +2915,7 @@ index = dirtab_slot.slot; DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); if (rc) { - filp->f_pos = -1; - return 0; - } - if (p->header.flag & BT_INTERNAL) { - jERROR(1,("jfs_readdir: bad index table\n")); - DT_PUTPAGE(mp); - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } if (p->header.flag & BT_INTERNAL) { @@ -2968,7 +2946,7 @@ * Find first entry of left-most leaf */ if (dtEmpty(ip)) { - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } @@ -3013,7 +2991,7 @@ } if (dtEmpty(ip)) { - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } @@ -3021,7 +2999,7 @@ jERROR(1, ("jfs_readdir: unexpected rc = %d from dtReadNext\n", rc)); - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } /* get start leaf page and index */ @@ -3029,7 +3007,7 @@ /* offset beyond directory eof ? */ if (bn < 0) { - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } } @@ -3038,7 +3016,7 @@ if (d_name == NULL) { DT_PUTPAGE(mp); jERROR(1, ("jfs_readdir: kmalloc failed!\n")); - filp->f_pos = -1; + filp->f_pos = DIREND; return 0; } while (1) { @@ -3087,13 +3065,13 @@ */ if (p->header.flag & BT_ROOT) { - filp->f_pos = -1; + filp->f_pos = DIREND; break; } bn = le64_to_cpu(p->header.next); if (bn == 0) { - filp->f_pos = -1; + filp->f_pos = DIREND; break; } diff -urN linux-2.5.6-pre3/fs/jfs/jfs_dtree.h linux-2.5.6/fs/jfs/jfs_dtree.h --- linux-2.5.6-pre3/fs/jfs/jfs_dtree.h Thu Mar 7 18:24:35 2002 +++ linux-2.5.6/fs/jfs/jfs_dtree.h Thu Mar 7 18:24:54 2002 @@ -252,6 +252,10 @@ #define DIRENTSIZ(namlen) \ ( (sizeof(struct dirent) - 2*(JFS_NAME_MAX+1) + 2*((namlen)+1) + 3) &~ 3 ) +/* + * Maximum file offset for directories. + */ +#define DIREND INT_MAX /* * external declarations diff -urN linux-2.5.6-pre3/fs/nfsd/nfsctl.c linux-2.5.6/fs/nfsd/nfsctl.c --- linux-2.5.6-pre3/fs/nfsd/nfsctl.c Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/fs/nfsd/nfsctl.c Thu Mar 7 18:24:55 2002 @@ -32,6 +32,7 @@ #include #include #include +#include static int nfsctl_svc(struct nfsctl_svc *data); static int nfsctl_addclient(struct nfsctl_client *data); diff -urN linux-2.5.6-pre3/fs/smbfs/ChangeLog linux-2.5.6/fs/smbfs/ChangeLog --- linux-2.5.6-pre3/fs/smbfs/ChangeLog Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/fs/smbfs/ChangeLog Thu Mar 7 18:24:55 2002 @@ -1,5 +1,18 @@ ChangeLog for smbfs. +2001-08-03 Urban Widmark + + * *.c: Unicode support + +2001-08-23 Jochen Dolze + + * proc.c: Correct rsize/wsize computation for readX/writeX + +2001-0?-?? Urban Widmark + + * *.c: Add LFS + * *.c: Move to a "driver" style handling of different servertypes. + (Not all operations are done this way. yet.) 2001-12-31 René Scharfe * inode.c: added smb_show_options to show mount options in /proc/mounts diff -urN linux-2.5.6-pre3/fs/smbfs/cache.c linux-2.5.6/fs/smbfs/cache.c --- linux-2.5.6-pre3/fs/smbfs/cache.c Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/fs/smbfs/cache.c Thu Mar 7 18:24:55 2002 @@ -84,7 +84,7 @@ struct list_head *next; if (d_validate(dent, parent)) { - if (dent->d_name.len <= SMB_MAXPATHLEN && + if (dent->d_name.len <= SMB_MAXNAMELEN && (unsigned long)dent->d_fsdata == fpos) { if (!dent->d_inode) { dput(dent); diff -urN linux-2.5.6-pre3/fs/smbfs/dir.c linux-2.5.6/fs/smbfs/dir.c --- linux-2.5.6-pre3/fs/smbfs/dir.c Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/fs/smbfs/dir.c Thu Mar 7 18:24:55 2002 @@ -187,7 +187,7 @@ ctl.filled = 0; ctl.valid = 1; read_really: - result = smb_proc_readdir(filp, dirent, filldir, &ctl); + result = server->ops->readdir(filp, dirent, filldir, &ctl); if (ctl.idx == -1) goto invalid_cache; /* retry */ ctl.head.end = ctl.fpos - 1; diff -urN linux-2.5.6-pre3/fs/smbfs/file.c linux-2.5.6/fs/smbfs/file.c --- linux-2.5.6-pre3/fs/smbfs/file.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/fs/smbfs/file.c Thu Mar 7 18:24:55 2002 @@ -55,12 +55,13 @@ smb_readpage_sync(struct dentry *dentry, struct page *page) { char *buffer = kmap(page); - unsigned long offset = page->index << PAGE_CACHE_SHIFT; - int rsize = smb_get_rsize(server_from_dentry(dentry)); + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int rsize = smb_get_rsize(server); int count = PAGE_SIZE; int result; - VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n", + VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", DENTRY_PATH(dentry), count, offset, rsize); result = smb_open(dentry, SMB_O_RDONLY); @@ -74,7 +75,7 @@ if (count < rsize) rsize = count; - result = smb_proc_read(dentry->d_inode, offset, rsize, buffer); + result = server->ops->read(dentry->d_inode,offset,rsize,buffer); if (result < 0) goto io_error; @@ -118,21 +119,23 @@ */ static int smb_writepage_sync(struct inode *inode, struct page *page, - unsigned long offset, unsigned int count) + unsigned long pageoffset, unsigned int count) { - char *buffer = kmap(page) + offset; - int wsize = smb_get_wsize(server_from_inode(inode)); + loff_t offset; + char *buffer = kmap(page) + pageoffset; + struct smb_sb_info *server = server_from_inode(inode); + unsigned int wsize = smb_get_wsize(server); int result, written = 0; - offset += page->index << PAGE_CACHE_SHIFT; - VERBOSE("file ino=%ld, fileid=%d, count=%d@%ld, wsize=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); + offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; + VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", + inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize); do { if (count < wsize) wsize = count; - result = smb_proc_write(inode, offset, wsize, buffer); + result = server->ops->write(inode, offset, wsize, buffer); if (result < 0) { PARANOIA("failed write, wsize=%d, result=%d\n", wsize, result); diff -urN linux-2.5.6-pre3/fs/smbfs/inode.c linux-2.5.6/fs/smbfs/inode.c --- linux-2.5.6-pre3/fs/smbfs/inode.c Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/fs/smbfs/inode.c Thu Mar 7 18:24:55 2002 @@ -445,8 +445,7 @@ if (server->conn_pid) kill_proc(server->conn_pid, SIGTERM, 1); - smb_kfree(server->mnt); - smb_kfree(server->temp_buf); + smb_kfree(server->ops); if (server->packet) smb_vfree(server->packet); @@ -468,6 +467,7 @@ struct inode *root_inode; struct smb_fattr root; int ver; + void *mem; if (!raw_data) goto out_no_data; @@ -494,22 +494,29 @@ if (!server->packet) goto out_no_mem; - /* Allocate the global temp buffer */ - server->temp_buf = smb_kmalloc(2*SMB_MAXPATHLEN+20, GFP_KERNEL); - if (!server->temp_buf) + /* Allocate the global temp buffer and some superblock helper structs */ + VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + 2*SMB_MAXPATHLEN + 20); + mem = smb_kmalloc(sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + 2*SMB_MAXPATHLEN + 20, GFP_KERNEL); + if (!mem) goto out_no_temp; + server->ops = mem; + server->mnt = mem + sizeof(struct smb_ops); + server->name_buf = mem + sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel); + server->temp_buf = mem + sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel) + + SMB_MAXPATHLEN + 1; + /* Setup NLS stuff */ server->remote_nls = NULL; server->local_nls = NULL; - server->name_buf = server->temp_buf + SMB_MAXPATHLEN + 20; - /* Allocate the mount data structure */ - /* FIXME: merge this with the other malloc and get a whole page? */ - mnt = smb_kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL); - if (!mnt) - goto out_no_mount; - server->mnt = mnt; + mnt = server->mnt; memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, @@ -565,9 +572,7 @@ out_no_root: iput(root_inode); out_bad_option: - smb_kfree(server->mnt); -out_no_mount: - smb_kfree(server->temp_buf); + smb_kfree(mem); out_no_temp: smb_vfree(server->packet); out_no_mem: @@ -630,8 +635,7 @@ error = smb_open(dentry, O_WRONLY); if (error) goto out; - error = smb_proc_trunc(server, SMB_I(inode)->fileid, - attr->ia_size); + error = server->ops->truncate(inode, attr->ia_size); if (error) goto out; error = vmtruncate(inode, attr->ia_size); diff -urN linux-2.5.6-pre3/fs/smbfs/proc.c linux-2.5.6/fs/smbfs/proc.c --- linux-2.5.6-pre3/fs/smbfs/proc.c Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/fs/smbfs/proc.c Thu Mar 7 18:24:55 2002 @@ -48,16 +48,29 @@ #define SMB_ST_BLKSIZE (PAGE_SIZE) #define SMB_ST_BLKSHIFT (PAGE_SHIFT) +static struct smb_ops smb_ops_core; +static struct smb_ops smb_ops_os2; +static struct smb_ops smb_ops_win95; +static struct smb_ops smb_ops_winNT; + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static void +smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); static int -smb_proc_setattr_ext(struct smb_sb_info *, struct inode *, - struct smb_fattr *); +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr); static int smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr); + u16 attr); static int -smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); - +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr); +static void +install_ops(struct smb_ops *dst, struct smb_ops *src); static void @@ -98,8 +111,8 @@ } /* no conversion, just a wrapper for memcpy. */ -static int convert_memcpy(char *output, int olen, - const char *input, int ilen, +static int convert_memcpy(unsigned char *output, int olen, + const unsigned char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to) { @@ -109,9 +122,25 @@ return ilen; } +static inline int write_char(unsigned char ch, char *output, int olen) +{ + if (olen < 4) + return -ENAMETOOLONG; + sprintf(output, ":x%02x", ch); + return 4; +} + +static inline int write_unichar(wchar_t ch, char *output, int olen) +{ + if (olen < 5) + return -ENAMETOOLONG; + sprintf(output, ":%04x", ch); + return 5; +} + /* convert from one "codepage" to another (possibly being utf8). */ -static int convert_cp(char *output, int olen, - const char *input, int ilen, +static int convert_cp(unsigned char *output, int olen, + const unsigned char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to) { @@ -119,20 +148,26 @@ int n; wchar_t ch; - if (!nls_from || !nls_to) { - PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to); - return convert_memcpy(output, olen, input, ilen, NULL, NULL); - } - while (ilen > 0) { /* convert by changing to unicode and back to the new cp */ - n = nls_from->char2uni((unsigned char *)input, ilen, &ch); - if (n < 0) + n = nls_from->char2uni(input, ilen, &ch); + if (n == -EINVAL) { + ilen--; + n = write_char(*input++, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + len += n; + continue; + } else if (n < 0) goto fail; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); + if (n == -EINVAL) + n = write_unichar(ch, output, olen); if (n < 0) goto fail; output += n; @@ -145,6 +180,42 @@ return n; } +/* ----------------------------------------------------------- */ + +/* + * nls_unicode + * + * This encodes/decodes little endian unicode format + */ + +static int uni2char(wchar_t uni, unsigned char *out, int boundlen) +{ + if (boundlen < 2) + return -EINVAL; + *out++ = uni & 0xff; + *out++ = uni >> 8; + return 2; +} + +static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) +{ + if (boundlen < 2) + return -EINVAL; + *uni = (rawstring[1] << 8) | rawstring[0]; + return 2; +} + +static struct nls_table unicode_table = { + "unicode", + uni2char, + char2uni, + NULL, /* not used by smbfs */ + NULL, + NULL, /* not a module */ +}; + +/* ----------------------------------------------------------- */ + static int setcodepage(struct nls_table **p, char *name) { struct nls_table *nls; @@ -157,7 +228,7 @@ } /* if already set, unload the previous one. */ - if (*p) + if (*p && *p != &unicode_table) unload_nls(*p); *p = nls; @@ -175,18 +246,25 @@ if (!*cp->remote_name) goto out; + /* local */ n = setcodepage(&server->local_nls, cp->local_name); if (n != 0) goto out; - n = setcodepage(&server->remote_nls, cp->remote_name); - if (n != 0) - setcodepage(&server->local_nls, NULL); + + /* remote */ + if (!strcmp(cp->remote_name, "unicode")) { + server->remote_nls = &unicode_table; + } else { + n = setcodepage(&server->remote_nls, cp->remote_name); + if (n != 0) + setcodepage(&server->local_nls, NULL); + } out: if (server->local_nls != NULL && server->remote_nls != NULL) - server->convert = convert_cp; + server->ops->convert = convert_cp; else - server->convert = convert_memcpy; + server->ops->convert = convert_memcpy; smb_unlock_server(server); return n; @@ -217,17 +295,19 @@ * smb_build_path: build the path to entry and name storing it in buf. * The path returned will have the trailing '\0'. */ -static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, - struct dentry * entry, struct qstr * name) +static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, + int maxlen, + struct dentry *entry, struct qstr *name) { - char *path = buf; + unsigned char *path = buf; int len; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; - if (maxlen < 2) + if (maxlen < (2< SMB_MAXNAMELEN + 1) - maxlen = SMB_MAXNAMELEN + 1; + if (maxlen > SMB_MAXPATHLEN + 1) + maxlen = SMB_MAXPATHLEN + 1; if (entry == NULL) goto test_name_and_out; @@ -237,8 +317,10 @@ */ if (IS_ROOT(entry) && !name) { *path++ = '\\'; + if (unicode) *path++ = '\0'; *path++ = '\0'; - return 2; + if (unicode) *path++ = '\0'; + return path-buf; } /* @@ -247,12 +329,12 @@ */ read_lock(&dparent_lock); while (!IS_ROOT(entry)) { - if (maxlen < 3) { + if (maxlen < (3<convert(path, maxlen-2, + len = server->ops->convert(path, maxlen-2, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); if (len < 0) { @@ -261,23 +343,30 @@ } reverse_string(path, len); path += len; + if (unicode) { + /* Note: reverse order */ + *path++ = '\0'; + maxlen--; + } *path++ = '\\'; maxlen -= len+1; entry = entry->d_parent; - if (IS_ROOT(entry)) - break; } read_unlock(&dparent_lock); reverse_string(buf, path-buf); - /* maxlen is at least 1 */ + /* maxlen has space for at least one char */ test_name_and_out: if (name) { - if (maxlen < 3) + if (maxlen < (3<convert(path, maxlen-2, + if (unicode) { + *path++ = '\0'; + maxlen--; + } + len = server->ops->convert(path, maxlen-2, name->name, name->len, server->local_nls, server->remote_nls); if (len < 0) @@ -285,8 +374,9 @@ path += len; maxlen -= len+1; } - /* maxlen is at least 1 */ + /* maxlen has space for at least one char */ *path++ = '\0'; + if (unicode) *path++ = '\0'; return path-buf; } @@ -304,16 +394,31 @@ return result; } +/* encode_path for non-trans2 request SMBs */ static int smb_simple_encode_path(struct smb_sb_info *server, char **p, struct dentry * entry, struct qstr * name) { char *s = *p; int res; int maxlen = ((char *)server->packet + server->packet_size) - s; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); if (!maxlen) return -ENAMETOOLONG; - *s++ = 4; + *s++ = 4; /* ASCII data format */ + + /* + * SMB Unicode strings must be 16bit aligned relative the start of the + * packet. If they are not they must be padded with 0. + */ + if (unicode) { + int align = s - (char *)server->packet; + if (align & 1) { + *s++ = '\0'; + maxlen--; + } + } + res = smb_encode_path(server, s, maxlen-1, entry, name); if (res < 0) return res; @@ -505,7 +610,8 @@ int smb_get_rsize(struct smb_sb_info *server) { - int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + /* readX has 12 parameters, read has 5 */ + int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", @@ -520,7 +626,8 @@ int smb_get_wsize(struct smb_sb_info *server) { - int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + /* writeX has 14 parameters, write has 5 */ + int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", @@ -836,13 +943,57 @@ /* now that we have an established connection we can detect the server type and enable bug workarounds */ - if (server->opt.protocol == SMB_PROTOCOL_NT1 && - (server->opt.max_xmit < 0x1000) && - !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_core); + else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_os2); + else if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + /* FIXME: can we kill the WIN95 flag now? */ server->mnt->flags |= SMB_MOUNT_WIN95; - VERBOSE("smb_newconn: detected WIN95 server\n"); + VERBOSE("detected WIN95 server\n"); + install_ops(server->ops, &smb_ops_win95); + } else { + /* + * Samba has max_xmit 65535 + * NT4spX has max_xmit 4536 (or something like that) + * win2k has ... + */ + VERBOSE("detected NT1 (Samba, NT4/5) server\n"); + install_ops(server->ops, &smb_ops_winNT); } + /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ + if (server->mnt->flags & SMB_MOUNT_OLDATTR) { + server->ops->getattr = smb_proc_getattr_core; + } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { + server->ops->getattr = smb_proc_getattr_ff; + } + + /* Decode server capabilities */ + if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { + /* Should be ok to set this now, as no one can access the + mount until the connection has been established. */ + SB_of(server)->s_maxbytes = ~0ULL >> 1; + VERBOSE("LFS enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_UNICODE) { + server->mnt->flags |= SMB_MOUNT_UNICODE; + VERBOSE("Unicode enabled\n"); + } else { + server->mnt->flags &= ~SMB_MOUNT_UNICODE; + } +#if 0 + /* flags we may test for other patches ... */ + if (server->opt.capabilities & SMB_CAP_LARGE_READX) { + VERBOSE("Large reads enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { + VERBOSE("Large writes enabled\n"); + } +#endif + VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", server->opt.protocol, server->opt.max_xmit, server->conn_pid, server->opt.capabilities); @@ -919,10 +1070,15 @@ WSET(buf, smb_uid, server->opt.server_uid); WSET(buf, smb_mid, 1); - if (server->opt.protocol > SMB_PROTOCOL_CORE) - { - *(buf+smb_flg) = 0x8; - WSET(buf, smb_flg2, 0x3); + if (server->opt.protocol > SMB_PROTOCOL_CORE) { + int flags = SMB_FLAGS_CASELESS_PATHNAMES; + int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | + SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ + + *(buf+smb_flg) = flags; + if (server->mnt->flags & SMB_MOUNT_UNICODE) + flags2 |= SMB_FLAGS2_UNICODE_STRINGS; + WSET(buf, smb_flg2, flags2); } *p++ = wct; /* wct */ p += 2 * wct; @@ -1181,8 +1337,8 @@ /* In smb_proc_read and smb_proc_write we do not retry, because the file-id would not be valid after a reconnection. */ -int -smb_proc_read(struct inode *inode, off_t offset, int count, char *data) +static int +smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) { struct smb_sb_info *server = server_from_inode(inode); __u16 returned_count, data_len; @@ -1230,15 +1386,15 @@ return result; } -int -smb_proc_write(struct inode *inode, off_t offset, int count, const char *data) +static int +smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) { struct smb_sb_info *server = server_from_inode(inode); int result; __u8 *p; __u16 fileid = SMB_I(inode)->fileid; - VERBOSE("ino=%ld, fileid=%d, count=%d@%ld, packet_size=%d\n", + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n", inode->i_ino, SMB_I(inode)->fileid, count, offset, server->packet_size); @@ -1261,6 +1417,94 @@ return result; } +/* + * In smb_proc_readX and smb_proc_writeX we do not retry, because the + * file-id would not be valid after a reconnection. + */ +static int +smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + u16 data_len, data_off; + unsigned char *buf; + int result; + + smb_lock_server(server); + smb_setup_header(server, SMBreadX, 12, 0); + buf = server->packet; + WSET(buf, smb_vwv0, 0x00ff); + WSET(buf, smb_vwv1, 0); + WSET(buf, smb_vwv2, SMB_I(inode)->fileid); + DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ + WSET(buf, smb_vwv5, count); + WSET(buf, smb_vwv6, 0); + DSET(buf, smb_vwv7, 0); + WSET(buf, smb_vwv9, 0); + DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ + WSET(buf, smb_vwv11, 0); + + result = smb_request_ok(server, SMBreadX, 12, -1); + if (result < 0) + goto out; + data_len = WVAL(server->packet, smb_vwv5); + data_off = WVAL(server->packet, smb_vwv6); + buf = smb_base(server->packet) + data_off; + + /* we can NOT simply trust the info given by the server ... */ + if (data_len > server->packet_size - (buf - server->packet)) { + printk(KERN_ERR "smb_proc_read: invalid data length!! " + "%d > %d - (%p - %p)\n", + data_len, server->packet_size, buf, server->packet); + result = -EIO; + goto out; + } + + memcpy(data, buf, data_len); + result = data_len; + +out: + smb_unlock_server(server); + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u8 *p; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset, + server->packet_size); + + smb_lock_server(server); + p = smb_setup_header(server, SMBwriteX, 14, count + 2); + WSET(server->packet, smb_vwv0, 0x00ff); + WSET(server->packet, smb_vwv1, 0); + WSET(server->packet, smb_vwv2, SMB_I(inode)->fileid); + DSET(server->packet, smb_vwv3, (u32)offset); /* low 32 bits */ + DSET(server->packet, smb_vwv5, 0); + WSET(server->packet, smb_vwv7, 0); /* write mode */ + WSET(server->packet, smb_vwv8, 0); + WSET(server->packet, smb_vwv9, 0); + WSET(server->packet, smb_vwv10, count); /* data length */ + WSET(server->packet, smb_vwv11, p + 2 - smb_base(server->packet)); + DSET(server->packet, smb_vwv12, (u32)(offset >> 32)); + *p++ = 0; /* FIXME: pad to short or long ... change +2 above also */ + *p++ = 0; + memcpy(p, data, count); + + result = smb_request_ok(server, SMBwriteX, 6, 0); + if (result >= 0) + result = WVAL(server->packet, smb_vwv2); + + smb_unlock_server(server); + return result; +} + int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) { @@ -1380,7 +1624,9 @@ struct smb_fattr fattr; /* first get current attribute */ - result = smb_proc_do_getattr(server, dentry, &fattr); + smb_init_dirent(server, &fattr); + result = server->ops->getattr(server, dentry, &fattr); + smb_finish_dirent(server, &fattr); if (result < 0) return result; @@ -1455,38 +1701,72 @@ return smb_request_ok(server, SMBflush, 0, 0); } -int -smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length) +static int +smb_proc_trunc32(struct inode *inode, loff_t length) { - char *p; + /* + * Writing 0bytes is old-SMB magic for truncating files. + * MAX_NON_LFS should prevent this from being called with a too + * large offset. + */ + return smb_proc_write(inode, length, 0, NULL); +} + +static int +smb_proc_trunc64(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int command; int result; + char *param = server->temp_buf; + char data[8]; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; smb_lock_server(server); - + command = TRANSACT2_SETFILEINFO; + + /* FIXME: must we also set allocation size? winNT seems to do that */ retry: - p = smb_setup_header(server, SMBwrite, 5, 3); - WSET(server->packet, smb_vwv0, fid); - WSET(server->packet, smb_vwv1, 0); - DSET(server->packet, smb_vwv2, length); - WSET(server->packet, smb_vwv4, 0); - *p++ = 1; - WSET(p, 0, 0); - - if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) { + WSET(param, 0, SMB_I(inode)->fileid); + WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); + WSET(param, 4, 0); + LSET(data, 0, length); + result = smb_trans2_request(server, command, + 8, data, 6, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (result < 0) { if (smb_retry(server)) goto retry; goto out; } + result = 0; + if (server->rcls != 0) + result = smb_errno(server); + +out: + smb_unlock_server(server); + return result; +} +static int +smb_proc_trunc95(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result = smb_proc_trunc32(inode, length); + /* * win9x doesn't appear to update the size immediately. * It will return the old file size after the truncate, - * confusing smbfs. - * NT and Samba return the new value immediately. + * confusing smbfs. So we force an update. + * + * FIXME: is this still necessary? */ - if (server->mnt->flags & SMB_MOUNT_WIN95) - smb_proc_flush(server, fid); -out: + smb_lock_server(server); + smb_proc_flush(server, SMB_I(inode)->fileid); smb_unlock_server(server); return result; } @@ -1567,7 +1847,6 @@ */ while (len > 2 && qname->name[len-1] == ' ') len--; - qname->len = len; smb_finish_dirent(server, fattr); @@ -1586,12 +1865,16 @@ } #endif - qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - qname->name = server->name_buf; + qname->len = 0; + len = server->ops->convert(server->name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (len > 0) { + qname->len = len; + qname->name = server->name_buf; + DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); + } - DEBUG1("len=%d, name=%.*s\n", qname->len, qname->len, qname->name); return p + 22; } @@ -1707,6 +1990,8 @@ for (i = 0; i < count; i++) { p = smb_decode_short_dirent(server, p, &qname, &fattr); + if (qname.len == 0) + continue; if (entries_seen == 2 && qname.name[0] == '.') { if (qname.len == 1) @@ -1744,7 +2029,9 @@ { char *result; unsigned int len = 0; + int n; __u16 date, time; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); /* * SMB doesn't have a concept of inode numbers ... @@ -1780,16 +2067,16 @@ result = p + WVAL(p, 0); len = DVAL(p, 60); if (len > 255) len = 255; - /* NT4 null terminates */ + /* NT4 null terminates, unless we are using unicode ... */ qname->name = p + 94; - if (len && qname->name[len-1] == '\0') + if (!unicode && len && qname->name[len-1] == '\0') len--; fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); /* change time (32) */ - fattr->f_size = DVAL(p, 40); + fattr->f_size = LVAL(p, 40); /* alloc size (48) */ fattr->attr = DVAL(p, 56); @@ -1819,10 +2106,14 @@ } #endif - qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - qname->name = server->name_buf; + qname->len = 0; + n = server->ops->convert(server->name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (n > 0) { + qname->len = n; + qname->name = server->name_buf; + } out: return result; @@ -1888,7 +2179,7 @@ */ mask = param + 12; - mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dir, &star); + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); if (mask_len < 0) { result = mask_len; goto unlock_return; @@ -2063,18 +2354,6 @@ return result; } -int -smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct smb_sb_info *server = server_from_dentry(filp->f_dentry); - - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - return smb_proc_readdir_long(filp, dirent, filldir, ctl); - else - return smb_proc_readdir_short(filp, dirent, filldir, ctl); -} - /* * This version uses the trans2 TRANSACT2_FINDFIRST message * to get the attribute data. @@ -2095,7 +2374,7 @@ int mask_len, result; retry: - mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dentry, NULL); + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); if (mask_len < 0) { result = mask_len; goto out; @@ -2207,29 +2486,25 @@ */ static int smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) + int *lrdata, unsigned char **rdata, + int *lrparam, unsigned char **rparam, + int infolevel) { char *p, *param = server->temp_buf; - __u16 date, time; - int off_date = 0, off_time = 2; - unsigned char *resp_data = NULL; - unsigned char *resp_param = NULL; - int resp_data_len = 0; - int resp_param_len = 0; int result; - retry: - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ +retry: + WSET(param, 0, infolevel); DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; result = smb_trans2_request(server, TRANSACT2_QPATHINFO, 0, NULL, p - param, param, - &resp_data_len, &resp_data, - &resp_param_len, &resp_param); + lrdata, rdata, + lrparam, rparam); if (result < 0) { if (smb_retry(server)) @@ -2244,13 +2519,36 @@ goto out; } result = -ENOENT; - if (resp_data_len < 22) + if (*lrdata < 22) { PARANOIA("not enough data for %s, len=%d\n", - ¶m[6], resp_data_len); + ¶m[6], *lrdata); goto out; } + result = 0; +out: + return result; +} + +static int +smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + u16 date, time; + int off_date = 0, off_time = 2; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + int result = smb_proc_getattr_trans2(server, dir, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param, + SMB_INFO_STANDARD); + if (result < 0) + goto out; + /* * Kludge alert: Win 95 swaps the date and time field, * contrary to the CIFS docs and Win NT practice. @@ -2276,37 +2574,51 @@ #endif attr->f_size = DVAL(resp_data, 12); attr->attr = WVAL(resp_data, 20); - result = 0; out: return result; } -/* - * Note: called with the server locked - */ static int -smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) +smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) { - int result; - struct inode *inode = dir->d_inode; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + int result = smb_proc_getattr_trans2(server, dir, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param, + SMB_QUERY_FILE_ALL_INFO); + if (result < 0) + goto out; + + attr->f_ctime = smb_ntutc2unixutc(LVAL(resp_data, 0)); + attr->f_atime = smb_ntutc2unixutc(LVAL(resp_data, 8)); + attr->f_mtime = smb_ntutc2unixutc(LVAL(resp_data, 16)); + /* change (24) */ + attr->attr = WVAL(resp_data, 32); + /* pad? (34) */ + /* allocated size (40) */ + attr->f_size = LVAL(resp_data, 48); - smb_init_dirent(server, fattr); +out: + return result; +} - /* - * Select whether to use core or trans2 getattr. - * Win 95 appears to break with the trans2 getattr. - */ - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2 || - (server->mnt->flags & (SMB_MOUNT_OLDATTR|SMB_MOUNT_WIN95)) ) { - result = smb_proc_getattr_core(server, dir, fattr); - } else { - if (server->mnt->flags & SMB_MOUNT_DIRATTR) - result = smb_proc_getattr_ff(server, dir, fattr); - else - result = smb_proc_getattr_trans2(server, dir, fattr); - } +static int +smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct inode *inode = dir->d_inode; + int result; + + /* FIXME: why not use the "all" version? */ + result = smb_proc_getattr_trans2_std(server, dir, attr); + if (result < 0) + goto out; /* * None of the getattr versions here can make win9x return the right @@ -2314,16 +2626,14 @@ * A seek-to-end does return the right size, but we only need to do * that on files we have written. */ - if (server->mnt->flags & SMB_MOUNT_WIN95 && - inode && - SMB_I(inode)->flags & SMB_F_LOCALWRITE && + if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && smb_is_open(inode)) { __u16 fileid = SMB_I(inode)->fileid; - fattr->f_size = smb_proc_seek(server, fileid, 2, 0); + attr->f_size = smb_proc_seek(server, fileid, 2, 0); } - smb_finish_dirent(server, fattr); +out: return result; } @@ -2334,7 +2644,11 @@ int result; smb_lock_server(server); - result = smb_proc_do_getattr(server, dir, fattr); + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + smb_unlock_server(server); return result; } @@ -2471,7 +2785,7 @@ retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; @@ -2596,3 +2910,50 @@ smb_unlock_server(server); return result; } + + +static void +install_ops(struct smb_ops *dst, struct smb_ops *src) +{ + memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); +} + +/* < LANMAN2 */ +static struct smb_ops smb_ops_core = +{ + read: smb_proc_read, + write: smb_proc_write, + readdir: smb_proc_readdir_short, + getattr: smb_proc_getattr_core, + truncate: smb_proc_trunc32, +}; + +/* LANMAN2, OS/2, others? */ +static struct smb_ops smb_ops_os2 = +{ + read: smb_proc_read, + write: smb_proc_write, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_trans2_std, + truncate: smb_proc_trunc32, +}; + +/* Win95, and possibly some NetApp versions too */ +static struct smb_ops smb_ops_win95 = +{ + read: smb_proc_read, /* does not support 12word readX */ + write: smb_proc_write, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_95, + truncate: smb_proc_trunc95, +}; + +/* Samba, NT4 and NT5 */ +static struct smb_ops smb_ops_winNT = +{ + read: smb_proc_readX, + write: smb_proc_writeX, + readdir: smb_proc_readdir_long, + getattr: smb_proc_getattr_trans2_all, + truncate: smb_proc_trunc64, +}; diff -urN linux-2.5.6-pre3/fs/smbfs/proto.h linux-2.5.6/fs/smbfs/proto.h --- linux-2.5.6-pre3/fs/smbfs/proto.h Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/fs/smbfs/proto.h Thu Mar 7 18:24:55 2002 @@ -1,5 +1,5 @@ /* - * Autogenerated with cproto on: Tue Oct 2 20:40:54 CEST 2001 + * Autogenerated with cproto on: Thu Nov 22 21:18:04 CET 2001 */ /* proc.c */ @@ -14,17 +14,13 @@ extern int smb_open(struct dentry *dentry, int wish); extern int smb_close(struct inode *ino); extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); -extern int smb_proc_read(struct inode *inode, off_t offset, int count, char *data); -extern int smb_proc_write(struct inode *inode, off_t offset, int count, const char *data); extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); extern int smb_proc_mkdir(struct dentry *dentry); extern int smb_proc_rmdir(struct dentry *dentry); extern int smb_proc_unlink(struct dentry *dentry); extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); -extern int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length); extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -extern int smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctl); extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); diff -urN linux-2.5.6-pre3/include/asm-alpha/jensen.h linux-2.5.6/include/asm-alpha/jensen.h --- linux-2.5.6-pre3/include/asm-alpha/jensen.h Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/include/asm-alpha/jensen.h Thu Mar 7 18:24:55 2002 @@ -7,9 +7,6 @@ * Defines for the AlphaPC EISA IO and memory address space. */ -/* The Jensen is strange */ -#define AUX_IRQ (9) - /* * NOTE! The memory operations do not set any memory barriers, as it's * not needed for cases like a frame buffer that is essentially memory-like. diff -urN linux-2.5.6-pre3/include/asm-alpha/keyboard.h linux-2.5.6/include/asm-alpha/keyboard.h --- linux-2.5.6-pre3/include/asm-alpha/keyboard.h Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/include/asm-alpha/keyboard.h Thu Mar 7 18:24:55 2002 @@ -60,7 +60,8 @@ * Machine specific bits for the PS/2 driver */ -#define AUX_IRQ 12 +/* Jensen puts this at 9, everyone else at the standard 12. */ +#define AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) #define aux_request_irq(hand, dev_id) \ request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) diff -urN linux-2.5.6-pre3/include/asm-alpha/pgalloc.h linux-2.5.6/include/asm-alpha/pgalloc.h --- linux-2.5.6-pre3/include/asm-alpha/pgalloc.h Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/include/asm-alpha/pgalloc.h Thu Mar 7 18:24:55 2002 @@ -230,121 +230,66 @@ * used to allocate a kernel page table - this turns on ASN bits * if any. */ -#ifndef CONFIG_SMP -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pmd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; -#else -#include -#define quicklists cpu_data[smp_processor_id()] -#endif -#define pgd_quicklist (quicklists.pgd_cache) -#define pmd_quicklist (quicklists.pmd_cache) -#define pte_quicklist (quicklists.pte_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) - -#define pmd_populate(mm, pmd, pte) pmd_set(pmd, pte) -#define pgd_populate(mm, pgd, pmd) pgd_set(pgd, pmd) - -extern pgd_t *get_pgd_slow(void); - -static inline pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if ((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } else - ret = (unsigned long *)get_pgd_slow(); - return (pgd_t *)ret; -} - -static inline void free_pgd_fast(pgd_t *pgd) -{ - *(unsigned long *)pgd = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} -static inline void free_pgd_slow(pgd_t *pgd) +static inline void +pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) { - free_page((unsigned long)pgd); + pmd_set(pmd, (pte_t *)((pte - mem_map) << PAGE_SHIFT)); } -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) { - pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); - if (ret) - clear_page(ret); - return ret; + pmd_set(pmd, pte); } -static inline pmd_t *pmd_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static inline void +pgd_populate(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) { - unsigned long *ret; - - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } - return (pmd_t *)ret; + pgd_set(pgd, pmd); } -static inline void pmd_free_fast(pmd_t *pmd) +extern pgd_t *pgd_alloc(struct mm_struct *mm); + +static inline void +pgd_free(pgd_t *pgd) { - *(unsigned long *)pmd = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pmd; - pgtable_cache_size++; + free_page((unsigned long)pgd); } -static inline void pmd_free_slow(pmd_t *pmd) +static inline pmd_t * +pmd_alloc_one(struct mm_struct *mm, unsigned long address) { - free_page((unsigned long)pmd); + pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); + if (ret) + clear_page(ret); + return ret; } -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline void +pmd_free(pmd_t *pmd) { - pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL); - if (pte) - clear_page(pte); - return pte; + free_page((unsigned long)pmd); } -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) -{ - unsigned long *ret; +extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr); - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - } - return (pte_t *)ret; +static inline void +pte_free_kernel(pte_t *pte) +{ + free_page((unsigned long)pte); } -static inline void pte_free_fast(pte_t *pte) +static inline struct page * +pte_alloc_one(struct mm_struct *mm, unsigned long addr) { - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; + return virt_to_page(pte_alloc_one_kernel(mm, addr)); } -static inline void pte_free_slow(pte_t *pte) +static inline void +pte_free(struct page *page) { - free_page((unsigned long)pte); + __free_page(page); } -#define pte_free(pte) pte_free_fast(pte) -#define pmd_free(pmd) pmd_free_fast(pmd) -#define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc(mm) get_pgd_fast() - -extern int do_check_pgt_cache(int, int); - #endif /* _ALPHA_PGALLOC_H */ diff -urN linux-2.5.6-pre3/include/asm-alpha/pgtable.h linux-2.5.6/include/asm-alpha/pgtable.h --- linux-2.5.6-pre3/include/asm-alpha/pgtable.h Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/include/asm-alpha/pgtable.h Thu Mar 7 18:24:55 2002 @@ -248,8 +248,13 @@ }) #endif -extern inline unsigned long pmd_page(pmd_t pmd) -{ return PAGE_OFFSET + ((pmd_val(pmd) & _PFN_MASK) >> (32-PAGE_SHIFT)); } +extern inline unsigned long +pmd_page_kernel(pmd_t pmd) +{ + return ((pmd_val(pmd) & _PFN_MASK) >> (32-PAGE_SHIFT)) + PAGE_OFFSET; +} + +#define pmd_page(pmd) (mem_map + ((pmd_val(pmd) & _PFN_MASK) >> 32)) extern inline unsigned long pgd_page(pgd_t pgd) { return PAGE_OFFSET + ((pgd_val(pgd) & _PFN_MASK) >> (32-PAGE_SHIFT)); } @@ -306,11 +311,17 @@ } /* Find an entry in the third-level page table.. */ -extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address) +extern inline pte_t * pte_offset_kernel(pmd_t * dir, unsigned long address) { - return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PAGE - 1)); + return (pte_t *) pmd_page_kernel(*dir) + + ((address >> PAGE_SHIFT) & (PTRS_PER_PAGE - 1)); } +#define pte_offset_map(dir,addr) pte_offset_kernel((dir),(addr)) +#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir),(addr)) +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) + extern pgd_t swapper_pg_dir[1024]; /* diff -urN linux-2.5.6-pre3/include/asm-alpha/smp.h linux-2.5.6/include/asm-alpha/smp.h --- linux-2.5.6-pre3/include/asm-alpha/smp.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/asm-alpha/smp.h Thu Mar 7 18:24:55 2002 @@ -29,10 +29,6 @@ unsigned long last_asn; int need_new_asn; int asn_lock; - unsigned long *pgd_cache; - unsigned long *pmd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; unsigned long ipi_count; unsigned long irq_attempt[NR_IRQS]; unsigned long prof_multiplier; diff -urN linux-2.5.6-pre3/include/asm-alpha/system.h linux-2.5.6/include/asm-alpha/system.h --- linux-2.5.6-pre3/include/asm-alpha/system.h Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/include/asm-alpha/system.h Thu Mar 7 18:24:55 2002 @@ -137,6 +137,7 @@ check_mmu_context(); \ } while (0) +struct task_struct; extern void alpha_switch_to(unsigned long, struct task_struct*); #define mb() \ diff -urN linux-2.5.6-pre3/include/asm-i386/io.h linux-2.5.6/include/asm-i386/io.h --- linux-2.5.6-pre3/include/asm-i386/io.h Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/include/asm-i386/io.h Thu Mar 7 18:24:55 2002 @@ -39,7 +39,8 @@ #define IO_SPACE_LIMIT 0xffff #define XQUAD_PORTIO_BASE 0xfe400000 -#define XQUAD_PORTIO_LEN 0x40000 /* 256k per quad. Only remapping 1st */ +#define XQUAD_PORTIO_QUAD 0x40000 /* 256k per quad. */ +#define XQUAD_PORTIO_LEN 0x80000 /* Only remapping first 2 quads */ #ifdef __KERNEL__ @@ -261,52 +262,65 @@ __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" #ifdef CONFIG_MULTIQUAD -/* Make the default portio routines operate on quad 0 for now */ -#define __OUT(s,s1,x) \ -__OUT1(s##_local,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ -__OUT1(s##_p_local,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ -__OUTQ0(s,s,x) \ -__OUTQ0(s,s##_p,x) -#else -#define __OUT(s,s1,x) \ -__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ -__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} -#endif /* CONFIG_MULTIQUAD */ - -#ifdef CONFIG_MULTIQUAD -#define __OUTQ0(s,ss,x) /* Do the equivalent of the portio op on quad 0 */ \ +#define __OUTQ(s,ss,x) /* Do the equivalent of the portio op on quads */ \ static inline void out##ss(unsigned x value, unsigned short port) { \ if (xquad_portio) \ write##s(value, (unsigned long) xquad_portio + port); \ else /* We're still in early boot, running on quad 0 */ \ out##ss##_local(value, port); \ -} +} \ +static inline void out##ss##_quad(unsigned x value, unsigned short port, int quad) { \ + if (xquad_portio) \ + write##s(value, (unsigned long) xquad_portio + (XQUAD_PORTIO_QUAD*quad)\ + + port); \ +} -#define __INQ0(s,ss) /* Do the equivalent of the portio op on quad 0 */ \ +#define __INQ(s,ss) /* Do the equivalent of the portio op on quads */ \ static inline RETURN_TYPE in##ss(unsigned short port) { \ if (xquad_portio) \ return read##s((unsigned long) xquad_portio + port); \ else /* We're still in early boot, running on quad 0 */ \ return in##ss##_local(port); \ +} \ +static inline RETURN_TYPE in##ss##_quad(unsigned short port, int quad) { \ + if (xquad_portio) \ + return read##s((unsigned long) xquad_portio + (XQUAD_PORTIO_QUAD*quad)\ + + port); \ + else\ + return 0;\ } #endif /* CONFIG_MULTIQUAD */ +#ifndef CONFIG_MULTIQUAD +#define __OUT(s,s1,x) \ +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} +#else +/* Make the default portio routines operate on quad 0 */ +#define __OUT(s,s1,x) \ +__OUT1(s##_local,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p_local,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ +__OUTQ(s,s,x) \ +__OUTQ(s,s##_p,x) +#endif /* CONFIG_MULTIQUAD */ + #define __IN1(s) \ static inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v; #define __IN2(s,s1,s2) \ __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" -#ifdef CONFIG_MULTIQUAD -#define __IN(s,s1,i...) \ -__IN1(s##_local) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ -__IN1(s##_p_local) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ -__INQ0(s,s) \ -__INQ0(s,s##_p) -#else +#ifndef CONFIG_MULTIQUAD #define __IN(s,s1,i...) \ __IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ __IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } +#else +/* Make the default portio routines operate on quad 0 */ +#define __IN(s,s1,i...) \ +__IN1(s##_local) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__IN1(s##_p_local) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__INQ(s,s) \ +__INQ(s,s##_p) #endif /* CONFIG_MULTIQUAD */ #define __INS(s) \ diff -urN linux-2.5.6-pre3/include/asm-i386/mpspec.h linux-2.5.6/include/asm-i386/mpspec.h --- linux-2.5.6-pre3/include/asm-i386/mpspec.h Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/include/asm-i386/mpspec.h Thu Mar 7 18:24:55 2002 @@ -198,6 +198,9 @@ MP_BUS_MCA }; extern int mp_bus_id_to_type [MAX_MP_BUSSES]; +extern int mp_bus_id_to_node [MAX_MP_BUSSES]; +extern int mp_bus_id_to_local [MAX_MP_BUSSES]; +extern int quad_local_to_mp_bus_id [NR_CPUS/4][4]; extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; extern unsigned int boot_cpu_physical_apicid; diff -urN linux-2.5.6-pre3/include/asm-i386/siginfo.h linux-2.5.6/include/asm-i386/siginfo.h --- linux-2.5.6-pre3/include/asm-i386/siginfo.h Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/include/asm-i386/siginfo.h Thu Mar 7 18:24:55 2002 @@ -108,6 +108,7 @@ #define SI_ASYNCIO -4 /* sent by AIO completion */ #define SI_SIGIO -5 /* sent by queued SIGIO */ #define SI_TKILL -6 /* sent by tkill system call */ +#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ #define SI_FROMUSER(siptr) ((siptr)->si_code <= 0) #define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) diff -urN linux-2.5.6-pre3/include/asm-x86_64/rwsem.h linux-2.5.6/include/asm-x86_64/rwsem.h --- linux-2.5.6-pre3/include/asm-x86_64/rwsem.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/include/asm-x86_64/rwsem.h Thu Mar 7 18:24:55 2002 @@ -157,9 +157,9 @@ " jmp 1b\n" ".previous\n" "# ending __up_read\n" - : "+m"(sem->count), "+d"(tmp) + : "+d"(tmp) : "a"(sem) - : "memory", "cc"); + : "memory"); } /* @@ -169,7 +169,7 @@ { __asm__ __volatile__( "# beginning __up_write\n\t" - " movl %2,%%edx\n\t" + " movl %1,%%edx\n\t" LOCK_PREFIX " xaddl %%edx,(%%rax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ " jnz 2f\n\t" /* jump if the lock is being waited upon */ "1:\n\t" @@ -181,7 +181,7 @@ " jmp 1b\n" ".previous\n" "# ending __up_write\n" - : "+m"(sem->count) + : : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS) : "memory", "cc", "edx"); } diff -urN linux-2.5.6-pre3/include/linux/cramfs_fs.h linux-2.5.6/include/linux/cramfs_fs.h --- linux-2.5.6-pre3/include/linux/cramfs_fs.h Tue Feb 19 18:10:58 2002 +++ linux-2.5.6/include/linux/cramfs_fs.h Thu Mar 7 18:24:55 2002 @@ -24,6 +24,12 @@ #define CRAMFS_OFFSET_WIDTH 26 /* + * Since inode.namelen is a unsigned 6-bit number, the maximum cramfs + * path length is 63 << 2 = 252. + */ +#define CRAMFS_MAXPATHLEN (((1 << CRAMFS_NAMELEN_WIDTH) - 1) << 2) + +/* * Reasonably terse representation of the inode data. */ struct cramfs_inode { @@ -52,14 +58,14 @@ * Superblock information at the beginning of the FS. */ struct cramfs_super { - u32 magic; /* 0x28cd3d45 - random number */ - u32 size; /* length in bytes */ - u32 flags; /* 0 */ - u32 future; /* 0 */ - u8 signature[16]; /* "Compressed ROMFS" */ + u32 magic; /* 0x28cd3d45 - random number */ + u32 size; /* length in bytes */ + u32 flags; /* feature flags */ + u32 future; /* reserved for future use */ + u8 signature[16]; /* "Compressed ROMFS" */ struct cramfs_info fsid; /* unique filesystem info */ - u8 name[16]; /* user-defined name */ - struct cramfs_inode root; /* Root inode data */ + u8 name[16]; /* user-defined name */ + struct cramfs_inode root; /* root inode data */ }; /* @@ -79,7 +85,10 @@ * if (flags & ~CRAMFS_SUPPORTED_FLAGS). Maybe that should be * changed to test super.future instead. */ -#define CRAMFS_SUPPORTED_FLAGS (0x7ff) +#define CRAMFS_SUPPORTED_FLAGS ( 0x000000ff \ + | CRAMFS_FLAG_HOLES \ + | CRAMFS_FLAG_WRONG_SIGNATURE \ + | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET ) /* Uncompression interfaces to the underlying zlib */ int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen); diff -urN linux-2.5.6-pre3/include/linux/hdlc/ioctl.h linux-2.5.6/include/linux/hdlc/ioctl.h --- linux-2.5.6-pre3/include/linux/hdlc/ioctl.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/include/linux/hdlc/ioctl.h Thu Mar 7 18:24:55 2002 @@ -0,0 +1,55 @@ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +union hdlc_settings { + raw_hdlc_proto raw_hdlc; + cisco_proto cisco; + fr_proto fr; + fr_proto_pvc fr_pvc; +}; + +union line_settings { + sync_serial_settings sync; + te1_settings te1; +}; + +#endif /* __HDLC_IOCTL_H__ */ diff -urN linux-2.5.6-pre3/include/linux/hdlc.h linux-2.5.6/include/linux/hdlc.h --- linux-2.5.6-pre3/include/linux/hdlc.h Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/include/linux/hdlc.h Thu Mar 7 18:24:55 2002 @@ -1,7 +1,7 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999, 2000 Krzysztof Halasa + * Copyright (C) 1999-2002 Krzysztof Halasa * * 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 @@ -12,64 +12,49 @@ #ifndef __HDLC_H #define __HDLC_H -/* Ioctls - to be changed */ -#define HDLCGSLOTMAP (0x89F4) /* E1/T1 slot bitmap */ -#define HDLCGCLOCK (0x89F5) /* clock sources */ -#define HDLCGCLOCKRATE (0x89F6) /* clock rate */ -#define HDLCGMODE (0x89F7) /* internal to hdlc.c - protocol used */ -#define HDLCGLINE (0x89F8) /* physical interface */ -#define HDLCSSLOTMAP (0x89F9) -#define HDLCSCLOCK (0x89FA) -#define HDLCSCLOCKRATE (0x89FB) -#define HDLCSMODE (0x89FC) /* internal to hdlc.c - select protocol */ -#define HDLCPVC (0x89FD) /* internal to hdlc.c - create/delete PVC */ -#define HDLCSLINE (0x89FE) -#define HDLCRUN (0x89FF) /* Download firmware and run board */ - -/* Modes */ -#define MODE_NONE 0x00000000 /* Not initialized */ -#define MODE_DCE 0x00000080 /* DCE */ -#define MODE_HDLC 0x00000100 /* Raw HDLC frames */ -#define MODE_CISCO 0x00000200 -#define MODE_PPP 0x00000400 -#define MODE_FR 0x00000800 /* Any LMI */ -#define MODE_FR_ANSI 0x00000801 -#define MODE_FR_CCITT 0x00000802 -#define MODE_X25 0x00001000 -#define MODE_MASK 0x0000FF00 -#define MODE_SOFT 0x80000000 /* Driver modes, using hardware HDLC */ - -/* Lines */ -#define LINE_DEFAULT 0x00000000 -#define LINE_V35 0x00000001 -#define LINE_RS232 0x00000002 -#define LINE_X21 0x00000003 -#define LINE_T1 0x00000004 -#define LINE_E1 0x00000005 -#define LINE_MASK 0x000000FF -#define LINE_LOOPBACK 0x80000000 /* On-card loopback */ - -#define CLOCK_EXT 0 /* External TX and RX clock - DTE */ -#define CLOCK_INT 1 /* Internal TX and RX clock - DCE */ -#define CLOCK_TXINT 2 /* Internal TX and external RX clock */ -#define CLOCK_TXFROMRX 3 /* TX clock derived from external RX clock */ +#define CLOCK_DEFAULT 0 /* Default (current) setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default (current) setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default (current) setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default (current) setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +/* PPP doesn't need any info now - supply length = 0 to ioctl */ -#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ -#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ #ifdef __KERNEL__ #include #include #include +#include -#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ +#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ +#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ -#define LINK_STATE_RELIABLE 0x01 -#define LINK_STATE_REQUEST 0x02 /* full stat sent (DCE) / req pending (DTE) */ -#define LINK_STATE_CHANGED 0x04 /* change in PVCs state, send full report */ -#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */ +#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ #define PVC_STATE_NEW 0x01 #define PVC_STATE_ACTIVE 0x02 @@ -112,6 +97,7 @@ typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) unsigned ea1 : 1; unsigned cr : 1; unsigned dlcih: 6; @@ -121,6 +107,19 @@ unsigned becn : 1; unsigned fecn : 1; unsigned dlcil: 4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned dlcih: 6; + unsigned cr : 1; + unsigned ea1 : 1; + + unsigned dlcil: 4; + unsigned fecn : 1; + unsigned becn : 1; + unsigned de : 1; + unsigned ea2 : 1; +#else +#error "Please fix " +#endif }__attribute__ ((packed)) fr_hdr; @@ -151,63 +150,96 @@ struct hdlc_device_struct *master; struct pvc_device_struct *next; - u8 state; - u8 newstate; + struct { + int active; + int new; + int deleted; + int fecn; + int becn; + }state; }pvc_device; -typedef struct { - u32 last_errors; /* last errors bit list */ - int last_poll; /* ! */ - u8 T391; /* ! link integrity verification polling timer */ - u8 T392; /* ! polling verification timer */ - u8 N391; /* full status polling counter */ - u8 N392; /* error threshold */ - u8 N393; /* monitored events count */ - u8 N391cnt; - - u8 state; /* ! */ - u32 txseq; /* ! TX sequence number - Cisco uses 4 bytes */ - u32 rxseq; /* ! RX sequence number */ -}fr_lmi; /* ! means used in Cisco HDLC as well */ - - typedef struct hdlc_device_struct { - /* to be initialized by hardware driver: */ + /* To be initialized by hardware driver */ struct net_device netdev; /* master net device - must be first */ struct net_device_stats stats; - struct ppp_device pppdev; - struct ppp_device *syncppp_ptr; + /* used by HDLC layer to take control over HDLC device from hw driver*/ + int (*attach)(struct hdlc_device_struct *hdlc, + unsigned short encoding, unsigned short parity); - /* set_mode may be NULL if HDLC-only board */ - int (*set_mode)(struct hdlc_device_struct *hdlc, int mode); - int (*open)(struct hdlc_device_struct *hdlc); - void (*close)(struct hdlc_device_struct *hdlc); - int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb); - int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr, - int cmd); - - /* Only in "hardware" FR modes etc. - may be NULL */ - int (*create_pvc)(pvc_device *pvc); - void (*destroy_pvc)(pvc_device *pvc); - int (*open_pvc)(pvc_device *pvc); - void (*close_pvc)(pvc_device *pvc); - - /* for hdlc.c internal use only */ - pvc_device *first_pvc; - u16 pvc_count; - int mode; + /* hardware driver must handle this instead of dev->hard_start_xmit */ + int (*xmit)(struct sk_buff *skb, struct net_device *dev); - struct timer_list timer; - fr_lmi lmi; + + /* Things below are for HDLC layer internal use only */ + int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); + int (*open)(struct hdlc_device_struct *hdlc); + void (*stop)(struct hdlc_device_struct *hdlc); + void (*detach)(struct hdlc_device_struct *hdlc); + void (*netif_rx)(struct sk_buff *skb); + int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */ + + union { + struct { + fr_proto settings; + pvc_device *first_pvc; + int pvc_count; + + struct timer_list timer; + int last_poll; + int reliable; + int changed; + int request; + int fullrep_sent; + u32 last_errors; /* last errors bit list */ + u8 n391cnt; + u8 txseq; /* TX sequence number */ + u8 rxseq; /* RX sequence number */ + }fr; + + struct { + cisco_proto settings; + + struct timer_list timer; + int last_poll; + int up; + u32 txseq; /* TX sequence number */ + u32 rxseq; /* RX sequence number */ + }cisco; + + struct { + raw_hdlc_proto settings; + }raw_hdlc; + + struct { + struct ppp_device pppdev; + struct ppp_device *syncppp_ptr; + int (*old_change_mtu)(struct net_device *dev, + int new_mtu); + }ppp; + }state; }hdlc_device; + +int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr); + + +/* Exported from hdlc.o */ + +/* Called by hardware driver when a user requests HDLC service */ +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +/* Must be used by hardware driver on module startup/exit */ int register_hdlc_device(hdlc_device *hdlc); void unregister_hdlc_device(hdlc_device *hdlc); -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb); static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc) @@ -246,33 +278,6 @@ } -static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state) -{ - *state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW); - if (status[2] & 0x08) - *state |= PVC_STATE_NEW; - else if (status[2] & 0x02) - *state |= PVC_STATE_ACTIVE; - - return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); -} - - -static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, - u8 state) -{ - status[0] = (dlci>>4) & 0x3F; - status[1] = ((dlci<<3) & 0x78) | 0x80; - status[2] = 0x80; - - if (state & PVC_STATE_NEW) - status[2] |= 0x08; - else if (state & PVC_STATE_ACTIVE) - status[2] |= 0x02; -} - - - static __inline__ u16 netdev_dlci(struct net_device *dev) { return ntohs(*(u16*)dev->dev_addr); @@ -282,37 +287,15 @@ static __inline__ u16 q922_to_dlci(u8 *hdr) { - return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4); + return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4); } static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci) { - hdr[0] = (dlci>>2) & 0xFC; - hdr[1] = ((dlci<<4) & 0xF0) | 0x01; -} - - - -static __inline__ int mode_is(hdlc_device *hdlc, int mask) -{ - return (hdlc->mode & mask) == mask; -} - - - -static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) -{ - pvc_device *pvc=hdlc->first_pvc; - - while (pvc) { - if (netdev_dlci(&pvc->netdev) == dlci) - return pvc; - pvc=pvc->next; - } - - return NULL; + hdr[0] = (dlci >> 2) & 0xFC; + hdr[1] = ((dlci << 4) & 0xF0) | 0x01; } @@ -332,5 +315,35 @@ } + +/* Must be called by hardware driver when HDLC device is being opened */ +static __inline__ int hdlc_open(hdlc_device *hdlc) +{ + if (hdlc->proto == -1) + return -ENOSYS; /* no protocol attached */ + + if (hdlc->open) + return hdlc->open(hdlc); + return 0; +} + + +/* Must be called by hardware driver when HDLC device is being closed */ +static __inline__ void hdlc_close(hdlc_device *hdlc) +{ + if (hdlc->stop) + hdlc->stop(hdlc); +} + + +/* May be used by hardware driver to gain control over HDLC device */ +static __inline__ void hdlc_detach(hdlc_device *hdlc) +{ + if (hdlc->detach) + hdlc->detach(hdlc); + hdlc->detach = NULL; +} + + #endif /* __KERNEL */ #endif /* __HDLC_H */ diff -urN linux-2.5.6-pre3/include/linux/if.h linux-2.5.6/include/linux/if.h --- linux-2.5.6-pre3/include/linux/if.h Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/include/linux/if.h Thu Mar 7 18:24:55 2002 @@ -21,6 +21,7 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ +#include /* Standard interface flags (netdevice->flags). */ #define IFF_UP 0x1 /* interface is up */ @@ -48,6 +49,29 @@ /* Private (from user) interface flags (netdevice->priv_flags). */ #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* cant'b be set by software */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ + + + /* * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments @@ -69,6 +93,19 @@ /* 3 bytes spare */ }; +struct if_settings +{ + unsigned int type; /* Type of physical device or protocol */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + union hdlc_settings ifsu_hdlc; + union line_settings ifsu_line; + } ifs_ifsu; +}; + +#define ifs_hdlc ifs_ifsu.ifsu_hdlc +#define ifs_line ifs_ifsu.ifsu_line + /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter @@ -98,6 +135,7 @@ char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; char * ifru_data; + struct if_settings *ifru_settings; } ifr_ifru; }; @@ -117,6 +155,7 @@ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ /* * Structure used in SIOCGIFCONF request. diff -urN linux-2.5.6-pre3/include/linux/if_ether.h linux-2.5.6/include/linux/if_ether.h --- linux-2.5.6-pre3/include/linux/if_ether.h Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/include/linux/if_ether.h Thu Mar 7 18:24:55 2002 @@ -85,6 +85,7 @@ #define ETH_P_CONTROL 0x0016 /* Card specific control frames */ #define ETH_P_IRDA 0x0017 /* Linux-IrDA */ #define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ /* * This is an Ethernet frame header. diff -urN linux-2.5.6-pre3/include/linux/pci.h linux-2.5.6/include/linux/pci.h --- linux-2.5.6-pre3/include/linux/pci.h Thu Mar 7 18:24:37 2002 +++ linux-2.5.6/include/linux/pci.h Thu Mar 7 18:24:55 2002 @@ -564,6 +564,10 @@ int pci_enable_device(struct pci_dev *dev); void pci_disable_device(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev); +#define HAVE_PCI_SET_MWI +int pci_set_mwi(struct pci_dev *dev); +void pci_clear_mwi(struct pci_dev *dev); +int pdev_set_mwi(struct pci_dev *dev); int pci_set_dma_mask(struct pci_dev *dev, u64 mask); int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask); int pci_assign_resource(struct pci_dev *dev, int i); diff -urN linux-2.5.6-pre3/include/linux/pci_ids.h linux-2.5.6/include/linux/pci_ids.h --- linux-2.5.6-pre3/include/linux/pci_ids.h Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/include/linux/pci_ids.h Thu Mar 7 18:24:55 2002 @@ -1494,7 +1494,11 @@ #define PCI_VENDOR_ID_BROADCOM 0x14e4 #define PCI_DEVICE_ID_TIGON3_5700 0x1644 #define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 #define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 #define PCI_VENDOR_ID_SYBA 0x1592 #define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 @@ -1517,6 +1521,9 @@ #define PCI_DEVICE_ID_MACROLINK_MCCR8 0x2000 #define PCI_DEVICE_ID_MACROLINK_MCCR 0x2001 +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 + #define PCI_VENDOR_ID_SYMPHONY 0x1c1c #define PCI_DEVICE_ID_SYMPHONY_101 0x0001 diff -urN linux-2.5.6-pre3/include/linux/pnpbios.h linux-2.5.6/include/linux/pnpbios.h --- linux-2.5.6-pre3/include/linux/pnpbios.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/linux/pnpbios.h Thu Mar 7 18:24:55 2002 @@ -142,7 +142,7 @@ extern int pnpbios_dont_use_current_config; extern void *pnpbios_kmalloc(size_t size, int f); -extern void pnpbios_init (void); +extern int pnpbios_init (void); extern void pnpbios_proc_init (void); extern int pnp_bios_dev_node_info (struct pnp_dev_node_info *data); diff -urN linux-2.5.6-pre3/include/linux/smb.h linux-2.5.6/include/linux/smb.h --- linux-2.5.6-pre3/include/linux/smb.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/include/linux/smb.h Thu Mar 7 18:24:55 2002 @@ -86,7 +86,7 @@ uid_t f_uid; gid_t f_gid; kdev_t f_rdev; - off_t f_size; + loff_t f_size; time_t f_atime; time_t f_mtime; time_t f_ctime; diff -urN linux-2.5.6-pre3/include/linux/smb_fs.h linux-2.5.6/include/linux/smb_fs.h --- linux-2.5.6-pre3/include/linux/smb_fs.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/include/linux/smb_fs.h Thu Mar 7 18:24:55 2002 @@ -117,6 +117,7 @@ #define SMB_CAP_NT_FIND 0x0200 #define SMB_CAP_DFS 0x1000 #define SMB_CAP_LARGE_READX 0x4000 +#define SMB_CAP_LARGE_WRITEX 0x8000 /* @@ -159,6 +160,30 @@ int filled, valid, idx; }; +#define SMB_OPS_NUM_STATIC 5 +struct smb_ops { + int (*read)(struct inode *inode, loff_t offset, int count, + char *data); + int (*write)(struct inode *inode, loff_t offset, int count, const + char *data); + int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl); + + int (*getattr)(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); + /* int (*setattr)(...); */ /* setattr is really icky! */ + + int (*truncate)(struct inode *inode, loff_t length); + + + /* --- --- --- end of "static" entries --- --- --- */ + + int (*convert)(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to); +}; + static inline int smb_is_open(struct inode *i) { diff -urN linux-2.5.6-pre3/include/linux/smb_fs_sb.h linux-2.5.6/include/linux/smb_fs_sb.h --- linux-2.5.6-pre3/include/linux/smb_fs_sb.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/linux/smb_fs_sb.h Thu Mar 7 18:24:55 2002 @@ -54,8 +54,7 @@ to put it on the stack. This points to temp_buf space. */ char *name_buf; - int (*convert)(char *, int, const char *, int, - struct nls_table *, struct nls_table *); + struct smb_ops *ops; }; diff -urN linux-2.5.6-pre3/include/linux/smb_mount.h linux-2.5.6/include/linux/smb_mount.h --- linux-2.5.6-pre3/include/linux/smb_mount.h Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/include/linux/smb_mount.h Thu Mar 7 18:24:55 2002 @@ -37,6 +37,7 @@ #define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ #define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ #define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ +#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ struct smb_mount_data_kernel { diff -urN linux-2.5.6-pre3/include/linux/smbno.h linux-2.5.6/include/linux/smbno.h --- linux-2.5.6-pre3/include/linux/smbno.h Tue Feb 19 18:11:04 2002 +++ linux-2.5.6/include/linux/smbno.h Thu Mar 7 18:24:55 2002 @@ -281,4 +281,51 @@ #define TRANSACT2_FINDNOTIFYNEXT 12 #define TRANSACT2_MKDIR 13 +/* Information Levels - Shared? */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 + +/* Information Levels - TRANSACT2_FINDFIRST */ +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + +/* Information Levels - TRANSACT2_QPATHINFO */ +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b + +/* Information Levels - TRANSACT2_SETFILEINFO */ +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + +/* smb_flg field flags */ +#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 +#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 +#define SMB_FLAGS_RESERVED 0x04 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_REQUEST_OPLOCK 0x20 +#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 +#define SMB_FLAGS_REPLY 0x80 + +/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ +#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 +#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 +#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 +#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 +#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 + #endif /* _SMBNO_H_ */ diff -urN linux-2.5.6-pre3/include/linux/sockios.h linux-2.5.6/include/linux/sockios.h --- linux-2.5.6-pre3/include/linux/sockios.h Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/include/linux/sockios.h Thu Mar 7 18:24:55 2002 @@ -81,6 +81,8 @@ #define SIOCGMIIREG 0x8948 /* Read MII PHY register. */ #define SIOCSMIIREG 0x8949 /* Write MII PHY register. */ +#define SIOCWANDEV 0x894A /* get/set netdev parameters */ + /* ARP cache control calls. */ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ #define SIOCDARP 0x8953 /* delete ARP table entry */ diff -urN linux-2.5.6-pre3/include/linux/spinlock.h linux-2.5.6/include/linux/spinlock.h --- linux-2.5.6-pre3/include/linux/spinlock.h Tue Feb 19 18:11:03 2002 +++ linux-2.5.6/include/linux/spinlock.h Thu Mar 7 18:24:55 2002 @@ -84,12 +84,12 @@ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } #endif -#define spin_lock_init(lock) do { } while(0) +#define spin_lock_init(lock) do { (void)(lock); } while(0) #define _raw_spin_lock(lock) (void)(lock) /* Not "unused variable". */ -#define spin_is_locked(lock) (0) -#define _raw_spin_trylock(lock) ({1; }) -#define spin_unlock_wait(lock) do { } while(0) -#define _raw_spin_unlock(lock) do { } while(0) +#define spin_is_locked(lock) ((void)(lock), 0) +#define _raw_spin_trylock(lock) ((void)(lock), 1) +#define spin_unlock_wait(lock) do { (void)(lock); } while(0) +#define _raw_spin_unlock(lock) do { (void)(lock); } while(0) #elif (DEBUG_SPINLOCKS < 2) diff -urN linux-2.5.6-pre3/include/net/irda/irda.h linux-2.5.6/include/net/irda/irda.h --- linux-2.5.6-pre3/include/net/irda/irda.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/net/irda/irda.h Thu Mar 7 18:24:55 2002 @@ -54,8 +54,8 @@ #define IRDA_MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif -#ifndef ALIGN -# define ALIGN __attribute__((aligned)) +#ifndef IRDA_ALIGN +# define IRDA_ALIGN __attribute__((aligned)) #endif #ifndef PACK # define PACK __attribute__((packed)) diff -urN linux-2.5.6-pre3/include/net/irda/irqueue.h linux-2.5.6/include/net/irda/irqueue.h --- linux-2.5.6-pre3/include/net/irda/irqueue.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/net/irda/irqueue.h Thu Mar 7 18:24:55 2002 @@ -49,8 +49,8 @@ #define HASHBIN_SIZE 8 #define HASHBIN_MASK 0x7 -#ifndef ALIGN -#define ALIGN __attribute__((aligned)) +#ifndef IRDA_ALIGN +#define IRDA_ALIGN __attribute__((aligned)) #endif #define Q_NULL { NULL, NULL, "", 0 } @@ -75,8 +75,8 @@ __u32 magic; int hb_type; int hb_size; - spinlock_t hb_mutex[HASHBIN_SIZE] ALIGN; - irda_queue_t *hb_queue[HASHBIN_SIZE] ALIGN; + spinlock_t hb_mutex[HASHBIN_SIZE] IRDA_ALIGN; + irda_queue_t *hb_queue[HASHBIN_SIZE] IRDA_ALIGN; irda_queue_t* hb_current; } hashbin_t; diff -urN linux-2.5.6-pre3/include/sound/core.h linux-2.5.6/include/sound/core.h --- linux-2.5.6-pre3/include/sound/core.h Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/include/sound/core.h Thu Mar 7 18:24:55 2002 @@ -22,6 +22,10 @@ * */ +#ifdef CONFIG_PM +#include /* wake_up() and struct semaphore */ +#endif + /* Typedef's */ typedef struct timeval snd_timestamp_t; typedef struct sndrv_interval snd_interval_t; diff -urN linux-2.5.6-pre3/include/sound/driver.h linux-2.5.6/include/sound/driver.h --- linux-2.5.6-pre3/include/sound/driver.h Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/include/sound/driver.h Thu Mar 7 18:24:55 2002 @@ -78,12 +78,4 @@ #include "sndmagic.h" -/* - * Temporary hack, until linux/init.h is fixed. - */ -#include -#ifndef __devexit_p -#define __devexit_p(x) x -#endif - #endif /* __SOUND_DRIVER_H */ diff -urN linux-2.5.6-pre3/include/sound/info.h linux-2.5.6/include/sound/info.h --- linux-2.5.6-pre3/include/sound/info.h Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/include/sound/info.h Thu Mar 7 18:24:55 2002 @@ -104,6 +104,11 @@ #ifdef CONFIG_PROC_FS extern snd_info_entry_t *snd_seq_root; +#ifdef CONFIG_SND_OSSEMUL +extern snd_info_entry_t *snd_oss_root; +#else +#define snd_oss_root NULL +#endif int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3))); int snd_info_init(void); @@ -138,6 +143,7 @@ #else #define snd_seq_root NULL +#define snd_oss_root NULL static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; } static inline int snd_info_init(void) { return 0; } diff -urN linux-2.5.6-pre3/include/sound/pcm.h linux-2.5.6/include/sound/pcm.h --- linux-2.5.6-pre3/include/sound/pcm.h Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/include/sound/pcm.h Thu Mar 7 18:24:55 2002 @@ -25,6 +25,7 @@ #include #include +#include typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; typedef sndrv_pcm_sframes_t snd_pcm_sframes_t; diff -urN linux-2.5.6-pre3/include/sound/pcm_params.h linux-2.5.6/include/sound/pcm_params.h --- linux-2.5.6-pre3/include/sound/pcm_params.h Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/include/sound/pcm_params.h Thu Mar 7 18:24:55 2002 @@ -22,8 +22,6 @@ * */ -#include - extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, const snd_mask_t *val); extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, diff -urN linux-2.5.6-pre3/include/sound/version.h linux-2.5.6/include/sound/version.h --- linux-2.5.6-pre3/include/sound/version.h Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/include/sound/version.h Thu Mar 7 18:24:55 2002 @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0beta12" -#define CONFIG_SND_DATE " (Tue Feb 26 09:34:24 2002 UTC)" +#define CONFIG_SND_DATE " (Wed Mar 06 07:56:20 2002 UTC)" diff -urN linux-2.5.6-pre3/init/do_mounts.c linux-2.5.6/init/do_mounts.c --- linux-2.5.6-pre3/init/do_mounts.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/init/do_mounts.c Thu Mar 7 18:24:55 2002 @@ -840,7 +840,7 @@ mount_devfs_fs (); } -#ifdef BUILD_CRAMDISK +#if defined(BUILD_CRAMDISK) && defined(CONFIG_BLK_DEV_RAM) /* * gzip declarations @@ -985,4 +985,4 @@ return result; } -#endif /* BUILD_CRAMDISK */ +#endif /* BUILD_CRAMDISK && CONFIG_BLK_DEV_RAM */ diff -urN linux-2.5.6-pre3/kernel/sched.c linux-2.5.6/kernel/sched.c --- linux-2.5.6-pre3/kernel/sched.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/kernel/sched.c Thu Mar 7 18:24:55 2002 @@ -1438,10 +1438,12 @@ idle->prio = MAX_PRIO; idle->state = TASK_RUNNING; idle->thread_info->cpu = cpu; - idle->thread_info->preempt_count = (idle->lock_depth >= 0); double_rq_unlock(idle_rq, rq); set_tsk_need_resched(idle); __restore_flags(flags); + + /* Set the preempt count _outside_ the spinlocks! */ + idle->thread_info->preempt_count = (idle->lock_depth >= 0); } extern void init_timervecs(void); diff -urN linux-2.5.6-pre3/net/core/dev.c linux-2.5.6/net/core/dev.c --- linux-2.5.6-pre3/net/core/dev.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/net/core/dev.c Thu Mar 7 18:24:56 2002 @@ -2110,7 +2110,8 @@ cmd == SIOCETHTOOL || cmd == SIOCGMIIPHY || cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG) { + cmd == SIOCSMIIREG || + cmd == SIOCWANDEV) { if (dev->do_ioctl) { if (!netif_device_present(dev)) return -ENODEV; @@ -2276,8 +2277,9 @@ */ default: - if (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) { + if (cmd == SIOCWANDEV || + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { dev_load(ifr.ifr_name); dev_probe_lock(); rtnl_lock(); diff -urN linux-2.5.6-pre3/scripts/cramfs/GNUmakefile linux-2.5.6/scripts/cramfs/GNUmakefile --- linux-2.5.6-pre3/scripts/cramfs/GNUmakefile Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/scripts/cramfs/GNUmakefile Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -CC = gcc -CFLAGS = -W -Wall -O2 -g -CPPFLAGS = -I../../include -LDLIBS = -lz -PROGS = mkcramfs cramfsck - -all: $(PROGS) - -distclean clean: - rm -f $(PROGS) - -.PHONY: all clean diff -urN linux-2.5.6-pre3/scripts/cramfs/cramfsck.c linux-2.5.6/scripts/cramfs/cramfsck.c --- linux-2.5.6-pre3/scripts/cramfs/cramfsck.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/scripts/cramfs/cramfsck.c Wed Dec 31 16:00:00 1969 @@ -1,588 +0,0 @@ -/* - * cramfsck - check a cramfs file system - * - * Copyright (C) 2000-2001 Transmeta Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program) - * 2000/06/03: Daniel Quinlan (CRC and length checking program) - * 2000/06/04: Daniel Quinlan (merged programs, added options, support - * for special files, preserve permissions and - * ownership, cramfs superblock v2, bogus mode - * test, pathname length test, etc.) - * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing, - * symlink size test) - * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512, - * fsck-compatible exit codes) - * 2000/07/15: Daniel Quinlan (initial support for block devices) - */ - -/* compile-time options */ -#define INCLUDE_FS_TESTS /* include cramfs checking and extraction */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define _LINUX_STRING_H_ -#include -#include -#include - -static const char *progname = "cramfsck"; - -static int fd; /* ROM image file descriptor */ -static char *filename; /* ROM image filename */ -struct cramfs_super *super; /* just find the cramfs superblock once */ -static int opt_verbose = 0; /* 1 = verbose (-v), 2+ = very verbose (-vv) */ -#ifdef INCLUDE_FS_TESTS -static int opt_extract = 0; /* extract cramfs (-x) */ -char *extract_dir = NULL; /* extraction directory (-x) */ - -unsigned long start_inode = 1 << 28; /* start of first non-root inode */ -unsigned long end_inode = 0; /* end of the directory structure */ -unsigned long start_data = 1 << 28; /* start of the data (256 MB = max) */ -unsigned long end_data = 0; /* end of the data */ -/* true? cramfs_super < start_inode < end_inode <= start_data <= end_data */ -static uid_t euid; /* effective UID */ - -#define PAD_SIZE 512 -#define PAGE_CACHE_SIZE (4096) - -/* Guarantee access to at least 8kB at a time */ -#define ROMBUFFER_BITS 13 -#define ROMBUFFERSIZE (1 << ROMBUFFER_BITS) -#define ROMBUFFERMASK (ROMBUFFERSIZE-1) -static char read_buffer[ROMBUFFERSIZE * 2]; -static unsigned long read_buffer_block = ~0UL; - -/* Uncompressing data structures... */ -static char outbuffer[PAGE_CACHE_SIZE*2]; -z_stream stream; - -#endif /* INCLUDE_FS_TESTS */ - -/* Input status of 0 to print help and exit without an error. */ -static void usage(int status) -{ - FILE *stream = status ? stderr : stdout; - - fprintf(stream, "usage: %s [-hv] [-x dir] file\n" - " -h print this help\n" - " -x dir extract into dir\n" - " -v be more verbose\n" - " file file to test\n", progname); - - exit(status); -} - -#ifdef INCLUDE_FS_TESTS -void print_node(char type, struct cramfs_inode *i, char *name) -{ - char info[10]; - - if (S_ISCHR(i->mode) || (S_ISBLK(i->mode))) { - /* major/minor numbers can be as high as 2^12 or 4096 */ - snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size)); - } - else { - /* size be as high as 2^24 or 16777216 */ - snprintf(info, 10, "%9d", i->size); - } - - printf("%c %04o %s %5d:%-3d %s\n", - type, i->mode & ~S_IFMT, info, i->uid, i->gid, name); -} - -/* - * Create a fake "blocked" access - */ -static void *romfs_read(unsigned long offset) -{ - unsigned int block = offset >> ROMBUFFER_BITS; - if (block != read_buffer_block) { - read_buffer_block = block; - lseek(fd, block << ROMBUFFER_BITS, SEEK_SET); - read(fd, read_buffer, ROMBUFFERSIZE * 2); - } - return read_buffer + (offset & ROMBUFFERMASK); -} - -static struct cramfs_inode *cramfs_iget(struct cramfs_inode * i) -{ - struct cramfs_inode *inode = malloc(sizeof(struct cramfs_inode)); - *inode = *i; - return inode; -} - -static struct cramfs_inode *iget(unsigned int ino) -{ - return cramfs_iget(romfs_read(ino)); -} - -void iput(struct cramfs_inode *inode) -{ - free(inode); -} - -/* - * Return the offset of the root directory, - * or 0 if none. - */ -static struct cramfs_inode *read_super(void) -{ - unsigned long offset; - - offset = super->root.offset << 2; - if (super->magic != CRAMFS_MAGIC) - return NULL; - if (memcmp(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)) != 0) - return NULL; - if (offset < sizeof(super)) - return NULL; - return cramfs_iget(&super->root); -} - -static int uncompress_block(void *src, int len) -{ - int err; - - stream.next_in = src; - stream.avail_in = len; - - stream.next_out = (unsigned char *) outbuffer; - stream.avail_out = PAGE_CACHE_SIZE*2; - - inflateReset(&stream); - - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - fprintf(stderr, "%s: error %d while decompressing! %p(%d)\n", - filename, err, src, len); - exit(4); - } - return stream.total_out; -} - -static void change_file_status(char *path, struct cramfs_inode *i) -{ - struct utimbuf epoch = { 0, 0 }; - - if (euid == 0) { - if (lchown(path, i->uid, i->gid) < 0) { - perror(path); - exit(8); - } - if (S_ISLNK(i->mode)) - return; - if ((S_ISUID | S_ISGID) & i->mode) { - if (chmod(path, i->mode) < 0) { - perror(path); - exit(8); - } - } - } - if (S_ISLNK(i->mode)) - return; - if (utime(path, &epoch) < 0) { - perror(path); - exit(8); - } -} - -static void do_symlink(char *path, struct cramfs_inode *i) -{ - unsigned long offset = i->offset << 2; - unsigned long curr = offset + 4; - unsigned long next = *(u32 *) romfs_read(offset); - unsigned long size; - - if (next > end_data) { - end_data = next; - } - - size = uncompress_block(romfs_read(curr), next - curr); - if (size != i->size) { - fprintf(stderr, "%s: size error in symlink `%s'\n", - filename, path); - exit(4); - } - outbuffer[size] = 0; - if (opt_verbose) { - char *str; - - str = malloc(strlen(outbuffer) + strlen(path) + 5); - strcpy(str, path); - strncat(str, " -> ", 4); - strncat(str, outbuffer, size); - - print_node('l', i, str); - if (opt_verbose > 1) { - printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); - } - } - if (opt_extract) { - symlink(outbuffer, path); - change_file_status(path, i); - } -} - -static void do_special_inode(char *path, struct cramfs_inode *i) -{ - dev_t devtype = 0; - char type; - - if (S_ISCHR(i->mode)) { - devtype = i->size; - type = 'c'; - } - else if (S_ISBLK(i->mode)) { - devtype = i->size; - type = 'b'; - } - else if (S_ISFIFO(i->mode)) - type = 'p'; - else if (S_ISSOCK(i->mode)) - type = 's'; - else { - fprintf(stderr, "%s: bogus mode on `%s' (%o)\n", filename, path, i->mode); - exit(4); - } - - if (opt_verbose) { - print_node(type, i, path); - } - - if (opt_extract) { - if (mknod(path, i->mode, devtype) < 0) { - perror(path); - exit(8); - } - change_file_status(path, i); - } -} - -static void do_uncompress(int fd, unsigned long offset, unsigned long size) -{ - unsigned long curr = offset + 4 * ((size + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE); - - do { - unsigned long out = PAGE_CACHE_SIZE; - unsigned long next = *(u32 *) romfs_read(offset); - - if (next > end_data) { - end_data = next; - } - - offset += 4; - if (curr == next) { - if (opt_verbose > 1) { - printf(" hole at %ld (%d)\n", curr, PAGE_CACHE_SIZE); - } - if (size < PAGE_CACHE_SIZE) - out = size; - memset(outbuffer, 0x00, out); - } - else { - if (opt_verbose > 1) { - printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); - } - out = uncompress_block(romfs_read(curr), next - curr); - } - if (size >= PAGE_CACHE_SIZE) { - if (out != PAGE_CACHE_SIZE) { - fprintf(stderr, "%s: Non-block (%ld) bytes\n", filename, out); - exit(4); - } - } else { - if (out != size) { - fprintf(stderr, "%s: Non-size (%ld vs %ld) bytes\n", filename, out, size); - exit(4); - } - } - size -= out; - if (opt_extract) { - write(fd, outbuffer, out); - } - curr = next; - } while (size); -} - -static void expand_fs(int pathlen, char *path, struct cramfs_inode *inode) -{ - if (S_ISDIR(inode->mode)) { - int count = inode->size; - unsigned long offset = inode->offset << 2; - char *newpath = malloc(pathlen + 256); - - if (count > 0 && offset < start_inode) { - start_inode = offset; - } - /* XXX - need to check end_inode for empty case? */ - memcpy(newpath, path, pathlen); - newpath[pathlen] = '/'; - pathlen++; - if (opt_verbose) { - print_node('d', inode, path); - } - if (opt_extract) { - mkdir(path, inode->mode); - change_file_status(path, inode); - } - while (count > 0) { - struct cramfs_inode *child = iget(offset); - int size; - int newlen = child->namelen << 2; - - size = sizeof(struct cramfs_inode) + newlen; - count -= size; - - offset += sizeof(struct cramfs_inode); - - memcpy(newpath + pathlen, romfs_read(offset), newlen); - newpath[pathlen + newlen] = 0; - if ((pathlen + newlen) - strlen(newpath) > 3) { - fprintf(stderr, "%s: invalid cramfs--bad path length\n", filename); - exit(4); - } - expand_fs(strlen(newpath), newpath, child); - - offset += newlen; - - if (offset > end_inode) { - end_inode = offset; - } - } - return; - } - if (S_ISREG(inode->mode)) { - int fd = 0; - unsigned long offset = inode->offset << 2; - - if (offset > 0 && offset < start_data) { - start_data = offset; - } - if (opt_verbose) { - print_node('f', inode, path); - } - if (opt_extract) { - fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, inode->mode); - } - if (inode->size) { - do_uncompress(fd, offset, inode->size); - } - if (opt_extract) { - close(fd); - change_file_status(path, inode); - } - return; - } - if (S_ISLNK(inode->mode)) { - unsigned long offset = inode->offset << 2; - - if (offset < start_data) { - start_data = offset; - } - do_symlink(path, inode); - return; - } - else { - do_special_inode(path, inode); - return; - } -} -#endif /* INCLUDE_FS_TESTS */ - -int main(int argc, char **argv) -{ - void *buf; - size_t length; - struct stat st; - u32 crc_old, crc_new; -#ifdef INCLUDE_FS_TESTS - struct cramfs_inode *root; -#endif /* INCLUDE_FS_TESTS */ - int c; /* for getopt */ - int start = 0; - - if (argc) - progname = argv[0]; - - /* command line options */ - while ((c = getopt(argc, argv, "hx:v")) != EOF) { - switch (c) { - case 'h': - usage(0); - case 'x': -#ifdef INCLUDE_FS_TESTS - opt_extract = 1; - extract_dir = malloc(strlen(optarg) + 1); - strcpy(extract_dir, optarg); - break; -#else /* not INCLUDE_FS_TESTS */ - fprintf(stderr, "%s: compiled without -x support\n", - progname); - exit(16); -#endif /* not INCLUDE_FS_TESTS */ - case 'v': - opt_verbose++; - break; - } - } - - if ((argc - optind) != 1) - usage(16); - filename = argv[optind]; - - /* find the physical size of the file or block device */ - if (lstat(filename, &st) < 0) { - perror(filename); - exit(8); - } - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror(filename); - exit(8); - } - if (S_ISBLK(st.st_mode)) { - if (ioctl(fd, BLKGETSIZE, &length) < 0) { - fprintf(stderr, "%s: warning--unable to determine filesystem size \n", filename); - exit(4); - } - length = length * 512; - } - else if (S_ISREG(st.st_mode)) { - length = st.st_size; - } - else { - fprintf(stderr, "%s is not a block device or file\n", filename); - exit(8); - } - - if (length < sizeof(struct cramfs_super)) { - fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); - exit(4); - } - - if (S_ISBLK(st.st_mode)) { - /* nasty because mmap of block devices fails */ - buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - read(fd, buf, length); - } - else { - /* nice and easy */ - buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - } - - /* XXX - this could be cleaner... */ - if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { - start = 0; - super = (struct cramfs_super *) buf; - } - else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && - ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) - { - start = PAD_SIZE; - super = (struct cramfs_super *) (buf + PAD_SIZE); - } - else { - fprintf(stderr, "%s: invalid cramfs--wrong magic\n", filename); - exit(4); - } - - if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { - /* length test */ - if (length < super->size) { - fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); - exit(4); - } - else if (length > super->size) { - fprintf(stderr, "%s: warning--file length too long, padded image?\n", filename); - } - - /* CRC test */ - crc_old = super->fsid.crc; - super->fsid.crc = crc32(0L, Z_NULL, 0); - crc_new = crc32(0L, Z_NULL, 0); - crc_new = crc32(crc_new, (unsigned char *) buf+start, super->size - start); - if (crc_new != crc_old) { - fprintf(stderr, "%s: invalid cramfs--crc error\n", filename); - exit(4); - } - } - else { - fprintf(stderr, "%s: warning--old cramfs image, no CRC\n", - filename); - } - -#ifdef INCLUDE_FS_TESTS - super = (struct cramfs_super *) malloc(sizeof(struct cramfs_super)); - if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { - memcpy(super, buf, sizeof(struct cramfs_super)); - } - else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && - ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) - { - memcpy(super, (buf + PAD_SIZE), sizeof(struct cramfs_super)); - } - - munmap(buf, length); - - /* file format test, uses fake "blocked" accesses */ - root = read_super(); - umask(0); - euid = geteuid(); - if (!root) { - fprintf(stderr, "%s: invalid cramfs--bad superblock\n", - filename); - exit(4); - } - stream.next_in = NULL; - stream.avail_in = 0; - inflateInit(&stream); - - if (!extract_dir) { - extract_dir = "root"; - } - - expand_fs(strlen(extract_dir), extract_dir, root); - inflateEnd(&stream); - - if (start_data != 1 << 28 && end_inode != start_data) { - fprintf(stderr, "%s: invalid cramfs--directory data end (%ld) != file data start (%ld)\n", filename, end_inode, start_data); - exit(4); - } - if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { - if (end_data > super->size) { - fprintf(stderr, "%s: invalid cramfs--invalid file data offset\n", filename); - exit(4); - } - } -#endif /* INCLUDE_FS_TESTS */ - - exit(0); -} diff -urN linux-2.5.6-pre3/scripts/cramfs/mkcramfs.c linux-2.5.6/scripts/cramfs/mkcramfs.c --- linux-2.5.6-pre3/scripts/cramfs/mkcramfs.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/scripts/cramfs/mkcramfs.c Wed Dec 31 16:00:00 1969 @@ -1,776 +0,0 @@ -/* - * mkcramfs - make a cramfs file system - * - * Copyright (C) 1999-2001 Transmeta Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PAD_SIZE 512 /* only 0 and 512 supported by kernel */ - -static const char *progname = "mkcramfs"; - -/* N.B. If you change the disk format of cramfs, please update fs/cramfs/README. */ - -/* Input status of 0 to print help and exit without an error. */ -static void usage(int status) -{ - FILE *stream = status ? stderr : stdout; - - fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n" - " -h print this help\n" - " -E make all warnings errors (non-zero exit status)\n" - " -e edition set edition number (part of fsid)\n" - " -i file insert a file image into the filesystem (requires >= 2.4.0)\n" - " -n name set name of cramfs filesystem\n" - " -p pad by %d bytes for boot code\n" - " -s sort directory entries (old option, ignored)\n" - " -z make explicit holes (requires >= 2.3.39)\n" - " dirname root of the filesystem to be compressed\n" - " outfile output file\n", progname, PAD_SIZE); - - exit(status); -} - -#define PAGE_CACHE_SIZE (4096) -/* The kernel assumes PAGE_CACHE_SIZE as block size. */ -static unsigned int blksize = PAGE_CACHE_SIZE; -static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */ -static int image_length = 0; - -/* - * If opt_holes is set, then mkcramfs can create explicit holes in the - * data, which saves 26 bytes per hole (which is a lot smaller a - * saving than most most filesystems). - * - * Note that kernels up to at least 2.3.39 don't support cramfs holes, - * which is why this is turned off by default. - */ -static int opt_edition = 0; -static int opt_errors = 0; -static int opt_holes = 0; -static int opt_pad = 0; -static char *opt_image = NULL; -static char *opt_name = NULL; - -static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid; - -#ifndef MIN -# define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) -#endif - -/* In-core version of inode / directory entry. */ -struct entry { - /* stats */ - char *name; - unsigned int mode, size, uid, gid; - - /* FS data */ - void *uncompressed; - /* points to other identical file */ - struct entry *same; - unsigned int offset; /* pointer to compressed data in archive */ - unsigned int dir_offset; /* Where in the archive is the directory entry? */ - - /* organization */ - struct entry *child; /* null for non-directories and empty directories */ - struct entry *next; -}; - -/* - * The longest file name component to allow for in the input directory tree. - * Ext2fs (and many others) allow up to 255 bytes. A couple of filesystems - * allow longer (e.g. smbfs 1024), but there isn't much use in supporting - * >255-byte names in the input directory tree given that such names get - * truncated to 255 bytes when written to cramfs. - */ -#define MAX_INPUT_NAMELEN 255 - -static int find_identical_file(struct entry *orig,struct entry *newfile) -{ - if(orig==newfile) return 1; - if(!orig) return 0; - if(orig->size==newfile->size && orig->uncompressed && !memcmp(orig->uncompressed,newfile->uncompressed,orig->size)) { - newfile->same=orig; - return 1; - } - return find_identical_file(orig->child,newfile) || - find_identical_file(orig->next,newfile); -} - -static void eliminate_doubles(struct entry *root,struct entry *orig) { - if(orig) { - if(orig->size && orig->uncompressed) - find_identical_file(root,orig); - eliminate_doubles(root,orig->child); - eliminate_doubles(root,orig->next); - } -} - -/* - * We define our own sorting function instead of using alphasort which - * uses strcoll and changes ordering based on locale information. - */ -static int cramsort (const void *a, const void *b) -{ - return strcmp ((*(const struct dirent **) a)->d_name, - (*(const struct dirent **) b)->d_name); -} - -static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub) -{ - struct dirent **dirlist; - int totalsize = 0, dircount, dirindex; - char *path, *endpath; - size_t len = strlen(name); - - /* Set up the path. */ - /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */ - path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1); - if (!path) { - perror(NULL); - exit(8); - } - memcpy(path, name, len); - endpath = path + len; - *endpath = '/'; - endpath++; - - /* read in the directory and sort */ - dircount = scandir(name, &dirlist, 0, cramsort); - - if (dircount < 0) { - perror(name); - exit(8); - } - - /* process directory */ - for (dirindex = 0; dirindex < dircount; dirindex++) { - struct dirent *dirent; - struct entry *entry; - struct stat st; - int size; - size_t namelen; - - dirent = dirlist[dirindex]; - - /* Ignore "." and ".." - we won't be adding them to the archive */ - if (dirent->d_name[0] == '.') { - if (dirent->d_name[1] == '\0') - continue; - if (dirent->d_name[1] == '.') { - if (dirent->d_name[2] == '\0') - continue; - } - } - namelen = strlen(dirent->d_name); - if (namelen > MAX_INPUT_NAMELEN) { - fprintf(stderr, - "Very long (%u bytes) filename `%s' found.\n" - " Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile. Exiting.\n", - namelen, dirent->d_name); - exit(8); - } - memcpy(endpath, dirent->d_name, namelen + 1); - - if (lstat(path, &st) < 0) { - perror(endpath); - warn_skip = 1; - continue; - } - entry = calloc(1, sizeof(struct entry)); - if (!entry) { - perror(NULL); - exit(8); - } - entry->name = strdup(dirent->d_name); - if (!entry->name) { - perror(NULL); - exit(8); - } - if (namelen > 255) { - /* Can't happen when reading from ext2fs. */ - - /* TODO: we ought to avoid chopping in half - multi-byte UTF8 characters. */ - entry->name[namelen = 255] = '\0'; - warn_namelen = 1; - } - entry->mode = st.st_mode; - entry->size = st.st_size; - entry->uid = st.st_uid; - if (entry->uid >= 1 << CRAMFS_UID_WIDTH) - warn_uid = 1; - entry->gid = st.st_gid; - if (entry->gid >= 1 << CRAMFS_GID_WIDTH) - /* TODO: We ought to replace with a default - gid instead of truncating; otherwise there - are security problems. Maybe mode should - be &= ~070. Same goes for uid once Linux - supports >16-bit uids. */ - warn_gid = 1; - size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3); - *fslen_ub += size; - if (S_ISDIR(st.st_mode)) { - entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub); - } else if (S_ISREG(st.st_mode)) { - /* TODO: We ought to open files in do_compress, one - at a time, instead of amassing all these memory - maps during parse_directory (which don't get used - until do_compress anyway). As it is, we tend to - get EMFILE errors (especially if mkcramfs is run - by non-root). - - While we're at it, do analagously for symlinks - (which would just save a little memory). */ - int fd = open(path, O_RDONLY); - if (fd < 0) { - perror(path); - warn_skip = 1; - continue; - } - if (entry->size) { - if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) { - warn_size = 1; - entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1; - } - - entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, fd, 0); - if (-1 == (int) (long) entry->uncompressed) { - perror("mmap"); - exit(8); - } - } - close(fd); - } else if (S_ISLNK(st.st_mode)) { - entry->uncompressed = malloc(entry->size); - if (!entry->uncompressed) { - perror(NULL); - exit(8); - } - if (readlink(path, entry->uncompressed, entry->size) < 0) { - perror(path); - warn_skip = 1; - continue; - } - } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) { - /* maybe we should skip sockets */ - entry->size = 0; - } else { - entry->size = st.st_rdev; - if (entry->size & -(1<size - 1) / blksize + 1); - - /* block pointers & data expansion allowance + data */ - if(entry->size) - *fslen_ub += (4+26)*blocks + entry->size + 3; - } - - /* Link it into the list */ - *prev = entry; - prev = &entry->next; - totalsize += size; - } - free(path); - free(dirlist); /* allocated by scandir() with malloc() */ - return totalsize; -} - -/* Returns sizeof(struct cramfs_super), which includes the root inode. */ -static unsigned int write_superblock(struct entry *root, char *base, int size) -{ - struct cramfs_super *super = (struct cramfs_super *) base; - unsigned int offset = sizeof(struct cramfs_super) + image_length; - - if (opt_pad) { - offset += opt_pad; - } - - super->magic = CRAMFS_MAGIC; - super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS; - if (opt_holes) - super->flags |= CRAMFS_FLAG_HOLES; - if (image_length > 0) - super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET; - super->size = size; - memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)); - - super->fsid.crc = crc32(0L, Z_NULL, 0); - super->fsid.edition = opt_edition; - super->fsid.blocks = total_blocks; - super->fsid.files = total_nodes; - - memset(super->name, 0x00, sizeof(super->name)); - if (opt_name) - strncpy(super->name, opt_name, sizeof(super->name)); - else - strncpy(super->name, "Compressed", sizeof(super->name)); - - super->root.mode = root->mode; - super->root.uid = root->uid; - super->root.gid = root->gid; - super->root.size = root->size; - super->root.offset = offset >> 2; - - return offset; -} - -static void set_data_offset(struct entry *entry, char *base, unsigned long offset) -{ - struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset); -#ifdef DEBUG - assert ((offset & 3) == 0); -#endif /* DEBUG */ - if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) { - fprintf(stderr, "filesystem too big. Exiting.\n"); - exit(8); - } - inode->offset = (offset >> 2); -} - - -/* - * We do a width-first printout of the directory - * entries, using a stack to remember the directories - * we've seen. - */ -#define MAXENTRIES (100) -static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset) -{ - int stack_entries = 0; - struct entry *entry_stack[MAXENTRIES]; - - for (;;) { - int dir_start = stack_entries; - while (entry) { - struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset); - size_t len = strlen(entry->name); - - entry->dir_offset = offset; - - inode->mode = entry->mode; - inode->uid = entry->uid; - inode->gid = entry->gid; - inode->size = entry->size; - inode->offset = 0; - /* Non-empty directories, regfiles and symlinks will - write over inode->offset later. */ - - offset += sizeof(struct cramfs_inode); - total_nodes++; /* another node */ - memcpy(base + offset, entry->name, len); - /* Pad up the name to a 4-byte boundary */ - while (len & 3) { - *(base + offset + len) = '\0'; - len++; - } - inode->namelen = len >> 2; - offset += len; - - /* TODO: this may get it wrong for chars >= 0x80. - Most filesystems use UTF8 encoding for filenames, - whereas the console is a single-byte character - set like iso-latin-1. */ - printf(" %s\n", entry->name); - if (entry->child) { - if (stack_entries >= MAXENTRIES) { - fprintf(stderr, "Exceeded MAXENTRIES. Raise this value in mkcramfs.c and recompile. Exiting.\n"); - exit(8); - } - entry_stack[stack_entries] = entry; - stack_entries++; - } - entry = entry->next; - } - - /* - * Reverse the order the stack entries pushed during - * this directory, for a small optimization of disk - * access in the created fs. This change makes things - * `ls -UR' order. - */ - { - struct entry **lo = entry_stack + dir_start; - struct entry **hi = entry_stack + stack_entries; - struct entry *tmp; - - while (lo < --hi) { - tmp = *lo; - *lo++ = *hi; - *hi = tmp; - } - } - - /* Pop a subdirectory entry from the stack, and recurse. */ - if (!stack_entries) - break; - stack_entries--; - entry = entry_stack[stack_entries]; - - set_data_offset(entry, base, offset); - printf("'%s':\n", entry->name); - entry = entry->child; - } - return offset; -} - -static int is_zero(char const *begin, unsigned len) -{ - if (opt_holes) - /* Returns non-zero iff the first LEN bytes from BEGIN are - all NULs. */ - return (len-- == 0 || - (begin[0] == '\0' && - (len-- == 0 || - (begin[1] == '\0' && - (len-- == 0 || - (begin[2] == '\0' && - (len-- == 0 || - (begin[3] == '\0' && - memcmp(begin, begin + 4, len) == 0)))))))); - else - /* Never create holes. */ - return 0; -} - -/* - * One 4-byte pointer per block and then the actual blocked - * output. The first block does not need an offset pointer, - * as it will start immediately after the pointer block; - * so the i'th pointer points to the end of the i'th block - * (i.e. the start of the (i+1)'th block or past EOF). - * - * Note that size > 0, as a zero-sized file wouldn't ever - * have gotten here in the first place. - */ -static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size) -{ - unsigned long original_size = size; - unsigned long original_offset = offset; - unsigned long new_size; - unsigned long blocks = (size - 1) / blksize + 1; - unsigned long curr = offset + 4 * blocks; - int change; - - total_blocks += blocks; - - do { - unsigned long len = 2 * blksize; - unsigned int input = size; - if (input > blksize) - input = blksize; - size -= input; - if (!is_zero (uncompressed, input)) { - compress(base + curr, &len, uncompressed, input); - curr += len; - } - uncompressed += input; - - if (len > blksize*2) { - /* (I don't think this can happen with zlib.) */ - printf("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len); - exit(8); - } - - *(u32 *) (base + offset) = curr; - offset += 4; - } while (size); - - curr = (curr + 3) & ~3; - new_size = curr - original_offset; - /* TODO: Arguably, original_size in these 2 lines should be - st_blocks * 512. But if you say that then perhaps - administrative data should also be included in both. */ - change = new_size - original_size; - printf("%6.2f%% (%+d bytes)\t%s\n", - (change * 100) / (double) original_size, change, name); - - return curr; -} - - -/* - * Traverse the entry tree, writing data for every item that has - * non-null entry->compressed (i.e. every symlink and non-empty - * regfile). - */ -static unsigned int write_data(struct entry *entry, char *base, unsigned int offset) -{ - do { - if (entry->uncompressed) { - if(entry->same) { - set_data_offset(entry, base, entry->same->offset); - entry->offset=entry->same->offset; - } else { - set_data_offset(entry, base, offset); - entry->offset=offset; - offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size); - } - } - else if (entry->child) - offset = write_data(entry->child, base, offset); - entry=entry->next; - } while (entry); - return offset; -} - -static unsigned int write_file(char *file, char *base, unsigned int offset) -{ - int fd; - char *buf; - - fd = open(file, O_RDONLY); - if (fd < 0) { - perror(file); - exit(8); - } - buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0); - memcpy(base + offset, buf, image_length); - munmap(buf, image_length); - close (fd); - /* Pad up the image_length to a 4-byte boundary */ - while (image_length & 3) { - *(base + offset + image_length) = '\0'; - image_length++; - } - return (offset + image_length); -} - -/* - * Maximum size fs you can create is roughly 256MB. (The last file's - * data must begin within 256MB boundary but can extend beyond that.) - * - * Note that if you want it to fit in a ROM then you're limited to what the - * hardware and kernel can support (64MB?). - */ -#define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \ - + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \ - + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ ) - - -/* - * Usage: - * - * mkcramfs directory-name outfile - * - * where "directory-name" is simply the root of the directory - * tree that we want to generate a compressed filesystem out - * of. - */ -int main(int argc, char **argv) -{ - struct stat st; /* used twice... */ - struct entry *root_entry; - char *rom_image; - ssize_t offset, written; - int fd; - /* initial guess (upper-bound) of required filesystem size */ - loff_t fslen_ub = sizeof(struct cramfs_super); - char const *dirname, *outfile; - u32 crc = crc32(0L, Z_NULL, 0); - int c; /* for getopt */ - - total_blocks = 0; - - if (argc) - progname = argv[0]; - - /* command line options */ - while ((c = getopt(argc, argv, "hEe:i:n:psz")) != EOF) { - switch (c) { - case 'h': - usage(0); - case 'E': - opt_errors = 1; - break; - case 'e': - opt_edition = atoi(optarg); - break; - case 'i': - opt_image = optarg; - if (lstat(opt_image, &st) < 0) { - perror(opt_image); - exit(16); - } - image_length = st.st_size; /* may be padded later */ - fslen_ub += (image_length + 3); /* 3 is for padding */ - break; - case 'n': - opt_name = optarg; - break; - case 'p': - opt_pad = PAD_SIZE; - fslen_ub += PAD_SIZE; - break; - case 's': - /* old option, ignored */ - break; - case 'z': - opt_holes = 1; - break; - } - } - - if ((argc - optind) != 2) - usage(16); - dirname = argv[optind]; - outfile = argv[optind + 1]; - - if (stat(dirname, &st) < 0) { - perror(dirname); - exit(16); - } - fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - root_entry = calloc(1, sizeof(struct entry)); - if (!root_entry) { - perror(NULL); - exit(8); - } - root_entry->mode = st.st_mode; - root_entry->uid = st.st_uid; - root_entry->gid = st.st_gid; - - root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub); - - /* always allocate a multiple of blksize bytes because that's - what we're going to write later on */ - fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1; - - if (fslen_ub > MAXFSLEN) { - fprintf(stderr, - "warning: guestimate of required size (upper bound) is %LdMB, but maximum image size is %uMB. We might die prematurely.\n", - fslen_ub >> 20, - MAXFSLEN >> 20); - fslen_ub = MAXFSLEN; - } - - /* find duplicate files. TODO: uses the most inefficient algorithm - possible. */ - eliminate_doubles(root_entry,root_entry); - - /* TODO: Why do we use a private/anonymous mapping here - followed by a write below, instead of just a shared mapping - and a couple of ftruncate calls? Is it just to save us - having to deal with removing the file afterwards? If we - really need this huge anonymous mapping, we ought to mmap - in smaller chunks, so that the user doesn't need nn MB of - RAM free. If the reason is to be able to write to - un-mmappable block devices, then we could try shared mmap - and revert to anonymous mmap if the shared mmap fails. */ - rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (-1 == (int) (long) rom_image) { - perror("ROM image map"); - exit(8); - } - - /* Skip the first opt_pad bytes for boot loader code */ - offset = opt_pad; - memset(rom_image, 0x00, opt_pad); - - /* Skip the superblock and come back to write it later. */ - offset += sizeof(struct cramfs_super); - - /* Insert a file image. */ - if (opt_image) { - printf("Including: %s\n", opt_image); - offset = write_file(opt_image, rom_image, offset); - } - - offset = write_directory_structure(root_entry->child, rom_image, offset); - printf("Directory data: %d bytes\n", offset); - - offset = write_data(root_entry, rom_image, offset); - - /* We always write a multiple of blksize bytes, so that - losetup works. */ - offset = ((offset - 1) | (blksize - 1)) + 1; - printf("Everything: %d kilobytes\n", offset >> 10); - - /* Write the superblock now that we can fill in all of the fields. */ - write_superblock(root_entry, rom_image+opt_pad, offset); - printf("Super block: %d bytes\n", sizeof(struct cramfs_super)); - - /* Put the checksum in. */ - crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad)); - ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc; - printf("CRC: %x\n", crc); - - /* Check to make sure we allocated enough space. */ - if (fslen_ub < offset) { - fprintf(stderr, "not enough space allocated for ROM image (%Ld allocated, %d used)\n", - fslen_ub, offset); - exit(8); - } - - written = write(fd, rom_image, offset); - if (written < 0) { - perror("ROM image"); - exit(8); - } - if (offset != written) { - fprintf(stderr, "ROM image write failed (%d %d)\n", written, offset); - exit(8); - } - - /* (These warnings used to come at the start, but they scroll off the - screen too quickly.) */ - if (warn_namelen) /* (can't happen when reading from ext2fs) */ - fprintf(stderr, /* bytes, not chars: think UTF8. */ - "warning: filenames truncated to 255 bytes.\n"); - if (warn_skip) - fprintf(stderr, "warning: files were skipped due to errors.\n"); - if (warn_size) - fprintf(stderr, - "warning: file sizes truncated to %luMB (minus 1 byte).\n", - 1L << (CRAMFS_SIZE_WIDTH - 20)); - if (warn_uid) /* (not possible with current Linux versions) */ - fprintf(stderr, - "warning: uids truncated to %u bits. (This may be a security concern.)\n", - CRAMFS_UID_WIDTH); - if (warn_gid) - fprintf(stderr, - "warning: gids truncated to %u bits. (This may be a security concern.)\n", - CRAMFS_GID_WIDTH); - if (warn_dev) - fprintf(stderr, - "WARNING: device numbers truncated to %u bits. This almost certainly means\n" - "that some device files will be wrong.\n", - CRAMFS_OFFSET_WIDTH); - if (opt_errors && - (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev)) - exit(8); - return 0; -} diff -urN linux-2.5.6-pre3/sound/Config.help linux-2.5.6/sound/Config.help --- linux-2.5.6-pre3/sound/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/Config.help Thu Mar 7 18:24:56 2002 @@ -0,0 +1,7 @@ +CONFIG_SOUND_PRIME + Say 'Y' or 'M' to enable Open Sound System drivers. + +CONFIG_SOUND_SND + Say 'Y' or 'M' to enable Advanced Linux Sound Architecture (ALSA) drivers. + You need to install also alsa-lib and alsa-utils packages available on + the ALSA website at . diff -urN linux-2.5.6-pre3/sound/core/Config.help linux-2.5.6/sound/core/Config.help --- linux-2.5.6-pre3/sound/core/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/Config.help Thu Mar 7 18:24:56 2002 @@ -0,0 +1,40 @@ +CONFIG_SND_SEQUENCER + Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature + allows routing and enqueing MIDI events. Events can be processed at given + time. + +CONFIG_SND_SEQ_DUMMY + Say 'Y' or 'M' to enable dummy sequencer client. This client is a simple + midi-through client. All normal input events are redirected to output port + immediately. + +CONFIG_SND_OSSEMUL + Say 'Y' to enable OSS (Open Sound System) API emulation code. + +CONFIG_SND_MIXER_OSS + Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). + +CONFIG_SND_PCM_OSS + Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). + +CONFIG_SND_SEQUENCER_OSS + Say 'Y' or 'M' to enable OSS sequencer emulation (both /dev/sequencer and + /dev/music interfaces). + +CONFIG_SND_RTCTIMER + Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC + timer as precise timing source and maps the RTC timer to the ALSA's timer + interface. ALSA sequencer code can also use this timing source. + +CONFIG_SND_VERBOSE_PRINTK + Say 'Y' to enable verbose log messages. These messages will help to + identify source file and position containing printed messages. + +CONFIG_SND_DEBUG + Say 'Y' to enable ALSA debug code. + +CONFIG_SND_DEBUG_MEMORY + Say 'Y' to enable debugging of memory allocation. + +CONFIG_SND_DEBUG_DETECTION + Say 'Y' to enable debugging of hardware detection. diff -urN linux-2.5.6-pre3/sound/core/Config.in linux-2.5.6/sound/core/Config.in --- linux-2.5.6-pre3/sound/core/Config.in Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/Config.in Thu Mar 7 18:24:56 2002 @@ -1,5 +1,14 @@ # ALSA soundcard-configuration +if [ "$CONFIG_X86_64" = "y" -a "$CONFIG_IA32_EMULATION" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_PPC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_SPARC64" = "y" ]; then + dep_tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi dep_tristate ' Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then dep_tristate ' Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER diff -urN linux-2.5.6-pre3/sound/core/Makefile linux-2.5.6/sound/core/Makefile --- linux-2.5.6-pre3/sound/core/Makefile Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/Makefile Thu Mar 7 18:24:56 2002 @@ -45,6 +45,11 @@ obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o +subdir-$(CONFIG_SND_BIT32_EMUL) += ioctl32 +ifeq ($(CONFIG_SND_BIT32_EMUL),y) + obj-y += ioctl32/_ioctl32.o +endif + # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o @@ -72,8 +77,8 @@ obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o -obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o @@ -85,7 +90,7 @@ obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o @@ -100,6 +105,10 @@ obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-hwdep.o + obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o +endif include $(TOPDIR)/Rules.make diff -urN linux-2.5.6-pre3/sound/core/info.c linux-2.5.6/sound/core/info.c --- linux-2.5.6-pre3/sound/core/info.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/info.c Thu Mar 7 18:24:56 2002 @@ -24,12 +24,12 @@ #include #include #include +#include #include #include #include #include #include -#include #ifdef CONFIG_DEVFS_FS #include #endif @@ -55,7 +55,7 @@ "memdebug", "detect", "devices", - "oss-devices", + "oss", "cards", "timers", "synth", @@ -124,6 +124,9 @@ struct proc_dir_entry *snd_proc_root = NULL; struct proc_dir_entry *snd_proc_dev = NULL; snd_info_entry_t *snd_seq_root = NULL; +#ifdef CONFIG_SND_OSSEMUL +snd_info_entry_t *snd_oss_root = NULL; +#endif #ifdef LINUX_2_2 static void snd_info_fill_inode(struct inode *inode, int fill) @@ -163,11 +166,13 @@ { snd_info_private_data_t *data; struct snd_info_entry *entry; - int ret = -EINVAL; + loff_t ret; data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); entry = data->entry; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) lock_kernel(); +#endif switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: switch (orig) { @@ -181,6 +186,7 @@ goto out; case 2: /* SEEK_END */ default: + ret = -EINVAL; goto out; } break; @@ -195,7 +201,9 @@ } ret = -ENXIO; out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) unlock_kernel(); +#endif return ret; } @@ -623,6 +631,19 @@ if (p == NULL) return -ENOMEM; snd_proc_dev = p; +#ifdef CONFIG_SND_OSSEMUL + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_oss_root = entry; + } +#endif #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) { snd_info_entry_t *entry; @@ -664,6 +685,10 @@ if (snd_seq_root) snd_info_unregister(snd_seq_root); #endif +#ifdef CONFIG_SND_OSSEMUL + if (snd_oss_root) + snd_info_unregister(snd_oss_root); +#endif snd_remove_proc_entry(snd_proc_root, snd_proc_dev); snd_remove_proc_entry(&proc_root, snd_proc_root); } diff -urN linux-2.5.6-pre3/sound/core/info_oss.c linux-2.5.6/sound/core/info_oss.c --- linux-2.5.6-pre3/sound/core/info_oss.c Tue Feb 19 18:10:56 2002 +++ linux-2.5.6/sound/core/info_oss.c Thu Mar 7 18:24:56 2002 @@ -114,7 +114,7 @@ snd_info_entry_t *entry; memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); - if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", NULL)) != NULL) { + if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->c.text.read_size = 2048; entry->c.text.read = snd_sndstat_proc_read; diff -urN linux-2.5.6-pre3/sound/core/ioctl32/Makefile linux-2.5.6/sound/core/ioctl32/Makefile --- linux-2.5.6-pre3/sound/core/ioctl32/Makefile Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/Makefile Thu Mar 7 18:24:56 2002 @@ -0,0 +1,17 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := _ioctl32.o + +list-multi := snd-ioctl32.o + +snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o + +obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o + +include $(TOPDIR)/Rules.make + +snd-ioctl32.o: $(snd-ioctl32-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ioctl32-objs) diff -urN linux-2.5.6-pre3/sound/core/ioctl32/hwdep32.c linux-2.5.6/sound/core/ioctl32/hwdep32.c --- linux-2.5.6-pre3/sound/core/ioctl32/hwdep32.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/hwdep32.c Thu Mar 7 18:24:56 2002 @@ -0,0 +1,37 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper hwdep_mappers[] = { + { SNDRV_HWDEP_IOCTL_PVERSION, NULL }, + { SNDRV_HWDEP_IOCTL_INFO, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_INFO, NULL }, + { 0 }, +}; diff -urN linux-2.5.6-pre3/sound/core/ioctl32/ioctl32.c linux-2.5.6/sound/core/ioctl32/ioctl32.c --- linux-2.5.6-pre3/sound/core/ioctl32/ioctl32.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/ioctl32.c Thu Mar 7 18:24:56 2002 @@ -0,0 +1,357 @@ +/* + * 32bit -> 64bit ioctl wrapper for control API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +/* + * register/unregister mappers + * exported for other modules + */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); + + +int snd_ioctl32_register(struct ioctl32_mapper *mappers) +{ + int err; + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + err = register_ioctl32_conversion(m->cmd, m->handler); + if (err < 0) { + unlock_kernel(); + return err; + } + m->registered++; + } + return 0; +} + +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) +{ + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + if (m->registered) { + unregister_ioctl32_conversion(m->cmd); + m->registered = 0; + } + } + unlock_kernel(); +} + + +/* + * Controls + */ + +struct sndrv_ctl_elem_list32 { + u32 offset; + u32 space; + u32 used; + u32 count; + u32 pids; + unsigned char reserved[50]; +}; + +#define CVT_sndrv_ctl_elem_list()\ +{\ + COPY(offset);\ + COPY(space);\ + COPY(used);\ + COPY(count);\ + CPTR(pids);\ +} + +DEFINE_ALSA_IOCTL(ctl_elem_list); + + +/* + * control element info + * it uses union, so the things are not easy.. + */ + +struct sndrv_ctl_elem_info32 { + struct sndrv_ctl_elem_id id; // the size of struct is same + s32 type; + u32 access; + u32 count; + s32 owner; + union { + struct { + s32 min; + s32 max; + s32 step; + } integer; + struct { + u32 items; + u32 item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +static int snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + struct sndrv_ctl_elem_info data; + struct sndrv_ctl_elem_info32 data32; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + data32.type = data.type; + data32.access = data.access; + data32.count = data.count; + data32.owner = data.owner; + switch (data.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + data32.value.integer.min = data.value.integer.min; + data32.value.integer.max = data.value.integer.min; + data32.value.integer.step = data.value.integer.step; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + data32.value.enumerated.items = data.value.enumerated.items; + data32.value.enumerated.item = data.value.enumerated.item; + memcpy(data32.value.enumerated.name, data.value.enumerated.name, + sizeof(data.value.enumerated.name)); + break; + default: + break; + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +struct sndrv_ctl_elem_value32 { + struct sndrv_ctl_elem_id id; + unsigned int indirect: 1; + union { + union { + s32 value[128]; + u32 value_ptr; + } integer; + union { + u32 item[128]; + u32 item_ptr; + } enumerated; + union { + unsigned char data[512]; + u32 data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; + unsigned char reserved[128]; +}; + + +/* hmm, it's so hard to retrieve the value type from the control id.. */ +static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) +{ + snd_ctl_file_t *ctl; + snd_kcontrol_t *kctl; + snd_ctl_elem_info_t info; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + + read_lock(&ctl->card->control_rwlock); + kctl = snd_ctl_find_id(ctl->card, id); + if (! kctl) { + read_unlock(&ctl->card->control_rwlock); + return -ENXIO; + } + info.id = *id; + err = kctl->info(kctl, &info); + if (err >= 0) + err = info.type; + read_unlock(&ctl->card->control_rwlock); + return err; +} + + +static int snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + // too big? + struct sndrv_ctl_elem_value data; + struct sndrv_ctl_elem_value32 data32; + int err, i; + int type; + /* FIXME: check the sane ioctl.. */ + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + data.indirect = data32.indirect; + if (data.indirect) /* FIXME: this is not correct for long arrays */ + data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr); + type = get_ctl_type(file, &data.id); + if (type < 0) + return type; + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +static struct ioctl32_mapper control_mappers[] = { + /* controls (without rawmidi, hwdep, timer releated ones) */ + { SNDRV_CTL_IOCTL_PVERSION, NULL }, + { SNDRV_CTL_IOCTL_CARD_INFO , NULL }, + { SNDRV_CTL_IOCTL_ELEM_LIST, AP(ctl_elem_list) }, + { SNDRV_CTL_IOCTL_ELEM_INFO, AP(ctl_elem_info) }, + { SNDRV_CTL_IOCTL_ELEM_READ, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_WRITE, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_LOCK, NULL }, + { SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL }, + { SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + { SNDRV_CTL_IOCTL_POWER, NULL }, + { SNDRV_CTL_IOCTL_POWER_STATE, NULL }, + { 0 } +}; + + +/* + */ + +extern struct ioctl32_mapper pcm_mappers[]; +extern struct ioctl32_mapper rawmidi_mappers[]; +extern struct ioctl32_mapper timer_mappers[]; +extern struct ioctl32_mapper hwdep_mappers[]; + +static void snd_ioctl32_done(void) +{ + snd_ioctl32_unregister(hwdep_mappers); + snd_ioctl32_unregister(timer_mappers); + snd_ioctl32_unregister(rawmidi_mappers); + snd_ioctl32_unregister(pcm_mappers); + snd_ioctl32_unregister(control_mappers); +} + +static int __init snd_ioctl32_init(void) +{ + int err; + + err = snd_ioctl32_register(control_mappers); + if (err < 0) + return err; + err = snd_ioctl32_register(pcm_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(rawmidi_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(timer_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(hwdep_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } +} + +module_init(snd_ioctl32_init) +module_exit(snd_ioctl32_done) diff -urN linux-2.5.6-pre3/sound/core/ioctl32/ioctl32.h linux-2.5.6/sound/core/ioctl32/ioctl32.h --- linux-2.5.6-pre3/sound/core/ioctl32/ioctl32.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/ioctl32.h Thu Mar 7 18:24:56 2002 @@ -0,0 +1,79 @@ +/* + * 32bit -> 64bit ioctl helpers + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This file registers the converters from 32-bit ioctls to 64-bit ones. + * The converter assumes that a 32-bit user-pointer can be casted by A(x) + * macro to a valid 64-bit pointer which is accessible via copy_from/to_user. + * + */ + +#ifndef __ALSA_IOCTL32_H +#define __ALSA_IOCTL32_H + +#define TO_PTR(x) A(x) + +#define COPY(x) (dst->x = src->x) +#define CPTR(x) (dst->x = (typeof(dst->x))A(src->x)) + +#define convert_from_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *dst = dstp;\ + struct sndrv_##type##32 *src = srcp;\ + CVT_##sndrv_##type();\ +} + +#define convert_to_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *src = srcp;\ + struct sndrv_##type##32 *dst = dstp;\ + CVT_##sndrv_##type();\ +} + + +#define DEFINE_ALSA_IOCTL(type) \ +static int snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)\ +{\ + struct sndrv_##type##32 data32;\ + struct sndrv_##type data;\ + int err;\ + if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\ + return -EFAULT;\ + memset(&data, 0, sizeof(data));\ + convert_from_32(type, &data, &data32);\ + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);\ + if (err < 0)\ + return err;\ + if (cmd & (_IOC_READ << _IOC_DIRSHIFT)) {\ + convert_to_32(type, &data32, &data);\ + if (copy_to_user((void*)arg, &data32, sizeof(data32)))\ + return -EFAULT;\ + }\ + return err;\ +} + +struct ioctl32_mapper { + unsigned int cmd; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + int registered; +}; + +int snd_ioctl32_register(struct ioctl32_mapper *mappers); +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers); + +#endif /* __ALSA_IOCTL32_H */ diff -urN linux-2.5.6-pre3/sound/core/ioctl32/pcm32.c linux-2.5.6/sound/core/ioctl32/pcm32.c --- linux-2.5.6-pre3/sound/core/ioctl32/pcm32.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/pcm32.c Thu Mar 7 18:24:56 2002 @@ -0,0 +1,296 @@ +/* + * 32bit -> 64bit ioctl wrapper for PCM API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "ioctl32.h" + + +/* wrapper for sndrv_pcm_[us]frames */ +struct sndrv_pcm_sframes_str { + sndrv_pcm_sframes_t val; +}; +struct sndrv_pcm_sframes_str32 { + s32 val; +}; +struct sndrv_pcm_uframes_str { + sndrv_pcm_uframes_t val; +}; +struct sndrv_pcm_uframes_str32 { + u32 val; +}; + +#define CVT_sndrv_pcm_sframes_str() { COPY(val); } +#define CVT_sndrv_pcm_uframes_str() { COPY(val); } + + +struct sndrv_interval32 { + u32 min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params32 { + u32 flags; + u32 masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +}; + +#define numberof(array) (sizeof(array)/sizeof(array[0])) + +#define CVT_sndrv_pcm_hw_params()\ +{\ + int i;\ + COPY(flags);\ + for (i = 0; i < numberof(dst->masks); i++)\ + COPY(masks[i]);\ + for (i = 0; i < numberof(dst->intervals); i++) {\ + COPY(intervals[i].min);\ + COPY(intervals[i].max);\ + COPY(intervals[i].openmin);\ + COPY(intervals[i].openmax);\ + COPY(intervals[i].integer);\ + COPY(intervals[i].empty);\ + }\ + COPY(rmask);\ + COPY(cmask);\ + COPY(info);\ + COPY(msbits);\ + COPY(rate_num);\ + COPY(rate_den);\ + COPY(fifo_size);\ +} + +struct sndrv_pcm_sw_params32 { + s32 tstamp_mode; + u32 period_step; + u32 sleep_min; + u32 avail_min; + u32 xfer_align; + u32 start_threshold; + u32 stop_threshold; + u32 silence_threshold; + u32 silence_size; + u32 boundary; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_pcm_sw_params()\ +{\ + COPY(tstamp_mode);\ + COPY(period_step);\ + COPY(sleep_min);\ + COPY(avail_min);\ + COPY(xfer_align);\ + COPY(start_threshold);\ + COPY(stop_threshold);\ + COPY(silence_threshold);\ + COPY(silence_size);\ + COPY(boundary);\ +} + +struct sndrv_pcm_channel_info32 { + u32 channel; + u32 offset; + u32 first; + u32 step; +}; + +#define CVT_sndrv_pcm_channel_info()\ +{\ + COPY(channel);\ + COPY(offset);\ + COPY(first);\ + COPY(step);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_pcm_status32 { + s32 state; + struct timeval32 trigger_tstamp; + struct timeval32 tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + unsigned char reserved[60]; +}; + +#define CVT_sndrv_pcm_status()\ +{\ + COPY(state);\ + COPY(trigger_tstamp.tv_sec);\ + COPY(trigger_tstamp.tv_usec);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(appl_ptr);\ + COPY(hw_ptr);\ + COPY(delay);\ + COPY(avail);\ + COPY(avail_max);\ + COPY(overrange);\ + COPY(suspended_state);\ +} + +struct sndrv_xferi32 { + s32 result; + u32 buf; + u32 frames; +}; + +#define CVT_sndrv_xferi()\ +{\ + COPY(result);\ + CPTR(buf);\ + COPY(frames);\ +} + +DEFINE_ALSA_IOCTL(pcm_uframes_str); +DEFINE_ALSA_IOCTL(pcm_sframes_str); +DEFINE_ALSA_IOCTL(pcm_hw_params); +DEFINE_ALSA_IOCTL(pcm_sw_params); +DEFINE_ALSA_IOCTL(pcm_channel_info); +DEFINE_ALSA_IOCTL(pcm_status); +DEFINE_ALSA_IOCTL(xferi); + +/* snd_xfern needs remapping of bufs */ +struct sndrv_xfern32 { + s32 result; + u32 bufs; /* this is void **; */ + u32 frames; +}; + +/* + * xfern ioctl nees to copy (up to) 128 pointers on stack. + * although we may pass the copied pointers through f_op->ioctl, but the ioctl + * handler there expands again the same 128 pointers on stack, so it is better + * to handle the function (calling pcm_readv/writev) directly in this handler. + */ +static int snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + struct sndrv_xfern32 data32, *srcptr = (struct sndrv_xfern32*)arg; + void *bufs[128]; + int err = 0, ch, i; + u32 *bufptr; + + /* FIXME: need to check whether fop->ioctl is sane */ + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL && substream->runtime, return -ENXIO); + + /* check validty of the command */ + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + case SNDRV_PCM_IOCTL_READN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + break; + } + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(data32.frames, &srcptr->frames)) + return -EFAULT; + __get_user(data32.bufs, &srcptr->bufs); + bufptr = (u32*)TO_PTR(data32.bufs); + for (i = 0; i < ch; i++) { + u32 ptr; + if (get_user(ptr, bufptr)) + return -EFAULT; + bufs[ch] = (void*)TO_PTR(ptr); + bufptr++; + } + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + err = snd_pcm_lib_writev(substream, bufs, data32.frames); + break; + case SNDRV_PCM_IOCTL_READN_FRAMES: + err = snd_pcm_lib_readv(substream, bufs, data32.frames); + break; + } + + if (err < 0) + return err; + if (put_user(err, &srcptr->result)) + return -EFAULT; + return err < 0 ? err : 0; +} + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper pcm_mappers[] = { + { SNDRV_PCM_IOCTL_PVERSION, NULL }, + { SNDRV_PCM_IOCTL_INFO, NULL }, + { SNDRV_PCM_IOCTL_HW_REFINE, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_PARAMS, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_FREE, NULL }, + { SNDRV_PCM_IOCTL_SW_PARAMS, AP(pcm_sw_params) }, + { SNDRV_PCM_IOCTL_STATUS, AP(pcm_status) }, + { SNDRV_PCM_IOCTL_DELAY, AP(pcm_sframes_str) }, + { SNDRV_PCM_IOCTL_CHANNEL_INFO, AP(pcm_channel_info) }, + { SNDRV_PCM_IOCTL_PREPARE, NULL }, + { SNDRV_PCM_IOCTL_RESET, NULL }, + { SNDRV_PCM_IOCTL_START, NULL }, + { SNDRV_PCM_IOCTL_DROP, NULL }, + { SNDRV_PCM_IOCTL_DRAIN, NULL }, + { SNDRV_PCM_IOCTL_PAUSE, NULL }, + { SNDRV_PCM_IOCTL_REWIND, AP(pcm_uframes_str) }, + { SNDRV_PCM_IOCTL_RESUME, NULL }, + { SNDRV_PCM_IOCTL_XRUN, NULL }, + { SNDRV_PCM_IOCTL_WRITEI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_READI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_WRITEN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_READN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_LINK, NULL }, + { SNDRV_PCM_IOCTL_UNLINK, NULL }, + + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -urN linux-2.5.6-pre3/sound/core/ioctl32/rawmidi32.c linux-2.5.6/sound/core/ioctl32/rawmidi32.c --- linux-2.5.6-pre3/sound/core/ioctl32/rawmidi32.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/rawmidi32.c Thu Mar 7 18:24:56 2002 @@ -0,0 +1,86 @@ +/* + * 32bit -> 64bit ioctl wrapper for raw MIDI API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_rawmidi_params32 { + s32 stream; + u32 buffer_size; + u32 avail_min; + unsigned int no_active_sensing: 1; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_params()\ +{\ + COPY(stream);\ + COPY(buffer_size);\ + COPY(avail_min);\ + COPY(no_active_sensing);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_rawmidi_status32 { + s32 stream; + struct timeval32 tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_status()\ +{\ + COPY(stream);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(avail);\ + COPY(xruns);\ +} + +DEFINE_ALSA_IOCTL(rawmidi_params); +DEFINE_ALSA_IOCTL(rawmidi_status); + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper rawmidi_mappers[] = { + { SNDRV_RAWMIDI_IOCTL_PVERSION, NULL }, + { SNDRV_RAWMIDI_IOCTL_INFO, NULL }, + { SNDRV_RAWMIDI_IOCTL_PARAMS, AP(rawmidi_params) }, + { SNDRV_RAWMIDI_IOCTL_STATUS, AP(rawmidi_status) }, + { SNDRV_RAWMIDI_IOCTL_DROP, NULL }, + { SNDRV_RAWMIDI_IOCTL_DRAIN, NULL }, + + { SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_INFO, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -urN linux-2.5.6-pre3/sound/core/ioctl32/timer32.c linux-2.5.6/sound/core/ioctl32/timer32.c --- linux-2.5.6-pre3/sound/core/ioctl32/timer32.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/core/ioctl32/timer32.c Thu Mar 7 18:24:56 2002 @@ -0,0 +1,93 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_timer_info32 { + u32 flags; + s32 card; + unsigned char id[64]; + unsigned char name[80]; + u32 ticks; + u32 resolution; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_info()\ +{\ + COPY(flags);\ + COPY(card);\ + memcpy(dst->id, src->id, sizeof(src->id));\ + memcpy(dst->name, src->name, sizeof(src->name));\ + COPY(ticks);\ + COPY(resolution);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_timer_status32 { + struct timeval32 tstamp; + u32 resolution; + u32 lost; + u32 overrun; + u32 queue; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_status()\ +{\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(resolution);\ + COPY(lost);\ + COPY(overrun);\ + COPY(queue);\ +} + +DEFINE_ALSA_IOCTL(timer_info); +DEFINE_ALSA_IOCTL(timer_status); + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper timer_mappers[] = { + { SNDRV_TIMER_IOCTL_PVERSION, NULL }, + { SNDRV_TIMER_IOCTL_NEXT_DEVICE, NULL }, + { SNDRV_TIMER_IOCTL_SELECT, NULL }, + { SNDRV_TIMER_IOCTL_INFO, AP(timer_info) }, + { SNDRV_TIMER_IOCTL_PARAMS, NULL }, + { SNDRV_TIMER_IOCTL_STATUS, AP(timer_status) }, + { SNDRV_TIMER_IOCTL_START, NULL }, + { SNDRV_TIMER_IOCTL_STOP, NULL }, + { SNDRV_TIMER_IOCTL_CONTINUE, NULL }, + { 0 }, +}; diff -urN linux-2.5.6-pre3/sound/core/oss/pcm_oss.c linux-2.5.6/sound/core/oss/pcm_oss.c --- linux-2.5.6-pre3/sound/core/oss/pcm_oss.c Tue Feb 19 18:10:59 2002 +++ linux-2.5.6/sound/core/oss/pcm_oss.c Thu Mar 7 18:24:56 2002 @@ -516,7 +516,6 @@ snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -525,11 +524,14 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, ptr, frames); - if (in_kernel) + ret = snd_pcm_lib_write(substream, ptr, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_write(substream, ptr, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ @@ -543,7 +545,6 @@ snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -556,11 +557,15 @@ if (ret < 0) break; } - if (in_kernel) - fs = snd_enter_user(); ret = snd_pcm_lib_read(substream, ptr, frames); - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_read(substream, ptr, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_read(substream, ptr, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; } @@ -570,7 +575,6 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -579,13 +583,17 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_writev(substream, bufs, frames); - if (in_kernel) + ret = snd_pcm_lib_writev(substream, bufs, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_writev(substream, bufs, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; + /* test, if we can't store new data, because the stream */ /* has not been started */ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) @@ -597,7 +605,6 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) { snd_pcm_runtime_t *runtime = substream->runtime; - mm_segment_t fs; int ret; while (1) { if (runtime->status->state == SNDRV_PCM_STATE_XRUN || @@ -610,11 +617,14 @@ if (ret < 0) break; } - if (in_kernel) + if (in_kernel) { + mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_readv(substream, bufs, frames); - if (in_kernel) + ret = snd_pcm_lib_readv(substream, bufs, frames); snd_leave_user(fs); + } else { + ret = snd_pcm_lib_readv(substream, bufs, frames); + } if (ret != -EPIPE && ret != -ESTRPIPE) break; } diff -urN linux-2.5.6-pre3/sound/core/rtctimer.c linux-2.5.6/sound/core/rtctimer.c --- linux-2.5.6-pre3/sound/core/rtctimer.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/rtctimer.c Thu Mar 7 18:24:56 2002 @@ -18,19 +18,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * - *================================================================ - * For enabling this timer, apply the patch file to your kernel. - * The configure script checks the patch automatically. - * The patches, rtc-xxx.dif, are found under utils/patches, where - * xxx is the kernel version. - *================================================================ - * */ #include #include #include +#include #include #include #include @@ -59,7 +52,7 @@ /* - * The harware depenant description for this timer. + * The hardware dependent description for this timer. */ static struct _snd_timer_hardware rtc_hw = { flags: SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, @@ -72,7 +65,7 @@ int rtctimer_freq = RTC_FREQ; /* frequency */ static snd_timer_t *rtctimer; -static volatile int rtc_inc = 0; +static atomic_t rtc_inc = ATOMIC_INIT(0); static rtc_task_t rtc_task; /* tasklet */ @@ -83,6 +76,10 @@ static int rtctimer_open(snd_timer_t *t) { + err = rtc_register(&rtc_task); + if (err < 0) + return err; + t->private_data = &rtc_task; MOD_INC_USE_COUNT; return 0; } @@ -90,6 +87,11 @@ static int rtctimer_close(snd_timer_t *t) { + rtc_task_t *rtc = t->private_data; + if (rtc) { + rtc_unregister(rtc); + t->private_data = NULL; + } MOD_DEC_USE_COUNT; return 0; } @@ -101,7 +103,7 @@ snd_assert(rtc != NULL, return -EINVAL); rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); rtc_control(rtc, RTC_PIE_ON, 0); - rtc_inc = 0; + atomic_set(&rtc_inc, 0); return 0; } @@ -119,12 +121,15 @@ */ static void rtctimer_interrupt(void *private_data) { - rtc_inc++; + atomic_inc(&rtc_inc); #ifdef USE_TASKLET tasklet_hi_schedule(&rtc_tq); #else - snd_timer_interrupt((snd_timer_t*)private_data, rtc_inc); - rtc_inc = 0; + { + int ticks = atomic_read(&rtc_inc); + snd_timer_interrupt((snd_timer_t*)private_data, ticks); + atomic_sub(ticks, &rtc_inc); + } #endif /* USE_TASKLET */ } @@ -132,20 +137,16 @@ static void rtctimer_interrupt2(unsigned long private_data) { snd_timer_t *timer = (snd_timer_t *)private_data; + int ticks; + snd_assert(timer != NULL, return); do { - snd_timer_interrupt(timer, 1); - } while (--rtc_inc > 0); + ticks = atomic_read(&rtc_inc); + snd_timer_interrupt(timer, ticks); + } while (!atomic_sub_and_test(ticks, &rtc_inc)); } #endif /* USE_TASKLET */ -static void rtctimer_private_free(snd_timer_t *timer) -{ - rtc_task_t *rtc = timer->private_data; - if (rtc) - rtc_unregister(rtc); -} - /* * ENTRY functions @@ -179,23 +180,16 @@ timer->hw = rtc_hw; timer->hw.resolution = NANO_SEC / rtctimer_freq; - /* register RTC callback */ + /* set up RTC callback */ rtc_task.func = rtctimer_interrupt; rtc_task.private_data = timer; - err = rtc_register(&rtc_task); - if (err < 0) { - snd_timer_global_free(timer); - return err; - } - timer->private_data = &rtc_task; - timer->private_free = rtctimer_private_free; err = snd_timer_global_register(timer); if (err < 0) { snd_timer_global_free(timer); return err; } - rtctimer = timer; + rtctimer = timer; /* remember this */ return 0; } @@ -210,7 +204,7 @@ /* - * exported stuffs + * exported stuff */ module_init(rtctimer_init) module_exit(rtctimer_exit) diff -urN linux-2.5.6-pre3/sound/core/seq/Makefile linux-2.5.6/sound/core/seq/Makefile --- linux-2.5.6-pre3/sound/core/seq/Makefile Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/seq/Makefile Thu Mar 7 18:24:56 2002 @@ -72,6 +72,7 @@ obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o diff -urN linux-2.5.6-pre3/sound/core/seq/seq_midi_event.c linux-2.5.6/sound/core/seq/seq_midi_event.c --- linux-2.5.6-pre3/sound/core/seq/seq_midi_event.c Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/sound/core/seq/seq_midi_event.c Thu Mar 7 18:24:56 2002 @@ -68,8 +68,8 @@ event_decode_t decode; } status_event[] = { /* 0x80 - 0xf0 */ - {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, - {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, @@ -78,9 +78,9 @@ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ /* 0xf0 - 0xff */ {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ - {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ - {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ - {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ @@ -92,7 +92,7 @@ {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ - {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ }; static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); @@ -128,6 +128,7 @@ } } dev->bufsize = bufsize; + dev->lastcmd = 0xff; spin_lock_init(&dev->lock); *rdev = dev; return 0; diff -urN linux-2.5.6-pre3/sound/core/seq/seq_virmidi.c linux-2.5.6/sound/core/seq/seq_virmidi.c --- linux-2.5.6-pre3/sound/core/seq/seq_virmidi.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/core/seq/seq_virmidi.c Thu Mar 7 18:24:56 2002 @@ -233,8 +233,7 @@ } vmidi->seq_mode = rdev->seq_mode; vmidi->client = rdev->client; - vmidi->port = rdev->port; - snd_midi_event_init(vmidi->parser); + vmidi->port = rdev->port; snd_virmidi_init_event(vmidi, &vmidi->event); vmidi->rdev = rdev; runtime->private_data = vmidi; diff -urN linux-2.5.6-pre3/sound/core/sound_oss.c linux-2.5.6/sound/core/sound_oss.c --- linux-2.5.6-pre3/sound/core/sound_oss.c Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/sound/core/sound_oss.c Thu Mar 7 18:24:56 2002 @@ -212,7 +212,7 @@ { snd_info_entry_t *entry; - entry = snd_info_create_module_entry(THIS_MODULE, "oss-devices", NULL); + entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); if (entry) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->c.text.read_size = PAGE_SIZE; diff -urN linux-2.5.6-pre3/sound/drivers/Config.help linux-2.5.6/sound/drivers/Config.help --- linux-2.5.6-pre3/sound/drivers/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/drivers/Config.help Thu Mar 7 18:24:56 2002 @@ -0,0 +1,18 @@ +CONFIG_SND_DUMMY + Say 'Y' or 'M' to include dummy driver. This driver does nothing, but + emulates various mixer controls and PCM devices. + +CONFIG_SND_VIRMIDI + Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to + connect applications using raw MIDI devices to sequencer. + +CONFIG_SND_MTPAV + Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport + MIDI adapter. + +CONFIG_SND_SERIAL_U16550 + Say 'Y' or 'M' to include support for MIDI serial port driver. It works + with serial UARTs 16550 and better. + +CONFIG_SND_MPU401 + Say 'Y' or 'M' to include support for MPU401 hardware using UART access. diff -urN linux-2.5.6-pre3/sound/drivers/mpu401/Makefile linux-2.5.6/sound/drivers/mpu401/Makefile --- linux-2.5.6-pre3/sound/drivers/mpu401/Makefile Tue Feb 19 18:11:05 2002 +++ linux-2.5.6/sound/drivers/mpu401/Makefile Thu Mar 7 18:24:56 2002 @@ -37,6 +37,7 @@ obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o +obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o diff -urN linux-2.5.6-pre3/sound/drivers/mpu401/mpu401_uart.c linux-2.5.6/sound/drivers/mpu401/mpu401_uart.c --- linux-2.5.6-pre3/sound/drivers/mpu401/mpu401_uart.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/sound/drivers/mpu401/mpu401_uart.c Thu Mar 7 18:24:56 2002 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff -urN linux-2.5.6-pre3/sound/drivers/mtpav.c linux-2.5.6/sound/drivers/mtpav.c --- linux-2.5.6-pre3/sound/drivers/mtpav.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/drivers/mtpav.c Thu Mar 7 18:24:56 2002 @@ -54,6 +54,7 @@ #include #include #include +#include #include #define SNDRV_GET_ID #include diff -urN linux-2.5.6-pre3/sound/drivers/opl3/Makefile linux-2.5.6/sound/drivers/opl3/Makefile --- linux-2.5.6-pre3/sound/drivers/opl3/Makefile Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/sound/drivers/opl3/Makefile Thu Mar 7 18:24:56 2002 @@ -29,7 +29,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o -obj-$(CONFIG_SND_SB) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB8) += snd-opl3-lib.o obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o @@ -54,7 +54,7 @@ obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o - obj-$(CONFIG_SND_SB) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB8) += snd-opl3-synth.o obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o diff -urN linux-2.5.6-pre3/sound/drivers/opl3/opl3_lib.c linux-2.5.6/sound/drivers/opl3/opl3_lib.c --- linux-2.5.6-pre3/sound/drivers/opl3/opl3_lib.c Tue Feb 19 18:10:57 2002 +++ linux-2.5.6/sound/drivers/opl3/opl3_lib.c Thu Mar 7 18:24:56 2002 @@ -28,6 +28,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Jaroslav Kysela , Hannu Savolainen 1993-1996, Rob Hooft"); diff -urN linux-2.5.6-pre3/sound/drivers/serial-u16550.c linux-2.5.6/sound/drivers/serial-u16550.c --- linux-2.5.6-pre3/sound/drivers/serial-u16550.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/drivers/serial-u16550.c Thu Mar 7 18:24:56 2002 @@ -39,7 +39,7 @@ * * Usage example for MS-124T, with A-B switch in A position: * setserial /dev/ttyS0 uart none - * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=1 snd_speed=19200 * * - In MS-124W S/A mode, one raw MIDI substream is supported @@ -49,7 +49,7 @@ * * Usage example for S/A mode: * setserial /dev/ttyS0 uart none - * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=2 * * - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI @@ -67,7 +67,7 @@ * * Usage example for M/B mode: * setserial /dev/ttyS0 uart none - * /sbin/insmod snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * /sbin/insmod snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ * snd_adaptor=3 * * - The MS-124W hardware's M/A mode is currently not supported. @@ -106,6 +106,7 @@ #include #include #include +#include #include #include #define SNDRV_GET_ID diff -urN linux-2.5.6-pre3/sound/i2c/Makefile linux-2.5.6/sound/i2c/Makefile --- linux-2.5.6-pre3/sound/i2c/Makefile Tue Feb 19 18:10:53 2002 +++ linux-2.5.6/sound/i2c/Makefile Thu Mar 7 18:24:56 2002 @@ -13,7 +13,7 @@ snd-cs8427-objs := cs8427.o snd-tea6330t-objs := tea6330t.o -# Module Dependency +# Toplevel Module Dependency obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o diff -urN linux-2.5.6-pre3/sound/isa/Config.help linux-2.5.6/sound/isa/Config.help --- linux-2.5.6-pre3/sound/isa/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/isa/Config.help Thu Mar 7 18:24:56 2002 @@ -0,0 +1,99 @@ +CONFIG_SND_AD1816A + Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or + compatible sound chips. + +CONFIG_SND_AD1848 + Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 + (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips + from Cirrus Logic, use CS4231, CS4232 or CS4236+ driver. + +CONFIG_SND_CS4231 + Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4232 + Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4236 + Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 + chips from Cirrus Logic - Crystal Semiconductors. + +CONFIG_SND_ES968 + Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. + +CONFIG_SND_ES1688 + Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. + +CONFIG_SND_ES18XX + Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. + +CONFIG_SND_GUSCLASSIC + Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. + +CONFIG_SND_GUSEXTREME + Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. + +CONFIG_SND_GUSMAX + Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. + +CONFIG_SND_INTERWAVE + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, + Panasonic PCA761AW). + +CONFIG_SND_INTERWAVE_STB + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + with TEA6330T bass and treble regulator (UltraSound 32-Pro). + +CONFIG_SND_OPTI92X_AD1848 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + AD1848 codec. + +CONFIG_SND_OPTI92X_CS4231 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + CS4231 codec. + +CONFIG_SND_OPTI93X + Say 'Y' or 'M' to include support for Opti93x soundcards. + +CONFIG_SND_SB8 + Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) + soundcards or 100% compatible from Creative. + +CONFIG_SND_SB16 + Say 'Y' or 'M' to include support for Sound Blaster 16 (including + Plug and Play version). + +CONFIG_SND_SBAWE + Say 'Y' or 'M' to include support for Sound Blaster AWE (including + Plug and Play version). + +CONFIG_SND_SB16_CSP + Say 'Y' to include support for CSP core. This special coprocessor + can do variable tasks like various compression and decompression + algorithms. + +CONFIG_SND_WAVEFRONT + Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez + and Tropez+ soundcards based on Wavefront chip. + +CONFIG_SND_ALS100 + Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, + ALS120 and ALS200 soundcards. + +CONFIG_SND_AZT2320 + Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. + +CONFIG_SND_CMI8330 + Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. + +CONFIG_SND_DT0197H + Say 'Y' or 'M' to include support for Diamond Technologies DT-0197H + soundcards. + +CONFIG_SND_OPL3SA2 + Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. + +CONFIG_SND_SGALAXY + Say 'Y' or 'M' to include support for Aztech Sound Galaxy. diff -urN linux-2.5.6-pre3/sound/isa/ad1816a/ad1816a_lib.c linux-2.5.6/sound/isa/ad1816a/ad1816a_lib.c --- linux-2.5.6-pre3/sound/isa/ad1816a/ad1816a_lib.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/sound/isa/ad1816a/ad1816a_lib.c Thu Mar 7 18:24:56 2002 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff -urN linux-2.5.6-pre3/sound/isa/ad1848/ad1848_lib.c linux-2.5.6/sound/isa/ad1848/ad1848_lib.c --- linux-2.5.6-pre3/sound/isa/ad1848/ad1848_lib.c Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/sound/isa/ad1848/ad1848_lib.c Thu Mar 7 18:24:56 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff -urN linux-2.5.6-pre3/sound/isa/azt2320.c linux-2.5.6/sound/isa/azt2320.c --- linux-2.5.6-pre3/sound/isa/azt2320.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/isa/azt2320.c Thu Mar 7 18:24:56 2002 @@ -134,7 +134,7 @@ static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = { /* PRO16V */ ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001), - /* --- */ + /* Aztech Sound Galaxy 16 */ ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002), /* Packard Bell Sound III 336 AM/SP */ ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001), diff -urN linux-2.5.6-pre3/sound/isa/cs423x/cs4231_lib.c linux-2.5.6/sound/isa/cs423x/cs4231_lib.c --- linux-2.5.6-pre3/sound/isa/cs423x/cs4231_lib.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/isa/cs423x/cs4231_lib.c Thu Mar 7 18:24:56 2002 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff -urN linux-2.5.6-pre3/sound/isa/es1688/es1688_lib.c linux-2.5.6/sound/isa/es1688/es1688_lib.c --- linux-2.5.6-pre3/sound/isa/es1688/es1688_lib.c Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/sound/isa/es1688/es1688_lib.c Thu Mar 7 18:24:56 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.6-pre3/sound/isa/gus/gus_main.c linux-2.5.6/sound/isa/gus/gus_main.c --- linux-2.5.6-pre3/sound/isa/gus/gus_main.c Tue Feb 19 18:10:55 2002 +++ linux-2.5.6/sound/isa/gus/gus_main.c Thu Mar 7 18:24:56 2002 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.6-pre3/sound/isa/opl3sa2.c linux-2.5.6/sound/isa/opl3sa2.c --- linux-2.5.6-pre3/sound/isa/opl3sa2.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/isa/opl3sa2.c Thu Mar 7 18:24:56 2002 @@ -177,7 +177,7 @@ ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021), /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021), - /* ??? */ + /* Yamaha OPL3-SA2 */ ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021), /* NeoMagic MagicWave 3DX */ ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210), diff -urN linux-2.5.6-pre3/sound/isa/sb/Makefile linux-2.5.6/sound/isa/sb/Makefile --- linux-2.5.6-pre3/sound/isa/sb/Makefile Tue Feb 19 18:11:01 2002 +++ linux-2.5.6/sound/isa/sb/Makefile Thu Mar 7 18:24:56 2002 @@ -27,12 +27,12 @@ obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o ifeq ($(CONFIG_SND_SB16_CSP),y) obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o endif -obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o endif diff -urN linux-2.5.6-pre3/sound/isa/sb/emu8000.c linux-2.5.6/sound/isa/sb/emu8000.c --- linux-2.5.6-pre3/sound/isa/sb/emu8000.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/sound/isa/sb/emu8000.c Thu Mar 7 18:24:56 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.6-pre3/sound/isa/sb/sb16_main.c linux-2.5.6/sound/isa/sb/sb16_main.c --- linux-2.5.6-pre3/sound/isa/sb/sb16_main.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/sound/isa/sb/sb16_main.c Thu Mar 7 18:24:56 2002 @@ -209,11 +209,11 @@ #else #define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ #define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ -#define snd_sb16_csp_update(chip) /*nop*/ +#define snd_sb16_csp_update(chip) /*nop*/ #define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ -#define snd_sb16_csp_playback_close(chip) /*nop*/ +#define snd_sb16_csp_playback_close(chip) /*nop*/ #define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ -#define snd_sb16_csp_capture_close(chip) /*nop*/ +#define snd_sb16_csp_capture_close(chip) /*nop*/ #endif diff -urN linux-2.5.6-pre3/sound/isa/sb/sb8.c linux-2.5.6/sound/isa/sb/sb8.c --- linux-2.5.6-pre3/sound/isa/sb/sb8.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/isa/sb/sb8.c Thu Mar 7 18:24:56 2002 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.6-pre3/sound/isa/sb/sb_common.c linux-2.5.6/sound/isa/sb/sb_common.c --- linux-2.5.6-pre3/sound/isa/sb/sb_common.c Tue Feb 19 18:11:02 2002 +++ linux-2.5.6/sound/isa/sb/sb_common.c Thu Mar 7 18:24:56 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff -urN linux-2.5.6-pre3/sound/isa/sgalaxy.c linux-2.5.6/sound/isa/sgalaxy.c --- linux-2.5.6-pre3/sound/isa/sgalaxy.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/isa/sgalaxy.c Thu Mar 7 18:24:56 2002 @@ -119,7 +119,7 @@ static int dma_bits[] = {1, 2, 0, 3}; int tmp, tmp1; - unsigned int flags; + unsigned long flags; if ((tmp = inb(port + 3)) == 0xff) { diff -urN linux-2.5.6-pre3/sound/isa/wavefront/wavefront_synth.c linux-2.5.6/sound/isa/wavefront/wavefront_synth.c --- linux-2.5.6-pre3/sound/isa/wavefront/wavefront_synth.c Tue Feb 19 18:11:00 2002 +++ linux-2.5.6/sound/isa/wavefront/wavefront_synth.c Thu Mar 7 18:24:56 2002 @@ -111,30 +111,6 @@ MODULE_PARM(osrun_time,"i"); MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); -/* - * This sucks, hopefully it'll get standardised - */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#define loops_per_sec loops_per_jiffy*HZ -#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) && defined(I_DIRTY_PAGES) /* linux/fs.h */ -#define loops_per_sec loops_per_jiffy*HZ -#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) -#define loops_per_sec loops_per_jiffy*HZ -#endif - -#if defined(__alpha__) || defined(__powerpc__) -#ifdef __SMP__ -#define LOOPS_PER_SEC (cpu_data[smp_processor_id()].loops_per_sec) -#else -#define LOOPS_PER_SEC (loops_per_sec) -#endif -#endif - -#if defined(__i386__) -#define LOOPS_PER_SEC (current_cpu_data.loops_per_sec) -#endif - /* if WF_DEBUG not defined, no run-time debugging messages will be available via the debug flag setting. Given the current beta state of the driver, this will remain set until a future @@ -323,26 +299,16 @@ { int i; - static int short_loop_cnt = 0; - - /* Compute the loop count that lets us sleep for about the - right amount of time, cache issues, bus speeds and all - other issues being unequal but largely irrelevant. - */ - - if (short_loop_cnt == 0) { - short_loop_cnt = wait_usecs * - (LOOPS_PER_SEC / 1000000); - } /* Spin for a short period of time, because >99% of all requests to the WaveFront can be serviced inline like this. */ - for (i = 0; i < short_loop_cnt; i++) { + for (i = 0; i < wait_usecs; i += 5) { if (wavefront_status (dev) & mask) { return 1; } + udelay(5); } for (i = 0; i < sleep_tries; i++) { @@ -1316,18 +1282,21 @@ for (i = 0; i < num_samples; i++) { char d[2]; + int val; - if ((d[0] = wavefront_read (dev)) == -1) { + if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); return -(EIO); } + d[0] = val; - if ((d[1] = wavefront_read (dev)) == -1) { + if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); return -(EIO); } + d[1] = val; header->hdr.ms.SampleNumber[i] = demunge_int32 ((unsigned char *) d, 2); diff -urN linux-2.5.6-pre3/sound/oss/via82cxxx_audio.c linux-2.5.6/sound/oss/via82cxxx_audio.c --- linux-2.5.6-pre3/sound/oss/via82cxxx_audio.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/oss/via82cxxx_audio.c Thu Mar 7 18:24:56 2002 @@ -354,8 +354,6 @@ static struct pci_device_id via_pci_tbl[] __initdata = { { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, - PCI_ANY_ID, PCI_ANY_ID, }, { 0, } }; MODULE_DEVICE_TABLE(pci,via_pci_tbl); diff -urN linux-2.5.6-pre3/sound/pci/Config.help linux-2.5.6/sound/pci/Config.help --- linux-2.5.6-pre3/sound/pci/Config.help Wed Dec 31 16:00:00 1969 +++ linux-2.5.6/sound/pci/Config.help Thu Mar 7 18:24:56 2002 @@ -0,0 +1,75 @@ +CONFIG_SND_ALI5451 + Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. + +CONFIG_SND_CS46XX + Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / + CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. + +CONFIG_SND_CS46XX_ACCEPT_VALID + Say 'Y' to allow sample resolution for mmap() transfers. + +CONFIG_SND_EMU10K1 + Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, + Audigy and E-mu APS (partially supported). + +CONFIG_SND_KORG1212 + Say 'Y' or 'M' to include support for Korg 1212IO. + +CONFIG_SND_NM256 + Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. + +CONFIG_SND_RME96 + Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and + Digi96/8 PRO/PAD/PST. + +CONFIG_SND_RME9652 + Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / + Digi9636) soundcards. + +CONFIG_SND_TRIDENT + +CONFIG_SND_YMFPCI + Say 'Y' or 'M' to include support for Yamaha PCI audio chips - + YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. + +CONFIG_SND_ALS4000 + Say 'Y' or 'M' to include support for Avance Logic ALS4000. + +CONFIG_SND_CMIPCI + Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI + soundcards. + +CONFIG_SND_ENS1370 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. + +CONFIG_SND_ENS1371 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and + Sound Blaster PCI 64 or 128 soundcards. + +CONFIG_SND_ES1938 + Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946) + soundcard. + +CONFIG_SND_ES1968 + Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. + +CONFIG_SND_MAESTRO3 + Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. + +CONFIG_SND_FM801 + Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. + +CONFIG_SND_ICE1712 + Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. + +CONFIG_SND_INTEL8X0 + Say 'Y' or 'M' to include support for Intel8x0 based soundcards. + +CONFIG_SND_SONICVIBES + Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. + +CONFIG_SND_VIA686 + Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge. + +CONFIG_SND_VIA8233 + Say 'Y' or 'M' to include support for VIA VT8233 South Bridge. diff -urN linux-2.5.6-pre3/sound/pci/ymfpci/ymfpci.c linux-2.5.6/sound/pci/ymfpci/ymfpci.c --- linux-2.5.6-pre3/sound/pci/ymfpci/ymfpci.c Thu Mar 7 18:24:38 2002 +++ linux-2.5.6/sound/pci/ymfpci/ymfpci.c Thu Mar 7 18:24:57 2002 @@ -109,7 +109,7 @@ } legacy_ctrl = 0; - legacy_ctrl2 = 0; + legacy_ctrl2 = 0x0800; /* SMOD = 01 */ if (id->device >= 0x0010) { /* YMF 744/754 */ if (snd_fm_port[dev] < 0) diff -urN linux-2.5.6-pre3/sound/synth/Makefile linux-2.5.6/sound/synth/Makefile --- linux-2.5.6-pre3/sound/synth/Makefile Tue Feb 19 18:10:54 2002 +++ linux-2.5.6/sound/synth/Makefile Thu Mar 7 18:24:57 2002 @@ -15,8 +15,8 @@ snd-util-mem-objs := util_mem.o # Toplevel Module Dependency -obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o +obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o endif