From e76438b14f1bc1c514ad347400361a112084f506 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:19:20 -0500 Subject: [PATCH] Import 2.2.11pre7 --- arch/alpha/kernel/setup.c | 2 +- drivers/net/Config.in | 1 + drivers/net/Makefile | 8 + drivers/net/Space.c | 4 + drivers/net/sis900.c | 1925 +++++++++++++++++++++++++++++++++++ drivers/sbus/audio/audio.c | 17 +- drivers/sbus/audio/cs4231.c | 47 +- drivers/sbus/audio/cs4231.h | 10 +- include/linux/in.h | 4 + include/linux/in6.h | 2 - include/linux/socket.h | 4 +- include/linux/sysctl.h | 1 + include/net/sock.h | 2 + include/net/tcp.h | 13 +- kernel/sysctl.c | 28 + net/ipv4/af_inet.c | 7 +- net/ipv4/route.c | 32 +- net/ipv4/syncookies.c | 7 +- net/ipv4/sysctl_net_ipv4.c | 27 +- net/ipv4/tcp.c | 35 +- net/ipv4/tcp_input.c | 57 +- net/ipv4/tcp_ipv4.c | 72 +- net/ipv6/tcp_ipv6.c | 68 +- net/netsyms.c | 4 +- 24 files changed, 2285 insertions(+), 92 deletions(-) create mode 100644 drivers/net/sis900.c diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 68726afa57b0..15715f469dd6 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -228,7 +228,7 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, #ifdef CONFIG_ALPHA_GENERIC "Booting GENERIC", #else - "Booting" + "Booting", #endif type_name, (*var_name ? " variation " : ""), var_name, alpha_mv.vector_name, diff --git a/drivers/net/Config.in b/drivers/net/Config.in index ad123ec16f56..d9b0a339b67d 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -93,6 +93,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 + tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900 tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN fi bool 'Other ISA cards' CONFIG_NET_ISA diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6f71f8d3470b..9cc1e7361099 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -566,6 +566,14 @@ else endif endif +ifeq ($(CONFIG_SIS900),y) +L_OBJS += sis900.o +else + ifeq ($(CONFIG_SIS900),m) + M_OBJS += sis900.o + endif +endif + ifeq ($(CONFIG_YELLOWFIN),y) L_OBJS += yellowfin.o else diff --git a/drivers/net/Space.c b/drivers/net/Space.c index aa014d9cf777..303c6d1965d7 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -111,6 +111,7 @@ extern int etherh_probe (struct device *dev); extern int am79c961_probe(struct device *dev); extern int epic100_probe(struct device *dev); extern int rtl8139_probe(struct device *dev); +extern int sis900_probe(struct device *dev); extern int hplance_probe(struct device *dev); extern int bagetlance_probe(struct device *); extern int dec_lance_probe(struct device *); @@ -205,6 +206,9 @@ struct devprobe pci_probes[] __initdata = { #ifdef CONFIG_RTL8139 {rtl8139_probe, 0}, #endif +#ifdef CONFIG_SIS900 + {sis900_probe, 0}, +#endif #ifdef CONFIG_YELLOWFIN {yellowfin_probe, 0}, #endif diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c new file mode 100644 index 000000000000..eddfd47d97cb --- /dev/null +++ b/drivers/net/sis900.c @@ -0,0 +1,1925 @@ +/*****************************************************************************/ +/* sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux. */ +/* */ +/* Silicon Integrated System Corporation */ +/* Revision: 1.05 Aug 7 1999 */ +/* */ +/*****************************************************************************/ + +/* + Modified from the driver which is originally written by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers based on this skeleton fall under the GPL and must retain + the authorship (implicit copyright) notice. + + The author may be reached as becker@tidalwave.net, or + Donald Becker + 312 Severn Ave. #W302 + Annapolis MD 21403 + + Support and updates [to the original skeleton] available at + http://www.tidalwave.net/~becker/pci-skeleton.html +*/ + +static const char *version = +"sis900.c:v1.05 8/07/99\n"; + +static int max_interrupt_work = 20; +#define sis900_debug debug +static int sis900_debug = 0; + +static int multicast_filter_limit = 128; + +#define MAX_UNITS 8 /* More are supported, limit only on options */ + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define TX_DMA_BURST 0 +#define RX_DMA_BURST 0 +#define TX_FIFO_THRESH 16 +#define TxDRNT_100 (1536>>5) +#define TxDRNT_10 16 //(1536>>5) +#define RxDRNT_100 8 +#define RxDRNT_10 8 //(1536>>5) +#define TRUE 1 +#define FALSE 0 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include + +#define RUN_AT(x) (jiffies + (x)) + +#include + +#if LINUX_VERSION_CODE < 0x20123 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif + +/* The I/O extent. */ +#define SIS900_TOTAL_SIZE 0x100 + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ + +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, +}; + +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +static struct device * sis900_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chp_idx, int fnd_cnt); + +static struct pci_id_info pci_tbl[] = +{{ "SiS 900 PCI Fast Ethernet", + 0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, + { "SiS 7016 PCI Fast Ethernet", + 0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, + {0,}, /* 0 terminated list. */ +}; + +/* The capability table matches the chip table above. */ +enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04}; +static int sis_cap_tbl[] = { + HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, + HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, +}; + +/* The rest of these values should never change. */ +#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ +#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */ + +/* Symbolic offsets to registers. */ +enum SIS900_registers { + cr=0x0, //Command Register + cfg=0x4, //Configuration Register + mear=0x8, //EEPROM Access Register + ptscr=0xc, //PCI Test Control Register + isr=0x10, //Interrupt Status Register + imr=0x14, //Interrupt Mask Register + ier=0x18, //Interrupt Enable Register + epar=0x18, //Enhanced PHY Access Register + txdp=0x20, //Transmit Descriptor Pointer Register + txcfg=0x24, //Transmit Configuration Register + rxdp=0x30, //Receive Descriptor Pointer Register + rxcfg=0x34, //Receive Configuration Register + flctrl=0x38, //Flow Control Register + rxlen=0x3c, //Receive Packet Length Register + rfcr=0x48, //Receive Filter Control Register + rfdr=0x4C, //Receive Filter Data Register + pmctrl=0xB0, //Power Management Control Register + pmer=0xB4 //Power Management Wake-up Event Register +}; + +#define RESET 0x00000100 +#define SWI 0x00000080 +#define RxRESET 0x00000020 +#define TxRESET 0x00000010 +#define RxDIS 0x00000008 +#define RxENA 0x00000004 +#define TxDIS 0x00000002 +#define TxENA 0x00000001 + +#define BISE 0x80000000 +#define EUPHCOM 0x00000100 +#define REQALG 0x00000080 +#define SB 0x00000040 +#define POW 0x00000020 +#define EXD 0x00000010 +#define PESEL 0x00000008 +#define LPM 0x00000004 +#define BEM 0x00000001 + +/* Interrupt register bits, using my own meaningful names. */ +#define WKEVT 0x10000000 +#define TxPAUSEEND 0x08000000 +#define TxPAUSE 0x04000000 +#define TxRCMP 0x02000000 +#define RxRCMP 0x01000000 +#define DPERR 0x00800000 +#define SSERR 0x00400000 +#define RMABT 0x00200000 +#define RTABT 0x00100000 +#define RxSOVR 0x00010000 +#define HIBERR 0x00008000 +#define SWINT 0x00001000 +#define MIBINT 0x00000800 +#define TxURN 0x00000400 +#define TxIDLE 0x00000200 +#define TxERR 0x00000100 +#define TxDESC 0x00000080 +#define TxOK 0x00000040 +#define RxORN 0x00000020 +#define RxIDLE 0x00000010 +#define RxEARLY 0x00000008 +#define RxERR 0x00000004 +#define RxDESC 0x00000002 +#define RxOK 0x00000001 + +#define IE 0x00000001 + +#define TxCSI 0x80000000 +#define TxHBI 0x40000000 +#define TxMLB 0x20000000 +#define TxATP 0x10000000 +#define TxIFG 0x0C000000 +#define TxMXF 0x03800000 +#define TxMXF_shift 0x23 +#define TxMXDMA 0x00700000 +#define TxMXDMA_shift 20 +#define TxRTCNT 0x000F0000 +#define TxRTCNT_shift 16 +#define TxFILLT 0x00007F00 +#define TxFILLT_shift 8 +#define TxDRNT 0x0000007F + +#define RxAEP 0x80000000 +#define RxARP 0x40000000 +#define RxATP 0x10000000 +#define RxAJAB 0x08000000 +#define RxMXF 0x03800000 +#define RxMXF_shift 23 +#define RxMXDMA 0x00700000 +#define RxMXDMA_shift 20 +#define RxDRNT 0x0000007F + +#define RFEN 0x80000000 +#define RFAAB 0x40000000 +#define RFAAM 0x20000000 +#define RFAAP 0x10000000 +#define RFPromiscuous (RFAAB|RFAAM|RFAAP) +#define RFAA_shift 28 +#define RFEP 0x00070000 +#define RFEP_shift 16 + +#define RFDAT 0x0000FFFF + +#define OWN 0x80000000 +#define MORE 0x40000000 +#define INTR 0x20000000 +#define OK 0x08000000 +#define DSIZE 0x00000FFF + +#define SUPCRC 0x10000000 +#define ABORT 0x04000000 +#define UNDERRUN 0x02000000 +#define NOCARRIER 0x01000000 +#define DEFERD 0x00800000 +#define EXCDEFER 0x00400000 +#define OWCOLL 0x00200000 +#define EXCCOLL 0x00100000 +#define COLCNT 0x000F0000 + +#define INCCRC 0x10000000 +// ABORT 0x04000000 +#define OVERRUN 0x02000000 +#define DEST 0x01800000 +#define BCAST 0x01800000 +#define MCAST 0x01000000 +#define UNIMATCH 0x00800000 +#define TOOLONG 0x00400000 +#define RUNT 0x00200000 +#define RXISERR 0x00100000 +#define CRCERR 0x00080000 +#define FAERR 0x00040000 +#define LOOPBK 0x00020000 +#define RXCOL 0x00010000 + +#define EuphLiteEEMACAddr 0x08 +#define EuphLiteEEVendorID 0x02 +#define EuphLiteEEDeviceID 0x03 +#define EuphLiteEECardTypeRev 0x0b +#define EuphLiteEEPlexusRev 0x0c +#define EuphLiteEEChecksum 0x0f + +#define RXSTS_shift 18 +#define OWN 0x80000000 +#define MORE 0x40000000 +#define INTR 0x20000000 +#define OK 0x08000000 +#define DSIZE 0x00000FFF +/* MII register offsets */ +#define MII_CONTROL 0x0000 +#define MII_STATUS 0x0001 +#define MII_PHY_ID0 0x0002 +#define MII_PHY_ID1 0x0003 +#define MII_ANAR 0x0004 +#define MII_ANLPAR 0x0005 +#define MII_ANER 0x0006 +/* MII Control register bit definitions. */ +#define MIICNTL_FDX 0x0100 +#define MIICNTL_RST_AUTO 0x0200 +#define MIICNTL_ISOLATE 0x0400 +#define MIICNTL_PWRDWN 0x0800 +#define MIICNTL_AUTO 0x1000 +#define MIICNTL_SPEED 0x2000 +#define MIICNTL_LPBK 0x4000 +#define MIICNTL_RESET 0x8000 +/* MII Status register bit significance. */ +#define MIISTAT_EXT 0x0001 +#define MIISTAT_JAB 0x0002 +#define MIISTAT_LINK 0x0004 +#define MIISTAT_CAN_AUTO 0x0008 +#define MIISTAT_FAULT 0x0010 +#define MIISTAT_AUTO_DONE 0x0020 +#define MIISTAT_CAN_T 0x0800 +#define MIISTAT_CAN_T_FDX 0x1000 +#define MIISTAT_CAN_TX 0x2000 +#define MIISTAT_CAN_TX_FDX 0x4000 +#define MIISTAT_CAN_T4 0x8000 +/* MII NWAY Register Bits ... +** valid for the ANAR (Auto-Negotiation Advertisement) and +** ANLPAR (Auto-Negotiation Link Partner) registers */ +#define MII_NWAY_NODE_SEL 0x001f +#define MII_NWAY_CSMA_CD 0x0001 +#define MII_NWAY_T 0x0020 +#define MII_NWAY_T_FDX 0x0040 +#define MII_NWAY_TX 0x0080 +#define MII_NWAY_TX_FDX 0x0100 +#define MII_NWAY_T4 0x0200 +#define MII_NWAY_RF 0x2000 +#define MII_NWAY_ACK 0x4000 +#define MII_NWAY_NP 0x8000 + +/* MII Auto-Negotiation Expansion Register Bits */ +#define MII_ANER_PDF 0x0010 +#define MII_ANER_LP_NP_ABLE 0x0008 +#define MII_ANER_NP_ABLE 0x0004 +#define MII_ANER_RX_PAGE 0x0002 +#define MII_ANER_LP_AN_ABLE 0x0001 +#define HALF_DUPLEX 1 +#define FDX_CAPABLE_DUPLEX_UNKNOWN 2 +#define FDX_CAPABLE_HALF_SELECTED 3 +#define FDX_CAPABLE_FULL_SELECTED 4 +#define HW_SPEED_UNCONFIG 0 +#define HW_SPEED_10_MBPS 10 +#define HW_SPEED_100_MBPS 100 +#define HW_SPEED_DEFAULT (HW_SPEED_10_MBPS) + +#define ACCEPT_ALL_PHYS 0x01 +#define ACCEPT_ALL_MCASTS 0x02 +#define ACCEPT_ALL_BCASTS 0x04 +#define ACCEPT_ALL_ERRORS 0x08 +#define ACCEPT_CAM_QUALIFIED 0x10 +#define MAC_LOOPBACK 0x20 +//#define FDX_CAPABLE_FULL_SELECTED 4 +#define CRC_SIZE 4 +#define MAC_HEADER_SIZE 14 + +typedef struct _EuphLiteDesc { + u32 llink; + unsigned char* buf; + u32 physAddr; + /* Hardware sees the physical address of descriptor */ + u32 plink; + u32 cmdsts; + u32 bufPhys; +} EuphLiteDesc; + +struct sis900_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + int chip_id; + int chip_revision; + unsigned char pci_bus, pci_devfn; +#if LINUX_VERSION_CODE > 0x20139 + struct net_device_stats stats; +#else + struct enet_statistics stats; +#endif + struct timer_list timer; /* Media selection timer. */ + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int cur_tx, dirty_tx, tx_flag; + + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[NUM_TX_DESC]; + EuphLiteDesc tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ + EuphLiteDesc rx_buf[NUM_RX_DESC]; + unsigned char *rx_bufs; + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + char phys[4]; /* MII device addresses. */ + int phy_idx; + u16 pmd_status; + unsigned int tx_full; /* The Tx queue is full. */ + u16 full_duplex; /* FullHalf-duplex. */ + u16 hunmbps; /* 10010 Mbps. */ + u16 LinkOn; + u16 LinkChange; +}; + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Silicon Integrated Systems Corporation"); +MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +#endif +#endif + +static int sis900_open(struct device *dev); +static u16 read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int val); +static void sis900_timer(unsigned long data); +static void sis900_tx_timeout(struct device *dev); +static void sis900_init_ring(struct device *dev); +static int sis900_start_xmit(struct sk_buff *skb, struct device *dev); +static int sis900_rx(struct device *dev); +static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int sis900_close(struct device *dev); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static struct enet_statistics *sis900_get_stats(struct device *dev); +static void set_rx_mode(struct device *dev); +static void sis900_reset(struct device *dev); +static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed); +static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex); +static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value); +static void elSetMediaType(struct device *dev, int speed, int duplex); + +/* A list of all installed SiS900 devices, for removing the driver module. */ +static struct device *root_sis900_dev = NULL; + +/* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well when dynamically adding drivers. So instead we detect just the + SiS 900 cards in slot order. */ + +int sis900_probe(struct device *dev) +{ + int cards_found = 0; + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + + if ( ! pcibios_present()) + return -ENODEV; + + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + int chip_idx, irq; + long ioaddr; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) { + break; + } + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, + &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, + &device); + + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id && + (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) + break; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; +#else + u32 pci_ioaddr; + u8 pci_irq_line; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; +#endif + } + + if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && + check_region(ioaddr, pci_tbl[chip_idx].io_size)) + continue; + + /* Activate the card: fix for brain-damaged Win98 BIOSes. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_tbl[chip_idx].flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d!" + "Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, + pci_command, new_command); + + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = pci_tbl[chip_idx].probe1(pci_bus, + pci_device_fn, + dev, + ioaddr, + irq, + chip_idx, + cards_found); + + if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { + u8 pci_latency; + + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 32) { + printk(KERN_NOTICE " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); + } + } + dev = 0; + cards_found++; + } + return cards_found ? 0 : -ENODEV; +} + +static struct device * sis900_probe1( int pci_bus, + int pci_devfn, + struct device *dev, + long ioaddr, + int irq, + int chip_idx, + int found_cnt) +{ + static int did_version = 0; /* Already printed version info. */ + struct sis900_private *tp; + int duplex=0; + int speed=0; + int i; + + if (did_version++ == 0) + printk(KERN_INFO "%s", version); + + dev = init_etherdev(dev, 0); + + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", + dev->name, pci_tbl[chip_idx].name, ioaddr, irq); + + if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) { + for (i = 0; i < 3; i++) + ((u16 *)(dev->dev_addr))[i] = + read_eeprom(ioaddr,i+EuphLiteEEMACAddr); + for (i = 0; i < 5; i++) + printk("%2.2x:", (u8)dev->dev_addr[i]); + printk("%2.2x.\n", dev->dev_addr[i]); + } else + printk(KERN_INFO "Error EEPROM read\n"); + + /* We do a request_region() to register /proc/ioports info. */ + request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Some data structures must be quadword aligned. */ + tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + + tp->next_module = root_sis900_dev; + root_sis900_dev = dev; + + tp->chip_id = chip_idx; + tp->pci_bus = pci_bus; + tp->pci_devfn = pci_devfn; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ + if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) { + int phy, phy_idx; + for (phy = 0, phy_idx = 0; + phy < 32 && phy_idx < sizeof(tp->phys); phy++) + { + int mii_status = mdio_read(dev, phy, MII_STATUS); + /* + { + int p; + int l; + for (p = 0 ; p < 4 ; p ++) { + for (l=0 ; l<16 ; l++) { + int status = mdio_read(dev, phy, l); + status = mdio_read(dev, phy, l); + printk(KERN_INFO "MII info addr[%d][%d]=%x\n", + p, l, status); + } + } + } + */ + + if (mii_status != 0xffff && mii_status != 0x0000) { + tp->phy_idx = phy_idx; + tp->phys[phy_idx++] = phy; + tp->pmd_status=mdio_read(dev, phy, MII_STATUS); + printk(KERN_INFO "%s: MII transceiver found " + "at address %d.\n", + dev->name, phy); + break; + } + } + + if (phy_idx == 0) { + printk(KERN_INFO "%s: No MII transceivers found!", + dev->name); + printk(KERN_INFO "Assuming SYM transceiver.\n"); + tp->phys[0] = -1; + } + } else { + tp->phys[0] = 32; + } + + if (tp->pmd_status > 0) { + tp->pmd_status= + elAutoNegotiate(dev, tp->phys[tp->phy_idx], &duplex, &speed); + if (tp->pmd_status & MIISTAT_LINK) { + if (duplex == FDX_CAPABLE_FULL_SELECTED) + tp->full_duplex=1; + if (speed == HW_SPEED_100_MBPS) + tp->hunmbps=1; + tp->LinkOn = TRUE; + } else { + tp->LinkOn = FALSE; + } + tp->LinkChange = FALSE; + } + + /* + if (found_cnt < MAX_UNITS && full_duplex[found_cnt] > 0) + tp->full_duplex = full_duplex[found_cnt]; + + if (tp->full_duplex) { + printk(KERN_INFO "%s: Media type is Full Duplex.\n", dev->name); + } else { + printk(KERN_INFO "%s: Media type is Half Duplex.\n", dev->name); + } + if (tp->hunmbps) { + printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); + } else { + printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); + } + */ + + /* The SiS900-specific entries in the device structure. */ + dev->open = &sis900_open; + dev->hard_start_xmit = &sis900_start_xmit; + dev->stop = &sis900_close; + dev->get_stats = &sis900_get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; + + return dev; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EECLK 0x00000004 /* EEPROM shift clock. */ +#define EECS 0x00000008 /* EEPROM chip select. */ +#define EEDO 0x00000002 /* EEPROM chip data out. */ +#define EEDI 0x00000001 /* EEPROM chip data in. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EEread 0x0180 +#define EEwrite 0x0140 +#define EEerase 0x01C0 +#define EEwriteEnable 0x0130 +#define EEwriteDisable 0x0100 +#define EEeraseAll 0x0120 +#define EEwriteAll 0x0110 +#define EEaddrMask 0x013F +#define EEcmdShift 16 + +static u16 read_eeprom(long ioaddr, int location) +{ + int i; + u16 retval = 0; + long ee_addr = ioaddr + mear; + u32 read_cmd = location | EEread; + + outl(0, ee_addr); + eeprom_delay(); + outl(EECLK, ee_addr); + eeprom_delay(); + + /* Shift the read command bits out. */ + for (i = 8; i >= 0; i--) { + u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; + outl(dataval, ee_addr); + eeprom_delay(); + outl(dataval | EECLK, ee_addr); + eeprom_delay(); + } + outb(EECS, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + outl(EECS, ee_addr); + eeprom_delay(); + outl(EECS | EECLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(0, ee_addr); + eeprom_delay(); + outl(EECLK, ee_addr); + 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_delay() inl(mdio_addr) + +#define MIIread 0x6000 +#define MIIwrite 0x6002 +#define MIIpmdMask 0x0F80 +#define MIIpmdShift 7 +#define MIIregMask 0x007C +#define MIIregShift 2 +#define MIIturnaroundBits 2 +#define MIIcmdLen 16 +#define MIIcmdShift 16 +#define MIIreset 0xFFFFFFFF +#define MIIwrLen 32 + +#define MDC 0x00000040 +#define MDDIR 0x00000020 +#define MDIO 0x00000010 + +static void mdio_idle(long mdio_addr) +{ + outl(MDIO | MDDIR, mdio_addr); + mdio_delay(); + outl(MDIO | MDDIR | MDC, mdio_addr); +} + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_reset(long mdio_addr) +{ + int i; + + for (i = 31; i >= 0; i--) { + outl(MDDIR | MDIO, mdio_addr); + mdio_delay(); + outl(MDDIR | MDIO | MDC, mdio_addr); + mdio_delay(); + } + return; +} + +static int mdio_read(struct device *dev, int phy_id, int location) +{ + long mdio_addr = dev->base_addr + mear; + int mii_cmd = MIIread|(phy_id<= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + outl(dataval | MDC, mdio_addr); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 16; i > 0; i--) { + outl(0, mdio_addr); + //mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); + outl(MDC, mdio_addr); + mdio_delay(); + } + return retval; +} + +static void mdio_write(struct device *dev, int phy_id, int location, int value) +{ + long mdio_addr = dev->base_addr + mear; + int mii_cmd = MIIwrite|(phy_id<= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outb(dataval, mdio_addr); + mdio_delay(); + outb(dataval | MDC, mdio_addr); + mdio_delay(); + } + mdio_delay(); + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outb(0, mdio_addr); + mdio_delay(); + outb(MDC, mdio_addr); + mdio_delay(); + } + return; +} + +static int +sis900_open(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (sis900_debug > 0) + printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n", + dev->name, (unsigned int)ioaddr, dev->irq); + + /* Soft reset the chip. */ + outl(0, ioaddr + imr); + outl(0, ioaddr + ier); + outl(0, ioaddr + rfcr); + outl(RESET | RxRESET | TxRESET, ioaddr + cr); + + if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) + { + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); + tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); + if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) { + if (tp->tx_bufs) + kfree(tp->tx_bufs); + if (sis900_debug > 0) + printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", + dev->name, TX_BUF_SIZE * NUM_TX_DESC); + return -ENOMEM; + } + + { + u32 rfcrSave; + u32 w; + u32 i; + + rfcrSave = inl(rfcr); + outl(rfcrSave & ~RFEN, rfcr); + for (i=0 ; i<3 ; i++) { + w = (u16)*((u16*)(dev->dev_addr)+i); + outl((((u32) i) << RFEP_shift), ioaddr + rfcr); + outl((u32)w, ioaddr + rfdr); + if (sis900_debug > 4) { + printk(KERN_INFO "Filter Addr[%d]=%x\n", + i, inl(ioaddr + rfdr)); + } + } + /* + for (i=0 ; i<3 ; i++) { + outl((((u32) i) << RFEP_shift), ioaddr + rfcr); + if (sis900_debug > 4) { + printk(KERN_INFO "Read Filter Addr[%d]=%x\n", + i, inl(ioaddr + rfdr)); + } + } + */ + outl(rfcrSave, rfcr); + } + + sis900_init_ring(dev); + outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); + outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); + + if (sis900_debug > 4) + printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp)); + + /* Check that the chip has finished the reset. */ + { + u32 status; + int j=0; + status = TxRCMP | RxRCMP; + while (status && (j++ < 30000)) { + status ^= (inl(isr) & status); + } + } + + outl(PESEL, ioaddr + cfg); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + /* + #define TX_DMA_BURST 0 + #define RX_DMA_BURST 0 + #define TX_FIFO_THRESH 16 + #define TxDRNT_100 (1536>>5) + #define TxDRNT_10 (1536>>5) + #define RxDRNT_100 (1536>>5) + #define RxDRNT_10 (1536>>5) + */ + //outl(RxENA | TxENA, ioaddr + cr); + outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); + outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, + ioaddr + txcfg); + //tp->full_duplex = tp->duplex_lock; + if (tp->phys[tp->phy_idx] >= 0 || + (sis_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) { + if (sis900_debug > 1) + if (tp->LinkOn) { + printk(KERN_INFO"%s: Setting %s%s-duplex.\n", + dev->name, + tp->hunmbps ? "100mbps " : "10mbps ", + tp->full_duplex ? "full" : "half"); + } else { + printk(KERN_INFO"%s: Media Link Off\n", dev->name); + } + } + set_rx_mode(dev); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl(RxENA, ioaddr + cr); + outl(IE, ioaddr + ier); + + if (sis900_debug > 1) + printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n", + dev->name, ioaddr, dev->irq); + + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.data = (unsigned long)dev; + tp->timer.function = &sis900_timer; /* timer handler */ + add_timer(&tp->timer); + + return 0; +} + +static void sis900_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 0; + int duplex, full_duplex=0; + int speed, hunmbps=0; + u16 status; + +// printk(KERN_INFO "%s: SiS900 timer\n", dev->name); + elMIIpollBit(dev, tp->phys[tp->phy_idx], MII_STATUS, MIISTAT_LINK, TRUE, &status); + if (status & MIISTAT_LINK) { +// printk(KERN_INFO "%s: SiS900 timer link\n", dev->name); + /* + if (!tp->LinkOn) { + printk(KERN_INFO "%s: AutoNegotiate ...\n", dev->name); + tp->LinkChange=TRUE; + tp->pmd_status= + elAutoNegotiate(dev, tp->phys[tp->phy_idx], + &duplex, &speed); + } + else { + printk(KERN_INFO "%s: Link Still On.\n", dev->name); + elPMDreadMode(dev, tp->phys[tp->phy_idx], + &speed, &duplex); + } + */ + elPMDreadMode(dev, tp->phys[tp->phy_idx], + &speed, &duplex); + + + if (duplex == FDX_CAPABLE_FULL_SELECTED) full_duplex=1; + if (speed == HW_SPEED_100_MBPS) hunmbps=1; + if (full_duplex != tp->full_duplex || hunmbps != tp->hunmbps) + tp->LinkChange = TRUE; + if (tp->LinkChange) { + tp->full_duplex=full_duplex; + tp->hunmbps=hunmbps; + //elSetMediaType(dev, speed, duplex); + printk(KERN_INFO "%s: Setting %s%s-duplex based on MII " + "#%d link partner ability.\n", + dev->name, + tp->hunmbps ? "100mbps " : "10mbps ", + tp->full_duplex ? "full" : "half", + tp->phys[0]); + } + tp->LinkOn=TRUE; + tp->LinkChange = FALSE; + } else { + if (tp->LinkOn) { + tp->LinkChange = TRUE; + tp->pmd_status= + elAutoNegotiate(dev, tp->phys[tp->phy_idx], + &duplex, &speed); + if (tp->pmd_status & MIISTAT_LINK) { + if (duplex == FDX_CAPABLE_FULL_SELECTED) + tp->full_duplex=1; + if (speed == HW_SPEED_100_MBPS) + tp->hunmbps=1; + } else { + tp->LinkOn = FALSE; + printk(KERN_INFO "%s: Link Off\n", dev->name); + } + } +// printk(KERN_INFO "%s: Link Off\n", dev->name); + } + next_tick = 2*HZ; + + if (sis900_debug > 3) { + printk(KERN_INFO "%s: Other registers are IntMask " + "%4.4x IntStatus %4.4x" + " RxStatus %4.4x.\n", + dev->name, + inw(ioaddr + imr), + inw(ioaddr + isr), + inl(ioaddr + rxcfg)); + } + + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void sis900_tx_timeout(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + if (sis900_debug > 0) + printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n", + dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x0000, ioaddr + imr); + /* Emit info to figure out what went wrong. */ + printk(KERN_INFO "%s: Tx queue start entry %d dirty entry %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", + dev->name, i, (unsigned int)&tp->tx_buf[i], + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + /* + printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); + for (mii_reg = 0; mii_reg < 8; mii_reg++) + printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); + printk(".\n"); + */ + + /* Soft reset the chip. */ + + tp->cur_rx = 0; //?????? + /* Must enable Tx/Rx before setting transfer thresholds! */ + /* + set_rx_mode(dev); + */ + { /* Save the unsent Tx packets. */ + struct sk_buff *saved_skb[NUM_TX_DESC], *skb; + int j; + for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) + saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; + tp->dirty_tx = tp->cur_tx = 0; + + for (i = 0; i < j; i++) { + skb = tp->tx_skbuff[i] = saved_skb[i]; + /* Always alignment */ + memcpy((unsigned char*)(tp->tx_buf[i].buf), + skb->data, skb->len); + tp->tx_buf[i].cmdsts = OWN | skb->len; + /* Note: the chip doesn't have auto-pad! */ + /* + outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN), + ioaddr + TxStatus0 + i*4); + */ + } + outl(TxENA, ioaddr + cr); + tp->cur_tx = i; + while (i < NUM_TX_DESC) + tp->tx_skbuff[i++] = 0; + if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ + dev->tbusy = 0; + tp->tx_full = 0; + } else { + tp->tx_full = 1; + } + } + + dev->trans_start = jiffies; + tp->stats.tx_errors++; + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +sis900_init_ring(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = 0; + tp->dirty_tx = tp->cur_tx = 0; + + /* Tx Buffer */ + for (i = 0; i < NUM_TX_DESC; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; + tp->tx_buf[i].bufPhys = + virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); + /* + printk(KERN_INFO "tp->tx_buf[%d].bufPhys=%8.8x\n", + i, tp->tx_buf[i].bufPhys); + */ + } + + /* Tx Descriptor */ + for (i = 0; i< NUM_TX_DESC; i++) { + tp->tx_buf[i].llink = (u32) + &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]); + tp->tx_buf[i].plink = (u32) + virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ? + (i+1) : 0].plink)); + tp->tx_buf[i].physAddr= + virt_to_bus(&(tp->tx_buf[i].plink)); + tp->tx_buf[i].cmdsts=0; + + /* + printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", + i, tp->tx_buf[i].physAddr); + */ + } + + /* Rx Buffer */ + for (i = 0; i < NUM_RX_DESC; i++) { + tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; + tp->rx_buf[i].bufPhys = + virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]); + /* + printk(KERN_INFO "tp->rx_buf[%d].bufPhys=%8.8x\n", + i, tp->rx_buf[i].bufPhys); + */ + } + + /* Rx Descriptor */ + for (i = 0; i< NUM_RX_DESC; i++) { + tp->rx_buf[i].llink = (u32) + &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]); + tp->rx_buf[i].plink = (u32) + virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ? + (i+1) : 0].plink)); + tp->rx_buf[i].physAddr= + virt_to_bus(&(tp->rx_buf[i].plink)); + tp->rx_buf[i].cmdsts=RX_BUF_SIZE; + /* + printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", + i, tp->tx_buf[i].physAddr); + */ + } +} + +static int +sis900_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int entry; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + sis900_tx_timeout(dev); + return 1; + } + + /* Calculate the next Tx descriptor entry. ????? */ + entry = tp->cur_tx % NUM_TX_DESC; + + tp->tx_skbuff[entry] = skb; + + if (sis900_debug > 5) { + int i; + printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)", + dev->name,skb->len); + + for (i = 0; i < skb->len; i++) { + printk("%2.2x ", + (u8)skb->data[i]); + } + printk(".\n"); + } + + memcpy(tp->tx_buf[entry].buf, + skb->data, skb->len); + + tp->tx_buf[entry].cmdsts=(OWN | skb->len); + + outl(TxENA, ioaddr + cr); + if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ + clear_bit(0, (void*)&dev->tbusy); + } else { + tp->tx_full = 1; + } + + /* Note: the chip doesn't have auto-pad! */ + + dev->trans_start = jiffies; + if (sis900_debug > 4) + printk(KERN_INFO "%s: Queued Tx packet at " + "%p size %d to slot %d.\n", + dev->name, skb->data, (int)skb->len, entry); + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_instance; + struct sis900_private *tp = (struct sis900_private *)dev->priv; + int boguscnt = max_interrupt_work; + int status; + long ioaddr = dev->base_addr; + +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_INFO "%s: SMP simultaneous entry of " + "an interrupt handler.\n", dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ + return; + } +#else + if (dev->interrupt) { + printk(KERN_INFO "%s: Re-entering the " + "interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; +#endif + + do { + status = inl(ioaddr + isr); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(status, ioaddr + isr); // ????? + + if (sis900_debug > 4) + printk(KERN_INFO "%s: interrupt status=%#4.4x " + "new intstat=%#4.4x.\n", + dev->name, status, inl(ioaddr + isr)); + + if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + break; + } + + if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */ + sis900_rx(dev); + + if (status & (TxOK | TxERR)) { + unsigned int dirty_tx; + + if (sis900_debug > 5) { + printk(KERN_INFO "TxOK:tp->cur_tx:%d," + "tp->dirty_tx:%x\n", + tp->cur_tx, tp->dirty_tx); + } + for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; + dirty_tx++) + { + int i; + int entry = dirty_tx % NUM_TX_DESC; + int txstatus = tp->tx_buf[entry].cmdsts; + + if (sis900_debug > 4) { + printk(KERN_INFO "%s: Tx Frame contents:" + "(len=%d)", + dev->name, (txstatus & DSIZE)); + + for (i = 0; i < (txstatus & DSIZE) ; + i++) { + printk("%2.2x ", + (u8)(tp->tx_buf[entry].buf[i])); + } + printk(".\n"); + } + if ( ! (txstatus & (OK | UNDERRUN))) + { + if (sis900_debug > 1) + printk(KERN_INFO "Tx NOT (OK," + "UnderRun)\n"); + break; /* It still hasn't been Txed */ + } + + /* Note: TxCarrierLost is always asserted + at 100mbps. */ + if (txstatus & (OWCOLL | ABORT)) { + /* There was an major error, log it. */ + if (sis900_debug > 1) + printk(KERN_INFO "Tx Out of " + " Window,Abort\n"); +#ifndef final_version + if (sis900_debug > 1) + printk(KERN_INFO "%s: Transmit " + "error, Tx status %8.8x.\n", + dev->name, txstatus); +#endif + tp->stats.tx_errors++; + if (txstatus & ABORT) { + tp->stats.tx_aborted_errors++; + /* + outl((TX_DMA_BURST<<8)| + 0x03000001, + ioaddr + TxConfig); + */ + } + if (txstatus & NOCARRIER) + tp->stats.tx_carrier_errors++; + if (txstatus & OWCOLL) + tp->stats.tx_window_errors++; +#ifdef ETHER_STATS + if ((txstatus & COLCNT)==COLCNT) + tp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + /* No count for tp->stats.tx_deferred */ +#endif + if (txstatus & UNDERRUN) { + if (sis900_debug > 1) + printk(KERN_INFO "Tx UnderRun\n"); + /* 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 >> 16) & 0xF; +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.tx_bytes += txstatus & DSIZE; +#endif + if (sis900_debug > 1) + printk(KERN_INFO "Tx Transmit OK\n"); + tp->stats.tx_packets++; + } + + /* Free the original skb. */ + if (sis900_debug > 1) + printk(KERN_INFO "Free original skb\n"); + dev_free_skb(tp->tx_skbuff[entry]); + tp->tx_skbuff[entry] = 0; + } // for dirty + +#ifndef final_version + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { + printk(KERN_INFO"%s: Out-of-sync dirty pointer," + " %d vs. %d, full=%d.\n", + dev->name, dirty_tx, + tp->cur_tx, tp->tx_full); + dirty_tx += NUM_TX_DESC; + } +#endif + + if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) { + /* The ring is no longer full, clear tbusy. */ + //printk(KERN_INFO "Tx Ring NO LONGER Full\n"); + tp->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + tp->dirty_tx = dirty_tx; + if (sis900_debug > 1) + printk(KERN_INFO "TxOK release, tp->cur_tx:%d, tp->dirty:%d\n", + tp->cur_tx, tp->dirty_tx); + } // if (TxOK | TxERR) + + /* Check uncommon events with one test. */ + if (status & (RxORN | TxERR | RxERR)) { + if (sis900_debug > 2) + printk(KERN_INFO "%s: Abnormal interrupt," + "status %8.8x.\n", dev->name, status); + + if (status == 0xffffffff) + break; + + if (status & (RxORN | RxERR)) + tp->stats.rx_errors++; + + if (status & RxORN) { + tp->stats.rx_over_errors++; + /* + tp->cur_rx = + inw(ioaddr + RxBufAddr) % RX_BUF_LEN; + outw(tp->cur_rx - 16, ioaddr + RxBufPtr); + */ + } + } + if (--boguscnt < 0) { + printk(KERN_INFO "%s: Too much work at interrupt, " + "IntrStatus=0x%4.4x.\n", + dev->name, status); + /* Clear all interrupt sources. */ + //outw(0xffff, ioaddr + IntrStatus); + break; + } + } while (1); + + if (sis900_debug > 3) + printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, inl(ioaddr + isr)); + +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else + dev->interrupt = 0; +#endif + return; +} + +/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the + field alignments and semantics. */ +static int sis900_rx(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + u16 cur_rx = tp->cur_rx % NUM_RX_DESC; + int rx_status=tp->rx_buf[cur_rx].cmdsts; + + if (sis900_debug > 4) + printk(KERN_INFO "%s: sis900_rx, current %4.4x," + " rx status=%8.8x\n", + dev->name, cur_rx, + rx_status); + + while (rx_status & OWN) { + int rx_size = rx_status & DSIZE; + rx_size -= CRC_SIZE; + + //printk(KERN_INFO "Rx OWN\n"); + if (sis900_debug > 4) { + int i; + printk(KERN_INFO "%s: sis900_rx, rx status %8.8x," + " size %4.4x, cur %4.4x.\n", + dev->name, rx_status, rx_size, cur_rx); + printk(KERN_INFO "%s: Rx Frame contents:", dev->name); + + for (i = 0; i < rx_size; i++) { + printk("%2.2x ", + (u8)(tp->rx_buf[cur_rx].buf[i])); + } + + printk(".\n"); + } + if (rx_status & TOOLONG) { + if (sis900_debug > 1) + printk(KERN_INFO "%s: Oversized Ethernet frame," + " status %4.4x!\n", + dev->name, rx_status); + tp->stats.rx_length_errors++; + } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) { + if (sis900_debug > 1) + printk(KERN_INFO"%s: Ethernet frame had errors," + " status %4.4x.\n", + dev->name, rx_status); + tp->stats.rx_errors++; + if (rx_status & (RXISERR | FAERR)) + tp->stats.rx_frame_errors++; + if (rx_status & (RUNT | TOOLONG)) + tp->stats.rx_length_errors++; + if (rx_status & CRCERR) tp->stats.rx_crc_errors++; + /* Reset the receiver, + based on RealTek recommendation. (Bug?) */ + /* + tp->cur_rx = 0; + outl(TxENA, ioaddr + cr); + outl(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | + (RX_DMA_BURST<<8), ioaddr + RxConfig); + */ + } else { + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + struct sk_buff *skb; + + //printk(KERN_INFO "Rx OK\n"); + skb = dev_alloc_skb(rx_size + 2); + if (skb == NULL) { + printk(KERN_INFO "%s: Memory squeeze," + "deferring packet.\n", + dev->name); + /* We should check that some rx space is free. + If not, + free one and mark stats->rx_dropped++. */ + tp->stats.rx_dropped++; + tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; + break; + } + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP fields. */ + if (rx_size+CRC_SIZE > RX_BUF_SIZE) { + /* + int semi_count = RX_BUF_LEN - ring_offset - 4; + memcpy(skb_put(skb, semi_count), + &rx_bufs[ring_offset + 4], semi_count); + memcpy(skb_put(skb, rx_size-semi_count), + rx_bufs, rx_size - semi_count); + if (sis900_debug > 4) { + int i; + printk(KERN_DEBUG"%s: Frame wrap @%d", + dev->name, semi_count); + for (i = 0; i < 16; i++) + printk(" %2.2x", rx_bufs[i]); + printk(".\n"); + memset(rx_bufs, 0xcc, 16); + } + */ + //printk(KERN_INFO "Frame Wrap....\n"); + } else { +#if 0 /* USE_IP_COPYSUM */ + eth_copy_and_sum(skb, + tp->rx_buf[cur_rx].buf, rx_size, 0); + skb_put(skb, rx_size); +#else + memcpy(skb_put(skb, rx_size), + tp->rx_buf[cur_rx].buf, rx_size); +#endif + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.rx_bytes += rx_size; +#endif + tp->stats.rx_packets++; + } + tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; + + cur_rx = ((cur_rx+1) % NUM_RX_DESC); + rx_status = tp->rx_buf[cur_rx].cmdsts; + //outw(cur_rx - 16, ioaddr + RxBufPtr); + } // while + if (sis900_debug > 4) + printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x " + "Cmd %2.2x.\n", + dev->name, cur_rx, + inb(ioaddr + cr)); + tp->cur_rx = cur_rx; + return 0; +} + +static int +sis900_close(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct sis900_private *tp = (struct sis900_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (sis900_debug > 1) + printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", + dev->name, inl(ioaddr + isr)); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x0000, ioaddr + imr); + + /* Stop the chip's Tx and Rx DMA processes. */ + outl(0x00, ioaddr + cr); + + del_timer(&tp->timer); + + free_irq(dev->irq, dev); + + for (i = 0; i < NUM_TX_DESC; i++) { + if (tp->tx_skbuff[i]) + dev_free_skb(tp->tx_skbuff[i]); + tp->tx_skbuff[i] = 0; + } + kfree(tp->rx_bufs); + kfree(tp->tx_bufs); + + /* Green! Put the chip in low-power mode. */ + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = tp->phys[0] & 0x3f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static struct enet_statistics * +sis900_get_stats(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + 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 u16 elComputeHashTableIndex(u8 *addr) +{ +#define POLYNOMIAL 0x04C11DB6L + u32 crc = 0xffffffff, msb; + int i, j; + u8 byte; + + for( i=0; i<6; i++ ) { + byte = *addr++; + for( j=0; j<8; j++ ) { + msb = crc >> 31; + crc <<= 1; + if( msb ^ ( byte & 1 )) { + crc ^= POLYNOMIAL; + crc |= 1; + } + byte >>= 1; + } + } + // 7 bit crc for 128 bit hash table + return( (int)(crc >> 25) ); +} + +static u16 elMIIpollBit(struct device *dev, + int phy_id, + int location, + u16 mask, + u16 polarity, + u16 *value) +{ + u32 i; + i=0; + while (1) { + *value = mdio_read(dev, phy_id, location); + if (polarity) { + if (mask & *value) return(TRUE); + } else { + if (mask & ~(*value)) return(TRUE); + } + if (++i == 1200) break; + } + return(FALSE); +} + +static u16 elPMDreadMode(struct device *dev, + int phy_id, + int *speed, + int *duplex) +{ + u16 status; + + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + + status = mdio_read(dev, phy_id, MII_ANLPAR); + + if ( !( status & + (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) { +// printk(KERN_INFO "%s: Link Partner not detected.\n", dev->name); + while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ; + while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ; + if (status & 0x80) + *speed = HW_SPEED_100_MBPS; + if (status & 0x40) + *duplex = FDX_CAPABLE_FULL_SELECTED; + if (sis900_debug > 3) { + printk(KERN_INFO"%s: Setting %s%s-duplex.\n", + dev->name, + *speed == HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } + } else { +// printk(KERN_INFO "%s: Link Partner detected\n", dev->name); + if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) { + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) { + *speed = HW_SPEED_100_MBPS; + } + if (sis900_debug > 3) { + printk(KERN_INFO"%s: Setting %s%s-duplex based on" + " auto-negotiated partner ability.\n", + dev->name, + *speed == HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } + } + return (status); +} + +static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed) +{ + u16 status; + + mdio_write(dev, phy_id, MII_CONTROL, 0); + mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | + MIICNTL_RST_AUTO); + elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status); + elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status); + elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status); + if (status & MIISTAT_LINK) { + elPMDreadMode(dev, phy_id, speed, duplex); + elSetMediaType(dev, *speed, *duplex); + } + return(status); +} + +static void elSetMediaType(struct device *dev, int speed, int duplex) +{ + long ioaddr = dev->base_addr; + u32 txCfgOn = 0, txCfgOff = TxDRNT; + u32 rxCfgOn = 0, rxCfgOff = 0; + + if (speed == HW_SPEED_100_MBPS) { + txCfgOn |= (TxDRNT_100 | TxHBI); + } else { + txCfgOn |= TxDRNT_10; + } + + if (duplex == FDX_CAPABLE_FULL_SELECTED) { + txCfgOn |= (TxCSI | TxHBI); + rxCfgOn |= RxATP; + } else { + txCfgOff |= (TxCSI | TxHBI); + rxCfgOff |= RxATP; + } + outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg); + outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg); +} + +static void set_rx_mode(struct device *dev) +{ + long ioaddr = dev->base_addr; + u16 mc_filter[8]; + int i; + int rx_mode; + u32 rxCfgOn = 0, rxCfgOff = 0; + u32 txCfgOn = 0, txCfgOff = 0; + + if (sis900_debug > 3) + printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--" + "RxCfg %8.8x.\n", + dev->name, dev->flags, inl(ioaddr + rxcfg)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); + rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | + ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS; + for (i=0 ; i<8 ; i++) + mc_filter[i]=0xffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | + ACCEPT_CAM_QUALIFIED; + for (i=0 ; i<8 ; i++) + mc_filter[i]=0xffff; + } else { + struct dev_mc_list *mclist; + rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | + ACCEPT_CAM_QUALIFIED; + for (i=0 ; i<8 ; i++) + mc_filter[i]=0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(elComputeHashTableIndex(mclist->dmi_addr), + mc_filter); + } + + for (i=0 ; i<8 ; i++) { + outl((u32)(0x00000004+i) << 16, ioaddr + rfcr); + outl(mc_filter[i], ioaddr + rfdr); + } + /* We can safely update without stopping the chip. */ + //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS; + //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS; + outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS | + ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr); + + if (rx_mode & ACCEPT_ALL_ERRORS) { + rxCfgOn = RxAEP | RxARP | RxAJAB; + } else { + rxCfgOff = RxAEP | RxARP | RxAJAB; + } + if (rx_mode & MAC_LOOPBACK) { + rxCfgOn |= RxATP; + txCfgOn |= TxMLB; + } else { + if (!(( (struct sis900_private *)(dev->priv) )->full_duplex)) + rxCfgOff |= RxATP; + txCfgOff |= TxMLB; + } + + if (sis900_debug > 2) { + printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg)); + printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg)); + } + + outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg); + outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg); + + if (sis900_debug > 2) { + printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg)); + printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg)); + printk(KERN_INFO "Receive Filter Register:%8.8x\n", + inl(ioaddr + rfcr)); + } + return; +} + +static void sis900_reset(struct device *dev) +{ + long ioaddr = dev->base_addr; + + outl(0, ioaddr + ier); + outl(0, ioaddr + imr); + outl(0, ioaddr + rfcr); + + outl(RxRESET | TxRESET | RESET, ioaddr + cr); + outl(PESEL, ioaddr + cfg); + + set_rx_mode(dev); +} + +#ifdef MODULE +int init_module(void) +{ + return sis900_probe(0); +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_sis900_dev) { + struct sis900_private *tp = + (struct sis900_private *)root_sis900_dev->priv; + next_dev = tp->next_module; + unregister_netdev(root_sis900_dev); + release_region(root_sis900_dev->base_addr, + pci_tbl[tp->chip_id].io_size); + kfree(tp); + kfree(root_sis900_dev); + root_sis900_dev = next_dev; + } +} + +#endif /* MODULE */ +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index b021019416db..e6da60429b71 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -117,6 +117,13 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex) * TODO: Make number of input/output buffers tunable parameters */ +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff + init_waitqueue_head(&drv->open_wait); + init_waitqueue_head(&drv->output_write_wait); + init_waitqueue_head(&drv->output_drain_wait); + init_waitqueue_head(&drv->input_read_wait); +#endif + drv->num_output_buffers = 8; drv->output_buffer_size = (4096 * 2); drv->playing_count = 0; @@ -264,6 +271,7 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv, int status) * If status & 2, a buffer was claimed for DMA and is still in use. * * The playing_count for non-DMA hardware should never be non-zero. + * Value of status for non-DMA hardware should always be 1. */ if (status & 1) { if (drv->playing_count) @@ -300,8 +308,9 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv, int status) /* If we got back a buffer, see if anyone wants to write to it */ if ((status & 1) || ((drv->output_count + drv->playing_count) - < drv->num_output_buffers)) + < drv->num_output_buffers)) { wake_up_interruptible(&drv->output_write_wait); + } /* If the output queue is empty, shut down the driver. */ if ((drv->output_count < 1) && (drv->playing_count < 1)) { @@ -664,7 +673,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file, case SOUND_MIXER_WRITE_CD: case SOUND_MIXER_WRITE_LINE: case SOUND_MIXER_WRITE_IMIX: - if(get_user(k, arg)) + if(COPY_IN(arg, k)) return -EFAULT; tprintk(("setting input volume (0x%x)", k)); if (drv->ops->get_input_channels) @@ -695,7 +704,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file, case SOUND_MIXER_WRITE_PCM: case SOUND_MIXER_WRITE_VOLUME: case SOUND_MIXER_WRITE_SPEAKER: - if(get_user(k, arg)) + if(COPY_IN(arg, k)) return -EFAULT; tprintk(("setting output volume (0x%x)", k)); if (drv->ops->get_output_channels) @@ -736,7 +745,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file, case SOUND_MIXER_WRITE_RECSRC: if (!drv->ops->set_input_port) return -EINVAL; - if(get_user(k, arg)) + if(COPY_IN(arg, k)) return -EFAULT; /* only one should ever be selected */ if (k & SOUND_MASK_IMIX) j = AUDIO_ANALOG_LOOPBACK; diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c index ab6b64562815..a1c700205f62 100644 --- a/drivers/sbus/audio/cs4231.c +++ b/drivers/sbus/audio/cs4231.c @@ -1,7 +1,9 @@ /* * drivers/sbus/audio/cs4231.c * - * Copyright 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu) + * Copyright 1996, 1997, 1998, 1999 Derrick J Brashear (shadow@andrew.cmu.edu) + * The 4231/ebus support was written by David Miller, who didn't bother + * crediting himself here, so I will. * * Based on the AMD7930 driver: * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) @@ -11,7 +13,7 @@ * * This was culled from the Crystal docs on the 4231a, and the addendum they * faxed me on the 4231. - * The APC DMA controller support unfortunately is not documented. Thanks, Sun + * The APC DMA controller support unfortunately is not documented. Thanks, Sun. */ #include @@ -69,15 +71,17 @@ static int cs4231_record_gain(struct sparcaudio_driver *drv, int value, static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned char balance); static void cs4231_ready(struct sparcaudio_driver *drv); -static void cs4231_playintr(struct sparcaudio_driver *drv); +static void cs4231_playintr(struct sparcaudio_driver *drv, int); static int cs4231_recintr(struct sparcaudio_driver *drv); static int cs4231_output_muted(struct sparcaudio_driver *drv, int value); static void cs4231_pollinput(struct sparcaudio_driver *drv); -static void eb4231_pollinput(struct sparcaudio_driver *drv); static int cs4231_length_to_samplecount(struct audio_prinfo *thisdir, unsigned int length); static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int value); +#ifdef EB4231_SUPPORT +static void eb4231_pollinput(struct sparcaudio_driver *drv); +#endif #define CHIP_READY udelay(100); cs4231_ready(drv); udelay(1000); @@ -1228,11 +1232,20 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar MOD_DEC_USE_COUNT; } -static void cs4231_playintr(struct sparcaudio_driver *drv) +static void cs4231_playintr(struct sparcaudio_driver *drv, int push) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; int status = 0; + if (!push) { + if (!cs4231_chip->perchip_info.play.active) { + cs4231_chip->regs->dmapnva = cs4231_chip->output_next_dma_handle; + cs4231_chip->regs->dmapnc = cs4231_chip->output_next_dma_size; + } + sparcaudio_output_done(drv, 0); + return; + } + if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 0) cs4231_chip->playlen = cs4231_chip->output_size; @@ -1484,14 +1497,16 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, cs4231_chip->perchip_info.play.active = 1; cs4231_chip->playing_count = 0; - cs4231_playintr(drv); if ((cs4231_chip->regs->dmacsr & APC_PPAUSE) || !(cs4231_chip->regs->dmacsr & APC_PDMA_READY)) { - cs4231_chip->regs->dmacsr &= ~(APC_XINT_PLAY | APC_PPAUSE); - cs4231_chip->regs->dmacsr |= APC_GENL_INT | APC_XINT_ENA | APC_XINT_PLAY - | APC_XINT_GENL | APC_XINT_PENA | APC_PDMA_READY; + cs4231_chip->regs->dmacsr &= ~APC_XINT_PLAY; + cs4231_chip->regs->dmacsr &= ~APC_PPAUSE; + + cs4231_playintr(drv, cs4231_chip->regs->dmapnva == 0 ? 1 : 0); + + cs4231_chip->regs->dmacsr |= APC_PLAY_SETUP; cs4231_enable_play(drv); - + cs4231_ready(drv); } } @@ -1544,11 +1559,8 @@ static void cs4231_stop_output(struct sparcaudio_driver *drv) cs4231_chip->output_next_dma_handle = 0; cs4231_chip->output_next_dma_size = 0; } -#if 1 /* Not safe without shutting off the DMA controller as well. -DaveM */ +#if 0 /* Not safe without shutting off the DMA controller as well. -DaveM */ /* Else subsequent speed setting changes are ignored by the chip. */ - cs4231_chip->regs->dmacsr &= ~(APC_GENL_INT | APC_XINT_ENA | APC_XINT_PLAY - | APC_XINT_GENL | APC_PDMA_READY - | APC_XINT_PENA ); cs4231_disable_play(drv); #endif } @@ -1897,11 +1909,12 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) */ if (dummy & APC_PLAY_INT) { - if (dummy & APC_XINT_PEMP) { + if (dummy & APC_XINT_PNVA) { cs4231_chip->perchip_info.play.samples += cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), cs4231_chip->playlen); - cs4231_playintr(drv); + if (!(dummy & APC_XINT_EMPT)) + cs4231_playintr(drv, 1); } /* Any other conditions we need worry about? */ } @@ -1933,7 +1946,7 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) cs4231_chip->perchip_info.play.error = 1; } cs4231_chip->perchip_info.play.active = 0; - cs4231_playintr(drv); + cs4231_playintr(drv, 0); cs4231_getsamplecount(drv, cs4231_chip->playlen, 0); } diff --git a/drivers/sbus/audio/cs4231.h b/drivers/sbus/audio/cs4231.h index 892ed7e88c4d..45702e86a04b 100644 --- a/drivers/sbus/audio/cs4231.h +++ b/drivers/sbus/audio/cs4231.h @@ -255,19 +255,19 @@ struct cs4231_chip { #define APC_XINT_PLAY 0x40000 /* Playback ext intr */ #define APC_XINT_CAPT 0x20000 /* Capture ext intr */ #define APC_XINT_GENL 0x10000 /* Error ext intr */ -#define APC_XINT_EMPT 0x8000 /* Pipe empty interrupt */ -#define APC_XINT_PEMP 0x4000 /* Play pipe empty */ +#define APC_XINT_EMPT 0x8000 /* Pipe empty interrupt (0 write to pva) */ +#define APC_XINT_PEMP 0x4000 /* Play pipe empty (pva and pnva not set) */ #define APC_XINT_PNVA 0x2000 /* Playback NVA dirty */ #define APC_XINT_PENA 0x1000 /* play pipe empty Int enable */ #define APC_XINT_COVF 0x800 /* Cap data dropped on floor */ #define APC_XINT_CNVA 0x400 /* Capture NVA dirty */ -#define APC_XINT_CEMP 0x200 /* Capture pipe empty interrupt */ +#define APC_XINT_CEMP 0x200 /* Capture pipe empty (cva and cnva not set) */ #define APC_XINT_CENA 0x100 /* Cap. pipe empty int enable */ #define APC_PPAUSE 0x80 /* Pause the play DMA */ #define APC_CPAUSE 0x40 /* Pause the capture DMA */ #define APC_CDC_RESET 0x20 /* CODEC RESET */ -#define APC_PDMA_READY 0x08 /* Play DMA Go */ -#define APC_CDMA_READY 0x04 /* Capture DMA Go */ +#define APC_PDMA_READY 0x08 /* Play DMA Go */ +#define APC_CDMA_READY 0x04 /* Capture DMA Go */ #define APC_CHIP_RESET 0x01 /* Reset the chip */ #define APC_INIT_SETUP (APC_CDMA_READY | APC_PDMA_READY | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_GENL | APC_INT_PENDING | APC_PLAY_INT | APC_CAPT_INT | APC_GENL_INT) diff --git a/include/linux/in.h b/include/linux/in.h index 8d6c8b5b6912..37db22a9a5b5 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -38,6 +38,10 @@ enum { IPPROTO_PIM = 103, /* Protocol Independent Multicast */ + IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ + IPPROTO_AH = 51, /* Authentication Header protocol */ + IPPROTO_COMP = 108, /* Compression Header protocol */ + IPPROTO_RAW = 255, /* Raw IP packets */ IPPROTO_MAX }; diff --git a/include/linux/in6.h b/include/linux/in6.h index 4f31677b98f6..ca5e768ba94c 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -129,8 +129,6 @@ struct in6_flowlabel_req #define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ #define IPPROTO_ROUTING 43 /* IPv6 routing header */ #define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ -#define IPPROTO_ESP 50 /* encapsulating security payload */ -#define IPPROTO_AH 51 /* authentication header */ #define IPPROTO_ICMPV6 58 /* ICMPv6 */ #define IPPROTO_NONE 59 /* IPv6 no next header */ #define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ diff --git a/include/linux/socket.h b/include/linux/socket.h index 5bc5bf252a47..d681aa5ba9ba 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -146,7 +146,7 @@ struct ucred { #define AF_DECnet 12 /* Reserved for DECnet project */ #define AF_NETBEUI 13 /* Reserved for 802.2LLC project*/ #define AF_SECURITY 14 /* Security callback pseudo AF */ -#define pseudo_AF_KEY 15 /* PF_KEY key management API */ +#define AF_KEY 15 /* PF_KEY key management API */ #define AF_NETLINK 16 #define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */ #define AF_PACKET 17 /* Packet family */ @@ -174,7 +174,7 @@ struct ucred { #define PF_DECnet AF_DECnet #define PF_NETBEUI AF_NETBEUI #define PF_SECURITY AF_SECURITY -#define PF_KEY pseudo_AF_KEY +#define PF_KEY AF_KEY #define PF_NETLINK AF_NETLINK #define PF_ROUTE AF_ROUTE #define PF_PACKET AF_PACKET diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 87a7f051ab96..aba1498a73de 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -470,6 +470,7 @@ extern int do_sysctl_strategy (ctl_table *table, extern ctl_handler sysctl_string; extern ctl_handler sysctl_intvec; +extern ctl_handler sysctl_jiffies; extern int do_string ( void *oldval, size_t *oldlenp, void *newval, size_t newlen, diff --git a/include/net/sock.h b/include/net/sock.h index da5a65b1073a..28cf919e76c3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -300,6 +300,7 @@ struct tcp_opt { __u32 last_seg_size; /* Size of last incoming segment */ __u32 rcv_mss; /* MSS used for delayed ACK decisions */ + __u32 partial_writers; /* # of clients wanting at the head packet */ struct open_request *syn_wait_queue; struct open_request **syn_wait_last; @@ -672,6 +673,7 @@ static inline void lock_sock(struct sock *sk) #if 0 /* debugging code: the test isn't even 100% correct, but it can catch bugs */ /* Note that a double lock is ok in theory - it's just _usually_ a bug */ +/* Actually it can easily happen with multiple writers */ if (atomic_read(&sk->sock_readers)) { printk("double lock on socket at %p\n", gethere()); here: diff --git a/include/net/tcp.h b/include/net/tcp.h index e8ab1f492f2d..38ea51d50133 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -120,6 +120,8 @@ struct tcp_tw_bucket { /* These _must_ match the beginning of struct sock precisely. * XXX Yes I know this is gross, but I'd have to edit every single * XXX networking file if I created a "struct sock_header". -DaveM + * Just don't forget -fno-strict-aliasing, but it should be really + * fixed -AK */ struct sock *sklist_next; struct sock *sklist_prev; @@ -140,12 +142,13 @@ struct tcp_tw_bucket { nonagle; /* And these are ours. */ - __u32 rcv_nxt; + __u32 rcv_nxt,snd_nxt; struct tcp_func *af_specific; struct tcp_bind_bucket *tb; struct tcp_tw_bucket *next_death; struct tcp_tw_bucket **pprev_death; int death_slot; + #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct in6_addr v6_daddr; struct in6_addr v6_rcv_saddr; @@ -476,7 +479,13 @@ extern int tcp_rcv_established(struct sock *sk, struct tcphdr *th, unsigned len); -extern int tcp_timewait_state_process(struct tcp_tw_bucket *tw, +enum tcp_tw_status { + TCP_TW_SUCCESS = 0, + TCP_TW_RST = 1, + TCP_TW_ACK = 2 + }; + +extern enum tcp_tw_status tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, struct tcphdr *th, unsigned len); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e5132d822d14..a5f4c3a4fcc5 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1035,6 +1035,34 @@ int sysctl_intvec(ctl_table *table, int *name, int nlen, return 0; } +/* Strategy function to convert jiffies to seconds */ +int sysctl_jiffies(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + if (oldval) { + size_t olen; + if (oldlenp) { + if (get_user(olen, oldlenp)) + return -EFAULT; + if (olen!=sizeof(int)) + return -EINVAL; + } + if (put_user(*(int *)(table->data) / HZ, (int *)oldval) || + (oldlenp && put_user(sizeof(int),oldlenp))) + return -EFAULT; + } + if (newval && newlen) { + int new; + if (newlen != sizeof(int)) + return -EINVAL; + if (get_user(new, (int *)newval)) + return -EFAULT; + *(int *)(table->data) = new*HZ; + } + return 1; +} + int do_string ( void *oldval, size_t *oldlenp, void *newval, size_t newlen, int rdwr, char *data, size_t max) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 6350b5b14bbd..e37eb6bd7eb0 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.87.2.4 1999/07/23 15:29:19 davem Exp $ + * Version: $Id: af_inet.c,v 1.87.2.5 1999/08/08 08:43:10 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -187,7 +187,6 @@ static __inline__ void kill_sk_later(struct sock *sk) atomic_read(&sk->rmem_alloc), atomic_read(&sk->wmem_alloc))); - sk->destroy = 1; sk->ack_backlog = 0; release_sock(sk); net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); @@ -202,9 +201,11 @@ void destroy_sock(struct sock *sk) */ net_delete_timer(sk); - if (sk->prot->destroy) + if (sk->prot->destroy && !sk->destroy) sk->prot->destroy(sk); + sk->destroy = 1; + kill_sk_queues(sk); /* Now if everything is gone we can free the socket diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 90fd7b6efcf6..06eb5fe58cd1 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.67.2.2 1999/07/23 15:29:26 davem Exp $ + * Version: $Id: route.c,v 1.67.2.3 1999/08/08 08:43:12 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -1927,16 +1927,30 @@ int ipv4_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp, return -EINVAL; } +static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, + void **context) +{ + int delay; + if (newlen != sizeof(int)) + return -EINVAL; + if (get_user(delay,(int *)newval)) + return -EFAULT; + rt_cache_flush(delay); + return 0; +} + ctl_table ipv4_route_table[] = { {NET_IPV4_ROUTE_FLUSH, "flush", - &flush_delay, sizeof(int), 0200, NULL, - &ipv4_sysctl_rtcache_flush}, + &flush_delay, sizeof(int), 0644, NULL, + &ipv4_sysctl_rtcache_flush, &ipv4_sysctl_rtcache_flush_strategy }, {NET_IPV4_ROUTE_MIN_DELAY, "min_delay", &ip_rt_min_delay, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_ROUTE_MAX_DELAY, "max_delay", &ip_rt_max_delay, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_ROUTE_GC_THRESH, "gc_thresh", &ipv4_dst_ops.gc_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -1945,13 +1959,13 @@ ctl_table ipv4_route_table[] = { &proc_dointvec}, {NET_IPV4_ROUTE_GC_MIN_INTERVAL, "gc_min_interval", &ip_rt_gc_min_interval, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_ROUTE_GC_TIMEOUT, "gc_timeout", &ip_rt_gc_timeout, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_ROUTE_GC_INTERVAL, "gc_interval", &ip_rt_gc_interval, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_ROUTE_REDIRECT_LOAD, "redirect_load", &ip_rt_redirect_load, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -1972,7 +1986,7 @@ ctl_table ipv4_route_table[] = { &proc_dointvec}, {NET_IPV4_ROUTE_MTU_EXPIRES, "mtu_expires", &ip_rt_mtu_expires, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {0} }; #endif diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 655176432476..fb4e8f8007e0 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: syncookies.c,v 1.7 1999/03/17 02:34:57 davem Exp $ + * $Id: syncookies.c,v 1.7.2.1 1999/08/08 08:43:13 davem Exp $ * * Missing: IPv6 support. */ @@ -145,7 +145,10 @@ cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) req->rmt_port = skb->h.th->source; req->af.v4_req.loc_addr = skb->nh.iph->daddr; req->af.v4_req.rmt_addr = skb->nh.iph->saddr; - req->class = &or_ipv4; /* for savety */ + req->class = &or_ipv4; /* for safety */ +#ifdef CONFIG_IP_TRANSPARENT_PROXY + req->lcl_port = skb->h.th->dest; +#endif req->af.v4_req.opt = NULL; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 10f5e9324dce..e578e4e7c49f 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,7 +1,7 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.38 1999/01/02 16:51:48 davem Exp $ + * $Id: sysctl_net_ipv4.c,v 1.38.2.1 1999/08/08 08:43:14 davem Exp $ * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] @@ -90,9 +90,23 @@ int ipv4_sysctl_forward(ctl_table *ctl, int write, struct file * filp, if (write && ipv4_devconf.forwarding != val) inet_forward_change(); - return ret; + return ret; } +static int ipv4_sysctl_forward_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, + void **context) +{ + int new; + if (newlen != sizeof(int)) + return -EINVAL; + if (get_user(new,(int *)newval)) + return -EFAULT; + if (new != ipv4_devconf.forwarding) + inet_forward_change(); + return 0; /* caller does change again and handles handles oldval */ +} ctl_table ipv4_table[] = { {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps", @@ -109,7 +123,7 @@ ctl_table ipv4_table[] = { &proc_dointvec}, {NET_IPV4_FORWARD, "ip_forward", &ipv4_devconf.forwarding, sizeof(int), 0644, NULL, - &ipv4_sysctl_forward}, + &ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy}, {NET_IPV4_DEFAULT_TTL, "ip_default_ttl", &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -132,12 +146,13 @@ ctl_table ipv4_table[] = { &sysctl_ip_masq_debug, sizeof(int), 0644, NULL, &proc_dointvec}, #endif {NET_IPV4_IPFRAG_TIME, "ipfrag_time", - &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, + &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies, + &sysctl_jiffies}, {NET_IPV4_TCP_MAX_KA_PROBES, "tcp_max_ka_probes", &sysctl_tcp_max_ka_probes, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_TCP_KEEPALIVE_TIME, "tcp_keepalive_time", &sysctl_tcp_keepalive_time, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV4_TCP_KEEPALIVE_PROBES, "tcp_keepalive_probes", &sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -148,7 +163,7 @@ ctl_table ipv4_table[] = { &sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV4_TCP_FIN_TIMEOUT, "tcp_fin_timeout", &sysctl_tcp_fin_timeout, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, #ifdef CONFIG_SYN_COOKIES {NET_TCP_SYNCOOKIES, "tcp_syncookies", &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec}, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c4e0e78cb9e0..9d644fc69911 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.140.2.2 1999/08/07 10:56:35 davem Exp $ + * Version: $Id: tcp.c,v 1.140.2.3 1999/08/08 08:43:16 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -725,6 +725,24 @@ static void wait_for_tcp_memory(struct sock * sk) lock_sock(sk); } +/* + * Wait for a buffer. + */ +static int wait_for_buffer(struct sock *sk) +{ + struct wait_queue wait = { current, NULL }; + int success = 0; + + release_sock(sk); + add_wait_queue(sk->sleep, &wait); + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); + lock_sock(sk); + return 0; +} + /* When all user supplied data has been queued set the PSH bit */ #define PSH_NEEDED (seglen == 0 && iovlen == 0) @@ -802,11 +820,21 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) tp->snd_nxt < TCP_SKB_CB(skb)->end_seq) { int last_byte_was_odd = (copy % 4); + /* + * Check for parallel writers sleeping in user access. + */ + if (tp->partial_writers++ > 0) { + wait_for_buffer(sk); + tp->partial_writers--; + continue; + } + copy = mss_now - copy; if(copy > skb_tailroom(skb)) copy = skb_tailroom(skb); if(copy > seglen) copy = seglen; + if(last_byte_was_odd) { if(copy_from_user(skb_put(skb, copy), from, copy)) @@ -819,6 +847,7 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) from, skb_put(skb, copy), copy, skb->csum, &err); } + /* * FIXME: the *_user functions should * return how much data was @@ -838,6 +867,10 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) seglen -= copy; if (PSH_NEEDED) TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH; + + if (--tp->partial_writers > 0) + wake_up_interruptible(sk->sleep); + continue; } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 024227f4ddd7..4c0fc202f6da 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.164.2.5 1999/06/30 09:27:05 davem Exp $ + * Version: $Id: tcp_input.c,v 1.164.2.6 1999/08/08 08:43:18 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -213,21 +213,19 @@ static __inline__ void tcp_bound_rto(struct tcp_opt *tp) extern __inline__ void tcp_replace_ts_recent(struct sock *sk, struct tcp_opt *tp, __u32 start_seq, __u32 end_seq) { - /* From draft-ietf-tcplw-high-performance: the correct - * test is last_ack_sent <= end_seq. - * (RFC1323 stated last_ack_sent < end_seq.) - * - * HOWEVER: The current check contradicts the draft statements. - * It has been done for good reasons. - * The implemented check improves security and eliminates - * unnecessary RTT overestimation. - * 1998/06/27 Andrey V. Savochkin + /* It is start_seq <= last_ack_seq combined + with in window check. If start_seq<=last_ack_seq<=rcv_nxt, + then segment is in window if end_seq>=rcv_nxt. */ - if (!before(end_seq, tp->last_ack_sent - sk->rcvbuf) && - !after(start_seq, tp->rcv_wup + tp->rcv_wnd)) { + if (!after(start_seq, tp->last_ack_sent) && + !before(end_seq, tp->rcv_nxt)) { /* PAWS bug workaround wrt. ACK frames, the PAWS discard * extra check below makes sure this can only happen * for pure ACK frames. -DaveM + * + * Plus: expired timestamps. + * + * Plus: resets failing PAWS. */ if((s32)(tp->rcv_tsval - tp->ts_recent) >= 0) { tp->ts_recent = tp->rcv_tsval; @@ -240,11 +238,10 @@ extern __inline__ void tcp_replace_ts_recent(struct sock *sk, struct tcp_opt *tp extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, unsigned len) { - /* ts_recent must be younger than 24 days */ - return (((s32)(tcp_time_stamp - tp->ts_recent_stamp) >= PAWS_24DAYS) || - (((s32)(tp->rcv_tsval - tp->ts_recent) < 0) && - /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */ - (len != (th->doff * 4)))); + return ((s32)(tp->rcv_tsval - tp->ts_recent) < 0 && + (s32)(tcp_time_stamp - tp->ts_recent_stamp) < PAWS_24DAYS && + /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */ + len != (th->doff * 4)); } @@ -946,11 +943,13 @@ void tcp_timewait_kill(struct tcp_tw_bucket *tw) /* We come here as a special case from the AF specific TCP input processing, * and the SKB has no owner. Essentially handling this is very simple, - * we just keep silently eating rx'd packets until none show up for the - * entire timeout period. The only special cases are for BSD TIME_WAIT - * reconnects and SYN/RST bits being set in the TCP header. + * we just keep silently eating rx'd packets, acking them if necessary, + * until none show up for the entire timeout period. + * + * Return 0, TCP_TW_ACK, TCP_TW_RST */ -int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, +enum tcp_tw_status +tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, struct tcphdr *th, unsigned len) { /* RFC 1122: @@ -971,7 +970,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, struct tcp_func *af_specific = tw->af_specific; __u32 isn; - isn = tw->rcv_nxt + 128000; + isn = tw->snd_nxt + 128000; if(isn == 0) isn++; tcp_tw_deschedule(tw); @@ -984,7 +983,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, skb_set_owner_r(skb, sk); af_specific = sk->tp_pinfo.af_tcp.af_specific; if(af_specific->conn_request(sk, skb, isn) < 0) - return 1; /* Toss a reset back. */ + return TCP_TW_RST; /* Toss a reset back. */ return 0; /* Discard the frame. */ } @@ -999,13 +998,17 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, tcp_timewait_kill(tw); } if(!th->rst) - return 1; /* toss a reset back */ + return TCP_TW_RST; /* toss a reset back */ + return 0; } else { /* In this case we must reset the TIMEWAIT timer. */ if(th->ack) tcp_tw_reschedule(tw); } - return 0; /* Discard the frame. */ + /* Ack old packets if necessary */ + if (!after(TCP_SKB_CB(skb)->end_seq, tw->rcv_nxt)) + return TCP_TW_ACK; + return 0; } /* Enter the time wait state. This is always called from BH @@ -1064,6 +1067,7 @@ void tcp_time_wait(struct sock *sk) tw->family = sk->family; tw->reuse = sk->reuse; tw->rcv_nxt = sk->tp_pinfo.af_tcp.rcv_nxt; + tw->snd_nxt = sk->tp_pinfo.af_tcp.snd_nxt; tw->af_specific = sk->tp_pinfo.af_tcp.af_specific; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -1535,7 +1539,6 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len) * Now tell the user we may have some data. */ if (!sk->dead) { - SOCK_DEBUG(sk, "Data wakeup.\n"); sk->data_ready(sk,0); } return(1); @@ -1975,7 +1978,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, flg &= __constant_htonl(0x00170000); /* Only SYN set? */ if (flg == __constant_htonl(0x00020000)) { - if (!after(TCP_SKB_CB(skb)->seq, req->rcv_isn)) { + if (TCP_SKB_CB(skb)->seq == req->rcv_isn) { /* retransmited syn. */ req->class->rtx_syn_ack(sk, req); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 03c41eb5ad1b..b67bfd827713 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.175.2.7 1999/07/23 15:38:46 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.175.2.8 1999/08/08 08:43:20 davem Exp $ * * IPv4 specific functions * @@ -178,6 +178,17 @@ static __inline__ void __tcp_inherit_port(struct sock *sk, struct sock *child) { struct tcp_bind_bucket *tb = (struct tcp_bind_bucket *)sk->prev; +#ifdef CONFIG_IP_TRANSPARENT_PROXY + if (child->num != sk->num) { + unsigned short snum = ntohs(child->num); + for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + tb && tb->port != snum; + tb = tb->next) + ; + if (tb == NULL) + tb = (struct tcp_bind_bucket *)sk->prev; + } +#endif if ((child->bind_next = tb->owners) != NULL) tb->owners->bind_pprev = &child->bind_next; tb->owners = child; @@ -1008,6 +1019,46 @@ static void tcp_v4_send_reset(struct sk_buff *skb) tcp_statistics.TcpOutRsts++; } +/* + * Send an ACK for a socket less packet (needed for time wait) + * + * FIXME: Does not echo timestamps yet. + * + * Assumes that the caller did basic address and flag checks. + */ +static void tcp_v4_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack) +{ + struct tcphdr *th = skb->h.th; + struct tcphdr rth; + struct ip_reply_arg arg; + + /* Swap the send and the receive. */ + memset(&rth, 0, sizeof(struct tcphdr)); + rth.dest = th->source; + rth.source = th->dest; + rth.doff = sizeof(struct tcphdr)/4; + + rth.seq = seq; + rth.ack_seq = ack; + rth.ack = 1; + + memset(&arg, 0, sizeof arg); + arg.iov[0].iov_base = (unsigned char *)&rth; + arg.iov[0].iov_len = sizeof rth; + arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr, + skb->nh.iph->saddr, /*XXX*/ + sizeof(struct tcphdr), + IPPROTO_TCP, + 0); + arg.n_iov = 1; + arg.csumoffset = offsetof(struct tcphdr, check) / 2; + + ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth); + + tcp_statistics.TcpOutSegs++; +} + + #ifdef CONFIG_IP_TRANSPARENT_PROXY /* @@ -1561,7 +1612,7 @@ static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb) sk = tcp_check_req(sk, skb, req); } #ifdef CONFIG_SYN_COOKIES - else { + else if (flg == __constant_htonl(0x00120000)) { sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt)); } #endif @@ -1721,10 +1772,19 @@ discard_it: return 0; do_time_wait: - if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk, - skb, th, skb->len)) - goto no_tcp_socket; - goto discard_it; + /* Sorry for the ugly switch. 2.3 will have a better solution. */ + switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk, + skb, th, skb->len)) { + case TCP_TW_ACK: + tcp_v4_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt, + ((struct tcp_tw_bucket *)sk)->rcv_nxt); + break; + case TCP_TW_RST: + goto no_tcp_socket; + default: + goto discard_it; + } + return 0; } static void __tcp_v4_rehash(struct sock *sk) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 75a887d295bd..834e8f28c65e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.104.2.5 1999/07/23 15:38:49 davem Exp $ + * $Id: tcp_ipv6.c,v 1.104.2.6 1999/08/08 08:43:23 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -1121,6 +1121,57 @@ static void tcp_v6_send_reset(struct sk_buff *skb) kfree_skb(buff); } +static void tcp_v6_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack) +{ + struct tcphdr *th = skb->h.th, *t1; + struct sk_buff *buff; + struct flowi fl; + + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); + if (buff == NULL) + return; + + skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); + + t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr)); + + /* Swap the send and the receive. */ + memset(t1, 0, sizeof(*t1)); + t1->dest = th->source; + t1->source = th->dest; + t1->doff = sizeof(*t1)/4; + t1->ack = 1; + t1->seq = seq; + t1->ack_seq = ack; + + buff->csum = csum_partial((char *)t1, sizeof(*t1), 0); + + fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr; + fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr; + fl.fl6_flowlabel = 0; + + t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr, + fl.nl_u.ip6_u.daddr, + sizeof(*t1), IPPROTO_TCP, + buff->csum); + + fl.proto = IPPROTO_TCP; + fl.oif = tcp_v6_iif(skb); + fl.uli_u.ports.dport = t1->dest; + fl.uli_u.ports.sport = t1->source; + + /* sk = NULL, but it is safe for now. static socket required. */ + buff->dst = ip6_route_output(NULL, &fl); + + if (buff->dst->error == 0) { + ip6_xmit(NULL, buff, &fl, NULL); + tcp_statistics.TcpOutSegs++; + return; + } + + kfree_skb(buff); +} + static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, struct ipv6hdr *ip6h, struct tcphdr *th, @@ -1409,10 +1460,19 @@ discard_it: return 0; do_time_wait: - if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk, - skb, th, skb->len)) + switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk, + skb, th, skb->len)) { + case TCP_TW_ACK: + tcp_v6_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt, + ((struct tcp_tw_bucket *)sk)->rcv_nxt); + break; + case TCP_TW_RST: goto no_tcp_socket; - goto discard_it; + default: + goto discard_it; + } + + return 0; } static int tcp_v6_rebuild_header(struct sock *sk) diff --git a/net/netsyms.c b/net/netsyms.c index a7ab547bc429..6d63862b9e57 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_HIPPI #include #endif @@ -177,6 +178,7 @@ EXPORT_SYMBOL(neigh_destroy); EXPORT_SYMBOL(neigh_parms_alloc); EXPORT_SYMBOL(neigh_parms_release); EXPORT_SYMBOL(neigh_rand_reach_time); +EXPORT_SYMBOL(neigh_compat_output); /* dst_entry */ EXPORT_SYMBOL(dst_alloc); @@ -238,6 +240,7 @@ EXPORT_SYMBOL(__ip_finish_output); EXPORT_SYMBOL(inet_dgram_ops); EXPORT_SYMBOL(ip_cmsg_recv); EXPORT_SYMBOL(__release_sock); +EXPORT_SYMBOL(inet_addr_type); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); @@ -321,7 +324,6 @@ EXPORT_SYMBOL(tcp_inherit_port); EXPORT_SYMBOL(tcp_v4_syn_recv_sock); EXPORT_SYMBOL(tcp_v4_do_rcv); EXPORT_SYMBOL(tcp_v4_connect); -EXPORT_SYMBOL(inet_addr_type); EXPORT_SYMBOL(net_reset_timer); EXPORT_SYMBOL(net_delete_timer); EXPORT_SYMBOL(udp_prot); -- 2.39.5