* [PATCH 1/2] add driver for enc28j60 ethernet chip
From: Claudio Lanconelli @ 2007-12-11 14:57 UTC (permalink / raw)
To: netdev; +Cc: jgarzik
[-- Attachment #1: Type: text/plain, Size: 237 bytes --]
These patches add support for Microchip enc28j60 ethernet chip
controlled via SPI.
I tested it on my custom board (S162) with ARM9 s3c2442 SoC.
Any comments are welcome.
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
[-- Attachment #2: enc28j60_p1.diff --]
[-- Type: text/x-patch, Size: 51496 bytes --]
drivers/net/enc28j60.c | 1400 +++++++++++++++++++++++++++++++++++++++++++++
drivers/net/enc28j60_hw.h | 303 ++++++++++
2 files changed, 1703 insertions(+), 0 deletions(-)
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
new file mode 100644
index 0000000..6182473
--- /dev/null
+++ b/drivers/net/enc28j60.c
@@ -0,0 +1,1400 @@
+/*
+ * Microchip ENC28J60 ethernet driver (MAC + PHY)
+ *
+ * Copyright (C) 2007 Eurek srl
+ * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
+ * based on enc28j60.c written by David Anders for 2.4 kernel version
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: enc28j60.c,v 1.10 2007/12/10 16:59:37 claudio Exp $
+ */
+
+#include <linux/autoconf.h>
+
+#if CONFIG_ENC28J60_DBGLEVEL > 1
+# define VERBOSE_DEBUG
+#endif
+#if CONFIG_ENC28J60_DBGLEVEL > 0
+# define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <asm/semaphore.h>
+
+#include "enc28j60_hw.h"
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN)
+
+#define MY_TX_TIMEOUT ((500*HZ)/1000)
+
+/* Max TX retries in case of collision as suggested by errata datasheet */
+#define MAX_TX_RETRYCOUNT 16
+
+/* Driver local data */
+struct enc28j60_net_local {
+ struct net_device_stats stats;
+
+ struct net_device *netdev;
+ struct spi_device *spi;
+ struct semaphore semlock; /* protect spi_transfer_buf */
+ uint8_t *spi_transfer_buf;
+ struct sk_buff *tx_skb;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ int bank; /* current register bank selected */
+ uint16_t next_pk_ptr; /* next packet pointer within FIFO */
+ int max_pk_counter; /* statistics: max packet counter */
+ int tx_retry_count;
+ int hw_enable;
+};
+
+/* Selects Full duplex vs. Half duplex mode */
+static int full_duplex = 0;
+
+static int enc28j60_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int enc28j60_net_close(struct net_device *dev);
+static struct net_device_stats *enc28j60_net_get_stats(struct net_device *dev);
+static void enc28j60_set_multicast_list(struct net_device *dev);
+static void enc28j60_net_tx_timeout(struct net_device *ndev);
+
+static int enc28j60_chipset_init(struct net_device *dev);
+static void enc28j60_hw_disable(struct enc28j60_net_local *priv);
+static void enc28j60_hw_enable(struct enc28j60_net_local *priv);
+static void enc28j60_hw_rx(struct enc28j60_net_local *priv);
+static void enc28j60_hw_tx(struct enc28j60_net_local *priv);
+
+/* Basic SPI operations */
+static int spi_read_buf(struct enc28j60_net_local *priv, int len,
+ uint8_t *data);
+static int spi_write_buf(struct enc28j60_net_local *priv, int len,
+ const uint8_t * data);
+static uint8_t spi_read_op(struct enc28j60_net_local *priv, uint8_t op,
+ uint8_t addr);
+static int spi_write_op(struct enc28j60_net_local *priv, uint8_t op,
+ uint8_t addr, uint8_t val);
+
+/* Low level routines */
+
+/* utility function for register access routines */
+static void enc28j60_set_bank(struct enc28j60_net_local *priv, uint8_t address);
+
+static inline int enc28j60_regb_read(struct enc28j60_net_local *priv,
+ uint8_t address);
+static inline int enc28j60_regw_read(struct enc28j60_net_local *priv,
+ uint8_t address);
+static inline void enc28j60_regb_write(struct enc28j60_net_local *priv,
+ uint8_t address, uint8_t data);
+static inline void enc28j60_regw_write(struct enc28j60_net_local *priv,
+ uint8_t address, uint16_t data);
+
+static inline void enc28j60_reg_bfset(struct enc28j60_net_local *priv,
+ uint8_t reg, uint8_t mask);
+static inline void enc28j60_reg_bfclr(struct enc28j60_net_local *priv,
+ uint8_t reg, uint8_t mask);
+
+static void enc28j60_soft_reset(struct enc28j60_net_local *priv);
+
+/* debug routines */
+static void dump_packet(struct enc28j60_net_local *priv, const char *msg,
+ int len, const char *data);
+static void enc28j60_dump_tsv(struct enc28j60_net_local *priv, const char *msg,
+ uint8_t tsv[TSV_SIZE]);
+static void enc28j60_dump_rsv(struct enc28j60_net_local *priv, const char *msg,
+ uint16_t pk_ptr, int len, uint16_t sts);
+static void enc28j60_dump_regs(struct enc28j60_net_local *priv,
+ const char *msg);
+
+/*
+ * SPI read buffer
+ * wait for the SPI transfer and copy received data to destination
+ */
+static int
+spi_read_buf(struct enc28j60_net_local *priv, int len, uint8_t *data)
+{
+ uint8_t *rx_buf;
+ uint8_t *tx_buf;
+ struct spi_transfer t;
+ struct spi_message msg;
+ int ret, slen;
+
+ slen = 1;
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = tx_buf = priv->spi_transfer_buf;
+ t.rx_buf = rx_buf = priv->spi_transfer_buf + 4;
+ t.len = slen + len;
+
+ down(&priv->semlock);
+ tx_buf[0] = ENC28J60_READ_BUF_MEM;
+ tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&t, &msg);
+ ret = spi_sync(priv->spi, &msg);
+ if (ret == 0) {
+ memcpy(data, &rx_buf[slen], len);
+ ret = msg.status;
+ }
+ up(&priv->semlock);
+ if (ret != 0)
+ dev_dbg(&priv->netdev->dev, "%s: failed: ret = %d\n",
+ __FUNCTION__, ret);
+
+ return ret;
+}
+
+/*
+ * SPI write buffer
+ */
+static int spi_write_buf(struct enc28j60_net_local *priv, int len,
+ const uint8_t * data)
+{
+ int ret;
+
+ if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+ ret = -EINVAL;
+ else {
+ down(&priv->semlock);
+ priv->spi_transfer_buf[0] = ENC28J60_WRITE_BUF_MEM;
+ memcpy(&priv->spi_transfer_buf[1], data, len);
+ ret = spi_write(priv->spi, priv->spi_transfer_buf, len + 1);
+ up(&priv->semlock);
+ if (ret != 0)
+ dev_dbg(&priv->netdev->dev, "%s: failed: ret = %d\n",
+ __FUNCTION__, ret);
+ }
+ return ret;
+}
+
+/*
+ * basic SPI read operation
+ */
+static uint8_t spi_read_op(struct enc28j60_net_local *priv, uint8_t op,
+ uint8_t addr)
+{
+ uint8_t tx_buf[2];
+ uint8_t rx_buf[4];
+ uint8_t val = 0;
+ int ret;
+ int slen;
+
+ slen = 1;
+ /* do dummy read if needed */
+ if (addr & SPRD_MASK)
+ slen++;
+
+ tx_buf[0] = op | (addr & ADDR_MASK);
+ ret = spi_write_then_read(priv->spi, tx_buf, 1, rx_buf, slen);
+ if (ret != 0)
+ dev_err(&priv->netdev->dev, "%s: failed: ret = %d\n",
+ __FUNCTION__, ret);
+ else
+ val = rx_buf[slen - 1];
+
+ return val;
+}
+
+/*
+ * basic SPI write operation
+ */
+static int spi_write_op(struct enc28j60_net_local *priv, uint8_t op,
+ uint8_t addr, uint8_t val)
+{
+ int ret;
+
+ down(&priv->semlock);
+ priv->spi_transfer_buf[0] = op | (addr & ADDR_MASK);
+ priv->spi_transfer_buf[1] = val;
+ ret = spi_write(priv->spi, priv->spi_transfer_buf, 2);
+ up(&priv->semlock);
+ if (ret != 0)
+ dev_dbg(&priv->netdev->dev, "%s: failed: ret = %d\n",
+ __FUNCTION__, ret);
+ return ret;
+}
+
+/*
+ * Issue a soft reset command
+ */
+static void enc28j60_soft_reset(struct enc28j60_net_local *priv)
+{
+ dev_vdbg(&priv->netdev->dev, "%s\n", __FUNCTION__);
+
+ spi_write_op(priv, ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
+ /* Errata workaround #1, CLKRDY check is unreliable,
+ * delay 1 mS instead */
+ udelay(1000);
+}
+
+/*
+ * select the current register bank if necessary
+ */
+static void enc28j60_set_bank(struct enc28j60_net_local *priv, uint8_t addr)
+{
+ if ((addr & BANK_MASK) != priv->bank) {
+ uint8_t b = (addr & BANK_MASK) >> 5;
+
+ if (b != (ECON1_BSEL1 | ECON1_BSEL0))
+ spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
+ ECON1_BSEL1 | ECON1_BSEL0);
+ if (b != 0)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, b);
+ priv->bank = (addr & BANK_MASK);
+ }
+}
+
+/*
+ * Register bit field Set
+ */
+static inline void enc28j60_reg_bfset(struct enc28j60_net_local *priv,
+ uint8_t addr, uint8_t mask)
+{
+ enc28j60_set_bank(priv, addr);
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, addr, mask);
+}
+
+/*
+ * Register bit field Clear
+ */
+static inline void enc28j60_reg_bfclr(struct enc28j60_net_local *priv,
+ uint8_t addr, uint8_t mask)
+{
+ enc28j60_set_bank(priv, addr);
+ spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, addr, mask);
+}
+
+/*
+ * Register byte read
+ */
+static inline int enc28j60_regb_read(struct enc28j60_net_local *priv,
+ uint8_t address)
+{
+ enc28j60_set_bank(priv, address);
+ return spi_read_op(priv, ENC28J60_READ_CTRL_REG, address);
+}
+
+/*
+ * Register word read
+ */
+static inline int enc28j60_regw_read(struct enc28j60_net_local *priv,
+ uint8_t address)
+{
+ int rl, rh;
+
+ enc28j60_set_bank(priv, address);
+ rl = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address);
+ rh = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address + 1);
+
+ return (rh << 8) | rl;
+}
+
+/*
+ * Register byte write
+ */
+static inline void enc28j60_regb_write(struct enc28j60_net_local *priv,
+ uint8_t address, uint8_t data)
+{
+ enc28j60_set_bank(priv, address);
+ spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, data);
+}
+
+/*
+ * Register word write
+ */
+static inline void enc28j60_regw_write(struct enc28j60_net_local *priv,
+ uint8_t address, uint16_t data)
+{
+ enc28j60_set_bank(priv, address);
+ spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, (uint8_t) data);
+ spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address + 1,
+ (uint8_t) (data >> 8));
+}
+
+/*
+ * Buffer memory read
+ * Select the starting address and execute a SPI buffer read
+ */
+static inline void enc28j60_mem_read(struct enc28j60_net_local *priv,
+ uint16_t addr, int len, uint8_t * data)
+{
+ enc28j60_regw_write(priv, ERDPTL, addr);
+
+#ifdef CONFIG_ENC28J60_WRITEVERIFY
+ {
+ uint16_t reg;
+ reg = enc28j60_regw_read(priv, ERDPTL);
+ if (reg != addr) {
+ dev_dbg(&priv->netdev->dev,
+ "%s() error writing ERDPT (0x%04x - 0x%04x)\n",
+ __FUNCTION__, reg, addr);
+ }
+ }
+#endif
+ spi_read_buf(priv, len, data);
+}
+
+/*
+ * Wait until the PHY operation is complete.
+ */
+static inline int wait_phy_ready(struct enc28j60_net_local *priv)
+{
+ unsigned long timeout = jiffies + 20 * HZ / 1000;
+ int ret = 1;
+
+ /* 20msec timeout read */
+ while ((enc28j60_regb_read(priv, MISTAT) & MISTAT_BUSY) != 0) {
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(&priv->netdev->dev,
+ "enc28j60: PHY ready timeout!\n");
+ ret = 0;
+ break;
+ }
+ cpu_relax();
+ }
+ return ret;
+}
+
+/*
+ * PHY register read
+ * PHY registers are not accessed directly
+ */
+static uint16_t enc28j60_phy_read(struct enc28j60_net_local *priv,
+ uint8_t address)
+{
+ /* set the PHY register address */
+ enc28j60_regb_write(priv, MIREGADR, address);
+ /* start the register read operation */
+ enc28j60_regb_write(priv, MICMD, MICMD_MIIRD);
+ udelay(11);
+ /* wait until the PHY read completes */
+ wait_phy_ready(priv);
+ /* quit reading */
+ enc28j60_regb_write(priv, MICMD, 0x00);
+ /* return the data */
+ return enc28j60_regw_read(priv, MIRDL);
+}
+
+static int enc28j60_phy_write(struct enc28j60_net_local *priv, uint8_t address,
+ uint16_t data)
+{
+ /* set the PHY register address */
+ enc28j60_regb_write(priv, MIREGADR, address);
+ /* write the PHY data */
+ enc28j60_regw_write(priv, MIWRL, data);
+ udelay(11);
+ /* wait until the PHY write completes and return */
+ return wait_phy_ready(priv);
+}
+
+/*
+ * read MAC address registers
+ */
+static void enc28j60_get_hw_macaddr(struct enc28j60_net_local *priv)
+{
+ struct net_device *ndev = priv->netdev;
+
+ /* NOTE: MAC address in ENC28J60 is byte-backward */
+ ndev->dev_addr[0] = enc28j60_regb_read(priv, MAADR5);
+ ndev->dev_addr[1] = enc28j60_regb_read(priv, MAADR4);
+ ndev->dev_addr[2] = enc28j60_regb_read(priv, MAADR3);
+ ndev->dev_addr[3] = enc28j60_regb_read(priv, MAADR2);
+ ndev->dev_addr[4] = enc28j60_regb_read(priv, MAADR1);
+ ndev->dev_addr[5] = enc28j60_regb_read(priv, MAADR0);
+
+ dev_dbg(&priv->spi->dev,
+ "%s() Get MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ __FUNCTION__, ndev->dev_addr[0], ndev->dev_addr[1],
+ ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4],
+ ndev->dev_addr[5]);
+}
+
+/*
+ * Program the hardware MAC address from dev->dev_addr.
+ */
+static void enc28j60_set_hw_macaddr(struct enc28j60_net_local *priv)
+{
+ struct net_device *ndev = priv->netdev;
+
+ if (!priv->hw_enable) {
+ /* NOTE: MAC address in ENC28J60 is byte-backward */
+ enc28j60_regb_write(priv, MAADR5, ndev->dev_addr[0]);
+ enc28j60_regb_write(priv, MAADR4, ndev->dev_addr[1]);
+ enc28j60_regb_write(priv, MAADR3, ndev->dev_addr[2]);
+ enc28j60_regb_write(priv, MAADR2, ndev->dev_addr[3]);
+ enc28j60_regb_write(priv, MAADR1, ndev->dev_addr[4]);
+ enc28j60_regb_write(priv, MAADR0, ndev->dev_addr[5]);
+
+ dev_dbg(&ndev->dev,
+ "%s() [%s] Setting MAC address to "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __FUNCTION__, ndev->name, ndev->dev_addr[0],
+ ndev->dev_addr[1], ndev->dev_addr[2], ndev->dev_addr[3],
+ ndev->dev_addr[4], ndev->dev_addr[5]);
+ } else
+ dev_dbg(&ndev->dev,
+ "%s() Warning: hw must be disabled to set hw "
+ "Mac address\n", __FUNCTION__);
+}
+
+/*
+ * Store the new hardware address in dev->dev_addr, and update the MAC.
+ */
+static int enc28j60_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ enc28j60_set_hw_macaddr(priv);
+
+ return 0;
+}
+
+static void enc28j60_dump_regs(struct enc28j60_net_local *priv, const char *msg)
+{
+ dev_dbg(&priv->spi->dev, "%s - HwRevID: 0x%02x\n", msg,
+ enc28j60_regb_read(priv, EREVID));
+
+#if CONFIG_ENC28J60_DBGLEVEL > 2
+ printk("Cntrl: ECON1 ECON2 ESTAT EIR EIE\n");
+ printk(" 0x%02x ", enc28j60_regb_read(priv, ECON1));
+ printk("0x%02x ", enc28j60_regb_read(priv, ECON2));
+ printk("0x%02x ", enc28j60_regb_read(priv, ESTAT));
+ printk("0x%02x ", enc28j60_regb_read(priv, EIR));
+ printk("0x%02x\n", enc28j60_regb_read(priv, EIE));
+
+ printk("MAC : MACON1 MACON3 MACON4 MAC-Address\n");
+ printk(" 0x%02x ", enc28j60_regb_read(priv, MACON1));
+ printk("0x%02x ", enc28j60_regb_read(priv, MACON3));
+ printk("0x%02x ", enc28j60_regb_read(priv, MACON4));
+ printk("%02x:", enc28j60_regb_read(priv, MAADR5));
+ printk("%02x:", enc28j60_regb_read(priv, MAADR4));
+ printk("%02x:", enc28j60_regb_read(priv, MAADR3));
+ printk("%02x:", enc28j60_regb_read(priv, MAADR2));
+ printk("%02x:", enc28j60_regb_read(priv, MAADR1));
+ printk("%02x\n", enc28j60_regb_read(priv, MAADR0));
+
+ printk("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\n");
+ printk(" 0x%04x ", enc28j60_regw_read(priv, ERXSTL));
+ printk("0x%04x ", enc28j60_regw_read(priv, ERXNDL));
+ printk("0x%04x ", enc28j60_regw_read(priv, ERXWRPTL));
+ printk("0x%04x ", enc28j60_regw_read(priv, ERXRDPTL));
+ printk("0x%02x ", enc28j60_regb_read(priv, ERXFCON));
+ printk("0x%02x ", enc28j60_regb_read(priv, EPKTCNT));
+ printk("0x%04x\n", enc28j60_regw_read(priv, MAMXFLL));
+
+ printk("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\n");
+ printk(" 0x%04x ", enc28j60_regw_read(priv, ETXSTL));
+ printk("0x%04x ", enc28j60_regw_read(priv, ETXNDL));
+ printk("0x%02x ", enc28j60_regb_read(priv, MACLCON1));
+ printk("0x%02x ", enc28j60_regb_read(priv, MACLCON2));
+ printk("0x%02x\n", enc28j60_regb_read(priv, MAPHSUP));
+#endif
+}
+
+/*
+ * ERXRDPT need to be set always at odd addresses, refer to errata datasheet
+ */
+static inline uint16_t erxrdpt_workaround(uint16_t next_packet_ptr,
+ uint16_t start, uint16_t end)
+{
+ uint16_t erxrdpt;
+
+ if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) {
+ erxrdpt = end;
+ } else
+ erxrdpt = next_packet_ptr - 1;
+
+ return erxrdpt;
+}
+
+static void enc28j60_rxfifo_init(struct enc28j60_net_local *priv,
+ uint16_t start, uint16_t end)
+{
+ uint16_t erxrdpt;
+
+ if (start > 0x1FFF || end > 0x1FFF || start > end)
+ dev_err(&priv->netdev->dev,
+ "%s(%d, %d) RXFIFO bad parameters, fatal error!!\n",
+ __FUNCTION__, start, end);
+
+ priv->next_pk_ptr = start;
+ /* set receive buffer start */
+ enc28j60_regw_write(priv, ERXSTL, start);
+ /* set receive pointer address */
+ erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end);
+ enc28j60_regw_write(priv, ERXRDPTL, erxrdpt);
+ /* set receive buffer end */
+ enc28j60_regw_write(priv, ERXNDL, end);
+}
+
+static void enc28j60_txfifo_init(struct enc28j60_net_local *priv,
+ uint16_t start, uint16_t end)
+{
+ if (start > 0x1FFF || end > 0x1FFF || start > end)
+ dev_err(&priv->netdev->dev,
+ "%s(%d, %d) TXFIFO bad parameters, fatal error!!\n",
+ __FUNCTION__, start, end);
+
+ /* set transmit buffer start */
+ enc28j60_regw_write(priv, ETXSTL, start);
+ /* set transmit buffer end */
+ enc28j60_regw_write(priv, ETXNDL, end);
+}
+
+static int enc28j60_hw_init(struct enc28j60_net_local *priv)
+{
+ uint8_t reg;
+
+ dev_dbg(&priv->spi->dev, "%s() - %s\n",
+ __FUNCTION__, full_duplex ? "FullDuplex" : "HalfDuplex");
+ /* first soft reset the chip */
+ enc28j60_soft_reset(priv);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank0\n", __FUNCTION__);
+
+ /* Clear ECON1 */
+ spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, ECON1, 0x00);
+ priv->bank = 0;
+ priv->hw_enable = 0;
+ priv->tx_retry_count = 0;
+
+ enc28j60_regb_write(priv, ECON2, ECON2_AUTOINC);
+ enc28j60_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
+ enc28j60_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
+
+ /*
+ * Check the RevID.
+ * If it's 0x00 or 0xFF probably the enc28j60 is not mounted or
+ * damaged
+ */
+ reg = enc28j60_regb_read(priv, EREVID);
+ if (reg == 0x00 || reg == 0xff)
+ return 0;
+
+ dev_vdbg(&priv->spi->dev, "%s() bank1\n", __FUNCTION__);
+
+ /* default filter mode: (unicast OR broadcast) AND crc valid */
+ enc28j60_regb_write(priv, ERXFCON,
+ ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank2\n", __FUNCTION__);
+ /* enable MAC receive */
+ enc28j60_regb_write(priv, MACON1,
+ MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
+ /* enable automatic padding and CRC operations */
+ if (full_duplex) {
+ enc28j60_regb_write(priv, MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN |
+ MACON3_FRMLNEN | MACON3_FULDPX);
+ /* set inter-frame gap (non-back-to-back) */
+ enc28j60_regb_write(priv, MAIPGL, 0x12);
+ /* set inter-frame gap (back-to-back) */
+ enc28j60_regb_write(priv, MABBIPG, 0x15);
+ } else {
+ enc28j60_regb_write(priv, MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN |
+ MACON3_FRMLNEN);
+ enc28j60_regb_write(priv, MACON4, 1 << 6); /* DEFER bit */
+ /* set inter-frame gap (non-back-to-back) */
+ enc28j60_regw_write(priv, MAIPGL, 0x0C12);
+ /* set inter-frame gap (back-to-back) */
+ enc28j60_regb_write(priv, MABBIPG, 0x12);
+ }
+ /*
+ * MACLCON1 (default)
+ * MACLCON2 (default)
+ * Set the maximum packet size which the controller will accept
+ */
+ enc28j60_regw_write(priv, MAMXFLL, MAX_FRAMELEN);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank3\n", __FUNCTION__);
+ /* NOTE: MAC address in ENC28J60 is byte-backward */
+ enc28j60_regb_write(priv, MAADR5, ENC28J60_MAC0);
+ enc28j60_regb_write(priv, MAADR4, ENC28J60_MAC1);
+ enc28j60_regb_write(priv, MAADR3, ENC28J60_MAC2);
+ enc28j60_regb_write(priv, MAADR2, ENC28J60_MAC3);
+ enc28j60_regb_write(priv, MAADR1, ENC28J60_MAC4);
+ enc28j60_regb_write(priv, MAADR0, ENC28J60_MAC5);
+
+ /* no loopback of transmitted frames */
+ dev_vdbg(&priv->spi->dev, "%s() PHY\n", __FUNCTION__);
+
+ /* Configure LEDs */
+ if (!enc28j60_phy_write(priv, PHLCON, ENC28J60_LAMPS_MODE))
+ return 0;
+
+ if (full_duplex) {
+ if (!enc28j60_phy_write(priv, PHCON1, PHCON1_PDPXMD))
+ return 0;
+ if (!enc28j60_phy_write(priv, PHCON2, 0x00))
+ return 0;
+ } else {
+ if (!enc28j60_phy_write(priv, PHCON1, 0x00))
+ return 0;
+ if (!enc28j60_phy_write(priv, PHCON2, PHCON2_HDLDIS))
+ return 0;
+ }
+ enc28j60_dump_regs(priv, "enc28j60 initialized");
+
+ return 1;
+}
+
+static void enc28j60_hw_enable(struct enc28j60_net_local *priv)
+{
+ /* enable interrutps */
+ dev_dbg(&priv->netdev->dev, "%s() enabling interrupts!\n",
+ __FUNCTION__);
+
+ enc28j60_reg_bfclr(priv, EIR, EIR_DMAIF | EIR_LINKIF |
+ EIR_TXIF | EIR_TXERIF | EIR_RXERIF | EIR_PKTIF);
+ enc28j60_regb_write(priv, EIE, EIE_INTIE | EIE_PKTIE |
+ EIE_TXIE | EIE_TXERIE | EIE_RXERIE);
+
+ /* enable receive logic */
+ enc28j60_reg_bfset(priv, ECON1, ECON1_RXEN);
+ priv->hw_enable = 1;
+}
+
+static void enc28j60_hw_disable(struct enc28j60_net_local *priv)
+{
+ /* disable interrutps */
+ enc28j60_regb_write(priv, EIE, 0x00);
+ /* disable packet reception */
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_RXEN);
+ priv->hw_enable = 0;
+}
+
+/*
+ * Read the Transmit Status Vector
+ */
+static inline void enc28j60_read_tsv(struct enc28j60_net_local *priv,
+ uint8_t tsv[TSV_SIZE])
+{
+ int endptr;
+
+ endptr = enc28j60_regw_read(priv, ETXNDL);
+ dev_vdbg(&priv->netdev->dev, "reading TSV at addr:0x%04x\n",
+ endptr + 1);
+ enc28j60_mem_read(priv, endptr + 1, sizeof(tsv), tsv);
+}
+
+static void enc28j60_dump_tsv(struct enc28j60_net_local *priv, const char *msg,
+ uint8_t tsv[TSV_SIZE])
+{
+ struct net_device *ndev = priv->netdev;
+ uint16_t tmp1, tmp2;
+
+ dev_vdbg(&ndev->dev, "%s - TSV:\n", msg);
+ tmp1 = tsv[1];
+ tmp1 <<= 8;
+ tmp1 |= tsv[0];
+
+ tmp2 = tsv[5];
+ tmp2 <<= 8;
+ tmp2 |= tsv[4];
+
+ dev_vdbg(&ndev->dev,
+ "ByteCount: %d, CollisionCount: %d, TotByteOnWire: %d\n", tmp1,
+ tsv[2] & 0x0f, tmp2);
+ dev_vdbg(&ndev->dev,
+ "TxDone: %d, CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
+ TSV_GETBIT(tsv, TSV_TXDONE), TSV_GETBIT(tsv, TSV_TXCRCERROR),
+ TSV_GETBIT(tsv, TSV_TXLENCHKERROR),
+ TSV_GETBIT(tsv, TSV_TXLENOUTOFRANGE));
+ dev_vdbg(&ndev->dev,
+ "Multicast: %d, Broadcast: %d, PacketDefer: %d, ExDefer: %d\n",
+ TSV_GETBIT(tsv, TSV_TXMULTICAST),
+ TSV_GETBIT(tsv, TSV_TXBROADCAST),
+ TSV_GETBIT(tsv, TSV_TXPACKETDEFER),
+ TSV_GETBIT(tsv, TSV_TXEXDEFER));
+ dev_vdbg(&ndev->dev,
+ "ExCollision: %d, LateCollision: %d, Giant: %d, Underrun: %d\n",
+ TSV_GETBIT(tsv, TSV_TXEXCOLLISION),
+ TSV_GETBIT(tsv, TSV_TXLATECOLLISION),
+ TSV_GETBIT(tsv, TSV_TXGIANT), TSV_GETBIT(tsv, TSV_TXUNDERRUN));
+ dev_vdbg(&ndev->dev,
+ "ControlFrame: %d, PauseFrame: %d, "
+ "BackPressApp: %d, VLanTagFrame: %d\n",
+ TSV_GETBIT(tsv, TSV_TXCONTROLFRAME),
+ TSV_GETBIT(tsv, TSV_TXPAUSEFRAME),
+ TSV_GETBIT(tsv, TSV_BACKPRESSUREAPP),
+ TSV_GETBIT(tsv, TSV_TXVLANTAGFRAME));
+}
+
+/*
+ * Calculate free space in RxFIFO
+ */
+static inline int enc28j60_get_free_rxfifo(struct enc28j60_net_local *priv)
+{
+ int epkcnt, erxst, erxnd, erxwr, erxrd;
+ int free_space;
+
+ epkcnt = enc28j60_regb_read(priv, EPKTCNT);
+ if (epkcnt >= 255)
+ free_space = -1;
+ else {
+ erxst = enc28j60_regw_read(priv, ERXSTL);
+ erxnd = enc28j60_regw_read(priv, ERXNDL);
+ erxwr = enc28j60_regw_read(priv, ERXWRPTL);
+ erxrd = enc28j60_regw_read(priv, ERXRDPTL);
+
+ if (erxwr > erxrd)
+ free_space = (erxnd - erxst) - (erxwr - erxrd);
+ else if (erxwr == erxrd)
+ free_space = (erxnd - erxst);
+ else
+ free_space = erxrd - erxwr - 1;
+ }
+ dev_vdbg(&priv->netdev->dev, "%s() free_space = %d\n", __FUNCTION__,
+ free_space);
+
+ return free_space;
+}
+
+static void enc28j60_irq_work_handler(struct work_struct *work)
+{
+ struct enc28j60_net_local *priv =
+ container_of(work, struct enc28j60_net_local, irq_work);
+ int intflags, loop, pk_counter;
+
+ dev_vdbg(&priv->netdev->dev, "%s(priv=%p)\n", __FUNCTION__, priv);
+
+ /* disable further interrupts */
+ enc28j60_reg_bfclr(priv, EIE, EIE_INTIE);
+
+ do {
+ loop = 0;
+ intflags = enc28j60_regb_read(priv, EIR);
+ /* DMA interrupt handler */
+ if ((intflags & EIR_DMAIF) != 0) {
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intDMA(%d)\n", loop);
+ enc28j60_reg_bfclr(priv, EIR, EIR_DMAIF);
+ }
+ /* LINK changed handler */
+ if ((intflags & EIR_LINKIF) != 0) {
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intLINK(%d)\n", loop);
+ /* read PHIR to clear the flag */
+ enc28j60_phy_read(priv, PHIR);
+ }
+ /* TX complete handler */
+ if ((intflags & EIR_TXIF) != 0) {
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intTX(%d,skb:%p)\n", loop,
+ priv->tx_skb);
+
+ priv->tx_retry_count = 0;
+ if (enc28j60_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
+ dev_err(&priv->netdev->dev,
+ "enc28j60 tx error (aborted)\n");
+ priv->stats.tx_errors++;
+ } else
+ priv->stats.tx_packets++;
+ if (priv->tx_skb) {
+ /* update statistics and free skb */
+ priv->stats.tx_bytes += priv->tx_skb->len;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(priv->netdev);
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_TXRTS);
+ enc28j60_reg_bfclr(priv, EIR, EIR_TXIF);
+ }
+ /* TX Error handler */
+ if ((intflags & EIR_TXERIF) != 0) {
+ uint8_t tsv[TSV_SIZE];
+
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intTXErr(%d)\n", loop);
+
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_TXRTS);
+ enc28j60_read_tsv(priv, tsv);
+ enc28j60_dump_tsv(priv, __FUNCTION__, tsv);
+ /* Reset TX logic */
+ enc28j60_reg_bfset(priv, ECON1, ECON1_TXRST);
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_TXRST);
+ /* Transmit Late collision check for retransmit */
+ if (TSV_GETBIT(tsv, TSV_TXLATECOLLISION)) {
+ if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
+ enc28j60_reg_bfset(priv, ECON1,
+ ECON1_TXRTS);
+ else
+ priv->stats.tx_errors++;
+ } else
+ priv->stats.tx_errors++;
+ /* Clear IF flag */
+ enc28j60_reg_bfclr(priv, EIR, EIR_TXERIF);
+ }
+ /* RX Error handler */
+ if ((intflags & EIR_RXERIF) != 0) {
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intRXErr(%d)\n", loop);
+
+ /* Check free FIFO space to flag RX overrun */
+ if (enc28j60_get_free_rxfifo(priv) <= 0) {
+ dev_err(&priv->netdev->dev,
+ "enc28j60 RX overrun\n");
+ priv->stats.rx_dropped++;
+ }
+ enc28j60_reg_bfclr(priv, EIR, EIR_RXERIF);
+ }
+ /*
+ * RX handler
+ * PKTIF don't work reliably!!! (look at the errata datasheet)
+ * check EPKTCNT is the suggested workaround
+ * process any packet pending (hw_rx MUST decrement PKCNT for
+ * any packet processed)
+ */
+ while ((pk_counter = enc28j60_regb_read(priv, EPKTCNT)) > 0) {
+ loop++;
+ dev_vdbg(&priv->netdev->dev, "intRX(%d), pk_cnt: %d\n",
+ loop, pk_counter);
+ /* update statistics */
+ if (pk_counter > priv->max_pk_counter) {
+ priv->max_pk_counter = pk_counter;
+ if (priv->max_pk_counter > 4)
+ dev_dbg(&priv->netdev->dev,
+ "enc28j60 RX, max_pk_cnt: %d\n",
+ priv->max_pk_counter);
+ else if (priv->max_pk_counter > 1)
+ dev_vdbg(&priv->netdev->dev,
+ "enc28j60 RX, max_pk_cnt: %d\n",
+ priv->max_pk_counter);
+ }
+ /*
+ * don't need to clear interrupt flag, automatically done
+ * when enc28j60_hw_rx() decrements the packet counter
+ */
+ enc28j60_hw_rx(priv);
+ }
+ } while (loop);
+
+ /* re-enable interrupts */
+ enc28j60_reg_bfset(priv, EIE, EIE_INTIE);
+ dev_vdbg(&priv->netdev->dev, "%s() exit\n", __FUNCTION__);
+}
+
+static void dump_packet(struct enc28j60_net_local *priv, const char *msg,
+ int len, const char *data)
+{
+#if CONFIG_ENC28J60_DBGLEVEL > 2
+ int k;
+ struct net_device *ndev = priv->netdev;
+
+ dev_vdbg(&ndev->dev, "%s(), packet len:%d", msg, len);
+ for (k = 0; len--; k++) {
+ if (!(k % 16))
+ printk("\n%04x: ", k);
+ printk("%02x ", data[k]);
+ }
+ printk("\n");
+#endif
+}
+
+/*
+ * Hardware transmit function.
+ * Fill the buffer memory and send the contents of the transmit buffer
+ * onto the network
+ */
+static void enc28j60_hw_tx(struct enc28j60_net_local *priv)
+{
+ dev_vdbg(&priv->netdev->dev, "%s(), packet len:%d\n",
+ __FUNCTION__, priv->tx_skb->len);
+
+ /* Set the write pointer to start of transmit buffer area */
+ enc28j60_regw_write(priv, EWRPTL, TXSTART_INIT);
+#ifdef CONFIG_ENC28J60_WRITEVERIFY
+ {
+ uint16_t reg;
+ reg = enc28j60_regw_read(priv, EWRPTL);
+ if (reg != TXSTART_INIT)
+ dev_dbg(&priv->netdev->dev,
+ "%s() ERWPT:0x%04x != 0x%04x\n", __FUNCTION__,
+ reg, TXSTART_INIT);
+ }
+#endif
+ /* Set the TXND pointer to correspond to the packet size given */
+ enc28j60_regw_write(priv, ETXNDL, TXSTART_INIT + priv->tx_skb->len);
+ /* write per-packet control byte */
+ spi_write_op(priv, ENC28J60_WRITE_BUF_MEM, 0, 0x00);
+ dev_vdbg(&priv->netdev->dev, "%s() after control byte ERWPT:0x%04x\n",
+ __FUNCTION__, enc28j60_regw_read(priv, EWRPTL));
+
+ dump_packet(priv, __FUNCTION__, priv->tx_skb->len, priv->tx_skb->data);
+
+ /* copy the packet into the transmit buffer */
+ spi_write_buf(priv, priv->tx_skb->len, priv->tx_skb->data);
+ dev_vdbg(&priv->netdev->dev,
+ "%s() after write packet ERWPT:0x%04x, len=%d\n", __FUNCTION__,
+ enc28j60_regw_read(priv, EWRPTL), priv->tx_skb->len);
+
+#ifdef CONFIG_ENC28J60_WRITEVERIFY
+ { /* readback and verify written data */
+ int test_len, k;
+ uint8_t test_buf[256];
+ int okflag = 1;
+
+ test_len = priv->tx_skb->len;
+ if (test_len > sizeof(test_buf))
+ test_len = sizeof(test_buf);
+
+ /* + 1 to skip control byte */
+ enc28j60_mem_read(priv, TXSTART_INIT + 1, test_len, test_buf);
+ for (k = 0; k < test_len; k++) {
+ if (priv->tx_skb->data[k] != test_buf[k]) {
+ dev_vdbg(&priv->netdev->dev,
+ "%s(),Err! differ [%d] "
+ "0x%02x - 0x%02x\n",
+ __FUNCTION__, k, priv->tx_skb->data[k],
+ test_buf[k]);
+ okflag = 0;
+ }
+ }
+ if (!okflag)
+ dev_dbg(&priv->netdev->dev,
+ "%s(), Write buffer verify error!\n",
+ __FUNCTION__);
+ else
+ dev_vdbg(&priv->netdev->dev,
+ "%s(), Write buffer verify OK\n",
+ __FUNCTION__);
+ }
+#endif
+ /* set TX request flag */
+ enc28j60_reg_bfset(priv, ECON1, ECON1_TXRTS);
+}
+
+/*
+ * Read the Receive Status Vector
+ */
+static void enc28j60_dump_rsv(struct enc28j60_net_local *priv, const char *msg,
+ uint16_t pk_ptr, int len, uint16_t sts)
+{
+ struct net_device *ndev = priv->netdev;
+
+ dev_vdbg(&ndev->dev, "%s - NexPk: 0x%04x - RSV:\n", msg, pk_ptr);
+ dev_vdbg(&ndev->dev, "ByteCount: %d, DribbleNibble: %d\n", len,
+ RSV_GETBIT(sts, RSV_DRIBBLENIBBLE));
+ dev_vdbg(&ndev->dev,
+ "RxOK: %d, CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
+ RSV_GETBIT(sts, RSV_RXOK), RSV_GETBIT(sts, RSV_CRCERROR),
+ RSV_GETBIT(sts, RSV_LENCHECKERR),
+ RSV_GETBIT(sts, RSV_LENOUTOFRANGE));
+ dev_vdbg(&ndev->dev,
+ "Multicast: %d, Broadcast: %d, "
+ "LongDropEvent: %d, CarrierEvent: %d\n",
+ RSV_GETBIT(sts, RSV_RXMULTICAST),
+ RSV_GETBIT(sts, RSV_RXBROADCAST),
+ RSV_GETBIT(sts, RSV_RXLONGEVDROPEV),
+ RSV_GETBIT(sts, RSV_CARRIEREV));
+ dev_vdbg(&ndev->dev,
+ "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, "
+ "VLanTagFrame: %d\n",
+ RSV_GETBIT(sts, RSV_RXCONTROLFRAME),
+ RSV_GETBIT(sts, RSV_RXPAUSEFRAME),
+ RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE),
+ RSV_GETBIT(sts, RSV_RXTYPEVLAN));
+}
+
+/*
+ * Hardware receive function.
+ * Read the buffer memory, update the FIFO pointer,
+ * check the status vector and decrement the packet counter
+ */
+static void enc28j60_hw_rx(struct enc28j60_net_local *priv)
+{
+ struct sk_buff *skb;
+ uint16_t erxrdpt, next_packet, rxstat;
+ uint8_t tmpv[6];
+ int len;
+
+ dev_vdbg(&priv->netdev->dev, "%s() pk_addr:0x%04x\n", __FUNCTION__,
+ priv->next_pk_ptr);
+
+ if (priv->next_pk_ptr > RXEND_INIT) {
+ dev_err(&priv->netdev->dev,
+ "%s() Invalid packet address!! 0x%04x\n", __FUNCTION__,
+ priv->next_pk_ptr);
+ return;
+ }
+ /* Read next packet pointer and rx status vector */
+ enc28j60_mem_read(priv, priv->next_pk_ptr, sizeof(tmpv), tmpv);
+
+ next_packet = tmpv[1];
+ next_packet <<= 8;
+ next_packet |= tmpv[0];
+
+ len = tmpv[3];
+ len <<= 8;
+ len |= tmpv[2];
+
+ rxstat = tmpv[5];
+ rxstat <<= 8;
+ rxstat |= tmpv[4];
+
+ enc28j60_dump_rsv(priv, __FUNCTION__, next_packet, len, rxstat);
+
+ if (!RSV_GETBIT(rxstat, RSV_RXOK)) {
+ dev_dbg(&priv->netdev->dev, "enc28j60: Rx Error (%04x)\n",
+ rxstat);
+
+ priv->stats.rx_errors++;
+ if (RSV_GETBIT(rxstat, RSV_CRCERROR))
+ priv->stats.rx_crc_errors++;
+ if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
+ priv->stats.rx_frame_errors++;
+ } else {
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ dev_err(&priv->netdev->dev,
+ "enc28j60: out of memory for Rx'd frame\n");
+ priv->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = priv->netdev;
+
+ /* copy the packet from the receive buffer */
+ enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(tmpv), len,
+ skb_put(skb, len));
+
+ priv->next_pk_ptr = next_packet;
+
+ /*
+ * Move the RX read pointer to the start of the next
+ * received packet.
+ * This frees the memory we just read out
+ */
+ erxrdpt =
+ erxrdpt_workaround(priv->next_pk_ptr, RXSTART_INIT,
+ RXEND_INIT);
+ enc28j60_regw_write(priv, ERXRDPTL, erxrdpt);
+
+ dev_vdbg(&priv->netdev->dev,
+ "%s() RxSize:%d, RxStat:0x%04x ERXRDPT:0x%04x\n",
+ __FUNCTION__, len, rxstat, erxrdpt);
+ dump_packet(priv, __FUNCTION__, skb->len, skb->data);
+
+ /* we are done with this packet, decrement the packet counter */
+ enc28j60_reg_bfset(priv, ECON2, ECON2_PKTDEC);
+
+ skb->protocol = eth_type_trans(skb, priv->netdev);
+
+ /* update statistics */
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ priv->netdev->last_rx = jiffies;
+ netif_rx(skb);
+ }
+}
+
+static int enc28j60_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ dev_vdbg(&dev->dev, "%s()\n", __FUNCTION__);
+
+ /* If some error occurs while trying to transmit this
+ * packet, you should return '1' from this function.
+ * In such a case you _may not_ do anything to the
+ * SKB, it is still owned by the network queueing
+ * layer when an error is returned. This means you
+ * may not modify any SKB fields, you may not free
+ * the SKB, etc.
+ */
+ netif_stop_queue(dev);
+
+ /* save the timestamp */
+ priv->netdev->trans_start = jiffies;
+ /* Remember the skb for deferred processing */
+ priv->tx_skb = skb;
+ schedule_work(&priv->tx_work);
+
+ return 0;
+}
+
+static void enc28j60_tx_work_handler(struct work_struct *work)
+{
+ struct enc28j60_net_local *priv =
+ container_of(work, struct enc28j60_net_local, tx_work);
+ dev_vdbg(&priv->netdev->dev, "%s()\n", __FUNCTION__);
+
+ /* actual delivery of data */
+ enc28j60_hw_tx(priv);
+}
+
+static irqreturn_t enc28j60_irq(int irq, void *dev_id)
+{
+ struct enc28j60_net_local *priv = dev_id;
+
+ dev_vdbg(&priv->netdev->dev, "%s(priv=%p)\n", __FUNCTION__, priv);
+
+ /*
+ * Can't do anything in interrupt context so fire of the interrupt
+ * handling workqueue.
+ */
+ schedule_work(&priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void enc28j60_net_tx_timeout(struct net_device *ndev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(ndev);
+
+ dev_dbg(&ndev->dev, "enc28j60 transmit timed out\n");
+
+ /* Reset the TX logic */
+ enc28j60_reg_bfset(priv, ECON1, ECON1_TXRST);
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_TXRST);
+ enc28j60_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF);
+ priv->stats.tx_errors++;
+ netif_wake_queue(ndev);
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int enc28j60_net_open(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s() enter [priv:%p]\n", __FUNCTION__, priv);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ dev_err(&dev->dev, "%s() invalid MAC address\n", __FUNCTION__);
+ return -EADDRNOTAVAIL;
+ }
+
+ /* Reset the hardware here */
+ enc28j60_hw_disable(priv);
+ enc28j60_reg_bfset(priv, ECON1, ECON1_TXRST | ECON1_RXRST);
+ enc28j60_reg_bfclr(priv, ECON1, ECON1_TXRST | ECON1_RXRST);
+
+ /* Update the MAC address (in case user has changed it) */
+ enc28j60_set_hw_macaddr(priv);
+
+ /* Enable interrupts */
+ enc28j60_hw_enable(priv);
+
+ /* We are now ready to accept transmit requests from
+ * the queueing layer of the networking.
+ */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/* The inverse routine to net_open(). */
+static int enc28j60_net_close(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s()\n", __FUNCTION__);
+
+ enc28j60_hw_disable(priv);
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *enc28j60_net_get_stats(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ return &priv->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ */
+static void enc28j60_set_multicast_list(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ if (!priv->hw_enable) {
+ if (dev->flags & IFF_PROMISC) {
+ dev_dbg(&dev->dev, "%s() promiscuous mode\n",
+ __FUNCTION__);
+ enc28j60_regb_write(priv, ERXFCON, 0x00);
+ } else if (dev->flags & IFF_ALLMULTI) {
+ dev_dbg(&dev->dev, "%s() multicast mode\n",
+ __FUNCTION__);
+ enc28j60_regb_write(priv, ERXFCON,
+ ERXFCON_UCEN | ERXFCON_CRCEN |
+ ERXFCON_BCEN | ERXFCON_MCEN);
+ } else {
+ dev_dbg(&dev->dev, "%s() normal mode\n", __FUNCTION__);
+ enc28j60_regb_write(priv, ERXFCON,
+ ERXFCON_UCEN | ERXFCON_CRCEN |
+ ERXFCON_BCEN);
+ }
+ } else
+ dev_dbg(&dev->dev,
+ "%s() Warning: hw must be disabled to set rx filter\n",
+ __FUNCTION__);
+}
+
+static int enc28j60_chipset_init(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ return enc28j60_hw_init(priv);
+}
+
+static int __devinit enc28j60_probe(struct spi_device *spi)
+{
+ struct net_device *dev;
+ struct enc28j60_net_local *priv;
+ int ret = 0;
+
+ dev_dbg(&spi->dev, "%s() start\n", __FUNCTION__);
+
+ dev = alloc_etherdev(sizeof(struct enc28j60_net_local));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+ priv = netdev_priv(dev);
+
+ priv->netdev = dev; /* priv to netdev reference */
+ priv->spi = spi; /* priv to spi reference */
+ priv->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_transfer_buf) {
+ ret = -ENOMEM;
+ goto error_buf;
+ }
+ init_MUTEX(&priv->semlock);
+
+ INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
+ INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
+ dev_set_drvdata(&spi->dev, priv); /* spi to priv reference */
+ SET_NETDEV_DEV(dev, &spi->dev);
+
+ if (!enc28j60_chipset_init(dev)) {
+ dev_dbg(&spi->dev, "enc28j60 not found\n");
+ ret = -EIO;
+ goto error_irq;
+ }
+ enc28j60_get_hw_macaddr(priv);
+
+ ret = request_irq(spi->irq, enc28j60_irq, IRQF_TRIGGER_FALLING,
+ "enc28j60", priv);
+ if (ret < 0) {
+ dev_err(&spi->dev, "request irq %d failed (ret = %d)\n",
+ spi->irq, ret);
+ goto error_irq;
+ }
+
+ dev->irq = spi->irq;
+
+ dev->open = enc28j60_net_open;
+ dev->stop = enc28j60_net_close;
+ dev->hard_start_xmit = enc28j60_send_packet;
+ dev->get_stats = enc28j60_net_get_stats;
+ dev->set_multicast_list = &enc28j60_set_multicast_list;
+ dev->set_mac_address = enc28j60_set_mac_address;
+ dev->tx_timeout = &enc28j60_net_tx_timeout;
+ dev->watchdog_timeo = MY_TX_TIMEOUT;
+
+ ret = register_netdev(dev);
+ if (ret) {
+ dev_err(&spi->dev,
+ "register netdev enc28j60 failed (ret = %d)\n", ret);
+ goto error_register;
+ }
+ dev_info(&spi->dev, "%s: enc28j60 driver registered (interrupt %d)\n",
+ dev->name, dev->irq);
+
+ return 0;
+
+ error_register:
+ free_irq(spi->irq, priv);
+ error_irq:
+ kfree(priv->spi_transfer_buf);
+ error_buf:
+ free_netdev(dev);
+ error_alloc:
+ return ret;
+}
+
+static int enc28j60_remove(struct spi_device *spi)
+{
+ struct enc28j60_net_local *priv = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "%s: stop\n", __FUNCTION__);
+
+ unregister_netdev(priv->netdev);
+ free_irq(spi->irq, priv);
+ kfree(priv->spi_transfer_buf);
+ free_netdev(priv->netdev);
+
+ return 0;
+}
+
+static struct spi_driver enc28j60_driver = {
+ .driver = {
+ .name = "enc28j60",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = enc28j60_probe,
+ .remove = __devexit_p(enc28j60_remove),
+};
+
+static int __init enc28j60_init(void)
+{
+ return spi_register_driver(&enc28j60_driver);
+}
+
+module_init(enc28j60_init);
+
+static void __exit enc28j60_exit(void)
+{
+ spi_unregister_driver(&enc28j60_driver);
+}
+
+module_exit(enc28j60_exit);
+
+MODULE_DESCRIPTION("ENC28J60 ethernet driver");
+MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
+MODULE_LICENSE("GPL");
+module_param(full_duplex, int, 0);
+MODULE_PARM_DESC(full_duplex, "Enable full duplex mode");
diff --git a/drivers/net/enc28j60_hw.h b/drivers/net/enc28j60_hw.h
new file mode 100644
index 0000000..78f415a
--- /dev/null
+++ b/drivers/net/enc28j60_hw.h
@@ -0,0 +1,303 @@
+/*
+ * enc28j60_hw.h: EDTP FrameThrower style enc28j60 registers
+ *
+ * $Id: enc28j60_hw.h,v 1.5 2007/12/11 10:35:40 claudio Exp $
+ */
+
+#ifndef _ENC28J60_HW_H
+#define _ENC28J60_HW_H
+
+/*
+ * ENC28J60 Control Registers
+ * Control register definitions are a combination of address,
+ * bank number, and Ethernet/MAC/PHY indicator bits.
+ * - Register address (bits 0-4)
+ * - Bank number (bits 5-6)
+ * - MAC/MII indicator (bit 7)
+ */
+#define ADDR_MASK 0x1F
+#define BANK_MASK 0x60
+#define SPRD_MASK 0x80
+/* All-bank registers */
+#define EIE 0x1B
+#define EIR 0x1C
+#define ESTAT 0x1D
+#define ECON2 0x1E
+#define ECON1 0x1F
+/* Bank 0 registers */
+#define ERDPTL (0x00|0x00)
+#define ERDPTH (0x01|0x00)
+#define EWRPTL (0x02|0x00)
+#define EWRPTH (0x03|0x00)
+#define ETXSTL (0x04|0x00)
+#define ETXSTH (0x05|0x00)
+#define ETXNDL (0x06|0x00)
+#define ETXNDH (0x07|0x00)
+#define ERXSTL (0x08|0x00)
+#define ERXSTH (0x09|0x00)
+#define ERXNDL (0x0A|0x00)
+#define ERXNDH (0x0B|0x00)
+#define ERXRDPTL (0x0C|0x00)
+#define ERXRDPTH (0x0D|0x00)
+#define ERXWRPTL (0x0E|0x00)
+#define ERXWRPTH (0x0F|0x00)
+#define EDMASTL (0x10|0x00)
+#define EDMASTH (0x11|0x00)
+#define EDMANDL (0x12|0x00)
+#define EDMANDH (0x13|0x00)
+#define EDMADSTL (0x14|0x00)
+#define EDMADSTH (0x15|0x00)
+#define EDMACSL (0x16|0x00)
+#define EDMACSH (0x17|0x00)
+/* Bank 1 registers */
+#define EHT0 (0x00|0x20)
+#define EHT1 (0x01|0x20)
+#define EHT2 (0x02|0x20)
+#define EHT3 (0x03|0x20)
+#define EHT4 (0x04|0x20)
+#define EHT5 (0x05|0x20)
+#define EHT6 (0x06|0x20)
+#define EHT7 (0x07|0x20)
+#define EPMM0 (0x08|0x20)
+#define EPMM1 (0x09|0x20)
+#define EPMM2 (0x0A|0x20)
+#define EPMM3 (0x0B|0x20)
+#define EPMM4 (0x0C|0x20)
+#define EPMM5 (0x0D|0x20)
+#define EPMM6 (0x0E|0x20)
+#define EPMM7 (0x0F|0x20)
+#define EPMCSL (0x10|0x20)
+#define EPMCSH (0x11|0x20)
+#define EPMOL (0x14|0x20)
+#define EPMOH (0x15|0x20)
+#define EWOLIE (0x16|0x20)
+#define EWOLIR (0x17|0x20)
+#define ERXFCON (0x18|0x20)
+#define EPKTCNT (0x19|0x20)
+/* Bank 2 registers */
+#define MACON1 (0x00|0x40|SPRD_MASK)
+/* #define MACON2 (0x01|0x40|SPRD_MASK) */
+#define MACON3 (0x02|0x40|SPRD_MASK)
+#define MACON4 (0x03|0x40|SPRD_MASK)
+#define MABBIPG (0x04|0x40|SPRD_MASK)
+#define MAIPGL (0x06|0x40|SPRD_MASK)
+#define MAIPGH (0x07|0x40|SPRD_MASK)
+#define MACLCON1 (0x08|0x40|SPRD_MASK)
+#define MACLCON2 (0x09|0x40|SPRD_MASK)
+#define MAMXFLL (0x0A|0x40|SPRD_MASK)
+#define MAMXFLH (0x0B|0x40|SPRD_MASK)
+#define MAPHSUP (0x0D|0x40|SPRD_MASK)
+#define MICON (0x11|0x40|SPRD_MASK)
+#define MICMD (0x12|0x40|SPRD_MASK)
+#define MIREGADR (0x14|0x40|SPRD_MASK)
+#define MIWRL (0x16|0x40|SPRD_MASK)
+#define MIWRH (0x17|0x40|SPRD_MASK)
+#define MIRDL (0x18|0x40|SPRD_MASK)
+#define MIRDH (0x19|0x40|SPRD_MASK)
+/* Bank 3 registers */
+#define MAADR1 (0x00|0x60|SPRD_MASK)
+#define MAADR0 (0x01|0x60|SPRD_MASK)
+#define MAADR3 (0x02|0x60|SPRD_MASK)
+#define MAADR2 (0x03|0x60|SPRD_MASK)
+#define MAADR5 (0x04|0x60|SPRD_MASK)
+#define MAADR4 (0x05|0x60|SPRD_MASK)
+#define EBSTSD (0x06|0x60)
+#define EBSTCON (0x07|0x60)
+#define EBSTCSL (0x08|0x60)
+#define EBSTCSH (0x09|0x60)
+#define MISTAT (0x0A|0x60|SPRD_MASK)
+#define EREVID (0x12|0x60)
+#define ECOCON (0x15|0x60)
+#define EFLOCON (0x17|0x60)
+#define EPAUSL (0x18|0x60)
+#define EPAUSH (0x19|0x60)
+/* PHY registers */
+#define PHCON1 0x00
+#define PHSTAT1 0x01
+#define PHHID1 0x02
+#define PHHID2 0x03
+#define PHCON2 0x10
+#define PHSTAT2 0x11
+#define PHIE 0x12
+#define PHIR 0x13
+#define PHLCON 0x14
+
+/* ENC28J60 EIE Register Bit Definitions */
+#define EIE_INTIE 0x80
+#define EIE_PKTIE 0x40
+#define EIE_DMAIE 0x20
+#define EIE_LINKIE 0x10
+#define EIE_TXIE 0x08
+#define EIE_WOLIE 0x04
+#define EIE_TXERIE 0x02
+#define EIE_RXERIE 0x01
+/* ENC28J60 EIR Register Bit Definitions */
+#define EIR_PKTIF 0x40
+#define EIR_DMAIF 0x20
+#define EIR_LINKIF 0x10
+#define EIR_TXIF 0x08
+#define EIR_WOLIF 0x04
+#define EIR_TXERIF 0x02
+#define EIR_RXERIF 0x01
+/* ENC28J60 ESTAT Register Bit Definitions */
+#define ESTAT_INT 0x80
+#define ESTAT_LATECOL 0x10
+#define ESTAT_RXBUSY 0x04
+#define ESTAT_TXABRT 0x02
+#define ESTAT_CLKRDY 0x01
+/* ENC28J60 ECON2 Register Bit Definitions */
+#define ECON2_AUTOINC 0x80
+#define ECON2_PKTDEC 0x40
+#define ECON2_PWRSV 0x20
+#define ECON2_VRPS 0x08
+/* ENC28J60 ECON1 Register Bit Definitions */
+#define ECON1_TXRST 0x80
+#define ECON1_RXRST 0x40
+#define ECON1_DMAST 0x20
+#define ECON1_CSUMEN 0x10
+#define ECON1_TXRTS 0x08
+#define ECON1_RXEN 0x04
+#define ECON1_BSEL1 0x02
+#define ECON1_BSEL0 0x01
+/* ENC28J60 MACON1 Register Bit Definitions */
+#define MACON1_LOOPBK 0x10
+#define MACON1_TXPAUS 0x08
+#define MACON1_RXPAUS 0x04
+#define MACON1_PASSALL 0x02
+#define MACON1_MARXEN 0x01
+/* ENC28J60 MACON2 Register Bit Definitions */
+#define MACON2_MARST 0x80
+#define MACON2_RNDRST 0x40
+#define MACON2_MARXRST 0x08
+#define MACON2_RFUNRST 0x04
+#define MACON2_MATXRST 0x02
+#define MACON2_TFUNRST 0x01
+/* ENC28J60 MACON3 Register Bit Definitions */
+#define MACON3_PADCFG2 0x80
+#define MACON3_PADCFG1 0x40
+#define MACON3_PADCFG0 0x20
+#define MACON3_TXCRCEN 0x10
+#define MACON3_PHDRLEN 0x08
+#define MACON3_HFRMLEN 0x04
+#define MACON3_FRMLNEN 0x02
+#define MACON3_FULDPX 0x01
+/* ENC28J60 MICMD Register Bit Definitions */
+#define MICMD_MIISCAN 0x02
+#define MICMD_MIIRD 0x01
+/* ENC28J60 MISTAT Register Bit Definitions */
+#define MISTAT_NVALID 0x04
+#define MISTAT_SCAN 0x02
+#define MISTAT_BUSY 0x01
+/* ENC28J60 ERXFCON Register Bit Definitions */
+#define ERXFCON_UCEN 0x80
+#define ERXFCON_ANDOR 0x40
+#define ERXFCON_CRCEN 0x20
+#define ERXFCON_PMEN 0x10
+#define ERXFCON_MPEN 0x08
+#define ERXFCON_HTEN 0x04
+#define ERXFCON_MCEN 0x02
+#define ERXFCON_BCEN 0x01
+
+/* ENC28J60 PHY PHCON1 Register Bit Definitions */
+#define PHCON1_PRST 0x8000
+#define PHCON1_PLOOPBK 0x4000
+#define PHCON1_PPWRSV 0x0800
+#define PHCON1_PDPXMD 0x0100
+/* ENC28J60 PHY PHSTAT1 Register Bit Definitions */
+#define PHSTAT1_PFDPX 0x1000
+#define PHSTAT1_PHDPX 0x0800
+#define PHSTAT1_LLSTAT 0x0004
+#define PHSTAT1_JBSTAT 0x0002
+/* ENC28J60 PHY PHCON2 Register Bit Definitions */
+#define PHCON2_FRCLINK 0x4000
+#define PHCON2_TXDIS 0x2000
+#define PHCON2_JABBER 0x0400
+#define PHCON2_HDLDIS 0x0100
+
+/* ENC28J60 Packet Control Byte Bit Definitions */
+#define PKTCTRL_PHUGEEN 0x08
+#define PKTCTRL_PPADEN 0x04
+#define PKTCTRL_PCRCEN 0x02
+#define PKTCTRL_POVERRIDE 0x01
+
+/* ENC28J60 Transmit Status Vector */
+#define TSV_TXBYTECNT 0
+#define TSV_TXCOLLISIONCNT 16
+#define TSV_TXCRCERROR 20
+#define TSV_TXLENCHKERROR 21
+#define TSV_TXLENOUTOFRANGE 22
+#define TSV_TXDONE 23
+#define TSV_TXMULTICAST 24
+#define TSV_TXBROADCAST 25
+#define TSV_TXPACKETDEFER 26
+#define TSV_TXEXDEFER 27
+#define TSV_TXEXCOLLISION 28
+#define TSV_TXLATECOLLISION 29
+#define TSV_TXGIANT 30
+#define TSV_TXUNDERRUN 31
+#define TSV_TOTBYTETXONWIRE 32
+#define TSV_TXCONTROLFRAME 48
+#define TSV_TXPAUSEFRAME 49
+#define TSV_BACKPRESSUREAPP 50
+#define TSV_TXVLANTAGFRAME 51
+
+#define TSV_SIZE 7
+#define TSV_BYTEOF(x) ((x) / 8)
+#define TSV_BITMASK(x) (1 << ((x) % 8))
+#define TSV_GETBIT(x, y) (((x)[TSV_BYTEOF(y)] & TSV_BITMASK(y)) ? 1 : 0)
+
+/* ENC28J60 Receive Status Vector */
+#define RSV_RXLONGEVDROPEV 16
+#define RSV_CARRIEREV 18
+#define RSV_CRCERROR 20
+#define RSV_LENCHECKERR 21
+#define RSV_LENOUTOFRANGE 22
+#define RSV_RXOK 23
+#define RSV_RXMULTICAST 24
+#define RSV_RXBROADCAST 25
+#define RSV_DRIBBLENIBBLE 26
+#define RSV_RXCONTROLFRAME 27
+#define RSV_RXPAUSEFRAME 28
+#define RSV_RXUNKNOWNOPCODE 29
+#define RSV_RXTYPEVLAN 30
+
+#define RSV_BITMASK(x) (1 << ((x) - 16))
+#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0)
+
+
+/* SPI operation codes */
+#define ENC28J60_READ_CTRL_REG 0x00
+#define ENC28J60_READ_BUF_MEM 0x3A
+#define ENC28J60_WRITE_CTRL_REG 0x40
+#define ENC28J60_WRITE_BUF_MEM 0x7A
+#define ENC28J60_BIT_FIELD_SET 0x80
+#define ENC28J60_BIT_FIELD_CLR 0xA0
+#define ENC28J60_SOFT_RESET 0xFF
+
+
+/* buffer boundaries applied to internal 8K ram
+ * entire available packet buffer space is allocated.
+ * Give TX buffer space for one full ethernet frame (~1500 bytes)
+ * receive buffer gets the rest */
+#define TXSTART_INIT 0x1A00
+#define TXEND_INIT 0x1FFF
+
+/* Put RX buffer at 0 as suggested by the Errata datasheet */
+#define RXSTART_INIT 0x0000
+#define RXEND_INIT 0x19FF
+
+/* maximum ethernet frame length */
+#define MAX_FRAMELEN 1518
+
+/* Prefered half duplex: LEDA: Link status LEDB: Rx/Tx activity */
+#define ENC28J60_LAMPS_MODE 0x3476
+
+/* Default MAC address for this interface */
+#define ENC28J60_MAC0 0x00
+#define ENC28J60_MAC1 0x00
+#define ENC28J60_MAC2 'F'
+#define ENC28J60_MAC3 'I'
+#define ENC28J60_MAC4 'C'
+#define ENC28J60_MAC5 'E'
+
+#endif
^ permalink raw reply related
* [PATCH 2/2] add driver for enc28j60 ethernet chip
From: Claudio Lanconelli @ 2007-12-11 14:58 UTC (permalink / raw)
To: netdev; +Cc: jgarzik
[-- Attachment #1: Type: text/plain, Size: 138 bytes --]
Second part contains changes on Kconfig and Makefile on dir drivers/net
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
[-- Attachment #2: enc28j60_p2.diff --]
[-- Type: text/x-patch, Size: 1605 bytes --]
drivers/net/Kconfig | 26 ++++++++++++++++++++++++++
drivers/net/Makefile | 1 +
2 files changed, 27 insertions(+), 0 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d9107e5..e643e0f 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -922,6 +922,32 @@ config DM9000
To compile this driver as a module, choose M here. The module
will be called dm9000.
+config ENC28J60
+ tristate "ENC28J60 support"
+ depends on EXPERIMENTAL && SPI && NET_ETHERNET
+ select CRC32
+ select MII
+ ---help---
+ Support for the Microchip EN28J60 ethernet chip.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will be
+ called enc28j60.
+
+config ENC28J60_DBGLEVEL
+ int "Debugging verbosity (0 = quiet, 3 = noisy)"
+ depends on ENC28J60
+ default "0"
+ ---help---
+ Determines the verbosity level of the enc28j60 driver debugging messages.
+
+config ENC28J60_WRITEVERIFY
+ bool "Enable write verify"
+ depends on ENC28J60
+ ---help---
+ Enable the verify after the buffer write useful for debugging purpose.
+ If unsure, say N.
+
config SMC911X
tristate "SMSC LAN911[5678] support"
select CRC32
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0e5fde4..e91e3a3 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -216,6 +216,7 @@ obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_FEC_8XX) += fec_8xx/
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
+obj-$(CONFIG_ENC28J60) += enc28j60.o
obj-$(CONFIG_MACB) += macb.o
^ permalink raw reply related
* Re: [Bugme-new] [Bug 9543] New: RTNL: assertion failed at net/ipv6/addrconf.c (2164)/RTNL: assertion failed at net/ipv4/devinet.c (1055)
From: Krzysztof Oledzki @ 2007-12-11 15:04 UTC (permalink / raw)
To: Andrew Morton; +Cc: bugme-daemon, Stephen Hemminger, David S. Miller, netdev
In-Reply-To: <20071211034603.cbcc4762.akpm@linux-foundation.org>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 2265 bytes --]
On Tue, 11 Dec 2007, Andrew Morton wrote:
> On Tue, 11 Dec 2007 03:20:48 -0800 (PST) bugme-daemon@bugzilla.kernel.org wrote:
>
>> http://bugzilla.kernel.org/show_bug.cgi?id=9543
>>
>> Summary: RTNL: assertion failed at net/ipv6/addrconf.c
>> (2164)/RTNL: assertion failed at net/ipv4/devinet.c
>> (1055)
>> Product: Drivers
>> Version: 2.5
>> KernelVersion: 2.6.24-rc4-git7
>> Platform: All
>> OS/Version: Linux
>> Tree: Mainline
>> Status: NEW
>> Severity: normal
>> Priority: P1
>> Component: Network
>> AssignedTo: jgarzik@pobox.com
>> ReportedBy: olel@ans.pl
>>
>>
>> Most recent kernel where this bug did not occur: 2.6.23
>> Distribution: Gentoo
>>
>> Problem Description:
>> ADDRCONF(NETDEV_CHANGE): bond0: link becomes ready
>> RTNL: assertion failed at net/ipv6/addrconf.c (2164)
>> Pid: 9, comm: events/0 Not tainted 2.6.24-rc4-git7 #1
>> [<78402cfb>] addrconf_notify+0x5b4/0x7b7
>> [<7812203a>] finish_task_switch+0x0/0x8c
>> [<781346ff>] worker_thread+0x0/0x85
>> [<78438e23>] schedule+0x545/0x55f
>> [<781408d1>] print_lock_contention_bug+0x11/0xd2
>> [<783bfa72>] rt_run_flush+0x43/0x8b
>> [<783bfa93>] rt_run_flush+0x64/0x8b
>> [<7813ac54>] notifier_call_chain+0x2a/0x52
>> [<7813ac9e>] raw_notifier_call_chain+0x17/0x1a
>> [<783a3471>] netdev_state_change+0x18/0x29
>> [<783ac6a9>] __linkwatch_run_queue+0x150/0x17e
>> [<783ac6f4>] linkwatch_event+0x1d/0x22
>> [<78133cdf>] run_workqueue+0xdb/0x1b6
>> [<78133c8b>] run_workqueue+0x87/0x1b6
>> [<783ac6d7>] linkwatch_event+0x0/0x22
>> [<781346ff>] worker_thread+0x0/0x85
>> [<78134778>] worker_thread+0x79/0x85
>> [<781371ad>] autoremove_wake_function+0x0/0x35
>> [<781370f6>] kthread+0x38/0x5e
>> [<781370be>] kthread+0x0/0x5e
>> [<78104baf>] kernel_thread_helper+0x7/0x10
>> =======================
>> RTNL: assertion failed at net/ipv6/addrconf.c (1610)
>
>
> Hopefully this is due to the bug you reported in bug #9542.
>
> Does this patch fix both issues?
Unfortunately not. I just updated bugzilla.
Best regards,
Krzysztof Olędzki
^ permalink raw reply
* Re: [PATCH 2/2] [TCP]: Include __tcp_reset_fack_counts to non-__ version
From: Christoph Hellwig @ 2007-12-11 15:13 UTC (permalink / raw)
To: Ilpo J?rvinen; +Cc: David Miller, netdev
In-Reply-To: <11973738392955-git-send-email-ilpo.jarvinen@helsinki.fi>
On Tue, Dec 11, 2007 at 01:50:39PM +0200, Ilpo J?rvinen wrote:
> + BUG_ON((prev != NULL) && !tcp_skb_adjacent(sk, prev, skb[queue]));
> +
> + tcp_for_write_queue_from(skb[queue], sk, queue) {
> + if ((prev != NULL) && !tcp_skb_adjacent(sk, prev, skb[queue]))
> + break;
> +
> + if (!before(TCP_SKB_CB(skb[queue])->seq, tcp_sk(sk)->snd_nxt) ||
> + TCP_SKB_CB(skb[queue])->fack_count == fc)
> + return;
There's quite a few overflows of the normal 80 char limit here. Because
you're current style is a little on the verbose side that's trivially
fixable, though:
BUG_ON(prev && !tcp_skb_adjacent(sk, prev, skb[queue]));
tcp_for_write_queue_from(skb[queue], sk, queue) {
if (prev && !tcp_skb_adjacent(sk, prev, skb[queue]))
break;
if (!before(TCP_SKB_CB(skb[queue])->seq,
tcp_sk(sk)->snd_nxt) ||
TCP_SKB_CB(skb[queue])->fack_count == fc)
return;
^ permalink raw reply
* [PATCH]drivers/net/phy/: default return value in ioctl phy.c
From: Rini van Zetten @ 2007-12-11 15:02 UTC (permalink / raw)
To: afleming; +Cc: netdev, linux-kernel
Hello Andy,
This patch (to 2.6.23.9) add a default return value EOPNOTSUPP to the ioctl
function. The problem with the always 0 return value is that the iwconfig
(wireless) tool found a valid device when an ethernet device uses the phy
abstraction layer.
I 've tetsted this with the macb driver.
Signed-off-by: Rini van Zetten <rini@arvoo.nl>
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index cb230f4..c07460d 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -405,6 +405,8 @@ int phy_mii_ioctl(struct phy_device *phydev,
&& phydev->drv->config_init)
phydev->drv->config_init(phydev);
break;
+ default:
+ return -EOPNOTSUPP;
}
return 0;
^ permalink raw reply related
* Re: [kvm-devel] [PATCH resent] virtio_net: Fix stalled inbound trafficon early packets
From: Christian Borntraeger @ 2007-12-11 15:27 UTC (permalink / raw)
To: dor.laor; +Cc: Rusty Russell, kvm-devel, netdev, virtualization
In-Reply-To: <200712111419.52880.borntraeger@de.ibm.com>
Am Dienstag, 11. Dezember 2007 schrieb Christian Borntraeger:
> > The way other physical NICs doing it is by dis/en/abling interrupt
> > using registers (look at e1000).
> > I suggest we can export add_status and use the original code but
> > before enabling napi add a call to add_status(dev,
> > VIRTIO_CONFIG_DEV_OPEN).
> > The host won't trigger an irq until it sees the above.
>
> That would also work. We already have VRING_AVAIL_F_NO_INTERRUPT in
> virtio_ring.c - maybe we can use that. Its hidden in callback and
> restart handling, what about adding an explicit startup?
Ok, just to give an example what I thought about:
---
drivers/block/virtio_blk.c | 3 ++-
drivers/net/virtio_net.c | 2 ++
drivers/virtio/virtio_ring.c | 16 +++++++++++++---
include/linux/virtio.h | 5 +++++
4 files changed, 22 insertions(+), 4 deletions(-)
Index: kvm/drivers/virtio/virtio_ring.c
===================================================================
--- kvm.orig/drivers/virtio/virtio_ring.c
+++ kvm/drivers/virtio/virtio_ring.c
@@ -241,6 +241,16 @@ static bool vring_restart(struct virtque
return true;
}
+static void vring_startup(struct virtqueue *_vq)
+{
+ struct vring_virtqueue *vq = to_vvq(_vq);
+ START_USE(vq);
+ if (vq->vq.callback)
+ vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
+ END_USE(vq);
+}
+
+
irqreturn_t vring_interrupt(int irq, void *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
@@ -265,6 +275,7 @@ static struct virtqueue_ops vring_vq_ops
.get_buf = vring_get_buf,
.kick = vring_kick,
.restart = vring_restart,
+ .startup = vring_startup,
.shutdown = vring_shutdown,
};
@@ -299,9 +310,8 @@ struct virtqueue *vring_new_virtqueue(un
vq->in_use = false;
#endif
- /* No callback? Tell other side not to bother us. */
- if (!callback)
- vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
+ /* disable interrupts until we enable them */
+ vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
/* Put everything in free lists. */
vq->num_free = num;
Index: kvm/include/linux/virtio.h
===================================================================
--- kvm.orig/include/linux/virtio.h
+++ kvm/include/linux/virtio.h
@@ -45,6 +45,9 @@ struct virtqueue
* vq: the struct virtqueue we're talking about.
* This returns "false" (and doesn't re-enable) if there are pending
* buffers in the queue, to avoid a race.
+ * @startup: enable callbacks
+ * vq: the struct virtqueue we're talking abount
+ * Returns 0 or an error
* @shutdown: "unadd" all buffers.
* vq: the struct virtqueue we're talking about.
* Remove everything from the queue.
@@ -67,6 +70,8 @@ struct virtqueue_ops {
bool (*restart)(struct virtqueue *vq);
+ void (*startup) (struct virtqueue *vq);
+
void (*shutdown)(struct virtqueue *vq);
};
Index: kvm/drivers/net/virtio_net.c
===================================================================
--- kvm.orig/drivers/net/virtio_net.c
+++ kvm/drivers/net/virtio_net.c
@@ -292,6 +292,8 @@ static int virtnet_open(struct net_devic
return -ENOMEM;
napi_enable(&vi->napi);
+
+ vi->rvq->vq_ops->startup(vi->rvq);
return 0;
}
Index: kvm/drivers/block/virtio_blk.c
===================================================================
--- kvm.orig/drivers/block/virtio_blk.c
+++ kvm/drivers/block/virtio_blk.c
@@ -183,7 +183,8 @@ static int virtblk_probe(struct virtio_d
err = PTR_ERR(vblk->vq);
goto out_free_vblk;
}
-
+ /* enable interrupts */
+ vblk->vq->vq_ops->startup(vblk->vq);
vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
if (!vblk->pool) {
err = -ENOMEM;
There is still one small problem: what if the host fills up all
host-to-guest buffers before we call startup? So I start to think that your
solution is better, given that the host is not only not sending interrupts
but also not filling any buffers as long as VIRTIO_CONFIG_DEV_OPEN is not
set. I will have a look but I think that add_status needs to be called
after napi_enable, otherwise we have the same race.
Christian
^ permalink raw reply
* [PATCH v2] XFRM: assorted IPsec fixups
From: Paul Moore @ 2007-12-11 16:30 UTC (permalink / raw)
To: netdev; +Cc: linux-audit, selinux
This patch fixes a number of small but potentially troublesome things in the
XFRM/IPsec code:
* Use the 'audit_enabled' variable already in include/linux/audit.h
Removed the need for extern declarations local to each XFRM audit fuction
* Convert 'sid' to 'secid'
The 'sid' name is specific to SELinux, 'secid' is the common naming
convention used by the kernel when refering to tokenized LSM labels
* Convert address display to use standard NIP* macros
Similar to what was recently done with the SPD audit code, this also also
includes the removal of some unnecessary memcpy() calls
* Move common code to xfrm_audit_common_stateinfo()
Code consolidation from the "less is more" book on software development
* Convert the SPI in audit records to host byte order
The current SPI values in the audit record are being displayed in network
byte order, probably not what was intended
* Proper spacing around commas in function arguments
Minor style tweak since I was already touching the code
Signed-off-by: Paul Moore <paul.moore@hp.com>
---
include/linux/xfrm.h | 2 +
include/net/xfrm.h | 18 ++++++------
net/xfrm/xfrm_policy.c | 15 +++++-----
net/xfrm/xfrm_state.c | 69 +++++++++++++++++++++--------------------------
security/selinux/xfrm.c | 20 +++++++-------
5 files changed, 58 insertions(+), 66 deletions(-)
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index b58adc5..f75a337 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -31,7 +31,7 @@ struct xfrm_sec_ctx {
__u8 ctx_doi;
__u8 ctx_alg;
__u16 ctx_len;
- __u32 ctx_sid;
+ __u32 ctx_secid;
char ctx_str[0];
};
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 0c380d9..3134ba6 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -547,7 +547,7 @@ struct xfrm_audit
};
#ifdef CONFIG_AUDITSYSCALL
-static inline struct audit_buffer *xfrm_audit_start(u32 auid, u32 sid)
+static inline struct audit_buffer *xfrm_audit_start(u32 auid, u32 secid)
{
struct audit_buffer *audit_buf = NULL;
char *secctx;
@@ -560,8 +560,8 @@ static inline struct audit_buffer *xfrm_audit_start(u32 auid, u32 sid)
audit_log_format(audit_buf, "auid=%u", auid);
- if (sid != 0 &&
- security_secid_to_secctx(sid, &secctx, &secctx_len) == 0) {
+ if (secid != 0 &&
+ security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) {
audit_log_format(audit_buf, " subj=%s", secctx);
security_release_secctx(secctx, secctx_len);
} else
@@ -570,13 +570,13 @@ static inline struct audit_buffer *xfrm_audit_start(u32 auid, u32 sid)
}
extern void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
- u32 auid, u32 sid);
+ u32 auid, u32 secid);
extern void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
- u32 auid, u32 sid);
+ u32 auid, u32 secid);
extern void xfrm_audit_state_add(struct xfrm_state *x, int result,
- u32 auid, u32 sid);
+ u32 auid, u32 secid);
extern void xfrm_audit_state_delete(struct xfrm_state *x, int result,
- u32 auid, u32 sid);
+ u32 auid, u32 secid);
#else
#define xfrm_audit_policy_add(x, r, a, s) do { ; } while (0)
#define xfrm_audit_policy_delete(x, r, a, s) do { ; } while (0)
@@ -706,13 +706,13 @@ extern int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
#ifdef CONFIG_SECURITY_NETWORK_XFRM
/* If neither has a context --> match
- * Otherwise, both must have a context and the sids, doi, alg must match
+ * Otherwise, both must have a context and the secids, doi, alg must match
*/
static inline int xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_ctx *s2)
{
return ((!s1 && !s2) ||
(s1 && s2 &&
- (s1->ctx_sid == s2->ctx_sid) &&
+ (s1->ctx_secid == s2->ctx_secid) &&
(s1->ctx_doi == s2->ctx_doi) &&
(s1->ctx_alg == s2->ctx_alg)));
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index d9bde91..8a89e2c 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -24,6 +24,7 @@
#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/cache.h>
+#include <linux/audit.h>
#include <net/dst.h>
#include <net/xfrm.h>
#include <net/ip.h>
@@ -2298,15 +2299,14 @@ static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
}
}
-void
-xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
+ u32 auid, u32 secid)
{
struct audit_buffer *audit_buf;
- extern int audit_enabled;
if (audit_enabled == 0)
return;
- audit_buf = xfrm_audit_start(sid, auid);
+ audit_buf = xfrm_audit_start(secid, auid);
if (audit_buf == NULL)
return;
audit_log_format(audit_buf, " op=SPD-add res=%u", result);
@@ -2315,15 +2315,14 @@ xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
}
EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
-void
-xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
+ u32 auid, u32 secid)
{
struct audit_buffer *audit_buf;
- extern int audit_enabled;
if (audit_enabled == 0)
return;
- audit_buf = xfrm_audit_start(sid, auid);
+ audit_buf = xfrm_audit_start(secid, auid);
if (audit_buf == NULL)
return;
audit_log_format(audit_buf, " op=SPD-delete res=%u", result);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5b860b6..e2a3dd1 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -19,6 +19,7 @@
#include <linux/ipsec.h>
#include <linux/module.h>
#include <linux/cache.h>
+#include <linux/audit.h>
#include <asm/uaccess.h>
#include "xfrm_hash.h"
@@ -1994,67 +1995,59 @@ void __init xfrm_state_init(void)
static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
struct audit_buffer *audit_buf)
{
- if (x->security)
- audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
- x->security->ctx_alg, x->security->ctx_doi,
- x->security->ctx_str);
+ struct xfrm_sec_ctx *ctx = x->security;
+ u32 spi = ntohl(x->id.spi);
- switch(x->props.family) {
- case AF_INET:
- audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
- NIPQUAD(x->props.saddr.a4),
- NIPQUAD(x->id.daddr.a4));
- break;
- case AF_INET6:
- {
- struct in6_addr saddr6, daddr6;
-
- memcpy(&saddr6, x->props.saddr.a6,
- sizeof(struct in6_addr));
- memcpy(&daddr6, x->id.daddr.a6,
- sizeof(struct in6_addr));
- audit_log_format(audit_buf,
- " src=" NIP6_FMT " dst=" NIP6_FMT,
- NIP6(saddr6), NIP6(daddr6));
- }
- break;
- }
+ if (ctx)
+ audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
+ ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
+
+ switch(x->props.family) {
+ case AF_INET:
+ audit_log_format(audit_buf,
+ " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
+ NIPQUAD(x->props.saddr.a4),
+ NIPQUAD(x->id.daddr.a4));
+ break;
+ case AF_INET6:
+ audit_log_format(audit_buf,
+ " src=" NIP6_FMT " dst=" NIP6_FMT,
+ NIP6(*(struct in6_addr *)x->props.saddr.a6),
+ NIP6(*(struct in6_addr *)x->id.daddr.a6));
+ break;
+ }
+
+ audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
}
-void
-xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid)
+void xfrm_audit_state_add(struct xfrm_state *x, int result,
+ u32 auid, u32 secid)
{
struct audit_buffer *audit_buf;
- extern int audit_enabled;
if (audit_enabled == 0)
return;
- audit_buf = xfrm_audit_start(sid, auid);
+ audit_buf = xfrm_audit_start(secid, auid);
if (audit_buf == NULL)
return;
- audit_log_format(audit_buf, " op=SAD-add res=%u",result);
+ audit_log_format(audit_buf, " op=SAD-add res=%u", result);
xfrm_audit_common_stateinfo(x, audit_buf);
- audit_log_format(audit_buf, " spi=%lu(0x%lx)",
- (unsigned long)x->id.spi, (unsigned long)x->id.spi);
audit_log_end(audit_buf);
}
EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
-void
-xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid)
+void xfrm_audit_state_delete(struct xfrm_state *x, int result,
+ u32 auid, u32 secid)
{
struct audit_buffer *audit_buf;
- extern int audit_enabled;
if (audit_enabled == 0)
return;
- audit_buf = xfrm_audit_start(sid, auid);
+ audit_buf = xfrm_audit_start(secid, auid);
if (audit_buf == NULL)
return;
- audit_log_format(audit_buf, " op=SAD-delete res=%u",result);
+ audit_log_format(audit_buf, " op=SAD-delete res=%u", result);
xfrm_audit_common_stateinfo(x, audit_buf);
- audit_log_format(audit_buf, " spi=%lu(0x%lx)",
- (unsigned long)x->id.spi, (unsigned long)x->id.spi);
audit_log_end(audit_buf);
}
EXPORT_SYMBOL_GPL(xfrm_audit_state_delete);
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index e076039..c925880 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -85,7 +85,7 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
if (!selinux_authorizable_ctx(ctx))
return -EINVAL;
- sel_sid = ctx->ctx_sid;
+ sel_sid = ctx->ctx_secid;
}
else
/*
@@ -132,7 +132,7 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *
/* Not a SELinux-labeled SA */
return 0;
- state_sid = x->security->ctx_sid;
+ state_sid = x->security->ctx_secid;
if (fl->secid != state_sid)
return 0;
@@ -175,13 +175,13 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
struct xfrm_sec_ctx *ctx = x->security;
if (!sid_set) {
- *sid = ctx->ctx_sid;
+ *sid = ctx->ctx_secid;
sid_set = 1;
if (!ckall)
break;
}
- else if (*sid != ctx->ctx_sid)
+ else if (*sid != ctx->ctx_secid)
return -EINVAL;
}
}
@@ -232,7 +232,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
ctx->ctx_str[str_len] = 0;
rc = security_context_to_sid(ctx->ctx_str,
str_len,
- &ctx->ctx_sid);
+ &ctx->ctx_secid);
if (rc)
goto out;
@@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
/*
* Does the subject have permission to set security context?
*/
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->sid, ctx->ctx_secid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
if (rc)
@@ -264,7 +264,7 @@ not_from_user:
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
- ctx->ctx_sid = sid;
+ ctx->ctx_secid = sid;
ctx->ctx_len = str_len;
memcpy(ctx->ctx_str,
ctx_str,
@@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
int rc = 0;
if (ctx)
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->sid, ctx->ctx_secid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
@@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
int rc = 0;
if (ctx)
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(tsec->sid, ctx->ctx_secid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
@@ -412,7 +412,7 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
if (x && selinux_authorizable_xfrm(x)) {
struct xfrm_sec_ctx *ctx = x->security;
- sel_sid = ctx->ctx_sid;
+ sel_sid = ctx->ctx_secid;
break;
}
}
^ permalink raw reply related
* Re: [PATCH] IPv6 support for NFS server
From: Brian Haley @ 2007-12-11 16:41 UTC (permalink / raw)
To: Aurélien Charbon; +Cc: Mailing list NFSv4, netdev ML
In-Reply-To: <475D86C1.6070400@ext.bull.net>
Hi Aurelien,
Aurélien Charbon wrote:
>
> Here is a cleanup for the ip_map caching patch in nfs server.
>
> It prepares for IPv6 text-based mounts and exports.
>
> Tests: tested with only IPv4 network and basic nfs ops (mount, file
> creation and modification)
In an email back on October 29th I sent-out a similar patch with a new
ipv6_addr_set_v4mapped() inline - it might be useful to pull that piece
into your patch since it cleans it up a bit to get rid of the
ipv6_addr_set() calls. I can re-send you that patch off-line if you
can't find it.
-Brian
^ permalink raw reply
* Re: [PATCH] IPv6 support for NFS server
From: Aurélien Charbon @ 2007-12-11 16:46 UTC (permalink / raw)
To: J. Bruce Fields; +Cc: netdev ML, Mailing list NFSv4
In-Reply-To: <20071210183414.GB28557@fieldses.org>
J. Bruce Fields wrote:
>On Mon, Dec 10, 2007 at 07:34:41PM +0100, Aurélien Charbon wrote:
>
>
>>Here is a cleanup for the ip_map caching patch in nfs server.
>>
>>It prepares for IPv6 text-based mounts and exports.
>>
>>Tests: tested with only IPv4 network and basic nfs ops (mount, file
>>creation and modification)
>>
>>
>
>Thanks! And also tested with an unmodified rpc.mountd?
>
>
Yes I also tested it with a basic nfs-utils-1.1.1 + CITI NFS4_ALL-1 patch.
Aurélien
--
********************************
Aurelien Charbon
Linux NFSv4 team
Bull SAS
Echirolles - France
http://nfsv4.bullopensource.org/
********************************
_______________________________________________
NFSv4 mailing list
NFSv4@linux-nfs.org
http://linux-nfs.org/cgi-bin/mailman/listinfo/nfsv4
^ permalink raw reply
* [0/2] [IPSEC]: Add host ICMP relookup support
From: Herbert Xu @ 2007-12-11 16:53 UTC (permalink / raw)
To: David S. Miller, netdev
Hi Dave:
These two patches implement the ICMP relookup feature for hosts
that's required by RFC 4301. I've made it conditional on policy
and state flags so it should have no impact on existing users.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* [PATCH 1/2] [IPSEC]: Added xfrm_decode_session_reverse and xfrmX_policy_check_reverse
From: Herbert Xu @ 2007-12-11 16:55 UTC (permalink / raw)
To: David S. Miller, netdev
In-Reply-To: <20071211165317.GA16420@gondor.apana.org.au>
[IPSEC]: Added xfrm_decode_session_reverse and xfrmX_policy_check_reverse
RFC 4301 requires us to relookup ICMP traffic that does not match any
policies using the reverse of its payload. This patch adds the functions
xfrm_decode_session_reverse and xfrmX_policy_check_reverse so we can get
the reverse flow to perform such a lookup.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
include/linux/xfrm.h | 1
include/net/xfrm.h | 63 ++++++++++++++++++++++++++++++++++++++++++++----
net/ipv4/xfrm4_policy.c | 10 +++----
net/ipv6/xfrm6_policy.c | 10 +++----
net/xfrm/xfrm_policy.c | 17 ++++++++----
5 files changed, 80 insertions(+), 21 deletions(-)
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index df2e62a..f3819ee 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -121,6 +121,7 @@ enum
XFRM_POLICY_IN = 0,
XFRM_POLICY_OUT = 1,
XFRM_POLICY_FWD = 2,
+ XFRM_POLICY_MASK = 3,
XFRM_POLICY_MAX = 3
};
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 92ee8e5..5fae184 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -240,7 +240,8 @@ struct xfrm_policy_afinfo {
int (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr);
struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy);
void (*decode_session)(struct sk_buff *skb,
- struct flowi *fl);
+ struct flowi *fl,
+ int reverse);
int (*get_tos)(struct flowi *fl);
int (*fill_dst)(struct xfrm_dst *xdst,
struct net_device *dev);
@@ -846,14 +847,23 @@ xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short
extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family);
-static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family)
+static inline int __xfrm_policy_check2(struct sock *sk, int dir,
+ struct sk_buff *skb,
+ unsigned int family, int reverse)
{
+ int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0);
+
if (sk && sk->sk_policy[XFRM_POLICY_IN])
- return __xfrm_policy_check(sk, dir, skb, family);
+ return __xfrm_policy_check(sk, ndir, skb, family);
return (!xfrm_policy_count[dir] && !skb->sp) ||
(skb->dst->flags & DST_NOPOLICY) ||
- __xfrm_policy_check(sk, dir, skb, family);
+ __xfrm_policy_check(sk, ndir, skb, family);
+}
+
+static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family)
+{
+ return __xfrm_policy_check2(sk, dir, skb, family, 0);
}
static inline int xfrm4_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
@@ -866,7 +876,34 @@ static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *s
return xfrm_policy_check(sk, dir, skb, AF_INET6);
}
-extern int xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family);
+static inline int xfrm4_policy_check_reverse(struct sock *sk, int dir,
+ struct sk_buff *skb)
+{
+ return __xfrm_policy_check2(sk, dir, skb, AF_INET, 1);
+}
+
+static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir,
+ struct sk_buff *skb)
+{
+ return __xfrm_policy_check2(sk, dir, skb, AF_INET6, 1);
+}
+
+extern int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
+ unsigned int family, int reverse);
+
+static inline int xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
+ unsigned int family)
+{
+ return __xfrm_decode_session(skb, fl, family, 0);
+}
+
+static inline int xfrm_decode_session_reverse(struct sk_buff *skb,
+ struct flowi *fl,
+ unsigned int family)
+{
+ return __xfrm_decode_session(skb, fl, family, 1);
+}
+
extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
@@ -927,6 +964,22 @@ static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *sk
{
return 1;
}
+static inline int xfrm_decode_session_reverse(struct sk_buff *skb,
+ struct flowi *fl,
+ unsigned int family)
+{
+ return -ENOSYS;
+}
+static inline int xfrm4_policy_check_reverse(struct sock *sk, int dir,
+ struct sk_buff *skb)
+{
+ return 1;
+}
+static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir,
+ struct sk_buff *skb)
+{
+ return 1;
+}
#endif
static __inline__
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 10b72d1..5ccae3a 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -115,7 +115,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev)
}
static void
-_decode_session4(struct sk_buff *skb, struct flowi *fl)
+_decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
{
struct iphdr *iph = ip_hdr(skb);
u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
@@ -131,8 +131,8 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
__be16 *ports = (__be16 *)xprth;
- fl->fl_ip_sport = ports[0];
- fl->fl_ip_dport = ports[1];
+ fl->fl_ip_sport = ports[!!reverse];
+ fl->fl_ip_dport = ports[!reverse];
}
break;
@@ -174,8 +174,8 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
}
}
fl->proto = iph->protocol;
- fl->fl4_dst = iph->daddr;
- fl->fl4_src = iph->saddr;
+ fl->fl4_dst = reverse ? iph->saddr : iph->daddr;
+ fl->fl4_src = reverse ? iph->daddr : iph->saddr;
fl->fl4_tos = iph->tos;
}
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 9c14e02..6714831 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -124,7 +124,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev)
}
static inline void
-_decode_session6(struct sk_buff *skb, struct flowi *fl)
+_decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
{
u16 offset = skb_network_header_len(skb);
struct ipv6hdr *hdr = ipv6_hdr(skb);
@@ -133,8 +133,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
u8 nexthdr = nh[IP6CB(skb)->nhoff];
memset(fl, 0, sizeof(struct flowi));
- ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr);
- ipv6_addr_copy(&fl->fl6_src, &hdr->saddr);
+ ipv6_addr_copy(&fl->fl6_dst, reverse ? &hdr->saddr : &hdr->daddr);
+ ipv6_addr_copy(&fl->fl6_src, reverse ? &hdr->daddr : &hdr->saddr);
while (pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
nh = skb_network_header(skb);
@@ -157,8 +157,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
if (pskb_may_pull(skb, nh + offset + 4 - skb->data)) {
__be16 *ports = (__be16 *)exthdr;
- fl->fl_ip_sport = ports[0];
- fl->fl_ip_dport = ports[1];
+ fl->fl_ip_sport = ports[!!reverse];
+ fl->fl_ip_dport = ports[!reverse];
}
fl->proto = nexthdr;
return;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 265c679..55fd8b7 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1732,8 +1732,8 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
return start;
}
-int
-xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
+int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
+ unsigned int family, int reverse)
{
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int err;
@@ -1741,12 +1741,12 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
- afinfo->decode_session(skb, fl);
+ afinfo->decode_session(skb, fl, reverse);
err = security_xfrm_decode_session(skb, &fl->secid);
xfrm_policy_put_afinfo(afinfo);
return err;
}
-EXPORT_SYMBOL(xfrm_decode_session);
+EXPORT_SYMBOL(__xfrm_decode_session);
static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp)
{
@@ -1768,11 +1768,16 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
int npols = 0;
int xfrm_nr;
int pi;
+ int reverse;
struct flowi fl;
- u8 fl_dir = policy_to_flow_dir(dir);
+ u8 fl_dir;
int xerr_idx = -1;
- if (xfrm_decode_session(skb, &fl, family) < 0)
+ reverse = dir & ~XFRM_POLICY_MASK;
+ dir &= XFRM_POLICY_MASK;
+ fl_dir = policy_to_flow_dir(dir);
+
+ if (__xfrm_decode_session(skb, &fl, family, reverse) < 0)
return 0;
nf_nat_decode_session(skb, &fl, family);
^ permalink raw reply related
* [PATCH 2/2] [IPSEC]: Add ICMP host relookup support
From: Herbert Xu @ 2007-12-11 16:55 UTC (permalink / raw)
To: David S. Miller, netdev
In-Reply-To: <20071211165317.GA16420@gondor.apana.org.au>
[IPSEC]: Add ICMP host relookup support
RFC 4301 requires us to relookup ICMP traffic that does not match any
policies using the reverse of its payload. This patch implements this
for ICMP traffic that originates from or terminates on localhost.
This is activated on outbound with the new policy flag XFRM_POLICY_ICMP,
and on inbound by the new state flag XFRM_STATE_ICMP.
On inbound the policy check is now performed by the ICMP protocol so
that it can repeat the policy check where necessary.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
include/linux/xfrm.h | 3 +
include/net/dst.h | 1
net/ipv4/af_inet.c | 1
net/ipv4/icmp.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
net/ipv6/icmp.c | 60 +++++++++++++++++++++++++++++++++--
net/xfrm/xfrm_policy.c | 17 ++++++++--
6 files changed, 154 insertions(+), 10 deletions(-)
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index f3819ee..b0c4cc8 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -337,6 +337,7 @@ struct xfrm_usersa_info {
#define XFRM_STATE_DECAP_DSCP 2
#define XFRM_STATE_NOPMTUDISC 4
#define XFRM_STATE_WILDRECV 8
+#define XFRM_STATE_ICMP 16
};
struct xfrm_usersa_id {
@@ -371,6 +372,8 @@ struct xfrm_userpolicy_info {
#define XFRM_POLICY_BLOCK 1
__u8 flags;
#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
+ /* Automatically expand selector to include matching ICMP payloads. */
+#define XFRM_POLICY_ICMP 2
__u8 share;
};
diff --git a/include/net/dst.h b/include/net/dst.h
index aaa2dbb..31468c9 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -268,6 +268,7 @@ extern void dst_init(void);
/* Flags for xfrm_lookup flags argument. */
enum {
XFRM_LOOKUP_WAIT = 1 << 0,
+ XFRM_LOOKUP_ICMP = 1 << 1,
};
struct flowi;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 0e4b6eb..48fc0f8 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1291,6 +1291,7 @@ static struct net_protocol udp_protocol = {
static struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
+ .no_policy = 1,
};
static int __init init_ipv4_mibs(void)
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index c0898c5..4e3bfcd 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -92,6 +92,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <net/checksum.h>
+#include <net/xfrm.h>
/*
* Build xmit assembly blocks
@@ -564,11 +565,71 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
}
}
};
+ int err;
+ struct rtable *rt2;
+
security_skb_classify_flow(skb_in, &fl);
- if (ip_route_output_key(&rt, &fl))
+ if (__ip_route_output_key(&rt, &fl))
+ goto out_unlock;
+
+ /* No need to clone since we're just using its address. */
+ rt2 = rt;
+
+ err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
+ switch (err) {
+ case 0:
+ if (rt != rt2)
+ goto route_done;
+ break;
+ case -EPERM:
+ rt = NULL;
+ break;
+ default:
+ goto out_unlock;
+ }
+
+ if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET))
+ goto out_unlock;
+
+ if (inet_addr_type(fl.fl4_src) == RTN_LOCAL)
+ err = __ip_route_output_key(&rt2, &fl);
+ else {
+ struct flowi fl2 = {};
+ struct dst_entry *odst;
+
+ fl2.fl4_dst = fl.fl4_src;
+ if (ip_route_output_key(&rt2, &fl2))
+ goto out_unlock;
+
+ /* Ugh! */
+ odst = skb_in->dst;
+ err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
+ RT_TOS(tos), rt2->u.dst.dev);
+
+ dst_release(&rt2->u.dst);
+ rt2 = (struct rtable *)skb_in->dst;
+ skb_in->dst = odst;
+ }
+
+ if (err)
+ goto out_unlock;
+
+ err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
+ XFRM_LOOKUP_ICMP);
+ if (err == -ENOENT) {
+ if (!rt)
+ goto out_unlock;
+ goto route_done;
+ }
+
+ dst_release(&rt->u.dst);
+ rt = rt2;
+
+ if (err)
goto out_unlock;
}
+route_done:
if (!icmpv4_xrlim_allow(rt, type, code))
goto ende;
@@ -917,6 +978,22 @@ int icmp_rcv(struct sk_buff *skb)
struct icmphdr *icmph;
struct rtable *rt = (struct rtable *)skb->dst;
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb) &&
+ skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
+ int nh;
+
+ if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
+ goto drop;
+
+ nh = skb_network_offset(skb);
+ skb_set_network_header(skb, sizeof(*icmph));
+
+ if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
+ goto drop;
+
+ skb_set_network_header(skb, nh);
+ }
+
ICMP_INC_STATS_BH(ICMP_MIB_INMSGS);
switch (skb->ip_summed) {
@@ -930,8 +1007,7 @@ int icmp_rcv(struct sk_buff *skb)
goto error;
}
- if (!pskb_pull(skb, sizeof(struct icmphdr)))
- goto error;
+ __skb_pull(skb, sizeof(*icmph));
icmph = icmp_hdr(skb);
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 4582bbc..478ee77 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -63,6 +63,7 @@
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/icmp.h>
+#include <net/xfrm.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -86,7 +87,7 @@ static int icmpv6_rcv(struct sk_buff *skb);
static struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
- .flags = INET6_PROTO_FINAL,
+ .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
static __inline__ int icmpv6_xmit_lock(void)
@@ -310,8 +311,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct ipv6_pinfo *np;
struct in6_addr *saddr = NULL;
struct dst_entry *dst;
+ struct dst_entry *dst2;
struct icmp6hdr tmp_hdr;
struct flowi fl;
+ struct flowi fl2;
struct icmpv6_msg msg;
int iif = 0;
int addr_type = 0;
@@ -418,9 +421,42 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
goto out_dst_release;
}
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ /* No need to clone since we're just using its address. */
+ dst2 = dst;
+
+ err = xfrm_lookup(&dst, &fl, sk, 0);
+ switch (err) {
+ case 0:
+ if (dst != dst2)
+ goto route_done;
+ break;
+ case -EPERM:
+ dst = NULL;
+ break;
+ default:
+ goto out;
+ }
+
+ if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6))
+ goto out;
+
+ if (ip6_dst_lookup(sk, &dst2, &fl))
goto out;
+ err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP);
+ if (err == -ENOENT) {
+ if (!dst)
+ goto out;
+ goto route_done;
+ }
+
+ dst_release(dst);
+ dst = dst2;
+
+ if (err)
+ goto out;
+
+route_done:
if (ipv6_addr_is_multicast(&fl.fl6_dst))
hlimit = np->mcast_hops;
else
@@ -610,6 +646,22 @@ static int icmpv6_rcv(struct sk_buff *skb)
struct icmp6hdr *hdr;
int type;
+ if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb) &&
+ skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
+ int nh;
+
+ if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr)))
+ goto drop_no_count;
+
+ nh = skb_network_offset(skb);
+ skb_set_network_header(skb, sizeof(*hdr));
+
+ if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
+ goto drop_no_count;
+
+ skb_set_network_header(skb, nh);
+ }
+
ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS);
saddr = &ipv6_hdr(skb)->saddr;
@@ -632,8 +684,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
}
}
- if (!pskb_pull(skb, sizeof(struct icmp6hdr)))
- goto discard_it;
+ __skb_pull(skb, sizeof(*hdr));
hdr = icmp6_hdr(skb);
@@ -719,6 +770,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
discard_it:
ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);
+drop_no_count:
kfree_skb(skb);
return 0;
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 55fd8b7..b04d88c 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1469,11 +1469,13 @@ restart:
goto dropdst;
}
+ err = -ENOENT;
+
if (!policy) {
/* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) ||
!xfrm_policy_count[XFRM_POLICY_OUT])
- return 0;
+ goto nopol;
policy = flow_cache_lookup(fl, dst_orig->ops->family,
dir, xfrm_policy_lookup);
@@ -1483,14 +1485,18 @@ restart:
}
if (!policy)
- return 0;
+ goto nopol;
family = dst_orig->ops->family;
- policy->curlft.use_time = get_seconds();
pols[0] = policy;
npols ++;
xfrm_nr += pols[0]->xfrm_nr;
+ if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP))
+ goto error;
+
+ policy->curlft.use_time = get_seconds();
+
switch (policy->action) {
default:
case XFRM_POLICY_BLOCK:
@@ -1649,6 +1655,11 @@ dropdst:
dst_release(dst_orig);
*dst_p = NULL;
return err;
+
+nopol:
+ if (flags & XFRM_LOOKUP_ICMP)
+ goto dropdst;
+ return 0;
}
EXPORT_SYMBOL(__xfrm_lookup);
^ permalink raw reply related
* RE: [PATCH] Increase virtual FIFOs in ucc_geth.
From: Joakim Tjernlund @ 2007-12-11 16:55 UTC (permalink / raw)
To: Li Yang; +Cc: netdev
In-Reply-To: <989B956029373F45A0B8AF029708189001B4D908@zch01exm26.fsl.freescale.net>
On Tue, 2007-12-11 at 19:51 +0800, Li Yang wrote:
> > -----Original Message-----
> > From: Joakim Tjernlund [mailto:joakim.tjernlund@transmode.se]
> > Sent: Tuesday, December 11, 2007 6:58 PM
> > To: Li Yang
> > Cc: netdev@vger.kernel.org
> > Subject: RE: [PATCH] Increase virtual FIFOs in ucc_geth.
> >
> >
> > On Tue, 2007-12-11 at 11:11 +0100, Joakim Tjernlund wrote:
> > > On Tue, 2007-12-11 at 17:49 +0800, Li Yang wrote:
> > > > > -----Original Message-----
> > > > > From: Joakim Tjernlund [mailto:Joakim.Tjernlund@transmode.se]
> > > > > Sent: Tuesday, December 11, 2007 2:46 AM
> > > > > To: Li Yang-r58472 <LeoLi@freescale.com> Netdev
> > > > > Cc: Joakim Tjernlund
> > > > > Subject: [PATCH] Increase virtual FIFOs in ucc_geth.
> > > > >
> > > > > Increase UCC_GETH_URFS_INIT to 1152 and
> > > > > UCC_GETH_UTFS_INIT to 896 to avoid HW Overrun/Underrun.
> > > >
> > > > Please be noted that these values are only used for
> > 10/100Mbps speed.
> > > > Did you get Overrun in 10/100M mode?
> > >
> > > I get both TX Underrun and RX overrun in 100Mbps, FD, just
> > > by running a tftp transfer. It feels like the URFET and/or URSFET
> > > isn't working. Why I don't know. CPU is MPC832x
> > >
> > > Jocke
> >
> > I am a bit confused how the RBMR and TBMR is supposed to work. In
> > ucc_get there is:
> > out_be32(&ugeth->p_tx_glbl_pram->tstate, ((u32)
> > function_code) << 24);
> > ugeth->p_rx_glbl_pram->rstate = function_code;
> > First, should not the rx part look the same as tx?
>
> To be consist with the chip RM, type for tstate is u32 and type for
> rstate is u8. Personally I don't think that it will be different to
> access tstate as u8, but it will be more readable to be the same as
> manual. Well, rstate probably should be changed to use IO accessor too.
>
> > Does programing rstate/tstate replace RBMR and TBMR?
>
> RBMR and TBMR? These are for other protocols.
>
> - Leo
BTW a ping -f -s 90 -l 10 lock the board, can't do
anything useful. NAPI problem?
Jocke
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: David Miller @ 2007-12-11 17:06 UTC (permalink / raw)
To: paul.moore; +Cc: netdev, linux-audit, selinux
In-Reply-To: <20071211163019.15059.73746.stgit@flek.lan>
From: Paul Moore <paul.moore@hp.com>
Date: Tue, 11 Dec 2007 11:30:19 -0500
Sorry for not pointing this out sooner:
> * Convert 'sid' to 'secid'
> The 'sid' name is specific to SELinux, 'secid' is the common naming
> convention used by the kernel when refering to tokenized LSM labels
...
> diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
> index b58adc5..f75a337 100644
> --- a/include/linux/xfrm.h
> +++ b/include/linux/xfrm.h
> @@ -31,7 +31,7 @@ struct xfrm_sec_ctx {
> __u8 ctx_doi;
> __u8 ctx_alg;
> __u16 ctx_len;
> - __u32 ctx_sid;
> + __u32 ctx_secid;
> char ctx_str[0];
> };
>
This datastructure has been exported to userspace, so we really can't
member names unless it was added only in 2.6.24 and I don't think it
was.
Correct me if I'm wrong.
^ permalink raw reply
* Re: [PATCH 1/2] [IPSEC]: Added xfrm_decode_session_reverse and xfrmX_policy_check_reverse
From: David Miller @ 2007-12-11 17:09 UTC (permalink / raw)
To: herbert; +Cc: netdev
In-Reply-To: <E1J28Np-0004IK-00@gondolin.me.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 12 Dec 2007 00:55:01 +0800
> [IPSEC]: Added xfrm_decode_session_reverse and xfrmX_policy_check_reverse
>
> RFC 4301 requires us to relookup ICMP traffic that does not match any
> policies using the reverse of its payload. This patch adds the functions
> xfrm_decode_session_reverse and xfrmX_policy_check_reverse so we can get
> the reverse flow to perform such a lookup.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
I suspect you generated this against a tree with your async
crypto bits in it, because there were a bunch of line offsets
when I applied this.
Nevertheless I fixed it up, patch applied, thanks.
^ permalink raw reply
* Re: [PATCH 2/2] [IPSEC]: Add ICMP host relookup support
From: David Miller @ 2007-12-11 17:10 UTC (permalink / raw)
To: herbert; +Cc: netdev
In-Reply-To: <E1J28Nq-0004IU-00@gondolin.me.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 12 Dec 2007 00:55:02 +0800
> diff --git a/include/net/dst.h b/include/net/dst.h
> index aaa2dbb..31468c9 100644
> --- a/include/net/dst.h
> +++ b/include/net/dst.h
> @@ -268,6 +268,7 @@ extern void dst_init(void);
> /* Flags for xfrm_lookup flags argument. */
> enum {
> XFRM_LOOKUP_WAIT = 1 << 0,
> + XFRM_LOOKUP_ICMP = 1 << 1,
> };
>
> struct flowi;
This enumeration doesn't exist in my tree, what are you generating
these patches against?
I'm even reverting the first patch for now until this is figured out.
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: Paul Moore @ 2007-12-11 17:15 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-audit, selinux
In-Reply-To: <20071211.090611.59888503.davem@davemloft.net>
On Tuesday 11 December 2007 12:06:11 pm David Miller wrote:
> From: Paul Moore <paul.moore@hp.com>
> Date: Tue, 11 Dec 2007 11:30:19 -0500
>
> Sorry for not pointing this out sooner:
No problem, better late than never ... despite reports to the contrary,
breaking userspace doesn't excite me as much as it used to ;)
> > * Convert 'sid' to 'secid'
> > The 'sid' name is specific to SELinux, 'secid' is the common naming
> > convention used by the kernel when refering to tokenized LSM labels
>
> ...
>
> > diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
> > index b58adc5..f75a337 100644
> > --- a/include/linux/xfrm.h
> > +++ b/include/linux/xfrm.h
> > @@ -31,7 +31,7 @@ struct xfrm_sec_ctx {
> > __u8 ctx_doi;
> > __u8 ctx_alg;
> > __u16 ctx_len;
> > - __u32 ctx_sid;
> > + __u32 ctx_secid;
> > char ctx_str[0];
> > };
>
> This datastructure has been exported to userspace, so we really can't
> member names unless it was added only in 2.6.24 and I don't think it
> was.
>
> Correct me if I'm wrong.
Ungh, I didn't think the whole structure was exported to userspace as a single
binary blob; I'd assumed it was passed back and forth as individual
fields/attributes. I guess the old adage about assuming applies here ...
Grrr, that "sid" really bothers me but I guess it's a wart we're going to have
to live with. Stoopid userspace :)
I still would like to see the rest of the changes make it into 2.6.25 (the SPI
byte order thing is particularly troublesome) so if you don't mind a "v3"
I'll respin this patch right now to remove the "sid -> secid" bits.
--
paul moore
linux security @ hp
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-12-11 17:19 UTC (permalink / raw)
To: paul.moore; +Cc: netdev, linux-audit, selinux, yoshfuji
In-Reply-To: <20071211163019.15059.73746.stgit@flek.lan>
In article <20071211163019.15059.73746.stgit@flek.lan> (at Tue, 11 Dec 2007 11:30:19 -0500), Paul Moore <paul.moore@hp.com> says:
> diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
> index 5b860b6..e2a3dd1 100644
> --- a/net/xfrm/xfrm_state.c
> +++ b/net/xfrm/xfrm_state.c
:
> @@ -1994,67 +1995,59 @@ void __init xfrm_state_init(void)
> static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x,
> struct audit_buffer *audit_buf)
> {
> - if (x->security)
> - audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
> - x->security->ctx_alg, x->security->ctx_doi,
> - x->security->ctx_str);
> + struct xfrm_sec_ctx *ctx = x->security;
> + u32 spi = ntohl(x->id.spi);
>
> - switch(x->props.family) {
> - case AF_INET:
> - audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
> - NIPQUAD(x->props.saddr.a4),
> - NIPQUAD(x->id.daddr.a4));
> - break;
> - case AF_INET6:
> - {
> - struct in6_addr saddr6, daddr6;
> -
> - memcpy(&saddr6, x->props.saddr.a6,
> - sizeof(struct in6_addr));
> - memcpy(&daddr6, x->id.daddr.a6,
> - sizeof(struct in6_addr));
> - audit_log_format(audit_buf,
> - " src=" NIP6_FMT " dst=" NIP6_FMT,
> - NIP6(saddr6), NIP6(daddr6));
> - }
> - break;
> - }
> + if (ctx)
> + audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
> + ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
> +
> + switch(x->props.family) {
> + case AF_INET:
> + audit_log_format(audit_buf,
> + " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
> + NIPQUAD(x->props.saddr.a4),
> + NIPQUAD(x->id.daddr.a4));
> + break;
> + case AF_INET6:
> + audit_log_format(audit_buf,
> + " src=" NIP6_FMT " dst=" NIP6_FMT,
> + NIP6(*(struct in6_addr *)x->props.saddr.a6),
> + NIP6(*(struct in6_addr *)x->id.daddr.a6));
> + break;
> + }
> +
> + audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi);
> }
>
Please do not mangle tabs into spaces.
--yoshfuji
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: David Miller @ 2007-12-11 17:21 UTC (permalink / raw)
To: paul.moore; +Cc: netdev, linux-audit, selinux
In-Reply-To: <200712111215.00720.paul.moore@hp.com>
From: Paul Moore <paul.moore@hp.com>
Date: Tue, 11 Dec 2007 12:15:00 -0500
> I still would like to see the rest of the changes make it into
> 2.6.25 (the SPI byte order thing is particularly troublesome) so if
> you don't mind a "v3" I'll respin this patch right now to remove the
> "sid -> secid" bits.
Technically this could break anything parsing the audit logs, but no
matter, I'd rather fix this now while we still can.
I would classify the spi endianness bit as a bug fix, could you please
just split out that fix for net-2.6, then we can make a second patch
after I rebase net-2.6.25 which can do the rest of your patch sans the
linux/xfrm.h change?
Thanks.
^ permalink raw reply
* Re: [PATCH 8/8] [PATCH v2] [CCID3]: Interface CCID3 code with newer Loss Intervals Database
From: Arnaldo Carvalho de Melo @ 2007-12-11 17:22 UTC (permalink / raw)
To: Gerrit Renker, dccp, netdev
In-Reply-To: <20071211094238.GA9926@gerrit.erg.abdn.ac.uk>
Em Tue, Dec 11, 2007 at 09:42:38AM +0000, Gerrit Renker escreveu:
> | When interfacing we must make sure that ccid3 tfrc_lh_slab is created
> | and then tfrc_li_cachep is not needed. I'm doing this while keeping
> | the structure of the patches, i.e. one introducing, the other removing.
> | But we need to create tfrc_lh_slab if we want the tree to be bisectable.
> |
> | I'm doing this and keeping your Signed-off-line, please holler if you
> | disagree for some reason.
> If you are just shifting and reordering then that is fine with me. But
> it seems you mean a different patch since in this one there is no slab
> initialisation.
This time around I'm not doing any reordering, just trying to use your
patches as is, but adding this patch as-is produces a kernel that will
crash, no?
> The loss history and the RX/TX packet history slabs are all created in
> tfrc.c using the three different __init routines of the dccp_tfrc_lib.
Yes, the init routines are called and in turn they create the slab
caches, but up to the patch "[PATCH 8/8] [PATCH v2] [CCID3]: Interface
CCID3 code with newer Loss Intervals Database" the new li slab is not
being created, no? See what I'm talking?
- Arnaldo
^ permalink raw reply
* Re: [PATCH 1/2] add driver for enc28j60 ethernet chip
From: Stephen Hemminger @ 2007-12-11 17:06 UTC (permalink / raw)
To: Claudio Lanconelli; +Cc: netdev, jgarzik
In-Reply-To: <475EA546.7030202@eptar.com>
On Tue, 11 Dec 2007 15:57:10 +0100
Claudio Lanconelli <lanconelli.claudio@eptar.com> wrote:
> These patches add support for Microchip enc28j60 ethernet chip
> controlled via SPI.
> I tested it on my custom board (S162) with ARM9 s3c2442 SoC.
> Any comments are welcome.
>
> Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
General comments:
* device driver does no carrier detection. This makes it useless
for bridging, bonding, or any form of failover.
* use random_ether_addr rather than hardocoding constant mac address
* use msglevel method (via ethtool) to control debug messages
rather than kernel configuration. This allows enabling debugging
without recompilation which is important in distributions.
* kernel uses u8 rather than uint8_t for byte variables
* use netdev_priv(netdev) rather than netdev->priv
* Please add ethtool support
* Consider using NAPI
Output from checkpatch:
ERROR: do not initialise statics to 0 or NULL
#148: FILE: drivers/net/enc28j60.c:73:
+static int full_duplex = 0;
ERROR: "foo * bar" should be "foo *bar"
#166: FILE: drivers/net/enc28j60.c:91:
+ const uint8_t * data);
ERROR: "foo * bar" should be "foo *bar"
#245: FILE: drivers/net/enc28j60.c:170:
+ const uint8_t * data)
ERROR: "foo * bar" should be "foo *bar"
#413: FILE: drivers/net/enc28j60.c:338:
+ uint16_t addr, int len, uint8_t * data)
WARNING: printk() should include KERN_ facility level
#557: FILE: drivers/net/enc28j60.c:482:
+ printk("Cntrl: ECON1 ECON2 ESTAT EIR EIE\n");
WARNING: printk() should include KERN_ facility level
#558: FILE: drivers/net/enc28j60.c:483:
+ printk(" 0x%02x ", enc28j60_regb_read(priv, ECON1));
WARNING: printk() should include KERN_ facility level
#564: FILE: drivers/net/enc28j60.c:489:
+ printk("MAC : MACON1 MACON3 MACON4 MAC-Address\n");
WARNING: printk() should include KERN_ facility level
#565: FILE: drivers/net/enc28j60.c:490:
+ printk(" 0x%02x ", enc28j60_regb_read(priv, MACON1));
WARNING: printk() should include KERN_ facility level
#575: FILE: drivers/net/enc28j60.c:500:
+ printk("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\n");
WARNING: printk() should include KERN_ facility level
#576: FILE: drivers/net/enc28j60.c:501:
+ printk(" 0x%04x ", enc28j60_regw_read(priv, ERXSTL));
WARNING: printk() should include KERN_ facility level
#584: FILE: drivers/net/enc28j60.c:509:
+ printk("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\n");
WARNING: printk() should include KERN_ facility level
#585: FILE: drivers/net/enc28j60.c:510:
+ printk(" 0x%04x ", enc28j60_regw_read(priv, ETXSTL));
WARNING: braces {} are not necessary for single statement blocks
#601: FILE: drivers/net/enc28j60.c:526:
+ if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) {
+ erxrdpt = end;
+ } else
WARNING: line over 80 characters
#810: FILE: drivers/net/enc28j60.c:735:
+ "ExCollision: %d, LateCollision: %d, Giant: %d, Underrun: %d\n",
WARNING: line over 80 characters
#961: FILE: drivers/net/enc28j60.c:886:
+ "enc28j60 RX, max_pk_cnt: %d\n",
WARNING: line over 80 characters
#965: FILE: drivers/net/enc28j60.c:890:
+ * don't need to clear interrupt flag, automatically done
WARNING: printk() should include KERN_ facility level
#987: FILE: drivers/net/enc28j60.c:912:
+ printk("\n%04x: ", k);
WARNING: labels should not be indented
#1423: FILE: drivers/net/enc28j60.c:1348:
+ error_register:
WARNING: labels should not be indented
#1425: FILE: drivers/net/enc28j60.c:1350:
+ error_irq:
WARNING: labels should not be indented
#1427: FILE: drivers/net/enc28j60.c:1352:
+ error_buf:
WARNING: labels should not be indented
#1429: FILE: drivers/net/enc28j60.c:1354:
+ error_alloc:
total: 4 errors, 17 warnings, 1703 lines checked
Your patch has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
My comments:
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
new file mode 100644
index 0000000..6182473
--- /dev/null
+++ b/drivers/net/enc28j60.c
@@ -0,0 +1,1400 @@
+/*
+ * Microchip ENC28J60 ethernet driver (MAC + PHY)
+ *
+ * Copyright (C) 2007 Eurek srl
+ * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
+ * based on enc28j60.c written by David Anders for 2.4 kernel version
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: enc28j60.c,v 1.10 2007/12/10 16:59:37 claudio Exp $
+ */
+
+#include <linux/autoconf.h>
Use msglvl instead see netdevice.h
+
+#if CONFIG_ENC28J60_DBGLEVEL > 1
+# define VERBOSE_DEBUG
+#endif
+#if CONFIG_ENC28J60_DBGLEVEL > 0
+# define DEBUG
+#endif
+
...
+
+#define MY_TX_TIMEOUT ((500*HZ)/1000)
That is a really short TX timeout, should be 2 seconds at least not 1/2 sec.
Having it less than a second causes increased wakeups.
+
+/* Max TX retries in case of collision as suggested by errata datasheet */
+#define MAX_TX_RETRYCOUNT 16
+
+/* Driver local data */
+struct enc28j60_net_local {
Rename something shorter like enc28j60_net or just enc28j60?
+ struct net_device_stats stats;
net_device_stats are now in net_device.
+ struct net_device *netdev;
+ struct spi_device *spi;
+ struct semaphore semlock; /* protect spi_transfer_buf */
Use mutex (or spin_lock) rather than semaphore
+ uint8_t *spi_transfer_buf;
+ struct sk_buff *tx_skb;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
Not sure why you need to have workqueue's for
tx_work and irq_work, rather than using a spin_lock
and doing directly.
+ int bank; /* current register bank selected */
bank is really unsigned.
+ uint16_t next_pk_ptr; /* next packet pointer within FIFO */
+ int max_pk_counter; /* statistics: max packet counter */
+ int tx_retry_count;
these are used as unsigned.
+ int hw_enable;
+};
+
+/* Selects Full duplex vs. Half duplex mode */
+static int full_duplex = 0;
Use ethtool for this.
+
+static int enc28j60_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int enc28j60_net_close(struct net_device *dev);
+static struct net_device_stats *enc28j60_net_get_stats(struct net_device *dev);
+static void enc28j60_set_multicast_list(struct net_device *dev);
+static void enc28j60_net_tx_timeout(struct net_device *ndev);
+
+static int enc28j60_chipset_init(struct net_device *dev);
+static void enc28j60_hw_disable(struct enc28j60_net_local *priv);
+static void enc28j60_hw_enable(struct enc28j60_net_local *priv);
+static void enc28j60_hw_rx(struct enc28j60_net_local *priv);
+static void enc28j60_hw_tx(struct enc28j60_net_local *priv);
If you order functions correctly in code, you don't have to waste lots
of space with all these forward declarations.
...
+ const char *msg);
+
+/*
+ * SPI read buffer
+ * wait for the SPI transfer and copy received data to destination
+ */
+static int
+spi_read_buf(struct enc28j60_net_local *priv, int len, uint8_t *data)
+{
+ uint8_t *rx_buf;
+ uint8_t *tx_buf;
+ struct spi_transfer t;
+ struct spi_message msg;
+ int ret, slen;
+
+ slen = 1;
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = tx_buf = priv->spi_transfer_buf;
+ t.rx_buf = rx_buf = priv->spi_transfer_buf + 4;
+ t.len = slen + len;
If you use structure initializer you can avoid having to do
the memset
+
+ down(&priv->semlock);
+ tx_buf[0] = ENC28J60_READ_BUF_MEM;
+ tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&t, &msg);
+ ret = spi_sync(priv->spi, &msg);
+ if (ret == 0) {
+ memcpy(data, &rx_buf[slen], len);
+ ret = msg.status;
+ }
+ up(&priv->semlock);
+ if (ret != 0)
+ dev_dbg(&priv->netdev->dev, "%s: failed: ret = %d\n",
+ __FUNCTION__, ret);
+
+ return ret;
+}
...
+/*
+ * Register word read
+ */
+static inline int enc28j60_regw_read(struct enc28j60_net_local *priv,
+ uint8_t address)
+{
I wouldn't bother marking these as "inline" since the compiler
will decide to inline in most cases. By telling the compiler to
inline it may generate bigger/slower code.
+ int rl, rh;
+
+ enc28j60_set_bank(priv, address);
+ rl = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address);
+ rh = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address + 1);
+
+ return (rh << 8) | rl;
+}
...
+/*
+ * Program the hardware MAC address from dev->dev_addr.
+ */
+static void enc28j60_set_hw_macaddr(struct enc28j60_net_local *priv)
+{
+ struct net_device *ndev = priv->netdev;
+
+ if (!priv->hw_enable) {
+ /* NOTE: MAC address in ENC28J60 is byte-backward */
+ enc28j60_regb_write(priv, MAADR5, ndev->dev_addr[0]);
+ enc28j60_regb_write(priv, MAADR4, ndev->dev_addr[1]);
+ enc28j60_regb_write(priv, MAADR3, ndev->dev_addr[2]);
+ enc28j60_regb_write(priv, MAADR2, ndev->dev_addr[3]);
+ enc28j60_regb_write(priv, MAADR1, ndev->dev_addr[4]);
+ enc28j60_regb_write(priv, MAADR0, ndev->dev_addr[5]);
+
+ dev_dbg(&ndev->dev,
+ "%s() [%s] Setting MAC address to "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __FUNCTION__, ndev->name, ndev->dev_addr[0],
+ ndev->dev_addr[1], ndev->dev_addr[2], ndev->dev_addr[3],
+ ndev->dev_addr[4], ndev->dev_addr[5]);
+ } else
+ dev_dbg(&ndev->dev,
+ "%s() Warning: hw must be disabled to set hw "
+ "Mac address\n", __FUNCTION__);
Should return -EINVAL/-EBUSY/... instead of printing message.
+}
+
+/*
+ * Store the new hardware address in dev->dev_addr, and update the MAC.
+ */
+static int enc28j60_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ enc28j60_set_hw_macaddr(priv);
+
+ return 0;
+}
...
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *enc28j60_net_get_stats(struct net_device *dev)
+{
+ struct enc28j60_net_local *priv = netdev_priv(dev);
+
+ return &priv->stats;
+}
If you use dev->stats, then you don't need your own get_stats function.
...
+static int enc28j60_hw_init(struct enc28j60_net_local *priv)
+{
+ uint8_t reg;
+
+ dev_dbg(&priv->spi->dev, "%s() - %s\n",
+ __FUNCTION__, full_duplex ? "FullDuplex" : "HalfDuplex");
+ /* first soft reset the chip */
+ enc28j60_soft_reset(priv);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank0\n", __FUNCTION__);
+
+ /* Clear ECON1 */
+ spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, ECON1, 0x00);
+ priv->bank = 0;
+ priv->hw_enable = 0;
+ priv->tx_retry_count = 0;
+
+ enc28j60_regb_write(priv, ECON2, ECON2_AUTOINC);
+ enc28j60_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
+ enc28j60_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
+
+ /*
+ * Check the RevID.
+ * If it's 0x00 or 0xFF probably the enc28j60 is not mounted or
+ * damaged
+ */
+ reg = enc28j60_regb_read(priv, EREVID);
+ if (reg == 0x00 || reg == 0xff)
+ return 0;
+
+ dev_vdbg(&priv->spi->dev, "%s() bank1\n", __FUNCTION__);
+
+ /* default filter mode: (unicast OR broadcast) AND crc valid */
+ enc28j60_regb_write(priv, ERXFCON,
+ ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank2\n", __FUNCTION__);
+ /* enable MAC receive */
+ enc28j60_regb_write(priv, MACON1,
+ MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
+ /* enable automatic padding and CRC operations */
+ if (full_duplex) {
+ enc28j60_regb_write(priv, MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN |
+ MACON3_FRMLNEN | MACON3_FULDPX);
+ /* set inter-frame gap (non-back-to-back) */
+ enc28j60_regb_write(priv, MAIPGL, 0x12);
+ /* set inter-frame gap (back-to-back) */
+ enc28j60_regb_write(priv, MABBIPG, 0x15);
+ } else {
+ enc28j60_regb_write(priv, MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN |
+ MACON3_FRMLNEN);
+ enc28j60_regb_write(priv, MACON4, 1 << 6); /* DEFER bit */
+ /* set inter-frame gap (non-back-to-back) */
+ enc28j60_regw_write(priv, MAIPGL, 0x0C12);
+ /* set inter-frame gap (back-to-back) */
+ enc28j60_regb_write(priv, MABBIPG, 0x12);
+ }
+ /*
+ * MACLCON1 (default)
+ * MACLCON2 (default)
+ * Set the maximum packet size which the controller will accept
+ */
+ enc28j60_regw_write(priv, MAMXFLL, MAX_FRAMELEN);
+
+ dev_vdbg(&priv->spi->dev, "%s() bank3\n", __FUNCTION__);
+ /* NOTE: MAC address in ENC28J60 is byte-backward */
+ enc28j60_regb_write(priv, MAADR5, ENC28J60_MAC0);
+ enc28j60_regb_write(priv, MAADR4, ENC28J60_MAC1);
+ enc28j60_regb_write(priv, MAADR3, ENC28J60_MAC2);
+ enc28j60_regb_write(priv, MAADR2, ENC28J60_MAC3);
+ enc28j60_regb_write(priv, MAADR1, ENC28J60_MAC4);
+ enc28j60_regb_write(priv, MAADR0, ENC28J60_MAC5);
Rather than having same address, please use random_ether_addr()
to avoid problems with two devices with same ethernet address.
...
+static int __devinit enc28j60_probe(struct spi_device *spi)
+{
+ struct net_device *dev;
+ struct enc28j60_net_local *priv;
+ int ret = 0;
+
+ dev_dbg(&spi->dev, "%s() start\n", __FUNCTION__);
+
+ dev = alloc_etherdev(sizeof(struct enc28j60_net_local));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+ priv = netdev_priv(dev);
+
+ priv->netdev = dev; /* priv to netdev reference */
+ priv->spi = spi; /* priv to spi reference */
+ priv->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
Why not declare the transfer buffer as an array in spi?
+ if (!priv->spi_transfer_buf) {
+ ret = -ENOMEM;
+ goto error_buf;
+ }
+ init_MUTEX(&priv->semlock);
+
+ INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
+ INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
+ dev_set_drvdata(&spi->dev, priv); /* spi to priv reference */
+ SET_NETDEV_DEV(dev, &spi->dev);
^ permalink raw reply related
* net-2.6.25 being rebased...
From: David Miller @ 2007-12-11 17:26 UTC (permalink / raw)
To: netdev
I want to work out all the conflicts now that Linus has pulled
in my most recent batch of fixes.
I hope to have it done in the next hour or so.
Thanks.
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: Paul Moore @ 2007-12-11 17:34 UTC (permalink / raw)
To: YOSHIFUJI Hideaki / 吉藤英明
Cc: netdev, linux-audit, selinux
In-Reply-To: <20071211.091957.98044450.yoshfuji@linux-ipv6.org>
On Tuesday 11 December 2007 12:19:57 pm YOSHIFUJI Hideaki / 吉藤英明 wrote:
> Please do not mangle tabs into spaces.
Yes indeed. Not quite sure what happened there but I just fixed it.
Thanks for pointing that out.
--
paul moore
linux security @ hp
^ permalink raw reply
* Re: [PATCH v2] XFRM: assorted IPsec fixups
From: Paul Moore @ 2007-12-11 17:39 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-audit, selinux
In-Reply-To: <20071211.092126.151519334.davem@davemloft.net>
On Tuesday 11 December 2007 12:21:26 pm David Miller wrote:
> From: Paul Moore <paul.moore@hp.com>
> Date: Tue, 11 Dec 2007 12:15:00 -0500
>
> > I still would like to see the rest of the changes make it into
> > 2.6.25 (the SPI byte order thing is particularly troublesome) so if
> > you don't mind a "v3" I'll respin this patch right now to remove the
> > "sid -> secid" bits.
>
> Technically this could break anything parsing the audit logs, but no
> matter, I'd rather fix this now while we still can.
True, this does change how userspace sees things but I think that any
userspace code that currently uses this SPI value successfully is either
lucky or has a workaround/hack in place.
> I would classify the spi endianness bit as a bug fix, could you please
> just split out that fix for net-2.6, then we can make a second patch
> after I rebase net-2.6.25 which can do the rest of your patch sans the
> linux/xfrm.h change?
Sure. Although that's enough of a change that I'd want to retest the patch a
bit first. If I can't get it done today expect something in your inbox
tomorrow.
Thanks for your patience.
--
paul moore
linux security @ hp
^ permalink raw reply
* [PATCH net-2.6.25 0/7] Make ipv4_devconf (all and default) live in net namespaces
From: Pavel Emelyanov @ 2007-12-11 17:44 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Linux Containers, devel
The ipv4_devconf_(all) and ipv4_devconf_dflt are currently
global, but should be per-namespace.
This set moves them on the struct net. Or, more precisely,
on the struct netns_ipv4, which in turn is on the struct net.
There are two minor things that are to be done additionally
to this set:
1. The snmp_seq_show() needs the IPV4_DEVCONF_ALL(FORWARDING)
value, but since this entry is still global no valid struct
net can be get in it, so I use the init_net's one. After
snmp is made per-namespace, this will be fixed easily.
2. The rt_fill_info() needs the IPV4_DEVCONF_ALL(MC_FORWARDING),
but the routing code is not tuned to work inside namespaces
yet, so I use the init_net in it as well. Denis is currently
working on ipv4 routing, so this will be prepared shortly.
Happily, all the other places of devconf-s usage can provide a
struct net pointer.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox