UMC U5D or U5S.
- "586" for generic Pentium CPUs, possibly lacking the TSC
(time stamp counter) register.
- - "Pentium" for the Intel Pentium/Pentium MMX, AMD K5, K6 and
- K6-3D.
+ - "Pentium" for the Intel Pentium/Pentium MMX, AMD K5.
- "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and
Intel Pentium II/Pentium Pro.
+ - "K6/II/III" for the AMD K6, K6-II and K6-III (aka K6-3D).
+ - "Athlon" for the AMD Athlon (aka K7)
If you don't know what to do, choose "386".
adhoc=1 there are no Access Points around
master=1 Adhoc master (the one who creates network sync)
slave=1 Adhoc slave(btw, it is still forming own net
- sometimes)
+ sometimes, and has problems with firmware...
+ change IbssJoinNetTimeout from /proc...)
channel=1..? meaningful in adhoc mode
all other parameters can be set via /proc interface
These parameters belong to .._card module, but alas, they are here
USB-UHCI High Bandwidth support
CONFIG_USB_UHCI_HIGH_BANDWIDTH
- This option enables the so called reclamation loop in usb-uhci, thus
+ This option enables the so-called reclamation loop in usb-uhci, thus
allowing much higher transfer bandwidth for USB-bulk and control
messages; isochronous transfers (audio, video etc.) are not affected.
Due to a very simple design of the UHCI controller, this may cause
The module will be called ov511.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
-USB ADMtek's Pegasus based ethernet devices support
+USB ADMtek Pegasus-based ethernet device support
CONFIG_USB_PEGASUS
- Say Y if you want to use your usb ethernet device. Note that
- the code is still experimental. If you have devices with other
- vendor IDs than ADMtek's you should change/add them in the
+ Say Y if you want to use your USB ethernet device. Note that
+ the code is still experimental. If you have devices with vendor
+ IDs other than ADMtek's, you should change/add them in the
driver code and send a message to me (petkan@spct.net) for
update.
+
This code 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 ov511.o. If you want to compile it as a
PLUSB driver
CONFIG_USB_PLUSB
- A driver for the Prolific PL-2302 USB to USB network device. This 'USB
- cable' connects two hosts via a point to point network with bandwidth of
- 5Mbit/s. Configure this driver after connecting the USB cable via
+ A driver for the Prolific PL-2302 USB-to-USB network device. This 'USB
+ cable' connects two hosts via a point-to-point network with bandwidth of
+ 5 Mbit/s. Configure this driver after connecting the USB cable via
ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2
(and vice versa on the other host).
-CRYSTAL LAN CS8900/CS8920 ETHERNET ADAPTERS
-Linux Network Interface Driver ver. 1.02
-===============================================================================
-
-
-TABLE OF CONTENTS
-
-1.0 CRYSTAL LAN CS8900/CS8920 ETHERNET ADAPTERS
- 1.1 Product Overview
- 1.2 Driver Description
- 1.2.1 Driver Name
- 1.2.2 File in the Driver Package
- 1.3 System Requirements
- 1.4 Licensing Information
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
- 2.1 CS8900-based Adapter Configuration
- 2.2 CS8920-based Adapter Configuration
-
-3.0 LOADING THE DRIVER AS A MODULE
-
-4.0 COMPILING THE DRIVER
- 4.1 Compiling the Driver As a Loadable Module
- 4.2 Compiling the Driver Into the Kernel
- 4.3 Compiling the Driver for a Linux v1.2.13 Kernel
-
-5.0 TESTING AND TROUBLESHOOTING
- 5.1 Known Defects and Limitations
- 5.2 Testing the Adapter
- 5.2.1 Diagnostic Self-Test
- 5.2.2 Diagnostic Network Test
- 5.3 Using the Adapter's LEDs
- 5.4 Resolving I/O Conflicts
-
-6.0 TECHNICAL SUPPORT
- 6.1 Contacting Crystal's Technical Support
- 6.2 Information Required Before Contacting Technical Support
- 6.3 Obtaining the Latest Driver Version
- 6.3.1 Crystal's Web Site
- 6.3.2 Crystal's Bulletin Board Service
-
-
-
-1.0 CRYSTAL LAN CS8900/CS8920 ETHERNET ADAPTERS
-===============================================================================
-
-
-1.1 PRODUCT OVERVIEW
-
-The CS8900-based ISA Ethernet Adapters from Crystal Semiconductor follow
-IEEE 802.3 standards and support half or full-duplex operation in ISA bus
-computers on 10 Mbps Ethernet networks. The adapters are designed for
-operation in 16-bit ISA or EISA bus expansion slots and are available in
-10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5
-or fiber networks).
-
-CS8920-based adapters are similar to the CS8900-based adapter with additional
-features for Plug and Play (PnP) support and Wakeup Frame recognition. As
-such, the configuration procedures differ somewhat between the two types of
-adapters. Refer to the "Adapter Configuration" section for details on
-configuring both types of adapters.
-
-
-1.2 DRIVER DESCRIPTION
-
-The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux v1.2.13
-and v2.0 (or greater) kernels. It can be compiled directly into the kernel or
-loaded at run-time as a device driver module.
-
-1.2.1 Driver Name: cs89x0
-
-1.2.2 Files in the Driver Archive:
-
- readme.txt - this file
- release.txt - known defects and modification log
- cs89x0.c - driver C code
- cs89x0.h - driver header file
- cs89x0.o - pre-compiled module (for v2.0 kernel)
-
-
-
-1.3 SYSTEM REQUIREMENTS
-
-The following hardware is required:
-
- * Crystal LAN (CS8900/20-based) Ethernet ISA Adapter
-
- * IBM or IBM-compatible PC with:
- * An 80386 or higher processor
- * 16 bytes of contiguous IO space available between 210h - 370h
- * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
-
- * Appropriate cable (and connector for AUI, 10BASE-2) for your network
- topology.
-
-The following software is required:
-
- * LINUX kernel version 1.2.13 or 2.X
-
- * CS8900/20 Setup Utility (DOS-based)
-
- * LINUX kernel sources for your kernel (if compiling into kernel)
-
- * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel
- or a module)
-
-
-
-1.4 LICENSING INFORMATION
-
-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, version 1.
-
-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.
-
-For a full copy of the GNU General Public License, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-===============================================================================
-
-Both the CS8900 and CS8920-based adapters can be configured using parameters
-stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup
-Utility if you want to change the adapter's configuration in EEPROM.
-
-When loading the driver as a module, you can specify many of the adapter's
-configuration parameters on the command-line to override the EEPROM's settings
-or for interface configuration when an EEPROM is not used. (CS8920-based
-adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
-
-Since the CS8900/20 Setup Utility is a DOS-based application, you must install
-and configure the adapter in a DOS-based system using the CS8900/20 Setup
-Utility before installation in the target LINUX system. (Not required if
-installing a CS8900-based adapter and the default configuration is acceptable.)
-
-
-2.1 CS8900-BASED ADAPTER CONFIGURATION
-
-CS8900-based adapters shipped from Crystal Semiconductor have been configured
-with the following "default" settings:
-
- Operation Mode: Memory Mode
- IRQ: 10
- Base I/O Address: 300
- Memory Base Address: D0000
- Optimization: DOS Client
- Transmission Mode: Half-duplex
- BootProm: None
- Media Type: Autodetect (3-media cards) or
- 10BASE-T (10BASE-T only adapter)
-
-You should only change the default configuration settings if conflicts with
-another adapter exist. To change the adapter's configuration, run the
-CS8900/20 Setup Utility.
-
-
-2.2 CS8920-BASED ADAPTER CONFIGURATION
-
-CS8920-based adapters are shipped from Crystal Semiconductor configured as Plug
-and Play (PnP) enabled. However, since Linux is not currently a PnP compatible
-operating system, you must install the CS8920 adapter in a DOS-based PC and
-run the CS8900/20 Setup Utility to disable PnP and configure the adapter before
-installation in the target Linux system. Failure to do this will leave the
-adapter inactive and the driver will be unable to communicate with the
-adapter.
-
-
- ****************************************************************
- * CS8920-BASED ADAPTERS: *
- * *
- * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT. *
- * SCO UNIX IS NOT A PnP OPERATING SYSTEM. THEREFORE, YOU MUST *
- * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND *
- * TO ACTIVATE THE ADAPTER. *
- ****************************************************************
-
-
-
-
-3.0 LOADING THE DRIVER AS A MODULE
-===============================================================================
-
-If the driver is compiled as a loadable module, you can load the driver module
-with the 'insmod' command. Many of the adapter's configuration parameters can
-be specified as command-line arguments to the load command. This facility
-provides a means to override the EEPROM's settings or for interface
-configuration when an EEPROM is not used.
-
-Example:
-
- insmod cs89x0.o io=0x200 irq=0xA media=aui
-
-This example loads the module and configures the adapter to use an IO port base
-address of 200h, interrupt 10, and use the AUI media connection. The following
-configuration options are available on the command line:
-
-* io=### - specify IO address (200h-360h)
-* irq=## - specify interrupt level
-* mmode=##### - specify memory base address
-* dma=# - specify DMA channel
-* media=rj45 - specify media type
- or media=2
- or media=aui
- or medai=auto
-* duplex=f - specify forced half/full/autonegotiate duplex
- or duplex=h
- or duplex=auto
-* debug=# - debug level
-
-NOTES:
-* If an EEPROM is present, any specified command-line parameter will override
-the corresponding configuration value stored in EEPROM.
-
-* If no "io" or "mmode" parameter is specified on the command-line, the driver
-will scan for the adapter. When scanning, the driver only reads I/O ports.
-This sometimes is not sufficient, (e.g. after a warm boot). If you wish to
-allow the driver to perform a more aggressive scan (one write to the IO base
-addresses to reset the data port pointer) you can specify an I/O address with
-an address value one greater than the configured address. Example, to scan for
-an adapter located at IO base 0x300, specify an IO address of 0x301. Only
-ports between 200h and 360h at 20h intervals are scanned.
-
-* The "duplex=auto" parameter is only supported for the CS8920.
-
-* The minimum command-line configuration required if an EEPROM is not present
-is:
-
- * io or mmode base address
- * irq
- * media type (no autodetect)
-
-The following additional parameters are CS89XX defaults (values used with no
-EEPROM or command-line argument).
-
- * DMA Burst = enabled
- * IOCHRDY Enabled = enabled
- * UseSA = enabled
- * CS8900 defaults to half-duplex if not specified on command-line
- * CS8920 defaults to autoneg if not specified on command-line
- * Use reset defaults for other config parameters
-
-* You can use ifconfig to set the adapter's Ethernet address.
-
-
-
-
-4.0 COMPILING THE DRIVER
-===============================================================================
-
-The cs89x0 driver can be compiled directly into the kernel or compiled into
-a loadable device driver module.
-
-NOTE: This part of the description relates to adding the driver to a kernel
-not containing the cs89x0 driver. This kernel already contains it.
-
-4.1 COMPILING THE DRIVER AS A LOADABLE MODULE
-
-To compile the driver into a loadable module, use the following command
-(single command line, without quotes):
-
-"gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall
--Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS
--c cs89x0.c"
-
-
-4.2 COMPILING THE DRIVER INTO THE KERNEL
-
-To compile the driver directly into the kernel requires editing four
-configuration files, copying the source file to the /linux/drivers/net
-directory and then running the make utility to rebuild the kernel.
-
-1. Edit the following configuration files by adding the statements as
-indicated. (When possible, try to locate the added text to the section of the
-file containing similar statements).
-
-a.) In /usr/src/linux/drivers/net/CONFIG, add
-
-CS89x0_OPTS =
-
-Example:
-
- WD_OPTS = #-DWD_SHMEM=0xDD000
- EL2_OPTS = #-DEL2_AUI
- CS89x0_OPTS =
- NE_OPTS =
- HP_OPTS =
- PLIP_OPTS =
-
-
-b.) In /usr/src/linux/drivers/net/Config.in, add:
-
-tristate 'CS89x0 support' CONFIG_CS89x0
-
-Example:
-
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
- fi
-
- tristate 'CS89x0 support' CONFIG_CS89x0
-
- tristate 'NE2000/NE1000 support' CONFIG_NE2000
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'NI5210 support' CONFIG_NI52
-
-
-c.) In /usr/src/linux/drivers/net/Makefile, add the following lines:
-
-ifeq ($(CONFIG_CS89x0),y)
-L_OBJS += cs89x0.o
-else
- ifeq ($(CONFIG_CS89x0),m)
- M_OBJS += cs89x0.o
- endif
-endif
-
-
-d.) In /linux/drivers/net/Space.c file, add the line:
-
-extern int cs89x0_probe(struct net_device *dev);
-
-
-Example:
-
- extern int ultra_probe(struct net_device *dev);
- extern int wd_probe(struct net_device *dev);
- extern int el2_probe(struct net_device *dev);
-
- extern int cs89x0_probe(struct net_device *dev);
-
- extern int ne_probe(struct net_device *dev);
- extern int hp_probe(struct net_device *dev);
- extern int hp_plus_probe(struct net_device *dev);
-
-
-Also add:
-
- #ifdef CONFIG_CS89x0
- && cs89x0_probe(dev)
- #endif
-
-
-2.) Copy the driver source files (cs89x0.c and cs89x0.h) and this README file
-into the /usr/src/linux/drivers/net directory.
-
-
-3.) Run 'make config' followed by 'make dep' and finally 'make' to rebuild
-the kernel.
-
-
-4.3 COMPILING THE DRIVER FOR A LINUX v1.2.13 KERNEL
-
-To compile the driver for Linux v1.2.13 (into the kernel or as a module),
-change the "SUPPORTS" define at the beginning of the cs89x0.c file.
-Example:
-
-#define SUPPORTS_1_2_13 1 /* supports Linux kernel v1.2.13 */
- or
-#define SUPPORTS_1_2_13 0 /* supports Linux kernel v2.0 (default) */
-
-
-
-5.0 TESTING AND TROUBLESHOOTING
-===============================================================================
-
-5.1 KNOWN DEFECTS and LIMITATIONS
-
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of
-known defects, driver limitations, and work arounds.
-
-
-5.2 TESTING THE ADAPTER
-
-Once the adapter has been installed and configured, the diagnostic option of
-the CS8900/20 Setup Utility can be used to test the functionality of the
-adapter and its network connection. Use the diagnostics 'Self Test' option to
-test the functionality of the adapter with the hardware configuration you have
-assigned. You can use the diagnostics 'Network Test' to test the ability of the
-adapter to communicate across the Ethernet with another PC equipped with a
-CS8900/20-based adapter card (it must also be running the CS8900/20 Setup
-Utility).
-
- NOTE: The Setup Utility's diagnostics are designed to run in a
- DOS-only operating system environment. DO NOT run the diagnostics
- from a DOS or command prompt session under Windows 95, Windows NT,
- OS/2, or other operating system.
-
- [AC - Question : Do they work in DOSEMU ?]
-
-To run the diagnostics tests on the CS8900/20 adapter:
-
- 1.) Boot DOS on the PC and start the CS8900/20 Setup Utility.
-
- 2.) The adapter's current configuration is displayed. Hit the ENTER key to
- get to the main menu.
-
- 4.) Select 'Diagnostics' (ALT-G) from the main menu.
- * Select 'Self-Test' to test the adapter's basic functionality.
- * Select 'Network Test' to test the network connection and cabling.
-
-
-5.2.1 DIAGNOSTIC SELF-TEST
-
-The diagnostic self-test checks the adapter's basic functionality as well as
-its ability to communicate across the ISA bus based on the system resources
-assigned during hardware configuration. The following tests are performed:
-
- * IO Register Read/Write Test
- The IO Register Read/Write test ensures that the CS8900/20 can be
- accessed in IO mode, and that the IO base address is correct.
-
- * Shared Memory Test
- The Shared Memory test ensures the CS8900/20 can be accessed in memory
- mode and that the range of memory addresses assigned does not conflict
- with other devices in the system.
-
- * Interrupt Test
- The Interrupt test ensures there are no conflicts with the assigned IRQ
- signal.
-
- * EEPROM Test
- The EEPROM test ensures the EEPROM can be read.
-
- * Chip RAM Test
- The Chip RAM test ensures the 4 K of memory internal to the CS8900/20 is
- working properly.
-
- * Internal Loop-back Test
- The Internal Loop Back test ensures the adapter's transmitter and
- receiver are operating properly. If this test fails, make sure the
- adapter's cable is connected to the network (check for LED activity for
- example).
-
- * Boot PROM Test
- The Boot PROM test ensures the Boot PROM is present, and can be read.
- Failure indicates the Boot PROM was not successfully read due to a
- hardware problem or due to a conflicts on the Boot PROM address
- assignment. (Test only applies if the adapter is configured to use the
- Boot PROM option.)
-
-Failure of a test item indicates a possible system resource conflict with
-another device on the ISA bus. In this case, you should use the Manual Setup
-option to reconfigure the adapter by selecting a different value for the system
-resource that failed.
-
-
-5.2.2 DIAGNOSTIC NETWORK TEST
-
-The Diagnostic Network Test verifies a working network connection by
-transferring data between two CS8900/20 adapters installed in different PCs
-on the same network. (Note: the diagnostic network test should not be run
-between two nodes across a router.)
-
-This test requires that each of the two PCs have a CS8900/20-based adapter
-installed and have the CS8900/20 Setup Utility running. The first PC is
-configured as a Responder and the other PC is configured as an Initiator.
-Once the Initiator is started, it sends data frames to the Responder which
-returns the frames to the Initiator.
-
-The total number of frames received and transmitted are displayed on the
-Initiator's display, along with a count of the number of frames received and
-transmitted OK or in error. The test can be terminated anytime by the user at
-either PC.
-
-To setup the Diagnostic Network Test:
-
- 1.) Select a PC with a CS8900/20-based adapter and a known working network
- connection to act as the Responder. Run the CS8900/20 Setup Utility
- and select 'Diagnostics -> Network Test -> Responder' from the main
- menu. Hit ENTER to start the Responder.
-
- 2.) Return to the PC with the CS8900/20-based adapter you want to test and
- start the CS8900/20 Setup Utility.
-
- 3.) From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
- Hit ENTER to start the test.
-
-You may stop the test on the Initiator at any time while allowing the Responder
-to continue running. In this manner, you can move to additional PCs and test
-them by starting the Initiator on another PC without having to stop/start the
-Responder.
-
-
-
-5.3 USING THE ADAPTER'S LEDs
-
-The 2 and 3-media adapters have two LEDs visible on the back end of the board
-located near the 10Base-T connector.
-
-Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T
-connection. (Only applies to 10Base-T. The green LED has no significance for
-a 10Base-2 or AUI connection.)
-
-TX/RX LED: The yellow LED lights briefly each time the adapter transmits or
-receives data. (The yellow LED will appear to "flicker" on a typical network.)
-
-
-5.4 RESOLVING I/O CONFLICTS
-
-An IO conflict occurs when two or more adapter use the same ISA resource (IO
-address, memory address or IRQ). You can usually detect an IO conflict in one
-of four ways after installing and or configuring the CS8900/20-based adapter:
-
- 1.) The system does not boot properly (or at all).
-
- 2.) The driver can not communicate with the adapter, reporting an "Adapter
- not found" error message.
-
- 3.) You cannot connect to the network or the driver will not load.
-
- 4.) If you have configured the adapter to run in memory mode but the driver
- reports it is using IO mode when loading, this is an indication of a
- memory address conflict.
-
-If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a
-diagnostic self-test. Normally, the ISA resource in conflict will fail the
-self-test. If so, reconfigure the adapter selecting another choice for the
-resource in conflict. Run the diagnostics again to check for further IO
-conflicts.
-
-In some cases, such as when the PC will not boot, it may be necessary to remove
-the adapter and reconfigure it by installing it in another PC to run the
-CS8900/20 Setup Utility. Once reinstalled in the target system, run the
-diagnostics self-test to ensure the new configuration is free of conflicts
-before loading the driver again.
-
-When manually configuring the adapter, keep in mind the typical ISA system
-resource usage as indicated in the tables below.
-
-I/O Address Device IRQ Device
------------ -------- --- --------
- 200-20F Game I/O adapter 3 COM2, Bus Mouse
- 230-23F Bus Mouse 4 COM1
- 270-27F LPT3: third parallel port 5 LPT2
- 2F0-2FF COM2: second serial port 6 Floppy Disk controller
- 320-32F Fixed disk controller 7 LPT1
- 8 Real-time Clock
- 9 EGA/VGA display adapter
- 12 Mouse (PS/2)
-Memory Address Device 13 Math Coprocessor
--------------- --------------------- 14 Hard Disk controller
-A000-BFFF EGA Graphics Adapter
-A000-C7FF VGA Graphics Adapter
-B000-BFFF Mono Graphics Adapter
-B800-BFFF Color Graphics Adapter
-E000-FFFF AT BIOS
-
-
-
-
-6.0 TECHNICAL SUPPORT
-===============================================================================
-
-6.1 CONTACTING CRYSTAL'S TECHNICAL SUPPORT
-
-Crystal's CS89XX Technical Application Support can be reached at:
-
-Telephone :(800) 888-5016 (from inside U.S. and Canada)
- :(512) 442-7555 (from outside the U.S. and Canada)
-Fax :(512) 912-3871
-E-mail :ethernet@crystal.cirrus.com
-WWW :http://www.crystal.com
-
-
-6.2 INFORMATION REQUIRED BEFORE CONTACTING TECHNICAL SUPPORT
-
-Before contacting Crystal for technical support, be prepared to provide as much
-of the following information as possible.
-
-1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
-
-2.) Adapter configuration
-
- * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
- * Plug and Play enabled/disabled (CS8920-based adapters only)
- * Configured for media auto-detect or specific media type (which type).
-
-3.) PC System's Configuration
-
- * Plug and Play system (yes/no)
- * BIOS (make and version)
- * System make and model
- * CPU (type and speed)
- * System RAM
- * SCSI Adapter
-
-4.) Software
-
- * CS89XX driver and version
- * Your network operating system and version
- * Your system's OS version
- * Version of all protocol support files
-
-5.) Any Error Message displayed.
-
-
-
-6.3 OBTAINING THE LATEST DRIVER VERSION
-
-You can obtain the latest CS89XX drivers and support software from Crystal's
-BBS or Web site. You can also contact Crystal's Technical Support (email:
-ethernet@crystal.cirrus.com) and request that you be registered for automatic
-software-update notification.
-
-
-6.3.1 CRYSTAL'S WEB SITE
-
-Crystal Semiconductor maintains a web page at http://www.crystal.com with the
-latest drivers and technical publications.
-
-
-6.3.2 CRYSTAL'S BULLETIN BOARD SERVICE
-
-Access to the BBS is available 24 hours a day, seven days a week. Baud
-rates from 300K to 14.4K are supported as well as most common file transfer
-protocols.
-
-To access the BBS, set your terminal software to use 8 data bits, 1 stop bit,
-and no parity. Dial (512) 441-3265 and press <ENTER> after connection is made.
-Login using your account name and password. (If you do not have an account,
-you may login as "GUEST". No password is required for the Guest account.)
-
-From the main system menu, select the "Enter Public File Area" menu option.
-From the Public File Area menu, select the "LAN (Local Area Network)" file
-area. A list of the latest drivers and support utilities available for the
-CS89XX ISA Ethernet adapter will be presented along with the option to download
-the file(s) of your choice.
-
-
+\r
+NOTE\r
+----\r
+\r
+This document was contributed by Cirrus Logic for kernel 2.2.5. This version\r
+has been updated for 2.3.48 by Andrew Morton <andrewm@uow.edu.au>\r
+\r
+Cirrus make a copy of this driver available at their website, as\r
+described below. In general, you should use the driver version which\r
+comes with your Linux distribution.\r
+\r
+\r
+\r
+CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS\r
+Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>\r
+===============================================================================\r
+ \r
+\r
+TABLE OF CONTENTS\r
+\r
+1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS\r
+ 1.1 Product Overview \r
+ 1.2 Driver Description\r
+ 1.2.1 Driver Name\r
+ 1.2.2 File in the Driver Package\r
+ 1.3 System Requirements\r
+ 1.4 Licensing Information\r
+\r
+2.0 ADAPTER INSTALLATION and CONFIGURATION\r
+ 2.1 CS8900-based Adapter Configuration\r
+ 2.2 CS8920-based Adapter Configuration \r
+\r
+3.0 LOADING THE DRIVER AS A MODULE\r
+\r
+4.0 COMPILING THE DRIVER\r
+ 4.1 Compiling the Driver as a Loadable Module\r
+ 4.2 Compiling the driver to support memory mode\r
+ 4.3 Compiling the driver to support Rx DMA \r
+ 4.4 Compiling the Driver into the Kernel\r
+\r
+5.0 TESTING AND TROUBLESHOOTING\r
+ 5.1 Known Defects and Limitations\r
+ 5.2 Testing the Adapter\r
+ 5.2.1 Diagnostic Self-Test\r
+ 5.2.2 Diagnostic Network Test\r
+ 5.3 Using the Adapter's LEDs\r
+ 5.4 Resolving I/O Conflicts\r
+\r
+6.0 TECHNICAL SUPPORT\r
+ 6.1 Contacting Cirrus Logic's Technical Support\r
+ 6.2 Information Required Before Contacting Technical Support\r
+ 6.3 Obtaining the Latest Driver Version\r
+ 6.4 Current maintainer\r
+\r
+\r
+\r
+1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS\r
+===============================================================================\r
+\r
+\r
+1.1 PRODUCT OVERVIEW\r
+\r
+The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow \r
+IEEE 802.3 standards and support half or full-duplex operation in ISA bus \r
+computers on 10 Mbps Ethernet networks. The adapters are designed for operation \r
+in 16-bit ISA or EISA bus expansion slots and are available in \r
+10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5 \r
+or fiber networks). \r
+\r
+CS8920-based adapters are similar to the CS8900-based adapter with additional \r
+features for Plug and Play (PnP) support and Wakeup Frame recognition. As \r
+such, the configuration procedures differ somewhat between the two types of \r
+adapters. Refer to the "Adapter Configuration" section for details on \r
+configuring both types of adapters.\r
+\r
+\r
+1.2 DRIVER DESCRIPTION\r
+\r
+The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux\r
+v2.3.48 or greater kernel. It can be compiled directly into the kernel\r
+or loaded at run-time as a device driver module.\r
+\r
+1.2.1 Driver Name: cs89x0\r
+\r
+1.2.2 Files in the Driver Archive:\r
+\r
+The files in the driver at Cirrus' website include:\r
+\r
+ readme.txt - this file\r
+ build - batch file to compile cs89x0.c.\r
+ cs89x0.c - driver C code\r
+ cs89x0.h - driver header file\r
+ cs89x0.o - pre-compiled module (for v2.2.5 kernel)\r
+ config/Config.in - sample file to include cs89x0 driver in the kernel.\r
+ config/Makefile - sample file to include cs89x0 driver in the kernel.\r
+ config/Space.c - sample file to include cs89x0 driver in the kernel.\r
+\r
+\r
+\r
+1.3 SYSTEM REQUIREMENTS\r
+\r
+The following hardware is required:\r
+\r
+ * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter \r
+\r
+ * IBM or IBM-compatible PC with:\r
+ * An 80386 or higher processor\r
+ * 16 bytes of contiguous IO space available between 210h - 370h\r
+ * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).\r
+\r
+ * Appropriate cable (and connector for AUI, 10BASE-2) for your network\r
+ topology.\r
+\r
+The following software is required:\r
+\r
+* LINUX kernel version 2.3.48 or higher\r
+\r
+ * CS8900/20 Setup Utility (DOS-based)\r
+\r
+ * LINUX kernel sources for your kernel (if compiling into kernel)\r
+\r
+ * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel \r
+ or a module) \r
+\r
+\r
+\r
+1.4 LICENSING INFORMATION\r
+\r
+This program is free software; you can redistribute it and/or modify it under\r
+the terms of the GNU General Public License as published by the Free Software\r
+Foundation, version 1.\r
+\r
+This program is distributed in the hope that it will be useful, but WITHOUT\r
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \r
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \r
+more details.\r
+\r
+For a full copy of the GNU General Public License, write to the Free Software\r
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+\r
+\r
+\r
+2.0 ADAPTER INSTALLATION and CONFIGURATION\r
+===============================================================================\r
+\r
+Both the CS8900 and CS8920-based adapters can be configured using parameters \r
+stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup \r
+Utility if you want to change the adapter's configuration in EEPROM. \r
+\r
+When loading the driver as a module, you can specify many of the adapter's \r
+configuration parameters on the command-line to override the EEPROM's settings \r
+or for interface configuration when an EEPROM is not used. (CS8920-based \r
+adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.\r
+\r
+Since the CS8900/20 Setup Utility is a DOS-based application, you must install \r
+and configure the adapter in a DOS-based system using the CS8900/20 Setup \r
+Utility before installation in the target LINUX system. (Not required if \r
+installing a CS8900-based adapter and the default configuration is acceptable.)\r
+ \r
+\r
+2.1 CS8900-BASED ADAPTER CONFIGURATION\r
+\r
+CS8900-based adapters shipped from Cirrus Logic have been configured \r
+with the following "default" settings:\r
+\r
+ Operation Mode: Memory Mode\r
+ IRQ: 10\r
+ Base I/O Address: 300\r
+ Memory Base Address: D0000\r
+ Optimization: DOS Client\r
+ Transmission Mode: Half-duplex\r
+ BootProm: None\r
+ Media Type: Autodetect (3-media cards) or \r
+ 10BASE-T (10BASE-T only adapter)\r
+\r
+You should only change the default configuration settings if conflicts with \r
+another adapter exists. To change the adapter's configuration, run the \r
+CS8900/20 Setup Utility. \r
+\r
+\r
+2.2 CS8920-BASED ADAPTER CONFIGURATION\r
+\r
+CS8920-based adapters are shipped from Cirrus Logic configured as Plug\r
+and Play (PnP) enabled. However, since the cs89x0 driver does NOT\r
+support PnP, you must install the CS8920 adapter in a DOS-based PC and\r
+run the CS8900/20 Setup Utility to disable PnP and configure the\r
+adapter before installation in the target Linux system. Failure to do\r
+this will leave the adapter inactive and the driver will be unable to\r
+communicate with the adapter. \r
+\r
+\r
+ **************************************************************** \r
+ * CS8920-BASED ADAPTERS: *\r
+ * * \r
+ * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT. * \r
+ * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST *\r
+ * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND *\r
+ * TO ACTIVATE THE ADAPTER. *\r
+ ****************************************************************\r
+\r
+\r
+\r
+\r
+3.0 LOADING THE DRIVER AS A MODULE\r
+===============================================================================\r
+\r
+If the driver is compiled as a loadable module, you can load the driver module\r
+with the 'modprobe' command. Many of the adapter's configuration parameters can \r
+be specified as command-line arguments to the load command. This facility \r
+provides a means to override the EEPROM's settings or for interface \r
+configuration when an EEPROM is not used.\r
+\r
+Example:\r
+\r
+ insmod cs89x0.o io=0x200 irq=0xA media=aui\r
+\r
+This exmaple loads the module and configures the adapter to use an IO port base\r
+address of 200h, interrupt 10, and use the AUI media connection. The following\r
+configuration options are available on the command line:\r
+\r
+* io=### - specify IO address (200h-360h)\r
+* irq=## - specify interrupt level\r
+* use_dma=1 - Enable DMA\r
+* dma=# - specify dma channel (Driver is compiled to support\r
+ Rx DMA only)\r
+* dmasize=# (16 or 64) - DMA size 16K or 64K. Default value is set to 16.\r
+* media=rj45 - specify media type\r
+ or media=bnc\r
+ or media=aui\r
+ or medai=auto\r
+* duplex=full - specify forced half/full/autonegotiate duplex\r
+ or duplex=half\r
+ or duplex=auto\r
+* debug=# - debug level (only available if the driver was compiled\r
+ for debugging)\r
+\r
+NOTES:\r
+\r
+a) If an EEPROM is present, any specified command-line parameter\r
+ will override the corresponding configuration value stored in\r
+ EEPROM.\r
+\r
+b) The "io" parameter must be specified on the command-line. \r
+\r
+c) In case you can not re-load the driver because Linux system\r
+ returns the "device or resource busy" message, try to re-load it by\r
+ increment the IO port address by one. The driver will write\r
+ commands to the IO base addresses to reset the data port pointer. \r
+ You can specify an I/O address with an address value one greater\r
+ than the configured address. Example, to scan for an adapter\r
+ located at IO base 0x300, specify an IO address of 0x301. \r
+\r
+d) The "duplex=auto" parameter is only supported for the CS8920.\r
+\r
+e) The minimum command-line configuration required if an EEPROM is\r
+ not present is:\r
+\r
+ io \r
+ irq \r
+ media type (no autodetect)\r
+\r
+f) The following addtional parameters are CS89XX defaults (values\r
+ used with no EEPROM or command-line argument).\r
+\r
+ * DMA Burst = enabled\r
+ * IOCHRDY Enabled = enabled\r
+ * UseSA = enabled\r
+ * CS8900 defaults to half-duplex if not specified on command-line\r
+ * CS8920 defaults to autoneg if not specified on command-line\r
+ * Use reset defaults for other config parameters\r
+ * dma_mode = 0\r
+\r
+g) You can use ifconfig to set the adapter's Ethernet address.\r
+\r
+h) Many Linux distributions use the 'modprobe' command to load\r
+ modules. This program uses the '/etc/conf.modules' file to\r
+ determine configuration information which is passed to a driver\r
+ module when it is loaded. All the configuration options which are\r
+ described above may be placed within /etc/conf.modules.\r
+\r
+ For example:\r
+\r
+ > cat /etc/conf.modules\r
+ ...\r
+ alias eth0 cs89x0\r
+ options cs89x0 io=0x0200 dma=5 use_dma=1\r
+ ...\r
+\r
+ In this example we are telling the module system that the\r
+ ethernet driver for this machine should use the cs89x0 driver. We\r
+ are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'\r
+ arguments to the driver when it is loaded.\r
+\r
+i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or\r
+ 7. You will probably find that other DMA channels will not work.\r
+\r
+j) The cs89x0 supports DMA for receiving only. DMA mode is\r
+ significantly more efficient. Flooding a 400 MHz Celeron machine\r
+ with large ping packets consumes 82% of its CPU capacity in non-DMA\r
+ mode. With DMA this is reduced to 45%.\r
+\r
+k) If your Linux kernel was compiled with inbuilt plug-and-play\r
+ support you will be able to find information about the cs89x0 card\r
+ with the command\r
+\r
+ cat /proc/isapnp\r
+\r
+l) If during DMA operation you find erratic behavior or network data\r
+ corruption you should use your PC's BIOS to slow the EISA bus clock.\r
+\r
+\r
+4.0 COMPILING THE DRIVER\r
+===============================================================================\r
+\r
+The cs89x0 driver can be compiled directly into the kernel or compiled into\r
+a loadable device driver module.\r
+\r
+\r
+4.1 COMPILING THE DRIVER AS A LOADABLE MODULE\r
+\r
+To compile the driver into a loadable module, use the following command \r
+(single command line, without quotes):\r
+\r
+"gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall \r
+-Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS \r
+-c cs89x0.c"\r
+\r
+4.2 COMPILING THE DRIVER TO SUPPORT MEMORY MODE\r
+\r
+Support for memory mode was not carried over into the 2.3 series kernels.\r
+\r
+4.3 COMPILING THE DRIVER TO SUPPORT Rx DMA\r
+\r
+The compile-time optionality for DMA was removed in the 2.3 kernel\r
+series. DMA support is now unconditionally part of the driver. It is\r
+enabled by the 'use_dma=1' module option.\r
+\r
+4.4 COMPILING THE DRIVER INTO THE KERNEL\r
+\r
+If your Linux distribution already has support for the cs89x0 driver\r
+then simply copy the source file to the /usr/src/linux/drivers/net\r
+directory to replace the original ones and run the make utility to\r
+rebuild the kernel. See Step 3 for rebuilding the kernel.\r
+\r
+If your Linux does not include the cs89x0 driver, you need to edit three \r
+configuration files, copy the source file to the /usr/src/linux/drivers/net\r
+directory, and then run the make utility to rebuild the kernel.\r
+\r
+1. Edit the following configuration files by adding the statements as\r
+indicated. (When possible, try to locate the added text to the section of the\r
+file containing similar statements).\r
+\r
+\r
+a.) In /usr/src/linux/drivers/net/Config.in, add:\r
+\r
+tristate 'CS89x0 support' CONFIG_CS89x0\r
+\r
+Example:\r
+\r
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then\r
+ tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I\r
+ fi\r
+\r
+ tristate 'CS89x0 support' CONFIG_CS89x0\r
+\r
+ tristate 'NE2000/NE1000 support' CONFIG_NE2000\r
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then\r
+ tristate 'NI5210 support' CONFIG_NI52\r
+\r
+\r
+b.) In /usr/src/linux/drivers/net/Makefile, add the following lines: \r
+\r
+ifeq ($(CONFIG_CS89x0),y)\r
+L_OBJS += cs89x0.o\r
+else\r
+ ifeq ($(CONFIG_CS89x0),m)\r
+ M_OBJS += cs89x0.o\r
+ endif\r
+endif\r
+\r
+\r
+c.) In /linux/drivers/net/Space.c file, add the line:\r
+\r
+extern int cs89x0_probe(struct device *dev);\r
+\r
+\r
+Example:\r
+\r
+ extern int ultra_probe(struct device *dev);\r
+ extern int wd_probe(struct device *dev);\r
+ extern int el2_probe(struct device *dev);\r
+\r
+ extern int cs89x0_probe(struct device *dev);\r
+\r
+ extern int ne_probe(struct device *dev);\r
+ extern int hp_probe(struct device *dev);\r
+ extern int hp_plus_probe(struct device *dev);\r
+\r
+\r
+Also add:\r
+\r
+ #ifdef CONFIG_CS89x0\r
+ { cs89x0_probe,0 },\r
+ #endif\r
+\r
+\r
+2.) Copy the driver source files (cs89x0.c and cs89x0.h) \r
+into the /usr/src/linux/drivers/net directory.\r
+\r
+\r
+3.) Go to /usr/src/linux directory and run 'make config' followed by 'make dep' \r
+and finally 'make' (or make bzImage) to rebuild the kernel. \r
+\r
+4.) Use the DOS 'setup' utility to disable plug and play on the NIC.\r
+\r
+\r
+5.0 TESTING AND TROUBLESHOOTING\r
+===============================================================================\r
+\r
+5.1 KNOWN DEFECTS and LIMITATIONS\r
+\r
+Refer to the RELEASE.TXT file distributed as part of this archive for a list of \r
+known defects, driver limitations, and work arounds.\r
+\r
+\r
+5.2 TESTING THE ADAPTER\r
+\r
+Once the adapter has been installed and configured, the diagnostic option of \r
+the CS8900/20 Setup Utility can be used to test the functionality of the \r
+adapter and its network connection. Use the diagnostics 'Self Test' option to\r
+test the functionality of the adapter with the hardware configuration you have\r
+assigned. You can use the diagnostics 'Network Test' to test the ability of the\r
+adapter to communicate across the Ethernet with another PC equipped with a \r
+CS8900/20-based adapter card (it must also be running the CS8900/20 Setup \r
+Utility).\r
+\r
+ NOTE: The Setup Utility's diagnostics are designed to run in a\r
+ DOS-only operating system environment. DO NOT run the diagnostics \r
+ from a DOS or command prompt session under Windows 95, Windows NT, \r
+ OS/2, or other operating system.\r
+\r
+To run the diagnostics tests on the CS8900/20 adapter:\r
+\r
+ 1.) Boot DOS on the PC and start the CS8900/20 Setup Utility.\r
+\r
+ 2.) The adapter's current configuration is displayed. Hit the ENTER key to\r
+ get to the main menu.\r
+\r
+ 4.) Select 'Diagnostics' (ALT-G) from the main menu. \r
+ * Select 'Self-Test' to test the adapter's basic functionality.\r
+ * Select 'Network Test' to test the network connection and cabling.\r
+\r
+\r
+5.2.1 DIAGNOSTIC SELF-TEST\r
+\r
+The diagnostic self-test checks the adapter's basic functionality as well as \r
+its ability to communicate across the ISA bus based on the system resources \r
+assigned during hardware configuration. The following tests are performed:\r
+\r
+ * IO Register Read/Write Test\r
+ The IO Register Read/Write test insures that the CS8900/20 can be \r
+ accessed in IO mode, and that the IO base address is correct.\r
+\r
+ * Shared Memory Test\r
+ The Shared Memory test insures the CS8900/20 can be accessed in memory \r
+ mode and that the range of memory addresses assigned does not conflict \r
+ with other devices in the system.\r
+\r
+ * Interrupt Test\r
+ The Interrupt test insures there are no conflicts with the assigned IRQ\r
+ signal.\r
+\r
+ * EEPROM Test\r
+ The EEPROM test insures the EEPROM can be read.\r
+\r
+ * Chip RAM Test\r
+ The Chip RAM test insures the 4K of memory internal to the CS8900/20 is\r
+ working properly.\r
+\r
+ * Internal Loop-back Test\r
+ The Internal Loop Back test insures the adapter's transmitter and \r
+ receiver are operating properly. If this test fails, make sure the \r
+ adapter's cable is connected to the network (check for LED activity for \r
+ example).\r
+\r
+ * Boot PROM Test\r
+ The Boot PROM test insures the Boot PROM is present, and can be read.\r
+ Failure indicates the Boot PROM was not successfully read due to a\r
+ hardware problem or due to a conflicts on the Boot PROM address\r
+ assignment. (Test only applies if the adapter is configured to use the\r
+ Boot PROM option.)\r
+\r
+Failure of a test item indicates a possible system resource conflict with \r
+another device on the ISA bus. In this case, you should use the Manual Setup \r
+option to reconfigure the adapter by selecting a different value for the system\r
+resource that failed.\r
+\r
+\r
+5.2.2 DIAGNOSTIC NETWORK TEST\r
+\r
+The Diagnostic Network Test verifies a working network connection by \r
+transferring data between two CS8900/20 adapters installed in different PCs \r
+on the same network. (Note: the diagnostic network test should not be run \r
+between two nodes across a router.) \r
+\r
+This test requires that each of the two PCs have a CS8900/20-based adapter\r
+installed and have the CS8900/20 Setup Utility running. The first PC is \r
+configured as a Responder and the other PC is configured as an Initiator. \r
+Once the Initiator is started, it sends data frames to the Responder which \r
+returns the frames to the Initiator.\r
+\r
+The total number of frames received and transmitted are displayed on the \r
+Initiator's display, along with a count of the number of frames received and \r
+transmitted OK or in error. The test can be terminated anytime by the user at \r
+either PC.\r
+\r
+To setup the Diagnostic Network Test:\r
+\r
+ 1.) Select a PC with a CS8900/20-based adapter and a known working network\r
+ connection to act as the Responder. Run the CS8900/20 Setup Utility \r
+ and select 'Diagnostics -> Network Test -> Responder' from the main \r
+ menu. Hit ENTER to start the Responder.\r
+\r
+ 2.) Return to the PC with the CS8900/20-based adapter you want to test and\r
+ start the CS8900/20 Setup Utility. \r
+\r
+ 3.) From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.\r
+ Hit ENTER to start the test.\r
+ \r
+You may stop the test on the Initiator at any time while allowing the Responder\r
+to continue running. In this manner, you can move to additional PCs and test \r
+them by starting the Initiator on another PC without having to stop/start the \r
+Responder.\r
+ \r
+\r
+\r
+5.3 USING THE ADAPTER'S LEDs\r
+\r
+The 2 and 3-media adapters have two LEDs visible on the back end of the board \r
+located near the 10Base-T connector. \r
+\r
+Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T \r
+connection. (Only applies to 10Base-T. The green LED has no significance for\r
+a 10Base-2 or AUI connection.)\r
+\r
+TX/RX LED: The yellow LED lights briefly each time the adapter transmits or \r
+receives data. (The yellow LED will appear to "flicker" on a typical network.)\r
+\r
+\r
+5.4 RESOLVING I/O CONFLICTS\r
+\r
+An IO conflict occurs when two or more adapter use the same ISA resource (IO \r
+address, memory address or IRQ). You can usually detect an IO conflict in one \r
+of four ways after installing and or configuring the CS8900/20-based adapter:\r
+\r
+ 1.) The system does not boot properly (or at all).\r
+\r
+ 2.) The driver can not communicate with the adapter, reporting an "Adapter\r
+ not found" error message.\r
+\r
+ 3.) You cannot connect to the network or the driver will not load.\r
+\r
+ 4.) If you have configured the adapter to run in memory mode but the driver\r
+ reports it is using IO mode when loading, this is an indication of a\r
+ memory address conflict.\r
+\r
+If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a \r
+diagnostic self-test. Normally, the ISA resource in conflict will fail the \r
+self-test. If so, reconfigure the adapter selecting another choice for the \r
+resource in conflict. Run the diagnostics again to check for further IO \r
+conflicts.\r
+\r
+In some cases, such as when the PC will not boot, it may be necessary to remove\r
+the adapter and reconfigure it by installing it in another PC to run the \r
+CS8900/20 Setup Utility. Once reinstalled in the target system, run the \r
+diagnostics self-test to ensure the new configuration is free of conflicts \r
+before loading the driver again.\r
+\r
+When manually configuring the adapter, keep in mind the typical ISA system \r
+resource usage as indicated in the tables below.\r
+\r
+I/O Address Device IRQ Device\r
+----------- -------- --- --------\r
+ 200-20F Game I/O adapter 3 COM2, Bus Mouse\r
+ 230-23F Bus Mouse 4 COM1\r
+ 270-27F LPT3: third parallel port 5 LPT2\r
+ 2F0-2FF COM2: second serial port 6 Floppy Disk controller\r
+ 320-32F Fixed disk controller 7 LPT1\r
+ 8 Real-time Clock\r
+ 9 EGA/VGA display adapter \r
+ 12 Mouse (PS/2) \r
+Memory Address Device 13 Math Coprocessor\r
+-------------- --------------------- 14 Hard Disk controller\r
+A000-BFFF EGA Graphics Adpater\r
+A000-C7FF VGA Graphics Adpater\r
+B000-BFFF Mono Graphics Adapter\r
+B800-BFFF Color Graphics Adapter\r
+E000-FFFF AT BIOS\r
+\r
+\r
+\r
+\r
+6.0 TECHNICAL SUPPORT\r
+===============================================================================\r
+\r
+6.1 CONTACTING CIRRUS LOGIC'S TECHNICAL SUPPORT\r
+\r
+Cirrus Logic's CS89XX Technical Application Support can be reached at:\r
+\r
+Telephone :(800) 888-5016 (from inside U.S. and Canada)\r
+ :(512) 442-7555 (from outside the U.S. and Canada)\r
+Fax :(512) 912-3871\r
+Email :ethernet@crystal.cirrus.com\r
+WWW :http://www.cirrus.com\r
+\r
+\r
+6.2 INFORMATION REQUIRED BEFORE CONTACTING TECHNICAL SUPPORT\r
+\r
+Before contacting Cirrus Logic for technical support, be prepared to provide as \r
+Much of the following information as possible. \r
+\r
+1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)\r
+\r
+2.) Adapter configuration\r
+\r
+ * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel\r
+ * Plug and Play enabled/disabled (CS8920-based adapters only)\r
+ * Configured for media auto-detect or specific media type (which type). \r
+\r
+3.) PC System's Configuration\r
+\r
+ * Plug and Play system (yes/no)\r
+ * BIOS (make and version)\r
+ * System make and model\r
+ * CPU (type and speed)\r
+ * System RAM\r
+ * SCSI Adapter\r
+\r
+4.) Software\r
+\r
+ * CS89XX driver and version\r
+ * Your network operating system and version\r
+ * Your system's OS version \r
+ * Version of all protocol support files\r
+\r
+5.) Any Error Message displayed.\r
+\r
+\r
+\r
+6.3 OBTAINING THE LATEST DRIVER VERSION\r
+\r
+You can obtain the latest CS89XX drivers and support software from Cirrus Logic's \r
+Web site. You can also contact Cirrus Logic's Technical Support (email:\r
+ethernet@crystal.cirrus.com) and request that you be registered for automatic \r
+software-update notification.\r
+\r
+Cirrus Logic maintains a web page at http://www.cirrus.com with the\r
+the latest drivers and technical publications.\r
+\r
+\r
+6.4 Current maintainer\r
+\r
+In February 2000 the maintenance of this driver was assumed by Andrew\r
+Morton <andrewm@uow.edu.au>\r
+\r
+\r
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 49
+SUBLEVEL = 50
EXTRAVERSION =
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
printk(" %d",local_bh_count(i));
printk(" ]\nStack dumps:");
- for(i=0;i< smp_num_cpus;i++) {
+ for(i = 0; i < smp_num_cpus; i++) {
unsigned long esp;
- if(i==cpu)
+ if (i == cpu)
continue;
printk("\nCPU %d:",i);
esp = init_tss[i].esp0;
- if(esp==NULL) {
+ if (!esp) {
/* tss->esp0 is set to NULL in cpu_init(),
* it's initialized when the cpu returns to user
* space. -- manfreds
if (vaddr >= end)
break;
#if CONFIG_X86_PAE
- pmd = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE);
+ pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
#else
pmd = (pmd_t *)pgd;
#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_IDEPCI is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
CONFIG_BLK_DEV_SL82C105=y
CONFIG_BLK_DEV_IDE_PMAC=y
CONFIG_BLK_DEV_IDEDMA_PMAC=y
#
# SCSI low-level drivers
#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AHA152X is not set
CONFIG_SCSI_MESH=y
CONFIG_SCSI_MESH_SYNC_RATE=5
CONFIG_SCSI_MAC53C94=y
-# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
#
# IEEE 1394 (FireWire) support
#
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_ETHERTAP is not set
# CONFIG_NET_SB1000 is not set
CONFIG_FB_MATROX_G100=y
# CONFIG_FB_MATROX_MULTIHEAD is not set
CONFIG_FB_ATY=y
-CONFIG_FB_ATY128=y
+# CONFIG_FB_ATY128 is not set
CONFIG_FB_3DFX=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FBCON_ADVANCED is not set
# CONFIG_WATCHDOG is not set
CONFIG_NVRAM=y
# CONFIG_RTC is not set
+# CONFIG_EFI_RTC is not set
#
# Video For Linux
# CONFIG_USB_STORAGE is not set
# CONFIG_USB_DABUSB is not set
# CONFIG_USB_PLUSB is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RIO500 is not set
#
# USB HID
# CONFIG_USB_HID is not set
CONFIG_USB_KBD=y
CONFIG_USB_MOUSE=y
-# CONFIG_USB_GRAPHIRE is not set
+# CONFIG_USB_WACOM is not set
# CONFIG_USB_WMFORCE is not set
CONFIG_INPUT_KEYBDEV=y
CONFIG_INPUT_MOUSEDEV=y
#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_IDEPCI is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
CONFIG_BLK_DEV_SL82C105=y
CONFIG_BLK_DEV_IDE_PMAC=y
CONFIG_BLK_DEV_IDEDMA_PMAC=y
#
# SCSI low-level drivers
#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AHA152X is not set
CONFIG_SCSI_MESH=y
CONFIG_SCSI_MESH_SYNC_RATE=5
CONFIG_SCSI_MAC53C94=y
-# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
#
# IEEE 1394 (FireWire) support
#
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_ETHERTAP is not set
# CONFIG_NET_SB1000 is not set
CONFIG_FB_MATROX_G100=y
# CONFIG_FB_MATROX_MULTIHEAD is not set
CONFIG_FB_ATY=y
-CONFIG_FB_ATY128=y
+# CONFIG_FB_ATY128 is not set
CONFIG_FB_3DFX=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FBCON_ADVANCED is not set
# CONFIG_WATCHDOG is not set
CONFIG_NVRAM=y
# CONFIG_RTC is not set
+# CONFIG_EFI_RTC is not set
#
# Video For Linux
# CONFIG_USB_STORAGE is not set
# CONFIG_USB_DABUSB is not set
# CONFIG_USB_PLUSB is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RIO500 is not set
#
# USB HID
# CONFIG_USB_HID is not set
CONFIG_USB_KBD=y
CONFIG_USB_MOUSE=y
-# CONFIG_USB_GRAPHIRE is not set
+# CONFIG_USB_WACOM is not set
# CONFIG_USB_WMFORCE is not set
CONFIG_INPUT_KEYBDEV=y
CONFIG_INPUT_MOUSEDEV=y
OX_OBJS += prep_setup.o
endif
ifeq ($(CONFIG_GEMINI),y)
- O_OBJS += gemini_prom.o gemini_pci.o gemini_setup.o
+ O_OBJS += gemini_prom.o gemini_pci.o gemini_setup.o open_pic.o
endif
all: $(KHEAD) kernel.o
$(TOPDIR)/include/asm/ptrace.h
$(CC) $(CFLAGS) -S mk_defs.c
cp ppc_defs.head ppc_defs.h
+# for bk, this way we can write to the file even if it's not checked out
+ chmod u+w ppc_defs.h
grep '^#define' mk_defs.s >> ppc_defs.h
rm mk_defs.s
{
struct device_node *np;
+ if (_machine != _MACH_Pmac)
+ return;
+
np = find_devices("mac-io");
while (np != NULL) {
/* KeyLargo contains several (5 ?) FCR registers in mac-io,
mtspr IBAT0L,r8
mtspr IBAT0U,r11
#if 0 /* Useful debug code, please leave in for now so I don't have to
- * look at docs when I need to setup a BAT ;
+ * look at docs when I need to setup a BAT ...
*/
bl setup_screen_bat
#endif
* prep needs the mmu to be turned on here, but pmac already has it on.
* this shouldn't bother the pmac since it just gets turned on again
* as we jump to our code at KERNELBASE. -- Cort
+ * Actually no, pmac doesn't have it on any more. BootX enters with MMU
+ * off, and in other cases, we now turn it off before changing BATs above.
*/
turn_on_mmu:
mfmsr r0
li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
mtspr SRR0,r4
mtspr SRR1,r3
+ SYNC
rfi
/* Load up the kernel context */
2:
tlbsync /* ... on all CPUs */
sync
#endif
+
bl load_up_mmu
/* Set up for using our exception vectors */
ori r3,r3,start_kernel@l
mtspr SRR0,r3
mtspr SRR1,r4
+ SYNC
rfi /* enable MMU and jump to start_kernel */
/*
li r3,0
mtspr DBAT1U,r3
mtspr IBAT1U,r3
- lis r3, 0x9100
+ lis r3, 0x8200
ori r4,r3,0x2a
mtspr DBAT1L,r4
mtspr IBAT1L,r4
- ori r3,r3,(BL_8M<<2)|0x2 /* set up BAT registers for 604 */
+ ori r3,r3,(BL_16M<<2)|0x2 /* set up BAT registers for 604 */
mtspr DBAT1U,r3
mtspr IBAT1U,r3
blr
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/irq.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/hydra.h>
#include <asm/system.h>
}
#endif /* __SMP__ */
+static struct proc_dir_entry * root_irq_dir;
+static struct proc_dir_entry * irq_dir [NR_IRQS];
+static struct proc_dir_entry * smp_affinity_entry [NR_IRQS];
+
+unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0xffffffff};
+
+#define HEX_DIGITS 8
+
+static int irq_affinity_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ if (count < HEX_DIGITS+1)
+ return -EINVAL;
+ return sprintf (page, "%08x\n", irq_affinity[(int)data]);
+}
+
+static unsigned int parse_hex_value (const char *buffer,
+ unsigned long count, unsigned long *ret)
+{
+ unsigned char hexnum [HEX_DIGITS];
+ unsigned long value;
+ int i;
+
+ if (!count)
+ return -EINVAL;
+ if (count > HEX_DIGITS)
+ count = HEX_DIGITS;
+ if (copy_from_user(hexnum, buffer, count))
+ return -EFAULT;
+
+ /*
+ * Parse the first 8 characters as a hex string, any non-hex char
+ * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
+ */
+ value = 0;
+
+ for (i = 0; i < count; i++) {
+ unsigned int c = hexnum[i];
+
+ switch (c) {
+ case '0' ... '9': c -= '0'; break;
+ case 'a' ... 'f': c -= 'a'-10; break;
+ case 'A' ... 'F': c -= 'A'-10; break;
+ default:
+ goto out;
+ }
+ value = (value << 4) | c;
+ }
+out:
+ *ret = value;
+ return 0;
+}
+
+static int irq_affinity_write_proc (struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int irq = (int) data, full_count = count, err;
+ unsigned long new_value;
+
+ if (!irq_desc[irq].handler->set_affinity)
+ return -EIO;
+
+ err = parse_hex_value(buffer, count, &new_value);
+
+#if CONFIG_SMP
+ /*
+ * Do not allow disabling IRQs completely - it's a too easy
+ * way to make the system unusable accidentally :-) At least
+ * one online CPU still has to be targeted.
+ */
+ if (!(new_value & cpu_online_map))
+ return -EINVAL;
+#endif
+
+ irq_affinity[irq] = new_value;
+ irq_desc[irq].handler->set_affinity(irq, new_value);
+
+ return full_count;
+}
+
+static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long *mask = (unsigned long *) data;
+ if (count < HEX_DIGITS+1)
+ return -EINVAL;
+ return sprintf (page, "%08lx\n", *mask);
+}
+
+static int prof_cpu_mask_write_proc (struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ unsigned long *mask = (unsigned long *) data, full_count = count, err;
+ unsigned long new_value;
+
+ err = parse_hex_value(buffer, count, &new_value);
+ if (err)
+ return err;
+
+ *mask = new_value;
+ return full_count;
+}
+
+#define MAX_NAMELEN 10
+
+static void register_irq_proc (unsigned int irq)
+{
+ struct proc_dir_entry *entry;
+ char name [MAX_NAMELEN];
+
+ if (!root_irq_dir || (irq_desc[irq].handler == NULL))
+ return;
+
+ memset(name, 0, MAX_NAMELEN);
+ sprintf(name, "%d", irq);
+
+ /* create /proc/irq/1234 */
+ irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+
+ /* create /proc/irq/1234/smp_affinity */
+ entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]);
+
+ entry->nlink = 1;
+ entry->data = (void *)irq;
+ entry->read_proc = irq_affinity_read_proc;
+ entry->write_proc = irq_affinity_write_proc;
+
+ smp_affinity_entry[irq] = entry;
+}
+
+unsigned long prof_cpu_mask = -1;
+
+void init_irq_proc (void)
+{
+ struct proc_dir_entry *entry;
+ int i;
+
+ /* create /proc/irq */
+ root_irq_dir = proc_mkdir("irq", 0);
+
+ /* create /proc/irq/prof_cpu_mask */
+ entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir);
+
+ entry->nlink = 1;
+ entry->data = (void *)&prof_cpu_mask;
+ entry->read_proc = prof_cpu_mask_read_proc;
+ entry->write_proc = prof_cpu_mask_write_proc;
+
+ /*
+ * Create entries for all existing IRQs.
+ */
+ for (i = 0; i < NR_IRQS; i++) {
+ if (irq_desc[i].handler == NULL)
+ continue;
+ register_irq_proc(i);
+ }
+}
return str;
}
-int pcibios_assign_resource(struct pci_dev *pdev, int resource)
-{
- return 0;
-}
-
/* the next two are stolen from the alpha port... */
void __init
pcibios_update_resource(struct pci_dev *dev, struct resource *root,
}
return 0;
}
-
-/*
- * Assign new address to PCI resource. We hope our resource information
- * is complete. We don't re-assign resources unless we are
- * forced to do so.
- *
- * Expects start=0, end=size-1, flags=resource type.
- */
-
-int pci_assign_resource(struct pci_dev *dev, int i)
-{
- struct resource *r = &dev->resource[i];
- struct resource *pr = pci_find_parent_resource(dev, r);
- unsigned long size = r->end + 1;
- u32 new, check;
-
- if (!pr) {
- printk(KERN_ERR "PCI: Cannot find parent resource for device %s\n", dev->slot_name);
- return -EINVAL;
- }
- if (r->flags & IORESOURCE_IO) {
- if (allocate_resource(pr, r, size, 0x100, ~0, size, NULL, NULL)) {
- printk(KERN_ERR "PCI: Allocation of I/O region %s/%d (%ld bytes) failed\n", dev->slot_name, i, size);
- return -EBUSY;
- }
- } else {
- if (allocate_resource(pr, r, size, 0x10000, ~0, size, NULL, NULL)) {
- printk(KERN_ERR "PCI: Allocation of memory region %s/%d (%ld bytes) failed\n", dev->slot_name, i, size);
- return -EBUSY;
- }
- }
- if (i < 6) {
- int reg = PCI_BASE_ADDRESS_0 + 4*i;
- new = r->start | (r->flags & PCI_REGION_FLAG_MASK);
- pci_write_config_dword(dev, reg, new);
- pci_read_config_dword(dev, reg, &check);
- if (new != check)
- printk(KERN_ERR "PCI: Error while updating region %s/%d (%08x != %08x)\n", dev->slot_name, i, new, check);
- } else if (i == PCI_ROM_RESOURCE) {
- r->flags |= PCI_ROM_ADDRESS_ENABLE;
- pci_write_config_dword(dev, dev->rom_base_reg, r->start | (r->flags & PCI_REGION_FLAG_MASK));
- }
- printk("PCI: Assigned addresses %08lx-%08lx to region %s/%d\n", r->start, r->end, dev->slot_name, i);
- return 0;
-}
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/prom.h>
+#include <asm/pci-bridge.h>
#include "pmac_pic.h"
/* pmac */struct pmac_irq_hw {
ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
? "enabled" : "disabled");
- feature_init();
-
#ifdef CONFIG_KGDB
zs_kgdb_hook(0);
#endif
|| p2pbridge->parent == NULL
|| strcmp(p2pbridge->parent->name, "pci") != 0)
return;
-
if (pci_device_loc(p2pbridge, &bus, &devfn) < 0)
return;
-
- pcibios_read_config_word(bus, devfn, PCI_BRIDGE_CONTROL, &val);
+ if (ppc_md.pcibios_read_config_word(bus, devfn, PCI_BRIDGE_CONTROL, &val) < 0) {
+ printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n");
+ return;
+ }
val &= ~PCI_BRIDGE_CTL_MASTER_ABORT;
- pcibios_write_config_word(bus, devfn, PCI_BRIDGE_CONTROL, val);
- pcibios_read_config_word(bus, devfn, PCI_BRIDGE_CONTROL, &val);
+ ppc_md.pcibios_write_config_word(bus, devfn, PCI_BRIDGE_CONTROL, val);
+ ppc_md.pcibios_read_config_word(bus, devfn, PCI_BRIDGE_CONTROL, &val);
}
static void __init ohare_init(void)
{
if (disp_bi == 0)
return;
- drawstring(s);
- drawchar('\n');
+ prom_drawstring(s);
+ prom_drawchar('\n');
}
#endif CONFIG_BOOTX_TEXT
extern unsigned long pte_misses;
extern unsigned long pte_errors;
-struct file_operations ppc_htab_operations = {
- llseek: ppc_htab_lseek,
- read: ppc_htab_read,
- write: ppc_htab_write,
-};
-
/* these will go into processor.h when I'm done debugging -- Cort */
#define MMCR0 952
#define MMCR0_PMC1_CYCLES (0x1<<7)
#define PMC1 953
#define PMC2 954
+struct file_operations ppc_htab_operations = {
+ llseek: ppc_htab_lseek,
+ read: ppc_htab_read,
+ write: ppc_htab_write,
+};
+
char *pmc1_lookup(unsigned long mmcr0)
{
switch ( mmcr0 & (0x7f<<7) )
static void clearscreen(void);
static void flushscreen(void);
-void drawchar(char c);
-void drawstring(const char *c);
-static void drawhex(unsigned long v);
+void prom_drawchar(char c);
+void prom_drawstring(const char *c);
+void prom_drawhex(unsigned long v);
static void scrollscreen(void);
static void draw_byte(unsigned char c, long locX, long locY);
static unsigned char vga_font[cmapsz];
+int bootx_text_mapped = 1;
+
#endif /* CONFIG_BOOTX_TEXT */
{
#ifdef CONFIG_BOOTX_TEXT
if (RELOC(disp_bi) != 0)
- drawstring(msg);
+ prom_drawstring(msg);
#endif
return;
}
#ifdef CONFIG_BOOTX_TEXT
prom_print(RELOC("booting...\n"));
flushscreen();
+ RELOC(bootx_text_mapped) = 0;
#endif
return phys;
}
prom_welcome(PTRRELOC(RELOC(disp_bi)), phys);
prom_print(RELOC("booting...\n"));
}
+ RELOC(bootx_text_mapped) = 0;
#endif
return phys;
prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n"));
prom_print(RELOC("\nstarted at : 0x"));
- drawhex(phys);
+ prom_drawhex(phys);
prom_print(RELOC("\nlinked at : 0x"));
- drawhex(KERNELBASE);
+ prom_drawhex(KERNELBASE);
prom_print(RELOC("\nframe buffer at : 0x"));
- drawhex((unsigned long)bi->dispDeviceBase);
+ prom_drawhex((unsigned long)bi->dispDeviceBase);
prom_print(RELOC(" (phys), 0x"));
- drawhex((unsigned long)bi->logicalDisplayBase);
+ prom_drawhex((unsigned long)bi->logicalDisplayBase);
prom_print(RELOC(" (log)"));
+ prom_print(RELOC("\nklimit : 0x"));
+ prom_drawhex(RELOC(klimit));
prom_print(RELOC("\nMSR : 0x"));
__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
- drawhex(flags);
+ prom_drawhex(flags);
__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
pvr >>= 16;
if (pvr > 1) {
prom_print(RELOC("\nHID0 : 0x"));
__asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
- drawhex(flags);
+ prom_drawhex(flags);
}
if (pvr == 8 || pvr == 12) {
prom_print(RELOC("\nICTC : 0x"));
__asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
- drawhex(flags);
+ prom_drawhex(flags);
}
prom_print(RELOC("\n\n"));
}
prom_print(RELOC("Initializing fake screen\n"));
- call_prom(RELOC("getprop"), 4, 1, dp, RELOC("width"), &width, sizeof(width));
- call_prom(RELOC("getprop"), 4, 1, dp, RELOC("height"), &height, sizeof(height));
- call_prom(RELOC("getprop"), 4, 1, dp, RELOC("depth"), &len, sizeof(len));
+ call_prom(RELOC("getprop"), 4, 1, dp, RELOC("width"),
+ &width, sizeof(width));
+ call_prom(RELOC("getprop"), 4, 1, dp, RELOC("height"),
+ &height, sizeof(height));
+ call_prom(RELOC("getprop"), 4, 1, dp, RELOC("depth"),
+ &depth, sizeof(depth));
pitch = width * ((depth + 7) / 8);
- call_prom(RELOC("getprop"), 4, 1, dp, RELOC("linebytes"), &len, sizeof(len));
+ call_prom(RELOC("getprop"), 4, 1, dp, RELOC("linebytes"),
+ &pitch, sizeof(pitch));
address = 0;
- call_prom(RELOC("getprop"), 4, 1, dp, RELOC("address"), &len, sizeof(len));
+ call_prom(RELOC("getprop"), 4, 1, dp, RELOC("address"),
+ &address, sizeof(address));
if (address == 0) {
prom_print(RELOC("Failed to get address\n"));
return;
bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
bi->dispDeviceRect[2] = width;
bi->dispDeviceRect[3] = height;
- RELOC(disp_bi) = 0;
}
#endif
disp_bi->logicalDisplayBase =
ioremap((unsigned long) disp_bi->dispDeviceBase,
disp_bi->dispDeviceRowBytes * disp_bi->dispDeviceRect[3]);
+ bootx_text_mapped = 1;
}
/* Calc the base address of a given point (x,y) */
__pmac
void
-drawchar(char c)
+prom_drawchar(char c)
{
unsigned long offset = reloc_offset();
int cline = 0, x;
+ if (!RELOC(bootx_text_mapped))
+ return;
+
switch (c) {
case '\b':
if (RELOC(g_loc_X) > 0)
__pmac
void
-drawstring(const char *c)
+prom_drawstring(const char *c)
{
+ unsigned long offset = reloc_offset();
+
+ if (!RELOC(bootx_text_mapped))
+ return;
while (*c)
- drawchar(*c++);
+ prom_drawchar(*c++);
}
__pmac
-static void
-drawhex(unsigned long v)
+void
+prom_drawhex(unsigned long v)
{
static char hex_table[] = "0123456789abcdef";
unsigned long offset = reloc_offset();
- drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]);
- drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]);
+ if (!RELOC(bootx_text_mapped))
+ return;
+ prom_drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]);
+ prom_drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]);
}
#endif
#include <asm/bootx.h>
#include <asm/machdep.h>
+#include <asm/feature.h>
#ifdef CONFIG_OAK
#include "oak_setup.h"
#endif /* CONFIG_OAK */
unsigned long r6, unsigned long r7)
{
parse_bootinfo();
-
+
if ( ppc_md.progress ) ppc_md.progress("id mach(): start", 0x100);
#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx)
ppc_md.setup_arch();
if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab);
+ paging_init();
}
void ppc_generic_ide_fix_driveid(struct hd_driveid *id)
id->eide_dma_time = __le16_to_cpu(id->eide_dma_time);
id->eide_pio = __le16_to_cpu(id->eide_pio);
id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy);
- for (i=0; i<2 i++)
+ for (i=0; i<2; i++)
id->words69_70[i] = __le16_to_cpu(id->words69_70[i]);
- for (i=0; i<4 i++)
+ for (i=0; i<4; i++)
id->words71_74[i] = __le16_to_cpu(id->words71_74[i]);
id->queue_depth = __le16_to_cpu(id->queue_depth);
- for (i=0; i<4 i++)
+ for (i=0; i<4; i++)
id->words76_79[i] = __le16_to_cpu(id->words76_79[i]);
id->major_rev_num = __le16_to_cpu(id->major_rev_num);
id->minor_rev_num = __le16_to_cpu(id->minor_rev_num);
}
#endif
#if 0
- setbat(0, disp_bi->dispDeviceBase, disp_bi->dispDeviceBase, 0x100000, IO_PAGE);
- disp_bi->logicalDisplayBase = disp_bi->dispDeviceBase;
+// This is bogus, BAT must be aligned.
+// setbat(0, disp_bi->dispDeviceBase, disp_bi->dispDeviceBase, 0x100000, IO_PAGE);
+// disp_bi->logicalDisplayBase = disp_bi->dispDeviceBase;
#endif
ioremap_base = 0xf0000000;
break;
static volatile unsigned char *sccc, *sccd;
unsigned long TXRDY, RXRDY;
extern void xmon_printf(const char *fmt, ...);
-extern void drawchar(char);
-extern void drawstring(const char *str);
+extern void prom_drawchar(char);
+extern void prom_drawstring(const char *str);
static int xmon_expect(const char *str, unsigned int timeout);
static int console = 0;
-static int use_screen = 0;
+static int use_screen = 1; /* default */
static int via_modem = 0;
static int xmon_use_sccb = 0;
static struct device_node *macio_node;
{
volatile unsigned char *base;
+ use_screen = 0;
+
if ( _machine == _MACH_Pmac )
{
struct device_node *np;
/* needs to be hacked if xmon_printk is to be used
from within find_via_pmu() */
if (!via_modem && disp_bi && find_via_pmu()) {
- drawstring("xmon uses screen and keyboard\n");
+ prom_drawstring("xmon uses screen and keyboard\n");
use_screen = 1;
return;
}
if (use_screen) {
/* write it on the screen */
for (i = 0; i < nb; ++i)
- drawchar(*p++);
+ prom_drawchar(*p++);
return nb;
}
#endif
ct = 1;
--i;
} else {
+ prom_drawchar(c);
if (console)
printk("%c", c);
ct = 0;
do {
if (--t < 0) {
on = 1 - on;
- drawchar(on? 0xdb: 0x20);
- drawchar('\b');
+ prom_drawchar(on? 0xdb: 0x20);
+ prom_drawchar('\b');
t = 200000;
}
pmu_poll();
} while (xmon_pmu_keycode == -1);
k = xmon_pmu_keycode;
if (on)
- drawstring(" \b");
+ prom_drawstring(" \b");
/* test for shift keys */
if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
{
int i, x;
+ if (macio_node != 0)
+ feature_set(macio_node, FEATURE_Serial_enable);
if (via_modem && macio_node != 0) {
unsigned int t0;
for (;;) {
c = xmon_read_poll();
if (c == -1) {
- if (readtb() - t0 > timeout) {
- printk("timeout\n");
+ if (readtb() - t0 > timeout)
return 0;
- }
continue;
}
if (c == '\n')
break;
- printk("%c", c);
if (c != '\r' && lineptr < &line[sizeof(line) - 1])
*lineptr++ = c;
}
xmon(struct pt_regs *excp)
{
struct pt_regs regs;
- int msr, cmd;
+ int msr, cmd, i;
+ unsigned *sp;
if (excp == NULL) {
asm volatile ("stw 0,0(%0)\n\
excp = ®s;
}
+ prom_drawstring("xmon pc="); prom_drawhex(excp->nip);
+ prom_drawstring(" lr="); prom_drawhex(excp->link);
+ prom_drawstring(" msr="); prom_drawhex(excp->msr);
+ prom_drawstring(" trap="); prom_drawhex(excp->trap);
+ prom_drawstring(" sp="); prom_drawhex(excp->gpr[1]);
+ sp = &excp->gpr[0];
+ for (i = 0; i < 32; ++i) {
+ if ((i & 7) == 0)
+ prom_drawstring("\n");
+ prom_drawstring(" ");
+ prom_drawhex(sp[i]);
+ }
+ sp = (unsigned *) excp->gpr[1];
+ for (i = 0; i < 64; ++i) {
+ if ((i & 7) == 0) {
+ prom_drawstring("\n");
+ prom_drawhex(sp);
+ prom_drawstring(" ");
+ }
+ prom_drawstring(" ");
+ prom_drawhex(sp[i]);
+ }
+ prom_drawstring("\n");
msr = get_msr();
set_msr(msr & ~0x8000); /* disable interrupts */
remove_bpts();
excprint(struct pt_regs *fp)
{
printf("vector: %x at pc = %x %s",
- fp->trap, fp->nip,/* pretty_lookup_name(fp->nip)*/"");
+ fp->trap, fp->nip, pretty_lookup_name(fp->nip));
printf(", msr = %x, sp = %x [%x]\n",
fp->msr, fp->gpr[1], fp);
if (fp->trap == 0x300 || fp->trap == 0x600)
if ( !sysmap || !sysmap_size )
return NULL;
+ /* adjust if addr is relative to kernelbase */
+ if ( addr < PAGE_OFFSET )
+ addr += PAGE_OFFSET;
+
cmp = simple_strtoul(c, &c, 8);
strcpy( last, strsep( &c, "\n"));
while ( c < (sysmap+sysmap_size) )
-/* $Id: setup.c,v 1.51 2000/02/26 04:24:32 davem Exp $
+/* $Id: setup.c,v 1.52 2000/03/03 23:48:41 davem Exp $
* linux/arch/sparc64/kernel/setup.c
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
}
if ((va >= KERNBASE) && (va < (KERNBASE + (4 * 1024 * 1024)))) {
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
/*
* Locked down tlb entry 63.
*/
+
tte = spitfire_get_dtlb_data(63);
res = PROM_TRUE;
goto done;
-/* $Id: blockops.S,v 1.19 1999/11/19 05:52:45 davem Exp $
+/* $Id: blockops.S,v 1.20 2000/03/03 23:48:38 davem Exp $
* blockops.S: UltraSparc block zero optimized routines.
*
* Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
sethi %hi(TLBTEMP_ENT1), %o3
rdpr %pstate, %g3
wrpr %g3, PSTATE_IE, %pstate
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %o4
+ stxa %g0, [%o4] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_TAG_READ, %o4
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %o5
+ stxa %g0, [%o5] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_DATA_ACCESS, %o5
stxa %o0, [%o2] ASI_DMMU
stxa %g1, [%o3] ASI_DTLB_DATA_ACCESS
membar #Sync
add %o3, (TLBTEMP_ENTSZ), %o3
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %g5
+ stxa %g0, [%g5] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_TAG_READ, %g5
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_DATA_ACCESS, %g7
stxa %o1, [%o2] ASI_DMMU
stxa %g2, [%o3] ASI_DTLB_DATA_ACCESS
sethi %hi(TLBTEMP_ENT2), %o3
rdpr %pstate, %g3
wrpr %g3, PSTATE_IE, %pstate
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %g5
+ stxa %g0, [%g5] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_TAG_READ, %g5
+
+ /* Spitfire Errata #32 workaround */
+ mov 0x8, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ flush %g6
+
ldxa [%o3] ASI_DTLB_DATA_ACCESS, %g7
stxa %o0, [%o2] ASI_DMMU
stxa %g1, [%o3] ASI_DTLB_DATA_ACCESS
-/* $Id: init.c,v 1.146 2000/02/09 21:11:09 davem Exp $
+/* $Id: init.c,v 1.147 2000/03/03 23:48:44 davem Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
/* Now fixup OBP's idea about where we really are mapped. */
prom_printf("Remapping the kernel... ");
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
phys_page = spitfire_get_dtlb_data(63) & _PAGE_PADDR;
phys_page += ((unsigned long)&prom_boot_page -
(unsigned long)&empty_zero_page);
: "memory");
tte_vaddr = (unsigned long) &empty_zero_page;
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
kern_locked_tte_data = tte_data = spitfire_get_dtlb_data(63);
remap_func = (void *) ((unsigned long) &prom_remap -
(unsigned long) &prom_boot_page);
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
remap_func(spitfire_get_dtlb_data(63) & _PAGE_PADDR,
(unsigned long) &empty_zero_page,
prom_get_mmu_ihandle());
/* Only DTLB must be checked for VPTE entries. */
for(i = 0; i < 63; i++) {
- unsigned long tag = spitfire_get_dtlb_tag(i);
+ unsigned long tag;
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
+ tag = spitfire_get_dtlb_tag(i);
if(((tag & ~(PAGE_MASK)) == 0) &&
((tag & (PAGE_MASK)) >= prom_reserved_base)) {
__asm__ __volatile__("stxa %%g0, [%0] %1"
for(i = 0; i < 63; i++) {
unsigned long data;
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
data = spitfire_get_dtlb_data(i);
if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) {
- unsigned long tag = spitfire_get_dtlb_tag(i);
+ unsigned long tag;
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+ tag = spitfire_get_dtlb_tag(i);
if(save_p) {
prom_dtlb[dtlb_seen].tlb_ent = i;
prom_dtlb[dtlb_seen].tlb_tag = tag;
for(i = 0; i < 63; i++) {
unsigned long data;
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
data = spitfire_get_itlb_data(i);
if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) {
- unsigned long tag = spitfire_get_itlb_tag(i);
+ unsigned long tag;
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+ tag = spitfire_get_itlb_tag(i);
if(save_p) {
prom_itlb[itlb_seen].tlb_ent = i;
prom_itlb[itlb_seen].tlb_tag = tag;
: "=r" (pstate)
: "i" (PSTATE_IE));
for(i = 0; i < 64; i++) {
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
if(!(spitfire_get_dtlb_data(i) & _PAGE_L)) {
__asm__ __volatile__("stxa %%g0, [%0] %1"
: /* no outputs */
spitfire_put_dtlb_data(i, 0x0UL);
membar("#Sync");
}
+
+ /* Spitfire Errata #32 workaround */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
if(!(spitfire_get_itlb_data(i) & _PAGE_L)) {
__asm__ __volatile__("stxa %%g0, [%0] %1"
: /* no outputs */
-/* $Id: ultra.S,v 1.37 2000/02/14 02:52:04 davem Exp $
+/* $Id: ultra.S,v 1.38 2000/03/03 23:48:44 davem Exp $
* ultra.S: Don't expand these all over the place...
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
__flush_tlb_range: /* %o0=(ctx&0x3ff), %o1=start&PAGE_MASK, %o2=SECONDARY_CONTEXT,
* %o3=end&PAGE_MASK, %o4=PAGE_SIZE, %o5=(end - start)
*/
-#define TLB_MAGIC 206 /* Students, do you know how I calculated this? -DaveM */
+#define TLB_MAGIC 207 /* Students, do you know how I calculated this? -DaveM */
/*IC3*/ cmp %o5, %o4
be,pt %xcc, __flush_tlb_page
srlx %o5, 13, %g5
wrpr %g1, PSTATE_IE, %pstate
mov TLB_TAG_ACCESS, %g3
mov (62 << 3), %g2
+
+ /* Spitfire Errata #32 workaround. */
+ mov 0x8, %o4
+ stxa %g0, [%o4] ASI_DMMU
+ flush %g6
+
1: ldxa [%g2] ASI_ITLB_TAG_READ, %o4
and %o4, 0x3ff, %o5
cmp %o5, %o0
wrpr %g1, 0x0, %pstate
4: stxa %g0, [%g3] ASI_IMMU
stxa %g0, [%g2] ASI_ITLB_DATA_ACCESS
+ flush %g6
+
+ /* Spitfire Errata #32 workaround. */
+ mov 0x8, %o4
+ stxa %g0, [%o4] ASI_DMMU
+ flush %g6
+
ba,pt %xcc, 2b
- flush %g6
+ nop
+
5: stxa %g0, [%g3] ASI_DMMU
/*IC9*/ stxa %g0, [%g2] ASI_DTLB_DATA_ACCESS
+ flush %g6
+
+ /* Spitfire Errata #32 workaround. */
+ mov 0x8, %o4
+ stxa %g0, [%o4] ASI_DMMU
+ flush %g6
+
ba,pt %xcc, 3b
- flush %g6
+ nop
.align 32
__flush_tlb_mm_slow:
clr %l6
99: retry
+ .data
+
+errata32_hwbug:
+ .xword 0
+
+ .text
+
/* These two are not performance critical... */
.globl xcall_flush_tlb_all
xcall_flush_tlb_all:
+
+ /* Spitfire Errata #32 workaround. */
+ sethi %hi(errata32_hwbug), %g4
+ stx %g0, [%g4 + %lo(errata32_hwbug)]
+
clr %g2
clr %g3
1: ldxa [%g3] ASI_DTLB_DATA_ACCESS, %g4
and %g4, _PAGE_L, %g5
brnz,pn %g5, 2f
mov TLB_TAG_ACCESS, %g7
+
stxa %g0, [%g7] ASI_DMMU
membar #Sync
-
stxa %g0, [%g3] ASI_DTLB_DATA_ACCESS
membar #Sync
+
+ /* Spitfire Errata #32 workaround. */
+ sethi %hi(errata32_hwbug), %g4
+ stx %g0, [%g4 + %lo(errata32_hwbug)]
+
2: ldxa [%g3] ASI_ITLB_DATA_ACCESS, %g4
and %g4, _PAGE_L, %g5
brnz,pn %g5, 2f
mov TLB_TAG_ACCESS, %g7
+
stxa %g0, [%g7] ASI_IMMU
membar #Sync
-
stxa %g0, [%g3] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+
+ /* Spitfire Errata #32 workaround. */
+ sethi %hi(errata32_hwbug), %g4
+ stx %g0, [%g4 + %lo(errata32_hwbug)]
+
2: add %g2, 1, %g2
cmp %g2, 63
ble,pt %icc, 1b
# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
-if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
- bool ' Boot support (linear, striped)' CONFIG_MD_BOOT
-fi
+#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
+# bool ' Boot support (linear, striped)' CONFIG_MD_BOOT
+#fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
/* port addresses for auto-detection */
#define ALI_NUM_PORTS 4
-static int __init ports[ALI_NUM_PORTS] = {0x074, 0x0f4, 0x034, 0x0e4};
+static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4};
/* register initialization data */
typedef struct { byte reg, data; } RegInitializer;
-static RegInitializer __init initData[] = {
+static RegInitializer initData[] __initdata = {
{0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
{0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
{0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
#include <linux/locks.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
-#include <linux/raid/md.h>
#include <linux/timer.h>
#endif
char buffer[16];
int stat;
- init_cdrom_command(&cgc, buffer, sizeof(buffer));
+ init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
/* These will be moved into the Uniform layer shortly... */
switch (cmd) {
if ((stat = cdrom_select_speed (drive, speed)) < 0)
return stat;
- init_cdrom_command(&cgc, &buf, sizeof(buf));
+ init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
#ifndef __ACER50__
/* Now with that done, update the speed fields */
return nslots;
}
- init_cdrom_command(&cgc, &buf, sizeof(buf));
+ init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
/* we have to cheat a little here. the packet will eventually
* be queued with ide_cdrom_packet(), which extracts the
* drive from cdi->handle. Since this device hasn't been
hwif = &ide_hwifs[i];
pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq);
memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
- hwif->chipset = ide_generic;
+ hwif->chipset = ide_pmac;
hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
case ide_cmd646: name = "cmd646"; break;
case ide_cy82c693: name = "cy82c693"; break;
case ide_4drives: name = "4drives"; break;
+ case ide_pmac: name = "mac-io"; break;
default: name = "(unknown)"; break;
}
len = sprintf(page, "%s\n", name);
nbd_dev[dev].refcnt++;
if (!(nbdev->flags & NBD_INITIALISED)) {
init_MUTEX(&nbdev->queue_lock);
+ INIT_LIST_HEAD(&nbdev->queue_head);
nbdev->flags |= NBD_INITIALISED;
}
MOD_INC_USE_COUNT;
-- Fixed CDDA ripping with cdda2wav - accept much larger requests of
number of frames and split the reads in blocks of 8.
- 3.05 Dec 13, 1999 - Jens Axboe <axboe@image.dk>
+ 3.06 Dec 13, 1999 - Jens Axboe <axboe@image.dk>
-- Added support for changing the region of DVD drives.
-- Added sense data to generic command.
+
+ 3.07 Feb 2, 2000 - Jens Axboe <axboe@suse.de>
+ -- Do same "read header length" trick in cdrom_get_disc_info() as
+ we do in cdrom_get_track_info() -- some drive don't obbey specs and
+ fail if they can't supply the full Mt Fuji size table.
+ -- Deleted stuff related to setting up write modes. It has a different
+ home now.
+ -- Clear header length in mode_select unconditionally.
+ -- Removed the register_disk() that was added, not needed here.
-------------------------------------------------------------------------*/
-#define REVISION "Revision: 3.06"
-#define VERSION "Id: cdrom.c 3.06 1999/12/13"
+#define REVISION "Revision: 3.07"
+#define VERSION "Id: cdrom.c 3.07 2000/02/02"
/* I use an error-log mask to give fine grain control over the type of
messages dumped to the system logs. The available masks include: */
return 0;
}
-static
struct cdrom_device_info *cdrom_find_device(kdev_t dev)
{
struct cdrom_device_info *cdi;
length = sizeof(struct cdrom_mechstat_header) +
cdi->capacity * sizeof(struct cdrom_slot);
- init_cdrom_command(&cgc, buf, length);
+ init_cdrom_command(&cgc, buf, length, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_MECHANISM_STATUS;
cgc.cmd[8] = (length >> 8) & 0xff;
cgc.cmd[9] = length & 0xff;
if (cdi->sanyo_slot && slot < 0)
return 0;
- init_cdrom_command(&cgc, NULL, 0);
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.cmd[0] = GPCMD_LOAD_UNLOAD;
cgc.cmd[4] = 2 + (slot >= 0);
cgc.cmd[8] = slot;
*curr = requested;
}
-void init_cdrom_command(struct cdrom_generic_command *cgc, void *buf, int len)
+void init_cdrom_command(struct cdrom_generic_command *cgc, void *buf, int len,
+ int type)
{
memset(cgc, 0, sizeof(struct cdrom_generic_command));
- memset(buf, 0, len);
+ if (buf)
+ memset(buf, 0, len);
cgc->buffer = (char *) buf;
cgc->buflen = len;
+ cgc->data_direction = type;
}
/* DVD handling */
}
}
cgc->cmd[9] = cgc->buflen;
+ cgc->data_direction = CGC_DATA_WRITE;
}
static void setup_send_key(struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
}
}
cgc->cmd[9] = cgc->buflen;
+ cgc->data_direction = CGC_DATA_READ;
}
static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, 0);
+ init_cdrom_command(&cgc, buf, 0, CGC_DATA_READ);
switch (ai->type) {
/* LU data send */
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf));
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[6] = s->physical.layer_num;
cgc.cmd[7] = s->type;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf));
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[6] = s->copyright.layer_num;
cgc.cmd[7] = s->type;
if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size);
+ init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[7] = s->type;
cgc.cmd[8] = size >> 8;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf));
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[7] = s->type;
cgc.cmd[9] = cgc.buflen = 0xff;
if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size);
+ init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[7] = s->type;
cgc.cmd[8] = size >> 8;
cgc->cmd[2] = page_code | (page_control << 6);
cgc->cmd[7] = cgc->buflen >> 8;
cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_READ;
return cdo->generic_packet(cdi, cgc);
}
struct cdrom_device_ops *cdo = cdi->ops;
memset(cgc->cmd, 0, sizeof(cgc->cmd));
-
+ memset(cgc->buffer, 0, 2);
cgc->cmd[0] = GPCMD_MODE_SELECT_10;
cgc->cmd[1] = 0x10; /* PF */
cgc->cmd[7] = cgc->buflen >> 8;
cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_WRITE;
return cdo->generic_packet(cdi, cgc);
}
char buffer[32];
int ret;
- init_cdrom_command(&cgc, buffer, 16);
+ init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
cgc.cmd[1] = 2; /* MSF addressing */
cgc.cmd[2] = 0x40; /* request subQ data */
cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL);
if (cgc.buffer == NULL)
return -ENOMEM;
+ cgc.data_direction = CGC_DATA_READ;
ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
if (!ret)
if (copy_to_user((char *)arg, cgc.buffer, blocksize))
kfree(cgc.buffer);
return -EFAULT;
}
-
+ cgc.data_direction = CGC_DATA_READ;
while (ra.nframes > 0) {
ret = cdrom_read_block(cdi, &cgc, lba, frames, 1,
CD_FRAMESIZE_RAW);
cgc.cmd[7] = entry.cdte_addr.msf.second;
cgc.cmd[8] = entry.cdte_addr.msf.frame;
cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+ cgc.data_direction = CGC_DATA_NONE;
return cdo->generic_packet(cdi, &cgc);
}
case CDROMPLAYMSF: {
cgc.cmd[6] = msf.cdmsf_min1;
cgc.cmd[7] = msf.cdmsf_sec1;
cgc.cmd[8] = msf.cdmsf_frame1;
+ cgc.data_direction = CGC_DATA_NONE;
return cdo->generic_packet(cdi, &cgc);
}
case CDROMPLAYBLK: {
cgc.cmd[5] = blk.from & 0xff;
cgc.cmd[7] = (blk.len >> 8) & 0xff;
cgc.cmd[8] = blk.len & 0xff;
+ cgc.data_direction = CGC_DATA_NONE;
return cdo->generic_packet(cdi, &cgc);
}
case CDROMVOLCTRL:
buffer[offset+13] = volctrl.channel2 & mask[offset+13];
buffer[offset+15] = volctrl.channel3 & mask[offset+15];
- /* clear the first three */
- memset(buffer, 0, 3);
-
/* set volume */
cgc.buffer = buffer;
return cdrom_mode_select(cdi, &cgc);
cgc.cmd[0] = GPCMD_START_STOP_UNIT;
cgc.cmd[1] = 1;
cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
+ cgc.data_direction = CGC_DATA_NONE;
return cdo->generic_packet(cdi, &cgc);
}
cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
cgc.cmd[0] = GPCMD_PAUSE_RESUME;
cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
+ cgc.data_direction = CGC_DATA_NONE;
return cdo->generic_packet(cdi, &cgc);
}
if (copy && !ret)
__copy_to_user(userbuf, cgc.buffer, cgc.buflen);
/* copy back sense data */
- if (ret && sense != NULL)
+ if (sense != NULL)
if (copy_to_user(sense, cgc.sense, sizeof(struct request_sense)))
ret = -EFAULT;
kfree(cgc.buffer);
struct cdrom_generic_command cgc;
int ret;
- init_cdrom_command(&cgc, ti, 8);
+ init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
cgc.cmd[1] = type & 3;
cgc.cmd[4] = (track & 0xff00) >> 8;
struct cdrom_device_info *cdi = cdrom_find_device(dev);
struct cdrom_device_ops *cdo = cdi->ops;
struct cdrom_generic_command cgc;
+ int ret;
/* set up command and get the disc info */
- init_cdrom_command(&cgc, di, sizeof(*di));
+ init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DISC_INFO;
- cgc.cmd[8] = cgc.buflen;
+ cgc.cmd[8] = cgc.buflen = 2;
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ /* not all drives have the same disc_info length, so requeue
+ * packet with the length the drive tells us it can supply
+ */
+ cgc.buflen = be16_to_cpu(di->disc_information_length) +
+ sizeof(di->disc_information_length);
+
+ if (cgc.buflen > sizeof(disc_information))
+ cgc.buflen = sizeof(disc_information);
+
+ cgc.cmd[8] = cgc.buflen;
return cdo->generic_packet(cdi, &cgc);
}
EXPORT_SYMBOL(cdrom_mode_select);
EXPORT_SYMBOL(cdrom_mode_sense);
EXPORT_SYMBOL(init_cdrom_command);
+EXPORT_SYMBOL(cdrom_find_device);
#ifdef CONFIG_SYSCTL
} /* End init_module */
#endif
-#ifdef MODULE
-/* -------------------- Begin cleanup_module ---------------------- */
#ifdef ENABLE_PCI
static struct pci_driver epca_driver;
#endif
+#ifdef MODULE
+/* -------------------- Begin cleanup_module ---------------------- */
+
void cleanup_module()
{ /* Begin cleanup_module */
*/
#define STLI_EISAPROBE 0
+static devfs_handle_t devfs_handle = NULL;
+
/*****************************************************************************/
/*
/*****************************************************************************/
-static devfs_handle_t devfs_handle = NULL;
-
void cleanup_module()
{
stlibrd_t *brdp;
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/mm.h>
+#include <linux/init.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/signal.h>
}
}
-#ifdef MODULE
-int init_module(void)
-{
-#else
-int init_stradis_cards(struct video_init *unused)
+
+static int __init stradis_init (void)
{
-#endif
struct pci_dev *dev = NULL;
int result = 0, i;
return 0;
}
-#ifdef MODULE
-void cleanup_module(void)
+
+static void __exit stradis_exit (void)
{
release_saa();
printk(KERN_INFO "stradis: module cleanup complete\n");
}
-#endif
+
+module_init(stradis_init);
+module_exit(stradis_exit);
+
{"i2c-tuner", i2c_tuner_init},
{"bttv", init_bttv_cards},
#endif
-#ifdef CONFIG_VIDEO_STRADIS
- {"stradis", init_stradis_cards},
-#endif
#ifdef CONFIG_VIDEO_BWQCAM
{"bw-qcam", init_bw_qcams},
#endif
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-int suppress_pollack = 0;
+static int suppress_pollack = 0;
MODULE_PARM(suppress_pollack, "0-1i");
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
-int suppress_pollack = 0;
+static int suppress_pollack = 0;
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
static void elp_timeout(struct net_device *dev)
{
- unsigned long flags;
elp_device *adapter = dev->priv;
int stat;
#define RX_RING_SIZE 16
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/isapnp.h>
$Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
*/
-#ifdef MODULE
#include <linux/module.h>
-#endif
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#ifndef NO_NOPCOMMANDS
if (stat & STAT_CNA) {
/* CU went 'not ready' */
- if (netif_running(dev->state)) {
+ if (netif_running(dev)) {
printk(KERN_WARNING "%s: oops! CU has left active state. stat: %04x/%04x.\n", dev->name, (int) stat, (int) p->scb->status);
}
}
#
source drivers/net/arcnet/Config.in
+source drivers/net/appletalk/Config.in
tristate 'Dummy net driver support' CONFIG_DUMMY
tristate 'Bonding driver support' CONFIG_BONDING
fi
tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT
- if [ "$CONFIG_OBSOLETE" = "y" ]; then
- tristate ' CS89x0 support' CONFIG_CS89x0
- fi
+ tristate ' CS89x0 support' CONFIG_CS89x0
tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
tristate ' DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP
tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
fi
fi
-#
-# AppleTalk
-#
-
-if [ "$CONFIG_ATALK" != "n" ]; then
- mainmenu_option next_comment
- comment 'Appletalk devices'
- dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
- dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
- if [ "$CONFIG_COPS" != "n" ]; then
- bool ' Dayna firmware support' CONFIG_COPS_DAYNA
- bool ' Tangent firmware support' CONFIG_COPS_TANGENT
- fi
- dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
- if [ "$CONFIG_IPDDP" != "n" ]; then
- bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
- bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
- fi
- endmenu
-fi
-
if [ ! "$CONFIG_PARPORT" = "n" ]; then
dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT
fi
MOD_SUB_DIRS :=
MOD_IN_SUB_DIRS :=
ALL_SUB_DIRS := $(SUB_DIRS) fc hamradio irda pcmcia tokenring wan sk98lin \
- arcnet skfp tulip
+ arcnet skfp tulip appletalk
O_TARGET := net.o
MOD_LIST_NAME := NET_MODULES
endif
endif
+ifeq ($(CONFIG_ATALK),y)
+SUB_DIRS += appletalk
+MOD_IN_SUB_DIRS += appletalk
+else
+ ifeq ($(CONFIG_ATALK),m)
+ MOD_IN_SUB_DIRS += appletalk
+ endif
+endif
+
#
# link order important here
#
obj-$(CONFIG_HYDRA) += hydra.o
obj-$(CONFIG_ARIADNE) += ariadne.o
obj-$(CONFIG_CS89x0) += cs89x0.o
-obj-$(CONFIG_LTPC) += ltpc.o
-obj-$(CONFIG_COPS) += cops.o
-obj-$(CONFIG_IPDDP) += ipddp.o
obj-$(CONFIG_MACSONIC) += macsonic.o
obj-$(CONFIG_MACMACE) += macmace.o
obj-$(CONFIG_MAC89x0) += mac89x0.o
* They used the DEC vendor ID by mistake
*/
#ifndef PCI_DEVICE_ID_FARALLON_PN9000SX
-#define PCI_DEVICE_ID_FARALLON_PN9000SX 0x1a
+#define PCI_DEVICE_ID_FARALLON_PN9000SX 0x1a
#endif
#ifndef PCI_VENDOR_ID_SGI
-#define PCI_VENDOR_ID_SGI 0x10a9
+#define PCI_VENDOR_ID_SGI 0x10a9
#endif
#ifndef PCI_DEVICE_ID_SGI_ACENIC
-#define PCI_DEVICE_ID_SGI_ACENIC 0x0009
+#define PCI_DEVICE_ID_SGI_ACENIC 0x0009
#endif
#ifndef wmb
#define __exit
#endif
+#ifndef SMP_CACHE_BYTES
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+#endif
+
+
#if (LINUX_VERSION_CODE < 0x02030e)
#define net_device device
#endif
return virt_ptr;
}
#define pci_free_consistent(cookie, size, ptr, dma_ptr) kfree(ptr)
-#define pci_map_single(cookie, address, size, dir) virt_to_bus(address)
+#define pci_map_single(cookie, address, size, dir) virt_to_bus(address)
#define pci_unmap_single(cookie, address, size, dir)
#endif
dev->start = 1;
}
-#define ace_mark_net_bh(foo) mark_bh(foo)
-#define ace_if_busy(dev) dev->tbusy
-#define ace_if_running(dev) dev->start
-#define ace_if_down(dev) {do{dev->start = 0;}while (0);}
+#define ace_mark_net_bh(foo) mark_bh(foo)
+#define netif_queue_stopped(dev) dev->tbusy
+#define netif_running(dev) dev->start
+#define ace_if_down(dev) {do{dev->start = 0;}while (0);}
#else
#define NET_BH 0
#define ace_mark_net_bh(foo) {do{} while(0);}
-#define ace_if_busy(dev) netif_queue_stopped(dev)
-#define ace_if_running(dev) netif_running(dev)
#define ace_if_down(dev) {do{} while(0);}
#endif
+
+#define ACE_MAX_MOD_PARMS 8
+#define BOARD_IDX_STATIC 0
+#define BOARD_IDX_OVERFLOW -1
+
+
#include "acenic.h"
/*
#define ACE_STD_BUFSIZE (ACE_STD_MTU + ETH_HLEN + 2+4+16)
#define ACE_JUMBO_BUFSIZE (ACE_JUMBO_MTU + ETH_HLEN + 2+4+16)
-#define DEF_TX_RATIO 24
/*
* There seems to be a magic difference in the effect between 995 and 996
* but little difference between 900 and 995 ... no idea why.
+ *
+ * There is now a default set of tuning parameters which is set, depending
+ * on whether or not the user enables Jumbo frames. It's assumed that if
+ * Jumbo frames are enabled, the user wants optimal tuning for that case.
*/
-#define DEF_TX_COAL 996
+#define DEF_TX_COAL 400 /* 996 */
#define DEF_TX_MAX_DESC 40
-#define DEF_RX_COAL 1000
+#define DEF_RX_COAL 120 /* 1000 */
#define DEF_RX_MAX_DESC 25
+#define DEF_TX_RATIO 21 /* 24 */
+
+#define DEF_JUMBO_TX_COAL 20
+#define DEF_JUMBO_TX_MAX_DESC 60
+#define DEF_JUMBO_RX_COAL 30
+#define DEF_JUMBO_RX_MAX_DESC 6
+#define DEF_JUMBO_TX_RATIO 21
+
#define TX_COAL_INTS_ONLY 0 /* seems not worth it */
#define DEF_TRACE 0
-#define DEF_STAT 2 * TICKS_PER_SEC
+#define DEF_STAT (2 * TICKS_PER_SEC)
-static int link[8] = {0, };
-static int trace[8] = {0, };
-static int tx_coal_tick[8] = {0, };
-static int rx_coal_tick[8] = {0, };
-static int max_tx_desc[8] = {0, };
-static int max_rx_desc[8] = {0, };
-static int tx_ratio[8] = {0, };
-static int dis_pci_mem_inval[8] = {1, 1, 1, 1, 1, 1, 1, 1};
+static int link[ACE_MAX_MOD_PARMS] = {0, };
+static int trace[ACE_MAX_MOD_PARMS] = {0, };
+static int tx_coal_tick[ACE_MAX_MOD_PARMS] = {0, };
+static int rx_coal_tick[ACE_MAX_MOD_PARMS] = {0, };
+static int max_tx_desc[ACE_MAX_MOD_PARMS] = {0, };
+static int max_rx_desc[ACE_MAX_MOD_PARMS] = {0, };
+static int tx_ratio[ACE_MAX_MOD_PARMS] = {0, };
+static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
static const char __initdata *version =
- "acenic.c: v0.41 02/16/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
+ "acenic.c: v0.42 03/02/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
" http://home.cern.ch/~jes/gige/acenic.html\n";
static struct net_device *root_dev = NULL;
pci_set_master(pdev);
-#ifdef __sparc__
- /* NOTE: Cache line size is in 32-bit word units. */
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10);
-#endif
-
/*
* Remap the regs into kernel space - this is abuse of
* dev->base_addr since it was means for I/O port
#else
dev->base_addr = pdev->resource[0].start;
#endif
-
ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000);
if (!ap->regs) {
printk(KERN_ERR "%s: Unable to map I/O register, "
if ((readl(&ap->regs->HostCtrl) >> 28) == 4) {
printk(KERN_ERR "%s: Driver compiled without Tigon I"
" support - NIC disabled\n", dev->name);
- iounmap(ap->regs);
- unregister_netdev(dev);
+ ace_init_cleanup(dev);
continue;
}
#endif
continue;
#ifdef MODULE
- if (ace_init(dev, boards_found))
- continue;
+ if (boards_found >= ACE_MAX_MOD_PARMS)
+ ap->board_idx = BOARD_IDX_OVERFLOW;
+ else
+ ap->board_idx = boards_found;
#else
- if (ace_init(dev, -1))
- continue;
+ ap->board_idx = BOARD_IDX_STATIC;
#endif
+ if (ace_init(dev))
+ continue;
+
boards_found++;
}
* or more boards. Otherwise, return failure (-ENODEV).
*/
-#ifdef MODULE
- return boards_found;
-#else
if (boards_found > 0)
return 0;
else
return -ENODEV;
-#endif
}
#ifdef MODULE
MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");
-MODULE_DESCRIPTION("AceNIC/3C985 Gigabit Ethernet driver");
+MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
while (root_dev) {
next = ((struct ace_private *)root_dev->priv)->next;
- ap = (struct ace_private *)root_dev->priv;
+ ap = root_dev->priv;
regs = ap->regs;
}
}
- ace_free_descriptors(root_dev);
-
- if (ap->trace_buf)
- kfree(ap->trace_buf);
- if (ap->info)
- pci_free_consistent(ap->pdev, sizeof(struct ace_info),
- ap->info, ap->info_dma);
- if (ap->skb)
- kfree(ap->skb);
- if (root_dev->irq)
- free_irq(root_dev->irq, root_dev);
- unregister_netdev(root_dev);
- iounmap(regs);
+ ace_init_cleanup(root_dev);
kfree(root_dev);
-
root_dev = next;
}
}
+#endif
int __init ace_module_init(void)
{
- int cards;
+ int status;
root_dev = NULL;
#ifdef NEW_NETINIT
- cards = acenic_probe();
+ status = acenic_probe();
#else
- cards = acenic_probe(NULL);
+ status = acenic_probe(NULL);
#endif
- return cards ? 0 : -ENODEV;
+ return status;
}
ace_module_cleanup();
}
#endif
-#endif
#if (LINUX_VERSION_CODE >= 0x02032a)
fail:
/* Clean up. */
+ ace_init_cleanup(dev);
+ return 1;
+}
+
+
+/*
+ * Generic cleanup handling data allocated during init. Used when the
+ * module is unloaded or if an error occurs during initialization
+ */
+static void ace_init_cleanup(struct net_device *dev)
+{
+ struct ace_private *ap;
+
+ ap = dev->priv;
+
ace_free_descriptors(dev);
- iounmap(ap->regs);
+
+ if (ap->info)
+ pci_free_consistent(ap->pdev, sizeof(struct ace_info),
+ ap->info, ap->info_dma);
+ if (ap->skb)
+ kfree(ap->skb);
+ if (ap->trace_buf)
+ kfree(ap->trace_buf);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
unregister_netdev(dev);
- return 1;
+ iounmap(ap->regs);
}
}
-static int __init ace_init(struct net_device *dev, int board_idx)
+static int __init ace_init(struct net_device *dev)
{
struct ace_private *ap;
struct ace_regs *regs;
struct ace_info *info = NULL;
unsigned long tmp_ptr, myjif;
u32 tig_ver, mac1, mac2, tmp, pci_state;
- int ecode = 0;
+ int board_idx, ecode = 0;
short i;
+ unsigned char cache;
ap = dev->priv;
regs = ap->regs;
+ board_idx = ap->board_idx;
+
/*
* aman@sgi.com - its useful to do a NIC reset here to
* address the `Firmware not running' problem subsequent
dev->dev_addr[4] = (mac2 >> 8) & 0xff;
dev->dev_addr[5] = mac2 & 0xff;
+ /*
+ * 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(ap->pdev, PCI_CACHE_LINE_SIZE, &cache);
+ if ((cache << 2) != SMP_CACHE_BYTES) {
+ printk(KERN_INFO " PCI cache line size set incorrectly "
+ "(%i bytes) by BIOS/FW, correcting to %i\n",
+ (cache << 2), SMP_CACHE_BYTES);
+ pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE,
+ SMP_CACHE_BYTES >> 2);
+ }
+
pci_state = readl(®s->PciState);
printk(KERN_INFO " PCI bus width: %i bits, speed: %iMHz, "
"latency: %i clks\n",
/*
* Tuning parameters only supported for 8 cards
*/
- if (board_idx > 7 || dis_pci_mem_inval[board_idx]) {
+ if (board_idx == BOARD_IDX_OVERFLOW ||
+ dis_pci_mem_inval[board_idx]) {
if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
pci_write_config_word(ap->pdev, PCI_COMMAND,
ap->pci_command);
- printk(KERN_INFO "%s: disabling PCI memory "
- "write and invalidate\n", dev->name);
+ printk(KERN_INFO " Disabling PCI memory "
+ "write and invalidate\n");
}
} else if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
- printk(KERN_INFO "%s: PCI memory write & invalidate "
- "enabled by BIOS, enabling counter "
- "measures\n", dev->name);
- switch(L1_CACHE_BYTES) {
+ printk(KERN_INFO " PCI memory write & invalidate "
+ "enabled by BIOS, enabling counter measures\n");
+
+ switch(SMP_CACHE_BYTES) {
case 16:
tmp |= DMA_WRITE_MAX_16;
break;
}
}
}
+
#ifdef __sparc__
- /* On this platform, we know what the best dma settings
+ /*
+ * On this platform, we know what the best dma settings
* are. We use 64-byte maximum bursts, because if we
* burst larger than the cache line size (or even cross
* a 64byte boundry in a single burst) the UltraSparc
* set will give the PCI controller proper hints about
* prefetching.
*/
- tmp = (tmp & ~(0xfc));
+ tmp = tmp & ~DMA_READ_WRITE_MASK;
tmp |= DMA_READ_MAX_64;
tmp |= DMA_WRITE_MAX_64;
#endif
writel(tmp, ®s->PciState);
+ if (!(ap->pci_command & PCI_COMMAND_FAST_BACK)) {
+ printk(KERN_INFO " Enabling PCI Fast Back to Back\n");
+ ap->pci_command |= PCI_COMMAND_FAST_BACK;
+ pci_write_config_word(ap->pdev, PCI_COMMAND, ap->pci_command);
+ }
+
/*
* Initialize the generic info block and the command+event rings
* and the control blocks for the transmit and receive rings
writel(1, ®s->AssistState);
writel(DEF_STAT, ®s->TuneStatTicks);
-
- writel(DEF_TX_COAL, ®s->TuneTxCoalTicks);
- writel(DEF_TX_MAX_DESC, ®s->TuneMaxTxDesc);
- writel(DEF_RX_COAL, ®s->TuneRxCoalTicks);
- writel(DEF_RX_MAX_DESC, ®s->TuneMaxRxDesc);
writel(DEF_TRACE, ®s->TuneTrace);
- writel(DEF_TX_RATIO, ®s->TxBufRat);
- if (board_idx >= 8) {
- printk(KERN_WARNING "%s: more then 8 NICs detected, "
- "ignoring module parameters!\n", dev->name);
- board_idx = -1;
- }
+ ace_set_rxtx_parms(dev, 0);
- if (board_idx >= 0) {
+ if (board_idx == BOARD_IDX_OVERFLOW) {
+ printk(KERN_WARNING "%s: more then %i NICs detected, "
+ "ignoring module parameters!\n",
+ dev->name, ACE_MAX_MOD_PARMS);
+ } else if (board_idx >= 0) {
if (tx_coal_tick[board_idx])
writel(tx_coal_tick[board_idx],
®s->TuneTxCoalTicks);
writel(readl(®s->CpuBCtrl) | CPU_HALT,
®s->CpuBCtrl);
writel(0, ®s->Mb0Lo);
- free_irq(dev->irq, dev);
- dev->irq = 0;
ecode = -EBUSY;
goto init_error;
"the RX mini ring\n", dev->name);
}
return 0;
+
init_error:
- iounmap(ap->regs);
- unregister_netdev(dev);
- if (ap->skb) {
- kfree(ap->skb);
- ap->skb = NULL;
- }
- if (ap->info)
- pci_free_consistent(ap->pdev, sizeof(struct ace_info),
- info, ap->info_dma);
+ ace_init_cleanup(dev);
return ecode;
}
+static void ace_set_rxtx_parms(struct net_device *dev, int jumbo)
+{
+ struct ace_private *ap;
+ struct ace_regs *regs;
+ int board_idx;
+
+ ap = dev->priv;
+ regs = ap->regs;
+
+ board_idx = ap->board_idx;
+
+ if (board_idx >= 0) {
+ if (!jumbo) {
+ if (!tx_coal_tick[board_idx])
+ writel(DEF_TX_COAL, ®s->TuneTxCoalTicks);
+ if (!max_tx_desc[board_idx])
+ writel(DEF_TX_MAX_DESC, ®s->TuneMaxTxDesc);
+ if (!rx_coal_tick[board_idx])
+ writel(DEF_RX_COAL, ®s->TuneRxCoalTicks);
+ if (!max_rx_desc[board_idx])
+ writel(DEF_RX_MAX_DESC, ®s->TuneMaxRxDesc);
+ if (!tx_ratio[board_idx])
+ writel(DEF_TX_RATIO, ®s->TxBufRat);
+ } else {
+ if (!tx_coal_tick[board_idx])
+ writel(DEF_JUMBO_TX_COAL,
+ ®s->TuneTxCoalTicks);
+ if (!max_tx_desc[board_idx])
+ writel(DEF_JUMBO_TX_MAX_DESC,
+ ®s->TuneMaxTxDesc);
+ if (!rx_coal_tick[board_idx])
+ writel(DEF_JUMBO_RX_COAL,
+ ®s->TuneRxCoalTicks);
+ if (!max_rx_desc[board_idx])
+ writel(DEF_JUMBO_RX_MAX_DESC,
+ ®s->TuneMaxRxDesc);
+ if (!tx_ratio[board_idx])
+ writel(DEF_JUMBO_TX_RATIO, ®s->TxBufRat);
+ }
+ }
+}
+
+
/*
* Monitor the card to detect hangs.
*/
static void ace_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
- struct ace_private *ap = (struct ace_private *)dev->priv;
+ struct ace_private *ap = dev->priv;
struct ace_regs *regs = ap->regs;
/*
{
#if 0
if (!ap->trace_buf)
- if (!(ap->trace_buf = kmalloc(ACE_TRACE_SIZE, GFP_KERNEL)));
+ if (!(ap->trace_buf = kmalloc(ACE_TRACE_SIZE, GFP_KERNEL)))
return;
#endif
}
{
struct ace_private *ap;
- ap = (struct ace_private *)dev->priv;
+ ap = dev->priv;
while (evtcsm != evtprd) {
switch (ap->evt_ring[evtcsm].evt) {
static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
{
- struct ace_private *ap = (struct ace_private *)dev->priv;
+ struct ace_private *ap = dev->priv;
u32 idx;
int mini_count = 0, std_count = 0;
skb = rip->skb;
rip->skb = NULL;
- pci_unmap_single(ap->pdev, rip->mapping, mapsize, PCI_DMA_FROMDEVICE);
+ pci_unmap_single(ap->pdev, rip->mapping, mapsize,
+ PCI_DMA_FROMDEVICE);
skb_put(skb, retdesc->size);
#if 0
/* unncessary */
u32 txcsm, rxretcsm, rxretprd;
u32 evtcsm, evtprd;
- ap = (struct ace_private *)dev->priv;
+ ap = dev->priv;
regs = ap->regs;
/*
ap->stats.tx_packets++;
ap->stats.tx_bytes += skb->len;
- pci_unmap_single(ap->pdev, mapping, skb->len, PCI_DMA_TODEVICE);
+ pci_unmap_single(ap->pdev, mapping, skb->len,
+ PCI_DMA_TODEVICE);
dev_kfree_skb_irq(skb);
ap->skb->tx_skbuff[idx].skb = NULL;
* Ie. skip the comparison of the tx producer vs. the
* consumer.
*/
- if (ace_if_busy(dev) && xchg(&ap->tx_full, 0)) {
+ if (netif_queue_stopped(dev) && xchg(&ap->tx_full, 0)) {
/*
* This does not need to be atomic (and expensive),
* I've seen cases where it would fail otherwise ;-(
* This has to go last in the interrupt handler and run with
* the spin lock released ... what lock?
*/
- if (ace_if_running(dev)) {
+ if (netif_running(dev)) {
int cur_size;
int run_bh = 0;
ace_if_down(dev);
netif_stop_queue(dev);
- ap = (struct ace_private *)dev->priv;
+ ap = dev->priv;
regs = ap->regs;
del_timer(&ap->timer);
writel(0, &ap->tx_ring[i].addr.addrhi);
writel(0, &ap->tx_ring[i].addr.addrlo);
writel(0, &ap->tx_ring[i].flagsize);
- pci_unmap_single(ap->pdev, mapping, skb->len, PCI_DMA_TODEVICE);
+ pci_unmap_single(ap->pdev, mapping, skb->len,
+ PCI_DMA_TODEVICE);
dev_kfree_skb(skb);
ap->skb->tx_skbuff[i].skb = NULL;
}
static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ace_private *ap = (struct ace_private *)dev->priv;
+ struct ace_private *ap = dev->priv;
struct ace_regs *regs = ap->regs;
unsigned long addr;
u32 idx, flagsize;
ap->skb->tx_skbuff[idx].skb = skb;
ap->skb->tx_skbuff[idx].mapping =
- pci_map_single(ap->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+ pci_map_single(ap->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping;
#if (BITS_PER_LONG == 64)
writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi);
ap->jumbo = 1;
if (!test_and_set_bit(0, &ap->jumbo_refill_busy))
ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE);
- ap->jumbo = 1;
+ ace_set_rxtx_parms(dev, 1);
}
} else {
netif_stop_queue(dev);
while (test_and_set_bit(0, &ap->jumbo_refill_busy));
synchronize_irq();
+ ace_set_rxtx_parms(dev, 0);
if (ap->jumbo){
struct cmd cmd;
static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
#ifdef ETHTOOL
- struct ace_private *ap = (struct ace_private *) dev->priv;
+ struct ace_private *ap = dev->priv;
struct ace_regs *regs = ap->regs;
struct ethtool_cmd ecmd;
u32 link, speed;
u16 *da;
struct cmd cmd;
- if(ace_if_running(dev))
+ if(netif_running(dev))
return -EBUSY;
memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
struct ace_private *ap;
struct ace_regs *regs;
- ap = (struct ace_private *)dev->priv;
+ ap = dev->priv;
regs = ap->regs;
if (!(readl(®s->CpuCtrl) & CPU_HALTED)) {
#define DMA_WRITE_MAX_128 0xa0
#define DMA_WRITE_MAX_256 0xc0
#define DMA_WRITE_MAX_1K 0xe0
+#define DMA_READ_WRITE_MASK 0xfc
#define MEM_READ_MULTIPLE 0x00020000
#define PCI_66MHZ 0x00080000
#define PCI_32BIT 0x00100000
unsigned char *trace_buf;
struct pci_dev *pdev;
struct net_device *next;
+ int board_idx;
u16 pci_command;
u8 pci_latency;
char name[48];
/*
* Prototypes
*/
-static int ace_init(struct net_device *dev, int board_idx);
+static int ace_init(struct net_device *dev);
static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs);
static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs);
static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs);
#endif
static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int ace_set_mac_addr(struct net_device *dev, void *p);
+static void ace_set_rxtx_parms(struct net_device *dev, int jumbo);
static int ace_allocate_descriptors(struct net_device *dev);
static void ace_free_descriptors(struct net_device *dev);
+static void ace_init_cleanup(struct net_device *dev);
static struct net_device_stats *ace_get_stats(struct net_device *dev);
static int read_eeprom_byte(struct net_device *dev, unsigned long offset);
--- /dev/null
+#
+# Appletalk driver configuration
+#
+
+if [ "$CONFIG_ATALK" != "n" ]; then
+ mainmenu_option next_comment
+ comment 'Appletalk devices'
+ dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
+ dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool ' Dayna firmware support' CONFIG_COPS_DAYNA
+ bool ' Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
+ dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
+ if [ "$CONFIG_IPDDP" != "n" ]; then
+ bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+ bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
+ fi
+ endmenu
+fi
--- /dev/null
+#
+# Makefile for drivers/net/appletalk
+#
+# 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).
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+obj-y :=
+obj-n :=
+obj-m :=
+obj- :=
+export-objs :=
+
+obj-$(CONFIG_LTPC) += ltpc.o
+obj-$(CONFIG_COPS) += cops.o
+obj-$(CONFIG_IPDDP) += ipddp.o
+
+L_TARGET := appletalk.a
+L_OBJS := $(filter-out $(export-objs), $(obj-y))
+LX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
+include $(TOPDIR)/Rules.make
+
--- /dev/null
+/* cops.c: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <jschlst@turbolinux.com>
+ *
+ * With more than a little help from;
+ * - Alan Cox <Alan.Cox@linux.org>
+ *
+ * Derived from:
+ * - skeleton.c: A network driver outline for linux.
+ * Written 1993-94 by Donald Becker.
+ * - ltpc.c: A driver for the LocalTalk PC card.
+ * Written by Bradford W. Johnson.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Changes:
+ * 19970608 Alan Cox Allowed dual card type support
+ * Can set board type in insmod
+ * Hooks for cops_setup routine
+ * (not yet implemented).
+ * 19971101 Jay Schulist Fixes for multiple lt* devices.
+ * 19980607 Steven Hirsch Fixed the badly broken support
+ * for Tangent type cards. Only
+ * tested on Daystar LT200. Some
+ * cleanup of formatting and program
+ * logic. Added emacs 'local-vars'
+ * setup for Jay's brace style.
+ * 20000211 Alan Cox Cleaned up for softnet
+ */
+
+static const char *version =
+"cops.c:v0.04 6/7/98 Jay Schulist <jschlst@turbolinux.com>\n";
+/*
+ * Sources:
+ * COPS Localtalk SDK. This provides almost all of the information
+ * needed.
+ */
+
+/*
+ * insmod/modprobe configurable stuff.
+ * - IO Port, choose one your card supports or 0 if you dare.
+ * - IRQ, also choose one your card supports or nothing and let
+ * the driver figure it out.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/if_arp.h>
+#include <linux/if_ltalk.h> /* For ltalk_setup() */
+#include <linux/delay.h> /* For udelay() */
+#include <linux/atalk.h>
+
+#include "cops.h" /* Our Stuff */
+#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */
+#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static const char *cardname = "cops";
+
+#ifdef CONFIG_COPS_DAYNA
+static int board_type = DAYNA; /* Module exported */
+#else
+static int board_type = TANGENT;
+#endif
+
+#ifdef MODULE
+static int io = 0x240; /* Default IO for Dayna */
+static int irq = 5; /* Default IRQ */
+#else
+static int io = 0; /* Default IO for Dayna */
+static int irq = 0; /* Default IRQ */
+#endif
+
+/*
+ * COPS Autoprobe information.
+ * Right now if port address is right but IRQ is not 5 this will
+ * return a 5 no matter what since we will still get a status response.
+ * Need one more additional check to narrow down after we have gotten
+ * the ioaddr. But since only other possible IRQs is 3 and 4 so no real
+ * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with
+ * this driver.
+ *
+ * This driver has 2 modes and they are: Dayna mode and Tangent mode.
+ * Each mode corresponds with the type of card. It has been found
+ * that there are 2 main types of cards and all other cards are
+ * the same and just have different names or only have minor differences
+ * such as more IO ports. As this driver is tested it will
+ * become more clear on exactly what cards are supported. The driver
+ * defaults to using Dayna mode. To change the drivers mode, simply
+ * select Dayna or Tangent mode when configuring the kernel.
+ *
+ * This driver should support:
+ * TANGENT driver mode:
+ * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200,
+ * COPS LT-1
+ * DAYNA driver mode:
+ * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
+ * Farallon PhoneNET PC III, Farallon PhoneNET PC II
+ * Other cards possibly supported mode unkown though:
+ * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
+ *
+ * Cards NOT supported by this driver but supported by the ltpc.c
+ * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ * Farallon PhoneNET PC
+ * Original Apple LocalTalk PC card
+ *
+ * N.B.
+ *
+ * The Daystar Digital LT200 boards do not support interrupt-driven
+ * IO. You must specify 'irq=0xff' as a module parameter to invoke
+ * polled mode. I also believe that the port probing logic is quite
+ * dangerous at best and certainly hopeless for a polled card. Best to
+ * specify both. - Steve H.
+ *
+ */
+
+/*
+ * Zero terminated list of IO ports to probe.
+ */
+
+static unsigned int cops_portlist[] = {
+ 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
+ 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360,
+ 0
+};
+
+/*
+ * Zero terminated list of IRQ ports to probe.
+ */
+
+static int cops_irqlist[] = {
+ 5, 4, 3, 0
+};
+
+static struct timer_list cops_timer;
+
+/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
+#ifndef COPS_DEBUG
+#define COPS_DEBUG 1
+#endif
+static unsigned int cops_debug = COPS_DEBUG;
+
+/* The number of low I/O ports used by the card. */
+#define COPS_IO_EXTENT 8
+
+/* Information that needs to be kept for each board. */
+
+struct cops_local
+{
+ struct net_device_stats stats;
+ int board; /* Holds what board type is. */
+ int nodeid; /* Set to 1 once have nodeid. */
+ unsigned char node_acquire; /* Node ID when acquired. */
+ struct at_addr node_addr; /* Full node addres */
+};
+
+/* Index to functions, as function prototypes. */
+extern int cops_probe (struct net_device *dev);
+static int cops_probe1 (struct net_device *dev, int ioaddr);
+static int cops_irq (int ioaddr, int board);
+
+static int cops_open (struct net_device *dev);
+static int cops_jumpstart (struct net_device *dev);
+static void cops_reset (struct net_device *dev, int sleep);
+static void cops_load (struct net_device *dev);
+static int cops_nodeid (struct net_device *dev, int nodeid);
+
+static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void cops_poll (unsigned long ltdev);
+static void cops_timeout(struct net_device *dev);
+static void cops_rx (struct net_device *dev);
+static int cops_send_packet (struct sk_buff *skb, struct net_device *dev);
+static void set_multicast_list (struct net_device *dev);
+static int cops_hard_header (struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len);
+
+static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static int cops_close (struct net_device *dev);
+static struct net_device_stats *cops_get_stats (struct net_device *dev);
+
+
+/*
+ * Check for a network adaptor of this type, and return '0' iff one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr in [1..0x1ff], always return failure.
+ * otherwise go with what we pass in.
+ */
+int __init cops_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if(base_addr == 0 && io)
+ base_addr=io;
+
+ if(base_addr > 0x1ff) /* Check a single specified location. */
+ return cops_probe1(dev, base_addr);
+ else if(base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ /* FIXME Does this really work for cards which generate irq?
+ * It's definitely N.G. for polled Tangent. sh
+ * Dayna cards don't autoprobe well at all, but if your card is
+ * at IRQ 5 & IO 0x240 we find it every time. ;) JS
+ */
+ for(i=0; cops_portlist[i]; i++) {
+ int ioaddr = cops_portlist[i];
+ if(check_region(ioaddr, COPS_IO_EXTENT))
+ continue;
+ if(cops_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+/*
+ * This is the real probe routine. Linux has a history of friendly device
+ * probes on the ISA bus. A good device probes avoids doing writes, and
+ * verifies that the correct device exists and functions.
+ */
+static int __init cops_probe1(struct net_device *dev, int ioaddr)
+{
+ struct cops_local *lp;
+ static unsigned version_printed = 0;
+
+ int board = board_type;
+
+ if(cops_debug && version_printed++ == 0)
+ printk("%s", version);
+
+ /*
+ * Since this board has jumpered interrupts, allocate the interrupt
+ * vector now. There is no point in waiting since no other device
+ * can use the interrupt, and this marks the irq as busy. Jumpered
+ * interrupts are typically not reported by the boards, and we must
+ * used AutoIRQ to find them.
+ */
+ switch (dev->irq)
+ {
+ case 0:
+ /* COPS AutoIRQ routine */
+ dev->irq = cops_irq(ioaddr, board);
+ if(!dev->irq)
+ return -EINVAL; /* No IRQ found on this port */
+ break;
+
+ case 1:
+ return -EINVAL;
+ break;
+
+ /* Fixup for users that don't know that IRQ 2 is really
+ * IRQ 9, or don't know which one to set.
+ */
+ case 2:
+ dev->irq = 9;
+ break;
+
+ /* Polled operation requested. Although irq of zero passed as
+ * a parameter tells the init routines to probe, we'll
+ * overload it to denote polled operation at runtime.
+ */
+ case 0xff:
+ dev->irq = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Reserve any actual interrupt. */
+ if(dev->irq && request_irq(dev->irq, &cops_interrupt, 0, cardname, dev))
+ return -EINVAL;
+
+ /* Grab the region so no one else tries to probe our ioports. */
+ request_region(ioaddr, COPS_IO_EXTENT, cardname);
+ dev->base_addr = ioaddr;
+
+ /* Initialize the private device structure. */
+ dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL);
+ if(dev->priv == NULL)
+ return -ENOMEM;
+
+ lp = (struct cops_local *)dev->priv;
+ memset(lp, 0, sizeof(struct cops_local));
+
+ /* Copy local board variable to lp struct. */
+ lp->board = board;
+
+ /* Fill in the fields of the device structure with LocalTalk values. */
+ ltalk_setup(dev);
+
+ dev->hard_start_xmit = cops_send_packet;
+ dev->tx_timeout = cops_timeout;
+ dev->watchdog_timeo = HZ * 2;
+ dev->hard_header = cops_hard_header;
+ dev->get_stats = cops_get_stats;
+ dev->open = cops_open;
+ dev->stop = cops_close;
+ dev->do_ioctl = cops_ioctl;
+ dev->set_multicast_list = set_multicast_list;
+ dev->mc_list = NULL;
+
+ /* Tell the user where the card is and what mode we're in. */
+ if(board==DAYNA)
+ printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n",
+ dev->name, cardname, ioaddr, dev->irq);
+ if(board==TANGENT) {
+ if(dev->irq)
+ printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n",
+ dev->name, cardname, ioaddr, dev->irq);
+ else
+ printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n",
+ dev->name, cardname, ioaddr);
+
+ }
+ return 0;
+}
+
+static int __init cops_irq (int ioaddr, int board)
+{ /*
+ * This does not use the IRQ to determine where the IRQ is. We just
+ * assume that when we get a correct status response that it's the IRQ.
+ * This really just verifies the IO port but since we only have access
+ * to such a small number of IRQs (5, 4, 3) this is not bad.
+ * This will probably not work for more than one card.
+ */
+ int irqaddr=0;
+ int i, x, status;
+
+ if(board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET);
+ inb(ioaddr+DAYNA_RESET);
+ udelay(333333);
+ }
+ if(board==TANGENT)
+ {
+ inb(ioaddr);
+ outb(0, ioaddr);
+ outb(0, ioaddr+TANG_RESET);
+ }
+
+ for(i=0; cops_irqlist[i] !=0; i++)
+ {
+ irqaddr = cops_irqlist[i];
+ for(x = 0xFFFF; x>0; x --) /* wait for response */
+ {
+ if(board==DAYNA)
+ {
+ status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
+ if(status == 1)
+ return irqaddr;
+ }
+ if(board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0)
+ return irqaddr;
+ }
+ }
+ }
+ return 0; /* no IRQ found */
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ */
+static int cops_open(struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+
+ if(dev->irq==0)
+ {
+ /*
+ * I don't know if the Dayna-style boards support polled
+ * operation. For now, only allow it for Tangent.
+ */
+ if(lp->board==TANGENT) /* Poll 20 times per second */
+ {
+ init_timer(&cops_timer);
+ cops_timer.function = cops_poll;
+ cops_timer.data = (unsigned long)dev;
+ cops_timer.expires = jiffies + 5;
+ add_timer(&cops_timer);
+ }
+ else
+ {
+ printk(KERN_WARNING "%s: No irq line set\n", dev->name);
+ return -EAGAIN;
+ }
+ }
+
+ cops_jumpstart(dev); /* Start the card up. */
+
+ netif_start_queue(dev);
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * This allows for a dynamic start/restart of the entire card.
+ */
+static int cops_jumpstart(struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+
+ /*
+ * Once the card has the firmware loaded and has acquired
+ * the nodeid, if it is reset it will lose it all.
+ */
+ cops_reset(dev,1); /* Need to reset card before load firmware. */
+ cops_load(dev); /* Load the firmware. */
+
+ /*
+ * If atalkd already gave us a nodeid we will use that
+ * one again, else we wait for atalkd to give us a nodeid
+ * in cops_ioctl. This may cause a problem if someone steals
+ * our nodeid while we are resetting.
+ */
+ if(lp->nodeid == 1)
+ cops_nodeid(dev,lp->node_acquire);
+
+ return 0;
+}
+
+static void tangent_wait_reset(int ioaddr)
+{
+ int timeout=0;
+
+ while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ mdelay(1); /* Wait 1 second */
+}
+
+/*
+ * Reset the LocalTalk board.
+ */
+static void cops_reset(struct net_device *dev, int sleep)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+
+ if(lp->board==TANGENT)
+ {
+ inb(ioaddr); /* Clear request latch. */
+ outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */
+ outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */
+
+ tangent_wait_reset(ioaddr);
+ outb(0, ioaddr+TANG_CLEAR_INT);
+ }
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */
+ inb(ioaddr+DAYNA_RESET); /* Clear the reset */
+ if(sleep)
+ {
+ long snap=jiffies;
+
+ /* Let card finish initializing, about 1/3 second */
+ while(jiffies-snap<HZ/3)
+ schedule();
+ }
+ else
+ udelay(333333);
+ }
+ netif_wake_queue(dev);
+ return;
+}
+
+static void cops_load (struct net_device *dev)
+{
+ struct ifreq ifr;
+ struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data;
+ struct cops_local *lp=(struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ int length, i = 0;
+
+ strcpy(ifr.ifr_name,"lt0");
+
+ /* Get card's firmware code and do some checks on it. */
+#ifdef CONFIG_COPS_DAYNA
+ if(lp->board==DAYNA)
+ {
+ ltf->length=sizeof(ffdrv_code);
+ ltf->data=ffdrv_code;
+ }
+ else
+#endif
+#ifdef CONFIG_COPS_TANGENT
+ if(lp->board==TANGENT)
+ {
+ ltf->length=sizeof(ltdrv_code);
+ ltf->data=ltdrv_code;
+ }
+ else
+#endif
+ {
+ printk(KERN_INFO "%s; unsupported board type.\n", dev->name);
+ return;
+ }
+
+ /* Check to make sure firmware is correct length. */
+ if(lp->board==DAYNA && ltf->length!=5983)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name);
+ return;
+ }
+ if(lp->board==TANGENT && ltf->length!=2501)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name);
+ return;
+ }
+
+ if(lp->board==DAYNA)
+ {
+ /*
+ * We must wait for a status response
+ * with the DAYNA board.
+ */
+ while(++i<65536)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1)
+ break;
+ }
+
+ if(i==65536)
+ return;
+ }
+
+ /*
+ * Upload the firmware and kick. Byte-by-byte works nicely here.
+ */
+ i=0;
+ length = ltf->length;
+ while(length--)
+ {
+ outb(ltf->data[i], ioaddr);
+ i++;
+ }
+
+ if(cops_debug > 1)
+ printk("%s: Uploaded firmware - %d bytes of %d bytes.\n",
+ dev->name, i, ltf->length);
+
+ if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */
+ outb(1, ioaddr+DAYNA_INT_CARD);
+ else /* Tell Tang to run the firmware code. */
+ inb(ioaddr);
+
+ if(lp->board==TANGENT)
+ {
+ tangent_wait_reset(ioaddr);
+ inb(ioaddr); /* Clear initial ready signal. */
+ }
+
+ return;
+}
+
+/*
+ * Get the LocalTalk Nodeid from the card. We can suggest
+ * any nodeid 1-254. The card will try and get that exact
+ * address else we can specify 0 as the nodeid and the card
+ * will autoprobe for a nodeid.
+ */
+static int cops_nodeid (struct net_device *dev, int nodeid)
+{
+ struct cops_local *lp = (struct cops_local *) dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if(lp->board == DAYNA)
+ {
+ /* Empty any pending adapter responses. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Kick any packets waiting. */
+ schedule();
+ }
+
+ outb(2, ioaddr); /* Output command packet length as 2. */
+ outb(0, ioaddr);
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
+ outb(nodeid, ioaddr); /* Suggest node address. */
+ }
+
+ if(lp->board == TANGENT)
+ {
+ /* Empty any pending adapter responses. */
+ while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */
+ cops_rx(dev); /* Kick out packets waiting. */
+ schedule();
+ }
+
+ /* Not sure what Tangent does if nodeid picked is used. */
+ if(nodeid == 0) /* Seed. */
+ nodeid = jiffies&0xFF; /* Get a random try */
+ outb(2, ioaddr); /* Command length LSB */
+ outb(0, ioaddr); /* Command length MSB */
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */
+ outb(nodeid, ioaddr); /* LAP address hint. */
+ outb(0xFF, ioaddr); /* Int. level to use */
+ }
+
+ lp->node_acquire=0; /* Set nodeid holder to 0. */
+ while(lp->node_acquire==0) /* Get *True* nodeid finally. */
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+
+ if(lp->board == DAYNA)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ if(lp->board == TANGENT)
+ {
+ if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ schedule();
+ }
+
+ if(cops_debug > 1)
+ printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n",
+ dev->name, lp->node_acquire);
+
+ lp->nodeid=1; /* Set got nodeid to 1. */
+
+ return 0;
+}
+
+/*
+ * Poll the Tangent type cards to see if we have work.
+ */
+
+static void cops_poll(unsigned long ltdev)
+{
+ int ioaddr, status;
+ int boguscount = 0;
+
+ struct net_device *dev = (struct net_device *)ltdev;
+
+ del_timer(&cops_timer);
+
+ if(dev == NULL)
+ return; /* We've been downed */
+
+ ioaddr = dev->base_addr;
+ do {
+ status=inb(ioaddr+TANG_CARD_STATUS);
+ if(status & TANG_RX_READY)
+ cops_rx(dev);
+ if(status & TANG_TX_READY)
+ netif_wake_queue(dev);
+ status = inb(ioaddr+TANG_CARD_STATUS);
+ } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
+
+ cops_timer.expires = jiffies+5;
+ add_timer(&cops_timer);
+
+ return;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = dev_id;
+ struct cops_local *lp;
+ int ioaddr, status;
+ int boguscount = 0;
+
+ ioaddr = dev->base_addr;
+ lp = (struct cops_local *)dev->priv;
+
+ if(lp->board==DAYNA)
+ {
+ do {
+ outb(0, ioaddr + COPS_CLEAR_INT);
+ status=inb(ioaddr+DAYNA_CARD_STATUS);
+ if((status&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev);
+ netif_wake_queue(dev);
+ } while(++boguscount < 20);
+ }
+ else
+ {
+ do {
+ status=inb(ioaddr+TANG_CARD_STATUS);
+ if(status & TANG_RX_READY)
+ cops_rx(dev);
+ if(status & TANG_TX_READY)
+ netif_wake_queue(dev);
+ status=inb(ioaddr+TANG_CARD_STATUS);
+ } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
+ }
+
+ return;
+}
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ */
+static void cops_rx(struct net_device *dev)
+{
+ int pkt_len = 0;
+ int rsp_type = 0;
+ struct sk_buff *skb;
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 0;
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli(); /* Disable interrupts. */
+
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr); /* Send out Zero length. */
+ outb(0, ioaddr);
+ outb(DATA_READ, ioaddr); /* Send read command out. */
+
+ /* Wait for DMA to turn around. */
+ while(++boguscount<1000000)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY)
+ break;
+ }
+
+ if(boguscount==1000000)
+ {
+ printk(KERN_WARNING "%s: DMA timed out.\n",dev->name);
+ return;
+ }
+ }
+
+ /* Get response length. */
+ if(lp->board==DAYNA)
+ pkt_len = inb(ioaddr) & 0xFF;
+ else
+ pkt_len = inb(ioaddr) & 0x00FF;
+ pkt_len |= (inb(ioaddr) << 8);
+ /* Input IO code. */
+ rsp_type=inb(ioaddr);
+
+ /* Malloc up new buffer. */
+ skb = dev_alloc_skb(pkt_len);
+ if(skb == NULL)
+ {
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ while(pkt_len--) /* Discard packet */
+ inb(ioaddr);
+ return;
+ }
+ skb->dev = dev;
+ skb_put(skb, pkt_len);
+ skb->protocol = htons(ETH_P_LOCALTALK);
+
+ insb(ioaddr, skb->data, pkt_len); /* Eat the Data */
+
+ if(lp->board==DAYNA)
+ outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */
+
+ restore_flags(flags); /* Restore interrupts. */
+
+ /* Check for bad response length */
+ if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
+ {
+ printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n",
+ dev->name, pkt_len);
+ lp->stats.tx_errors++;
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Set nodeid and then get out. */
+ if(rsp_type == LAP_INIT_RSP)
+ { /* Nodeid taken from received packet. */
+ lp->node_acquire = skb->data[0];
+ kfree_skb(skb);
+ return;
+ }
+
+ /* One last check to make sure we have a good packet. */
+ if(rsp_type != LAP_RESPONSE)
+ {
+ printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type);
+ lp->stats.tx_errors++;
+ kfree_skb(skb);
+ return;
+ }
+
+ skb->mac.raw = skb->data; /* Point to entire packet. */
+ skb_pull(skb,3);
+ skb->h.raw = skb->data; /* Point to data (Skip header). */
+
+ /* Update the counters. */
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+
+ /* Send packet to a higher place. */
+ netif_rx(skb);
+
+ return;
+}
+
+static void cops_timeout(struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ lp->stats.tx_errors++;
+ if(lp->board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name);
+ }
+ printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name);
+ cops_jumpstart(dev); /* Restart the card. */
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+}
+
+
+/*
+ * Make the card transmit a LocalTalk packet.
+ */
+
+static int cops_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ */
+
+ netif_stop_queue(dev);
+
+ save_flags(flags);
+ cli(); /* Disable interrupts. */
+ if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+ if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0);
+
+ /* Output IO length. */
+ outb(skb->len, ioaddr);
+ if(lp->board == DAYNA)
+ outb(skb->len >> 8, ioaddr);
+ else
+ outb((skb->len >> 8)&0x0FF, ioaddr);
+
+ /* Output IO code. */
+ outb(LAP_WRITE, ioaddr);
+
+ if(lp->board == DAYNA) /* Check the transmit buffer again. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+
+ outsb(ioaddr, skb->data, skb->len); /* Send out the data. */
+
+ if(lp->board==DAYNA) /* Dayna requires you kick the card */
+ outb(1, ioaddr+DAYNA_INT_CARD);
+
+ restore_flags(flags); /* Restore interrupts. */
+
+ /* Done sending packet, update counters and cleanup. */
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+ dev_kfree_skb (skb);
+ return 0;
+}
+
+/*
+ * Dummy function to keep the Appletalk layer happy.
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ if(cops_debug >= 3)
+ printk("%s: set_multicast_list executed\n", dev->name);
+}
+
+/*
+ * Another Dummy function to keep the Appletalk layer happy.
+ */
+
+static int cops_hard_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len)
+{
+ if(cops_debug >= 3)
+ printk("%s: cops_hard_header executed. Wow!\n", dev->name);
+ return 0;
+}
+
+/*
+ * System ioctls for the COPS LocalTalk card.
+ */
+
+static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr;
+ struct at_addr *aa=(struct at_addr *)&lp->node_addr;
+
+ switch(cmd)
+ {
+ case SIOCSIFADDR:
+ /* Get and set the nodeid and network # atalkd wants. */
+ cops_nodeid(dev, sa->sat_addr.s_node);
+ aa->s_net = sa->sat_addr.s_net;
+ aa->s_node = lp->node_acquire;
+
+ /* Set broardcast address. */
+ dev->broadcast[0] = 0xFF;
+
+ /* Set hardware address. */
+ dev->dev_addr[0] = aa->s_node;
+ dev->addr_len = 1;
+ return 0;
+
+ case SIOCGIFADDR:
+ sa->sat_addr.s_net = aa->s_net;
+ sa->sat_addr.s_node = aa->s_node;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * The inverse routine to cops_open().
+ */
+
+static int cops_close(struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+
+ /* If we were running polled, yank the timer.
+ */
+ if(lp->board==TANGENT && dev->irq==0)
+ del_timer(&cops_timer);
+
+ netif_stop_queue(dev);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *cops_get_stats(struct net_device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ return &lp->stats;
+}
+
+#ifdef MODULE
+static char lt_name[16];
+
+static struct net_device cops0_dev =
+{
+ lt_name, /* device name */
+ 0, 0, 0, 0,
+ 0x0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, cops_probe
+};
+
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(board_type, "i");
+
+int init_module(void)
+{
+ int result, err;
+
+ if(io == 0)
+ printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
+ cardname);
+
+ /* Copy the parameters from insmod into the device structure. */
+ cops0_dev.base_addr = io;
+ cops0_dev.irq = irq;
+
+ err=dev_alloc_name(&cops0_dev, "lt%d");
+ if(err < 0)
+ return err;
+
+ if((result = register_netdev(&cops0_dev)) != 0)
+ return result;
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ unregister_netdev(&cops0_dev);
+ if(cops0_dev.priv)
+ kfree_s(cops0_dev.priv, sizeof(struct cops_local));
+ if(cops0_dev.irq)
+ free_irq(cops0_dev.irq, &cops0_dev);
+ release_region(cops0_dev.base_addr, COPS_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c cops.c"
+ * c-basic-offset: 4
+ * c-file-offsets: ((substatement-open . 0))
+ * End:
+ */
--- /dev/null
+/* cops.h: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <jschlst@turbolinux.com>
+ */
+
+#ifndef __LINUX_COPSLTALK_H
+#define __LINUX_COPSLTALK_H
+
+#ifdef __KERNEL__
+
+/* Max LLAP size we will accept. */
+#define MAX_LLAP_SIZE 603
+
+/* Tangent */
+#define TANG_CARD_STATUS 1
+#define TANG_CLEAR_INT 1
+#define TANG_RESET 3
+
+#define TANG_TX_READY 1
+#define TANG_RX_READY 2
+
+/* Dayna */
+#define DAYNA_CMD_DATA 0
+#define DAYNA_CLEAR_INT 1
+#define DAYNA_CARD_STATUS 2
+#define DAYNA_INT_CARD 3
+#define DAYNA_RESET 4
+
+#define DAYNA_RX_READY 0
+#define DAYNA_TX_READY 1
+#define DAYNA_RX_REQUEST 3
+
+/* Same on both card types */
+#define COPS_CLEAR_INT 1
+
+/* LAP response codes received from the cards. */
+#define LAP_INIT 1 /* Init cmd */
+#define LAP_INIT_RSP 2 /* Init response */
+#define LAP_WRITE 3 /* Write cmd */
+#define DATA_READ 4 /* Data read */
+#define LAP_RESPONSE 4 /* Received ALAP frame response */
+#define LAP_GETSTAT 5 /* Get LAP and HW status */
+#define LAP_RSPSTAT 6 /* Status response */
+
+#endif
+
+/*
+ * Structure to hold the firmware information.
+ */
+struct ltfirmware
+{
+ unsigned int length;
+ unsigned char * data;
+};
+
+#define DAYNA 1
+#define TANGENT 2
+
+#endif
--- /dev/null
+
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * separate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <jschlst@turbolinux.com>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_DAYNA
+
+unsigned char ffdrv_code[] = {
+ 58,3,0,50,228,149,33,255,255,34,226,149,
+ 249,17,40,152,33,202,154,183,237,82,77,68,
+ 11,107,98,19,54,0,237,176,175,50,80,0,
+ 62,128,237,71,62,32,237,57,51,62,12,237,
+ 57,50,237,57,54,62,6,237,57,52,62,12,
+ 237,57,49,33,107,137,34,32,128,33,83,130,
+ 34,40,128,33,86,130,34,42,128,33,112,130,
+ 34,36,128,33,211,130,34,38,128,62,0,237,
+ 57,16,33,63,148,34,34,128,237,94,205,15,
+ 130,251,205,168,145,24,141,67,111,112,121,114,
+ 105,103,104,116,32,40,67,41,32,49,57,56,
+ 56,32,45,32,68,97,121,110,97,32,67,111,
+ 109,109,117,110,105,99,97,116,105,111,110,115,
+ 32,32,32,65,108,108,32,114,105,103,104,116,
+ 115,32,114,101,115,101,114,118,101,100,46,32,
+ 32,40,68,40,68,7,16,8,34,7,22,6,
+ 16,5,12,4,8,3,6,140,0,16,39,128,
+ 0,4,96,10,224,6,0,7,126,2,64,11,
+ 118,12,6,13,0,14,193,15,0,5,96,3,
+ 192,1,64,9,8,62,9,211,66,62,192,211,
+ 66,62,100,61,32,253,6,28,33,205,129,14,
+ 66,237,163,194,253,129,6,28,33,205,129,14,
+ 64,237,163,194,9,130,201,62,47,50,71,152,
+ 62,47,211,68,58,203,129,237,57,20,58,204,
+ 129,237,57,21,33,77,152,54,132,205,233,129,
+ 58,228,149,254,209,40,6,56,4,62,0,24,
+ 2,219,96,33,233,149,119,230,62,33,232,149,
+ 119,213,33,8,152,17,7,0,25,119,19,25,
+ 119,209,201,251,237,77,245,197,213,229,221,229,
+ 205,233,129,62,1,50,106,137,205,158,139,221,
+ 225,225,209,193,241,251,237,77,245,197,213,219,
+ 72,237,56,16,230,46,237,57,16,237,56,12,
+ 58,72,152,183,32,26,6,20,17,128,2,237,
+ 56,46,187,32,35,237,56,47,186,32,29,219,
+ 72,230,1,32,3,5,32,232,175,50,72,152,
+ 229,221,229,62,1,50,106,137,205,158,139,221,
+ 225,225,24,25,62,1,50,72,152,58,201,129,
+ 237,57,12,58,202,129,237,57,13,237,56,16,
+ 246,17,237,57,16,209,193,241,251,237,77,245,
+ 197,229,213,221,229,237,56,16,230,17,237,57,
+ 16,237,56,20,58,34,152,246,16,246,8,211,
+ 68,62,6,61,32,253,58,34,152,246,8,211,
+ 68,58,203,129,237,57,20,58,204,129,237,57,
+ 21,237,56,16,246,34,237,57,16,221,225,209,
+ 225,193,241,251,237,77,33,2,0,57,126,230,
+ 3,237,100,1,40,2,246,128,230,130,245,62,
+ 5,211,64,241,211,64,201,229,213,243,237,56,
+ 16,230,46,237,57,16,237,56,12,251,70,35,
+ 35,126,254,175,202,77,133,254,129,202,15,133,
+ 230,128,194,191,132,43,58,44,152,119,33,76,
+ 152,119,35,62,132,119,120,254,255,40,4,58,
+ 49,152,119,219,72,43,43,112,17,3,0,237,
+ 56,52,230,248,237,57,52,219,72,230,1,194,
+ 141,131,209,225,237,56,52,246,6,237,57,52,
+ 62,1,55,251,201,62,3,211,66,62,192,211,
+ 66,62,48,211,66,0,0,219,66,230,1,40,
+ 4,219,67,24,240,205,203,135,58,75,152,254,
+ 255,202,128,132,58,49,152,254,161,250,207,131,
+ 58,34,152,211,68,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,24,0,62,
+ 14,211,66,62,33,211,66,62,1,211,66,62,
+ 64,211,66,62,3,211,66,62,209,211,66,62,
+ 100,71,219,66,230,1,32,6,5,32,247,195,
+ 248,132,219,67,71,58,44,152,184,194,248,132,
+ 62,100,71,219,66,230,1,32,6,5,32,247,
+ 195,248,132,219,67,62,100,71,219,66,230,1,
+ 32,6,5,32,247,195,248,132,219,67,254,133,
+ 32,7,62,0,50,74,152,24,17,254,173,32,
+ 7,62,1,50,74,152,24,6,254,141,194,248,
+ 132,71,209,225,58,49,152,254,132,32,10,62,
+ 50,205,2,134,205,144,135,24,27,254,140,32,
+ 15,62,110,205,2,134,62,141,184,32,5,205,
+ 144,135,24,8,62,10,205,2,134,205,8,134,
+ 62,1,50,106,137,205,158,139,237,56,52,246,
+ 6,237,57,52,175,183,251,201,62,20,135,237,
+ 57,20,175,237,57,21,237,56,16,246,2,237,
+ 57,16,237,56,20,95,237,56,21,123,254,10,
+ 48,244,237,56,16,230,17,237,57,16,209,225,
+ 205,144,135,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,183,251,201,209,
+ 225,243,219,72,230,1,40,13,62,10,211,66,
+ 0,0,219,66,230,192,202,226,132,237,56,52,
+ 246,6,237,57,52,62,1,55,251,201,205,203,
+ 135,62,1,50,106,137,205,158,139,237,56,52,
+ 246,6,237,57,52,183,251,201,209,225,62,1,
+ 50,106,137,205,158,139,237,56,52,246,6,237,
+ 57,52,62,2,55,251,201,209,225,243,219,72,
+ 230,1,202,213,132,62,10,211,66,0,0,219,
+ 66,230,192,194,213,132,229,62,1,50,106,137,
+ 42,40,152,205,65,143,225,17,3,0,205,111,
+ 136,62,6,211,66,58,44,152,211,66,237,56,
+ 52,246,6,237,57,52,183,251,201,209,197,237,
+ 56,52,230,248,237,57,52,219,72,230,1,32,
+ 15,193,225,237,56,52,246,6,237,57,52,62,
+ 1,55,251,201,14,23,58,37,152,254,0,40,
+ 14,14,2,254,1,32,5,62,140,119,24,3,
+ 62,132,119,43,43,197,205,203,135,193,62,1,
+ 211,66,62,64,211,66,62,3,211,66,62,193,
+ 211,66,62,100,203,39,71,219,66,230,1,32,
+ 6,5,32,247,195,229,133,33,238,151,219,67,
+ 71,58,44,152,184,194,229,133,119,62,100,71,
+ 219,66,230,1,32,6,5,32,247,195,229,133,
+ 219,67,35,119,13,32,234,193,225,62,1,50,
+ 106,137,205,158,139,237,56,52,246,6,237,57,
+ 52,175,183,251,201,33,234,151,35,35,62,255,
+ 119,193,225,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,251,201,243,61,
+ 32,253,251,201,62,3,211,66,62,192,211,66,
+ 58,49,152,254,140,32,19,197,229,213,17,181,
+ 129,33,185,129,1,2,0,237,176,209,225,193,
+ 24,27,229,213,33,187,129,58,49,152,230,15,
+ 87,30,2,237,92,25,17,181,129,126,18,19,
+ 35,126,18,209,225,58,34,152,246,8,211,68,
+ 58,49,152,254,165,40,14,254,164,40,10,62,
+ 10,211,66,62,224,211,66,24,25,58,74,152,
+ 254,0,40,10,62,10,211,66,62,160,211,66,
+ 24,8,62,10,211,66,62,128,211,66,62,11,
+ 211,66,62,6,211,66,205,147,143,62,5,211,
+ 66,62,224,211,66,62,5,211,66,62,96,211,
+ 66,62,5,61,32,253,62,5,211,66,62,224,
+ 211,66,62,14,61,32,253,62,5,211,66,62,
+ 233,211,66,62,128,211,66,58,181,129,61,32,
+ 253,62,1,211,66,62,192,211,66,1,254,19,
+ 237,56,46,187,32,6,13,32,247,195,226,134,
+ 62,192,211,66,0,0,219,66,203,119,40,250,
+ 219,66,203,87,40,250,243,237,56,16,230,17,
+ 237,57,16,237,56,20,251,62,5,211,66,62,
+ 224,211,66,58,182,129,61,32,253,229,33,181,
+ 129,58,183,129,203,63,119,35,58,184,129,119,
+ 225,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,62,47,211,68,62,5,211,
+ 66,62,233,211,66,58,181,129,61,32,253,62,
+ 5,211,66,62,224,211,66,58,182,129,61,32,
+ 253,62,5,211,66,62,96,211,66,201,229,213,
+ 58,50,152,230,15,87,30,2,237,92,33,187,
+ 129,25,17,181,129,126,18,35,19,126,18,209,
+ 225,58,71,152,246,8,211,68,58,50,152,254,
+ 165,40,14,254,164,40,10,62,10,211,66,62,
+ 224,211,66,24,8,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,195,248,135,
+ 62,3,211,66,62,192,211,66,197,229,213,17,
+ 181,129,33,183,129,1,2,0,237,176,209,225,
+ 193,62,47,211,68,62,10,211,66,62,224,211,
+ 66,62,11,211,66,62,118,211,66,62,1,211,
+ 66,62,0,211,66,205,147,143,195,16,136,62,
+ 3,211,66,62,192,211,66,197,229,213,17,181,
+ 129,33,183,129,1,2,0,237,176,209,225,193,
+ 62,47,211,68,62,10,211,66,62,224,211,66,
+ 62,11,211,66,62,118,211,66,205,147,143,62,
+ 5,211,66,62,224,211,66,62,5,211,66,62,
+ 96,211,66,62,5,61,32,253,62,5,211,66,
+ 62,224,211,66,62,14,61,32,253,62,5,211,
+ 66,62,233,211,66,62,128,211,66,58,181,129,
+ 61,32,253,62,1,211,66,62,192,211,66,1,
+ 254,19,237,56,46,187,32,6,13,32,247,195,
+ 88,136,62,192,211,66,0,0,219,66,203,119,
+ 40,250,219,66,203,87,40,250,62,5,211,66,
+ 62,224,211,66,58,182,129,61,32,253,62,5,
+ 211,66,62,96,211,66,201,197,14,67,6,0,
+ 62,3,211,66,62,192,211,66,62,48,211,66,
+ 0,0,219,66,230,1,40,4,219,67,24,240,
+ 62,5,211,66,62,233,211,66,62,128,211,66,
+ 58,181,129,61,32,253,237,163,29,62,192,211,
+ 66,219,66,230,4,40,250,237,163,29,32,245,
+ 219,66,230,4,40,250,62,255,71,219,66,230,
+ 4,40,3,5,32,247,219,66,230,4,40,250,
+ 62,5,211,66,62,224,211,66,58,182,129,61,
+ 32,253,62,5,211,66,62,96,211,66,58,71,
+ 152,254,1,202,18,137,62,16,211,66,62,56,
+ 211,66,62,14,211,66,62,33,211,66,62,1,
+ 211,66,62,248,211,66,237,56,48,246,153,230,
+ 207,237,57,48,62,3,211,66,62,221,211,66,
+ 193,201,58,71,152,211,68,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,62,14,
+ 211,66,62,33,211,66,62,1,211,66,62,248,
+ 211,66,237,56,48,246,145,246,8,230,207,237,
+ 57,48,62,3,211,66,62,221,211,66,193,201,
+ 44,3,1,0,70,69,1,245,197,213,229,175,
+ 50,72,152,237,56,16,230,46,237,57,16,237,
+ 56,12,62,1,211,66,0,0,219,66,95,230,
+ 160,32,3,195,20,139,123,230,96,194,72,139,
+ 62,48,211,66,62,1,211,66,62,64,211,66,
+ 237,91,40,152,205,207,143,25,43,55,237,82,
+ 218,70,139,34,42,152,98,107,58,44,152,190,
+ 194,210,138,35,35,62,130,190,194,200,137,62,
+ 1,50,48,152,62,175,190,202,82,139,62,132,
+ 190,32,44,50,50,152,62,47,50,71,152,229,
+ 175,50,106,137,42,40,152,205,65,143,225,54,
+ 133,43,70,58,44,152,119,43,112,17,3,0,
+ 62,10,205,2,134,205,111,136,195,158,138,62,
+ 140,190,32,19,50,50,152,58,233,149,230,4,
+ 202,222,138,62,1,50,71,152,195,219,137,126,
+ 254,160,250,185,138,254,166,242,185,138,50,50,
+ 152,43,126,35,229,213,33,234,149,95,22,0,
+ 25,126,254,132,40,18,254,140,40,14,58,50,
+ 152,230,15,87,126,31,21,242,65,138,56,2,
+ 175,119,58,50,152,230,15,87,58,233,149,230,
+ 62,31,21,242,85,138,218,98,138,209,225,195,
+ 20,139,58,50,152,33,100,137,230,15,95,22,
+ 0,25,126,50,71,152,209,225,58,50,152,254,
+ 164,250,135,138,58,73,152,254,0,40,4,54,
+ 173,24,2,54,133,43,70,58,44,152,119,43,
+ 112,17,3,0,205,70,135,175,50,106,137,205,
+ 208,139,58,199,129,237,57,12,58,200,129,237,
+ 57,13,237,56,16,246,17,237,57,16,225,209,
+ 193,241,251,237,77,62,129,190,194,227,138,54,
+ 130,43,70,58,44,152,119,43,112,17,3,0,
+ 205,144,135,195,20,139,35,35,126,254,132,194,
+ 227,138,175,50,106,137,205,158,139,24,42,58,
+ 201,154,254,1,40,7,62,1,50,106,137,24,
+ 237,58,106,137,254,1,202,222,138,62,128,166,
+ 194,222,138,221,229,221,33,67,152,205,127,142,
+ 205,109,144,221,225,225,209,193,241,251,237,77,
+ 58,106,137,254,1,202,44,139,58,50,152,254,
+ 164,250,44,139,58,73,152,238,1,50,73,152,
+ 221,229,221,33,51,152,205,127,142,221,225,62,
+ 1,50,106,137,205,158,139,195,13,139,24,208,
+ 24,206,24,204,230,64,40,3,195,20,139,195,
+ 20,139,43,126,33,8,152,119,35,58,44,152,
+ 119,43,237,91,35,152,205,203,135,205,158,139,
+ 195,13,139,175,50,78,152,62,3,211,66,62,
+ 192,211,66,201,197,33,4,0,57,126,35,102,
+ 111,62,1,50,106,137,219,72,205,141,139,193,
+ 201,62,1,50,78,152,34,40,152,54,0,35,
+ 35,54,0,195,163,139,58,78,152,183,200,229,
+ 33,181,129,58,183,129,119,35,58,184,129,119,
+ 225,62,47,211,68,62,14,211,66,62,193,211,
+ 66,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,195,3,140,58,78,152,183,
+ 200,58,71,152,211,68,254,69,40,4,254,70,
+ 32,17,58,73,152,254,0,40,10,62,10,211,
+ 66,62,160,211,66,24,8,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,219,66,
+ 230,1,40,4,219,67,24,240,62,14,211,66,
+ 62,33,211,66,42,40,152,205,65,143,62,1,
+ 211,66,62,248,211,66,237,56,48,246,145,246,
+ 8,230,207,237,57,48,62,3,211,66,62,221,
+ 211,66,201,62,16,211,66,62,56,211,66,62,
+ 48,211,66,0,0,219,66,230,1,40,4,219,
+ 67,24,240,62,14,211,66,62,33,211,66,62,
+ 1,211,66,62,248,211,66,237,56,48,246,153,
+ 230,207,237,57,48,62,3,211,66,62,221,211,
+ 66,201,229,213,33,234,149,95,22,0,25,126,
+ 254,132,40,4,254,140,32,2,175,119,123,209,
+ 225,201,6,8,14,0,31,48,1,12,16,250,
+ 121,201,33,4,0,57,94,35,86,33,2,0,
+ 57,126,35,102,111,221,229,34,89,152,237,83,
+ 91,152,221,33,63,152,205,127,142,58,81,152,
+ 50,82,152,58,80,152,135,50,80,152,205,162,
+ 140,254,3,56,16,58,81,152,135,60,230,15,
+ 50,81,152,175,50,80,152,24,23,58,79,152,
+ 205,162,140,254,3,48,13,58,81,152,203,63,
+ 50,81,152,62,255,50,79,152,58,81,152,50,
+ 82,152,58,79,152,135,50,79,152,62,32,50,
+ 83,152,50,84,152,237,56,16,230,17,237,57,
+ 16,219,72,62,192,50,93,152,62,93,50,94,
+ 152,58,93,152,61,50,93,152,32,9,58,94,
+ 152,61,50,94,152,40,44,62,170,237,57,20,
+ 175,237,57,21,237,56,16,246,2,237,57,16,
+ 219,72,230,1,202,29,141,237,56,20,71,237,
+ 56,21,120,254,10,48,237,237,56,16,230,17,
+ 237,57,16,243,62,14,211,66,62,65,211,66,
+ 251,58,39,152,23,23,60,50,39,152,71,58,
+ 82,152,160,230,15,40,22,71,14,10,219,66,
+ 230,16,202,186,141,219,72,230,1,202,186,141,
+ 13,32,239,16,235,42,89,152,237,91,91,152,
+ 205,47,131,48,7,61,202,186,141,195,227,141,
+ 221,225,33,0,0,201,221,33,55,152,205,127,
+ 142,58,84,152,61,50,84,152,40,19,58,82,
+ 152,246,1,50,82,152,58,79,152,246,1,50,
+ 79,152,195,29,141,221,225,33,1,0,201,221,
+ 33,59,152,205,127,142,58,80,152,246,1,50,
+ 80,152,58,82,152,135,246,1,50,82,152,58,
+ 83,152,61,50,83,152,194,29,141,221,225,33,
+ 2,0,201,221,229,33,0,0,57,17,4,0,
+ 25,126,50,44,152,230,128,50,85,152,58,85,
+ 152,183,40,6,221,33,88,2,24,4,221,33,
+ 150,0,58,44,152,183,40,53,60,40,50,60,
+ 40,47,61,61,33,86,152,119,35,119,35,54,
+ 129,175,50,48,152,221,43,221,229,225,124,181,
+ 40,42,33,86,152,17,3,0,205,189,140,17,
+ 232,3,27,123,178,32,251,58,48,152,183,40,
+ 224,58,44,152,71,62,7,128,230,127,71,58,
+ 85,152,176,50,44,152,24,162,221,225,201,183,
+ 221,52,0,192,221,52,1,192,221,52,2,192,
+ 221,52,3,192,55,201,245,62,1,211,100,241,
+ 201,245,62,1,211,96,241,201,33,2,0,57,
+ 126,35,102,111,237,56,48,230,175,237,57,48,
+ 62,48,237,57,49,125,237,57,32,124,237,57,
+ 33,62,0,237,57,34,62,88,237,57,35,62,
+ 0,237,57,36,237,57,37,33,128,2,125,237,
+ 57,38,124,237,57,39,237,56,48,246,97,230,
+ 207,237,57,48,62,0,237,57,0,62,0,211,
+ 96,211,100,201,33,2,0,57,126,35,102,111,
+ 237,56,48,230,175,237,57,48,62,12,237,57,
+ 49,62,76,237,57,32,62,0,237,57,33,237,
+ 57,34,125,237,57,35,124,237,57,36,62,0,
+ 237,57,37,33,128,2,125,237,57,38,124,237,
+ 57,39,237,56,48,246,97,230,207,237,57,48,
+ 62,1,211,96,201,33,2,0,57,126,35,102,
+ 111,229,237,56,48,230,87,237,57,48,125,237,
+ 57,40,124,237,57,41,62,0,237,57,42,62,
+ 67,237,57,43,62,0,237,57,44,58,106,137,
+ 254,1,32,5,33,6,0,24,3,33,128,2,
+ 125,237,57,46,124,237,57,47,237,56,50,230,
+ 252,246,2,237,57,50,225,201,33,4,0,57,
+ 94,35,86,33,2,0,57,126,35,102,111,237,
+ 56,48,230,87,237,57,48,125,237,57,40,124,
+ 237,57,41,62,0,237,57,42,62,67,237,57,
+ 43,62,0,237,57,44,123,237,57,46,122,237,
+ 57,47,237,56,50,230,244,246,0,237,57,50,
+ 237,56,48,246,145,230,207,237,57,48,201,213,
+ 237,56,46,95,237,56,47,87,237,56,46,111,
+ 237,56,47,103,183,237,82,32,235,33,128,2,
+ 183,237,82,209,201,213,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,209,201,245,
+ 197,1,52,0,237,120,230,253,237,121,193,241,
+ 201,245,197,1,52,0,237,120,246,2,237,121,
+ 193,241,201,33,2,0,57,126,35,102,111,126,
+ 35,110,103,201,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,203,124,40,6,33,0,125,34,100,
+ 152,42,104,152,35,35,35,229,205,120,139,193,
+ 201,205,186,149,229,42,40,152,35,35,35,229,
+ 205,39,144,193,124,230,3,103,221,117,254,221,
+ 116,255,237,91,42,152,35,35,35,183,237,82,
+ 32,12,17,5,0,42,42,152,205,171,149,242,
+ 169,144,42,40,152,229,205,120,139,193,195,198,
+ 149,237,91,42,152,42,98,152,25,34,98,152,
+ 19,19,19,42,102,152,25,34,102,152,237,91,
+ 100,152,33,158,253,25,237,91,102,152,205,171,
+ 149,242,214,144,33,0,0,34,102,152,62,1,
+ 50,95,152,205,225,144,195,198,149,58,95,152,
+ 183,200,237,91,96,152,42,102,152,205,171,149,
+ 242,5,145,237,91,102,152,33,98,2,25,237,
+ 91,96,152,205,171,149,250,37,145,237,91,96,
+ 152,42,102,152,183,237,82,32,7,42,98,152,
+ 125,180,40,13,237,91,102,152,42,96,152,205,
+ 171,149,242,58,145,237,91,104,152,42,102,152,
+ 25,35,35,35,229,205,120,139,193,175,50,95,
+ 152,201,195,107,139,205,206,149,250,255,243,205,
+ 225,144,251,58,230,149,183,194,198,149,17,1,
+ 0,42,98,152,205,171,149,250,198,149,62,1,
+ 50,230,149,237,91,96,152,42,104,152,25,221,
+ 117,252,221,116,253,237,91,104,152,42,96,152,
+ 25,35,35,35,221,117,254,221,116,255,35,35,
+ 35,229,205,39,144,124,230,3,103,35,35,35,
+ 221,117,250,221,116,251,235,221,110,252,221,102,
+ 253,115,35,114,35,54,4,62,1,211,100,211,
+ 84,195,198,149,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,33,109,152,54,0,33,107,152,229,
+ 205,240,142,193,62,47,50,34,152,62,132,50,
+ 49,152,205,241,145,205,61,145,58,39,152,60,
+ 50,39,152,24,241,205,206,149,251,255,33,109,
+ 152,126,183,202,198,149,110,221,117,251,33,109,
+ 152,54,0,221,126,251,254,1,40,28,254,3,
+ 40,101,254,4,202,190,147,254,5,202,147,147,
+ 254,8,40,87,33,107,152,229,205,240,142,195,
+ 198,149,58,201,154,183,32,21,33,111,152,126,
+ 50,229,149,205,52,144,33,110,152,110,38,0,
+ 229,205,11,142,193,237,91,96,152,42,104,152,
+ 25,221,117,254,221,116,255,35,35,54,2,17,
+ 2,0,43,43,115,35,114,58,44,152,35,35,
+ 119,58,228,149,35,119,62,1,211,100,211,84,
+ 62,1,50,201,154,24,169,205,153,142,58,231,
+ 149,183,40,250,175,50,231,149,33,110,152,126,
+ 254,255,40,91,58,233,149,230,63,183,40,83,
+ 94,22,0,33,234,149,25,126,183,40,13,33,
+ 110,152,94,33,234,150,25,126,254,3,32,36,
+ 205,81,148,125,180,33,110,152,94,22,0,40,
+ 17,33,234,149,25,54,0,33,107,152,229,205,
+ 240,142,193,195,198,149,33,234,150,25,54,0,
+ 33,110,152,94,22,0,33,234,149,25,126,50,
+ 49,152,254,132,32,37,62,47,50,34,152,42,
+ 107,152,229,33,110,152,229,205,174,140,193,193,
+ 125,180,33,110,152,94,22,0,33,234,150,202,
+ 117,147,25,52,195,120,147,58,49,152,254,140,
+ 32,7,62,1,50,34,152,24,210,62,32,50,
+ 106,152,24,19,58,49,152,95,58,106,152,163,
+ 183,58,106,152,32,11,203,63,50,106,152,58,
+ 106,152,183,32,231,254,2,40,51,254,4,40,
+ 38,254,8,40,26,254,16,40,13,254,32,32,
+ 158,62,165,50,49,152,62,69,24,190,62,164,
+ 50,49,152,62,70,24,181,62,163,50,49,152,
+ 175,24,173,62,162,50,49,152,62,1,24,164,
+ 62,161,50,49,152,62,3,24,155,25,54,0,
+ 221,126,251,254,8,40,7,58,230,149,183,202,
+ 32,146,33,107,152,229,205,240,142,193,211,84,
+ 195,198,149,237,91,96,152,42,104,152,25,221,
+ 117,254,221,116,255,35,35,54,6,17,2,0,
+ 43,43,115,35,114,58,228,149,35,35,119,58,
+ 233,149,35,119,205,146,142,195,32,146,237,91,
+ 96,152,42,104,152,25,229,205,160,142,193,58,
+ 231,149,183,40,250,175,50,231,149,243,237,91,
+ 96,152,42,104,152,25,221,117,254,221,116,255,
+ 78,35,70,221,113,252,221,112,253,89,80,42,
+ 98,152,183,237,82,34,98,152,203,124,40,19,
+ 33,0,0,34,98,152,34,102,152,34,96,152,
+ 62,1,50,95,152,24,40,221,94,252,221,86,
+ 253,19,19,19,42,96,152,25,34,96,152,237,
+ 91,100,152,33,158,253,25,237,91,96,152,205,
+ 171,149,242,55,148,33,0,0,34,96,152,175,
+ 50,230,149,251,195,32,146,245,62,1,50,231,
+ 149,62,16,237,57,0,211,80,241,251,237,77,
+ 201,205,186,149,229,229,33,0,0,34,37,152,
+ 33,110,152,126,50,234,151,58,44,152,33,235,
+ 151,119,221,54,253,0,221,54,254,0,195,230,
+ 148,33,236,151,54,175,33,3,0,229,33,234,
+ 151,229,205,174,140,193,193,33,236,151,126,254,
+ 255,40,74,33,245,151,110,221,117,255,33,249,
+ 151,126,221,166,255,221,119,255,33,253,151,126,
+ 221,166,255,221,119,255,58,232,149,95,221,126,
+ 255,163,221,119,255,183,40,15,230,191,33,110,
+ 152,94,22,0,33,234,149,25,119,24,12,33,
+ 110,152,94,22,0,33,234,149,25,54,132,33,
+ 0,0,195,198,149,221,110,253,221,102,254,35,
+ 221,117,253,221,116,254,17,32,0,221,110,253,
+ 221,102,254,205,171,149,250,117,148,58,233,149,
+ 203,87,40,84,33,1,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,53,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,14,33,110,
+ 152,94,22,0,33,234,149,25,54,140,24,159,
+ 221,110,253,221,102,254,35,221,117,253,221,116,
+ 254,17,32,0,221,110,253,221,102,254,205,171,
+ 149,250,12,149,33,2,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,54,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,15,33,110,
+ 152,94,22,0,33,234,149,25,54,132,195,211,
+ 148,221,110,253,221,102,254,35,221,117,253,221,
+ 116,254,17,32,0,221,110,253,221,102,254,205,
+ 171,149,250,96,149,33,1,0,195,198,149,124,
+ 170,250,179,149,237,82,201,124,230,128,237,82,
+ 60,201,225,253,229,221,229,221,33,0,0,221,
+ 57,233,221,249,221,225,253,225,201,233,225,253,
+ 229,221,229,221,33,0,0,221,57,94,35,86,
+ 35,235,57,249,235,233,0,0,0,0,0,0,
+ 62,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 175,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,133,1,0,0,0,63,
+ 255,255,255,255,0,0,0,63,0,0,0,0,
+ 0,0,0,0,0,0,0,24,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0
+ } ;
+
+#endif
--- /dev/null
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * separate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <jschlst@turbolinux.com>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_TANGENT
+
+unsigned char ltdrv_code[] = {
+ 58,3,0,50,148,10,33,143,15,62,85,119,
+ 190,32,9,62,170,119,190,32,3,35,24,241,
+ 34,146,10,249,17,150,10,33,143,15,183,237,
+ 82,77,68,11,107,98,19,54,0,237,176,62,
+ 16,237,57,51,62,0,237,57,50,237,57,54,
+ 62,12,237,57,49,62,195,33,39,2,50,56,
+ 0,34,57,0,237,86,205,30,2,251,205,60,
+ 10,24,169,67,111,112,121,114,105,103,104,116,
+ 32,40,99,41,32,49,57,56,56,45,49,57,
+ 57,50,44,32,80,114,105,110,116,105,110,103,
+ 32,67,111,109,109,117,110,105,99,97,116,105,
+ 111,110,115,32,65,115,115,111,99,105,97,116,
+ 101,115,44,32,73,110,99,46,65,108,108,32,
+ 114,105,103,104,116,115,32,114,101,115,101,114,
+ 118,101,100,46,32,32,4,4,22,40,255,60,
+ 4,96,10,224,6,0,7,126,2,64,11,246,
+ 12,6,13,0,14,193,15,0,5,96,3,192,
+ 1,0,9,8,62,3,211,82,62,192,211,82,
+ 201,62,3,211,82,62,213,211,82,201,62,5,
+ 211,82,62,224,211,82,201,62,5,211,82,62,
+ 224,211,82,201,62,5,211,82,62,96,211,82,
+ 201,6,28,33,180,1,14,82,237,163,194,4,
+ 2,33,39,2,34,64,0,58,3,0,230,1,
+ 192,62,11,237,121,62,118,237,121,201,33,182,
+ 10,54,132,205,253,1,201,245,197,213,229,42,
+ 150,10,14,83,17,98,2,67,20,237,162,58,
+ 179,1,95,219,82,230,1,32,6,29,32,247,
+ 195,17,3,62,1,211,82,219,82,95,230,160,
+ 32,10,237,162,32,225,21,32,222,195,15,3,
+ 237,162,123,230,96,194,21,3,62,48,211,82,
+ 62,1,211,82,175,211,82,237,91,150,10,43,
+ 55,237,82,218,19,3,34,152,10,98,107,58,
+ 154,10,190,32,81,62,1,50,158,10,35,35,
+ 62,132,190,32,44,54,133,43,70,58,154,10,
+ 119,43,112,17,3,0,205,137,3,62,16,211,
+ 82,62,56,211,82,205,217,1,42,150,10,14,
+ 83,17,98,2,67,20,58,178,1,95,195,59,
+ 2,62,129,190,194,227,2,54,130,43,70,58,
+ 154,10,119,43,112,17,3,0,205,137,3,195,
+ 254,2,35,35,126,254,132,194,227,2,205,61,
+ 3,24,20,62,128,166,194,222,2,221,229,221,
+ 33,175,10,205,93,6,205,144,7,221,225,225,
+ 209,193,241,251,237,77,221,229,221,33,159,10,
+ 205,93,6,221,225,205,61,3,195,247,2,24,
+ 237,24,235,24,233,230,64,40,2,24,227,24,
+ 225,175,50,179,10,205,208,1,201,197,33,4,
+ 0,57,126,35,102,111,205,51,3,193,201,62,
+ 1,50,179,10,34,150,10,54,0,58,179,10,
+ 183,200,62,14,211,82,62,193,211,82,62,10,
+ 211,82,62,224,211,82,62,6,211,82,58,154,
+ 10,211,82,62,16,211,82,62,56,211,82,62,
+ 48,211,82,219,82,230,1,40,4,219,83,24,
+ 242,62,14,211,82,62,33,211,82,62,1,211,
+ 82,62,9,211,82,62,32,211,82,205,217,1,
+ 201,14,83,205,208,1,24,23,14,83,205,208,
+ 1,205,226,1,58,174,1,61,32,253,205,244,
+ 1,58,174,1,61,32,253,205,226,1,58,175,
+ 1,61,32,253,62,5,211,82,62,233,211,82,
+ 62,128,211,82,58,176,1,61,32,253,237,163,
+ 27,62,192,211,82,219,82,230,4,40,250,237,
+ 163,27,122,179,32,243,219,82,230,4,40,250,
+ 58,178,1,71,219,82,230,4,40,3,5,32,
+ 247,219,82,230,4,40,250,205,235,1,58,177,
+ 1,61,32,253,205,244,1,201,229,213,35,35,
+ 126,230,128,194,145,4,43,58,154,10,119,43,
+ 70,33,181,10,119,43,112,17,3,0,243,62,
+ 10,211,82,219,82,230,128,202,41,4,209,225,
+ 62,1,55,251,201,205,144,3,58,180,10,254,
+ 255,202,127,4,205,217,1,58,178,1,71,219,
+ 82,230,1,32,6,5,32,247,195,173,4,219,
+ 83,71,58,154,10,184,194,173,4,58,178,1,
+ 71,219,82,230,1,32,6,5,32,247,195,173,
+ 4,219,83,58,178,1,71,219,82,230,1,32,
+ 6,5,32,247,195,173,4,219,83,254,133,194,
+ 173,4,58,179,1,24,4,58,179,1,135,61,
+ 32,253,209,225,205,137,3,205,61,3,183,251,
+ 201,209,225,243,62,10,211,82,219,82,230,128,
+ 202,164,4,62,1,55,251,201,205,144,3,205,
+ 61,3,183,251,201,209,225,62,2,55,251,201,
+ 243,62,14,211,82,62,33,211,82,251,201,33,
+ 4,0,57,94,35,86,33,2,0,57,126,35,
+ 102,111,221,229,34,193,10,237,83,195,10,221,
+ 33,171,10,205,93,6,58,185,10,50,186,10,
+ 58,184,10,135,50,184,10,205,112,6,254,3,
+ 56,16,58,185,10,135,60,230,15,50,185,10,
+ 175,50,184,10,24,23,58,183,10,205,112,6,
+ 254,3,48,13,58,185,10,203,63,50,185,10,
+ 62,255,50,183,10,58,185,10,50,186,10,58,
+ 183,10,135,50,183,10,62,32,50,187,10,50,
+ 188,10,6,255,219,82,230,16,32,3,5,32,
+ 247,205,180,4,6,40,219,82,230,16,40,3,
+ 5,32,247,62,10,211,82,219,82,230,128,194,
+ 46,5,219,82,230,16,40,214,237,95,71,58,
+ 186,10,160,230,15,40,32,71,14,10,62,10,
+ 211,82,219,82,230,128,202,119,5,205,180,4,
+ 195,156,5,219,82,230,16,202,156,5,13,32,
+ 229,16,225,42,193,10,237,91,195,10,205,252,
+ 3,48,7,61,202,156,5,195,197,5,221,225,
+ 33,0,0,201,221,33,163,10,205,93,6,58,
+ 188,10,61,50,188,10,40,19,58,186,10,246,
+ 1,50,186,10,58,183,10,246,1,50,183,10,
+ 195,46,5,221,225,33,1,0,201,221,33,167,
+ 10,205,93,6,58,184,10,246,1,50,184,10,
+ 58,186,10,135,246,1,50,186,10,58,187,10,
+ 61,50,187,10,194,46,5,221,225,33,2,0,
+ 201,221,229,33,0,0,57,17,4,0,25,126,
+ 50,154,10,230,128,50,189,10,58,189,10,183,
+ 40,6,221,33,88,2,24,4,221,33,150,0,
+ 58,154,10,183,40,49,60,40,46,61,33,190,
+ 10,119,35,119,35,54,129,175,50,158,10,221,
+ 43,221,229,225,124,181,40,42,33,190,10,17,
+ 3,0,205,206,4,17,232,3,27,123,178,32,
+ 251,58,158,10,183,40,224,58,154,10,71,62,
+ 7,128,230,127,71,58,189,10,176,50,154,10,
+ 24,166,221,225,201,183,221,52,0,192,221,52,
+ 1,192,221,52,2,192,221,52,3,192,55,201,
+ 6,8,14,0,31,48,1,12,16,250,121,201,
+ 33,2,0,57,94,35,86,35,78,35,70,35,
+ 126,35,102,105,79,120,68,103,237,176,201,33,
+ 2,0,57,126,35,102,111,62,17,237,57,48,
+ 125,237,57,40,124,237,57,41,62,0,237,57,
+ 42,62,64,237,57,43,62,0,237,57,44,33,
+ 128,2,125,237,57,46,124,237,57,47,62,145,
+ 237,57,48,211,68,58,149,10,211,66,201,33,
+ 2,0,57,126,35,102,111,62,33,237,57,48,
+ 62,64,237,57,32,62,0,237,57,33,237,57,
+ 34,125,237,57,35,124,237,57,36,62,0,237,
+ 57,37,33,128,2,125,237,57,38,124,237,57,
+ 39,62,97,237,57,48,211,67,58,149,10,211,
+ 66,201,237,56,46,95,237,56,47,87,237,56,
+ 46,111,237,56,47,103,183,237,82,32,235,33,
+ 128,2,183,237,82,201,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,201,205,106,
+ 10,221,110,6,221,102,7,126,35,110,103,195,
+ 118,10,205,106,10,33,0,0,34,205,10,34,
+ 198,10,34,200,10,33,143,15,34,207,10,237,
+ 91,207,10,42,146,10,183,237,82,17,0,255,
+ 25,34,203,10,203,124,40,6,33,0,125,34,
+ 203,10,42,207,10,229,205,37,3,195,118,10,
+ 205,106,10,229,42,150,10,35,35,35,229,205,
+ 70,7,193,124,230,3,103,221,117,254,221,116,
+ 255,237,91,152,10,35,35,35,183,237,82,32,
+ 12,17,5,0,42,152,10,205,91,10,242,203,
+ 7,42,150,10,229,205,37,3,195,118,10,237,
+ 91,152,10,42,200,10,25,34,200,10,42,205,
+ 10,25,34,205,10,237,91,203,10,33,158,253,
+ 25,237,91,205,10,205,91,10,242,245,7,33,
+ 0,0,34,205,10,62,1,50,197,10,205,5,
+ 8,33,0,0,57,249,195,118,10,205,106,10,
+ 58,197,10,183,202,118,10,237,91,198,10,42,
+ 205,10,205,91,10,242,46,8,237,91,205,10,
+ 33,98,2,25,237,91,198,10,205,91,10,250,
+ 78,8,237,91,198,10,42,205,10,183,237,82,
+ 32,7,42,200,10,125,180,40,13,237,91,205,
+ 10,42,198,10,205,91,10,242,97,8,237,91,
+ 207,10,42,205,10,25,229,205,37,3,175,50,
+ 197,10,195,118,10,205,29,3,33,0,0,57,
+ 249,195,118,10,205,106,10,58,202,10,183,40,
+ 22,205,14,7,237,91,209,10,19,19,19,205,
+ 91,10,242,139,8,33,1,0,195,118,10,33,
+ 0,0,195,118,10,205,126,10,252,255,205,108,
+ 8,125,180,194,118,10,237,91,200,10,33,0,
+ 0,205,91,10,242,118,10,237,91,207,10,42,
+ 198,10,25,221,117,254,221,116,255,35,35,35,
+ 229,205,70,7,193,124,230,3,103,35,35,35,
+ 221,117,252,221,116,253,229,221,110,254,221,102,
+ 255,229,33,212,10,229,205,124,6,193,193,221,
+ 110,252,221,102,253,34,209,10,33,211,10,54,
+ 4,33,209,10,227,205,147,6,193,62,1,50,
+ 202,10,243,221,94,252,221,86,253,42,200,10,
+ 183,237,82,34,200,10,203,124,40,17,33,0,
+ 0,34,200,10,34,205,10,34,198,10,50,197,
+ 10,24,37,221,94,252,221,86,253,42,198,10,
+ 25,34,198,10,237,91,203,10,33,158,253,25,
+ 237,91,198,10,205,91,10,242,68,9,33,0,
+ 0,34,198,10,205,5,8,33,0,0,57,249,
+ 251,195,118,10,205,106,10,33,49,13,126,183,
+ 40,16,205,42,7,237,91,47,13,19,19,19,
+ 205,91,10,242,117,9,58,142,15,198,1,50,
+ 142,15,195,118,10,33,49,13,126,254,1,40,
+ 25,254,3,202,7,10,254,5,202,21,10,33,
+ 49,13,54,0,33,47,13,229,205,207,6,195,
+ 118,10,58,141,15,183,32,72,33,51,13,126,
+ 50,149,10,205,86,7,33,50,13,126,230,127,
+ 183,32,40,58,142,15,230,127,50,142,15,183,
+ 32,5,198,1,50,142,15,33,50,13,126,111,
+ 23,159,103,203,125,58,142,15,40,5,198,128,
+ 50,142,15,33,50,13,119,33,50,13,126,111,
+ 23,159,103,229,205,237,5,193,33,211,10,54,
+ 2,33,2,0,34,209,10,58,154,10,33,212,
+ 10,119,58,148,10,33,213,10,119,33,209,10,
+ 229,205,147,6,193,24,128,42,47,13,229,33,
+ 50,13,229,205,191,4,193,24,239,33,211,10,
+ 54,6,33,3,0,34,209,10,58,154,10,33,
+ 212,10,119,58,148,10,33,213,10,119,33,214,
+ 10,54,5,33,209,10,229,205,147,6,24,200,
+ 205,106,10,33,49,13,54,0,33,47,13,229,
+ 205,207,6,33,209,10,227,205,147,6,193,205,
+ 80,9,205,145,8,24,248,124,170,250,99,10,
+ 237,82,201,124,230,128,237,82,60,201,225,253,
+ 229,221,229,221,33,0,0,221,57,233,221,249,
+ 221,225,253,225,201,233,225,253,229,221,229,221,
+ 33,0,0,221,57,94,35,86,35,235,57,249,
+ 235,233,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0
+ } ;
+
+#endif
--- /dev/null
+/*
+ * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
+ * Appletalk-IP to IP Decapsulation driver for Linux
+ *
+ * Authors:
+ * - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ * - DDP-IP Decap by: Jay Schulist <jschlst@turbolinux.com>
+ *
+ * Derived from:
+ * - Almost all code already existed in net/appletalk/ddp.c I just
+ * moved/reorginized it into a driver file. Original IP-over-DDP code
+ * was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ * - skeleton.c: A network driver outline for linux.
+ * Written 1993-94 by Donald Becker.
+ * - dummy.c: A dummy net driver. By Nick Holloway.
+ * - MacGate: A user space Daemon for Appletalk-IP Decap for
+ * Linux by Jay Schulist <jschlst@turbolinux.com>
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ */
+
+static const char *version =
+ "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/atalk.h>
+#include <linux/ip.h>
+#include <net/route.h>
+#include <linux/inet.h>
+
+#include "ipddp.h" /* Our stuff */
+
+static struct ipddp_route *ipddp_route_list = NULL;
+
+#ifdef CONFIG_IPDDP_ENCAP
+static int ipddp_mode = IPDDP_ENCAP;
+#else
+static int ipddp_mode = IPDDP_DECAP;
+#endif
+
+/* Use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
+#ifndef IPDDP_DEBUG
+#define IPDDP_DEBUG 1
+#endif
+static unsigned int ipddp_debug = IPDDP_DEBUG;
+
+/* Index to functions, as function prototypes. */
+static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *ipddp_get_stats(struct net_device *dev);
+static int ipddp_create(struct ipddp_route *new_rt);
+static int ipddp_delete(struct ipddp_route *rt);
+static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt);
+static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+
+static int ipddp_open(struct net_device *dev)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static int ipddp_close(struct net_device *dev)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+int ipddp_init(struct net_device *dev)
+{
+ static unsigned version_printed = 0;
+
+ if (ipddp_debug && version_printed++ == 0)
+ printk("%s", version);
+
+ /* Let the user now what mode we are in */
+ if(ipddp_mode == IPDDP_ENCAP)
+ printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n",
+ dev->name);
+ if(ipddp_mode == IPDDP_DECAP)
+ printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@turbolinux.com>\n",
+ dev->name);
+
+ /* Fill in the device structure with ethernet-generic values. */
+ ether_setup(dev);
+
+ /* Initalize the device structure. */
+ dev->hard_start_xmit = ipddp_xmit;
+
+ dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ if(!dev->priv)
+ return -ENOMEM;
+ memset(dev->priv,0,sizeof(struct enet_statistics));
+
+ dev->open = ipddp_open;
+ dev->stop = ipddp_close;
+ dev->get_stats = ipddp_get_stats;
+ dev->do_ioctl = ipddp_ioctl;
+
+ dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */
+ dev->mtu = 585;
+ dev->flags |= IFF_NOARP;
+
+ /*
+ * The worst case header we will need is currently a
+ * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
+ * We send over SNAP so that takes another 8 bytes.
+ */
+ dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
+
+ return 0;
+}
+
+/*
+ * Get the current statistics. This may be called with the card open or closed.
+ */
+static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
+{
+ return (struct net_device_stats *)dev->priv;
+}
+
+/*
+ * Transmit LLAP/ELAP frame using aarp_send_ddp.
+ */
+static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ u32 paddr = ((struct rtable*)skb->dst)->rt_gateway;
+ struct ddpehdr *ddp;
+ struct ipddp_route *rt;
+ struct at_addr *our_addr;
+
+ /*
+ * Find appropriate route to use, based only on IP number.
+ */
+ for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
+ {
+ if(rt->ip == paddr)
+ break;
+ }
+ if(rt == NULL)
+ return 0;
+
+ our_addr = atalk_find_dev_addr(rt->dev);
+
+ if(ipddp_mode == IPDDP_DECAP)
+ /*
+ * Pull off the excess room that should not be there.
+ * This is due to a hard-header problem. This is the
+ * quick fix for now though, till it breaks.
+ */
+ skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
+
+ /* Create the Extended DDP header */
+ ddp = (struct ddpehdr *)skb->data;
+ ddp->deh_len = skb->len;
+ ddp->deh_hops = 1;
+ ddp->deh_pad = 0;
+ ddp->deh_sum = 0;
+
+ /*
+ * For Localtalk we need aarp_send_ddp to strip the
+ * long DDP header and place a shot DDP header on it.
+ */
+ if(rt->dev->type == ARPHRD_LOCALTLK)
+ {
+ ddp->deh_dnet = 0; /* FIXME more hops?? */
+ ddp->deh_snet = 0;
+ }
+ else
+ {
+ ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */
+ ddp->deh_snet = our_addr->s_net;
+ }
+ ddp->deh_dnode = rt->at.s_node;
+ ddp->deh_snode = our_addr->s_node;
+ ddp->deh_dport = 72;
+ ddp->deh_sport = 72;
+
+ *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */
+ *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */
+
+ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
+
+ ((struct net_device_stats *) dev->priv)->tx_packets++;
+ ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
+
+ if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0)
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * Create a routing entry. We first verify that the
+ * record does not already exist. If it does we return -EEXIST
+ */
+static int ipddp_create(struct ipddp_route *new_rt)
+{
+ struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL);
+ struct ipddp_route *test;
+
+ if(rt == NULL)
+ return -ENOMEM;
+
+ rt->ip = new_rt->ip;
+ rt->at = new_rt->at;
+ rt->next = NULL;
+ rt->dev = atrtr_get_dev(&rt->at);
+ if(rt->dev == NULL)
+ return (-ENETUNREACH);
+
+ test = ipddp_find_route(rt);
+ if(test != NULL)
+ return (-EEXIST);
+
+ rt->next = ipddp_route_list;
+ ipddp_route_list = rt;
+
+ return 0;
+}
+
+/*
+ * Delete a route, we only delete a FULL match.
+ * If route does not exist we return -ENOENT.
+ */
+static int ipddp_delete(struct ipddp_route *rt)
+{
+ struct ipddp_route **r = &ipddp_route_list;
+ struct ipddp_route *tmp;
+
+ while((tmp = *r) != NULL)
+ {
+ if(tmp->ip == rt->ip
+ && tmp->at.s_net == rt->at.s_net
+ && tmp->at.s_node == rt->at.s_node)
+ {
+ *r = tmp->next;
+ kfree_s(tmp, sizeof(struct ipddp_route));
+ return 0;
+ }
+ r = &tmp->next;
+ }
+
+ return (-ENOENT);
+}
+
+/*
+ * Find a routing entry, we only return a FULL match
+ */
+static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt)
+{
+ struct ipddp_route *f;
+
+ for(f = ipddp_route_list; f != NULL; f = f->next)
+ {
+ if(f->ip == rt->ip
+ && f->at.s_net == rt->at.s_net
+ && f->at.s_node == rt->at.s_node)
+ return (f);
+ }
+
+ return (NULL);
+}
+
+static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data;
+
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch(cmd)
+ {
+ case SIOCADDIPDDPRT:
+ return (ipddp_create(rt));
+
+ case SIOCFINDIPDDPRT:
+ if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route)))
+ return -EFAULT;
+ return 0;
+
+ case SIOCDELIPDDPRT:
+ return (ipddp_delete(rt));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef MODULE /* Module specific functions for ipddp.c */
+
+static struct net_device dev_ipddp=
+{
+ "ipddp0\0 ",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NULL, ipddp_init
+};
+
+MODULE_PARM(ipddp_mode, "i");
+
+int init_module(void)
+{
+ int err;
+
+ err=dev_alloc_name(&dev_ipddp, "ipddp%d");
+ if(err < 0)
+ return err;
+
+ if(register_netdev(&dev_ipddp) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unregister_netdev(&dev_ipddp);
+ kfree(dev_ipddp.priv);
+ dev_ipddp.priv = NULL;
+}
+
+#endif /* MODULE */
--- /dev/null
+/*
+ * ipddp.h: Header for IP-over-DDP driver for Linux.
+ */
+
+#ifndef __LINUX_IPDDP_H
+#define __LINUX_IPDDP_H
+
+#ifdef __KERNEL__
+
+#define SIOCADDIPDDPRT (SIOCDEVPRIVATE)
+#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1)
+#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2)
+
+struct ipddp_route
+{
+ struct net_device *dev; /* Carrier device */
+ __u32 ip; /* IP address */
+ struct at_addr at; /* Gateway appletalk address */
+ int flags;
+ struct ipddp_route *next;
+};
+
+#define IPDDP_ENCAP 1
+#define IPDDP_DECAP 2
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_IPDDP_H */
--- /dev/null
+/*** ltpc.c -- a driver for the LocalTalk PC card.
+ *
+ * Copyright (c) 1995,1996 Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This is ALPHA code at best. It may not work for you. It may
+ * damage your equipment. It may damage your relations with other
+ * users of your network. Use it at your own risk!
+ *
+ * Based in part on:
+ * skeleton.c by Donald Becker
+ * dummy.c by Nick Holloway and Alan Cox
+ * loopback.c by Ross Biro, Fred van Kampen, Donald Becker
+ * the netatalk source code (UMICH)
+ * lots of work on the card...
+ *
+ * I do not have access to the (proprietary) SDK that goes with the card.
+ * If you do, I don't want to know about it, and you can probably write
+ * a better driver yourself anyway. This does mean that the pieces that
+ * talk to the card are guesswork on my part, so use at your own risk!
+ *
+ * This is my first try at writing Linux networking code, and is also
+ * guesswork. Again, use at your own risk! (Although on this part, I'd
+ * welcome suggestions)
+ *
+ * This is a loadable kernel module which seems to work at my site
+ * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with
+ * the kernel support from 1.3.3b2 including patches routing.patch
+ * and ddp.disappears.from.chooser. In order to run it, you will need
+ * to patch ddp.c and aarp.c in the kernel, but only a little...
+ *
+ * I'm fairly confident that while this is arguably badly written, the
+ * problems that people experience will be "higher level", that is, with
+ * complications in the netatalk code. The driver itself doesn't do
+ * anything terribly complicated -- it pretends to be an ether device
+ * as far as netatalk is concerned, strips the DDP data out of the ether
+ * frame and builds a LLAP packet to send out the card. In the other
+ * direction, it receives LLAP frames from the card and builds a fake
+ * ether packet that it then tosses up to the networking code. You can
+ * argue (correctly) that this is an ugly way to do things, but it
+ * requires a minimal amount of fooling with the code in ddp.c and aarp.c.
+ *
+ * The card will do a lot more than is used here -- I *think* it has the
+ * layers up through ATP. Even if you knew how that part works (which I
+ * don't) it would be a big job to carve up the kernel ddp code to insert
+ * things at a higher level, and probably a bad idea...
+ *
+ * There are a number of other cards that do LocalTalk on the PC. If
+ * nobody finds any insurmountable (at the netatalk level) problems
+ * here, this driver should encourage people to put some work into the
+ * other cards (some of which I gather are still commercially available)
+ * and also to put hooks for LocalTalk into the official ddp code.
+ *
+ * I welcome comments and suggestions. This is my first try at Linux
+ * networking stuff, and there are probably lots of things that I did
+ * suboptimally.
+ *
+ ***/
+
+/***
+ *
+ * $Log: ltpc.c,v $
+ * Revision 1.1.2.1 2000/03/01 05:35:07 jgarzik
+ * at and tr cleanup
+ *
+ * Revision 1.8 1997/01/28 05:44:54 bradford
+ * Clean up for non-module a little.
+ * Hacked about a bit to clean things up - Alan Cox
+ * Probably broken it from the origina 1.8
+ *
+
+ * 1998/11/09: David Huggins-Daines <dhd@debian.org>
+ * Cleaned up the initialization code to use the standard autoirq methods,
+ and to probe for things in the standard order of i/o, irq, dma. This
+ removes the "reset the reset" hack, because I couldn't figure out an
+ easy way to get the card to trigger an interrupt after it.
+ * Added support for passing configuration parameters on the kernel command
+ line and through insmod
+ * Changed the device name from "ltalk0" to "lt0", both to conform with the
+ other localtalk driver, and to clear up the inconsistency between the
+ module and the non-module versions of the driver :-)
+ * Added a bunch of comments (I was going to make some enums for the state
+ codes and the register offsets, but I'm still not sure exactly what their
+ semantics are)
+ * Don't poll anymore in interrupt-driven mode
+ * It seems to work as a module now (as of 2.1.127), but I don't think
+ I'm responsible for that...
+
+ *
+ * Revision 1.7 1996/12/12 03:42:33 bradford
+ * DMA alloc cribbed from 3c505.c.
+ *
+ * Revision 1.6 1996/12/12 03:18:58 bradford
+ * Added virt_to_bus; works in 2.1.13.
+ *
+ * Revision 1.5 1996/12/12 03:13:22 root
+ * xmitQel initialization -- think through better though.
+ *
+ * Revision 1.4 1996/06/18 14:55:55 root
+ * Change names to ltpc. Tabs. Took a shot at dma alloc,
+ * although more needs to be done eventually.
+ *
+ * Revision 1.3 1996/05/22 14:59:39 root
+ * Change dev->open, dev->close to track dummy.c in 1.99.(around 7)
+ *
+ * Revision 1.2 1996/05/22 14:58:24 root
+ * Change tabs mostly.
+ *
+ * Revision 1.1 1996/04/23 04:45:09 root
+ * Initial revision
+ *
+ * Revision 0.16 1996/03/05 15:59:56 root
+ * Change ARPHRD_LOCALTLK definition to the "real" one.
+ *
+ * Revision 0.15 1996/03/05 06:28:30 root
+ * Changes for kernel 1.3.70. Still need a few patches to kernel, but
+ * it's getting closer.
+ *
+ * Revision 0.14 1996/02/25 17:38:32 root
+ * More cleanups. Removed query to card on get_stats.
+ *
+ * Revision 0.13 1996/02/21 16:27:40 root
+ * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65.
+ * Clean up receive code a little.
+ *
+ * Revision 0.12 1996/02/19 16:34:53 root
+ * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup
+ * range. Change debug to mask: 1 for verbose, 2 for higher level stuff
+ * including packet printing, 4 for lower level (card i/o) stuff.
+ *
+ * Revision 0.11 1996/02/12 15:53:38 root
+ * Added router sends (requires new aarp.c patch)
+ *
+ * Revision 0.10 1996/02/11 00:19:35 root
+ * Change source LTALK_LOGGING debug switch to insmod ... debug=2.
+ *
+ * Revision 0.9 1996/02/10 23:59:35 root
+ * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk
+ * has a *different* definition of struct sockaddr_at than the Linux kernel
+ * does. This is an "insidious and invidious" bug...
+ * (Actually the preceding comment is false -- it's the atalk.h in the
+ * ancient atalk-0.06 that's the problem)
+ *
+ * Revision 0.8 1996/02/10 19:09:00 root
+ * Merge 1.3 changes. Tested OK under 1.3.60.
+ *
+ * Revision 0.7 1996/02/10 17:56:56 root
+ * Added debug=1 parameter on insmod for debugging prints. Tried
+ * to fix timer unload on rmmod, but I don't think that's the problem.
+ *
+ * Revision 0.6 1995/12/31 19:01:09 root
+ * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!)
+ * Clean up initial probing -- sometimes the card wakes up latched in reset.
+ *
+ * Revision 0.5 1995/12/22 06:03:44 root
+ * Added comments in front and cleaned up a bit.
+ * This version sent out to people.
+ *
+ * Revision 0.4 1995/12/18 03:46:44 root
+ * Return shortDDP to longDDP fake to 0/0. Added command structs.
+ *
+ ***/
+
+/* ltpc jumpers are:
+*
+* Interrupts -- set at most one. If none are set, the driver uses
+* polled mode. Because the card was developed in the XT era, the
+* original documentation refers to IRQ2. Since you'll be running
+* this on an AT (or later) class machine, that really means IRQ9.
+*
+* SW1 IRQ 4
+* SW2 IRQ 3
+* SW3 IRQ 9 (2 in original card documentation only applies to XT)
+*
+*
+* DMA -- choose DMA 1 or 3, and set both corresponding switches.
+*
+* SW4 DMA 3
+* SW5 DMA 1
+* SW6 DMA 3
+* SW7 DMA 1
+*
+*
+* I/O address -- choose one.
+*
+* SW8 220 / 240
+*/
+
+/* To have some stuff logged, do
+* insmod ltpc.o debug=1
+*
+* For a whole bunch of stuff, use higher numbers.
+*
+* The default is 0, i.e. no messages except for the probe results.
+*/
+
+/* insmod-tweakable variables */
+static int debug=0;
+#define DEBUG_VERBOSE 1
+#define DEBUG_UPPER 2
+#define DEBUG_LOWER 4
+
+static int io=0;
+static int irq=0;
+static int dma=0;
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/if_arp.h>
+#include <linux/if_ltalk.h>
+
+#include <linux/delay.h>
+#include <linux/timer.h>
+
+#include <linux/atalk.h>
+
+/* our stuff */
+#include "ltpc.h"
+
+/* function prototypes */
+static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
+ void *dbuf, int dbuflen);
+static int sendup_buffer (struct net_device *dev);
+
+/* Dma Memory related stuff, cribbed directly from 3c505.c */
+
+static unsigned long dma_mem_alloc(int size)
+{
+ int order = get_order(size);
+
+ return __get_dma_pages(GFP_KERNEL, order);
+}
+
+/* DMA data buffer, DMA command buffer */
+static unsigned char *ltdmabuf;
+static unsigned char *ltdmacbuf;
+
+/* private struct, holds our appletalk address */
+
+struct ltpc_private
+{
+ struct net_device_stats stats;
+ struct at_addr my_addr;
+};
+
+/* transmit queue element struct */
+
+struct xmitQel {
+ struct xmitQel *next;
+ /* command buffer */
+ unsigned char *cbuf;
+ short cbuflen;
+ /* data buffer */
+ unsigned char *dbuf;
+ short dbuflen;
+ unsigned char QWrite; /* read or write data */
+ unsigned char mailbox;
+};
+
+/* the transmit queue itself */
+
+static struct xmitQel *xmQhd=NULL,*xmQtl=NULL;
+
+static void enQ(struct xmitQel *qel)
+{
+ unsigned long flags;
+ qel->next = NULL;
+ save_flags(flags);
+ cli();
+ if (xmQtl) {
+ xmQtl->next = qel;
+ } else {
+ xmQhd = qel;
+ }
+ xmQtl = qel;
+ restore_flags(flags);
+
+ if (debug&DEBUG_LOWER)
+ printk("enqueued a 0x%02x command\n",qel->cbuf[0]);
+}
+
+static struct xmitQel *deQ(void)
+{
+ unsigned long flags;
+ int i;
+ struct xmitQel *qel=NULL;
+ save_flags(flags);
+ cli();
+ if (xmQhd) {
+ qel = xmQhd;
+ xmQhd = qel->next;
+ if(!xmQhd) xmQtl = NULL;
+ }
+ restore_flags(flags);
+
+ if ((debug&DEBUG_LOWER) && qel) {
+ int n;
+ printk("ltpc: dequeued command ");
+ n = qel->cbuflen;
+ if (n>100) n=100;
+ for(i=0;i<n;i++) printk("%02x ",qel->cbuf[i]);
+ printk("\n");
+ }
+
+ return qel;
+}
+
+/* and... the queue elements we'll be using */
+static struct xmitQel qels[16];
+
+/* and their corresponding mailboxes */
+static unsigned char mailbox[16];
+static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+static int wait_timeout(struct net_device *dev, int c)
+{
+ /* returns true if it stayed c */
+ /* this uses base+6, but it's ok */
+ int i;
+ int timeout;
+
+ /* twenty second or so total */
+
+ for(i=0;i<20000;i++) {
+ if ( c != inb_p(dev->base_addr+6) ) return 0;
+ for(timeout=loops_per_sec/1000; timeout > 0; timeout--) ;
+ }
+ return 1; /* timed out */
+}
+
+/* get the first free mailbox */
+
+static int getmbox(void)
+{
+ unsigned long flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ for(i=1;i<16;i++) if(!mboxinuse[i]) {
+ mboxinuse[i]=1;
+ restore_flags(flags);
+ return i;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+/* read a command from the card */
+static void handlefc(struct net_device *dev)
+{
+ /* called *only* from idle, non-reentrant */
+ int dma = dev->dma;
+ int base = dev->base_addr;
+ unsigned long flags;
+
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_READ);
+ set_dma_addr(dma,virt_to_bus(ltdmacbuf));
+ set_dma_count(dma,50);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ inb_p(base+3);
+ inb_p(base+2);
+
+ if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n");
+}
+
+/* read data from the card */
+static void handlefd(struct net_device *dev)
+{
+ int dma = dev->dma;
+ int base = dev->base_addr;
+ unsigned long flags;
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_READ);
+ set_dma_addr(dma,virt_to_bus(ltdmabuf));
+ set_dma_count(dma,800);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ inb_p(base+3);
+ inb_p(base+2);
+
+ if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n");
+ sendup_buffer(dev);
+}
+
+static void handlewrite(struct net_device *dev)
+{
+ /* called *only* from idle, non-reentrant */
+ /* on entry, 0xfb and ltdmabuf holds data */
+ int dma = dev->dma;
+ int base = dev->base_addr;
+ unsigned long flags;
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_WRITE);
+ set_dma_addr(dma,virt_to_bus(ltdmabuf));
+ set_dma_count(dma,800);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ inb_p(base+3);
+ inb_p(base+2);
+
+ if ( wait_timeout(dev,0xfb) ) {
+ flags=claim_dma_lock();
+ printk("timed out in handlewrite, dma res %d\n",
+ get_dma_residue(dev->dma) );
+ release_dma_lock(flags);
+ }
+}
+
+static void handleread(struct net_device *dev)
+{
+ /* on entry, 0xfb */
+ /* on exit, ltdmabuf holds data */
+ int dma = dev->dma;
+ int base = dev->base_addr;
+ unsigned long flags;
+
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_READ);
+ set_dma_addr(dma,virt_to_bus(ltdmabuf));
+ set_dma_count(dma,800);
+ enable_dma(dma);
+ release_dma_lock(flags);
+
+ inb_p(base+3);
+ inb_p(base+2);
+ if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n");
+}
+
+static void handlecommand(struct net_device *dev)
+{
+ /* on entry, 0xfa and ltdmacbuf holds command */
+ int dma = dev->dma;
+ int base = dev->base_addr;
+ unsigned long flags;
+
+ flags=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_WRITE);
+ set_dma_addr(dma,virt_to_bus(ltdmacbuf));
+ set_dma_count(dma,50);
+ enable_dma(dma);
+ release_dma_lock(flags);
+ inb_p(base+3);
+ inb_p(base+2);
+ if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n");
+}
+
+/* ready made command for getting the result from the card */
+static unsigned char rescbuf[2] = {LT_GETRESULT,0};
+static unsigned char resdbuf[2];
+
+static int QInIdle=0;
+
+/* idle expects to be called with the IRQ line high -- either because of
+ * an interrupt, or because the line is tri-stated
+ */
+
+static void idle(struct net_device *dev)
+{
+ unsigned long flags;
+ int state;
+ /* FIXME This is initialized to shut the warning up, but I need to
+ * think this through again.
+ */
+ struct xmitQel *q=0;
+ int oops;
+ int i;
+ int base = dev->base_addr;
+
+ save_flags(flags);
+ cli();
+ if(QInIdle) {
+ restore_flags(flags);
+ return;
+ }
+ QInIdle = 1;
+
+
+ restore_flags(flags);
+
+ /* this tri-states the IRQ line */
+ (void) inb_p(base+6);
+
+ oops = 100;
+
+loop:
+ if (0>oops--) {
+ printk("idle: looped too many times\n");
+ goto done;
+ }
+
+ state = inb_p(base+6);
+ if (state != inb_p(base+6)) goto loop;
+
+ switch(state) {
+ case 0xfc:
+ /* incoming command */
+ if (debug&DEBUG_LOWER) printk("idle: fc\n");
+ handlefc(dev);
+ break;
+ case 0xfd:
+ /* incoming data */
+ if(debug&DEBUG_LOWER) printk("idle: fd\n");
+ handlefd(dev);
+ break;
+ case 0xf9:
+ /* result ready */
+ if (debug&DEBUG_LOWER) printk("idle: f9\n");
+ if(!mboxinuse[0]) {
+ mboxinuse[0] = 1;
+ qels[0].cbuf = rescbuf;
+ qels[0].cbuflen = 2;
+ qels[0].dbuf = resdbuf;
+ qels[0].dbuflen = 2;
+ qels[0].QWrite = 0;
+ qels[0].mailbox = 0;
+ enQ(&qels[0]);
+ }
+ inb_p(dev->base_addr+1);
+ inb_p(dev->base_addr+0);
+ if( wait_timeout(dev,0xf9) )
+ printk("timed out idle f9\n");
+ break;
+ case 0xf8:
+ /* ?? */
+ if (xmQhd) {
+ inb_p(dev->base_addr+1);
+ inb_p(dev->base_addr+0);
+ if(wait_timeout(dev,0xf8) )
+ printk("timed out idle f8\n");
+ } else {
+ goto done;
+ }
+ break;
+ case 0xfa:
+ /* waiting for command */
+ if(debug&DEBUG_LOWER) printk("idle: fa\n");
+ if (xmQhd) {
+ q=deQ();
+ memcpy(ltdmacbuf,q->cbuf,q->cbuflen);
+ ltdmacbuf[1] = q->mailbox;
+ if (debug>1) {
+ int n;
+ printk("ltpc: sent command ");
+ n = q->cbuflen;
+ if (n>100) n=100;
+ for(i=0;i<n;i++)
+ printk("%02x ",ltdmacbuf[i]);
+ printk("\n");
+ }
+ handlecommand(dev);
+ if(0xfa==inb_p(base+6)) {
+ /* we timed out, so return */
+ goto done;
+ }
+ } else {
+ /* we don't seem to have a command */
+ if (!mboxinuse[0]) {
+ mboxinuse[0] = 1;
+ qels[0].cbuf = rescbuf;
+ qels[0].cbuflen = 2;
+ qels[0].dbuf = resdbuf;
+ qels[0].dbuflen = 2;
+ qels[0].QWrite = 0;
+ qels[0].mailbox = 0;
+ enQ(&qels[0]);
+ } else {
+ printk("trouble: response command already queued\n");
+ goto done;
+ }
+ }
+ break;
+ case 0Xfb:
+ /* data transfer ready */
+ if(debug&DEBUG_LOWER) printk("idle: fb\n");
+ if(q->QWrite) {
+ memcpy(ltdmabuf,q->dbuf,q->dbuflen);
+ handlewrite(dev);
+ } else {
+ handleread(dev);
+ /* non-zero mailbox numbers are for
+ commmands, 0 is for GETRESULT
+ requests */
+ if(q->mailbox) {
+ memcpy(q->dbuf,ltdmabuf,q->dbuflen);
+ } else {
+ /* this was a result */
+ mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1];
+ mboxinuse[0]=0;
+ }
+ }
+ break;
+ }
+ goto loop;
+
+done:
+ QInIdle=0;
+
+ /* now set the interrupts back as appropriate */
+ /* the first read takes it out of tri-state (but still high) */
+ /* the second resets it */
+ /* note that after this point, any read of base+6 will
+ trigger an interrupt */
+
+ if (dev->irq) {
+ inb_p(base+7);
+ inb_p(base+7);
+ }
+ return;
+}
+
+
+static int do_write(struct net_device *dev, void *cbuf, int cbuflen,
+ void *dbuf, int dbuflen)
+{
+
+ int i = getmbox();
+ int ret;
+
+ if(i) {
+ qels[i].cbuf = (unsigned char *) cbuf;
+ qels[i].cbuflen = cbuflen;
+ qels[i].dbuf = (unsigned char *) dbuf;
+ qels[i].dbuflen = dbuflen;
+ qels[i].QWrite = 1;
+ qels[i].mailbox = i; /* this should be initted rather */
+ enQ(&qels[i]);
+ idle(dev);
+ ret = mailbox[i];
+ mboxinuse[i]=0;
+ return ret;
+ }
+ printk("ltpc: could not allocate mbox\n");
+ return -1;
+}
+
+static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
+ void *dbuf, int dbuflen)
+{
+
+ int i = getmbox();
+ int ret;
+
+ if(i) {
+ qels[i].cbuf = (unsigned char *) cbuf;
+ qels[i].cbuflen = cbuflen;
+ qels[i].dbuf = (unsigned char *) dbuf;
+ qels[i].dbuflen = dbuflen;
+ qels[i].QWrite = 0;
+ qels[i].mailbox = i; /* this should be initted rather */
+ enQ(&qels[i]);
+ idle(dev);
+ ret = mailbox[i];
+ mboxinuse[i]=0;
+ return ret;
+ }
+ printk("ltpc: could not allocate mbox\n");
+ return -1;
+}
+
+/* end of idle handlers -- what should be seen is do_read, do_write */
+
+static struct timer_list ltpc_timer;
+
+static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *ltpc_get_stats(struct net_device *dev);
+
+static int ltpc_open(struct net_device *dev)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int ltpc_close(struct net_device *dev)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int read_30 ( struct net_device *dev)
+{
+ lt_command c;
+ c.getflags.command = LT_GETFLAGS;
+ return do_read(dev, &c, sizeof(c.getflags),&c,0);
+}
+
+static int set_30 (struct net_device *dev,int x)
+{
+ lt_command c;
+ c.setflags.command = LT_SETFLAGS;
+ c.setflags.flags = x;
+ return do_write(dev, &c, sizeof(c.setflags),&c,0);
+}
+
+/* LLAP to DDP translation */
+
+static int sendup_buffer (struct net_device *dev)
+{
+ /* on entry, command is in ltdmacbuf, data in ltdmabuf */
+ /* called from idle, non-reentrant */
+
+ int dnode, snode, llaptype, len;
+ int sklen;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
+ struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
+
+ if (ltc->command != LT_RCVLAP) {
+ printk("unknown command 0x%02x from ltpc card\n",ltc->command);
+ return(-1);
+ }
+ dnode = ltc->dnode;
+ snode = ltc->snode;
+ llaptype = ltc->laptype;
+ len = ltc->length;
+
+ sklen = len;
+ if (llaptype == 1)
+ sklen += 8; /* correct for short ddp */
+ if(sklen > 800) {
+ printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n",
+ dev->name,sklen);
+ return -1;
+ }
+
+ if ( (llaptype==0) || (llaptype>2) ) {
+ printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype);
+ return -1;
+ }
+
+
+ skb = dev_alloc_skb(3+sklen);
+ if (skb == NULL)
+ {
+ printk("%s: dropping packet due to memory squeeze.\n",
+ dev->name);
+ return -1;
+ }
+ skb->dev = dev;
+
+ if (sklen > len)
+ skb_reserve(skb,8);
+ skb_put(skb,len+3);
+ skb->protocol = htons(ETH_P_LOCALTALK);
+ /* add LLAP header */
+ skb->data[0] = dnode;
+ skb->data[1] = snode;
+ skb->data[2] = llaptype;
+ skb->mac.raw = skb->data; /* save pointer to llap header */
+ skb_pull(skb,3);
+
+ /* copy ddp(s,e)hdr + contents */
+ memcpy(skb->data,(void*)ltdmabuf,len);
+
+ skb->h.raw = skb->data;
+
+ stats->rx_packets++;
+ stats->rx_bytes+=skb->len;
+
+ /* toss it onwards */
+ netif_rx(skb);
+ return 0;
+}
+
+/* the handler for the board interrupt */
+
+static void ltpc_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
+{
+ struct net_device *dev = dev_id;
+
+ if (dev==NULL) {
+ printk("ltpc_interrupt: unknown device.\n");
+ return;
+ }
+
+ inb_p(dev->base_addr+6); /* disable further interrupts from board */
+
+ idle(dev); /* handle whatever is coming in */
+
+ /* idle re-enables interrupts from board */
+
+ return;
+}
+
+/***
+ *
+ * The ioctls that the driver responds to are:
+ *
+ * SIOCSIFADDR -- do probe using the passed node hint.
+ * SIOCGIFADDR -- return net, node.
+ *
+ * some of this stuff should be done elsewhere.
+ *
+ ***/
+
+static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr;
+ /* we'll keep the localtalk node address in dev->pa_addr */
+ struct at_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr;
+ struct lt_init c;
+ int ltflags;
+
+ if(debug&DEBUG_VERBOSE) printk("ltpc_ioctl called\n");
+
+ switch(cmd) {
+ case SIOCSIFADDR:
+
+ aa->s_net = sa->sat_addr.s_net;
+
+ /* this does the probe and returns the node addr */
+ c.command = LT_INIT;
+ c.hint = sa->sat_addr.s_node;
+
+ aa->s_node = do_read(dev,&c,sizeof(c),&c,0);
+
+ /* get all llap frames raw */
+ ltflags = read_30(dev);
+ ltflags |= LT_FLAG_ALLLAP;
+ set_30 (dev,ltflags);
+
+ dev->broadcast[0] = 0xFF;
+ dev->dev_addr[0] = aa->s_node;
+
+ dev->addr_len=1;
+
+ return 0;
+
+ case SIOCGIFADDR:
+
+ sa->sat_addr.s_net = aa->s_net;
+ sa->sat_addr.s_node = aa->s_node;
+
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void set_multicast_list(struct net_device *dev)
+{
+ /* This needs to be present to keep netatalk happy. */
+ /* Actually netatalk needs fixing! */
+}
+
+static int ltpc_hard_header (struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ if(debug&DEBUG_VERBOSE)
+ printk("ltpc_hard_header called for device %s\n",
+ dev->name);
+ return 0;
+}
+
+static int ltpc_init(struct net_device *dev)
+{
+ /* Initialize the device structure. */
+
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+ ltalk_setup(dev);
+ dev->hard_start_xmit = ltpc_xmit;
+ dev->hard_header = ltpc_hard_header;
+
+ dev->priv = kmalloc(sizeof(struct ltpc_private), GFP_KERNEL);
+ if(!dev->priv)
+ {
+ printk(KERN_INFO "%s: could not allocate statistics buffer\n", dev->name);
+ return -ENOMEM;
+ }
+
+ memset(dev->priv, 0, sizeof(struct ltpc_private));
+ dev->get_stats = ltpc_get_stats;
+
+ dev->open = ltpc_open;
+ dev->stop = ltpc_close;
+
+ /* add the ltpc-specific things */
+ dev->do_ioctl = <pc_ioctl;
+
+ dev->set_multicast_list = &set_multicast_list;
+ dev->mc_list = NULL;
+
+ return 0;
+}
+
+static int ltpc_poll_counter = 0;
+
+static void ltpc_poll(unsigned long l)
+{
+ struct net_device *dev = (struct net_device *) l;
+
+ del_timer(<pc_timer);
+
+ if(debug&DEBUG_VERBOSE) {
+ if (!ltpc_poll_counter) {
+ ltpc_poll_counter = 50;
+ printk("ltpc poll is alive\n");
+ }
+ ltpc_poll_counter--;
+ }
+
+ if (!dev)
+ return; /* we've been downed */
+
+ idle(dev);
+ ltpc_timer.expires = jiffies+5;
+
+ add_timer(<pc_timer);
+}
+
+/* DDP to LLAP translation */
+
+static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ /* in kernel 1.3.xx, on entry skb->data points to ddp header,
+ * and skb->len is the length of the ddp data + ddp header
+ */
+
+ struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
+
+ int i;
+ struct lt_sendlap cbuf;
+
+ cbuf.command = LT_SENDLAP;
+ cbuf.dnode = skb->data[0];
+ cbuf.laptype = skb->data[2];
+ skb_pull(skb,3); /* skip past LLAP header */
+ cbuf.length = skb->len; /* this is host order */
+ skb->h.raw=skb->data;
+
+ if(debug&DEBUG_UPPER) {
+ printk("command ");
+ for(i=0;i<6;i++)
+ printk("%02x ",((unsigned char *)&cbuf)[i]);
+ printk("\n");
+ }
+
+ do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len);
+
+ if(debug&DEBUG_UPPER) {
+ printk("sent %d ddp bytes\n",skb->len);
+ for(i=0;i<skb->len;i++) printk("%02x ",skb->h.raw[i]);
+ printk("\n");
+ }
+
+ stats->tx_packets++;
+ stats->tx_bytes+=skb->len;
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+static struct net_device_stats *ltpc_get_stats(struct net_device *dev)
+{
+ struct net_device_stats *stats = &((struct ltpc_private *) dev->priv)->stats;
+ return stats;
+}
+
+/* initialization stuff */
+
+int __init ltpc_probe_dma(int base)
+{
+ int dma = 0;
+ int timeout;
+ unsigned long f;
+
+ if (!request_dma(1,"ltpc")) {
+ f=claim_dma_lock();
+ disable_dma(1);
+ clear_dma_ff(1);
+ set_dma_mode(1,DMA_MODE_WRITE);
+ set_dma_addr(1,virt_to_bus(ltdmabuf));
+ set_dma_count(1,sizeof(struct lt_mem));
+ enable_dma(1);
+ release_dma_lock(f);
+ dma|=1;
+ }
+ if (!request_dma(3,"ltpc")) {
+ f=claim_dma_lock();
+ disable_dma(3);
+ clear_dma_ff(3);
+ set_dma_mode(3,DMA_MODE_WRITE);
+ set_dma_addr(3,virt_to_bus(ltdmabuf));
+ set_dma_count(3,sizeof(struct lt_mem));
+ enable_dma(3);
+ release_dma_lock(f);
+ dma|=2;
+ }
+
+ /* set up request */
+
+ /* FIXME -- do timings better! */
+
+ ltdmabuf[0] = LT_READMEM;
+ ltdmabuf[1] = 1; /* mailbox */
+ ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */
+ ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */
+ ltdmabuf[6] = 0; /* dunno if this is necessary */
+
+ inb_p(io+1);
+ inb_p(io+0);
+ timeout = jiffies+100*HZ/100;
+ while(time_before(jiffies, timeout)) {
+ if ( 0xfa == inb_p(io+6) ) break;
+ }
+
+ inb_p(io+3);
+ inb_p(io+2);
+ while(time_before(jiffies, timeout)) {
+ if ( 0xfb == inb_p(io+6) ) break;
+ }
+
+ /* release the other dma channel (if we opened both of them) */
+
+ if ( (dma&0x2) && (get_dma_residue(3)==sizeof(struct lt_mem)) ){
+ dma&=1;
+ free_dma(3);
+ }
+
+ if ( (dma&0x1) && (get_dma_residue(1)==sizeof(struct lt_mem)) ){
+ dma&=0x2;
+ free_dma(1);
+ }
+
+ /* fix up dma number */
+ dma|=1;
+
+ return dma;
+}
+
+int __init ltpc_probe(struct net_device *dev)
+{
+ int err;
+ int x=0,y=0;
+ int timeout;
+ int autoirq;
+ unsigned long flags;
+ unsigned long f;
+
+ save_flags(flags);
+
+ /* probe for the I/O port address */
+ if (io != 0x240 && !check_region(0x220,8)) {
+ x = inb_p(0x220+6);
+ if ( (x!=0xff) && (x>=0xf0) ) io = 0x220;
+ }
+
+ if (io != 0x220 && !check_region(0x240,8)) {
+ y = inb_p(0x240+6);
+ if ( (y!=0xff) && (y>=0xf0) ) io = 0x240;
+ }
+
+ if(io) {
+ /* found it, now grab it */
+ request_region(io,8,"ltpc");
+ } else {
+ /* give up in despair */
+ printk ("LocalTalk card not found; 220 = %02x, 240 = %02x.\n",
+ x,y);
+ restore_flags(flags);
+ return -1;
+ }
+
+ /* probe for the IRQ line */
+ if (irq < 2) {
+ autoirq_setup(2);
+
+ /* reset the interrupt line */
+ inb_p(io+7);
+ inb_p(io+7);
+ /* trigger an interrupt (I hope) */
+ inb_p(io+6);
+
+ autoirq = autoirq_report(1);
+
+ if (autoirq == 0) {
+ printk("ltpc: probe at %#x failed to detect IRQ line.\n",
+ io);
+ }
+ else {
+ irq = autoirq;
+ }
+ }
+
+ /* allocate a DMA buffer */
+ ltdmabuf = (unsigned char *) dma_mem_alloc(1000);
+
+ if (ltdmabuf) ltdmacbuf = <dmabuf[800];
+
+ if (!ltdmabuf) {
+ printk("ltpc: mem alloc failed\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ if(debug&DEBUG_VERBOSE) {
+ printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf);
+ }
+
+ /* reset the card */
+
+ inb_p(io+1);
+ inb_p(io+3);
+ timeout = jiffies+2*HZ/100;
+ while(time_before(jiffies, timeout)) ; /* hold it in reset for a coupla jiffies */
+ inb_p(io+0);
+ inb_p(io+2);
+ inb_p(io+7); /* clear reset */
+ inb_p(io+4);
+ inb_p(io+5);
+ inb_p(io+5); /* enable dma */
+ inb_p(io+6); /* tri-state interrupt line */
+
+ timeout = jiffies+100*HZ/100;
+
+ while(time_before(jiffies, timeout)) {
+ /* wait for the card to complete initialization */
+ }
+
+ /* now, figure out which dma channel we're using, unless it's
+ already been specified */
+ /* well, 0 is a legal DMA channel, but the LTPC card doesn't
+ use it... */
+ if (dma == 0) {
+ dma = ltpc_probe_dma(io);
+ if (!dma) { /* no dma channel */
+ printk("No DMA channel found on ltpc card.\n");
+ restore_flags(flags);
+ return -1;
+ }
+ }
+
+ /* print out friendly message */
+
+ if(irq)
+ printk("Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma);
+ else
+ printk("Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma);
+
+ /* seems more logical to do this *after* probing the card... */
+ err = ltpc_init(dev);
+ if (err) return err;
+
+ dev->base_addr = io;
+ dev->irq = irq;
+ dev->dma = dma;
+
+ /* the card will want to send a result at this point */
+ /* (I think... leaving out this part makes the kernel crash,
+ so I put it back in...) */
+
+ f=claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma,DMA_MODE_READ);
+ set_dma_addr(dma,virt_to_bus(ltdmabuf));
+ set_dma_count(dma,0x100);
+ enable_dma(dma);
+ release_dma_lock(f);
+
+ (void) inb_p(io+3);
+ (void) inb_p(io+2);
+ timeout = jiffies+100*HZ/100;
+ while(time_before(jiffies, timeout)) {
+ if( 0xf9 == inb_p(io+6)) break;
+ }
+
+ if(debug&DEBUG_VERBOSE) {
+ printk("setting up timer and irq\n");
+ }
+
+ if (irq) {
+ /* grab it and don't let go :-) */
+ (void) request_irq( irq, <pc_interrupt, 0, "ltpc", dev);
+ (void) inb_p(io+7); /* enable interrupts from board */
+ (void) inb_p(io+7); /* and reset irq line */
+ } else {
+ /* polled mode -- 20 times per second */
+ /* this is really, really slow... should it poll more often? */
+ init_timer(<pc_timer);
+ ltpc_timer.function=ltpc_poll;
+ ltpc_timer.data = (unsigned long) dev;
+
+ ltpc_timer.expires = jiffies + 5;
+ add_timer(<pc_timer);
+ restore_flags(flags);
+ }
+
+ return 0;
+}
+
+/* handles "ltpc=io,irq,dma" kernel command lines */
+static int __init ltpc_setup(char *str)
+{
+ int ints[5];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] == 0) {
+ if (str && !strncmp(str, "auto", 4)) {
+ /* do nothing :-) */
+ }
+ else {
+ /* usage message */
+ printk (KERN_ERR
+ "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n");
+ }
+ return 1;
+ } else {
+ io = ints[1];
+ if (ints[0] > 1) {
+ irq = ints[2];
+ return 1;
+ }
+ if (ints[0] > 2) {
+ dma = ints[3];
+ return 1;
+ }
+ /* ignore any other paramters */
+ }
+ return 1;
+}
+
+__setup("ltpc=", ltpc_setup);
+
+#ifdef MODULE
+
+static char dev_name[8];
+
+static struct net_device dev_ltpc = {
+ dev_name,
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NULL, ltpc_probe };
+
+MODULE_PARM(debug, "i");
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(dma, "i");
+
+int init_module(void)
+{
+ int err, result;
+
+ if(io == 0)
+ printk(KERN_NOTICE
+ "ltpc: Autoprobing is not recommended for modules\n");
+
+ /* Find a name for this unit */
+ err=dev_alloc_name(&dev_ltpc,"lt%d");
+
+ if(err<0)
+ return err;
+
+ if ((result = register_netdev(&dev_ltpc)) != 0) {
+ printk(KERN_DEBUG "could not register Localtalk-PC device\n");
+ return result;
+ } else {
+ if(debug&DEBUG_VERBOSE) printk("0 from register_netdev\n");
+ return 0;
+ }
+}
+
+void cleanup_module(void)
+{
+ long timeout;
+
+ ltpc_timer.data = 0; /* signal the poll routine that we're done */
+
+ if(debug&DEBUG_VERBOSE) printk("freeing irq\n");
+
+ if(dev_ltpc.irq) {
+ free_irq(dev_ltpc.irq,&dev_ltpc);
+ dev_ltpc.irq = 0;
+ }
+
+ if(del_timer(<pc_timer))
+ {
+ /* either the poll was never started, or a poll is in process */
+ if(debug&DEBUG_VERBOSE) printk("waiting\n");
+ /* if it's in process, wait a bit for it to finish */
+ timeout = jiffies+HZ;
+ add_timer(<pc_timer);
+ while(del_timer(<pc_timer) && time_after(timeout, jiffies))
+ {
+ add_timer(<pc_timer);
+ schedule();
+ }
+ }
+
+ if(debug&DEBUG_VERBOSE) printk("freeing dma\n");
+
+ if(dev_ltpc.dma) {
+ free_dma(dev_ltpc.dma);
+ dev_ltpc.dma = 0;
+ }
+
+ if(debug&DEBUG_VERBOSE) printk("freeing ioaddr\n");
+
+ if(dev_ltpc.base_addr) {
+ release_region(dev_ltpc.base_addr,8);
+ dev_ltpc.base_addr = 0;
+ }
+
+ if(debug&DEBUG_VERBOSE) printk("free_pages\n");
+
+ free_pages( (unsigned long) ltdmabuf, get_order(1000));
+ ltdmabuf=NULL;
+ ltdmacbuf=NULL;
+
+ if(debug&DEBUG_VERBOSE) printk("unregister_netdev\n");
+
+ unregister_netdev(&dev_ltpc);
+
+ if(debug&DEBUG_VERBOSE) printk("returning from cleanup_module\n");
+}
+#endif /* MODULE */
+
--- /dev/null
+/*** ltpc.h
+ *
+ *
+ ***/
+
+#define LT_GETRESULT 0x00
+#define LT_WRITEMEM 0x01
+#define LT_READMEM 0x02
+#define LT_GETFLAGS 0x04
+#define LT_SETFLAGS 0x05
+#define LT_INIT 0x10
+#define LT_SENDLAP 0x13
+#define LT_RCVLAP 0x14
+
+/* the flag that we care about */
+#define LT_FLAG_ALLLAP 0x04
+
+struct lt_getresult {
+ unsigned char command;
+ unsigned char mailbox;
+};
+
+struct lt_mem {
+ unsigned char command;
+ unsigned char mailbox;
+ unsigned short addr; /* host order */
+ unsigned short length; /* host order */
+};
+
+struct lt_setflags {
+ unsigned char command;
+ unsigned char mailbox;
+ unsigned char flags;
+};
+
+struct lt_getflags {
+ unsigned char command;
+ unsigned char mailbox;
+};
+
+struct lt_init {
+ unsigned char command;
+ unsigned char mailbox;
+ unsigned char hint;
+};
+
+struct lt_sendlap {
+ unsigned char command;
+ unsigned char mailbox;
+ unsigned char dnode;
+ unsigned char laptype;
+ unsigned short length; /* host order */
+};
+
+struct lt_rcvlap {
+ unsigned char command;
+ unsigned char dnode;
+ unsigned char snode;
+ unsigned char laptype;
+ unsigned short length; /* host order */
+};
+
+union lt_command {
+ struct lt_getresult getresult;
+ struct lt_mem mem;
+ struct lt_setflags setflags;
+ struct lt_getflags getflags;
+ struct lt_init init;
+ struct lt_sendlap sendlap;
+ struct lt_rcvlap rcvlap;
+};
+typedef union lt_command lt_command;
+
+++ /dev/null
-/* cops.c: LocalTalk driver for Linux.
- *
- * Authors:
- * - Jay Schulist <jschlst@turbolinux.com>
- *
- * With more than a little help from;
- * - Alan Cox <Alan.Cox@linux.org>
- *
- * Derived from:
- * - skeleton.c: A network driver outline for linux.
- * Written 1993-94 by Donald Becker.
- * - ltpc.c: A driver for the LocalTalk PC card.
- * Written by Bradford W. Johnson.
- *
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.
- *
- * This software may be used and distributed according to the terms
- * of the GNU Public License, incorporated herein by reference.
- *
- * Changes:
- * 19970608 Alan Cox Allowed dual card type support
- * Can set board type in insmod
- * Hooks for cops_setup routine
- * (not yet implemented).
- * 19971101 Jay Schulist Fixes for multiple lt* devices.
- * 19980607 Steven Hirsch Fixed the badly broken support
- * for Tangent type cards. Only
- * tested on Daystar LT200. Some
- * cleanup of formatting and program
- * logic. Added emacs 'local-vars'
- * setup for Jay's brace style.
- * 20000211 Alan Cox Cleaned up for softnet
- */
-
-static const char *version =
-"cops.c:v0.04 6/7/98 Jay Schulist <jschlst@turbolinux.com>\n";
-/*
- * Sources:
- * COPS Localtalk SDK. This provides almost all of the information
- * needed.
- */
-
-/*
- * insmod/modprobe configurable stuff.
- * - IO Port, choose one your card supports or 0 if you dare.
- * - IRQ, also choose one your card supports or nothing and let
- * the driver figure it out.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/version.h>
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#include <linux/if_arp.h>
-#include <linux/if_ltalk.h> /* For ltalk_setup() */
-#include <linux/delay.h> /* For udelay() */
-#include <linux/atalk.h>
-
-#include "cops.h" /* Our Stuff */
-#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */
-#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */
-
-/*
- * The name of the card. Is used for messages and in the requests for
- * io regions, irqs and dma channels
- */
-
-static const char *cardname = "cops";
-
-#ifdef CONFIG_COPS_DAYNA
-static int board_type = DAYNA; /* Module exported */
-#else
-static int board_type = TANGENT;
-#endif
-
-#ifdef MODULE
-static int io = 0x240; /* Default IO for Dayna */
-static int irq = 5; /* Default IRQ */
-#else
-static int io = 0; /* Default IO for Dayna */
-static int irq = 0; /* Default IRQ */
-#endif
-
-/*
- * COPS Autoprobe information.
- * Right now if port address is right but IRQ is not 5 this will
- * return a 5 no matter what since we will still get a status response.
- * Need one more additional check to narrow down after we have gotten
- * the ioaddr. But since only other possible IRQs is 3 and 4 so no real
- * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with
- * this driver.
- *
- * This driver has 2 modes and they are: Dayna mode and Tangent mode.
- * Each mode corresponds with the type of card. It has been found
- * that there are 2 main types of cards and all other cards are
- * the same and just have different names or only have minor differences
- * such as more IO ports. As this driver is tested it will
- * become more clear on exactly what cards are supported. The driver
- * defaults to using Dayna mode. To change the drivers mode, simply
- * select Dayna or Tangent mode when configuring the kernel.
- *
- * This driver should support:
- * TANGENT driver mode:
- * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200,
- * COPS LT-1
- * DAYNA driver mode:
- * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
- * Farallon PhoneNET PC III, Farallon PhoneNET PC II
- * Other cards possibly supported mode unkown though:
- * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
- *
- * Cards NOT supported by this driver but supported by the ltpc.c
- * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
- * Farallon PhoneNET PC
- * Original Apple LocalTalk PC card
- *
- * N.B.
- *
- * The Daystar Digital LT200 boards do not support interrupt-driven
- * IO. You must specify 'irq=0xff' as a module parameter to invoke
- * polled mode. I also believe that the port probing logic is quite
- * dangerous at best and certainly hopeless for a polled card. Best to
- * specify both. - Steve H.
- *
- */
-
-/*
- * Zero terminated list of IO ports to probe.
- */
-
-static unsigned int cops_portlist[] = {
- 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
- 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360,
- 0
-};
-
-/*
- * Zero terminated list of IRQ ports to probe.
- */
-
-static int cops_irqlist[] = {
- 5, 4, 3, 0
-};
-
-static struct timer_list cops_timer;
-
-/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
-#ifndef COPS_DEBUG
-#define COPS_DEBUG 1
-#endif
-static unsigned int cops_debug = COPS_DEBUG;
-
-/* The number of low I/O ports used by the card. */
-#define COPS_IO_EXTENT 8
-
-/* Information that needs to be kept for each board. */
-
-struct cops_local
-{
- struct net_device_stats stats;
- int board; /* Holds what board type is. */
- int nodeid; /* Set to 1 once have nodeid. */
- unsigned char node_acquire; /* Node ID when acquired. */
- struct at_addr node_addr; /* Full node addres */
-};
-
-/* Index to functions, as function prototypes. */
-extern int cops_probe (struct net_device *dev);
-static int cops_probe1 (struct net_device *dev, int ioaddr);
-static int cops_irq (int ioaddr, int board);
-
-static int cops_open (struct net_device *dev);
-static int cops_jumpstart (struct net_device *dev);
-static void cops_reset (struct net_device *dev, int sleep);
-static void cops_load (struct net_device *dev);
-static int cops_nodeid (struct net_device *dev, int nodeid);
-
-static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs);
-static void cops_poll (unsigned long ltdev);
-static void cops_timeout(struct net_device *dev);
-static void cops_rx (struct net_device *dev);
-static int cops_send_packet (struct sk_buff *skb, struct net_device *dev);
-static void set_multicast_list (struct net_device *dev);
-static int cops_hard_header (struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr,
- unsigned len);
-
-static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
-static int cops_close (struct net_device *dev);
-static struct net_device_stats *cops_get_stats (struct net_device *dev);
-
-
-/*
- * Check for a network adaptor of this type, and return '0' iff one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr in [1..0x1ff], always return failure.
- * otherwise go with what we pass in.
- */
-int __init cops_probe(struct net_device *dev)
-{
- int i;
- int base_addr = dev ? dev->base_addr : 0;
-
- if(base_addr == 0 && io)
- base_addr=io;
-
- if(base_addr > 0x1ff) /* Check a single specified location. */
- return cops_probe1(dev, base_addr);
- else if(base_addr != 0) /* Don't probe at all. */
- return -ENXIO;
-
- /* FIXME Does this really work for cards which generate irq?
- * It's definitely N.G. for polled Tangent. sh
- * Dayna cards don't autoprobe well at all, but if your card is
- * at IRQ 5 & IO 0x240 we find it every time. ;) JS
- */
- for(i=0; cops_portlist[i]; i++) {
- int ioaddr = cops_portlist[i];
- if(check_region(ioaddr, COPS_IO_EXTENT))
- continue;
- if(cops_probe1(dev, ioaddr) == 0)
- return 0;
- }
-
- return -ENODEV;
-}
-
-/*
- * This is the real probe routine. Linux has a history of friendly device
- * probes on the ISA bus. A good device probes avoids doing writes, and
- * verifies that the correct device exists and functions.
- */
-static int __init cops_probe1(struct net_device *dev, int ioaddr)
-{
- struct cops_local *lp;
- static unsigned version_printed = 0;
-
- int board = board_type;
-
- if(cops_debug && version_printed++ == 0)
- printk("%s", version);
-
- /*
- * Since this board has jumpered interrupts, allocate the interrupt
- * vector now. There is no point in waiting since no other device
- * can use the interrupt, and this marks the irq as busy. Jumpered
- * interrupts are typically not reported by the boards, and we must
- * used AutoIRQ to find them.
- */
- switch (dev->irq)
- {
- case 0:
- /* COPS AutoIRQ routine */
- dev->irq = cops_irq(ioaddr, board);
- if(!dev->irq)
- return -EINVAL; /* No IRQ found on this port */
- break;
-
- case 1:
- return -EINVAL;
- break;
-
- /* Fixup for users that don't know that IRQ 2 is really
- * IRQ 9, or don't know which one to set.
- */
- case 2:
- dev->irq = 9;
- break;
-
- /* Polled operation requested. Although irq of zero passed as
- * a parameter tells the init routines to probe, we'll
- * overload it to denote polled operation at runtime.
- */
- case 0xff:
- dev->irq = 0;
- break;
-
- default:
- break;
- }
-
- /* Reserve any actual interrupt. */
- if(dev->irq && request_irq(dev->irq, &cops_interrupt, 0, cardname, dev))
- return -EINVAL;
-
- /* Grab the region so no one else tries to probe our ioports. */
- request_region(ioaddr, COPS_IO_EXTENT, cardname);
- dev->base_addr = ioaddr;
-
- /* Initialize the private device structure. */
- dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL);
- if(dev->priv == NULL)
- return -ENOMEM;
-
- lp = (struct cops_local *)dev->priv;
- memset(lp, 0, sizeof(struct cops_local));
-
- /* Copy local board variable to lp struct. */
- lp->board = board;
-
- /* Fill in the fields of the device structure with LocalTalk values. */
- ltalk_setup(dev);
-
- dev->hard_start_xmit = cops_send_packet;
- dev->tx_timeout = cops_timeout;
- dev->watchdog_timeo = HZ * 2;
- dev->hard_header = cops_hard_header;
- dev->get_stats = cops_get_stats;
- dev->open = cops_open;
- dev->stop = cops_close;
- dev->do_ioctl = cops_ioctl;
- dev->set_multicast_list = set_multicast_list;
- dev->mc_list = NULL;
-
- /* Tell the user where the card is and what mode we're in. */
- if(board==DAYNA)
- printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n",
- dev->name, cardname, ioaddr, dev->irq);
- if(board==TANGENT) {
- if(dev->irq)
- printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n",
- dev->name, cardname, ioaddr, dev->irq);
- else
- printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n",
- dev->name, cardname, ioaddr);
-
- }
- return 0;
-}
-
-static int __init cops_irq (int ioaddr, int board)
-{ /*
- * This does not use the IRQ to determine where the IRQ is. We just
- * assume that when we get a correct status response that it's the IRQ.
- * This really just verifies the IO port but since we only have access
- * to such a small number of IRQs (5, 4, 3) this is not bad.
- * This will probably not work for more than one card.
- */
- int irqaddr=0;
- int i, x, status;
-
- if(board==DAYNA)
- {
- outb(0, ioaddr+DAYNA_RESET);
- inb(ioaddr+DAYNA_RESET);
- udelay(333333);
- }
- if(board==TANGENT)
- {
- inb(ioaddr);
- outb(0, ioaddr);
- outb(0, ioaddr+TANG_RESET);
- }
-
- for(i=0; cops_irqlist[i] !=0; i++)
- {
- irqaddr = cops_irqlist[i];
- for(x = 0xFFFF; x>0; x --) /* wait for response */
- {
- if(board==DAYNA)
- {
- status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
- if(status == 1)
- return irqaddr;
- }
- if(board==TANGENT)
- {
- if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0)
- return irqaddr;
- }
- }
- }
- return 0; /* no IRQ found */
-}
-
-/*
- * Open/initialize the board. This is called (in the current kernel)
- * sometime after booting when the 'ifconfig' program is run.
- */
-static int cops_open(struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
-
- if(dev->irq==0)
- {
- /*
- * I don't know if the Dayna-style boards support polled
- * operation. For now, only allow it for Tangent.
- */
- if(lp->board==TANGENT) /* Poll 20 times per second */
- {
- init_timer(&cops_timer);
- cops_timer.function = cops_poll;
- cops_timer.data = (unsigned long)dev;
- cops_timer.expires = jiffies + 5;
- add_timer(&cops_timer);
- }
- else
- {
- printk(KERN_WARNING "%s: No irq line set\n", dev->name);
- return -EAGAIN;
- }
- }
-
- cops_jumpstart(dev); /* Start the card up. */
-
- netif_start_queue(dev);
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
-
- return 0;
-}
-
-/*
- * This allows for a dynamic start/restart of the entire card.
- */
-static int cops_jumpstart(struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
-
- /*
- * Once the card has the firmware loaded and has acquired
- * the nodeid, if it is reset it will lose it all.
- */
- cops_reset(dev,1); /* Need to reset card before load firmware. */
- cops_load(dev); /* Load the firmware. */
-
- /*
- * If atalkd already gave us a nodeid we will use that
- * one again, else we wait for atalkd to give us a nodeid
- * in cops_ioctl. This may cause a problem if someone steals
- * our nodeid while we are resetting.
- */
- if(lp->nodeid == 1)
- cops_nodeid(dev,lp->node_acquire);
-
- return 0;
-}
-
-static void tangent_wait_reset(int ioaddr)
-{
- int timeout=0;
-
- while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
- mdelay(1); /* Wait 1 second */
-}
-
-/*
- * Reset the LocalTalk board.
- */
-static void cops_reset(struct net_device *dev, int sleep)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
- int ioaddr=dev->base_addr;
-
- if(lp->board==TANGENT)
- {
- inb(ioaddr); /* Clear request latch. */
- outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */
- outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */
-
- tangent_wait_reset(ioaddr);
- outb(0, ioaddr+TANG_CLEAR_INT);
- }
- if(lp->board==DAYNA)
- {
- outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */
- inb(ioaddr+DAYNA_RESET); /* Clear the reset */
- if(sleep)
- {
- long snap=jiffies;
-
- /* Let card finish initializing, about 1/3 second */
- while(jiffies-snap<HZ/3)
- schedule();
- }
- else
- udelay(333333);
- }
- netif_wake_queue(dev);
- return;
-}
-
-static void cops_load (struct net_device *dev)
-{
- struct ifreq ifr;
- struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data;
- struct cops_local *lp=(struct cops_local *)dev->priv;
- int ioaddr=dev->base_addr;
- int length, i = 0;
-
- strcpy(ifr.ifr_name,"lt0");
-
- /* Get card's firmware code and do some checks on it. */
-#ifdef CONFIG_COPS_DAYNA
- if(lp->board==DAYNA)
- {
- ltf->length=sizeof(ffdrv_code);
- ltf->data=ffdrv_code;
- }
- else
-#endif
-#ifdef CONFIG_COPS_TANGENT
- if(lp->board==TANGENT)
- {
- ltf->length=sizeof(ltdrv_code);
- ltf->data=ltdrv_code;
- }
- else
-#endif
- {
- printk(KERN_INFO "%s; unsupported board type.\n", dev->name);
- return;
- }
-
- /* Check to make sure firmware is correct length. */
- if(lp->board==DAYNA && ltf->length!=5983)
- {
- printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name);
- return;
- }
- if(lp->board==TANGENT && ltf->length!=2501)
- {
- printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name);
- return;
- }
-
- if(lp->board==DAYNA)
- {
- /*
- * We must wait for a status response
- * with the DAYNA board.
- */
- while(++i<65536)
- {
- if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1)
- break;
- }
-
- if(i==65536)
- return;
- }
-
- /*
- * Upload the firmware and kick. Byte-by-byte works nicely here.
- */
- i=0;
- length = ltf->length;
- while(length--)
- {
- outb(ltf->data[i], ioaddr);
- i++;
- }
-
- if(cops_debug > 1)
- printk("%s: Uploaded firmware - %d bytes of %d bytes.\n",
- dev->name, i, ltf->length);
-
- if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */
- outb(1, ioaddr+DAYNA_INT_CARD);
- else /* Tell Tang to run the firmware code. */
- inb(ioaddr);
-
- if(lp->board==TANGENT)
- {
- tangent_wait_reset(ioaddr);
- inb(ioaddr); /* Clear initial ready signal. */
- }
-
- return;
-}
-
-/*
- * Get the LocalTalk Nodeid from the card. We can suggest
- * any nodeid 1-254. The card will try and get that exact
- * address else we can specify 0 as the nodeid and the card
- * will autoprobe for a nodeid.
- */
-static int cops_nodeid (struct net_device *dev, int nodeid)
-{
- struct cops_local *lp = (struct cops_local *) dev->priv;
- int ioaddr = dev->base_addr;
-
- if(lp->board == DAYNA)
- {
- /* Empty any pending adapter responses. */
- while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
- {
- outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */
- if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
- cops_rx(dev); /* Kick any packets waiting. */
- schedule();
- }
-
- outb(2, ioaddr); /* Output command packet length as 2. */
- outb(0, ioaddr);
- outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
- outb(nodeid, ioaddr); /* Suggest node address. */
- }
-
- if(lp->board == TANGENT)
- {
- /* Empty any pending adapter responses. */
- while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
- {
- outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */
- cops_rx(dev); /* Kick out packets waiting. */
- schedule();
- }
-
- /* Not sure what Tangent does if nodeid picked is used. */
- if(nodeid == 0) /* Seed. */
- nodeid = jiffies&0xFF; /* Get a random try */
- outb(2, ioaddr); /* Command length LSB */
- outb(0, ioaddr); /* Command length MSB */
- outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */
- outb(nodeid, ioaddr); /* LAP address hint. */
- outb(0xFF, ioaddr); /* Int. level to use */
- }
-
- lp->node_acquire=0; /* Set nodeid holder to 0. */
- while(lp->node_acquire==0) /* Get *True* nodeid finally. */
- {
- outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
-
- if(lp->board == DAYNA)
- {
- if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
- cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
- }
- if(lp->board == TANGENT)
- {
- if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
- cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
- }
- schedule();
- }
-
- if(cops_debug > 1)
- printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n",
- dev->name, lp->node_acquire);
-
- lp->nodeid=1; /* Set got nodeid to 1. */
-
- return 0;
-}
-
-/*
- * Poll the Tangent type cards to see if we have work.
- */
-
-static void cops_poll(unsigned long ltdev)
-{
- int ioaddr, status;
- int boguscount = 0;
-
- struct net_device *dev = (struct net_device *)ltdev;
-
- del_timer(&cops_timer);
-
- if(dev == NULL)
- return; /* We've been downed */
-
- ioaddr = dev->base_addr;
- do {
- status=inb(ioaddr+TANG_CARD_STATUS);
- if(status & TANG_RX_READY)
- cops_rx(dev);
- if(status & TANG_TX_READY)
- netif_wake_queue(dev);
- status = inb(ioaddr+TANG_CARD_STATUS);
- } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
-
- cops_timer.expires = jiffies+5;
- add_timer(&cops_timer);
-
- return;
-}
-
-/*
- * The typical workload of the driver:
- * Handle the network interface interrupts.
- */
-static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
- struct net_device *dev = dev_id;
- struct cops_local *lp;
- int ioaddr, status;
- int boguscount = 0;
-
- ioaddr = dev->base_addr;
- lp = (struct cops_local *)dev->priv;
-
- if(lp->board==DAYNA)
- {
- do {
- outb(0, ioaddr + COPS_CLEAR_INT);
- status=inb(ioaddr+DAYNA_CARD_STATUS);
- if((status&0x03)==DAYNA_RX_REQUEST)
- cops_rx(dev);
- netif_wake_queue(dev);
- } while(++boguscount < 20);
- }
- else
- {
- do {
- status=inb(ioaddr+TANG_CARD_STATUS);
- if(status & TANG_RX_READY)
- cops_rx(dev);
- if(status & TANG_TX_READY)
- netif_wake_queue(dev);
- status=inb(ioaddr+TANG_CARD_STATUS);
- } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
- }
-
- return;
-}
-
-/*
- * We have a good packet(s), get it/them out of the buffers.
- */
-static void cops_rx(struct net_device *dev)
-{
- int pkt_len = 0;
- int rsp_type = 0;
- struct sk_buff *skb;
- struct cops_local *lp = (struct cops_local *)dev->priv;
- int ioaddr = dev->base_addr;
- int boguscount = 0;
- unsigned long flags;
-
-
- save_flags(flags);
- cli(); /* Disable interrupts. */
-
- if(lp->board==DAYNA)
- {
- outb(0, ioaddr); /* Send out Zero length. */
- outb(0, ioaddr);
- outb(DATA_READ, ioaddr); /* Send read command out. */
-
- /* Wait for DMA to turn around. */
- while(++boguscount<1000000)
- {
- if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY)
- break;
- }
-
- if(boguscount==1000000)
- {
- printk(KERN_WARNING "%s: DMA timed out.\n",dev->name);
- return;
- }
- }
-
- /* Get response length. */
- if(lp->board==DAYNA)
- pkt_len = inb(ioaddr) & 0xFF;
- else
- pkt_len = inb(ioaddr) & 0x00FF;
- pkt_len |= (inb(ioaddr) << 8);
- /* Input IO code. */
- rsp_type=inb(ioaddr);
-
- /* Malloc up new buffer. */
- skb = dev_alloc_skb(pkt_len);
- if(skb == NULL)
- {
- printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n",
- dev->name);
- lp->stats.rx_dropped++;
- while(pkt_len--) /* Discard packet */
- inb(ioaddr);
- return;
- }
- skb->dev = dev;
- skb_put(skb, pkt_len);
- skb->protocol = htons(ETH_P_LOCALTALK);
-
- insb(ioaddr, skb->data, pkt_len); /* Eat the Data */
-
- if(lp->board==DAYNA)
- outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */
-
- restore_flags(flags); /* Restore interrupts. */
-
- /* Check for bad response length */
- if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
- {
- printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n",
- dev->name, pkt_len);
- lp->stats.tx_errors++;
- kfree_skb(skb);
- return;
- }
-
- /* Set nodeid and then get out. */
- if(rsp_type == LAP_INIT_RSP)
- { /* Nodeid taken from received packet. */
- lp->node_acquire = skb->data[0];
- kfree_skb(skb);
- return;
- }
-
- /* One last check to make sure we have a good packet. */
- if(rsp_type != LAP_RESPONSE)
- {
- printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type);
- lp->stats.tx_errors++;
- kfree_skb(skb);
- return;
- }
-
- skb->mac.raw = skb->data; /* Point to entire packet. */
- skb_pull(skb,3);
- skb->h.raw = skb->data; /* Point to data (Skip header). */
-
- /* Update the counters. */
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += skb->len;
-
- /* Send packet to a higher place. */
- netif_rx(skb);
-
- return;
-}
-
-static void cops_timeout(struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
- int ioaddr = dev->base_addr;
-
- lp->stats.tx_errors++;
- if(lp->board==TANGENT)
- {
- if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
- printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name);
- }
- printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name);
- cops_jumpstart(dev); /* Restart the card. */
- dev->trans_start = jiffies;
- netif_wake_queue(dev);
-}
-
-
-/*
- * Make the card transmit a LocalTalk packet.
- */
-
-static int cops_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
- int ioaddr = dev->base_addr;
- unsigned long flags;
-
- /*
- * Block a timer-based transmit from overlapping.
- */
-
- netif_stop_queue(dev);
-
- save_flags(flags);
- cli(); /* Disable interrupts. */
- if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */
- while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
- if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
- while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0);
-
- /* Output IO length. */
- outb(skb->len, ioaddr);
- if(lp->board == DAYNA)
- outb(skb->len >> 8, ioaddr);
- else
- outb((skb->len >> 8)&0x0FF, ioaddr);
-
- /* Output IO code. */
- outb(LAP_WRITE, ioaddr);
-
- if(lp->board == DAYNA) /* Check the transmit buffer again. */
- while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
-
- outsb(ioaddr, skb->data, skb->len); /* Send out the data. */
-
- if(lp->board==DAYNA) /* Dayna requires you kick the card */
- outb(1, ioaddr+DAYNA_INT_CARD);
-
- restore_flags(flags); /* Restore interrupts. */
-
- /* Done sending packet, update counters and cleanup. */
- lp->stats.tx_packets++;
- lp->stats.tx_bytes += skb->len;
- dev->trans_start = jiffies;
- dev_kfree_skb (skb);
- return 0;
-}
-
-/*
- * Dummy function to keep the Appletalk layer happy.
- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- if(cops_debug >= 3)
- printk("%s: set_multicast_list executed\n", dev->name);
-}
-
-/*
- * Another Dummy function to keep the Appletalk layer happy.
- */
-
-static int cops_hard_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr,
- unsigned len)
-{
- if(cops_debug >= 3)
- printk("%s: cops_hard_header executed. Wow!\n", dev->name);
- return 0;
-}
-
-/*
- * System ioctls for the COPS LocalTalk card.
- */
-
-static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
- struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr;
- struct at_addr *aa=(struct at_addr *)&lp->node_addr;
-
- switch(cmd)
- {
- case SIOCSIFADDR:
- /* Get and set the nodeid and network # atalkd wants. */
- cops_nodeid(dev, sa->sat_addr.s_node);
- aa->s_net = sa->sat_addr.s_net;
- aa->s_node = lp->node_acquire;
-
- /* Set broardcast address. */
- dev->broadcast[0] = 0xFF;
-
- /* Set hardware address. */
- dev->dev_addr[0] = aa->s_node;
- dev->addr_len = 1;
- return 0;
-
- case SIOCGIFADDR:
- sa->sat_addr.s_net = aa->s_net;
- sa->sat_addr.s_node = aa->s_node;
- return 0;
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/*
- * The inverse routine to cops_open().
- */
-
-static int cops_close(struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
-
- /* If we were running polled, yank the timer.
- */
- if(lp->board==TANGENT && dev->irq==0)
- del_timer(&cops_timer);
-
- netif_stop_queue(dev);
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
-
- return 0;
-}
-
-/*
- * Get the current statistics.
- * This may be called with the card open or closed.
- */
-static struct net_device_stats *cops_get_stats(struct net_device *dev)
-{
- struct cops_local *lp = (struct cops_local *)dev->priv;
- return &lp->stats;
-}
-
-#ifdef MODULE
-static char lt_name[16];
-
-static struct net_device cops0_dev =
-{
- lt_name, /* device name */
- 0, 0, 0, 0,
- 0x0, 0, /* I/O address, IRQ */
- 0, 0, 0, NULL, cops_probe
-};
-
-
-MODULE_PARM(io, "i");
-MODULE_PARM(irq, "i");
-MODULE_PARM(board_type, "i");
-
-int init_module(void)
-{
- int result, err;
-
- if(io == 0)
- printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
- cardname);
-
- /* Copy the parameters from insmod into the device structure. */
- cops0_dev.base_addr = io;
- cops0_dev.irq = irq;
-
- err=dev_alloc_name(&cops0_dev, "lt%d");
- if(err < 0)
- return err;
-
- if((result = register_netdev(&cops0_dev)) != 0)
- return result;
-
- return 0;
-}
-
-void cleanup_module(void)
-{
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- unregister_netdev(&cops0_dev);
- if(cops0_dev.priv)
- kfree_s(cops0_dev.priv, sizeof(struct cops_local));
- if(cops0_dev.irq)
- free_irq(cops0_dev.irq, &cops0_dev);
- release_region(cops0_dev.base_addr, COPS_IO_EXTENT);
-}
-#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c cops.c"
- * c-basic-offset: 4
- * c-file-offsets: ((substatement-open . 0))
- * End:
- */
+++ /dev/null
-/* cops.h: LocalTalk driver for Linux.
- *
- * Authors:
- * - Jay Schulist <jschlst@turbolinux.com>
- */
-
-#ifndef __LINUX_COPSLTALK_H
-#define __LINUX_COPSLTALK_H
-
-#ifdef __KERNEL__
-
-/* Max LLAP size we will accept. */
-#define MAX_LLAP_SIZE 603
-
-/* Tangent */
-#define TANG_CARD_STATUS 1
-#define TANG_CLEAR_INT 1
-#define TANG_RESET 3
-
-#define TANG_TX_READY 1
-#define TANG_RX_READY 2
-
-/* Dayna */
-#define DAYNA_CMD_DATA 0
-#define DAYNA_CLEAR_INT 1
-#define DAYNA_CARD_STATUS 2
-#define DAYNA_INT_CARD 3
-#define DAYNA_RESET 4
-
-#define DAYNA_RX_READY 0
-#define DAYNA_TX_READY 1
-#define DAYNA_RX_REQUEST 3
-
-/* Same on both card types */
-#define COPS_CLEAR_INT 1
-
-/* LAP response codes received from the cards. */
-#define LAP_INIT 1 /* Init cmd */
-#define LAP_INIT_RSP 2 /* Init response */
-#define LAP_WRITE 3 /* Write cmd */
-#define DATA_READ 4 /* Data read */
-#define LAP_RESPONSE 4 /* Received ALAP frame response */
-#define LAP_GETSTAT 5 /* Get LAP and HW status */
-#define LAP_RSPSTAT 6 /* Status response */
-
-#endif
-
-/*
- * Structure to hold the firmware information.
- */
-struct ltfirmware
-{
- unsigned int length;
- unsigned char * data;
-};
-
-#define DAYNA 1
-#define TANGENT 2
-
-#endif
+++ /dev/null
-
-/*
- * The firmware this driver downloads into the Localtalk card is a
- * separate program and is not GPL'd source code, even though the Linux
- * side driver and the routine that loads this data into the card are.
- *
- * It is taken from the COPS SDK and is under the following license
- *
- * This material is licensed to you strictly for use in conjunction with
- * the use of COPS LocalTalk adapters.
- * There is no charge for this SDK. And no waranty express or implied
- * about its fitness for any purpose. However, we will cheerefully
- * refund every penny you paid for this SDK...
- * Regards,
- *
- * Thomas F. Divine
- * Chief Scientist
- */
-
-
-/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux.
- *
- * Authors:
- * - Jay Schulist <jschlst@turbolinux.com>
- */
-
-#include <linux/config.h>
-
-#ifdef CONFIG_COPS_DAYNA
-
-unsigned char ffdrv_code[] = {
- 58,3,0,50,228,149,33,255,255,34,226,149,
- 249,17,40,152,33,202,154,183,237,82,77,68,
- 11,107,98,19,54,0,237,176,175,50,80,0,
- 62,128,237,71,62,32,237,57,51,62,12,237,
- 57,50,237,57,54,62,6,237,57,52,62,12,
- 237,57,49,33,107,137,34,32,128,33,83,130,
- 34,40,128,33,86,130,34,42,128,33,112,130,
- 34,36,128,33,211,130,34,38,128,62,0,237,
- 57,16,33,63,148,34,34,128,237,94,205,15,
- 130,251,205,168,145,24,141,67,111,112,121,114,
- 105,103,104,116,32,40,67,41,32,49,57,56,
- 56,32,45,32,68,97,121,110,97,32,67,111,
- 109,109,117,110,105,99,97,116,105,111,110,115,
- 32,32,32,65,108,108,32,114,105,103,104,116,
- 115,32,114,101,115,101,114,118,101,100,46,32,
- 32,40,68,40,68,7,16,8,34,7,22,6,
- 16,5,12,4,8,3,6,140,0,16,39,128,
- 0,4,96,10,224,6,0,7,126,2,64,11,
- 118,12,6,13,0,14,193,15,0,5,96,3,
- 192,1,64,9,8,62,9,211,66,62,192,211,
- 66,62,100,61,32,253,6,28,33,205,129,14,
- 66,237,163,194,253,129,6,28,33,205,129,14,
- 64,237,163,194,9,130,201,62,47,50,71,152,
- 62,47,211,68,58,203,129,237,57,20,58,204,
- 129,237,57,21,33,77,152,54,132,205,233,129,
- 58,228,149,254,209,40,6,56,4,62,0,24,
- 2,219,96,33,233,149,119,230,62,33,232,149,
- 119,213,33,8,152,17,7,0,25,119,19,25,
- 119,209,201,251,237,77,245,197,213,229,221,229,
- 205,233,129,62,1,50,106,137,205,158,139,221,
- 225,225,209,193,241,251,237,77,245,197,213,219,
- 72,237,56,16,230,46,237,57,16,237,56,12,
- 58,72,152,183,32,26,6,20,17,128,2,237,
- 56,46,187,32,35,237,56,47,186,32,29,219,
- 72,230,1,32,3,5,32,232,175,50,72,152,
- 229,221,229,62,1,50,106,137,205,158,139,221,
- 225,225,24,25,62,1,50,72,152,58,201,129,
- 237,57,12,58,202,129,237,57,13,237,56,16,
- 246,17,237,57,16,209,193,241,251,237,77,245,
- 197,229,213,221,229,237,56,16,230,17,237,57,
- 16,237,56,20,58,34,152,246,16,246,8,211,
- 68,62,6,61,32,253,58,34,152,246,8,211,
- 68,58,203,129,237,57,20,58,204,129,237,57,
- 21,237,56,16,246,34,237,57,16,221,225,209,
- 225,193,241,251,237,77,33,2,0,57,126,230,
- 3,237,100,1,40,2,246,128,230,130,245,62,
- 5,211,64,241,211,64,201,229,213,243,237,56,
- 16,230,46,237,57,16,237,56,12,251,70,35,
- 35,126,254,175,202,77,133,254,129,202,15,133,
- 230,128,194,191,132,43,58,44,152,119,33,76,
- 152,119,35,62,132,119,120,254,255,40,4,58,
- 49,152,119,219,72,43,43,112,17,3,0,237,
- 56,52,230,248,237,57,52,219,72,230,1,194,
- 141,131,209,225,237,56,52,246,6,237,57,52,
- 62,1,55,251,201,62,3,211,66,62,192,211,
- 66,62,48,211,66,0,0,219,66,230,1,40,
- 4,219,67,24,240,205,203,135,58,75,152,254,
- 255,202,128,132,58,49,152,254,161,250,207,131,
- 58,34,152,211,68,62,10,211,66,62,128,211,
- 66,62,11,211,66,62,6,211,66,24,0,62,
- 14,211,66,62,33,211,66,62,1,211,66,62,
- 64,211,66,62,3,211,66,62,209,211,66,62,
- 100,71,219,66,230,1,32,6,5,32,247,195,
- 248,132,219,67,71,58,44,152,184,194,248,132,
- 62,100,71,219,66,230,1,32,6,5,32,247,
- 195,248,132,219,67,62,100,71,219,66,230,1,
- 32,6,5,32,247,195,248,132,219,67,254,133,
- 32,7,62,0,50,74,152,24,17,254,173,32,
- 7,62,1,50,74,152,24,6,254,141,194,248,
- 132,71,209,225,58,49,152,254,132,32,10,62,
- 50,205,2,134,205,144,135,24,27,254,140,32,
- 15,62,110,205,2,134,62,141,184,32,5,205,
- 144,135,24,8,62,10,205,2,134,205,8,134,
- 62,1,50,106,137,205,158,139,237,56,52,246,
- 6,237,57,52,175,183,251,201,62,20,135,237,
- 57,20,175,237,57,21,237,56,16,246,2,237,
- 57,16,237,56,20,95,237,56,21,123,254,10,
- 48,244,237,56,16,230,17,237,57,16,209,225,
- 205,144,135,62,1,50,106,137,205,158,139,237,
- 56,52,246,6,237,57,52,175,183,251,201,209,
- 225,243,219,72,230,1,40,13,62,10,211,66,
- 0,0,219,66,230,192,202,226,132,237,56,52,
- 246,6,237,57,52,62,1,55,251,201,205,203,
- 135,62,1,50,106,137,205,158,139,237,56,52,
- 246,6,237,57,52,183,251,201,209,225,62,1,
- 50,106,137,205,158,139,237,56,52,246,6,237,
- 57,52,62,2,55,251,201,209,225,243,219,72,
- 230,1,202,213,132,62,10,211,66,0,0,219,
- 66,230,192,194,213,132,229,62,1,50,106,137,
- 42,40,152,205,65,143,225,17,3,0,205,111,
- 136,62,6,211,66,58,44,152,211,66,237,56,
- 52,246,6,237,57,52,183,251,201,209,197,237,
- 56,52,230,248,237,57,52,219,72,230,1,32,
- 15,193,225,237,56,52,246,6,237,57,52,62,
- 1,55,251,201,14,23,58,37,152,254,0,40,
- 14,14,2,254,1,32,5,62,140,119,24,3,
- 62,132,119,43,43,197,205,203,135,193,62,1,
- 211,66,62,64,211,66,62,3,211,66,62,193,
- 211,66,62,100,203,39,71,219,66,230,1,32,
- 6,5,32,247,195,229,133,33,238,151,219,67,
- 71,58,44,152,184,194,229,133,119,62,100,71,
- 219,66,230,1,32,6,5,32,247,195,229,133,
- 219,67,35,119,13,32,234,193,225,62,1,50,
- 106,137,205,158,139,237,56,52,246,6,237,57,
- 52,175,183,251,201,33,234,151,35,35,62,255,
- 119,193,225,62,1,50,106,137,205,158,139,237,
- 56,52,246,6,237,57,52,175,251,201,243,61,
- 32,253,251,201,62,3,211,66,62,192,211,66,
- 58,49,152,254,140,32,19,197,229,213,17,181,
- 129,33,185,129,1,2,0,237,176,209,225,193,
- 24,27,229,213,33,187,129,58,49,152,230,15,
- 87,30,2,237,92,25,17,181,129,126,18,19,
- 35,126,18,209,225,58,34,152,246,8,211,68,
- 58,49,152,254,165,40,14,254,164,40,10,62,
- 10,211,66,62,224,211,66,24,25,58,74,152,
- 254,0,40,10,62,10,211,66,62,160,211,66,
- 24,8,62,10,211,66,62,128,211,66,62,11,
- 211,66,62,6,211,66,205,147,143,62,5,211,
- 66,62,224,211,66,62,5,211,66,62,96,211,
- 66,62,5,61,32,253,62,5,211,66,62,224,
- 211,66,62,14,61,32,253,62,5,211,66,62,
- 233,211,66,62,128,211,66,58,181,129,61,32,
- 253,62,1,211,66,62,192,211,66,1,254,19,
- 237,56,46,187,32,6,13,32,247,195,226,134,
- 62,192,211,66,0,0,219,66,203,119,40,250,
- 219,66,203,87,40,250,243,237,56,16,230,17,
- 237,57,16,237,56,20,251,62,5,211,66,62,
- 224,211,66,58,182,129,61,32,253,229,33,181,
- 129,58,183,129,203,63,119,35,58,184,129,119,
- 225,62,10,211,66,62,224,211,66,62,11,211,
- 66,62,118,211,66,62,47,211,68,62,5,211,
- 66,62,233,211,66,58,181,129,61,32,253,62,
- 5,211,66,62,224,211,66,58,182,129,61,32,
- 253,62,5,211,66,62,96,211,66,201,229,213,
- 58,50,152,230,15,87,30,2,237,92,33,187,
- 129,25,17,181,129,126,18,35,19,126,18,209,
- 225,58,71,152,246,8,211,68,58,50,152,254,
- 165,40,14,254,164,40,10,62,10,211,66,62,
- 224,211,66,24,8,62,10,211,66,62,128,211,
- 66,62,11,211,66,62,6,211,66,195,248,135,
- 62,3,211,66,62,192,211,66,197,229,213,17,
- 181,129,33,183,129,1,2,0,237,176,209,225,
- 193,62,47,211,68,62,10,211,66,62,224,211,
- 66,62,11,211,66,62,118,211,66,62,1,211,
- 66,62,0,211,66,205,147,143,195,16,136,62,
- 3,211,66,62,192,211,66,197,229,213,17,181,
- 129,33,183,129,1,2,0,237,176,209,225,193,
- 62,47,211,68,62,10,211,66,62,224,211,66,
- 62,11,211,66,62,118,211,66,205,147,143,62,
- 5,211,66,62,224,211,66,62,5,211,66,62,
- 96,211,66,62,5,61,32,253,62,5,211,66,
- 62,224,211,66,62,14,61,32,253,62,5,211,
- 66,62,233,211,66,62,128,211,66,58,181,129,
- 61,32,253,62,1,211,66,62,192,211,66,1,
- 254,19,237,56,46,187,32,6,13,32,247,195,
- 88,136,62,192,211,66,0,0,219,66,203,119,
- 40,250,219,66,203,87,40,250,62,5,211,66,
- 62,224,211,66,58,182,129,61,32,253,62,5,
- 211,66,62,96,211,66,201,197,14,67,6,0,
- 62,3,211,66,62,192,211,66,62,48,211,66,
- 0,0,219,66,230,1,40,4,219,67,24,240,
- 62,5,211,66,62,233,211,66,62,128,211,66,
- 58,181,129,61,32,253,237,163,29,62,192,211,
- 66,219,66,230,4,40,250,237,163,29,32,245,
- 219,66,230,4,40,250,62,255,71,219,66,230,
- 4,40,3,5,32,247,219,66,230,4,40,250,
- 62,5,211,66,62,224,211,66,58,182,129,61,
- 32,253,62,5,211,66,62,96,211,66,58,71,
- 152,254,1,202,18,137,62,16,211,66,62,56,
- 211,66,62,14,211,66,62,33,211,66,62,1,
- 211,66,62,248,211,66,237,56,48,246,153,230,
- 207,237,57,48,62,3,211,66,62,221,211,66,
- 193,201,58,71,152,211,68,62,10,211,66,62,
- 128,211,66,62,11,211,66,62,6,211,66,62,
- 6,211,66,58,44,152,211,66,62,16,211,66,
- 62,56,211,66,62,48,211,66,0,0,62,14,
- 211,66,62,33,211,66,62,1,211,66,62,248,
- 211,66,237,56,48,246,145,246,8,230,207,237,
- 57,48,62,3,211,66,62,221,211,66,193,201,
- 44,3,1,0,70,69,1,245,197,213,229,175,
- 50,72,152,237,56,16,230,46,237,57,16,237,
- 56,12,62,1,211,66,0,0,219,66,95,230,
- 160,32,3,195,20,139,123,230,96,194,72,139,
- 62,48,211,66,62,1,211,66,62,64,211,66,
- 237,91,40,152,205,207,143,25,43,55,237,82,
- 218,70,139,34,42,152,98,107,58,44,152,190,
- 194,210,138,35,35,62,130,190,194,200,137,62,
- 1,50,48,152,62,175,190,202,82,139,62,132,
- 190,32,44,50,50,152,62,47,50,71,152,229,
- 175,50,106,137,42,40,152,205,65,143,225,54,
- 133,43,70,58,44,152,119,43,112,17,3,0,
- 62,10,205,2,134,205,111,136,195,158,138,62,
- 140,190,32,19,50,50,152,58,233,149,230,4,
- 202,222,138,62,1,50,71,152,195,219,137,126,
- 254,160,250,185,138,254,166,242,185,138,50,50,
- 152,43,126,35,229,213,33,234,149,95,22,0,
- 25,126,254,132,40,18,254,140,40,14,58,50,
- 152,230,15,87,126,31,21,242,65,138,56,2,
- 175,119,58,50,152,230,15,87,58,233,149,230,
- 62,31,21,242,85,138,218,98,138,209,225,195,
- 20,139,58,50,152,33,100,137,230,15,95,22,
- 0,25,126,50,71,152,209,225,58,50,152,254,
- 164,250,135,138,58,73,152,254,0,40,4,54,
- 173,24,2,54,133,43,70,58,44,152,119,43,
- 112,17,3,0,205,70,135,175,50,106,137,205,
- 208,139,58,199,129,237,57,12,58,200,129,237,
- 57,13,237,56,16,246,17,237,57,16,225,209,
- 193,241,251,237,77,62,129,190,194,227,138,54,
- 130,43,70,58,44,152,119,43,112,17,3,0,
- 205,144,135,195,20,139,35,35,126,254,132,194,
- 227,138,175,50,106,137,205,158,139,24,42,58,
- 201,154,254,1,40,7,62,1,50,106,137,24,
- 237,58,106,137,254,1,202,222,138,62,128,166,
- 194,222,138,221,229,221,33,67,152,205,127,142,
- 205,109,144,221,225,225,209,193,241,251,237,77,
- 58,106,137,254,1,202,44,139,58,50,152,254,
- 164,250,44,139,58,73,152,238,1,50,73,152,
- 221,229,221,33,51,152,205,127,142,221,225,62,
- 1,50,106,137,205,158,139,195,13,139,24,208,
- 24,206,24,204,230,64,40,3,195,20,139,195,
- 20,139,43,126,33,8,152,119,35,58,44,152,
- 119,43,237,91,35,152,205,203,135,205,158,139,
- 195,13,139,175,50,78,152,62,3,211,66,62,
- 192,211,66,201,197,33,4,0,57,126,35,102,
- 111,62,1,50,106,137,219,72,205,141,139,193,
- 201,62,1,50,78,152,34,40,152,54,0,35,
- 35,54,0,195,163,139,58,78,152,183,200,229,
- 33,181,129,58,183,129,119,35,58,184,129,119,
- 225,62,47,211,68,62,14,211,66,62,193,211,
- 66,62,10,211,66,62,224,211,66,62,11,211,
- 66,62,118,211,66,195,3,140,58,78,152,183,
- 200,58,71,152,211,68,254,69,40,4,254,70,
- 32,17,58,73,152,254,0,40,10,62,10,211,
- 66,62,160,211,66,24,8,62,10,211,66,62,
- 128,211,66,62,11,211,66,62,6,211,66,62,
- 6,211,66,58,44,152,211,66,62,16,211,66,
- 62,56,211,66,62,48,211,66,0,0,219,66,
- 230,1,40,4,219,67,24,240,62,14,211,66,
- 62,33,211,66,42,40,152,205,65,143,62,1,
- 211,66,62,248,211,66,237,56,48,246,145,246,
- 8,230,207,237,57,48,62,3,211,66,62,221,
- 211,66,201,62,16,211,66,62,56,211,66,62,
- 48,211,66,0,0,219,66,230,1,40,4,219,
- 67,24,240,62,14,211,66,62,33,211,66,62,
- 1,211,66,62,248,211,66,237,56,48,246,153,
- 230,207,237,57,48,62,3,211,66,62,221,211,
- 66,201,229,213,33,234,149,95,22,0,25,126,
- 254,132,40,4,254,140,32,2,175,119,123,209,
- 225,201,6,8,14,0,31,48,1,12,16,250,
- 121,201,33,4,0,57,94,35,86,33,2,0,
- 57,126,35,102,111,221,229,34,89,152,237,83,
- 91,152,221,33,63,152,205,127,142,58,81,152,
- 50,82,152,58,80,152,135,50,80,152,205,162,
- 140,254,3,56,16,58,81,152,135,60,230,15,
- 50,81,152,175,50,80,152,24,23,58,79,152,
- 205,162,140,254,3,48,13,58,81,152,203,63,
- 50,81,152,62,255,50,79,152,58,81,152,50,
- 82,152,58,79,152,135,50,79,152,62,32,50,
- 83,152,50,84,152,237,56,16,230,17,237,57,
- 16,219,72,62,192,50,93,152,62,93,50,94,
- 152,58,93,152,61,50,93,152,32,9,58,94,
- 152,61,50,94,152,40,44,62,170,237,57,20,
- 175,237,57,21,237,56,16,246,2,237,57,16,
- 219,72,230,1,202,29,141,237,56,20,71,237,
- 56,21,120,254,10,48,237,237,56,16,230,17,
- 237,57,16,243,62,14,211,66,62,65,211,66,
- 251,58,39,152,23,23,60,50,39,152,71,58,
- 82,152,160,230,15,40,22,71,14,10,219,66,
- 230,16,202,186,141,219,72,230,1,202,186,141,
- 13,32,239,16,235,42,89,152,237,91,91,152,
- 205,47,131,48,7,61,202,186,141,195,227,141,
- 221,225,33,0,0,201,221,33,55,152,205,127,
- 142,58,84,152,61,50,84,152,40,19,58,82,
- 152,246,1,50,82,152,58,79,152,246,1,50,
- 79,152,195,29,141,221,225,33,1,0,201,221,
- 33,59,152,205,127,142,58,80,152,246,1,50,
- 80,152,58,82,152,135,246,1,50,82,152,58,
- 83,152,61,50,83,152,194,29,141,221,225,33,
- 2,0,201,221,229,33,0,0,57,17,4,0,
- 25,126,50,44,152,230,128,50,85,152,58,85,
- 152,183,40,6,221,33,88,2,24,4,221,33,
- 150,0,58,44,152,183,40,53,60,40,50,60,
- 40,47,61,61,33,86,152,119,35,119,35,54,
- 129,175,50,48,152,221,43,221,229,225,124,181,
- 40,42,33,86,152,17,3,0,205,189,140,17,
- 232,3,27,123,178,32,251,58,48,152,183,40,
- 224,58,44,152,71,62,7,128,230,127,71,58,
- 85,152,176,50,44,152,24,162,221,225,201,183,
- 221,52,0,192,221,52,1,192,221,52,2,192,
- 221,52,3,192,55,201,245,62,1,211,100,241,
- 201,245,62,1,211,96,241,201,33,2,0,57,
- 126,35,102,111,237,56,48,230,175,237,57,48,
- 62,48,237,57,49,125,237,57,32,124,237,57,
- 33,62,0,237,57,34,62,88,237,57,35,62,
- 0,237,57,36,237,57,37,33,128,2,125,237,
- 57,38,124,237,57,39,237,56,48,246,97,230,
- 207,237,57,48,62,0,237,57,0,62,0,211,
- 96,211,100,201,33,2,0,57,126,35,102,111,
- 237,56,48,230,175,237,57,48,62,12,237,57,
- 49,62,76,237,57,32,62,0,237,57,33,237,
- 57,34,125,237,57,35,124,237,57,36,62,0,
- 237,57,37,33,128,2,125,237,57,38,124,237,
- 57,39,237,56,48,246,97,230,207,237,57,48,
- 62,1,211,96,201,33,2,0,57,126,35,102,
- 111,229,237,56,48,230,87,237,57,48,125,237,
- 57,40,124,237,57,41,62,0,237,57,42,62,
- 67,237,57,43,62,0,237,57,44,58,106,137,
- 254,1,32,5,33,6,0,24,3,33,128,2,
- 125,237,57,46,124,237,57,47,237,56,50,230,
- 252,246,2,237,57,50,225,201,33,4,0,57,
- 94,35,86,33,2,0,57,126,35,102,111,237,
- 56,48,230,87,237,57,48,125,237,57,40,124,
- 237,57,41,62,0,237,57,42,62,67,237,57,
- 43,62,0,237,57,44,123,237,57,46,122,237,
- 57,47,237,56,50,230,244,246,0,237,57,50,
- 237,56,48,246,145,230,207,237,57,48,201,213,
- 237,56,46,95,237,56,47,87,237,56,46,111,
- 237,56,47,103,183,237,82,32,235,33,128,2,
- 183,237,82,209,201,213,237,56,38,95,237,56,
- 39,87,237,56,38,111,237,56,39,103,183,237,
- 82,32,235,33,128,2,183,237,82,209,201,245,
- 197,1,52,0,237,120,230,253,237,121,193,241,
- 201,245,197,1,52,0,237,120,246,2,237,121,
- 193,241,201,33,2,0,57,126,35,102,111,126,
- 35,110,103,201,33,0,0,34,102,152,34,96,
- 152,34,98,152,33,202,154,34,104,152,237,91,
- 104,152,42,226,149,183,237,82,17,0,255,25,
- 34,100,152,203,124,40,6,33,0,125,34,100,
- 152,42,104,152,35,35,35,229,205,120,139,193,
- 201,205,186,149,229,42,40,152,35,35,35,229,
- 205,39,144,193,124,230,3,103,221,117,254,221,
- 116,255,237,91,42,152,35,35,35,183,237,82,
- 32,12,17,5,0,42,42,152,205,171,149,242,
- 169,144,42,40,152,229,205,120,139,193,195,198,
- 149,237,91,42,152,42,98,152,25,34,98,152,
- 19,19,19,42,102,152,25,34,102,152,237,91,
- 100,152,33,158,253,25,237,91,102,152,205,171,
- 149,242,214,144,33,0,0,34,102,152,62,1,
- 50,95,152,205,225,144,195,198,149,58,95,152,
- 183,200,237,91,96,152,42,102,152,205,171,149,
- 242,5,145,237,91,102,152,33,98,2,25,237,
- 91,96,152,205,171,149,250,37,145,237,91,96,
- 152,42,102,152,183,237,82,32,7,42,98,152,
- 125,180,40,13,237,91,102,152,42,96,152,205,
- 171,149,242,58,145,237,91,104,152,42,102,152,
- 25,35,35,35,229,205,120,139,193,175,50,95,
- 152,201,195,107,139,205,206,149,250,255,243,205,
- 225,144,251,58,230,149,183,194,198,149,17,1,
- 0,42,98,152,205,171,149,250,198,149,62,1,
- 50,230,149,237,91,96,152,42,104,152,25,221,
- 117,252,221,116,253,237,91,104,152,42,96,152,
- 25,35,35,35,221,117,254,221,116,255,35,35,
- 35,229,205,39,144,124,230,3,103,35,35,35,
- 221,117,250,221,116,251,235,221,110,252,221,102,
- 253,115,35,114,35,54,4,62,1,211,100,211,
- 84,195,198,149,33,0,0,34,102,152,34,96,
- 152,34,98,152,33,202,154,34,104,152,237,91,
- 104,152,42,226,149,183,237,82,17,0,255,25,
- 34,100,152,33,109,152,54,0,33,107,152,229,
- 205,240,142,193,62,47,50,34,152,62,132,50,
- 49,152,205,241,145,205,61,145,58,39,152,60,
- 50,39,152,24,241,205,206,149,251,255,33,109,
- 152,126,183,202,198,149,110,221,117,251,33,109,
- 152,54,0,221,126,251,254,1,40,28,254,3,
- 40,101,254,4,202,190,147,254,5,202,147,147,
- 254,8,40,87,33,107,152,229,205,240,142,195,
- 198,149,58,201,154,183,32,21,33,111,152,126,
- 50,229,149,205,52,144,33,110,152,110,38,0,
- 229,205,11,142,193,237,91,96,152,42,104,152,
- 25,221,117,254,221,116,255,35,35,54,2,17,
- 2,0,43,43,115,35,114,58,44,152,35,35,
- 119,58,228,149,35,119,62,1,211,100,211,84,
- 62,1,50,201,154,24,169,205,153,142,58,231,
- 149,183,40,250,175,50,231,149,33,110,152,126,
- 254,255,40,91,58,233,149,230,63,183,40,83,
- 94,22,0,33,234,149,25,126,183,40,13,33,
- 110,152,94,33,234,150,25,126,254,3,32,36,
- 205,81,148,125,180,33,110,152,94,22,0,40,
- 17,33,234,149,25,54,0,33,107,152,229,205,
- 240,142,193,195,198,149,33,234,150,25,54,0,
- 33,110,152,94,22,0,33,234,149,25,126,50,
- 49,152,254,132,32,37,62,47,50,34,152,42,
- 107,152,229,33,110,152,229,205,174,140,193,193,
- 125,180,33,110,152,94,22,0,33,234,150,202,
- 117,147,25,52,195,120,147,58,49,152,254,140,
- 32,7,62,1,50,34,152,24,210,62,32,50,
- 106,152,24,19,58,49,152,95,58,106,152,163,
- 183,58,106,152,32,11,203,63,50,106,152,58,
- 106,152,183,32,231,254,2,40,51,254,4,40,
- 38,254,8,40,26,254,16,40,13,254,32,32,
- 158,62,165,50,49,152,62,69,24,190,62,164,
- 50,49,152,62,70,24,181,62,163,50,49,152,
- 175,24,173,62,162,50,49,152,62,1,24,164,
- 62,161,50,49,152,62,3,24,155,25,54,0,
- 221,126,251,254,8,40,7,58,230,149,183,202,
- 32,146,33,107,152,229,205,240,142,193,211,84,
- 195,198,149,237,91,96,152,42,104,152,25,221,
- 117,254,221,116,255,35,35,54,6,17,2,0,
- 43,43,115,35,114,58,228,149,35,35,119,58,
- 233,149,35,119,205,146,142,195,32,146,237,91,
- 96,152,42,104,152,25,229,205,160,142,193,58,
- 231,149,183,40,250,175,50,231,149,243,237,91,
- 96,152,42,104,152,25,221,117,254,221,116,255,
- 78,35,70,221,113,252,221,112,253,89,80,42,
- 98,152,183,237,82,34,98,152,203,124,40,19,
- 33,0,0,34,98,152,34,102,152,34,96,152,
- 62,1,50,95,152,24,40,221,94,252,221,86,
- 253,19,19,19,42,96,152,25,34,96,152,237,
- 91,100,152,33,158,253,25,237,91,96,152,205,
- 171,149,242,55,148,33,0,0,34,96,152,175,
- 50,230,149,251,195,32,146,245,62,1,50,231,
- 149,62,16,237,57,0,211,80,241,251,237,77,
- 201,205,186,149,229,229,33,0,0,34,37,152,
- 33,110,152,126,50,234,151,58,44,152,33,235,
- 151,119,221,54,253,0,221,54,254,0,195,230,
- 148,33,236,151,54,175,33,3,0,229,33,234,
- 151,229,205,174,140,193,193,33,236,151,126,254,
- 255,40,74,33,245,151,110,221,117,255,33,249,
- 151,126,221,166,255,221,119,255,33,253,151,126,
- 221,166,255,221,119,255,58,232,149,95,221,126,
- 255,163,221,119,255,183,40,15,230,191,33,110,
- 152,94,22,0,33,234,149,25,119,24,12,33,
- 110,152,94,22,0,33,234,149,25,54,132,33,
- 0,0,195,198,149,221,110,253,221,102,254,35,
- 221,117,253,221,116,254,17,32,0,221,110,253,
- 221,102,254,205,171,149,250,117,148,58,233,149,
- 203,87,40,84,33,1,0,34,37,152,221,54,
- 253,0,221,54,254,0,24,53,33,236,151,54,
- 175,33,3,0,229,33,234,151,229,205,174,140,
- 193,193,33,236,151,126,254,255,40,14,33,110,
- 152,94,22,0,33,234,149,25,54,140,24,159,
- 221,110,253,221,102,254,35,221,117,253,221,116,
- 254,17,32,0,221,110,253,221,102,254,205,171,
- 149,250,12,149,33,2,0,34,37,152,221,54,
- 253,0,221,54,254,0,24,54,33,236,151,54,
- 175,33,3,0,229,33,234,151,229,205,174,140,
- 193,193,33,236,151,126,254,255,40,15,33,110,
- 152,94,22,0,33,234,149,25,54,132,195,211,
- 148,221,110,253,221,102,254,35,221,117,253,221,
- 116,254,17,32,0,221,110,253,221,102,254,205,
- 171,149,250,96,149,33,1,0,195,198,149,124,
- 170,250,179,149,237,82,201,124,230,128,237,82,
- 60,201,225,253,229,221,229,221,33,0,0,221,
- 57,233,221,249,221,225,253,225,201,233,225,253,
- 229,221,229,221,33,0,0,221,57,94,35,86,
- 35,235,57,249,235,233,0,0,0,0,0,0,
- 62,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 175,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,133,1,0,0,0,63,
- 255,255,255,255,0,0,0,63,0,0,0,0,
- 0,0,0,0,0,0,0,24,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0
- } ;
-
-#endif
+++ /dev/null
-/*
- * The firmware this driver downloads into the Localtalk card is a
- * separate program and is not GPL'd source code, even though the Linux
- * side driver and the routine that loads this data into the card are.
- *
- * It is taken from the COPS SDK and is under the following license
- *
- * This material is licensed to you strictly for use in conjunction with
- * the use of COPS LocalTalk adapters.
- * There is no charge for this SDK. And no waranty express or implied
- * about its fitness for any purpose. However, we will cheerefully
- * refund every penny you paid for this SDK...
- * Regards,
- *
- * Thomas F. Divine
- * Chief Scientist
- */
-
-
-/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux.
- *
- * Authors:
- * - Jay Schulist <jschlst@turbolinux.com>
- */
-
-#include <linux/config.h>
-
-#ifdef CONFIG_COPS_TANGENT
-
-unsigned char ltdrv_code[] = {
- 58,3,0,50,148,10,33,143,15,62,85,119,
- 190,32,9,62,170,119,190,32,3,35,24,241,
- 34,146,10,249,17,150,10,33,143,15,183,237,
- 82,77,68,11,107,98,19,54,0,237,176,62,
- 16,237,57,51,62,0,237,57,50,237,57,54,
- 62,12,237,57,49,62,195,33,39,2,50,56,
- 0,34,57,0,237,86,205,30,2,251,205,60,
- 10,24,169,67,111,112,121,114,105,103,104,116,
- 32,40,99,41,32,49,57,56,56,45,49,57,
- 57,50,44,32,80,114,105,110,116,105,110,103,
- 32,67,111,109,109,117,110,105,99,97,116,105,
- 111,110,115,32,65,115,115,111,99,105,97,116,
- 101,115,44,32,73,110,99,46,65,108,108,32,
- 114,105,103,104,116,115,32,114,101,115,101,114,
- 118,101,100,46,32,32,4,4,22,40,255,60,
- 4,96,10,224,6,0,7,126,2,64,11,246,
- 12,6,13,0,14,193,15,0,5,96,3,192,
- 1,0,9,8,62,3,211,82,62,192,211,82,
- 201,62,3,211,82,62,213,211,82,201,62,5,
- 211,82,62,224,211,82,201,62,5,211,82,62,
- 224,211,82,201,62,5,211,82,62,96,211,82,
- 201,6,28,33,180,1,14,82,237,163,194,4,
- 2,33,39,2,34,64,0,58,3,0,230,1,
- 192,62,11,237,121,62,118,237,121,201,33,182,
- 10,54,132,205,253,1,201,245,197,213,229,42,
- 150,10,14,83,17,98,2,67,20,237,162,58,
- 179,1,95,219,82,230,1,32,6,29,32,247,
- 195,17,3,62,1,211,82,219,82,95,230,160,
- 32,10,237,162,32,225,21,32,222,195,15,3,
- 237,162,123,230,96,194,21,3,62,48,211,82,
- 62,1,211,82,175,211,82,237,91,150,10,43,
- 55,237,82,218,19,3,34,152,10,98,107,58,
- 154,10,190,32,81,62,1,50,158,10,35,35,
- 62,132,190,32,44,54,133,43,70,58,154,10,
- 119,43,112,17,3,0,205,137,3,62,16,211,
- 82,62,56,211,82,205,217,1,42,150,10,14,
- 83,17,98,2,67,20,58,178,1,95,195,59,
- 2,62,129,190,194,227,2,54,130,43,70,58,
- 154,10,119,43,112,17,3,0,205,137,3,195,
- 254,2,35,35,126,254,132,194,227,2,205,61,
- 3,24,20,62,128,166,194,222,2,221,229,221,
- 33,175,10,205,93,6,205,144,7,221,225,225,
- 209,193,241,251,237,77,221,229,221,33,159,10,
- 205,93,6,221,225,205,61,3,195,247,2,24,
- 237,24,235,24,233,230,64,40,2,24,227,24,
- 225,175,50,179,10,205,208,1,201,197,33,4,
- 0,57,126,35,102,111,205,51,3,193,201,62,
- 1,50,179,10,34,150,10,54,0,58,179,10,
- 183,200,62,14,211,82,62,193,211,82,62,10,
- 211,82,62,224,211,82,62,6,211,82,58,154,
- 10,211,82,62,16,211,82,62,56,211,82,62,
- 48,211,82,219,82,230,1,40,4,219,83,24,
- 242,62,14,211,82,62,33,211,82,62,1,211,
- 82,62,9,211,82,62,32,211,82,205,217,1,
- 201,14,83,205,208,1,24,23,14,83,205,208,
- 1,205,226,1,58,174,1,61,32,253,205,244,
- 1,58,174,1,61,32,253,205,226,1,58,175,
- 1,61,32,253,62,5,211,82,62,233,211,82,
- 62,128,211,82,58,176,1,61,32,253,237,163,
- 27,62,192,211,82,219,82,230,4,40,250,237,
- 163,27,122,179,32,243,219,82,230,4,40,250,
- 58,178,1,71,219,82,230,4,40,3,5,32,
- 247,219,82,230,4,40,250,205,235,1,58,177,
- 1,61,32,253,205,244,1,201,229,213,35,35,
- 126,230,128,194,145,4,43,58,154,10,119,43,
- 70,33,181,10,119,43,112,17,3,0,243,62,
- 10,211,82,219,82,230,128,202,41,4,209,225,
- 62,1,55,251,201,205,144,3,58,180,10,254,
- 255,202,127,4,205,217,1,58,178,1,71,219,
- 82,230,1,32,6,5,32,247,195,173,4,219,
- 83,71,58,154,10,184,194,173,4,58,178,1,
- 71,219,82,230,1,32,6,5,32,247,195,173,
- 4,219,83,58,178,1,71,219,82,230,1,32,
- 6,5,32,247,195,173,4,219,83,254,133,194,
- 173,4,58,179,1,24,4,58,179,1,135,61,
- 32,253,209,225,205,137,3,205,61,3,183,251,
- 201,209,225,243,62,10,211,82,219,82,230,128,
- 202,164,4,62,1,55,251,201,205,144,3,205,
- 61,3,183,251,201,209,225,62,2,55,251,201,
- 243,62,14,211,82,62,33,211,82,251,201,33,
- 4,0,57,94,35,86,33,2,0,57,126,35,
- 102,111,221,229,34,193,10,237,83,195,10,221,
- 33,171,10,205,93,6,58,185,10,50,186,10,
- 58,184,10,135,50,184,10,205,112,6,254,3,
- 56,16,58,185,10,135,60,230,15,50,185,10,
- 175,50,184,10,24,23,58,183,10,205,112,6,
- 254,3,48,13,58,185,10,203,63,50,185,10,
- 62,255,50,183,10,58,185,10,50,186,10,58,
- 183,10,135,50,183,10,62,32,50,187,10,50,
- 188,10,6,255,219,82,230,16,32,3,5,32,
- 247,205,180,4,6,40,219,82,230,16,40,3,
- 5,32,247,62,10,211,82,219,82,230,128,194,
- 46,5,219,82,230,16,40,214,237,95,71,58,
- 186,10,160,230,15,40,32,71,14,10,62,10,
- 211,82,219,82,230,128,202,119,5,205,180,4,
- 195,156,5,219,82,230,16,202,156,5,13,32,
- 229,16,225,42,193,10,237,91,195,10,205,252,
- 3,48,7,61,202,156,5,195,197,5,221,225,
- 33,0,0,201,221,33,163,10,205,93,6,58,
- 188,10,61,50,188,10,40,19,58,186,10,246,
- 1,50,186,10,58,183,10,246,1,50,183,10,
- 195,46,5,221,225,33,1,0,201,221,33,167,
- 10,205,93,6,58,184,10,246,1,50,184,10,
- 58,186,10,135,246,1,50,186,10,58,187,10,
- 61,50,187,10,194,46,5,221,225,33,2,0,
- 201,221,229,33,0,0,57,17,4,0,25,126,
- 50,154,10,230,128,50,189,10,58,189,10,183,
- 40,6,221,33,88,2,24,4,221,33,150,0,
- 58,154,10,183,40,49,60,40,46,61,33,190,
- 10,119,35,119,35,54,129,175,50,158,10,221,
- 43,221,229,225,124,181,40,42,33,190,10,17,
- 3,0,205,206,4,17,232,3,27,123,178,32,
- 251,58,158,10,183,40,224,58,154,10,71,62,
- 7,128,230,127,71,58,189,10,176,50,154,10,
- 24,166,221,225,201,183,221,52,0,192,221,52,
- 1,192,221,52,2,192,221,52,3,192,55,201,
- 6,8,14,0,31,48,1,12,16,250,121,201,
- 33,2,0,57,94,35,86,35,78,35,70,35,
- 126,35,102,105,79,120,68,103,237,176,201,33,
- 2,0,57,126,35,102,111,62,17,237,57,48,
- 125,237,57,40,124,237,57,41,62,0,237,57,
- 42,62,64,237,57,43,62,0,237,57,44,33,
- 128,2,125,237,57,46,124,237,57,47,62,145,
- 237,57,48,211,68,58,149,10,211,66,201,33,
- 2,0,57,126,35,102,111,62,33,237,57,48,
- 62,64,237,57,32,62,0,237,57,33,237,57,
- 34,125,237,57,35,124,237,57,36,62,0,237,
- 57,37,33,128,2,125,237,57,38,124,237,57,
- 39,62,97,237,57,48,211,67,58,149,10,211,
- 66,201,237,56,46,95,237,56,47,87,237,56,
- 46,111,237,56,47,103,183,237,82,32,235,33,
- 128,2,183,237,82,201,237,56,38,95,237,56,
- 39,87,237,56,38,111,237,56,39,103,183,237,
- 82,32,235,33,128,2,183,237,82,201,205,106,
- 10,221,110,6,221,102,7,126,35,110,103,195,
- 118,10,205,106,10,33,0,0,34,205,10,34,
- 198,10,34,200,10,33,143,15,34,207,10,237,
- 91,207,10,42,146,10,183,237,82,17,0,255,
- 25,34,203,10,203,124,40,6,33,0,125,34,
- 203,10,42,207,10,229,205,37,3,195,118,10,
- 205,106,10,229,42,150,10,35,35,35,229,205,
- 70,7,193,124,230,3,103,221,117,254,221,116,
- 255,237,91,152,10,35,35,35,183,237,82,32,
- 12,17,5,0,42,152,10,205,91,10,242,203,
- 7,42,150,10,229,205,37,3,195,118,10,237,
- 91,152,10,42,200,10,25,34,200,10,42,205,
- 10,25,34,205,10,237,91,203,10,33,158,253,
- 25,237,91,205,10,205,91,10,242,245,7,33,
- 0,0,34,205,10,62,1,50,197,10,205,5,
- 8,33,0,0,57,249,195,118,10,205,106,10,
- 58,197,10,183,202,118,10,237,91,198,10,42,
- 205,10,205,91,10,242,46,8,237,91,205,10,
- 33,98,2,25,237,91,198,10,205,91,10,250,
- 78,8,237,91,198,10,42,205,10,183,237,82,
- 32,7,42,200,10,125,180,40,13,237,91,205,
- 10,42,198,10,205,91,10,242,97,8,237,91,
- 207,10,42,205,10,25,229,205,37,3,175,50,
- 197,10,195,118,10,205,29,3,33,0,0,57,
- 249,195,118,10,205,106,10,58,202,10,183,40,
- 22,205,14,7,237,91,209,10,19,19,19,205,
- 91,10,242,139,8,33,1,0,195,118,10,33,
- 0,0,195,118,10,205,126,10,252,255,205,108,
- 8,125,180,194,118,10,237,91,200,10,33,0,
- 0,205,91,10,242,118,10,237,91,207,10,42,
- 198,10,25,221,117,254,221,116,255,35,35,35,
- 229,205,70,7,193,124,230,3,103,35,35,35,
- 221,117,252,221,116,253,229,221,110,254,221,102,
- 255,229,33,212,10,229,205,124,6,193,193,221,
- 110,252,221,102,253,34,209,10,33,211,10,54,
- 4,33,209,10,227,205,147,6,193,62,1,50,
- 202,10,243,221,94,252,221,86,253,42,200,10,
- 183,237,82,34,200,10,203,124,40,17,33,0,
- 0,34,200,10,34,205,10,34,198,10,50,197,
- 10,24,37,221,94,252,221,86,253,42,198,10,
- 25,34,198,10,237,91,203,10,33,158,253,25,
- 237,91,198,10,205,91,10,242,68,9,33,0,
- 0,34,198,10,205,5,8,33,0,0,57,249,
- 251,195,118,10,205,106,10,33,49,13,126,183,
- 40,16,205,42,7,237,91,47,13,19,19,19,
- 205,91,10,242,117,9,58,142,15,198,1,50,
- 142,15,195,118,10,33,49,13,126,254,1,40,
- 25,254,3,202,7,10,254,5,202,21,10,33,
- 49,13,54,0,33,47,13,229,205,207,6,195,
- 118,10,58,141,15,183,32,72,33,51,13,126,
- 50,149,10,205,86,7,33,50,13,126,230,127,
- 183,32,40,58,142,15,230,127,50,142,15,183,
- 32,5,198,1,50,142,15,33,50,13,126,111,
- 23,159,103,203,125,58,142,15,40,5,198,128,
- 50,142,15,33,50,13,119,33,50,13,126,111,
- 23,159,103,229,205,237,5,193,33,211,10,54,
- 2,33,2,0,34,209,10,58,154,10,33,212,
- 10,119,58,148,10,33,213,10,119,33,209,10,
- 229,205,147,6,193,24,128,42,47,13,229,33,
- 50,13,229,205,191,4,193,24,239,33,211,10,
- 54,6,33,3,0,34,209,10,58,154,10,33,
- 212,10,119,58,148,10,33,213,10,119,33,214,
- 10,54,5,33,209,10,229,205,147,6,24,200,
- 205,106,10,33,49,13,54,0,33,47,13,229,
- 205,207,6,33,209,10,227,205,147,6,193,205,
- 80,9,205,145,8,24,248,124,170,250,99,10,
- 237,82,201,124,230,128,237,82,60,201,225,253,
- 229,221,229,221,33,0,0,221,57,233,221,249,
- 221,225,253,225,201,233,225,253,229,221,229,221,
- 33,0,0,221,57,94,35,86,35,235,57,249,
- 235,233,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0
- } ;
-
-#endif
-/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
+/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
+ * driver for linux.
+ */
+
/*
Written 1996 by Russell Nelson, with reference to skeleton.c
written 1993-1994 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
- The author may be reached at nelson@crynwr.com, Crynwr
- Software, 11 Grant St., Potsdam, NY 13676
+ The author may be reached at nelson@crynwr.com, Crynwr
+ Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
Changelog:
: as an example. Disabled autoprobing in init_module(),
: not a good thing to do to other devices while Linux
: is running from all accounts.
-
+
+ Russ Nelson : Jul 13 1998. Added RxOnly DMA support.
+
+ Melody Lee : Aug 10 1999. Changes for Linux 2.2.5 compatibility.
+ : email: ethernet@crystal.cirrus.com
+
Alan Cox : Removed 1.2 support, added 2.1 extra counters.
+
+ Andrew Morton : andrewm@uow.edu.au
+ : Kernel 2.3.48
+ : Handle kmalloc() failures
+ : Other resource allocation fixes
+ : Add SMP locks
+ : Integrate Russ Nelson's ALLOW_DMA functionality back in.
+ : If ALLOW_DMA is true, make DMA runtime selectable
+ : Folded in changes from Cirrus (Melody Lee
+ : <klee@crystal.cirrus.com>)
+ : Don't call netif_wake_queue() in net_send_packet()
+ : Fixed an out-of-mem bug in dma_rx()
+ : Updated Documentation/cs89x0.txt
*/
static char *version =
-"cs89x0.c:v1.03 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
-
-/* ======================= configure the driver here ======================= */
-
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 2
-#endif
+"cs89x0.c: (kernel 2.3.48) Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
/* ======================= end of configuration ======================= */
#define MOD_DEC_USE_COUNT
#endif
-#define PRINTK(x) printk x
+/*
+ * Set this to zero to disable DMA code
+ *
+ * Note that even if DMA is turned off we still support the 'dma' and 'use_dma'
+ * module options so we don't break any startup scripts.
+ */
+#define ALLOW_DMA 1
+
+/*
+ * Set this to zero to remove all the debug statements via
+ * dead code elimination
+ */
+#define DEBUGGING 0
/*
Sources:
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#if ALLOW_DMA
+#include <asm/dma.h>
+#endif
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+
#include "cs89x0.h"
/* First, a few definitions that the brave might change. */
/* A zero-terminated list of I/O addresses to be probed. */
static unsigned int netcard_portlist[] __initdata =
- { 0x300, 0x320, 0x340, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
+ { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
-static unsigned int net_debug = NET_DEBUG;
+#if DEBUGGING
+static unsigned int net_debug = 5;
+#else
+#define net_debug 0 /* gcc will remove all the debug code for us */
+#endif
/* The number of low I/O ports used by the ethercard. */
#define NETCARD_IO_EXTENT 16
+/* we allow the user to override various values normally set in the EEPROM */
+#define FORCE_RJ45 0x0001 /* pick one of these three */
+#define FORCE_AUI 0x0002
+#define FORCE_BNC 0x0004
+
+#define FORCE_AUTO 0x0010 /* pick one of these three */
+#define FORCE_HALF 0x0020
+#define FORCE_FULL 0x0030
+
/* Information that need to be kept for each board. */
struct net_local {
struct net_device_stats stats;
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */
- int send_cmd; /* the propercommand used to send a packet. */
- int auto_neg_cnf;
- int adapter_cnf;
- int isa_config;
- int irq_map;
- int rx_mode;
- int curr_rx_cfg;
- int linectl;
- int send_underrun; /* keep track of how many underruns in a row we get */
- struct sk_buff *skb;
+ int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
+ int auto_neg_cnf; /* auto-negotiation word from EEPROM */
+ int adapter_cnf; /* adapter configuration from EEPROM */
+ int isa_config; /* ISA configuration from EEPROM */
+ int irq_map; /* IRQ map from EEPROM */
+ int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
+ int curr_rx_cfg; /* a copy of PP_RxCFG */
+ int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */
+ int send_underrun; /* keep track of how many underruns in a row we get */
+ int force; /* force various values; see FORCE* above. */
+ spinlock_t lock;
+#if ALLOW_DMA
+ int use_dma; /* Flag: we're using dma */
+ int dma; /* DMA channel */
+ int dmasize; /* 16 or 64 */
+ unsigned char *dma_buff; /* points to the beginning of the buffer */
+ unsigned char *end_dma_buff; /* points to the end of the buffer */
+ unsigned char *rx_dma_ptr; /* points to the next packet */
+#endif
};
/* Index to functions, as function prototypes. */
static int cs89x0_probe1(struct net_device *dev, int ioaddr);
static int net_open(struct net_device *dev);
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct net_device *dev);
static void net_timeout(struct net_device *dev);
static int get_eeprom_data(struct net_device *dev, int off, int len, int *buffer);
static int get_eeprom_cksum(int off, int len, int *buffer);
static int set_mac_address(struct net_device *dev, void *addr);
-
+static void count_rx_errors(int status, struct net_local *lp);
+#if ALLOW_DMA
+static void get_dma_channel(struct net_device *dev);
+static void release_dma_buff(struct net_local *lp);
+#endif
/* Example routines you must write ;->. */
#define tx_done(dev) 1
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
(detachable devices only).
+ Return 0 on success.
*/
int __init cs89x0_probe(struct net_device *dev)
int i;
int base_addr = dev ? dev->base_addr : 0;
+ if (net_debug)
+ printk("cs89x0:cs89x0_probe()\n");
+
if (base_addr > 0x1ff) /* Check a single specified location. */
return cs89x0_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
if (cs89x0_probe1(dev, ioaddr) == 0)
return 0;
}
- printk("cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
+ printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
return ENODEV;
}
-extern int inline readreg(struct net_device *dev, int portno)
+extern int inline
+readreg(struct net_device *dev, int portno)
{
outw(portno, dev->base_addr + ADD_PORT);
return inw(dev->base_addr + DATA_PORT);
}
-extern void inline writereg(struct net_device *dev, int portno, int value)
+extern void inline
+writereg(struct net_device *dev, int portno, int value)
{
outw(portno, dev->base_addr + ADD_PORT);
outw(value, dev->base_addr + DATA_PORT);
}
-
-extern int inline readword(struct net_device *dev, int portno)
+extern int inline
+readword(struct net_device *dev, int portno)
{
return inw(dev->base_addr + portno);
}
-extern void inline writeword(struct net_device *dev, int portno, int value)
+extern void inline
+writeword(struct net_device *dev, int portno, int value)
{
outw(value, dev->base_addr + portno);
}
-static int __init wait_eeprom_ready(struct net_device *dev)
+static int __init
+wait_eeprom_ready(struct net_device *dev)
{
int timeout = jiffies;
/* check to see if the EEPROM is ready, a timeout is used -
return 0;
}
-static int __init get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
+static int __init
+get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
{
int i;
return 0;
}
-static int __init get_eeprom_cksum(int off, int len, int *buffer)
+static int __init
+get_eeprom_cksum(int off, int len, int *buffer)
{
int i, cksum;
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probes avoids doing writes, and
- verifies that the correct device exists and functions. */
+ verifies that the correct device exists and functions.
+ Return 0 on success.
+ */
-static int __init cs89x0_probe1(struct net_device *dev, int ioaddr)
+static int __init
+cs89x0_probe1(struct net_device *dev, int ioaddr)
{
struct net_local *lp;
static unsigned version_printed = 0;
int i;
unsigned rev_type = 0;
int eeprom_buff[CHKSUM_LEN];
+ int retval;
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == 0)
+ {
+ retval = ENOMEM;
+ goto out;
+ }
memset(dev->priv, 0, sizeof(struct net_local));
}
lp = (struct net_local *)dev->priv;
}
if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
- return ENODEV;
+ {
+ retval = ENODEV;
+ goto out1;
+ }
/* Fill in the 'dev' fields. */
dev->base_addr = ioaddr;
if (net_debug && version_printed++ == 0)
printk(version);
- printk("%s: cs89%c0%s rev %c found at %#3lx",
+ printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx ",
dev->name,
lp->chip_type==CS8900?'0':'2',
lp->chip_type==CS8920M?"M":"",
/* First check to see if an EEPROM is attached*/
if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
- printk("\ncs89x0: No EEPROM, relying on command line....\n");
+ printk(KERN_WARNING "\ncs89x0: No EEPROM, relying on command line....\n");
else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
- printk("\ncs89x0: EEPROM read failed, relying on command line.\n");
+ printk(KERN_WARNING "\ncs89x0: EEPROM read failed, relying on command line.\n");
} else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
- printk("\ncs89x0: EEPROM checksum bad, relying on command line\n");
+ printk(KERN_WARNING "\ncs89x0: EEPROM checksum bad, relying on command line\n");
} else {
/* get transmission control word but keep the autonegotiation bits */
if (!lp->auto_neg_cnf) lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
if (!lp->adapter_cnf) lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
/* Store ISA configuration */
lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2];
- /* store the initial memory base address */
dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
+
+ /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
+ /* store the initial memory base address */
for (i = 0; i < ETH_ALEN/2; i++) {
dev->dev_addr[i*2] = eeprom_buff[i];
dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
}
}
+ /* allow them to force multiple transceivers. If they force multiple, autosense */
+ {
+ int count = 0;
+ if (lp->force & FORCE_RJ45) {lp->adapter_cnf |= A_CNF_10B_T; count++; }
+ if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_AUI; count++; }
+ if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_10B_2; count++; }
+ if (count > 1) {lp->adapter_cnf |= A_CNF_MEDIA_AUTO; }
+ else if (lp->force & FORCE_RJ45){lp->adapter_cnf |= A_CNF_MEDIA_10B_T; }
+ else if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_MEDIA_AUI; }
+ else if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_MEDIA_10B_2; }
+ }
+
+ /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
- printk(" media %s%s%s",
+ /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
+
+ /* FIXME: we don't set the Ethernet address on the command line. Use
+ ifconfig IFACE hw ether AABBCCDDEEFF */
+
+ printk(KERN_INFO "cs89x0 media %s%s%s",
(lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"",
(lp->adapter_cnf & A_CNF_AUI)?"AUI,":"",
(lp->adapter_cnf & A_CNF_10B_2)?"BNC,":"");
dev->irq = i;
}
- printk(" IRQ %d", dev->irq);
+ printk(", IRQ %d", dev->irq);
+#if ALLOW_DMA
+ if (lp->use_dma)
+ {
+ get_dma_channel(dev);
+ printk(", DMA %d", dev->dma);
+ }
+ else
+#endif
+ {
+ printk(", programmed I/O");
+ }
/* print the ethernet address. */
+ printk(", MAC ");
for (i = 0; i < ETH_ALEN; i++)
- printk(" %2.2x", dev->dev_addr[i]);
+ {
+ printk("%s%02x", i ? ":" : "", dev->dev_addr[i]);
+ }
/* Grab the region so we can find another board if autoIRQ fails. */
+
+ /*
+ * FIXME: we should check this, but really the isapnp stuff should have given
+ * us a free region. Sort this out when the isapnp is sorted out
+ */
request_region(ioaddr, NETCARD_IO_EXTENT,"cs89x0");
dev->open = net_open;
ether_setup(dev);
printk("\n");
+ if (net_debug)
+ printk("cs89x0_probe1() successful\n");
return 0;
+out1:
+ kfree(dev->priv);
+ dev->priv = 0;
+out:
+ return retval;
+}
+
+\f
+/*********************************
+ * This page contains DMA routines
+**********************************/
+
+#if ALLOW_DMA
+
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+
+static void
+get_dma_channel(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ if (lp->dma) {
+ dev->dma = lp->dma;
+ lp->isa_config |= ISA_RxDMA;
+ } else {
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ return;
+ dev->dma = lp->isa_config & DMA_NO_MASK;
+ if (lp->chip_type == CS8900)
+ dev->dma += 5;
+ if (dev->dma < 5 || dev->dma > 7) {
+ lp->isa_config &= ~ANY_ISA_DMA;
+ return;
+ }
+ }
+ return;
+}
+
+static void
+write_dma(struct net_device *dev, int chip_type, int dma)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ return;
+ if (chip_type == CS8900) {
+ writereg(dev, PP_CS8900_ISADMA, dma-5);
+ } else {
+ writereg(dev, PP_CS8920_ISADMA, dma);
+ }
+}
+
+static void
+set_dma_cfg(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ if (lp->use_dma)
+ {
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ {
+ if (net_debug > 3)
+ printk("set_dma_cfg(): no DMA\n");
+ return;
+ }
+ if (lp->isa_config & ISA_RxDMA)
+ {
+ lp->curr_rx_cfg |= RX_DMA_ONLY;
+ if (net_debug > 3)
+ printk("set_dma_cfg(): RX_DMA_ONLY\n");
+ }
+ else
+ {
+ lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */
+ if (net_debug > 3)
+ printk("set_dma_cfg(): AUTO_RX_DMA\n");
+ }
+ }
+}
+
+static int
+dma_bufcfg(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if (lp->use_dma)
+ return (lp->isa_config & ANY_ISA_DMA)? RX_DMA_ENBL : 0;
+ else
+ return 0;
+}
+
+static int
+dma_busctl(struct net_device *dev)
+{
+ int retval = 0;
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if (lp->use_dma)
+ {
+ if (lp->isa_config & ANY_ISA_DMA)
+ retval |= RESET_RX_DMA; /* Reset the DMA pointer */
+ if (lp->isa_config & DMA_BURST)
+ retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
+ if (lp->dmasize == 64)
+ retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
+ retval |= MEMORY_ON; /* we need memory enabled to use DMA. */
+ }
+ return retval;
+}
+
+static void
+dma_rx(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ struct sk_buff *skb;
+ int status, length;
+ unsigned char *bp = lp->rx_dma_ptr;
+
+ {
+ int i;
+ for (i = 0; i < 1000; i++)
+ ;
+ }
+
+ status = bp[0] + (bp[1]<<8);
+ length = bp[2] + (bp[3]<<8);
+ bp += 4;
+ if (net_debug > 5)
+ {
+ printk( "%s: receiving DMA packet at %lx, status %x, length %x\n",
+ dev->name, (unsigned long)bp, status, length);
+ }
+ if ((status & RX_OK) == 0) {
+ count_rx_errors(status, lp);
+ goto skip_this_frame;
+ }
+
+ /* Malloc up new buffer. */
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ if (net_debug) /* I don't think we want to do this to a stressed system */
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+
+ /* AKPM: advance bp to the next frame */
+skip_this_frame:
+ bp += (length + 3) & ~3;
+ if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+ lp->rx_dma_ptr = bp;
+ return;
+ }
+
+ skb->len = length;
+ skb->dev = dev;
+
+ if (bp + length > lp->end_dma_buff) {
+ int semi_cnt = lp->end_dma_buff - bp;
+ memcpy(skb_put(skb,semi_cnt), bp, semi_cnt);
+ memcpy(skb_put(skb,length - semi_cnt), lp->dma_buff,
+ length - semi_cnt);
+ } else {
+ memcpy(skb_put(skb,length), bp, length);
+ }
+ bp += (length + 3) & ~3;
+ if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+ lp->rx_dma_ptr = bp;
+
+ if (net_debug > 3)
+ {
+ printk( "%s: received %d byte DMA packet of type %x\n",
+ dev->name, length,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ return;
}
-void __init
-reset_chip(struct net_device *dev)
+#endif /* ALLOW_DMA */
+
+void __init reset_chip(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
outb(0, ioaddr + DATA_PORT + 1);
outw(PP_CS8920_ISAMemB, ioaddr + ADD_PORT);
- outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT);
- outb((dev->mem_start >> 24) & 0xff, ioaddr + DATA_PORT + 1);
+ outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
+ outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT + 1);
}
/* Wait until the chip is reset */
reset_start_time = jiffies;
}
+#define DETECTED_NONE 0
+#define DETECTED_RJ45H 1
+#define DETECTED_RJ45F 2
+#define DETECTED_AUI 3
+#define DETECTED_BNC 4
+
static int
detect_tp(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int timenow = jiffies;
+ int fdx;
if (net_debug > 1) printk("%s: Attempting TP\n", dev->name);
for (timenow = jiffies; jiffies - timenow < 15; )
;
if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
- return 0;
-
- if (lp->chip_type != CS8900) {
+ return DETECTED_NONE;
+
+ if (lp->chip_type == CS8900) {
+ switch (lp->force & 0xf0) {
+#if 0
+ case FORCE_AUTO:
+ printk("%s: cs8900 doesn't autonegotiate\n",dev->name);
+ return DETECTED_NONE;
+#endif
+ /* CS8900 doesn't support AUTO, change to HALF*/
+ case FORCE_AUTO:
+ lp->force &= ~FORCE_AUTO;
+ lp->force |= FORCE_HALF;
+ break;
+ case FORCE_HALF:
+ break;
+ case FORCE_FULL:
+ writereg(dev, PP_TestCTL, readreg(dev, PP_TestCTL) | FDX_8900);
+ break;
+ }
+ fdx = readreg(dev, PP_TestCTL) & FDX_8900;
+ } else {
+ switch (lp->force & 0xf0) {
+ case FORCE_AUTO:
+ lp->auto_neg_cnf = AUTO_NEG_ENABLE;
+ break;
+ case FORCE_HALF:
+ lp->auto_neg_cnf = 0;
+ break;
+ case FORCE_FULL:
+ lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
+ break;
+ }
writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
- printk("%s: negotiating duplex...\n",dev->name);
+ printk(KERN_INFO "%s: negotiating duplex...\n",dev->name);
while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
if (jiffies - timenow > 4000) {
- printk("**** Full / half duplex auto-negotiation timed out ****\n");
+ printk(KERN_ERR "**** Full / half duplex auto-negotiation timed out ****\n");
break;
}
}
}
- if (readreg(dev, PP_AutoNegST) & FDX_ACTIVE)
- printk("%s: using full duplex\n", dev->name);
- else
- printk("%s: using half duplex\n", dev->name);
+ fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
}
-
- return A_CNF_MEDIA_10B_T;
+ if (fdx)
+ return DETECTED_RJ45F;
+ else
+ return DETECTED_RJ45H;
}
/* send a test packet - return true if carrier bits are ok */
static int
send_test_pkt(struct net_device *dev)
{
- int ioaddr = dev->base_addr;
char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
0, 46, /* A 46 in network order */
0, 0, /* DSAP=0 & SSAP=0 fields */
memcpy(test_packet, dev->dev_addr, ETH_ALEN);
memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN);
- outw(TX_AFTER_ALL, ioaddr + TX_CMD_PORT);
- outw(ETH_ZLEN, ioaddr + TX_LEN_PORT);
+ writeword(dev, TX_CMD_PORT, TX_AFTER_ALL);
+ writeword(dev, TX_LEN_PORT, ETH_ZLEN);
/* Test to see if the chip has allocated memory for the packet */
while (jiffies - timenow < 5)
return 0; /* this shouldn't happen */
/* Write the contents of the packet */
- if (dev->mem_start) {
- memcpy((void *)dev->mem_start + PP_TxFrame, test_packet, ETH_ZLEN);
- } else {
- outsw(ioaddr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
- }
+ outsw(dev->base_addr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
if (net_debug > 1) printk("Sending test packet ");
/* wait a couple of jiffies for packet to be received */
writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(dev))
- return A_CNF_MEDIA_AUI;
+ return DETECTED_AUI;
else
- return 0;
+ return DETECTED_NONE;
}
static int
writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(dev))
- return A_CNF_MEDIA_10B_2;
+ return DETECTED_BNC;
else
- return 0;
+ return DETECTED_NONE;
}
\f
registers that "should" only need to be set once at boot, so that
there is non-reboot way to recover if something goes wrong.
*/
+
+/* AKPM: do we need to do any locking here? */
+
static int
net_open(struct net_device *dev)
{
if (dev->irq < 2) {
/* Allow interrupts to be generated by the chip */
+/* Cirrus' release had this: */
+#if 0
+ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+#endif
+/* And 2.3.47 had this: */
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+
for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
- if (request_irq (i, NULL, 0, "cs8920", dev) != -EBUSY) {
+ if (request_irq (i, NULL, 0, "cs89x0", dev) != -EBUSY) {
write_irq(dev, lp->chip_type, i);
writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", dev) == 0)
if (i >= CS8920_NO_INTS) {
writereg(dev, PP_BusCTL, 0); /* disable interrupts. */
+ if (net_debug)
+ printk("cs89x0: can't get an interrupt\n");
return -EAGAIN;
}
} else {
if (((1 << dev->irq) & lp->irq_map) == 0) {
- printk("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
+ printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
return -EAGAIN;
}
+/* FIXME: Cirrus' release had this: */
+ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+/* And 2.3.47 had this: */
+#if 0
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+#endif
write_irq(dev, lp->chip_type, dev->irq);
if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) {
+ if (net_debug)
+ printk("cs89x0: request_irq(%d) failed\n", dev->irq);
return -EAGAIN;
}
}
+#if ALLOW_DMA
+ if (lp->use_dma)
+ {
+ if (lp->isa_config & ANY_ISA_DMA) {
+ unsigned long flags;
+ lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
+ (lp->dmasize * 1024) / PAGE_SIZE);
+
+ if (!lp->dma_buff) {
+ printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize);
+ goto release_irq;
+ }
+ if (net_debug > 1)
+ {
+ printk( "%s: dma %lx %lx\n",
+ dev->name,
+ (unsigned long)lp->dma_buff,
+ (unsigned long)virt_to_bus(lp->dma_buff));
+ }
+ if ((unsigned long)virt_to_bus(lp->dma_buff) >= MAX_DMA_ADDRESS ||
+ !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) {
+ printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name);
+ goto release_irq;
+ }
+ memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */
+ if (request_dma(dev->dma, "cs89x0")) {
+ printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
+ goto release_irq;
+ }
+ write_dma(dev, lp->chip_type, dev->dma);
+ lp->rx_dma_ptr = lp->dma_buff;
+ lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024;
+ spin_lock_irqsave(&lp->lock, flags);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, 0x14); /* auto_init as well */
+ set_dma_addr(dev->dma, virt_to_bus(lp->dma_buff));
+ set_dma_count(dev->dma, lp->dmasize*1024);
+ enable_dma(dev->dma);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ }
+#endif /* ALLOW_DMA */
+
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
}
if (!result) {
- printk("%s: EEPROM is configured for unavailable media\n", dev->name);
+ printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
release_irq:
+#if ALLOW_DMA
+ release_dma_buff(lp);
+#endif
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
free_irq(dev->irq, dev);
return -EAGAIN;
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = detect_tp(dev);
- if (!result) printk("%s: 10Base-T (RJ-45) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_10B_T; /* Yes! I don't care if I see a link pulse */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
+ }
break;
case A_CNF_MEDIA_AUI:
result = detect_aui(dev);
- if (!result) printk("%s: 10Base-5 (AUI) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_AUI; /* Yes! I don't care if I see a carrrier */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
+ }
break;
case A_CNF_MEDIA_10B_2:
result = detect_bnc(dev);
- if (!result) printk("%s: 10Base-2 (BNC) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_10B_2; /* Yes! I don't care if I can xmit a packet */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
+ }
break;
case A_CNF_MEDIA_AUTO:
writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
if (lp->adapter_cnf & A_CNF_10B_T)
- if ((result = detect_tp(dev)) != 0)
+ if ((result = detect_tp(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_AUI)
- if ((result = detect_aui(dev)) != 0)
+ if ((result = detect_aui(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_10B_2)
- if ((result = detect_bnc(dev)) != 0)
+ if ((result = detect_bnc(dev)) != DETECTED_NONE)
break;
- printk("%s: no media detected\n", dev->name);
+ printk(KERN_ERR "%s: no media detected\n", dev->name);
goto release_irq;
}
switch(result) {
- case 0: printk("%s: no network cable attached to configured media\n", dev->name);
+ case DETECTED_NONE:
+ printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
goto release_irq;
- case A_CNF_MEDIA_10B_T: printk("%s: using 10Base-T (RJ-45)\n", dev->name);break;
- case A_CNF_MEDIA_AUI: printk("%s: using 10Base-5 (AUI)\n", dev->name);break;
- case A_CNF_MEDIA_10B_2: printk("%s: using 10Base-2 (BNC)\n", dev->name);break;
- default: printk("%s: unexpected result was %x\n", dev->name, result); goto release_irq;
+ case DETECTED_RJ45H:
+ printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
+ break;
+ case DETECTED_RJ45F:
+ printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
+ break;
+ case DETECTED_AUI:
+ printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);
+ break;
+ case DETECTED_BNC:
+ printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);
+ break;
}
/* Turn on both receive and transmit operations */
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
+
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
-
+#if ALLOW_DMA
+ set_dma_cfg(dev);
+#endif
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
- TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
+ TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
- TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
+#if ALLOW_DMA
+ dma_bufcfg(dev) |
+#endif
+ TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, ENABLE_IRQ
+ | (dev->mem_start?MEMORY_ON : 0) /* turn memory on */
+#if ALLOW_DMA
+ | dma_busctl(dev)
+#endif
);
MOD_INC_USE_COUNT;
netif_start_queue(dev);
+ if (net_debug)
+ printk("cs89x0: net_open() succeeded\n");
return 0;
}
static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- short ioaddr = dev->base_addr;
- unsigned long flags;
if (net_debug > 3)
- printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ {
+ printk("%s: sent %d byte packet of type %x\n",
+ dev->name, skb->len,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
/* keep the upload from being interrupted, since we
ask the chip to start transmitting before the
whole packet has been completely uploaded. */
- save_flags(flags);
- cli();
+ spin_lock_irq(&lp->lock);
netif_stop_queue(dev);
-
+
/* initiate a transmit sequence */
- outw(lp->send_cmd, ioaddr + TX_CMD_PORT);
- outw(skb->len, ioaddr + TX_LEN_PORT);
+ writeword(dev, TX_CMD_PORT, lp->send_cmd);
+ writeword(dev, TX_LEN_PORT, skb->len);
/* Test to see if the chip has allocated memory for the packet */
- if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
- /* Gasp! It hasn't. But that shouldn't happen since
- we're waiting for TxOk, so return 1 and requeue this packet. */
- restore_flags(flags);
+ if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0)
+ {
+ /*
+ * Gasp! It hasn't. But that shouldn't happen since
+ * we're waiting for TxOk, so return 1 and requeue this packet.
+ */
+
+ spin_unlock_irq(&lp->lock);
+ if (net_debug) printk("cs89x0: Tx buffer not free!\n");
return 1;
}
/* Write the contents of the packet */
- outsw(ioaddr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
-
- restore_flags(flags);
+ outsw(dev->base_addr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
+ spin_unlock_irq(&lp->lock);
dev->trans_start = jiffies;
dev_kfree_skb (skb);
- netif_wake_queue(dev);
-
+
+ /*
+ * We DO NOT call netif_wake_queue() here.
+ * We also DO NOT call netif_start_queue().
+ *
+ * Either of these would cause another bottom half run through
+ * net_send_packet() before this packet has fully gone out. That causes
+ * us to hit the "Gasp!" above and the send is rescheduled. it runs like
+ * a dog. We just return and wait for the Tx completion interrupt handler
+ * to restart the netdevice layer
+ */
+
return 0;
}
\f
struct net_device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
-
+
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
case ISQ_TRANSMITTER_EVENT:
lp->stats.tx_packets++;
netif_wake_queue(dev); /* Inform upper layers. */
- if ((status & TX_OK) == 0) lp->stats.tx_errors++;
- if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
- if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
- if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
- if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+ if ((status & ( TX_OK |
+ TX_LOST_CRS |
+ TX_SQE_ERROR |
+ TX_LATE_COL |
+ TX_16_COL)) != TX_OK)
+ {
+ if ((status & TX_OK) == 0) lp->stats.tx_errors++;
+ if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
+ if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
+ if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
+ if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+ }
break;
case ISQ_BUFFER_EVENT:
if (status & READY_FOR_TX) {
event of a tx underrun */
netif_wake_queue(dev); /* Inform upper layers. */
}
+#if ALLOW_DMA
+ if (lp->use_dma && (status & RX_DMA)) {
+ int count = readreg(dev, PP_DmaFrameCnt);
+ while(count) {
+ if (net_debug > 5)
+ printk("%s: receiving %d DMA frames\n", dev->name, count);
+ if (net_debug > 2 && count >1)
+ printk("%s: receiving %d DMA frames\n", dev->name, count);
+ dma_rx(dev);
+ if (--count == 0)
+ count = readreg(dev, PP_DmaFrameCnt);
+ if (net_debug > 2 && count > 0)
+ printk("%s: continuing with %d DMA frames\n", dev->name, count);
+ }
+ }
+#endif
break;
case ISQ_RX_MISS_EVENT:
lp->stats.rx_missed_errors += (status >>6);
}
}
+static void
+count_rx_errors(int status, struct net_local *lp)
+{
+ lp->stats.rx_errors++;
+ if (status & RX_RUNT) lp->stats.rx_length_errors++;
+ if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
+ if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
+ /* per str 172 */
+ lp->stats.rx_crc_errors++;
+ if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+ return;
+}
+
/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
struct sk_buff *skb;
int status, length;
+ int ioaddr = dev->base_addr;
status = inw(ioaddr + RX_FRAME_PORT);
length = inw(ioaddr + RX_FRAME_PORT);
+
if ((status & RX_OK) == 0) {
- lp->stats.rx_errors++;
- if (status & RX_RUNT) lp->stats.rx_length_errors++;
- if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
- if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
- /* per str 172 */
- lp->stats.rx_crc_errors++;
- if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+ count_rx_errors(status, lp);
return;
}
/* Malloc up new buffer. */
skb = alloc_skb(length, GFP_ATOMIC);
if (skb == NULL) {
- printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+#if 0 /* Again, this seems a cruel thing to do */
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+#endif
lp->stats.rx_dropped++;
return;
}
skb->len = length;
skb->dev = dev;
- insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
+ insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
if (length & 1)
skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT);
- if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
- dev->name, length,
- (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ if (net_debug > 3)
+ {
+ printk( "%s: received %d byte packet of type %x\n",
+ dev->name, length,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
return;
}
+#if ALLOW_DMA
+static void release_dma_buff(struct net_local *lp)
+{
+ if (lp->dma_buff)
+ {
+ free_pages((unsigned long)(lp->dma_buff), (lp->dmasize * 1024) / PAGE_SIZE);
+ lp->dma_buff = 0;
+ }
+}
+#endif
+
/* The inverse routine to net_open(). */
static int
net_close(struct net_device *dev)
{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
netif_stop_queue(dev);
writereg(dev, PP_RxCFG, 0);
free_irq(dev->irq, dev);
- /* Update the statistics here. */
+#if ALLOW_DMA
+ if (lp->use_dma && lp->dma) {
+ free_dma(dev->dma);
+ release_dma_buff(lp);
+ }
+#endif
+ /* Update the statistics here. */
MOD_DEC_USE_COUNT;
return 0;
-
}
/* Get the current statistics. This may be called with the card open or
net_get_stats(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned long flags;
- cli();
+ spin_lock_irqsave(&lp->lock, flags);
/* Update the statistics from the device registers. */
lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
- sti();
+ spin_unlock_irqrestore(&lp->lock, flags);
return &lp->stats;
}
static void set_multicast_list(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned long flags;
+ spin_lock_irqsave(&lp->lock, flags);
if(dev->flags&IFF_PROMISC)
{
lp->rx_mode = RX_ALL_ACCEPT;
/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
(lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
+ spin_unlock_irqrestore(&lp->lock, flags);
}
static int set_mac_address(struct net_device *dev, void *addr)
{
int i;
+
if (netif_running(dev))
return -EBUSY;
- printk("%s: Setting MAC address to ", dev->name);
- for (i = 0; i < 6; i++)
- printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
- printk(".\n");
+ if (net_debug)
+ {
+ printk("%s: Setting MAC address to ", dev->name);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+ printk(".\n");
+ }
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
0, 0,
0, 0, 0, NULL, NULL };
+/*
+ * Support the 'debug' module parm even if we're compiled for non-debug to
+ * avoid breaking someone's startup scripts
+ */
+
static int io=0;
static int irq=0;
static int debug=0;
static char media[8];
static int duplex=-1;
+static int use_dma = 0; /* These generate unused var warnings if ALLOW_DMA = 0 */
+static int dma=0;
+static int dmasize=16; /* or 64 */
+
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(media, "s");
MODULE_PARM(duplex, "i");
+MODULE_PARM(dma , "i");
+MODULE_PARM(dmasize , "i");
+MODULE_PARM(use_dma , "i");
+
+MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>");
EXPORT_NO_SYMBOLS;
{
struct net_local *lp;
+#if DEBUGGING
net_debug = debug;
+#endif
dev_cs89x0.name = namespace;
dev_cs89x0.irq = irq;
dev_cs89x0.base_addr = io;
+
dev_cs89x0.init = cs89x0_probe;
dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev_cs89x0.priv == 0)
+ {
+ printk(KERN_ERR "cs89x0.c: Out of memory.\n");
+ return -ENOMEM;
+ }
memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
lp = (struct net_local *)dev_cs89x0.priv;
+#if ALLOW_DMA
+ if (use_dma)
+ {
+ lp->use_dma = use_dma;
+ lp->dma = dma;
+ lp->dmasize = dmasize;
+ }
+#endif
+
+ spin_lock_init(&lp->lock);
+
/* boy, they'd better get these right */
if (!strcmp(media, "rj45"))
lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
lp->auto_neg_cnf = AUTO_NEG_ENABLE;
if (io == 0) {
- printk(KERN_NOTICE "cs89x0.c: Module autoprobing not allowed.\n");
- printk(KERN_NOTICE "cs89x0.c: Append io=0xNNN\n");
+ printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
+ printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
return -EPERM;
}
+
+#if ALLOW_DMA
+ if (use_dma && dmasize != 16 && dmasize != 64) {
+ printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
+ return -EPERM;
+ }
+#endif
+
if (register_netdev(&dev_cs89x0) != 0) {
- printk(KERN_WARNING "cs89x0.c: No card found at 0x%x\n", io);
+ printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
return -ENXIO;
}
return 0;
void
cleanup_module(void)
{
-
-#endif
-#ifdef MODULE
- outw(0, dev_cs89x0.base_addr + ADD_PORT);
-#endif
-#ifdef MODULE
-
+ outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);
if (dev_cs89x0.priv != NULL) {
/* Free up the private structure, or leak memory :-) */
unregister_netdev(&dev_cs89x0);
#define ENDEC_LOOPBACK 0x0200
#define AUI_LOOPBACK 0x0400
#define BACKOFF_OFF 0x0800
+#define FDX_8900 0x4000
#define FAST_TEST 0x8000
/* PP_RxEvent - Receive Event Bit definition - Read-only */
+++ /dev/null
-/*
- * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
- * Appletalk-IP to IP Decapsulation driver for Linux
- *
- * Authors:
- * - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
- * - DDP-IP Decap by: Jay Schulist <jschlst@turbolinux.com>
- *
- * Derived from:
- * - Almost all code already existed in net/appletalk/ddp.c I just
- * moved/reorginized it into a driver file. Original IP-over-DDP code
- * was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
- * - skeleton.c: A network driver outline for linux.
- * Written 1993-94 by Donald Becker.
- * - dummy.c: A dummy net driver. By Nick Holloway.
- * - MacGate: A user space Daemon for Appletalk-IP Decap for
- * Linux by Jay Schulist <jschlst@turbolinux.com>
- *
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.
- *
- * This software may be used and distributed according to the terms
- * of the GNU Public License, incorporated herein by reference.
- */
-
-static const char *version =
- "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
-
-#include <linux/config.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#endif
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/atalk.h>
-#include <linux/ip.h>
-#include <net/route.h>
-#include <linux/inet.h>
-
-#include "ipddp.h" /* Our stuff */
-
-static struct ipddp_route *ipddp_route_list = NULL;
-
-#ifdef CONFIG_IPDDP_ENCAP
-static int ipddp_mode = IPDDP_ENCAP;
-#else
-static int ipddp_mode = IPDDP_DECAP;
-#endif
-
-/* Use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
-#ifndef IPDDP_DEBUG
-#define IPDDP_DEBUG 1
-#endif
-static unsigned int ipddp_debug = IPDDP_DEBUG;
-
-/* Index to functions, as function prototypes. */
-static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev);
-static struct net_device_stats *ipddp_get_stats(struct net_device *dev);
-static int ipddp_create(struct ipddp_route *new_rt);
-static int ipddp_delete(struct ipddp_route *rt);
-static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt);
-static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-
-
-static int ipddp_open(struct net_device *dev)
-{
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
-
- return 0;
-}
-
-static int ipddp_close(struct net_device *dev)
-{
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
-
- return 0;
-}
-
-int ipddp_init(struct net_device *dev)
-{
- static unsigned version_printed = 0;
-
- if (ipddp_debug && version_printed++ == 0)
- printk("%s", version);
-
- /* Let the user now what mode we are in */
- if(ipddp_mode == IPDDP_ENCAP)
- printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n",
- dev->name);
- if(ipddp_mode == IPDDP_DECAP)
- printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@turbolinux.com>\n",
- dev->name);
-
- /* Fill in the device structure with ethernet-generic values. */
- ether_setup(dev);
-
- /* Initalize the device structure. */
- dev->hard_start_xmit = ipddp_xmit;
-
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
- if(!dev->priv)
- return -ENOMEM;
- memset(dev->priv,0,sizeof(struct enet_statistics));
-
- dev->open = ipddp_open;
- dev->stop = ipddp_close;
- dev->get_stats = ipddp_get_stats;
- dev->do_ioctl = ipddp_ioctl;
-
- dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */
- dev->mtu = 585;
- dev->flags |= IFF_NOARP;
-
- /*
- * The worst case header we will need is currently a
- * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
- * We send over SNAP so that takes another 8 bytes.
- */
- dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
-
- return 0;
-}
-
-/*
- * Get the current statistics. This may be called with the card open or closed.
- */
-static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
-{
- return (struct net_device_stats *)dev->priv;
-}
-
-/*
- * Transmit LLAP/ELAP frame using aarp_send_ddp.
- */
-static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- u32 paddr = ((struct rtable*)skb->dst)->rt_gateway;
- struct ddpehdr *ddp;
- struct ipddp_route *rt;
- struct at_addr *our_addr;
-
- /*
- * Find appropriate route to use, based only on IP number.
- */
- for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
- {
- if(rt->ip == paddr)
- break;
- }
- if(rt == NULL)
- return 0;
-
- our_addr = atalk_find_dev_addr(rt->dev);
-
- if(ipddp_mode == IPDDP_DECAP)
- /*
- * Pull off the excess room that should not be there.
- * This is due to a hard-header problem. This is the
- * quick fix for now though, till it breaks.
- */
- skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
-
- /* Create the Extended DDP header */
- ddp = (struct ddpehdr *)skb->data;
- ddp->deh_len = skb->len;
- ddp->deh_hops = 1;
- ddp->deh_pad = 0;
- ddp->deh_sum = 0;
-
- /*
- * For Localtalk we need aarp_send_ddp to strip the
- * long DDP header and place a shot DDP header on it.
- */
- if(rt->dev->type == ARPHRD_LOCALTLK)
- {
- ddp->deh_dnet = 0; /* FIXME more hops?? */
- ddp->deh_snet = 0;
- }
- else
- {
- ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */
- ddp->deh_snet = our_addr->s_net;
- }
- ddp->deh_dnode = rt->at.s_node;
- ddp->deh_snode = our_addr->s_node;
- ddp->deh_dport = 72;
- ddp->deh_sport = 72;
-
- *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */
- *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */
-
- skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
-
- ((struct net_device_stats *) dev->priv)->tx_packets++;
- ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
-
- if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Create a routing entry. We first verify that the
- * record does not already exist. If it does we return -EEXIST
- */
-static int ipddp_create(struct ipddp_route *new_rt)
-{
- struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL);
- struct ipddp_route *test;
-
- if(rt == NULL)
- return -ENOMEM;
-
- rt->ip = new_rt->ip;
- rt->at = new_rt->at;
- rt->next = NULL;
- rt->dev = atrtr_get_dev(&rt->at);
- if(rt->dev == NULL)
- return (-ENETUNREACH);
-
- test = ipddp_find_route(rt);
- if(test != NULL)
- return (-EEXIST);
-
- rt->next = ipddp_route_list;
- ipddp_route_list = rt;
-
- return 0;
-}
-
-/*
- * Delete a route, we only delete a FULL match.
- * If route does not exist we return -ENOENT.
- */
-static int ipddp_delete(struct ipddp_route *rt)
-{
- struct ipddp_route **r = &ipddp_route_list;
- struct ipddp_route *tmp;
-
- while((tmp = *r) != NULL)
- {
- if(tmp->ip == rt->ip
- && tmp->at.s_net == rt->at.s_net
- && tmp->at.s_node == rt->at.s_node)
- {
- *r = tmp->next;
- kfree_s(tmp, sizeof(struct ipddp_route));
- return 0;
- }
- r = &tmp->next;
- }
-
- return (-ENOENT);
-}
-
-/*
- * Find a routing entry, we only return a FULL match
- */
-static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt)
-{
- struct ipddp_route *f;
-
- for(f = ipddp_route_list; f != NULL; f = f->next)
- {
- if(f->ip == rt->ip
- && f->at.s_net == rt->at.s_net
- && f->at.s_node == rt->at.s_node)
- return (f);
- }
-
- return (NULL);
-}
-
-static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data;
-
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- switch(cmd)
- {
- case SIOCADDIPDDPRT:
- return (ipddp_create(rt));
-
- case SIOCFINDIPDDPRT:
- if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route)))
- return -EFAULT;
- return 0;
-
- case SIOCDELIPDDPRT:
- return (ipddp_delete(rt));
-
- default:
- return -EINVAL;
- }
-}
-
-#ifdef MODULE /* Module specific functions for ipddp.c */
-
-static struct net_device dev_ipddp=
-{
- "ipddp0\0 ",
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, ipddp_init
-};
-
-MODULE_PARM(ipddp_mode, "i");
-
-int init_module(void)
-{
- int err;
-
- err=dev_alloc_name(&dev_ipddp, "ipddp%d");
- if(err < 0)
- return err;
-
- if(register_netdev(&dev_ipddp) != 0)
- return -EIO;
-
- return 0;
-}
-
-void cleanup_module(void)
-{
- unregister_netdev(&dev_ipddp);
- kfree(dev_ipddp.priv);
- dev_ipddp.priv = NULL;
-}
-
-#endif /* MODULE */
+++ /dev/null
-/*
- * ipddp.h: Header for IP-over-DDP driver for Linux.
- */
-
-#ifndef __LINUX_IPDDP_H
-#define __LINUX_IPDDP_H
-
-#ifdef __KERNEL__
-
-#define SIOCADDIPDDPRT (SIOCDEVPRIVATE)
-#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1)
-#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2)
-
-struct ipddp_route
-{
- struct net_device *dev; /* Carrier device */
- __u32 ip; /* IP address */
- struct at_addr at; /* Gateway appletalk address */
- int flags;
- struct ipddp_route *next;
-};
-
-#define IPDDP_ENCAP 1
-#define IPDDP_DECAP 2
-
-#endif /* __KERNEL__ */
-#endif /* __LINUX_IPDDP_H */
+++ /dev/null
-/*** ltpc.c -- a driver for the LocalTalk PC card.
- *
- * Copyright (c) 1995,1996 Bradford W. Johnson <johns393@maroon.tc.umn.edu>
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * This is ALPHA code at best. It may not work for you. It may
- * damage your equipment. It may damage your relations with other
- * users of your network. Use it at your own risk!
- *
- * Based in part on:
- * skeleton.c by Donald Becker
- * dummy.c by Nick Holloway and Alan Cox
- * loopback.c by Ross Biro, Fred van Kampen, Donald Becker
- * the netatalk source code (UMICH)
- * lots of work on the card...
- *
- * I do not have access to the (proprietary) SDK that goes with the card.
- * If you do, I don't want to know about it, and you can probably write
- * a better driver yourself anyway. This does mean that the pieces that
- * talk to the card are guesswork on my part, so use at your own risk!
- *
- * This is my first try at writing Linux networking code, and is also
- * guesswork. Again, use at your own risk! (Although on this part, I'd
- * welcome suggestions)
- *
- * This is a loadable kernel module which seems to work at my site
- * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with
- * the kernel support from 1.3.3b2 including patches routing.patch
- * and ddp.disappears.from.chooser. In order to run it, you will need
- * to patch ddp.c and aarp.c in the kernel, but only a little...
- *
- * I'm fairly confident that while this is arguably badly written, the
- * problems that people experience will be "higher level", that is, with
- * complications in the netatalk code. The driver itself doesn't do
- * anything terribly complicated -- it pretends to be an ether device
- * as far as netatalk is concerned, strips the DDP data out of the ether
- * frame and builds a LLAP packet to send out the card. In the other
- * direction, it receives LLAP frames from the card and builds a fake
- * ether packet that it then tosses up to the networking code. You can
- * argue (correctly) that this is an ugly way to do things, but it
- * requires a minimal amount of fooling with the code in ddp.c and aarp.c.
- *
- * The card will do a lot more than is used here -- I *think* it has the
- * layers up through ATP. Even if you knew how that part works (which I
- * don't) it would be a big job to carve up the kernel ddp code to insert
- * things at a higher level, and probably a bad idea...
- *
- * There are a number of other cards that do LocalTalk on the PC. If
- * nobody finds any insurmountable (at the netatalk level) problems
- * here, this driver should encourage people to put some work into the
- * other cards (some of which I gather are still commercially available)
- * and also to put hooks for LocalTalk into the official ddp code.
- *
- * I welcome comments and suggestions. This is my first try at Linux
- * networking stuff, and there are probably lots of things that I did
- * suboptimally.
- *
- ***/
-
-/***
- *
- * $Log: ltpc.c,v $
- * Revision 1.8 1997/01/28 05:44:54 bradford
- * Clean up for non-module a little.
- * Hacked about a bit to clean things up - Alan Cox
- * Probably broken it from the origina 1.8
- *
-
- * 1998/11/09: David Huggins-Daines <dhd@debian.org>
- * Cleaned up the initialization code to use the standard autoirq methods,
- and to probe for things in the standard order of i/o, irq, dma. This
- removes the "reset the reset" hack, because I couldn't figure out an
- easy way to get the card to trigger an interrupt after it.
- * Added support for passing configuration parameters on the kernel command
- line and through insmod
- * Changed the device name from "ltalk0" to "lt0", both to conform with the
- other localtalk driver, and to clear up the inconsistency between the
- module and the non-module versions of the driver :-)
- * Added a bunch of comments (I was going to make some enums for the state
- codes and the register offsets, but I'm still not sure exactly what their
- semantics are)
- * Don't poll anymore in interrupt-driven mode
- * It seems to work as a module now (as of 2.1.127), but I don't think
- I'm responsible for that...
-
- *
- * Revision 1.7 1996/12/12 03:42:33 bradford
- * DMA alloc cribbed from 3c505.c.
- *
- * Revision 1.6 1996/12/12 03:18:58 bradford
- * Added virt_to_bus; works in 2.1.13.
- *
- * Revision 1.5 1996/12/12 03:13:22 root
- * xmitQel initialization -- think through better though.
- *
- * Revision 1.4 1996/06/18 14:55:55 root
- * Change names to ltpc. Tabs. Took a shot at dma alloc,
- * although more needs to be done eventually.
- *
- * Revision 1.3 1996/05/22 14:59:39 root
- * Change dev->open, dev->close to track dummy.c in 1.99.(around 7)
- *
- * Revision 1.2 1996/05/22 14:58:24 root
- * Change tabs mostly.
- *
- * Revision 1.1 1996/04/23 04:45:09 root
- * Initial revision
- *
- * Revision 0.16 1996/03/05 15:59:56 root
- * Change ARPHRD_LOCALTLK definition to the "real" one.
- *
- * Revision 0.15 1996/03/05 06:28:30 root
- * Changes for kernel 1.3.70. Still need a few patches to kernel, but
- * it's getting closer.
- *
- * Revision 0.14 1996/02/25 17:38:32 root
- * More cleanups. Removed query to card on get_stats.
- *
- * Revision 0.13 1996/02/21 16:27:40 root
- * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65.
- * Clean up receive code a little.
- *
- * Revision 0.12 1996/02/19 16:34:53 root
- * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup
- * range. Change debug to mask: 1 for verbose, 2 for higher level stuff
- * including packet printing, 4 for lower level (card i/o) stuff.
- *
- * Revision 0.11 1996/02/12 15:53:38 root
- * Added router sends (requires new aarp.c patch)
- *
- * Revision 0.10 1996/02/11 00:19:35 root
- * Change source LTALK_LOGGING debug switch to insmod ... debug=2.
- *
- * Revision 0.9 1996/02/10 23:59:35 root
- * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk
- * has a *different* definition of struct sockaddr_at than the Linux kernel
- * does. This is an "insidious and invidious" bug...
- * (Actually the preceding comment is false -- it's the atalk.h in the
- * ancient atalk-0.06 that's the problem)
- *
- * Revision 0.8 1996/02/10 19:09:00 root
- * Merge 1.3 changes. Tested OK under 1.3.60.
- *
- * Revision 0.7 1996/02/10 17:56:56 root
- * Added debug=1 parameter on insmod for debugging prints. Tried
- * to fix timer unload on rmmod, but I don't think that's the problem.
- *
- * Revision 0.6 1995/12/31 19:01:09 root
- * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!)
- * Clean up initial probing -- sometimes the card wakes up latched in reset.
- *
- * Revision 0.5 1995/12/22 06:03:44 root
- * Added comments in front and cleaned up a bit.
- * This version sent out to people.
- *
- * Revision 0.4 1995/12/18 03:46:44 root
- * Return shortDDP to longDDP fake to 0/0. Added command structs.
- *
- ***/
-
-/* ltpc jumpers are:
-*
-* Interrupts -- set at most one. If none are set, the driver uses
-* polled mode. Because the card was developed in the XT era, the
-* original documentation refers to IRQ2. Since you'll be running
-* this on an AT (or later) class machine, that really means IRQ9.
-*
-* SW1 IRQ 4
-* SW2 IRQ 3
-* SW3 IRQ 9 (2 in original card documentation only applies to XT)
-*
-*
-* DMA -- choose DMA 1 or 3, and set both corresponding switches.
-*
-* SW4 DMA 3
-* SW5 DMA 1
-* SW6 DMA 3
-* SW7 DMA 1
-*
-*
-* I/O address -- choose one.
-*
-* SW8 220 / 240
-*/
-
-/* To have some stuff logged, do
-* insmod ltpc.o debug=1
-*
-* For a whole bunch of stuff, use higher numbers.
-*
-* The default is 0, i.e. no messages except for the probe results.
-*/
-
-/* insmod-tweakable variables */
-static int debug=0;
-#define DEBUG_VERBOSE 1
-#define DEBUG_UPPER 2
-#define DEBUG_LOWER 4
-
-static int io=0;
-static int irq=0;
-static int dma=0;
-
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#endif
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#include <linux/if_arp.h>
-#include <linux/if_ltalk.h>
-
-#include <linux/delay.h>
-#include <linux/timer.h>
-
-#include <linux/atalk.h>
-
-/* our stuff */
-#include "ltpc.h"
-
-/* function prototypes */
-static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
- void *dbuf, int dbuflen);
-static int sendup_buffer (struct net_device *dev);
-
-/* Dma Memory related stuff, cribbed directly from 3c505.c */
-
-static unsigned long dma_mem_alloc(int size)
-{
- int order = get_order(size);
-
- return __get_dma_pages(GFP_KERNEL, order);
-}
-
-/* DMA data buffer, DMA command buffer */
-static unsigned char *ltdmabuf;
-static unsigned char *ltdmacbuf;
-
-/* private struct, holds our appletalk address */
-
-struct ltpc_private
-{
- struct net_device_stats stats;
- struct at_addr my_addr;
-};
-
-/* transmit queue element struct */
-
-struct xmitQel {
- struct xmitQel *next;
- /* command buffer */
- unsigned char *cbuf;
- short cbuflen;
- /* data buffer */
- unsigned char *dbuf;
- short dbuflen;
- unsigned char QWrite; /* read or write data */
- unsigned char mailbox;
-};
-
-/* the transmit queue itself */
-
-static struct xmitQel *xmQhd=NULL,*xmQtl=NULL;
-
-static void enQ(struct xmitQel *qel)
-{
- unsigned long flags;
- qel->next = NULL;
- save_flags(flags);
- cli();
- if (xmQtl) {
- xmQtl->next = qel;
- } else {
- xmQhd = qel;
- }
- xmQtl = qel;
- restore_flags(flags);
-
- if (debug&DEBUG_LOWER)
- printk("enqueued a 0x%02x command\n",qel->cbuf[0]);
-}
-
-static struct xmitQel *deQ(void)
-{
- unsigned long flags;
- int i;
- struct xmitQel *qel=NULL;
- save_flags(flags);
- cli();
- if (xmQhd) {
- qel = xmQhd;
- xmQhd = qel->next;
- if(!xmQhd) xmQtl = NULL;
- }
- restore_flags(flags);
-
- if ((debug&DEBUG_LOWER) && qel) {
- int n;
- printk("ltpc: dequeued command ");
- n = qel->cbuflen;
- if (n>100) n=100;
- for(i=0;i<n;i++) printk("%02x ",qel->cbuf[i]);
- printk("\n");
- }
-
- return qel;
-}
-
-/* and... the queue elements we'll be using */
-static struct xmitQel qels[16];
-
-/* and their corresponding mailboxes */
-static unsigned char mailbox[16];
-static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
-static int wait_timeout(struct net_device *dev, int c)
-{
- /* returns true if it stayed c */
- /* this uses base+6, but it's ok */
- int i;
- int timeout;
-
- /* twenty second or so total */
-
- for(i=0;i<20000;i++) {
- if ( c != inb_p(dev->base_addr+6) ) return 0;
- for(timeout=loops_per_sec/1000; timeout > 0; timeout--) ;
- }
- return 1; /* timed out */
-}
-
-/* get the first free mailbox */
-
-static int getmbox(void)
-{
- unsigned long flags;
- int i;
-
- save_flags(flags);
- cli();
- for(i=1;i<16;i++) if(!mboxinuse[i]) {
- mboxinuse[i]=1;
- restore_flags(flags);
- return i;
- }
- restore_flags(flags);
- return 0;
-}
-
-/* read a command from the card */
-static void handlefc(struct net_device *dev)
-{
- /* called *only* from idle, non-reentrant */
- int dma = dev->dma;
- int base = dev->base_addr;
- unsigned long flags;
-
-
- flags=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_READ);
- set_dma_addr(dma,virt_to_bus(ltdmacbuf));
- set_dma_count(dma,50);
- enable_dma(dma);
- release_dma_lock(flags);
-
- inb_p(base+3);
- inb_p(base+2);
-
- if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n");
-}
-
-/* read data from the card */
-static void handlefd(struct net_device *dev)
-{
- int dma = dev->dma;
- int base = dev->base_addr;
- unsigned long flags;
-
- flags=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_READ);
- set_dma_addr(dma,virt_to_bus(ltdmabuf));
- set_dma_count(dma,800);
- enable_dma(dma);
- release_dma_lock(flags);
-
- inb_p(base+3);
- inb_p(base+2);
-
- if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n");
- sendup_buffer(dev);
-}
-
-static void handlewrite(struct net_device *dev)
-{
- /* called *only* from idle, non-reentrant */
- /* on entry, 0xfb and ltdmabuf holds data */
- int dma = dev->dma;
- int base = dev->base_addr;
- unsigned long flags;
-
- flags=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_WRITE);
- set_dma_addr(dma,virt_to_bus(ltdmabuf));
- set_dma_count(dma,800);
- enable_dma(dma);
- release_dma_lock(flags);
-
- inb_p(base+3);
- inb_p(base+2);
-
- if ( wait_timeout(dev,0xfb) ) {
- flags=claim_dma_lock();
- printk("timed out in handlewrite, dma res %d\n",
- get_dma_residue(dev->dma) );
- release_dma_lock(flags);
- }
-}
-
-static void handleread(struct net_device *dev)
-{
- /* on entry, 0xfb */
- /* on exit, ltdmabuf holds data */
- int dma = dev->dma;
- int base = dev->base_addr;
- unsigned long flags;
-
-
- flags=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_READ);
- set_dma_addr(dma,virt_to_bus(ltdmabuf));
- set_dma_count(dma,800);
- enable_dma(dma);
- release_dma_lock(flags);
-
- inb_p(base+3);
- inb_p(base+2);
- if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n");
-}
-
-static void handlecommand(struct net_device *dev)
-{
- /* on entry, 0xfa and ltdmacbuf holds command */
- int dma = dev->dma;
- int base = dev->base_addr;
- unsigned long flags;
-
- flags=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_WRITE);
- set_dma_addr(dma,virt_to_bus(ltdmacbuf));
- set_dma_count(dma,50);
- enable_dma(dma);
- release_dma_lock(flags);
- inb_p(base+3);
- inb_p(base+2);
- if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n");
-}
-
-/* ready made command for getting the result from the card */
-static unsigned char rescbuf[2] = {LT_GETRESULT,0};
-static unsigned char resdbuf[2];
-
-static int QInIdle=0;
-
-/* idle expects to be called with the IRQ line high -- either because of
- * an interrupt, or because the line is tri-stated
- */
-
-static void idle(struct net_device *dev)
-{
- unsigned long flags;
- int state;
- /* FIXME This is initialized to shut the warning up, but I need to
- * think this through again.
- */
- struct xmitQel *q=0;
- int oops;
- int i;
- int base = dev->base_addr;
-
- save_flags(flags);
- cli();
- if(QInIdle) {
- restore_flags(flags);
- return;
- }
- QInIdle = 1;
-
-
- restore_flags(flags);
-
- /* this tri-states the IRQ line */
- (void) inb_p(base+6);
-
- oops = 100;
-
-loop:
- if (0>oops--) {
- printk("idle: looped too many times\n");
- goto done;
- }
-
- state = inb_p(base+6);
- if (state != inb_p(base+6)) goto loop;
-
- switch(state) {
- case 0xfc:
- /* incoming command */
- if (debug&DEBUG_LOWER) printk("idle: fc\n");
- handlefc(dev);
- break;
- case 0xfd:
- /* incoming data */
- if(debug&DEBUG_LOWER) printk("idle: fd\n");
- handlefd(dev);
- break;
- case 0xf9:
- /* result ready */
- if (debug&DEBUG_LOWER) printk("idle: f9\n");
- if(!mboxinuse[0]) {
- mboxinuse[0] = 1;
- qels[0].cbuf = rescbuf;
- qels[0].cbuflen = 2;
- qels[0].dbuf = resdbuf;
- qels[0].dbuflen = 2;
- qels[0].QWrite = 0;
- qels[0].mailbox = 0;
- enQ(&qels[0]);
- }
- inb_p(dev->base_addr+1);
- inb_p(dev->base_addr+0);
- if( wait_timeout(dev,0xf9) )
- printk("timed out idle f9\n");
- break;
- case 0xf8:
- /* ?? */
- if (xmQhd) {
- inb_p(dev->base_addr+1);
- inb_p(dev->base_addr+0);
- if(wait_timeout(dev,0xf8) )
- printk("timed out idle f8\n");
- } else {
- goto done;
- }
- break;
- case 0xfa:
- /* waiting for command */
- if(debug&DEBUG_LOWER) printk("idle: fa\n");
- if (xmQhd) {
- q=deQ();
- memcpy(ltdmacbuf,q->cbuf,q->cbuflen);
- ltdmacbuf[1] = q->mailbox;
- if (debug>1) {
- int n;
- printk("ltpc: sent command ");
- n = q->cbuflen;
- if (n>100) n=100;
- for(i=0;i<n;i++)
- printk("%02x ",ltdmacbuf[i]);
- printk("\n");
- }
- handlecommand(dev);
- if(0xfa==inb_p(base+6)) {
- /* we timed out, so return */
- goto done;
- }
- } else {
- /* we don't seem to have a command */
- if (!mboxinuse[0]) {
- mboxinuse[0] = 1;
- qels[0].cbuf = rescbuf;
- qels[0].cbuflen = 2;
- qels[0].dbuf = resdbuf;
- qels[0].dbuflen = 2;
- qels[0].QWrite = 0;
- qels[0].mailbox = 0;
- enQ(&qels[0]);
- } else {
- printk("trouble: response command already queued\n");
- goto done;
- }
- }
- break;
- case 0Xfb:
- /* data transfer ready */
- if(debug&DEBUG_LOWER) printk("idle: fb\n");
- if(q->QWrite) {
- memcpy(ltdmabuf,q->dbuf,q->dbuflen);
- handlewrite(dev);
- } else {
- handleread(dev);
- /* non-zero mailbox numbers are for
- commmands, 0 is for GETRESULT
- requests */
- if(q->mailbox) {
- memcpy(q->dbuf,ltdmabuf,q->dbuflen);
- } else {
- /* this was a result */
- mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1];
- mboxinuse[0]=0;
- }
- }
- break;
- }
- goto loop;
-
-done:
- QInIdle=0;
-
- /* now set the interrupts back as appropriate */
- /* the first read takes it out of tri-state (but still high) */
- /* the second resets it */
- /* note that after this point, any read of base+6 will
- trigger an interrupt */
-
- if (dev->irq) {
- inb_p(base+7);
- inb_p(base+7);
- }
- return;
-}
-
-
-static int do_write(struct net_device *dev, void *cbuf, int cbuflen,
- void *dbuf, int dbuflen)
-{
-
- int i = getmbox();
- int ret;
-
- if(i) {
- qels[i].cbuf = (unsigned char *) cbuf;
- qels[i].cbuflen = cbuflen;
- qels[i].dbuf = (unsigned char *) dbuf;
- qels[i].dbuflen = dbuflen;
- qels[i].QWrite = 1;
- qels[i].mailbox = i; /* this should be initted rather */
- enQ(&qels[i]);
- idle(dev);
- ret = mailbox[i];
- mboxinuse[i]=0;
- return ret;
- }
- printk("ltpc: could not allocate mbox\n");
- return -1;
-}
-
-static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
- void *dbuf, int dbuflen)
-{
-
- int i = getmbox();
- int ret;
-
- if(i) {
- qels[i].cbuf = (unsigned char *) cbuf;
- qels[i].cbuflen = cbuflen;
- qels[i].dbuf = (unsigned char *) dbuf;
- qels[i].dbuflen = dbuflen;
- qels[i].QWrite = 0;
- qels[i].mailbox = i; /* this should be initted rather */
- enQ(&qels[i]);
- idle(dev);
- ret = mailbox[i];
- mboxinuse[i]=0;
- return ret;
- }
- printk("ltpc: could not allocate mbox\n");
- return -1;
-}
-
-/* end of idle handlers -- what should be seen is do_read, do_write */
-
-static struct timer_list ltpc_timer;
-
-static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev);
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev);
-
-static int ltpc_open(struct net_device *dev)
-{
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
- return 0;
-}
-
-static int ltpc_close(struct net_device *dev)
-{
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
- return 0;
-}
-
-static int read_30 ( struct net_device *dev)
-{
- lt_command c;
- c.getflags.command = LT_GETFLAGS;
- return do_read(dev, &c, sizeof(c.getflags),&c,0);
-}
-
-static int set_30 (struct net_device *dev,int x)
-{
- lt_command c;
- c.setflags.command = LT_SETFLAGS;
- c.setflags.flags = x;
- return do_write(dev, &c, sizeof(c.setflags),&c,0);
-}
-
-/* LLAP to DDP translation */
-
-static int sendup_buffer (struct net_device *dev)
-{
- /* on entry, command is in ltdmacbuf, data in ltdmabuf */
- /* called from idle, non-reentrant */
-
- int dnode, snode, llaptype, len;
- int sklen;
- struct sk_buff *skb;
- struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
- struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
-
- if (ltc->command != LT_RCVLAP) {
- printk("unknown command 0x%02x from ltpc card\n",ltc->command);
- return(-1);
- }
- dnode = ltc->dnode;
- snode = ltc->snode;
- llaptype = ltc->laptype;
- len = ltc->length;
-
- sklen = len;
- if (llaptype == 1)
- sklen += 8; /* correct for short ddp */
- if(sklen > 800) {
- printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n",
- dev->name,sklen);
- return -1;
- }
-
- if ( (llaptype==0) || (llaptype>2) ) {
- printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype);
- return -1;
- }
-
-
- skb = dev_alloc_skb(3+sklen);
- if (skb == NULL)
- {
- printk("%s: dropping packet due to memory squeeze.\n",
- dev->name);
- return -1;
- }
- skb->dev = dev;
-
- if (sklen > len)
- skb_reserve(skb,8);
- skb_put(skb,len+3);
- skb->protocol = htons(ETH_P_LOCALTALK);
- /* add LLAP header */
- skb->data[0] = dnode;
- skb->data[1] = snode;
- skb->data[2] = llaptype;
- skb->mac.raw = skb->data; /* save pointer to llap header */
- skb_pull(skb,3);
-
- /* copy ddp(s,e)hdr + contents */
- memcpy(skb->data,(void*)ltdmabuf,len);
-
- skb->h.raw = skb->data;
-
- stats->rx_packets++;
- stats->rx_bytes+=skb->len;
-
- /* toss it onwards */
- netif_rx(skb);
- return 0;
-}
-
-/* the handler for the board interrupt */
-
-static void ltpc_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
-{
- struct net_device *dev = dev_id;
-
- if (dev==NULL) {
- printk("ltpc_interrupt: unknown device.\n");
- return;
- }
-
- inb_p(dev->base_addr+6); /* disable further interrupts from board */
-
- idle(dev); /* handle whatever is coming in */
-
- /* idle re-enables interrupts from board */
-
- return;
-}
-
-/***
- *
- * The ioctls that the driver responds to are:
- *
- * SIOCSIFADDR -- do probe using the passed node hint.
- * SIOCGIFADDR -- return net, node.
- *
- * some of this stuff should be done elsewhere.
- *
- ***/
-
-static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr;
- /* we'll keep the localtalk node address in dev->pa_addr */
- struct at_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr;
- struct lt_init c;
- int ltflags;
-
- if(debug&DEBUG_VERBOSE) printk("ltpc_ioctl called\n");
-
- switch(cmd) {
- case SIOCSIFADDR:
-
- aa->s_net = sa->sat_addr.s_net;
-
- /* this does the probe and returns the node addr */
- c.command = LT_INIT;
- c.hint = sa->sat_addr.s_node;
-
- aa->s_node = do_read(dev,&c,sizeof(c),&c,0);
-
- /* get all llap frames raw */
- ltflags = read_30(dev);
- ltflags |= LT_FLAG_ALLLAP;
- set_30 (dev,ltflags);
-
- dev->broadcast[0] = 0xFF;
- dev->dev_addr[0] = aa->s_node;
-
- dev->addr_len=1;
-
- return 0;
-
- case SIOCGIFADDR:
-
- sa->sat_addr.s_net = aa->s_net;
- sa->sat_addr.s_node = aa->s_node;
-
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- /* This needs to be present to keep netatalk happy. */
- /* Actually netatalk needs fixing! */
-}
-
-static int ltpc_hard_header (struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- if(debug&DEBUG_VERBOSE)
- printk("ltpc_hard_header called for device %s\n",
- dev->name);
- return 0;
-}
-
-static int ltpc_init(struct net_device *dev)
-{
- /* Initialize the device structure. */
-
- /* Fill in the fields of the device structure with ethernet-generic values. */
- ltalk_setup(dev);
- dev->hard_start_xmit = ltpc_xmit;
- dev->hard_header = ltpc_hard_header;
-
- dev->priv = kmalloc(sizeof(struct ltpc_private), GFP_KERNEL);
- if(!dev->priv)
- {
- printk(KERN_INFO "%s: could not allocate statistics buffer\n", dev->name);
- return -ENOMEM;
- }
-
- memset(dev->priv, 0, sizeof(struct ltpc_private));
- dev->get_stats = ltpc_get_stats;
-
- dev->open = ltpc_open;
- dev->stop = ltpc_close;
-
- /* add the ltpc-specific things */
- dev->do_ioctl = <pc_ioctl;
-
- dev->set_multicast_list = &set_multicast_list;
- dev->mc_list = NULL;
-
- return 0;
-}
-
-static int ltpc_poll_counter = 0;
-
-static void ltpc_poll(unsigned long l)
-{
- struct net_device *dev = (struct net_device *) l;
-
- del_timer(<pc_timer);
-
- if(debug&DEBUG_VERBOSE) {
- if (!ltpc_poll_counter) {
- ltpc_poll_counter = 50;
- printk("ltpc poll is alive\n");
- }
- ltpc_poll_counter--;
- }
-
- if (!dev)
- return; /* we've been downed */
-
- idle(dev);
- ltpc_timer.expires = jiffies+5;
-
- add_timer(<pc_timer);
-}
-
-/* DDP to LLAP translation */
-
-static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- /* in kernel 1.3.xx, on entry skb->data points to ddp header,
- * and skb->len is the length of the ddp data + ddp header
- */
-
- struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
-
- int i;
- struct lt_sendlap cbuf;
-
- cbuf.command = LT_SENDLAP;
- cbuf.dnode = skb->data[0];
- cbuf.laptype = skb->data[2];
- skb_pull(skb,3); /* skip past LLAP header */
- cbuf.length = skb->len; /* this is host order */
- skb->h.raw=skb->data;
-
- if(debug&DEBUG_UPPER) {
- printk("command ");
- for(i=0;i<6;i++)
- printk("%02x ",((unsigned char *)&cbuf)[i]);
- printk("\n");
- }
-
- do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len);
-
- if(debug&DEBUG_UPPER) {
- printk("sent %d ddp bytes\n",skb->len);
- for(i=0;i<skb->len;i++) printk("%02x ",skb->h.raw[i]);
- printk("\n");
- }
-
- stats->tx_packets++;
- stats->tx_bytes+=skb->len;
-
- dev_kfree_skb(skb);
- return 0;
-}
-
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev)
-{
- struct net_device_stats *stats = &((struct ltpc_private *) dev->priv)->stats;
- return stats;
-}
-
-/* initialization stuff */
-
-int __init ltpc_probe_dma(int base)
-{
- int dma = 0;
- int timeout;
- unsigned long f;
-
- if (!request_dma(1,"ltpc")) {
- f=claim_dma_lock();
- disable_dma(1);
- clear_dma_ff(1);
- set_dma_mode(1,DMA_MODE_WRITE);
- set_dma_addr(1,virt_to_bus(ltdmabuf));
- set_dma_count(1,sizeof(struct lt_mem));
- enable_dma(1);
- release_dma_lock(f);
- dma|=1;
- }
- if (!request_dma(3,"ltpc")) {
- f=claim_dma_lock();
- disable_dma(3);
- clear_dma_ff(3);
- set_dma_mode(3,DMA_MODE_WRITE);
- set_dma_addr(3,virt_to_bus(ltdmabuf));
- set_dma_count(3,sizeof(struct lt_mem));
- enable_dma(3);
- release_dma_lock(f);
- dma|=2;
- }
-
- /* set up request */
-
- /* FIXME -- do timings better! */
-
- ltdmabuf[0] = LT_READMEM;
- ltdmabuf[1] = 1; /* mailbox */
- ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */
- ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */
- ltdmabuf[6] = 0; /* dunno if this is necessary */
-
- inb_p(io+1);
- inb_p(io+0);
- timeout = jiffies+100*HZ/100;
- while(time_before(jiffies, timeout)) {
- if ( 0xfa == inb_p(io+6) ) break;
- }
-
- inb_p(io+3);
- inb_p(io+2);
- while(time_before(jiffies, timeout)) {
- if ( 0xfb == inb_p(io+6) ) break;
- }
-
- /* release the other dma channel (if we opened both of them) */
-
- if ( (dma&0x2) && (get_dma_residue(3)==sizeof(struct lt_mem)) ){
- dma&=1;
- free_dma(3);
- }
-
- if ( (dma&0x1) && (get_dma_residue(1)==sizeof(struct lt_mem)) ){
- dma&=0x2;
- free_dma(1);
- }
-
- /* fix up dma number */
- dma|=1;
-
- return dma;
-}
-
-int __init ltpc_probe(struct net_device *dev)
-{
- int err;
- int x=0,y=0;
- int timeout;
- int autoirq;
- unsigned long flags;
- unsigned long f;
-
- save_flags(flags);
-
- /* probe for the I/O port address */
- if (io != 0x240 && !check_region(0x220,8)) {
- x = inb_p(0x220+6);
- if ( (x!=0xff) && (x>=0xf0) ) io = 0x220;
- }
-
- if (io != 0x220 && !check_region(0x240,8)) {
- y = inb_p(0x240+6);
- if ( (y!=0xff) && (y>=0xf0) ) io = 0x240;
- }
-
- if(io) {
- /* found it, now grab it */
- request_region(io,8,"ltpc");
- } else {
- /* give up in despair */
- printk ("LocalTalk card not found; 220 = %02x, 240 = %02x.\n",
- x,y);
- restore_flags(flags);
- return -1;
- }
-
- /* probe for the IRQ line */
- if (irq < 2) {
- autoirq_setup(2);
-
- /* reset the interrupt line */
- inb_p(io+7);
- inb_p(io+7);
- /* trigger an interrupt (I hope) */
- inb_p(io+6);
-
- autoirq = autoirq_report(1);
-
- if (autoirq == 0) {
- printk("ltpc: probe at %#x failed to detect IRQ line.\n",
- io);
- }
- else {
- irq = autoirq;
- }
- }
-
- /* allocate a DMA buffer */
- ltdmabuf = (unsigned char *) dma_mem_alloc(1000);
-
- if (ltdmabuf) ltdmacbuf = <dmabuf[800];
-
- if (!ltdmabuf) {
- printk("ltpc: mem alloc failed\n");
- restore_flags(flags);
- return(-1);
- }
-
- if(debug&DEBUG_VERBOSE) {
- printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf);
- }
-
- /* reset the card */
-
- inb_p(io+1);
- inb_p(io+3);
- timeout = jiffies+2*HZ/100;
- while(time_before(jiffies, timeout)) ; /* hold it in reset for a coupla jiffies */
- inb_p(io+0);
- inb_p(io+2);
- inb_p(io+7); /* clear reset */
- inb_p(io+4);
- inb_p(io+5);
- inb_p(io+5); /* enable dma */
- inb_p(io+6); /* tri-state interrupt line */
-
- timeout = jiffies+100*HZ/100;
-
- while(time_before(jiffies, timeout)) {
- /* wait for the card to complete initialization */
- }
-
- /* now, figure out which dma channel we're using, unless it's
- already been specified */
- /* well, 0 is a legal DMA channel, but the LTPC card doesn't
- use it... */
- if (dma == 0) {
- dma = ltpc_probe_dma(io);
- if (!dma) { /* no dma channel */
- printk("No DMA channel found on ltpc card.\n");
- restore_flags(flags);
- return -1;
- }
- }
-
- /* print out friendly message */
-
- if(irq)
- printk("Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma);
- else
- printk("Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma);
-
- /* seems more logical to do this *after* probing the card... */
- err = ltpc_init(dev);
- if (err) return err;
-
- dev->base_addr = io;
- dev->irq = irq;
- dev->dma = dma;
-
- /* the card will want to send a result at this point */
- /* (I think... leaving out this part makes the kernel crash,
- so I put it back in...) */
-
- f=claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma,DMA_MODE_READ);
- set_dma_addr(dma,virt_to_bus(ltdmabuf));
- set_dma_count(dma,0x100);
- enable_dma(dma);
- release_dma_lock(f);
-
- (void) inb_p(io+3);
- (void) inb_p(io+2);
- timeout = jiffies+100*HZ/100;
- while(time_before(jiffies, timeout)) {
- if( 0xf9 == inb_p(io+6)) break;
- }
-
- if(debug&DEBUG_VERBOSE) {
- printk("setting up timer and irq\n");
- }
-
- if (irq) {
- /* grab it and don't let go :-) */
- (void) request_irq( irq, <pc_interrupt, 0, "ltpc", dev);
- (void) inb_p(io+7); /* enable interrupts from board */
- (void) inb_p(io+7); /* and reset irq line */
- } else {
- /* polled mode -- 20 times per second */
- /* this is really, really slow... should it poll more often? */
- init_timer(<pc_timer);
- ltpc_timer.function=ltpc_poll;
- ltpc_timer.data = (unsigned long) dev;
-
- ltpc_timer.expires = jiffies + 5;
- add_timer(<pc_timer);
- restore_flags(flags);
- }
-
- return 0;
-}
-
-/* handles "ltpc=io,irq,dma" kernel command lines */
-static int __init ltpc_setup(char *str)
-{
- int ints[5];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- if (ints[0] == 0) {
- if (str && !strncmp(str, "auto", 4)) {
- /* do nothing :-) */
- }
- else {
- /* usage message */
- printk (KERN_ERR
- "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n");
- }
- return 1;
- } else {
- io = ints[1];
- if (ints[0] > 1) {
- irq = ints[2];
- return 1;
- }
- if (ints[0] > 2) {
- dma = ints[3];
- return 1;
- }
- /* ignore any other paramters */
- }
- return 1;
-}
-
-__setup("ltpc=", ltpc_setup);
-
-#ifdef MODULE
-
-static char dev_name[8];
-
-static struct net_device dev_ltpc = {
- dev_name,
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, ltpc_probe };
-
-MODULE_PARM(debug, "i");
-MODULE_PARM(io, "i");
-MODULE_PARM(irq, "i");
-MODULE_PARM(dma, "i");
-
-int init_module(void)
-{
- int err, result;
-
- if(io == 0)
- printk(KERN_NOTICE
- "ltpc: Autoprobing is not recommended for modules\n");
-
- /* Find a name for this unit */
- err=dev_alloc_name(&dev_ltpc,"lt%d");
-
- if(err<0)
- return err;
-
- if ((result = register_netdev(&dev_ltpc)) != 0) {
- printk(KERN_DEBUG "could not register Localtalk-PC device\n");
- return result;
- } else {
- if(debug&DEBUG_VERBOSE) printk("0 from register_netdev\n");
- return 0;
- }
-}
-
-void cleanup_module(void)
-{
- long timeout;
-
- ltpc_timer.data = 0; /* signal the poll routine that we're done */
-
- if(debug&DEBUG_VERBOSE) printk("freeing irq\n");
-
- if(dev_ltpc.irq) {
- free_irq(dev_ltpc.irq,&dev_ltpc);
- dev_ltpc.irq = 0;
- }
-
- if(del_timer(<pc_timer))
- {
- /* either the poll was never started, or a poll is in process */
- if(debug&DEBUG_VERBOSE) printk("waiting\n");
- /* if it's in process, wait a bit for it to finish */
- timeout = jiffies+HZ;
- add_timer(<pc_timer);
- while(del_timer(<pc_timer) && time_after(timeout, jiffies))
- {
- add_timer(<pc_timer);
- schedule();
- }
- }
-
- if(debug&DEBUG_VERBOSE) printk("freeing dma\n");
-
- if(dev_ltpc.dma) {
- free_dma(dev_ltpc.dma);
- dev_ltpc.dma = 0;
- }
-
- if(debug&DEBUG_VERBOSE) printk("freeing ioaddr\n");
-
- if(dev_ltpc.base_addr) {
- release_region(dev_ltpc.base_addr,8);
- dev_ltpc.base_addr = 0;
- }
-
- if(debug&DEBUG_VERBOSE) printk("free_pages\n");
-
- free_pages( (unsigned long) ltdmabuf, get_order(1000));
- ltdmabuf=NULL;
- ltdmacbuf=NULL;
-
- if(debug&DEBUG_VERBOSE) printk("unregister_netdev\n");
-
- unregister_netdev(&dev_ltpc);
-
- if(debug&DEBUG_VERBOSE) printk("returning from cleanup_module\n");
-}
-#endif /* MODULE */
-
+++ /dev/null
-/*** ltpc.h
- *
- *
- ***/
-
-#define LT_GETRESULT 0x00
-#define LT_WRITEMEM 0x01
-#define LT_READMEM 0x02
-#define LT_GETFLAGS 0x04
-#define LT_SETFLAGS 0x05
-#define LT_INIT 0x10
-#define LT_SENDLAP 0x13
-#define LT_RCVLAP 0x14
-
-/* the flag that we care about */
-#define LT_FLAG_ALLLAP 0x04
-
-struct lt_getresult {
- unsigned char command;
- unsigned char mailbox;
-};
-
-struct lt_mem {
- unsigned char command;
- unsigned char mailbox;
- unsigned short addr; /* host order */
- unsigned short length; /* host order */
-};
-
-struct lt_setflags {
- unsigned char command;
- unsigned char mailbox;
- unsigned char flags;
-};
-
-struct lt_getflags {
- unsigned char command;
- unsigned char mailbox;
-};
-
-struct lt_init {
- unsigned char command;
- unsigned char mailbox;
- unsigned char hint;
-};
-
-struct lt_sendlap {
- unsigned char command;
- unsigned char mailbox;
- unsigned char dnode;
- unsigned char laptype;
- unsigned short length; /* host order */
-};
-
-struct lt_rcvlap {
- unsigned char command;
- unsigned char dnode;
- unsigned char snode;
- unsigned char laptype;
- unsigned short length; /* host order */
-};
-
-union lt_command {
- struct lt_getresult getresult;
- struct lt_mem mem;
- struct lt_setflags setflags;
- struct lt_getflags getflags;
- struct lt_init init;
- struct lt_sendlap sendlap;
- struct lt_rcvlap rcvlap;
-};
-typedef union lt_command lt_command;
-
unsigned short cmd;
unsigned char irq, latency;
- if ( debug >= 0)
- ncr885e_debug = debug;
-
while(( pdev = pci_find_device( PCI_VENDOR_ID_NCR,
PCI_DEVICE_ID_NCR_53C885_ETHERNET,
pdev )) != NULL ) {
int
init_module(void)
{
- if ( debug >= 0)
- ncr885e_debug = debug;
-
return ncr885e_probe();
}
static struct net_device *init_alloc_dev(int sizeof_priv)
{
struct net_device *dev;
- int alloc_size = sizeof(struct net_device) + IFNAMSIZ
- + sizeof_priv + 3;
- alloc_size &= ~3; /* Round to dword boundary. */
- dev = (struct net_device *)kmalloc(alloc_size, GFP_KERNEL);
- if(dev==NULL)
+ int alloc_size;
+
+ /* 32-byte alignment */
+ alloc_size = sizeof (*dev) + IFNAMSIZ + sizeof_priv + 31;
+ alloc_size &= ~31;
+
+ dev = (struct net_device *) kmalloc (alloc_size, GFP_KERNEL);
+ if (dev == NULL)
{
printk(KERN_ERR "alloc_dev: Unable to allocate device memory.\n");
return NULL;
}
+
memset(dev, 0, alloc_size);
+
if (sizeof_priv)
dev->priv = (void *) (dev + 1);
+
dev->name = sizeof_priv + (char *)(dev + 1);
return dev;
}
#
mainmenu_option next_comment
-comment 'Token Ring driver support'
+comment 'Token Ring devices'
bool 'Token Ring driver support' CONFIG_TR
if [ "$CONFIG_TR" != "n" ]; then
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
-# Note 2! The CFLAGS definition is now inherited from the
-# parent makefile.
-#
-#
-# Note : at this point, these files are compiled on all systems.
-# In the future, some of these should be built conditionally.
-#
-
-SUB_DIRS :=
+SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS)
+obj-y :=
+obj-n :=
+obj-m :=
+obj- :=
+export-objs :=
-L_TARGET := tr.a
-L_OBJS :=
-M_OBJS :=
-
-ifeq ($(CONFIG_IBMTR),y)
- L_OBJS += ibmtr.o
-else
- ifeq ($(CONFIG_IBMTR),m)
- M_OBJS += ibmtr.o
- endif
-endif
-
-ifeq ($(CONFIG_IBMOL),y)
- L_OBJS += olympic.o
-else
- ifeq ($(CONFIG_IBMOL),m)
- M_OBJS += olympic.o
- endif
-endif
-
-ifeq ($(CONFIG_TMS380TR),y)
- L_OBJS += tms380tr.o
- ifeq ($(CONFIG_ABYSS),y)
- L_OBJS += abyss.o
- endif
- ifeq ($(CONFIG_MADGEMC),y)
- L_OBJS += madgemc.o
- endif
- ifeq ($(CONFIG_TMSPCI),y)
- L_OBJS += tmspci.o
- endif
-else
- ifeq ($(CONFIG_TMS380TR),m)
- M_OBJS += tms380tr.o
- ifeq ($(CONFIG_ABYSS),m)
- M_OBJS += abyss.o
- endif
- ifeq ($(CONFIG_MADGEMC),m)
- M_OBJS += madgemc.o
- endif
- ifeq ($(CONFIG_TMSPCI),m)
- M_OBJS += tmspci.o
- endif
- endif
-endif
+obj-$(CONFIG_IBMTR) += ibmtr.o
+obj-$(CONFIG_IBMOL) += olympic.o
+obj-$(CONFIG_TMS380TR) += tms380tr.o
+obj-$(CONFIG_ABYSS) += abyss.o
+obj-$(CONFIG_MADGEMC) += madgemc.o
+obj-$(CONFIG_TMSPCI) += tmspci.o
+obj-$(CONFIG_SMCTR) += smctr.o
-ifeq ($(CONFIG_SMCTR),y)
- L_OBJS += smctr.o
-else
- ifeq ($(CONFIG_SMCTR),m)
- M_OBJS += smctr.o
- endif
-endif
+L_TARGET := tr.a
+L_OBJS := $(filter-out $(export-objs), $(obj-y))
+LX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
include $(TOPDIR)/Rules.make
-
Support and updates available at
http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html
+
+
+ Linux kernel version history:
+
+ LK1.1.0:
+ - Jeff Garzik: softnet 'n stuff
+
+ LK1.1.1:
+ - Justin Guyett: softnet and locking fixes
+ - Jeff Garzik: use PCI interface
+
*/
static const char *versionA =
-"via-rhine.c:v1.01 2/27/99 Written by Donald Becker\n";
+"via-rhine.c:v1.01-LK1.1.1 3/2/2000 Written by Donald Becker\n";
static const char *versionB =
" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
static int max_interrupt_work = 20;
-static int min_pci_latency = 64;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1518 effectively disables this feature. */
MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(min_pci_latency, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(rx_copybreak, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
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. Buffers consumed this way are replaced by newly allocated
-skbuffs in the last phase of netdev_rx().
+skbuffs in the last phase of via_rhine_rx().
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
*/
-\f
+
/* This table drives the PCI probe routines. It's mostly boilerplate in all
of the drivers, and will likely be provided by some future kernel.
Note the matching code -- the first table entry matchs all 56** cards but
second only the 1234 card.
*/
+
enum pci_flags_bit {
PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
};
-struct pci_id_info {
+
+enum via_rhine_chips {
+ vt86c100a = 0,
+ vt3043,
+};
+
+struct via_rhine_chip_info {
const char *name;
- u16 vendor_id, device_id, device_id_mask, flags;
+ u16 flags;
int io_size;
- struct net_device *(*probe1)(struct pci_dev *pdev, long ioaddr, int irq, int chip_idx, int fnd_cnt);
};
-static struct net_device *via_probe1(struct pci_dev *pdev, long ioaddr, int irq,
- int chp_idx, int fnd_cnt);
-static struct pci_id_info pci_tbl[] = {
- { "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff,
- PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
- { "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff,
- PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
- {0,}, /* 0 terminated list. */
+/* directly indexed by enum via_rhine_chips, above */
+static struct via_rhine_chip_info via_rhine_chip_info[] __devinitdata =
+{
+ {"VIA VT86C100A Rhine-II",
+ PCI_USES_MEM | PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
+ 128,},
+ {"VIA VT3043 Rhine",
+ PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER,
+ 128,},
+};
+
+static struct pci_device_id via_rhine_pci_tbl[] __devinitdata =
+{
+ {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt86c100a},
+ {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt3043},
+ {0,}, /* terminate list */
};
+MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl);
+
/* A chip capabilities table, matching the entries in pci_tbl[] above. */
struct sk_buff* tx_skbuff[TX_RING_SIZE];
unsigned char *tx_buf[TX_RING_SIZE]; /* Tx bounce buffers */
unsigned char *tx_bufs; /* Tx bounce buffer region. */
- struct net_device *next_module; /* Link for devices of this type. */
struct net_device_stats stats;
struct timer_list timer; /* Media monitoring timer. */
spinlock_t lock;
/* Frequently used values: keep some adjacent for cache effect. */
int chip_id;
- long in_interrupt; /* Word-long for SMP locks. */
struct rx_desc *rx_head_desc;
- unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
- unsigned int cur_tx, dirty_tx;
+ unsigned short int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned short int cur_tx, dirty_tx;
unsigned int rx_buf_sz; /* Based on MTU+slack. */
u16 chip_cmd; /* Current setting for ChipCmd */
unsigned int tx_full:1; /* The Tx queue is full. */
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 void check_duplex(struct net_device *dev);
-static void netdev_timer(unsigned long data);
-static void tx_timeout(struct net_device *dev);
-static void init_ring(struct net_device *dev);
-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 int netdev_rx(struct net_device *dev);
-static void netdev_error(struct net_device *dev, int intr_status);
-static void set_rx_mode(struct net_device *dev);
-static struct net_device_stats *get_stats(struct net_device *dev);
+static int via_rhine_open(struct net_device *dev);
+static void via_rhine_check_duplex(struct net_device *dev);
+static void via_rhine_timer(unsigned long data);
+static void via_rhine_tx_timeout(struct net_device *dev);
+static void via_rhine_init_ring(struct net_device *dev);
+static int via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev);
+static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static void via_rhine_tx(struct net_device *dev);
+static void via_rhine_rx(struct net_device *dev);
+static void via_rhine_error(struct net_device *dev, int intr_status);
+static void via_rhine_set_rx_mode(struct net_device *dev);
+static struct net_device_stats *via_rhine_get_stats(struct net_device *dev);
static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static int netdev_close(struct net_device *dev);
-
-\f
+static int via_rhine_close(struct net_device *dev);
-/* A list of our installed devices, for removing the driver module. */
-static struct net_device *root_net_dev = NULL;
-/* Ideally we would detect all network cards in slot order. That would
- be best done a central PCI probe dispatch, which wouldn't work
- well when dynamically adding drivers. So instead we detect just the
- cards we know about in slot order. */
-
-static int pci_etherdev_probe(struct pci_id_info pci_tbl[])
+static int __devinit via_rhine_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
- int cards_found = 0;
- int pci_index = 0;
- unsigned char pci_bus, pci_device_fn;
struct net_device *dev;
+ struct netdev_private *np;
+ int i, option;
+ int chip_id = (int) ent->driver_data;
+ int irq = pdev->irq;
+ static int card_idx = -1;
+ static int did_version = 0;
+ long ioaddr;
+ int io_size;
+
+ /* print version once and once only */
+ if (! did_version++) {
+ printk (KERN_INFO "%s", versionA);
+ printk (KERN_INFO "%s", versionB);
+ }
+
+ card_idx++;
+ option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+ io_size = via_rhine_chip_info[chip_id].io_size;
- for (;pci_index < 0xff; pci_index++) {
- u16 vendor, device, pci_command, new_command;
- int chip_idx, irq;
- long pciaddr;
- long ioaddr;
- struct pci_dev *pdev;
-
- if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
- &pci_bus, &pci_device_fn)
- != PCIBIOS_SUCCESSFUL)
- break;
-
- pdev = pci_find_slot (pci_bus, pci_device_fn);
- if (!pdev) continue;
- vendor = pdev->vendor;
- device = pdev->device;
-
- for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
- if (vendor == pci_tbl[chip_idx].vendor_id
- && (device & pci_tbl[chip_idx].device_id_mask) ==
- pci_tbl[chip_idx].device_id)
- break;
- if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */
- continue;
#ifdef VIA_USE_IO
- pciaddr = pdev->resource[0].start;
+ ioaddr = pci_resource_start (pdev, 0);
#else
- pciaddr = pdev->resource[1].start;
+ ioaddr = pci_resource_start (pdev, 1);
#endif
- irq = pdev->irq;
-
- if (debug > 2)
- printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n",
- pci_tbl[chip_idx].name, pciaddr, irq);
-
- if (pci_tbl[chip_idx].flags & PCI_USES_IO) {
- ioaddr = pciaddr & ~3;
- if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
- continue;
- } else if ((ioaddr = (long)ioremap(pciaddr & ~0xf,
- pci_tbl[chip_idx].io_size)) == 0) {
- printk(KERN_INFO "Failed to map PCI address %#lx.\n",
- pciaddr);
- continue;
- }
-
- pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
- if (pci_command != new_command) {
- printk(KERN_INFO " The PCI BIOS has not enabled the"
- " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n",
- pdev->bus->number, pdev->devfn, pci_command, new_command);
- pci_write_config_word(pdev, PCI_COMMAND, new_command);
- }
-
- dev = pci_tbl[chip_idx].probe1(pdev, ioaddr, irq, chip_idx, cards_found);
- if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
- u8 pci_latency;
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency < min_pci_latency) {
- printk(KERN_INFO " PCI latency timer (CFLT) is "
- "unreasonably low at %d. Setting to %d clocks.\n",
- pci_latency, min_pci_latency);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, min_pci_latency);
- }
- }
- dev = 0;
- cards_found++;
+ if (pci_enable_device (pdev)) {
+ printk (KERN_ERR "unable to init PCI device (card #%d)\n",
+ card_idx);
+ goto err_out;
+ }
+
+ if (via_rhine_chip_info[chip_id].flags & PCI_USES_MASTER)
+ pci_set_master (pdev);
+
+ dev = init_etherdev(NULL, sizeof (*np));
+ if (dev == NULL) {
+ printk (KERN_ERR "init_ethernet failed for card #%d\n",
+ card_idx);
+ goto err_out;
+ }
+
+ if (!request_region(pci_resource_start (pdev, 0), io_size, dev->name)) {
+ printk (KERN_ERR "request_region failed for device %s, region 0x%X @ 0x%lX\n",
+ dev->name, io_size,
+ pci_resource_start (pdev, 0));
+ goto err_out_free_netdev;
+ }
+ if (!request_mem_region(pci_resource_start (pdev, 1), io_size, dev->name)) {
+ printk (KERN_ERR "request_mem_region failed for device %s, region 0x%X @ 0x%lX\n",
+ dev->name, io_size,
+ pci_resource_start (pdev, 1));
+ goto err_out_free_pio;
}
- return cards_found ? 0 : -ENODEV;
-}
-
-
-static struct net_device *via_probe1(struct pci_dev *pdev,
- long ioaddr, int irq,
- int chip_id, int card_idx)
-{
- struct net_device *dev;
- struct netdev_private *np;
- int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
-
- dev = init_etherdev(NULL, 0);
- if(dev==NULL)
- return NULL;
-
- printk(KERN_INFO "%s: %s at 0x%lx, ",
- dev->name, pci_tbl[chip_id].name, ioaddr);
-
-#ifdef VIA_USE_IO
- if (!request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name)) {
- unregister_netdev (dev);
- kfree (dev);
- return NULL;
+#ifndef VIA_USE_IO
+ ioaddr = (long) ioremap (ioaddr, io_size);
+ if (!ioaddr) {
+ printk (KERN_ERR "ioremap failed for device %s, region 0x%X @ 0x%X\n",
+ dev->name, io_size,
+ pci_resource_start (pdev, 1));
+ goto err_out_free_mmio;
}
#endif
+ printk(KERN_INFO "%s: %s at 0x%lx, ",
+ dev->name, via_rhine_chip_info[chip_id].name, ioaddr);
+
/* Ideally we would be read the EEPROM but access may be locked. */
for (i = 0; i <6; i++)
dev->dev_addr[i] = readb(ioaddr + StationAddr + i);
dev->base_addr = ioaddr;
dev->irq = irq;
- /* Make certain the descriptor lists are cache-aligned. */
- np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31);
- /* FIXME! check return !!! */
- memset(np, 0, sizeof(*np));
- dev->priv = np;
-
- np->next_module = root_net_dev;
- root_net_dev = dev;
-
- np->lock = SPIN_LOCK_UNLOCKED;
+ np = dev->priv;
+ spin_lock_init (&np->lock);
np->chip_id = chip_id;
if (dev->mem_start)
np->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 = &mii_ioctl;
- dev->tx_timeout = tx_timeout;
+ dev->open = via_rhine_open;
+ dev->hard_start_xmit = via_rhine_start_tx;
+ dev->stop = via_rhine_close;
+ dev->get_stats = via_rhine_get_stats;
+ dev->set_multicast_list = via_rhine_set_rx_mode;
+ dev->do_ioctl = mii_ioctl;
+ dev->tx_timeout = via_rhine_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
+
+ pdev->driver_data = dev;
if (cap_tbl[np->chip_id].flags & CanHaveMII) {
int phy, phy_idx = 0;
np->mii_cnt = phy_idx;
}
- return dev;
+ return 0;
+
+#ifndef VIA_USE_IO
+/* note this is ifdef'd because the ioremap is ifdef'd...
+ * so additional exit conditions above this must move
+ * release_mem_region outside of the ifdef */
+err_out_free_mmio:
+ release_mem_region(pci_resource_start (pdev, 1), io_size, dev->name));
+#endif
+err_out_free_pio:
+ release_region(pci_resource_start (pdev, 0), io_size);
+err_out_free_netdev:
+ unregister_netdev (dev);
+ kfree (dev);
+err_out:
+ return -ENODEV;
}
-\f
+
/* Read and write over the MII Management Data I/O (MDIO) interface. */
static int mdio_read(struct net_device *dev, int phy_id, int regnum)
return;
}
-\f
-static int netdev_open(struct net_device *dev)
+
+static int via_rhine_open(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
/* Reset the chip. */
writew(CmdReset, ioaddr + ChipCmd);
- if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
+ if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev))
return -EAGAIN;
if (debug > 1)
- printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+ printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n",
dev->name, dev->irq);
MOD_INC_USE_COUNT;
- init_ring(dev);
+ via_rhine_init_ring(dev);
writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
/* Configure the FIFO thresholds. */
writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */
np->tx_thresh = 0x20;
- np->rx_thresh = 0x60; /* Written in set_rx_mode(). */
+ np->rx_thresh = 0x60; /* Written in via_rhine_set_rx_mode(). */
if (dev->if_port == 0)
dev->if_port = np->default_port;
netif_start_queue(dev);
- np->in_interrupt = 0;
- set_rx_mode(dev);
+ via_rhine_set_rx_mode(dev);
/* Enable interrupts by setting the interrupt mask. */
writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped|
np->chip_cmd |= CmdFDuplex;
writew(np->chip_cmd, ioaddr + ChipCmd);
- check_duplex(dev);
+ via_rhine_check_duplex(dev);
if (debug > 2)
- printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x "
+ printk(KERN_DEBUG "%s: Done via_rhine_open(), status %4.4x "
"MII status: %4.4x.\n",
dev->name, readw(ioaddr + ChipCmd),
mdio_read(dev, np->phys[0], 1));
init_timer(&np->timer);
np->timer.expires = RUN_AT(1);
np->timer.data = (unsigned long)dev;
- np->timer.function = &netdev_timer; /* timer handler */
+ np->timer.function = &via_rhine_timer; /* timer handler */
add_timer(&np->timer);
return 0;
}
-static void check_duplex(struct net_device *dev)
+static void via_rhine_check_duplex(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+ int negotiated = mii_reg5 & np->advertising;
int duplex;
if (np->duplex_lock || mii_reg5 == 0xffff)
return;
- duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
if (np->full_duplex != duplex) {
np->full_duplex = duplex;
if (debug)
}
}
-static void netdev_timer(unsigned long data)
+static void via_rhine_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct netdev_private *np = (struct netdev_private *)dev->priv;
printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
dev->name, readw(ioaddr + IntrStatus));
}
- check_duplex(dev);
+ via_rhine_check_duplex(dev);
np->timer.expires = RUN_AT(next_tick);
add_timer(&np->timer);
}
-static void tx_timeout(struct net_device *dev)
+static void via_rhine_tx_timeout (struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = (struct netdev_private *) dev->priv;
long ioaddr = dev->base_addr;
- printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status "
- "%4.4x, resetting...\n",
- dev->name, readw(ioaddr + IntrStatus),
- mdio_read(dev, np->phys[0], 1));
+ printk (KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status "
+ "%4.4x, resetting...\n",
+ dev->name, readw (ioaddr + IntrStatus),
+ mdio_read (dev, np->phys[0], 1));
- /* Perhaps we should reinitialize the hardware here. */
- dev->if_port = 0;
- /* Stop and restart the chip's Tx processes . */
+ /* Perhaps we should reinitialize the hardware here. */
+ dev->if_port = 0;
+ /* Stop and restart the chip's Tx processes . */
- /* Trigger an immediate transmit demand. */
+ /* Trigger an immediate transmit demand. */
- dev->trans_start = jiffies;
- np->stats.tx_errors++;
- return;
+ dev->trans_start = jiffies;
+ np->stats.tx_errors++;
+
+ netif_start_queue (dev);
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void init_ring(struct net_device *dev)
+static void via_rhine_init_ring(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
int i;
- np->tx_full = 0;
np->cur_rx = np->cur_tx = 0;
np->dirty_rx = np->dirty_tx = 0;
np->rx_ring[i].rx_status = 0;
np->rx_ring[i].rx_length = DescOwn;
}
- np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_skbuff[i] = 0;
return;
}
-static int start_tx(struct sk_buff *skb, struct net_device *dev)
+static int via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
unsigned entry;
+ unsigned long flags;
/* Caution: the write order is important here, set the field
with the "ownership" bits last. */
+ /* lock eth irq */
+ spin_lock_irqsave (&np->lock, flags);
+
/* Calculate the next Tx descriptor entry. */
entry = np->cur_tx % TX_RING_SIZE;
/* Wake the potentially-idle transmit channel. */
writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
- if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1)
- netif_start_queue(dev); /* Typical path */
- else
- np->tx_full = 1;
+ if (np->cur_tx == np->dirty_tx + TX_RING_SIZE)
+ netif_stop_queue(dev);
+
dev->trans_start = jiffies;
+ spin_unlock_irqrestore (&np->lock, flags);
+
if (debug > 4) {
printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
dev->name, np->cur_tx, entry);
/* 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)
+static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs)
{
struct net_device *dev = (struct net_device *)dev_instance;
- struct netdev_private *np;
long ioaddr, boguscnt = max_interrupt_work;
+ u32 intr_status;
ioaddr = dev->base_addr;
- np = (struct netdev_private *)dev->priv;
- spin_lock (&np->lock);
-
- do {
- u32 intr_status = readw(ioaddr + IntrStatus);
-
+ while ((intr_status = readw(ioaddr + IntrStatus))) {
/* Acknowledge all of the current interrupt sources ASAP. */
writew(intr_status & 0xffff, ioaddr + IntrStatus);
printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
dev->name, intr_status);
- if (intr_status == 0)
- break;
-
if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped |
IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
- netdev_rx(dev);
-
- for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
- int entry = np->dirty_tx % TX_RING_SIZE;
- int txstatus;
- if (np->tx_ring[entry].tx_own)
- break;
- txstatus = np->tx_ring[entry].tx_status;
- if (debug > 6)
- printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
- entry, txstatus);
- if (txstatus & 0x8000) {
- if (debug > 1)
- printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
- dev->name, txstatus);
- np->stats.tx_errors++;
- if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
- if (txstatus & 0x0200) np->stats.tx_window_errors++;
- if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
- if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
- if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
- /* Transmitter restarted in 'abnormal' handler. */
- } else {
- np->stats.collisions += (txstatus >> 3) & 15;
- np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
- np->stats.tx_packets++;
- }
- /* Free the original skb. */
- dev_kfree_skb_irq(np->tx_skbuff[entry]);
- np->tx_skbuff[entry] = 0;
- }
- if (np->tx_full &&
- netif_queue_stopped(dev) &&
- np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
- /* The ring is no longer full, clear tbusy. */
- np->tx_full = 0;
- netif_wake_queue (dev);
- }
+ via_rhine_rx(dev);
+
+ if (intr_status & (IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ IntrTxAborted))
+ via_rhine_tx(dev);
/* Abnormal error summary/uncommon events handlers. */
if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
- netdev_error(dev, intr_status);
+ via_rhine_error(dev, intr_status);
if (--boguscnt < 0) {
printk(KERN_WARNING "%s: Too much work at interrupt, "
dev->name, intr_status);
break;
}
- } while (1);
+ }
if (debug > 3)
printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
dev->name, readw(ioaddr + IntrStatus));
+}
+
+/* This routine is logically part of the interrupt handler, but isolated
+ for clarity and better register allocation. */
+static void via_rhine_tx(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int txstatus = 0, entry = np->dirty_tx % TX_RING_SIZE;
+
+ spin_lock (&np->lock);
+
+ /* if tx_full is set, they're all dirty, not clean */
+ while (np->dirty_tx != np->cur_tx) {
+ if (np->tx_ring[entry].tx_own) /* transmit request pending */
+ break;
+ txstatus = np->tx_ring[entry].tx_status;
+ if (debug > 6)
+ printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
+ entry, txstatus);
+ if (txstatus & 0x8000) {
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
+ dev->name, txstatus);
+ np->stats.tx_errors++;
+ if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
+ if (txstatus & 0x0200) np->stats.tx_window_errors++;
+ if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
+ if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
+ if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
+ /* Transmitter restarted in 'abnormal' handler. */
+ } else {
+ np->stats.collisions += (txstatus >> 3) & 15;
+ np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+ np->stats.tx_packets++;
+ }
+ /* Free the original skb. */
+ dev_kfree_skb_irq(np->tx_skbuff[entry]);
+ np->tx_skbuff[entry] = NULL;
+ entry = (++np->dirty_tx) % TX_RING_SIZE;
+ }
+ if ((np->cur_tx - np->dirty_tx) <= TX_RING_SIZE/2)
+ netif_wake_queue (dev);
spin_unlock (&np->lock);
}
/* This routine is logically part of the interrupt handler, but isolated
for clarity and better register allocation. */
-static int netdev_rx(struct net_device *dev)
+static void via_rhine_rx(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
- int entry = np->cur_rx % RX_RING_SIZE;
- int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+ int entry = (np->dirty_rx = np->cur_rx) % RX_RING_SIZE;
+ int boguscnt = RX_RING_SIZE;
if (debug > 4) {
- printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+ printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %4.4x.\n",
entry, np->rx_head_desc->rx_length);
}
u16 desc_status = desc->rx_status;
if (debug > 4)
- printk(KERN_DEBUG " netdev_rx() status is %4.4x.\n",
+ printk(KERN_DEBUG " via_rhine_rx() status is %4.4x.\n",
desc_status);
if (--boguscnt < 0)
break;
if ((desc_status & RxWholePkt) != RxWholePkt) {
printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
"multiple buffers, entry %#x length %d status %4.4x!\n",
- dev->name, np->cur_rx, data_size, desc_status);
+ dev->name, entry, data_size, desc_status);
printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
dev->name, np->rx_head_desc,
- &np->rx_ring[np->cur_rx % RX_RING_SIZE]);
+ &np->rx_ring[entry]);
np->stats.rx_length_errors++;
} else if (desc_status & RxErr) {
/* There was a error. */
if (debug > 2)
- printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n",
+ printk(KERN_DEBUG " via_rhine_rx() Rx error was %8.8x.\n",
desc_status);
np->stats.rx_errors++;
if (desc_status & 0x0030) np->stats.rx_length_errors++;
}
/* Refill the Rx ring buffers. */
- for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+ while (np->dirty_rx != np->cur_rx) {
struct sk_buff *skb;
- entry = np->dirty_rx % RX_RING_SIZE;
+ 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;
/* Pre-emptively restart Rx engine. */
writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
- return 0;
}
-static void netdev_error(struct net_device *dev, int intr_status)
+static void via_rhine_error(struct net_device *dev, int intr_status)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
/* Link failed, restart autonegotiation. */
mdio_write(dev, np->phys[0], 0, 0x3300);
else
- check_duplex(dev);
+ via_rhine_check_duplex(dev);
if (debug)
printk(KERN_ERR "%s: MII status changed: Autonegotiation "
"advertising %4.4x partner %4.4x.\n", dev->name,
printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
"threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
}
- if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) {
+ if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug > 1) {
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status);
/* Recovery for other fault sources not known. */
}
}
-static struct enet_statistics *get_stats(struct net_device *dev)
+static struct net_device_stats *via_rhine_get_stats(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
return crc;
}
-static void set_rx_mode(struct net_device *dev)
+static void via_rhine_set_rx_mode(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
}
}
-static int netdev_close(struct net_device *dev)
+static int via_rhine_close(struct net_device *dev)
{
long ioaddr = dev->base_addr;
struct netdev_private *np = (struct netdev_private *)dev->priv;
return 0;
}
-static int __init via_rhine_init_module (void)
+
+static void __devexit via_rhine_remove_one (struct pci_dev *pdev)
{
- if (debug) /* Emit version even if no cards detected. */
- printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
-#ifdef CARDBUS
- register_driver(ðerdev_ops);
- return 0;
-#else
- return pci_etherdev_probe(pci_tbl);
+ struct net_device *dev = pdev->driver_data;
+ struct netdev_private *np = (struct netdev_private *)(dev->priv);
+
+ unregister_netdev(dev);
+
+ release_region(pci_resource_start (pdev, 0),
+ via_rhine_chip_info[np->chip_id].io_size);
+ release_mem_region(pci_resource_start (pdev, 1),
+ via_rhine_chip_info[np->chip_id].io_size);
+
+#ifndef VIA_USE_IO
+ iounmap((char *)(dev->base_addr));
#endif
+
+ kfree(dev);
}
-static void __exit via_rhine_cleanup_module (void)
+
+static struct pci_driver via_rhine_driver = {
+ name: "via-rhine",
+ id_table: via_rhine_pci_tbl,
+ probe: via_rhine_init_one,
+ remove: via_rhine_remove_one,
+};
+
+
+static int __init via_rhine_init (void)
{
+ return pci_module_init (&via_rhine_driver);
+}
-#ifdef CARDBUS
- unregister_driver(ðerdev_ops);
-#endif
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- while (root_net_dev) {
- struct netdev_private *np =
- (struct netdev_private *)(root_net_dev->priv);
- unregister_netdev(root_net_dev);
-#ifdef VIA_USE_IO
- release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size);
-#else
- iounmap((char *)(root_net_dev->base_addr));
-#endif
- kfree(root_net_dev);
- root_net_dev = np->next_module;
-#if 0
- kfree(np); /* Assumption: no struct realignment. */
-#endif
- }
+static void __exit via_rhine_cleanup (void)
+{
+ pci_unregister_driver (&via_rhine_driver);
}
-module_init(via_rhine_init_module);
-module_exit(via_rhine_cleanup_module);
+
+module_init(via_rhine_init);
+module_exit(via_rhine_cleanup);
/*
real_ioaddr = ioaddr = pci_resource_start (pdev, 0);
#else
real_ioaddr = ioaddr = pci_resource_start (pdev, 1);
- ioaddr = ioremap(ioaddr, YELLOWFIN_SIZE);
+ ioaddr = (long) ioremap(ioaddr, YELLOWFIN_SIZE);
#endif
irq = pdev->irq;
# Parport configuration.
#
+mainmenu_option next_comment
+comment 'Parallel port support'
+
tristate 'Parallel port support' CONFIG_PARPORT
if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
bool ' IEEE 1284 transfer modes' CONFIG_PARPORT_1284
fi
+
+endmenu
#define ECR_TST 06
#define ECR_CNF 07
+#undef DEBUG
+
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+#define DPRINTK(stuff...)
+#endif
+
+
+#define NR_SUPERIOS 3
+static struct superio_struct { /* For Super-IO chips autodetection */
+ int io;
+ int irq;
+ int dma;
+} superios[NR_SUPERIOS]= { {0,},};
+
/* frob_control, but for ECR */
static void frob_econtrol (struct parport *pb, unsigned char m,
unsigned char v)
{
unsigned char ectr = inb (ECONTROL (pb));
-#ifdef DEBUG_PARPORT
- printk (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
+ DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
m, v, ectr, (ectr & ~m) ^ v);
-#endif
+
outb ((ectr & ~m) ^ v, ECONTROL (pb));
}
#ifdef CONFIG_PARPORT_PC_FIFO
-/* Safely change the mode bits in the ECR */
+/* Safely change the mode bits in the ECR
+ Returns:
+ 0 : Success
+ -EBUSY: Could not drain FIFO in some finite amount of time,
+ mode not changed!
+ */
static int change_mode(struct parport *p, int m)
{
const struct parport_pc_private *priv = p->physport->private_data;
unsigned char oecr;
int mode;
+ DPRINTK("parport change_mode ECP-ISA to mode 0x%02x\n",m);
+
if (!priv->ecr) {
printk (KERN_DEBUG "change_mode: but there's no ECR!\n");
return 0;
parport_ieee1284_read_byte,
};
+/* Super-IO chipset detection, Winbond, SMSC */
+
+static void show_parconfig_smsc37c669(int io, int key)
+{
+ int cr1,cr4,cra,cr23,cr26,cr27,i=0;
+ char *modes[]={ "SPP and Bidirectional (PS/2)",
+ "EPP and SPP",
+ "ECP",
+ "ECP and EPP"};
+
+ outb(key,io);
+ outb(key,io);
+ outb(1,io);
+ cr1=inb(io+1);
+ outb(4,io);
+ cr4=inb(io+1);
+ outb(0x0a,io);
+ cra=inb(io+1);
+ outb(0x23,io);
+ cr23=inb(io+1);
+ outb(0x26,io);
+ cr26=inb(io+1);
+ outb(0x27,io);
+ cr27=inb(io+1);
+ outb(0xaa,io);
+
+ printk ("SMSC 37c669 LPT Config: cr_1=0x%02x, 4=0x%02x, "
+ "A=0x%2x, 23=0x%02x, 26=0x%02x, 27=0x%02x\n",
+ cr1,cr4,cra,cr23,cr26,cr27);
+
+ /* The documentation calls DMA and IRQ-Lines by letters, so
+ the board maker can/will wire them
+ appropriately/randomly... G=reserved H=IDE-irq, */
+ printk ("SMSC LPT Config: io=0x%04x, irq=%c, dma=%c, "
+ "fifo threshold=%d\n", cr23*4,
+ (cr27 &0x0f) ? 'A'-1+(cr27 &0x0f): '-',
+ (cr26 &0x0f) ? 'A'-1+(cr26 &0x0f): '-', cra & 0x0f);
+ printk("SMSC LPT Config: enabled=%s power=%s\n",
+ (cr23*4 >=0x100) ?"yes":"no", (cr1 & 4) ? "yes" : "no");
+ printk("SMSC LPT Config: Port mode=%s, EPP version =%s\n",
+ (cr1 & 0x08 ) ? "Standard mode only (SPP)" : modes[cr4 & 0x03],
+ (cr4 & 40) ? "1.7" : "1.9");
+
+ /* Heuristics ! BIOS setup for this mainboard device limits
+ the choices to standard settings, i.e. io-address and IRQ
+ are related, however DMA can be 1 or 3, assume DMA_A=DMA1,
+ DMA_C=DMA3 (this is true e.g. for TYAN 1564D Tomcat IV) */
+ if(cr23*4 >=0x100) { /* if active */
+ while((superios[i].io!= 0) && (i<NR_SUPERIOS))
+ i++;
+ if(i==NR_SUPERIOS)
+ printk("Super-IO: too many chips!\n");
+ else {
+ int d;
+ switch (cr23*4) {
+ case 0x3bc:
+ superios[i].io = 0x3bc;
+ superios[i].irq = 7;
+ break;
+ case 0x378:
+ superios[i].io = 0x378;
+ superios[i].irq = 7;
+ break;
+ case 0x278:
+ superios[i].io = 0x278;
+ superios[i].irq = 5;
+ }
+ d=(cr26 &0x0f);
+ if((d==1) || (d==3))
+ superios[i].dma= d;
+ else
+ superios[i].dma= PARPORT_DMA_NONE;
+ }
+ }
+}
+
+
+static void show_parconfig_winbond(int io, int key)
+{
+ int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
+ char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
+ "EPP-1.9 and SPP",
+ "ECP",
+ "ECP and EPP-1.9",
+ "Standard (SPP)",
+ "EPP-1.7 and SPP", /* 5 */
+ "undefined!",
+ "ECP and EPP-1.7"};
+ char *irqtypes[]={"pulsed low, high-Z", "follows nACK"};
+
+ /* The registers are called compatible-PnP because the
+ register layout is modelled after ISA-PnP, the access
+ method is just another ... */
+ outb(key,io);
+ outb(key,io);
+ outb(0x07,io); /* Register 7: Select Logical Device */
+ outb(0x01,io+1); /* LD1 is Parallel Port */
+ outb(0x30,io);
+ cr30=inb(io+1);
+ outb(0x60,io);
+ cr60=inb(io+1);
+ outb(0x61,io);
+ cr61=inb(io+1);
+ outb(0x70,io);
+ cr70=inb(io+1);
+ outb(0x74,io);
+ cr74=inb(io+1);
+ outb(0xf0,io);
+ crf0=inb(io+1);
+ outb(0xaa,io);
+
+ printk("Winbond LPT Config: cr_30=%02x 60,61=%02x%02x "
+ "70=%02x 74=%02x, f0=%02x\n", cr30,cr60,cr61,cr70,cr74,crf0);
+ printk("Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ",
+ (cr30 & 0x01) ? "yes":"no", cr60,cr61,cr70&0x0f );
+ if ((cr74 & 0x07) > 3)
+ printk("dma=none\n");
+ else
+ printk("dma=%d\n",cr74 & 0x07);
+ printk("Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n",
+ irqtypes[crf0>>7], (crf0>>3)&0x0f);
+ printk("Winbond LPT Config: Port mode=%s\n", modes[crf0 & 0x07]);
+
+ if(cr30 & 0x01) { /* the settings can be interrogated later ... */
+ while((superios[i].io!= 0) && (i<NR_SUPERIOS))
+ i++;
+ if(i==NR_SUPERIOS)
+ printk("Super-IO: too many chips!\n");
+ else {
+ superios[i].io = (cr60<<8)|cr61;
+ superios[i].irq = cr70&0x0f;
+ superios[i].dma = (((cr74 & 0x07) > 3) ?
+ PARPORT_DMA_NONE : (cr74 & 0x07));
+ }
+ }
+}
+
+static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+{
+ char *type=NULL;
+ int id,progif=2;
+
+ if (devid == devrev)
+ /* simple heuristics, we happened to read some
+ non-winbond register */
+ return;
+
+ printk("Winbond chip at EFER=0x%x key=0x%02x devid=%02x devrev=%02x "
+ "oldid=%02x\n", efer,key,devid,devrev,oldid);
+ id=(devid<<8) | devrev;
+
+ /* Values are from public data sheets pdf files, I can just
+ confirm 83977TF is correct :-) */
+ if (id == 0x9773) type="83977TF";
+ else if (id == 0x9774) type="83977ATF";
+ else if ((id & ~0x0f) == 0x5270) type="83977CTF / SMSC 97w36x";
+ else if ((id & ~0x0f) == 0x52f0) type="83977EF / SMSC 97x35x";
+ else if ((id & ~0x0f) == 0x5210) type="83627";
+ else if ((id & ~0x0f) == 0x6010) type="83697HF";
+ else if ((oldid &0x0f ) == 0x0c) { type="83877TF"; progif=1;}
+ else if ((oldid &0x0f ) == 0x0c) { type="83877ATF"; progif=1;}
+ else progif=0;
+
+ if(type==NULL)
+ printk("Winbond unkown chip type\n");
+ else
+ printk("Winbond chip type %s\n",type);
+
+ if(progif==2)
+ show_parconfig_winbond(efer,key);
+ return;
+}
+
+static void decode_smsc(int efer, int key, int devid, int devrev)
+{
+ char *type=NULL;
+ void (*func)(int io, int key);
+ int id;
+
+ if (devid == devrev)
+ /* simple heuristics, we happened to read some
+ non-winbond register */
+ return;
+
+ func=NULL;
+ printk("SMSC chip at EFER=0x%x key=0x%02x devid=%02x devrev=%02x\n",
+ efer,key,devid,devrev);
+ id=(devid<<8) | devrev;
+
+ if (id==0x0302) {type="37c669"; func=show_parconfig_smsc37c669;}
+ else if (id==0x6582) type="37c665IR";
+ else if ((id==0x6502) && (key==0x44)) type="37c665GT";
+ else if ((id==0x6502) && (key==0x55)) type="37c666GT";
+
+ if(type==NULL)
+ printk("SMSC unknown chip type\n");
+ else
+ printk("SMSC chip type %s\n",type);
+
+ if(func) (func)(efer,key);
+ return;
+}
+
+
+static void winbond_check(int io, int key)
+{
+ int devid,devrev,oldid;
+
+ outb(key,io);
+ outb(key,io); /* Write Magic Sequence to EFER, extended
+ funtion enable register */
+ outb(0x20,io); /* Write EFIR, extended function index register */
+ devid=inb(io+1); /* Read EFDR, extended function data register */
+ outb(0x21,io);
+ devrev=inb(io+1);
+ outb(0x09,io);
+ oldid=inb(io+1);
+ outb(0xaa,io); /* Magic Seal */
+
+ decode_winbond(io,key,devid,devrev,oldid);
+}
+
+static void winbond_check2(int io,int key)
+{
+ int devid,devrev,oldid;
+
+ outb(key,io); /* Write Magic Byte to EFER, extended
+ funtion enable register */
+ outb(0x20,io+2); /* Write EFIR, extended function index register */
+ devid=inb(io+2); /* Read EFDR, extended function data register */
+ outb(0x21,io+1);
+ devrev=inb(io+2);
+ outb(0x09,io+1);
+ oldid=inb(io+2);
+ outb(0xaa,io); /* Magic Seal */
+
+ decode_winbond(io,key,devid,devrev,oldid);
+}
+
+static void smsc_check(int io, int key)
+{
+ int devid,devrev;
+
+ outb(key,io);
+ outb(key,io); /* Write Magic Sequence to EFER, extended
+ funtion enable register */
+ outb(0x0d,io); /* Write EFIR, extended function index register */
+ devid=inb(io+1); /* Read EFDR, extended function data register */
+ outb(0x0e,io);
+ devrev=inb(io+1);
+ outb(0xaa,io); /* Magic Seal */
+
+ decode_smsc(io,key,devid,devrev);
+}
+
+
+static void detect_and_report_winbond (void)
+{
+ printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
+
+ winbond_check(0x3f0,0x87);
+ winbond_check(0x370,0x87);
+ winbond_check(0x2e ,0x87);
+ winbond_check(0x4e ,0x87);
+ winbond_check(0x3f0,0x86);
+ winbond_check2(0x250,0x88);
+ winbond_check2(0x250,0x89);
+}
+
+static void detect_and_report_smsc (void)
+{
+ printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
+ smsc_check(0x3f0,0x55);
+ smsc_check(0x370,0x55);
+ smsc_check(0x3f0,0x44);
+ smsc_check(0x370,0x44);
+}
+
+static int get_superio_dma (struct parport *p)
+{
+ int i=0;
+ while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
+ i++;
+ if (i!=NR_SUPERIOS)
+ return superios[i].dma;
+ return PARPORT_DMA_NONE;
+}
+
+static int get_superio_irq (struct parport *p)
+{
+ int i=0;
+ while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
+ i++;
+ if (i!=NR_SUPERIOS)
+ return superios[i].irq;
+ return PARPORT_IRQ_NONE;
+}
+
+
/* --- Mode detection ------------------------------------- */
/*
static int __devinit parport_ECP_supported(struct parport *pb)
{
int i;
- int config;
+ int config, configb;
int pword;
struct parport_pc_private *priv = pb->private_data;
+ int intrline[]={0,7,9,10,11,14,15,5}; /* Translate ECP
+ intrLine to ISA irq
+ value */
/* If there is no ECR, we have no hope of supporting ECP. */
if (!priv->ecr)
printk (KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
config & 0x80 ? "Level" : "Pulses");
- config = inb (CONFIGB (pb));
- if (!(config & 0x40)) {
+ configb = inb (CONFIGB (pb));
+ if (!(configb & 0x40)) {
printk (KERN_WARNING "0x%lx: IRQ conflict!\n", pb->base);
pb->irq = PARPORT_IRQ_NONE;
}
+ printk (KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
+ pb->base, config, configb);
+ printk (KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
+ if ((configb >>3) & 0x07)
+ printk("%d",intrline[(configb >>3) & 0x07]);
+ else
+ printk("<none or set by other means>");
+ printk ( " dma=");
+ if( (configb & 0x03 ) == 0x00)
+ printk("<none or set by other means>\n");
+ else
+ printk("%d\n",configb & 0x07);
/* Go back to mode 000 */
frob_econtrol (pb, 0xe0, ECR_SPP << 5);
if (priv->ecr) {
pb->irq = programmable_irq_support(pb);
- if (pb->irq != PARPORT_IRQ_NONE)
- goto out;
}
if (pb->modes & PARPORT_MODE_ECP)
if (pb->irq == PARPORT_IRQ_NONE)
pb->irq = irq_probe_SPP(pb);
-out:
+ if (pb->irq == PARPORT_IRQ_NONE)
+ pb->irq = get_superio_irq(pb);
+
return pb->irq;
}
{
const struct parport_pc_private *priv = p->private_data;
if (priv->ecr)
- p->dma = programmable_dma_support(p);
+ p->dma = programmable_dma_support(p); /* ask ECP chipset first */
+ if (p->dma == PARPORT_DMA_NONE)
+ /* ask known Super-IO chips proper, although these
+ claim ECP compatible, some don't report their DMA
+ conforming to ECP standards */
+ p->dma = get_superio_dma(p);
return p->dma;
}
/* --- Initialisation code -------------------------------- */
struct parport *__devinit parport_pc_probe_port (unsigned long int base,
- unsigned long int base_hi,
- int irq, int dma,
- struct pci_dev *dev)
+ unsigned long int base_hi,
+ int irq, int dma,
+ struct pci_dev *dev)
{
struct parport_pc_private *priv;
struct parport_operations *ops;
parport_dma_probe(p);
}
}
- if (p->dma == PARPORT_DMA_AUTO)
+ if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
+ is mandatory (see above) */
p->dma = PARPORT_DMA_NONE;
#ifdef CONFIG_PARPORT_PC_FIFO
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
+static int superio = 0;
MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
MODULE_DESCRIPTION("PC-style parallel port driver");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
MODULE_PARM_DESC(dma, "DMA channel");
MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
+MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
+MODULE_PARM(superio, "i");
int init_module(void)
{
/* Work out how many ports we have, then get parport_share to parse
the irq values. */
unsigned int i;
+ if (superio) {
+ detect_and_report_winbond ();
+ detect_and_report_smsc ();
+ }
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
if (i) {
if (parport_parse_irqs(i, irq, irqval)) return 1;
static int yenta_suspend(pci_socket_t *socket)
{
yenta_set_socket(socket, &dead_socket);
+
+ /*
+ * This does not work currently. The controller
+ * loses too much informationduring D3 to come up
+ * cleanly. We should probably fix yenta_init()
+ * to update all the critical registers, notably
+ * the IO and MEM bridging region data.. That is
+ * something that pci_set_power_state() should
+ * probably know about bridges anyway.
+ *
pci_set_power_state(socket->dev, 3);
+ */
+
return 0;
}
/* scsi_wait_cmd sets the command length */
SRpnt->sr_cmd_len = 0;
- /*
- * FIXME(eric) - need to set the data direction here.
- */
- SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN;
-
+ SRpnt->sr_data_direction = cgc->data_direction;
scsi_wait_req(SRpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen,
SR_TIMEOUT, MAX_RETRIES);
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.3 Feb 22 2000 Ollie Lho
+ * bug fix for record mask setting
* v0.2 Feb 10 2000 Ollie Lho
* add ac97_read_proc for /proc/driver/vnedor/ac97
* v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-#define arraysize(x) (sizeof(x)/sizeof((x)[0]))
+static int sigmatel_init(struct ac97_codec * codec);
+
+#define arraysize(x) (sizeof(x)/sizeof((x)[0]))
static struct {
unsigned int id;
{0x414B4D00, "Asahi Kasei AK4540" , NULL},
{0x41445340, "Analog Devices AD1881" , NULL},
{0x43525900, "Cirrus Logic CS4297" , NULL},
+ {0x43525903, "Cirrus Logic CS4297" , NULL},
{0x43525913, "Cirrus Logic CS4297A" , NULL},
+ {0x43525923, "Cirrus Logic CS4298" , NULL},
{0x43525931, "Cirrus Logic CS4299" , NULL},
- {0x4e534331, "National Semiconductor LM4549", NULL},
+ {0x4e534331, "National Semiconductor LM4549" , NULL},
{0x83847600, "SigmaTel STAC????" , NULL},
{0x83847604, "SigmaTel STAC9701/3/4/5", NULL},
{0x83847605, "SigmaTel STAC9704" , NULL},
{0x83847608, "SigmaTel STAC9708" , NULL},
- {0x83847609, "SigmaTel STAC9721/23" , NULL},
+ {0x83847609, "SigmaTel STAC9721/23" , sigmatel_init},
{0x54524108, "TriTech TR28028" , NULL},
{0x574D4C00, "Wolfson WM9704" , NULL},
{0x00000000, NULL, NULL}
};
static unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
+ [AC97_REC_MIC] = SOUND_MIXER_MIC,
+ [AC97_REC_CD] = SOUND_MIXER_CD,
+ [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
+ [AC97_REC_AUX] = SOUND_MIXER_LINE1,
+ [AC97_REC_LINE] = SOUND_MIXER_LINE,
+ [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
[AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
};
[SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
[SOUND_MIXER_LINE1] = AC97_REC_AUX,
[SOUND_MIXER_LINE] = AC97_REC_LINE,
+ [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
[SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
};
}
#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n", oss_channel, mh->offset, val, ret);
+ printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
+ "0x%04x -> 0x%04x\n",
+ oss_channel, codec->id ? "Secondary" : "Primary",
+ mh->offset, val, ret);
#endif
return ret;
#ifdef DEBUG
printk(" 0x%04x", val);
#endif
+
codec->codec_write(codec, mh->offset, val);
#ifdef DEBUG
if (rw) {
/* read it from the card */
- val = codec->codec_read(codec, 0x1a) & 0x7;
- return ac97_rm2oss[val];
+ val = codec->codec_read(codec, AC97_RECORD_SELECT);
+#ifdef DEBUG
+ printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
+#endif
+ return (1 << ac97_rm2oss[val & 0x07]);
}
/* else, write the first set in the mask as the
val |= val << 8; /* set both channels */
#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%x\n", val);
+ printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
#endif
- codec->codec_write(codec, 0x1a, val);
+ codec->codec_write(codec, AC97_RECORD_SELECT, val);
return 0;
};
return -EINVAL;
/* do we ever want to touch the hardware? */
- val = codec->read_mixer(codec, i);
- /* val = codec->mixer_state[i]; */
- break;
+ /* val = codec->read_mixer(codec, i); */
+ val = codec->mixer_state[i];
+ break;
}
return put_user(val, (int *)arg);
}
codec->codec_write(codec, AC97_RESET, 0L);
if ((cap = codec->codec_read(codec, AC97_RESET)) & 0x8000)
return 0;
-
+
codec->name = NULL;
codec->codec_init = NULL;
+
id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
for (i = 0; i < arraysize(ac97_codec_ids); i++) {
codec->record_sources = AC97_RECORD_MASK;
if (!(cap & 0x04))
codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+ if (!(cap & 0x10))
+ codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
/* generic OSS to AC97 wrapper */
codec->read_mixer = ac97_read_mixer;
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.13 Mar 03 2000 Ollie Lho
+ * new pci_* for 2.4 kernel, back ported to 2.2
+ * v0.12 Feb 23 2000 Ollie Lho
+ * Preliminary Recording support
+ * v0.11.2 Feb 19 2000 Ollie Lho
+ * removed incomplete full-dulplex support
* v0.11.1 Jan 28 2000 Ollie Lho
* small bug in setting sample rate for 4d-nx (reported by Aaron)
* v0.11 Jan 27 2000 Ollie Lho
* Clean up of low level channel register access code. (done)
* Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
* Dual AC97 codecs support (done partially, need channel binding to test)
- * Recording support
+ * Recording support (done)
* Mmap support
* "Channel Binding" ioctl extension
+ * new pci device driver interface for 2.4 kernel
*/
#include <linux/module.h>
#include "trident.h"
-#undef DEBUG
-
-#define DRIVER_VERSION "0.11.1"
+#define DRIVER_VERSION "0.13"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */
-/* The first 32 channels are called Bank A. They are (should be) reserved
- for MIDI synthesizer. But since that is not supported yet, we can (ab)use
- them to play PCM samples */
-#undef ABUSE_BANK_A
-
-/* maxinum number of instances of opening /dev/dspN, can your CPU handle this ?
- NOTE: If /dev/dsp is opened O_RDWR (i.e. full duplex) it will consume 2 HW
- channels */
-#ifdef ABUSE_BANK_A
-#define NR_HW_CH 64
-#else
+#define TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */
+
#define NR_HW_CH 32
-#endif
/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only
have 2 SDATA_IN lines (currently) */
static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
-struct pci_audio_info {
- u16 vendor;
- u16 device;
- char *name;
+enum {
+ TRIDENT_4D_DX = 0,
+ TRIDENT_4D_NX,
+ SIS_7018
};
-static struct pci_audio_info pci_audio_devices[] = {
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, "Trident 4DWave DX"},
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, "Trident 4DWave NX"},
- {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, "SiS 7018 PCI Audio"}
+static char * card_names[] = {
+ "Trident 4DWave DX",
+ "Trident 4DWave NX",
+ "SiS 7018 PCI Audio"
};
+static struct pci_device_id trident_pci_tbl [] __devinitdata = {
+ {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
+ {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX},
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
+};
+
+MODULE_DEVICE_TABLE (pci, trident_pci_tbl);
+
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct trident_state {
unsigned int magic;
/* OSS buffer manangemeent stuff */
void *rawbuf;
+ dma_addr_t dma_handle;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
/* our buffer acts like a circular ring */
- unsigned hwptr; /* where dma last started, update by update_ptr */
+ unsigned hwptr; /* where dma last started, updated by update_ptr */
unsigned swptr; /* where driver last clear/filled, updated by read/write */
- int count; /* bytes to be comsumed by dma machine */
+ int count; /* bytes to be comsumed or been generated by dma machine */
unsigned total_bytes; /* total bytes dmaed by hardware */
unsigned error; /* number of over/underruns */
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
- unsigned endcleared:1;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
- } dma_dac, dma_adc;
+ } dmabuf;
};
/* hardware channels */
struct trident_channel {
int num; /* channel number */
- u32 lba; /* reg 0xe4 */
- u32 eso; /* reg 0xe8 */
- u32 delta;
- u16 attribute; /* reg 0xec */
+ u32 lba; /* Loop Begine Address, where dma buffer starts */
+ u32 eso; /* End Sample Offset, wehre dma buffer ends (in the unit of samples) */
+ u32 delta; /* delta value, sample rate / 48k for playback, 48k/sample rate for recording */
+ u16 attribute; /* control where PCM data go and come */
u16 fm_vol;
- u32 control; /* reg 0xf0 */
+ u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */
};
struct trident_pcm_bank_address {
spinlock_t lock;
/* PCI device stuff */
- struct pci_audio_info *pci_info;
struct pci_dev * pci_dev;
u16 pci_id;
if (bank->bitmap == ~0UL) {
/* no more free channels avaliable */
printk(KERN_ERR "trident: no more channels available on Bank B.\n");
-#ifdef ABUSE_BANK_A
- goto bank_a;
-#endif
return NULL;
}
for (idx = 31; idx >= 0; idx--) {
return channel;
}
}
-
-#ifdef ABUSE_BANK_A
- /* channels in Bank A should be reserved for synthesizer
- not for normal use (channels in Bank A can't record) */
- bank_a:
- bank = &card->banks[BANK_A];
- if (bank->bitmap == ~0UL) {
- /* no more free channels avaliable */
- printk(KERN_ERR "trident: no more channels available on Bank A.\n");
- return NULL;
- }
- for (idx = 31; idx >= 0; idx--) {
- if (!(bank->bitmap & (1 << idx))) {
- struct trident_channel *channel = &bank->channels[idx];
- banks->bitmap |= 1 << idx;
- channel->num = idx;
- return channels;
- }
- }
-#endif
return NULL;
}
{
int bank;
-#ifdef ABUSE_BANK_A
- if (channel < 0 || channel > 63)
- return;
-#else
if (channel < 31 || channel > 63)
return;
-#endif
bank = channel >> 5;
channel = channel & 0x1f;
}
/* called with spin lock held */
-static int trident_write_voice_regs(struct trident_state *state, unsigned int rec)
+static int trident_write_voice_regs(struct trident_state *state)
{
unsigned int data[CHANNEL_REGS + 1];
struct trident_channel *channel;
- if (rec)
- channel = state->dma_adc.channel;
- else
- channel = state->dma_dac.channel;
+ channel = state->dmabuf.channel;
data[1] = channel->lba;
data[4] = channel->control;
return trident_load_channel_registers(state->card, data, channel->num);
}
-static int compute_rate(u32 rate)
+static int compute_rate_play(u32 rate)
{
int delta;
/* We special case 44100 and 8000 since rounding with the equation
return delta;
}
+static int compute_rate_rec(u32 rate)
+{
+ int delta;
+
+ if (rate == 44100)
+ delta = 0x116a;
+ else if (rate == 8000)
+ delta = 0x6000;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = ((48000 << 12) / rate) & 0x0000ffff;
+
+ return delta;
+}
/* set playback sample rate */
static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
if (rate > 48000)
rate = 48000;
rate = 4000;
dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate(rate);
+ dmabuf->channel->delta = compute_rate_play(rate);
- trident_write_voice_regs(state, 0);
+ trident_write_voice_regs(state);
#ifdef DEBUG
printk("trident: called trident_set_dac_rate : rate = %d\n", rate);
/* set recording sample rate */
static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
+
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
dmabuf->rate = rate;
- dmabuf->channel->delta = compute_rate(rate);
+ dmabuf->channel->delta = compute_rate_rec(rate);
- trident_write_voice_regs(state, 1);
+ trident_write_voice_regs(state);
#ifdef DEBUG
printk("trident: called trident_set_adc_rate : rate = %d\n", rate);
/* prepare channel attributes for playback */
static void trident_play_setup(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
struct trident_channel *channel = dmabuf->channel;
channel->lba = virt_to_bus(dmabuf->rawbuf);
- channel->delta = compute_rate(dmabuf->rate);
+ channel->delta = compute_rate_play(dmabuf->rate);
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* FIXME: channel attributes are configured by ioctls, but it is not implemented
- so just set to ZERO for the moment */
+ /* FIXME: channel attributes are configured by ioctls, but it is not
+ implemented so just set to ZERO for the moment */
channel->attribute = 0;
} else {
channel->attribute = 0;
"Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
channel->lba, channel->delta, channel->eso, channel->control);
#endif
- trident_write_voice_regs(state, 0);
+ trident_write_voice_regs(state);
}
/* prepare channel attributes for recording */
{
u16 w;
struct trident_card *card = state->card;
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
struct trident_channel *channel = dmabuf->channel;
/* Enable AC-97 ADC (capture) */
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+ /* enable and set record channel */
+ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
w = inw(TRID_REG(card, T4D_MISCINT));
outw(w | 0x1000, TRID_REG(card, T4D_MISCINT));
+ /* enable and set record channel */
+ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
break;
default:
return;
}
channel->lba = virt_to_bus(dmabuf->rawbuf);
- channel->delta = compute_rate(dmabuf->rate);
+ channel->delta = compute_rate_rec(dmabuf->rate);
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* FIXME: channel attributes are configured by ioctls, but it is not implemented
- so just set to ZERO for the moment */
- channel->attribute = 0;
+ /* FIXME: channel attributes are configured by ioctls, but it is not
+ implemented so just set to 0x8a80 for the moment, record from PCM L/R
+ input and mono = (left + right + 1)/2*/
+ channel->attribute = 0x8A80;
} else {
channel->attribute = 0;
}
"Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
channel->lba, channel->delta, channel->eso, channel->control);
#endif
- trident_write_voice_regs(state, 1);
+ trident_write_voice_regs(state);
}
/* get current playback/recording dma buffer pointer (byte offset from LBA),
called with spinlock held! */
-extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state, unsigned rec)
+extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
u32 cso;
- if (rec)
- dmabuf = &state->dma_adc;
- else
- dmabuf = &state->dma_dac;
-
if (!dmabuf->enable)
return 0;
}
#ifdef DEBUG
- printk("trident: trident_get_dma_addr: chip reported channel: %d, cso = %d\n",
+ printk("trident: trident_get_dma_addr: chip reported channel: %d, "
+ "cso = 0x%04x\n",
dmabuf->channel->num, cso);
#endif
/* ESO and CSO are in units of Samples, convert to byte offset */
/* Stop recording (lock held) */
extern __inline__ void __stop_adc(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
- dmabuf->enable &= ~DMA_RUNNING;
+ dmabuf->enable &= ~ADC_RUNNING;
trident_stop_voice(card, chan_num);
trident_disable_voice_irq(card, chan_num);
}
static void start_adc(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_adc;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
- if ((dmabuf->mapped ||
- dmabuf->count < (signed)(dmabuf->dmasize - 2*dmabuf->fragsize))
- && dmabuf->ready) {
- dmabuf->enable |= DMA_RUNNING;
+ if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) {
+ dmabuf->enable |= ADC_RUNNING;
trident_enable_voice_irq(card, chan_num);
trident_start_voice(card, chan_num);
}
/* stop playback (lock held) */
extern __inline__ void __stop_dac(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
- dmabuf->enable &= ~DMA_RUNNING;
+ dmabuf->enable &= ~DAC_RUNNING;
trident_stop_voice(card, chan_num);
trident_disable_voice_irq(card, chan_num);
}
static void start_dac(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned int chan_num = dmabuf->channel->num;
struct trident_card *card = state->card;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
- dmabuf->enable |= DMA_RUNNING;
+ dmabuf->enable |= DAC_RUNNING;
trident_enable_voice_irq(card, chan_num);
trident_start_voice(card, chan_num);
}
#define DMABUF_MINORDER 1
/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
-static int alloc_dmabuf(struct trident_state *state, unsigned rec)
+static int alloc_dmabuf(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
void *rawbuf;
int order;
unsigned long map, mapend;
- if (rec)
- dmabuf = &state->dma_adc;
- else
- dmabuf = &state->dma_dac;
-
/* alloc as big a chunk as we can, FIXME: is this necessary ?? */
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
- if ((rawbuf = (void *)__get_free_pages(GFP_KERNEL, order)))
+ if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
+ PAGE_SIZE << order,
+ &dmabuf->dma_handle)))
break;
if (!rawbuf)
return -ENOMEM;
+
#ifdef DEBUG
printk("trident: allocated %ld (order = %d) bytes at %p\n",
PAGE_SIZE << order, order, rawbuf);
#endif
- /* for 4DWave and 7018, there are only 30 (31) siginifcan bits for Loop Begin Address
- (LBA) which limits the address space to 1 (2) GB, bad T^2 design */
- if ((virt_to_bus(rawbuf) + (PAGE_SIZE << order) - 1) & ~0x3fffffff) {
- printk(KERN_ERR "trident: DMA buffer beyond 1 GB; "
- "bus address = 0x%lx, size = %ld\n",
- virt_to_bus(rawbuf), PAGE_SIZE << order);
- free_pages((unsigned long)rawbuf, order);
- return -ENOMEM;
- }
-
dmabuf->ready = dmabuf->mapped = 0;
dmabuf->rawbuf = rawbuf;
dmabuf->buforder = order;
}
/* free DMA buffer */
-static void dealloc_dmabuf(struct dmabuf *dmabuf)
+static void dealloc_dmabuf(struct trident_state *state)
{
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long map, mapend;
if (dmabuf->rawbuf) {
/* undo marking the pages as reserved */
mapend = MAP_NR(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
for (map = MAP_NR(dmabuf->rawbuf); map <= mapend; map++)
- clear_bit(PG_reserved, &mem_map[map].flags);
- free_pages((unsigned long)dmabuf->rawbuf, dmabuf->buforder);
+ clear_bit(PG_reserved, &mem_map[map].flags);
+ pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
+ dmabuf->rawbuf, dmabuf->dma_handle);
}
dmabuf->rawbuf = NULL;
dmabuf->mapped = dmabuf->ready = 0;
static int prog_dmabuf(struct trident_state *state, unsigned rec)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned bytepersec;
unsigned bufsize;
unsigned long flags;
int ret;
- if (rec)
- dmabuf = &state->dma_adc;
- else
- dmabuf = &state->dma_dac;
-
spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
- dmabuf->count = dmabuf->error = dmabuf->endcleared = 0;
+ dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf)
- if ((ret = alloc_dmabuf(state, rec)))
+ if ((ret = alloc_dmabuf(state)))
return ret;
/* FIXME: figure out all this OSS fragment stuff */
*/
static void trident_clear_tail(struct trident_state *state)
{
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned swptr;
unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;
unsigned int len;
if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
return;
-
if (swptr < dmabuf->dmasize/2)
len = dmabuf->dmasize/2 - swptr;
else
memset(dmabuf->rawbuf + swptr, silence, len);
- spin_lock_irqsave(&state->card->lock, flags);
+ spin_lock_irqsave(&state->card->lock, flags);
dmabuf->swptr += len;
dmabuf->count += len;
spin_unlock_irqrestore(&state->card->lock, flags);
static int drain_dac(struct trident_state *state, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
unsigned long tmo;
int count;
return 0;
}
-/* call with spinlock held! */
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
static void trident_update_ptr(struct trident_state *state)
{
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned hwptr;
int diff;
- /* update ADC pointer */
- if (state->dma_adc.ready) {
- dmabuf = &state->dma_adc;
- hwptr = trident_get_dma_addr(state, 1);
- diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ /* update hardware pointer */
+ hwptr = trident_get_dma_addr(state);
+ diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+ dmabuf->hwptr = hwptr;
+ dmabuf->total_bytes += diff;
- dmabuf->hwptr = hwptr;
- dmabuf->total_bytes += diff;
- dmabuf->count += diff;
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == ADC_RUNNING) {
+ if (dmabuf->mapped) {
+ dmabuf->count -= diff;
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
+ wake_up(&dmabuf->wait);
+ } else {
+ dmabuf->count += diff;
- if (dmabuf->count >= (signed)dmabuf->fragsize)
- wake_up(&dmabuf->wait);
- if (!dmabuf->mapped) {
- if (dmabuf->count > (signed)(dmabuf->dmasize - ((3 * dmabuf->fragsize) >> 1))) {
- __stop_adc(state);
+ if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+ /* buffer underrun or buffer overrun, we have no way to recover
+ it here, just stop the machine and let the process force hwptr
+ and swptr to sync */
+ __stop_adc(state);
dmabuf->error++;
}
+ /* since dma machine only interrupts at ESO and ESO/2, we sure have at
+ least half of dma buffer free, so wake up the process unconditionally */
+ wake_up(&dmabuf->wait);
}
}
-
- /* update DAC pointer */
- if (state->dma_dac.ready) {
- dmabuf = &state->dma_dac;
- hwptr = trident_get_dma_addr(state, 0);
- diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-
- dmabuf->hwptr = hwptr;
- dmabuf->total_bytes += diff;
-
+ /* error handling and process wake up for DAC */
+ if (dmabuf->enable == DAC_RUNNING) {
if (dmabuf->mapped) {
dmabuf->count += diff;
- if (dmabuf->count >= (signed)dmabuf->fragsize)
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
wake_up(&dmabuf->wait);
- }
- else {
+ } else {
dmabuf->count -= diff;
+
if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
/* buffer underrun or buffer overrun, we have no way to recover
it here, just stop the machine and let the process force hwptr
__stop_dac(state);
dmabuf->error++;
}
- /* since dma machine only interrupts at ESO and ESO/2, we sure have at
+ /* since dma machine only interrupts at ESO and ESO/2, we sure have at
least half of dma buffer free, so wake up the process unconditionally */
wake_up(&dmabuf->wait);
}
} else {
printk("trident: spurious channel irq %d.\n",
63 - i);
- trident_stop_voice(card, i);
- trident_disable_voice_irq(card, i);
+ trident_stop_voice(card, 63 - i);
+ trident_disable_voice_irq(card, 63 - i);
}
}
}
}
- if (event & SB_IRQ){
- /* Midi - TODO */
- }
-
/* manually clear interrupt status, bad hardware design, blame T^2 */
outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
TRID_REG(card, T4D_MISCINT));
return -ESPIPE;
}
-/* in this loop, dma_adc.count signifies the amount of data thats waiting
- to be copied to the user's buffer. it is filled by the interrupt
- handler and drained by this loop. */
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
+ the user's buffer. it is filled by the dma machine and drained by this loop. */
static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
+#ifdef DEBUG
+ printk("trident: trident_read called, count = %d\n", count);
+#endif
+
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
return -ESPIPE;
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
+ if (dmabuf->count > (signed) dmabuf->dmasize) {
+ /* buffer overrun, we are recovering from sleep_on_timeout,
+ resync hwptr and swptr, make process flush the buffer */
+ dmabuf->count = dmabuf->dmasize;
+ dmabuf->swptr = dmabuf->hwptr;
+ }
swptr = dmabuf->swptr;
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count < cnt)
if (cnt > count)
cnt = count;
-
if (cnt <= 0) {
+ unsigned long tmo;
+ /* buffer is empty, start the dma machine and wait for data to be
+ recorded */
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
- ret = ret ? ret : -EAGAIN;
+ if (!ret) ret = -EAGAIN;
return ret;
}
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, HZ)) {
- printk(KERN_ERR
- "(trident) read: chip lockup? "
+ /* No matter how much space left in the buffer, we have to wait untill
+ CSO == ESO/2 or CSO == ESO when address engine interrupts */
+ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
+ tmo >>= sample_shift[dmabuf->fmt];
+ /* There are two situations when sleep_on_timeout returns, one is when
+ the interrupt is serviced correctly and the process is waked up by
+ ISR ON TIME. Another is when timeout is expired, which means that
+ either interrupt is NOT serviced correctly (pending interrupt) or it
+ is TOO LATE for the process to be scheduled to run (scheduler latency)
+ which results in a (potential) buffer overrun. And worse, there is
+ NOTHING we can do to prevent it. */
+ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+#ifdef DEBUG
+ printk(KERN_ERR "trident: recording schedule timeout, "
"dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
dmabuf->hwptr, dmabuf->swptr);
- stop_adc(state);
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->count = 0;
- dmabuf->hwptr = 0;
- dmabuf->swptr = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
+#endif
+ /* a buffer overrun, we delay the recovery untill next time the
+ while loop begin and we REALLY have space to record */
}
if (signal_pending(current)) {
ret = ret ? ret : -ERESTARTSYS;
}
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
- ret = ret ? ret : -EFAULT;
+ if (!ret) ret = -EFAULT;
return ret;
}
ret += cnt;
start_adc(state);
}
-
return ret;
}
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+ the soundcard. it is drained by the dma machine and filled by this loop. */
static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf = &state->dma_dac;
+ struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned swptr;
#ifdef DEBUG
printk("trident: trident_write called, count = %d\n", count);
-#endif
+#endif
VALIDATE_STATE(state);
if (ppos != &file->f_pos)
cnt = count;
if (cnt <= 0) {
unsigned long tmo;
- /* buffer is full, start the dma machine and wait for data to be played */
+ /* buffer is full, start the dma machine and wait for data to be
+ played */
start_dac(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
CSO == ESO/2 or CSO == ESO when address engine interrupts */
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
tmo >>= sample_shift[dmabuf->fmt];
- /* There are two situations when sleep_on_timeout returns, one is when the
- interrupt is serviced correctly and the process is waked up by ISR ON TIME.
- Another is when timeout is expired, which means that either interrupt is NOT
- serviced correctly (pending interrupt) or it is TOO LATE for the process to
- be scheduled to run (scheduler latency) which results in a (potential) buffer
- underrun. And worse, there is NOTHING we can do to prevent it. */
+ /* There are two situations when sleep_on_timeout returns, one is when
+ the interrupt is serviced correctly and the process is waked up by
+ ISR ON TIME. Another is when timeout is expired, which means that
+ either interrupt is NOT serviced correctly (pending interrupt) or it
+ is TOO LATE for the process to be scheduled to run (scheduler latency)
+ which results in a (potential) buffer underrun. And worse, there is
+ NOTHING we can do to prevent it. */
if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
#ifdef DEBUG
- printk(KERN_ERR "trident: schedule timeout, "
+ printk(KERN_ERR "trident: playback schedule timeout, "
"dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
dmabuf->hwptr, dmabuf->swptr);
spin_lock_irqsave(&state->card->lock, flags);
dmabuf->swptr = swptr;
dmabuf->count += cnt;
- dmabuf->endcleared = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait)
{
- struct trident_state *s = (struct trident_state *)file->private_data;
+ struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
unsigned int mask = 0;
- VALIDATE_STATE(s);
+ VALIDATE_STATE(state);
if (file->f_mode & FMODE_WRITE)
- poll_wait(file, &s->dma_dac.wait, wait);
+ poll_wait(file, &dmabuf->wait, wait);
if (file->f_mode & FMODE_READ)
- poll_wait(file, &s->dma_adc.wait, wait);
+ poll_wait(file, &dmabuf->wait, wait);
- spin_lock_irqsave(&s->card->lock, flags);
- trident_update_ptr(s);
+ spin_lock_irqsave(&state->card->lock, flags);
+ trident_update_ptr(state);
if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ if (dmabuf->mapped) {
+ if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
- if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+ if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
}
}
- spin_unlock_irqrestore(&s->card->lock, flags);
+ spin_unlock_irqrestore(&state->card->lock, flags);
return mask;
}
static int trident_mmap(struct file *file, struct vm_area_struct *vma)
{
struct trident_state *state = (struct trident_state *)file->private_data;
- struct dmabuf *dmabuf;
+ struct dmabuf *dmabuf = &state->dmabuf;
int ret;
unsigned long size;
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf(state, 0)) != 0)
return ret;
- dmabuf = &state->dma_dac;
} else if (vma->vm_flags & VM_READ) {
if ((ret = prog_dmabuf(state, 1)) != 0)
return ret;
- dmabuf = &state->dma_adc;
} else
return -EINVAL;
static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret;
VALIDATE_STATE(state);
- mapped = ((file->f_mode & FMODE_WRITE) && state->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && state->dma_adc.mapped);
+ mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
+ ((file->f_mode & FMODE_READ) && dmabuf->mapped);
#ifdef DEBUG
printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
_IOC_NR(cmd), arg ? *(int *)arg : 0);
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_RESET:
+ /* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
synchronize_irq();
- state->dma_dac.ready = 0;
- state->dma_dac.swptr = state->dma_dac.hwptr = 0;
- state->dma_dac.count = state->dma_dac.total_bytes = 0;
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
synchronize_irq();
- state->dma_adc.ready = 0;
- state->dma_adc.swptr = state->dma_adc.hwptr = 0;
- state->dma_adc.count = state->dma_adc.total_bytes = 0;
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
}
return 0;
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
trident_set_dac_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
trident_set_adc_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
- return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate :
- state->dma_dac.rate,
- (int *)arg);
+ return put_user(dmabuf->rate, (int *)arg);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val)
- state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val)
- state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
return 0;
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
return val;
- return put_user(state->dma_dac.fragsize, (int *)arg);
+ return put_user(dmabuf->fragsize, (int *)arg);
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf(state, 1)))
return val;
- return put_user(state->dma_adc.fragsize, (int *)arg);
+ return put_user(dmabuf->fragsize, (int *)arg);
}
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val == AFMT_S16_LE)
- state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_16BIT;
+ dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val == AFMT_S16_LE)
- state->dma_adc.fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_16BIT;
+ dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
}
- if (file->f_mode & FMODE_WRITE)
- return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ?
- AFMT_S16_LE : AFMT_U8, (int *)arg);
- else
- return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ?
- AFMT_S16_LE : AFMT_U8, (int *)arg);
+ return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
case SNDCTL_DSP_CHANNELS:
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 0) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- state->dma_dac.ready = 0;
+ dmabuf->ready = 0;
if (val >= 2)
- state->dma_dac.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- state->dma_adc.ready = 0;
+ dmabuf->ready = 0;
if (val >= 2)
- state->dma_adc.fmt |= TRIDENT_FMT_STEREO;
+ dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
- state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO;
+ dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
}
- if (file->f_mode & FMODE_WRITE)
- return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *)arg);
- else
- return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *)arg);
+ return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
+
case SNDCTL_DSP_POST:
/* FIXME: the same as RESET ?? */
return 0;
case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && state->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && state->dma_dac.subdivision))
+ if (dmabuf->subdivision)
return -EINVAL;
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
- if (file->f_mode & FMODE_READ)
- state->dma_adc.subdivision = val;
- if (file->f_mode & FMODE_WRITE)
- state->dma_dac.subdivision = val;
+ dmabuf->subdivision = val;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
get_user_ret(val, (int *)arg, -EFAULT);
- if (file->f_mode & FMODE_READ) {
- state->dma_adc.ossfragshift = val & 0xffff;
- state->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (state->dma_adc.ossfragshift < 4)
- state->dma_adc.ossfragshift = 4;
- if (state->dma_adc.ossfragshift > 15)
- state->dma_adc.ossfragshift = 15;
- if (state->dma_adc.ossmaxfrags < 4)
- state->dma_adc.ossmaxfrags = 4;
- }
- if (file->f_mode & FMODE_WRITE) {
- state->dma_dac.ossfragshift = val & 0xffff;
- state->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (state->dma_dac.ossfragshift < 4)
- state->dma_dac.ossfragshift = 4;
- if (state->dma_dac.ossfragshift > 15)
- state->dma_dac.ossfragshift = 15;
- if (state->dma_dac.ossmaxfrags < 4)
- state->dma_dac.ossmaxfrags = 4;
- }
+
+ dmabuf->ossfragshift = val & 0xffff;
+ dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+ if (dmabuf->ossfragshift < 4)
+ dmabuf->ossfragshift = 4;
+ if (dmabuf->ossfragshift > 15)
+ dmabuf->ossfragshift = 15;
+ if (dmabuf->ossmaxfrags < 4)
+ dmabuf->ossmaxfrags = 4;
+
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
- if (!state->dma_dac.enable && (val = prog_dmabuf(state, 0)) != 0)
+ if (!dmabuf->enable && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
- abinfo.fragsize = state->dma_dac.fragsize;
- abinfo.bytes = state->dma_dac.dmasize - state->dma_dac.count;
- abinfo.fragstotal = state->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> state->dma_dac.fragshift;
+ abinfo.fragsize = dmabuf->fragsize;
+ abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+ abinfo.fragstotal = dmabuf->numfrag;
+ abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
- if (!state->dma_adc.enable && (val = prog_dmabuf(state, 1)) != 0)
+ if (!dmabuf->enable && (val = prog_dmabuf(state, 1)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
- abinfo.fragsize = state->dma_adc.fragsize;
- abinfo.bytes = state->dma_adc.count;
- abinfo.fragstotal = state->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> state->dma_adc.fragshift;
+ abinfo.fragsize = dmabuf->fragsize;
+ abinfo.bytes = dmabuf->count;
+ abinfo.fragstotal = dmabuf->numfrag;
+ abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
return 0;
case SNDCTL_DSP_GETCAPS:
- return put_user(/* DSP_CAP_DUPLEX|*/DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
- (int *)arg);
+ return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
- if (file->f_mode & FMODE_READ && state->dma_adc.enable)
+ if (file->f_mode & FMODE_READ && dmabuf->enable)
val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && state->dma_dac.enable)
+ if (file->f_mode & FMODE_WRITE && dmabuf->enable)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
- if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
start_adc(state);
- }
- else
+ } else
stop_adc(state);
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
- if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0)))
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
start_dac(state);
- }
- else
+ } else
stop_dac(state);
}
return 0;
return -EINVAL;
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
- cinfo.bytes = state->dma_adc.total_bytes;
- cinfo.blocks = state->dma_adc.count >> state->dma_adc.fragshift;
- cinfo.ptr = state->dma_adc.hwptr;
- if (state->dma_adc.mapped)
- state->dma_adc.count &= state->dma_adc.fragsize-1;
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.ptr = dmabuf->hwptr;
+ if (dmabuf->mapped)
+ dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
-
+
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
- cinfo.bytes = state->dma_dac.total_bytes;
- cinfo.blocks = state->dma_dac.count >> state->dma_dac.fragshift;
- cinfo.ptr = state->dma_dac.hwptr;
- if (state->dma_dac.mapped)
- state->dma_dac.count &= state->dma_dac.fragsize-1;
+ cinfo.bytes = dmabuf->total_bytes;
+ cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.ptr = dmabuf->hwptr;
+ if (dmabuf->mapped)
+ dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_SETDUPLEX:
- /* XXX fix */
- return 0;
+ return -EINVAL;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
- val = state->dma_dac.count;
+ val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
return put_user(val, (int *)arg);
case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate :
- state->dma_dac.rate, (int *)arg);
+ return put_user(dmabuf->rate, (int *)arg);
case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_WRITE)
- return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *)arg);
- else
- return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *)arg);
-
+ return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+ (int *)arg);
+
case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_WRITE)
- return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ?
- AFMT_S16_LE : AFMT_U8, (int *)arg);
- else
- return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ?
- AFMT_S16_LE : AFMT_U8, (int *)arg);
+ return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+ AFMT_S16_LE : AFMT_U8, (int *)arg);
+
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
found_virt:
/* found a free virtual channel, allocate hardware channels */
- if (file->f_mode & FMODE_READ)
- if ((state->dma_adc.channel = trident_alloc_pcm_channel(card)) == NULL) {
- kfree (card->states[i]);
- card->states[i] = NULL;;
- return -ENODEV;
- }
- if (file->f_mode & FMODE_WRITE)
- if ((state->dma_dac.channel = trident_alloc_pcm_channel(card)) == NULL) {
- kfree (card->states[i]);
- card->states[i] = NULL;
- if (file->f_mode & FMODE_READ)
- /* free previously allocated hardware channel */
- trident_free_pcm_channel(card, state->dma_adc.channel->num);
- return -ENODEV;
- }
+ if ((state->dmabuf.channel = trident_alloc_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;;
+ return -ENODEV;
+ }
/* initialize the virtual channel */
state->virt = i;
state->card = card;
state->magic = TRIDENT_STATE_MAGIC;
- init_waitqueue_head(&state->dma_adc.wait);
- init_waitqueue_head(&state->dma_dac.wait);
+ init_waitqueue_head(&state->dmabuf.wait);
init_MUTEX(&state->open_sem);
file->private_data = state;
down(&state->open_sem);
- /* set default sample format, Refer to OSS Programmer's Guide */
- if (file->f_mode & FMODE_READ) {
- /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample */
- state->dma_adc.fmt = TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT;
- state->dma_adc.ossfragshift = 0;
- state->dma_adc.ossmaxfrags = 0;
- state->dma_adc.subdivision = 0;
- trident_set_adc_rate(state, 48000);
- }
-
- /* according to OSS document, /dev/dsp should be default to unsigned 8-bits,
- mono, with sample rate 8kHz and /dev/dspW will accept 16-bits sample */
+ /* set default sample format. According to OSS Programmer's Guide /dev/dsp
+ should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+ /dev/dspW will accept 16-bits sample */
if (file->f_mode & FMODE_WRITE) {
- state->dma_dac.fmt &= ~TRIDENT_FMT_MASK;
+ state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
if ((minor & 0xf) == SND_DEV_DSP16)
- state->dma_dac.fmt |= TRIDENT_FMT_16BIT;
- state->dma_dac.ossfragshift = 0;
- state->dma_dac.ossmaxfrags = 0;
- state->dma_dac.subdivision = 0;
+ state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
+ state->dmabuf.ossfragshift = 0;
+ state->dmabuf.ossmaxfrags = 0;
+ state->dmabuf.subdivision = 0;
trident_set_dac_rate(state, 8000);
}
+ if (file->f_mode & FMODE_READ) {
+ /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample,
+ to be dealed with in trident_set_adc_rate() ?? */
+ state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
+ state->dmabuf.ossfragshift = 0;
+ state->dmabuf.ossmaxfrags = 0;
+ state->dmabuf.subdivision = 0;
+ trident_set_adc_rate(state, 8000);
+ }
+
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&state->open_sem);
- //FIXME put back in
- //MOD_INC_USE_COUNT;
+ MOD_INC_USE_COUNT;
return 0;
}
static int trident_release(struct inode *inode, struct file *file)
{
struct trident_state *state = (struct trident_state *)file->private_data;
+ struct dmabuf *dmabuf = &state->dmabuf;
VALIDATE_STATE(state);
-
if (file->f_mode & FMODE_WRITE) {
trident_clear_tail(state);
drain_dac(state, file->f_flags & O_NONBLOCK);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
- dealloc_dmabuf(&state->dma_dac);
- trident_free_pcm_channel(state->card, state->dma_dac.channel->num);
+ dealloc_dmabuf(state);
+ trident_free_pcm_channel(state->card, dmabuf->channel->num);
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
- dealloc_dmabuf(&state->dma_adc);
- trident_free_pcm_channel(state->card, state->dma_adc.channel->num);
+ dealloc_dmabuf(state);
+ trident_free_pcm_channel(state->card, dmabuf->channel->num);
}
kfree(state->card->states[state->virt]);
/* we're covered by the open_sem */
up(&state->open_sem);
- //FIXME put back in
- //MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
return 0;
}
};
/* trident specific AC97 functions */
-/* Write AC97 mixer registers */
+/* Write AC97 codec registers */
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
{
struct trident_card *card = (struct trident_card *)codec->private_data;
address = SI_AC97_WRITE;
mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
if (codec->id)
- mask |= SI_AC97_SECONDARY;
+ mask |= SI_AC97_SECONDARY;
busy = SI_AC97_BUSY_WRITE;
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
address = NX_ACR1_AC97_W;
mask = NX_AC97_BUSY_WRITE;
if (codec->id)
- mask |= NX_AC97_WRITE_SECONDARY;
+ mask |= NX_AC97_WRITE_SECONDARY;
busy = NX_AC97_BUSY_WRITE;
break;
}
match:
file->private_data = card->ac97_codec[i];
- //FIXME put back in
- //MOD_INC_USE_COUNT;
+ MOD_INC_USE_COUNT;
return 0;
}
static int trident_release_mixdev(struct inode *inode, struct file *file)
{
- //struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
-
- //FIXME put back in
- //MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
return 0;
}
}
/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
- untill "ACCESS" time (in prog_dmabuf calles by open/read/write/ioctl/mmap) */
-static int __init trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info)
+ untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
- u16 w;
unsigned long iobase;
struct trident_card *card;
- iobase = pcidev->resource[0].start;
+ if (!pci_dma_supported(pci_dev, TRIDENT_DMA_MASK)) {
+ printk(KERN_ERR "trident: architecture does not support"
+ " 30bit PCI busmaster DMA\n");
+ return -1;
+ }
+
+ iobase = pci_dev->resource[0].start;
if (check_region(iobase, 256)) {
printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n",
iobase);
- return 0;
+ return -1;
}
- /* just to be sure that IO space and bus master is on */
- pci_set_master(pcidev);
- pci_read_config_word(pcidev, PCI_COMMAND, &w);
- w |= PCI_COMMAND_IO|PCI_COMMAND_MASTER;
- pci_write_config_word(pcidev, PCI_COMMAND, w);
-
if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "trident: out of memory\n");
- return 0;
+ return -1;
}
memset(card, 0, sizeof(*card));
card->iobase = iobase;
- card->pci_info = pci_info;
- card->pci_id = pci_info->device;
- card->irq = pcidev->irq;
+ card->pci_dev = pci_dev;
+ card->pci_id = pci_id->device;
+ card->irq = pci_dev->irq;
card->next = devs;
card->magic = TRIDENT_CARD_MAGIC;
card->banks[BANK_A].addresses = &bank_a_addrs;
card->banks[BANK_B].addresses = &bank_b_addrs;
card->banks[BANK_B].bitmap = 0UL;
spin_lock_init(&card->lock);
- devs = card;
+ devs = card;
+
+ pci_set_master(pci_dev);
+ pci_enable_device(pci_dev);
printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n",
- card->pci_info->name, card->iobase, card->irq);
+ card_names[pci_id->driver_data], card->iobase, card->irq);
/* claim our iospace and irq */
- request_region(card->iobase, 256, card->pci_info->name);
- if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, card->pci_info->name, card)) {
+ request_region(card->iobase, 256, card_names[pci_id->driver_data]);
+ if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ,
+ card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq);
release_region(card->iobase, 256);
kfree(card);
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return 0;
+ return -1;
}
/* initilize AC97 codec and register /dev/mixer */
if (trident_ac97_init(card) <= 0) {
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return 0;
+ return -1;
}
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
+ pci_dev->driver_data = card;
+ pci_dev->dma_mask = TRIDENT_DMA_MASK;
+
/* Enable Address Engine Interrupts */
trident_enable_loop_interrupts(card);
- return 1;
+ return 0;
}
-static int __init init_trident(void)
+static void __devexit trident_remove(struct pci_dev *pci_dev)
{
- struct pci_dev *pcidev = NULL;
- int foundone = 0;
int i;
+ struct trident_card *card = pci_dev->driver_data;
- if (!pci_present()) /* No PCI bus in this machine! */
- return -ENODEV;
+ /* Kill interrupts, and SP/DIF */
+ trident_disable_loop_interrupts(card);
- printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
- DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+ /* free hardware resources */
+ free_irq(card->irq, devs);
+ release_region(card->iobase, 256);
- for (i = 0; i < sizeof (pci_audio_devices); i++) {
- pcidev = NULL;
- while ((pcidev = pci_find_device(pci_audio_devices[i].vendor,
- pci_audio_devices[i].device,
- pcidev)) != NULL) {
- foundone += trident_install(pcidev, pci_audio_devices + i);
+ /* unregister audio devices */
+ for (i = 0; i < NR_AC97; i++)
+ if (devs->ac97_codec[i] != NULL) {
+ unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+ kfree (card->ac97_codec[i]);
}
- }
+ unregister_sound_dsp(card->dev_audio);
- if (!foundone)
- return -ENODEV;
- return 0;
+ kfree(card);
}
MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho");
MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver");
-static void __exit cleanup_trident(void)
-{
- while (devs != NULL) {
- int i;
- /* Kill interrupts, and SP/DIF */
- trident_disable_loop_interrupts(devs);
+#define TRIDENT_MODULE_NAME "trident"
- /* free hardware resources */
- free_irq(devs->irq, devs);
- release_region(devs->iobase, 256);
+static struct pci_driver trident_pci_driver = {
+ name: TRIDENT_MODULE_NAME,
+ id_table: trident_pci_tbl,
+ probe: trident_probe,
+ remove: trident_remove,
+};
- /* unregister audio devices */
- for (i = 0; i < NR_AC97; i++)
- if (devs->ac97_codec[i] != NULL) {
- unregister_sound_mixer(devs->ac97_codec[i]->dev_mixer);
- kfree (devs->ac97_codec[i]);
- }
- unregister_sound_dsp(devs->dev_audio);
+static int __init trident_init_module (void)
+{
+ printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
+ DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
- kfree(devs);
- devs = devs->next;
- }
+ return pci_module_init (&trident_pci_driver);
+}
+
+static void __exit trident_cleanup_module (void)
+{
+ pci_unregister_driver (&trident_pci_driver);
}
-module_init(init_trident);
-module_exit(cleanup_trident);
+module_init(trident_init_module);
+module_exit(trident_cleanup_module);
#define TRIDENT_FMT_16BIT 0x02
#define TRIDENT_FMT_MASK 0x03
-#define DMA_ENABLE 0x01
-#define DMA_RUNNING 0x02
-
+#define DAC_RUNNING 0x01
+#define ADC_RUNNING 0x02
/* Register Addresses */
/* operational registers common to DX, NX, 7018 */
enum trident_op_registers {
+ T4D_REC_CH = 0x70,
T4D_START_A = 0x80, T4D_STOP_A = 0x84,
T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c,
T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94,
dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB
- dep_tristate ' USB ADMteks Pegasus based devices support' CONFIG_USB_PEGASUS $CONFIG_USB
+ dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB
dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB
comment 'USB HID'
int status = TD_CC_NOERROR;
__u32 datab[4];
- __u8 * data_buf = datab;
+ __u8 * data_buf = (__u8 *) datab;
__u16 bmRType_bReq;
__u16 wValue;
urb_value , 0,
buf, 0, HZ * 5));
}
+
+ return -ENOIOCTLCMD;
}
#endif /* CONFIG_USB_SERIAL_FTDI_SIO */
rinfo->riva.Architecture = rci->arch_rev;
rinfo->pd = pd;
- rinfo->base0_region_size = pci_resource_start (pd, 0);
- rinfo->base1_region_size = pci_resource_start (pd, 1);
+ rinfo->base0_region_size = pci_resource_len (pd, 0);
+ rinfo->base1_region_size = pci_resource_len (pd, 1);
assert (rinfo->base0_region_size >= 0x00800000); /* from GGI */
assert (rinfo->base0_region_size >= 0x01000000); /* from GGI */
/* syncing will go here */
if (kind == BDEV_FILE || kind == BDEV_FS)
fsync_dev(rdev);
- if (atomic_dec_and_test(&bdev->bd_openers) && MAJOR(rdev) != RAMDISK_MAJOR) {
+ if (atomic_dec_and_test(&bdev->bd_openers)) {
/* invalidating buffers will go here */
invalidate_buffers(rdev);
}
}
}
#ifdef __powerpc__
- entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
- if (entry)
- entry->proc_fops = &ppc_htab_operations;
+ {
+ extern struct file_operations ppc_htab_operations;
+ entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
+ if (entry)
+ entry->proc_fops = &ppc_htab_operations;
+ }
#endif
}
proc_mkdir("openprom", 0);
#endif
proc_tty_init();
-#ifdef __powerpc__
- proc_register(&proc_root, &proc_root_ppc_htab);
-#endif
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
#ifdef __KERNEL__
+#include <linux/config.h>
#include <linux/types.h>
#ifndef LINUX_VERSION_CODE
#define _PPC_TYPES_H
#ifndef __ASSEMBLY__
-/*
- * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
- * header files exported to user space
- */
+#ifdef __KERNEL__
typedef unsigned short umode_t;
u32 u[4];
} __attribute((aligned(16))) vector128;
-#ifdef __KERNEL__
-
#define BITS_PER_LONG 32
/* DMA addresses are 32-bits wide */
/* record mux defines */
#define AC97_RECMUX_MIC 0x0000
#define AC97_RECMUX_CD 0x0101
-#define AC97_RECMUX_VIDEO 0x0202 /* not used */
-#define AC97_RECMUX_AUX 0x0303
-#define AC97_RECMUX_LINE 0x0404
+#define AC97_RECMUX_VIDEO 0x0202
+#define AC97_RECMUX_AUX 0x0303
+#define AC97_RECMUX_LINE 0x0404
#define AC97_RECMUX_STEREO_MIX 0x0505
#define AC97_RECMUX_MONO_MIX 0x0606
#define AC97_RECMUX_PHONE 0x0707
-
/* general purpose register bit defines */
#define AC97_GP_LPBK 0x0080 /* Loopback mode */
#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */
#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */
#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */
-
/* powerdown control and status bit defines */
/* status */
SOUND_MASK_PHONEIN|SOUND_MASK_PHONEOUT)
#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
- SOUND_MASK_CD|SOUND_MASK_VIDEO|\
+ SOUND_MASK_CD|SOUND_MASK_IGAIN|SOUND_MASK_VIDEO|\
SOUND_MASK_LINE1| SOUND_MASK_LINE|\
SOUND_MASK_PHONEIN)
* 1994, 1995 Eberhard Moenkeberg, emoenke@gwdg.de
* 1996 David van Leeuwen, david@tm.tno.nl
* 1997, 1998 Erik Andersen, andersee@debian.org
- * 1998, 1999 Jens Axboe, axboe@image.dk
+ * 1998-2000 Jens Axboe, axboe@suse.de
*/
#ifndef _LINUX_CDROM_H
#define _LINUX_CDROM_H
-#include <linux/types.h>
#include <asm/byteorder.h>
/*******************************************************
struct cdrom_read
{
int cdread_lba;
- caddr_t cdread_bufaddr;
+ char *cdread_bufaddr;
int cdread_buflen;
};
#define CDROM_PACKET_SIZE 12
+#define CGC_DATA_UNKNOWN 0
+#define CGC_DATA_WRITE 1
+#define CGC_DATA_READ 2
+#define CGC_DATA_NONE 3
+
/* for CDROM_PACKET_COMMAND ioctl */
struct cdrom_generic_command
{
unsigned int buflen;
int stat;
struct request_sense *sense;
+ unsigned char data_direction;
void *reserved[3];
};
struct cdrom_generic_command *cgc,
int page_code, int page_control);
extern void init_cdrom_command(struct cdrom_generic_command *cgc,
- void *buffer, int len);
+ void *buffer, int len, int type);
+extern struct cdrom_device_info *cdrom_find_device(kdev_t dev);
typedef struct {
__u16 disc_information_length;
__u8 reserved1 : 3;
__u8 erasable : 1;
__u8 border_status : 2;
- __u8 disc_border : 2;
+ __u8 disc_status : 2;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
- __u8 disc_border : 2;
+ __u8 disc_status : 2;
__u8 border_status : 2;
__u8 erasable : 1;
__u8 reserved1 : 3;
};
typedef struct {
- struct mode_page_header header;
#if defined(__BIG_ENDIAN_BITFIELD)
__u8 ps : 1;
__u8 reserved1 : 1;
__u8 subhdr1;
__u8 subhdr2;
__u8 subhdr3;
-} write_param_page __attribute__((packed));
+} __attribute__((packed)) write_param_page;
#endif /* End of kernel only stuff */
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd6580, ide_umc8672, ide_ht6560b,
ide_pdc4030, ide_rz1000, ide_trm290,
- ide_cmd646, ide_cy82c693, ide_4drives
+ ide_cmd646, ide_cy82c693, ide_4drives,
+ ide_pmac
} hwif_chipset_t;
#ifdef CONFIG_BLK_DEV_IDEPCI
swap_free(entry);
if ((shp != shm_lock(shp->id)) && (is_shmzero == 0))
BUG();
- if (is_shmzero) shm_swp--;
+ if (is_shmzero == 0) shm_swp--;
}
- if (is_shmzero) shm_rss++;
+ if (is_shmzero == 0) shm_rss++;
pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
SHM_ENTRY(shp, idx) = pte;
} else
if [ "$CONFIG_IPX" != "n" ]; then
source net/ipx/Config.in
fi
-tristate 'Appletalk DDP' CONFIG_ATALK
+tristate 'Appletalk protocol support' CONFIG_ATALK
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'DECnet Support (EXPERIMENTAL)' CONFIG_DECNET
if [ "$CONFIG_DECNET" != "n" ]; then
EXPORT_SYMBOL(nf_unregister_queue_handler);
EXPORT_SYMBOL(nf_hook_slow);
EXPORT_SYMBOL(nf_hooks);
+EXPORT_SYMBOL(nf_setsockopt);
+EXPORT_SYMBOL(nf_getsockopt);
#endif
EXPORT_SYMBOL(register_gifconf);