# HG changeset patch # User Florian Pose # Date 1137522495 0 # Node ID 7506e67dd122cd5a7be853bd65b151b23adcda7c # Parent 6b3b8acb71b5ca295fec06c2f94be6a73fd69f50 Vereinheitlichte Schnittstellen, Include-Verzeichnis und Module getrennt. diff -r 6b3b8acb71b5 -r 7506e67dd122 Makefile --- a/Makefile Fri Jan 13 15:47:44 2006 +0000 +++ b/Makefile Tue Jan 17 18:28:15 2006 +0000 @@ -13,7 +13,7 @@ #------------------------------------------------------------------------------ # Kbuild-Abschnitt -obj-m := drivers/ rt/ mini/ +obj-m := master/ devices/ rt/ mini/ #------------------------------------------------------------------------------ @@ -38,10 +38,6 @@ @echo "# EtherCAT Konfigurationsdatei Kernel 2.6" > $(CONFIG_FILE) @echo >> $(CONFIG_FILE) @echo "KERNELDIR = /usr/src/linux" >> $(CONFIG_FILE) - @echo "RTAIDIR =" >> $(CONFIG_FILE) - @echo "RTLIBDIR =" >> $(CONFIG_FILE) - @echo >> $(CONFIG_FILE) - @echo "MAKE_RT = yes" >> $(CONFIG_FILE) @echo >> $(CONFIG_FILE) @echo "$(CONFIG_FILE) erstellt." diff -r 6b3b8acb71b5 -r 7506e67dd122 devices/8139too.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/8139too.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,3016 @@ +/****************************************************************************** + * + * drv_8139too.c + * + * EtherCAT-Treiber für RTL8139-kompatible Netzwerkkarten. + * + * Autoren: Wilhelm Hagemeister, Florian Pose + * + * $Date$ + * $Author$ + * + * (C) Copyright IgH 2005 + * Ingenieurgemeinschaft IgH + * Heinz-Bäcker Str. 34 + * D-45356 Essen + * Tel.: +49 201/61 99 31 + * Fax.: +49 201/61 98 36 + * E-mail: sp@igh-essen.com + * + ******************************************************************************/ + +/* + 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. + + Maintained by Jeff Garzik + Copyright 2000-2002 Jeff Garzik + + Much code comes from Donald Becker's rtl8139.c driver, + versions 1.13 and older. This driver was originally based + on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: + + ---------- + + Written 1997-2001 by Donald Becker. + This software may be used and distributed according to the + terms of the GNU General Public License (GPL), incorporated + herein by reference. Drivers based on or derived from this + code fall under the GPL and must retain the authorship, + copyright and license notice. This file is not a complete + program and may only be used when the entire operating + system is licensed under the GPL. + + This driver is for boards based on the RTL8129 and RTL8139 + PCI ethernet chips. + + The author may be reached as becker@scyld.com, or C/O Scyld + Computing Corporation 410 Severn Ave., Suite 210 Annapolis + MD 21403 + + Support and updates available at + http://www.scyld.com/network/rtl8139.html + + Twister-tuning table provided by Kinston + . + + ---------- + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Contributors: + + Donald Becker - he wrote the original driver, kudos to him! + (but please don't e-mail him for support, this isn't his driver) + + Tigran Aivazian - bug fixes, skbuff free cleanup + + Martin Mares - suggestions for PCI cleanup + + David S. Miller - PCI DMA and softnet updates + + Ernst Gill - fixes ported from BSD driver + + Daniel Kobras - identified specific locations of + posted MMIO write bugginess + + Gerard Sharp - bug fix, testing and feedback + + David Ford - Rx ring wrap fix + + Dan DeMaggio - swapped RTL8139 cards with me, and allowed me + to find and fix a crucial bug on older chipsets. + + Donald Becker/Chris Butterworth/Marcus Westergren - + Noticed various Rx packet size-related buglets. + + Santiago Garcia Mantinan - testing and feedback + + Jens David - 2.2.x kernel backports + + Martin Dennett - incredibly helpful insight on undocumented + features of the 8139 chips + + Jean-Jacques Michel - bug fix + + Tobias Ringström - Rx interrupt status checking suggestion + + Andrew Morton - Clear blocked signals, avoid + buffer overrun setting current->comm. + + Kalle Olavi Niemitalo - Wake-on-LAN ioctls + + Robert Kuebel - Save kernel thread from dying on any signal. + + Submitting bug reports: + + "rtl8139-diag -mmmaaavvveefN" output + enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log + +*/ + +#define DRV_NAME "8139too_ec" +#define DRV_VERSION "0.9.27" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#include "../include/EtherCAT_dev.h" + +#define LIT(X) #X +#define STR(X) LIT(X) + +#define COMPILE_INFO "Revision " STR(EC_REV) \ + ", compiled by " STR(EC_USER) \ + " at " STR(EC_DATE) + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION +#define PFX DRV_NAME ": " + +/* Default Message level */ +#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK) + + +/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ +#ifdef CONFIG_8139TOO_PIO +#define USE_IO_OPS 1 +#endif + +/* define to 1, 2 or 3 to enable copious debugging info */ +#define RTL8139_DEBUG 0 + +/* define to 1 to disable lightweight runtime debugging checks */ +#undef RTL8139_NDEBUG + + +#if RTL8139_DEBUG +/* note: prints function name for you */ +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + +#ifdef RTL8139_NDEBUG +# define assert(expr) do {} while (0) +#else +# define assert(expr) \ + if(unlikely(!(expr))) { \ + printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + + +/* A few user-configurable values. */ +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* bitmapped message enable number */ +static int debug = -1; + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +// Uncomment for debugging +//#define EC_DEBUG + +// Device index for EtherCAT device selection +static int ec_device_index = -1; +static int ec_device_master_index = 0; + +static ec_device_t *rtl_ec_dev; +int rtl_ec_dev_registered = 0; + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/* + * Receive ring size + * Warning: 64K ring has hardware issues and may lock up. + */ +#if defined(CONFIG_SH_DREAMCAST) +#define RX_BUF_IDX 1 /* 16K ring */ +#else +#define RX_BUF_IDX 2 /* 32K ring */ +#endif +#define RX_BUF_LEN (8192 << RX_BUF_IDX) +#define RX_BUF_PAD 16 +#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ + +#if RX_BUF_LEN == 65536 +#define RX_BUF_TOT_LEN RX_BUF_LEN +#else +#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) +#endif + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + + +enum { + HAS_MII_XCVR = 0x010000, + HAS_CHIP_XCVR = 0x020000, + HAS_LNK_CHNG = 0x040000, +}; + +#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ +#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ +#define RTL_MIN_IO_SIZE 0x80 +#define RTL8139B_IO_SIZE 256 + +#define RTL8129_CAPS HAS_MII_XCVR +#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG + +typedef enum { + RTL8139 = 0, + RTL8129, +} board_t; + + +/* indexed by board_t, above */ +static struct { + const char *name; + u32 hw_flags; +} board_info[] __devinitdata = { + { "RealTek RTL8139", RTL8139_CAPS }, + { "RealTek RTL8129", RTL8129_CAPS }, +}; + + +static struct pci_device_id rtl8139_pci_tbl[] = { + {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + +#ifdef CONFIG_SH_SECUREEDGE5410 + /* Bogus 8139 silicon reports 8129 without external PROM :-( */ + {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, +#endif +#ifdef CONFIG_8139TOO_8129 + {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, +#endif + + /* some crazy cards report invalid vendor ids like + * 0x0001 here. The other ids are valid and constant, + * so we simply don't match on the main vendor id. + */ + {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, + {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 }, + {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 }, + + {0,} +}; +MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "early_rx" }, + { "tx_buf_mapped" }, + { "tx_timeouts" }, + { "rx_lost_in_ring" }, +}; + +/* The rest of these values should never change. */ + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + FlashReg = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + TxSummary = 0x60, + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ + RxNoWrap = (1 << 7), +}; + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +}; + +enum Cfg9346Bits { + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, +}; + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139A_G, + CH_8139B, + CH_8130, + CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) + +/* directly indexed by chip_t, above */ +const static struct { + const char *name; + u32 version; /* from RTL8139C/RTL8139D docs */ + u32 flags; +} rtl_chip_info[] = { + { "RTL-8139", + HW_REVID(1, 0, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139 rev K", + HW_REVID(1, 1, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139A", + HW_REVID(1, 1, 1, 0, 0, 0, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139A rev G", + HW_REVID(1, 1, 1, 0, 0, 1, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139B", + HW_REVID(1, 1, 1, 1, 0, 0, 0), + HasLWake, + }, + + { "RTL-8130", + HW_REVID(1, 1, 1, 1, 1, 0, 0), + HasLWake, + }, + + { "RTL-8139C", + HW_REVID(1, 1, 1, 0, 1, 0, 0), + HasLWake, + }, + + { "RTL-8100", + HW_REVID(1, 1, 1, 1, 0, 1, 0), + HasLWake, + }, + + { "RTL-8100B/8139D", + HW_REVID(1, 1, 1, 0, 1, 0, 1), + HasLWake, + }, + + { "RTL-8101", + HW_REVID(1, 1, 1, 0, 1, 1, 1), + HasLWake, + }, +}; + +struct rtl_extra_stats { + unsigned long early_rx; + unsigned long tx_buf_mapped; + unsigned long tx_timeouts; + unsigned long rx_lost_in_ring; +}; + +struct rtl8139_private { + void __iomem *mmio_addr; + int drv_flags; + struct pci_dev *pci_dev; + u32 msg_enable; + struct net_device_stats stats; + unsigned char *rx_ring; + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int tx_flag; + unsigned long cur_tx; + unsigned long dirty_tx; + unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + dma_addr_t rx_ring_dma; + dma_addr_t tx_bufs_dma; + signed char phys[4]; /* MII device addresses. */ + char twistie, twist_row, twist_col; /* Twister tune state. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + spinlock_t lock; + spinlock_t rx_lock; + chip_t chipset; + pid_t thr_pid; + wait_queue_head_t thr_wait; + struct completion thr_exited; + u32 rx_config; + struct rtl_extra_stats xstats; + int time_to_die; + struct mii_if_info mii; + unsigned int regs_len; + unsigned long fifo_copy_timeout; +}; + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +MODULE_AUTHOR ("Wilhelm Hagemeister , Florian Pose "); +MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver with EtherCAT functionality"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(COMPILE_INFO); + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +module_param(multicast_filter_limit, int, 0); +module_param_array(media, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +module_param(debug, int, 0); +MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); +MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); +MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +module_param(ec_device_index, int, -1); +module_param(ec_device_master_index, int, 0); +MODULE_PARM_DESC(ec_device_index, "Index of the device reserved for EtherCAT."); +MODULE_PARM_DESC(ec_device_master_index, "Index of the EtherCAT master to register the device."); + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); +static int rtl8139_open (struct net_device *dev); +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 val); +static void rtl8139_start_thread(struct net_device *dev); +static void rtl8139_tx_timeout (struct net_device *dev); +static void rtl8139_init_ring (struct net_device *dev); +static int rtl8139_start_xmit (struct sk_buff *skb, + struct net_device *dev); +int rtl8139_poll(struct net_device *dev, int *budget); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void rtl8139_poll_controller(struct net_device *dev); +#endif +irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs); +static int rtl8139_close (struct net_device *dev); +static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); +static void rtl8139_set_rx_mode (struct net_device *dev); +static void __set_rx_mode (struct net_device *dev); +static void rtl8139_hw_start (struct net_device *dev); +static struct ethtool_ops rtl8139_ethtool_ops; + +/* write MMIO register, with flush */ +/* Flush avoids rtl8139 bug w/ posted MMIO writes */ +#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) +#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) +#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) + + +#define MMIO_FLUSH_AUDIT_COMPLETE 1 +#if MMIO_FLUSH_AUDIT_COMPLETE + +/* write MMIO register */ +#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) + +#else + +/* write MMIO register, then flush */ +#define RTL_W8 RTL_W8_F +#define RTL_W16 RTL_W16_F +#define RTL_W32 RTL_W32_F + +#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ + +/* read MMIO register */ +#define RTL_R8(reg) ioread8 (ioaddr + (reg)) +#define RTL_R16(reg) ioread16 (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg))) + + +static const u16 rtl8139_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | + TxErr | TxOK | RxErr | RxOK; + +static const u16 rtl8139_norx_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | + TxErr | TxOK | RxErr ; + +#if RX_BUF_IDX == 0 +static const unsigned int rtl8139_rx_config = + RxCfgRcv8K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 1 +static const unsigned int rtl8139_rx_config = + RxCfgRcv16K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 2 +static const unsigned int rtl8139_rx_config = + RxCfgRcv32K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 3 +static const unsigned int rtl8139_rx_config = + RxCfgRcv64K | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#else +#error "Invalid configuration for 8139_RXBUF_IDX" +#endif + +static const unsigned int rtl8139_tx_config = + TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); + +static void __rtl8139_cleanup_dev (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + struct pci_dev *pdev; + + assert (dev != NULL); + assert (tp->pci_dev != NULL); + pdev = tp->pci_dev; + +#ifdef USE_IO_OPS + if (tp->mmio_addr) + ioport_unmap (tp->mmio_addr); +#else + if (tp->mmio_addr) + pci_iounmap (pdev, tp->mmio_addr); +#endif /* USE_IO_OPS */ + + /* it's ok to call this even if we have no regions to free */ + pci_release_regions (pdev); + + free_netdev(dev); + pci_set_drvdata (pdev, NULL); +} + + +static void rtl8139_chip_reset (void __iomem *ioaddr) +{ + int i; + + /* Soft reset the chip. */ + RTL_W8 (ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + barrier(); + if ((RTL_R8 (ChipCmd) & CmdReset) == 0) + break; + udelay (10); + } +} + + +static int __devinit rtl8139_init_board (struct pci_dev *pdev, + struct net_device **dev_out) +{ + void __iomem *ioaddr; + struct net_device *dev; + struct rtl8139_private *tp; + u8 tmp8; + int rc, disable_dev_on_err = 0; + unsigned int i; + unsigned long pio_start, pio_end, pio_flags, pio_len; + unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; + u32 version; + + assert (pdev != NULL); + + *dev_out = NULL; + + /* dev and priv zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*tp)); + if (dev == NULL) { + printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + tp = netdev_priv(dev); + tp->pci_dev = pdev; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + pio_start = pci_resource_start (pdev, 0); + pio_end = pci_resource_end (pdev, 0); + pio_flags = pci_resource_flags (pdev, 0); + pio_len = pci_resource_len (pdev, 0); + + mmio_start = pci_resource_start (pdev, 1); + mmio_end = pci_resource_end (pdev, 1); + mmio_flags = pci_resource_flags (pdev, 1); + mmio_len = pci_resource_len (pdev, 1); + + /* set this immediately, we need to know before + * we talk to the chip directly */ + DPRINTK("PIO region size == 0x%02X\n", pio_len); + DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); + +#ifdef USE_IO_OPS + /* make sure PCI base addr 0 is PIO */ + if (!(pio_flags & IORESOURCE_IO)) { + printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, "8139too"); + if (rc) + goto err_out; + disable_dev_on_err = 1; + + /* enable PCI bus-mastering */ + pci_set_master (pdev); + +#ifdef USE_IO_OPS + ioaddr = ioport_map(pio_start, pio_len); + if (!ioaddr) { + printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); + rc = -EIO; + goto err_out; + } + dev->base_addr = pio_start; + tp->mmio_addr = ioaddr; + tp->regs_len = pio_len; +#else + /* ioremap MMIO region */ + ioaddr = pci_iomap(pdev, 1, 0); + if (ioaddr == NULL) { + printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); + rc = -EIO; + goto err_out; + } + dev->base_addr = (long) ioaddr; + tp->mmio_addr = ioaddr; + tp->regs_len = mmio_len; +#endif /* USE_IO_OPS */ + + /* Bring old chips out of low-power mode. */ + RTL_W8 (HltClk, 'R'); + + /* check for missing/broken hardware */ + if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { + printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", + pci_name(pdev)); + rc = -EIO; + goto err_out; + } + + /* identify chip attached to board */ + version = RTL_R32 (TxConfig) & HW_REVID_MASK; + for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) + if (version == rtl_chip_info[i].version) { + tp->chipset = i; + goto match; + } + + /* if unknown chip, assume array element #0, original RTL-8139 in this case */ + printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", + pci_name(pdev)); + printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); + tp->chipset = 0; + +match: + DPRINTK ("chipset id (%d) == index %d, '%s'\n", + version, i, rtl_chip_info[i].name); + + if (tp->chipset >= CH_8139B) { + u8 new_tmp8 = tmp8 = RTL_R8 (Config1); + DPRINTK("PCI PM wakeup\n"); + if ((rtl_chip_info[tp->chipset].flags & HasLWake) && + (tmp8 & LWAKE)) + new_tmp8 &= ~LWAKE; + new_tmp8 |= Cfg1_PM_Enable; + if (new_tmp8 != tmp8) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tmp8); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + if (rtl_chip_info[tp->chipset].flags & HasLWake) { + tmp8 = RTL_R8 (Config4); + if (tmp8 & LWPTN) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config4, tmp8 & ~LWPTN); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + } + } else { + DPRINTK("Old chip wakeup\n"); + tmp8 = RTL_R8 (Config1); + tmp8 &= ~(SLEEP | PWRDN); + RTL_W8 (Config1, tmp8); + } + + rtl8139_chip_reset (ioaddr); + + *dev_out = dev; + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + if (disable_dev_on_err) + pci_disable_device (pdev); + return rc; +} + + +static int __devinit rtl8139_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtl8139_private *tp; + int i, addr_len, option; + void __iomem *ioaddr; + static int board_idx = -1; + u8 pci_rev; + + assert (pdev != NULL); + assert (ent != NULL); + + board_idx++; + + /* when we're built into the kernel, the driver version message + * is only printed if at least one 8139 board has been found + */ +#ifndef MODULE + { + static int printed_version; + if (!printed_version++) + printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); + } +#endif + + pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { + printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pci_name(pdev), pdev->vendor, pdev->device, pci_rev); + printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); + } + + i = rtl8139_init_board (pdev, &dev); + if (i < 0) + return i; + + assert (dev != NULL); + tp = netdev_priv(dev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (board_idx == ec_device_index) + { + printk(KERN_INFO "Registering EtherCAT device...\n"); + rtl_ec_dev = EtherCAT_dev_register(ec_device_master_index, dev, + rtl8139_interrupt, THIS_MODULE); + + if (rtl_ec_dev) strcpy(dev->name, "ec0"); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + ioaddr = tp->mmio_addr; + assert (ioaddr != NULL); + + addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + ((u16 *) (dev->dev_addr))[i] = + le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); + + /* The Rtl8139-specific entries in the device structure. */ + dev->open = rtl8139_open; + dev->hard_start_xmit = rtl8139_start_xmit; + dev->poll = rtl8139_poll; + dev->weight = 64; + dev->stop = rtl8139_close; + dev->get_stats = rtl8139_get_stats; + dev->set_multicast_list = rtl8139_set_rx_mode; + dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &rtl8139_ethtool_ops; + dev->tx_timeout = rtl8139_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = rtl8139_poll_controller; +#endif + + /* note: the hardware is not capable of sg/csum/highdma, however + * through the use of skb_copy_and_csum_dev we enable these + * features + */ + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + + dev->irq = pdev->irq; + + /* tp zeroed and aligned in alloc_etherdev */ + tp = netdev_priv(dev); + + /* note: tp->chipset set in rtl8139_init_board */ + tp->drv_flags = board_info[ent->driver_data].hw_flags; + tp->mmio_addr = ioaddr; + tp->msg_enable = + (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); + spin_lock_init (&tp->lock); + spin_lock_init (&tp->rx_lock); + + init_waitqueue_head (&tp->thr_wait); + init_completion (&tp->thr_exited); + tp->mii.dev = dev; + tp->mii.mdio_read = mdio_read; + tp->mii.mdio_write = mdio_write; + tp->mii.phy_id_mask = 0x3f; + tp->mii.reg_num_mask = 0x1f; + + /* dev is fully set up and ready to use now */ + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + /* EtherCAT-Karten nicht beim Stack anmelden. */ + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + DPRINTK("About to register device named %s (%p)...\n", dev->name, dev); + i = register_netdev (dev); + if (i) goto err_out; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + pci_set_drvdata (pdev, dev); + + printk (KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + board_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", + dev->name, rtl_chip_info[tp->chipset].name); + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ +#ifdef CONFIG_8139TOO_8129 + if (tp->drv_flags & HAS_MII_XCVR) { + int phy, phy_idx = 0; + for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + u16 advertising = mdio_read(dev, phy, 4); + tp->phys[phy_idx++] = phy; + printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " + "advertising %4.4x.\n", + dev->name, phy, mii_status, advertising); + } + } + if (phy_idx == 0) { + printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " + "transceiver.\n", + dev->name); + tp->phys[0] = 32; + } + } else +#endif + tp->phys[0] = 32; + tp->mii.phy_id = tp->phys[0]; + + /* The lower four bits are the media type. */ + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + if (option > 0) { + tp->mii.full_duplex = (option & 0x210) ? 1 : 0; + tp->default_port = option & 0xFF; + if (tp->default_port) + tp->mii.force_media = 1; + } + if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) + tp->mii.full_duplex = full_duplex[board_idx]; + if (tp->mii.full_duplex) { + printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + /* Changing the MII-advertised media because might prevent + re-connection. */ + tp->mii.force_media = 1; + } + if (tp->default_port) { + printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", + (option & 0x20 ? 100 : 10), + (option & 0x10 ? "full" : "half")); + mdio_write(dev, tp->phys[0], 0, + ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ + ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Put the chip into low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); + return i; +} + + +static void __devexit rtl8139_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + assert (dev != NULL); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + unregister_netdev (dev); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); +} + + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() RTL_R32(Cfg9346) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + int read_cmd = location | (EE_READ_CMD << addr_len); + + RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + RTL_W8 (Cfg9346, EE_ENB | dataval); + eeprom_delay (); + RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); + eeprom_delay (); + } + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + for (i = 16; i > 0; i--) { + RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); + eeprom_delay (); + retval = + (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : + 0); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + } + + /* Terminate the EEPROM access. */ + RTL_W8 (Cfg9346, ~EE_CS); + eeprom_delay (); + + return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ +#define MDIO_DIR 0x80 +#define MDIO_DATA_OUT 0x04 +#define MDIO_DATA_IN 0x02 +#define MDIO_CLK 0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() RTL_R8(Config4) + + +static char mii_2_8139_map[8] = { + BasicModeCtrl, + BasicModeStatus, + 0, + 0, + NWayAdvert, + NWayLPAR, + NWayExpansion, + 0 +}; + + +#ifdef CONFIG_8139TOO_8129 +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync (void __iomem *ioaddr) +{ + int i; + + for (i = 32; i >= 0; i--) { + RTL_W8 (Config4, MDIO_WRITE1); + mdio_delay (); + RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); + mdio_delay (); + } +} +#endif + +static int mdio_read (struct net_device *dev, int phy_id, int location) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int retval = 0; +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + return location < 8 && mii_2_8139_map[location] ? + RTL_R16 (mii_2_8139_map[location]) : 0; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + + RTL_W8 (Config4, MDIO_DIR | dataval); + mdio_delay (); + RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); + mdio_delay (); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif + + return (retval >> 1) & 0xffff; +} + + +static void mdio_write (struct net_device *dev, int phy_id, int location, + int value) +{ + struct rtl8139_private *tp = netdev_priv(dev); +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + if (location == 0) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W16 (BasicModeCtrl, value); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } else if (location < 8 && mii_2_8139_map[location]) + RTL_W16 (mii_2_8139_map[location], value); + return; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + RTL_W8 (Config4, dataval); + mdio_delay (); + RTL_W8 (Config4, dataval | MDIO_CLK); + mdio_delay (); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif +} + + +static int rtl8139_open (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int retval; + void __iomem *ioaddr = tp->mmio_addr; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#ifdef EC_DEBUG + printk(KERN_DEBUG "%s: open\n", dev->name); +#endif + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + retval = request_irq(dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) + return retval; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + &tp->tx_bufs_dma); + tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + &tp->rx_ring_dma); + if (tp->tx_bufs == NULL || tp->rx_ring == NULL) + { + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + free_irq(dev->irq, dev); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + if (tp->tx_bufs) + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + if (tp->rx_ring) + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + + return -ENOMEM; + } + + tp->mii.full_duplex = tp->mii.force_media; + tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; + + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + netif_start_queue (dev); + + if (netif_msg_ifup(tp)) + { + printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", + dev->name, pci_resource_start (tp->pci_dev, 1), + dev->irq, RTL_R8 (MediaStatus), + tp->mii.full_duplex ? "full" : "half"); + } + + rtl8139_start_thread(dev); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + return 0; +} + + +static void rtl_check_media (struct net_device *dev, unsigned int init_media) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + if (tp->phys[0] >= 0) { + mii_check_media(&tp->mii, netif_msg_link(tp), init_media); + } +} + +/* Start the hardware at open or resume. */ +static void rtl8139_hw_start (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 i; + u8 tmp; + + /* Bring old chips out of low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'R'); + + rtl8139_chip_reset (ioaddr); + + /* unlock Config[01234] and BMCR register writes */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Restore our idea of the MAC address. */ + RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); + RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + RTL_W32 (TxConfig, rtl8139_tx_config); + + tp->cur_rx = 0; + + rtl_check_media (dev, 1); + + if (tp->chipset >= CH_8139B) { + /* Disable magic packet scanning, which is enabled + * when PM is enabled in Config1. It can be reenabled + * via ETHTOOL_SWOL if desired. */ + RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); + } + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* init Tx buffer DMA addresses */ + for (i = 0; i < NUM_TX_DESC; i++) + RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); + + RTL_W32 (RxMissed, 0); + + rtl8139_set_rx_mode (dev); + + /* no early-rx interrupts */ + RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); + + /* make sure RxTx has started */ + tmp = RTL_R8 (ChipCmd); + if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W16 (IntrMask, rtl8139_intr_mask); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void rtl8139_init_ring (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int i; + + tp->cur_rx = 0; + tp->cur_tx = 0; + tp->dirty_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) + tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; +} + + +/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ +static int next_tick = 3 * HZ; + +#ifndef CONFIG_8139TOO_TUNE_TWISTER +static inline void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) {} +#else +enum TwisterParamVals { + PARA78_default = 0x78fa8388, + PARA7c_default = 0xcb38de43, /* param[0][3] */ + PARA7c_xxx = 0xcb38de43, +}; + +static const unsigned long param[4][4] = { + {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} +}; + +static void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) +{ + int linkcase; + void __iomem *ioaddr = tp->mmio_addr; + + /* This is a complicated state machine to configure the "twister" for + impedance/echos based on the cable length. + All of this is magic and undocumented. + */ + switch (tp->twistie) { + case 1: + if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { + /* We have link beat, let us tune the twister. */ + RTL_W16 (CSCR, CSCR_LinkDownOffCmd); + tp->twistie = 2; /* Change to state 2. */ + next_tick = HZ / 10; + } else { + /* Just put in some reasonable defaults for when beat returns. */ + RTL_W16 (CSCR, CSCR_LinkDownCmd); + RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + tp->twistie = 0; /* Bail from future actions. */ + } + break; + case 2: + /* Read how long it took to hear the echo. */ + linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; + if (linkcase == 0x7000) + tp->twist_row = 3; + else if (linkcase == 0x3000) + tp->twist_row = 2; + else if (linkcase == 0x1000) + tp->twist_row = 1; + else + tp->twist_row = 0; + tp->twist_col = 0; + tp->twistie = 3; /* Change to state 2. */ + next_tick = HZ / 10; + break; + case 3: + /* Put out four tuning parameters, one per 100msec. */ + if (tp->twist_col == 0) + RTL_W16 (FIFOTMS, 0); + RTL_W32 (PARA7c, param[(int) tp->twist_row] + [(int) tp->twist_col]); + next_tick = HZ / 10; + if (++tp->twist_col >= 4) { + /* For short cables we are done. + For long cables (row == 3) check for mistune. */ + tp->twistie = + (tp->twist_row == 3) ? 4 : 0; + } + break; + case 4: + /* Special case for long cables: check for mistune. */ + if ((RTL_R16 (CSCR) & + CSCR_LinkStatusBits) == 0x7000) { + tp->twistie = 0; + break; + } else { + RTL_W32 (PARA7c, 0xfb38de03); + tp->twistie = 5; + next_tick = HZ / 10; + } + break; + case 5: + /* Retune for shorter cable (column 2). */ + RTL_W32 (FIFOTMS, 0x20); + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + RTL_W32 (FIFOTMS, 0x00); + tp->twist_row = 2; + tp->twist_col = 0; + tp->twistie = 3; + next_tick = HZ / 10; + break; + + default: + /* do nothing */ + break; + } +} +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ + +static inline void rtl8139_thread_iter (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + int mii_lpa; + + mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); + + if (!tp->mii.force_media && mii_lpa != 0xffff) { + int duplex = (mii_lpa & LPA_100FULL) + || (mii_lpa & 0x01C0) == 0x0040; + if (tp->mii.full_duplex != duplex) { + tp->mii.full_duplex = duplex; + + if (mii_lpa) { + printk (KERN_INFO + "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", + dev->name, + tp->mii.full_duplex ? "full" : "half", + tp->phys[0], mii_lpa); + } else { + printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", + dev->name); + } +#if 0 + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); + RTL_W8 (Cfg9346, Cfg9346_Lock); +#endif + } + } + + next_tick = HZ * 60; + + rtl8139_tune_twister (dev, tp); + + DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", + dev->name, RTL_R16 (NWayLPAR)); + DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n", + dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); + DPRINTK ("%s: Chip config %2.2x %2.2x.\n", + dev->name, RTL_R8 (Config0), + RTL_R8 (Config1)); +} + +static int rtl8139_thread (void *data) +{ + struct net_device *dev = data; + struct rtl8139_private *tp = netdev_priv(dev); + unsigned long timeout; + + daemonize("%s", dev->name); + allow_signal(SIGTERM); + + while (1) { + timeout = next_tick; + do { + timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); + /* make swsusp happy with our thread */ + try_to_freeze(); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + flush_signals(current); + } + + if (tp->time_to_die) + break; + + if (rtnl_lock_interruptible ()) + break; + rtl8139_thread_iter (dev, tp, tp->mmio_addr); + rtnl_unlock (); + } + + complete_and_exit (&tp->thr_exited, 0); +} + +static void rtl8139_start_thread(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + tp->thr_pid = -1; + tp->twistie = 0; + tp->time_to_die = 0; + if (tp->chipset == CH_8139_K) + tp->twistie = 1; + else if (tp->drv_flags & HAS_LNK_CHNG) + return; + + tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES); + if (tp->thr_pid < 0) { + printk (KERN_WARNING "%s: unable to start kernel thread\n", + dev->name); + } +} + +static inline void rtl8139_tx_clear (struct rtl8139_private *tp) +{ + tp->cur_tx = 0; + tp->dirty_tx = 0; + + /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ +} + + +static void rtl8139_tx_timeout (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int i; + u8 tmp8; + unsigned long flags; + + printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " + "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), + RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus)); + /* Emit info to figure out what went wrong. */ + printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", + dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + + tp->xstats.tx_timeouts++; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + printk(KERN_DEBUG "%s: tx_timeout\n", dev->name); + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_TIMEOUT); + } + + /* disable Tx ASAP, if not already */ + tmp8 = RTL_R8 (ChipCmd); + if (tmp8 & CmdTxEnb) + RTL_W8 (ChipCmd, CmdRxEnb); + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + spin_lock(&tp->rx_lock); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0x0000); + + /* Stop a shared interrupt from scavenging while we are. */ + spin_lock_irqsave (&tp->lock, flags); + rtl8139_tx_clear (tp); + spin_unlock_irqrestore (&tp->lock, flags); + + /* ...and finally, reset everything */ + + if (netif_running(dev)) + { + rtl8139_hw_start (dev); + netif_wake_queue (dev); + } + + spin_unlock(&tp->rx_lock); + } + else + { + rtl8139_tx_clear (tp); + rtl8139_hw_start(dev); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +} + +static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int entry; + unsigned int len = skb->len; + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % NUM_TX_DESC; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + /* Note: the chip doesn't have auto-pad! */ + if (likely(len < TX_BUF_SIZE)) + { + if (len < ETH_ZLEN) + memset(tp->tx_buf[entry], 0, ETH_ZLEN); + + skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) dev_kfree_skb(skb); + } + else + { + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) dev_kfree_skb(skb); + tp->stats.tx_dropped++; + return 0; + } + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + spin_lock_irq(&tp->lock); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), + tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); + + dev->trans_start = jiffies; + + tp->cur_tx++; + wmb(); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) + netif_stop_queue (dev); + + spin_unlock_irq(&tp->lock); + + if (netif_msg_tx_queued(tp)) + printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + return 0; +} + + +static void rtl8139_tx_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + unsigned long dirty_tx, tx_left; + + assert (dev != NULL); + assert (ioaddr != NULL); + + dirty_tx = tp->dirty_tx; + tx_left = tp->cur_tx - dirty_tx; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#if 0 + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + rtl_ec_dev.tx_intr_cnt++; + rdtscl(rtl_ec_dev.tx_time); // Get CPU cycles + } +#endif + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + while (tx_left > 0) { + int entry = dirty_tx % NUM_TX_DESC; + int txstatus; + + txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); + + if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) + break; /* It still hasn't been Txed */ + + /* Note: TxCarrierLost is always asserted at 100mbps. */ + if (txstatus & (TxOutOfWindow | TxAborted)) { + /* There was an major error, log it. */ + if (netif_msg_tx_err(tp)) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, txstatus); + tp->stats.tx_errors++; + if (txstatus & TxAborted) { + tp->stats.tx_aborted_errors++; + RTL_W32 (TxConfig, TxClearAbt); + RTL_W16 (IntrStatus, TxErr); + wmb(); + } + if (txstatus & TxCarrierLost) + tp->stats.tx_carrier_errors++; + if (txstatus & TxOutOfWindow) + tp->stats.tx_window_errors++; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_ERROR); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + } else { + if (txstatus & TxUnderrun) { + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; + tp->stats.tx_fifo_errors++; + } + tp->stats.collisions += (txstatus >> 24) & 15; + tp->stats.tx_bytes += txstatus & 0x7ff; + tp->stats.tx_packets++; + } + + dirty_tx++; + tx_left--; + } + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +#ifndef RTL8139_NDEBUG + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev) && tp->cur_tx - dirty_tx > NUM_TX_DESC) { + printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", + dev->name, dirty_tx, tp->cur_tx); + dirty_tx += NUM_TX_DESC; + } +#endif /* RTL8139_NDEBUG */ + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* only wake the queue if we did work, and the queue is stopped */ + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + mb(); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + netif_wake_queue (dev); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + } +} + + +/* TODO: clean this up! Rx reset need not be this intensive */ +static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, + struct rtl8139_private *tp, void __iomem *ioaddr) +{ + u8 tmp8; +#ifdef CONFIG_8139_OLD_RX_RESET + int tmp_work; +#endif + + if (netif_msg_rx_err (tp)) + printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n", + dev->name, rx_status); + tp->stats.rx_errors++; + if (!(rx_status & RxStatusOK)) { + if (rx_status & RxTooLong) { + DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", + dev->name, rx_status); + /* A.C.: The chip hangs here. */ + } + if (rx_status & (RxBadSymbol | RxBadAlign)) + tp->stats.rx_frame_errors++; + if (rx_status & (RxRunt | RxTooLong)) + tp->stats.rx_length_errors++; + if (rx_status & RxCRCErr) + tp->stats.rx_crc_errors++; + } else { + tp->xstats.rx_lost_in_ring++; + } + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + EtherCAT_dev_state(rtl_ec_dev, EC_DEVICE_STATE_ERROR); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +#ifndef CONFIG_8139_OLD_RX_RESET + tmp8 = RTL_R8 (ChipCmd); + RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); + RTL_W8 (ChipCmd, tmp8); + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; +#else + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + + /* disable receive */ + RTL_W8_F (ChipCmd, CmdTxEnb); + tmp_work = 200; + while (--tmp_work > 0) { + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if (!(tmp8 & CmdRxEnb)) + break; + } + if (tmp_work <= 0) + printk (KERN_WARNING PFX "rx stop wait too long\n"); + /* restart receive */ + tmp_work = 200; + while (--tmp_work > 0) { + RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) + break; + } + if (tmp_work <= 0) + printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); + + /* and reinitialize all rx related registers */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* A.C.: Reset the multicast list. */ + __set_rx_mode (dev); +#endif +} + +#if RX_BUF_IDX == 3 +static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, + u32 offset, unsigned int size) +{ + u32 left = RX_BUF_LEN - offset; + + if (size > left) { + memcpy(skb->data, ring + offset, left); + memcpy(skb->data+left, ring, size - left); + } else + memcpy(skb->data, ring + offset, size); +} +#endif + +static void rtl8139_isr_ack(struct rtl8139_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + u16 status; + + status = RTL_R16 (IntrStatus) & RxAckBits; + + /* Clear out errors and receive interrupts */ + if (likely(status != 0)) { + if (unlikely(status & (RxFIFOOver | RxOverflow))) { + tp->stats.rx_errors++; + if (status & RxFIFOOver) + tp->stats.rx_fifo_errors++; + } + RTL_W16_F (IntrStatus, RxAckBits); + } +} + +static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, + int budget) +{ + void __iomem *ioaddr = tp->mmio_addr; + int received = 0; + unsigned char *rx_ring = tp->rx_ring; + unsigned int cur_rx = tp->cur_rx; + unsigned int rx_size = 0; + + DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ +#if 0 + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + rtl_ec_dev.rx_intr_cnt++; + rdtscl(rtl_ec_dev.rx_time); // Get CPU cycles + } +#endif + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + while ((EtherCAT_dev_is_ec(rtl_ec_dev, dev) || netif_running(dev)) + && received < budget + && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + u32 ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status; + unsigned int pkt_size; + struct sk_buff *skb; + + rmb(); + + /* read size+status of next frame from DMA ring buffer */ + rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); + rx_size = rx_status >> 16; + pkt_size = rx_size - 4; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev) && netif_msg_rx_status(tp)) + printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," + " cur %4.4x.\n", dev->name, rx_status, + rx_size, cur_rx); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +#if RTL8139_DEBUG > 2 + { + int i; + DPRINTK ("%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + printk (" %2.2x", + rx_ring[ring_offset + i]); + printk (".\n"); + } +#endif + + /* Packet copy from FIFO still in progress. + * Theoretically, this should never happen + * since EarlyRx is disabled. + */ + if (unlikely(rx_size == 0xfff0)) { + if (!tp->fifo_copy_timeout) + tp->fifo_copy_timeout = jiffies + 2; + else if (time_after(jiffies, tp->fifo_copy_timeout)) { + DPRINTK ("%s: hung FIFO. Reset.", dev->name); + rx_size = 0; + goto no_early_rx; + } + if (netif_msg_intr(tp)) { + printk(KERN_DEBUG "%s: fifo copy in progress.", + dev->name); + } + tp->xstats.early_rx++; + break; + } + +no_early_rx: + tp->fifo_copy_timeout = 0; + + /* If Rx err or invalid rx_size/rx_status received + * (which happens if we get lost in the ring), + * Rx process gets reset, so we abort any further + * Rx processing. + */ + if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || + (rx_size < 8) || + (!(rx_status & RxStatusOK)))) { + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + received = -1; + goto out; + } + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + skb = dev_alloc_skb(pkt_size + 2); + + if (likely(skb)) { + skb->dev = dev; + skb_reserve (skb, 2); /* 16 byte align the IP fields. */ +#if RX_BUF_IDX == 3 + wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); +#else + eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); +#endif + skb_put (skb, pkt_size); + + skb->protocol = eth_type_trans (skb, dev); + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + + netif_receive_skb (skb); + } else { + if (net_ratelimit()) + printk (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + tp->stats.rx_dropped++; + } + } + else + { + if (EtherCAT_dev_receive(rtl_ec_dev, + &rx_ring[ring_offset + 4] + ETH_HLEN, + pkt_size - ETH_HLEN) == 0) + { + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + received++; + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); + + rtl8139_isr_ack(tp); + } + + if (unlikely(!received || rx_size == 0xfff0)) + rtl8139_isr_ack(tp); + +#if RTL8139_DEBUG > 1 + DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); +#endif + + tp->cur_rx = cur_rx; + + /* + * The receive buffer should be mostly empty. + * Tell NAPI to reenable the Rx irq. + */ + if (tp->fifo_copy_timeout) + received = budget; + +out: + return received; +} + + +static void rtl8139_weird_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr, + int status, int link_changed) +{ + DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + /* Update the error count. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + if ((status & RxUnderrun) && link_changed && + (tp->drv_flags & HAS_LNK_CHNG)) { + rtl_check_media(dev, 0); + status &= ~RxUnderrun; + } + + if (status & (RxUnderrun | RxErr)) + tp->stats.rx_errors++; + + if (status & PCSTimeout) + tp->stats.rx_length_errors++; + if (status & RxUnderrun) + tp->stats.rx_fifo_errors++; + if (status & PCIErr) { + u16 pci_cmd_status; + pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); + pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); + + printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", + dev->name, pci_cmd_status); + } +} + +int rtl8139_poll(struct net_device *dev, int *budget) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int orig_budget = min(*budget, dev->quota); + int done = 1; + + spin_lock(&tp->rx_lock); + if (likely(RTL_R16(IntrStatus) & RxAckBits)) { + int work_done; + + work_done = rtl8139_rx(dev, tp, orig_budget); + if (likely(work_done > 0)) { + *budget -= work_done; + dev->quota -= work_done; + done = (work_done < orig_budget); + } + } + + if (done) { + /* + * Order is important since data can get interrupted + * again when we think we are done. + */ + + local_irq_disable(); + RTL_W16_F(IntrMask, rtl8139_intr_mask); + __netif_rx_complete(dev); + local_irq_enable(); + } + spin_unlock(&tp->rx_lock); + + return !done; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u16 status, ackstat; + int link_changed = 0; /* avoid bogus "uninit" warning */ + int handled = 0; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { +#if 0 // FIXME + rtl_ec_dev.intr_cnt++; +#endif + status = RTL_R16 (IntrStatus); + } + else + { + spin_lock(&tp->lock); + + status = RTL_R16 (IntrStatus); + + if (unlikely((status & rtl8139_intr_mask) == 0)) + goto out; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + handled = 1; + + /* h/w no longer present (hotplug?) or major error, bail */ + if (unlikely(status == 0xFFFF)) + goto out; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + /* close possible race's with dev_close */ + if (unlikely(!netif_running(dev))) { + RTL_W16 (IntrMask, 0); + goto out; + } + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* Acknowledge all of the current interrupt sources ASAP, but + an first get an additional status bit from CSCR. */ + if (unlikely(status & RxUnderrun)) + link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; + + ackstat = status & ~(RxAckBits | TxErr); + if (ackstat) { + RTL_W16 (IntrStatus, ackstat); + } + + /* Receive packets are processed by poll routine. + If not running start it now. */ + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (status & RxAckBits) + { + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + /* Polling vormerken */ + if (netif_rx_schedule_prep(dev)) { + RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); + __netif_rx_schedule (dev); + } + } + else + { + /* Beim EtherCAT-Device einfach alle Frames empfangen */ + rtl8139_rx(dev, tp, 100); // FIXME Das ist echt dirty... + } + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* Check uncommon events with one test. */ + if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) + rtl8139_weird_interrupt (dev, tp, ioaddr, + status, link_changed); + + if (status & (TxOK | TxErr)) { + rtl8139_tx_interrupt (dev, tp, ioaddr); + if (status & TxErr) + RTL_W16 (IntrStatus, TxErr); + } + out: + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + spin_unlock (&tp->lock); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling receive - used by netconsole and other diagnostic tools + * to allow network i/o with interrupts disabled. + */ +static void rtl8139_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + rtl8139_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + +static int rtl8139_close (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int ret = 0; + unsigned long flags; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (!EtherCAT_dev_is_ec(rtl_ec_dev, dev)) + { + netif_stop_queue(dev); + if (tp->thr_pid >= 0) { + tp->time_to_die = 1; + wmb(); + ret = kill_proc (tp->thr_pid, SIGTERM, 1); + if (ret) { + printk (KERN_ERR "%s: unable to signal thread\n", dev->name); + return ret; + } + wait_for_completion (&tp->thr_exited); + } + + if (netif_msg_ifdown(tp)) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + + spin_lock_irqsave (&tp->lock, flags); + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8 (ChipCmd, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + synchronize_irq (dev->irq); /* racy, but that's ok here */ + free_irq (dev->irq, dev); + } + else + { + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8 (ChipCmd, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + rtl8139_tx_clear (tp); + + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + tp->rx_ring = NULL; + tp->tx_bufs = NULL; + + /* Green! Put the chip in low-power mode. */ + RTL_W8 (Cfg9346, Cfg9346_Unlock); + + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; +} + + +/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to + kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and + other threads or interrupts aren't messing with the 8139. */ +static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mmio_addr; + + spin_lock_irq(&np->lock); + if (rtl_chip_info[np->chipset].flags & HasLWake) { + u8 cfg3 = RTL_R8 (Config3); + u8 cfg5 = RTL_R8 (Config5); + + wol->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + + wol->wolopts = 0; + if (cfg3 & Cfg3_LinkUp) + wol->wolopts |= WAKE_PHY; + if (cfg3 & Cfg3_Magic) + wol->wolopts |= WAKE_MAGIC; + /* (KON)FIXME: See how netdev_set_wol() handles the + following constants. */ + if (cfg5 & Cfg5_UWF) + wol->wolopts |= WAKE_UCAST; + if (cfg5 & Cfg5_MWF) + wol->wolopts |= WAKE_MCAST; + if (cfg5 & Cfg5_BWF) + wol->wolopts |= WAKE_BCAST; + } + spin_unlock_irq(&np->lock); +} + + +/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes + that wol points to kernel memory and other threads or interrupts + aren't messing with the 8139. */ +static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mmio_addr; + u32 support; + u8 cfg3, cfg5; + + support = ((rtl_chip_info[np->chipset].flags & HasLWake) + ? (WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) + : 0); + if (wol->wolopts & ~support) + return -EINVAL; + + spin_lock_irq(&np->lock); + cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); + if (wol->wolopts & WAKE_PHY) + cfg3 |= Cfg3_LinkUp; + if (wol->wolopts & WAKE_MAGIC) + cfg3 |= Cfg3_Magic; + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config3, cfg3); + RTL_W8 (Cfg9346, Cfg9346_Lock); + + cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); + /* (KON)FIXME: These are untested. We may have to set the + CRC0, Wakeup0 and LSBCRC0 registers too, but I have no + documentation. */ + if (wol->wolopts & WAKE_UCAST) + cfg5 |= Cfg5_UWF; + if (wol->wolopts & WAKE_MCAST) + cfg5 |= Cfg5_MWF; + if (wol->wolopts & WAKE_BCAST) + cfg5 |= Cfg5_BWF; + RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ + spin_unlock_irq(&np->lock); + + return 0; +} + +static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct rtl8139_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); + info->regdump_len = np->regs_len; +} + +static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return 0; +} + +static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + int rc; + spin_lock_irq(&np->lock); + rc = mii_ethtool_sset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return rc; +} + +static int rtl8139_nway_reset(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii); +} + +static u32 rtl8139_get_link(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii); +} + +static u32 rtl8139_get_msglevel(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return np->msg_enable; +} + +static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) +{ + struct rtl8139_private *np = netdev_priv(dev); + np->msg_enable = datum; +} + +/* TODO: we are too slack to do reg dumping for pio, for now */ +#ifdef CONFIG_8139TOO_PIO +#define rtl8139_get_regs_len NULL +#define rtl8139_get_regs NULL +#else +static int rtl8139_get_regs_len(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return np->regs_len; +} + +static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct rtl8139_private *np = netdev_priv(dev); + + regs->version = RTL_REGS_VER; + + spin_lock_irq(&np->lock); + memcpy_fromio(regbuf, np->mmio_addr, regs->len); + spin_unlock_irq(&np->lock); +} +#endif /* CONFIG_8139TOO_MMIO */ + +static int rtl8139_get_stats_count(struct net_device *dev) +{ + return RTL_NUM_STATS; +} + +static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct rtl8139_private *np = netdev_priv(dev); + + data[0] = np->xstats.early_rx; + data[1] = np->xstats.tx_buf_mapped; + data[2] = np->xstats.tx_timeouts; + data[3] = np->xstats.rx_lost_in_ring; +} + +static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static struct ethtool_ops rtl8139_ethtool_ops = { + .get_drvinfo = rtl8139_get_drvinfo, + .get_settings = rtl8139_get_settings, + .set_settings = rtl8139_set_settings, + .get_regs_len = rtl8139_get_regs_len, + .get_regs = rtl8139_get_regs, + .nway_reset = rtl8139_nway_reset, + .get_link = rtl8139_get_link, + .get_msglevel = rtl8139_get_msglevel, + .set_msglevel = rtl8139_set_msglevel, + .get_wol = rtl8139_get_wol, + .set_wol = rtl8139_set_wol, + .get_strings = rtl8139_get_strings, + .get_stats_count = rtl8139_get_stats_count, + .get_ethtool_stats = rtl8139_get_ethtool_stats, +}; + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + int rc; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev) || !netif_running(dev)) + return -EINVAL; + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&np->lock); + + return rc; +} + + +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev) || netif_running(dev)) + { + spin_lock_irqsave (&tp->lock, flags); + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + spin_unlock_irqrestore (&tp->lock, flags); + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + return &tp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + This routine is not state sensitive and need not be SMP locked. */ + +static void __set_rx_mode (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; + u32 tmp; + + DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", + dev->name, dev->flags, RTL_R32 (RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", + dev->name); + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + /* We can safely update without stopping the chip. */ + tmp = rtl8139_rx_config | rx_mode; + if (tp->rx_config != tmp) { + RTL_W32_F (RxConfig, tmp); + tp->rx_config = tmp; + } + RTL_W32_F (MAR0 + 0, mc_filter[0]); + RTL_W32_F (MAR0 + 4, mc_filter[1]); +} + +static void rtl8139_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct rtl8139_private *tp = netdev_priv(dev); + + spin_lock_irqsave (&tp->lock, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore (&tp->lock, flags); +} + +#ifdef CONFIG_PM + +static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + pci_save_state (pdev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev) || !netif_running (dev)) + return 0; + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + netif_device_detach (dev); + + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + RTL_W16 (IntrMask, 0); + RTL_W8 (ChipCmd, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + pci_set_power_state (pdev, PCI_D3hot); + + return 0; +} + + +static int rtl8139_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + pci_restore_state (pdev); + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + if (EtherCAT_dev_is_ec(rtl_ec_dev, dev) || !netif_running (dev)) + return 0; + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + pci_set_power_state (pdev, PCI_D0); + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + netif_device_attach (dev); + return 0; +} + +#endif /* CONFIG_PM */ + + +static struct pci_driver rtl8139_pci_driver = { + .name = DRV_NAME, + .id_table = rtl8139_pci_tbl, + .probe = rtl8139_init_one, + .remove = __devexit_p(rtl8139_remove_one), +#ifdef CONFIG_PM + .suspend = rtl8139_suspend, + .resume = rtl8139_resume, +#endif /* CONFIG_PM */ +}; + + +static int __init rtl8139_init_module (void) +{ + /* when we're a module, we always print a version message, + * even if no 8139 board is found. + */ +#ifdef MODULE + printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); +#endif + + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + printk(KERN_INFO "Initializing RTL8139-EtherCAT module. %s\n", COMPILE_INFO); + printk(KERN_INFO "EtherCAT device index is %i.\n", ec_device_index); + + if (pci_module_init(&rtl8139_pci_driver) < 0) + { + printk(KERN_ERR "Could not init PCI module.\n"); + goto out_ec_dev; + } + + if (!rtl_ec_dev) + { + printk(KERN_WARNING "NO EtherCAT device registered!\n"); + } + + return 0; + + out_ec_dev: + if (rtl_ec_dev) { + printk(KERN_INFO "Unregistering RTL8139-EtherCAT device...\n"); + EtherCAT_dev_unregister(ec_device_master_index); + rtl_ec_dev = NULL; + } + + return -1; + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +} + + +static void __exit rtl8139_cleanup_module (void) +{ + /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + printk(KERN_INFO "Cleaning up RTL8139-EtherCAT module...\n"); + + if (rtl_ec_dev) { + printk(KERN_INFO "Unregistering RTL8139-EtherCAT device...\n"); + EtherCAT_dev_unregister(ec_device_master_index); + rtl_ec_dev = NULL; + } + + pci_unregister_driver(&rtl8139_pci_driver); + + printk(KERN_INFO "RTL8139-EtherCAT module cleaned up.\n"); + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ +} + + +module_init(rtl8139_init_module); +module_exit(rtl8139_cleanup_module); diff -r 6b3b8acb71b5 -r 7506e67dd122 devices/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/Makefile Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------------ +# +# Makefile +# +# IgH EtherCAT-Treiber - EtherCAT Geraete +# +# $Id$ +# +#------------------------------------------------------------------------------ + +ifneq ($(KERNELRELEASE),) + +#------------------------------------------------------------------------------ +# Kbuild-Abschnitt + +obj-m := ec_8139too.o + +ec_8139too-objs := 8139too.o + +REV = `svnversion $(src)` +DATE = `date` + +EXTRA_CFLAGS = -DEC_REV="$(REV)" -DEC_USER="$(USER)" -DEC_DATE="$(DATE)" + +#------------------------------------------------------------------------------ + +else + +#------------------------------------------------------------------------------ +# Default-Abschnitt + +ifneq ($(wildcard ethercat.conf),) +include ethercat.conf +else +KERNELDIR = /lib/modules/`uname -r`/build +endif + +modules: + $(MAKE) -C $(KERNELDIR) M=`pwd` modules + +clean: + $(MAKE) -C $(KERNELDIR) M=`pwd` clean + +#------------------------------------------------------------------------------ + +endif diff -r 6b3b8acb71b5 -r 7506e67dd122 devices/original_8139too.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/original_8139too.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,2646 @@ +/* + + 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. + + Maintained by Jeff Garzik + Copyright 2000-2002 Jeff Garzik + + Much code comes from Donald Becker's rtl8139.c driver, + versions 1.13 and older. This driver was originally based + on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: + + ---------- + + Written 1997-2001 by Donald Becker. + This software may be used and distributed according to the + terms of the GNU General Public License (GPL), incorporated + herein by reference. Drivers based on or derived from this + code fall under the GPL and must retain the authorship, + copyright and license notice. This file is not a complete + program and may only be used when the entire operating + system is licensed under the GPL. + + This driver is for boards based on the RTL8129 and RTL8139 + PCI ethernet chips. + + The author may be reached as becker@scyld.com, or C/O Scyld + Computing Corporation 410 Severn Ave., Suite 210 Annapolis + MD 21403 + + Support and updates available at + http://www.scyld.com/network/rtl8139.html + + Twister-tuning table provided by Kinston + . + + ---------- + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Contributors: + + Donald Becker - he wrote the original driver, kudos to him! + (but please don't e-mail him for support, this isn't his driver) + + Tigran Aivazian - bug fixes, skbuff free cleanup + + Martin Mares - suggestions for PCI cleanup + + David S. Miller - PCI DMA and softnet updates + + Ernst Gill - fixes ported from BSD driver + + Daniel Kobras - identified specific locations of + posted MMIO write bugginess + + Gerard Sharp - bug fix, testing and feedback + + David Ford - Rx ring wrap fix + + Dan DeMaggio - swapped RTL8139 cards with me, and allowed me + to find and fix a crucial bug on older chipsets. + + Donald Becker/Chris Butterworth/Marcus Westergren - + Noticed various Rx packet size-related buglets. + + Santiago Garcia Mantinan - testing and feedback + + Jens David - 2.2.x kernel backports + + Martin Dennett - incredibly helpful insight on undocumented + features of the 8139 chips + + Jean-Jacques Michel - bug fix + + Tobias Ringström - Rx interrupt status checking suggestion + + Andrew Morton - Clear blocked signals, avoid + buffer overrun setting current->comm. + + Kalle Olavi Niemitalo - Wake-on-LAN ioctls + + Robert Kuebel - Save kernel thread from dying on any signal. + + Submitting bug reports: + + "rtl8139-diag -mmmaaavvveefN" output + enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log + +*/ + +#define DRV_NAME "8139too" +#define DRV_VERSION "0.9.27" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION +#define PFX DRV_NAME ": " + +/* Default Message level */ +#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK) + + +/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ +#ifdef CONFIG_8139TOO_PIO +#define USE_IO_OPS 1 +#endif + +/* define to 1, 2 or 3 to enable copious debugging info */ +#define RTL8139_DEBUG 0 + +/* define to 1 to disable lightweight runtime debugging checks */ +#undef RTL8139_NDEBUG + + +#if RTL8139_DEBUG +/* note: prints function name for you */ +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + +#ifdef RTL8139_NDEBUG +# define assert(expr) do {} while (0) +#else +# define assert(expr) \ + if(unlikely(!(expr))) { \ + printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + + +/* A few user-configurable values. */ +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* bitmapped message enable number */ +static int debug = -1; + +/* + * Receive ring size + * Warning: 64K ring has hardware issues and may lock up. + */ +#if defined(CONFIG_SH_DREAMCAST) +#define RX_BUF_IDX 1 /* 16K ring */ +#else +#define RX_BUF_IDX 2 /* 32K ring */ +#endif +#define RX_BUF_LEN (8192 << RX_BUF_IDX) +#define RX_BUF_PAD 16 +#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ + +#if RX_BUF_LEN == 65536 +#define RX_BUF_TOT_LEN RX_BUF_LEN +#else +#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) +#endif + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + + +enum { + HAS_MII_XCVR = 0x010000, + HAS_CHIP_XCVR = 0x020000, + HAS_LNK_CHNG = 0x040000, +}; + +#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ +#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ +#define RTL_MIN_IO_SIZE 0x80 +#define RTL8139B_IO_SIZE 256 + +#define RTL8129_CAPS HAS_MII_XCVR +#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG + +typedef enum { + RTL8139 = 0, + RTL8129, +} board_t; + + +/* indexed by board_t, above */ +static struct { + const char *name; + u32 hw_flags; +} board_info[] __devinitdata = { + { "RealTek RTL8139", RTL8139_CAPS }, + { "RealTek RTL8129", RTL8129_CAPS }, +}; + + +static struct pci_device_id rtl8139_pci_tbl[] = { + {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + +#ifdef CONFIG_SH_SECUREEDGE5410 + /* Bogus 8139 silicon reports 8129 without external PROM :-( */ + {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, +#endif +#ifdef CONFIG_8139TOO_8129 + {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, +#endif + + /* some crazy cards report invalid vendor ids like + * 0x0001 here. The other ids are valid and constant, + * so we simply don't match on the main vendor id. + */ + {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, + {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 }, + {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 }, + + {0,} +}; +MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "early_rx" }, + { "tx_buf_mapped" }, + { "tx_timeouts" }, + { "rx_lost_in_ring" }, +}; + +/* The rest of these values should never change. */ + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + FlashReg = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + TxSummary = 0x60, + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ + RxNoWrap = (1 << 7), +}; + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +}; + +enum Cfg9346Bits { + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, +}; + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139A_G, + CH_8139B, + CH_8130, + CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) + +/* directly indexed by chip_t, above */ +const static struct { + const char *name; + u32 version; /* from RTL8139C/RTL8139D docs */ + u32 flags; +} rtl_chip_info[] = { + { "RTL-8139", + HW_REVID(1, 0, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139 rev K", + HW_REVID(1, 1, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139A", + HW_REVID(1, 1, 1, 0, 0, 0, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139A rev G", + HW_REVID(1, 1, 1, 0, 0, 1, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139B", + HW_REVID(1, 1, 1, 1, 0, 0, 0), + HasLWake, + }, + + { "RTL-8130", + HW_REVID(1, 1, 1, 1, 1, 0, 0), + HasLWake, + }, + + { "RTL-8139C", + HW_REVID(1, 1, 1, 0, 1, 0, 0), + HasLWake, + }, + + { "RTL-8100", + HW_REVID(1, 1, 1, 1, 0, 1, 0), + HasLWake, + }, + + { "RTL-8100B/8139D", + HW_REVID(1, 1, 1, 0, 1, 0, 1), + HasLWake, + }, + + { "RTL-8101", + HW_REVID(1, 1, 1, 0, 1, 1, 1), + HasLWake, + }, +}; + +struct rtl_extra_stats { + unsigned long early_rx; + unsigned long tx_buf_mapped; + unsigned long tx_timeouts; + unsigned long rx_lost_in_ring; +}; + +struct rtl8139_private { + void __iomem *mmio_addr; + int drv_flags; + struct pci_dev *pci_dev; + u32 msg_enable; + struct net_device_stats stats; + unsigned char *rx_ring; + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int tx_flag; + unsigned long cur_tx; + unsigned long dirty_tx; + unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + dma_addr_t rx_ring_dma; + dma_addr_t tx_bufs_dma; + signed char phys[4]; /* MII device addresses. */ + char twistie, twist_row, twist_col; /* Twister tune state. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + spinlock_t lock; + spinlock_t rx_lock; + chip_t chipset; + pid_t thr_pid; + wait_queue_head_t thr_wait; + struct completion thr_exited; + u32 rx_config; + struct rtl_extra_stats xstats; + int time_to_die; + struct mii_if_info mii; + unsigned int regs_len; + unsigned long fifo_copy_timeout; +}; + +MODULE_AUTHOR ("Jeff Garzik "); +MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_param(multicast_filter_limit, int, 0); +module_param_array(media, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +module_param(debug, int, 0); +MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); +MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); +MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); + +static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); +static int rtl8139_open (struct net_device *dev); +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 val); +static void rtl8139_start_thread(struct net_device *dev); +static void rtl8139_tx_timeout (struct net_device *dev); +static void rtl8139_init_ring (struct net_device *dev); +static int rtl8139_start_xmit (struct sk_buff *skb, + struct net_device *dev); +static int rtl8139_poll(struct net_device *dev, int *budget); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void rtl8139_poll_controller(struct net_device *dev); +#endif +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs); +static int rtl8139_close (struct net_device *dev); +static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); +static void rtl8139_set_rx_mode (struct net_device *dev); +static void __set_rx_mode (struct net_device *dev); +static void rtl8139_hw_start (struct net_device *dev); +static struct ethtool_ops rtl8139_ethtool_ops; + +/* write MMIO register, with flush */ +/* Flush avoids rtl8139 bug w/ posted MMIO writes */ +#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) +#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) +#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) + + +#define MMIO_FLUSH_AUDIT_COMPLETE 1 +#if MMIO_FLUSH_AUDIT_COMPLETE + +/* write MMIO register */ +#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) + +#else + +/* write MMIO register, then flush */ +#define RTL_W8 RTL_W8_F +#define RTL_W16 RTL_W16_F +#define RTL_W32 RTL_W32_F + +#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ + +/* read MMIO register */ +#define RTL_R8(reg) ioread8 (ioaddr + (reg)) +#define RTL_R16(reg) ioread16 (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg))) + + +static const u16 rtl8139_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | + TxErr | TxOK | RxErr | RxOK; + +static const u16 rtl8139_norx_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | + TxErr | TxOK | RxErr ; + +#if RX_BUF_IDX == 0 +static const unsigned int rtl8139_rx_config = + RxCfgRcv8K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 1 +static const unsigned int rtl8139_rx_config = + RxCfgRcv16K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 2 +static const unsigned int rtl8139_rx_config = + RxCfgRcv32K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 3 +static const unsigned int rtl8139_rx_config = + RxCfgRcv64K | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#else +#error "Invalid configuration for 8139_RXBUF_IDX" +#endif + +static const unsigned int rtl8139_tx_config = + TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); + +static void __rtl8139_cleanup_dev (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + struct pci_dev *pdev; + + assert (dev != NULL); + assert (tp->pci_dev != NULL); + pdev = tp->pci_dev; + +#ifdef USE_IO_OPS + if (tp->mmio_addr) + ioport_unmap (tp->mmio_addr); +#else + if (tp->mmio_addr) + pci_iounmap (pdev, tp->mmio_addr); +#endif /* USE_IO_OPS */ + + /* it's ok to call this even if we have no regions to free */ + pci_release_regions (pdev); + + free_netdev(dev); + pci_set_drvdata (pdev, NULL); +} + + +static void rtl8139_chip_reset (void __iomem *ioaddr) +{ + int i; + + /* Soft reset the chip. */ + RTL_W8 (ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + barrier(); + if ((RTL_R8 (ChipCmd) & CmdReset) == 0) + break; + udelay (10); + } +} + + +static int __devinit rtl8139_init_board (struct pci_dev *pdev, + struct net_device **dev_out) +{ + void __iomem *ioaddr; + struct net_device *dev; + struct rtl8139_private *tp; + u8 tmp8; + int rc, disable_dev_on_err = 0; + unsigned int i; + unsigned long pio_start, pio_end, pio_flags, pio_len; + unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; + u32 version; + + assert (pdev != NULL); + + *dev_out = NULL; + + /* dev and priv zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*tp)); + if (dev == NULL) { + printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + tp = netdev_priv(dev); + tp->pci_dev = pdev; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + pio_start = pci_resource_start (pdev, 0); + pio_end = pci_resource_end (pdev, 0); + pio_flags = pci_resource_flags (pdev, 0); + pio_len = pci_resource_len (pdev, 0); + + mmio_start = pci_resource_start (pdev, 1); + mmio_end = pci_resource_end (pdev, 1); + mmio_flags = pci_resource_flags (pdev, 1); + mmio_len = pci_resource_len (pdev, 1); + + /* set this immediately, we need to know before + * we talk to the chip directly */ + DPRINTK("PIO region size == 0x%02X\n", pio_len); + DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); + +#ifdef USE_IO_OPS + /* make sure PCI base addr 0 is PIO */ + if (!(pio_flags & IORESOURCE_IO)) { + printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, "8139too"); + if (rc) + goto err_out; + disable_dev_on_err = 1; + + /* enable PCI bus-mastering */ + pci_set_master (pdev); + +#ifdef USE_IO_OPS + ioaddr = ioport_map(pio_start, pio_len); + if (!ioaddr) { + printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); + rc = -EIO; + goto err_out; + } + dev->base_addr = pio_start; + tp->mmio_addr = ioaddr; + tp->regs_len = pio_len; +#else + /* ioremap MMIO region */ + ioaddr = pci_iomap(pdev, 1, 0); + if (ioaddr == NULL) { + printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); + rc = -EIO; + goto err_out; + } + dev->base_addr = (long) ioaddr; + tp->mmio_addr = ioaddr; + tp->regs_len = mmio_len; +#endif /* USE_IO_OPS */ + + /* Bring old chips out of low-power mode. */ + RTL_W8 (HltClk, 'R'); + + /* check for missing/broken hardware */ + if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { + printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", + pci_name(pdev)); + rc = -EIO; + goto err_out; + } + + /* identify chip attached to board */ + version = RTL_R32 (TxConfig) & HW_REVID_MASK; + for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) + if (version == rtl_chip_info[i].version) { + tp->chipset = i; + goto match; + } + + /* if unknown chip, assume array element #0, original RTL-8139 in this case */ + printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", + pci_name(pdev)); + printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); + tp->chipset = 0; + +match: + DPRINTK ("chipset id (%d) == index %d, '%s'\n", + version, i, rtl_chip_info[i].name); + + if (tp->chipset >= CH_8139B) { + u8 new_tmp8 = tmp8 = RTL_R8 (Config1); + DPRINTK("PCI PM wakeup\n"); + if ((rtl_chip_info[tp->chipset].flags & HasLWake) && + (tmp8 & LWAKE)) + new_tmp8 &= ~LWAKE; + new_tmp8 |= Cfg1_PM_Enable; + if (new_tmp8 != tmp8) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tmp8); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + if (rtl_chip_info[tp->chipset].flags & HasLWake) { + tmp8 = RTL_R8 (Config4); + if (tmp8 & LWPTN) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config4, tmp8 & ~LWPTN); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + } + } else { + DPRINTK("Old chip wakeup\n"); + tmp8 = RTL_R8 (Config1); + tmp8 &= ~(SLEEP | PWRDN); + RTL_W8 (Config1, tmp8); + } + + rtl8139_chip_reset (ioaddr); + + *dev_out = dev; + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + if (disable_dev_on_err) + pci_disable_device (pdev); + return rc; +} + + +static int __devinit rtl8139_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtl8139_private *tp; + int i, addr_len, option; + void __iomem *ioaddr; + static int board_idx = -1; + u8 pci_rev; + + assert (pdev != NULL); + assert (ent != NULL); + + board_idx++; + + /* when we're built into the kernel, the driver version message + * is only printed if at least one 8139 board has been found + */ +#ifndef MODULE + { + static int printed_version; + if (!printed_version++) + printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); + } +#endif + + pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { + printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pci_name(pdev), pdev->vendor, pdev->device, pci_rev); + printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); + } + + i = rtl8139_init_board (pdev, &dev); + if (i < 0) + return i; + + assert (dev != NULL); + tp = netdev_priv(dev); + + ioaddr = tp->mmio_addr; + assert (ioaddr != NULL); + + addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + ((u16 *) (dev->dev_addr))[i] = + le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); + + /* The Rtl8139-specific entries in the device structure. */ + dev->open = rtl8139_open; + dev->hard_start_xmit = rtl8139_start_xmit; + dev->poll = rtl8139_poll; + dev->weight = 64; + dev->stop = rtl8139_close; + dev->get_stats = rtl8139_get_stats; + dev->set_multicast_list = rtl8139_set_rx_mode; + dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &rtl8139_ethtool_ops; + dev->tx_timeout = rtl8139_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = rtl8139_poll_controller; +#endif + + /* note: the hardware is not capable of sg/csum/highdma, however + * through the use of skb_copy_and_csum_dev we enable these + * features + */ + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + + dev->irq = pdev->irq; + + /* tp zeroed and aligned in alloc_etherdev */ + tp = netdev_priv(dev); + + /* note: tp->chipset set in rtl8139_init_board */ + tp->drv_flags = board_info[ent->driver_data].hw_flags; + tp->mmio_addr = ioaddr; + tp->msg_enable = + (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); + spin_lock_init (&tp->lock); + spin_lock_init (&tp->rx_lock); + init_waitqueue_head (&tp->thr_wait); + init_completion (&tp->thr_exited); + tp->mii.dev = dev; + tp->mii.mdio_read = mdio_read; + tp->mii.mdio_write = mdio_write; + tp->mii.phy_id_mask = 0x3f; + tp->mii.reg_num_mask = 0x1f; + + /* dev is fully set up and ready to use now */ + DPRINTK("about to register device named %s (%p)...\n", dev->name, dev); + i = register_netdev (dev); + if (i) goto err_out; + + pci_set_drvdata (pdev, dev); + + printk (KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + board_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", + dev->name, rtl_chip_info[tp->chipset].name); + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ +#ifdef CONFIG_8139TOO_8129 + if (tp->drv_flags & HAS_MII_XCVR) { + int phy, phy_idx = 0; + for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + u16 advertising = mdio_read(dev, phy, 4); + tp->phys[phy_idx++] = phy; + printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " + "advertising %4.4x.\n", + dev->name, phy, mii_status, advertising); + } + } + if (phy_idx == 0) { + printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " + "transceiver.\n", + dev->name); + tp->phys[0] = 32; + } + } else +#endif + tp->phys[0] = 32; + tp->mii.phy_id = tp->phys[0]; + + /* The lower four bits are the media type. */ + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + if (option > 0) { + tp->mii.full_duplex = (option & 0x210) ? 1 : 0; + tp->default_port = option & 0xFF; + if (tp->default_port) + tp->mii.force_media = 1; + } + if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) + tp->mii.full_duplex = full_duplex[board_idx]; + if (tp->mii.full_duplex) { + printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + /* Changing the MII-advertised media because might prevent + re-connection. */ + tp->mii.force_media = 1; + } + if (tp->default_port) { + printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", + (option & 0x20 ? 100 : 10), + (option & 0x10 ? "full" : "half")); + mdio_write(dev, tp->phys[0], 0, + ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ + ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Put the chip into low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); + return i; +} + + +static void __devexit rtl8139_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + assert (dev != NULL); + + unregister_netdev (dev); + + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); +} + + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() RTL_R32(Cfg9346) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + int read_cmd = location | (EE_READ_CMD << addr_len); + + RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + RTL_W8 (Cfg9346, EE_ENB | dataval); + eeprom_delay (); + RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); + eeprom_delay (); + } + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + for (i = 16; i > 0; i--) { + RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); + eeprom_delay (); + retval = + (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : + 0); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + } + + /* Terminate the EEPROM access. */ + RTL_W8 (Cfg9346, ~EE_CS); + eeprom_delay (); + + return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ +#define MDIO_DIR 0x80 +#define MDIO_DATA_OUT 0x04 +#define MDIO_DATA_IN 0x02 +#define MDIO_CLK 0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() RTL_R8(Config4) + + +static char mii_2_8139_map[8] = { + BasicModeCtrl, + BasicModeStatus, + 0, + 0, + NWayAdvert, + NWayLPAR, + NWayExpansion, + 0 +}; + + +#ifdef CONFIG_8139TOO_8129 +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync (void __iomem *ioaddr) +{ + int i; + + for (i = 32; i >= 0; i--) { + RTL_W8 (Config4, MDIO_WRITE1); + mdio_delay (); + RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); + mdio_delay (); + } +} +#endif + +static int mdio_read (struct net_device *dev, int phy_id, int location) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int retval = 0; +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + return location < 8 && mii_2_8139_map[location] ? + RTL_R16 (mii_2_8139_map[location]) : 0; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + + RTL_W8 (Config4, MDIO_DIR | dataval); + mdio_delay (); + RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); + mdio_delay (); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif + + return (retval >> 1) & 0xffff; +} + + +static void mdio_write (struct net_device *dev, int phy_id, int location, + int value) +{ + struct rtl8139_private *tp = netdev_priv(dev); +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + if (location == 0) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W16 (BasicModeCtrl, value); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } else if (location < 8 && mii_2_8139_map[location]) + RTL_W16 (mii_2_8139_map[location], value); + return; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + RTL_W8 (Config4, dataval); + mdio_delay (); + RTL_W8 (Config4, dataval | MDIO_CLK); + mdio_delay (); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif +} + + +static int rtl8139_open (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int retval; + void __iomem *ioaddr = tp->mmio_addr; + + retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) + return retval; + + tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + &tp->tx_bufs_dma); + tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + &tp->rx_ring_dma); + if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { + free_irq(dev->irq, dev); + + if (tp->tx_bufs) + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + if (tp->rx_ring) + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + + return -ENOMEM; + + } + + tp->mii.full_duplex = tp->mii.force_media; + tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; + + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + netif_start_queue (dev); + + if (netif_msg_ifup(tp)) + printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", + dev->name, pci_resource_start (tp->pci_dev, 1), + dev->irq, RTL_R8 (MediaStatus), + tp->mii.full_duplex ? "full" : "half"); + + rtl8139_start_thread(dev); + + return 0; +} + + +static void rtl_check_media (struct net_device *dev, unsigned int init_media) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + if (tp->phys[0] >= 0) { + mii_check_media(&tp->mii, netif_msg_link(tp), init_media); + } +} + +/* Start the hardware at open or resume. */ +static void rtl8139_hw_start (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 i; + u8 tmp; + + /* Bring old chips out of low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'R'); + + rtl8139_chip_reset (ioaddr); + + /* unlock Config[01234] and BMCR register writes */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Restore our idea of the MAC address. */ + RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); + RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + RTL_W32 (TxConfig, rtl8139_tx_config); + + tp->cur_rx = 0; + + rtl_check_media (dev, 1); + + if (tp->chipset >= CH_8139B) { + /* Disable magic packet scanning, which is enabled + * when PM is enabled in Config1. It can be reenabled + * via ETHTOOL_SWOL if desired. */ + RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); + } + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* init Tx buffer DMA addresses */ + for (i = 0; i < NUM_TX_DESC; i++) + RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); + + RTL_W32 (RxMissed, 0); + + rtl8139_set_rx_mode (dev); + + /* no early-rx interrupts */ + RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); + + /* make sure RxTx has started */ + tmp = RTL_R8 (ChipCmd); + if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W16 (IntrMask, rtl8139_intr_mask); +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void rtl8139_init_ring (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int i; + + tp->cur_rx = 0; + tp->cur_tx = 0; + tp->dirty_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) + tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; +} + + +/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ +static int next_tick = 3 * HZ; + +#ifndef CONFIG_8139TOO_TUNE_TWISTER +static inline void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) {} +#else +enum TwisterParamVals { + PARA78_default = 0x78fa8388, + PARA7c_default = 0xcb38de43, /* param[0][3] */ + PARA7c_xxx = 0xcb38de43, +}; + +static const unsigned long param[4][4] = { + {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} +}; + +static void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) +{ + int linkcase; + void __iomem *ioaddr = tp->mmio_addr; + + /* This is a complicated state machine to configure the "twister" for + impedance/echos based on the cable length. + All of this is magic and undocumented. + */ + switch (tp->twistie) { + case 1: + if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { + /* We have link beat, let us tune the twister. */ + RTL_W16 (CSCR, CSCR_LinkDownOffCmd); + tp->twistie = 2; /* Change to state 2. */ + next_tick = HZ / 10; + } else { + /* Just put in some reasonable defaults for when beat returns. */ + RTL_W16 (CSCR, CSCR_LinkDownCmd); + RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + tp->twistie = 0; /* Bail from future actions. */ + } + break; + case 2: + /* Read how long it took to hear the echo. */ + linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; + if (linkcase == 0x7000) + tp->twist_row = 3; + else if (linkcase == 0x3000) + tp->twist_row = 2; + else if (linkcase == 0x1000) + tp->twist_row = 1; + else + tp->twist_row = 0; + tp->twist_col = 0; + tp->twistie = 3; /* Change to state 2. */ + next_tick = HZ / 10; + break; + case 3: + /* Put out four tuning parameters, one per 100msec. */ + if (tp->twist_col == 0) + RTL_W16 (FIFOTMS, 0); + RTL_W32 (PARA7c, param[(int) tp->twist_row] + [(int) tp->twist_col]); + next_tick = HZ / 10; + if (++tp->twist_col >= 4) { + /* For short cables we are done. + For long cables (row == 3) check for mistune. */ + tp->twistie = + (tp->twist_row == 3) ? 4 : 0; + } + break; + case 4: + /* Special case for long cables: check for mistune. */ + if ((RTL_R16 (CSCR) & + CSCR_LinkStatusBits) == 0x7000) { + tp->twistie = 0; + break; + } else { + RTL_W32 (PARA7c, 0xfb38de03); + tp->twistie = 5; + next_tick = HZ / 10; + } + break; + case 5: + /* Retune for shorter cable (column 2). */ + RTL_W32 (FIFOTMS, 0x20); + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + RTL_W32 (FIFOTMS, 0x00); + tp->twist_row = 2; + tp->twist_col = 0; + tp->twistie = 3; + next_tick = HZ / 10; + break; + + default: + /* do nothing */ + break; + } +} +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ + +static inline void rtl8139_thread_iter (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + int mii_lpa; + + mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); + + if (!tp->mii.force_media && mii_lpa != 0xffff) { + int duplex = (mii_lpa & LPA_100FULL) + || (mii_lpa & 0x01C0) == 0x0040; + if (tp->mii.full_duplex != duplex) { + tp->mii.full_duplex = duplex; + + if (mii_lpa) { + printk (KERN_INFO + "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", + dev->name, + tp->mii.full_duplex ? "full" : "half", + tp->phys[0], mii_lpa); + } else { + printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", + dev->name); + } +#if 0 + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); + RTL_W8 (Cfg9346, Cfg9346_Lock); +#endif + } + } + + next_tick = HZ * 60; + + rtl8139_tune_twister (dev, tp); + + DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", + dev->name, RTL_R16 (NWayLPAR)); + DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n", + dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); + DPRINTK ("%s: Chip config %2.2x %2.2x.\n", + dev->name, RTL_R8 (Config0), + RTL_R8 (Config1)); +} + +static int rtl8139_thread (void *data) +{ + struct net_device *dev = data; + struct rtl8139_private *tp = netdev_priv(dev); + unsigned long timeout; + + daemonize("%s", dev->name); + allow_signal(SIGTERM); + + while (1) { + timeout = next_tick; + do { + timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); + /* make swsusp happy with our thread */ + try_to_freeze(); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + flush_signals(current); + } + + if (tp->time_to_die) + break; + + if (rtnl_lock_interruptible ()) + break; + rtl8139_thread_iter (dev, tp, tp->mmio_addr); + rtnl_unlock (); + } + + complete_and_exit (&tp->thr_exited, 0); +} + +static void rtl8139_start_thread(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + tp->thr_pid = -1; + tp->twistie = 0; + tp->time_to_die = 0; + if (tp->chipset == CH_8139_K) + tp->twistie = 1; + else if (tp->drv_flags & HAS_LNK_CHNG) + return; + + tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES); + if (tp->thr_pid < 0) { + printk (KERN_WARNING "%s: unable to start kernel thread\n", + dev->name); + } +} + +static inline void rtl8139_tx_clear (struct rtl8139_private *tp) +{ + tp->cur_tx = 0; + tp->dirty_tx = 0; + + /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ +} + + +static void rtl8139_tx_timeout (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int i; + u8 tmp8; + unsigned long flags; + + printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " + "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), + RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus)); + /* Emit info to figure out what went wrong. */ + printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", + dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + + tp->xstats.tx_timeouts++; + + /* disable Tx ASAP, if not already */ + tmp8 = RTL_R8 (ChipCmd); + if (tmp8 & CmdTxEnb) + RTL_W8 (ChipCmd, CmdRxEnb); + + spin_lock(&tp->rx_lock); + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0x0000); + + /* Stop a shared interrupt from scavenging while we are. */ + spin_lock_irqsave (&tp->lock, flags); + rtl8139_tx_clear (tp); + spin_unlock_irqrestore (&tp->lock, flags); + + /* ...and finally, reset everything */ + if (netif_running(dev)) { + rtl8139_hw_start (dev); + netif_wake_queue (dev); + } + spin_unlock(&tp->rx_lock); +} + + +static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int entry; + unsigned int len = skb->len; + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % NUM_TX_DESC; + + /* Note: the chip doesn't have auto-pad! */ + if (likely(len < TX_BUF_SIZE)) { + if (len < ETH_ZLEN) + memset(tp->tx_buf[entry], 0, ETH_ZLEN); + skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); + dev_kfree_skb(skb); + } else { + dev_kfree_skb(skb); + tp->stats.tx_dropped++; + return 0; + } + + spin_lock_irq(&tp->lock); + RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), + tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); + + dev->trans_start = jiffies; + + tp->cur_tx++; + wmb(); + + if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) + netif_stop_queue (dev); + spin_unlock_irq(&tp->lock); + + if (netif_msg_tx_queued(tp)) + printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); + + return 0; +} + + +static void rtl8139_tx_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + unsigned long dirty_tx, tx_left; + + assert (dev != NULL); + assert (ioaddr != NULL); + + dirty_tx = tp->dirty_tx; + tx_left = tp->cur_tx - dirty_tx; + while (tx_left > 0) { + int entry = dirty_tx % NUM_TX_DESC; + int txstatus; + + txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); + + if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) + break; /* It still hasn't been Txed */ + + /* Note: TxCarrierLost is always asserted at 100mbps. */ + if (txstatus & (TxOutOfWindow | TxAborted)) { + /* There was an major error, log it. */ + if (netif_msg_tx_err(tp)) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, txstatus); + tp->stats.tx_errors++; + if (txstatus & TxAborted) { + tp->stats.tx_aborted_errors++; + RTL_W32 (TxConfig, TxClearAbt); + RTL_W16 (IntrStatus, TxErr); + wmb(); + } + if (txstatus & TxCarrierLost) + tp->stats.tx_carrier_errors++; + if (txstatus & TxOutOfWindow) + tp->stats.tx_window_errors++; + } else { + if (txstatus & TxUnderrun) { + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; + tp->stats.tx_fifo_errors++; + } + tp->stats.collisions += (txstatus >> 24) & 15; + tp->stats.tx_bytes += txstatus & 0x7ff; + tp->stats.tx_packets++; + } + + dirty_tx++; + tx_left--; + } + +#ifndef RTL8139_NDEBUG + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { + printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", + dev->name, dirty_tx, tp->cur_tx); + dirty_tx += NUM_TX_DESC; + } +#endif /* RTL8139_NDEBUG */ + + /* only wake the queue if we did work, and the queue is stopped */ + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + mb(); + netif_wake_queue (dev); + } +} + + +/* TODO: clean this up! Rx reset need not be this intensive */ +static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, + struct rtl8139_private *tp, void __iomem *ioaddr) +{ + u8 tmp8; +#ifdef CONFIG_8139_OLD_RX_RESET + int tmp_work; +#endif + + if (netif_msg_rx_err (tp)) + printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n", + dev->name, rx_status); + tp->stats.rx_errors++; + if (!(rx_status & RxStatusOK)) { + if (rx_status & RxTooLong) { + DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", + dev->name, rx_status); + /* A.C.: The chip hangs here. */ + } + if (rx_status & (RxBadSymbol | RxBadAlign)) + tp->stats.rx_frame_errors++; + if (rx_status & (RxRunt | RxTooLong)) + tp->stats.rx_length_errors++; + if (rx_status & RxCRCErr) + tp->stats.rx_crc_errors++; + } else { + tp->xstats.rx_lost_in_ring++; + } + +#ifndef CONFIG_8139_OLD_RX_RESET + tmp8 = RTL_R8 (ChipCmd); + RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); + RTL_W8 (ChipCmd, tmp8); + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; +#else + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + + /* disable receive */ + RTL_W8_F (ChipCmd, CmdTxEnb); + tmp_work = 200; + while (--tmp_work > 0) { + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if (!(tmp8 & CmdRxEnb)) + break; + } + if (tmp_work <= 0) + printk (KERN_WARNING PFX "rx stop wait too long\n"); + /* restart receive */ + tmp_work = 200; + while (--tmp_work > 0) { + RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) + break; + } + if (tmp_work <= 0) + printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); + + /* and reinitialize all rx related registers */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; + + DPRINTK("init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* A.C.: Reset the multicast list. */ + __set_rx_mode (dev); +#endif +} + +#if RX_BUF_IDX == 3 +static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, + u32 offset, unsigned int size) +{ + u32 left = RX_BUF_LEN - offset; + + if (size > left) { + memcpy(skb->data, ring + offset, left); + memcpy(skb->data+left, ring, size - left); + } else + memcpy(skb->data, ring + offset, size); +} +#endif + +static void rtl8139_isr_ack(struct rtl8139_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + u16 status; + + status = RTL_R16 (IntrStatus) & RxAckBits; + + /* Clear out errors and receive interrupts */ + if (likely(status != 0)) { + if (unlikely(status & (RxFIFOOver | RxOverflow))) { + tp->stats.rx_errors++; + if (status & RxFIFOOver) + tp->stats.rx_fifo_errors++; + } + RTL_W16_F (IntrStatus, RxAckBits); + } +} + +static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, + int budget) +{ + void __iomem *ioaddr = tp->mmio_addr; + int received = 0; + unsigned char *rx_ring = tp->rx_ring; + unsigned int cur_rx = tp->cur_rx; + unsigned int rx_size = 0; + + DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); + + while (netif_running(dev) && received < budget + && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + u32 ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status; + unsigned int pkt_size; + struct sk_buff *skb; + + rmb(); + + /* read size+status of next frame from DMA ring buffer */ + rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); + rx_size = rx_status >> 16; + pkt_size = rx_size - 4; + + if (netif_msg_rx_status(tp)) + printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," + " cur %4.4x.\n", dev->name, rx_status, + rx_size, cur_rx); +#if RTL8139_DEBUG > 2 + { + int i; + DPRINTK ("%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + printk (" %2.2x", + rx_ring[ring_offset + i]); + printk (".\n"); + } +#endif + + /* Packet copy from FIFO still in progress. + * Theoretically, this should never happen + * since EarlyRx is disabled. + */ + if (unlikely(rx_size == 0xfff0)) { + if (!tp->fifo_copy_timeout) + tp->fifo_copy_timeout = jiffies + 2; + else if (time_after(jiffies, tp->fifo_copy_timeout)) { + DPRINTK ("%s: hung FIFO. Reset.", dev->name); + rx_size = 0; + goto no_early_rx; + } + if (netif_msg_intr(tp)) { + printk(KERN_DEBUG "%s: fifo copy in progress.", + dev->name); + } + tp->xstats.early_rx++; + break; + } + +no_early_rx: + tp->fifo_copy_timeout = 0; + + /* If Rx err or invalid rx_size/rx_status received + * (which happens if we get lost in the ring), + * Rx process gets reset, so we abort any further + * Rx processing. + */ + if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || + (rx_size < 8) || + (!(rx_status & RxStatusOK)))) { + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + received = -1; + goto out; + } + + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + + skb = dev_alloc_skb (pkt_size + 2); + if (likely(skb)) { + skb->dev = dev; + skb_reserve (skb, 2); /* 16 byte align the IP fields. */ +#if RX_BUF_IDX == 3 + wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); +#else + eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); +#endif + skb_put (skb, pkt_size); + + skb->protocol = eth_type_trans (skb, dev); + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + + netif_receive_skb (skb); + } else { + if (net_ratelimit()) + printk (KERN_WARNING + "%s: Memory squeeze, dropping packet.\n", + dev->name); + tp->stats.rx_dropped++; + } + received++; + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); + + rtl8139_isr_ack(tp); + } + + if (unlikely(!received || rx_size == 0xfff0)) + rtl8139_isr_ack(tp); + +#if RTL8139_DEBUG > 1 + DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + RTL_R16 (RxBufAddr), + RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); +#endif + + tp->cur_rx = cur_rx; + + /* + * The receive buffer should be mostly empty. + * Tell NAPI to reenable the Rx irq. + */ + if (tp->fifo_copy_timeout) + received = budget; + +out: + return received; +} + + +static void rtl8139_weird_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr, + int status, int link_changed) +{ + DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + /* Update the error count. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + if ((status & RxUnderrun) && link_changed && + (tp->drv_flags & HAS_LNK_CHNG)) { + rtl_check_media(dev, 0); + status &= ~RxUnderrun; + } + + if (status & (RxUnderrun | RxErr)) + tp->stats.rx_errors++; + + if (status & PCSTimeout) + tp->stats.rx_length_errors++; + if (status & RxUnderrun) + tp->stats.rx_fifo_errors++; + if (status & PCIErr) { + u16 pci_cmd_status; + pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); + pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); + + printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", + dev->name, pci_cmd_status); + } +} + +static int rtl8139_poll(struct net_device *dev, int *budget) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int orig_budget = min(*budget, dev->quota); + int done = 1; + + spin_lock(&tp->rx_lock); + if (likely(RTL_R16(IntrStatus) & RxAckBits)) { + int work_done; + + work_done = rtl8139_rx(dev, tp, orig_budget); + if (likely(work_done > 0)) { + *budget -= work_done; + dev->quota -= work_done; + done = (work_done < orig_budget); + } + } + + if (done) { + /* + * Order is important since data can get interrupted + * again when we think we are done. + */ + local_irq_disable(); + RTL_W16_F(IntrMask, rtl8139_intr_mask); + __netif_rx_complete(dev); + local_irq_enable(); + } + spin_unlock(&tp->rx_lock); + + return !done; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u16 status, ackstat; + int link_changed = 0; /* avoid bogus "uninit" warning */ + int handled = 0; + + spin_lock (&tp->lock); + status = RTL_R16 (IntrStatus); + + /* shared irq? */ + if (unlikely((status & rtl8139_intr_mask) == 0)) + goto out; + + handled = 1; + + /* h/w no longer present (hotplug?) or major error, bail */ + if (unlikely(status == 0xFFFF)) + goto out; + + /* close possible race's with dev_close */ + if (unlikely(!netif_running(dev))) { + RTL_W16 (IntrMask, 0); + goto out; + } + + /* Acknowledge all of the current interrupt sources ASAP, but + an first get an additional status bit from CSCR. */ + if (unlikely(status & RxUnderrun)) + link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; + + ackstat = status & ~(RxAckBits | TxErr); + if (ackstat) + RTL_W16 (IntrStatus, ackstat); + + /* Receive packets are processed by poll routine. + If not running start it now. */ + if (status & RxAckBits){ + if (netif_rx_schedule_prep(dev)) { + RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); + __netif_rx_schedule (dev); + } + } + + /* Check uncommon events with one test. */ + if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) + rtl8139_weird_interrupt (dev, tp, ioaddr, + status, link_changed); + + if (status & (TxOK | TxErr)) { + rtl8139_tx_interrupt (dev, tp, ioaddr); + if (status & TxErr) + RTL_W16 (IntrStatus, TxErr); + } + out: + spin_unlock (&tp->lock); + + DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling receive - used by netconsole and other diagnostic tools + * to allow network i/o with interrupts disabled. + */ +static void rtl8139_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + rtl8139_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + +static int rtl8139_close (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int ret = 0; + unsigned long flags; + + netif_stop_queue (dev); + + if (tp->thr_pid >= 0) { + tp->time_to_die = 1; + wmb(); + ret = kill_proc (tp->thr_pid, SIGTERM, 1); + if (ret) { + printk (KERN_ERR "%s: unable to signal thread\n", dev->name); + return ret; + } + wait_for_completion (&tp->thr_exited); + } + + if (netif_msg_ifdown(tp)) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", + dev->name, RTL_R16 (IntrStatus)); + + spin_lock_irqsave (&tp->lock, flags); + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8 (ChipCmd, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + synchronize_irq (dev->irq); /* racy, but that's ok here */ + free_irq (dev->irq, dev); + + rtl8139_tx_clear (tp); + + pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + tp->rx_ring = NULL; + tp->tx_bufs = NULL; + + /* Green! Put the chip in low-power mode. */ + RTL_W8 (Cfg9346, Cfg9346_Unlock); + + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; +} + + +/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to + kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and + other threads or interrupts aren't messing with the 8139. */ +static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mmio_addr; + + spin_lock_irq(&np->lock); + if (rtl_chip_info[np->chipset].flags & HasLWake) { + u8 cfg3 = RTL_R8 (Config3); + u8 cfg5 = RTL_R8 (Config5); + + wol->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + + wol->wolopts = 0; + if (cfg3 & Cfg3_LinkUp) + wol->wolopts |= WAKE_PHY; + if (cfg3 & Cfg3_Magic) + wol->wolopts |= WAKE_MAGIC; + /* (KON)FIXME: See how netdev_set_wol() handles the + following constants. */ + if (cfg5 & Cfg5_UWF) + wol->wolopts |= WAKE_UCAST; + if (cfg5 & Cfg5_MWF) + wol->wolopts |= WAKE_MCAST; + if (cfg5 & Cfg5_BWF) + wol->wolopts |= WAKE_BCAST; + } + spin_unlock_irq(&np->lock); +} + + +/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes + that wol points to kernel memory and other threads or interrupts + aren't messing with the 8139. */ +static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mmio_addr; + u32 support; + u8 cfg3, cfg5; + + support = ((rtl_chip_info[np->chipset].flags & HasLWake) + ? (WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) + : 0); + if (wol->wolopts & ~support) + return -EINVAL; + + spin_lock_irq(&np->lock); + cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); + if (wol->wolopts & WAKE_PHY) + cfg3 |= Cfg3_LinkUp; + if (wol->wolopts & WAKE_MAGIC) + cfg3 |= Cfg3_Magic; + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config3, cfg3); + RTL_W8 (Cfg9346, Cfg9346_Lock); + + cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); + /* (KON)FIXME: These are untested. We may have to set the + CRC0, Wakeup0 and LSBCRC0 registers too, but I have no + documentation. */ + if (wol->wolopts & WAKE_UCAST) + cfg5 |= Cfg5_UWF; + if (wol->wolopts & WAKE_MCAST) + cfg5 |= Cfg5_MWF; + if (wol->wolopts & WAKE_BCAST) + cfg5 |= Cfg5_BWF; + RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ + spin_unlock_irq(&np->lock); + + return 0; +} + +static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct rtl8139_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); + info->regdump_len = np->regs_len; +} + +static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return 0; +} + +static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + int rc; + spin_lock_irq(&np->lock); + rc = mii_ethtool_sset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return rc; +} + +static int rtl8139_nway_reset(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii); +} + +static u32 rtl8139_get_link(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii); +} + +static u32 rtl8139_get_msglevel(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return np->msg_enable; +} + +static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) +{ + struct rtl8139_private *np = netdev_priv(dev); + np->msg_enable = datum; +} + +/* TODO: we are too slack to do reg dumping for pio, for now */ +#ifdef CONFIG_8139TOO_PIO +#define rtl8139_get_regs_len NULL +#define rtl8139_get_regs NULL +#else +static int rtl8139_get_regs_len(struct net_device *dev) +{ + struct rtl8139_private *np = netdev_priv(dev); + return np->regs_len; +} + +static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct rtl8139_private *np = netdev_priv(dev); + + regs->version = RTL_REGS_VER; + + spin_lock_irq(&np->lock); + memcpy_fromio(regbuf, np->mmio_addr, regs->len); + spin_unlock_irq(&np->lock); +} +#endif /* CONFIG_8139TOO_MMIO */ + +static int rtl8139_get_stats_count(struct net_device *dev) +{ + return RTL_NUM_STATS; +} + +static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct rtl8139_private *np = netdev_priv(dev); + + data[0] = np->xstats.early_rx; + data[1] = np->xstats.tx_buf_mapped; + data[2] = np->xstats.tx_timeouts; + data[3] = np->xstats.rx_lost_in_ring; +} + +static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static struct ethtool_ops rtl8139_ethtool_ops = { + .get_drvinfo = rtl8139_get_drvinfo, + .get_settings = rtl8139_get_settings, + .set_settings = rtl8139_set_settings, + .get_regs_len = rtl8139_get_regs_len, + .get_regs = rtl8139_get_regs, + .nway_reset = rtl8139_nway_reset, + .get_link = rtl8139_get_link, + .get_msglevel = rtl8139_get_msglevel, + .set_msglevel = rtl8139_set_msglevel, + .get_wol = rtl8139_get_wol, + .set_wol = rtl8139_set_wol, + .get_strings = rtl8139_get_strings, + .get_stats_count = rtl8139_get_stats_count, + .get_ethtool_stats = rtl8139_get_ethtool_stats, +}; + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8139_private *np = netdev_priv(dev); + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&np->lock); + + return rc; +} + + +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (netif_running(dev)) { + spin_lock_irqsave (&tp->lock, flags); + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + spin_unlock_irqrestore (&tp->lock, flags); + } + + return &tp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + This routine is not state sensitive and need not be SMP locked. */ + +static void __set_rx_mode (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; + u32 tmp; + + DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", + dev->name, dev->flags, RTL_R32 (RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", + dev->name); + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + /* We can safely update without stopping the chip. */ + tmp = rtl8139_rx_config | rx_mode; + if (tp->rx_config != tmp) { + RTL_W32_F (RxConfig, tmp); + tp->rx_config = tmp; + } + RTL_W32_F (MAR0 + 0, mc_filter[0]); + RTL_W32_F (MAR0 + 4, mc_filter[1]); +} + +static void rtl8139_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct rtl8139_private *tp = netdev_priv(dev); + + spin_lock_irqsave (&tp->lock, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore (&tp->lock, flags); +} + +#ifdef CONFIG_PM + +static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + pci_save_state (pdev); + + if (!netif_running (dev)) + return 0; + + netif_device_detach (dev); + + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + RTL_W16 (IntrMask, 0); + RTL_W8 (ChipCmd, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + pci_set_power_state (pdev, PCI_D3hot); + + return 0; +} + + +static int rtl8139_resume (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + + pci_restore_state (pdev); + if (!netif_running (dev)) + return 0; + pci_set_power_state (pdev, PCI_D0); + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + netif_device_attach (dev); + return 0; +} + +#endif /* CONFIG_PM */ + + +static struct pci_driver rtl8139_pci_driver = { + .name = DRV_NAME, + .id_table = rtl8139_pci_tbl, + .probe = rtl8139_init_one, + .remove = __devexit_p(rtl8139_remove_one), +#ifdef CONFIG_PM + .suspend = rtl8139_suspend, + .resume = rtl8139_resume, +#endif /* CONFIG_PM */ +}; + + +static int __init rtl8139_init_module (void) +{ + /* when we're a module, we always print a version message, + * even if no 8139 board is found. + */ +#ifdef MODULE + printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); +#endif + + return pci_module_init (&rtl8139_pci_driver); +} + + +static void __exit rtl8139_cleanup_module (void) +{ + pci_unregister_driver (&rtl8139_pci_driver); +} + + +module_init(rtl8139_init_module); +module_exit(rtl8139_cleanup_module); diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/8139too.c --- a/drivers/8139too.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3045 +0,0 @@ -/****************************************************************************** - * - * drv_8139too.c - * - * EtherCAT-Treiber für RTL8139-kompatible Netzwerkkarten. - * - * Autoren: Wilhelm Hagemeister, Florian Pose - * - * $Date$ - * $Author$ - * - * (C) Copyright IgH 2005 - * Ingenieurgemeinschaft IgH - * Heinz-Bäcker Str. 34 - * D-45356 Essen - * Tel.: +49 201/61 99 31 - * Fax.: +49 201/61 98 36 - * E-mail: sp@igh-essen.com - * - ******************************************************************************/ - -/* - 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. - - Maintained by Jeff Garzik - Copyright 2000-2002 Jeff Garzik - - Much code comes from Donald Becker's rtl8139.c driver, - versions 1.13 and older. This driver was originally based - on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: - - ---------- - - Written 1997-2001 by Donald Becker. - This software may be used and distributed according to the - terms of the GNU General Public License (GPL), incorporated - herein by reference. Drivers based on or derived from this - code fall under the GPL and must retain the authorship, - copyright and license notice. This file is not a complete - program and may only be used when the entire operating - system is licensed under the GPL. - - This driver is for boards based on the RTL8129 and RTL8139 - PCI ethernet chips. - - The author may be reached as becker@scyld.com, or C/O Scyld - Computing Corporation 410 Severn Ave., Suite 210 Annapolis - MD 21403 - - Support and updates available at - http://www.scyld.com/network/rtl8139.html - - Twister-tuning table provided by Kinston - . - - ---------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - Contributors: - - Donald Becker - he wrote the original driver, kudos to him! - (but please don't e-mail him for support, this isn't his driver) - - Tigran Aivazian - bug fixes, skbuff free cleanup - - Martin Mares - suggestions for PCI cleanup - - David S. Miller - PCI DMA and softnet updates - - Ernst Gill - fixes ported from BSD driver - - Daniel Kobras - identified specific locations of - posted MMIO write bugginess - - Gerard Sharp - bug fix, testing and feedback - - David Ford - Rx ring wrap fix - - Dan DeMaggio - swapped RTL8139 cards with me, and allowed me - to find and fix a crucial bug on older chipsets. - - Donald Becker/Chris Butterworth/Marcus Westergren - - Noticed various Rx packet size-related buglets. - - Santiago Garcia Mantinan - testing and feedback - - Jens David - 2.2.x kernel backports - - Martin Dennett - incredibly helpful insight on undocumented - features of the 8139 chips - - Jean-Jacques Michel - bug fix - - Tobias Ringström - Rx interrupt status checking suggestion - - Andrew Morton - Clear blocked signals, avoid - buffer overrun setting current->comm. - - Kalle Olavi Niemitalo - Wake-on-LAN ioctls - - Robert Kuebel - Save kernel thread from dying on any signal. - - Submitting bug reports: - - "rtl8139-diag -mmmaaavvveefN" output - enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log - -*/ - -#define DRV_NAME "8139too_ecat" -#define DRV_VERSION "0.9.27" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -#include "ec_device.h" -#include "ec_master.h" -#include "ec_module.h" - -#define LITERAL(X) #X -#define STRINGIFY(X) LITERAL(X) - -#define COMPILE_INFO "Revision " STRINGIFY(EC_REV) \ - ", compiled by " STRINGIFY(EC_USER) \ - " at " STRINGIFY(EC_DATE) - -/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION -#define PFX DRV_NAME ": " - -/* Default Message level */ -#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ - NETIF_MSG_PROBE | \ - NETIF_MSG_LINK) - - -/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ -#ifdef CONFIG_8139TOO_PIO -#define USE_IO_OPS 1 -#endif - -/* define to 1, 2 or 3 to enable copious debugging info */ -#define RTL8139_DEBUG 0 - -/* define to 1 to disable lightweight runtime debugging checks */ -#undef RTL8139_NDEBUG - - -#if RTL8139_DEBUG -/* note: prints function name for you */ -# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -#ifdef RTL8139_NDEBUG -# define assert(expr) do {} while (0) -#else -# define assert(expr) \ - if(unlikely(!(expr))) { \ - printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* A few user-configurable values. */ -/* media options */ -#define MAX_UNITS 8 -static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The RTL chips use a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; - -/* bitmapped message enable number */ -static int debug = -1; - -/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -// Uncomment for debugging -//#define ECAT_DEBUG - -// Device index for EtherCAT device selection -static int ec_device_index = -1; -static int ec_device_master_index = 0; - -static EtherCAT_device_t rtl_ecat_dev; -int rtl_ecat_dev_registered = 0; - -/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -/* - * Receive ring size - * Warning: 64K ring has hardware issues and may lock up. - */ -#if defined(CONFIG_SH_DREAMCAST) -#define RX_BUF_IDX 1 /* 16K ring */ -#else -#define RX_BUF_IDX 2 /* 32K ring */ -#endif -#define RX_BUF_LEN (8192 << RX_BUF_IDX) -#define RX_BUF_PAD 16 -#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ - -#if RX_BUF_LEN == 65536 -#define RX_BUF_TOT_LEN RX_BUF_LEN -#else -#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) -#endif - -/* Number of Tx descriptor registers. */ -#define NUM_TX_DESC 4 - -/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE -#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) - -/* PCI Tuning Parameters - Threshold is bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ -#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ -#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ -#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) - - -enum { - HAS_MII_XCVR = 0x010000, - HAS_CHIP_XCVR = 0x020000, - HAS_LNK_CHNG = 0x040000, -}; - -#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ -#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ -#define RTL_MIN_IO_SIZE 0x80 -#define RTL8139B_IO_SIZE 256 - -#define RTL8129_CAPS HAS_MII_XCVR -#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG - -typedef enum { - RTL8139 = 0, - RTL8129, -} board_t; - - -/* indexed by board_t, above */ -static struct { - const char *name; - u32 hw_flags; -} board_info[] __devinitdata = { - { "RealTek RTL8139", RTL8139_CAPS }, - { "RealTek RTL8129", RTL8129_CAPS }, -}; - - -static struct pci_device_id rtl8139_pci_tbl[] = { - {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - -#ifdef CONFIG_SH_SECUREEDGE5410 - /* Bogus 8139 silicon reports 8129 without external PROM :-( */ - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, -#endif -#ifdef CONFIG_8139TOO_8129 - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, -#endif - - /* some crazy cards report invalid vendor ids like - * 0x0001 here. The other ids are valid and constant, - * so we simply don't match on the main vendor id. - */ - {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 }, - - {0,} -}; -MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); - -static struct { - const char str[ETH_GSTRING_LEN]; -} ethtool_stats_keys[] = { - { "early_rx" }, - { "tx_buf_mapped" }, - { "tx_timeouts" }, - { "rx_lost_in_ring" }, -}; - -/* The rest of these values should never change. */ - -/* Symbolic offsets to registers. */ -enum RTL8139_registers { - MAC0 = 0, /* Ethernet hardware address. */ - MAR0 = 8, /* Multicast filter. */ - TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ - TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ - RxBuf = 0x30, - ChipCmd = 0x37, - RxBufPtr = 0x38, - RxBufAddr = 0x3A, - IntrMask = 0x3C, - IntrStatus = 0x3E, - TxConfig = 0x40, - RxConfig = 0x44, - Timer = 0x48, /* A general-purpose counter. */ - RxMissed = 0x4C, /* 24 bits valid, write clears. */ - Cfg9346 = 0x50, - Config0 = 0x51, - Config1 = 0x52, - FlashReg = 0x54, - MediaStatus = 0x58, - Config3 = 0x59, - Config4 = 0x5A, /* absent on RTL-8139A */ - HltClk = 0x5B, - MultiIntr = 0x5C, - TxSummary = 0x60, - BasicModeCtrl = 0x62, - BasicModeStatus = 0x64, - NWayAdvert = 0x66, - NWayLPAR = 0x68, - NWayExpansion = 0x6A, - /* Undocumented registers, but required for proper operation. */ - FIFOTMS = 0x70, /* FIFO Control and test. */ - CSCR = 0x74, /* Chip Status and Configuration Register. */ - PARA78 = 0x78, - PARA7c = 0x7c, /* Magic transceiver parameter register. */ - Config5 = 0xD8, /* absent on RTL-8139A */ -}; - -enum ClearBitMasks { - MultiIntrClear = 0xF000, - ChipCmdClear = 0xE2, - Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), -}; - -enum ChipCmdBits { - CmdReset = 0x10, - CmdRxEnb = 0x08, - CmdTxEnb = 0x04, - RxBufEmpty = 0x01, -}; - -/* Interrupt register bits, using my own meaningful names. */ -enum IntrStatusBits { - PCIErr = 0x8000, - PCSTimeout = 0x4000, - RxFIFOOver = 0x40, - RxUnderrun = 0x20, - RxOverflow = 0x10, - TxErr = 0x08, - TxOK = 0x04, - RxErr = 0x02, - RxOK = 0x01, - - RxAckBits = RxFIFOOver | RxOverflow | RxOK, -}; - -enum TxStatusBits { - TxHostOwns = 0x2000, - TxUnderrun = 0x4000, - TxStatOK = 0x8000, - TxOutOfWindow = 0x20000000, - TxAborted = 0x40000000, - TxCarrierLost = 0x80000000, -}; -enum RxStatusBits { - RxMulticast = 0x8000, - RxPhysical = 0x4000, - RxBroadcast = 0x2000, - RxBadSymbol = 0x0020, - RxRunt = 0x0010, - RxTooLong = 0x0008, - RxCRCErr = 0x0004, - RxBadAlign = 0x0002, - RxStatusOK = 0x0001, -}; - -/* Bits in RxConfig. */ -enum rx_mode_bits { - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0x08, - AcceptMulticast = 0x04, - AcceptMyPhys = 0x02, - AcceptAllPhys = 0x01, -}; - -/* Bits in TxConfig. */ -enum tx_config_bits { - - /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ - TxIFGShift = 24, - TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ - TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ - TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ - TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ - - TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ - TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ - TxClearAbt = (1 << 0), /* Clear abort (WO) */ - TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ - TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ - - TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ -}; - -/* Bits in Config1 */ -enum Config1Bits { - Cfg1_PM_Enable = 0x01, - Cfg1_VPD_Enable = 0x02, - Cfg1_PIO = 0x04, - Cfg1_MMIO = 0x08, - LWAKE = 0x10, /* not on 8139, 8139A */ - Cfg1_Driver_Load = 0x20, - Cfg1_LED0 = 0x40, - Cfg1_LED1 = 0x80, - SLEEP = (1 << 1), /* only on 8139, 8139A */ - PWRDN = (1 << 0), /* only on 8139, 8139A */ -}; - -/* Bits in Config3 */ -enum Config3Bits { - Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ - Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ - Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ - Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ - Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ - Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ - Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ - Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ -}; - -/* Bits in Config4 */ -enum Config4Bits { - LWPTN = (1 << 2), /* not on 8139, 8139A */ -}; - -/* Bits in Config5 */ -enum Config5Bits { - Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ - Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ - Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ - Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ - Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ - Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ - Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ -}; - -enum RxConfigBits { - /* rx fifo threshold */ - RxCfgFIFOShift = 13, - RxCfgFIFONone = (7 << RxCfgFIFOShift), - - /* Max DMA burst */ - RxCfgDMAShift = 8, - RxCfgDMAUnlimited = (7 << RxCfgDMAShift), - - /* rx ring buffer length */ - RxCfgRcv8K = 0, - RxCfgRcv16K = (1 << 11), - RxCfgRcv32K = (1 << 12), - RxCfgRcv64K = (1 << 11) | (1 << 12), - - /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ - RxNoWrap = (1 << 7), -}; - -/* Twister tuning parameters from RealTek. - Completely undocumented, but required to tune bad links on some boards. */ -enum CSCRBits { - CSCR_LinkOKBit = 0x0400, - CSCR_LinkChangeBit = 0x0800, - CSCR_LinkStatusBits = 0x0f000, - CSCR_LinkDownOffCmd = 0x003c0, - CSCR_LinkDownCmd = 0x0f3c0, -}; - -enum Cfg9346Bits { - Cfg9346_Lock = 0x00, - Cfg9346_Unlock = 0xC0, -}; - -typedef enum { - CH_8139 = 0, - CH_8139_K, - CH_8139A, - CH_8139A_G, - CH_8139B, - CH_8130, - CH_8139C, - CH_8100, - CH_8100B_8139D, - CH_8101, -} chip_t; - -enum chip_flags { - HasHltClk = (1 << 0), - HasLWake = (1 << 1), -}; - -#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ - (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) -#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) - -/* directly indexed by chip_t, above */ -const static struct { - const char *name; - u32 version; /* from RTL8139C/RTL8139D docs */ - u32 flags; -} rtl_chip_info[] = { - { "RTL-8139", - HW_REVID(1, 0, 0, 0, 0, 0, 0), - HasHltClk, - }, - - { "RTL-8139 rev K", - HW_REVID(1, 1, 0, 0, 0, 0, 0), - HasHltClk, - }, - - { "RTL-8139A", - HW_REVID(1, 1, 1, 0, 0, 0, 0), - HasHltClk, /* XXX undocumented? */ - }, - - { "RTL-8139A rev G", - HW_REVID(1, 1, 1, 0, 0, 1, 0), - HasHltClk, /* XXX undocumented? */ - }, - - { "RTL-8139B", - HW_REVID(1, 1, 1, 1, 0, 0, 0), - HasLWake, - }, - - { "RTL-8130", - HW_REVID(1, 1, 1, 1, 1, 0, 0), - HasLWake, - }, - - { "RTL-8139C", - HW_REVID(1, 1, 1, 0, 1, 0, 0), - HasLWake, - }, - - { "RTL-8100", - HW_REVID(1, 1, 1, 1, 0, 1, 0), - HasLWake, - }, - - { "RTL-8100B/8139D", - HW_REVID(1, 1, 1, 0, 1, 0, 1), - HasLWake, - }, - - { "RTL-8101", - HW_REVID(1, 1, 1, 0, 1, 1, 1), - HasLWake, - }, -}; - -struct rtl_extra_stats { - unsigned long early_rx; - unsigned long tx_buf_mapped; - unsigned long tx_timeouts; - unsigned long rx_lost_in_ring; -}; - -struct rtl8139_private { - void __iomem *mmio_addr; - int drv_flags; - struct pci_dev *pci_dev; - u32 msg_enable; - struct net_device_stats stats; - unsigned char *rx_ring; - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ - unsigned int tx_flag; - unsigned long cur_tx; - unsigned long dirty_tx; - unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ - unsigned char *tx_bufs; /* Tx bounce buffer region. */ - dma_addr_t rx_ring_dma; - dma_addr_t tx_bufs_dma; - signed char phys[4]; /* MII device addresses. */ - char twistie, twist_row, twist_col; /* Twister tune state. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - spinlock_t lock; - spinlock_t rx_lock; - chip_t chipset; - pid_t thr_pid; - wait_queue_head_t thr_wait; - struct completion thr_exited; - u32 rx_config; - struct rtl_extra_stats xstats; - int time_to_die; - struct mii_if_info mii; - unsigned int regs_len; - unsigned long fifo_copy_timeout; -}; - -/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -MODULE_AUTHOR ("Wilhelm Hagemeister , Florian Pose "); -MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver with EtherCAT functionality"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(COMPILE_INFO); - -/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -module_param(multicast_filter_limit, int, 0); -module_param_array(media, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(debug, int, 0); -MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); -MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); -MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); -MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); - -/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -module_param(ec_device_index, int, -1); -module_param(ec_device_master_index, int, 0); -MODULE_PARM_DESC(ec_device_index, "Index of the device reserved for EtherCAT."); -MODULE_PARM_DESC(ec_device_master_index, "Index of the EtherCAT master to register the device."); - -/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); -static int rtl8139_open (struct net_device *dev); -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 val); -static void rtl8139_start_thread(struct net_device *dev); -static void rtl8139_tx_timeout (struct net_device *dev); -static void rtl8139_init_ring (struct net_device *dev); -static int rtl8139_start_xmit (struct sk_buff *skb, - struct net_device *dev); -int rtl8139_poll(struct net_device *dev, int *budget); -#ifdef CONFIG_NET_POLL_CONTROLLER -static void rtl8139_poll_controller(struct net_device *dev); -#endif -irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs); -static int rtl8139_close (struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); -static void rtl8139_set_rx_mode (struct net_device *dev); -static void __set_rx_mode (struct net_device *dev); -static void rtl8139_hw_start (struct net_device *dev); -static struct ethtool_ops rtl8139_ethtool_ops; - -/* write MMIO register, with flush */ -/* Flush avoids rtl8139 bug w/ posted MMIO writes */ -#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) -#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) -#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) - - -#define MMIO_FLUSH_AUDIT_COMPLETE 1 -#if MMIO_FLUSH_AUDIT_COMPLETE - -/* write MMIO register */ -#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) - -#else - -/* write MMIO register, then flush */ -#define RTL_W8 RTL_W8_F -#define RTL_W16 RTL_W16_F -#define RTL_W32 RTL_W32_F - -#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ - -/* read MMIO register */ -#define RTL_R8(reg) ioread8 (ioaddr + (reg)) -#define RTL_R16(reg) ioread16 (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg))) - - -static const u16 rtl8139_intr_mask = - PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | - TxErr | TxOK | RxErr | RxOK; - -static const u16 rtl8139_norx_intr_mask = - PCIErr | PCSTimeout | RxUnderrun | - TxErr | TxOK | RxErr ; - -#if RX_BUF_IDX == 0 -static const unsigned int rtl8139_rx_config = - RxCfgRcv8K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 1 -static const unsigned int rtl8139_rx_config = - RxCfgRcv16K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 2 -static const unsigned int rtl8139_rx_config = - RxCfgRcv32K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 3 -static const unsigned int rtl8139_rx_config = - RxCfgRcv64K | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#else -#error "Invalid configuration for 8139_RXBUF_IDX" -#endif - -static const unsigned int rtl8139_tx_config = - TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); - -static void __rtl8139_cleanup_dev (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - struct pci_dev *pdev; - - assert (dev != NULL); - assert (tp->pci_dev != NULL); - pdev = tp->pci_dev; - -#ifdef USE_IO_OPS - if (tp->mmio_addr) - ioport_unmap (tp->mmio_addr); -#else - if (tp->mmio_addr) - pci_iounmap (pdev, tp->mmio_addr); -#endif /* USE_IO_OPS */ - - /* it's ok to call this even if we have no regions to free */ - pci_release_regions (pdev); - - free_netdev(dev); - pci_set_drvdata (pdev, NULL); -} - - -static void rtl8139_chip_reset (void __iomem *ioaddr) -{ - int i; - - /* Soft reset the chip. */ - RTL_W8 (ChipCmd, CmdReset); - - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) { - barrier(); - if ((RTL_R8 (ChipCmd) & CmdReset) == 0) - break; - udelay (10); - } -} - - -static int __devinit rtl8139_init_board (struct pci_dev *pdev, - struct net_device **dev_out) -{ - void __iomem *ioaddr; - struct net_device *dev; - struct rtl8139_private *tp; - u8 tmp8; - int rc, disable_dev_on_err = 0; - unsigned int i; - unsigned long pio_start, pio_end, pio_flags, pio_len; - unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 version; - - assert (pdev != NULL); - - *dev_out = NULL; - - /* dev and priv zeroed in alloc_etherdev */ - dev = alloc_etherdev (sizeof (*tp)); - if (dev == NULL) { - printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); - - tp = netdev_priv(dev); - tp->pci_dev = pdev; - - /* enable device (incl. PCI PM wakeup and hotplug setup) */ - rc = pci_enable_device (pdev); - if (rc) - goto err_out; - - pio_start = pci_resource_start (pdev, 0); - pio_end = pci_resource_end (pdev, 0); - pio_flags = pci_resource_flags (pdev, 0); - pio_len = pci_resource_len (pdev, 0); - - mmio_start = pci_resource_start (pdev, 1); - mmio_end = pci_resource_end (pdev, 1); - mmio_flags = pci_resource_flags (pdev, 1); - mmio_len = pci_resource_len (pdev, 1); - - /* set this immediately, we need to know before - * we talk to the chip directly */ - DPRINTK("PIO region size == 0x%02X\n", pio_len); - DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); - -#ifdef USE_IO_OPS - /* make sure PCI base addr 0 is PIO */ - if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } - /* check for weird/broken PCI region reporting */ - if (pio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } -#else - /* make sure PCI base addr 1 is MMIO */ - if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } - if (mmio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } -#endif - - rc = pci_request_regions (pdev, "8139too"); - if (rc) - goto err_out; - disable_dev_on_err = 1; - - /* enable PCI bus-mastering */ - pci_set_master (pdev); - -#ifdef USE_IO_OPS - ioaddr = ioport_map(pio_start, pio_len); - if (!ioaddr) { - printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); - rc = -EIO; - goto err_out; - } - dev->base_addr = pio_start; - tp->mmio_addr = ioaddr; - tp->regs_len = pio_len; -#else - /* ioremap MMIO region */ - ioaddr = pci_iomap(pdev, 1, 0); - if (ioaddr == NULL) { - printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); - rc = -EIO; - goto err_out; - } - dev->base_addr = (long) ioaddr; - tp->mmio_addr = ioaddr; - tp->regs_len = mmio_len; -#endif /* USE_IO_OPS */ - - /* Bring old chips out of low-power mode. */ - RTL_W8 (HltClk, 'R'); - - /* check for missing/broken hardware */ - if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { - printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", - pci_name(pdev)); - rc = -EIO; - goto err_out; - } - - /* identify chip attached to board */ - version = RTL_R32 (TxConfig) & HW_REVID_MASK; - for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) - if (version == rtl_chip_info[i].version) { - tp->chipset = i; - goto match; - } - - /* if unknown chip, assume array element #0, original RTL-8139 in this case */ - printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", - pci_name(pdev)); - printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); - tp->chipset = 0; - -match: - DPRINTK ("chipset id (%d) == index %d, '%s'\n", - version, i, rtl_chip_info[i].name); - - if (tp->chipset >= CH_8139B) { - u8 new_tmp8 = tmp8 = RTL_R8 (Config1); - DPRINTK("PCI PM wakeup\n"); - if ((rtl_chip_info[tp->chipset].flags & HasLWake) && - (tmp8 & LWAKE)) - new_tmp8 &= ~LWAKE; - new_tmp8 |= Cfg1_PM_Enable; - if (new_tmp8 != tmp8) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tmp8); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } - if (rtl_chip_info[tp->chipset].flags & HasLWake) { - tmp8 = RTL_R8 (Config4); - if (tmp8 & LWPTN) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config4, tmp8 & ~LWPTN); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } - } - } else { - DPRINTK("Old chip wakeup\n"); - tmp8 = RTL_R8 (Config1); - tmp8 &= ~(SLEEP | PWRDN); - RTL_W8 (Config1, tmp8); - } - - rtl8139_chip_reset (ioaddr); - - *dev_out = dev; - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - if (disable_dev_on_err) - pci_disable_device (pdev); - return rc; -} - - -static int __devinit rtl8139_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev = NULL; - struct rtl8139_private *tp; - int i, addr_len, option; - void __iomem *ioaddr; - static int board_idx = -1; - u8 pci_rev; - - assert (pdev != NULL); - assert (ent != NULL); - - board_idx++; - - /* when we're built into the kernel, the driver version message - * is only printed if at least one 8139 board has been found - */ -#ifndef MODULE - { - static int printed_version; - if (!printed_version++) - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); - } -#endif - - pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); - - if (pdev->vendor == PCI_VENDOR_ID_REALTEK && - pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { - printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", - pci_name(pdev), pdev->vendor, pdev->device, pci_rev); - printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); - } - - i = rtl8139_init_board (pdev, &dev); - if (i < 0) - return i; - - assert (dev != NULL); - tp = netdev_priv(dev); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (board_idx == ec_device_index) - { - printk("EtherCAT registering board %d.\n", board_idx); - - if (EtherCAT_device_assign(&rtl_ecat_dev, dev) < 0) - goto err_out; - - strcpy(dev->name,"ecat0"); //device name überschreiben - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - ioaddr = tp->mmio_addr; - assert (ioaddr != NULL); - - addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; - for (i = 0; i < 3; i++) - ((u16 *) (dev->dev_addr))[i] = - le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); - - /* The Rtl8139-specific entries in the device structure. */ - dev->open = rtl8139_open; - dev->hard_start_xmit = rtl8139_start_xmit; - dev->poll = rtl8139_poll; - dev->weight = 64; - dev->stop = rtl8139_close; - dev->get_stats = rtl8139_get_stats; - dev->set_multicast_list = rtl8139_set_rx_mode; - dev->do_ioctl = netdev_ioctl; - dev->ethtool_ops = &rtl8139_ethtool_ops; - dev->tx_timeout = rtl8139_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = rtl8139_poll_controller; -#endif - - /* note: the hardware is not capable of sg/csum/highdma, however - * through the use of skb_copy_and_csum_dev we enable these - * features - */ - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; - - dev->irq = pdev->irq; - - /* tp zeroed and aligned in alloc_etherdev */ - tp = netdev_priv(dev); - - /* note: tp->chipset set in rtl8139_init_board */ - tp->drv_flags = board_info[ent->driver_data].hw_flags; - tp->mmio_addr = ioaddr; - tp->msg_enable = - (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); - spin_lock_init (&tp->lock); - spin_lock_init (&tp->rx_lock); - - init_waitqueue_head (&tp->thr_wait); - init_completion (&tp->thr_exited); - tp->mii.dev = dev; - tp->mii.mdio_read = mdio_read; - tp->mii.mdio_write = mdio_write; - tp->mii.phy_id_mask = 0x3f; - tp->mii.reg_num_mask = 0x1f; - - /* dev is fully set up and ready to use now */ - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - /* EtherCAT-Karten nicht beim Stack anmelden. */ - if (dev != rtl_ecat_dev.dev) - { - DPRINTK("About to register device named %s (%p)...\n", dev->name, dev); - i = register_netdev (dev); - if (i) goto err_out; - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - pci_set_drvdata (pdev, dev); - - printk (KERN_INFO "%s: %s at 0x%lx, " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " - "IRQ %d\n", - dev->name, - board_info[ent->driver_data].name, - dev->base_addr, - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5], - dev->irq); - - printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", - dev->name, rtl_chip_info[tp->chipset].name); - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, but - takes too much time. */ -#ifdef CONFIG_8139TOO_8129 - if (tp->drv_flags & HAS_MII_XCVR) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { - u16 advertising = mdio_read(dev, phy, 4); - tp->phys[phy_idx++] = phy; - printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " - "advertising %4.4x.\n", - dev->name, phy, mii_status, advertising); - } - } - if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " - "transceiver.\n", - dev->name); - tp->phys[0] = 32; - } - } else -#endif - tp->phys[0] = 32; - tp->mii.phy_id = tp->phys[0]; - - /* The lower four bits are the media type. */ - option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; - if (option > 0) { - tp->mii.full_duplex = (option & 0x210) ? 1 : 0; - tp->default_port = option & 0xFF; - if (tp->default_port) - tp->mii.force_media = 1; - } - if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) - tp->mii.full_duplex = full_duplex[board_idx]; - if (tp->mii.full_duplex) { - printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); - /* Changing the MII-advertised media because might prevent - re-connection. */ - tp->mii.force_media = 1; - } - if (tp->default_port) { - printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", - (option & 0x20 ? 100 : 10), - (option & 0x10 ? "full" : "half")); - mdio_write(dev, tp->phys[0], 0, - ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ - ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ - } - - /* Put the chip into low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - pci_disable_device (pdev); - return i; -} - - -static void __devexit rtl8139_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - - assert (dev != NULL); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - unregister_netdev (dev); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - __rtl8139_cleanup_dev (dev); - pci_disable_device (pdev); -} - - -/* Serial EEPROM section. */ - -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ -#define EE_CS 0x08 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x00 -#define EE_WRITE_1 0x02 -#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ -#define EE_ENB (0x80 | EE_CS) - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ - -#define eeprom_delay() RTL_R32(Cfg9346) - -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5) -#define EE_READ_CMD (6) -#define EE_ERASE_CMD (7) - -static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len) -{ - int i; - unsigned retval = 0; - int read_cmd = location | (EE_READ_CMD << addr_len); - - RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - RTL_W8 (Cfg9346, EE_ENB | dataval); - eeprom_delay (); - RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); - eeprom_delay (); - } - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - - for (i = 16; i > 0; i--) { - RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); - eeprom_delay (); - retval = - (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : - 0); - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - } - - /* Terminate the EEPROM access. */ - RTL_W8 (Cfg9346, ~EE_CS); - eeprom_delay (); - - return retval; -} - -/* MII serial management: mostly bogus for now. */ -/* Read and write the MII management registers using software-generated - serial MDIO protocol. - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues. */ -#define MDIO_DIR 0x80 -#define MDIO_DATA_OUT 0x04 -#define MDIO_DATA_IN 0x02 -#define MDIO_CLK 0x01 -#define MDIO_WRITE0 (MDIO_DIR) -#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) - -#define mdio_delay() RTL_R8(Config4) - - -static char mii_2_8139_map[8] = { - BasicModeCtrl, - BasicModeStatus, - 0, - 0, - NWayAdvert, - NWayLPAR, - NWayExpansion, - 0 -}; - - -#ifdef CONFIG_8139TOO_8129 -/* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync (void __iomem *ioaddr) -{ - int i; - - for (i = 32; i >= 0; i--) { - RTL_W8 (Config4, MDIO_WRITE1); - mdio_delay (); - RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); - mdio_delay (); - } -} -#endif - -static int mdio_read (struct net_device *dev, int phy_id, int location) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int retval = 0; -#ifdef CONFIG_8139TOO_8129 - void __iomem *ioaddr = tp->mmio_addr; - int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void __iomem *ioaddr = tp->mmio_addr; - return location < 8 && mii_2_8139_map[location] ? - RTL_R16 (mii_2_8139_map[location]) : 0; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (ioaddr); - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; - - RTL_W8 (Config4, MDIO_DIR | dataval); - mdio_delay (); - RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); - mdio_delay (); - } - - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - RTL_W8 (Config4, 0); - mdio_delay (); - retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); - RTL_W8 (Config4, MDIO_CLK); - mdio_delay (); - } -#endif - - return (retval >> 1) & 0xffff; -} - - -static void mdio_write (struct net_device *dev, int phy_id, int location, - int value) -{ - struct rtl8139_private *tp = netdev_priv(dev); -#ifdef CONFIG_8139TOO_8129 - void __iomem *ioaddr = tp->mmio_addr; - int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void __iomem *ioaddr = tp->mmio_addr; - if (location == 0) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W16 (BasicModeCtrl, value); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } else if (location < 8 && mii_2_8139_map[location]) - RTL_W16 (mii_2_8139_map[location], value); - return; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (ioaddr); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = - (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - RTL_W8 (Config4, dataval); - mdio_delay (); - RTL_W8 (Config4, dataval | MDIO_CLK); - mdio_delay (); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - RTL_W8 (Config4, 0); - mdio_delay (); - RTL_W8 (Config4, MDIO_CLK); - mdio_delay (); - } -#endif -} - - -static int rtl8139_open (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int retval; - void __iomem *ioaddr = tp->mmio_addr; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -#ifdef ECAT_DEBUG - printk(KERN_DEBUG "%s: open\n", dev->name); -#endif - - if (dev != rtl_ecat_dev.dev) - { - retval = request_irq(dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) - return retval; - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - &tp->tx_bufs_dma); - tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - &tp->rx_ring_dma); - if (tp->tx_bufs == NULL || tp->rx_ring == NULL) - { - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - free_irq(dev->irq, dev); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - if (tp->tx_bufs) - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - if (tp->rx_ring) - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - - return -ENOMEM; - } - - tp->mii.full_duplex = tp->mii.force_media; - tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; - - rtl8139_init_ring (dev); - rtl8139_hw_start (dev); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - netif_start_queue (dev); - - if (netif_msg_ifup(tp)) - { - printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d" - " GP Pins %2.2x %s-duplex.\n", - dev->name, pci_resource_start (tp->pci_dev, 1), - dev->irq, RTL_R8 (MediaStatus), - tp->mii.full_duplex ? "full" : "half"); - } - - rtl8139_start_thread(dev); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - return 0; -} - - -static void rtl_check_media (struct net_device *dev, unsigned int init_media) -{ - struct rtl8139_private *tp = netdev_priv(dev); - - if (tp->phys[0] >= 0) { - mii_check_media(&tp->mii, netif_msg_link(tp), init_media); - } -} - -/* Start the hardware at open or resume. */ -static void rtl8139_hw_start (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u32 i; - u8 tmp; - - /* Bring old chips out of low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'R'); - - rtl8139_chip_reset (ioaddr); - - /* unlock Config[01234] and BMCR register writes */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Restore our idea of the MAC address. */ - RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); - RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); - - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - RTL_W32 (TxConfig, rtl8139_tx_config); - - tp->cur_rx = 0; - - rtl_check_media (dev, 1); - - if (tp->chipset >= CH_8139B) { - /* Disable magic packet scanning, which is enabled - * when PM is enabled in Config1. It can be reenabled - * via ETHTOOL_SWOL if desired. */ - RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); - } - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* init Tx buffer DMA addresses */ - for (i = 0; i < NUM_TX_DESC; i++) - RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); - - RTL_W32 (RxMissed, 0); - - rtl8139_set_rx_mode (dev); - - /* no early-rx interrupts */ - RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); - - /* make sure RxTx has started */ - tmp = RTL_R8 (ChipCmd); - if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - /* Enable all known interrupts by setting the interrupt mask. */ - RTL_W16 (IntrMask, rtl8139_intr_mask); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void rtl8139_init_ring (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int i; - - tp->cur_rx = 0; - tp->cur_tx = 0; - tp->dirty_tx = 0; - - for (i = 0; i < NUM_TX_DESC; i++) - tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; -} - - -/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ -static int next_tick = 3 * HZ; - -#ifndef CONFIG_8139TOO_TUNE_TWISTER -static inline void rtl8139_tune_twister (struct net_device *dev, - struct rtl8139_private *tp) {} -#else -enum TwisterParamVals { - PARA78_default = 0x78fa8388, - PARA7c_default = 0xcb38de43, /* param[0][3] */ - PARA7c_xxx = 0xcb38de43, -}; - -static const unsigned long param[4][4] = { - {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} -}; - -static void rtl8139_tune_twister (struct net_device *dev, - struct rtl8139_private *tp) -{ - int linkcase; - void __iomem *ioaddr = tp->mmio_addr; - - /* This is a complicated state machine to configure the "twister" for - impedance/echos based on the cable length. - All of this is magic and undocumented. - */ - switch (tp->twistie) { - case 1: - if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { - /* We have link beat, let us tune the twister. */ - RTL_W16 (CSCR, CSCR_LinkDownOffCmd); - tp->twistie = 2; /* Change to state 2. */ - next_tick = HZ / 10; - } else { - /* Just put in some reasonable defaults for when beat returns. */ - RTL_W16 (CSCR, CSCR_LinkDownCmd); - RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ - RTL_W32 (PARA78, PARA78_default); - RTL_W32 (PARA7c, PARA7c_default); - tp->twistie = 0; /* Bail from future actions. */ - } - break; - case 2: - /* Read how long it took to hear the echo. */ - linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; - if (linkcase == 0x7000) - tp->twist_row = 3; - else if (linkcase == 0x3000) - tp->twist_row = 2; - else if (linkcase == 0x1000) - tp->twist_row = 1; - else - tp->twist_row = 0; - tp->twist_col = 0; - tp->twistie = 3; /* Change to state 2. */ - next_tick = HZ / 10; - break; - case 3: - /* Put out four tuning parameters, one per 100msec. */ - if (tp->twist_col == 0) - RTL_W16 (FIFOTMS, 0); - RTL_W32 (PARA7c, param[(int) tp->twist_row] - [(int) tp->twist_col]); - next_tick = HZ / 10; - if (++tp->twist_col >= 4) { - /* For short cables we are done. - For long cables (row == 3) check for mistune. */ - tp->twistie = - (tp->twist_row == 3) ? 4 : 0; - } - break; - case 4: - /* Special case for long cables: check for mistune. */ - if ((RTL_R16 (CSCR) & - CSCR_LinkStatusBits) == 0x7000) { - tp->twistie = 0; - break; - } else { - RTL_W32 (PARA7c, 0xfb38de03); - tp->twistie = 5; - next_tick = HZ / 10; - } - break; - case 5: - /* Retune for shorter cable (column 2). */ - RTL_W32 (FIFOTMS, 0x20); - RTL_W32 (PARA78, PARA78_default); - RTL_W32 (PARA7c, PARA7c_default); - RTL_W32 (FIFOTMS, 0x00); - tp->twist_row = 2; - tp->twist_col = 0; - tp->twistie = 3; - next_tick = HZ / 10; - break; - - default: - /* do nothing */ - break; - } -} -#endif /* CONFIG_8139TOO_TUNE_TWISTER */ - -static inline void rtl8139_thread_iter (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr) -{ - int mii_lpa; - - mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); - - if (!tp->mii.force_media && mii_lpa != 0xffff) { - int duplex = (mii_lpa & LPA_100FULL) - || (mii_lpa & 0x01C0) == 0x0040; - if (tp->mii.full_duplex != duplex) { - tp->mii.full_duplex = duplex; - - if (mii_lpa) { - printk (KERN_INFO - "%s: Setting %s-duplex based on MII #%d link" - " partner ability of %4.4x.\n", - dev->name, - tp->mii.full_duplex ? "full" : "half", - tp->phys[0], mii_lpa); - } else { - printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", - dev->name); - } -#if 0 - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); - RTL_W8 (Cfg9346, Cfg9346_Lock); -#endif - } - } - - next_tick = HZ * 60; - - rtl8139_tune_twister (dev, tp); - - DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", - dev->name, RTL_R16 (NWayLPAR)); - DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n", - dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); - DPRINTK ("%s: Chip config %2.2x %2.2x.\n", - dev->name, RTL_R8 (Config0), - RTL_R8 (Config1)); -} - -static int rtl8139_thread (void *data) -{ - struct net_device *dev = data; - struct rtl8139_private *tp = netdev_priv(dev); - unsigned long timeout; - - daemonize("%s", dev->name); - allow_signal(SIGTERM); - - while (1) { - timeout = next_tick; - do { - timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); - /* make swsusp happy with our thread */ - try_to_freeze(); - } while (!signal_pending (current) && (timeout > 0)); - - if (signal_pending (current)) { - flush_signals(current); - } - - if (tp->time_to_die) - break; - - if (rtnl_lock_interruptible ()) - break; - rtl8139_thread_iter (dev, tp, tp->mmio_addr); - rtnl_unlock (); - } - - complete_and_exit (&tp->thr_exited, 0); -} - -static void rtl8139_start_thread(struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - - tp->thr_pid = -1; - tp->twistie = 0; - tp->time_to_die = 0; - if (tp->chipset == CH_8139_K) - tp->twistie = 1; - else if (tp->drv_flags & HAS_LNK_CHNG) - return; - - tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES); - if (tp->thr_pid < 0) { - printk (KERN_WARNING "%s: unable to start kernel thread\n", - dev->name); - } -} - -static inline void rtl8139_tx_clear (struct rtl8139_private *tp) -{ - tp->cur_tx = 0; - tp->dirty_tx = 0; - - /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ -} - - -static void rtl8139_tx_timeout (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int i; - u8 tmp8; - unsigned long flags; - - printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " - "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), - RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus)); - /* Emit info to figure out what went wrong. */ - printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", - dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - - tp->xstats.tx_timeouts++; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - printk(KERN_DEBUG "%s: tx_timeout\n", dev->name); - - if (dev == rtl_ecat_dev.dev) - { - if (rtl_ecat_dev.state != ECAT_DS_SENT) - { - printk(KERN_WARNING "EtherCAT: Wrong status at timeout: %i\n", - rtl_ecat_dev.state); - } - - rtl_ecat_dev.state = ECAT_DS_TIMEOUT; - } - - /* disable Tx ASAP, if not already */ - tmp8 = RTL_R8 (ChipCmd); - if (tmp8 & CmdTxEnb) - RTL_W8 (ChipCmd, CmdRxEnb); - - if (dev != rtl_ecat_dev.dev) - { - spin_lock(&tp->rx_lock); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0x0000); - - /* Stop a shared interrupt from scavenging while we are. */ - spin_lock_irqsave (&tp->lock, flags); - rtl8139_tx_clear (tp); - spin_unlock_irqrestore (&tp->lock, flags); - - /* ...and finally, reset everything */ - - if (netif_running(dev)) - { - rtl8139_hw_start (dev); - netif_wake_queue (dev); - } - - spin_unlock(&tp->rx_lock); - } - else - { - rtl8139_tx_clear (tp); - rtl8139_hw_start(dev); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ -} - -static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned int entry; - unsigned int len = skb->len; - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % NUM_TX_DESC; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - /* Note: the chip doesn't have auto-pad! */ - if (likely(len < TX_BUF_SIZE)) - { - if (len < ETH_ZLEN) - memset(tp->tx_buf[entry], 0, ETH_ZLEN); - - skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); - if (dev != rtl_ecat_dev.dev) dev_kfree_skb(skb); - } - else - { - if (dev != rtl_ecat_dev.dev) dev_kfree_skb(skb); - tp->stats.tx_dropped++; - return 0; - } - - if (dev != rtl_ecat_dev.dev) - { - spin_lock_irq(&tp->lock); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), - tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); - - dev->trans_start = jiffies; - - tp->cur_tx++; - wmb(); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) - netif_stop_queue (dev); - - spin_unlock_irq(&tp->lock); - - if (netif_msg_tx_queued(tp)) - printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", - dev->name, len, entry); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - return 0; -} - - -static void rtl8139_tx_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr) -{ - unsigned long dirty_tx, tx_left; - - assert (dev != NULL); - assert (ioaddr != NULL); - - dirty_tx = tp->dirty_tx; - tx_left = tp->cur_tx - dirty_tx; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev) - { - rtl_ecat_dev.tx_intr_cnt++; - rdtscl(rtl_ecat_dev.tx_time); // Get CPU cycles - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - while (tx_left > 0) { - int entry = dirty_tx % NUM_TX_DESC; - int txstatus; - - txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); - - if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) - break; /* It still hasn't been Txed */ - - /* Note: TxCarrierLost is always asserted at 100mbps. */ - if (txstatus & (TxOutOfWindow | TxAborted)) { - /* There was an major error, log it. */ - if (netif_msg_tx_err(tp)) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, txstatus); - tp->stats.tx_errors++; - if (txstatus & TxAborted) { - tp->stats.tx_aborted_errors++; - RTL_W32 (TxConfig, TxClearAbt); - RTL_W16 (IntrStatus, TxErr); - wmb(); - } - if (txstatus & TxCarrierLost) - tp->stats.tx_carrier_errors++; - if (txstatus & TxOutOfWindow) - tp->stats.tx_window_errors++; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev) - { - rtl_ecat_dev.state = ECAT_DS_ERROR; - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - } else { - if (txstatus & TxUnderrun) { - /* Add 64 to the Tx FIFO threshold. */ - if (tp->tx_flag < 0x00300000) - tp->tx_flag += 0x00020000; - tp->stats.tx_fifo_errors++; - } - tp->stats.collisions += (txstatus >> 24) & 15; - tp->stats.tx_bytes += txstatus & 0x7ff; - tp->stats.tx_packets++; - } - - dirty_tx++; - tx_left--; - } - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - -#ifndef RTL8139_NDEBUG - if (dev != rtl_ecat_dev.dev && tp->cur_tx - dirty_tx > NUM_TX_DESC) { - printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", - dev->name, dirty_tx, tp->cur_tx); - dirty_tx += NUM_TX_DESC; - } -#endif /* RTL8139_NDEBUG */ - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - /* only wake the queue if we did work, and the queue is stopped */ - if (tp->dirty_tx != dirty_tx) { - tp->dirty_tx = dirty_tx; - mb(); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - netif_wake_queue (dev); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - } -} - - -/* TODO: clean this up! Rx reset need not be this intensive */ -static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, - struct rtl8139_private *tp, void __iomem *ioaddr) -{ - u8 tmp8; -#ifdef CONFIG_8139_OLD_RX_RESET - int tmp_work; -#endif - - if (netif_msg_rx_err (tp)) - printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n", - dev->name, rx_status); - tp->stats.rx_errors++; - if (!(rx_status & RxStatusOK)) { - if (rx_status & RxTooLong) { - DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", - dev->name, rx_status); - /* A.C.: The chip hangs here. */ - } - if (rx_status & (RxBadSymbol | RxBadAlign)) - tp->stats.rx_frame_errors++; - if (rx_status & (RxRunt | RxTooLong)) - tp->stats.rx_length_errors++; - if (rx_status & RxCRCErr) - tp->stats.rx_crc_errors++; - } else { - tp->xstats.rx_lost_in_ring++; - } - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev) - { - rtl_ecat_dev.state = ECAT_DS_ERROR; - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -#ifndef CONFIG_8139_OLD_RX_RESET - tmp8 = RTL_R8 (ChipCmd); - RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); - RTL_W8 (ChipCmd, tmp8); - RTL_W32 (RxConfig, tp->rx_config); - tp->cur_rx = 0; -#else - /* Reset the receiver, based on RealTek recommendation. (Bug?) */ - - /* disable receive */ - RTL_W8_F (ChipCmd, CmdTxEnb); - tmp_work = 200; - while (--tmp_work > 0) { - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if (!(tmp8 & CmdRxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "rx stop wait too long\n"); - /* restart receive */ - tmp_work = 200; - while (--tmp_work > 0) { - RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); - - /* and reinitialize all rx related registers */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - tp->cur_rx = 0; - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* A.C.: Reset the multicast list. */ - __set_rx_mode (dev); -#endif -} - -#if RX_BUF_IDX == 3 -static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, - u32 offset, unsigned int size) -{ - u32 left = RX_BUF_LEN - offset; - - if (size > left) { - memcpy(skb->data, ring + offset, left); - memcpy(skb->data+left, ring, size - left); - } else - memcpy(skb->data, ring + offset, size); -} -#endif - -static void rtl8139_isr_ack(struct rtl8139_private *tp) -{ - void __iomem *ioaddr = tp->mmio_addr; - u16 status; - - status = RTL_R16 (IntrStatus) & RxAckBits; - - /* Clear out errors and receive interrupts */ - if (likely(status != 0)) { - if (unlikely(status & (RxFIFOOver | RxOverflow))) { - tp->stats.rx_errors++; - if (status & RxFIFOOver) - tp->stats.rx_fifo_errors++; - } - RTL_W16_F (IntrStatus, RxAckBits); - } -} - -static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, - int budget) -{ - void __iomem *ioaddr = tp->mmio_addr; - int received = 0; - unsigned char *rx_ring = tp->rx_ring; - unsigned int cur_rx = tp->cur_rx; - unsigned int rx_size = 0; - - DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev) - { - rtl_ecat_dev.rx_intr_cnt++; - rdtscl(rtl_ecat_dev.rx_time); // Get CPU cycles - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - while ((dev == rtl_ecat_dev.dev || netif_running(dev)) - && received < budget - && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { - u32 ring_offset = cur_rx % RX_BUF_LEN; - u32 rx_status; - unsigned int pkt_size; - struct sk_buff *skb; - - rmb(); - - /* read size+status of next frame from DMA ring buffer */ - rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); - rx_size = rx_status >> 16; - pkt_size = rx_size - 4; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev && netif_msg_rx_status(tp)) - printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," - " cur %4.4x.\n", dev->name, rx_status, - rx_size, cur_rx); - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - -#if RTL8139_DEBUG > 2 - { - int i; - DPRINTK ("%s: Frame contents ", dev->name); - for (i = 0; i < 70; i++) - printk (" %2.2x", - rx_ring[ring_offset + i]); - printk (".\n"); - } -#endif - - /* Packet copy from FIFO still in progress. - * Theoretically, this should never happen - * since EarlyRx is disabled. - */ - if (unlikely(rx_size == 0xfff0)) { - if (!tp->fifo_copy_timeout) - tp->fifo_copy_timeout = jiffies + 2; - else if (time_after(jiffies, tp->fifo_copy_timeout)) { - DPRINTK ("%s: hung FIFO. Reset.", dev->name); - rx_size = 0; - goto no_early_rx; - } - if (netif_msg_intr(tp)) { - printk(KERN_DEBUG "%s: fifo copy in progress.", - dev->name); - } - tp->xstats.early_rx++; - break; - } - -no_early_rx: - tp->fifo_copy_timeout = 0; - - /* If Rx err or invalid rx_size/rx_status received - * (which happens if we get lost in the ring), - * Rx process gets reset, so we abort any further - * Rx processing. - */ - if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || - (rx_size < 8) || - (!(rx_status & RxStatusOK)))) { - rtl8139_rx_err (rx_status, dev, tp, ioaddr); - received = -1; - goto out; - } - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - /* Malloc up new buffer, compatible with net-2e. */ - /* Omit the four octet CRC from the length. */ - skb = dev_alloc_skb(pkt_size + 2); - - if (likely(skb)) { - skb->dev = dev; - skb_reserve (skb, 2); /* 16 byte align the IP fields. */ -#if RX_BUF_IDX == 3 - wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); -#else - eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); -#endif - skb_put (skb, pkt_size); - - skb->protocol = eth_type_trans (skb, dev); - - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - - netif_receive_skb (skb); - } else { - if (net_ratelimit()) - printk (KERN_WARNING - "%s: Memory squeeze, dropping packet.\n", - dev->name); - tp->stats.rx_dropped++; - } - } - else - { - if (rtl_ecat_dev.state != ECAT_DS_SENT) - { - printk(KERN_WARNING "EtherCAT: Received frame while not in SENT state!\n"); - } - else - { - // Copy received data to ethercat-device buffer, skip Ethernet-II header - memcpy(rtl_ecat_dev.rx_data, &rx_ring[ring_offset + 4] + ETH_HLEN, - pkt_size - ETH_HLEN); - rtl_ecat_dev.rx_data_length = pkt_size - ETH_HLEN; - - rtl_ecat_dev.state = ECAT_DS_RECEIVED; - - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - } - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - received++; - - cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; - RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); - - rtl8139_isr_ack(tp); - } - - if (unlikely(!received || rx_size == 0xfff0)) - rtl8139_isr_ack(tp); - -#if RTL8139_DEBUG > 1 - DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); -#endif - - tp->cur_rx = cur_rx; - - /* - * The receive buffer should be mostly empty. - * Tell NAPI to reenable the Rx irq. - */ - if (tp->fifo_copy_timeout) - received = budget; - -out: - return received; -} - - -static void rtl8139_weird_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr, - int status, int link_changed) -{ - DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", - dev->name, status); - - assert (dev != NULL); - assert (tp != NULL); - assert (ioaddr != NULL); - - /* Update the error count. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - if ((status & RxUnderrun) && link_changed && - (tp->drv_flags & HAS_LNK_CHNG)) { - rtl_check_media(dev, 0); - status &= ~RxUnderrun; - } - - if (status & (RxUnderrun | RxErr)) - tp->stats.rx_errors++; - - if (status & PCSTimeout) - tp->stats.rx_length_errors++; - if (status & RxUnderrun) - tp->stats.rx_fifo_errors++; - if (status & PCIErr) { - u16 pci_cmd_status; - pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); - pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); - - printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", - dev->name, pci_cmd_status); - } -} - -int rtl8139_poll(struct net_device *dev, int *budget) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int orig_budget = min(*budget, dev->quota); - int done = 1; - - spin_lock(&tp->rx_lock); - if (likely(RTL_R16(IntrStatus) & RxAckBits)) { - int work_done; - - work_done = rtl8139_rx(dev, tp, orig_budget); - if (likely(work_done > 0)) { - *budget -= work_done; - dev->quota -= work_done; - done = (work_done < orig_budget); - } - } - - if (done) { - /* - * Order is important since data can get interrupted - * again when we think we are done. - */ - - local_irq_disable(); - RTL_W16_F(IntrMask, rtl8139_intr_mask); - __netif_rx_complete(dev); - local_irq_enable(); - } - spin_unlock(&tp->rx_lock); - - return !done; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *) dev_instance; - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u16 status, ackstat; - int link_changed = 0; /* avoid bogus "uninit" warning */ - int handled = 0; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev) - { - rtl_ecat_dev.intr_cnt++; - status = RTL_R16 (IntrStatus); - } - else - { - spin_lock(&tp->lock); - - status = RTL_R16 (IntrStatus); - - if (unlikely((status & rtl8139_intr_mask) == 0)) - goto out; - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - handled = 1; - - /* h/w no longer present (hotplug?) or major error, bail */ - if (unlikely(status == 0xFFFF)) - goto out; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - /* close possible race's with dev_close */ - if (unlikely(!netif_running(dev))) { - RTL_W16 (IntrMask, 0); - goto out; - } - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - /* Acknowledge all of the current interrupt sources ASAP, but - an first get an additional status bit from CSCR. */ - if (unlikely(status & RxUnderrun)) - link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; - - ackstat = status & ~(RxAckBits | TxErr); - if (ackstat) { - RTL_W16 (IntrStatus, ackstat); - //printk("ECAT-NIC ack\n"); //HM - } - - /* Receive packets are processed by poll routine. - If not running start it now. */ - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (status & RxAckBits) - { - if (dev != rtl_ecat_dev.dev) - { - /* Polling vormerken */ - if (netif_rx_schedule_prep(dev)) { - RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); - __netif_rx_schedule (dev); - } - } - else - { - /* Beim EtherCAT-Device einfach alle Frames empfangen */ - rtl8139_rx(dev, tp, 100); // FIXME Das ist echt dirty... - } - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - /* Check uncommon events with one test. */ - if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) - rtl8139_weird_interrupt (dev, tp, ioaddr, - status, link_changed); - - if (status & (TxOK | TxErr)) { - rtl8139_tx_interrupt (dev, tp, ioaddr); - if (status & TxErr) - RTL_W16 (IntrStatus, TxErr); - } - out: - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - spin_unlock (&tp->lock); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); - return IRQ_RETVAL(handled); -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling receive - used by netconsole and other diagnostic tools - * to allow network i/o with interrupts disabled. - */ -static void rtl8139_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - rtl8139_interrupt(dev->irq, dev, NULL); - enable_irq(dev->irq); -} -#endif - -static int rtl8139_close (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int ret = 0; - unsigned long flags; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev != rtl_ecat_dev.dev) - { - netif_stop_queue(dev); - if (tp->thr_pid >= 0) { - tp->time_to_die = 1; - wmb(); - ret = kill_proc (tp->thr_pid, SIGTERM, 1); - if (ret) { - printk (KERN_ERR "%s: unable to signal thread\n", dev->name); - return ret; - } - wait_for_completion (&tp->thr_exited); - } - - if (netif_msg_ifdown(tp)) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); - - spin_lock_irqsave (&tp->lock, flags); - - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8 (ChipCmd, 0); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - - synchronize_irq (dev->irq); /* racy, but that's ok here */ - free_irq (dev->irq, dev); - } - else - { - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8 (ChipCmd, 0); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - rtl8139_tx_clear (tp); - - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - tp->rx_ring = NULL; - tp->tx_bufs = NULL; - - /* Green! Put the chip in low-power mode. */ - RTL_W8 (Cfg9346, Cfg9346_Unlock); - - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; -} - - -/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to - kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and - other threads or interrupts aren't messing with the 8139. */ -static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mmio_addr; - - spin_lock_irq(&np->lock); - if (rtl_chip_info[np->chipset].flags & HasLWake) { - u8 cfg3 = RTL_R8 (Config3); - u8 cfg5 = RTL_R8 (Config5); - - wol->supported = WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; - - wol->wolopts = 0; - if (cfg3 & Cfg3_LinkUp) - wol->wolopts |= WAKE_PHY; - if (cfg3 & Cfg3_Magic) - wol->wolopts |= WAKE_MAGIC; - /* (KON)FIXME: See how netdev_set_wol() handles the - following constants. */ - if (cfg5 & Cfg5_UWF) - wol->wolopts |= WAKE_UCAST; - if (cfg5 & Cfg5_MWF) - wol->wolopts |= WAKE_MCAST; - if (cfg5 & Cfg5_BWF) - wol->wolopts |= WAKE_BCAST; - } - spin_unlock_irq(&np->lock); -} - - -/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes - that wol points to kernel memory and other threads or interrupts - aren't messing with the 8139. */ -static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mmio_addr; - u32 support; - u8 cfg3, cfg5; - - support = ((rtl_chip_info[np->chipset].flags & HasLWake) - ? (WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) - : 0); - if (wol->wolopts & ~support) - return -EINVAL; - - spin_lock_irq(&np->lock); - cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); - if (wol->wolopts & WAKE_PHY) - cfg3 |= Cfg3_LinkUp; - if (wol->wolopts & WAKE_MAGIC) - cfg3 |= Cfg3_Magic; - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config3, cfg3); - RTL_W8 (Cfg9346, Cfg9346_Lock); - - cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); - /* (KON)FIXME: These are untested. We may have to set the - CRC0, Wakeup0 and LSBCRC0 registers too, but I have no - documentation. */ - if (wol->wolopts & WAKE_UCAST) - cfg5 |= Cfg5_UWF; - if (wol->wolopts & WAKE_MCAST) - cfg5 |= Cfg5_MWF; - if (wol->wolopts & WAKE_BCAST) - cfg5 |= Cfg5_BWF; - RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ - spin_unlock_irq(&np->lock); - - return 0; -} - -static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct rtl8139_private *np = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(np->pci_dev)); - info->regdump_len = np->regs_len; -} - -static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii, cmd); - spin_unlock_irq(&np->lock); - return 0; -} - -static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - int rc; - spin_lock_irq(&np->lock); - rc = mii_ethtool_sset(&np->mii, cmd); - spin_unlock_irq(&np->lock); - return rc; -} - -static int rtl8139_nway_reset(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return mii_nway_restart(&np->mii); -} - -static u32 rtl8139_get_link(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return mii_link_ok(&np->mii); -} - -static u32 rtl8139_get_msglevel(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return np->msg_enable; -} - -static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) -{ - struct rtl8139_private *np = netdev_priv(dev); - np->msg_enable = datum; -} - -/* TODO: we are too slack to do reg dumping for pio, for now */ -#ifdef CONFIG_8139TOO_PIO -#define rtl8139_get_regs_len NULL -#define rtl8139_get_regs NULL -#else -static int rtl8139_get_regs_len(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return np->regs_len; -} - -static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) -{ - struct rtl8139_private *np = netdev_priv(dev); - - regs->version = RTL_REGS_VER; - - spin_lock_irq(&np->lock); - memcpy_fromio(regbuf, np->mmio_addr, regs->len); - spin_unlock_irq(&np->lock); -} -#endif /* CONFIG_8139TOO_MMIO */ - -static int rtl8139_get_stats_count(struct net_device *dev) -{ - return RTL_NUM_STATS; -} - -static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) -{ - struct rtl8139_private *np = netdev_priv(dev); - - data[0] = np->xstats.early_rx; - data[1] = np->xstats.tx_buf_mapped; - data[2] = np->xstats.tx_timeouts; - data[3] = np->xstats.rx_lost_in_ring; -} - -static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) -{ - memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); -} - -static struct ethtool_ops rtl8139_ethtool_ops = { - .get_drvinfo = rtl8139_get_drvinfo, - .get_settings = rtl8139_get_settings, - .set_settings = rtl8139_set_settings, - .get_regs_len = rtl8139_get_regs_len, - .get_regs = rtl8139_get_regs, - .nway_reset = rtl8139_nway_reset, - .get_link = rtl8139_get_link, - .get_msglevel = rtl8139_get_msglevel, - .set_msglevel = rtl8139_set_msglevel, - .get_wol = rtl8139_get_wol, - .set_wol = rtl8139_set_wol, - .get_strings = rtl8139_get_strings, - .get_stats_count = rtl8139_get_stats_count, - .get_ethtool_stats = rtl8139_get_ethtool_stats, -}; - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - int rc; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev || !netif_running(dev)) - return -EINVAL; - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); - spin_unlock_irq(&np->lock); - - return rc; -} - - -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned long flags; - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev || netif_running(dev)) - { - spin_lock_irqsave (&tp->lock, flags); - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - spin_unlock_irqrestore (&tp->lock, flags); - } - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - return &tp->stats; -} - -/* Set or clear the multicast filter for this adaptor. - This routine is not state sensitive and need not be SMP locked. */ - -static void __set_rx_mode (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u32 mc_filter[2]; /* Multicast hash filter */ - int i, rx_mode; - u32 tmp; - - DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", - dev->name, dev->flags, RTL_R32 (RxConfig)); - - /* Note: do not reorder, GCC is clever about common statements. */ - if (dev->flags & IFF_PROMISC) { - /* Unconditionally log net taps. */ - printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", - dev->name); - rx_mode = - AcceptBroadcast | AcceptMulticast | AcceptMyPhys | - AcceptAllPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else { - struct dev_mc_list *mclist; - rx_mode = AcceptBroadcast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0; - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - rx_mode |= AcceptMulticast; - } - } - - /* We can safely update without stopping the chip. */ - tmp = rtl8139_rx_config | rx_mode; - if (tp->rx_config != tmp) { - RTL_W32_F (RxConfig, tmp); - tp->rx_config = tmp; - } - RTL_W32_F (MAR0 + 0, mc_filter[0]); - RTL_W32_F (MAR0 + 4, mc_filter[1]); -} - -static void rtl8139_set_rx_mode (struct net_device *dev) -{ - unsigned long flags; - struct rtl8139_private *tp = netdev_priv(dev); - - spin_lock_irqsave (&tp->lock, flags); - __set_rx_mode(dev); - spin_unlock_irqrestore (&tp->lock, flags); -} - -#ifdef CONFIG_PM - -static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned long flags; - - pci_save_state (pdev); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev || !netif_running (dev)) - return 0; - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - netif_device_detach (dev); - - spin_lock_irqsave (&tp->lock, flags); - - /* Disable interrupts, stop Tx and Rx. */ - RTL_W16 (IntrMask, 0); - RTL_W8 (ChipCmd, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - - pci_set_power_state (pdev, PCI_D3hot); - - return 0; -} - - -static int rtl8139_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - - pci_restore_state (pdev); - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - if (dev == rtl_ecat_dev.dev || !netif_running (dev)) - return 0; - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - pci_set_power_state (pdev, PCI_D0); - rtl8139_init_ring (dev); - rtl8139_hw_start (dev); - netif_device_attach (dev); - return 0; -} - -#endif /* CONFIG_PM */ - - -static struct pci_driver rtl8139_pci_driver = { - .name = DRV_NAME, - .id_table = rtl8139_pci_tbl, - .probe = rtl8139_init_one, - .remove = __devexit_p(rtl8139_remove_one), -#ifdef CONFIG_PM - .suspend = rtl8139_suspend, - .resume = rtl8139_resume, -#endif /* CONFIG_PM */ -}; - - -static int __init rtl8139_init_module (void) -{ - /* when we're a module, we always print a version message, - * even if no 8139 board is found. - */ -#ifdef MODULE - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); -#endif - - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - printk(KERN_INFO "Initializing RTL8139-EtherCAT module. %s\n", COMPILE_INFO); - - EtherCAT_device_init(&rtl_ecat_dev); - rtl_ecat_dev.isr = rtl8139_interrupt; - rtl_ecat_dev.module = THIS_MODULE; - - if (pci_module_init(&rtl8139_pci_driver) < 0) - { - printk(KERN_ERR "Could not init PCI module.\n"); - return -1; - } - - printk(KERN_INFO "EtherCAT device index is %i.\n", ec_device_index); - - if (rtl_ecat_dev.dev) - { - printk(KERN_INFO "Registering EtherCAT device...\n"); - if (EtherCAT_register_device(ec_device_master_index, &rtl_ecat_dev) < 0) - { - printk(KERN_ERR "Could not register device.\n"); - goto out_module; - } - - rtl_ecat_dev_registered = 1; - - printk(KERN_INFO "EtherCAT device registered and opened.\n"); - } - else - { - printk(KERN_WARNING "NO EtherCAT device registered!\n"); - } - - return 0; - - out_module: - - pci_unregister_driver(&rtl8139_pci_driver); - return -1; - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ -} - - -static void __exit rtl8139_cleanup_module (void) -{ - /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - - printk(KERN_INFO "Cleaning up RTL8139-EtherCAT module...\n"); - - if (rtl_ecat_dev_registered && rtl_ecat_dev.dev) - { - printk(KERN_INFO "Unregistering RTL8139-EtherCAT device...\n"); - EtherCAT_unregister_device(ec_device_master_index, &rtl_ecat_dev); - } - - pci_unregister_driver(&rtl8139_pci_driver); - EtherCAT_device_clear(&rtl_ecat_dev); - - printk(KERN_INFO "RTL8139-EtherCAT module cleaned up.\n"); - - /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ -} - - -module_init(rtl8139_init_module); -module_exit(rtl8139_cleanup_module); diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/EtherCAT.h --- a/drivers/EtherCAT.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -/****************************************************************************** - * - * E t h e r C A T . h - * - * Header zur Nutzung der EtherCAT-Funktionen. - * - * $Id$ - * - *****************************************************************************/ - -#include "../drivers/ec_master.h" -#include "../drivers/ec_device.h" -#include "../drivers/ec_types.h" -#include "../drivers/ec_module.h" - -/*****************************************************************************/ - diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/Makefile --- a/drivers/Makefile Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -#------------------------------------------------------------------------------ -# -# Makefile -# -# IgH EtherCAT-Treiber -# -# $Id$ -# -#------------------------------------------------------------------------------ - -ifneq ($(KERNELRELEASE),) - -#------------------------------------------------------------------------------ -# Kbuild-Abschnitt - -obj-m := 8139too-ecat.o ecat-master.o - -8139too-ecat-objs := 8139too.o - -ecat-master-objs := ec_module.o ec_master.o ec_device.o ec_slave.o \ - ec_command.o ec_types.o ec_domain.o - -REV = `svnversion $(src)` -DATE = `date` - -EXTRA_CFLAGS = -DEC_REV="$(REV)" -DEC_USER="$(USER)" -DEC_DATE="$(DATE)" - -#------------------------------------------------------------------------------ - -else - -#------------------------------------------------------------------------------ -# Default-Abschnitt - -ifneq ($(wildcard ethercat.conf),) -include ethercat.conf -else -KERNELDIR = /lib/modules/`uname -r`/build -endif - -modules: - $(MAKE) -C $(KERNELDIR) M=`pwd` modules - -clean: - $(MAKE) -C $(KERNELDIR) M=`pwd` clean - -#------------------------------------------------------------------------------ - -endif diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_command.c --- a/drivers/ec_command.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,263 +0,0 @@ -/****************************************************************************** - * - * e c _ c o m m a n d . c - * - * Methoden für ein EtherCAT-Kommando. - * - * $Id$ - * - *****************************************************************************/ - -#include - -#include "ec_command.h" - -/*****************************************************************************/ - -/** - Kommando-Konstruktor. - - Initialisiert alle Variablen innerhalb des Kommandos auf die - Default-Werte. - - @param cmd Zeiger auf das zu initialisierende Kommando. -*/ - -void EtherCAT_command_init(EtherCAT_command_t *cmd) -{ - cmd->type = ECAT_CMD_NONE; - cmd->address.logical = 0x00000000; - cmd->data_length = 0; - cmd->state = ECAT_CS_READY; - cmd->index = 0; - cmd->working_counter = 0; -} - -/*****************************************************************************/ - -/** - Kommando-Destruktor. - - Setzt alle Attribute auf den Anfangswert zurueck. - - @param cmd Zeiger auf das zu initialisierende Kommando. -*/ - -void EtherCAT_command_clear(EtherCAT_command_t *cmd) -{ - EtherCAT_command_init(cmd); -} - -/*****************************************************************************/ - -#define ECAT_FUNC_HEADER \ - EtherCAT_command_init(cmd) - -#define ECAT_FUNC_WRITE_FOOTER \ - cmd->data_length = length; \ - memcpy(cmd->data, data, length); - -#define ECAT_FUNC_READ_FOOTER \ - cmd->data_length = length; - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-NPRD-Kommando. - - @param cmd Zeiger auf das Kommando - @param node_address Adresse des Knotens (Slaves) - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu lesenden Daten -*/ - -void EtherCAT_command_read(EtherCAT_command_t *cmd, - unsigned short node_address, - unsigned short offset, - unsigned int length) -{ - if (unlikely(node_address == 0x0000)) - printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n"); - - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_NPRD; - cmd->address.phy.dev.node = node_address; - cmd->address.phy.mem = offset; - - ECAT_FUNC_READ_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-NPWR-Kommando. - - Alloziert ein "node-adressed physical write"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param node_address Adresse des Knotens (Slaves) - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu schreibenden Daten - @param data Zeiger auf Speicher mit zu schreibenden Daten -*/ - -void EtherCAT_command_write(EtherCAT_command_t *cmd, - unsigned short node_address, - unsigned short offset, - unsigned int length, - const unsigned char *data) -{ - if (unlikely(node_address == 0x0000)) - printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n"); - - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_NPWR; - cmd->address.phy.dev.node = node_address; - cmd->address.phy.mem = offset; - - ECAT_FUNC_WRITE_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-APRD-Kommando. - - Alloziert ein "autoincerement physical read"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param ring_position (Negative) Position des Slaves im Bus - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu lesenden Daten -*/ - -void EtherCAT_command_position_read(EtherCAT_command_t *cmd, - short ring_position, - unsigned short offset, - unsigned int length) -{ - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_APRD; - cmd->address.phy.dev.pos = ring_position; - cmd->address.phy.mem = offset; - - ECAT_FUNC_READ_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-APWR-Kommando. - - Alloziert ein "autoincrement physical write"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param ring_position (Negative) Position des Slaves im Bus - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu schreibenden Daten - @param data Zeiger auf Speicher mit zu schreibenden Daten -*/ - -void EtherCAT_command_position_write(EtherCAT_command_t *cmd, - short ring_position, - unsigned short offset, - unsigned int length, - const unsigned char *data) -{ - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_APWR; - cmd->address.phy.dev.pos = ring_position; - cmd->address.phy.mem = offset; - - ECAT_FUNC_WRITE_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-BRD-Kommando. - - Alloziert ein "broadcast read"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu lesenden Daten -*/ - -void EtherCAT_command_broadcast_read(EtherCAT_command_t *cmd, - unsigned short offset, - unsigned int length) -{ - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_BRD; - cmd->address.phy.dev.node = 0x0000; - cmd->address.phy.mem = offset; - - ECAT_FUNC_READ_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-BWR-Kommando. - - Alloziert ein "broadcast write"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param offset Physikalische Speicheradresse im Slave - @param length Länge der zu schreibenden Daten - @param data Zeiger auf Speicher mit zu schreibenden Daten -*/ - -void EtherCAT_command_broadcast_write(EtherCAT_command_t *cmd, - unsigned short offset, - unsigned int length, - const unsigned char *data) -{ - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_BWR; - cmd->address.phy.dev.node = 0x0000; - cmd->address.phy.mem = offset; - - ECAT_FUNC_WRITE_FOOTER; -} - -/*****************************************************************************/ - -/** - Initialisiert ein EtherCAT-LRW-Kommando. - - Alloziert ein "logical read write"-Kommando - und fügt es in die Liste des Masters ein. - - @param cmd Zeiger auf das Kommando - @param offset Logische Speicheradresse - @param length Länge der zu lesenden/schreibenden Daten - @param data Zeiger auf Speicher mit zu lesenden/schreibenden Daten -*/ - -void EtherCAT_command_logical_read_write(EtherCAT_command_t *cmd, - unsigned int offset, - unsigned int length, - unsigned char *data) -{ - ECAT_FUNC_HEADER; - - cmd->type = ECAT_CMD_LRW; - cmd->address.logical = offset; - - ECAT_FUNC_WRITE_FOOTER; -} - -/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_command.h --- a/drivers/ec_command.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/****************************************************************************** - * - * e c _ c o m m a n d . h - * - * Struktur für ein EtherCAT-Kommando. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_COMMAND_H_ -#define _EC_COMMAND_H_ - -#include "ec_globals.h" - -/*****************************************************************************/ - -/** - Status eines EtherCAT-Kommandos. -*/ - -typedef enum -{ - ECAT_CS_READY, ECAT_CS_SENT, ECAT_CS_RECEIVED -} -EtherCAT_command_state_t; - -/*****************************************************************************/ - -/** - EtherCAT-Adresse. - - Im EtherCAT-Rahmen sind 4 Bytes für die Adresse reserviert, die - ja nach Kommandoty eine andere bedeutung haben: Bei Autoinkrement- - befehlen sind die ersten zwei Bytes die (negative) - Autoinkrement-Adresse, bei Knoten-adressierten Befehlen entsprechen - sie der Knotenadresse. Das dritte und vierte Byte entspricht in - diesen Fällen der physikalischen Speicheradresse auf dem Slave. - Bei einer logischen Adressierung entsprechen alle vier Bytes - der logischen Adresse. -*/ - -typedef union -{ - struct - { - union - { - short pos; /**< (Negative) Ring-Position des Slaves */ - unsigned short node; /**< Konfigurierte Knotenadresse */ - } - dev; - - unsigned short mem; /**< Physikalische Speicheradresse im Slave */ - } - phy; - - unsigned long logical; /**< Logische Adresse */ - unsigned char raw[4]; /**< Rohdaten für die Generierung des Frames */ -} -EtherCAT_address_t; - -/*****************************************************************************/ - -/** - EtherCAT-Kommando. -*/ - -typedef struct EtherCAT_command -{ - EtherCAT_cmd_type_t type; /**< Typ des Kommandos (APRD, NPWR, etc...) */ - EtherCAT_address_t address; /**< Adresse des/der Empfänger */ - unsigned int data_length; /**< Länge der zu sendenden und/oder - empfangenen Daten */ - - EtherCAT_command_state_t state; /**< Zustand des Kommandos - (bereit, gesendet, etc...) */ - unsigned char index; /**< Kommando-Index, mit der das Kommando gesendet - wurde (wird vom Master beim Senden gesetzt. */ - unsigned int working_counter; /**< Working-Counter bei Empfang (wird - vom Master gesetzt) */ - - unsigned char data[ECAT_FRAME_BUFFER_SIZE]; /**< Kommandodaten */ -} -EtherCAT_command_t; - -/*****************************************************************************/ - -void EtherCAT_command_init(EtherCAT_command_t *); -void EtherCAT_command_clear(EtherCAT_command_t *); - -void EtherCAT_command_read(EtherCAT_command_t *, - unsigned short, - unsigned short, - unsigned int); -void EtherCAT_command_write(EtherCAT_command_t *, - unsigned short, - unsigned short, - unsigned int, - const unsigned char *); -void EtherCAT_command_position_read(EtherCAT_command_t *, - short, - unsigned short, - unsigned int); -void EtherCAT_command_position_write(EtherCAT_command_t *, - short, - unsigned short, - unsigned int, - const unsigned char *); -void EtherCAT_command_broadcast_read(EtherCAT_command_t *, - unsigned short, - unsigned int); -void EtherCAT_command_broadcast_write(EtherCAT_command_t *, - unsigned short, - unsigned int, - const unsigned char *); -void EtherCAT_command_logical_read_write(EtherCAT_command_t *, - unsigned int, - unsigned int, - unsigned char *); - -/*****************************************************************************/ - -#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_device.c --- a/drivers/ec_device.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,363 +0,0 @@ -/****************************************************************************** - * - * e c _ d e v i c e . c - * - * Methoden für ein EtherCAT-Gerät. - * - * $Id$ - * - *****************************************************************************/ - -#include -#include -#include -#include -#include - -#include "ec_device.h" - -/*****************************************************************************/ - -/** - EtherCAT-Geräte-Konstuktor. - - Initialisiert ein EtherCAT-Gerät, indem es die Variablen - in der Struktur auf die Default-Werte setzt. - - @param ecd Zu initialisierendes EtherCAT-Gerät -*/ - -void EtherCAT_device_init(EtherCAT_device_t *ecd) -{ - ecd->dev = NULL; - ecd->tx_skb = NULL; - ecd->rx_skb = NULL; - ecd->tx_time = 0; - ecd->rx_time = 0; - ecd->tx_intr_cnt = 0; - ecd->rx_intr_cnt = 0; - ecd->intr_cnt = 0; - ecd->state = ECAT_DS_READY; - ecd->rx_data_length = 0; - ecd->isr = NULL; - ecd->module = NULL; - ecd->error_reported = 0; -} - -/*****************************************************************************/ - -/** - EtherCAT-Geräte-Destuktor. - - Gibt den dynamisch allozierten Speicher des - EtherCAT-Gerätes (die beiden Socket-Buffer) wieder frei. - - @param ecd EtherCAT-Gerät -*/ - -void EtherCAT_device_clear(EtherCAT_device_t *ecd) -{ - ecd->dev = NULL; - - if (ecd->tx_skb) { - dev_kfree_skb(ecd->tx_skb); - ecd->tx_skb = NULL; - } - - if (ecd->rx_skb) { - dev_kfree_skb(ecd->rx_skb); - ecd->rx_skb = NULL; - } -} - -/*****************************************************************************/ - -/** - Weist einem EtherCAT-Gerät das entsprechende net_device zu. - - Prüft das net_device, allokiert Socket-Buffer in Sende- und - Empfangsrichtung und weist dem EtherCAT-Gerät und den - Socket-Buffern das net_device zu. - - @param ecd EtherCAT-Device - @param dev net_device - - @return 0: Erfolg, < 0: Konnte Socket-Buffer nicht allozieren. -*/ - -int EtherCAT_device_assign(EtherCAT_device_t *ecd, - struct net_device *dev) -{ - if (!dev) { - printk("EtherCAT: Device is NULL!\n"); - return -1; - } - - if ((ecd->tx_skb = dev_alloc_skb(ECAT_FRAME_BUFFER_SIZE)) == NULL) { - printk(KERN_ERR "EtherCAT: Could not allocate device tx socket buffer!\n"); - return -1; - } - - if ((ecd->rx_skb = dev_alloc_skb(ECAT_FRAME_BUFFER_SIZE)) == NULL) { - dev_kfree_skb(ecd->tx_skb); - ecd->tx_skb = NULL; - printk(KERN_ERR "EtherCAT: Could not allocate device rx socket buffer!\n"); - return -1; - } - - ecd->dev = dev; - ecd->tx_skb->dev = dev; - ecd->rx_skb->dev = dev; - - printk("EtherCAT: Assigned Device %X.\n", (unsigned) dev); - - return 0; -} - -/*****************************************************************************/ - -/** - Führt die open()-Funktion des Netzwerktreibers aus. - - Dies entspricht einem "ifconfig up". Vorher wird der Zeiger - auf das EtherCAT-Gerät auf Gültigkeit geprüft und der - Gerätezustand zurückgesetzt. - - @param ecd EtherCAT-Gerät - - @return 0 bei Erfolg, < 0: Ungültiger Zeiger, oder open() - fehlgeschlagen -*/ - -int EtherCAT_device_open(EtherCAT_device_t *ecd) -{ - unsigned int i; - - if (!ecd) { - printk(KERN_ERR "EtherCAT: Trying to open a NULL device!\n"); - return -1; - } - - if (!ecd->dev) { - printk(KERN_ERR "EtherCAT: No net_device to open!\n"); - return -1; - } - - // Device could have received frames before - for (i = 0; i < 4; i++) EtherCAT_device_call_isr(ecd); - - // Reset old device state - ecd->state = ECAT_DS_READY; - ecd->tx_intr_cnt = 0; - ecd->rx_intr_cnt = 0; - - return ecd->dev->open(ecd->dev); -} - -/*****************************************************************************/ - -/** - Führt die stop()-Funktion des net_devices aus. - - @param ecd EtherCAT-Gerät - - @return 0 bei Erfolg, < 0: Kein Gerät zum Schliessen -*/ - -int EtherCAT_device_close(EtherCAT_device_t *ecd) -{ - if (!ecd->dev) { - printk("EtherCAT: No device to close!\n"); - return -1; - } - - printk("EtherCAT: Stopping device (txcnt: %u, rxcnt: %u)\n", - (unsigned int) ecd->tx_intr_cnt, - (unsigned int) ecd->rx_intr_cnt); - - return ecd->dev->stop(ecd->dev); -} - -/*****************************************************************************/ - -/** - Sendet einen Rahmen über das EtherCAT-Gerät. - - Kopiert die zu sendenden Daten in den statischen Socket- - Buffer, fügt den Ethernat-II-Header hinzu und ruft die - start_xmit()-Funktion der Netzwerkkarte auf. - - @param ecd EtherCAT-Gerät - @param data Zeiger auf die zu sendenden Daten - @param length Länge der zu sendenden Daten - - @return 0 bei Erfolg, < 0: Vorheriger Rahmen noch - nicht empfangen, oder kein Speicher mehr vorhanden -*/ - -int EtherCAT_device_send(EtherCAT_device_t *ecd, - unsigned char *data, - unsigned int length) -{ - unsigned char *frame_data; - struct ethhdr *eth; - - if (unlikely(ecd->state == ECAT_DS_SENT)) { - printk(KERN_WARNING "EtherCAT: Warning - Trying to send frame" - " while last was not received!\n"); - } - - // Clear transmit socket buffer and reserve - // space for Ethernet-II header - skb_trim(ecd->tx_skb, 0); - skb_reserve(ecd->tx_skb, ETH_HLEN); - - // Copy data to socket buffer - frame_data = skb_put(ecd->tx_skb, length); - memcpy(frame_data, data, length); - - // Add Ethernet-II-Header - if (unlikely((eth = (struct ethhdr *) - skb_push(ecd->tx_skb, ETH_HLEN)) == NULL)) { - printk(KERN_ERR "EtherCAT: device_send -" - " Could not allocate Ethernet-II header!\n"); - return -1; - } - - // Protocol type - eth->h_proto = htons(0x88A4); - // Hardware address - memcpy(eth->h_source, ecd->dev->dev_addr, ecd->dev->addr_len); - // Broadcast address - memset(eth->h_dest, 0xFF, ecd->dev->addr_len); - - rdtscl(ecd->tx_time); // Get CPU cycles - - // Start sending of frame - ecd->state = ECAT_DS_SENT; - ecd->dev->hard_start_xmit(ecd->tx_skb, ecd->dev); - - return 0; -} - -/*****************************************************************************/ - -/** - Holt einen empfangenen Rahmen von der Netzwerkkarte. - - Zuerst wird geprüft, ob überhaupt ein Rahmen empfangen - wurde. Wenn ja, wird dieser in den angegebenen - Speicherbereich kopiert. - - @param ecd EtherCAT-Gerät - @param data Zeiger auf den Speicherbereich, in den die - empfangenen Daten kopiert werden sollen - - @return Anzahl der kopierten Bytes bei Erfolg, sonst < 0 -*/ - -int EtherCAT_device_receive(EtherCAT_device_t *ecd, - unsigned char *data) -{ - if (unlikely(ecd->state != ECAT_DS_RECEIVED)) { - if (likely(ecd->error_reported)) { - printk(KERN_ERR "EtherCAT: receive - Nothing received!\n"); - ecd->error_reported = 1; - } - return -1; - } - - if (unlikely(ecd->rx_data_length > ECAT_FRAME_BUFFER_SIZE)) { - if (likely(ecd->error_reported)) { - printk(KERN_ERR "EtherCAT: receive - " - " Reveived frame is too long (%i Bytes)!\n", - ecd->rx_data_length); - ecd->error_reported = 1; - } - return -1; - } - - if (unlikely(ecd->error_reported)) { - ecd->error_reported = 0; - } - - memcpy(data, ecd->rx_data, ecd->rx_data_length); - - return ecd->rx_data_length; -} - -/*****************************************************************************/ - -/** - Ruft die Interrupt-Routine der Netzwerkkarte auf. - - @param ecd EtherCAT-Gerät - - @return Anzahl der kopierten Bytes bei Erfolg, sonst < 0 -*/ - -void EtherCAT_device_call_isr(EtherCAT_device_t *ecd) -{ - if (likely(ecd->isr)) ecd->isr(0, ecd->dev, NULL); -} - -/*****************************************************************************/ - -/** - Gibt alle Informationen über das Device-Objekt aus. - - @param ecd EtherCAT-Gerät -*/ - -void EtherCAT_device_debug(EtherCAT_device_t *ecd) -{ - printk(KERN_DEBUG "---EtherCAT device information begin---\n"); - - if (ecd) - { - printk(KERN_DEBUG "Assigned net_device: %X\n", - (unsigned) ecd->dev); - printk(KERN_DEBUG "Transmit socket buffer: %X\n", - (unsigned) ecd->tx_skb); - printk(KERN_DEBUG "Receive socket buffer: %X\n", - (unsigned) ecd->rx_skb); - printk(KERN_DEBUG "Time of last transmission: %u\n", - (unsigned) ecd->tx_time); - printk(KERN_DEBUG "Time of last receive: %u\n", - (unsigned) ecd->rx_time); - printk(KERN_DEBUG "Number of transmit interrupts: %u\n", - (unsigned) ecd->tx_intr_cnt); - printk(KERN_DEBUG "Number of receive interrupts: %u\n", - (unsigned) ecd->rx_intr_cnt); - printk(KERN_DEBUG "Total Number of interrupts: %u\n", - (unsigned) ecd->intr_cnt); - printk(KERN_DEBUG "Actual device state: %i\n", - (int) ecd->state); - printk(KERN_DEBUG "Receive buffer: %X\n", - (unsigned) ecd->rx_data); - printk(KERN_DEBUG "Receive buffer fill state: %u/%u\n", - (unsigned) ecd->rx_data_length, ECAT_FRAME_BUFFER_SIZE); - } - else - { - printk(KERN_DEBUG "Device is NULL!\n"); - } - - printk(KERN_DEBUG "---EtherCAT device information end---\n"); -} - -/*****************************************************************************/ - -EXPORT_SYMBOL(EtherCAT_device_init); -EXPORT_SYMBOL(EtherCAT_device_clear); -EXPORT_SYMBOL(EtherCAT_device_assign); -EXPORT_SYMBOL(EtherCAT_device_open); -EXPORT_SYMBOL(EtherCAT_device_close); - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_device.h --- a/drivers/ec_device.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -/****************************************************************************** - * - * e c _ d e v i c e . h - * - * Struktur für ein EtherCAT-Gerät. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_DEVICE_H_ -#define _EC_DEVICE_H_ - -#include - -#include "ec_globals.h" - -/*****************************************************************************/ - -/** - Zustand eines EtherCAT-Gerätes. - - Eine Für EtherCAT reservierte Netzwerkkarte kann bestimmte Zustände haben. -*/ - -typedef enum -{ - ECAT_DS_READY, /**< Das Gerät ist bereit zum Senden */ - ECAT_DS_SENT, /**< Das Gerät hat einen Rahmen abgesendet, - aber noch keine Antwort enpfangen */ - ECAT_DS_RECEIVED, /**< Das Gerät hat eine Antwort auf einen - zuvor gesendeten Rahmen empfangen */ - ECAT_DS_TIMEOUT, /**< Nach dem Senden eines Rahmens meldete - das Gerät einen Timeout */ - ECAT_DS_ERROR /**< Nach dem Senden eines frames hat das - Gerät einen Fehler festgestellt. */ -} -EtherCAT_device_state_t; - -/*****************************************************************************/ - -/** - EtherCAT-Gerät. - - Ein EtherCAT-Gerät ist eine Netzwerkkarte, die vom - EtherCAT-Master dazu verwendet wird, um Frames zu senden - und zu empfangen. -*/ - -typedef struct -{ - struct net_device *dev; /**< Zeiger auf das reservierte net_device */ - struct sk_buff *tx_skb; /**< Zeiger auf Transmit-Socketbuffer */ - struct sk_buff *rx_skb; /**< Zeiger auf Receive-Socketbuffer */ - unsigned long tx_time; /**< Zeit des letzten Sendens */ - unsigned long rx_time; /**< Zeit des letzten Empfangs */ - unsigned long tx_intr_cnt; /**< Anzahl Tx-Interrupts */ - unsigned long rx_intr_cnt; /**< Anzahl Rx-Interrupts */ - unsigned long intr_cnt; /**< Anzahl Interrupts */ - volatile EtherCAT_device_state_t state; /**< Gesendet, Empfangen, - Timeout, etc. */ - unsigned char rx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Puffer für - empfangene Rahmen */ - volatile unsigned int rx_data_length; /**< Länge des zuletzt - empfangenen Rahmens */ - irqreturn_t (*isr)(int, void *, struct pt_regs *); /**< Adresse der ISR */ - struct module *module; /**< Zeiger auf das Modul, das das Gerät zur - Verfügung stellt. */ - int error_reported; /**< Zeigt an, ob ein Fehler im zyklischen Code - bereits gemeldet wurde. */ -} -EtherCAT_device_t; - -/*****************************************************************************/ - -void EtherCAT_device_init(EtherCAT_device_t *); -int EtherCAT_device_assign(EtherCAT_device_t *, struct net_device *); -void EtherCAT_device_clear(EtherCAT_device_t *); - -int EtherCAT_device_open(EtherCAT_device_t *); -int EtherCAT_device_close(EtherCAT_device_t *); - -int EtherCAT_device_send(EtherCAT_device_t *, unsigned char *, unsigned int); -int EtherCAT_device_receive(EtherCAT_device_t *, unsigned char *); -void EtherCAT_device_call_isr(EtherCAT_device_t *); - -void EtherCAT_device_debug(EtherCAT_device_t *); - -/*****************************************************************************/ - -#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_domain.c --- a/drivers/ec_domain.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/****************************************************************************** - * - * e c _ d o m a i n . c - * - * Methoden für Gruppen von EtherCAT-Slaves. - * - * $Id$ - * - *****************************************************************************/ - -#include - -#include "ec_globals.h" -#include "ec_domain.h" - -/*****************************************************************************/ - -/** - Konstruktor einer EtherCAT-Domäne. - - @param pd Zeiger auf die zu initialisierende Domäne -*/ - -void EtherCAT_domain_init(EtherCAT_domain_t *dom) -{ - dom->number = 0; - dom->data_size = 0; - dom->logical_offset = 0; - dom->response_count = 0; - - memset(dom->data, 0x00, ECAT_FRAME_BUFFER_SIZE); -} - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_domain.h --- a/drivers/ec_domain.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/****************************************************************************** - * - * e c _ d o m a i n . h - * - * Struktur für eine Gruppe von EtherCAT-Slaves. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_DOMAIN_H_ -#define _EC_DOMAIN_H_ - -#include "ec_globals.h" -#include "ec_slave.h" -#include "ec_command.h" - -/*****************************************************************************/ - -/** - EtherCAT-Domäne - - Verwaltet die Prozessdaten und das hierfür nötige Kommando einer bestimmten - Menge von Slaves. -*/ - -typedef struct EtherCAT_domain -{ - unsigned int number; /*<< Domänen-Identifikation */ - EtherCAT_command_t command; /**< Kommando zum Senden und Empfangen der - Prozessdaten */ - unsigned char data[ECAT_FRAME_BUFFER_SIZE]; /**< Prozessdaten-Array */ - unsigned int data_size; /**< Größe der Prozessdaten */ - unsigned int logical_offset; /**< Logische Basisaddresse */ - unsigned int response_count; /**< Anzahl antwortender Slaves */ -} -EtherCAT_domain_t; - -/*****************************************************************************/ - -void EtherCAT_domain_init(EtherCAT_domain_t *); - -/*****************************************************************************/ - -#endif - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_globals.h --- a/drivers/ec_globals.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/****************************************************************************** - * - * e c _ g l o b a l s . h - * - * Globale Definitionen und Makros für das EtherCAT-Protokoll. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_GLOBALS_ -#define _EC_GLOBALS_ - -/*****************************************************************************/ - -/** - Maximale Größe eines EtherCAT-Frames -*/ -#define ECAT_FRAME_BUFFER_SIZE 1500 - -/** - Maximale Anzahl der Prozessdatendomänen in einem Master -*/ -#define ECAT_MAX_DOMAINS 10 - -/** - NULL-Define, falls noch nicht definiert. -*/ - -#ifndef NULL -#define NULL ((void *) 0) -#endif - -/*****************************************************************************/ - -/** - EtherCAT-Kommando-Typ -*/ - -typedef enum -{ - ECAT_CMD_NONE = 0x00, /**< Dummy */ - ECAT_CMD_APRD = 0x01, /**< Auto-increment physical read */ - ECAT_CMD_APWR = 0x02, /**< Auto-increment physical write */ - ECAT_CMD_NPRD = 0x04, /**< Node-addressed physical read */ - ECAT_CMD_NPWR = 0x05, /**< Node-addressed physical write */ - ECAT_CMD_BRD = 0x07, /**< Broadcast read */ - ECAT_CMD_BWR = 0x08, /**< Broadcast write */ - ECAT_CMD_LRW = 0x0C /**< Logical read/write */ -} -EtherCAT_cmd_type_t; - -/*****************************************************************************/ - -/** - Zustand eines EtherCAT-Slaves -*/ - -typedef enum -{ - ECAT_STATE_UNKNOWN = 0x00, /**< Status unbekannt */ - ECAT_STATE_INIT = 0x01, /**< Init-Zustand (Keine Mailbox- - Kommunikation, Kein I/O) */ - ECAT_STATE_PREOP = 0x02, /**< Pre-Operational (Mailbox- - Kommunikation, Kein I/O) */ - ECAT_STATE_SAVEOP = 0x04, /**< Save-Operational (Mailbox- - Kommunikation und Input Update) */ - ECAT_STATE_OP = 0x08, /**< Operational, (Mailbox- - Kommunikation und Input/Output Update) */ - ECAT_ACK_STATE = 0x10 /**< Acknoledge-Bit beim Zustandswechsel - (dies ist kein eigener Zustand) */ -} -EtherCAT_state_t; - -/*****************************************************************************/ - -typedef struct EtherCAT_master EtherCAT_master_t; - -/*****************************************************************************/ - -#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_master.c --- a/drivers/ec_master.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1126 +0,0 @@ -/****************************************************************************** - * - * e c _ m a s t e r . c - * - * Methoden für einen EtherCAT-Master. - * - * $Id$ - * - *****************************************************************************/ - -#include -#include -#include -#include -#include - -#include "ec_globals.h" -#include "ec_master.h" - -/*****************************************************************************/ - -/** - Konstruktor des EtherCAT-Masters. - - @param master Zeiger auf den zu initialisierenden EtherCAT-Master -*/ - -void EtherCAT_master_init(EtherCAT_master_t *master) -{ - master->bus_slaves = NULL; - master->bus_slaves_count = 0; - master->dev = NULL; - master->command_index = 0x00; - master->tx_data_length = 0; - master->rx_data_length = 0; - master->domain_count = 0; - master->debug_level = 0; - master->bus_time = 0; - master->frames_lost = 0; - master->t_lost_output = 0; -} - -/*****************************************************************************/ - -/** - Destruktor eines EtherCAT-Masters. - - Entfernt alle Kommandos aus der Liste, löscht den Zeiger - auf das Slave-Array und gibt die Prozessdaten frei. - - @param master Zeiger auf den zu löschenden Master -*/ - -void EtherCAT_master_clear(EtherCAT_master_t *master) -{ - if (master->bus_slaves) { - kfree(master->bus_slaves); - master->bus_slaves = NULL; - } - - master->domain_count = 0; -} - -/*****************************************************************************/ - -/** - Öffnet ein EtherCAT-Geraet für den Master. - - Registriert das Geraet beim Master, der es daraufhin oeffnet. - - @param master Der EtherCAT-Master - @param device Das EtherCAT-Geraet - @return 0, wenn alles o.k., - < 0, wenn bereits ein Geraet registriert - oder das Geraet nicht geoeffnet werden konnte. -*/ - -int EtherCAT_master_open(EtherCAT_master_t *master, - EtherCAT_device_t *device) -{ - if (!master || !device) { - printk(KERN_ERR "EtherCAT: Illegal parameters for master_open()!\n"); - return -1; - } - - if (master->dev) { - printk(KERN_ERR "EtherCAT: Master already has a device.\n"); - return -1; - } - - if (EtherCAT_device_open(device) < 0) { - printk(KERN_ERR "EtherCAT: Could not open device %X!\n", - (unsigned int) master->dev); - return -1; - } - - master->dev = device; - - return 0; -} - -/*****************************************************************************/ - -/** - Schliesst das EtherCAT-Geraet, auf dem der Master arbeitet. - - @param master Der EtherCAT-Master - @param device Das EtherCAT-Geraet -*/ - -void EtherCAT_master_close(EtherCAT_master_t *master, - EtherCAT_device_t *device) -{ - if (master->dev != device) { - printk(KERN_WARNING "EtherCAT: Warning -" - " Trying to close an unknown device!\n"); - return; - } - - if (EtherCAT_device_close(master->dev) < 0) { - printk(KERN_WARNING "EtherCAT: Warning -" - " Could not close device!\n"); - } - - master->dev = NULL; -} - -/*****************************************************************************/ - -/** - Sendet ein einzelnes Kommando in einem Frame und - wartet auf dessen Empfang. - - @param master EtherCAT-Master - @param cmd Kommando zum Senden/Empfangen - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_simple_send_receive(EtherCAT_master_t *master, - EtherCAT_command_t *cmd) -{ - unsigned int tries_left; - - if (unlikely(EtherCAT_simple_send(master, cmd) < 0)) - return -1; - - udelay(3); - - EtherCAT_device_call_isr(master->dev); - - tries_left = 20; - while (unlikely(master->dev->state == ECAT_DS_SENT - && tries_left)) { - udelay(1); - EtherCAT_device_call_isr(master->dev); - tries_left--; - } - - if (unlikely(EtherCAT_simple_receive(master, cmd) < 0)) - return -1; - - return 0; -} - -/*****************************************************************************/ - -/** - Sendet ein einzelnes Kommando in einem Frame. - - @param master EtherCAT-Master - @param cmd Kommando zum Senden - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_simple_send(EtherCAT_master_t *master, - EtherCAT_command_t *cmd) -{ - unsigned int length, framelength, i; - - if (unlikely(master->debug_level > 0)) { - printk(KERN_DEBUG "EtherCAT_send_receive_command\n"); - } - - if (unlikely(cmd->state != ECAT_CS_READY)) { - printk(KERN_WARNING "EtherCAT_send_receive_command:" - "Command not in ready state!\n"); - } - - length = cmd->data_length + 12; - framelength = length + 2; - - if (unlikely(framelength > ECAT_FRAME_BUFFER_SIZE)) { - printk(KERN_ERR "EtherCAT: Frame too long (%i)!\n", framelength); - return -1; - } - - if (framelength < 46) framelength = 46; - - if (unlikely(master->debug_level > 0)) { - printk(KERN_DEBUG "Frame length: %i\n", framelength); - } - - master->tx_data[0] = length & 0xFF; - master->tx_data[1] = ((length & 0x700) >> 8) | 0x10; - - cmd->index = master->command_index; - master->command_index = (master->command_index + 1) % 0x0100; - - if (unlikely(master->debug_level > 0)) { - printk(KERN_DEBUG "Sending command index %i\n", cmd->index); - } - - cmd->state = ECAT_CS_SENT; - - master->tx_data[2 + 0] = cmd->type; - master->tx_data[2 + 1] = cmd->index; - master->tx_data[2 + 2] = cmd->address.raw[0]; - master->tx_data[2 + 3] = cmd->address.raw[1]; - master->tx_data[2 + 4] = cmd->address.raw[2]; - master->tx_data[2 + 5] = cmd->address.raw[3]; - master->tx_data[2 + 6] = cmd->data_length & 0xFF; - master->tx_data[2 + 7] = (cmd->data_length & 0x700) >> 8; - master->tx_data[2 + 8] = 0x00; - master->tx_data[2 + 9] = 0x00; - - if (likely(cmd->type == ECAT_CMD_APWR - || cmd->type == ECAT_CMD_NPWR - || cmd->type == ECAT_CMD_BWR - || cmd->type == ECAT_CMD_LRW)) // Write commands - { - for (i = 0; i < cmd->data_length; i++) - master->tx_data[2 + 10 + i] = cmd->data[i]; - } - else // Read commands - { - for (i = 0; i < cmd->data_length; i++) master->tx_data[2 + 10 + i] = 0x00; - } - - master->tx_data[2 + 10 + cmd->data_length] = 0x00; - master->tx_data[2 + 11 + cmd->data_length] = 0x00; - - // Pad with zeros - for (i = cmd->data_length + 12 + 2; i < 46; i++) master->tx_data[i] = 0x00; - - master->tx_data_length = framelength; - - if (unlikely(master->debug_level > 0)) { - printk(KERN_DEBUG "device send...\n"); - } - - // Send frame - if (unlikely(EtherCAT_device_send(master->dev, - master->tx_data, - framelength) != 0)) { - printk(KERN_ERR "EtherCAT: Could not send!\n"); - return -1; - } - - if (unlikely(master->debug_level > 0)) { - printk(KERN_DEBUG "EtherCAT_send done.\n"); - } - - return 0; -} - -/*****************************************************************************/ - -/** - Wartet auf den Empfang eines einzeln gesendeten - Kommandos. - - @param master EtherCAT-Master - @param cmd Gesendetes Kommando - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_simple_receive(EtherCAT_master_t *master, - EtherCAT_command_t *cmd) -{ - unsigned int length; - int ret; - unsigned char command_type, command_index; - - if (unlikely((ret = EtherCAT_device_receive(master->dev, - master->rx_data)) < 0)) - return -1; - - master->rx_data_length = (unsigned int) ret; - - if (unlikely(master->rx_data_length < 2)) { - printk(KERN_ERR "EtherCAT: Received frame with incomplete EtherCAT" - " header!\n"); - output_debug_data(master); - return -1; - } - - // Länge des gesamten Frames prüfen - length = ((master->rx_data[1] & 0x07) << 8) - | (master->rx_data[0] & 0xFF); - - if (unlikely(length > master->rx_data_length)) { - printk(KERN_ERR "EtherCAT: Received corrupted frame (length does" - " not match)!\n"); - output_debug_data(master); - return -1; - } - - command_type = master->rx_data[2]; - command_index = master->rx_data[2 + 1]; - length = (master->rx_data[2 + 6] & 0xFF) - | ((master->rx_data[2 + 7] & 0x07) << 8); - - if (unlikely(master->rx_data_length - 2 < length + 12)) { - printk(KERN_ERR "EtherCAT: Received frame with" - " incomplete command data!\n"); - output_debug_data(master); - return -1; - } - - if (likely(cmd->state == ECAT_CS_SENT - && cmd->type == command_type - && cmd->index == command_index - && cmd->data_length == length)) - { - cmd->state = ECAT_CS_RECEIVED; - - // Empfangene Daten in Kommandodatenspeicher kopieren - memcpy(cmd->data, master->rx_data + 2 + 10, length); - - // Working-Counter setzen - cmd->working_counter - = ((master->rx_data[length + 2 + 10] & 0xFF) - | ((master->rx_data[length + 2 + 11] & 0xFF) << 8)); - - if (unlikely(master->debug_level > 1)) { - output_debug_data(master); - } - } - else - { - printk(KERN_WARNING "EtherCAT: WARNING - Send/Receive anomaly!\n"); - output_debug_data(master); - } - - master->dev->state = ECAT_DS_READY; - - return 0; -} - -/*****************************************************************************/ - -/** - Durchsucht den Bus nach Slaves. - - @param master Der EtherCAT-Master - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_scan_for_slaves(EtherCAT_master_t *master) -{ - EtherCAT_command_t cmd; - EtherCAT_slave_t *cur; - unsigned int i, j; - unsigned char data[2]; - - // Determine number of slaves on bus - - EtherCAT_command_broadcast_read(&cmd, 0x0000, 4); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - master->bus_slaves_count = cmd.working_counter; - printk("EtherCAT: Found %i slaves on bus.\n", master->bus_slaves_count); - - if (!master->bus_slaves_count) return 0; - - if (!(master->bus_slaves = - (EtherCAT_slave_t *) kmalloc(master->bus_slaves_count * - sizeof(EtherCAT_slave_t), GFP_KERNEL))) { - printk(KERN_ERR "EtherCAT: Could not allocate memory for bus slaves!\n"); - return -1; - } - - // For every slave in the list - for (i = 0; i < master->bus_slaves_count; i++) - { - cur = master->bus_slaves + i; - - EtherCAT_slave_init(cur); - - // Read base data - - EtherCAT_command_read(&cmd, cur->station_address, 0x0000, 4); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Slave %i did not respond" - " while reading base data!\n", i); - return -1; - } - - // Get base data - - cur->type = cmd.data[0]; - cur->revision = cmd.data[1]; - cur->build = cmd.data[2] | (cmd.data[3] << 8); - - // Read identification from "Slave Information Interface" (SII) - - if (unlikely(EtherCAT_read_slave_information(master, - cur->station_address, - 0x0008, - &cur->vendor_id) != 0)) - { - printk(KERN_ERR "EtherCAT: Could not read SII vendor id!\n"); - return -1; - } - - if (unlikely(EtherCAT_read_slave_information(master, - cur->station_address, - 0x000A, - &cur->product_code) != 0)) - { - printk(KERN_ERR "EtherCAT: Could not read SII product code!\n"); - return -1; - } - - if (unlikely(EtherCAT_read_slave_information(master, - cur->station_address, - 0x000C, - &cur->revision_number) != 0)) - { - printk(KERN_ERR "EtherCAT: Could not read SII revision number!\n"); - return -1; - } - - if (unlikely(EtherCAT_read_slave_information(master, - cur->station_address, - 0x000E, - &cur->serial_number) != 0)) - { - printk(KERN_ERR "EtherCAT: Could not read SII serial number!\n"); - return -1; - } - - // Search for identification in "database" - - for (j = 0; j < slave_ident_count; j++) - { - if (unlikely(slave_idents[j].vendor_id == cur->vendor_id - && slave_idents[j].product_code == cur->product_code)) - { - cur->desc = slave_idents[j].desc; - break; - } - } - - if (unlikely(!cur->desc)) { - printk(KERN_ERR "EtherCAT: Unknown slave device (vendor %X, code %X) at " - " position %i.\n", cur->vendor_id, cur->product_code, i); - return -1; - } - - // Set ring position - cur->ring_position = -i; - cur->station_address = i + 1; - - // Write station address - - data[0] = cur->station_address & 0x00FF; - data[1] = (cur->station_address & 0xFF00) >> 8; - - EtherCAT_command_position_write(&cmd, cur->ring_position, - 0x0010, 2, data); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Slave %i did not repond" - " while writing station address!\n", i); - return -1; - } - } - - return 0; -} - -/*****************************************************************************/ - -/** - Registriert einen Slave beim Master. - - @param master Der EtherCAT-Master - - @return 0 bei Erfolg, sonst < 0 -*/ - -void *EtherCAT_register_slave(EtherCAT_master_t *master, - unsigned int bus_index, - const char *vendor_name, - const char *product_name, - unsigned int domain) -{ - EtherCAT_slave_t *slave; - EtherCAT_domain_t *dom; - unsigned int j; - - if (bus_index >= master->bus_slaves_count) { - printk(KERN_ERR "EtherCAT: Illegal bus index! (%i / %i)\n", bus_index, - master->bus_slaves_count); - return NULL; - } - - slave = master->bus_slaves + bus_index; - - if (slave->process_data) { - printk(KERN_ERR "EtherCAT: Slave %i is already registered!\n", bus_index); - return NULL; - } - - if (strcmp(vendor_name, slave->desc->vendor_name) || - strcmp(product_name, slave->desc->product_name)) { - printk(KERN_ERR "Invalid Slave Type! Requested: \"%s %s\", present: \"%s" - "%s\".\n", vendor_name, product_name, slave->desc->vendor_name, - slave->desc->product_name); - return NULL; - } - - // Check, if process data domain already exists... - dom = NULL; - for (j = 0; j < master->domain_count; j++) { - if (domain == master->domains[j].number) { - dom = master->domains + j; - } - } - - // Create process data domain - if (!dom) { - if (master->domain_count > ECAT_MAX_DOMAINS - 1) { - printk(KERN_ERR "EtherCAT: Too many domains!\n"); - return NULL; - } - - dom = master->domains + master->domain_count; - EtherCAT_domain_init(dom); - dom->number = domain; - dom->logical_offset = master->domain_count * ECAT_FRAME_BUFFER_SIZE; - master->domain_count++; - } - - if (dom->data_size + slave->desc->process_data_size - > ECAT_FRAME_BUFFER_SIZE - 14) { - printk(KERN_ERR "EtherCAT: Oversized domain %i: %i / %i Bytes!\n", - dom->number, dom->data_size + slave->desc->process_data_size, - ECAT_FRAME_BUFFER_SIZE - 14); - return NULL; - } - - slave->process_data = dom->data + dom->data_size; - dom->data_size += slave->desc->process_data_size; - - return slave->process_data; -} - -/*****************************************************************************/ - -/** - Liest Daten aus dem Slave-Information-Interface - eines EtherCAT-Slaves. - - @param master EtherCAT-Master - @param node_address Knotenadresse des Slaves - @param offset Adresse des zu lesenden SII-Registers - @param target Zeiger auf einen 4 Byte großen Speicher - zum Ablegen der Daten - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_read_slave_information(EtherCAT_master_t *master, - unsigned short int node_address, - unsigned short int offset, - unsigned int *target) -{ - EtherCAT_command_t cmd; - unsigned char data[10]; - unsigned int tries_left; - - // Initiate read operation - - data[0] = 0x00; - data[1] = 0x01; - data[2] = offset & 0xFF; - data[3] = (offset & 0xFF00) >> 8; - data[4] = 0x00; - data[5] = 0x00; - - EtherCAT_command_write(&cmd, node_address, 0x502, 6, data); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: SII-read - Slave %04X did not respond!\n", - node_address); - return -1; - } - - // Der Slave legt die Informationen des Slave-Information-Interface - // in das Datenregister und löscht daraufhin ein Busy-Bit. Solange - // den Status auslesen, bis das Bit weg ist. - - tries_left = 100; - while (likely(tries_left)) - { - udelay(10); - - EtherCAT_command_read(&cmd, node_address, 0x502, 10); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) != 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: SII-read status -" - " Slave %04X did not respond!\n", node_address); - return -1; - } - - if (likely((cmd.data[1] & 0x81) == 0)) { - memcpy(target, cmd.data + 6, 4); - break; - } - - tries_left--; - } - - if (unlikely(!tries_left)) { - printk(KERN_WARNING "EtherCAT: SSI-read. Slave %04X timed out!\n", - node_address); - return -1; - } - - return 0; -} - -/*****************************************************************************/ - -/** - Ändert den Zustand eines Slaves (asynchron). - - Führt eine (asynchrone) Zustandsänderung bei einem Slave durch. - - @param master EtherCAT-Master - @param slave Slave, dessen Zustand geändert werden soll - @param state_and_ack Neuer Zustand, evtl. mit gesetztem - Acknowledge-Flag - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_state_change(EtherCAT_master_t *master, - EtherCAT_slave_t *slave, - unsigned char state_and_ack) -{ - EtherCAT_command_t cmd; - unsigned char data[2]; - unsigned int tries_left; - - data[0] = state_and_ack; - data[1] = 0x00; - - EtherCAT_command_write(&cmd, slave->station_address, - 0x0120, 2, data); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) != 0)) { - printk(KERN_ERR "EtherCAT: Could not set state %02X - Unable to send!\n", - state_and_ack); - return -1; - } - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Could not set state %02X - Device \"%s %s\"" - " (%d) did not respond!\n", state_and_ack, slave->desc->vendor_name, - slave->desc->product_name, slave->ring_position * (-1)); - return -1; - } - - slave->requested_state = state_and_ack & 0x0F; - - tries_left = 100; - while (likely(tries_left)) - { - udelay(10); - - EtherCAT_command_read(&cmd, slave->station_address, 0x0130, 2); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) != 0)) { - printk(KERN_ERR "EtherCAT: Could not check state %02X - Unable to" - " send!\n", state_and_ack); - return -1; - } - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Could not check state %02X - Device did not" - " respond!\n", state_and_ack); - return -1; - } - - if (unlikely(cmd.data[0] & 0x10)) { // State change error - printk(KERN_ERR "EtherCAT: Could not set state %02X - Device refused" - " state change (code %02X)!\n", state_and_ack, cmd.data[0]); - return -1; - } - - if (likely(cmd.data[0] == (state_and_ack & 0x0F))) { - // State change successful - break; - } - - tries_left--; - } - - if (unlikely(!tries_left)) { - printk(KERN_ERR "EtherCAT: Could not check state %02X - Timeout while" - " checking!\n", state_and_ack); - return -1; - } - - slave->current_state = state_and_ack & 0x0F; - - return 0; -} - -/*****************************************************************************/ - -/** - Konfiguriert einen Slave und setzt den Operational-Zustand. - - Führt eine komplette Konfiguration eines Slaves durch, - setzt Sync-Manager und FMMU's, führt die entsprechenden - Zustandsübergänge durch, bis der Slave betriebsbereit ist. - - @param master EtherCAT-Master - @param slave Zu aktivierender Slave - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_activate_slave(EtherCAT_master_t *master, - EtherCAT_slave_t *slave) -{ - EtherCAT_command_t cmd; - const EtherCAT_slave_desc_t *desc; - unsigned char fmmu[16]; - unsigned char data[256]; - - desc = slave->desc; - - if (unlikely(EtherCAT_state_change(master, slave, ECAT_STATE_INIT) != 0)) - return -1; - - // Resetting FMMU's - - memset(data, 0x00, 256); - - EtherCAT_command_write(&cmd, slave->station_address, 0x0600, 256, data); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Resetting FMMUs - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - - // Resetting Sync Manager channels - - if (desc->type != ECAT_ST_SIMPLE_NOSYNC) - { - memset(data, 0x00, 256); - - EtherCAT_command_write(&cmd, slave->station_address, 0x0800, 256, data); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Resetting SMs - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - } - - // Init Mailbox communication - - if (desc->type == ECAT_ST_MAILBOX) - { - if (desc->sm0) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0800, 8, - desc->sm0); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - } - - if (desc->sm1) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0808, 8, - desc->sm1); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM1 -" - " Slave %04X did not respond!\n", - slave->station_address); - return -1; - } - } - } - - // Change state to PREOP - - if (unlikely(EtherCAT_state_change(master, slave, ECAT_STATE_PREOP) != 0)) - return -1; - - // Set FMMU's - - if (desc->fmmu0) - { - if (unlikely(!slave->process_data)) { - printk(KERN_ERR "EtherCAT: Warning - Slave %04X is not assigned to any" - " process data object!\n", slave->station_address); - return -1; - } - - memcpy(fmmu, desc->fmmu0, 16); - - fmmu[0] = slave->logical_address & 0x000000FF; - fmmu[1] = (slave->logical_address & 0x0000FF00) >> 8; - fmmu[2] = (slave->logical_address & 0x00FF0000) >> 16; - fmmu[3] = (slave->logical_address & 0xFF000000) >> 24; - - EtherCAT_command_write(&cmd, slave->station_address, 0x0600, 16, fmmu); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting FMMU0 - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - } - - // Set Sync Managers - - if (desc->type != ECAT_ST_MAILBOX) - { - if (desc->sm0) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0800, 8, - desc->sm0); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - } - - if (desc->sm1) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0808, 8, - desc->sm1); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM1 - Slave %04X did not" - " respond!\n", slave->station_address); - return -1; - } - } - } - - if (desc->sm2) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0810, 8, desc->sm2); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM2 - Slave %04X did not respond!\n", - slave->station_address); - return -1; - } - } - - if (desc->sm3) - { - EtherCAT_command_write(&cmd, slave->station_address, 0x0818, 8, desc->sm3); - - if (unlikely(EtherCAT_simple_send_receive(master, &cmd) < 0)) - return -1; - - if (unlikely(cmd.working_counter != 1)) { - printk(KERN_ERR "EtherCAT: Setting SM3 - Slave %04X did not respond!\n", - slave->station_address); - return -1; - } - } - - // Change state to SAVEOP - if (unlikely(EtherCAT_state_change(master, slave, ECAT_STATE_SAVEOP) != 0)) - return -1; - - // Change state to OP - if (unlikely(EtherCAT_state_change(master, slave, ECAT_STATE_OP) != 0)) - return -1; - - return 0; -} - -/*****************************************************************************/ - -/** - Setzt einen Slave zurück in den Init-Zustand. - - @param master EtherCAT-Master - @param slave Zu deaktivierender Slave - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_deactivate_slave(EtherCAT_master_t *master, - EtherCAT_slave_t *slave) -{ - if (unlikely(EtherCAT_state_change(master, slave, - ECAT_STATE_INIT) != 0)) - return -1; - - return 0; -} - -/*****************************************************************************/ - -/** - Sendet und empfängt Prozessdaten der angegebenen Domäne - - @param master EtherCAT-Master - domain Domäne - timeout_us Timeout in Mikrosekunden - - @return 0 bei Erfolg, sonst < 0 -*/ - -int EtherCAT_process_data_cycle(EtherCAT_master_t *master, unsigned int domain, - unsigned int timeout_us) -{ - unsigned int i; - EtherCAT_domain_t *dom; - unsigned long start_ticks, end_ticks, timeout_ticks; - - ecat_output_lost_frames(master); // Evtl. verlorene Frames ausgeben - - // Domäne bestimmen - dom = NULL; - for (i = 0; i < master->domain_count; i++) { - if (master->domains[i].number == domain) { - dom = master->domains + i; - break; - } - } - - if (unlikely(!dom)) { - printk(KERN_ERR "EtherCAT: No such domain: %i!\n", domain); - return -1; - } - - EtherCAT_command_logical_read_write(&dom->command, - dom->logical_offset, dom->data_size, - dom->data); - - rdtscl(start_ticks); // Sendezeit nehmen - - if (unlikely(EtherCAT_simple_send(master, &dom->command) < 0)) { - printk(KERN_ERR "EtherCAT: Could not send process data command!\n"); - return -1; - } - - timeout_ticks = timeout_us * cpu_khz / 1000; - - // Warten - do { - EtherCAT_device_call_isr(master->dev); - rdtscl(end_ticks); // Empfangszeit nehmen - } - while (unlikely(master->dev->state == ECAT_DS_SENT - && end_ticks - start_ticks < timeout_ticks)); - - master->bus_time = (end_ticks - start_ticks) * 1000 / cpu_khz; - - if (unlikely(end_ticks - start_ticks >= timeout_ticks)) { - master->dev->state = ECAT_DS_READY; - master->frames_lost++; - ecat_output_lost_frames(master); - return -1; - } - - if (unlikely(EtherCAT_simple_receive(master, &dom->command) < 0)) { - printk(KERN_ERR "EtherCAT: Could not receive cyclic command!\n"); - return -1; - } - - if (unlikely(dom->command.state != ECAT_CS_RECEIVED)) { - printk(KERN_WARNING "EtherCAT: Process data command not received!\n"); - return -1; - } - - if (dom->command.working_counter != dom->response_count) { - dom->response_count = dom->command.working_counter; - printk(KERN_INFO "EtherCAT: Domain %i State change - %i slaves" - " responding.\n", dom->number, dom->response_count); - } - - // Daten vom Kommando in den Prozessdatenspeicher kopieren - memcpy(dom->data, dom->command.data, dom->data_size); - - return 0; -} - -/*****************************************************************************/ - -/** - Gibt Frame-Inhalte zwecks Debugging aus. - - @param master EtherCAT-Master -*/ - -void output_debug_data(const EtherCAT_master_t *master) -{ - unsigned int i; - - printk(KERN_DEBUG "EtherCAT: tx_data content (%i Bytes):\n", - master->tx_data_length); - - printk(KERN_DEBUG); - for (i = 0; i < master->tx_data_length; i++) - { - printk("%02X ", master->tx_data[i]); - if ((i + 1) % 16 == 0) printk("\n" KERN_DEBUG); - } - printk("\n"); - - printk(KERN_DEBUG "EtherCAT: rx_data content (%i Bytes):\n", - master->rx_data_length); - - printk(KERN_DEBUG); - for (i = 0; i < master->rx_data_length; i++) - { - printk("%02X ", master->rx_data[i]); - if ((i + 1) % 16 == 0) printk("\n" KERN_DEBUG); - } - printk("\n"); -} - -/*****************************************************************************/ - -/** - Gibt von Zeit zu Zeit die Anzahl verlorener Frames aus. - - @param master EtherCAT-Master -*/ - -void ecat_output_lost_frames(EtherCAT_master_t *master) -{ - unsigned long int t; - - if (master->frames_lost) { - rdtscl(t); - if ((t - master->t_lost_output) / cpu_khz > 1000) { - printk(KERN_ERR "EtherCAT: %u frame(s) LOST!\n", master->frames_lost); - master->frames_lost = 0; - master->t_lost_output = t; - } - } -} - -/*****************************************************************************/ - -EXPORT_SYMBOL(EtherCAT_master_open); -EXPORT_SYMBOL(EtherCAT_master_close); -EXPORT_SYMBOL(EtherCAT_activate_slave); -EXPORT_SYMBOL(EtherCAT_deactivate_slave); -EXPORT_SYMBOL(EtherCAT_process_data_cycle); - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_master.h --- a/drivers/ec_master.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/****************************************************************************** - * - * e c _ m a s t e r . h - * - * Struktur für einen EtherCAT-Master. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_MASTER_H_ -#define _EC_MASTER_H_ - -#include "ec_device.h" -#include "ec_slave.h" -#include "ec_command.h" -#include "ec_domain.h" - -/*****************************************************************************/ - -/** - EtherCAT-Master - - Verwaltet die EtherCAT-Slaves und kommuniziert mit - dem zugewiesenen EtherCAT-Gerät. -*/ - -struct EtherCAT_master -{ - EtherCAT_slave_t *bus_slaves; /**< Array von Slaves auf dem Bus */ - unsigned int bus_slaves_count; /**< Anzahl Slaves auf dem Bus */ - EtherCAT_device_t *dev; /**< Zeiger auf das zugewiesene EtherCAT-Gerät */ - unsigned char command_index; /**< Aktueller Kommando-Index */ - unsigned char tx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Statischer Speicher - für zu sendende Daten */ - unsigned int tx_data_length; /**< Länge der Daten im Sendespeicher */ - unsigned char rx_data[ECAT_FRAME_BUFFER_SIZE]; /**< Statische Speicher für - eine Kopie des Rx-Buffers - im EtherCAT-Gerät */ - unsigned int rx_data_length; /**< Länge der Daten im Empfangsspeicher */ - EtherCAT_domain_t domains[ECAT_MAX_DOMAINS]; /** Prozessdatendomänen */ - unsigned int domain_count; - int debug_level; /**< Debug-Level im Master-Code */ - unsigned int bus_time; /**< Letzte Bus-Zeit in Mikrosekunden */ - unsigned int frames_lost; /**< Anzahl verlorene Frames */ - unsigned long t_lost_output; /*<< Timer-Ticks bei der letzten Ausgabe von - verlorenen Frames */ -}; - -/*****************************************************************************/ - -// Public methods - -void *EtherCAT_register_slave(EtherCAT_master_t *, unsigned int, - const char *, const char *, unsigned int); -int EtherCAT_activate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); -int EtherCAT_deactivate_slave(EtherCAT_master_t *, EtherCAT_slave_t *); -int EtherCAT_process_data_cycle(EtherCAT_master_t *, unsigned int, - unsigned int); - -// Private Methods - -// Master creation and deletion -void EtherCAT_master_init(EtherCAT_master_t *); -void EtherCAT_master_clear(EtherCAT_master_t *); - -// Registration of devices -int EtherCAT_master_open(EtherCAT_master_t *, EtherCAT_device_t *); -void EtherCAT_master_close(EtherCAT_master_t *, EtherCAT_device_t *); - -// Sending and receiving -int EtherCAT_simple_send_receive(EtherCAT_master_t *, EtherCAT_command_t *); -int EtherCAT_simple_send(EtherCAT_master_t *, EtherCAT_command_t *); -int EtherCAT_simple_receive(EtherCAT_master_t *, EtherCAT_command_t *); - -// Slave management -int EtherCAT_scan_for_slaves(EtherCAT_master_t *); -int EtherCAT_read_slave_information(EtherCAT_master_t *, unsigned short int, - unsigned short int, unsigned int *); -int EtherCAT_state_change(EtherCAT_master_t *, EtherCAT_slave_t *, - unsigned char); - -// Misc. - -void output_debug_data(const EtherCAT_master_t *); -void ecat_output_lost_frames(EtherCAT_master_t *); - -/*****************************************************************************/ - -#endif - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_module.c --- a/drivers/ec_module.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,282 +0,0 @@ -/****************************************************************************** - * - * ec_module.c - * - * EtherCAT-Master-Treiber - * - * Autoren: Wilhelm Hagemeister, Florian Pose - * - * $Id$ - * - * (C) Copyright IgH 2005 - * Ingenieurgemeinschaft IgH - * Heinz-Bäcker Str. 34 - * D-45356 Essen - * Tel.: +49 201/61 99 31 - * Fax.: +49 201/61 98 36 - * E-mail: sp@igh-essen.com - * - *****************************************************************************/ - -#include -#include -#include - -#include "ec_module.h" - -int __init ecat_init_module(void); -void __exit ecat_cleanup_module(void); - -/*****************************************************************************/ - -#define LIT(X) #X -#define STR(X) LIT(X) - -#define COMPILE_INFO "Revision " STR(EC_REV) \ - ", compiled by " STR(EC_USER) \ - " at " STR(EC_DATE) - -/*****************************************************************************/ - -int ecat_master_count = 1; -EtherCAT_master_t *ecat_masters = NULL; -int *ecat_masters_reserved = NULL; - -/*****************************************************************************/ - -MODULE_AUTHOR ("Wilhelm Hagemeister ," - "Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT master driver module"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(COMPILE_INFO); - -module_param(ecat_master_count, int, 1); -MODULE_PARM_DESC(ecat_master_count, - "Number of EtherCAT master to initialize."); - -module_init(ecat_init_module); -module_exit(ecat_cleanup_module); - -EXPORT_SYMBOL(EtherCAT_register_device); -EXPORT_SYMBOL(EtherCAT_unregister_device); -EXPORT_SYMBOL(EtherCAT_request); -EXPORT_SYMBOL(EtherCAT_release); - -/*****************************************************************************/ - -/** - Init-Funktion des EtherCAT-Master-Treibermodules - - Initialisiert soviele Master, wie im Parameter ecat_master_count - angegeben wurde (Default ist 1). - - @returns 0, wenn alles o.k., -1 bei ungueltiger Anzahl Master - oder zu wenig Speicher. -*/ - -int __init ecat_init_module(void) -{ - unsigned int i; - - printk(KERN_ERR "EtherCAT: Master driver, %s\n", COMPILE_INFO); - - if (ecat_master_count < 1) { - printk(KERN_ERR "EtherCAT: Error - Illegal" - " ecat_master_count: %i\n", ecat_master_count); - return -1; - } - - printk(KERN_ERR "EtherCAT: Initializing %i EtherCAT master(s)...\n", - ecat_master_count); - - if ((ecat_masters = - (EtherCAT_master_t *) kmalloc(sizeof(EtherCAT_master_t) - * ecat_master_count, - GFP_KERNEL)) == NULL) { - printk(KERN_ERR "EtherCAT: Could not allocate" - " memory for EtherCAT master(s)!\n"); - return -1; - } - - if ((ecat_masters_reserved = - (int *) kmalloc(sizeof(int) * ecat_master_count, - GFP_KERNEL)) == NULL) { - printk(KERN_ERR "EtherCAT: Could not allocate" - " memory for reservation flags!\n"); - kfree(ecat_masters); - return -1; - } - - for (i = 0; i < ecat_master_count; i++) - { - EtherCAT_master_init(&ecat_masters[i]); - ecat_masters_reserved[i] = 0; - } - - printk(KERN_ERR "EtherCAT: Master driver initialized.\n"); - - return 0; -} - -/*****************************************************************************/ - -/** - Cleanup-Funktion des EtherCAT-Master-Treibermoduls - - Entfernt alle Master-Instanzen. -*/ - -void __exit ecat_cleanup_module(void) -{ - unsigned int i; - - printk(KERN_ERR "EtherCAT: Cleaning up master driver...\n"); - - if (ecat_masters) - { - for (i = 0; i < ecat_master_count; i++) - { - if (ecat_masters_reserved[i]) { - printk(KERN_WARNING "EtherCAT: Warning -" - " Master %i is still in use!\n", i); - } - - EtherCAT_master_clear(&ecat_masters[i]); - } - - kfree(ecat_masters); - } - - printk(KERN_ERR "EtherCAT: Master driver cleaned up.\n"); -} - -/*****************************************************************************/ - -/** - Setzt das EtherCAT-Geraet, auf dem der Master arbeitet. - - Registriert das Geraet beim Master, der es daraufhin oeffnet. - - @param master Der EtherCAT-Master - @param device Das EtherCAT-Geraet - @return 0, wenn alles o.k., - < 0, wenn bereits ein Geraet registriert - oder das Geraet nicht geoeffnet werden konnte. -*/ - -int EtherCAT_register_device(int index, EtherCAT_device_t *device) -{ - if (index < 0 || index >= ecat_master_count) { - printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", index); - return -1; - } - - return EtherCAT_master_open(&ecat_masters[index], device); -} - -/*****************************************************************************/ - -/** - Loescht das EtherCAT-Geraet, auf dem der Master arbeitet. - - @param master Der EtherCAT-Master - @param device Das EtherCAT-Geraet -*/ - -void EtherCAT_unregister_device(int index, EtherCAT_device_t *device) -{ - if (index < 0 || index >= ecat_master_count) { - printk(KERN_WARNING "EtherCAT: Master %i does not exist!\n", index); - return; - } - - EtherCAT_master_close(&ecat_masters[index], device); -} - -/*****************************************************************************/ - -/** - Reserviert einen bestimmten EtherCAT-Master und das zugehörige Gerät. - - Gibt einen Zeiger auf den reservierten EtherCAT-Master zurueck. - - @param index Index des gewuenschten Masters - @returns Zeiger auf EtherCAT-Master oder NULL, wenn Parameter ungueltig. -*/ - -EtherCAT_master_t *EtherCAT_request(int index) -{ - if (index < 0 || index >= ecat_master_count) { - printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", index); - return NULL; - } - - if (ecat_masters_reserved[index]) { - printk(KERN_ERR "EtherCAT: Master %i already in use!\n", index); - return NULL; - } - - if (!ecat_masters[index].dev) { - printk(KERN_ERR "EtherCAT: Master %i has no device assigned yet!\n", - index); - return NULL; - } - - if (!ecat_masters[index].dev->module) { - printk(KERN_ERR "EtherCAT: Master %i device module is not set!\n", index); - return NULL; - } - - if (!try_module_get(ecat_masters[index].dev->module)) { - printk(KERN_ERR "EtherCAT: Could not reserve device module!\n"); - return NULL; - } - - if (EtherCAT_scan_for_slaves(&ecat_masters[index]) != 0) { - printk(KERN_ERR "EtherCAT: Could not scan for slaves!\n"); - return NULL; - } - - ecat_masters_reserved[index] = 1; - - printk(KERN_INFO "EtherCAT: Reserved master %i.\n", index); - - return &ecat_masters[index]; -} - -/*****************************************************************************/ - -/** - Gibt einen zuvor angeforderten EtherCAT-Master wieder frei. - - @param master Zeiger auf den freizugebenden EtherCAT-Master. -*/ - -void EtherCAT_release(EtherCAT_master_t *master) -{ - unsigned int i; - - for (i = 0; i < ecat_master_count; i++) - { - if (&ecat_masters[i] == master) - { - if (!ecat_masters[i].dev) { - printk(KERN_WARNING "EtherCAT: Could not release device" - "module because of no device!\n"); - return; - } - - module_put(ecat_masters[i].dev->module); - ecat_masters_reserved[i] = 0; - - printk(KERN_INFO "EtherCAT: Released master %i.\n", i); - - return; - } - } - - printk(KERN_WARNING "EtherCAT: Master %X was never requested!\n", - (unsigned int) master); -} - -/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_module.h --- a/drivers/ec_module.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/****************************************************************************** - * - * ec_module.h - * - * EtherCAT-Master-Treiber - * - * Autoren: Wilhelm Hagemeister, Florian Pose - * - * $Id$ - * - * (C) Copyright IgH 2005 - * Ingenieurgemeinschaft IgH - * Heinz-Bäcker Str. 34 - * D-45356 Essen - * Tel.: +49 201/61 99 31 - * Fax.: +49 201/61 98 36 - * E-mail: sp@igh-essen.com - * - *****************************************************************************/ - -#include -#include -#include - -#include "ec_master.h" - -/*****************************************************************************/ - -// Registration of devices -int EtherCAT_register_device(int, EtherCAT_device_t *); -void EtherCAT_unregister_device(int, EtherCAT_device_t *); - -EtherCAT_master_t *EtherCAT_request(int); -void EtherCAT_release(EtherCAT_master_t *); - -/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_slave.c --- a/drivers/ec_slave.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ -/****************************************************************************** - * - * e c _ s l a v e . c - * - * Methoden für einen EtherCAT-Slave. - * - * $Id$ - * - *****************************************************************************/ - -#include - -#include "ec_globals.h" -#include "ec_slave.h" - -/*****************************************************************************/ - -/** - EtherCAT-Slave-Konstruktor. - - Initialisiert einen EtherCAT-Slave. - - ACHTUNG! Dieser Konstruktor wird quasi nie aufgerufen. Bitte immer das - Makro ECAT_INIT_SLAVE() in ec_slave.h anpassen! - - @param slave Zeiger auf den zu initialisierenden Slave -*/ - -void EtherCAT_slave_init(EtherCAT_slave_t *slave) -{ - slave->type = 0; - slave->revision = 0; - slave->build = 0; - slave->ring_position = 0; - slave->station_address = 0; - slave->vendor_id = 0; - slave->product_code = 0; - slave->revision_number = 0; - slave->serial_number = 0; - slave->desc = NULL; - slave->logical_address = 0; - slave->current_state = ECAT_STATE_UNKNOWN; - slave->requested_state = ECAT_STATE_UNKNOWN; - slave->process_data = NULL; - slave->domain = 0; - slave->error_reported = 0; -} - -/*****************************************************************************/ - -/** - Liest einen bestimmten Kanal des Slaves als Integer-Wert. - - Prüft zuerst, ob der entsprechende Slave eine - bekannte Beschreibung besitzt, ob dort eine - read()-Funktion hinterlegt ist und ob die angegebene - Kanalnummer gültig ist. Wenn ja, wird der dekodierte - Wert zurückgegeben, sonst ist der Wert 0. - - @param slave EtherCAT-Slave - @param channel Kanalnummer - - @return Gelesener Wert bzw. 0 -*/ - -int EtherCAT_read_value(EtherCAT_slave_t *slave, - unsigned int channel) -{ - if (unlikely(!slave->desc)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Reading failed on slave %04X (addr %0X)" - " - Slave has no description.\n", - slave->station_address, (unsigned int) slave); - slave->error_reported = 1; - } - return 0; - } - - if (unlikely(!slave->desc->read)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Reading failed on slave %04X (addr %0X)" - " - Slave type (%s %s) has no read method.\n", - slave->station_address, (unsigned int) slave, - slave->desc->vendor_name, slave->desc->product_name); - slave->error_reported = 1; - } - return 0; - } - - if (unlikely(channel >= slave->desc->channel_count)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Reading failed on slave %4X (addr %0X)" - " - Type (%s %s) has no channel %i.\n", - slave->station_address, (unsigned int) slave, - slave->desc->vendor_name, slave->desc->product_name, - channel); - slave->error_reported = 1; - } - return 0; - } - - if (unlikely(!slave->process_data)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Reading failed on slave %4X (addr %0X)" - " - Slave does not belong to any process data object!\n", - slave->station_address, (unsigned int) slave); - slave->error_reported = 1; - } - return 0; - } - - if (unlikely(slave->error_reported)) - slave->error_reported = 0; - - return slave->desc->read(slave->process_data, channel); -} - -/*****************************************************************************/ - -/** - Schreibt einen bestimmten Kanal des Slaves als Integer-Wert . - - Prüft zuerst, ob der entsprechende Slave eine - bekannte Beschreibung besitzt, ob dort eine - write()-Funktion hinterlegt ist und ob die angegebene - Kanalnummer gültig ist. Wenn ja, wird der Wert entsprechend - kodiert und geschrieben. - - @param slave EtherCAT-Slave - @param channel Kanalnummer - @param value Zu schreibender Wert -*/ - -void EtherCAT_write_value(EtherCAT_slave_t *slave, - unsigned int channel, - int value) -{ - if (unlikely(!slave->desc)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Writing failed on slave %04X (addr %0X)" - " - Slave has no description.\n", - slave->station_address, (unsigned int) slave); - slave->error_reported = 1; - } - return; - } - - if (unlikely(!slave->desc->write)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Writing failed on slave %04X (addr %0X)" - " - Type (%s %s) has no write method.\n", - slave->station_address, (unsigned int) slave, - slave->desc->vendor_name, slave->desc->product_name); - slave->error_reported = 1; - } - return; - } - - if (unlikely(channel >= slave->desc->channel_count)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Writing failed on slave %4X (addr %0X)" - " - Type (%s %s) has no channel %i.\n", - slave->station_address, (unsigned int) slave, - slave->desc->vendor_name, slave->desc->product_name, - channel); - slave->error_reported = 1; - } - return; - } - - if (unlikely(!slave->process_data)) { - if (likely(slave->error_reported)) { - printk(KERN_WARNING "EtherCAT: Writing failed on slave %4X (addr %0X)" - " - Slave does not belong to any process data object!\n", - slave->station_address, (unsigned int) slave); - slave->error_reported = 1; - } - return; - } - - if (unlikely(slave->error_reported)) - slave->error_reported = 0; - - slave->desc->write(slave->process_data, channel, value); -} - -/*****************************************************************************/ - -EXPORT_SYMBOL(EtherCAT_write_value); -EXPORT_SYMBOL(EtherCAT_read_value); - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_slave.h --- a/drivers/ec_slave.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/****************************************************************************** - * - * e c _ s l a v e . h - * - * Struktur für einen EtherCAT-Slave. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_SLAVE_H_ -#define _EC_SLAVE_H_ - -#include "ec_types.h" - -/*****************************************************************************/ - -/** - EtherCAT-Slave - - Achtung: Bei Änderungen dieser Struktur immer das Define - ECAT_INIT_SLAVE anpassen! -*/ - -typedef struct -{ - // Base data - unsigned char type; /**< Slave-Typ */ - unsigned char revision; /**< Revision */ - unsigned short build; /**< Build-Nummer */ - - // Addresses - short ring_position; /**< (Negative) Position des Slaves im Bus */ - unsigned short station_address; /**< Konfigurierte Slave-Adresse */ - - // Slave information interface - unsigned int vendor_id; /**< Identifikationsnummer des Herstellers */ - unsigned int product_code; /**< Herstellerspezifischer Produktcode */ - unsigned int revision_number; /**< Revisionsnummer */ - unsigned int serial_number; /**< Seriennummer der Klemme */ - - const EtherCAT_slave_desc_t *desc; /**< Zeiger auf die Beschreibung - des Slave-Typs */ - - unsigned int logical_address; /**< Konfigurierte, logische adresse */ - - EtherCAT_state_t current_state; /**< Aktueller Zustand */ - EtherCAT_state_t requested_state; /**< Angeforderter Zustand */ - - unsigned char *process_data; /**< Zeiger auf den Speicherbereich - innerhalb eines Prozessdatenobjekts */ - unsigned int domain; /**< Prozessdatendomäne */ - int error_reported; /**< Ein Zugriffsfehler wurde bereits gemeldet */ -} -EtherCAT_slave_t; - -#define ECAT_INIT_SLAVE(TYPE, DOMAIN) {0, 0, 0, 0, 0, 0, 0, 0, 0, \ - TYPE, 0, ECAT_STATE_UNKNOWN, \ - ECAT_STATE_UNKNOWN, NULL, DOMAIN, 0} - -/*****************************************************************************/ - -// Slave construction and deletion -void EtherCAT_slave_init(EtherCAT_slave_t *); - -int EtherCAT_read_value(EtherCAT_slave_t *, unsigned int); -void EtherCAT_write_value(EtherCAT_slave_t *, unsigned int, int); - -/*****************************************************************************/ - -#endif - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:2 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_types.c --- a/drivers/ec_types.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -/****************************************************************************** - * - * e c _ t y p e s . c - * - * EtherCAT-Slave-Typen. - * - * $Id$ - * - *****************************************************************************/ - -#include - -#include "ec_globals.h" -#include "ec_types.h" - -/*****************************************************************************/ - -/* Konfigurationen der Sync-Manager */ - -unsigned char sm0_multi[] = {0x00, 0x18, 0xF6, 0x00, 0x26, 0x00, 0x01, 0x00}; -unsigned char sm1_multi[] = {0xF6, 0x18, 0xF6, 0x00, 0x22, 0x00, 0x01, 0x00}; - -unsigned char sm0_1014[] = {0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}; - -unsigned char sm0_2004[] = {0x00, 0x0F, 0x01, 0x00, 0x46, 0x00, 0x01, 0x00}; - -unsigned char sm2_31xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00}; -unsigned char sm3_31xx[] = {0x00, 0x11, 0x06, 0x00, 0x20, 0x00, 0x01, 0x00}; - -unsigned char sm2_41xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00}; - -/* Konfigurationen der Memory-Management-Units */ - -unsigned char fmmu0_1014[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, - 0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; - -unsigned char fmmu0_2004[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, - 0x00, 0x0F, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; - -unsigned char fmmu0_31xx[] = {0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, - 0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; - -unsigned char fmmu0_41xx[] = {0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, - 0x00, 0x10, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; - -/*****************************************************************************/ - -/* Lese- und Schreibfunktionen */ - -int read_1014(unsigned char *data, unsigned int channel) -{ - return (data[0] >> channel) & 0x01; -} - -void write_2004(unsigned char *data, unsigned int channel, int value) -{ - if (value) - { - data[0] |= (1 << channel); - } - else - { - data[0] &= ~(1 << channel); - } -} - -int read_31xx(unsigned char *data, unsigned int channel) -{ - return (short int) ((data[channel * 3 + 2] << 8) | data[channel * 3 + 1]); -} - -void write_41xx(unsigned char *data, unsigned int channel, int value) -{ - data[channel * 3 + 1] = (value & 0xFF00) >> 8; - data[channel * 3 + 2] = value & 0xFF; -} - -/*****************************************************************************/ - -/* Klemmen-Objekte */ - -EtherCAT_slave_desc_t Beckhoff_EK1100[] = -{{ - "Beckhoff", "EK1100", "Bus Coupler", - ECAT_ST_SIMPLE_NOSYNC, - NULL, NULL, NULL, NULL, - NULL, - 0, 0, - NULL, NULL -}}; - -EtherCAT_slave_desc_t Beckhoff_EL1014[] = -{{ - "Beckhoff", "EL1014", "4x Digital Input", - ECAT_ST_SIMPLE, - sm0_1014, NULL, NULL, NULL, - fmmu0_1014, - 1, 4, - read_1014, NULL -}}; - -EtherCAT_slave_desc_t Beckhoff_EL2004[] = -{{ - "Beckhoff", "EL2004", "4x Digital Output", - ECAT_ST_SIMPLE, - sm0_2004, NULL, NULL, NULL, - fmmu0_2004, - 1, 4, - NULL, write_2004 -}}; - -EtherCAT_slave_desc_t Beckhoff_EL3102[] = -{{ - "Beckhoff", "EL3102", "2x Analog Input diff.", - ECAT_ST_MAILBOX, - sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, - fmmu0_31xx, - 6, 2, - read_31xx, NULL -}}; - -EtherCAT_slave_desc_t Beckhoff_EL3162[] = -{{ - "Beckhoff", "EL3162", "2x Analog Input", - ECAT_ST_MAILBOX, - sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, - fmmu0_31xx, - 6, 2, - read_31xx, NULL -}}; - -EtherCAT_slave_desc_t Beckhoff_EL4102[] = -{{ - "Beckhoff", "EL4102", "2x Analog Output", - ECAT_ST_MAILBOX, - sm0_multi, sm1_multi, sm2_41xx, NULL, - fmmu0_41xx, - 4, 2, - NULL, write_41xx -}}; - -EtherCAT_slave_desc_t Beckhoff_EL4132[] = -{{ - "Beckhoff", "EL4132", "2x Analog Output diff.", - ECAT_ST_MAILBOX, - sm0_multi, sm1_multi, sm2_41xx, NULL, - fmmu0_41xx, - 4, 2, - NULL, write_41xx -}}; - -EtherCAT_slave_desc_t Beckhoff_EL5001[] = -{{ - "Beckhoff", "EL5001", "SSI-Interface", - ECAT_ST_SIMPLE, - NULL, NULL, NULL, NULL, // Noch nicht eingepflegt... - NULL, - 0, 0, - NULL, NULL -}}; - -/*****************************************************************************/ - -/** - Beziehung zwischen Identifikationsnummern und Klemmen-Objekt. - - Diese Tabelle stellt die Beziehungen zwischen bestimmten Kombinationen - aus Vendor-IDs und Product-Codes und der entsprechenden Klemme her. - Neue Klemmen müssen hier eingetragen werden. -*/ - -EtherCAT_slave_ident_t slave_idents[] = -{ - {0x00000002, 0x03F63052, Beckhoff_EL1014}, - {0x00000002, 0x044C2C52, Beckhoff_EK1100}, - {0x00000002, 0x07D43052, Beckhoff_EL2004}, - {0x00000002, 0x0C1E3052, Beckhoff_EL3102}, - {0x00000002, 0x0C5A3052, Beckhoff_EL3162}, - {0x00000002, 0x10063052, Beckhoff_EL4102}, - {0x00000002, 0x10243052, Beckhoff_EL4132}, - {0x00000002, 0x13893052, Beckhoff_EL5001} -}; - -unsigned int slave_ident_count = sizeof(slave_idents) - / sizeof(EtherCAT_slave_ident_t); - - -/*****************************************************************************/ - -EXPORT_SYMBOL(Beckhoff_EK1100); -EXPORT_SYMBOL(Beckhoff_EL1014); -EXPORT_SYMBOL(Beckhoff_EL2004); -EXPORT_SYMBOL(Beckhoff_EL3102); -EXPORT_SYMBOL(Beckhoff_EL3162); -EXPORT_SYMBOL(Beckhoff_EL4102); -EXPORT_SYMBOL(Beckhoff_EL4132); -EXPORT_SYMBOL(Beckhoff_EL5001); - -/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/ec_types.h --- a/drivers/ec_types.h Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/****************************************************************************** - * - * e c _ t y p e s . h - * - * EtherCAT-Slave-Typen. - * - * $Id$ - * - *****************************************************************************/ - -#ifndef _EC_TYPES_H_ -#define _EC_TYPES_H_ - -/*****************************************************************************/ - -/** - Typ eines EtherCAT-Slaves. - - Dieser Typ muss für die Konfiguration bekannt sein. Der - Master entscheidet danach, ober bspw. Mailboxes konfigurieren, - oder Sync-Manager setzen soll. -*/ - -typedef enum -{ - ECAT_ST_SIMPLE, ECAT_ST_MAILBOX, ECAT_ST_SIMPLE_NOSYNC -} -EtherCAT_slave_type_t; - -/*****************************************************************************/ - -/** - Beschreibung eines EtherCAT-Slave-Typs. - - Diese Beschreibung dient zur Konfiguration einer bestimmten - Slave-Art. Sie enthält die Konfigurationsdaten für die - Slave-internen Sync-Manager und FMMU's. -*/ - -typedef struct slave_desc -{ - const char *vendor_name; /**< Name des Herstellers */ - const char *product_name; /**< Name des Slaves-Typs */ - const char *product_desc; /**< Genauere Beschreibung des Slave-Typs */ - - const EtherCAT_slave_type_t type; /**< Art des Slave-Typs */ - - const unsigned char *sm0; /**< Konfigurationsdaten des - ersten Sync-Managers */ - const unsigned char *sm1; /**< Konfigurationsdaten des - zweiten Sync-Managers */ - const unsigned char *sm2; /**< Konfigurationsdaten des - dritten Sync-Managers */ - const unsigned char *sm3; /**< Konfigurationsdaten des - vierten Sync-Managers */ - - const unsigned char *fmmu0; /**< Konfigurationsdaten - der ersten FMMU */ - - const unsigned int process_data_size; /**< Länge der Prozessdaten in Bytes */ - const unsigned int channel_count; /**< Anzahl der Kanäle */ - - int (*read) (unsigned char *, unsigned int); /**< Funktion zum Dekodieren - und Lesen der Kanaldaten */ - void (*write) (unsigned char *, unsigned int, int); /**< Funktion zum - Kodieren und Schreiben - der Kanaldaten */ -} -EtherCAT_slave_desc_t; - -/*****************************************************************************/ - -/** - Identifikation eines Slave-Typs. - - Diese Struktur wird zur 1:n-Zuordnung von Hersteller- und - Produktcodes zu den einzelnen Slave-Typen verwendet. -*/ - -typedef struct slave_ident -{ - const unsigned int vendor_id; /**< Hersteller-Code */ - const unsigned int product_code; /**< Herstellerspezifischer Produktcode */ - const EtherCAT_slave_desc_t *desc; /**< Zeiger auf den dazugehörigen - Slave-Typ */ -} -EtherCAT_slave_ident_t; - -extern EtherCAT_slave_ident_t slave_idents[]; /**< Statisches Array der - Slave-Identifikationen */ -extern unsigned int slave_ident_count; /**< Anzahl der vorhandenen - Slave-Identifikationen */ - -/*****************************************************************************/ - -extern EtherCAT_slave_desc_t Beckhoff_EK1100[]; -extern EtherCAT_slave_desc_t Beckhoff_EL1014[]; -extern EtherCAT_slave_desc_t Beckhoff_EL2004[]; -extern EtherCAT_slave_desc_t Beckhoff_EL3102[]; -extern EtherCAT_slave_desc_t Beckhoff_EL3162[]; -extern EtherCAT_slave_desc_t Beckhoff_EL4102[]; -extern EtherCAT_slave_desc_t Beckhoff_EL4132[]; -extern EtherCAT_slave_desc_t Beckhoff_EL5001[]; - -/*****************************************************************************/ - -#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 drivers/original_8139too.c --- a/drivers/original_8139too.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2646 +0,0 @@ -/* - - 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. - - Maintained by Jeff Garzik - Copyright 2000-2002 Jeff Garzik - - Much code comes from Donald Becker's rtl8139.c driver, - versions 1.13 and older. This driver was originally based - on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: - - ---------- - - Written 1997-2001 by Donald Becker. - This software may be used and distributed according to the - terms of the GNU General Public License (GPL), incorporated - herein by reference. Drivers based on or derived from this - code fall under the GPL and must retain the authorship, - copyright and license notice. This file is not a complete - program and may only be used when the entire operating - system is licensed under the GPL. - - This driver is for boards based on the RTL8129 and RTL8139 - PCI ethernet chips. - - The author may be reached as becker@scyld.com, or C/O Scyld - Computing Corporation 410 Severn Ave., Suite 210 Annapolis - MD 21403 - - Support and updates available at - http://www.scyld.com/network/rtl8139.html - - Twister-tuning table provided by Kinston - . - - ---------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - Contributors: - - Donald Becker - he wrote the original driver, kudos to him! - (but please don't e-mail him for support, this isn't his driver) - - Tigran Aivazian - bug fixes, skbuff free cleanup - - Martin Mares - suggestions for PCI cleanup - - David S. Miller - PCI DMA and softnet updates - - Ernst Gill - fixes ported from BSD driver - - Daniel Kobras - identified specific locations of - posted MMIO write bugginess - - Gerard Sharp - bug fix, testing and feedback - - David Ford - Rx ring wrap fix - - Dan DeMaggio - swapped RTL8139 cards with me, and allowed me - to find and fix a crucial bug on older chipsets. - - Donald Becker/Chris Butterworth/Marcus Westergren - - Noticed various Rx packet size-related buglets. - - Santiago Garcia Mantinan - testing and feedback - - Jens David - 2.2.x kernel backports - - Martin Dennett - incredibly helpful insight on undocumented - features of the 8139 chips - - Jean-Jacques Michel - bug fix - - Tobias Ringström - Rx interrupt status checking suggestion - - Andrew Morton - Clear blocked signals, avoid - buffer overrun setting current->comm. - - Kalle Olavi Niemitalo - Wake-on-LAN ioctls - - Robert Kuebel - Save kernel thread from dying on any signal. - - Submitting bug reports: - - "rtl8139-diag -mmmaaavvveefN" output - enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log - -*/ - -#define DRV_NAME "8139too" -#define DRV_VERSION "0.9.27" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION -#define PFX DRV_NAME ": " - -/* Default Message level */ -#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ - NETIF_MSG_PROBE | \ - NETIF_MSG_LINK) - - -/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ -#ifdef CONFIG_8139TOO_PIO -#define USE_IO_OPS 1 -#endif - -/* define to 1, 2 or 3 to enable copious debugging info */ -#define RTL8139_DEBUG 0 - -/* define to 1 to disable lightweight runtime debugging checks */ -#undef RTL8139_NDEBUG - - -#if RTL8139_DEBUG -/* note: prints function name for you */ -# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -#ifdef RTL8139_NDEBUG -# define assert(expr) do {} while (0) -#else -# define assert(expr) \ - if(unlikely(!(expr))) { \ - printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* A few user-configurable values. */ -/* media options */ -#define MAX_UNITS 8 -static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The RTL chips use a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; - -/* bitmapped message enable number */ -static int debug = -1; - -/* - * Receive ring size - * Warning: 64K ring has hardware issues and may lock up. - */ -#if defined(CONFIG_SH_DREAMCAST) -#define RX_BUF_IDX 1 /* 16K ring */ -#else -#define RX_BUF_IDX 2 /* 32K ring */ -#endif -#define RX_BUF_LEN (8192 << RX_BUF_IDX) -#define RX_BUF_PAD 16 -#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ - -#if RX_BUF_LEN == 65536 -#define RX_BUF_TOT_LEN RX_BUF_LEN -#else -#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) -#endif - -/* Number of Tx descriptor registers. */ -#define NUM_TX_DESC 4 - -/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE -#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) - -/* PCI Tuning Parameters - Threshold is bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ -#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ -#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ -#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) - - -enum { - HAS_MII_XCVR = 0x010000, - HAS_CHIP_XCVR = 0x020000, - HAS_LNK_CHNG = 0x040000, -}; - -#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ -#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ -#define RTL_MIN_IO_SIZE 0x80 -#define RTL8139B_IO_SIZE 256 - -#define RTL8129_CAPS HAS_MII_XCVR -#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG - -typedef enum { - RTL8139 = 0, - RTL8129, -} board_t; - - -/* indexed by board_t, above */ -static struct { - const char *name; - u32 hw_flags; -} board_info[] __devinitdata = { - { "RealTek RTL8139", RTL8139_CAPS }, - { "RealTek RTL8129", RTL8129_CAPS }, -}; - - -static struct pci_device_id rtl8139_pci_tbl[] = { - {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - -#ifdef CONFIG_SH_SECUREEDGE5410 - /* Bogus 8139 silicon reports 8129 without external PROM :-( */ - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, -#endif -#ifdef CONFIG_8139TOO_8129 - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, -#endif - - /* some crazy cards report invalid vendor ids like - * 0x0001 here. The other ids are valid and constant, - * so we simply don't match on the main vendor id. - */ - {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 }, - - {0,} -}; -MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); - -static struct { - const char str[ETH_GSTRING_LEN]; -} ethtool_stats_keys[] = { - { "early_rx" }, - { "tx_buf_mapped" }, - { "tx_timeouts" }, - { "rx_lost_in_ring" }, -}; - -/* The rest of these values should never change. */ - -/* Symbolic offsets to registers. */ -enum RTL8139_registers { - MAC0 = 0, /* Ethernet hardware address. */ - MAR0 = 8, /* Multicast filter. */ - TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ - TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ - RxBuf = 0x30, - ChipCmd = 0x37, - RxBufPtr = 0x38, - RxBufAddr = 0x3A, - IntrMask = 0x3C, - IntrStatus = 0x3E, - TxConfig = 0x40, - RxConfig = 0x44, - Timer = 0x48, /* A general-purpose counter. */ - RxMissed = 0x4C, /* 24 bits valid, write clears. */ - Cfg9346 = 0x50, - Config0 = 0x51, - Config1 = 0x52, - FlashReg = 0x54, - MediaStatus = 0x58, - Config3 = 0x59, - Config4 = 0x5A, /* absent on RTL-8139A */ - HltClk = 0x5B, - MultiIntr = 0x5C, - TxSummary = 0x60, - BasicModeCtrl = 0x62, - BasicModeStatus = 0x64, - NWayAdvert = 0x66, - NWayLPAR = 0x68, - NWayExpansion = 0x6A, - /* Undocumented registers, but required for proper operation. */ - FIFOTMS = 0x70, /* FIFO Control and test. */ - CSCR = 0x74, /* Chip Status and Configuration Register. */ - PARA78 = 0x78, - PARA7c = 0x7c, /* Magic transceiver parameter register. */ - Config5 = 0xD8, /* absent on RTL-8139A */ -}; - -enum ClearBitMasks { - MultiIntrClear = 0xF000, - ChipCmdClear = 0xE2, - Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), -}; - -enum ChipCmdBits { - CmdReset = 0x10, - CmdRxEnb = 0x08, - CmdTxEnb = 0x04, - RxBufEmpty = 0x01, -}; - -/* Interrupt register bits, using my own meaningful names. */ -enum IntrStatusBits { - PCIErr = 0x8000, - PCSTimeout = 0x4000, - RxFIFOOver = 0x40, - RxUnderrun = 0x20, - RxOverflow = 0x10, - TxErr = 0x08, - TxOK = 0x04, - RxErr = 0x02, - RxOK = 0x01, - - RxAckBits = RxFIFOOver | RxOverflow | RxOK, -}; - -enum TxStatusBits { - TxHostOwns = 0x2000, - TxUnderrun = 0x4000, - TxStatOK = 0x8000, - TxOutOfWindow = 0x20000000, - TxAborted = 0x40000000, - TxCarrierLost = 0x80000000, -}; -enum RxStatusBits { - RxMulticast = 0x8000, - RxPhysical = 0x4000, - RxBroadcast = 0x2000, - RxBadSymbol = 0x0020, - RxRunt = 0x0010, - RxTooLong = 0x0008, - RxCRCErr = 0x0004, - RxBadAlign = 0x0002, - RxStatusOK = 0x0001, -}; - -/* Bits in RxConfig. */ -enum rx_mode_bits { - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0x08, - AcceptMulticast = 0x04, - AcceptMyPhys = 0x02, - AcceptAllPhys = 0x01, -}; - -/* Bits in TxConfig. */ -enum tx_config_bits { - - /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ - TxIFGShift = 24, - TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ - TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ - TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ - TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ - - TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ - TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ - TxClearAbt = (1 << 0), /* Clear abort (WO) */ - TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ - TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ - - TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ -}; - -/* Bits in Config1 */ -enum Config1Bits { - Cfg1_PM_Enable = 0x01, - Cfg1_VPD_Enable = 0x02, - Cfg1_PIO = 0x04, - Cfg1_MMIO = 0x08, - LWAKE = 0x10, /* not on 8139, 8139A */ - Cfg1_Driver_Load = 0x20, - Cfg1_LED0 = 0x40, - Cfg1_LED1 = 0x80, - SLEEP = (1 << 1), /* only on 8139, 8139A */ - PWRDN = (1 << 0), /* only on 8139, 8139A */ -}; - -/* Bits in Config3 */ -enum Config3Bits { - Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ - Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ - Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ - Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ - Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ - Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ - Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ - Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ -}; - -/* Bits in Config4 */ -enum Config4Bits { - LWPTN = (1 << 2), /* not on 8139, 8139A */ -}; - -/* Bits in Config5 */ -enum Config5Bits { - Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ - Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ - Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ - Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ - Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ - Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ - Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ -}; - -enum RxConfigBits { - /* rx fifo threshold */ - RxCfgFIFOShift = 13, - RxCfgFIFONone = (7 << RxCfgFIFOShift), - - /* Max DMA burst */ - RxCfgDMAShift = 8, - RxCfgDMAUnlimited = (7 << RxCfgDMAShift), - - /* rx ring buffer length */ - RxCfgRcv8K = 0, - RxCfgRcv16K = (1 << 11), - RxCfgRcv32K = (1 << 12), - RxCfgRcv64K = (1 << 11) | (1 << 12), - - /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ - RxNoWrap = (1 << 7), -}; - -/* Twister tuning parameters from RealTek. - Completely undocumented, but required to tune bad links on some boards. */ -enum CSCRBits { - CSCR_LinkOKBit = 0x0400, - CSCR_LinkChangeBit = 0x0800, - CSCR_LinkStatusBits = 0x0f000, - CSCR_LinkDownOffCmd = 0x003c0, - CSCR_LinkDownCmd = 0x0f3c0, -}; - -enum Cfg9346Bits { - Cfg9346_Lock = 0x00, - Cfg9346_Unlock = 0xC0, -}; - -typedef enum { - CH_8139 = 0, - CH_8139_K, - CH_8139A, - CH_8139A_G, - CH_8139B, - CH_8130, - CH_8139C, - CH_8100, - CH_8100B_8139D, - CH_8101, -} chip_t; - -enum chip_flags { - HasHltClk = (1 << 0), - HasLWake = (1 << 1), -}; - -#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ - (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) -#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) - -/* directly indexed by chip_t, above */ -const static struct { - const char *name; - u32 version; /* from RTL8139C/RTL8139D docs */ - u32 flags; -} rtl_chip_info[] = { - { "RTL-8139", - HW_REVID(1, 0, 0, 0, 0, 0, 0), - HasHltClk, - }, - - { "RTL-8139 rev K", - HW_REVID(1, 1, 0, 0, 0, 0, 0), - HasHltClk, - }, - - { "RTL-8139A", - HW_REVID(1, 1, 1, 0, 0, 0, 0), - HasHltClk, /* XXX undocumented? */ - }, - - { "RTL-8139A rev G", - HW_REVID(1, 1, 1, 0, 0, 1, 0), - HasHltClk, /* XXX undocumented? */ - }, - - { "RTL-8139B", - HW_REVID(1, 1, 1, 1, 0, 0, 0), - HasLWake, - }, - - { "RTL-8130", - HW_REVID(1, 1, 1, 1, 1, 0, 0), - HasLWake, - }, - - { "RTL-8139C", - HW_REVID(1, 1, 1, 0, 1, 0, 0), - HasLWake, - }, - - { "RTL-8100", - HW_REVID(1, 1, 1, 1, 0, 1, 0), - HasLWake, - }, - - { "RTL-8100B/8139D", - HW_REVID(1, 1, 1, 0, 1, 0, 1), - HasLWake, - }, - - { "RTL-8101", - HW_REVID(1, 1, 1, 0, 1, 1, 1), - HasLWake, - }, -}; - -struct rtl_extra_stats { - unsigned long early_rx; - unsigned long tx_buf_mapped; - unsigned long tx_timeouts; - unsigned long rx_lost_in_ring; -}; - -struct rtl8139_private { - void __iomem *mmio_addr; - int drv_flags; - struct pci_dev *pci_dev; - u32 msg_enable; - struct net_device_stats stats; - unsigned char *rx_ring; - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ - unsigned int tx_flag; - unsigned long cur_tx; - unsigned long dirty_tx; - unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ - unsigned char *tx_bufs; /* Tx bounce buffer region. */ - dma_addr_t rx_ring_dma; - dma_addr_t tx_bufs_dma; - signed char phys[4]; /* MII device addresses. */ - char twistie, twist_row, twist_col; /* Twister tune state. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - spinlock_t lock; - spinlock_t rx_lock; - chip_t chipset; - pid_t thr_pid; - wait_queue_head_t thr_wait; - struct completion thr_exited; - u32 rx_config; - struct rtl_extra_stats xstats; - int time_to_die; - struct mii_if_info mii; - unsigned int regs_len; - unsigned long fifo_copy_timeout; -}; - -MODULE_AUTHOR ("Jeff Garzik "); -MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - -module_param(multicast_filter_limit, int, 0); -module_param_array(media, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(debug, int, 0); -MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); -MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); -MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); -MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); - -static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); -static int rtl8139_open (struct net_device *dev); -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 val); -static void rtl8139_start_thread(struct net_device *dev); -static void rtl8139_tx_timeout (struct net_device *dev); -static void rtl8139_init_ring (struct net_device *dev); -static int rtl8139_start_xmit (struct sk_buff *skb, - struct net_device *dev); -static int rtl8139_poll(struct net_device *dev, int *budget); -#ifdef CONFIG_NET_POLL_CONTROLLER -static void rtl8139_poll_controller(struct net_device *dev); -#endif -static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs); -static int rtl8139_close (struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); -static void rtl8139_set_rx_mode (struct net_device *dev); -static void __set_rx_mode (struct net_device *dev); -static void rtl8139_hw_start (struct net_device *dev); -static struct ethtool_ops rtl8139_ethtool_ops; - -/* write MMIO register, with flush */ -/* Flush avoids rtl8139 bug w/ posted MMIO writes */ -#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) -#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) -#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) - - -#define MMIO_FLUSH_AUDIT_COMPLETE 1 -#if MMIO_FLUSH_AUDIT_COMPLETE - -/* write MMIO register */ -#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) - -#else - -/* write MMIO register, then flush */ -#define RTL_W8 RTL_W8_F -#define RTL_W16 RTL_W16_F -#define RTL_W32 RTL_W32_F - -#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ - -/* read MMIO register */ -#define RTL_R8(reg) ioread8 (ioaddr + (reg)) -#define RTL_R16(reg) ioread16 (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg))) - - -static const u16 rtl8139_intr_mask = - PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | - TxErr | TxOK | RxErr | RxOK; - -static const u16 rtl8139_norx_intr_mask = - PCIErr | PCSTimeout | RxUnderrun | - TxErr | TxOK | RxErr ; - -#if RX_BUF_IDX == 0 -static const unsigned int rtl8139_rx_config = - RxCfgRcv8K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 1 -static const unsigned int rtl8139_rx_config = - RxCfgRcv16K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 2 -static const unsigned int rtl8139_rx_config = - RxCfgRcv32K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#elif RX_BUF_IDX == 3 -static const unsigned int rtl8139_rx_config = - RxCfgRcv64K | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); -#else -#error "Invalid configuration for 8139_RXBUF_IDX" -#endif - -static const unsigned int rtl8139_tx_config = - TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); - -static void __rtl8139_cleanup_dev (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - struct pci_dev *pdev; - - assert (dev != NULL); - assert (tp->pci_dev != NULL); - pdev = tp->pci_dev; - -#ifdef USE_IO_OPS - if (tp->mmio_addr) - ioport_unmap (tp->mmio_addr); -#else - if (tp->mmio_addr) - pci_iounmap (pdev, tp->mmio_addr); -#endif /* USE_IO_OPS */ - - /* it's ok to call this even if we have no regions to free */ - pci_release_regions (pdev); - - free_netdev(dev); - pci_set_drvdata (pdev, NULL); -} - - -static void rtl8139_chip_reset (void __iomem *ioaddr) -{ - int i; - - /* Soft reset the chip. */ - RTL_W8 (ChipCmd, CmdReset); - - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) { - barrier(); - if ((RTL_R8 (ChipCmd) & CmdReset) == 0) - break; - udelay (10); - } -} - - -static int __devinit rtl8139_init_board (struct pci_dev *pdev, - struct net_device **dev_out) -{ - void __iomem *ioaddr; - struct net_device *dev; - struct rtl8139_private *tp; - u8 tmp8; - int rc, disable_dev_on_err = 0; - unsigned int i; - unsigned long pio_start, pio_end, pio_flags, pio_len; - unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 version; - - assert (pdev != NULL); - - *dev_out = NULL; - - /* dev and priv zeroed in alloc_etherdev */ - dev = alloc_etherdev (sizeof (*tp)); - if (dev == NULL) { - printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); - - tp = netdev_priv(dev); - tp->pci_dev = pdev; - - /* enable device (incl. PCI PM wakeup and hotplug setup) */ - rc = pci_enable_device (pdev); - if (rc) - goto err_out; - - pio_start = pci_resource_start (pdev, 0); - pio_end = pci_resource_end (pdev, 0); - pio_flags = pci_resource_flags (pdev, 0); - pio_len = pci_resource_len (pdev, 0); - - mmio_start = pci_resource_start (pdev, 1); - mmio_end = pci_resource_end (pdev, 1); - mmio_flags = pci_resource_flags (pdev, 1); - mmio_len = pci_resource_len (pdev, 1); - - /* set this immediately, we need to know before - * we talk to the chip directly */ - DPRINTK("PIO region size == 0x%02X\n", pio_len); - DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); - -#ifdef USE_IO_OPS - /* make sure PCI base addr 0 is PIO */ - if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } - /* check for weird/broken PCI region reporting */ - if (pio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } -#else - /* make sure PCI base addr 1 is MMIO */ - if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } - if (mmio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); - rc = -ENODEV; - goto err_out; - } -#endif - - rc = pci_request_regions (pdev, "8139too"); - if (rc) - goto err_out; - disable_dev_on_err = 1; - - /* enable PCI bus-mastering */ - pci_set_master (pdev); - -#ifdef USE_IO_OPS - ioaddr = ioport_map(pio_start, pio_len); - if (!ioaddr) { - printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); - rc = -EIO; - goto err_out; - } - dev->base_addr = pio_start; - tp->mmio_addr = ioaddr; - tp->regs_len = pio_len; -#else - /* ioremap MMIO region */ - ioaddr = pci_iomap(pdev, 1, 0); - if (ioaddr == NULL) { - printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); - rc = -EIO; - goto err_out; - } - dev->base_addr = (long) ioaddr; - tp->mmio_addr = ioaddr; - tp->regs_len = mmio_len; -#endif /* USE_IO_OPS */ - - /* Bring old chips out of low-power mode. */ - RTL_W8 (HltClk, 'R'); - - /* check for missing/broken hardware */ - if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { - printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", - pci_name(pdev)); - rc = -EIO; - goto err_out; - } - - /* identify chip attached to board */ - version = RTL_R32 (TxConfig) & HW_REVID_MASK; - for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) - if (version == rtl_chip_info[i].version) { - tp->chipset = i; - goto match; - } - - /* if unknown chip, assume array element #0, original RTL-8139 in this case */ - printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", - pci_name(pdev)); - printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); - tp->chipset = 0; - -match: - DPRINTK ("chipset id (%d) == index %d, '%s'\n", - version, i, rtl_chip_info[i].name); - - if (tp->chipset >= CH_8139B) { - u8 new_tmp8 = tmp8 = RTL_R8 (Config1); - DPRINTK("PCI PM wakeup\n"); - if ((rtl_chip_info[tp->chipset].flags & HasLWake) && - (tmp8 & LWAKE)) - new_tmp8 &= ~LWAKE; - new_tmp8 |= Cfg1_PM_Enable; - if (new_tmp8 != tmp8) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tmp8); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } - if (rtl_chip_info[tp->chipset].flags & HasLWake) { - tmp8 = RTL_R8 (Config4); - if (tmp8 & LWPTN) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config4, tmp8 & ~LWPTN); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } - } - } else { - DPRINTK("Old chip wakeup\n"); - tmp8 = RTL_R8 (Config1); - tmp8 &= ~(SLEEP | PWRDN); - RTL_W8 (Config1, tmp8); - } - - rtl8139_chip_reset (ioaddr); - - *dev_out = dev; - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - if (disable_dev_on_err) - pci_disable_device (pdev); - return rc; -} - - -static int __devinit rtl8139_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev = NULL; - struct rtl8139_private *tp; - int i, addr_len, option; - void __iomem *ioaddr; - static int board_idx = -1; - u8 pci_rev; - - assert (pdev != NULL); - assert (ent != NULL); - - board_idx++; - - /* when we're built into the kernel, the driver version message - * is only printed if at least one 8139 board has been found - */ -#ifndef MODULE - { - static int printed_version; - if (!printed_version++) - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); - } -#endif - - pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); - - if (pdev->vendor == PCI_VENDOR_ID_REALTEK && - pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { - printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", - pci_name(pdev), pdev->vendor, pdev->device, pci_rev); - printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); - } - - i = rtl8139_init_board (pdev, &dev); - if (i < 0) - return i; - - assert (dev != NULL); - tp = netdev_priv(dev); - - ioaddr = tp->mmio_addr; - assert (ioaddr != NULL); - - addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; - for (i = 0; i < 3; i++) - ((u16 *) (dev->dev_addr))[i] = - le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); - - /* The Rtl8139-specific entries in the device structure. */ - dev->open = rtl8139_open; - dev->hard_start_xmit = rtl8139_start_xmit; - dev->poll = rtl8139_poll; - dev->weight = 64; - dev->stop = rtl8139_close; - dev->get_stats = rtl8139_get_stats; - dev->set_multicast_list = rtl8139_set_rx_mode; - dev->do_ioctl = netdev_ioctl; - dev->ethtool_ops = &rtl8139_ethtool_ops; - dev->tx_timeout = rtl8139_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = rtl8139_poll_controller; -#endif - - /* note: the hardware is not capable of sg/csum/highdma, however - * through the use of skb_copy_and_csum_dev we enable these - * features - */ - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; - - dev->irq = pdev->irq; - - /* tp zeroed and aligned in alloc_etherdev */ - tp = netdev_priv(dev); - - /* note: tp->chipset set in rtl8139_init_board */ - tp->drv_flags = board_info[ent->driver_data].hw_flags; - tp->mmio_addr = ioaddr; - tp->msg_enable = - (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); - spin_lock_init (&tp->lock); - spin_lock_init (&tp->rx_lock); - init_waitqueue_head (&tp->thr_wait); - init_completion (&tp->thr_exited); - tp->mii.dev = dev; - tp->mii.mdio_read = mdio_read; - tp->mii.mdio_write = mdio_write; - tp->mii.phy_id_mask = 0x3f; - tp->mii.reg_num_mask = 0x1f; - - /* dev is fully set up and ready to use now */ - DPRINTK("about to register device named %s (%p)...\n", dev->name, dev); - i = register_netdev (dev); - if (i) goto err_out; - - pci_set_drvdata (pdev, dev); - - printk (KERN_INFO "%s: %s at 0x%lx, " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " - "IRQ %d\n", - dev->name, - board_info[ent->driver_data].name, - dev->base_addr, - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5], - dev->irq); - - printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", - dev->name, rtl_chip_info[tp->chipset].name); - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, but - takes too much time. */ -#ifdef CONFIG_8139TOO_8129 - if (tp->drv_flags & HAS_MII_XCVR) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { - u16 advertising = mdio_read(dev, phy, 4); - tp->phys[phy_idx++] = phy; - printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " - "advertising %4.4x.\n", - dev->name, phy, mii_status, advertising); - } - } - if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " - "transceiver.\n", - dev->name); - tp->phys[0] = 32; - } - } else -#endif - tp->phys[0] = 32; - tp->mii.phy_id = tp->phys[0]; - - /* The lower four bits are the media type. */ - option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; - if (option > 0) { - tp->mii.full_duplex = (option & 0x210) ? 1 : 0; - tp->default_port = option & 0xFF; - if (tp->default_port) - tp->mii.force_media = 1; - } - if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) - tp->mii.full_duplex = full_duplex[board_idx]; - if (tp->mii.full_duplex) { - printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); - /* Changing the MII-advertised media because might prevent - re-connection. */ - tp->mii.force_media = 1; - } - if (tp->default_port) { - printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", - (option & 0x20 ? 100 : 10), - (option & 0x10 ? "full" : "half")); - mdio_write(dev, tp->phys[0], 0, - ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ - ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ - } - - /* Put the chip into low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - pci_disable_device (pdev); - return i; -} - - -static void __devexit rtl8139_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - - assert (dev != NULL); - - unregister_netdev (dev); - - __rtl8139_cleanup_dev (dev); - pci_disable_device (pdev); -} - - -/* Serial EEPROM section. */ - -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ -#define EE_CS 0x08 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x00 -#define EE_WRITE_1 0x02 -#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ -#define EE_ENB (0x80 | EE_CS) - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ - -#define eeprom_delay() RTL_R32(Cfg9346) - -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5) -#define EE_READ_CMD (6) -#define EE_ERASE_CMD (7) - -static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len) -{ - int i; - unsigned retval = 0; - int read_cmd = location | (EE_READ_CMD << addr_len); - - RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - RTL_W8 (Cfg9346, EE_ENB | dataval); - eeprom_delay (); - RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); - eeprom_delay (); - } - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - - for (i = 16; i > 0; i--) { - RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); - eeprom_delay (); - retval = - (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : - 0); - RTL_W8 (Cfg9346, EE_ENB); - eeprom_delay (); - } - - /* Terminate the EEPROM access. */ - RTL_W8 (Cfg9346, ~EE_CS); - eeprom_delay (); - - return retval; -} - -/* MII serial management: mostly bogus for now. */ -/* Read and write the MII management registers using software-generated - serial MDIO protocol. - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues. */ -#define MDIO_DIR 0x80 -#define MDIO_DATA_OUT 0x04 -#define MDIO_DATA_IN 0x02 -#define MDIO_CLK 0x01 -#define MDIO_WRITE0 (MDIO_DIR) -#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) - -#define mdio_delay() RTL_R8(Config4) - - -static char mii_2_8139_map[8] = { - BasicModeCtrl, - BasicModeStatus, - 0, - 0, - NWayAdvert, - NWayLPAR, - NWayExpansion, - 0 -}; - - -#ifdef CONFIG_8139TOO_8129 -/* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync (void __iomem *ioaddr) -{ - int i; - - for (i = 32; i >= 0; i--) { - RTL_W8 (Config4, MDIO_WRITE1); - mdio_delay (); - RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); - mdio_delay (); - } -} -#endif - -static int mdio_read (struct net_device *dev, int phy_id, int location) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int retval = 0; -#ifdef CONFIG_8139TOO_8129 - void __iomem *ioaddr = tp->mmio_addr; - int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void __iomem *ioaddr = tp->mmio_addr; - return location < 8 && mii_2_8139_map[location] ? - RTL_R16 (mii_2_8139_map[location]) : 0; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (ioaddr); - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; - - RTL_W8 (Config4, MDIO_DIR | dataval); - mdio_delay (); - RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); - mdio_delay (); - } - - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - RTL_W8 (Config4, 0); - mdio_delay (); - retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); - RTL_W8 (Config4, MDIO_CLK); - mdio_delay (); - } -#endif - - return (retval >> 1) & 0xffff; -} - - -static void mdio_write (struct net_device *dev, int phy_id, int location, - int value) -{ - struct rtl8139_private *tp = netdev_priv(dev); -#ifdef CONFIG_8139TOO_8129 - void __iomem *ioaddr = tp->mmio_addr; - int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void __iomem *ioaddr = tp->mmio_addr; - if (location == 0) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W16 (BasicModeCtrl, value); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } else if (location < 8 && mii_2_8139_map[location]) - RTL_W16 (mii_2_8139_map[location], value); - return; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (ioaddr); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = - (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - RTL_W8 (Config4, dataval); - mdio_delay (); - RTL_W8 (Config4, dataval | MDIO_CLK); - mdio_delay (); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - RTL_W8 (Config4, 0); - mdio_delay (); - RTL_W8 (Config4, MDIO_CLK); - mdio_delay (); - } -#endif -} - - -static int rtl8139_open (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int retval; - void __iomem *ioaddr = tp->mmio_addr; - - retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) - return retval; - - tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - &tp->tx_bufs_dma); - tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - &tp->rx_ring_dma); - if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { - free_irq(dev->irq, dev); - - if (tp->tx_bufs) - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - if (tp->rx_ring) - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - - return -ENOMEM; - - } - - tp->mii.full_duplex = tp->mii.force_media; - tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; - - rtl8139_init_ring (dev); - rtl8139_hw_start (dev); - netif_start_queue (dev); - - if (netif_msg_ifup(tp)) - printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d" - " GP Pins %2.2x %s-duplex.\n", - dev->name, pci_resource_start (tp->pci_dev, 1), - dev->irq, RTL_R8 (MediaStatus), - tp->mii.full_duplex ? "full" : "half"); - - rtl8139_start_thread(dev); - - return 0; -} - - -static void rtl_check_media (struct net_device *dev, unsigned int init_media) -{ - struct rtl8139_private *tp = netdev_priv(dev); - - if (tp->phys[0] >= 0) { - mii_check_media(&tp->mii, netif_msg_link(tp), init_media); - } -} - -/* Start the hardware at open or resume. */ -static void rtl8139_hw_start (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u32 i; - u8 tmp; - - /* Bring old chips out of low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'R'); - - rtl8139_chip_reset (ioaddr); - - /* unlock Config[01234] and BMCR register writes */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Restore our idea of the MAC address. */ - RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); - RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); - - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - RTL_W32 (TxConfig, rtl8139_tx_config); - - tp->cur_rx = 0; - - rtl_check_media (dev, 1); - - if (tp->chipset >= CH_8139B) { - /* Disable magic packet scanning, which is enabled - * when PM is enabled in Config1. It can be reenabled - * via ETHTOOL_SWOL if desired. */ - RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); - } - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* init Tx buffer DMA addresses */ - for (i = 0; i < NUM_TX_DESC; i++) - RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); - - RTL_W32 (RxMissed, 0); - - rtl8139_set_rx_mode (dev); - - /* no early-rx interrupts */ - RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); - - /* make sure RxTx has started */ - tmp = RTL_R8 (ChipCmd); - if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - /* Enable all known interrupts by setting the interrupt mask. */ - RTL_W16 (IntrMask, rtl8139_intr_mask); -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void rtl8139_init_ring (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - int i; - - tp->cur_rx = 0; - tp->cur_tx = 0; - tp->dirty_tx = 0; - - for (i = 0; i < NUM_TX_DESC; i++) - tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; -} - - -/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ -static int next_tick = 3 * HZ; - -#ifndef CONFIG_8139TOO_TUNE_TWISTER -static inline void rtl8139_tune_twister (struct net_device *dev, - struct rtl8139_private *tp) {} -#else -enum TwisterParamVals { - PARA78_default = 0x78fa8388, - PARA7c_default = 0xcb38de43, /* param[0][3] */ - PARA7c_xxx = 0xcb38de43, -}; - -static const unsigned long param[4][4] = { - {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} -}; - -static void rtl8139_tune_twister (struct net_device *dev, - struct rtl8139_private *tp) -{ - int linkcase; - void __iomem *ioaddr = tp->mmio_addr; - - /* This is a complicated state machine to configure the "twister" for - impedance/echos based on the cable length. - All of this is magic and undocumented. - */ - switch (tp->twistie) { - case 1: - if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { - /* We have link beat, let us tune the twister. */ - RTL_W16 (CSCR, CSCR_LinkDownOffCmd); - tp->twistie = 2; /* Change to state 2. */ - next_tick = HZ / 10; - } else { - /* Just put in some reasonable defaults for when beat returns. */ - RTL_W16 (CSCR, CSCR_LinkDownCmd); - RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ - RTL_W32 (PARA78, PARA78_default); - RTL_W32 (PARA7c, PARA7c_default); - tp->twistie = 0; /* Bail from future actions. */ - } - break; - case 2: - /* Read how long it took to hear the echo. */ - linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; - if (linkcase == 0x7000) - tp->twist_row = 3; - else if (linkcase == 0x3000) - tp->twist_row = 2; - else if (linkcase == 0x1000) - tp->twist_row = 1; - else - tp->twist_row = 0; - tp->twist_col = 0; - tp->twistie = 3; /* Change to state 2. */ - next_tick = HZ / 10; - break; - case 3: - /* Put out four tuning parameters, one per 100msec. */ - if (tp->twist_col == 0) - RTL_W16 (FIFOTMS, 0); - RTL_W32 (PARA7c, param[(int) tp->twist_row] - [(int) tp->twist_col]); - next_tick = HZ / 10; - if (++tp->twist_col >= 4) { - /* For short cables we are done. - For long cables (row == 3) check for mistune. */ - tp->twistie = - (tp->twist_row == 3) ? 4 : 0; - } - break; - case 4: - /* Special case for long cables: check for mistune. */ - if ((RTL_R16 (CSCR) & - CSCR_LinkStatusBits) == 0x7000) { - tp->twistie = 0; - break; - } else { - RTL_W32 (PARA7c, 0xfb38de03); - tp->twistie = 5; - next_tick = HZ / 10; - } - break; - case 5: - /* Retune for shorter cable (column 2). */ - RTL_W32 (FIFOTMS, 0x20); - RTL_W32 (PARA78, PARA78_default); - RTL_W32 (PARA7c, PARA7c_default); - RTL_W32 (FIFOTMS, 0x00); - tp->twist_row = 2; - tp->twist_col = 0; - tp->twistie = 3; - next_tick = HZ / 10; - break; - - default: - /* do nothing */ - break; - } -} -#endif /* CONFIG_8139TOO_TUNE_TWISTER */ - -static inline void rtl8139_thread_iter (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr) -{ - int mii_lpa; - - mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); - - if (!tp->mii.force_media && mii_lpa != 0xffff) { - int duplex = (mii_lpa & LPA_100FULL) - || (mii_lpa & 0x01C0) == 0x0040; - if (tp->mii.full_duplex != duplex) { - tp->mii.full_duplex = duplex; - - if (mii_lpa) { - printk (KERN_INFO - "%s: Setting %s-duplex based on MII #%d link" - " partner ability of %4.4x.\n", - dev->name, - tp->mii.full_duplex ? "full" : "half", - tp->phys[0], mii_lpa); - } else { - printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", - dev->name); - } -#if 0 - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); - RTL_W8 (Cfg9346, Cfg9346_Lock); -#endif - } - } - - next_tick = HZ * 60; - - rtl8139_tune_twister (dev, tp); - - DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", - dev->name, RTL_R16 (NWayLPAR)); - DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n", - dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); - DPRINTK ("%s: Chip config %2.2x %2.2x.\n", - dev->name, RTL_R8 (Config0), - RTL_R8 (Config1)); -} - -static int rtl8139_thread (void *data) -{ - struct net_device *dev = data; - struct rtl8139_private *tp = netdev_priv(dev); - unsigned long timeout; - - daemonize("%s", dev->name); - allow_signal(SIGTERM); - - while (1) { - timeout = next_tick; - do { - timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); - /* make swsusp happy with our thread */ - try_to_freeze(); - } while (!signal_pending (current) && (timeout > 0)); - - if (signal_pending (current)) { - flush_signals(current); - } - - if (tp->time_to_die) - break; - - if (rtnl_lock_interruptible ()) - break; - rtl8139_thread_iter (dev, tp, tp->mmio_addr); - rtnl_unlock (); - } - - complete_and_exit (&tp->thr_exited, 0); -} - -static void rtl8139_start_thread(struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - - tp->thr_pid = -1; - tp->twistie = 0; - tp->time_to_die = 0; - if (tp->chipset == CH_8139_K) - tp->twistie = 1; - else if (tp->drv_flags & HAS_LNK_CHNG) - return; - - tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES); - if (tp->thr_pid < 0) { - printk (KERN_WARNING "%s: unable to start kernel thread\n", - dev->name); - } -} - -static inline void rtl8139_tx_clear (struct rtl8139_private *tp) -{ - tp->cur_tx = 0; - tp->dirty_tx = 0; - - /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ -} - - -static void rtl8139_tx_timeout (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int i; - u8 tmp8; - unsigned long flags; - - printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " - "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), - RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus)); - /* Emit info to figure out what went wrong. */ - printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", - dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - - tp->xstats.tx_timeouts++; - - /* disable Tx ASAP, if not already */ - tmp8 = RTL_R8 (ChipCmd); - if (tmp8 & CmdTxEnb) - RTL_W8 (ChipCmd, CmdRxEnb); - - spin_lock(&tp->rx_lock); - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0x0000); - - /* Stop a shared interrupt from scavenging while we are. */ - spin_lock_irqsave (&tp->lock, flags); - rtl8139_tx_clear (tp); - spin_unlock_irqrestore (&tp->lock, flags); - - /* ...and finally, reset everything */ - if (netif_running(dev)) { - rtl8139_hw_start (dev); - netif_wake_queue (dev); - } - spin_unlock(&tp->rx_lock); -} - - -static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned int entry; - unsigned int len = skb->len; - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % NUM_TX_DESC; - - /* Note: the chip doesn't have auto-pad! */ - if (likely(len < TX_BUF_SIZE)) { - if (len < ETH_ZLEN) - memset(tp->tx_buf[entry], 0, ETH_ZLEN); - skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); - dev_kfree_skb(skb); - } else { - dev_kfree_skb(skb); - tp->stats.tx_dropped++; - return 0; - } - - spin_lock_irq(&tp->lock); - RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), - tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); - - dev->trans_start = jiffies; - - tp->cur_tx++; - wmb(); - - if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) - netif_stop_queue (dev); - spin_unlock_irq(&tp->lock); - - if (netif_msg_tx_queued(tp)) - printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", - dev->name, len, entry); - - return 0; -} - - -static void rtl8139_tx_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr) -{ - unsigned long dirty_tx, tx_left; - - assert (dev != NULL); - assert (ioaddr != NULL); - - dirty_tx = tp->dirty_tx; - tx_left = tp->cur_tx - dirty_tx; - while (tx_left > 0) { - int entry = dirty_tx % NUM_TX_DESC; - int txstatus; - - txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); - - if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) - break; /* It still hasn't been Txed */ - - /* Note: TxCarrierLost is always asserted at 100mbps. */ - if (txstatus & (TxOutOfWindow | TxAborted)) { - /* There was an major error, log it. */ - if (netif_msg_tx_err(tp)) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, txstatus); - tp->stats.tx_errors++; - if (txstatus & TxAborted) { - tp->stats.tx_aborted_errors++; - RTL_W32 (TxConfig, TxClearAbt); - RTL_W16 (IntrStatus, TxErr); - wmb(); - } - if (txstatus & TxCarrierLost) - tp->stats.tx_carrier_errors++; - if (txstatus & TxOutOfWindow) - tp->stats.tx_window_errors++; - } else { - if (txstatus & TxUnderrun) { - /* Add 64 to the Tx FIFO threshold. */ - if (tp->tx_flag < 0x00300000) - tp->tx_flag += 0x00020000; - tp->stats.tx_fifo_errors++; - } - tp->stats.collisions += (txstatus >> 24) & 15; - tp->stats.tx_bytes += txstatus & 0x7ff; - tp->stats.tx_packets++; - } - - dirty_tx++; - tx_left--; - } - -#ifndef RTL8139_NDEBUG - if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { - printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", - dev->name, dirty_tx, tp->cur_tx); - dirty_tx += NUM_TX_DESC; - } -#endif /* RTL8139_NDEBUG */ - - /* only wake the queue if we did work, and the queue is stopped */ - if (tp->dirty_tx != dirty_tx) { - tp->dirty_tx = dirty_tx; - mb(); - netif_wake_queue (dev); - } -} - - -/* TODO: clean this up! Rx reset need not be this intensive */ -static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, - struct rtl8139_private *tp, void __iomem *ioaddr) -{ - u8 tmp8; -#ifdef CONFIG_8139_OLD_RX_RESET - int tmp_work; -#endif - - if (netif_msg_rx_err (tp)) - printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n", - dev->name, rx_status); - tp->stats.rx_errors++; - if (!(rx_status & RxStatusOK)) { - if (rx_status & RxTooLong) { - DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", - dev->name, rx_status); - /* A.C.: The chip hangs here. */ - } - if (rx_status & (RxBadSymbol | RxBadAlign)) - tp->stats.rx_frame_errors++; - if (rx_status & (RxRunt | RxTooLong)) - tp->stats.rx_length_errors++; - if (rx_status & RxCRCErr) - tp->stats.rx_crc_errors++; - } else { - tp->xstats.rx_lost_in_ring++; - } - -#ifndef CONFIG_8139_OLD_RX_RESET - tmp8 = RTL_R8 (ChipCmd); - RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); - RTL_W8 (ChipCmd, tmp8); - RTL_W32 (RxConfig, tp->rx_config); - tp->cur_rx = 0; -#else - /* Reset the receiver, based on RealTek recommendation. (Bug?) */ - - /* disable receive */ - RTL_W8_F (ChipCmd, CmdTxEnb); - tmp_work = 200; - while (--tmp_work > 0) { - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if (!(tmp8 & CmdRxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "rx stop wait too long\n"); - /* restart receive */ - tmp_work = 200; - while (--tmp_work > 0) { - RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); - - /* and reinitialize all rx related registers */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - tp->cur_rx = 0; - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* A.C.: Reset the multicast list. */ - __set_rx_mode (dev); -#endif -} - -#if RX_BUF_IDX == 3 -static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, - u32 offset, unsigned int size) -{ - u32 left = RX_BUF_LEN - offset; - - if (size > left) { - memcpy(skb->data, ring + offset, left); - memcpy(skb->data+left, ring, size - left); - } else - memcpy(skb->data, ring + offset, size); -} -#endif - -static void rtl8139_isr_ack(struct rtl8139_private *tp) -{ - void __iomem *ioaddr = tp->mmio_addr; - u16 status; - - status = RTL_R16 (IntrStatus) & RxAckBits; - - /* Clear out errors and receive interrupts */ - if (likely(status != 0)) { - if (unlikely(status & (RxFIFOOver | RxOverflow))) { - tp->stats.rx_errors++; - if (status & RxFIFOOver) - tp->stats.rx_fifo_errors++; - } - RTL_W16_F (IntrStatus, RxAckBits); - } -} - -static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, - int budget) -{ - void __iomem *ioaddr = tp->mmio_addr; - int received = 0; - unsigned char *rx_ring = tp->rx_ring; - unsigned int cur_rx = tp->cur_rx; - unsigned int rx_size = 0; - - DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); - - while (netif_running(dev) && received < budget - && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { - u32 ring_offset = cur_rx % RX_BUF_LEN; - u32 rx_status; - unsigned int pkt_size; - struct sk_buff *skb; - - rmb(); - - /* read size+status of next frame from DMA ring buffer */ - rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); - rx_size = rx_status >> 16; - pkt_size = rx_size - 4; - - if (netif_msg_rx_status(tp)) - printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x," - " cur %4.4x.\n", dev->name, rx_status, - rx_size, cur_rx); -#if RTL8139_DEBUG > 2 - { - int i; - DPRINTK ("%s: Frame contents ", dev->name); - for (i = 0; i < 70; i++) - printk (" %2.2x", - rx_ring[ring_offset + i]); - printk (".\n"); - } -#endif - - /* Packet copy from FIFO still in progress. - * Theoretically, this should never happen - * since EarlyRx is disabled. - */ - if (unlikely(rx_size == 0xfff0)) { - if (!tp->fifo_copy_timeout) - tp->fifo_copy_timeout = jiffies + 2; - else if (time_after(jiffies, tp->fifo_copy_timeout)) { - DPRINTK ("%s: hung FIFO. Reset.", dev->name); - rx_size = 0; - goto no_early_rx; - } - if (netif_msg_intr(tp)) { - printk(KERN_DEBUG "%s: fifo copy in progress.", - dev->name); - } - tp->xstats.early_rx++; - break; - } - -no_early_rx: - tp->fifo_copy_timeout = 0; - - /* If Rx err or invalid rx_size/rx_status received - * (which happens if we get lost in the ring), - * Rx process gets reset, so we abort any further - * Rx processing. - */ - if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || - (rx_size < 8) || - (!(rx_status & RxStatusOK)))) { - rtl8139_rx_err (rx_status, dev, tp, ioaddr); - received = -1; - goto out; - } - - /* Malloc up new buffer, compatible with net-2e. */ - /* Omit the four octet CRC from the length. */ - - skb = dev_alloc_skb (pkt_size + 2); - if (likely(skb)) { - skb->dev = dev; - skb_reserve (skb, 2); /* 16 byte align the IP fields. */ -#if RX_BUF_IDX == 3 - wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); -#else - eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); -#endif - skb_put (skb, pkt_size); - - skb->protocol = eth_type_trans (skb, dev); - - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - - netif_receive_skb (skb); - } else { - if (net_ratelimit()) - printk (KERN_WARNING - "%s: Memory squeeze, dropping packet.\n", - dev->name); - tp->stats.rx_dropped++; - } - received++; - - cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; - RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); - - rtl8139_isr_ack(tp); - } - - if (unlikely(!received || rx_size == 0xfff0)) - rtl8139_isr_ack(tp); - -#if RTL8139_DEBUG > 1 - DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); -#endif - - tp->cur_rx = cur_rx; - - /* - * The receive buffer should be mostly empty. - * Tell NAPI to reenable the Rx irq. - */ - if (tp->fifo_copy_timeout) - received = budget; - -out: - return received; -} - - -static void rtl8139_weird_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void __iomem *ioaddr, - int status, int link_changed) -{ - DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", - dev->name, status); - - assert (dev != NULL); - assert (tp != NULL); - assert (ioaddr != NULL); - - /* Update the error count. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - if ((status & RxUnderrun) && link_changed && - (tp->drv_flags & HAS_LNK_CHNG)) { - rtl_check_media(dev, 0); - status &= ~RxUnderrun; - } - - if (status & (RxUnderrun | RxErr)) - tp->stats.rx_errors++; - - if (status & PCSTimeout) - tp->stats.rx_length_errors++; - if (status & RxUnderrun) - tp->stats.rx_fifo_errors++; - if (status & PCIErr) { - u16 pci_cmd_status; - pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); - pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); - - printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", - dev->name, pci_cmd_status); - } -} - -static int rtl8139_poll(struct net_device *dev, int *budget) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int orig_budget = min(*budget, dev->quota); - int done = 1; - - spin_lock(&tp->rx_lock); - if (likely(RTL_R16(IntrStatus) & RxAckBits)) { - int work_done; - - work_done = rtl8139_rx(dev, tp, orig_budget); - if (likely(work_done > 0)) { - *budget -= work_done; - dev->quota -= work_done; - done = (work_done < orig_budget); - } - } - - if (done) { - /* - * Order is important since data can get interrupted - * again when we think we are done. - */ - local_irq_disable(); - RTL_W16_F(IntrMask, rtl8139_intr_mask); - __netif_rx_complete(dev); - local_irq_enable(); - } - spin_unlock(&tp->rx_lock); - - return !done; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *) dev_instance; - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u16 status, ackstat; - int link_changed = 0; /* avoid bogus "uninit" warning */ - int handled = 0; - - spin_lock (&tp->lock); - status = RTL_R16 (IntrStatus); - - /* shared irq? */ - if (unlikely((status & rtl8139_intr_mask) == 0)) - goto out; - - handled = 1; - - /* h/w no longer present (hotplug?) or major error, bail */ - if (unlikely(status == 0xFFFF)) - goto out; - - /* close possible race's with dev_close */ - if (unlikely(!netif_running(dev))) { - RTL_W16 (IntrMask, 0); - goto out; - } - - /* Acknowledge all of the current interrupt sources ASAP, but - an first get an additional status bit from CSCR. */ - if (unlikely(status & RxUnderrun)) - link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; - - ackstat = status & ~(RxAckBits | TxErr); - if (ackstat) - RTL_W16 (IntrStatus, ackstat); - - /* Receive packets are processed by poll routine. - If not running start it now. */ - if (status & RxAckBits){ - if (netif_rx_schedule_prep(dev)) { - RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); - __netif_rx_schedule (dev); - } - } - - /* Check uncommon events with one test. */ - if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) - rtl8139_weird_interrupt (dev, tp, ioaddr, - status, link_changed); - - if (status & (TxOK | TxErr)) { - rtl8139_tx_interrupt (dev, tp, ioaddr); - if (status & TxErr) - RTL_W16 (IntrStatus, TxErr); - } - out: - spin_unlock (&tp->lock); - - DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); - return IRQ_RETVAL(handled); -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling receive - used by netconsole and other diagnostic tools - * to allow network i/o with interrupts disabled. - */ -static void rtl8139_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - rtl8139_interrupt(dev->irq, dev, NULL); - enable_irq(dev->irq); -} -#endif - -static int rtl8139_close (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - int ret = 0; - unsigned long flags; - - netif_stop_queue (dev); - - if (tp->thr_pid >= 0) { - tp->time_to_die = 1; - wmb(); - ret = kill_proc (tp->thr_pid, SIGTERM, 1); - if (ret) { - printk (KERN_ERR "%s: unable to signal thread\n", dev->name); - return ret; - } - wait_for_completion (&tp->thr_exited); - } - - if (netif_msg_ifdown(tp)) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); - - spin_lock_irqsave (&tp->lock, flags); - - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8 (ChipCmd, 0); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - - synchronize_irq (dev->irq); /* racy, but that's ok here */ - free_irq (dev->irq, dev); - - rtl8139_tx_clear (tp); - - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - tp->rx_ring = NULL; - tp->tx_bufs = NULL; - - /* Green! Put the chip in low-power mode. */ - RTL_W8 (Cfg9346, Cfg9346_Unlock); - - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; -} - - -/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to - kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and - other threads or interrupts aren't messing with the 8139. */ -static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mmio_addr; - - spin_lock_irq(&np->lock); - if (rtl_chip_info[np->chipset].flags & HasLWake) { - u8 cfg3 = RTL_R8 (Config3); - u8 cfg5 = RTL_R8 (Config5); - - wol->supported = WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; - - wol->wolopts = 0; - if (cfg3 & Cfg3_LinkUp) - wol->wolopts |= WAKE_PHY; - if (cfg3 & Cfg3_Magic) - wol->wolopts |= WAKE_MAGIC; - /* (KON)FIXME: See how netdev_set_wol() handles the - following constants. */ - if (cfg5 & Cfg5_UWF) - wol->wolopts |= WAKE_UCAST; - if (cfg5 & Cfg5_MWF) - wol->wolopts |= WAKE_MCAST; - if (cfg5 & Cfg5_BWF) - wol->wolopts |= WAKE_BCAST; - } - spin_unlock_irq(&np->lock); -} - - -/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes - that wol points to kernel memory and other threads or interrupts - aren't messing with the 8139. */ -static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mmio_addr; - u32 support; - u8 cfg3, cfg5; - - support = ((rtl_chip_info[np->chipset].flags & HasLWake) - ? (WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) - : 0); - if (wol->wolopts & ~support) - return -EINVAL; - - spin_lock_irq(&np->lock); - cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); - if (wol->wolopts & WAKE_PHY) - cfg3 |= Cfg3_LinkUp; - if (wol->wolopts & WAKE_MAGIC) - cfg3 |= Cfg3_Magic; - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config3, cfg3); - RTL_W8 (Cfg9346, Cfg9346_Lock); - - cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); - /* (KON)FIXME: These are untested. We may have to set the - CRC0, Wakeup0 and LSBCRC0 registers too, but I have no - documentation. */ - if (wol->wolopts & WAKE_UCAST) - cfg5 |= Cfg5_UWF; - if (wol->wolopts & WAKE_MCAST) - cfg5 |= Cfg5_MWF; - if (wol->wolopts & WAKE_BCAST) - cfg5 |= Cfg5_BWF; - RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ - spin_unlock_irq(&np->lock); - - return 0; -} - -static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct rtl8139_private *np = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(np->pci_dev)); - info->regdump_len = np->regs_len; -} - -static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii, cmd); - spin_unlock_irq(&np->lock); - return 0; -} - -static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - int rc; - spin_lock_irq(&np->lock); - rc = mii_ethtool_sset(&np->mii, cmd); - spin_unlock_irq(&np->lock); - return rc; -} - -static int rtl8139_nway_reset(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return mii_nway_restart(&np->mii); -} - -static u32 rtl8139_get_link(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return mii_link_ok(&np->mii); -} - -static u32 rtl8139_get_msglevel(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return np->msg_enable; -} - -static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) -{ - struct rtl8139_private *np = netdev_priv(dev); - np->msg_enable = datum; -} - -/* TODO: we are too slack to do reg dumping for pio, for now */ -#ifdef CONFIG_8139TOO_PIO -#define rtl8139_get_regs_len NULL -#define rtl8139_get_regs NULL -#else -static int rtl8139_get_regs_len(struct net_device *dev) -{ - struct rtl8139_private *np = netdev_priv(dev); - return np->regs_len; -} - -static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) -{ - struct rtl8139_private *np = netdev_priv(dev); - - regs->version = RTL_REGS_VER; - - spin_lock_irq(&np->lock); - memcpy_fromio(regbuf, np->mmio_addr, regs->len); - spin_unlock_irq(&np->lock); -} -#endif /* CONFIG_8139TOO_MMIO */ - -static int rtl8139_get_stats_count(struct net_device *dev) -{ - return RTL_NUM_STATS; -} - -static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) -{ - struct rtl8139_private *np = netdev_priv(dev); - - data[0] = np->xstats.early_rx; - data[1] = np->xstats.tx_buf_mapped; - data[2] = np->xstats.tx_timeouts; - data[3] = np->xstats.rx_lost_in_ring; -} - -static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) -{ - memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); -} - -static struct ethtool_ops rtl8139_ethtool_ops = { - .get_drvinfo = rtl8139_get_drvinfo, - .get_settings = rtl8139_get_settings, - .set_settings = rtl8139_set_settings, - .get_regs_len = rtl8139_get_regs_len, - .get_regs = rtl8139_get_regs, - .nway_reset = rtl8139_nway_reset, - .get_link = rtl8139_get_link, - .get_msglevel = rtl8139_get_msglevel, - .set_msglevel = rtl8139_set_msglevel, - .get_wol = rtl8139_get_wol, - .set_wol = rtl8139_set_wol, - .get_strings = rtl8139_get_strings, - .get_stats_count = rtl8139_get_stats_count, - .get_ethtool_stats = rtl8139_get_ethtool_stats, -}; - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct rtl8139_private *np = netdev_priv(dev); - int rc; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); - spin_unlock_irq(&np->lock); - - return rc; -} - - -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned long flags; - - if (netif_running(dev)) { - spin_lock_irqsave (&tp->lock, flags); - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - spin_unlock_irqrestore (&tp->lock, flags); - } - - return &tp->stats; -} - -/* Set or clear the multicast filter for this adaptor. - This routine is not state sensitive and need not be SMP locked. */ - -static void __set_rx_mode (struct net_device *dev) -{ - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u32 mc_filter[2]; /* Multicast hash filter */ - int i, rx_mode; - u32 tmp; - - DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", - dev->name, dev->flags, RTL_R32 (RxConfig)); - - /* Note: do not reorder, GCC is clever about common statements. */ - if (dev->flags & IFF_PROMISC) { - /* Unconditionally log net taps. */ - printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", - dev->name); - rx_mode = - AcceptBroadcast | AcceptMulticast | AcceptMyPhys | - AcceptAllPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else { - struct dev_mc_list *mclist; - rx_mode = AcceptBroadcast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0; - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - rx_mode |= AcceptMulticast; - } - } - - /* We can safely update without stopping the chip. */ - tmp = rtl8139_rx_config | rx_mode; - if (tp->rx_config != tmp) { - RTL_W32_F (RxConfig, tmp); - tp->rx_config = tmp; - } - RTL_W32_F (MAR0 + 0, mc_filter[0]); - RTL_W32_F (MAR0 + 4, mc_filter[1]); -} - -static void rtl8139_set_rx_mode (struct net_device *dev) -{ - unsigned long flags; - struct rtl8139_private *tp = netdev_priv(dev); - - spin_lock_irqsave (&tp->lock, flags); - __set_rx_mode(dev); - spin_unlock_irqrestore (&tp->lock, flags); -} - -#ifdef CONFIG_PM - -static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct rtl8139_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned long flags; - - pci_save_state (pdev); - - if (!netif_running (dev)) - return 0; - - netif_device_detach (dev); - - spin_lock_irqsave (&tp->lock, flags); - - /* Disable interrupts, stop Tx and Rx. */ - RTL_W16 (IntrMask, 0); - RTL_W8 (ChipCmd, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - - pci_set_power_state (pdev, PCI_D3hot); - - return 0; -} - - -static int rtl8139_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - - pci_restore_state (pdev); - if (!netif_running (dev)) - return 0; - pci_set_power_state (pdev, PCI_D0); - rtl8139_init_ring (dev); - rtl8139_hw_start (dev); - netif_device_attach (dev); - return 0; -} - -#endif /* CONFIG_PM */ - - -static struct pci_driver rtl8139_pci_driver = { - .name = DRV_NAME, - .id_table = rtl8139_pci_tbl, - .probe = rtl8139_init_one, - .remove = __devexit_p(rtl8139_remove_one), -#ifdef CONFIG_PM - .suspend = rtl8139_suspend, - .resume = rtl8139_resume, -#endif /* CONFIG_PM */ -}; - - -static int __init rtl8139_init_module (void) -{ - /* when we're a module, we always print a version message, - * even if no 8139 board is found. - */ -#ifdef MODULE - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); -#endif - - return pci_module_init (&rtl8139_pci_driver); -} - - -static void __exit rtl8139_cleanup_module (void) -{ - pci_unregister_driver (&rtl8139_pci_driver); -} - - -module_init(rtl8139_init_module); -module_exit(rtl8139_cleanup_module); diff -r 6b3b8acb71b5 -r 7506e67dd122 include/EtherCAT_dev.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/EtherCAT_dev.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * Oeffentliche EtherCAT-Schnittstellen fuer EtherCAT-Geraetetreiber. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _ETHERCAT_DEVICE_H_ +#define _ETHERCAT_DEVICE_H_ + +#include + +/*****************************************************************************/ + +struct ec_device; + +typedef struct ec_device ec_device_t; + +/*****************************************************************************/ + +typedef enum +{ + EC_DEVICE_STATE_READY, EC_DEVICE_STATE_SENT, EC_DEVICE_STATE_RECEIVED, + EC_DEVICE_STATE_TIMEOUT, EC_DEVICE_STATE_ERROR +} +ec_device_state_t; + +/*****************************************************************************/ + +ec_device_t *EtherCAT_dev_register(unsigned int, struct net_device *, + irqreturn_t (*)(int, void *, + struct pt_regs *), + struct module *); +void EtherCAT_dev_unregister(unsigned int); +int EtherCAT_dev_is_ec(ec_device_t *, struct net_device *); +void EtherCAT_dev_state(ec_device_t *, ec_device_state_t); +int EtherCAT_dev_receive(ec_device_t *, void *, unsigned int); + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 include/EtherCAT_rt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/EtherCAT_rt.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,36 @@ +/****************************************************************************** + * + * Oeffentliche EtherCAT-Schnittstellen fuer Echtzeitprozesse. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _ETHERCAT_RT_H_ +#define _ETHERCAT_RT_H_ + +/*****************************************************************************/ + +struct ec_master; +typedef struct ec_master ec_master_t; + +/*****************************************************************************/ + +ec_master_t *EtherCAT_rt_request_master(unsigned int master_index); + +void EtherCAT_rt_release_master(ec_master_t *master); + +void *EtherCAT_rt_register_slave(ec_master_t *master, unsigned int slave_index, + const char *vendor_name, + const char *product_name); + +int EtherCAT_rt_activate_slaves(ec_master_t *master); + +int EtherCAT_rt_deactivate_slaves(ec_master_t *master); + +int EtherCAT_rt_domain_cycle(ec_master_t *master, unsigned int domain, + unsigned int timeout_us); + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 master/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/Makefile Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------------ +# +# Makefile +# +# IgH EtherCAT-Treiber +# +# $Id$ +# +#------------------------------------------------------------------------------ + +ifneq ($(KERNELRELEASE),) + +#------------------------------------------------------------------------------ +# Kbuild-Abschnitt + +obj-m := ec_master.o + +ec_master-objs := module.o master.o device.o slave.o command.o types.o domain.o + +REV = `svnversion $(src)` +DATE = `date` + +EXTRA_CFLAGS = -DEC_REV="$(REV)" -DEC_USER="$(USER)" -DEC_DATE="$(DATE)" + +#------------------------------------------------------------------------------ + +else + +#------------------------------------------------------------------------------ +# Default-Abschnitt + +ifneq ($(wildcard ethercat.conf),) +include ethercat.conf +else +KERNELDIR = /lib/modules/`uname -r`/build +endif + +modules: + $(MAKE) -C $(KERNELDIR) M=`pwd` modules + +clean: + $(MAKE) -C $(KERNELDIR) M=`pwd` clean + +#------------------------------------------------------------------------------ + +endif diff -r 6b3b8acb71b5 -r 7506e67dd122 master/command.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/command.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,250 @@ +/****************************************************************************** + * + * c o m m a n d . c + * + * Methoden für ein EtherCAT-Kommando. + * + * $Id$ + * + *****************************************************************************/ + +#include + +#include "command.h" + +/*****************************************************************************/ + +/** + Kommando-Konstruktor. + + Initialisiert alle Variablen innerhalb des Kommandos auf die + Default-Werte. + + @param cmd Zeiger auf das zu initialisierende Kommando. +*/ + +void ec_command_init(ec_command_t *cmd) +{ + cmd->type = EC_COMMAND_NONE; + cmd->address.logical = 0x00000000; + cmd->data_length = 0; + cmd->state = EC_COMMAND_STATE_READY; + cmd->index = 0; + cmd->working_counter = 0; +} + +/*****************************************************************************/ + +/** + Kommando-Destruktor. + + Setzt alle Attribute auf den Anfangswert zurueck. + + @param cmd Zeiger auf das zu initialisierende Kommando. +*/ + +void ec_command_clear(ec_command_t *cmd) +{ + ec_command_init(cmd); +} + +/*****************************************************************************/ + +#define EC_FUNC_HEADER \ + ec_command_init(cmd) + +#define EC_FUNC_WRITE_FOOTER \ + cmd->data_length = length; \ + memcpy(cmd->data, data, length); + +#define EC_FUNC_READ_FOOTER \ + cmd->data_length = length; + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-NPRD-Kommando. + + @param cmd Zeiger auf das Kommando + @param node_address Adresse des Knotens (Slaves) + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten +*/ + +void ec_command_read(ec_command_t *cmd, unsigned short node_address, + unsigned short offset, unsigned int length) +{ + if (unlikely(node_address == 0x0000)) + printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n"); + + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_NPRD; + cmd->address.phy.dev.node = node_address; + cmd->address.phy.mem = offset; + + EC_FUNC_READ_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-NPWR-Kommando. + + Alloziert ein "node-adressed physical write"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param node_address Adresse des Knotens (Slaves) + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten +*/ + +void ec_command_write(ec_command_t *cmd, unsigned short node_address, + unsigned short offset, unsigned int length, + const unsigned char *data) +{ + if (unlikely(node_address == 0x0000)) + printk(KERN_WARNING "EtherCAT: Warning - Using node address 0x0000!\n"); + + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_NPWR; + cmd->address.phy.dev.node = node_address; + cmd->address.phy.mem = offset; + + EC_FUNC_WRITE_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-APRD-Kommando. + + Alloziert ein "autoincerement physical read"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param ring_position (Negative) Position des Slaves im Bus + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten +*/ + +void ec_command_position_read(ec_command_t *cmd, short ring_position, + unsigned short offset, unsigned int length) +{ + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_APRD; + cmd->address.phy.dev.pos = ring_position; + cmd->address.phy.mem = offset; + + EC_FUNC_READ_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-APWR-Kommando. + + Alloziert ein "autoincrement physical write"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param ring_position (Negative) Position des Slaves im Bus + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten +*/ + +void ec_command_position_write(ec_command_t *cmd, short ring_position, + unsigned short offset, unsigned int length, + const unsigned char *data) +{ + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_APWR; + cmd->address.phy.dev.pos = ring_position; + cmd->address.phy.mem = offset; + + EC_FUNC_WRITE_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-BRD-Kommando. + + Alloziert ein "broadcast read"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu lesenden Daten +*/ + +void ec_command_broadcast_read(ec_command_t *cmd, unsigned short offset, + unsigned int length) +{ + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_BRD; + cmd->address.phy.dev.node = 0x0000; + cmd->address.phy.mem = offset; + + EC_FUNC_READ_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-BWR-Kommando. + + Alloziert ein "broadcast write"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param offset Physikalische Speicheradresse im Slave + @param length Länge der zu schreibenden Daten + @param data Zeiger auf Speicher mit zu schreibenden Daten +*/ + +void ec_command_broadcast_write(ec_command_t *cmd, unsigned short offset, + unsigned int length, const unsigned char *data) +{ + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_BWR; + cmd->address.phy.dev.node = 0x0000; + cmd->address.phy.mem = offset; + + EC_FUNC_WRITE_FOOTER; +} + +/*****************************************************************************/ + +/** + Initialisiert ein EtherCAT-LRW-Kommando. + + Alloziert ein "logical read write"-Kommando + und fügt es in die Liste des Masters ein. + + @param cmd Zeiger auf das Kommando + @param offset Logische Speicheradresse + @param length Länge der zu lesenden/schreibenden Daten + @param data Zeiger auf Speicher mit zu lesenden/schreibenden Daten +*/ + +void ec_command_logical_read_write(ec_command_t *cmd, unsigned int offset, + unsigned int length, unsigned char *data) +{ + EC_FUNC_HEADER; + + cmd->type = EC_COMMAND_LRW; + cmd->address.logical = offset; + + EC_FUNC_WRITE_FOOTER; +} + +/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/command.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/command.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,103 @@ +/****************************************************************************** + * + * c o m m a n d . h + * + * Struktur für ein EtherCAT-Kommando. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_COMMAND_H_ +#define _EC_COMMAND_H_ + +#include "globals.h" + +/*****************************************************************************/ + +/** + Status eines EtherCAT-Kommandos. +*/ + +typedef enum +{ + EC_COMMAND_STATE_READY, EC_COMMAND_STATE_SENT, EC_COMMAND_STATE_RECEIVED +} +ec_command_state_t; + +/*****************************************************************************/ + +/** + EtherCAT-Adresse. + + Im EtherCAT-Rahmen sind 4 Bytes für die Adresse reserviert, die + ja nach Kommandoty eine andere bedeutung haben: Bei Autoinkrement- + befehlen sind die ersten zwei Bytes die (negative) + Autoinkrement-Adresse, bei Knoten-adressierten Befehlen entsprechen + sie der Knotenadresse. Das dritte und vierte Byte entspricht in + diesen Fällen der physikalischen Speicheradresse auf dem Slave. + Bei einer logischen Adressierung entsprechen alle vier Bytes + der logischen Adresse. +*/ + +typedef union +{ + struct + { + union + { + short pos; /**< (Negative) Ring-Position des Slaves */ + unsigned short node; /**< Konfigurierte Knotenadresse */ + } + dev; + + unsigned short mem; /**< Physikalische Speicheradresse im Slave */ + } + phy; + + unsigned long logical; /**< Logische Adresse */ + unsigned char raw[4]; /**< Rohdaten für die Generierung des Frames */ +} +ec_address_t; + +/*****************************************************************************/ + +/** + EtherCAT-Kommando. +*/ + +typedef struct ec_command +{ + ec_command_type_t type; /**< Typ des Kommandos (APRD, NPWR, etc...) */ + ec_address_t address; /**< Adresse des/der Empfänger */ + unsigned int data_length; /**< Länge der zu sendenden und/oder + empfangenen Daten */ + ec_command_state_t state; /**< Zustand des Kommandos + (bereit, gesendet, etc...) */ + unsigned char index; /**< Kommando-Index, mit der das Kommando gesendet + wurde (wird vom Master beim Senden gesetzt. */ + unsigned int working_counter; /**< Working-Counter bei Empfang (wird + vom Master gesetzt) */ + unsigned char data[EC_FRAME_SIZE]; /**< Kommandodaten */ +} +ec_command_t; + +/*****************************************************************************/ + +void ec_command_read(ec_command_t *, unsigned short, unsigned short, + unsigned int); +void ec_command_write(ec_command_t *, unsigned short, unsigned short, + unsigned int, const unsigned char *); +void ec_command_position_read(ec_command_t *, short, unsigned short, + unsigned int); +void ec_command_position_write(ec_command_t *, short, unsigned short, + unsigned int, const unsigned char *); +void ec_command_broadcast_read(ec_command_t *, unsigned short, unsigned int); +void ec_command_broadcast_write(ec_command_t *, unsigned short, unsigned int, + const unsigned char *); +void ec_command_logical_read_write(ec_command_t *, unsigned int, unsigned int, + unsigned char *); + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 master/device.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/device.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,383 @@ +/****************************************************************************** + * + * d e v i c e . c + * + * Methoden für ein EtherCAT-Gerät. + * + * $Id$ + * + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "device.h" + +/*****************************************************************************/ + +/** + EtherCAT-Geräte-Konstuktor. + + Initialisiert ein EtherCAT-Gerät, indem es die Variablen + in der Struktur auf die Default-Werte setzt. + + @param ecd Zu initialisierendes EtherCAT-Gerät +*/ + +int ec_device_init(ec_device_t *ecd) +{ + ecd->dev = NULL; + ecd->open = 0; + ecd->tx_time = 0; + ecd->rx_time = 0; + ecd->tx_intr_cnt = 0; + ecd->rx_intr_cnt = 0; + ecd->intr_cnt = 0; + ecd->state = EC_DEVICE_STATE_READY; + ecd->rx_data_length = 0; + ecd->isr = NULL; + ecd->module = NULL; + ecd->error_reported = 0; + + if ((ecd->tx_skb = dev_alloc_skb(EC_FRAME_SIZE)) == NULL) { + printk(KERN_ERR "EtherCAT: Could not allocate device tx socket buffer!\n"); + return -1; + } + + if ((ecd->rx_skb = dev_alloc_skb(EC_FRAME_SIZE)) == NULL) { + dev_kfree_skb(ecd->tx_skb); + ecd->tx_skb = NULL; + printk(KERN_ERR "EtherCAT: Could not allocate device rx socket buffer!\n"); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + EtherCAT-Geräte-Destuktor. + + Gibt den dynamisch allozierten Speicher des + EtherCAT-Gerätes (die beiden Socket-Buffer) wieder frei. + + @param ecd EtherCAT-Gerät +*/ + +void ec_device_clear(ec_device_t *ecd) +{ + if (ecd->open) ec_device_close(ecd); + + ecd->dev = NULL; + + if (ecd->tx_skb) { + dev_kfree_skb(ecd->tx_skb); + ecd->tx_skb = NULL; + } + + if (ecd->rx_skb) { + dev_kfree_skb(ecd->rx_skb); + ecd->rx_skb = NULL; + } +} + +/*****************************************************************************/ + +/** + Führt die open()-Funktion des Netzwerktreibers aus. + + Dies entspricht einem "ifconfig up". Vorher wird der Zeiger + auf das EtherCAT-Gerät auf Gültigkeit geprüft und der + Gerätezustand zurückgesetzt. + + @param ecd EtherCAT-Gerät + + @return 0 bei Erfolg, < 0: Ungültiger Zeiger, oder open() + fehlgeschlagen +*/ + +int ec_device_open(ec_device_t *ecd) +{ + unsigned int i; + + if (!ecd) { + printk(KERN_ERR "EtherCAT: Trying to open a NULL device!\n"); + return -1; + } + + if (!ecd->dev) { + printk(KERN_ERR "EtherCAT: No net_device to open!\n"); + return -1; + } + + if (ecd->open) { + printk(KERN_WARNING "EtherCAT: Device already opened!\n"); + } + else { + // Device could have received frames before + for (i = 0; i < 4; i++) ec_device_call_isr(ecd); + + // Reset old device state + ecd->state = EC_DEVICE_STATE_READY; + ecd->tx_intr_cnt = 0; + ecd->rx_intr_cnt = 0; + + if (ecd->dev->open(ecd->dev) == 0) ecd->open = 1; + } + + return ecd->open ? 0 : -1; +} + +/*****************************************************************************/ + +/** + Führt die stop()-Funktion des net_devices aus. + + @param ecd EtherCAT-Gerät + + @return 0 bei Erfolg, < 0: Kein Gerät zum Schliessen oder + Schliessen fehlgeschlagen. +*/ + +int ec_device_close(ec_device_t *ecd) +{ + if (!ecd->dev) { + printk(KERN_ERR "EtherCAT: No device to close!\n"); + return -1; + } + + if (!ecd->open) { + printk(KERN_WARNING "EtherCAT: Device already closed!\n"); + } + else { + printk(KERN_INFO "EtherCAT: Stopping device (txcnt: %u, rxcnt: %u)\n", + (unsigned int) ecd->tx_intr_cnt, (unsigned int) ecd->rx_intr_cnt); + + if (ecd->dev->stop(ecd->dev) == 0) ecd->open = 0; + } + + return !ecd->open ? 0 : -1; +} + +/*****************************************************************************/ + +/** + Sendet einen Rahmen über das EtherCAT-Gerät. + + Kopiert die zu sendenden Daten in den statischen Socket- + Buffer, fügt den Ethernat-II-Header hinzu und ruft die + start_xmit()-Funktion der Netzwerkkarte auf. + + @param ecd EtherCAT-Gerät + @param data Zeiger auf die zu sendenden Daten + @param length Länge der zu sendenden Daten + + @return 0 bei Erfolg, < 0: Vorheriger Rahmen noch + nicht empfangen, oder kein Speicher mehr vorhanden +*/ + +int ec_device_send(ec_device_t *ecd, unsigned char *data, unsigned int length) +{ + unsigned char *frame_data; + struct ethhdr *eth; + + if (unlikely(ecd->state == EC_DEVICE_STATE_SENT)) { + printk(KERN_WARNING "EtherCAT: Warning - Trying to send frame while last " + " was not received!\n"); + } + + // Clear transmit socket buffer and reserve + // space for Ethernet-II header + skb_trim(ecd->tx_skb, 0); + skb_reserve(ecd->tx_skb, ETH_HLEN); + + // Copy data to socket buffer + frame_data = skb_put(ecd->tx_skb, length); + memcpy(frame_data, data, length); + + // Add Ethernet-II-Header + if (unlikely((eth = (struct ethhdr *) + skb_push(ecd->tx_skb, ETH_HLEN)) == NULL)) { + printk(KERN_ERR "EtherCAT: device_send -" + " Could not allocate Ethernet-II header!\n"); + return -1; + } + + // Protocol type + eth->h_proto = htons(0x88A4); + // Hardware address + memcpy(eth->h_source, ecd->dev->dev_addr, ecd->dev->addr_len); + // Broadcast address + memset(eth->h_dest, 0xFF, ecd->dev->addr_len); + + rdtscl(ecd->tx_time); // Get CPU cycles + + // Start sending of frame + ecd->state = EC_DEVICE_STATE_SENT; + ecd->dev->hard_start_xmit(ecd->tx_skb, ecd->dev); + + return 0; +} + +/*****************************************************************************/ + +/** + Holt einen empfangenen Rahmen von der Netzwerkkarte. + + Zuerst wird geprüft, ob überhaupt ein Rahmen empfangen + wurde. Wenn ja, wird dieser in den angegebenen + Speicherbereich kopiert. + + @param ecd EtherCAT-Gerät + @param data Zeiger auf den Speicherbereich, in den die + empfangenen Daten kopiert werden sollen + + @return Anzahl der kopierten Bytes bei Erfolg, sonst < 0 +*/ + +int ec_device_receive(ec_device_t *ecd, unsigned char *data) +{ + if (unlikely(ecd->state != EC_DEVICE_STATE_RECEIVED)) { + if (likely(ecd->error_reported)) { + printk(KERN_ERR "EtherCAT: receive - Nothing received!\n"); + ecd->error_reported = 1; + } + return -1; + } + + if (unlikely(ecd->rx_data_length > EC_FRAME_SIZE)) { + if (likely(ecd->error_reported)) { + printk(KERN_ERR "EtherCAT: receive - " + " Reveived frame is too long (%i Bytes)!\n", + ecd->rx_data_length); + ecd->error_reported = 1; + } + return -1; + } + + if (unlikely(ecd->error_reported)) { + ecd->error_reported = 0; + } + + memcpy(data, ecd->rx_data, ecd->rx_data_length); + + return ecd->rx_data_length; +} + +/*****************************************************************************/ + +/** + Ruft die Interrupt-Routine der Netzwerkkarte auf. + + @param ecd EtherCAT-Gerät + + @return Anzahl der kopierten Bytes bei Erfolg, sonst < 0 +*/ + +void ec_device_call_isr(ec_device_t *ecd) +{ + if (likely(ecd->isr)) ecd->isr(0, ecd->dev, NULL); +} + +/*****************************************************************************/ + +/** + Gibt alle Informationen über das Device-Objekt aus. + + @param ecd EtherCAT-Gerät +*/ + +void ec_device_debug(ec_device_t *ecd) +{ + printk(KERN_DEBUG "---EtherCAT device information begin---\n"); + + if (ecd) + { + printk(KERN_DEBUG "Assigned net_device: %X\n", + (unsigned) ecd->dev); + printk(KERN_DEBUG "Transmit socket buffer: %X\n", + (unsigned) ecd->tx_skb); + printk(KERN_DEBUG "Receive socket buffer: %X\n", + (unsigned) ecd->rx_skb); + printk(KERN_DEBUG "Time of last transmission: %u\n", + (unsigned) ecd->tx_time); + printk(KERN_DEBUG "Time of last receive: %u\n", + (unsigned) ecd->rx_time); + printk(KERN_DEBUG "Number of transmit interrupts: %u\n", + (unsigned) ecd->tx_intr_cnt); + printk(KERN_DEBUG "Number of receive interrupts: %u\n", + (unsigned) ecd->rx_intr_cnt); + printk(KERN_DEBUG "Total Number of interrupts: %u\n", + (unsigned) ecd->intr_cnt); + printk(KERN_DEBUG "Actual device state: %i\n", + (int) ecd->state); + printk(KERN_DEBUG "Receive buffer: %X\n", + (unsigned) ecd->rx_data); + printk(KERN_DEBUG "Receive buffer fill state: %u/%u\n", + (unsigned) ecd->rx_data_length, EC_FRAME_SIZE); + } + else + { + printk(KERN_DEBUG "Device is NULL!\n"); + } + + printk(KERN_DEBUG "---EtherCAT device information end---\n"); +} + +/****************************************************************************** + * + * Treiberschnittstelle + * + *****************************************************************************/ + +void EtherCAT_dev_state(ec_device_t *ecd, ec_device_state_t state) +{ + if (state == EC_DEVICE_STATE_TIMEOUT && ecd->state != EC_DEVICE_STATE_SENT) { + printk(KERN_WARNING "EtherCAT: Wrong status at timeout: %i\n", ecd->state); + } + + ecd->state = state; +} + +/*****************************************************************************/ + +int EtherCAT_dev_is_ec(ec_device_t *ecd, struct net_device *dev) +{ + return ecd->dev == dev; +} + +/*****************************************************************************/ + +int EtherCAT_dev_receive(ec_device_t *ecd, void *data, unsigned int size) +{ + if (ecd->state != EC_DEVICE_STATE_SENT) + { + printk(KERN_WARNING "EtherCAT: Received frame while not in SENT state!\n"); + return -1; + } + + // Copy received data to ethercat-device buffer, skip Ethernet-II header + memcpy(ecd->rx_data, data, size); + ecd->rx_data_length = size; + ecd->state = EC_DEVICE_STATE_RECEIVED; + + return 0; +} + +/*****************************************************************************/ + +EXPORT_SYMBOL(EtherCAT_dev_is_ec); +EXPORT_SYMBOL(EtherCAT_dev_state); +EXPORT_SYMBOL(EtherCAT_dev_receive); + +/*****************************************************************************/ + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/device.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/device.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,65 @@ +/****************************************************************************** + * + * d e v i c e . h + * + * Struktur für ein EtherCAT-Gerät. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_DEVICE_H_ +#define _EC_DEVICE_H_ + +#include + +#include "globals.h" +#include "../include/EtherCAT_dev.h" + +/*****************************************************************************/ + +/** + EtherCAT-Gerät. + + Ein EtherCAT-Gerät ist eine Netzwerkkarte, die vom + EtherCAT-Master dazu verwendet wird, um Frames zu senden + und zu empfangen. +*/ + +struct ec_device +{ + struct net_device *dev; /**< Zeiger auf das reservierte net_device */ + unsigned int open; /**< Das net_device ist geoeffnet. */ + struct sk_buff *tx_skb; /**< Zeiger auf Transmit-Socketbuffer */ + struct sk_buff *rx_skb; /**< Zeiger auf Receive-Socketbuffer */ + unsigned long tx_time; /**< Zeit des letzten Sendens */ + unsigned long rx_time; /**< Zeit des letzten Empfangs */ + unsigned long tx_intr_cnt; /**< Anzahl Tx-Interrupts */ + unsigned long rx_intr_cnt; /**< Anzahl Rx-Interrupts */ + unsigned long intr_cnt; /**< Anzahl Interrupts */ + volatile ec_device_state_t state; /**< Gesendet, Empfangen, + Timeout, etc. */ + unsigned char rx_data[EC_FRAME_SIZE]; /**< Puffer für + empfangene Rahmen */ + volatile unsigned int rx_data_length; /**< Länge des zuletzt + empfangenen Rahmens */ + irqreturn_t (*isr)(int, void *, struct pt_regs *); /**< Adresse der ISR */ + struct module *module; /**< Zeiger auf das Modul, das das Gerät zur + Verfügung stellt. */ + int error_reported; /**< Zeigt an, ob ein Fehler im zyklischen Code + bereits gemeldet wurde. */ +}; + +/*****************************************************************************/ + +int ec_device_init(ec_device_t *); +void ec_device_clear(ec_device_t *); +int ec_device_open(ec_device_t *); +int ec_device_close(ec_device_t *); +void ec_device_call_isr(ec_device_t *); +int ec_device_send(ec_device_t *, unsigned char *, unsigned int); +int ec_device_receive(ec_device_t *, unsigned char *); + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 master/domain.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/domain.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,40 @@ +/****************************************************************************** + * + * d o m a i n . c + * + * Methoden für Gruppen von EtherCAT-Slaves. + * + * $Id$ + * + *****************************************************************************/ + +#include + +#include "globals.h" +#include "domain.h" + +/*****************************************************************************/ + +/** + Konstruktor einer EtherCAT-Domäne. + + @param pd Zeiger auf die zu initialisierende Domäne +*/ + +void ec_domain_init(ec_domain_t *dom) +{ + dom->number = 0; + dom->data_size = 0; + dom->logical_offset = 0; + dom->response_count = 0; + + memset(dom->data, 0x00, EC_FRAME_SIZE); +} + +/*****************************************************************************/ + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/domain.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/domain.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,51 @@ +/****************************************************************************** + * + * d o m a i n . h + * + * Struktur für eine Gruppe von EtherCAT-Slaves. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_DOMAIN_H_ +#define _EC_DOMAIN_H_ + +#include "globals.h" +#include "slave.h" +#include "command.h" + +/*****************************************************************************/ + +/** + EtherCAT-Domäne + + Verwaltet die Prozessdaten und das hierfür nötige Kommando einer bestimmten + Menge von Slaves. +*/ + +typedef struct ec_domain +{ + unsigned int number; /*<< Domänen-Identifikation */ + ec_command_t command; /**< Kommando zum Senden und Empfangen der + Prozessdaten */ + unsigned char data[EC_FRAME_SIZE]; /**< Prozessdaten-Array */ + unsigned int data_size; /**< Größe der Prozessdaten */ + unsigned int logical_offset; /**< Logische Basisaddresse */ + unsigned int response_count; /**< Anzahl antwortender Slaves */ +} +ec_domain_t; + +/*****************************************************************************/ + +void ec_domain_init(ec_domain_t *); + +/*****************************************************************************/ + +#endif + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/globals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/globals.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * g l o b a l s . h + * + * Globale Definitionen und Makros für das EtherCAT-Protokoll. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_GLOBALS_ +#define _EC_GLOBALS_ + +/*****************************************************************************/ + +/** + Maximale Größe eines EtherCAT-Frames +*/ +#define EC_FRAME_SIZE 1500 + +/** + Maximale Anzahl der Prozessdatendomänen in einem Master +*/ +#define EC_MAX_DOMAINS 10 + +/** + NULL-Define, falls noch nicht definiert. +*/ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +/*****************************************************************************/ + +/** + EtherCAT-Kommando-Typ +*/ + +typedef enum +{ + EC_COMMAND_NONE = 0x00, /**< Dummy */ + EC_COMMAND_APRD = 0x01, /**< Auto-increment physical read */ + EC_COMMAND_APWR = 0x02, /**< Auto-increment physical write */ + EC_COMMAND_NPRD = 0x04, /**< Node-addressed physical read */ + EC_COMMAND_NPWR = 0x05, /**< Node-addressed physical write */ + EC_COMMAND_BRD = 0x07, /**< Broadcast read */ + EC_COMMAND_BWR = 0x08, /**< Broadcast write */ + EC_COMMAND_LRW = 0x0C /**< Logical read/write */ +} +ec_command_type_t; + +/*****************************************************************************/ + +/** + Zustand eines EtherCAT-Slaves +*/ + +typedef enum +{ + EC_SLAVE_STATE_UNKNOWN = 0x00, /**< Status unbekannt */ + EC_SLAVE_STATE_INIT = 0x01, /**< Init-Zustand (Keine Mailbox- + Kommunikation, Kein I/O) */ + EC_SLAVE_STATE_PREOP = 0x02, /**< Pre-Operational (Mailbox- + Kommunikation, Kein I/O) */ + EC_SLAVE_STATE_SAVEOP = 0x04, /**< Save-Operational (Mailbox- + Kommunikation und Input Update) */ + EC_SLAVE_STATE_OP = 0x08, /**< Operational, (Mailbox- + Kommunikation und Input/Output Update) */ + EC_ACK = 0x10 /**< Acknoledge-Bit beim Zustandswechsel + (dies ist kein eigener Zustand) */ +} +ec_slave_state_t; + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 master/master.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/master.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,1114 @@ +/****************************************************************************** + * + * m a s t e r . c + * + * Methoden für einen EtherCAT-Master. + * + * $Id$ + * + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "globals.h" +#include "master.h" +#include "device.h" +#include "command.h" + +/*****************************************************************************/ + +// Prototypen + +int ec_simple_send(ec_master_t *, ec_command_t *); +int ec_simple_receive(ec_master_t *, ec_command_t *); +void ec_output_debug_data(const ec_master_t *); +int ec_read_slave_information(ec_master_t *, unsigned short, unsigned short, + unsigned int *); +void ec_output_lost_frames(ec_master_t *); + +/*****************************************************************************/ + +/** + Konstruktor des EtherCAT-Masters. + + @param master Zeiger auf den zu initialisierenden EtherCAT-Master +*/ + +void ec_master_init(ec_master_t *master) +{ + master->bus_slaves = NULL; + master->bus_slaves_count = 0; + master->device_registered = 0; + master->command_index = 0x00; + master->tx_data_length = 0; + master->rx_data_length = 0; + master->domain_count = 0; + master->debug_level = 0; + master->bus_time = 0; + master->frames_lost = 0; + master->t_lost_output = 0; +} + +/*****************************************************************************/ + +/** + Destruktor eines EtherCAT-Masters. + + Entfernt alle Kommandos aus der Liste, löscht den Zeiger + auf das Slave-Array und gibt die Prozessdaten frei. + + @param master Zeiger auf den zu löschenden Master +*/ + +void ec_master_clear(ec_master_t *master) +{ + if (master->bus_slaves) { + kfree(master->bus_slaves); + master->bus_slaves = NULL; + } + + ec_device_clear(&master->device); + + master->domain_count = 0; +} + +/*****************************************************************************/ + +/** + Öffnet das EtherCAT-Geraet des Masters. + + @param master Der EtherCAT-Master + + @return 0, wenn alles o.k., < 0, wenn das Geraet nicht geoeffnet werden + konnte. +*/ + +int ec_master_open(ec_master_t *master) +{ + if (!master->device_registered) { + printk(KERN_ERR "EtherCAT: No device registered!\n"); + return -1; + } + + if (ec_device_open(&master->device) < 0) { + printk(KERN_ERR "EtherCAT: Could not open device!\n"); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + Schliesst das EtherCAT-Geraet, auf dem der Master arbeitet. + + @param master Der EtherCAT-Master + @param device Das EtherCAT-Geraet +*/ + +void ec_master_close(ec_master_t *master) +{ + if (!master->device_registered) { + printk(KERN_WARNING "EtherCAT: Warning -" + " Trying to close an unregistered device!\n"); + return; + } + + if (ec_device_close(&master->device) < 0) { + printk(KERN_WARNING "EtherCAT: Warning - Could not close device!\n"); + } +} + +/*****************************************************************************/ + +/** + Sendet ein einzelnes Kommando in einem Frame und + wartet auf dessen Empfang. + + @param master EtherCAT-Master + @param cmd Kommando zum Senden/Empfangen + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_simple_send_receive(ec_master_t *master, ec_command_t *cmd) +{ + unsigned int tries_left; + + if (unlikely(ec_simple_send(master, cmd) < 0)) + return -1; + + tries_left = 20; + + do + { + udelay(1); + ec_device_call_isr(&master->device); + tries_left--; + } + while (unlikely(master->device.state == EC_DEVICE_STATE_SENT && tries_left)); + + if (unlikely(ec_simple_receive(master, cmd) < 0)) + return -1; + + return 0; +} + +/*****************************************************************************/ + +/** + Sendet ein einzelnes Kommando in einem Frame. + + @param master EtherCAT-Master + @param cmd Kommando zum Senden + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_simple_send(ec_master_t *master, ec_command_t *cmd) +{ + unsigned int length, framelength, i; + + if (unlikely(master->debug_level > 0)) { + printk(KERN_DEBUG "EtherCAT: ec_simple_send\n"); + } + + if (unlikely(cmd->state != EC_COMMAND_STATE_READY)) { + printk(KERN_WARNING "EtherCAT: cmd not in ready state!\n"); + } + + length = cmd->data_length + 12; + framelength = length + 2; + + if (unlikely(framelength > EC_FRAME_SIZE)) { + printk(KERN_ERR "EtherCAT: Frame too long (%i)!\n", framelength); + return -1; + } + + if (framelength < 46) framelength = 46; + + if (unlikely(master->debug_level > 0)) { + printk(KERN_DEBUG "EtherCAT: Frame length: %i\n", framelength); + } + + master->tx_data[0] = length & 0xFF; + master->tx_data[1] = ((length & 0x700) >> 8) | 0x10; + + cmd->index = master->command_index; + master->command_index = (master->command_index + 1) % 0x0100; + + if (unlikely(master->debug_level > 0)) { + printk(KERN_DEBUG "EtherCAT: Sending command index %i\n", cmd->index); + } + + cmd->state = EC_COMMAND_STATE_SENT; + + master->tx_data[2 + 0] = cmd->type; + master->tx_data[2 + 1] = cmd->index; + master->tx_data[2 + 2] = cmd->address.raw[0]; + master->tx_data[2 + 3] = cmd->address.raw[1]; + master->tx_data[2 + 4] = cmd->address.raw[2]; + master->tx_data[2 + 5] = cmd->address.raw[3]; + master->tx_data[2 + 6] = cmd->data_length & 0xFF; + master->tx_data[2 + 7] = (cmd->data_length & 0x700) >> 8; + master->tx_data[2 + 8] = 0x00; + master->tx_data[2 + 9] = 0x00; + + if (likely(cmd->type == EC_COMMAND_APWR + || cmd->type == EC_COMMAND_NPWR + || cmd->type == EC_COMMAND_BWR + || cmd->type == EC_COMMAND_LRW)) // Write commands + { + for (i = 0; i < cmd->data_length; i++) + master->tx_data[2 + 10 + i] = cmd->data[i]; + } + else // Read commands + { + for (i = 0; i < cmd->data_length; i++) master->tx_data[2 + 10 + i] = 0x00; + } + + master->tx_data[2 + 10 + cmd->data_length] = 0x00; + master->tx_data[2 + 11 + cmd->data_length] = 0x00; + + // Pad with zeros + for (i = cmd->data_length + 12 + 2; i < 46; i++) master->tx_data[i] = 0x00; + + master->tx_data_length = framelength; + + if (unlikely(master->debug_level > 0)) { + printk(KERN_DEBUG "EtherCAT: Device send...\n"); + } + + // Send frame + if (unlikely(ec_device_send(&master->device, master->tx_data, + framelength) != 0)) { + printk(KERN_ERR "EtherCAT: Could not send!\n"); + return -1; + } + + if (unlikely(master->debug_level > 0)) { + printk(KERN_DEBUG "EtherCAT: ec_simple_send done.\n"); + } + + return 0; +} + +/*****************************************************************************/ + +/** + Wartet auf den Empfang eines einzeln gesendeten + Kommandos. + + @param master EtherCAT-Master + @param cmd Gesendetes Kommando + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_simple_receive(ec_master_t *master, ec_command_t *cmd) +{ + unsigned int length; + int ret; + unsigned char command_type, command_index; + + if (unlikely((ret = ec_device_receive(&master->device, master->rx_data)) < 0)) + return -1; + + master->rx_data_length = (unsigned int) ret; + + if (unlikely(master->rx_data_length < 2)) { + printk(KERN_ERR "EtherCAT: Received frame with incomplete EtherCAT" + " header!\n"); + ec_output_debug_data(master); + return -1; + } + + // Länge des gesamten Frames prüfen + length = ((master->rx_data[1] & 0x07) << 8) + | (master->rx_data[0] & 0xFF); + + if (unlikely(length > master->rx_data_length)) { + printk(KERN_ERR "EtherCAT: Received corrupted frame (length does" + " not match)!\n"); + ec_output_debug_data(master); + return -1; + } + + command_type = master->rx_data[2]; + command_index = master->rx_data[2 + 1]; + length = (master->rx_data[2 + 6] & 0xFF) + | ((master->rx_data[2 + 7] & 0x07) << 8); + + if (unlikely(master->rx_data_length - 2 < length + 12)) { + printk(KERN_ERR "EtherCAT: Received frame with" + " incomplete command data!\n"); + ec_output_debug_data(master); + return -1; + } + + if (likely(cmd->state == EC_COMMAND_STATE_SENT + && cmd->type == command_type + && cmd->index == command_index + && cmd->data_length == length)) + { + cmd->state = EC_COMMAND_STATE_RECEIVED; + + // Empfangene Daten in Kommandodatenspeicher kopieren + memcpy(cmd->data, master->rx_data + 2 + 10, length); + + // Working-Counter setzen + cmd->working_counter + = ((master->rx_data[length + 2 + 10] & 0xFF) + | ((master->rx_data[length + 2 + 11] & 0xFF) << 8)); + + if (unlikely(master->debug_level > 1)) { + ec_output_debug_data(master); + } + } + else + { + printk(KERN_WARNING "EtherCAT: WARNING - Send/Receive anomaly!\n"); + ec_output_debug_data(master); + } + + master->device.state = EC_DEVICE_STATE_READY; + + return 0; +} + +/*****************************************************************************/ + +/** + Durchsucht den Bus nach Slaves. + + @param master Der EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_scan_for_slaves(ec_master_t *master) +{ + ec_command_t cmd; + ec_slave_t *cur; + unsigned int i, j; + unsigned char data[2]; + + // Determine number of slaves on bus + + ec_command_broadcast_read(&cmd, 0x0000, 4); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + master->bus_slaves_count = cmd.working_counter; + printk("EtherCAT: Found %i slaves on bus.\n", master->bus_slaves_count); + + if (!master->bus_slaves_count) return 0; + + if (!(master->bus_slaves = (ec_slave_t *) kmalloc(master->bus_slaves_count + * sizeof(ec_slave_t), + GFP_KERNEL))) { + printk(KERN_ERR "EtherCAT: Could not allocate memory for bus slaves!\n"); + return -1; + } + + // For every slave in the list + for (i = 0; i < master->bus_slaves_count; i++) + { + cur = master->bus_slaves + i; + + ec_slave_init(cur); + + // Read base data + + ec_command_read(&cmd, cur->station_address, 0x0000, 4); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Slave %i did not respond" + " while reading base data!\n", i); + return -1; + } + + // Get base data + + cur->type = cmd.data[0]; + cur->revision = cmd.data[1]; + cur->build = cmd.data[2] | (cmd.data[3] << 8); + + // Read identification from "Slave Information Interface" (SII) + + if (unlikely(ec_read_slave_information(master, cur->station_address, + 0x0008, &cur->vendor_id) != 0)) { + printk(KERN_ERR "EtherCAT: Could not read SII vendor id!\n"); + return -1; + } + + if (unlikely(ec_read_slave_information(master, cur->station_address, + 0x000A, &cur->product_code) != 0)) { + printk(KERN_ERR "EtherCAT: Could not read SII product code!\n"); + return -1; + } + + if (unlikely(ec_read_slave_information(master, cur->station_address, + 0x000C, + &cur->revision_number) != 0)) { + printk(KERN_ERR "EtherCAT: Could not read SII revision number!\n"); + return -1; + } + + if (unlikely(ec_read_slave_information(master, cur->station_address, + 0x000E, + &cur->serial_number) != 0)) { + printk(KERN_ERR "EtherCAT: Could not read SII serial number!\n"); + return -1; + } + + // Search for identification in "database" + + for (j = 0; j < slave_ident_count; j++) + { + if (unlikely(slave_idents[j].vendor_id == cur->vendor_id + && slave_idents[j].product_code == cur->product_code)) + { + cur->desc = slave_idents[j].desc; + break; + } + } + + if (unlikely(!cur->desc)) { + printk(KERN_ERR "EtherCAT: Unknown slave device (vendor %X, code %X) at " + " position %i.\n", cur->vendor_id, cur->product_code, i); + return -1; + } + + // Set ring position + cur->ring_position = -i; + cur->station_address = i + 1; + + // Write station address + + data[0] = cur->station_address & 0x00FF; + data[1] = (cur->station_address & 0xFF00) >> 8; + + ec_command_position_write(&cmd, cur->ring_position, 0x0010, 2, data); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Slave %i did not repond" + " while writing station address!\n", i); + return -1; + } + } + + return 0; +} + +/*****************************************************************************/ + +/** + Liest Daten aus dem Slave-Information-Interface + eines EtherCAT-Slaves. + + @param master EtherCAT-Master + @param node_address Knotenadresse des Slaves + @param offset Adresse des zu lesenden SII-Registers + @param target Zeiger auf einen 4 Byte großen Speicher + zum Ablegen der Daten + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_read_slave_information(ec_master_t *master, + unsigned short int node_address, + unsigned short int offset, + unsigned int *target) +{ + ec_command_t cmd; + unsigned char data[10]; + unsigned int tries_left; + + // Initiate read operation + + data[0] = 0x00; + data[1] = 0x01; + data[2] = offset & 0xFF; + data[3] = (offset & 0xFF00) >> 8; + data[4] = 0x00; + data[5] = 0x00; + + ec_command_write(&cmd, node_address, 0x502, 6, data); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: SII-read - Slave %04X did not respond!\n", + node_address); + return -1; + } + + // Der Slave legt die Informationen des Slave-Information-Interface + // in das Datenregister und löscht daraufhin ein Busy-Bit. Solange + // den Status auslesen, bis das Bit weg ist. + + tries_left = 100; + while (likely(tries_left)) + { + udelay(10); + + ec_command_read(&cmd, node_address, 0x502, 10); + + if (unlikely(ec_simple_send_receive(master, &cmd) != 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: SII-read status -" + " Slave %04X did not respond!\n", node_address); + return -1; + } + + if (likely((cmd.data[1] & 0x81) == 0)) { + memcpy(target, cmd.data + 6, 4); + break; + } + + tries_left--; + } + + if (unlikely(!tries_left)) { + printk(KERN_WARNING "EtherCAT: SSI-read. Slave %04X timed out!\n", + node_address); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + Ändert den Zustand eines Slaves (asynchron). + + Führt eine (asynchrone) Zustandsänderung bei einem Slave durch. + + @param master EtherCAT-Master + @param slave Slave, dessen Zustand geändert werden soll + @param state_and_ack Neuer Zustand, evtl. mit gesetztem + Acknowledge-Flag + + @return 0 bei Erfolg, sonst < 0 +*/ + +int ec_state_change(ec_master_t *master, ec_slave_t *slave, + unsigned char state_and_ack) +{ + ec_command_t cmd; + unsigned char data[2]; + unsigned int tries_left; + + data[0] = state_and_ack; + data[1] = 0x00; + + ec_command_write(&cmd, slave->station_address, 0x0120, 2, data); + + if (unlikely(ec_simple_send_receive(master, &cmd) != 0)) { + printk(KERN_ERR "EtherCAT: Could not set state %02X - Unable to send!\n", + state_and_ack); + return -1; + } + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Could not set state %02X - Device \"%s %s\"" + " (%d) did not respond!\n", state_and_ack, slave->desc->vendor_name, + slave->desc->product_name, slave->ring_position * (-1)); + return -1; + } + + slave->requested_state = state_and_ack & 0x0F; + + tries_left = 100; + while (likely(tries_left)) + { + udelay(10); + + ec_command_read(&cmd, slave->station_address, 0x0130, 2); + + if (unlikely(ec_simple_send_receive(master, &cmd) != 0)) { + printk(KERN_ERR "EtherCAT: Could not check state %02X - Unable to" + " send!\n", state_and_ack); + return -1; + } + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Could not check state %02X - Device did not" + " respond!\n", state_and_ack); + return -1; + } + + if (unlikely(cmd.data[0] & 0x10)) { // State change error + printk(KERN_ERR "EtherCAT: Could not set state %02X - Device refused" + " state change (code %02X)!\n", state_and_ack, cmd.data[0]); + return -1; + } + + if (likely(cmd.data[0] == (state_and_ack & 0x0F))) { + // State change successful + break; + } + + tries_left--; + } + + if (unlikely(!tries_left)) { + printk(KERN_ERR "EtherCAT: Could not check state %02X - Timeout while" + " checking!\n", state_and_ack); + return -1; + } + + slave->current_state = state_and_ack & 0x0F; + + return 0; +} + +/*****************************************************************************/ + +/** + Gibt Frame-Inhalte zwecks Debugging aus. + + @param master EtherCAT-Master +*/ + +void ec_output_debug_data(const ec_master_t *master) +{ + unsigned int i; + + printk(KERN_DEBUG "EtherCAT: tx_data content (%i Bytes):\n", + master->tx_data_length); + + printk(KERN_DEBUG); + for (i = 0; i < master->tx_data_length; i++) + { + printk("%02X ", master->tx_data[i]); + if ((i + 1) % 16 == 0) printk("\n" KERN_DEBUG); + } + printk("\n"); + + printk(KERN_DEBUG "EtherCAT: rx_data content (%i Bytes):\n", + master->rx_data_length); + + printk(KERN_DEBUG); + for (i = 0; i < master->rx_data_length; i++) + { + printk("%02X ", master->rx_data[i]); + if ((i + 1) % 16 == 0) printk("\n" KERN_DEBUG); + } + printk("\n"); +} + +/*****************************************************************************/ + +/** + Gibt von Zeit zu Zeit die Anzahl verlorener Frames aus. + + @param master EtherCAT-Master +*/ + +void ec_output_lost_frames(ec_master_t *master) +{ + unsigned long int t; + + if (master->frames_lost) { + rdtscl(t); + if ((t - master->t_lost_output) / cpu_khz > 1000) { + printk(KERN_ERR "EtherCAT: %u frame(s) LOST!\n", master->frames_lost); + master->frames_lost = 0; + master->t_lost_output = t; + } + } +} + +/****************************************************************************** + * + * Echtzeitschnittstelle + * + *****************************************************************************/ + +/** + Registriert einen Slave beim Master. + + @param master Der EtherCAT-Master + + @return 0 bei Erfolg, sonst < 0 +*/ + +void *EtherCAT_rt_register_slave(ec_master_t *master, unsigned int bus_index, + const char *vendor_name, + const char *product_name, unsigned int domain) +{ + ec_slave_t *slave; + ec_domain_t *dom; + unsigned int j; + + if (bus_index >= master->bus_slaves_count) { + printk(KERN_ERR "EtherCAT: Illegal bus index! (%i / %i)\n", bus_index, + master->bus_slaves_count); + return NULL; + } + + slave = master->bus_slaves + bus_index; + + if (slave->process_data) { + printk(KERN_ERR "EtherCAT: Slave %i is already registered!\n", bus_index); + return NULL; + } + + if (strcmp(vendor_name, slave->desc->vendor_name) || + strcmp(product_name, slave->desc->product_name)) { + printk(KERN_ERR "Invalid Slave Type! Requested: \"%s %s\", present: \"%s" + "%s\".\n", vendor_name, product_name, slave->desc->vendor_name, + slave->desc->product_name); + return NULL; + } + + // Check, if process data domain already exists... + dom = NULL; + for (j = 0; j < master->domain_count; j++) { + if (domain == master->domains[j].number) { + dom = master->domains + j; + } + } + + // Create process data domain + if (!dom) { + if (master->domain_count > EC_MAX_DOMAINS - 1) { + printk(KERN_ERR "EtherCAT: Too many domains!\n"); + return NULL; + } + + dom = master->domains + master->domain_count; + ec_domain_init(dom); + dom->number = domain; + dom->logical_offset = master->domain_count * EC_FRAME_SIZE; + master->domain_count++; + } + + if (dom->data_size + slave->desc->process_data_size > EC_FRAME_SIZE - 14) { + printk(KERN_ERR "EtherCAT: Oversized domain %i: %i / %i Bytes!\n", + dom->number, dom->data_size + slave->desc->process_data_size, + EC_FRAME_SIZE - 14); + return NULL; + } + + slave->process_data = dom->data + dom->data_size; + dom->data_size += slave->desc->process_data_size; + + return slave->process_data; +} + +/*****************************************************************************/ + +/** + Konfiguriert einen Slave und setzt den Operational-Zustand. + + Führt eine komplette Konfiguration eines Slaves durch, + setzt Sync-Manager und FMMU's, führt die entsprechenden + Zustandsübergänge durch, bis der Slave betriebsbereit ist. + + @param master EtherCAT-Master + @param slave Zu aktivierender Slave + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_rt_activate_slaves(ec_master_t *master) +{ + unsigned int i; + ec_slave_t *slave; + ec_command_t cmd; + const ec_slave_desc_t *desc; + unsigned char fmmu[16]; + unsigned char data[256]; + + for (i = 0; i < master->bus_slaves_count; i++) + { + slave = master->bus_slaves + i; + desc = slave->desc; + + if (unlikely(ec_state_change(master, slave, EC_SLAVE_STATE_INIT) != 0)) + return -1; + + // Resetting FMMU's + + memset(data, 0x00, 256); + + ec_command_write(&cmd, slave->station_address, 0x0600, 256, data); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Resetting FMMUs - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + + // Resetting Sync Manager channels + + if (desc->type != EC_NOSYNC_SLAVE) + { + memset(data, 0x00, 256); + + ec_command_write(&cmd, slave->station_address, 0x0800, 256, data); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Resetting SMs - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + // Init Mailbox communication + + if (desc->type == EC_MAILBOX_SLAVE) + { + if (desc->sm0) + { + ec_command_write(&cmd, slave->station_address, 0x0800, 8, + desc->sm0); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + if (desc->sm1) + { + ec_command_write(&cmd, slave->station_address, 0x0808, 8, + desc->sm1); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM1 -" + " Slave %04X did not respond!\n", + slave->station_address); + return -1; + } + } + } + + // Change state to PREOP + + if (unlikely(ec_state_change(master, slave, EC_SLAVE_STATE_PREOP) != 0)) + return -1; + + // Set FMMU's + + if (desc->fmmu0) + { + if (unlikely(!slave->process_data)) { + printk(KERN_ERR "EtherCAT: Warning - Slave %04X is not assigned to any" + " process data object!\n", slave->station_address); + return -1; + } + + memcpy(fmmu, desc->fmmu0, 16); + + fmmu[0] = slave->logical_address & 0x000000FF; + fmmu[1] = (slave->logical_address & 0x0000FF00) >> 8; + fmmu[2] = (slave->logical_address & 0x00FF0000) >> 16; + fmmu[3] = (slave->logical_address & 0xFF000000) >> 24; + + ec_command_write(&cmd, slave->station_address, 0x0600, 16, fmmu); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting FMMU0 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + // Set Sync Managers + + if (desc->type != EC_MAILBOX_SLAVE) + { + if (desc->sm0) + { + ec_command_write(&cmd, slave->station_address, 0x0800, 8, + desc->sm0); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM0 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + if (desc->sm1) + { + ec_command_write(&cmd, slave->station_address, 0x0808, 8, + desc->sm1); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM1 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + } + + if (desc->sm2) + { + ec_command_write(&cmd, slave->station_address, 0x0810, 8, desc->sm2); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM2 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + if (desc->sm3) + { + ec_command_write(&cmd, slave->station_address, 0x0818, 8, desc->sm3); + + if (unlikely(ec_simple_send_receive(master, &cmd) < 0)) + return -1; + + if (unlikely(cmd.working_counter != 1)) { + printk(KERN_ERR "EtherCAT: Setting SM3 - Slave %04X did not" + " respond!\n", slave->station_address); + return -1; + } + } + + // Change state to SAVEOP + if (unlikely(ec_state_change(master, slave, EC_SLAVE_STATE_SAVEOP) != 0)) + return -1; + + // Change state to OP + if (unlikely(ec_state_change(master, slave, EC_SLAVE_STATE_OP) != 0)) + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + Setzt einen Slave zurück in den Init-Zustand. + + @param master EtherCAT-Master + @param slave Zu deaktivierender Slave + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_rt_deactivate_slaves(ec_master_t *master) +{ + ec_slave_t *slave; + unsigned int i; + + for (i = 0; i < master->bus_slaves_count; i++) + { + slave = master->bus_slaves + 1; + + if (unlikely(ec_state_change(master, slave, EC_SLAVE_STATE_INIT) != 0)) + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +/** + Sendet und empfängt Prozessdaten der angegebenen Domäne + + @param master EtherCAT-Master + domain Domäne + timeout_us Timeout in Mikrosekunden + + @return 0 bei Erfolg, sonst < 0 +*/ + +int EtherCAT_rt_domain_cycle(ec_master_t *master, unsigned int domain, + unsigned int timeout_us) +{ + unsigned int i; + ec_domain_t *dom; + unsigned long start_ticks, end_ticks, timeout_ticks; + + ec_output_lost_frames(master); // Evtl. verlorene Frames ausgeben + + // Domäne bestimmen + dom = NULL; + for (i = 0; i < master->domain_count; i++) { + if (master->domains[i].number == domain) { + dom = master->domains + i; + break; + } + } + + if (unlikely(!dom)) { + printk(KERN_ERR "EtherCAT: No such domain: %i!\n", domain); + return -1; + } + + ec_command_logical_read_write(&dom->command, dom->logical_offset, + dom->data_size, dom->data); + + rdtscl(start_ticks); // Sendezeit nehmen + + if (unlikely(ec_simple_send(master, &dom->command) < 0)) { + printk(KERN_ERR "EtherCAT: Could not send process data command!\n"); + return -1; + } + + timeout_ticks = timeout_us * cpu_khz / 1000; + + // Warten + do { + ec_device_call_isr(&master->device); + rdtscl(end_ticks); // Empfangszeit nehmen + } + while (unlikely(master->device.state == EC_DEVICE_STATE_SENT + && end_ticks - start_ticks < timeout_ticks)); + + master->bus_time = (end_ticks - start_ticks) * 1000 / cpu_khz; + + if (unlikely(end_ticks - start_ticks >= timeout_ticks)) { + master->device.state = EC_DEVICE_STATE_READY; + master->frames_lost++; + ec_output_lost_frames(master); + return -1; + } + + if (unlikely(ec_simple_receive(master, &dom->command) < 0)) { + printk(KERN_ERR "EtherCAT: Could not receive cyclic command!\n"); + return -1; + } + + if (unlikely(dom->command.state != EC_COMMAND_STATE_RECEIVED)) { + printk(KERN_WARNING "EtherCAT: Process data command not received!\n"); + return -1; + } + + if (dom->command.working_counter != dom->response_count) { + dom->response_count = dom->command.working_counter; + printk(KERN_INFO "EtherCAT: Domain %i State change - %i slaves" + " responding.\n", dom->number, dom->response_count); + } + + // Daten vom Kommando in den Prozessdatenspeicher kopieren + memcpy(dom->data, dom->command.data, dom->data_size); + + return 0; +} + +/*****************************************************************************/ + +EXPORT_SYMBOL(EtherCAT_rt_register_slave); +EXPORT_SYMBOL(EtherCAT_rt_activate_slaves); +EXPORT_SYMBOL(EtherCAT_rt_deactivate_slaves); +EXPORT_SYMBOL(EtherCAT_rt_domain_cycle); + +/*****************************************************************************/ + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/master.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/master.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * m a s t e r . h + * + * Struktur für einen EtherCAT-Master. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_MASTER_H_ +#define _EC_MASTER_H_ + +#include "device.h" +#include "slave.h" +#include "command.h" +#include "domain.h" + +/*****************************************************************************/ + +/** + EtherCAT-Master + + Verwaltet die EtherCAT-Slaves und kommuniziert mit + dem zugewiesenen EtherCAT-Gerät. +*/ + +typedef struct ec_master +{ + ec_slave_t *bus_slaves; /**< Array von Slaves auf dem Bus */ + unsigned int bus_slaves_count; /**< Anzahl Slaves auf dem Bus */ + ec_device_t device; /**< EtherCAT-Gerät */ + unsigned int device_registered; /**< Ein Geraet hat sich registriert. */ + unsigned char command_index; /**< Aktueller Kommando-Index */ + unsigned char tx_data[EC_FRAME_SIZE]; /**< Statischer Speicher + für zu sendende Daten */ + unsigned int tx_data_length; /**< Länge der Daten im Sendespeicher */ + unsigned char rx_data[EC_FRAME_SIZE]; /**< Statische Speicher für + eine Kopie des Rx-Buffers + im EtherCAT-Gerät */ + unsigned int rx_data_length; /**< Länge der Daten im Empfangsspeicher */ + ec_domain_t domains[EC_MAX_DOMAINS]; /** Prozessdatendomänen */ + unsigned int domain_count; + int debug_level; /**< Debug-Level im Master-Code */ + unsigned int bus_time; /**< Letzte Bus-Zeit in Mikrosekunden */ + unsigned int frames_lost; /**< Anzahl verlorene Frames */ + unsigned long t_lost_output; /*<< Timer-Ticks bei der letzten Ausgabe von + verlorenen Frames */ +} +ec_master_t; + +/*****************************************************************************/ + +// Private Methods + +// Master creation and deletion +void ec_master_init(ec_master_t *); +void ec_master_clear(ec_master_t *); + +// Registration of devices +int ec_master_open(ec_master_t *); +void ec_master_close(ec_master_t *); + +// Slave management +int ec_scan_for_slaves(ec_master_t *); + +/*****************************************************************************/ + +#endif + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/module.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,336 @@ +/****************************************************************************** + * + * m o d u l e . c + * + * EtherCAT-Master-Treiber + * + * Autoren: Wilhelm Hagemeister, Florian Pose + * + * $Id$ + * + * (C) Copyright IgH 2005 + * Ingenieurgemeinschaft IgH + * Heinz-Bäcker Str. 34 + * D-45356 Essen + * Tel.: +49 201/61 99 31 + * Fax.: +49 201/61 98 36 + * E-mail: sp@igh-essen.com + * + *****************************************************************************/ + +#include +#include +#include + +#include "master.h" +#include "device.h" + +/*****************************************************************************/ + +int __init ec_init_module(void); +void __exit ec_cleanup_module(void); + +/*****************************************************************************/ + +#define LIT(X) #X +#define STR(X) LIT(X) + +#define COMPILE_INFO "Revision " STR(EC_REV) \ + ", compiled by " STR(EC_USER) \ + " at " STR(EC_DATE) + +/*****************************************************************************/ + +int ec_master_count = 1; +ec_master_t *ec_masters = NULL; +int *ec_masters_reserved = NULL; + +/*****************************************************************************/ + +MODULE_AUTHOR ("Wilhelm Hagemeister ," + "Florian Pose "); +MODULE_DESCRIPTION ("EtherCAT master driver module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(COMPILE_INFO); + +module_param(ec_master_count, int, 1); +MODULE_PARM_DESC(ec_master_count, "Number of EtherCAT master to initialize."); + +/*****************************************************************************/ + +/** + Init-Funktion des EtherCAT-Master-Treibermodules + + Initialisiert soviele Master, wie im Parameter ec_master_count + angegeben wurde (Default ist 1). + + @returns 0, wenn alles o.k., -1 bei ungueltiger Anzahl Master + oder zu wenig Speicher. +*/ + +int __init ec_init_module(void) +{ + unsigned int i; + + printk(KERN_ERR "EtherCAT: Master driver, %s\n", COMPILE_INFO); + + if (ec_master_count < 1) { + printk(KERN_ERR "EtherCAT: Error - Illegal" + " ec_master_count: %i\n", ec_master_count); + return -1; + } + + printk(KERN_ERR "EtherCAT: Initializing %i EtherCAT master(s)...\n", + ec_master_count); + + if ((ec_masters = + (ec_master_t *) kmalloc(sizeof(ec_master_t) + * ec_master_count, + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "EtherCAT: Could not allocate" + " memory for EtherCAT master(s)!\n"); + return -1; + } + + if ((ec_masters_reserved = + (int *) kmalloc(sizeof(int) * ec_master_count, + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "EtherCAT: Could not allocate" + " memory for reservation flags!\n"); + kfree(ec_masters); + return -1; + } + + for (i = 0; i < ec_master_count; i++) + { + ec_master_init(&ec_masters[i]); + ec_masters_reserved[i] = 0; + } + + printk(KERN_ERR "EtherCAT: Master driver initialized.\n"); + + return 0; +} + +/*****************************************************************************/ + +/** + Cleanup-Funktion des EtherCAT-Master-Treibermoduls + + Entfernt alle Master-Instanzen. +*/ + +void __exit ec_cleanup_module(void) +{ + unsigned int i; + + printk(KERN_ERR "EtherCAT: Cleaning up master driver...\n"); + + if (ec_masters) + { + for (i = 0; i < ec_master_count; i++) + { + if (ec_masters_reserved[i]) { + printk(KERN_WARNING "EtherCAT: Warning -" + " Master %i is still in use!\n", i); + } + + ec_master_clear(&ec_masters[i]); + } + + kfree(ec_masters); + } + + printk(KERN_ERR "EtherCAT: Master driver cleaned up.\n"); +} + +/****************************************************************************** + * + * Treiberschnittstelle + * + *****************************************************************************/ + +/** + Setzt das EtherCAT-Geraet, auf dem der Master arbeitet. + + + @param master Der EtherCAT-Master + @param device Das EtherCAT-Geraet + @return 0, wenn alles o.k., + < 0, wenn bereits ein Geraet registriert + oder das Geraet nicht geoeffnet werden konnte. +*/ + +ec_device_t *EtherCAT_dev_register(unsigned int master_index, + struct net_device *dev, + irqreturn_t (*isr)(int, void *, + struct pt_regs *), + struct module *module) +{ + ec_device_t *ecd; + + if (master_index >= ec_master_count) { + printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", master_index); + return NULL; + } + + if (!dev) { + printk("EtherCAT: Device is NULL!\n"); + return NULL; + } + + if (ec_masters[master_index].device_registered) { + printk(KERN_ERR "EtherCAT: Master %i already has a device!\n", + master_index); + return NULL; + } + + ecd = &ec_masters[master_index].device; + + if (ec_device_init(ecd) < 0) { + return NULL; + } + + ecd->dev = dev; + ecd->tx_skb->dev = dev; + ecd->rx_skb->dev = dev; + ecd->isr = isr; + ecd->module = module; + + return ecd; +} + +/*****************************************************************************/ + +/** + Loescht das EtherCAT-Geraet, auf dem der Master arbeitet. + + @param master Der EtherCAT-Master + @param device Das EtherCAT-Geraet +*/ + +void EtherCAT_dev_unregister(unsigned int master_index) +{ + if (master_index >= ec_master_count) { + printk(KERN_WARNING "EtherCAT: Master %i does not exist!\n", master_index); + return; + } + + ec_masters[master_index].device_registered = 0; + ec_device_clear(&ec_masters[master_index].device); +} + +/****************************************************************************** + * + * Echtzeitschnittstelle + * + *****************************************************************************/ + +/** + Reserviert einen bestimmten EtherCAT-Master und das zugehörige Gerät. + + Gibt einen Zeiger auf den reservierten EtherCAT-Master zurueck. + + @param index Index des gewuenschten Masters + @returns Zeiger auf EtherCAT-Master oder NULL, wenn Parameter ungueltig. +*/ + +ec_master_t *EtherCAT_rt_request_master(int index) +{ + ec_master_t *master; + + if (index < 0 || index >= ec_master_count) { + printk(KERN_ERR "EtherCAT: Master %i does not exist!\n", index); + goto req_return; + } + + if (ec_masters_reserved[index]) { + printk(KERN_ERR "EtherCAT: Master %i already in use!\n", index); + goto req_return; + } + + master = &ec_masters[index]; + + if (!master->device_registered) { + printk(KERN_ERR "EtherCAT: Master %i has no device assigned yet!\n", + index); + goto req_return; + } + + if (!try_module_get(master->device.module)) { + printk(KERN_ERR "EtherCAT: Could not reserve device module!\n"); + goto req_return; + } + + if (ec_master_open(master) < 0) { + printk(KERN_ERR "EtherCAT: Could not open device!\n"); + goto req_module_put; + } + + if (ec_scan_for_slaves(master) != 0) { + printk(KERN_ERR "EtherCAT: Could not scan for slaves!\n"); + goto req_close; + } + + ec_masters_reserved[index] = 1; + printk(KERN_INFO "EtherCAT: Reserved master %i.\n", index); + + return master; + + req_close: + ec_master_close(master); + + req_module_put: + module_put(master->device.module); + + req_return: + return NULL; +} + +/*****************************************************************************/ + +/** + Gibt einen zuvor angeforderten EtherCAT-Master wieder frei. + + @param master Zeiger auf den freizugebenden EtherCAT-Master. +*/ + +void EtherCAT_rt_release_master(ec_master_t *master) +{ + unsigned int i; + + for (i = 0; i < ec_master_count; i++) + { + if (&ec_masters[i] == master) + { + if (!master->device_registered) { + printk(KERN_WARNING "EtherCAT: Could not release device" + "module because of no device!\n"); + return; + } + + ec_master_close(master); + module_put(master->device.module); + ec_masters_reserved[i] = 0; + + printk(KERN_INFO "EtherCAT: Released master %i.\n", i); + + return; + } + } + + printk(KERN_WARNING "EtherCAT: Master %X was never requested!\n", + (unsigned int) master); +} + +/*****************************************************************************/ + +module_init(ec_init_module); +module_exit(ec_cleanup_module); + +EXPORT_SYMBOL(EtherCAT_dev_register); +EXPORT_SYMBOL(EtherCAT_dev_unregister); +EXPORT_SYMBOL(EtherCAT_rt_request_master); +EXPORT_SYMBOL(EtherCAT_rt_release_master); + +/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/slave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/slave.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,200 @@ +/****************************************************************************** + * + * s l a v e . c + * + * Methoden für einen EtherCAT-Slave. + * + * $Id$ + * + *****************************************************************************/ + +#include + +#include "globals.h" +#include "slave.h" + +/*****************************************************************************/ + +/** + EtherCAT-Slave-Konstruktor. + + Initialisiert einen EtherCAT-Slave. + + ACHTUNG! Dieser Konstruktor wird quasi nie aufgerufen. Bitte immer das + Makro ECAT_INIT_SLAVE() in ec_slave.h anpassen! + + @param slave Zeiger auf den zu initialisierenden Slave +*/ + +void ec_slave_init(ec_slave_t *slave) +{ + slave->type = 0; + slave->revision = 0; + slave->build = 0; + slave->ring_position = 0; + slave->station_address = 0; + slave->vendor_id = 0; + slave->product_code = 0; + slave->revision_number = 0; + slave->serial_number = 0; + slave->desc = NULL; + slave->logical_address = 0; + slave->current_state = EC_SLAVE_STATE_UNKNOWN; + slave->requested_state = EC_SLAVE_STATE_UNKNOWN; + slave->process_data = NULL; + slave->domain = 0; + slave->error_reported = 0; +} + +/*****************************************************************************/ + +#if 0 +/** + Liest einen bestimmten Kanal des Slaves als Integer-Wert. + + Prüft zuerst, ob der entsprechende Slave eine + bekannte Beschreibung besitzt, ob dort eine + read()-Funktion hinterlegt ist und ob die angegebene + Kanalnummer gültig ist. Wenn ja, wird der dekodierte + Wert zurückgegeben, sonst ist der Wert 0. + + @param slave EtherCAT-Slave + @param channel Kanalnummer + + @return Gelesener Wert bzw. 0 +*/ + +int EtherCAT_read_value(EtherCAT_slave_t *slave, + unsigned int channel) +{ + if (unlikely(!slave->desc)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Reading failed on slave %04X (addr %0X)" + " - Slave has no description.\n", + slave->station_address, (unsigned int) slave); + slave->error_reported = 1; + } + return 0; + } + + if (unlikely(!slave->desc->read)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Reading failed on slave %04X (addr %0X)" + " - Slave type (%s %s) has no read method.\n", + slave->station_address, (unsigned int) slave, + slave->desc->vendor_name, slave->desc->product_name); + slave->error_reported = 1; + } + return 0; + } + + if (unlikely(channel >= slave->desc->channel_count)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Reading failed on slave %4X (addr %0X)" + " - Type (%s %s) has no channel %i.\n", + slave->station_address, (unsigned int) slave, + slave->desc->vendor_name, slave->desc->product_name, + channel); + slave->error_reported = 1; + } + return 0; + } + + if (unlikely(!slave->process_data)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Reading failed on slave %4X (addr %0X)" + " - Slave does not belong to any process data object!\n", + slave->station_address, (unsigned int) slave); + slave->error_reported = 1; + } + return 0; + } + + if (unlikely(slave->error_reported)) + slave->error_reported = 0; + + return slave->desc->read(slave->process_data, channel); +} + +/*****************************************************************************/ + +/** + Schreibt einen bestimmten Kanal des Slaves als Integer-Wert . + + Prüft zuerst, ob der entsprechende Slave eine + bekannte Beschreibung besitzt, ob dort eine + write()-Funktion hinterlegt ist und ob die angegebene + Kanalnummer gültig ist. Wenn ja, wird der Wert entsprechend + kodiert und geschrieben. + + @param slave EtherCAT-Slave + @param channel Kanalnummer + @param value Zu schreibender Wert +*/ + +void EtherCAT_write_value(EtherCAT_slave_t *slave, + unsigned int channel, + int value) +{ + if (unlikely(!slave->desc)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Writing failed on slave %04X (addr %0X)" + " - Slave has no description.\n", + slave->station_address, (unsigned int) slave); + slave->error_reported = 1; + } + return; + } + + if (unlikely(!slave->desc->write)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Writing failed on slave %04X (addr %0X)" + " - Type (%s %s) has no write method.\n", + slave->station_address, (unsigned int) slave, + slave->desc->vendor_name, slave->desc->product_name); + slave->error_reported = 1; + } + return; + } + + if (unlikely(channel >= slave->desc->channel_count)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Writing failed on slave %4X (addr %0X)" + " - Type (%s %s) has no channel %i.\n", + slave->station_address, (unsigned int) slave, + slave->desc->vendor_name, slave->desc->product_name, + channel); + slave->error_reported = 1; + } + return; + } + + if (unlikely(!slave->process_data)) { + if (likely(slave->error_reported)) { + printk(KERN_WARNING "EtherCAT: Writing failed on slave %4X (addr %0X)" + " - Slave does not belong to any process data object!\n", + slave->station_address, (unsigned int) slave); + slave->error_reported = 1; + } + return; + } + + if (unlikely(slave->error_reported)) + slave->error_reported = 0; + + slave->desc->write(slave->process_data, channel, value); +} + +/*****************************************************************************/ + +EXPORT_SYMBOL(EtherCAT_write_value); +EXPORT_SYMBOL(EtherCAT_read_value); +#endif + +/*****************************************************************************/ + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/slave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/slave.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * s l a v e . h + * + * Struktur für einen EtherCAT-Slave. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_SLAVE_H_ +#define _EC_SLAVE_H_ + +#include "types.h" + +/*****************************************************************************/ + +/** + EtherCAT-Slave + + Achtung: Bei Änderungen dieser Struktur immer das Define + ECAT_INIT_SLAVE anpassen! +*/ + +typedef struct +{ + // Base data + unsigned char type; /**< Slave-Typ */ + unsigned char revision; /**< Revision */ + unsigned short build; /**< Build-Nummer */ + + // Addresses + short ring_position; /**< (Negative) Position des Slaves im Bus */ + unsigned short station_address; /**< Konfigurierte Slave-Adresse */ + + // Slave information interface + unsigned int vendor_id; /**< Identifikationsnummer des Herstellers */ + unsigned int product_code; /**< Herstellerspezifischer Produktcode */ + unsigned int revision_number; /**< Revisionsnummer */ + unsigned int serial_number; /**< Seriennummer der Klemme */ + + const ec_slave_desc_t *desc; /**< Zeiger auf die Beschreibung + des Slave-Typs */ + + unsigned int logical_address; /**< Konfigurierte, logische adresse */ + + ec_slave_state_t current_state; /**< Aktueller Zustand */ + ec_slave_state_t requested_state; /**< Angeforderter Zustand */ + + unsigned char *process_data; /**< Zeiger auf den Speicherbereich + innerhalb eines Prozessdatenobjekts */ + unsigned int domain; /**< Prozessdatendomäne */ + int error_reported; /**< Ein Zugriffsfehler wurde bereits gemeldet */ +} +ec_slave_t; + +#define EC_INIT_SLAVE(TYPE, DOMAIN) {0, 0, 0, 0, 0, 0, 0, 0, 0, \ + TYPE, 0, ECAT_STATE_UNKNOWN, \ + EC_STATE_UNKNOWN, NULL, DOMAIN, 0} + +/*****************************************************************************/ + +// Slave construction and deletion +void ec_slave_init(ec_slave_t *); + +#if 0 +int EtherCAT_read_value(EtherCAT_slave_t *, unsigned int); +void EtherCAT_write_value(EtherCAT_slave_t *, unsigned int, int); +#endif + +/*****************************************************************************/ + +#endif + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:2 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/types.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/types.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,186 @@ +/****************************************************************************** + * + * t y p e s . c + * + * EtherCAT-Slave-Typen. + * + * $Id$ + * + *****************************************************************************/ + +#include + +#include "globals.h" +#include "types.h" + +/*****************************************************************************/ + +/* Konfigurationen der Sync-Manager */ + +unsigned char sm0_multi[] = {0x00, 0x18, 0xF6, 0x00, 0x26, 0x00, 0x01, 0x00}; +unsigned char sm1_multi[] = {0xF6, 0x18, 0xF6, 0x00, 0x22, 0x00, 0x01, 0x00}; + +unsigned char sm0_1014[] = {0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}; + +unsigned char sm0_2004[] = {0x00, 0x0F, 0x01, 0x00, 0x46, 0x00, 0x01, 0x00}; + +unsigned char sm2_31xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00}; +unsigned char sm3_31xx[] = {0x00, 0x11, 0x06, 0x00, 0x20, 0x00, 0x01, 0x00}; + +unsigned char sm2_41xx[] = {0x00, 0x10, 0x04, 0x00, 0x24, 0x00, 0x01, 0x00}; + +/* Konfigurationen der Memory-Management-Units */ + +unsigned char fmmu0_1014[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_2004[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x0F, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_31xx[] = {0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, + 0x00, 0x11, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00}; + +unsigned char fmmu0_41xx[] = {0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; + +/*****************************************************************************/ + +/* Lese- und Schreibfunktionen */ + +int read_1014(unsigned char *data, unsigned int channel) +{ + return (data[0] >> channel) & 0x01; +} + +void write_2004(unsigned char *data, unsigned int channel, int value) +{ + if (value) { + data[0] |= (1 << channel); + } + else { + data[0] &= ~(1 << channel); + } +} + +int read_31xx(unsigned char *data, unsigned int channel) +{ + return (short int) ((data[channel * 3 + 2] << 8) | data[channel * 3 + 1]); +} + +void write_41xx(unsigned char *data, unsigned int channel, int value) +{ + data[channel * 3 + 1] = (value & 0xFF00) >> 8; + data[channel * 3 + 2] = value & 0xFF; +} + +/*****************************************************************************/ + +/* Klemmen-Objekte */ + +ec_slave_desc_t Beckhoff_EK1100[] = +{{ + "Beckhoff", "EK1100", "Bus Coupler", + EC_NOSYNC_SLAVE, + NULL, NULL, NULL, NULL, + NULL, + 0, 0, + NULL, NULL +}}; + +ec_slave_desc_t Beckhoff_EL1014[] = +{{ + "Beckhoff", "EL1014", "4x Digital Input", + EC_SIMPLE_SLAVE, + sm0_1014, NULL, NULL, NULL, + fmmu0_1014, + 1, 4, + read_1014, NULL +}}; + +ec_slave_desc_t Beckhoff_EL2004[] = +{{ + "Beckhoff", "EL2004", "4x Digital Output", + EC_SIMPLE_SLAVE, + sm0_2004, NULL, NULL, NULL, + fmmu0_2004, + 1, 4, + NULL, write_2004 +}}; + +ec_slave_desc_t Beckhoff_EL3102[] = +{{ + "Beckhoff", "EL3102", "2x Analog Input diff.", + EC_MAILBOX_SLAVE, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6, 2, + read_31xx, NULL +}}; + +ec_slave_desc_t Beckhoff_EL3162[] = +{{ + "Beckhoff", "EL3162", "2x Analog Input", + EC_MAILBOX_SLAVE, + sm0_multi, sm1_multi, sm2_31xx, sm3_31xx, + fmmu0_31xx, + 6, 2, + read_31xx, NULL +}}; + +ec_slave_desc_t Beckhoff_EL4102[] = +{{ + "Beckhoff", "EL4102", "2x Analog Output", + EC_MAILBOX_SLAVE, + sm0_multi, sm1_multi, sm2_41xx, NULL, + fmmu0_41xx, + 4, 2, + NULL, write_41xx +}}; + +ec_slave_desc_t Beckhoff_EL4132[] = +{{ + "Beckhoff", "EL4132", "2x Analog Output diff.", + EC_MAILBOX_SLAVE, + sm0_multi, sm1_multi, sm2_41xx, NULL, + fmmu0_41xx, + 4, 2, + NULL, write_41xx +}}; + +ec_slave_desc_t Beckhoff_EL5001[] = +{{ + "Beckhoff", "EL5001", "SSI-Interface", + EC_SIMPLE_SLAVE, + NULL, NULL, NULL, NULL, // Noch nicht eingepflegt... + NULL, + 0, 0, + NULL, NULL +}}; + +/*****************************************************************************/ + +/** + Beziehung zwischen Identifikationsnummern und Klemmen-Objekt. + + Diese Tabelle stellt die Beziehungen zwischen bestimmten Kombinationen + aus Vendor-IDs und Product-Codes und der entsprechenden Klemme her. + Neue Klemmen müssen hier eingetragen werden. +*/ + +ec_slave_ident_t slave_idents[] = +{ + {0x00000002, 0x03F63052, Beckhoff_EL1014}, + {0x00000002, 0x044C2C52, Beckhoff_EK1100}, + {0x00000002, 0x07D43052, Beckhoff_EL2004}, + {0x00000002, 0x0C1E3052, Beckhoff_EL3102}, + {0x00000002, 0x0C5A3052, Beckhoff_EL3162}, + {0x00000002, 0x10063052, Beckhoff_EL4102}, + {0x00000002, 0x10243052, Beckhoff_EL4132}, + {0x00000002, 0x13893052, Beckhoff_EL5001} +}; + +unsigned int slave_ident_count = sizeof(slave_idents) + / sizeof(ec_slave_ident_t); + + +/*****************************************************************************/ diff -r 6b3b8acb71b5 -r 7506e67dd122 master/types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/types.h Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * t y p e s . h + * + * EtherCAT-Slave-Typen. + * + * $Id$ + * + *****************************************************************************/ + +#ifndef _EC_TYPES_H_ +#define _EC_TYPES_H_ + +/*****************************************************************************/ + +/** + Typ eines EtherCAT-Slaves. + + Dieser Typ muss für die Konfiguration bekannt sein. Der + Master entscheidet danach, ober bspw. Mailboxes konfigurieren, + oder Sync-Manager setzen soll. +*/ + +typedef enum +{ + EC_SIMPLE_SLAVE, EC_MAILBOX_SLAVE, EC_NOSYNC_SLAVE +} +ec_slave_type_t; + +/*****************************************************************************/ + +/** + Beschreibung eines EtherCAT-Slave-Typs. + + Diese Beschreibung dient zur Konfiguration einer bestimmten + Slave-Art. Sie enthält die Konfigurationsdaten für die + Slave-internen Sync-Manager und FMMU's. +*/ + +typedef struct slave_desc +{ + const char *vendor_name; /**< Name des Herstellers */ + const char *product_name; /**< Name des Slaves-Typs */ + const char *product_desc; /**< Genauere Beschreibung des Slave-Typs */ + + const ec_slave_type_t type; /**< Art des Slave-Typs */ + + const unsigned char *sm0; /**< Konfigurationsdaten des + ersten Sync-Managers */ + const unsigned char *sm1; /**< Konfigurationsdaten des + zweiten Sync-Managers */ + const unsigned char *sm2; /**< Konfigurationsdaten des + dritten Sync-Managers */ + const unsigned char *sm3; /**< Konfigurationsdaten des + vierten Sync-Managers */ + + const unsigned char *fmmu0; /**< Konfigurationsdaten + der ersten FMMU */ + + const unsigned int process_data_size; /**< Länge der Prozessdaten in Bytes */ + const unsigned int channel_count; /**< Anzahl der Kanäle */ + + int (*read) (unsigned char *, unsigned int); /**< Funktion zum Dekodieren + und Lesen der Kanaldaten */ + void (*write) (unsigned char *, unsigned int, int); /**< Funktion zum + Kodieren und Schreiben + der Kanaldaten */ +} +ec_slave_desc_t; + +/*****************************************************************************/ + +/** + Identifikation eines Slave-Typs. + + Diese Struktur wird zur 1:n-Zuordnung von Hersteller- und + Produktcodes zu den einzelnen Slave-Typen verwendet. +*/ + +typedef struct slave_ident +{ + const unsigned int vendor_id; /**< Hersteller-Code */ + const unsigned int product_code; /**< Herstellerspezifischer Produktcode */ + const ec_slave_desc_t *desc; /**< Zeiger auf den dazugehörigen + Slave-Typ */ +} +ec_slave_ident_t; + +extern ec_slave_ident_t slave_idents[]; /**< Statisches Array der + Slave-Identifikationen */ +extern unsigned int slave_ident_count; /**< Anzahl der vorhandenen + Slave-Identifikationen */ + +/*****************************************************************************/ + +#endif diff -r 6b3b8acb71b5 -r 7506e67dd122 mini/Makefile --- a/mini/Makefile Fri Jan 13 15:47:44 2006 +0000 +++ b/mini/Makefile Tue Jan 17 18:28:15 2006 +0000 @@ -13,9 +13,9 @@ #---------------------------------------------------------------- # Kbuild-Abschnitt -obj-m := ec_mini_mod.o +obj-m := ec_mini.o -ec_mini_mod-objs := ec_mini.o +ec_mini-objs := mini.o #---------------------------------------------------------------- diff -r 6b3b8acb71b5 -r 7506e67dd122 mini/ec_mini.c --- a/mini/ec_mini.c Fri Jan 13 15:47:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/****************************************************************************** - * - * ec_mini.c - * - * Minimalmodul für EtherCAT - * - * $Id$ - * - *****************************************************************************/ - -#include -#include -#include - -#include "../drivers/ec_master.h" -#include "../drivers/ec_device.h" -#include "../drivers/ec_types.h" -#include "../drivers/ec_module.h" - -/*****************************************************************************/ - -// Auskommentieren, wenn keine zyklischen Daten erwuenscht -#define ECAT_CYCLIC_DATA - -/*****************************************************************************/ - -static EtherCAT_master_t *ecat_master = NULL; - -static EtherCAT_slave_t ecat_slaves[] = -{ -#if 0 - // Block 1 - ECAT_INIT_SLAVE(Beckhoff_EK1100, 1), - ECAT_INIT_SLAVE(Beckhoff_EL4102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3162, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - - ECAT_INIT_SLAVE(Beckhoff_EL4102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL4102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL4102, 1), - - ECAT_INIT_SLAVE(Beckhoff_EL3162, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3162, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3162, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3102, 1), - -#endif - -#if 1 - // Block 2 - ECAT_INIT_SLAVE(Beckhoff_EK1100, 1), - ECAT_INIT_SLAVE(Beckhoff_EL4102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3162, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL3102, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - - // Block 3 - ECAT_INIT_SLAVE(Beckhoff_EK1100, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL2004, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1), - ECAT_INIT_SLAVE(Beckhoff_EL1014, 1) -#endif -}; - -#define ECAT_SLAVES_COUNT (sizeof(ecat_slaves) / sizeof(EtherCAT_slave_t)) - -#ifdef ECAT_CYCLIC_DATA - -int value; -int dig1; - -struct timer_list timer; -unsigned long last_start_jiffies; - -#endif // ECAT_CYCLIC_DATA - -/****************************************************************************** - * - * Function: next2004 - * - *****************************************************************************/ - -#ifdef ECAT_CYCLIC_DATA - -static int next2004(int *wrap) -{ - static int i = 0; - unsigned int j = 0; - - *wrap = 0; - - for (j = 0; j < ECAT_SLAVES_COUNT; j++) - { - i++; - - i %= ECAT_SLAVES_COUNT; - - if (i == 0) *wrap = 1; - - if (ecat_slaves[i].desc == Beckhoff_EL2004) - { - return i; - } - } - - return -1; -} -#endif - -/****************************************************************************** - * - * Function: run - * - * Beschreibung: Zyklischer Prozess - * - *****************************************************************************/ - -#ifdef ECAT_CYCLIC_DATA - -static void run(unsigned long data) -{ - static int ms = 0; - static int cnt = 0; - static unsigned long int k = 0; - static int firstrun = 1; - - static int klemme = 0; - static int kanal = 0; - static int up_down = 0; - int wrap = 0; - - ms++; - ms %= 1000; - - if (firstrun) klemme = next2004(&wrap); - - if (cnt++ > 20) - { - cnt = 0; - - if (++kanal > 3) - { - kanal = 0; - klemme = next2004(&wrap); - - if (wrap == 1) - { - if (up_down == 1) up_down = 0; - else up_down = 1; - } - } - } - - if (klemme >= 0) - EtherCAT_write_value(&ecat_slaves[klemme], kanal, up_down); - - // Prozessdaten lesen und schreiben - rdtscl(k); - EtherCAT_process_data_cycle(ecat_master, 1, 100); - firstrun = 0; - - timer.expires += HZ / 1000; - add_timer(&timer); -} - -#endif // ECAT_CYCLIC_DATA - -/****************************************************************************** - * - * Function: init - * - *****************************************************************************/ - -int __init init_module() -{ - unsigned int i; - - printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); - - if ((ecat_master = EtherCAT_request(0)) == NULL) { - printk(KERN_ERR "EtherCAT master 0 not available!\n"); - goto out_return; - } - - printk("Checking EtherCAT slaves.\n"); - - if (EtherCAT_check_slaves(ecat_master, ecat_slaves, - ECAT_SLAVES_COUNT) != 0) { - printk(KERN_ERR "EtherCAT: Could not init slaves!\n"); - goto out_release_master; - } - - printk("Activating all EtherCAT slaves.\n"); - - for (i = 0; i < ECAT_SLAVES_COUNT; i++) { - if (EtherCAT_activate_slave(ecat_master, &ecat_slaves[i]) != 0) { - printk(KERN_ERR "EtherCAT: Could not activate slave %i!\n", i); - goto out_release_master; - } - } - -#ifdef ECAT_CYCLIC_DATA - printk("Starting cyclic sample thread.\n"); - - init_timer(&timer); - - timer.function = run; - timer.data = 0; - timer.expires = jiffies+10; // Das erste Mal sofort feuern - last_start_jiffies = timer.expires; - add_timer(&timer); - - printk("Initialised sample thread.\n"); -#endif - - printk(KERN_INFO "=== Minimal EtherCAT environment started. ===\n"); - - return 0; - - out_release_master: - EtherCAT_release(ecat_master); - - out_return: - return -1; -} - -/****************************************************************************** - * - * Function: cleanup - * - *****************************************************************************/ - -void __exit cleanup_module() -{ - unsigned int i; - - printk(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n"); - - if (ecat_master) - { -#ifdef ECAT_CYCLIC_DATA - del_timer_sync(&timer); -#endif // ECAT_CYCLIC_DATA - - printk(KERN_INFO "Deactivating slaves.\n"); - - for (i = 0; i < ECAT_SLAVES_COUNT; i++) { - EtherCAT_deactivate_slave(ecat_master, &ecat_slaves[i]); - } - - EtherCAT_release(ecat_master); - } - - printk(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n"); -} - -/*****************************************************************************/ - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("Minimal EtherCAT environment"); - -module_init(init_module); -module_exit(cleanup_module); - -/*****************************************************************************/ - -/* Emacs-Konfiguration -;;; Local Variables: *** -;;; c-basic-offset:4 *** -;;; End: *** -*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 mini/mini.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mini/mini.c Tue Jan 17 18:28:15 2006 +0000 @@ -0,0 +1,160 @@ +/****************************************************************************** + * + * m i n i . c + * + * Minimalmodul für EtherCAT + * + * $Id$ + * + *****************************************************************************/ + +#include +#include +#include + +#include "../include/EtherCAT_rt.h" + +/*****************************************************************************/ + +// Auskommentieren, wenn keine zyklischen Daten erwuenscht +#define ECAT_CYCLIC_DATA + +/*****************************************************************************/ + +ec_master_t *master = NULL; + +#ifdef ECAT_CYCLIC_DATA + +int value; +int dig1; + +struct timer_list timer; +unsigned long last_start_jiffies; + +#endif // ECAT_CYCLIC_DATA + +/****************************************************************************** + * + * Function: run + * + * Beschreibung: Zyklischer Prozess + * + *****************************************************************************/ + +#ifdef ECAT_CYCLIC_DATA + +static void run(unsigned long data) +{ + static int ms = 0; + static unsigned long int k = 0; + static int firstrun = 1; + + ms++; + ms %= 1000; + +#if 0 + if (klemme >= 0) + EtherCAT_write_value(&ecat_slaves[klemme], kanal, up_down); +#endif + + // Prozessdaten lesen und schreiben + rdtscl(k); + EtherCAT_rt_domain_cycle(master, 1, 100); + firstrun = 0; + + timer.expires += HZ / 1000; + add_timer(&timer); +} + +#endif // ECAT_CYCLIC_DATA + +/****************************************************************************** + * + * Function: init + * + *****************************************************************************/ + +int __init init_mini_module(void) +{ + printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); + + if ((master = EtherCAT_rt_request_master(0)) == NULL) { + printk(KERN_ERR "EtherCAT master 0 not available!\n"); + goto out_return; + } + + //check_slaves(); + + printk("Activating all EtherCAT slaves.\n"); + + if (EtherCAT_rt_activate_slaves(master) != 0) { + printk(KERN_ERR "EtherCAT: Could not activate slaves!\n"); + goto out_release_master; + } + +#ifdef ECAT_CYCLIC_DATA + printk("Starting cyclic sample thread.\n"); + + init_timer(&timer); + + timer.function = run; + timer.data = 0; + timer.expires = jiffies+10; // Das erste Mal sofort feuern + last_start_jiffies = timer.expires; + add_timer(&timer); + + printk("Initialised sample thread.\n"); +#endif + + printk(KERN_INFO "=== Minimal EtherCAT environment started. ===\n"); + + return 0; + + out_release_master: + EtherCAT_rt_release_master(master); + + out_return: + return -1; +} + +/****************************************************************************** + * + * Function: cleanup + * + *****************************************************************************/ + +void __exit cleanup_mini_module(void) +{ + printk(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n"); + + if (master) + { +#ifdef ECAT_CYCLIC_DATA + del_timer_sync(&timer); +#endif // ECAT_CYCLIC_DATA + + printk(KERN_INFO "Deactivating slaves.\n"); + + EtherCAT_rt_deactivate_slaves(master); + EtherCAT_rt_release_master(master); + } + + printk(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n"); +} + +/*****************************************************************************/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Florian Pose "); +MODULE_DESCRIPTION ("Minimal EtherCAT environment"); + +module_init(init_mini_module); +module_exit(cleanup_mini_module); + +/*****************************************************************************/ + +/* Emacs-Konfiguration +;;; Local Variables: *** +;;; c-basic-offset:4 *** +;;; End: *** +*/ diff -r 6b3b8acb71b5 -r 7506e67dd122 rt/msr_module.c --- a/rt/msr_module.c Fri Jan 13 15:47:44 2006 +0000 +++ b/rt/msr_module.c Tue Jan 17 18:28:15 2006 +0000 @@ -32,10 +32,7 @@ #include "msr_jitter.h" // EtherCAT -#include "../drivers/ec_master.h" -#include "../drivers/ec_device.h" -#include "../drivers/ec_types.h" -#include "../drivers/ec_module.h" +#include "../include/EtherCAT_rt.h" // Defines/Makros #define TSC2US(T1, T2) ((T2 - T1) * 1000UL / cpu_khz) @@ -54,16 +51,18 @@ // EtherCAT -static EtherCAT_master_t *ecat_master = NULL; +ec_master_t *master = NULL; static unsigned int ecat_bus_time = 0; static unsigned int ecat_timeouts = 0; -static EtherCAT_slave_t ecat_slaves[] = +#if 0 +static ec_slave_t slaves[] = { // Block 1 ECAT_INIT_SLAVE(Beckhoff_EK1100, 0), ECAT_INIT_SLAVE(Beckhoff_EL3102, 0) }; +#endif #define ECAT_SLAVES_COUNT (sizeof(ecat_slaves) / sizeof(EtherCAT_slave_t)) @@ -87,20 +86,22 @@ // Prozessdaten lesen msr_jitter_run(MSR_ABTASTFREQUENZ); +#if 0 if (debug_counter == 0) { - ecat_master->debug_level = 2; - } - - // Prozessdaten schreiben - - if (EtherCAT_process_data_cycle(ecat_master, 0, 40) < 0) - ecat_timeouts++; - + master->debug_level = 2; + } +#endif + + // Prozessdaten lesen und schreiben + EtherCAT_rt_domain_cycle(master, 0, 40); + +#if 0 if (debug_counter == 0) { - ecat_master->debug_level = 0; - } - - value = EtherCAT_read_value(&ecat_slaves[1], 0); + master->debug_level = 0; + } +#endif + + // value = EtherCAT_read_value(&ecat_slaves[1], 0); debug_counter++; if (debug_counter >= MSR_ABTASTFREQUENZ * 5) debug_counter = 0; @@ -183,9 +184,8 @@ * the init/clean material *****************************************************************************/ -int __init init_module() -{ - unsigned int i; +int __init init_rt_module(void) +{ struct ipipe_domain_attr attr; //ipipe // Als allererstes die RT-lib initialisieren @@ -200,25 +200,24 @@ printk(KERN_INFO "=== Starting EtherCAT environment... ===\n"); - if ((ecat_master = EtherCAT_request(0)) == NULL) { + if ((master = EtherCAT_rt_request_master(0)) == NULL) { printk(KERN_ERR "EtherCAT master 0 not available!\n"); goto out_msr_cleanup; } +#if 0 printk("Checking EtherCAT slaves.\n"); - - if (EtherCAT_check_slaves(ecat_master, ecat_slaves, ECAT_SLAVES_COUNT) != 0) { + if (EtherCAT_check_slaves(master, ecat_slaves, ECAT_SLAVES_COUNT) != 0) { printk(KERN_ERR "EtherCAT: Could not init slaves!\n"); goto out_release_master; } +#endif printk("Activating all EtherCAT slaves.\n"); - for (i = 0; i < ECAT_SLAVES_COUNT; i++) { - if (EtherCAT_activate_slave(ecat_master, ecat_slaves + i) < 0) { - printk(KERN_ERR "EtherCAT: Could not activate slave %i!\n", i); - goto out_release_master; - } + if (EtherCAT_rt_activate_slaves(master) < 0) { + printk(KERN_ERR "EtherCAT: Could not activate slaves!\n"); + goto out_release_master; } do_gettimeofday(&process_time); @@ -234,7 +233,7 @@ return 0; out_release_master: - EtherCAT_release(ecat_master); + EtherCAT_rt_release_master(master); out_msr_cleanup: msr_rtlib_cleanup(); @@ -245,28 +244,24 @@ /*****************************************************************************/ -void __exit cleanup_module() -{ - unsigned int i; - +void __exit cleanup_rt_module(void) +{ msr_print_info("msk_modul: unloading..."); ipipe_tune_timer(1000000000UL / HZ, 0); //alten Timertakt wieder herstellen ipipe_unregister_domain(&this_domain); - if (ecat_master) + if (master) { printk(KERN_INFO "=== Stopping EtherCAT environment... ===\n"); printk(KERN_INFO "Deactivating slaves.\n"); - for (i = 0; i < ECAT_SLAVES_COUNT; i++) { - if (EtherCAT_deactivate_slave(ecat_master, ecat_slaves + i) < 0) { - printk(KERN_WARNING "Warning - Could not deactivate slave!\n"); - } + if (EtherCAT_rt_deactivate_slaves(master) < 0) { + printk(KERN_WARNING "Warning - Could not deactivate slaves!\n"); } - EtherCAT_release(ecat_master); + EtherCAT_rt_release_master(master); printk(KERN_INFO "=== EtherCAT environment stopped. ===\n"); } @@ -282,7 +277,7 @@ MODULE_AUTHOR ("Wilhelm Hagemeister "); MODULE_DESCRIPTION ("EtherCAT test environment"); -module_init(init_module); -module_exit(cleanup_module); - -/*****************************************************************************/ +module_init(init_rt_module); +module_exit(cleanup_rt_module); + +/*****************************************************************************/