* [PATCH 6/9] netdev: bfin_mac: add support for wake-on-lan magic packets
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Michael Hennerich
In-Reply-To: <1273505954-32588-1-git-send-email-vapier@gentoo.org>
From: Michael Hennerich <michael.hennerich@analog.com>
Note that WOL works only in PM Suspend Standby Mode (Sleep Mode).
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/bfin_mac.c | 76 +++++++++++++++++++++++++++++++++++++++++++++--
drivers/net/bfin_mac.h | 3 ++
2 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index bee5d7a..967d018 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -464,6 +464,14 @@ static int mii_probe(struct net_device *dev)
* Ethtool support
*/
+/*
+ * interrupt routine for magic packet wakeup
+ */
+static irqreturn_t bfin_mac_wake_interrupt(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
static int
bfin_mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
@@ -498,11 +506,57 @@ static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev,
strcpy(info->bus_info, dev_name(&dev->dev));
}
+static void bfin_mac_ethtool_getwol(struct net_device *dev,
+ struct ethtool_wolinfo *wolinfo)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+
+ wolinfo->supported = WAKE_MAGIC;
+ wolinfo->wolopts = lp->wol;
+}
+
+static int bfin_mac_ethtool_setwol(struct net_device *dev,
+ struct ethtool_wolinfo *wolinfo)
+{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ int rc;
+
+ if (wolinfo->wolopts & (WAKE_MAGICSECURE |
+ WAKE_UCAST |
+ WAKE_MCAST |
+ WAKE_BCAST |
+ WAKE_ARP))
+ return -EOPNOTSUPP;
+
+ lp->wol = wolinfo->wolopts;
+
+ if (lp->wol && !lp->irq_wake_requested) {
+ /* register wake irq handler */
+ rc = request_irq(IRQ_MAC_WAKEDET, bfin_mac_wake_interrupt,
+ IRQF_DISABLED, "EMAC_WAKE", dev);
+ if (rc)
+ return rc;
+ lp->irq_wake_requested = true;
+ }
+
+ if (!lp->wol && lp->irq_wake_requested) {
+ free_irq(IRQ_MAC_WAKEDET, dev);
+ lp->irq_wake_requested = false;
+ }
+
+ /* Make sure the PHY driver doesn't suspend */
+ device_init_wakeup(&dev->dev, lp->wol);
+
+ return 0;
+}
+
static const struct ethtool_ops bfin_mac_ethtool_ops = {
.get_settings = bfin_mac_ethtool_getsettings,
.set_settings = bfin_mac_ethtool_setsettings,
.get_link = ethtool_op_get_link,
.get_drvinfo = bfin_mac_ethtool_getdrvinfo,
+ .get_wol = bfin_mac_ethtool_getwol,
+ .set_wol = bfin_mac_ethtool_setwol,
};
/**************************************************************************/
@@ -1477,9 +1531,16 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev)
static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct net_device *net_dev = platform_get_drvdata(pdev);
+ struct bfin_mac_local *lp = netdev_priv(net_dev);
- if (netif_running(net_dev))
- bfin_mac_close(net_dev);
+ if (lp->wol) {
+ bfin_write_EMAC_OPMODE((bfin_read_EMAC_OPMODE() & ~TE) | RE);
+ bfin_write_EMAC_WKUP_CTL(MPKE);
+ enable_irq_wake(IRQ_MAC_WAKEDET);
+ } else {
+ if (netif_running(net_dev))
+ bfin_mac_close(net_dev);
+ }
return 0;
}
@@ -1487,9 +1548,16 @@ static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg)
static int bfin_mac_resume(struct platform_device *pdev)
{
struct net_device *net_dev = platform_get_drvdata(pdev);
+ struct bfin_mac_local *lp = netdev_priv(net_dev);
- if (netif_running(net_dev))
- bfin_mac_open(net_dev);
+ if (lp->wol) {
+ bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
+ bfin_write_EMAC_WKUP_CTL(0);
+ disable_irq_wake(IRQ_MAC_WAKEDET);
+ } else {
+ if (netif_running(net_dev))
+ bfin_mac_open(net_dev);
+ }
return 0;
}
diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h
index 87c454f..1ae7b82 100644
--- a/drivers/net/bfin_mac.h
+++ b/drivers/net/bfin_mac.h
@@ -66,6 +66,9 @@ struct bfin_mac_local {
unsigned char Mac[6]; /* MAC address of the board */
spinlock_t lock;
+ int wol; /* Wake On Lan */
+ int irq_wake_requested;
+
/* MII and PHY stuffs */
int old_link; /* used by bf537_adjust_link */
int old_speed;
--
1.7.1
^ permalink raw reply related
* [PATCH 5/9] netdev: bfin_mac: clear RXCKS if hardware generated checksum is not enabled
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Sonic Zhang
In-Reply-To: <1273505954-32588-1-git-send-email-vapier@gentoo.org>
From: Sonic Zhang <sonic.zhang@analog.com>
Otherwise we might be get a setting mismatch from a previous module or
bootloader and what the driver currently expects.
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/bfin_mac.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index b57b5ac..bee5d7a 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -515,10 +515,11 @@ void setup_system_regs(struct net_device *dev)
* Configure checksum support and rcve frame word alignment
*/
sysctl = bfin_read_EMAC_SYSCTL();
+ sysctl |= RXDWA;
#if defined(BFIN_MAC_CSUM_OFFLOAD)
- sysctl |= RXDWA | RXCKS;
+ sysctl |= RXCKS;
#else
- sysctl |= RXDWA;
+ sysctl &= ~RXCKS;
#endif
bfin_write_EMAC_SYSCTL(sysctl);
--
1.7.1
^ permalink raw reply related
* [PATCH 4/9 v2] netdev: bfin_mac: deduce Ethernet FCS from hardware IP payload checksum
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Sonic Zhang, Jon Kowal
In-Reply-To: <1273505954-32588-1-git-send-email-vapier@gentoo.org>
From: Sonic Zhang <sonic.zhang@analog.com>
IP checksum is based on 16-bit one's complement algorithm, so to deduce a
value from checksum is equal to add its complement.
Unfortunately, the Blackfin on-chip MAC checksum logic only works when the
IP packet has a header length of 20 bytes. This is true for most IPv4
packets, but not for IPv6 packets or IPv4 packets which use header options.
So only use the hardware checksum when appropriate.
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Jon Kowal <jon.kowal@dspecialists.de>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
v2
- folded the ipv4 checks into this patch
drivers/net/bfin_mac.c | 35 +++++++++++++++++++++++++++++++++--
1 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index f9ba598..b57b5ac 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -986,6 +986,7 @@ out:
return NETDEV_TX_OK;
}
+#define IP_HEADER_OFF 0
#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
@@ -994,6 +995,10 @@ static void bfin_mac_rx(struct net_device *dev)
struct sk_buff *skb, *new_skb;
unsigned short len;
struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+ unsigned int i;
+ unsigned char fcs[ETH_FCS_LEN + 1];
+#endif
/* check if frame status word reports an error condition
* we which case we simply drop the packet
@@ -1027,6 +1032,8 @@ static void bfin_mac_rx(struct net_device *dev)
current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
+ /* Deduce Ethernet FCS length from Ethernet payload length */
+ len -= ETH_FCS_LEN;
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, dev);
@@ -1034,8 +1041,32 @@ static void bfin_mac_rx(struct net_device *dev)
bfin_rx_hwtstamp(dev, skb);
#if defined(BFIN_MAC_CSUM_OFFLOAD)
- skb->csum = current_rx_ptr->status.ip_payload_csum;
- skb->ip_summed = CHECKSUM_COMPLETE;
+ /* Checksum offloading only works for IPv4 packets with the standard IP header
+ * length of 20 bytes, because the blackfin MAC checksum calculation is
+ * based on that assumption. We must NOT use the calculated checksum if our
+ * IP version or header break that assumption.
+ */
+ if (skb->data[IP_HEADER_OFF] == 0x45) {
+ skb->csum = current_rx_ptr->status.ip_payload_csum;
+ /*
+ * Deduce Ethernet FCS from hardware generated IP payload checksum.
+ * IP checksum is based on 16-bit one's complement algorithm.
+ * To deduce a value from checksum is equal to add its inversion.
+ * If the IP payload len is odd, the inversed FCS should also
+ * begin from odd address and leave first byte zero.
+ */
+ if (skb->len % 2) {
+ fcs[0] = 0;
+ for (i = 0; i < ETH_FCS_LEN; i++)
+ fcs[i + 1] = ~skb->data[skb->len + i];
+ skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum);
+ } else {
+ for (i = 0; i < ETH_FCS_LEN; i++)
+ fcs[i] = ~skb->data[skb->len + i];
+ skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum);
+ }
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
#endif
netif_rx(skb);
--
1.7.1
^ permalink raw reply related
* [PATCH 3/9] netdev: bfin_mac: invalid data cache only once for each new rx skb buffer
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Sonic Zhang
In-Reply-To: <1273505954-32588-1-git-send-email-vapier@gentoo.org>
From: Sonic Zhang <sonic.zhang@analog.com>
The skb buffer isn't actually used until we finish transferring and pass
it up to higher layers, so only invalidate the range once before we start
receiving actual data. This also avoids the problem with data invalidating
on Blackfin systems -- there is no invalidate-only, just invalidate+flush.
So when running in writeback mode, there is the small (but not uncommon)
possibility of the flush overwriting valid DMA-ed data from the cache.
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/bfin_mac.c | 13 ++++++++-----
1 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index c888465..f9ba598 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -203,6 +203,11 @@ static int desc_list_init(void)
goto init_error;
}
skb_reserve(new_skb, NET_IP_ALIGN);
+ /* Invidate the data cache of skb->data range when it is write back
+ * cache. It will prevent overwritting the new data from DMA
+ */
+ blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
+ (unsigned long)new_skb->end);
r->skb = new_skb;
/*
@@ -1012,19 +1017,17 @@ static void bfin_mac_rx(struct net_device *dev)
}
/* reserve 2 bytes for RXDWA padding */
skb_reserve(new_skb, NET_IP_ALIGN);
- current_rx_ptr->skb = new_skb;
- current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
-
/* Invidate the data cache of skb->data range when it is write back
* cache. It will prevent overwritting the new data from DMA
*/
blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
(unsigned long)new_skb->end);
+ current_rx_ptr->skb = new_skb;
+ current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
+
len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
skb_put(skb, len);
- blackfin_dcache_invalidate_range((unsigned long)skb->head,
- (unsigned long)skb->tail);
skb->protocol = eth_type_trans(skb, dev);
--
1.7.1
^ permalink raw reply related
* [PATCH 2/9] netdev: bfin_mac: handler RX status errors
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Peter Meerwald, Graf Yang
In-Reply-To: <1273505954-32588-1-git-send-email-vapier@gentoo.org>
From: Peter Meerwald <pmeerw@pmeerw.net>
Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/bfin_mac.c | 17 ++++++++++++++---
1 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index 6a9519f..c888465 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -981,12 +981,25 @@ out:
return NETDEV_TX_OK;
}
+#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
+ RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
+
static void bfin_mac_rx(struct net_device *dev)
{
struct sk_buff *skb, *new_skb;
unsigned short len;
struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
+ /* check if frame status word reports an error condition
+ * we which case we simply drop the packet
+ */
+ if (current_rx_ptr->status.status_word & RX_ERROR_MASK) {
+ printk(KERN_NOTICE DRV_NAME
+ ": rx: receive error - packet dropped\n");
+ dev->stats.rx_dropped++;
+ goto out;
+ }
+
/* allocate a new skb for next time receive */
skb = current_rx_ptr->skb;
@@ -1025,11 +1038,9 @@ static void bfin_mac_rx(struct net_device *dev)
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
+out:
current_rx_ptr->status.status_word = 0x00000000;
current_rx_ptr = current_rx_ptr->next;
-
-out:
- return;
}
/* interrupt routine to handle rx and error signal */
--
1.7.1
^ permalink raw reply related
* [PATCH 1/9] netdev: bfin_mac: add support for IEEE 1588 PTP
From: Mike Frysinger @ 2010-05-10 15:39 UTC (permalink / raw)
To: netdev, David S. Miller; +Cc: uclinux-dist-devel, Barry Song
From: Barry Song <barry.song@analog.com>
Newer on-chip MAC peripherals support IEEE 1588 PTP in the hardware, so
extend the driver to support this functionality.
Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/net/Kconfig | 7 +
drivers/net/bfin_mac.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++-
drivers/net/bfin_mac.h | 15 ++
3 files changed, 362 insertions(+), 1 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7b832c7..ac536b9 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -887,6 +887,13 @@ config BFIN_MAC_RMII
help
Use Reduced PHY MII Interface
+config BFIN_MAC_USE_HWSTAMP
+ bool "Use IEEE 1588 hwstamp"
+ depends on BFIN_MAC && BF518
+ default y
+ help
+ To support the IEEE 1588 Precision Time Protocol (PTP), select y here
+
config SMC9194
tristate "SMC 9194 support"
depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN)
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index 587f93c..6a9519f 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -33,6 +33,7 @@
#include <asm/dma.h>
#include <linux/dma-mapping.h>
+#include <asm/div64.h>
#include <asm/dpmc.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
@@ -551,6 +552,309 @@ static int bfin_mac_set_mac_address(struct net_device *dev, void *p)
return 0;
}
+#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
+#define bfin_mac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE)
+
+static int bfin_mac_hwtstamp_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ struct hwtstamp_config config;
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u16 ptpctl;
+ u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
+ __func__, config.flags, config.tx_type, config.rx_filter);
+
+ /* reserved for future extensions */
+ if (config.flags)
+ return -EINVAL;
+
+ if ((config.tx_type != HWTSTAMP_TX_OFF) &&
+ (config.tx_type != HWTSTAMP_TX_ON))
+ return -ERANGE;
+
+ ptpctl = bfin_read_EMAC_PTP_CTL();
+
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ /*
+ * Dont allow any timestamping
+ */
+ ptpfv3 = 0xFFFFFFFF;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ /*
+ * Clear the five comparison mask bits (bits[12:8]) in EMAC_PTP_CTL)
+ * to enable all the field matches.
+ */
+ ptpctl &= ~0x1F00;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of the EMAC_PTP_FOFF register.
+ */
+ ptpfoff = 0x4A24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
+ * registers.
+ */
+ ptpfv1 = 0x11040800;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * The default value (0xFFFC) allows the timestamping of both
+ * received Sync messages and Delay_Req messages.
+ */
+ ptpfv3 = 0xFFFFFFFC;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ /* Clear all five comparison mask bits (bits[12:8]) in the
+ * EMAC_PTP_CTL register to enable all the field matches.
+ */
+ ptpctl &= ~0x1F00;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of the EMAC_PTP_FOFF register, except set
+ * the PTPCOF field to 0x2A.
+ */
+ ptpfoff = 0x2A24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
+ * registers.
+ */
+ ptpfv1 = 0x11040800;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * To allow the timestamping of Pdelay_Req and Pdelay_Resp, set
+ * the value to 0xFFF0.
+ */
+ ptpfv3 = 0xFFFFFFF0;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ /*
+ * Clear bits 8 and 12 of the EMAC_PTP_CTL register to enable only the
+ * EFTM and PTPCM field comparison.
+ */
+ ptpctl &= ~0x1100;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+ /*
+ * Keep the default values of all the fields of the EMAC_PTP_FOFF
+ * register, except set the PTPCOF field to 0x0E.
+ */
+ ptpfoff = 0x0E24170C;
+ bfin_write_EMAC_PTP_FOFF(ptpfoff);
+ /*
+ * Program bits [15:0] of the EMAC_PTP_FV1 register to 0x88F7, which
+ * corresponds to PTP messages on the MAC layer.
+ */
+ ptpfv1 = 0x110488F7;
+ bfin_write_EMAC_PTP_FV1(ptpfv1);
+ ptpfv2 = 0x0140013F;
+ bfin_write_EMAC_PTP_FV2(ptpfv2);
+ /*
+ * To allow the timestamping of Pdelay_Req and Pdelay_Resp
+ * messages, set the value to 0xFFF0.
+ */
+ ptpfv3 = 0xFFFFFFF0;
+ bfin_write_EMAC_PTP_FV3(ptpfv3);
+
+ config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ if (config.tx_type == HWTSTAMP_TX_OFF &&
+ bfin_mac_hwtstamp_is_none(config.rx_filter)) {
+ ptpctl &= ~PTP_EN;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+
+ SSYNC();
+ } else {
+ ptpctl |= PTP_EN;
+ bfin_write_EMAC_PTP_CTL(ptpctl);
+
+ /*
+ * clear any existing timestamp
+ */
+ bfin_read_EMAC_PTP_RXSNAPLO();
+ bfin_read_EMAC_PTP_RXSNAPHI();
+
+ bfin_read_EMAC_PTP_TXSNAPLO();
+ bfin_read_EMAC_PTP_TXSNAPHI();
+
+ /*
+ * Set registers so that rollover occurs soon to test this.
+ */
+ bfin_write_EMAC_PTP_TIMELO(0x00000000);
+ bfin_write_EMAC_PTP_TIMEHI(0xFF800000);
+
+ SSYNC();
+
+ lp->compare.last_update = 0;
+ timecounter_init(&lp->clock,
+ &lp->cycles,
+ ktime_to_ns(ktime_get_real()));
+ timecompare_update(&lp->compare, 0);
+ }
+
+ lp->stamp_cfg = config;
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
+
+static void bfin_dump_hwtamp(char *s, ktime_t *hw, ktime_t *ts, struct timecompare *cmp)
+{
+ ktime_t sys = ktime_get_real();
+
+ pr_debug("%s %s hardware:%d,%d transform system:%d,%d system:%d,%d, cmp:%lld, %lld\n",
+ __func__, s, hw->tv.sec, hw->tv.nsec, ts->tv.sec, ts->tv.nsec, sys.tv.sec,
+ sys.tv.nsec, cmp->offset, cmp->skew);
+}
+
+static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ union skb_shared_tx *shtx = skb_tx(skb);
+
+ if (shtx->hardware) {
+ int timeout_cnt = MAX_TIMEOUT_CNT;
+
+ /* When doing time stamping, keep the connection to the socket
+ * a while longer
+ */
+ shtx->in_progress = 1;
+
+ /*
+ * The timestamping is done at the EMAC module's MII/RMII interface
+ * when the module sees the Start of Frame of an event message packet. This
+ * interface is the closest possible place to the physical Ethernet transmission
+ * medium, providing the best timing accuracy.
+ */
+ while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt))
+ udelay(1);
+ if (timeout_cnt == 0)
+ printk(KERN_ERR DRV_NAME
+ ": fails to timestamp the TX packet\n");
+ else {
+ struct skb_shared_hwtstamps shhwtstamps;
+ u64 ns;
+ u64 regval;
+
+ regval = bfin_read_EMAC_PTP_TXSNAPLO();
+ regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32;
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ ns = timecounter_cyc2time(&lp->clock,
+ regval);
+ timecompare_update(&lp->compare, ns);
+ shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ shhwtstamps.syststamp =
+ timecompare_transform(&lp->compare, ns);
+ skb_tstamp_tx(skb, &shhwtstamps);
+
+ bfin_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare);
+ }
+ }
+}
+
+static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u32 valid;
+ u64 regval, ns;
+ struct skb_shared_hwtstamps *shhwtstamps;
+
+ if (bfin_mac_hwtstamp_is_none(lp->stamp_cfg.rx_filter))
+ return;
+
+ valid = bfin_read_EMAC_PTP_ISTAT() & RXEL;
+ if (!valid)
+ return;
+
+ shhwtstamps = skb_hwtstamps(skb);
+
+ regval = bfin_read_EMAC_PTP_RXSNAPLO();
+ regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32;
+ ns = timecounter_cyc2time(&lp->clock, regval);
+ timecompare_update(&lp->compare, ns);
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+ shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns);
+
+ bfin_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare);
+}
+
+/*
+ * bfin_read_clock - read raw cycle counter (to be used by time counter)
+ */
+static cycle_t bfin_read_clock(const struct cyclecounter *tc)
+{
+ u64 stamp;
+
+ stamp = bfin_read_EMAC_PTP_TIMELO();
+ stamp |= (u64)bfin_read_EMAC_PTP_TIMEHI() << 32ULL;
+
+ return stamp;
+}
+
+#define PTP_CLK 25000000
+
+static void bfin_mac_hwtstamp_init(struct net_device *netdev)
+{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+ u64 append;
+
+ /* Initialize hardware timer */
+ append = PTP_CLK * (1ULL << 32);
+ do_div(append, get_sclk());
+ bfin_write_EMAC_PTP_ADDEND((u32)append);
+
+ memset(&lp->cycles, 0, sizeof(lp->cycles));
+ lp->cycles.read = bfin_read_clock;
+ lp->cycles.mask = CLOCKSOURCE_MASK(64);
+ lp->cycles.mult = 1000000000 / PTP_CLK;
+ lp->cycles.shift = 0;
+
+ /* Synchronize our NIC clock against system wall clock */
+ memset(&lp->compare, 0, sizeof(lp->compare));
+ lp->compare.source = &lp->clock;
+ lp->compare.target = ktime_get_real;
+ lp->compare.num_samples = 10;
+
+ /* Initialize hwstamp config */
+ lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE;
+ lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF;
+}
+
+#else
+# define bfin_mac_hwtstamp_is_none(cfg) 0
+# define bfin_mac_hwtstamp_init(dev)
+# define bfin_mac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP)
+# define bfin_rx_hwtstamp(dev, skb)
+# define bfin_tx_hwtstamp(dev, skb)
+#endif
+
static void adjust_tx_list(void)
{
int timeout_cnt = MAX_TIMEOUT_CNT;
@@ -608,18 +912,32 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
{
u16 *data;
u32 data_align = (unsigned long)(skb->data) & 0x3;
+ union skb_shared_tx *shtx = skb_tx(skb);
+
current_tx_ptr->skb = skb;
if (data_align == 0x2) {
/* move skb->data to current_tx_ptr payload */
data = (u16 *)(skb->data) - 1;
- *data = (u16)(skb->len);
+ *data = (u16)(skb->len);
+ /*
+ * When transmitting an Ethernet packet, the PTP_TSYNC module requires
+ * a DMA_Length_Word field associated with the packet. The lower 12 bits
+ * of this field are the length of the packet payload in bytes and the higher
+ * 4 bits are the timestamping enable field.
+ */
+ if (shtx->hardware)
+ *data |= 0x1000;
+
current_tx_ptr->desc_a.start_addr = (u32)data;
/* this is important! */
blackfin_dcache_flush_range((u32)data,
(u32)((u8 *)data + skb->len + 4));
} else {
*((u16 *)(current_tx_ptr->packet)) = (u16)(skb->len);
+ /* enable timestamping for the sent packet */
+ if (shtx->hardware)
+ *((u16 *)(current_tx_ptr->packet)) |= 0x1000;
memcpy((u8 *)(current_tx_ptr->packet + 2), skb->data,
skb->len);
current_tx_ptr->desc_a.start_addr =
@@ -653,6 +971,9 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
out:
adjust_tx_list();
+
+ bfin_tx_hwtstamp(dev, skb);
+
current_tx_ptr = current_tx_ptr->next;
dev->trans_start = jiffies;
dev->stats.tx_packets++;
@@ -664,9 +985,11 @@ static void bfin_mac_rx(struct net_device *dev)
{
struct sk_buff *skb, *new_skb;
unsigned short len;
+ struct bfin_mac_local *lp __maybe_unused = netdev_priv(dev);
/* allocate a new skb for next time receive */
skb = current_rx_ptr->skb;
+
new_skb = dev_alloc_skb(PKT_BUF_SZ + NET_IP_ALIGN);
if (!new_skb) {
printk(KERN_NOTICE DRV_NAME
@@ -691,6 +1014,9 @@ static void bfin_mac_rx(struct net_device *dev)
(unsigned long)skb->tail);
skb->protocol = eth_type_trans(skb, dev);
+
+ bfin_rx_hwtstamp(dev, skb);
+
#if defined(BFIN_MAC_CSUM_OFFLOAD)
skb->csum = current_rx_ptr->status.ip_payload_csum;
skb->ip_summed = CHECKSUM_COMPLETE;
@@ -874,6 +1200,16 @@ static void bfin_mac_set_multicast_list(struct net_device *dev)
}
}
+static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCSHWTSTAMP:
+ return bfin_mac_hwtstamp_ioctl(netdev, ifr, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
/*
* this puts the device in an inactive state
*/
@@ -958,6 +1294,7 @@ static const struct net_device_ops bfin_mac_netdev_ops = {
.ndo_set_mac_address = bfin_mac_set_mac_address,
.ndo_tx_timeout = bfin_mac_timeout,
.ndo_set_multicast_list = bfin_mac_set_multicast_list,
+ .ndo_do_ioctl = bfin_mac_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1049,6 +1386,8 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev)
goto out_err_reg_ndev;
}
+ bfin_mac_hwtstamp_init(ndev);
+
/* now, print out the card info, in a short format.. */
dev_info(&pdev->dev, "%s, Version %s\n", DRV_DESC, DRV_VERSION);
diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h
index 052b5dc..87c454f 100644
--- a/drivers/net/bfin_mac.h
+++ b/drivers/net/bfin_mac.h
@@ -7,6 +7,12 @@
*
* Licensed under the GPL-2 or later.
*/
+#ifndef _BFIN_MAC_H_
+#define _BFIN_MAC_H_
+
+#include <linux/net_tstamp.h>
+#include <linux/clocksource.h>
+#include <linux/timecompare.h>
#define BFIN_MAC_CSUM_OFFLOAD
@@ -67,6 +73,15 @@ struct bfin_mac_local {
struct phy_device *phydev;
struct mii_bus *mii_bus;
+
+#if defined(CONFIG_BFIN_MAC_USE_HWSTAMP)
+ struct cyclecounter cycles;
+ struct timecounter clock;
+ struct timecompare compare;
+ struct hwtstamp_config stamp_cfg;
+#endif
};
extern void bfin_get_ether_addr(char *addr);
+
+#endif
--
1.7.1
^ permalink raw reply related
* Re: [Uclinux-dist-devel] [PATCH 09/11] netdev: bfin_mac: only use hardware checksum in normal IPv4 mode
From: Mike Frysinger @ 2010-05-10 15:35 UTC (permalink / raw)
To: David Miller; +Cc: netdev, jon.kowal, uclinux-dist-devel
In-Reply-To: <20100510.044222.233422592.davem@davemloft.net>
On Mon, May 10, 2010 at 07:42, David Miller wrote:
> From: Mike Frysinger <vapier@gentoo.org>
> Date: Sun, 9 May 2010 06:18:55 -0400
>
>> From: Jon Kowal <jon.kowal@dspecialists.de>
>>
>> The Blackfin on-chip MAC checksum logic only works when the IP packet has
>> a header length of 20 bytes. This is true for most IPv4 packets, but not
>> for IPv6 packets or IPv4 packets which use header options. So only use
>> the hardware checksum when appropriate.
>>
>> Signed-off-by: Jon Kowal <jon.kowal@dspecialists.de>
>> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
>> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
>
> In order to make the tree bisectable, combine bug fixes into the changes
> that add the bugs instead of letting them live in the final tree when
> they don't need to.
>
> You should have just combined this patch into the patch that added the
> FCS inversion stuff.
i dont think that is true. this bug existed before the FCS inversion
stuff -- notice the csum complete behavior changes.
-mike
^ permalink raw reply
* Re: TCP-MD5 checksum failure on x86_64 SMP
From: Eric Dumazet @ 2010-05-10 15:18 UTC (permalink / raw)
To: Bijay Singh
Cc: Stephen Hemminger, David Miller, <bhaskie@gmail.com>,
<bhutchings@solarflare.com>, <netdev@vger.kernel.org>
In-Reply-To: <D1C2B1EC-ABE8-4D87-9227-6D0F1C1C8B77@guavus.com>
Le lundi 10 mai 2010 à 14:55 +0000, Bijay Singh a écrit :
> Hi,
> I had noticed the corruption in the context and actually did what is mentioned.
>
> I allocated the context on the stack and plugged in the md5.c functions. I was able to temporarily solve the problem, all this before I got a response on this thread.
>
> But now I have seeing another problem, when i change the MTU on the interface from 1500 to 4470 none of the message from the peer get thru and I get hash failed message. I am wondering if this is another bug getting hit in this scenario.
Thats very fine, but you mix very different problems.
Step by step resolution is required, and clean patches too, because
plugging md5.c functions is not an option for stable series :)
Obviously, nobody seriously used TCP-MD5 on linux, but you...
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Eric Dumazet @ 2010-05-10 15:14 UTC (permalink / raw)
To: Joakim Tjernlund; +Cc: Patrick McHardy, netdev
In-Reply-To: <OF60B75A13.F85EAE42-ONC125771F.00518947-C125771F.0051E8DC@transmode.se>
Le lundi 10 mai 2010 à 16:54 +0200, Joakim Tjernlund a écrit :
> Patrick McHardy <kaber@trash.net> wrote on 2010/05/10 16:41:40:
> >
> > Eric Dumazet wrote:
> > > Le lundi 10 mai 2010 à 16:26 +0200, Patrick McHardy a écrit :
> > >
> > >> Is the intention just to avoid accounting the packet as dropped?
> > >> That seems fine to me since in case of NET_XMIT_CN its actually
> > >> not the currently transmitted packet that was dropped.
> > >>
> > >> But part of the intention of the above mentioned patch was actually
> > >> to inform higher layers of congestion so they can take action if
> > >> desired, which would be defeated by this patch.
> > >>
> > >
> > > I see, so maybe we want following patch instead ?
> > >
> > > (letting NET_XMIT_CN be given to caller, but accounting current packet
> > > as transmitted ?)
> >
> > Perfect, thanks. I'd suggest to change macvlan in a similar fashion
> > for consistency though.
> >
> > In any case please feel free to add my
> >
> > Acked-by: Patrick McHardy <kaber@trash.net>
>
> hmm, as I don't follow this could you tell me where the dropped pkgs
> are accounted in my case:
> eth0 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
> RX packets:8886910 errors:0 dropped:0 overruns:0 frame:0
> TX packets:8880219 errors:0 dropped:0 overruns:0 carrier:0
> collisions:0 txqueuelen:100
> RX bytes:1626842951 (1.5 GiB) TX bytes:1555540810 (1.4 GiB)
>
> eth0.1 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
> RX packets:2163164 errors:0 dropped:0 overruns:0 frame:0
> TX packets:2161943 errors:0 dropped:98 overruns:0 carrier:0
> collisions:0 txqueuelen:0
> RX bytes:2467090557 (2.2 GiB) TX bytes:2480246455 (2.3 GiB)
>
> eth0.1.1 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
> RX packets:2163164 errors:0 dropped:0 overruns:0 frame:0
> TX packets:2161943 errors:0 dropped:98 overruns:0 carrier:0
> collisions:0 txqueuelen:0
> RX bytes:2458437901 (2.2 GiB) TX bytes:2471598683 (2.3 GiB)
>
> -
As I already said to you, this is a side effect, because your eth0.1.1
and eth0.1 have no queue.
Packet is not dropped, its only a congestion indication.
If you use a queue on eth0.1.1, queue itself handle the queueing
(obviously), and queue drops are not reported in 'ifconfig -a', but in
'tc -s -d qdisc'
^ permalink raw reply
* Re: [PATCH v2] can: Add driver for esd CAN-USB/2 device
From: Wolfgang Grandegger @ 2010-05-10 14:59 UTC (permalink / raw)
To: Matthias Fuchs
Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <201005101640.39048.matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
On 05/10/2010 04:40 PM, Matthias Fuchs wrote:
> Hi Wolfgang,
>
> On Monday 10 May 2010 15:43, Wolfgang Grandegger wrote:
>> Hi Matthias,
>>
>> On 05/10/2010 03:28 PM, Matthias Fuchs wrote:
>>> This patch adds a driver for esd's USB high speed
>>> CAN interface. The driver supports devices with
>>> multiple CAN interfaces.
>>>
>>> Signed-off-by: Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
>>> ---
>>>
>>> version 2:
>>> - use bus-error reporting and counters
>>
>> I don't find support for CAN_CTRLMODE_BERR_REPORTING, apart from setting
>> the flag. If it's set bus error interrupts should be enabled or disabled
>> otherwise.
>>
>> Wolfgang.
> I expect my implementation to be complete.
>
> 1) In esd_usb2_rx_event() I pass tx/rxerr in cf->data[6/7].
> 2) I implemented esd_usb2_get_berr_counter(). This function will
> return the last tx/rx error counters from the hardware that
> are kept in priv->bec.
> 3) tx/rx error counter cannot be requested from the hardware so
> they are stored in priv->bec when they are received from the hardware.
> There is no way to enable or disable this behavior.
> 4) Finally I enabled the whole interface via CAN_CTRLMODE_BERR_REPORTING
> for the CAN framework.
BERR_REPORTING != bus error counter
> I don't see where I missed anything.
The equivalent to the following SJA1000 code is missing:
/* enable interrupts */
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
priv->write_reg(priv, REG_IER, IRQ_ALL);
else
priv->write_reg(priv, REG_IER, IRQ_ALL & ~IRQ_BEI);
The user must now explicitly enable bus error reporting with
"ip ... type can berr_reporting on ....
Wolfgang.
> Matthias
>
>
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Joakim Tjernlund @ 2010-05-10 14:54 UTC (permalink / raw)
To: Patrick McHardy; +Cc: Eric Dumazet, netdev
In-Reply-To: <4BE81B24.4020601@trash.net>
Patrick McHardy <kaber@trash.net> wrote on 2010/05/10 16:41:40:
>
> Eric Dumazet wrote:
> > Le lundi 10 mai 2010 à 16:26 +0200, Patrick McHardy a écrit :
> >
> >> Is the intention just to avoid accounting the packet as dropped?
> >> That seems fine to me since in case of NET_XMIT_CN its actually
> >> not the currently transmitted packet that was dropped.
> >>
> >> But part of the intention of the above mentioned patch was actually
> >> to inform higher layers of congestion so they can take action if
> >> desired, which would be defeated by this patch.
> >>
> >
> > I see, so maybe we want following patch instead ?
> >
> > (letting NET_XMIT_CN be given to caller, but accounting current packet
> > as transmitted ?)
>
> Perfect, thanks. I'd suggest to change macvlan in a similar fashion
> for consistency though.
>
> In any case please feel free to add my
>
> Acked-by: Patrick McHardy <kaber@trash.net>
hmm, as I don't follow this could you tell me where the dropped pkgs
are accounted in my case:
eth0 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8886910 errors:0 dropped:0 overruns:0 frame:0
TX packets:8880219 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:1626842951 (1.5 GiB) TX bytes:1555540810 (1.4 GiB)
eth0.1 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2163164 errors:0 dropped:0 overruns:0 frame:0
TX packets:2161943 errors:0 dropped:98 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2467090557 (2.2 GiB) TX bytes:2480246455 (2.3 GiB)
eth0.1.1 Link encap:Ethernet HWaddr 00:AA:BB:CC:DD:EE
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2163164 errors:0 dropped:0 overruns:0 frame:0
TX packets:2161943 errors:0 dropped:98 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2458437901 (2.2 GiB) TX bytes:2471598683 (2.3 GiB)
^ permalink raw reply
* Re: TCP-MD5 checksum failure on x86_64 SMP
From: Bijay Singh @ 2010-05-10 14:55 UTC (permalink / raw)
To: Eric Dumazet
Cc: Stephen Hemminger, David Miller, <bhaskie@gmail.com>,
<bhutchings@solarflare.com>, <netdev@vger.kernel.org>
In-Reply-To: <1273268446.2325.53.camel@edumazet-laptop>
Hi,
I had noticed the corruption in the context and actually did what is mentioned.
I allocated the context on the stack and plugged in the md5.c functions. I was able to temporarily solve the problem, all this before I got a response on this thread.
But now I have seeing another problem, when i change the MTU on the interface from 1500 to 4470 none of the message from the peer get thru and I get hash failed message. I am wondering if this is another bug getting hit in this scenario.
Regards,
Bijay
On 08-May-2010, at 3:10 AM, Eric Dumazet wrote:
> Le vendredi 07 mai 2010 à 10:36 -0700, Stephen Hemminger a écrit :
>> On Fri, 07 May 2010 19:21:33 +0200
>> Eric Dumazet <eric.dumazet@gmail.com> wrote:
>>
>>> Le vendredi 07 mai 2010 à 10:14 -0700, Stephen Hemminger a écrit :
>>>
>>>> Forget the per cpu data; the pool should just be scrapped.
>>>>
>>>> The only reason the pool exists is that the crypto hash state which
>>>> should just be moved into the md5_info (88 bytes). The pseudo
>>>> header can just be generated on the stack before passing to the crypto
>>>> code.
>>>
>>>
>>> Sure, but I'm afraid there is no generic API do do that (if we want to
>>> reuse crypto/md5.c code).
>>
>> It looks like the pool is just an optimization to avoid opening too
>> many crypto API connections. This should only be an issue if offloading
>> MD5.
>
> You mean we could allocate two contexts per socket, one for tx path, one
> for rx path, but TCP-MD5 implementors chose to use percpu allocations to
> factorize them. They should have allocated two contexts per cpu (one for
> process context, preemption disabled, one for BH context)
>
> As you said, this could be allocated on stack, with some changes to
> crypto API I guess. Since TCP is not a module, md5 is also static, so
> there is no module loading involved.
>
> struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32
> type,u32 mask)
>
> -->
>
> struct crypto_tfm *__crypto_alloc_tfm_onstack(struct crypto_alg *alg,
> u32 type, u32 mask, void *storage, size_t storage_max_length)
>
>
> Or a direct plug to crypto/md5.c functions and hand crafted context.
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Joakim Tjernlund @ 2010-05-10 14:50 UTC (permalink / raw)
To: Patrick McHardy; +Cc: Eric Dumazet, netdev
In-Reply-To: <4BE8191C.8020302@trash.net>
Patrick McHardy <kaber@trash.net> wrote on 2010/05/10 16:33:00:
>
> Joakim Tjernlund wrote:
> > Eric Dumazet <eric.dumazet@gmail.com> wrote on 2010/05/07 10:53:23:
> >>> 3) I would expect lost pkgs to be accounted on eth0 instead of
> >>> the VLAN interface(s) since that is where the pkg is lost, why
> >>> isn't it so?
> >> You try to send packets on eth0.XXX, some are dropped, and accounted for
> >> on eth0.XXX stats. What is wrong with this ?
> >
> > In this case one lost pkg is accounted for twice, once on eth0.1 and
> > once more on eth0.1.1. Note that eth0.1.1 is stacked on
> > top of eth0.1
> >
> > I would at least expect eth0 to also account lost pkgs too.
> > I was confused by the current accounting as I knew that
> > the underlying HW I/F should be the only I/F that could
> > drop pkgs.
>
> In case of NET_XMIT_CN, the packet is dropped by the qdisc before
> it reaches eth0, so its only accounted on the upper devices.
hmm, I am afraid I don't follow this. Why would a pkg be dropped before
it reaches eth0?
>
> >> If you want to avoid this, just add queues to your vlans
> >>
> >> ip link add link eth0 eth0.103 txqueuelen 100 type vlan id 103
> >
> >>From memory now, but that didn't help. Still accounts pgks
> > as described. Why would where to account pkgs be affected by
> > queue or no queue?
>
> If a queue is used on the vlan device, it will queue the packet
> until the lower device is able to transmit it (unless its own
> queue overflows).
And if a pkg is is lost this also changes where to account dropped?
I don't understand this. The queue may prevent pkg loss to some degree
but I don't get why a queue!=0 would change on which interface to
account for lost pkg's.
Jocke
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Eric Dumazet @ 2010-05-10 14:51 UTC (permalink / raw)
To: Patrick McHardy, David Miller; +Cc: Joakim Tjernlund, netdev
In-Reply-To: <4BE81B24.4020601@trash.net>
Le lundi 10 mai 2010 à 16:41 +0200, Patrick McHardy a écrit :
> Eric Dumazet wrote:
> > Le lundi 10 mai 2010 à 16:26 +0200, Patrick McHardy a écrit :
> >
> >> Is the intention just to avoid accounting the packet as dropped?
> >> That seems fine to me since in case of NET_XMIT_CN its actually
> >> not the currently transmitted packet that was dropped.
> >>
> >> But part of the intention of the above mentioned patch was actually
> >> to inform higher layers of congestion so they can take action if
> >> desired, which would be defeated by this patch.
> >>
> >
> > I see, so maybe we want following patch instead ?
> >
> > (letting NET_XMIT_CN be given to caller, but accounting current packet
> > as transmitted ?)
>
> Perfect, thanks. I'd suggest to change macvlan in a similar fashion
> for consistency though.
>
> In any case please feel free to add my
>
> Acked-by: Patrick McHardy <kaber@trash.net>
Indeed, thanks !
[PATCH net-next-2.6] net: congestion notifications are not dropped packets
vlan/macvlan start_xmit() can inform caller of congestion with
NET_XMIT_CN return value. This doesnt mean packet was dropped.
Increment normal stat counters instead of tx_dropped.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Patrick McHardy <kaber@trash.net>
---
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 9a939d8..0f8cd95 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -243,7 +243,7 @@ netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
int ret;
ret = macvlan_queue_xmit(skb, dev);
- if (likely(ret == NET_XMIT_SUCCESS)) {
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index b5249c5..55be908 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -327,7 +327,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
len = skb->len;
ret = dev_queue_xmit(skb);
- if (likely(ret == NET_XMIT_SUCCESS)) {
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
@@ -353,7 +353,7 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
len = skb->len;
ret = dev_queue_xmit(skb);
- if (likely(ret == NET_XMIT_SUCCESS)) {
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
^ permalink raw reply related
* Re: VLAN I/F's and TX queue.
From: Patrick McHardy @ 2010-05-10 14:41 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Joakim Tjernlund, netdev
In-Reply-To: <1273502195.2221.4.camel@edumazet-laptop>
Eric Dumazet wrote:
> Le lundi 10 mai 2010 à 16:26 +0200, Patrick McHardy a écrit :
>
>> Is the intention just to avoid accounting the packet as dropped?
>> That seems fine to me since in case of NET_XMIT_CN its actually
>> not the currently transmitted packet that was dropped.
>>
>> But part of the intention of the above mentioned patch was actually
>> to inform higher layers of congestion so they can take action if
>> desired, which would be defeated by this patch.
>>
>
> I see, so maybe we want following patch instead ?
>
> (letting NET_XMIT_CN be given to caller, but accounting current packet
> as transmitted ?)
Perfect, thanks. I'd suggest to change macvlan in a similar fashion
for consistency though.
In any case please feel free to add my
Acked-by: Patrick McHardy <kaber@trash.net>
> diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
> index b5249c5..55be908 100644
> --- a/net/8021q/vlan_dev.c
> +++ b/net/8021q/vlan_dev.c
> @@ -327,7 +327,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
> len = skb->len;
> ret = dev_queue_xmit(skb);
>
> - if (likely(ret == NET_XMIT_SUCCESS)) {
> + if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
> txq->tx_packets++;
> txq->tx_bytes += len;
> } else
> @@ -353,7 +353,7 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
> len = skb->len;
> ret = dev_queue_xmit(skb);
>
> - if (likely(ret == NET_XMIT_SUCCESS)) {
> + if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
> txq->tx_packets++;
> txq->tx_bytes += len;
> } else
>
>
^ permalink raw reply
* Re: [PATCH v2] can: Add driver for esd CAN-USB/2 device
From: Matthias Fuchs @ 2010-05-10 14:40 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <4BE80D6E.3040205-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
Hi Wolfgang,
On Monday 10 May 2010 15:43, Wolfgang Grandegger wrote:
> Hi Matthias,
>
> On 05/10/2010 03:28 PM, Matthias Fuchs wrote:
> > This patch adds a driver for esd's USB high speed
> > CAN interface. The driver supports devices with
> > multiple CAN interfaces.
> >
> > Signed-off-by: Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
> > ---
> >
> > version 2:
> > - use bus-error reporting and counters
>
> I don't find support for CAN_CTRLMODE_BERR_REPORTING, apart from setting
> the flag. If it's set bus error interrupts should be enabled or disabled
> otherwise.
>
> Wolfgang.
I expect my implementation to be complete.
1) In esd_usb2_rx_event() I pass tx/rxerr in cf->data[6/7].
2) I implemented esd_usb2_get_berr_counter(). This function will
return the last tx/rx error counters from the hardware that
are kept in priv->bec.
3) tx/rx error counter cannot be requested from the hardware so
they are stored in priv->bec when they are received from the hardware.
There is no way to enable or disable this behavior.
4) Finally I enabled the whole interface via CAN_CTRLMODE_BERR_REPORTING
for the CAN framework.
I don't see where I missed anything.
Matthias
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Eric Dumazet @ 2010-05-10 14:36 UTC (permalink / raw)
To: Patrick McHardy; +Cc: Joakim Tjernlund, netdev
In-Reply-To: <4BE81793.3060905@trash.net>
Le lundi 10 mai 2010 à 16:26 +0200, Patrick McHardy a écrit :
>
> Is the intention just to avoid accounting the packet as dropped?
> That seems fine to me since in case of NET_XMIT_CN its actually
> not the currently transmitted packet that was dropped.
>
> But part of the intention of the above mentioned patch was actually
> to inform higher layers of congestion so they can take action if
> desired, which would be defeated by this patch.
>
I see, so maybe we want following patch instead ?
(letting NET_XMIT_CN be given to caller, but accounting current packet
as transmitted ?)
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index b5249c5..55be908 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -327,7 +327,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
len = skb->len;
ret = dev_queue_xmit(skb);
- if (likely(ret == NET_XMIT_SUCCESS)) {
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
@@ -353,7 +353,7 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
len = skb->len;
ret = dev_queue_xmit(skb);
- if (likely(ret == NET_XMIT_SUCCESS)) {
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
^ permalink raw reply related
* Re: [PATCH net-next-2.6] net: adjust handle_macvlan to pass port struct to hook
From: Patrick McHardy @ 2010-05-10 14:35 UTC (permalink / raw)
To: Jiri Pirko; +Cc: netdev, davem
In-Reply-To: <20100506113352.GB3406@psychotron.lab.eng.brq.redhat.com>
Jiri Pirko wrote:
> Now there's null check here and also again in the hook. Looking at bridge bits
> which are simmilar, port structure is rcu_dereferenced right away in
> handle_bridge and passed to hook. Looks nicer.
>
> Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Looks fine, thanks.
Acked-by: Patrick McHardy <kaber@trash.net>
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Patrick McHardy @ 2010-05-10 14:33 UTC (permalink / raw)
To: Joakim Tjernlund; +Cc: Eric Dumazet, netdev
In-Reply-To: <OF803EE436.793B95D8-ONC125771C.003295C9-C125771C.00341AFF@transmode.se>
Joakim Tjernlund wrote:
> Eric Dumazet <eric.dumazet@gmail.com> wrote on 2010/05/07 10:53:23:
>>> 3) I would expect lost pkgs to be accounted on eth0 instead of
>>> the VLAN interface(s) since that is where the pkg is lost, why
>>> isn't it so?
>> You try to send packets on eth0.XXX, some are dropped, and accounted for
>> on eth0.XXX stats. What is wrong with this ?
>
> In this case one lost pkg is accounted for twice, once on eth0.1 and
> once more on eth0.1.1. Note that eth0.1.1 is stacked on
> top of eth0.1
>
> I would at least expect eth0 to also account lost pkgs too.
> I was confused by the current accounting as I knew that
> the underlying HW I/F should be the only I/F that could
> drop pkgs.
In case of NET_XMIT_CN, the packet is dropped by the qdisc before
it reaches eth0, so its only accounted on the upper devices.
>> If you want to avoid this, just add queues to your vlans
>>
>> ip link add link eth0 eth0.103 txqueuelen 100 type vlan id 103
>
>>From memory now, but that didn't help. Still accounts pgks
> as described. Why would where to account pkgs be affected by
> queue or no queue?
If a queue is used on the vlan device, it will queue the packet
until the lower device is able to transmit it (unless its own
queue overflows).
^ permalink raw reply
* Re: VLAN I/F's and TX queue.
From: Patrick McHardy @ 2010-05-10 14:26 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Joakim Tjernlund, netdev
In-Reply-To: <1273222403.2261.26.camel@edumazet-laptop>
Eric Dumazet wrote:
> Le vendredi 07 mai 2010 à 10:04 +0200, Joakim Tjernlund a écrit :
>> So I did some more testing
>> two nodes A and B connected over a slow link.
>> Create two VLAN's as above and start pinging from A to B
>> with pkg size 9600, start a few(4-10) parallel ping processes.
>>
>> Now I see dropped packages on B, the receiver of pings, and no
>> pkg loss on A.
>>
>
> dropped on RX path or TX path ?
>
>> 1) since the link is symmetrical, why do I only see pkg loss
>> at B?
>>
>> 2) pkg loss in B only manifests on the VLAN's interfaces and
>> always in pair as if one lost pkg is counted twice?
>>
>
> Congestion notifications can be stacked since commit cbbef5e183079
> (vlan/macvlan: propagate transmission state to upper layers)
>
>> 3) I would expect lost pkgs to be accounted on eth0 instead of
>> the VLAN interface(s) since that is where the pkg is lost, why
>> isn't it so?
>
> You try to send packets on eth0.XXX, some are dropped, and accounted for
> on eth0.XXX stats. What is wrong with this ?
>
> If you want to avoid this, just add queues to your vlans
>
> ip link add link eth0 eth0.103 txqueuelen 100 type vlan id 103
>
> Patrick what do you think of special casing NET_XMIT_CN ?
Is the intention just to avoid accounting the packet as dropped?
That seems fine to me since in case of NET_XMIT_CN its actually
not the currently transmitted packet that was dropped.
But part of the intention of the above mentioned patch was actually
to inform higher layers of congestion so they can take action if
desired, which would be defeated by this patch.
> diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
> index b5249c5..c671b1a 100644
> --- a/net/8021q/vlan_dev.c
> +++ b/net/8021q/vlan_dev.c
> @@ -327,6 +327,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
> len = skb->len;
> ret = dev_queue_xmit(skb);
>
> + ret = net_xmit_eval(ret);
> +
> if (likely(ret == NET_XMIT_SUCCESS)) {
> txq->tx_packets++;
> txq->tx_bytes += len;
> @@ -353,6 +355,8 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
> len = skb->len;
> ret = dev_queue_xmit(skb);
>
> + ret = net_xmit_eval(ret);
> +
> if (likely(ret == NET_XMIT_SUCCESS)) {
> txq->tx_packets++;
> txq->tx_bytes += len;
>
>
^ permalink raw reply
* Re: [PATCH v2] sctp: Fix a race between ICMP protocol unreachable and connect()
From: Vlad Yasevich @ 2010-05-10 13:59 UTC (permalink / raw)
To: Wei Yongjun; +Cc: davem, netdev, linux-sctp
In-Reply-To: <4BE775C7.9070702@cn.fujitsu.com>
Wei Yongjun wrote:
> Hi Vlad:
>
>> [.. removed leftover debuggin printk. should probably be queued for stable
>> as well... ]
>>
>> ICMP protocol unreachable handling completely disregarded
>> the fact that the user may have locket the socket. It proceeded
>> to destroy the association, even though the user may have
>> held the lock and had a ref on the association. This resulted
>> in the following:
>>
>> Attempt to release alive inet socket f6afcc00
>>
>> =========================
>> [ BUG: held lock freed! ]
>> -------------------------
>> somenu/2672 is freeing memory f6afcc00-f6afcfff, with a lock still held
>> there!
>> (sk_lock-AF_INET){+.+.+.}, at: [<c122098a>] sctp_connect+0x13/0x4c
>> 1 lock held by somenu/2672:
>> #0: (sk_lock-AF_INET){+.+.+.}, at: [<c122098a>] sctp_connect+0x13/0x4c
>>
>> stack backtrace:
>> Pid: 2672, comm: somenu Not tainted 2.6.32-telco #55
>> Call Trace:
>> [<c1232266>] ? printk+0xf/0x11
>> [<c1038553>] debug_check_no_locks_freed+0xce/0xff
>> [<c10620b4>] kmem_cache_free+0x21/0x66
>> [<c1185f25>] __sk_free+0x9d/0xab
>> [<c1185f9c>] sk_free+0x1c/0x1e
>> [<c1216e38>] sctp_association_put+0x32/0x89
>> [<c1220865>] __sctp_connect+0x36d/0x3f4
>> [<c122098a>] ? sctp_connect+0x13/0x4c
>> [<c102d073>] ? autoremove_wake_function+0x0/0x33
>> [<c12209a8>] sctp_connect+0x31/0x4c
>> [<c11d1e80>] inet_dgram_connect+0x4b/0x55
>> [<c11834fa>] sys_connect+0x54/0x71
>> [<c103a3a2>] ? lock_release_non_nested+0x88/0x239
>> [<c1054026>] ? might_fault+0x42/0x7c
>> [<c1054026>] ? might_fault+0x42/0x7c
>> [<c11847ab>] sys_socketcall+0x6d/0x178
>> [<c10da994>] ? trace_hardirqs_on_thunk+0xc/0x10
>> [<c1002959>] syscall_call+0x7/0xb
>>
>> This was because the sctp_wait_for_connect() would aqcure the socket
>> lock and then proceed to release the last reference count on the
>> association, thus cause the fully destruction path to finish freeing
>> the socket.
>>
>> The simplest solution is to start a very short timer in case the socket
>> is owned by user. When the timer expires, we can do some verification
>> and be able to do the release properly.
>>
>
> After reviewed this patch, I think we should delete active ICMP proto
> unreachable timer when free transport. since I don't reproduce this BUG,
> so I just do compile test only, sorry.
Hi Wei
To reproduce with the original code (before my patch), add the following rule
# iptables -A INPUT -p sctp -j REJECT --reject-with icmp-proto-unreachable
and then try connecting to the localhost. I was never able to reproduce the race
on any kind of network, but on localhost it happens every time.
>
> [PATCH] sctp: delete active ICMP proto unreachable timer when free transport
>
> transport may be free before ICMP proto unreachable timer expire, so
> we should delete active ICMP proto unreachable timer when transport
> is going away.
>
> Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
> ---
> net/sctp/transport.c | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/net/sctp/transport.c b/net/sctp/transport.c
> index 4a36803..165d54e 100644
> --- a/net/sctp/transport.c
> +++ b/net/sctp/transport.c
> @@ -173,6 +173,10 @@ void sctp_transport_free(struct sctp_transport *transport)
> del_timer(&transport->T3_rtx_timer))
> sctp_transport_put(transport);
>
> + /* Delete the ICMP proto unreachable timer if it's active. */
> + if (timer_pending(&transport->proto_unreach_timer) &&
> + del_timer(&transport->proto_unreach_timer))
> + sctp_association_put(transport->asoc);
>
> sctp_transport_put(transport);
> }
ACK. This fixes a race against close(). Although that will be fairly hard to
do, it is possible.
Acked-by: Vlad Yasevich <vladislav.yasevich@hp.com>
-vlad
^ permalink raw reply
* Re: [PATCH v2] can: Add driver for esd CAN-USB/2 device
From: Wolfgang Grandegger @ 2010-05-10 13:43 UTC (permalink / raw)
To: Matthias Fuchs
Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <201005101528.50834.matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
Hi Matthias,
On 05/10/2010 03:28 PM, Matthias Fuchs wrote:
> This patch adds a driver for esd's USB high speed
> CAN interface. The driver supports devices with
> multiple CAN interfaces.
>
> Signed-off-by: Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
> ---
>
> version 2:
> - use bus-error reporting and counters
I don't find support for CAN_CTRLMODE_BERR_REPORTING, apart from setting
the flag. If it's set bus error interrupts should be enabled or disabled
otherwise.
Wolfgang.
^ permalink raw reply
* [PATCH v2] can: Add driver for esd CAN-USB/2 device
From: Matthias Fuchs @ 2010-05-10 13:28 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
linux-usb-u79uwXL29TY76Z2rM5mHXA
This patch adds a driver for esd's USB high speed
CAN interface. The driver supports devices with
multiple CAN interfaces.
Signed-off-by: Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
---
version 2:
- use bus-error reporting and counters
- minor cleanup
- rebased against current net-next-2.6 tree
- initial post to linux-usb list for review
drivers/net/can/usb/Kconfig | 6 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/esd_usb2.c | 1126 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 1133 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/can/usb/esd_usb2.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 97ff6fe..0452549 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -7,4 +7,10 @@ config CAN_EMS_USB
This driver is for the one channel CPC-USB/ARM7 CAN/USB interface
from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+config CAN_ESD_USB2
+ tristate "ESD USB/2 CAN/USB interface"
+ ---help---
+ This driver supports the CAN-USB/2 interface
+ from esd electronic system design gmbh (http://www.esd.eu).
+
endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index 0afd51d..fce3cf1 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -3,5 +3,6 @@
#
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
+obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
new file mode 100644
index 0000000..24b935b
--- /dev/null
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -0,0 +1,1126 @@
+/*
+ * CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>, esd gmbh
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>");
+MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 interfaces");
+MODULE_LICENSE("GPL v2");
+
+/* Define these values to match your devices */
+#define USB_ESDGMBH_VENDOR_ID 0x0ab4
+#define USB_CANUSB2_PRODUCT_ID 0x0010
+
+#define ESD_USB2_CAN_CLOCK 60000000
+#define ESD_USB2_MAX_NETS 2
+
+/* USB2 commands */
+#define CMD_VERSION 1 /* also used for VERSION_REPLY */
+#define CMD_CAN_RX 2 /* device to host only */
+#define CMD_CAN_TX 3 /* also used for TX_DONE */
+#define CMD_SETBAUD 4 /* also used for SETBAUD_REPLY */
+#define CMD_TS 5 /* also used for TS_REPLY */
+#define CMD_IDADD 6 /* also used for IDADD_REPLY */
+
+/* esd CAN message flags - dlc field */
+#define ESD_RTR 0x10
+
+/* esd CAN message flags - id field */
+#define ESD_EXTID 0x20000000
+#define ESD_EVENT 0x40000000
+#define ESD_IDMASK 0x1fffffff
+
+/* esd CAN event ids used by this driver */
+#define ESD_EV_CAN_ERROR_EXT 2
+
+/* baudrate message flags */
+#define ESD_USB2_UBR 0x80000000
+#define ESD_USB2_LOM 0x40000000
+#define ESD_USB2_NO_BAUDRATE 0x7fffffff
+#define ESD_USB2_TSEG1_MIN 1
+#define ESD_USB2_TSEG1_MAX 16
+#define ESD_USB2_TSEG1_SHIFT 16
+#define ESD_USB2_TSEG2_MIN 1
+#define ESD_USB2_TSEG2_MAX 8
+#define ESD_USB2_TSEG2_SHIFT 20
+#define ESD_USB2_SJW_MAX 4
+#define ESD_USB2_SJW_SHIFT 14
+#define ESD_USB2_BRP_MIN 1
+#define ESD_USB2_BRP_MAX 1024
+#define ESD_USB2_BRP_INC 1
+#define ESD_USB2_3_SAMPLES 0x00800000
+
+/* esd IDADD message */
+#define ESD_ID_ENABLE 0x80
+#define ESD_MAX_ID_SEGMENT 64
+
+/* SJA1000 ECC register (emulated by usb2 firmware) */
+#define SJA1000_ECC_SEG 0x1F
+#define SJA1000_ECC_DIR 0x20
+#define SJA1000_ECC_ERR 0x06
+#define SJA1000_ECC_BIT 0x00
+#define SJA1000_ECC_FORM 0x40
+#define SJA1000_ECC_STUFF 0x80
+#define SJA1000_ECC_MASK 0xc0
+
+/* esd bus state event codes */
+#define ESD_BUSSTATE_MASK 0xc0
+#define ESD_BUSSTATE_WARN 0x40
+#define ESD_BUSSTATE_ERRPASSIVE 0x80
+#define ESD_BUSSTATE_BUSOFF 0xc0
+
+#define RX_BUFFER_SIZE 1024
+#define MAX_RX_URBS 4
+#define MAX_TX_URBS 16 /* must be power of 2 */
+
+struct header_msg {
+ u8 len; /* len is always the total message length in 32bit words */
+ u8 cmd;
+ u8 rsvd[2];
+};
+
+struct version_msg {
+ u8 len;
+ u8 cmd;
+ u8 rsvd;
+ u8 flags;
+ __le32 drv_version;
+};
+
+struct version_reply_msg {
+ u8 len;
+ u8 cmd;
+ u8 nets;
+ u8 features;
+ __le32 version;
+ u8 name[16];
+ __le32 rsvd;
+ __le32 ts;
+};
+
+struct rx_msg {
+ u8 len;
+ u8 cmd;
+ u8 net;
+ u8 dlc;
+ __le32 ts;
+ __le32 id; /* upper 3 bits contain flags */
+ u8 data[8];
+};
+
+struct tx_msg {
+ u8 len;
+ u8 cmd;
+ u8 net;
+ u8 dlc;
+ __le32 hnd;
+ __le32 id; /* upper 3 bits contain flags */
+ u8 data[8];
+};
+
+struct tx_done_msg {
+ u8 len;
+ u8 cmd;
+ u8 net;
+ u8 status;
+ __le32 hnd;
+ __le32 ts;
+};
+
+struct id_filter_msg {
+ u8 len;
+ u8 cmd;
+ u8 net;
+ u8 option;
+ __le32 mask[ESD_MAX_ID_SEGMENT + 1];
+};
+
+struct set_baudrate_msg {
+ u8 len;
+ u8 cmd;
+ u8 net;
+ u8 rsvd;
+ __le32 baud;
+};
+
+/* Main message type used between library and application */
+struct __attribute__ ((packed)) esd_usb2_msg {
+ union {
+ struct header_msg hdr;
+ struct version_msg version;
+ struct version_reply_msg version_reply;
+ struct rx_msg rx;
+ struct tx_msg tx;
+ struct tx_done_msg txdone;
+ struct set_baudrate_msg setbaud;
+ struct id_filter_msg filter;
+ } msg;
+};
+
+static struct usb_device_id esd_usb2_table[] = {
+ {USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, esd_usb2_table);
+
+struct esd_usb2_net_priv;
+
+struct esd_tx_urb_context {
+ struct esd_usb2_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct esd_usb2 {
+ struct usb_device *udev;
+ struct esd_usb2_net_priv *nets[ESD_USB2_MAX_NETS];
+
+ struct usb_anchor rx_submitted;
+
+ int net_count;
+ u32 version;
+ int rxinitdone;
+};
+
+struct esd_usb2_net_priv {
+ struct can_priv can; /* must be the first member */
+
+ atomic_t active_tx_jobs;
+ struct usb_anchor tx_submitted;
+ struct esd_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ int open_time;
+ struct esd_usb2 *usb2;
+ struct net_device *netdev;
+ int index;
+ u8 old_state;
+ struct can_berr_counter bec;
+};
+
+static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv,
+ struct esd_usb2_msg *msg)
+{
+ struct net_device_stats *stats = &priv->netdev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 id = le32_to_cpu(msg->msg.rx.id) & ESD_IDMASK;
+
+ if (id == ESD_EV_CAN_ERROR_EXT) {
+ u8 state = msg->msg.rx.data[0];
+ u8 ecc = msg->msg.rx.data[1];
+ u8 txerr = msg->msg.rx.data[2];
+ u8 rxerr = msg->msg.rx.data[3];
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb == NULL) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ if (state != priv->old_state) {
+ priv->old_state = state;
+
+ switch (state & ESD_BUSSTATE_MASK) {
+ case ESD_BUSSTATE_BUSOFF:
+ priv->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(priv->netdev);
+ break;
+ case ESD_BUSSTATE_WARN:
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ priv->can.can_stats.error_warning++;
+ break;
+ case ESD_BUSSTATE_ERRPASSIVE:
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ break;
+ }
+ } else {
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & SJA1000_ECC_MASK) {
+ case SJA1000_ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case SJA1000_ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case SJA1000_ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & SJA1000_ECC_SEG;
+ break;
+ }
+
+ /* Error occured during transmission? */
+ if (!(ecc & SJA1000_ECC_DIR))
+ cf->data[2] |= CAN_ERR_PROT_TX;
+
+ if (priv->can.state == CAN_STATE_ERROR_WARNING ||
+ priv->can.state == CAN_STATE_ERROR_PASSIVE) {
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+ }
+
+ netif_rx(skb);
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+}
+
+static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv,
+ struct esd_usb2_msg *msg)
+{
+ struct net_device_stats *stats = &priv->netdev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ int i;
+ u32 id;
+
+ if (!netif_device_present(priv->netdev))
+ return;
+
+ id = le32_to_cpu(msg->msg.rx.id);
+
+ if (id & ESD_EVENT) {
+ esd_usb2_rx_event(priv, msg);
+ } else {
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (skb == NULL) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ cf->can_id = id & ESD_IDMASK;
+ cf->can_dlc = get_can_dlc(msg->msg.rx.dlc);
+
+ if (id & ESD_EXTID)
+ cf->can_id |= CAN_EFF_FLAG;
+
+ if (msg->msg.rx.dlc & ESD_RTR) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else {
+ for (i = 0; i < cf->can_dlc; i++)
+ cf->data[i] = msg->msg.rx.data[i];
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+
+ return;
+}
+
+static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv,
+ struct esd_usb2_msg *msg)
+{
+ struct net_device_stats *stats = &priv->netdev->stats;
+ struct net_device *netdev = priv->netdev;
+ struct esd_tx_urb_context *context;
+
+ if (!netif_device_present(netdev))
+ return;
+
+ context = &priv->tx_contexts[msg->msg.txdone.hnd & (MAX_TX_URBS - 1)];
+
+ if (!msg->msg.txdone.status) {
+ stats->tx_packets++;
+ stats->tx_bytes += context->dlc;
+ can_get_echo_skb(netdev, context->echo_index);
+ } else {
+ stats->tx_errors++;
+ can_free_echo_skb(netdev, context->echo_index);
+ }
+
+ /* Release context */
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_jobs);
+
+ netif_wake_queue(netdev);
+}
+
+static void esd_usb2_read_bulk_callback(struct urb *urb)
+{
+ struct esd_usb2 *dev = urb->context;
+ int retval;
+ int pos = 0;
+ int i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ dev_info(dev->udev->dev.parent,
+ "Rx URB aborted (%d)\n", urb->status);
+ goto resubmit_urb;
+ }
+
+ while (pos < urb->actual_length) {
+ struct esd_usb2_msg *msg;
+
+ msg = (struct esd_usb2_msg *)(urb->transfer_buffer + pos);
+
+ switch (msg->msg.hdr.cmd) {
+ case CMD_CAN_RX:
+ esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg);
+ break;
+
+ case CMD_CAN_TX:
+ esd_usb2_tx_done_msg(dev->nets[msg->msg.txdone.net],
+ msg);
+ break;
+ }
+
+ pos += msg->msg.hdr.len << 2;
+
+ if (pos > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "format error\n");
+ break;
+ }
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+ urb->transfer_buffer, RX_BUFFER_SIZE,
+ esd_usb2_read_bulk_callback, dev);
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval == -ENODEV) {
+ for (i = 0; i < dev->net_count; i++) {
+ if (dev->nets[i])
+ netif_device_detach(dev->nets[i]->netdev);
+ }
+ } else if (retval) {
+ dev_err(dev->udev->dev.parent,
+ "failed resubmitting read bulk urb: %d\n", retval);
+ }
+
+ return;
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static void esd_usb2_write_bulk_callback(struct urb *urb)
+{
+ struct esd_tx_urb_context *context = urb->context;
+ struct esd_usb2_net_priv *priv;
+ struct esd_usb2 *dev;
+ struct net_device *netdev;
+ size_t size = sizeof(struct esd_usb2_msg);
+
+ BUG_ON(!context);
+
+ priv = context->priv;
+ netdev = priv->netdev;
+ dev = priv->usb2;
+
+ /* free up our allocated buffer */
+ usb_buffer_free(urb->dev, size,
+ urb->transfer_buffer, urb->transfer_dma);
+
+ if (!netif_device_present(netdev))
+ return;
+
+ if (urb->status)
+ dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n",
+ urb->status);
+
+ netdev->trans_start = jiffies;
+}
+
+#ifdef CONFIG_SYSFS
+static ssize_t show_firmware(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(d);
+ struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d.%d.%d\n",
+ (dev->version >> 12) & 0xf,
+ (dev->version >> 8) & 0xf,
+ dev->version & 0xff);
+}
+static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL);
+
+static ssize_t show_hardware(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(d);
+ struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d.%d.%d\n",
+ (dev->version >> 28) & 0xf,
+ (dev->version >> 24) & 0xf,
+ (dev->version >> 16) & 0xff);
+}
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+static ssize_t show_nets(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(d);
+ struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d", dev->net_count);
+}
+static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL);
+#endif
+
+static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg)
+{
+ int actual_length;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev, 2),
+ msg,
+ msg->msg.hdr.len << 2,
+ &actual_length,
+ 1000);
+}
+
+static int esd_usb2_wait_msg(struct esd_usb2 *dev,
+ struct esd_usb2_msg *msg)
+{
+ int actual_length;
+
+ return usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev, 1),
+ msg,
+ sizeof(*msg),
+ &actual_length,
+ 1000);
+}
+
+static int esd_usb2_setup_rx_urbs(struct esd_usb2 *dev)
+{
+ int i, err = 0;
+
+ if (dev->rxinitdone)
+ return 0;
+
+ for (i = 0; i < MAX_RX_URBS; i++) {
+ struct urb *urb = NULL;
+ u8 *buf = NULL;
+
+ /* create a URB, and a buffer for it */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ buf = usb_buffer_alloc(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for USB buffer\n");
+ err = -ENOMEM;
+ goto freeurb;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev, 1),
+ buf, RX_BUFFER_SIZE,
+ esd_usb2_read_bulk_callback, dev);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf,
+ urb->transfer_dma);
+ }
+
+freeurb:
+ /* Drop reference, USB core will take care of freeing it */
+ usb_free_urb(urb);
+ if (err)
+ break;
+ }
+
+ /* Did we submit any URBs */
+ if (i == 0) {
+ dev_err(dev->udev->dev.parent, "couldn't setup read URBs\n");
+ return err;
+ }
+
+ /* Warn if we've couldn't transmit all the URBs */
+ if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "rx performance may be slow\n");
+ }
+
+ dev->rxinitdone = 1;
+ return 0;
+}
+
+/*
+ * Start interface
+ */
+static int esd_usb2_start(struct esd_usb2_net_priv *priv)
+{
+ struct esd_usb2 *dev = priv->usb2;
+ struct net_device *netdev = priv->netdev;
+ struct esd_usb2_msg msg;
+ int err, i;
+
+ /*
+ * Enable all IDs
+ * The IDADD message takes up to 64 32 bit bitmasks (2048 bits).
+ * Each bit represents one 11 bit CAN identifier. A set bit
+ * enables reception of the corresponding CAN identifier. A cleared
+ * bit disabled this identifier. An additional bitmask value
+ * following the CAN 2.0A bits is used to enable reception of
+ * extended CAN frames. Only the LSB of this final mask is checked
+ * for the complete 29 bit ID range. The IDADD message also allows
+ * filter configuration for an ID subset. In this case you can add
+ * the number of the starting bitmask (0..64) to the filter.option
+ * field followed by only some bitmasks.
+ */
+ msg.msg.hdr.cmd = CMD_IDADD;
+ msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT;
+ msg.msg.filter.net = priv->index;
+ msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */
+ for (i = 0; i < ESD_MAX_ID_SEGMENT; i++)
+ msg.msg.filter.mask[i] = cpu_to_le32(0xffffffff);
+ /* enable 29bit extended IDs */
+ msg.msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001);
+
+ err = esd_usb2_send_msg(dev, &msg);
+ if (err)
+ goto failed;
+
+ err = esd_usb2_setup_rx_urbs(dev);
+ if (err)
+ goto failed;
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+
+failed:
+ if (err == -ENODEV)
+ netif_device_detach(netdev);
+
+ dev_err(netdev->dev.parent, "couldn't start device: %d\n", err);
+
+ return err;
+}
+
+static void unlink_all_urbs(struct esd_usb2 *dev)
+{
+ struct esd_usb2_net_priv *priv;
+ int i;
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+ for (i = 0; i < dev->net_count; i++) {
+ priv = dev->nets[i];
+ if (priv) {
+ usb_kill_anchored_urbs(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_jobs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+ }
+ }
+}
+
+static int esd_usb2_open(struct net_device *netdev)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+ int err;
+
+ /* common open */
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ /* finally start device */
+ err = esd_usb2_start(priv);
+ if (err) {
+ dev_warn(netdev->dev.parent,
+ "couldn't start device: %d\n", err);
+ close_candev(netdev);
+ return err;
+ }
+
+ priv->open_time = jiffies;
+
+ netif_start_queue(netdev);
+
+ return 0;
+}
+
+static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+ struct esd_usb2 *dev = priv->usb2;
+ struct esd_tx_urb_context *context = NULL;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct esd_usb2_msg *msg;
+ struct urb *urb;
+ u8 *buf;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+ size_t size = sizeof(struct esd_usb2_msg);
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ /* create a URB, and a buffer for it, and copy the data to the URB */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ dev_err(netdev->dev.parent, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nourbmem;
+ }
+
+ buf = usb_buffer_alloc(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(netdev->dev.parent, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nobufmem;
+ }
+
+ msg = (struct esd_usb2_msg *)buf;
+
+ msg->msg.hdr.len = 3; /* minimal length */
+ msg->msg.hdr.cmd = CMD_CAN_TX;
+ msg->msg.tx.net = priv->index;
+ msg->msg.tx.dlc = cf->can_dlc;
+ msg->msg.tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->msg.tx.dlc |= ESD_RTR;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ msg->msg.tx.id |= cpu_to_le32(ESD_EXTID);
+
+ for (i = 0; i < cf->can_dlc; i++)
+ msg->msg.tx.data[i] = cf->data[i];
+
+ msg->msg.hdr.len += (cf->can_dlc + 3) >> 2;
+
+ for (i = 0; i < MAX_TX_URBS; i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ /*
+ * This may never happen.
+ */
+ if (!context) {
+ dev_warn(netdev->dev.parent, "couldn't find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ /* hnd must not be 0 - MSB is stripped in txdone handling */
+ msg->msg.tx.hnd = 0x80000000 | i; /* returned in TX done message */
+
+ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf,
+ msg->msg.hdr.len << 2,
+ esd_usb2_write_bulk_callback, context);
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_jobs);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_jobs);
+ usb_unanchor_urb(urb);
+
+ stats->tx_dropped++;
+
+ if (err == -ENODEV)
+ netif_device_detach(netdev);
+ else
+ dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err);
+
+ goto releasebuf;
+ }
+
+ netdev->trans_start = jiffies;
+
+ /* Slow down tx path */
+ if (atomic_read(&priv->active_tx_jobs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ /*
+ * Release our reference to this URB, the USB core will eventually free
+ * it entirely.
+ */
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ usb_buffer_free(dev->udev, size, buf, urb->transfer_dma);
+
+nobufmem:
+ usb_free_urb(urb);
+
+nourbmem:
+ return ret;
+}
+
+static int esd_usb2_close(struct net_device *netdev)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+ struct esd_usb2_msg msg;
+ int i;
+
+ /* Disable all IDs (see esd_usb2_start()) */
+ msg.msg.hdr.cmd = CMD_IDADD;
+ msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT;
+ msg.msg.filter.net = priv->index;
+ msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */
+ for (i = 0; i <= ESD_MAX_ID_SEGMENT; i++)
+ msg.msg.filter.mask[i] = 0;
+ esd_usb2_send_msg(priv->usb2, &msg);
+
+ /* set CAN controller to reset mode */
+ msg.msg.hdr.len = 2;
+ msg.msg.hdr.cmd = CMD_SETBAUD;
+ msg.msg.setbaud.net = priv->index;
+ msg.msg.setbaud.rsvd = 0;
+ msg.msg.setbaud.baud = cpu_to_le32(ESD_USB2_NO_BAUDRATE);
+ esd_usb2_send_msg(priv->usb2, &msg);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ netif_stop_queue(netdev);
+
+ close_candev(netdev);
+
+ priv->open_time = 0;
+
+ return 0;
+}
+
+static const struct net_device_ops esd_usb2_netdev_ops = {
+ .ndo_open = esd_usb2_open,
+ .ndo_stop = esd_usb2_close,
+ .ndo_start_xmit = esd_usb2_start_xmit,
+};
+
+static struct can_bittiming_const esd_usb2_bittiming_const = {
+ .name = "esd_usb2",
+ .tseg1_min = ESD_USB2_TSEG1_MIN,
+ .tseg1_max = ESD_USB2_TSEG1_MAX,
+ .tseg2_min = ESD_USB2_TSEG2_MIN,
+ .tseg2_max = ESD_USB2_TSEG2_MAX,
+ .sjw_max = ESD_USB2_SJW_MAX,
+ .brp_min = ESD_USB2_BRP_MIN,
+ .brp_max = ESD_USB2_BRP_MAX,
+ .brp_inc = ESD_USB2_BRP_INC,
+};
+
+static int esd_usb2_set_bittiming(struct net_device *netdev)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct esd_usb2_msg msg;
+ u32 canbtr;
+
+ canbtr = ESD_USB2_UBR;
+ canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1);
+ canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1))
+ << ESD_USB2_SJW_SHIFT;
+ canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1)
+ & (ESD_USB2_TSEG1_MAX - 1))
+ << ESD_USB2_TSEG1_SHIFT;
+ canbtr |= ((bt->phase_seg2 - 1) & (ESD_USB2_TSEG2_MAX - 1))
+ << ESD_USB2_TSEG2_SHIFT;
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ canbtr |= ESD_USB2_3_SAMPLES;
+
+ msg.msg.hdr.len = 2;
+ msg.msg.hdr.cmd = CMD_SETBAUD;
+ msg.msg.setbaud.net = priv->index;
+ msg.msg.setbaud.rsvd = 0;
+ msg.msg.setbaud.baud = cpu_to_le32(canbtr);
+
+ dev_info(netdev->dev.parent, "setting BTR=%#x\n", canbtr);
+
+ return esd_usb2_send_msg(priv->usb2, &msg);
+}
+
+static int esd_usb2_get_berr_counter(const struct net_device *netdev,
+ struct can_berr_counter *bec)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+
+ bec->txerr = priv->bec.txerr;
+ bec->rxerr = priv->bec.rxerr;
+
+ return 0;
+}
+
+static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+ struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ netif_wake_queue(netdev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int esd_usb2_probe_one_net(struct usb_interface *intf, int index)
+{
+ struct esd_usb2 *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct esd_usb2_net_priv *priv;
+ int err;
+ int i;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "couldn't alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_jobs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->usb2 = dev;
+ priv->netdev = netdev;
+ priv->index = index;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = ESD_USB2_CAN_CLOCK;
+ priv->can.bittiming_const = &esd_usb2_bittiming_const;
+ priv->can.do_set_bittiming = esd_usb2_set_bittiming;
+ priv->can.do_set_mode = esd_usb2_set_mode;
+ priv->can.do_get_berr_counter = esd_usb2_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_BERR_REPORTING;
+
+ netdev->flags |= IFF_ECHO; /* we support local echo */
+
+ netdev->netdev_ops = &esd_usb2_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev,
+ "couldn't register CAN device: %d\n", err);
+ free_candev(netdev);
+ return -ENOMEM;
+ }
+
+ dev->nets[index] = priv;
+ dev_info(netdev->dev.parent, "device %s registered\n", netdev->name);
+ return 0;
+}
+
+/*
+ * probe function for new USB2 devices
+ *
+ * check version information and number of available
+ * CAN interfaces
+ */
+static int esd_usb2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct esd_usb2 *dev;
+ struct esd_usb2_msg msg;
+ int i, err = -ENOMEM;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->udev = interface_to_usbdev(intf);
+
+ init_usb_anchor(&dev->rx_submitted);
+
+ usb_set_intfdata(intf, dev);
+
+ /* query number of CAN interfaces (nets) */
+ msg.msg.hdr.cmd = CMD_VERSION;
+ msg.msg.hdr.len = 2;
+ msg.msg.version.rsvd = 0;
+ msg.msg.version.flags = 0;
+ msg.msg.version.drv_version = 0;
+
+ if (esd_usb2_send_msg(dev, &msg) < 0) {
+ dev_err(&intf->dev, "sending version message failed\n");
+ goto free_dev;
+ }
+
+ if (esd_usb2_wait_msg(dev, &msg) < 0) {
+ dev_err(&intf->dev, "no version message answer\n");
+ goto free_dev;
+ }
+
+ dev->net_count = (int)msg.msg.version_reply.nets;
+ dev->version = le32_to_cpu(msg.msg.version_reply.version);
+
+#ifdef CONFIG_SYSFS
+ if (device_create_file(&intf->dev, &dev_attr_firmware))
+ dev_err(&intf->dev,
+ "Couldn't create device file for firmware\n");
+
+ if (device_create_file(&intf->dev, &dev_attr_hardware))
+ dev_err(&intf->dev,
+ "Couldn't create device file for hardware\n");
+
+ if (device_create_file(&intf->dev, &dev_attr_nets))
+ dev_err(&intf->dev,
+ "Couldn't create device file for nets\n");
+#endif
+
+ /* do per device probing */
+ for (i = 0; i < dev->net_count; i++)
+ esd_usb2_probe_one_net(intf, i);
+
+ return 0;
+
+free_dev:
+ kfree(dev);
+ return err;
+}
+
+/*
+ * called by the usb core when the device is removed from the system
+ */
+static void esd_usb2_disconnect(struct usb_interface *intf)
+{
+ struct esd_usb2 *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ int i;
+
+#ifdef CONFIG_SYSFS
+ device_remove_file(&intf->dev, &dev_attr_firmware);
+ device_remove_file(&intf->dev, &dev_attr_hardware);
+ device_remove_file(&intf->dev, &dev_attr_nets);
+#endif
+ usb_set_intfdata(intf, NULL);
+
+ if (dev) {
+ for (i = 0; i < dev->net_count; i++) {
+ if (dev->nets[i]) {
+ netdev = dev->nets[i]->netdev;
+ unregister_netdev(netdev);
+ free_candev(netdev);
+ }
+ }
+ unlink_all_urbs(dev);
+ }
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver esd_usb2_driver = {
+ .name = "esd_usb2",
+ .probe = esd_usb2_probe,
+ .disconnect = esd_usb2_disconnect,
+ .id_table = esd_usb2_table,
+};
+
+static int __init esd_usb2_init(void)
+{
+ int err;
+
+ /* register this driver with the USB subsystem */
+ err = usb_register(&esd_usb2_driver);
+
+ if (err) {
+ err("usb_register failed. Error number %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+module_init(esd_usb2_init);
+
+static void __exit esd_usb2_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&esd_usb2_driver);
+}
+module_exit(esd_usb2_exit);
--
1.5.6.3
^ permalink raw reply related
* Re: [PATCH net-next-2.6] net: trans_start cleanups
From: David Miller @ 2010-05-10 12:02 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1273435324.2325.61.camel@edumazet-laptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Sun, 09 May 2010 22:02:03 +0200
> Now that core network takes care of trans_start updates, dont do it
> in drivers themselves, if possible. Drivers can avoid one cache miss
> (on dev->trans_start) in their start_xmit() handler.
>
> Exceptions are NETIF_F_LLTX drivers
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Looks good, applied, thanks Eric.
^ permalink raw reply
* [PATCH] net: Fix FDDI and TR config checks in ipv4 arp and LLC.
From: David Miller @ 2010-05-10 11:59 UTC (permalink / raw)
To: netdev
Need to check both CONFIG_FOO and CONFIG_FOO_MODULE
Signed-off-by: David S. Miller <davem@davemloft.net>
---
Committed to net-2.6
net/ipv4/arp.c | 6 +++---
net/llc/llc_sap.c | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 6e74706..80769f1 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -661,13 +661,13 @@ struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
#endif
#endif
-#ifdef CONFIG_FDDI
+#if defined(CONFIG_FDDI) || defined(CONFIG_FDDI_MODULE)
case ARPHRD_FDDI:
arp->ar_hrd = htons(ARPHRD_ETHER);
arp->ar_pro = htons(ETH_P_IP);
break;
#endif
-#ifdef CONFIG_TR
+#if defined(CONFIG_TR) || defined(CONFIG_TR_MODULE)
case ARPHRD_IEEE802_TR:
arp->ar_hrd = htons(ARPHRD_IEEE802);
arp->ar_pro = htons(ETH_P_IP);
@@ -1051,7 +1051,7 @@ static int arp_req_set(struct net *net, struct arpreq *r,
return -EINVAL;
}
switch (dev->type) {
-#ifdef CONFIG_FDDI
+#if defined(CONFIG_FDDI) || defined(CONFIG_FDDI_MODULE)
case ARPHRD_FDDI:
/*
* According to RFC 1390, FDDI devices should accept ARP
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index a432f0e..94e7fca 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -31,7 +31,7 @@ static int llc_mac_header_len(unsigned short devtype)
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK:
return sizeof(struct ethhdr);
-#ifdef CONFIG_TR
+#if defined(CONFIG_TR) || defined(CONFIG_TR_MODULE)
case ARPHRD_IEEE802_TR:
return sizeof(struct trh_hdr);
#endif
--
1.7.0.4
^ permalink raw reply related
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