* [PATCH 2/7] CAN: Add PF_CAN core module
From: Urs Thuermann @ 2007-10-05 10:49 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, Arnaldo Carvalho de Melo, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
In-Reply-To: <20071005104905.20150.0@janus.isnogud.escape.de>
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60163 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 78 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 969 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 120 +++++
net/can/proc.c | 532 +++++++++++++++++++++++++
10 files changed, 1936 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20071004"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern int can_rx_register(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data, char *ident);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
+#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
+ KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
+ __func__, ##args) : 0)
+#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
+#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(fmt, args...)
+#define DBG_FRAME(fmt, cf)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-10-05 08:11:10.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-10-05 08:11:15.000000000 +0200
@@ -218,6 +218,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-10-05 08:11:10.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-10-05 08:11:15.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-10-05 11:16:25.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# Controller Area Network (CAN) network layer core configuration
+#
+
+menuconfig CAN
+ depends on NET
+ tristate "CAN bus subsystem support"
+ ---help---
+ Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
+ communications protocol which was developed by Bosch in
+ 1991, mainly for automotive, but now widely used in marine
+ (NMEA2000), industrial, and medical applications.
+ More information on the CAN network protocol family PF_CAN
+ is contained in <Documentation/networking/can.txt>.
+
+ If you want CAN support you should say Y here and also to the
+ specific driver for your controller(s) below.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages. Select this if you are having a problem with CAN
+ support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-10-05 11:15:08.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-10-05 11:09:07.000000000 +0200
@@ -0,0 +1,969 @@
+/*
+ * af_can.c - Protocol family CAN core module
+ * (used by different CAN protocol modules)
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/uaccess.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+static __initdata const char banner[] = KERN_INFO
+ "can: controller area network core (" CAN_VERSION_STRING ")\n";
+
+MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
+ "Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+MODULE_ALIAS_NETPROTO(PF_CAN);
+
+static int stats_timer __read_mostly = 1;
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+#define DBG_PREFIX "can"
+#define DBG_VAR can_debug
+static int can_debug __read_mostly;
+module_param_named(debug, can_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ switch (cmd) {
+
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (ret) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for success and correct type */
+ if (!cp || cp->type != sock->type) {
+ ret = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ ret = -EPERM;
+ goto errout;
+ }
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk) {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ return ret;
+}
+
+/*
+ * af_can tx path
+ */
+
+/**
+ * can_send - transmit a CAN frame (optional with local loopback)
+ * @skb: pointer to socket buffer with CAN frame in data section
+ * @loop: loopback for listeners on local CAN sockets (recommended default!)
+ *
+ * Return:
+ * 0 on success
+ * -ENETDOWN when the selected interface is down
+ * -ENOBUFS on full driver queue (see net_xmit_errno())
+ * -ENOMEM when local loopback failed at calling skb_clone()
+ * -EPERM when trying to send on a non-CAN interface
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ if (loop) {
+ /* local loopback of sent CAN frames */
+
+ /* indication for the CAN driver: do loopback */
+ skb->pkt_type = PACKET_LOOPBACK;
+
+ /*
+ * The reference to the originating sock may be required
+ * by the receiving socket to check whether the frame is
+ * its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ * Therefore we have to ensure that skb->sk remains the
+ * reference to the originating sock by restoring skb->sk
+ * after each skb_clone() or skb_orphan() usage.
+ */
+
+ if (!(skb->dev->flags & IFF_ECHO)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } else {
+ /* indication for the CAN driver: no loopback required */
+ skb->pkt_type = PACKET_HOST;
+ }
+
+ /* send to netdevice */
+ err = dev_queue_xmit(skb);
+ if (err > 0)
+ err = net_xmit_errno(err);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
+ struct dev_rcv_lists *d)
+{
+ canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
+
+ /* filter error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* reduce condition testing at receive time */
+ *can_id &= *mask;
+
+ /* inverse can_id/can_mask filter */
+ if (inv)
+ return &d->rx[RX_INV];
+
+ /* mask == 0 => no condition testing at receive time */
+ if (!(*mask))
+ return &d->rx[RX_ALL];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ return &d->rx_sff[*can_id];
+ }
+
+ /* default: filter via can_id/can_mask */
+ return &d->rx[RX_FIL];
+}
+
+/**
+ * can_rx_register - subscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier (see description)
+ * @mask: CAN mask (see description)
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ * @ident: string for calling module indentification
+ *
+ * Description:
+ * Invokes the callback function with the received sk_buff and the given
+ * parameter 'data' on a matching receive filter. A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * Return:
+ * 0 on success
+ * -ENOMEM on missing cache mem to create subscription entry
+ * -ENODEV unknown device
+ */
+int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+ void (*func)(struct sk_buff *, void *), void *data,
+ char *ident)
+{
+ struct receiver *r;
+ struct hlist_head *rl;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ rl = find_rcv_list(&can_id, &mask, d);
+
+ r->can_id = can_id;
+ r->mask = mask;
+ r->matches = 0;
+ r->func = func;
+ r->data = data;
+ r->ident = ident;
+
+ hlist_add_head_rcu(&r->list, rl);
+ d->entries++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * can_rx_delete_receiver - rcu callback for single receiver entry removal
+ */
+static void can_rx_delete_receiver(struct rcu_head *rp)
+{
+ struct receiver *r = container_of(rp, struct receiver, rcu);
+
+ DBG("removing receiver at %p\n", r);
+ kmem_cache_free(rcv_cache, r);
+}
+
+/**
+ * can_rx_unregister - unsubscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier
+ * @mask: CAN mask
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Removes subscription entry depending on given (subscription) values.
+ */
+void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
+ void (*func)(struct sk_buff *, void *), void *data)
+{
+ struct receiver *r = NULL;
+ struct hlist_head *rl;
+ struct hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_ERR "BUG: receive list not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ goto out;
+ }
+
+ rl = find_rcv_list(&can_id, &mask, d);
+
+ /*
+ * Search the receiver list for the item to delete. This should
+ * exist, since no receiver may be unregistered that hasn't
+ * been registered before.
+ */
+
+ hlist_for_each_entry_rcu(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bugs in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ int matches = 0;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ canid_t can_id = cf->can_id;
+
+ if (d->entries == 0)
+ return 0;
+
+ if (can_id & CAN_ERR_FLAG) {
+ /* check for error frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&rx_alldev_list, skb);
+
+ /* find receive list for this device */
+ d = find_dev_rcv_lists(dev);
+ if (d)
+ matches += can_rcv_filter(d, skb);
+
+ rcu_read_unlock();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * af_can protocol functions
+ */
+
+/**
+ * can_proto_register - register CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL invalid (out of range) protocol number
+ * -EBUSY protocol already in use
+ * -ENOBUF if proto_register() fails
+ */
+int can_proto_register(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+EXPORT_SYMBOL(can_proto_unregister);
+
+/*
+ * af_can notifier to create/remove CAN netdevice specific structs
+ */
+static int can_notifier(struct notifier_block *nb, unsigned long msg,
+ void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &init_net)
+ return NOTIFY_DONE;
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_REGISTER:
+
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf)
+{
+ char idbuf[12];
+ char hexbuf[28];
+ int dlc;
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
+ else
+ sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ sprintf(hexbuf, "(RTR)");
+ else
+ hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
+
+ printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d\n",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+
+ print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->head, skb->end - skb->head, 0);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .family = PF_CAN,
+ .create = can_create,
+ .owner = THIS_MODULE,
+};
+
+/* notifier block for netdevice event */
+static struct notifier_block can_netdev_notifier __read_mostly = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef AF_CAN_H
+#define AF_CAN_H
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/can.h>
+
+/* af_can rx dispatcher structures */
+
+struct receiver {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ canid_t can_id;
+ canid_t mask;
+ unsigned long matches;
+ void (*func)(struct sk_buff *, void *);
+ void *data;
+ char *ident;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+struct s_stats {
+ unsigned long jiffies_init;
+
+ unsigned long rx_frames;
+ unsigned long tx_frames;
+ unsigned long matches;
+
+ unsigned long total_rx_rate;
+ unsigned long total_tx_rate;
+ unsigned long total_rx_match_ratio;
+
+ unsigned long current_rx_rate;
+ unsigned long current_tx_rate;
+ unsigned long current_rx_match_ratio;
+
+ unsigned long max_rx_rate;
+ unsigned long max_tx_rate;
+ unsigned long max_rx_match_ratio;
+
+ unsigned long rx_frames_delta;
+ unsigned long tx_frames_delta;
+ unsigned long matches_delta;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,532 @@
+/*
+ * proc.c - procfs support for Protocol family CAN core module
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/can/core.h>
+
+#include "af_can.h"
+
+/*
+ * proc filenames for the PF_CAN core
+ */
+
+#define CAN_PROC_VERSION "version"
+#define CAN_PROC_STATS "stats"
+#define CAN_PROC_RESET_STATS "reset_stats"
+#define CAN_PROC_RCVLIST_ALL "rcvlist_all"
+#define CAN_PROC_RCVLIST_FIL "rcvlist_fil"
+#define CAN_PROC_RCVLIST_INV "rcvlist_inv"
+#define CAN_PROC_RCVLIST_SFF "rcvlist_sff"
+#define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
+#define CAN_PROC_RCVLIST_ERR "rcvlist_err"
+
+static struct proc_dir_entry *can_dir;
+static struct proc_dir_entry *pde_version;
+static struct proc_dir_entry *pde_stats;
+static struct proc_dir_entry *pde_reset_stats;
+static struct proc_dir_entry *pde_rcvlist_all;
+static struct proc_dir_entry *pde_rcvlist_fil;
+static struct proc_dir_entry *pde_rcvlist_inv;
+static struct proc_dir_entry *pde_rcvlist_sff;
+static struct proc_dir_entry *pde_rcvlist_eff;
+static struct proc_dir_entry *pde_rcvlist_err;
+
+static int user_reset;
+
+static const char rx_list_name[][8] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_stat_update() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+void can_stat_update(unsigned long data)
+{
+ unsigned long j = jiffies; /* snapshot */
+
+ /* restart counting in timer context on user request */
+ if (user_reset)
+ can_init_stats();
+
+ /* restart counting on jiffies overflow */
+ if (j < stats.jiffies_init)
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.tx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+static void can_remove_proc_readentry(const char *name)
+{
+ if (can_dir)
+ remove_proc_entry(name, can_dir);
+}
+
+/*
+ * can_init_proc - create main CAN proc directory and procfs entries
+ */
+void can_init_proc(void)
+{
+ /* create /proc/net/can directory */
+ can_dir = proc_mkdir("can", init_net.proc_net);
+
+ if (!can_dir) {
+ printk(KERN_INFO "can: failed to create /proc/net/can . "
+ "CONFIG_PROC_FS missing?\n");
+ return;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * can_remove_proc - remove procfs entries and main CAN proc directory
+ */
+void can_remove_proc(void)
+{
+ if (pde_version)
+ can_remove_proc_readentry(CAN_PROC_VERSION);
+
+ if (pde_stats)
+ can_remove_proc_readentry(CAN_PROC_STATS);
+
+ if (pde_reset_stats)
+ can_remove_proc_readentry(CAN_PROC_RESET_STATS);
+
+ if (pde_rcvlist_err)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
+
+ if (pde_rcvlist_all)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
+
+ if (pde_rcvlist_fil)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
+
+ if (pde_rcvlist_inv)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
+
+ if (pde_rcvlist_eff)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
+
+ if (pde_rcvlist_sff)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
+
+ if (can_dir)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply
* [PATCH 4/7] CAN: Add broadcast manager (bcm) protocol
From: Urs Thuermann @ 2007-10-05 10:49 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, Arnaldo Carvalho de Melo, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
In-Reply-To: <20071005104905.20150.0@janus.isnogud.escape.de>
[-- Attachment #1: 04-can-bcm-proto.diff --]
[-- Type: text/plain, Size: 49805 bytes --]
This patch adds the CAN broadcast manager (bcm) protocol.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can/bcm.h | 65 +
net/can/Kconfig | 13
net/can/Makefile | 3
net/can/bcm.c | 1763 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1844 insertions(+)
Index: net-2.6.24/include/linux/can/bcm.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/bcm.h 2007-10-05 11:13:17.000000000 +0200
@@ -0,0 +1,65 @@
+/*
+ * linux/can/bcm.h
+ *
+ * Definitions for CAN Broadcast Manager (BCM)
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_BCM_H
+#define CAN_BCM_H
+
+/**
+ * struct bcm_msg_head - head of messages to/from the broadcast manager
+ * @opcode: opcode, see enum below.
+ * @flags: special flags, see below.
+ * @count: number of frames to send before changing interval.
+ * @ival1: interval for the first @count frames.
+ * @ival2: interval for the following frames.
+ * @can_id: CAN ID of frames to be sent or received.
+ * @nframes: number of frames appended to the message head.
+ * @frames: array of CAN frames.
+ */
+struct bcm_msg_head {
+ int opcode;
+ int flags;
+ int count;
+ struct timeval ival1, ival2;
+ canid_t can_id;
+ int nframes;
+ struct can_frame frames[0];
+};
+
+enum {
+ TX_SETUP = 1, /* create (cyclic) transmission task */
+ TX_DELETE, /* remove (cyclic) transmission task */
+ TX_READ, /* read properties of (cyclic) transmission task */
+ TX_SEND, /* send one CAN frame */
+ RX_SETUP, /* create RX content filter subscription */
+ RX_DELETE, /* remove RX content filter subscription */
+ RX_READ, /* read properties of RX content filter subscription */
+ TX_STATUS, /* reply to TX_READ request */
+ TX_EXPIRED, /* notification on performed transmissions (count=0) */
+ RX_STATUS, /* reply to RX_READ request */
+ RX_TIMEOUT, /* cyclic message is absent */
+ RX_CHANGED /* updated CAN frame (detected content change) */
+};
+
+#define SETTIMER 0x0001
+#define STARTTIMER 0x0002
+#define TX_COUNTEVT 0x0004
+#define TX_ANNOUNCE 0x0008
+#define TX_CP_CAN_ID 0x0010
+#define RX_FILTER_ID 0x0020
+#define RX_CHECK_DLC 0x0040
+#define RX_NO_AUTOTIMER 0x0080
+#define RX_ANNOUNCE_RESUME 0x0100
+#define TX_RESET_MULTI_IDX 0x0200
+#define RX_RTR_FRAME 0x0400
+
+#endif /* CAN_BCM_H */
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- net-2.6.24.orig/net/can/Kconfig 2007-10-05 11:12:35.000000000 +0200
+++ net-2.6.24/net/can/Kconfig 2007-10-05 11:13:49.000000000 +0200
@@ -27,6 +27,19 @@
socket has several filter options e.g. ID masking / error frames.
To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
+config CAN_BCM
+ tristate "Broadcast Manager CAN Protocol (with content filtering)"
+ depends on CAN
+ default N
+ ---help---
+ The Broadcast Manager offers content filtering, timeout monitoring,
+ sending of RTR frames, and cyclic CAN messages without permanent user
+ interaction. The BCM can be 'programmed' via the BSD socket API and
+ informs you on demand e.g. only on content updates / timeouts.
+ You probably want to use the bcm socket in most cases where cyclic
+ CAN messages are used on the bus (e.g. in automotive environments).
+ To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+
config CAN_DEBUG_CORE
bool "CAN Core debugging messages"
depends on CAN
Index: net-2.6.24/net/can/Makefile
===================================================================
--- net-2.6.24.orig/net/can/Makefile 2007-10-05 11:11:41.000000000 +0200
+++ net-2.6.24/net/can/Makefile 2007-10-05 11:13:17.000000000 +0200
@@ -7,3 +7,6 @@
obj-$(CONFIG_CAN_RAW) += can-raw.o
can-raw-objs := raw.o
+
+obj-$(CONFIG_CAN_BCM) += can-bcm.o
+can-bcm-objs := bcm.o
Index: net-2.6.24/net/can/bcm.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/bcm.c 2007-10-05 11:13:17.000000000 +0200
@@ -0,0 +1,1763 @@
+/*
+ * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/bcm.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+/* use of last_frames[index].can_dlc */
+#define RX_RECV 0x40 /* received data for this element */
+#define RX_THR 0x80 /* element not been sent due to throttle feature */
+#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
+
+/* get best masking value for can_rx_register() for a given single can_id */
+#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
+
+#define CAN_BCM_VERSION CAN_VERSION
+static __initdata const char banner[] = KERN_INFO
+ "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+#define DBG_PREFIX "can-bcm"
+#define DBG_VAR bcm_debug
+static int bcm_debug;
+module_param_named(debug, bcm_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+/* easy access to can_frame payload */
+static inline u64 GET_U64(const struct can_frame *cp)
+{
+ return *(u64 *)cp->data;
+}
+
+struct bcm_op {
+ struct list_head list;
+ int ifindex;
+ canid_t can_id;
+ int flags;
+ unsigned long j_ival1, j_ival2, j_lastmsg;
+ unsigned long frames_abs, frames_filtered;
+ struct timer_list timer, thrtimer;
+ struct timeval ival1, ival2;
+ ktime_t rx_stamp;
+ int rx_ifindex;
+ int count;
+ int nframes;
+ int currframe;
+ struct can_frame *frames;
+ struct can_frame *last_frames;
+ struct can_frame sframe;
+ struct can_frame last_sframe;
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+};
+
+static struct proc_dir_entry *proc_dir;
+
+struct bcm_sock {
+ struct sock sk;
+ int bound;
+ int ifindex;
+ struct notifier_block notifier;
+ struct list_head rx_ops;
+ struct list_head tx_ops;
+ unsigned long dropped_usr_msgs;
+ struct proc_dir_entry *bcm_proc_read;
+ char procname [9]; /* pointer printed in ASCII with \0 */
+};
+
+static inline struct bcm_sock *bcm_sk(const struct sock *sk)
+{
+ return (struct bcm_sock *)sk;
+}
+
+#define CFSIZ sizeof(struct can_frame)
+#define OPSIZ sizeof(struct bcm_op)
+#define MHSIZ sizeof(struct bcm_msg_head)
+
+/*
+ * rounded_tv2jif - calculate jiffies from timeval including optional up
+ * @tv: pointer to timeval
+ *
+ * Description:
+ * Unlike timeval_to_jiffies() provided in include/linux/jiffies.h, this
+ * function is intentionally more relaxed on precise timer ticks to get
+ * exact one jiffy for requested 1000us on a 1000HZ machine.
+ * This code is to be removed when upgrading to kernel hrtimer.
+ *
+ * Return:
+ * calculated jiffies (max: ULONG_MAX)
+ */
+static unsigned long rounded_tv2jif(const struct timeval *tv)
+{
+ unsigned long sec = tv->tv_sec;
+ unsigned long usec = tv->tv_usec;
+ unsigned long jif;
+
+ if (sec > ULONG_MAX / HZ)
+ return ULONG_MAX;
+
+ /* round up to get at least the requested time */
+ usec += 1000000 / HZ - 1;
+
+ jif = usec / (1000000 / HZ);
+
+ if (sec * HZ > ULONG_MAX - jif)
+ return ULONG_MAX;
+
+ return jif + sec * HZ;
+}
+
+/*
+ * procfs functions
+ */
+static char *bcm_proc_getifname(int ifindex)
+{
+ struct net_device *dev;
+
+ if (!ifindex)
+ return "any";
+
+ dev = __dev_get_by_index(&init_net, ifindex); /* no usage counting */
+ if (dev)
+ return dev->name;
+
+ return "???";
+}
+
+static int bcm_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct sock *sk = (struct sock *)data;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+
+ len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p",
+ sk->sk_socket);
+ len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo);
+ len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
+ bo->dropped_usr_msgs);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
+ bcm_proc_getifname(bo->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+
+ list_for_each_entry(op, &bo->rx_ops, list) {
+
+ unsigned long reduction;
+
+ /* print only active entries & prevent division by zero */
+ if (!op->frames_abs)
+ continue;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "rx_op: %03X %-5s ",
+ op->can_id, bcm_proc_getifname(op->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
+ op->nframes,
+ (op->flags & RX_CHECK_DLC)?'d':' ');
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "timeo=%ld ", op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "thr=%ld ", op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "# recv %ld (%ld) => reduction: ",
+ op->frames_filtered, op->frames_abs);
+
+ reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n",
+ (reduction == 100)?"near ":"", reduction);
+
+ if (len > PAGE_SIZE - 200) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ list_for_each_entry(op, &bo->tx_ops, list) {
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "tx_op: %03X %s [%d] ",
+ op->can_id, bcm_proc_getifname(op->ifindex),
+ op->nframes);
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ",
+ op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ",
+ op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
+ op->frames_abs);
+
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+ */
+static void bcm_can_tx(struct bcm_op *op)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct can_frame *cf = &op->frames[op->currframe];
+
+ DBG_FRAME("BCM: bcm_can_tx: sending frame", cf);
+
+ /* no target device? => exit */
+ if (!op->ifindex)
+ return;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (!dev) {
+ /* RFC: should this bcm_op remove itself here? */
+ return;
+ }
+
+ skb = alloc_skb(CFSIZ, gfp_any());
+ if (!skb)
+ goto out;
+
+ memcpy(skb_put(skb, CFSIZ), cf, CFSIZ);
+
+ /* send with loopback */
+ skb->dev = dev;
+ skb->sk = op->sk;
+ can_send(skb, 1);
+
+ /* update statistics */
+ op->currframe++;
+ op->frames_abs++;
+
+ /* reached last frame? */
+ if (op->currframe >= op->nframes)
+ op->currframe = 0;
+ out:
+ dev_put(dev);
+}
+
+/*
+ * bcm_send_to_user - send a BCM message to the userspace
+ * (consisting of bcm_msg_head + x CAN frames)
+ */
+static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
+ struct can_frame *frames, int has_timestamp)
+{
+ struct sk_buff *skb;
+ struct can_frame *firstframe;
+ struct sockaddr_can *addr;
+ struct sock *sk = op->sk;
+ int datalen = head->nframes * CFSIZ;
+ int err;
+
+ skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
+ if (!skb)
+ return;
+
+ memcpy(skb_put(skb, sizeof(*head)), head, sizeof(*head));
+
+ if (head->nframes) {
+ /* can_frames starting here */
+ firstframe = (struct can_frame *) skb_tail_pointer(skb);
+
+ memcpy(skb_put(skb, datalen), frames, datalen);
+
+ /*
+ * the BCM uses the can_dlc-element of the can_frame
+ * structure for internal purposes. This is only
+ * relevant for updates that are generated by the
+ * BCM, where nframes is 1
+ */
+ if (head->nframes == 1)
+ firstframe->can_dlc &= BCM_CAN_DLC_MASK;
+ }
+
+ if (has_timestamp) {
+ /* restore rx timestamp */
+ skb->tstamp = op->rx_stamp;
+ }
+
+ /*
+ * Put the datagram to the queue so that bcm_recvmsg() can
+ * get it from there. We need to pass the interface index to
+ * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
+ * containing the interface index.
+ */
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
+ addr = (struct sockaddr_can *)skb->cb;
+ memset(addr, 0, sizeof(*addr));
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = op->rx_ifindex;
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err < 0) {
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ DBG("sock_queue_rcv_skb failed: %d\n", err);
+ kfree_skb(skb);
+ /* don't care about overflows in this statistic */
+ bo->dropped_usr_msgs++;
+ }
+}
+
+/*
+ * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
+ */
+static void bcm_tx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+
+ DBG("Called with bcm_op %p\n", op);
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ op->count--;
+ if (!op->count && (op->flags & TX_COUNTEVT)) {
+ struct bcm_msg_head msg_head;
+
+ /* create notification to user */
+ DBG("sending TX_EXPIRED for can_id %03X\n",
+ op->can_id);
+
+ msg_head.opcode = TX_EXPIRED;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+ }
+ }
+
+ DBG("count=%d j_ival1=%ld j_ival2=%ld\n",
+ op->count, op->j_ival1, op->j_ival2);
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ op->timer.expires = jiffies + op->j_ival1;
+ add_timer(&op->timer);
+
+ DBG("adding timer ival1. func=%p data=%p exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+
+ } else {
+ if (op->j_ival2) {
+ op->timer.expires = jiffies + op->j_ival2;
+ add_timer(&op->timer);
+
+ DBG("adding timer ival2. func=%p data=%p exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+
+ } else
+ DBG("no timer restart\n");
+ }
+
+ return;
+}
+
+/*
+ * bcm_rx_changed - create a RX_CHANGED notification due to changed content
+ */
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
+{
+ struct bcm_msg_head head;
+
+ op->j_lastmsg = jiffies;
+
+ /* update statistics */
+ op->frames_filtered++;
+
+ /* prevent statistics overflow */
+ if (op->frames_filtered > ULONG_MAX/100)
+ op->frames_filtered = op->frames_abs = 0;
+
+ DBG("setting j_lastmsg to 0x%08X for rx_op %p\n",
+ (unsigned int) op->j_lastmsg, op);
+ DBG("sending notification\n");
+
+ head.opcode = RX_CHANGED;
+ head.flags = op->flags;
+ head.count = op->count;
+ head.ival1 = op->ival1;
+ head.ival2 = op->ival2;
+ head.can_id = op->can_id;
+ head.nframes = 1;
+
+ bcm_send_to_user(op, &head, data, 1);
+}
+
+/*
+ * bcm_rx_update_and_send - process a detected relevant receive content change
+ * 1. update the last received data
+ * 2. send a notification to the user (if possible)
+ */
+static void bcm_rx_update_and_send(struct bcm_op *op,
+ struct can_frame *lastdata,
+ struct can_frame *rxdata)
+{
+ unsigned long nexttx = op->j_lastmsg + op->j_ival2;
+
+ memcpy(lastdata, rxdata, CFSIZ);
+
+ /* mark as used */
+ lastdata->can_dlc |= RX_RECV;
+
+ /* throttle bcm_rx_changed ? */
+ if ((op->thrtimer.expires) ||
+ ((op->j_ival2) && (nexttx > jiffies))) {
+ /* we are already waiting OR we have to start waiting */
+
+ /* mark as 'throttled' */
+ lastdata->can_dlc |= RX_THR;
+
+ if (!(op->thrtimer.expires)) {
+ /* start the timer only the first time */
+ op->thrtimer.expires = nexttx;
+ add_timer(&op->thrtimer);
+
+ DBG("adding thrtimer. func=%p data=%p exp=0x%08X\n",
+ op->thrtimer.function,
+ (char *) op->thrtimer.data,
+ (unsigned int) op->thrtimer.expires);
+ }
+
+ } else {
+ /* send RX_CHANGED to the user immediately */
+ bcm_rx_changed(op, rxdata);
+ }
+}
+
+/*
+ * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
+ * received data stored in op->last_frames[]
+ */
+static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
+ struct can_frame *rxdata)
+{
+ /*
+ * no one uses the MSBs of can_dlc for comparation,
+ * so we use it here to detect the first time of reception
+ */
+
+ if (!(op->last_frames[index].can_dlc & RX_RECV)) {
+ /* received data for the first time => send update to user */
+ DBG("first time :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ /* do a real check in can_frame data section */
+
+ DBG("op->frames[index].data = 0x%016llx\n",
+ GET_U64(&op->frames[index]));
+ DBG("op->last_frames[index].data = 0x%016llx\n",
+ GET_U64(&op->last_frames[index]));
+ DBG("rxdata->data = 0x%016llx\n", GET_U64(rxdata));
+
+ if ((GET_U64(&op->frames[index]) & GET_U64(rxdata)) !=
+ (GET_U64(&op->frames[index]) & GET_U64(&op->last_frames[index]))) {
+ DBG("relevant data change :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ if (op->flags & RX_CHECK_DLC) {
+ /* do a real check in can_frame dlc */
+ if (rxdata->can_dlc != (op->last_frames[index].can_dlc &
+ BCM_CAN_DLC_MASK)) {
+ DBG("dlc change :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index],
+ rxdata);
+ return;
+ }
+ }
+ DBG("no relevant change :(\n");
+}
+
+/*
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ */
+static void bcm_rx_starttimer(struct bcm_op *op)
+{
+ if (op->flags & RX_NO_AUTOTIMER)
+ return;
+
+ if (op->j_ival1) {
+ op->timer.expires = jiffies + op->j_ival1;
+
+ DBG("adding rx timeout timer ival1. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ add_timer(&op->timer);
+ }
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static void bcm_rx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct bcm_msg_head msg_head;
+
+ DBG("sending RX_TIMEOUT for can_id %03X. op is %p\n", op->can_id, op);
+
+ msg_head.opcode = RX_TIMEOUT;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+
+ /* no restart of the timer is done here! */
+
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+ /* clear received can_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, op->nframes * CFSIZ);
+ DBG("RX_ANNOUNCE_RESTART\n");
+ }
+}
+
+/*
+ * bcm_rx_thr_handler - the time for blocked content updates is over now:
+ * Check for throttled data and send it to the userspace
+ */
+static void bcm_rx_thr_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ int i = 0;
+
+ /* mark disabled / consumed timer */
+ op->thrtimer.expires = 0;
+
+ if (op->nframes > 1) {
+ DBG("sending MUX RX_CHANGED for can_id %03X. op is %p\n",
+ op->can_id, op);
+ /* for MUX filter we start at index 1 */
+ for (i = 1; i < op->nframes; i++) {
+ if ((op->last_frames) &&
+ (op->last_frames[i].can_dlc & RX_THR)) {
+ op->last_frames[i].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[i]);
+ }
+ }
+
+ } else {
+ DBG("sending simple RX_CHANGED for can_id %03X. op is %p\n",
+ op->can_id, op);
+ /* for RX_FILTER_ID and simple filter */
+ if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) {
+ op->last_frames[0].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[0]);
+ }
+ }
+}
+
+/*
+ * bcm_rx_handler - handle a CAN frame receiption
+ */
+static void bcm_rx_handler(struct sk_buff *skb, void *data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct can_frame rxframe;
+ int i;
+
+ /* disable timeout */
+ del_timer(&op->timer);
+
+ DBG("Called with bcm_op %p\n", op);
+
+ if (skb->len == sizeof(rxframe)) {
+ memcpy(&rxframe, skb->data, sizeof(rxframe));
+ /* save rx timestamp */
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+ /* update statistics */
+ op->frames_abs++;
+ kfree_skb(skb);
+ DBG("got can_frame with can_id %03X\n", rxframe.can_id);
+
+ } else {
+ DBG("Wrong skb->len = %d\n", skb->len);
+ kfree_skb(skb);
+ return;
+ }
+
+ DBG_FRAME("BCM: bcm_rx_handler: CAN frame", &rxframe);
+
+ if (op->can_id != rxframe.can_id) {
+ DBG("ERROR! Got wrong can_id %03X! Expected %03X.\n",
+ rxframe.can_id, op->can_id);
+ return;
+ }
+
+ if (op->flags & RX_RTR_FRAME) {
+ /* send reply for RTR-request */
+ DBG("RTR-request\n");
+
+ /* send op->frames[0] to CAN device */
+ bcm_can_tx(op);
+ return;
+ }
+
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ DBG("Easy does it with RX_FILTER_ID\n");
+
+ bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes == 1) {
+ /* simple compare with index 0 */
+ DBG("Simple compare\n");
+
+ bcm_rx_cmp_to_index(op, 0, &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes > 1) {
+ /* multiplex compare */
+ DBG("Multiplex compare\n");
+
+ /*
+ * find the first multiplex mask that fits.
+ * Remark: The MUX-mask is stored in index 0
+ */
+
+ for (i = 1; i < op->nframes; i++) {
+ if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+ (GET_U64(&op->frames[0]) &
+ GET_U64(&op->frames[i]))) {
+ DBG("found MUX index %d\n", i);
+ bcm_rx_cmp_to_index(op, i, &rxframe);
+ break;
+ }
+ }
+ bcm_rx_starttimer(op);
+ }
+}
+
+/*
+ * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
+ */
+static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id,
+ int ifindex)
+{
+ struct bcm_op *op;
+
+ list_for_each_entry(op, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex))
+ return op;
+ }
+
+ return NULL;
+}
+
+static void bcm_remove_op(struct bcm_op *op)
+{
+ del_timer(&op->timer);
+ del_timer(&op->thrtimer);
+
+ if ((op->frames) && (op->frames != &op->sframe))
+ kfree(op->frames);
+
+ if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+ kfree(op->last_frames);
+
+ kfree(op);
+
+ return;
+}
+
+static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+{
+ if (op->rx_reg_dev == dev) {
+ can_rx_unregister(dev, op->can_id, REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ /* mark as removed subscription */
+ op->rx_reg_dev = NULL;
+ } else
+ printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+ "mismatch %p %p\n", op->rx_reg_dev, dev);
+}
+
+/*
+ * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
+ */
+static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+ DBG("removing rx_op %p for can_id %03X\n",
+ op, op->can_id);
+
+ /*
+ * Don't care if we're bound or not (due to netdev
+ * problems) can_rx_unregister() is always a save
+ * thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net,
+ op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
+ */
+static int bcm_delete_tx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+ DBG("removing rx_op %p for can_id %03X\n",
+ op, op->can_id);
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
+ */
+static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
+ int ifindex)
+{
+ struct bcm_op *op = bcm_find_op(ops, msg_head->can_id, ifindex);
+
+ if (!op) {
+ DBG("TRX_READ: did not find op for can_id %03X\n",
+ msg_head->can_id);
+ return -EINVAL;
+ }
+
+ DBG("TRX_READ: sending status for can_id %03X\n",
+ msg_head->can_id);
+ /* put current values into msg_head */
+ msg_head->flags = op->flags;
+ msg_head->count = op->count;
+ msg_head->ival1 = op->ival1;
+ msg_head->ival2 = op->ival2;
+ msg_head->nframes = op->nframes;
+
+ bcm_send_to_user(op, msg_head, op->frames, 0);
+
+ return MHSIZ;
+}
+
+/*
+ * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
+ */
+static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int i, err;
+
+ /* we need a real device to send frames */
+ if (!ifindex)
+ return -ENODEV;
+
+ /* we need at least one can_frame */
+ if (msg_head->nframes < 1)
+ return -EINVAL;
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex);
+
+ if (op) {
+ /* update existing BCM operation */
+
+ DBG("TX_SETUP: modifying existing tx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ /* update can_frames content */
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+ if (err < 0)
+ return err;
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ DBG("TX_SETUP: creating new tx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ op->can_id = msg_head->can_id;
+
+ /* create array for can_frames and copy the data */
+ if (msg_head->nframes > 1) {
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+ } else
+ op->frames = &op->sframe;
+
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ kfree(op);
+ return err;
+ }
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ /* tx_ops never compare with previous received messages */
+ op->last_frames = NULL;
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
+
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kmalloc) structure */
+ init_timer(&op->timer);
+
+ /* currently unused in tx_ops */
+ init_timer(&op->thrtimer);
+
+ /* handler for tx_ops */
+ op->timer.function = bcm_tx_timeout_handler;
+
+ /* timer.data points to this op-structure */
+ op->timer.data = (unsigned long)op;
+
+ /* add this bcm_op to the list of the tx_ops */
+ list_add(&op->list, &bo->tx_ops);
+
+ } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
+
+ if (op->nframes != msg_head->nframes) {
+ op->nframes = msg_head->nframes;
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ /* check flags */
+
+ op->flags = msg_head->flags;
+
+ if (op->flags & TX_RESET_MULTI_IDX) {
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ if (op->flags & SETTIMER) {
+ /* set timer values */
+
+ op->count = msg_head->count;
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ DBG("TX_SETUP: SETTIMER count=%d j_ival1=%ld j_ival2=%ld\n",
+ op->count, op->j_ival1, op->j_ival2);
+
+ /* disable an active timer due to zero values? */
+ if (!op->j_ival1 && !op->j_ival2) {
+ del_timer(&op->timer);
+ DBG("TX_SETUP: SETTIMER disabled timer.\n");
+ }
+ }
+
+ if ((op->flags & STARTTIMER) &&
+ ((op->j_ival1 && op->count) || op->j_ival2)) {
+
+ del_timer(&op->timer);
+
+ /* spec: send can_frame when starting timer */
+ op->flags |= TX_ANNOUNCE;
+
+ if (op->j_ival1 && (op->count > 0)) {
+ op->timer.expires = jiffies + op->j_ival1;
+ /* op->count-- is done in bcm_tx_timeout_handler */
+ DBG("TX_SETUP: adding timer ival1. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ } else {
+ op->timer.expires = jiffies + op->j_ival2;
+ DBG("TX_SETUP: adding timer ival2. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+ }
+
+ add_timer(&op->timer);
+ }
+
+ if (op->flags & TX_ANNOUNCE)
+ bcm_can_tx(op);
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int do_rx_register;
+ int err = 0;
+
+ if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+ /* be robust against wrong usage ... */
+ msg_head->flags |= RX_FILTER_ID;
+ msg_head->nframes = 0; /* ignore trailing garbage */
+ }
+
+ if ((msg_head->flags & RX_RTR_FRAME) &&
+ ((msg_head->nframes != 1) ||
+ (!(msg_head->can_id & CAN_RTR_FLAG)))) {
+
+ DBG("RX_SETUP: bad RX_RTR_FRAME setup!\n");
+ return -EINVAL;
+ }
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex);
+ if (op) {
+ /* update existing BCM operation */
+
+ DBG("RX_SETUP: modifying existing rx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ if (msg_head->nframes) {
+ /* update can_frames content */
+ err = memcpy_fromiovec((u8 *)op->frames,
+ msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0)
+ return err;
+
+ /* clear last_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, msg_head->nframes * CFSIZ);
+ }
+
+ op->nframes = msg_head->nframes;
+
+ /* Only an update -> do not call can_rx_register() */
+ do_rx_register = 0;
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ DBG("RX_SETUP: creating new rx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ op->can_id = msg_head->can_id;
+ op->nframes = msg_head->nframes;
+
+ if (msg_head->nframes > 1) {
+ /* create array for can_frames and copy the data */
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ /* create and init array for received can_frames */
+ op->last_frames = kzalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->last_frames) {
+ kfree(op->frames);
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ } else {
+ op->frames = &op->sframe;
+ op->last_frames = &op->last_sframe;
+ }
+
+ if (msg_head->nframes) {
+ err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ if (op->last_frames != &op->last_sframe)
+ kfree(op->last_frames);
+ kfree(op);
+ return err;
+ }
+ }
+
+ op->sk = sk;
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+ init_timer(&op->timer);
+
+ /* init throttle timer for RX_CHANGED */
+ init_timer(&op->thrtimer);
+
+ /* handler for rx timeouts */
+ op->timer.function = bcm_rx_timeout_handler;
+
+ /* timer.data points to this op-structure */
+ op->timer.data = (unsigned long)op;
+
+ /* handler for RX_CHANGED throttle timeouts */
+ op->thrtimer.function = bcm_rx_thr_handler;
+
+ /* timer.data points to this op-structure */
+ op->thrtimer.data = (unsigned long)op;
+
+ /* mark disabled timer */
+ op->thrtimer.expires = 0;
+
+ /* add this bcm_op to the list of the rx_ops */
+ list_add(&op->list, &bo->rx_ops);
+
+ /* call can_rx_register() */
+ do_rx_register = 1;
+
+ } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+ /* check flags */
+ op->flags = msg_head->flags;
+
+ if (op->flags & RX_RTR_FRAME) {
+
+ /* no timers in RTR-mode */
+ del_timer(&op->thrtimer);
+ del_timer(&op->timer);
+
+ /*
+ * funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ */
+ if ((op->flags & TX_CP_CAN_ID) ||
+ (op->frames[0].can_id == op->can_id))
+ op->frames[0].can_id = op->can_id & ~CAN_RTR_FLAG;
+
+ } else {
+ if (op->flags & SETTIMER) {
+
+ /* set timer value */
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ DBG("RX_SETUP: SETTIMER j_ival1=%ld j_ival2=%ld\n",
+ op->j_ival1, op->j_ival2);
+
+ /* disable an active timer due to zero value? */
+ if (!op->j_ival1) {
+ del_timer(&op->timer);
+ DBG("RX_SETUP: disabled timer rx timeouts.\n");
+ }
+
+ /* free currently blocked msgs ? */
+ if (op->thrtimer.expires) {
+ DBG("RX_SETUP: unblocking throttled msgs.\n");
+ del_timer(&op->thrtimer);
+ /* send blocked msgs hereafter */
+ op->thrtimer.expires = jiffies + 2;
+ add_timer(&op->thrtimer);
+ }
+ /*
+ * if (op->j_ival2) is zero, no (new) throttling
+ * will happen. For details see functions
+ * bcm_rx_update_and_send() and bcm_rx_thr_handler()
+ */
+ }
+
+ if ((op->flags & STARTTIMER) && op->j_ival1) {
+
+ del_timer(&op->timer);
+ op->timer.expires = jiffies + op->j_ival1;
+
+ DBG("RX_SETUP: adding timer ival1. func=%p data=%p"
+ " exp=0x%08X\n",
+ (char *) op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ add_timer(&op->timer);
+ }
+ }
+
+ /* now we can register for can_ids, if we added a new bcm_op */
+ if (do_rx_register) {
+ DBG("RX_SETUP: can_rx_register() for can_id %03X. "
+ "rx_op is %p\n", op->can_id, op);
+
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (dev) {
+ err = can_rx_register(dev, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op,
+ "bcm");
+
+ op->rx_reg_dev = dev;
+ dev_put(dev);
+ }
+
+ } else
+ err = can_rx_register(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op, "bcm");
+ if (err) {
+ /* this bcm rx op is broken -> remove it */
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return err;
+ }
+ }
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
+ */
+static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ int err;
+
+ /* just copy and send one can_frame */
+
+ if (!ifindex) /* we need a real device to send frames */
+ return -ENODEV;
+
+ skb = alloc_skb(CFSIZ, GFP_KERNEL);
+
+ if (!skb)
+ return -ENOMEM;
+
+ err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ DBG_FRAME("BCM: TX_SEND: sending frame",
+ (struct can_frame *)skb->data);
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ skb->dev = dev;
+ skb->sk = sk;
+ can_send(skb, 1); /* send with loopback */
+ dev_put(dev);
+
+ return CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_sendmsg - process BCM commands (opcodes) from the userspace
+ */
+static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+ struct bcm_msg_head msg_head;
+ int ret; /* read bytes or error codes as return value */
+
+ if (!bo->bound) {
+ DBG("sock %p not bound\n", sk);
+ return -ENOTCONN;
+ }
+
+ /* check for alternative ifindex for this bcm_op */
+
+ if (!ifindex && msg->msg_name) {
+ /* no bound device as default => check msg_name */
+ struct sockaddr_can *addr =
+ (struct sockaddr_can *)msg->msg_name;
+
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ ifindex = addr->can_ifindex; /* ifindex from sendto() */
+
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ DBG("device %d not found\n", ifindex);
+ return -ENODEV;
+ }
+
+ if (dev->type != ARPHRD_CAN) {
+ DBG("device %d no CAN device\n", ifindex);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ dev_put(dev);
+ }
+ }
+
+ /* read message head information */
+
+ ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ);
+ if (ret < 0)
+ return ret;
+
+ DBG("opcode %d for can_id %03X\n", msg_head.opcode, msg_head.can_id);
+
+ lock_sock(sk);
+
+ switch (msg_head.opcode) {
+
+ case TX_SETUP:
+ ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case RX_SETUP:
+ ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case TX_DELETE:
+ if (bcm_delete_tx_op(&bo->tx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case RX_DELETE:
+ if (bcm_delete_rx_op(&bo->rx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case TX_READ:
+ /* reuse msg_head for the reply to TX_READ */
+ msg_head.opcode = TX_STATUS;
+ ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
+ break;
+
+ case RX_READ:
+ /* reuse msg_head for the reply to RX_READ */
+ msg_head.opcode = RX_STATUS;
+ ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
+ break;
+
+ case TX_SEND:
+ /* we need at least one can_frame */
+ if (msg_head.nframes < 1)
+ ret = -EINVAL;
+ else
+ ret = bcm_tx_send(msg, ifindex, sk);
+ break;
+
+ default:
+ DBG("Unknown opcode %d\n", msg_head.opcode);
+ ret = -EINVAL;
+ break;
+ }
+
+ release_sock(sk);
+
+ return ret;
+}
+
+/*
+ * notification handler for netdevice status changes
+ */
+static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
+ void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
+ struct sock *sk = &bo->sk;
+ struct bcm_op *op;
+ int notify_enodev = 0;
+
+ DBG("msg %ld for dev %p (%s idx %d) sk %p bo->ifindex %d\n",
+ msg, dev, dev->name, dev->ifindex, sk, bo->ifindex);
+
+ if (dev->nd_net != &init_net)
+ return NOTIFY_DONE;
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+ /* remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list)
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
+ /* remove device reference, if this is our bound device */
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ notify_enodev = 1;
+ }
+
+ release_sock(sk);
+
+ if (notify_enodev) {
+ sk->sk_err = ENODEV;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ break;
+
+ case NETDEV_DOWN:
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * initial settings for all BCM sockets to be set at socket creation time
+ */
+static int bcm_init(struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ bo->bound = 0;
+ bo->ifindex = 0;
+ bo->dropped_usr_msgs = 0;
+ bo->bcm_proc_read = NULL;
+
+ INIT_LIST_HEAD(&bo->tx_ops);
+ INIT_LIST_HEAD(&bo->rx_ops);
+
+ /* set notifier */
+ bo->notifier.notifier_call = bcm_notifier;
+
+ register_netdevice_notifier(&bo->notifier);
+
+ return 0;
+}
+
+/*
+ * standard socket functions
+ */
+static int bcm_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op, *next;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ /* remove bcm_ops, timer, rx_unregister(), etc. */
+
+ unregister_netdevice_notifier(&bo->notifier);
+
+ lock_sock(sk);
+
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
+ DBG("removing tx_op %p for can_id %03X\n", op, op->can_id);
+ bcm_remove_op(op);
+ }
+
+ list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+ DBG("removing rx_op %p for can_id %03X\n", op, op->can_id);
+
+ /*
+ * Don't care if we're bound or not (due to netdev problems)
+ * can_rx_unregister() is always a save thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ bcm_remove_op(op);
+ }
+
+ /* remove procfs entry */
+ if (proc_dir && bo->bcm_proc_read)
+ remove_proc_entry(bo->procname, proc_dir);
+
+ /* remove device reference */
+ if (bo->bound) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ }
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+ int flags)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ if (bo->bound)
+ return -EISCONN;
+
+ /* bind a device to this socket */
+ if (addr->can_ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, addr->can_ifindex);
+ if (!dev) {
+ DBG("could not find device index %d\n",
+ addr->can_ifindex);
+ return -ENODEV;
+ }
+
+ if (dev->type != ARPHRD_CAN) {
+ DBG("device %d no CAN device\n", addr->can_ifindex);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ bo->ifindex = dev->ifindex;
+ dev_put(dev);
+
+ DBG("socket %p bound to device %s (idx %d)\n",
+ sock, dev->name, dev->ifindex);
+
+ } else {
+ /* no interface reference for ifindex = 0 ('any' CAN device) */
+ bo->ifindex = 0;
+ }
+
+ bo->bound = 1;
+
+ if (proc_dir) {
+ /* unique socket address as filename */
+ sprintf(bo->procname, "%p", sock);
+ bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
+ proc_dir,
+ bcm_read_proc, sk);
+ }
+
+ return 0;
+}
+
+static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int error = 0;
+ int noblock;
+ int err;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ noblock = flags & MSG_DONTWAIT;
+ flags &= ~MSG_DONTWAIT;
+ skb = skb_recv_datagram(sk, flags, noblock, &error);
+ if (!skb)
+ return error;
+
+ DBG("delivering skbuff %p\n", skb);
+ DBG_SKB(skb);
+
+ if (skb->len < size)
+ size = skb->len;
+
+ err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (err < 0) {
+ skb_free_datagram(sk, skb);
+ return err;
+ }
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (msg->msg_name) {
+ msg->msg_namelen = sizeof(struct sockaddr_can);
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ }
+
+ DBG("freeing sock %p, skbuff %p\n", sk, skb);
+ skb_free_datagram(sk, skb);
+
+ return size;
+}
+
+static struct proto_ops bcm_ops __read_mostly = {
+ .family = PF_CAN,
+ .release = bcm_release,
+ .bind = sock_no_bind,
+ .connect = bcm_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = bcm_sendmsg,
+ .recvmsg = bcm_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto bcm_proto __read_mostly = {
+ .name = "CAN_BCM",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct bcm_sock),
+ .init = bcm_init,
+};
+
+static struct can_proto bcm_can_proto __read_mostly = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_BCM,
+ .capability = -1,
+ .ops = &bcm_ops,
+ .prot = &bcm_proto,
+};
+
+static int __init bcm_module_init(void)
+{
+ int err;
+
+ printk(banner);
+
+ err = can_proto_register(&bcm_can_proto);
+ if (err < 0) {
+ printk(KERN_ERR "can: registration of bcm protocol failed\n");
+ return err;
+ }
+
+ /* create /proc/net/can-bcm directory */
+ proc_dir = proc_mkdir("can-bcm", init_net.proc_net);
+
+ if (proc_dir)
+ proc_dir->owner = THIS_MODULE;
+
+ return 0;
+}
+
+static void __exit bcm_module_exit(void)
+{
+ can_proto_unregister(&bcm_can_proto);
+
+ if (proc_dir)
+ proc_net_remove(&init_net, "can-bcm");
+}
+
+module_init(bcm_module_init);
+module_exit(bcm_module_exit);
--
^ permalink raw reply
* Re: [PATCH] ieee80211_if_set_type: make check for master dev more explicit
From: Johannes Berg @ 2007-10-05 12:01 UTC (permalink / raw)
To: John W. Linville
Cc: Daniel Drake, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20071004191301.GJ6037-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 307 bytes --]
On Thu, 2007-10-04 at 15:13 -0400, John W. Linville wrote:
> > That should work as well although I think you should update the comment
> > above :)
>
> The comment still seem applicable. How would you word it?
Whoops, you're right, I misremembered explaining the use of the pointer.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply
* e1000e oops
From: maximilian attems @ 2007-10-05 13:11 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel
Linux tau 2.6.23-rc8-mm2-686 #1 SMP Wed Oct 3 23:56:32 CEST 2007 i686
GNU/Linux
eth0 renamed to eth1
sysfs: duplicate filename 'eth1' can not be created
WARNING: at fs/sysfs/dir.c:433 sysfs_add_one()
[<c0104f9c>] dump_trace+0x68/0x1d5
[<c0105121>] show_trace_log_lvl+0x18/0x2c
[<c0105b9b>] show_trace+0xf/0x11
[<c0105c81>] dump_stack+0x12/0x14
[<c01ac02e>] sysfs_add_one+0x54/0xb8
[<c01acc2a>] sysfs_create_link+0xb3/0x125
[<c023c05f>] device_rename+0x13d/0x196
[<c025a970>] dev_change_name+0x137/0x205
[<c025b12c>] dev_ioctl+0x357/0x44f
[<c017b05f>] do_ioctl+0x1f/0x62
[<c017b2d9>] vfs_ioctl+0x237/0x249
[<c017b330>] sys_ioctl+0x45/0x5d
[<c0103eda>] syscall_call+0x7/0xb
[<b7edbde4>] 0xb7edbde4
=======================
net eth1: device_rename: sysfs_create_symlink failed (-17)
00:19.0 Ethernet controller [0200]: Intel Corporation 82801I (ICH9 Family) Gigabit Ethernet Controller [8086:294c] (rev 02)
Subsystem: Intel Corporation Unknown device [8086:0001]
Flags: bus master, fast devsel, latency 0, IRQ 218
Memory at 50380000 (32-bit, non-prefetchable) [size=128K]
Memory at 503a4000 (32-bit, non-prefetchable) [size=4K]
I/O ports at 20e0 [size=32]
Capabilities: [c8] Power Management version 2
Capabilities: [d0] Message Signalled Interrupts: Mask- 64bit+ Queue=0/0 Enable+
Capabilities: [e0] Vendor Specific Information
no idea if aboves belongs to netdev or sysfs dep.
--
maks
^ permalink raw reply
* Re: (2.6.18.8) tg3: eth0: The system may be re-ordering memory-mapped I/O cycles to the network device, attempting to recover.
From: Michael Chan @ 2007-10-05 14:16 UTC (permalink / raw)
To: NetArt - Grzegorz Nosek; +Cc: netdev
In-Reply-To: <20071005095722.GA23224@tech.serwery.pl>
NetArt - Grzegorz Nosek wrote:
>
> I have encountered an error which looks tg3-related. Upon adding some
> htb queue rules (which I don't have handy ATM but can provide if
> needed), after some time we get such messages in the kernel log:
>
Please provide the HTB queue rules and the type of traffic to make
this happen. Thanks.
^ permalink raw reply
* [PATCH 1/5] rtnl_unlock cleanups
From: Denis V. Lunev @ 2007-10-05 14:44 UTC (permalink / raw)
To: davem; +Cc: devel, containers, kuznet, netdev
There is no need to process outstanding netlink user->kernel packets
during rtnl_unlock now. There is no rtnl_trylock in the rtnetlink_rcv
anymore.
Normal code path is the following:
netlink_sendmsg
netlink_unicast
netlink_sendskb
skb_queue_tail
netlink_data_ready
rtnetlink_rcv
mutex_lock(&rtnl_mutex);
netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
mutex_unlock(&rtnl_mutex);
So, it is possible, that packets can be present in the rtnl->sk_receive_queue
during rtnl_unlock, but there is no need to process them at that moment as
rtnetlink_rcv for that packet is pending.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
--- ./net/core/rtnetlink.c.rtnl2 2007-08-26 19:30:38.000000000 +0400
+++ ./net/core/rtnetlink.c 2007-10-01 13:09:03.000000000 +0400
@@ -75,8 +75,6 @@ void __rtnl_unlock(void)
void rtnl_unlock(void)
{
mutex_unlock(&rtnl_mutex);
- if (rtnl && rtnl->sk_receive_queue.qlen)
- rtnl->sk_data_ready(rtnl, 0);
netdev_run_todo();
}
@@ -1319,11 +1317,9 @@ static void rtnetlink_rcv(struct sock *s
unsigned int qlen = 0;
do {
- mutex_lock(&rtnl_mutex);
+ rtnl_lock();
qlen = netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
- mutex_unlock(&rtnl_mutex);
-
- netdev_run_todo();
+ rtnl_unlock();
} while (qlen);
}
^ permalink raw reply
* [PATCH 2/5] make netlink processing routines semi-synchronious (inspired by rtnl) v2
From: Denis V. Lunev @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem; +Cc: netdev, kuznet
The code in netfilter/nfnetlink.c and in ./net/netlink/genetlink.c looks
like outdated copy/paste from rtnetlink.c. Push them into sync with the
original.
Changes from v1:
- deleted comment in nfnetlink_rcv_msg by request of Patrick McHardy
Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Patrick McHardy <kaber@trash.net>
--- ./net/netfilter/nfnetlink.c.nlk3 2007-10-01 09:47:53.000000000 +0400
+++ ./net/netfilter/nfnetlink.c 2007-10-01 17:13:09.000000000 +0400
@@ -44,26 +44,14 @@ static struct sock *nfnl = NULL;
static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
static DEFINE_MUTEX(nfnl_mutex);
-static void nfnl_lock(void)
+static inline void nfnl_lock(void)
{
mutex_lock(&nfnl_mutex);
}
-static int nfnl_trylock(void)
-{
- return !mutex_trylock(&nfnl_mutex);
-}
-
-static void __nfnl_unlock(void)
-{
- mutex_unlock(&nfnl_mutex);
-}
-
-static void nfnl_unlock(void)
+static inline void nfnl_unlock(void)
{
mutex_unlock(&nfnl_mutex);
- if (nfnl->sk_receive_queue.qlen)
- nfnl->sk_data_ready(nfnl, 0);
}
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
@@ -147,9 +135,7 @@ static int nfnetlink_rcv_msg(struct sk_b
ss = nfnetlink_get_subsys(type);
if (!ss) {
#ifdef CONFIG_KMOD
- /* don't call nfnl_unlock, since it would reenter
- * with further packet processing */
- __nfnl_unlock();
+ nfnl_unlock();
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
nfnl_lock();
ss = nfnetlink_get_subsys(type);
@@ -188,10 +174,9 @@ static void nfnetlink_rcv(struct sock *s
unsigned int qlen = 0;
do {
- if (nfnl_trylock())
- return;
+ nfnl_lock();
qlen = netlink_run_queue(sk, qlen, nfnetlink_rcv_msg);
- __nfnl_unlock();
+ nfnl_unlock();
} while (qlen);
}
--- ./net/netlink/genetlink.c.nlk3 2007-08-26 19:30:38.000000000 +0400
+++ ./net/netlink/genetlink.c 2007-10-01 16:05:29.000000000 +0400
@@ -22,22 +22,14 @@ struct sock *genl_sock = NULL;
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
-static void genl_lock(void)
+static inline void genl_lock(void)
{
mutex_lock(&genl_mutex);
}
-static int genl_trylock(void)
-{
- return !mutex_trylock(&genl_mutex);
-}
-
-static void genl_unlock(void)
+static inline void genl_unlock(void)
{
mutex_unlock(&genl_mutex);
-
- if (genl_sock && genl_sock->sk_receive_queue.qlen)
- genl_sock->sk_data_ready(genl_sock, 0);
}
#define GENL_FAM_TAB_SIZE 16
@@ -483,8 +475,7 @@ static void genl_rcv(struct sock *sk, in
unsigned int qlen = 0;
do {
- if (genl_trylock())
- return;
+ genl_lock();
qlen = netlink_run_queue(sk, qlen, genl_rcv_msg);
genl_unlock();
} while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);
^ permalink raw reply
* Re: (2.6.18.8) tg3: eth0: The system may be re-ordering memory-mapped I/O cycles to the network device, attempting to recover.
From: NetArt - Grzegorz Nosek @ 2007-10-05 14:44 UTC (permalink / raw)
To: Michael Chan; +Cc: NetArt - Grzegorz Nosek, netdev
In-Reply-To: <1551EAE59135BE47B544934E30FC4FC002AABAA7@nt-irva-0751.brcm.ad.broadcom.com>
On Fri, Oct 05, 2007 at 07:16:50AM -0700, Michael Chan wrote:
> NetArt - Grzegorz Nosek wrote:
>
> >
> > I have encountered an error which looks tg3-related. Upon adding some
> > htb queue rules (which I don't have handy ATM but can provide if
> > needed), after some time we get such messages in the kernel log:
> >
>
> Please provide the HTB queue rules and the type of traffic to make
> this happen. Thanks.
>
The HTB rules are as simple as below:
INTERFACE="eth0"
TC="/sbin/tc"
$TC qdisc del dev $INTERFACE root
$TC qdisc add dev $INTERFACE root handle 1:0 htb
$TC class add dev $INTERFACE parent 1:0 classid 1:1 htb rate 1000Mbit ceil 1000Mbit
$TC class add dev $INTERFACE parent 1:1 classid 1:2 htb rate 5Mbit ceil 5Mbit
$TC qdisc add dev $INTERFACE parent 1:2 sfq
$TC filter add dev $INTERFACE parent 1:0 protocol ip prio 10 u32 match ip src 10.10.10.10 flowid 1:2
(the IP address was changed but it's one of the machine's 65 local IP
addresses)
The traffic is 4-5kpps both ways, 2Mbps in, 40Mbps out. There are
several database servers running. The point of these rules was to
limit the data rate of 10.10.10.10 (a mysql server) to 5Mbps. Currently
the traffic from this IP accounts for much of the whole traffic but overall
it's pretty bursty. It ranges from nothing at all to 10Mbps to 70Mbps
peaks.
It may be related that on other machines the HTB queues are saturated
only occasionally and usually the data rates are well below HTB
settings, so a drastically overlimit queue may well be unique to this
server.
Best regards,
Grzegorz Nosek
^ permalink raw reply
* [PATCH 3/5] cleanup 3rd argument in netlink_sendskb
From: Denis V. Lunev @ 2007-10-05 14:46 UTC (permalink / raw)
To: davem; +Cc: netdev, kuznet, devel, containers
netlink_sendskb does not use third argument. Clean it and save a couple of
bytes.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
--- ./include/linux/netlink.h.nlk5 2007-08-26 19:30:37.000000000 +0400
+++ ./include/linux/netlink.h 2007-10-02 11:53:09.000000000 +0400
@@ -194,7 +194,7 @@ struct sock *netlink_getsockbyfilp(struc
int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock,
long timeo, struct sock *ssk);
void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
-int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
+int netlink_sendskb(struct sock *sk, struct sk_buff *skb);
/*
* skb should fit one page. This choice is good for headerless malloc.
--- ./ipc/mqueue.c.nlk5 2007-08-26 19:30:37.000000000 +0400
+++ ./ipc/mqueue.c 2007-10-02 11:52:27.000000000 +0400
@@ -521,8 +521,7 @@ static void __do_notify(struct mqueue_in
break;
case SIGEV_THREAD:
set_cookie(info->notify_cookie, NOTIFY_WOKENUP);
- netlink_sendskb(info->notify_sock,
- info->notify_cookie, 0);
+ netlink_sendskb(info->notify_sock, info->notify_cookie);
break;
}
/* after notification unregisters process */
@@ -568,7 +567,7 @@ static void remove_notification(struct m
if (info->notify_owner != NULL &&
info->notify.sigev_notify == SIGEV_THREAD) {
set_cookie(info->notify_cookie, NOTIFY_REMOVED);
- netlink_sendskb(info->notify_sock, info->notify_cookie, 0);
+ netlink_sendskb(info->notify_sock, info->notify_cookie);
}
put_pid(info->notify_owner);
info->notify_owner = NULL;
--- ./net/netlink/af_netlink.c.nlk5 2007-10-02 11:43:09.000000000 +0400
+++ ./net/netlink/af_netlink.c 2007-10-02 11:50:19.000000000 +0400
@@ -796,7 +796,7 @@ int netlink_attachskb(struct sock *sk, s
return 0;
}
-int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol)
+int netlink_sendskb(struct sock *sk, struct sk_buff *skb)
{
int len = skb->len;
@@ -858,7 +858,7 @@ retry:
if (err)
return err;
- return netlink_sendskb(sk, skb, ssk->sk_protocol);
+ return netlink_sendskb(sk, skb);
}
int netlink_has_listeners(struct sock *sk, unsigned int group)
^ permalink raw reply
* [PATCH 4/5] unify netlink kernel socket recognition
From: Denis V. Lunev @ 2007-10-05 14:47 UTC (permalink / raw)
To: davem; +Cc: netdev, kuznet, devel, containers
There are currently two ways to determine whether the netlink socket is a
kernel one or a user one. This patch creates a single inline call for
this purpose and unifies all the calls in the af_netlink.c
No similar calls are found outside af_netlink.c.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
--- ./net/netlink/af_netlink.c.nlk4 2007-08-26 19:30:38.000000000 +0400
+++ ./net/netlink/af_netlink.c 2007-10-02 12:27:33.000000000 +0400
@@ -92,6 +92,11 @@ static inline struct netlink_sock *nlk_s
return container_of(sk, struct netlink_sock, sk);
}
+static inline int netlink_is_kernel(struct sock *sk)
+{
+ return nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET;
+}
+
struct nl_pid_hash {
struct hlist_head *table;
unsigned long rehash_time;
@@ -489,7 +494,7 @@ static int netlink_release(struct socket
module_put(nlk->module);
netlink_table_grab();
- if (nlk->flags & NETLINK_KERNEL_SOCKET) {
+ if (netlink_is_kernel(sk)) {
kfree(nl_table[sk->sk_protocol].listeners);
nl_table[sk->sk_protocol].module = NULL;
nl_table[sk->sk_protocol].registered = 0;
@@ -716,7 +721,7 @@ static struct sock *netlink_getsockbypid
/* Don't bother queuing skb if kernel socket has no input function */
nlk = nlk_sk(sock);
- if ((nlk->pid == 0 && !nlk->data_ready) ||
+ if ((netlink_is_kernel(sock) && !nlk->data_ready) ||
(sock->sk_state == NETLINK_CONNECTED &&
nlk->dst_pid != nlk_sk(ssk)->pid)) {
sock_put(sock);
@@ -762,7 +767,7 @@ int netlink_attachskb(struct sock *sk, s
test_bit(0, &nlk->state)) {
DECLARE_WAITQUEUE(wait, current);
if (!timeo) {
- if (!ssk || nlk_sk(ssk)->pid == 0)
+ if (!ssk || netlink_is_kernel(ssk))
netlink_overrun(sk);
sock_put(sk);
kfree_skb(skb);
@@ -861,7 +875,7 @@ int netlink_has_listeners(struct sock *s
int res = 0;
unsigned long *listeners;
- BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+ BUG_ON(!netlink_is_kernel(sk));
rcu_read_lock();
listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
^ permalink raw reply
* [PATCH 5/5] make netlink user -> kernel interface synchronious
From: Denis V. Lunev @ 2007-10-05 14:48 UTC (permalink / raw)
To: davem; +Cc: netdev, kuznet, devel, containers
This patch make processing netlink user -> kernel messages synchronious.
This change was inspired by the talk with Alexey Kuznetsov about current
netlink messages processing. He says that he was badly wrong when introduced
asynchronious user -> kernel communication.
The call netlink_unicast is the only path to send message to the kernel
netlink socket. But, unfortunately, it is also used to send data to the
user.
Before this change the user message has been attached to the socket queue
and sk->sk_data_ready was called. The process has been blocked until all
pending messages were processed. The bad thing is that this processing
may occur in the arbitrary process context.
This patch changes nlk->data_ready callback to get 1 skb and force packet
processing right in the netlink_unicast.
Kernel -> user path in netlink_unicast remains untouched.
EINTR processing for in netlink_run_queue was changed. It forces rtnl_lock
drop, but the process remains in the cycle until the message will be fully
processed. So, there is no need to use this kludges now.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
--- ./drivers/connector/connector.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./drivers/connector/connector.c 2007-10-05 16:33:04.000000000 +0400
@@ -235,18 +235,6 @@ out:
}
/*
- * Netlink socket input callback - dequeues the skbs and calls the
- * main netlink receiving function.
- */
-static void cn_input(struct sock *sk, int len)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL)
- cn_rx_skb(skb);
-}
-
-/*
* Notification routing.
*
* Gets id and checks if there are notification request for it's idx
@@ -442,7 +430,7 @@ static int __devinit cn_init(void)
struct cn_dev *dev = &cdev;
int err;
- dev->input = cn_input;
+ dev->input = cn_rx_skb;
dev->id.idx = cn_idx;
dev->id.val = cn_val;
--- ./drivers/scsi/scsi_netlink.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./drivers/scsi/scsi_netlink.c 2007-10-05 16:49:45.000000000 +0400
@@ -64,7 +64,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
err = -EBADMSG;
- goto next_msg;
+ return;
}
hdr = NLMSG_DATA(nlh);
@@ -99,27 +99,6 @@ next_msg:
/**
- * scsi_nl_rcv_msg -
- * Receive handler for a socket. Extracts a received message buffer from
- * the socket, and starts message processing.
- *
- * @sk: socket
- * @len: unused
- *
- **/
-static void
-scsi_nl_rcv(struct sock *sk, int len)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
- scsi_nl_rcv_msg(skb);
- kfree_skb(skb);
- }
-}
-
-
-/**
* scsi_nl_rcv_event -
* Event handler for a netlink socket.
*
@@ -168,7 +147,7 @@ scsi_netlink_init(void)
}
scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
- SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
+ SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
THIS_MODULE);
if (!scsi_nl_sock) {
printk(KERN_ERR "%s: register of recieve handler failed\n",
--- ./drivers/scsi/scsi_transport_iscsi.c.nlk6 2007-10-05 17:51:40.000000000 +0400
+++ ./drivers/scsi/scsi_transport_iscsi.c 2007-10-05 17:53:08.000000000 +0400
@@ -1097,61 +1097,49 @@ iscsi_if_recv_msg(struct sk_buff *skb, s
}
/*
- * Get message from skb (based on rtnetlink_rcv_skb). Each message is
- * processed by iscsi_if_recv_msg. Malformed skbs with wrong lengths or
- * invalid creds are discarded silently.
+ * Get message from skb. Each message is processed by iscsi_if_recv_msg.
+ * Malformed skbs with wrong lengths or invalid creds are not processed.
*/
static void
-iscsi_if_rx(struct sock *sk, int len)
+iscsi_if_rx(struct sk_buff *skb)
{
- struct sk_buff *skb;
-
mutex_lock(&rx_queue_mutex);
- while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
- if (NETLINK_CREDS(skb)->uid) {
- skb_pull(skb, skb->len);
- goto free_skb;
+ while (skb->len >= NLMSG_SPACE(0)) {
+ int err;
+ uint32_t rlen;
+ struct nlmsghdr *nlh;
+ struct iscsi_uevent *ev;
+
+ nlh = nlmsg_hdr(skb);
+ if (nlh->nlmsg_len < sizeof(*nlh) ||
+ skb->len < nlh->nlmsg_len) {
+ break;
}
- while (skb->len >= NLMSG_SPACE(0)) {
- int err;
- uint32_t rlen;
- struct nlmsghdr *nlh;
- struct iscsi_uevent *ev;
-
- nlh = nlmsg_hdr(skb);
- if (nlh->nlmsg_len < sizeof(*nlh) ||
- skb->len < nlh->nlmsg_len) {
- break;
- }
-
- ev = NLMSG_DATA(nlh);
- rlen = NLMSG_ALIGN(nlh->nlmsg_len);
- if (rlen > skb->len)
- rlen = skb->len;
-
- err = iscsi_if_recv_msg(skb, nlh);
- if (err) {
- ev->type = ISCSI_KEVENT_IF_ERROR;
- ev->iferror = err;
- }
- do {
- /*
- * special case for GET_STATS:
- * on success - sending reply and stats from
- * inside of if_recv_msg(),
- * on error - fall through.
- */
- if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
- break;
- err = iscsi_if_send_reply(
- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
- nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
- } while (err < 0 && err != -ECONNREFUSED);
- skb_pull(skb, rlen);
+ ev = NLMSG_DATA(nlh);
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+
+ err = iscsi_if_recv_msg(skb, nlh);
+ if (err) {
+ ev->type = ISCSI_KEVENT_IF_ERROR;
+ ev->iferror = err;
}
-free_skb:
- kfree_skb(skb);
+ do {
+ /*
+ * special case for GET_STATS:
+ * on success - sending reply and stats from
+ * inside of if_recv_msg(),
+ * on error - fall through.
+ */
+ if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
+ break;
+ err = iscsi_if_send_reply(
+ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
+ } while (err < 0 && err != -ECONNREFUSED);
+ skb_pull(skb, rlen);
}
mutex_unlock(&rx_queue_mutex);
}
--- ./fs/ecryptfs/netlink.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./fs/ecryptfs/netlink.c 2007-10-05 16:33:04.000000000 +0400
@@ -165,22 +165,10 @@ static int ecryptfs_process_nl_quit(stru
* it to its desired netlink context element and wake up the process
* that is waiting for a response.
*/
-static void ecryptfs_receive_nl_message(struct sock *sk, int len)
+static void ecryptfs_receive_nl_message(struct sk_buff *skb)
{
- struct sk_buff *skb;
struct nlmsghdr *nlh;
- int rc = 0; /* skb_recv_datagram requires this */
-receive:
- skb = skb_recv_datagram(sk, 0, 0, &rc);
- if (rc == -EINTR)
- goto receive;
- else if (rc < 0) {
- ecryptfs_printk(KERN_ERR, "Error occurred while "
- "receiving eCryptfs netlink message; "
- "rc = [%d]\n", rc);
- return;
- }
nlh = nlmsg_hdr(skb);
if (!NLMSG_OK(nlh, skb->len)) {
ecryptfs_printk(KERN_ERR, "Received corrupt netlink "
--- ./include/linux/connector.h.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./include/linux/connector.h 2007-10-05 16:33:04.000000000 +0400
@@ -153,7 +153,7 @@ struct cn_dev {
u32 seq, groups;
struct sock *nls;
- void (*input) (struct sock * sk, int len);
+ void (*input) (struct sk_buff *skb);
struct cn_queue_dev *cbdev;
};
--- ./include/linux/netlink.h.nlk6 2007-10-05 16:28:17.000000000 +0400
+++ ./include/linux/netlink.h 2007-10-05 16:33:04.000000000 +0400
@@ -175,7 +175,7 @@ struct netlink_skb_parms
extern struct sock *netlink_kernel_create(struct net *net,
int unit,unsigned int groups,
- void (*input)(struct sock *sk, int len),
+ void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex,
struct module *module);
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
--- ./include/net/netlink.h.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./include/net/netlink.h 2007-10-05 16:33:04.000000000 +0400
@@ -220,9 +220,9 @@ struct nl_info {
u32 pid;
};
-extern unsigned int netlink_run_queue(struct sock *sk, unsigned int qlen,
- int (*cb)(struct sk_buff *,
- struct nlmsghdr *));
+extern int netlink_rcv_skb(struct sk_buff *skb,
+ int (*cb)(struct sk_buff *,
+ struct nlmsghdr *));
extern int nlmsg_notify(struct sock *sk, struct sk_buff *skb,
u32 pid, unsigned int group, int report,
gfp_t flags);
--- ./kernel/audit.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./kernel/audit.c 2007-10-05 16:50:01.000000000 +0400
@@ -847,18 +847,10 @@ static void audit_receive_skb(struct sk_
}
/* Receive messages from netlink socket. */
-static void audit_receive(struct sock *sk, int length)
+static void audit_receive(struct sk_buff *skb)
{
- struct sk_buff *skb;
- unsigned int qlen;
-
mutex_lock(&audit_cmd_mutex);
-
- for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
- skb = skb_dequeue(&sk->sk_receive_queue);
- audit_receive_skb(skb);
- kfree_skb(skb);
- }
+ audit_receive_skb(skb);
mutex_unlock(&audit_cmd_mutex);
}
--- ./net/core/rtnetlink.c.nlk6 2007-10-05 16:28:09.000000000 +0400
+++ ./net/core/rtnetlink.c 2007-10-05 16:50:29.000000000 +0400
@@ -1312,15 +1312,11 @@ static int rtnetlink_rcv_msg(struct sk_b
return doit(skb, nlh, (void *)&rta_buf[0]);
}
-static void rtnetlink_rcv(struct sock *sk, int len)
+static void rtnetlink_rcv(struct sk_buff *skb)
{
- unsigned int qlen = 0;
-
- do {
- rtnl_lock();
- qlen = netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
- rtnl_unlock();
- } while (qlen);
+ rtnl_lock();
+ netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
+ rtnl_unlock();
}
static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
--- ./net/decnet/netfilter/dn_rtmsg.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./net/decnet/netfilter/dn_rtmsg.c 2007-10-05 16:50:45.000000000 +0400
@@ -115,17 +115,6 @@ static inline void dnrmg_receive_user_sk
RCV_SKB_FAIL(-EINVAL);
}
-static void dnrmg_receive_user_sk(struct sock *sk, int len)
-{
- struct sk_buff *skb;
- unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
-
- for (; qlen && (skb = skb_dequeue(&sk->sk_receive_queue)); qlen--) {
- dnrmg_receive_user_skb(skb);
- kfree_skb(skb);
- }
-}
-
static struct nf_hook_ops dnrmg_ops = {
.hook = dnrmg_hook,
.pf = PF_DECnet,
@@ -139,7 +128,8 @@ static int __init dn_rtmsg_init(void)
dnrmg = netlink_kernel_create(&init_net,
NETLINK_DNRTMSG, DNRNG_NLGRP_MAX,
- dnrmg_receive_user_sk, NULL, THIS_MODULE);
+ dnrmg_receive_user_skb,
+ NULL, THIS_MODULE);
if (dnrmg == NULL) {
printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
return -ENOMEM;
--- ./net/ipv4/fib_frontend.c.nlk6 2007-10-05 16:33:04.000000000 +0400
+++ ./net/ipv4/fib_frontend.c 2007-10-05 16:33:14.000000000 +0400
@@ -62,6 +62,9 @@ static struct hlist_head fib_table_hash[
#define FIB_TABLE_HASHSZ 256
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
+static struct sock *fibnl = NULL;
+
+
struct fib_table *fib_new_table(u32 id)
{
struct fib_table *tb;
@@ -806,13 +809,13 @@ static void nl_fib_input(struct sk_buff
pid = NETLINK_CB(skb).pid; /* pid of sending process */
NETLINK_CB(skb).pid = 0; /* from kernel */
NETLINK_CB(skb).dst_group = 0; /* unicast */
- netlink_unicast(sk, skb, pid, MSG_DONTWAIT);
+ netlink_unicast(fibnl, skb, pid, MSG_DONTWAIT);
}
static void nl_fib_lookup_init(void)
{
- netlink_kernel_create(&init_net, NETLINK_FIB_LOOKUP, 0, nl_fib_input,
- NULL, THIS_MODULE);
+ fibnl = netlink_kernel_create(&init_net, NETLINK_FIB_LOOKUP, 0,
+ nl_fib_input, NULL, THIS_MODULE);
}
static void fib_disable_ip(struct net_device *dev, int force)
--- ./net/ipv4/inet_diag.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv4/inet_diag.c 2007-10-05 16:50:56.000000000 +0400
@@ -839,15 +839,11 @@ static int inet_diag_rcv_msg(struct sk_b
static DEFINE_MUTEX(inet_diag_mutex);
-static void inet_diag_rcv(struct sock *sk, int len)
+static void inet_diag_rcv(struct sk_buff *skb)
{
- unsigned int qlen = 0;
-
- do {
- mutex_lock(&inet_diag_mutex);
- qlen = netlink_run_queue(sk, qlen, &inet_diag_rcv_msg);
- mutex_unlock(&inet_diag_mutex);
- } while (qlen);
+ mutex_lock(&inet_diag_mutex);
+ netlink_rcv_skb(skb, &inet_diag_rcv_msg);
+ mutex_unlock(&inet_diag_mutex);
}
static DEFINE_SPINLOCK(inet_diag_register_lock);
--- ./net/ipv4/netfilter/ip_queue.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv4/netfilter/ip_queue.c 2007-10-05 16:51:04.000000000 +0400
@@ -475,7 +475,7 @@ ipq_dev_drop(int ifindex)
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
static inline void
-ipq_rcv_skb(struct sk_buff *skb)
+__ipq_rcv_skb(struct sk_buff *skb)
{
int status, type, pid, flags, nlmsglen, skblen;
struct nlmsghdr *nlh;
@@ -533,19 +533,10 @@ ipq_rcv_skb(struct sk_buff *skb)
}
static void
-ipq_rcv_sk(struct sock *sk, int len)
+ipq_rcv_skb(struct sk_buff *skb)
{
- struct sk_buff *skb;
- unsigned int qlen;
-
mutex_lock(&ipqnl_mutex);
-
- for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
- skb = skb_dequeue(&sk->sk_receive_queue);
- ipq_rcv_skb(skb);
- kfree_skb(skb);
- }
-
+ __ipq_rcv_skb(skb);
mutex_unlock(&ipqnl_mutex);
}
@@ -670,7 +661,7 @@ static int __init ip_queue_init(void)
netlink_register_notifier(&ipq_nl_notifier);
ipqnl = netlink_kernel_create(&init_net, NETLINK_FIREWALL, 0,
- ipq_rcv_sk, NULL, THIS_MODULE);
+ ipq_rcv_skb, NULL, THIS_MODULE);
if (ipqnl == NULL) {
printk(KERN_ERR "ip_queue: failed to create netlink socket\n");
goto cleanup_netlink_notifier;
--- ./net/ipv6/netfilter/ip6_queue.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv6/netfilter/ip6_queue.c 2007-10-05 16:51:11.000000000 +0400
@@ -464,7 +464,7 @@ ipq_dev_drop(int ifindex)
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
static inline void
-ipq_rcv_skb(struct sk_buff *skb)
+__ipq_rcv_skb(struct sk_buff *skb)
{
int status, type, pid, flags, nlmsglen, skblen;
struct nlmsghdr *nlh;
@@ -522,19 +522,10 @@ ipq_rcv_skb(struct sk_buff *skb)
}
static void
-ipq_rcv_sk(struct sock *sk, int len)
+ipq_rcv_skb(struct sk_buff *skb)
{
- struct sk_buff *skb;
- unsigned int qlen;
-
mutex_lock(&ipqnl_mutex);
-
- for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
- skb = skb_dequeue(&sk->sk_receive_queue);
- ipq_rcv_skb(skb);
- kfree_skb(skb);
- }
-
+ __ipq_rcv_skb(skb);
mutex_unlock(&ipqnl_mutex);
}
@@ -658,8 +649,8 @@ static int __init ip6_queue_init(void)
struct proc_dir_entry *proc;
netlink_register_notifier(&ipq_nl_notifier);
- ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0, ipq_rcv_sk,
- NULL, THIS_MODULE);
+ ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0,
+ ipq_rcv_skb, NULL, THIS_MODULE);
if (ipqnl == NULL) {
printk(KERN_ERR "ip6_queue: failed to create netlink socket\n");
goto cleanup_netlink_notifier;
--- ./net/netfilter/nfnetlink.c.nlk6 2007-10-05 16:28:13.000000000 +0400
+++ ./net/netfilter/nfnetlink.c 2007-10-05 16:51:28.000000000 +0400
@@ -169,15 +169,11 @@ static int nfnetlink_rcv_msg(struct sk_b
}
}
-static void nfnetlink_rcv(struct sock *sk, int len)
+static void nfnetlink_rcv(struct sk_buff *skb)
{
- unsigned int qlen = 0;
-
- do {
- nfnl_lock();
- qlen = netlink_run_queue(sk, qlen, nfnetlink_rcv_msg);
- nfnl_unlock();
- } while (qlen);
+ nfnl_lock();
+ netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
+ nfnl_unlock();
}
static void __exit nfnetlink_exit(void)
--- ./net/netlink/af_netlink.c.nlk6 2007-10-05 16:29:35.000000000 +0400
+++ ./net/netlink/af_netlink.c 2007-10-05 18:07:41.000000000 +0400
@@ -80,7 +80,7 @@ struct netlink_sock {
struct netlink_callback *cb;
struct mutex *cb_mutex;
struct mutex cb_def_mutex;
- void (*data_ready)(struct sock *sk, int bytes);
+ void (*netlink_rcv)(struct sk_buff *skb);
struct module *module;
};
@@ -127,7 +127,6 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_
static int netlink_dump(struct sock *sk);
static void netlink_destroy_callback(struct netlink_callback *cb);
-static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb);
static DEFINE_RWLOCK(nl_table_lock);
static atomic_t nl_table_users = ATOMIC_INIT(0);
@@ -711,21 +710,17 @@ static void netlink_overrun(struct sock
static struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid)
{
- int protocol = ssk->sk_protocol;
- struct net *net;
struct sock *sock;
struct netlink_sock *nlk;
- net = ssk->sk_net;
- sock = netlink_lookup(net, protocol, pid);
+ sock = netlink_lookup(ssk->sk_net, ssk->sk_protocol, pid);
if (!sock)
return ERR_PTR(-ECONNREFUSED);
/* Don't bother queuing skb if kernel socket has no input function */
nlk = nlk_sk(sock);
- if ((netlink_is_kernel(sock) && !nlk->data_ready) ||
- (sock->sk_state == NETLINK_CONNECTED &&
- nlk->dst_pid != nlk_sk(ssk)->pid)) {
+ if (sock->sk_state == NETLINK_CONNECTED &&
+ nlk->dst_pid != nlk_sk(ssk)->pid) {
sock_put(sock);
return ERR_PTR(-ECONNREFUSED);
}
@@ -838,7 +833,34 @@ static inline struct sk_buff *netlink_tr
return skb;
}
-int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)
+static inline void netlink_rcv_wake(struct sock *sk)
+{
+ struct netlink_sock *nlk = nlk_sk(sk);
+
+ if (skb_queue_empty(&sk->sk_receive_queue))
+ clear_bit(0, &nlk->state);
+ if (!test_bit(0, &nlk->state))
+ wake_up_interruptible(&nlk->wait);
+}
+
+static inline int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb)
+{
+ int ret;
+ struct netlink_sock *nlk = nlk_sk(sk);
+
+ ret = -ECONNREFUSED;
+ if (nlk->netlink_rcv != NULL) {
+ ret = skb->len;
+ skb_set_owner_r(skb, sk);
+ nlk->netlink_rcv(skb);
+ }
+ kfree_skb(skb);
+ sock_put(sk);
+ return ret;
+}
+
+int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
+ u32 pid, int nonblock)
{
struct sock *sk;
int err;
@@ -853,6 +875,9 @@ retry:
kfree_skb(skb);
return PTR_ERR(sk);
}
+ if (netlink_is_kernel(sk))
+ return netlink_unicast_kernel(sk, skb);
+
err = netlink_attachskb(sk, skb, nonblock, timeo, ssk);
if (err == 1)
goto retry;
@@ -1152,16 +1177,6 @@ static void netlink_cmsg_recv_pktinfo(st
put_cmsg(msg, SOL_NETLINK, NETLINK_PKTINFO, sizeof(info), &info);
}
-static inline void netlink_rcv_wake(struct sock *sk)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
-
- if (skb_queue_empty(&sk->sk_receive_queue))
- clear_bit(0, &nlk->state);
- if (!test_bit(0, &nlk->state))
- wake_up_interruptible(&nlk->wait);
-}
-
static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
@@ -1309,11 +1324,7 @@ out:
static void netlink_data_ready(struct sock *sk, int len)
{
- struct netlink_sock *nlk = nlk_sk(sk);
-
- if (nlk->data_ready)
- nlk->data_ready(sk, len);
- netlink_rcv_wake(sk);
+ BUG();
}
/*
@@ -1324,7 +1335,7 @@ static void netlink_data_ready(struct so
struct sock *
netlink_kernel_create(struct net *net, int unit, unsigned int groups,
- void (*input)(struct sock *sk, int len),
+ void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex, struct module *module)
{
struct socket *sock;
@@ -1353,7 +1364,7 @@ netlink_kernel_create(struct net *net, i
sk = sock->sk;
sk->sk_data_ready = netlink_data_ready;
if (input)
- nlk_sk(sk)->data_ready = input;
+ nlk_sk(sk)->netlink_rcv = input;
if (netlink_insert(sk, net, 0))
goto out_sock_release;
@@ -1553,12 +1564,7 @@ int netlink_dump_start(struct sock *ssk,
netlink_dump(sk);
sock_put(sk);
-
- /* We successfully started a dump, by returning -EINTR we
- * signal the queue mangement to interrupt processing of
- * any netlink messages so userspace gets a chance to read
- * the results. */
- return -EINTR;
+ return 0;
}
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
@@ -1595,13 +1601,15 @@ void netlink_ack(struct sk_buff *in_skb,
netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
}
-static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
+int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
struct nlmsghdr *))
{
struct nlmsghdr *nlh;
int err;
while (skb->len >= nlmsg_total_size(0)) {
+ int msglen;
+
nlh = nlmsg_hdr(skb);
err = 0;
@@ -1617,86 +1625,20 @@ static int netlink_rcv_skb(struct sk_buf
goto skip;
err = cb(skb, nlh);
- if (err == -EINTR) {
- /* Not an error, but we interrupt processing */
- netlink_queue_skip(nlh, skb);
- return err;
- }
skip:
if (nlh->nlmsg_flags & NLM_F_ACK || err)
netlink_ack(skb, nlh, err);
- netlink_queue_skip(nlh, skb);
+ msglen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (msglen > skb->len)
+ msglen = skb->len;
+ skb_pull(skb, msglen);
}
return 0;
}
/**
- * nelink_run_queue - Process netlink receive queue.
- * @sk: Netlink socket containing the queue
- * @qlen: Initial queue length
- * @cb: Callback function invoked for each netlink message found
- *
- * Processes as much as there was in the queue upon entry and invokes
- * a callback function for each netlink message found. The callback
- * function may refuse a message by returning a negative error code
- * but setting the error pointer to 0 in which case this function
- * returns with a qlen != 0.
- *
- * qlen must be initialized to 0 before the initial entry, afterwards
- * the function may be called repeatedly until the returned qlen is 0.
- *
- * The callback function may return -EINTR to signal that processing
- * of netlink messages shall be interrupted. In this case the message
- * currently being processed will NOT be requeued onto the receive
- * queue.
- */
-unsigned int netlink_run_queue(struct sock *sk, unsigned int qlen,
- int (*cb)(struct sk_buff *, struct nlmsghdr *))
-{
- struct sk_buff *skb;
-
- if (!qlen || qlen > skb_queue_len(&sk->sk_receive_queue))
- qlen = skb_queue_len(&sk->sk_receive_queue);
-
- for (; qlen; qlen--) {
- skb = skb_dequeue(&sk->sk_receive_queue);
- if (netlink_rcv_skb(skb, cb)) {
- if (skb->len)
- skb_queue_head(&sk->sk_receive_queue, skb);
- else {
- kfree_skb(skb);
- qlen--;
- }
- break;
- }
-
- kfree_skb(skb);
- }
-
- return qlen;
-}
-
-/**
- * netlink_queue_skip - Skip netlink message while processing queue.
- * @nlh: Netlink message to be skipped
- * @skb: Socket buffer containing the netlink messages.
- *
- * Pulls the given netlink message off the socket buffer so the next
- * call to netlink_queue_run() will not reconsider the message.
- */
-static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
-{
- int msglen = NLMSG_ALIGN(nlh->nlmsg_len);
-
- if (msglen > skb->len)
- msglen = skb->len;
-
- skb_pull(skb, msglen);
-}
-
-/**
* nlmsg_notify - send a notification netlink message
* @sk: netlink socket to use
* @skb: notification message
@@ -2008,7 +1950,7 @@ panic:
core_initcall(netlink_proto_init);
EXPORT_SYMBOL(netlink_ack);
-EXPORT_SYMBOL(netlink_run_queue);
+EXPORT_SYMBOL(netlink_rcv_skb);
EXPORT_SYMBOL(netlink_broadcast);
EXPORT_SYMBOL(netlink_dump_start);
EXPORT_SYMBOL(netlink_kernel_create);
--- ./net/netlink/genetlink.c.nlk6 2007-10-05 16:28:13.000000000 +0400
+++ ./net/netlink/genetlink.c 2007-10-05 16:52:21.000000000 +0400
@@ -470,15 +470,11 @@ static int genl_rcv_msg(struct sk_buff *
return ops->doit(skb, &info);
}
-static void genl_rcv(struct sock *sk, int len)
+static void genl_rcv(struct sk_buff *skb)
{
- unsigned int qlen = 0;
-
- do {
- genl_lock();
- qlen = netlink_run_queue(sk, qlen, genl_rcv_msg);
- genl_unlock();
- } while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);
+ genl_lock();
+ netlink_rcv_skb(skb, &genl_rcv_msg);
+ genl_unlock();
}
/**************************************************************************
--- ./net/xfrm/xfrm_user.c.nlk6 2007-10-05 16:27:16.000000000 +0400
+++ ./net/xfrm/xfrm_user.c 2007-10-05 16:52:31.000000000 +0400
@@ -1881,16 +1881,11 @@ static int xfrm_user_rcv_msg(struct sk_b
return link->doit(skb, nlh, attrs);
}
-static void xfrm_netlink_rcv(struct sock *sk, int len)
+static void xfrm_netlink_rcv(struct sk_buff *skb)
{
- unsigned int qlen = 0;
-
- do {
- mutex_lock(&xfrm_cfg_mutex);
- qlen = netlink_run_queue(sk, qlen, &xfrm_user_rcv_msg);
- mutex_unlock(&xfrm_cfg_mutex);
-
- } while (qlen);
+ mutex_lock(&xfrm_cfg_mutex);
+ netlink_rcv_skb(skb, &xfrm_user_rcv_msg);
+ mutex_unlock(&xfrm_cfg_mutex);
}
static inline size_t xfrm_expire_msgsize(void)
^ permalink raw reply
* [patch 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390
--
Sorry, 2nd attempt, because patch 5/5 missed title line
The following patches in qeth and lcs driver are intended for 2.6.24:
- qeth: drop non-IP outbound packets on HiperSocket devices
- qeth: EDDP-mode - correct filling of qdio buffers
- qeth: avoid duplicate deletion of multicast addresses
- lcs: avoid kernel panic in lcs_recovery driven by channel errors
- qeth: discard inbound packets with unknown header id
^ permalink raw reply
* [patch 1/5] qeth: HiperSockets layer-3 interface drop non IPv4 or non IPv6 packets
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390; +Cc: Klaus D. Wacker
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
[-- Attachment #1: 705-qeth-pktdrop.diff --]
[-- Type: text/plain, Size: 1195 bytes --]
From: Klaus D. Wacker <kdwacker@de.ibm.com>
HiperSockets infrastructure (layer-3 mode) supports only IPv4 or
IPv6 packets. Sending other packet types disturbs TCP/IP on z/VM,
which issues messages about invalid packets.
Qeth send routine will detect packet type on sending over a
HiperSockets interface (in layer-3 mode) and drop non IP packets.
The error and drop count of the interface is incremented.
Signed-off-by: Klaus D. Wacker <kdwacker@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/qeth_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -4711,8 +4711,8 @@ qeth_send_packet(struct qeth_card *card,
rc = qeth_do_send_packet(card, queue, new_skb, hdr,
elements_needed, ctx);
else {
- if ((skb->protocol == htons(ETH_P_ARP)) &&
- (card->dev->flags & IFF_NOARP)) {
+ if ((!card->options.layer2) &&
+ (ipv == 0)) {
__qeth_free_new_skb(skb, new_skb);
return -EPERM;
}
--
^ permalink raw reply
* [patch 2/5] qeth: EDDP does not work on large MTUs
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390; +Cc: Frank Blaschka
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
[-- Attachment #1: 708-qeth-eddp.diff --]
[-- Type: text/plain, Size: 2048 bytes --]
From: Frank Blaschka <frank.blaschka@de.ibm.com>
Fix filling the qdio buffers in EDDP mode.
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/qeth_eddp.c | 16 +++++++++-------
drivers/s390/net/qeth_main.c | 3 ++-
2 files changed, 11 insertions(+), 8 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/qeth_eddp.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_eddp.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_eddp.c
@@ -159,13 +159,15 @@ qeth_eddp_fill_buffer(struct qeth_qdio_o
buffer = buf->buffer;
/* fill one skb into buffer */
for (i = 0; i < ctx->elements_per_skb; ++i){
- buffer->element[buf->next_element_to_fill].addr =
- ctx->elements[element].addr;
- buffer->element[buf->next_element_to_fill].length =
- ctx->elements[element].length;
- buffer->element[buf->next_element_to_fill].flags =
- ctx->elements[element].flags;
- buf->next_element_to_fill++;
+ if (ctx->elements[element].length != 0) {
+ buffer->element[buf->next_element_to_fill].
+ addr = ctx->elements[element].addr;
+ buffer->element[buf->next_element_to_fill].
+ length = ctx->elements[element].length;
+ buffer->element[buf->next_element_to_fill].
+ flags = ctx->elements[element].flags;
+ buf->next_element_to_fill++;
+ }
element++;
elements--;
}
Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -4500,7 +4500,8 @@ qeth_do_send_packet(struct qeth_card *ca
/* check if we have enough elements (including following
* free buffers) to handle eddp context */
if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
- printk("eddp tx_dropped 1\n");
+ if (net_ratelimit())
+ PRINT_WARN("eddp tx_dropped 1\n");
rc = -EBUSY;
goto out;
}
--
^ permalink raw reply
* [patch 3/5] qeth: avoid duplicate deletion of multicast addresses
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
[-- Attachment #1: 709-qeth-multicast.diff --]
[-- Type: text/plain, Size: 1257 bytes --]
From: Ursula Braun <braunu@de.ibm.com>
if qeth_set_multicast_list() is performed on 2 CPUs in parallel,
card->ip_list may end corrupted.
Solution: In function __qeth_delete_all_mc()
remove card->ip_list entry before invoking
qeth_deregister_addr_entry(). Thus a 2nd invocation of
qeth_set_multicast_list() cannot try to remove the
same entry twice.
Signed-off-by Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/qeth_main.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -820,14 +820,15 @@ __qeth_delete_all_mc(struct qeth_card *c
again:
list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
if (addr->is_multicast) {
+ list_del(&addr->entry);
spin_unlock_irqrestore(&card->ip_lock, *flags);
rc = qeth_deregister_addr_entry(card, addr);
spin_lock_irqsave(&card->ip_lock, *flags);
if (!rc) {
- list_del(&addr->entry);
kfree(addr);
goto again;
- }
+ } else
+ list_add(&addr->entry, &card->ip_list);
}
}
}
--
^ permalink raw reply
* [patch 4/5] lcs: Channel errors drive lcs_recovery which leads to kernel panic.
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390; +Cc: Klaus D. Wacker
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
[-- Attachment #1: 710-lcs-panic.diff --]
[-- Type: text/plain, Size: 2164 bytes --]
From: Klaus D. Wacker <kdwacker@de.ibm.com>
When the lcs irq routine detects channel failures it drives device recovery.
After this event the device is no longer usable for shutdown requests,
because the lcs_irq routine may get wrong channel status information.
In such a case the lcs_irq routine marks the channel in 'error' state.
The channel state comes back to 'running' after restarting the channels.
Signed-off-by: Klaus D. Wacker <kdwacker@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/lcs.c | 11 ++++++++---
drivers/s390/net/lcs.h | 1 +
2 files changed, 9 insertions(+), 3 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/lcs.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/lcs.c
+++ linux-2.6-uschi/drivers/s390/net/lcs.c
@@ -1400,11 +1400,14 @@ lcs_irq(struct ccw_device *cdev, unsigne
PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
cdev->dev.bus_id, dstat, cstat);
if (rc) {
- lcs_schedule_recovery(card);
- wake_up(&card->wait_q);
- return;
+ channel->state = LCS_CH_STATE_ERROR;
}
}
+ if (channel->state == LCS_CH_STATE_ERROR) {
+ lcs_schedule_recovery(card);
+ wake_up(&card->wait_q);
+ return;
+ }
/* How far in the ccw chain have we processed? */
if ((channel->state != LCS_CH_STATE_INIT) &&
(irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
@@ -1708,6 +1711,8 @@ lcs_stopcard(struct lcs_card *card)
if (card->read.state != LCS_CH_STATE_STOPPED &&
card->write.state != LCS_CH_STATE_STOPPED &&
+ card->read.state != LCS_CH_STATE_ERROR &&
+ card->write.state != LCS_CH_STATE_ERROR &&
card->state == DEV_STATE_UP) {
lcs_clear_multicast_list(card);
rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
Index: linux-2.6-uschi/drivers/s390/net/lcs.h
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/lcs.h
+++ linux-2.6-uschi/drivers/s390/net/lcs.h
@@ -138,6 +138,7 @@ enum lcs_channel_states {
LCS_CH_STATE_RUNNING,
LCS_CH_STATE_SUSPENDED,
LCS_CH_STATE_CLEARED,
+ LCS_CH_STATE_ERROR,
};
/**
--
^ permalink raw reply
* [patch 5/5] qeth: discard inbound packets with unknown header id
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
To: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
[-- Attachment #1: qeth-inbound.diff --]
[-- Type: text/plain, Size: 1217 bytes --]
From: Ursula Braun <braunu@de.ibm.com>
Debugging statements are added for inbound packets with unknown
header id. Those packets are discarded and no longer processed as
osn-packets.
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/qeth_main.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -2699,10 +2699,15 @@ qeth_process_inbound_buffer(struct qeth_
qeth_layer2_rebuild_skb(card, skb, hdr);
else if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3)
vlan_tag = qeth_rebuild_skb(card, skb, hdr);
- else { /*in case of OSN*/
+ else if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN) {
skb_push(skb, sizeof(struct qeth_hdr));
skb_copy_to_linear_data(skb, hdr,
sizeof(struct qeth_hdr));
+ } else { /* unknown header type */
+ dev_kfree_skb_any(skb);
+ QETH_DBF_TEXT(trace, 3, "inbunkno");
+ QETH_DBF_HEX(control, 3, hdr, QETH_DBF_CONTROL_LEN);
+ continue;
}
/* is device UP ? */
if (!(card->dev->flags & IFF_UP)){
--
^ permalink raw reply
* [RFC/PATCH 0/4]UDP memory accounting (take 4)
From: Satoshi OSHIMA @ 2007-10-05 14:59 UTC (permalink / raw)
To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
Cc: Yumiko SUGITA, "??@RedHat"
Hi,
I post a patch set of UDP memory accounting and
limitation.
This patch set is for kernel 2.6.23-rc9. The
differences from take 3 are
* fixing double accounting bug of ip_send_page()
* adding UDP memory limitation.
How to use UDP memory limitation:
This patch set add
/proc/sys/net/ipv4/udp_mem
as a tuning parameter.
When you give the number that is greater than 4096,
UDP memory limitation will work. The number of pages
for socket buffer is limited up to udp_mem[pages].
Currently this function drops the packet when
it is sent or received and the number of pages for
socket buffer is beyond the limit. It won't collect
the buffer that is already allocated.
On the other hand, udp_mem is specified as 4096 or
smaller, UDP memory limitaion will not work.
The deafult number is 0.
Comment, review and test are welcome.
By the way, David pointed out that we should have
the better solution such as memory reclaiming by
callback from vmm. I seriously consider it. But
As Herbert pointed out, it is very difficult to
apply it to TCP because some of the buffer is
already acked. I couldn't find the good solution
that is applicable for TCP, UDP, route cache and so on.
Let me know, if you find the good way to solve
this problem.
Thanks,
Satoshi Oshima
^ permalink raw reply
* [RFC/PATCH 1/4] UDP memory usage accounting (take 4): fix send buffer check
From: Satoshi OSHIMA @ 2007-10-05 15:00 UTC (permalink / raw)
To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>
This patch introduces sndbuf size check before
memory allcation for send buffer.
signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>
Index: 2.6.23-rc7-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc7-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc7-udp_limit/net/ipv4/ip_output.c
@@ -1004,6 +1004,11 @@ alloc_new_skb:
frag = &skb_shinfo(skb)->frags[i];
}
} else if (i < MAX_SKB_FRAGS) {
+ if (atomic_read(&sk->sk_wmem_alloc) + PAGE_SIZE
+ > 2 * sk->sk_sndbuf) {
+ err = -ENOBUFS;
+ goto error;
+ }
if (copy > PAGE_SIZE)
copy = PAGE_SIZE;
page = alloc_pages(sk->sk_allocation, 0);
^ permalink raw reply
* [RFC/PATCH 2/4] UDP memory usage accounting (take 4): accounting unit and variable
From: Satoshi OSHIMA @ 2007-10-05 15:01 UTC (permalink / raw)
To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>
This patch introduces global variable for UDP memory accounting.
The unit is page.
signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>
Index: 2.6.23-rc3-udp_limit/include/net/sock.h
===================================================================
--- 2.6.23-rc3-udp_limit.orig/include/net/sock.h
+++ 2.6.23-rc3-udp_limit/include/net/sock.h
@@ -723,6 +723,13 @@ static inline int sk_stream_wmem_schedul
sk_stream_mem_schedule(sk, size, 0);
}
+#define SK_DATAGRAM_MEM_QUANTUM ((int)PAGE_SIZE)
+
+static inline int sk_datagram_pages(int amt)
+{
+ return DIV_ROUND_UP(amt, SK_DATAGRAM_MEM_QUANTUM);
+}
+
/* Used by processes to "lock" a socket state, so that
* interrupts and bottom half handlers won't change it
* from under us. It essentially blocks any incoming
Index: 2.6.23-rc3-udp_limit/include/net/udp.h
===================================================================
--- 2.6.23-rc3-udp_limit.orig/include/net/udp.h
+++ 2.6.23-rc3-udp_limit/include/net/udp.h
@@ -65,6 +65,8 @@ extern rwlock_t udp_hash_lock;
extern struct proto udp_prot;
+extern atomic_t udp_memory_allocated;
+
struct sk_buff;
/*
Index: 2.6.23-rc3-udp_limit/net/ipv4/proc.c
===================================================================
--- 2.6.23-rc3-udp_limit.orig/net/ipv4/proc.c
+++ 2.6.23-rc3-udp_limit/net/ipv4/proc.c
@@ -66,7 +66,8 @@ static int sockstat_seq_show(struct seq_
fold_prot_inuse(&tcp_prot), atomic_read(&tcp_orphan_count),
tcp_death_row.tw_count, atomic_read(&tcp_sockets_allocated),
atomic_read(&tcp_memory_allocated));
- seq_printf(seq, "UDP: inuse %d\n", fold_prot_inuse(&udp_prot));
+ seq_printf(seq, "UDP: inuse %d mem %d\n", fold_prot_inuse(&udp_prot),
+ atomic_read(&udp_memory_allocated));
seq_printf(seq, "UDPLITE: inuse %d\n", fold_prot_inuse(&udplite_prot));
seq_printf(seq, "RAW: inuse %d\n", fold_prot_inuse(&raw_prot));
seq_printf(seq, "FRAG: inuse %d memory %d\n", ip_frag_nqueues,
Index: 2.6.23-rc3-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc3-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc3-udp_limit/net/ipv4/udp.c
@@ -113,6 +113,10 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_sta
struct hlist_head udp_hash[UDP_HTABLE_SIZE];
DEFINE_RWLOCK(udp_hash_lock);
+atomic_t udp_memory_allocated;
+
+EXPORT_SYMBOL(udp_memory_allocated);
+
static int udp_port_rover;
static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
^ permalink raw reply
* [RFC/PATCH 3/4] UDP memory usage accounting (take 4): memory usage accounting
From: Satoshi OSHIMA @ 2007-10-05 15:02 UTC (permalink / raw)
To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>
This patch introduces memory usage accounting for UDP.
signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>
Index: 2.6.23-rc8-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/ip_output.c
@@ -743,6 +743,8 @@ static inline int ip_ufo_append_data(str
/* specify the length of each IP datagram fragment*/
skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+ atomic_add(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
__skb_queue_tail(&sk->sk_write_queue, skb);
return 0;
@@ -924,6 +926,9 @@ alloc_new_skb:
}
if (skb == NULL)
goto error;
+ if (sk->sk_prot->memory_allocated)
+ atomic_add(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
/*
* Fill in the control structures
@@ -1023,6 +1028,8 @@ alloc_new_skb:
frag = &skb_shinfo(skb)->frags[i];
skb->truesize += PAGE_SIZE;
atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
+ if (sk->sk_prot->memory_allocated)
+ atomic_inc(sk->sk_prot->memory_allocated);
} else {
err = -EMSGSIZE;
goto error;
@@ -1123,7 +1130,9 @@ ssize_t ip_append_page(struct sock *sk,
if (unlikely(!skb)) {
err = -ENOBUFS;
goto error;
- }
+ } else if (sk->sk_prot->memory_allocated)
+ atomic_add(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
/*
* Fill in the control structures
@@ -1202,13 +1211,14 @@ int ip_push_pending_frames(struct sock *
struct iphdr *iph;
__be16 df = 0;
__u8 ttl;
- int err = 0;
+ int err = 0, send_page_size;
if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
goto out;
tail_skb = &(skb_shinfo(skb)->frag_list);
/* move skb->data to ip header from ext header */
+ send_page_size = sk_datagram_pages(skb->truesize);
if (skb->data < skb_network_header(skb))
__skb_pull(skb, skb_network_offset(skb));
while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
@@ -1218,6 +1228,7 @@ int ip_push_pending_frames(struct sock *
skb->len += tmp_skb->len;
skb->data_len += tmp_skb->len;
skb->truesize += tmp_skb->truesize;
+ send_page_size += sk_datagram_pages(tmp_skb->truesize);
__sock_put(tmp_skb->sk);
tmp_skb->destructor = NULL;
tmp_skb->sk = NULL;
@@ -1269,6 +1280,8 @@ int ip_push_pending_frames(struct sock *
/* Netfilter gets whole the not fragmented skb. */
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
skb->dst->dev, dst_output);
+ if (sk->sk_prot->memory_allocated)
+ atomic_sub(send_page_size, sk->sk_prot->memory_allocated);
if (err) {
if (err > 0)
err = inet->recverr ? net_xmit_errno(err) : 0;
@@ -1298,9 +1311,15 @@ void ip_flush_pending_frames(struct sock
{
struct inet_sock *inet = inet_sk(sk);
struct sk_buff *skb;
+ int num_flush_mem = 0;
- while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
+ while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
+ num_flush_mem += sk_datagram_pages(skb->truesize);
kfree_skb(skb);
+ }
+
+ if (sk->sk_prot->memory_allocated)
+ atomic_sub(num_flush_mem, sk->sk_prot->memory_allocated);
inet->cork.flags &= ~IPCORK_OPT;
kfree(inet->cork.opt);
Index: 2.6.23-rc8-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/udp.c
@@ -887,6 +887,9 @@ try_again:
err = ulen;
out_free:
+ atomic_sub(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
+
skb_free_datagram(sk, skb);
out:
return err;
@@ -894,6 +897,9 @@ out:
csum_copy_err:
UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
+ atomic_sub(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
+
skb_kill_datagram(sk, skb, flags);
if (noblock)
@@ -1019,6 +1025,9 @@ int udp_queue_rcv_skb(struct sock * sk,
goto drop;
}
+ atomic_add(sk_datagram_pages(skb->truesize),
+ sk->sk_prot->memory_allocated);
+
UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag);
return 0;
@@ -1443,6 +1452,7 @@ struct proto udp_prot = {
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.get_port = udp_v4_get_port,
+ .memory_allocated = &udp_memory_allocated,
.obj_size = sizeof(struct udp_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
Index: 2.6.23-rc8-udp_limit/net/ipv4/af_inet.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/af_inet.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/af_inet.c
@@ -126,13 +126,41 @@ extern void ip_mc_drop_socket(struct soc
static struct list_head inetsw[SOCK_MAX];
static DEFINE_SPINLOCK(inetsw_lock);
+/**
+ * __skb_queue_purge_and_sub_memory_allocated
+ * - empty a list and subtruct memory allocation counter
+ * @sk: sk
+ * @list: list to empty
+ * Delete all buffers on an &sk_buff list and subtruct the
+ * turesize of the sk_buff for memory accounting. Each buffer
+ * is removed from the list and one reference dropped. This
+ * function does not take the list lock and the caller must
+ * hold the relevant locks to use it.
+ */
+void __skb_queue_purge_and_sub_memory_allocated(struct sock *sk,
+ struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+ int purged_skb_size = 0;
+ while ((skb = __skb_dequeue(list)) != NULL) {
+ purged_skb_size += sk_datagram_pages(skb->truesize);
+ kfree_skb(skb);
+ }
+ atomic_sub(purged_skb_size, sk->sk_prot->memory_allocated);
+}
+
/* New destruction routine */
void inet_sock_destruct(struct sock *sk)
{
struct inet_sock *inet = inet_sk(sk);
- __skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_prot->memory_allocated && sk->sk_type != SOCK_STREAM)
+ __skb_queue_purge_and_sub_memory_allocated(sk,
+ &sk->sk_receive_queue);
+ else
+ __skb_queue_purge(&sk->sk_receive_queue);
+
__skb_queue_purge(&sk->sk_error_queue);
if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) {
^ permalink raw reply
* [RFC/PATCH 4/4] UDP memory usage accounting (take 4): memory limitation
From: Satoshi OSHIMA @ 2007-10-05 15:02 UTC (permalink / raw)
To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>
This patch introduces memory limitation for UDP.
signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>
Index: 2.6.23-rc9-udp_limit/include/net/udp.h
===================================================================
--- 2.6.23-rc9-udp_limit.orig/include/net/udp.h
+++ 2.6.23-rc9-udp_limit/include/net/udp.h
@@ -65,7 +65,10 @@ extern rwlock_t udp_hash_lock;
extern struct proto udp_prot;
+/* Used by memory accounting and capping */
+#define UDP_MIN_SKB_PAGES 4096
extern atomic_t udp_memory_allocated;
+extern int sysctl_udp_mem;
struct sk_buff;
Index: 2.6.23-rc9-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/udp.c
@@ -114,8 +114,10 @@ struct hlist_head udp_hash[UDP_HTABLE_SI
DEFINE_RWLOCK(udp_hash_lock);
atomic_t udp_memory_allocated;
+int sysctl_udp_mem = 0;
EXPORT_SYMBOL(udp_memory_allocated);
+EXPORT_SYMBOL(sysctl_udp_mem);
static int udp_port_rover;
@@ -1018,6 +1020,16 @@ int udp_queue_rcv_skb(struct sock * sk,
goto drop;
}
+ if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES) {
+ if ((atomic_read(sk->sk_prot->memory_allocated)
+ + sk_datagram_pages(skb->truesize))
+ >= sk->sk_prot->sysctl_mem[0]) {
+ UDP_INC_STATS_BH(UDP_MIB_RCVBUFERRORS,
+ up->pcflag);
+ goto drop;
+ }
+ }
+
if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
/* Note that an ENOMEM error is charged twice */
if (rc == -ENOMEM)
@@ -1453,6 +1465,7 @@ struct proto udp_prot = {
.unhash = udp_lib_unhash,
.get_port = udp_v4_get_port,
.memory_allocated = &udp_memory_allocated,
+ .sysctl_mem = &sysctl_udp_mem,
.obj_size = sizeof(struct udp_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
Index: 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/sysctl_net_ipv4.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
@@ -17,6 +17,7 @@
#include <net/ip.h>
#include <net/route.h>
#include <net/tcp.h>
+#include <net/udp.h>
#include <net/cipso_ipv4.h>
/* From af_inet.c */
@@ -599,6 +600,14 @@ ctl_table ipv4_table[] = {
.proc_handler = &proc_dointvec
},
{
+ .ctl_name = NET_UDP_MEM,
+ .procname = "udp_mem",
+ .data = &sysctl_udp_mem,
+ .maxlen = sizeof(sysctl_udp_mem),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
.ctl_name = NET_TCP_APP_WIN,
.procname = "tcp_app_win",
.data = &sysctl_tcp_app_win,
Index: 2.6.23-rc9-udp_limit/include/linux/sysctl.h
===================================================================
--- 2.6.23-rc9-udp_limit.orig/include/linux/sysctl.h
+++ 2.6.23-rc9-udp_limit/include/linux/sysctl.h
@@ -441,6 +441,7 @@ enum
NET_TCP_ALLOWED_CONG_CONTROL=123,
NET_TCP_MAX_SSTHRESH=124,
NET_TCP_FRTO_RESPONSE=125,
+ NET_UDP_MEM=126,
};
enum {
Index: 2.6.23-rc9-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/ip_output.c
@@ -75,6 +75,7 @@
#include <net/icmp.h>
#include <net/checksum.h>
#include <net/inetpeer.h>
+#include <net/udp.h>
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
@@ -910,6 +911,17 @@ alloc_new_skb:
if (datalen == length + fraggap)
alloclen += rt->u.dst.trailer_len;
+ if (sk->sk_prot->sysctl_mem)
+ if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+ if ((atomic_read(sk->sk_prot->memory_allocated)
+ + sk_datagram_pages(
+ SKB_DATA_ALIGN(alloclen + hh_len + 15)
+ + sizeof(struct sk_buff)))
+ >= sk->sk_prot->sysctl_mem[0]) {
+ err = -ENOBUFS;
+ goto error;
+ }
+
if (transhdrlen) {
skb = sock_alloc_send_skb(sk,
alloclen + hh_len + 15,
@@ -1009,6 +1021,15 @@ alloc_new_skb:
frag = &skb_shinfo(skb)->frags[i];
}
} else if (i < MAX_SKB_FRAGS) {
+ if (sk->sk_prot->sysctl_mem)
+ if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+ if ((atomic_read(sk->sk_prot->memory_allocated)
+ + sk_datagram_pages(PAGE_SIZE))
+ >= sk->sk_prot->sysctl_mem[0]) {
+ err = -ENOBUFS;
+ goto error;
+ }
+
if (atomic_read(&sk->sk_wmem_alloc) + PAGE_SIZE
> 2 * sk->sk_sndbuf) {
err = -ENOBUFS;
@@ -1126,6 +1147,17 @@ ssize_t ip_append_page(struct sock *sk,
fraggap = skb_prev->len - maxfraglen;
alloclen = fragheaderlen + hh_len + fraggap + 15;
+
+ if (sk->sk_prot->sysctl_mem)
+ if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+ if((atomic_read(sk->sk_prot->memory_allocated)
+ + sk_datagram_pages(alloclen
+ + sizeof(struct sk_buff)))
+ >= sk->sk_prot->sysctl_mem[0]) {
+ err = -ENOBUFS;
+ goto error;
+ }
+
skb = sock_wmalloc(sk, alloclen, 1, sk->sk_allocation);
if (unlikely(!skb)) {
err = -ENOBUFS;
^ permalink raw reply
* Re: [patch 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Jeff Garzik @ 2007-10-05 15:07 UTC (permalink / raw)
To: Ursula Braun; +Cc: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>
Did these patches change on resend? Or just addressees?
Jeff
^ permalink raw reply
* Re: [patch 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Ursula Braun1 @ 2007-10-05 15:35 UTC (permalink / raw)
To: Jeff Garzik; +Cc: davem, netdev, linux-s390
In-Reply-To: <47065321.9080403@garzik.org>
Jeff,
patches have not changed on resend, just the header part of [patch 5/5].
Ursula
Jeff Garzik
<jeff@garzik.org>
Sent by: To
linux-s390-owner@ Ursula Braun1/Germany/IBM@IBMDE
vger.kernel.org cc
davem@davemloft.net,
netdev@vger.kernel.org,
05.10.2007 17:07 linux-s390@vger.kernel.org
Subject
Re: [patch 0/5] s390: qeth and lcs
patches for 2.6.24 - RESEND
Did these patches change on resend? Or just addressees?
Jeff
-
To unsubscribe from this list: send the line "unsubscribe linux-s390" 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: [PATCH]: Third (final?) release of Sun Neptune driver
From: Stephen Hemminger @ 2007-10-05 15:35 UTC (permalink / raw)
To: David Miller
Cc: netdev, Ariel.Hendel, greg.onufer, jeff, Ashley.Saulsbury,
Matheos.Worku
In-Reply-To: <20071005.031209.57156822.davem@davemloft.net>
On Fri, 05 Oct 2007 03:12:09 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:
>
> I just checked the following into net-2.6.24, besides minor
> tweaks and bug fixes, this is probably what is going to Linus.
>
> I verified it builds no just on sparc64 but also even on 32-bit
> platforms such as x86. :-)
>
> Thanks!
Looks good, minor stuff...
Surprisingly low number of comments. Which is okay by me, but others
might find it harder. Perhaps some explanation of the queue stuff and
other hardware surprises would be useful.
> commit 147fd2f057af893f63df5bad497bb60ac3abbaab
> Author: David S. Miller <davem@sunset.davemloft.net>
> Date: Fri Oct 5 02:48:00 2007 -0700
>
> [NIU]: Add Sun Neptune ethernet driver.
>
> Signed-off-by: David S. Miller <davem@davemloft.net>
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 0e48b29..9c635a2 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2594,6 +2594,13 @@ config NETXEN_NIC
> help
> This enables the support for NetXen's Gigabit Ethernet card.
>
> +config NIU
> + tristate "Sun Neptune 10Gbit Ethernet support"
> + depends on PCI
> + help
> + This enables support for cards based upon Sun's
> + Neptune chipset.
> +
> config PASEMI_MAC
> tristate "PA Semi 1/10Gbit MAC"
> depends on PPC64 && PCI
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index eb5c655..d2e0f35 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -241,3 +241,4 @@ obj-$(CONFIG_NETCONSOLE) += netconsole.o
> obj-$(CONFIG_FS_ENET) += fs_enet/
>
> obj-$(CONFIG_NETXEN_NIC) += netxen/
> +obj-$(CONFIG_NIU) += niu.o
> diff --git a/drivers/net/niu.c b/drivers/net/niu.c
> new file mode 100644
> index 0000000..0dfabbe
> --- /dev/null
> +++ b/drivers/net/niu.c
> @@ -0,0 +1,7940 @@
> +/* niu.c: Neptune ethernet driver.
> + *
> + * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/netdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/etherdevice.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/bitops.h>
> +#include <linux/mii.h>
> +#include <linux/if_ether.h>
> +#include <linux/if_vlan.h>
> +#include <linux/ip.h>
> +#include <linux/in.h>
> +#include <linux/ipv6.h>
> +#include <linux/log2.h>
> +#include <linux/jiffies.h>
> +#include <linux/crc32.h>
> +
> +#include <asm/io.h>
> +
> +#ifdef CONFIG_SPARC64
> +#include <linux/of_device.h>
> +#endif
> +
> +#include "niu.h"
> +
> +#define DRV_MODULE_NAME "niu"
> +#define PFX DRV_MODULE_NAME ": "
> +#define DRV_MODULE_VERSION "0.5"
> +#define DRV_MODULE_RELDATE "October 5, 2007"
> +
> +static char version[] __devinitdata =
> + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
> +
> +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
> +MODULE_DESCRIPTION("NIU ethernet driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRV_MODULE_VERSION);
> +
> +#ifndef DMA_44BIT_MASK
> +#define DMA_44BIT_MASK 0x00000fffffffffffULL
> +#endif
> +
> +#ifndef readq
> +static u64 readq(void __iomem *reg)
> +{
> + return (((u64)readl(reg + 0x4UL) << 32) |
> + (u64)readl(reg));
> +}
> +
> +static void writeq(u64 val, void __iomem *reg)
> +{
> + writel(val & 0xffffffff, reg);
> + writel(val >> 32, reg + 0x4UL);
> +}
> +#endif
> +
> +static struct pci_device_id niu_pci_tbl[] = {
> + {PCI_DEVICE(PCI_VENDOR_ID_SUN, 0xabcd)},
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, niu_pci_tbl);
> +
> +#define NIU_TX_TIMEOUT (5 * HZ)
> +
> +#define nr64(reg) readq(np->regs + (reg))
> +#define nw64(reg, val) writeq((val), np->regs + (reg))
> +
> +#define nr64_mac(reg) readq(np->mac_regs + (reg))
> +#define nw64_mac(reg, val) writeq((val), np->mac_regs + (reg))
> +
> +#define nr64_ipp(reg) readq(np->regs + np->ipp_off + (reg))
> +#define nw64_ipp(reg, val) writeq((val), np->regs + np->ipp_off + (reg))
> +
> +#define nr64_pcs(reg) readq(np->regs + np->pcs_off + (reg))
> +#define nw64_pcs(reg, val) writeq((val), np->regs + np->pcs_off + (reg))
> +
> +#define nr64_xpcs(reg) readq(np->regs + np->xpcs_off + (reg))
> +#define nw64_xpcs(reg, val) writeq((val), np->regs + np->xpcs_off + (reg))
> +
> +static unsigned int niu_debug;
> +#define NIU_DEBUG_INTERRUPT 0x00000001
> +#define NIU_DEBUG_TX_WORK 0x00000002
> +#define NIU_DEBUG_RX_WORK 0x00000004
> +#define NIU_DEBUG_POLL 0x00000008
> +#define NIU_DEBUG_PROBE 0x00010000
> +#define NIU_DEBUG_MDIO 0x00020000
> +#define NIU_DEBUG_MII 0x00040000
> +#define NIU_DEBUG_INIT_HW 0x00080000
> +#define NIU_DEBUG_STOP_HW 0x00100000
These match with existing netif_msg_XXX and your
driver already has msglvl ethtool hook.
> +
> +module_param(niu_debug, int, 0);
> +MODULE_PARM_DESC(niu_debug,
> +"NIU bitmapped debugging message enable value:\n"
> +" 0x00000001 Log interrupt events\n"
> +" 0x00000002 Log TX work\n"
> +" 0x00000004 Log RX work\n"
> +" 0x00000008 Log NAPI poll\n"
> +" 0x00010000 Log device probe events\n"
> +" 0x00020000 Log MDIO reads and writes\n"
> +" 0x00040000 Log MII reads and writes\n"
> +" 0x00080000 Log HW initialization\n"
> +" 0x00100000 Log HW shutdown\n"
> +);
> +
> +#define niudbg(TYPE, f, a...) \
> +do { if (niu_debug & NIU_DEBUG_##TYPE) \
> + printk(KERN_ERR PFX f, ## a); \
Do you really want to print all these at KERN_ERR level?
> +} while (0)
> +
> +#define niu_lock_parent(np, flags) \
> + spin_lock_irqsave(&np->parent->lock, flags)
> +#define niu_unlock_parent(np, flags) \
> + spin_unlock_irqrestore(&np->parent->lock, flags)
> +
> +static int __niu_wait_bits_clear_mac(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay)
> +{
> + while (--limit >= 0) {
> + u64 val = nr64_mac(reg);
> +
> + if (!(val & bits))
> + break;
> + udelay(delay);
> + }
> + if (limit < 0)
> + return -ENODEV;
> + return 0;
> +}
> +
> +static int __niu_set_and_wait_clear_mac(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay,
> + const char *reg_name)
> +{
> + int err;
> +
> + nw64_mac(reg, bits);
> + err = __niu_wait_bits_clear_mac(np, reg, bits, limit, delay);
> + if (err)
> + printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> + "would not clear, val[%llx]\n",
> + np->dev->name, (unsigned long long) bits, reg_name,
> + (unsigned long long) nr64_mac(reg));
> + return err;
> +}
> +
> +#define niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> + __niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static int __niu_wait_bits_clear_ipp(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay)
> +{
> + while (--limit >= 0) {
> + u64 val = nr64_ipp(reg);
> +
> + if (!(val & bits))
> + break;
> + udelay(delay);
> + }
> + if (limit < 0)
> + return -ENODEV;
> + return 0;
> +}
> +
> +static int __niu_set_and_wait_clear_ipp(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay,
> + const char *reg_name)
> +{
> + int err;
> + u64 val;
> +
> + val = nr64_ipp(reg);
> + val |= bits;
> + nw64_ipp(reg, val);
> +
> + err = __niu_wait_bits_clear_ipp(np, reg, bits, limit, delay);
> + if (err)
> + printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> + "would not clear, val[%llx]\n",
> + np->dev->name, (unsigned long long) bits, reg_name,
> + (unsigned long long) nr64_ipp(reg));
> + return err;
> +}
> +
> +#define niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> + __niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static int __niu_wait_bits_clear(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay)
> +{
> + BUILD_BUG_ON(limit <= 0 || delay < 0);
redundant you already checked
> + while (--limit >= 0) {
> + u64 val = nr64(reg);
> +
> + if (!(val & bits))
> + break;
> + udelay(delay);
> + }
> + if (limit < 0)
> + return -ENODEV;
> + return 0;
> +}
> +
> +#define niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY) \
> +({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> + __niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY); \
> +})
> +
> +static int __niu_set_and_wait_clear(struct niu *np, unsigned long reg,
> + u64 bits, int limit, int delay,
> + const char *reg_name)
> +{
> + int err;
> +
> + nw64(reg, bits);
> + err = __niu_wait_bits_clear(np, reg, bits, limit, delay);
> + if (err)
> + printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> + "would not clear, val[%llx]\n",
> + np->dev->name, (unsigned long long) bits, reg_name,
> + (unsigned long long) nr64(reg));
> + return err;
> +}
> +
> +#define niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({ BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> + __niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static void niu_ldg_rearm(struct niu *np, struct niu_ldg *lp, int on)
> +{
> + u64 val = (u64) lp->timer;
> +
> + if (on)
> + val |= LDG_IMGMT_ARM;
> +
> + nw64(LDG_IMGMT(lp->ldg_num), val);
> +}
> +
> +static int niu_ldn_irq_enable(struct niu *np, int ldn, int on)
> +{
> + unsigned long mask_reg, bits;
> + u64 val;
> +
> + if (ldn < 0 || ldn > LDN_MAX)
> + return -EINVAL;
> +
> + if (ldn < 64) {
> + mask_reg = LD_IM0(ldn);
> + bits = LD_IM0_MASK;
> + } else {
> + mask_reg = LD_IM1(ldn - 64);
> + bits = LD_IM1_MASK;
> + }
> +
> + val = nr64(mask_reg);
> + if (on)
> + val &= ~bits;
> + else
> + val |= bits;
> + nw64(mask_reg, val);
> +
> + return 0;
> +}
> +
> +static int niu_enable_ldn_in_ldg(struct niu *np, struct niu_ldg *lp, int on)
> +{
> + struct niu_parent *parent = np->parent;
> + int i;
> +
> + for (i = 0; i <= LDN_MAX; i++) {
> + int err;
> +
> + if (parent->ldg_map[i] != lp->ldg_num)
> + continue;
> +
> + err = niu_ldn_irq_enable(np, i, on);
> + if (err)
> + return err;
> + }
> + return 0;
> +}
> +
> +static int niu_enable_interrupts(struct niu *np, int on)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_ldg; i++) {
> + struct niu_ldg *lp = &np->ldg[i];
> + int err;
> +
> + err = niu_enable_ldn_in_ldg(np, lp, on);
> + if (err)
> + return err;
> + }
> + for (i = 0; i < np->num_ldg; i++)
> + niu_ldg_rearm(np, &np->ldg[i], on);
> +
> + return 0;
> +}
> +
> +static u32 phy_encode(u32 type, int port)
> +{
> + return (type << (port * 2));
> +}
> +
> +static u32 phy_decode(u32 val, int port)
> +{
> + return (val >> (port * 2)) & PORT_TYPE_MASK;
> +}
> +
> +static int mdio_wait(struct niu *np)
> +{
> + int limit = 1000;
> + u64 val;
> +
> + while (--limit > 0) {
> + val = nr64(MIF_FRAME_OUTPUT);
> + if ((val >> MIF_FRAME_OUTPUT_TA_SHIFT) & 0x1)
> + return val & MIF_FRAME_OUTPUT_DATA;
> +
> + udelay(10);
> + }
> +
> + return -ENODEV;
> +}
> +
> +static int mdio_read(struct niu *np, int port, int dev, int reg)
> +{
> + int err;
> +
> + nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
> + err = mdio_wait(np);
> + if (err < 0)
> + return err;
> +
> + nw64(MIF_FRAME_OUTPUT, MDIO_READ_OP(port, dev));
> + return mdio_wait(np);
> +}
> +
> +static int mdio_write(struct niu *np, int port, int dev, int reg, int data)
> +{
> + int err;
> +
> + nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
> + err = mdio_wait(np);
> + if (err < 0)
> + return err;
> +
> + nw64(MIF_FRAME_OUTPUT, MDIO_WRITE_OP(port, dev, data));
> + err = mdio_wait(np);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +static int mii_read(struct niu *np, int port, int reg)
> +{
> + nw64(MIF_FRAME_OUTPUT, MII_READ_OP(port, reg));
> + return mdio_wait(np);
> +}
> +
> +static int mii_write(struct niu *np, int port, int reg, int data)
> +{
> + int err;
> +
> + nw64(MIF_FRAME_OUTPUT, MII_WRITE_OP(port, reg, data));
> + err = mdio_wait(np);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +static int esr2_set_tx_cfg(struct niu *np, unsigned long channel, u32 val)
> +{
> + int err;
> +
> + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> + ESR2_TI_PLL_TX_CFG_L(channel),
> + val & 0xffff);
> + if (!err)
> + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> + ESR2_TI_PLL_TX_CFG_H(channel),
> + val >> 16);
> + return err;
> +}
> +
> +static int esr2_set_rx_cfg(struct niu *np, unsigned long channel, u32 val)
> +{
> + int err;
> +
> + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> + ESR2_TI_PLL_RX_CFG_L(channel),
> + val & 0xffff);
> + if (!err)
> + err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> + ESR2_TI_PLL_RX_CFG_H(channel),
> + val >> 16);
> + return err;
> +}
> +
> +/* Mode is always 10G fiber. */
> +static int serdes_init_niu(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u32 tx_cfg, rx_cfg;
> + unsigned long i;
> +
> + tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV);
> + rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT |
> + PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH |
> + PLL_RX_CFG_EQ_LP_ADAPTIVE);
> +
> + if (lp->loopback_mode == LOOPBACK_PHY) {
> + u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS;
> +
> + mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> + ESR2_TI_PLL_TEST_CFG_L, test_cfg);
> +
> + tx_cfg |= PLL_TX_CFG_ENTEST;
> + rx_cfg |= PLL_RX_CFG_ENTEST;
> + }
> +
> + for (i = 0; i < 4; i++) {
> + int err = esr2_set_tx_cfg(np, i, tx_cfg);
> + if (err)
> + return err;
> + }
> +
> + for (i = 0; i < 4; i++) {
> + int err = esr2_set_rx_cfg(np, i, rx_cfg);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int esr_read_rxtx_ctrl(struct niu *np, unsigned long chan, u32 *val)
> +{
> + int err;
> +
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, ESR_RXTX_CTRL_L(chan));
> + if (err >= 0) {
> + *val = (err & 0xffff);
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_CTRL_H(chan));
> + if (err >= 0)
> + *val |= ((err & 0xffff) << 16);
> + err = 0;
> + }
> + return err;
> +}
> +
> +static int esr_read_glue0(struct niu *np, unsigned long chan, u32 *val)
> +{
> + int err;
> +
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_GLUE_CTRL0_L(chan));
> + if (err >= 0) {
> + *val = (err & 0xffff);
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_GLUE_CTRL0_H(chan));
> + if (err >= 0) {
> + *val |= ((err & 0xffff) << 16);
> + err = 0;
> + }
> + }
> + return err;
> +}
> +
> +static int esr_read_reset(struct niu *np, u32 *val)
> +{
> + int err;
> +
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_L);
> + if (err >= 0) {
> + *val = (err & 0xffff);
> + err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_H);
> + if (err >= 0) {
> + *val |= ((err & 0xffff) << 16);
> + err = 0;
> + }
> + }
> + return err;
> +}
> +
> +static int esr_write_rxtx_ctrl(struct niu *np, unsigned long chan, u32 val)
> +{
> + int err;
> +
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_CTRL_L(chan), val & 0xffff);
> + if (!err)
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_CTRL_H(chan), (val >> 16));
> + return err;
> +}
> +
> +static int esr_write_glue0(struct niu *np, unsigned long chan, u32 val)
> +{
> + int err;
> +
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_GLUE_CTRL0_L(chan), val & 0xffff);
> + if (!err)
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_GLUE_CTRL0_H(chan), (val >> 16));
> + return err;
> +}
> +
> +static int esr_reset(struct niu *np)
> +{
> + u32 reset;
> + int err;
> +
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_L, 0x0000);
> + if (err)
> + return err;
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_H, 0xffff);
> + if (err)
> + return err;
> + udelay(200);
> +
> +
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_L, 0xffff);
> + if (err)
> + return err;
> + udelay(200);
> +
> + err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> + ESR_RXTX_RESET_CTRL_H, 0x0000);
> + if (err)
> + return err;
> + udelay(200);
> +
> + err = esr_read_reset(np, &reset);
> + if (err)
> + return err;
> + if (reset != 0) {
> + printk(KERN_ERR PFX "Port %u ESR_RESET did not clear [%08x]\n",
> + np->port, reset);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int serdes_init_10g(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + unsigned long ctrl_reg, test_cfg_reg, i;
> + u64 ctrl_val, test_cfg_val, sig, mask, val;
> + int err;
> +
> + switch (np->port) {
> + case 0:
> + ctrl_reg = ENET_SERDES_0_CTRL_CFG;
> + test_cfg_reg = ENET_SERDES_0_TEST_CFG;
> + break;
> + case 1:
> + ctrl_reg = ENET_SERDES_1_CTRL_CFG;
> + test_cfg_reg = ENET_SERDES_1_TEST_CFG;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + ctrl_val = (ENET_SERDES_CTRL_SDET_0 |
> + ENET_SERDES_CTRL_SDET_1 |
> + ENET_SERDES_CTRL_SDET_2 |
> + ENET_SERDES_CTRL_SDET_3 |
> + (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) |
> + (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) |
> + (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) |
> + (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) |
> + (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) |
> + (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) |
> + (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) |
> + (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT));
> + test_cfg_val = 0;
> +
> + if (lp->loopback_mode == LOOPBACK_PHY) {
> + test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
> + ENET_SERDES_TEST_MD_0_SHIFT) |
> + (ENET_TEST_MD_PAD_LOOPBACK <<
> + ENET_SERDES_TEST_MD_1_SHIFT) |
> + (ENET_TEST_MD_PAD_LOOPBACK <<
> + ENET_SERDES_TEST_MD_2_SHIFT) |
> + (ENET_TEST_MD_PAD_LOOPBACK <<
> + ENET_SERDES_TEST_MD_3_SHIFT));
> + }
> +
> + nw64(ctrl_reg, ctrl_val);
> + nw64(test_cfg_reg, test_cfg_val);
> +
> + for (i = 0; i < 4; i++) {
> + u32 rxtx_ctrl, glue0;
> + int err;
> +
> + err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl);
> + if (err)
> + return err;
> + err = esr_read_glue0(np, i, &glue0);
> + if (err)
> + return err;
> +
> + rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO);
> + rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH |
> + (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT));
> +
> + glue0 &= ~(ESR_GLUE_CTRL0_SRATE |
> + ESR_GLUE_CTRL0_THCNT |
> + ESR_GLUE_CTRL0_BLTIME);
> + glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB |
> + (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) |
> + (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) |
> + (BLTIME_300_CYCLES <<
> + ESR_GLUE_CTRL0_BLTIME_SHIFT));
> +
> + err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl);
> + if (err)
> + return err;
> + err = esr_write_glue0(np, i, glue0);
> + if (err)
> + return err;
> + }
> +
> + err = esr_reset(np);
> + if (err)
> + return err;
> +
> + sig = nr64(ESR_INT_SIGNALS);
> + switch (np->port) {
> + case 0:
> + mask = ESR_INT_SIGNALS_P0_BITS;
> + val = (ESR_INT_SRDY0_P0 |
> + ESR_INT_DET0_P0 |
> + ESR_INT_XSRDY_P0 |
> + ESR_INT_XDP_P0_CH3 |
> + ESR_INT_XDP_P0_CH2 |
> + ESR_INT_XDP_P0_CH1 |
> + ESR_INT_XDP_P0_CH0);
> + break;
> +
> + case 1:
> + mask = ESR_INT_SIGNALS_P1_BITS;
> + val = (ESR_INT_SRDY0_P1 |
> + ESR_INT_DET0_P1 |
> + ESR_INT_XSRDY_P1 |
> + ESR_INT_XDP_P1_CH3 |
> + ESR_INT_XDP_P1_CH2 |
> + ESR_INT_XDP_P1_CH1 |
> + ESR_INT_XDP_P1_CH0);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + if ((sig & mask) != val) {
> + printk(KERN_ERR PFX "Port %u signal bits [%08x] are not "
> + "[%08x]\n", np->port, (int) (sig & mask), (int) val);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int serdes_init_1g(struct niu *np)
> +{
> + u64 val;
> +
> + val = nr64(ENET_SERDES_1_PLL_CFG);
> + val &= ~ENET_SERDES_PLL_FBDIV2;
> + switch (np->port) {
> + case 0:
> + val |= ENET_SERDES_PLL_HRATE0;
> + break;
> + case 1:
> + val |= ENET_SERDES_PLL_HRATE1;
> + break;
> + case 2:
> + val |= ENET_SERDES_PLL_HRATE2;
> + break;
> + case 3:
> + val |= ENET_SERDES_PLL_HRATE3;
> + break;
> + default:
> + return -EINVAL;
> + }
> + nw64(ENET_SERDES_1_PLL_CFG, val);
> +
> + return 0;
> +}
> +
> +static int bcm8704_reset(struct niu *np)
> +{
> + int err, limit;
> +
> + err = mdio_read(np, np->phy_addr,
> + BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
> + if (err < 0)
> + return err;
> + err |= BMCR_RESET;
> + err = mdio_write(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> + MII_BMCR, err);
> + if (err)
> + return err;
> +
> + limit = 1000;
> + while (--limit >= 0) {
> + err = mdio_read(np, np->phy_addr,
> + BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
> + if (err < 0)
> + return err;
> + if (!(err & BMCR_RESET))
> + break;
> + }
> + if (limit < 0) {
> + printk(KERN_ERR PFX "Port %u PHY will not reset "
> + "(bmcr=%04x)\n", np->port, (err & 0xffff));
> + return -ENODEV;
> + }
> + return 0;
> +}
> +
> +/* When written, certain PHY registers need to be read back twice
> + * in order for the bits to settle properly.
> + */
> +static int bcm8704_user_dev3_readback(struct niu *np, int reg)
> +{
> + int err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
> + if (err < 0)
> + return err;
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
> + if (err < 0)
> + return err;
> + return 0;
> +}
> +
> +static int bcm8704_init_user_dev3(struct niu *np)
> +{
> + int err;
> +
> + err = mdio_write(np, np->phy_addr,
> + BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL,
> + (USER_CONTROL_OPTXRST_LVL |
> + USER_CONTROL_OPBIASFLT_LVL |
> + USER_CONTROL_OBTMPFLT_LVL |
> + USER_CONTROL_OPPRFLT_LVL |
> + USER_CONTROL_OPTXFLT_LVL |
> + USER_CONTROL_OPRXLOS_LVL |
> + USER_CONTROL_OPRXFLT_LVL |
> + USER_CONTROL_OPTXON_LVL |
> + (0x3f << USER_CONTROL_RES1_SHIFT)));
> + if (err)
> + return err;
> +
> + err = mdio_write(np, np->phy_addr,
> + BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL,
> + (USER_PMD_TX_CTL_XFP_CLKEN |
> + (1 << USER_PMD_TX_CTL_TX_DAC_TXD_SH) |
> + (2 << USER_PMD_TX_CTL_TX_DAC_TXCK_SH) |
> + USER_PMD_TX_CTL_TSCK_LPWREN));
> + if (err)
> + return err;
> +
> + err = bcm8704_user_dev3_readback(np, BCM8704_USER_CONTROL);
> + if (err)
> + return err;
> + err = bcm8704_user_dev3_readback(np, BCM8704_USER_PMD_TX_CONTROL);
> + if (err)
> + return err;
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_OPT_DIGITAL_CTRL);
> + if (err < 0)
> + return err;
> + err &= ~USER_ODIG_CTRL_GPIOS;
> + err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT);
> + err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_OPT_DIGITAL_CTRL, err);
> + if (err)
> + return err;
> +
> + udelay(1000000);
> +
> + return 0;
> +}
> +
> +static int xcvr_init_10g(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u16 analog_stat0, tx_alarm_status;
> + int err;
> + u64 val;
> +
> + val = nr64_mac(XMAC_CONFIG);
> + val &= ~XMAC_CONFIG_LED_POLARITY;
> + val |= XMAC_CONFIG_FORCE_LED_ON;
> + nw64_mac(XMAC_CONFIG, val);
> +
> + /* XXX shared resource, lock parent XXX */
> + val = nr64(MIF_CONFIG);
> + val |= MIF_CONFIG_INDIRECT_MODE;
> + nw64(MIF_CONFIG, val);
> +
> + err = bcm8704_reset(np);
> + if (err)
> + return err;
> +
> + err = bcm8704_init_user_dev3(np);
> + if (err)
> + return err;
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> + MII_BMCR);
> + if (err < 0)
> + return err;
> + err &= ~BMCR_LOOPBACK;
> +
> + if (lp->loopback_mode == LOOPBACK_MAC)
> + err |= BMCR_LOOPBACK;
> +
> + err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> + MII_BMCR, err);
> + if (err)
> + return err;
> +
> +#if 1
> + err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
> + MII_STAT1000);
> + if (err < 0)
> + return err;
> + printk(KERN_INFO PFX "Port %u PMA_PMD(MII_STAT1000) [%04x]\n",
> + np->port, err);
pr_info() ?
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 0x20);
> + if (err < 0)
> + return err;
> + printk(KERN_INFO PFX "Port %u USER_DEV3(0x20) [%04x]\n",
> + np->port, err);
pr_info() ?
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> + MII_NWAYTEST);
> + if (err < 0)
> + return err;
> + printk(KERN_INFO PFX "Port %u PHYXS(MII_NWAYTEST) [%04x]\n",
> + np->port, err);
> +#endif
> +
> + /* XXX dig this out it might not be so useful XXX */
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_ANALOG_STATUS0);
> + if (err < 0)
> + return err;
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_ANALOG_STATUS0);
> + if (err < 0)
> + return err;
> + analog_stat0 = err;
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_TX_ALARM_STATUS);
> + if (err < 0)
> + return err;
> + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> + BCM8704_USER_TX_ALARM_STATUS);
> + if (err < 0)
> + return err;
> + tx_alarm_status = err;
> +
> + if (analog_stat0 != 0x03fc) {
> + if ((analog_stat0 == 0x43bc) && (tx_alarm_status != 0)) {
> + printk(KERN_INFO PFX "Port %u cable not connected "
> + "or bad cable.\n", np->port);
pr_info() ?
> + } else if (analog_stat0 == 0x639c) {
> + printk(KERN_INFO PFX "Port %u optical module is bad "
> + "or missing.\n", np->port);
pr_info() ?
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int mii_reset(struct niu *np)
> +{
> + int limit, err;
> +
> + err = mii_write(np, np->phy_addr, MII_BMCR, BMCR_RESET);
> +
> + limit = 1000;
> + while (--limit >= 0) {
> + err = mii_read(np, np->phy_addr, MII_BMCR);
> + if (err < 0)
> + return err;
> + if (!(err & BMCR_RESET))
> + break;
> + }
> + if (limit < 0) {
> + printk(KERN_ERR PFX "Port %u MII would not reset, "
> + "bmcr[%04x]\n", np->port, err);
> + return -ENODEV;
> + }
> +
> + err = mii_write(np, np->phy_addr, MII_BMCR, 0);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static int mii_init_common(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u16 bmcr, bmsr, adv, estat;
> + int err;
> +
> + err = mii_reset(np);
> + if (err)
> + return err;
> +
> + err = mii_read(np, np->phy_addr, MII_BMSR);
> + if (err < 0)
> + return err;
> + bmsr = err;
> +
> + estat = 0;
> + if (bmsr & BMSR_ESTATEN) {
> + err = mii_read(np, np->phy_addr, MII_ESTATUS);
> + if (err < 0)
> + return err;
> + estat = err;
> + }
> +
> + bmcr = 0;
> +
> + if (lp->loopback_mode == LOOPBACK_MAC) {
> + bmcr |= BMCR_LOOPBACK;
> + bmcr &= ~BMCR_ANENABLE;
> + if (lp->active_speed == SPEED_1000)
> + bmcr |= BMCR_SPEED1000;
> + if (lp->active_duplex == DUPLEX_FULL)
> + bmcr |= BMCR_FULLDPLX;
> + } else {
> + bmcr &= ~BMCR_LOOPBACK;
> + }
> + if (lp->loopback_mode == LOOPBACK_PHY) {
> + u16 aux;
> +
> + aux = (BCM5464R_AUX_CTL_EXT_LB |
> + BCM5464R_AUX_CTL_WRITE_1);
> + err = mii_write(np, np->phy_addr, BCM5464R_AUX_CTL, aux);
> + if (err)
> + return err;
> + }
> +
> + /* XXX configurable XXX */
> + adv = 0;
> + if (bmsr & BMSR_10HALF)
> + adv |= ADVERTISE_10HALF;
> + if (bmsr & BMSR_10FULL)
> + adv |= ADVERTISE_10FULL;
> + if (bmsr & BMSR_100HALF)
> + adv |= ADVERTISE_100HALF;
> + if (bmsr & BMSR_100FULL)
> + adv |= ADVERTISE_100FULL;
> + if (bmsr & BMSR_100BASE4)
> + adv |= ADVERTISE_100BASE4;
> + adv |= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
> + err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv);
> + if (err)
> + return err;
> +
> + if (bmsr & BMSR_ESTATEN) {
> + u16 ctrl1000;
> +
> + ctrl1000 = (BCM5464R_CTRL1000_AS_MASTER |
> + BCM5464R_CTRL1000_ENABLE_AS_MASTER);
> + if (estat & ESTATUS_1000_TFULL)
> + ctrl1000 |= ADVERTISE_1000FULL;
> + if (estat & ESTATUS_1000_THALF)
> + ctrl1000 |= ADVERTISE_1000HALF;
> + err = mii_write(np, np->phy_addr, MII_CTRL1000, ctrl1000);
> + if (err)
> + return err;
> + }
> + bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
> +
> + err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
> + if (err)
> + return err;
> +
> + err = mii_read(np, np->phy_addr, MII_BMCR);
> + if (err < 0)
> + return err;
> + err = mii_read(np, np->phy_addr, MII_BMSR);
> + if (err < 0)
> + return err;
> +#if 0
> + printk(KERN_ERR PFX "Port %u after MII init bmcr[%04x] bmsr[%04x]\n",
> + np->port, bmcr, bmsr);
> +#endif
> +
> + return 0;
> +}
> +
> +static int xcvr_init_1g(struct niu *np)
> +{
> + u64 val;
> +
> + /* XXX shared resource, lock parent XXX */
> + val = nr64(MIF_CONFIG);
> + val &= ~MIF_CONFIG_INDIRECT_MODE;
> + nw64(MIF_CONFIG, val);
> +
> + return mii_init_common(np);
> +}
> +
> +static int niu_xcvr_init(struct niu *np)
> +{
> + const struct niu_phy_ops *ops = np->phy_ops;
> + int err;
> +
> + err = 0;
> + if (ops->xcvr_init)
> + err = ops->xcvr_init(np);
> +
> + return err;
> +}
> +
> +static int niu_serdes_init(struct niu *np)
> +{
> + const struct niu_phy_ops *ops = np->phy_ops;
> + int err;
> +
> + err = 0;
> + if (ops->serdes_init)
> + err = ops->serdes_init(np);
> +
> + return err;
> +}
> +
> +static void niu_init_xif(struct niu *);
> +
> +static int niu_link_status_common(struct niu *np, int link_up)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + struct net_device *dev = np->dev;
> + unsigned long flags;
> +
> + if (!netif_carrier_ok(dev) && link_up) {
> + printk(KERN_INFO PFX "%s: Link is up at %s, %s duplex\n",
> + dev->name,
> + (lp->active_speed == SPEED_10000 ?
> + "10Gb/sec" :
> + (lp->active_speed == SPEED_1000 ?
> + "1Gb/sec" :
> + (lp->active_speed == SPEED_100 ?
> + "100Mbit/sec" : "10Mbit/sec"))),
> + (lp->active_duplex == DUPLEX_FULL ?
> + "full" : "half"));
pr_info() ?
> +
> + spin_lock_irqsave(&np->lock, flags);
> + niu_init_xif(np);
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + netif_carrier_on(dev);
> + } else if (netif_carrier_ok(dev) && !link_up) {
> + printk(KERN_WARNING PFX "%s: Link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + return 0;
> +}
> +
> +static int link_status_10g(struct niu *np, int *link_up_p)
> +{
> + unsigned long flags;
> + int err, link_up;
> +
> + if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> + return -EINVAL;
> +
> + link_up = 0;
> +
> + spin_lock_irqsave(&np->lock, flags);
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
> + BCM8704_PMD_RCV_SIGDET);
> + if (err < 0)
> + return err;
> + if (!(err & PMD_RCV_SIGDET_GLOBAL))
> + goto out;
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> + BCM8704_PCS_10G_R_STATUS);
> + if (err < 0)
> + return err;
> + if (!(err & PCS_10G_R_STATUS_BLK_LOCK))
> + goto out;
> +
> + err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> + BCM8704_PHYXS_XGXS_LANE_STAT);
> + if (err < 0)
> + return err;
> +
> + if (err != (PHYXS_XGXS_LANE_STAT_ALINGED |
> + PHYXS_XGXS_LANE_STAT_MAGIC |
> + PHYXS_XGXS_LANE_STAT_LANE3 |
> + PHYXS_XGXS_LANE_STAT_LANE2 |
> + PHYXS_XGXS_LANE_STAT_LANE1 |
> + PHYXS_XGXS_LANE_STAT_LANE0))
> + goto out;
> +
> + link_up = 1;
> + np->link_config.active_speed = SPEED_10000;
> + np->link_config.active_duplex = DUPLEX_FULL;
> +
> +out:
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + *link_up_p = link_up;
> + return 0;
> +}
> +
> +static int link_status_1g(struct niu *np, int *link_up_p)
> +{
> + u16 current_speed, bmsr;
> + unsigned long flags;
> + u8 current_duplex;
> + int err, link_up;
> +
> + if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> + return -EINVAL;
> +
> + link_up = 0;
> + current_speed = SPEED_INVALID;
> + current_duplex = DUPLEX_INVALID;
> +
> + spin_lock_irqsave(&np->lock, flags);
> +
> + err = mii_read(np, np->phy_addr, MII_BMSR);
> + if (err < 0)
> + goto out;
> +
> + bmsr = err;
> + if (bmsr & BMSR_LSTATUS) {
> + u16 adv, lpa, common, estat;
> +
> + err = mii_read(np, np->phy_addr, MII_ADVERTISE);
> + if (err < 0)
> + goto out;
> + adv = err;
> +
> + err = mii_read(np, np->phy_addr, MII_LPA);
> + if (err < 0)
> + goto out;
> + lpa = err;
> +
> + common = adv & lpa;
> +
> + err = mii_read(np, np->phy_addr, MII_ESTATUS);
> + if (err < 0)
> + goto out;
> + estat = err;
> +
> + if (estat & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {
> + current_speed = SPEED_1000;
> + if (estat == ESTATUS_1000_TFULL)
> + current_duplex = DUPLEX_FULL;
> + else
> + current_duplex = DUPLEX_HALF;
> + } else {
> + if (common & ADVERTISE_100BASE4) {
> + current_speed = SPEED_100;
> + current_duplex = DUPLEX_HALF;
> + } else if (common & ADVERTISE_100FULL) {
> + current_speed = SPEED_100;
> + current_duplex = DUPLEX_FULL;
> + } else if (common & ADVERTISE_100HALF) {
> + current_speed = SPEED_100;
> + current_duplex = DUPLEX_HALF;
> + } else if (common & ADVERTISE_10FULL) {
> + current_speed = SPEED_10;
> + current_duplex = DUPLEX_FULL;
> + } else if (common & ADVERTISE_10HALF) {
> + current_speed = SPEED_10;
> + current_duplex = DUPLEX_HALF;
> + } else
> + goto out;
> + }
> + link_up = 1;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + *link_up_p = link_up;
> + return 0;
> +}
> +
> +static int niu_link_status(struct niu *np, int *link_up_p)
> +{
> + const struct niu_phy_ops *ops = np->phy_ops;
> + int err;
> +
> + err = 0;
> + if (ops->link_status)
> + err = ops->link_status(np, link_up_p);
> +
> + return err;
> +}
> +
> +static void niu_timer(unsigned long __opaque)
> +{
> + struct niu *np = (struct niu *) __opaque;
> + unsigned long off;
> + int err, link_up;
> +
> + err = niu_link_status(np, &link_up);
> + if (!err)
> + niu_link_status_common(np, link_up);
> +
> + if (netif_carrier_ok(np->dev))
> + off = 5 * HZ;
> + else
> + off = 1 * HZ;
> + np->timer.expires = jiffies + off;
> +
> + add_timer(&np->timer);
> +}
> +
> +static const struct niu_phy_ops phy_ops_10g_fiber_niu = {
> + .serdes_init = serdes_init_niu,
> + .xcvr_init = xcvr_init_10g,
> + .link_status = link_status_10g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_10g_fiber = {
> + .serdes_init = serdes_init_10g,
> + .xcvr_init = xcvr_init_10g,
> + .link_status = link_status_10g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_10g_copper = {
> + .serdes_init = serdes_init_10g,
> + .link_status = link_status_10g, /* XXX */
> +};
> +
> +static const struct niu_phy_ops phy_ops_1g_fiber = {
> + .serdes_init = serdes_init_1g,
> + .xcvr_init = xcvr_init_1g,
> + .link_status = link_status_1g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_1g_copper = {
> + .xcvr_init = xcvr_init_1g,
> + .link_status = link_status_1g,
> +};
> +
> +struct niu_phy_template {
> + const struct niu_phy_ops *ops;
> + u32 phy_addr_base;
> +};
> +
> +static const struct niu_phy_template phy_template_niu = {
> + .ops = &phy_ops_10g_fiber_niu,
> + .phy_addr_base = 16,
> +};
> +
> +static const struct niu_phy_template phy_template_10g_fiber = {
> + .ops = &phy_ops_10g_fiber,
> + .phy_addr_base = 8,
> +};
> +
> +static const struct niu_phy_template phy_template_10g_copper = {
> + .ops = &phy_ops_10g_copper,
> + .phy_addr_base = 10,
> +};
> +
> +static const struct niu_phy_template phy_template_1g_fiber = {
> + .ops = &phy_ops_1g_fiber,
> + .phy_addr_base = 0,
> +};
> +
> +static const struct niu_phy_template phy_template_1g_copper = {
> + .ops = &phy_ops_1g_copper,
> + .phy_addr_base = 0,
> +};
> +
> +static int niu_determine_phy_disposition(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + u8 plat_type = parent->plat_type;
> + const struct niu_phy_template *tp;
> + u32 phy_addr_off = 0;
> +
> + if (plat_type == PLAT_TYPE_NIU) {
> + tp = &phy_template_niu;
> + phy_addr_off += np->port;
> + } else {
> + switch (np->flags & (NIU_FLAGS_10G | NIU_FLAGS_FIBER)) {
> + case 0:
> + /* 1G copper */
> + tp = &phy_template_1g_copper;
> + if (plat_type == PLAT_TYPE_VF_P0)
> + phy_addr_off = 10;
> + else if (plat_type == PLAT_TYPE_VF_P1)
> + phy_addr_off = 26;
> +
> + phy_addr_off += (np->port ^ 0x3);
> + break;
> +
> + case NIU_FLAGS_10G:
> + /* 10G copper */
> + tp = &phy_template_1g_copper;
> + break;
> +
> + case NIU_FLAGS_FIBER:
> + /* 1G fiber */
> + tp = &phy_template_1g_fiber;
> + break;
> +
> + case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
> + /* 10G fiber */
> + tp = &phy_template_10g_fiber;
> + if (plat_type == PLAT_TYPE_VF_P0 ||
> + plat_type == PLAT_TYPE_VF_P1)
> + phy_addr_off = 8;
> + phy_addr_off += np->port;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + }
> +
> + np->phy_ops = tp->ops;
> + np->phy_addr = tp->phy_addr_base + phy_addr_off;
> +
> + return 0;
> +}
> +
> +static int niu_init_link(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + int err, ignore;
> +
> + if (parent->plat_type == PLAT_TYPE_NIU) {
> + err = niu_xcvr_init(np);
> + if (err)
> + return err;
> + msleep(200);
> + }
> + err = niu_serdes_init(np);
> + if (err)
> + return err;
> + msleep(200);
> + err = niu_xcvr_init(np);
> + if (!err)
> + niu_link_status(np, &ignore);
> + return 0;
> +}
> +
> +static void niu_set_primary_mac(struct niu *np, unsigned char *addr)
> +{
> + u16 reg0 = addr[4] << 8 | addr[5];
> + u16 reg1 = addr[2] << 8 | addr[3];
> + u16 reg2 = addr[0] << 8 | addr[1];
> +
> + if (np->flags & NIU_FLAGS_XMAC) {
> + nw64_mac(XMAC_ADDR0, reg0);
> + nw64_mac(XMAC_ADDR1, reg1);
> + nw64_mac(XMAC_ADDR2, reg2);
> + } else {
> + nw64_mac(BMAC_ADDR0, reg0);
> + nw64_mac(BMAC_ADDR1, reg1);
> + nw64_mac(BMAC_ADDR2, reg2);
> + }
> +}
> +
> +static int niu_num_alt_addr(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + return XMAC_NUM_ALT_ADDR;
> + else
> + return BMAC_NUM_ALT_ADDR;
> +}
> +
> +static int niu_set_alt_mac(struct niu *np, int index, unsigned char *addr)
> +{
> + u16 reg0 = addr[4] << 8 | addr[5];
> + u16 reg1 = addr[2] << 8 | addr[3];
> + u16 reg2 = addr[0] << 8 | addr[1];
> +
> + if (index >= niu_num_alt_addr(np))
> + return -EINVAL;
> +
> + if (np->flags & NIU_FLAGS_XMAC) {
> + nw64_mac(XMAC_ALT_ADDR0(index), reg0);
> + nw64_mac(XMAC_ALT_ADDR1(index), reg1);
> + nw64_mac(XMAC_ALT_ADDR2(index), reg2);
> + } else {
> + nw64_mac(BMAC_ALT_ADDR0(index), reg0);
> + nw64_mac(BMAC_ALT_ADDR1(index), reg1);
> + nw64_mac(BMAC_ALT_ADDR2(index), reg2);
> + }
> +
> + return 0;
> +}
> +
> +static int niu_enable_alt_mac(struct niu *np, int index, int on)
> +{
> + unsigned long reg;
> + u64 val, mask;
> +
> + if (index >= niu_num_alt_addr(np))
> + return -EINVAL;
> +
> + if (np->flags & NIU_FLAGS_XMAC)
> + reg = XMAC_ADDR_CMPEN;
> + else
> + reg = BMAC_ADDR_CMPEN;
> +
> + mask = 1 << index;
> +
> + val = nr64_mac(reg);
> + if (on)
> + val |= mask;
> + else
> + val &= ~mask;
> + nw64_mac(reg, val);
> +
> + return 0;
> +}
> +
> +static void __set_rdc_table_num_hw(struct niu *np, unsigned long reg,
> + int num, int mac_pref)
> +{
> + u64 val = nr64_mac(reg);
> + val &= ~(HOST_INFO_MACRDCTBLN | HOST_INFO_MPR);
> + val |= num;
> + if (mac_pref)
> + val |= HOST_INFO_MPR;
> + nw64_mac(reg, val);
> +}
> +
> +static int __set_rdc_table_num(struct niu *np,
> + int xmac_index, int bmac_index,
> + int rdc_table_num, int mac_pref)
> +{
> + unsigned long reg;
> +
> + if (rdc_table_num & ~HOST_INFO_MACRDCTBLN)
> + return -EINVAL;
> + if (np->flags & NIU_FLAGS_XMAC)
> + reg = XMAC_HOST_INFO(xmac_index);
> + else
> + reg = BMAC_HOST_INFO(bmac_index);
> + __set_rdc_table_num_hw(np, reg, rdc_table_num, mac_pref);
> + return 0;
> +}
> +
> +static int niu_set_primary_mac_rdc_table(struct niu *np, int table_num,
> + int mac_pref)
> +{
> + return __set_rdc_table_num(np, 17, 0, table_num, mac_pref);
> +}
> +
> +static int niu_set_multicast_mac_rdc_table(struct niu *np, int table_num,
> + int mac_pref)
> +{
> + return __set_rdc_table_num(np, 16, 8, table_num, mac_pref);
> +}
> +
> +static int niu_set_alt_mac_rdc_table(struct niu *np, int idx,
> + int table_num, int mac_pref)
> +{
> + if (idx >= niu_num_alt_addr(np))
> + return -EINVAL;
> + return __set_rdc_table_num(np, idx, idx + 1, table_num, mac_pref);
> +}
> +
> +static u64 vlan_entry_set_parity(u64 reg_val)
> +{
> + u64 port01_mask;
> + u64 port23_mask;
> +
> + port01_mask = 0x00ff;
> + port23_mask = 0xff00;
> +
> + if (hweight64(reg_val & port01_mask) & 1)
> + reg_val |= ENET_VLAN_TBL_PARITY0;
> + else
> + reg_val &= ~ENET_VLAN_TBL_PARITY0;
> +
> + if (hweight64(reg_val & port23_mask) & 1)
> + reg_val |= ENET_VLAN_TBL_PARITY1;
> + else
> + reg_val &= ~ENET_VLAN_TBL_PARITY1;
> +
> + return reg_val;
> +}
> +
> +static void vlan_tbl_write(struct niu *np, unsigned long index,
> + int port, int vpr, int rdc_table)
> +{
> + u64 reg_val = nr64(ENET_VLAN_TBL(index));
> +
> + reg_val &= ~((ENET_VLAN_TBL_VPR |
> + ENET_VLAN_TBL_VLANRDCTBLN) <<
> + ENET_VLAN_TBL_SHIFT(port));
> + if (vpr)
> + reg_val |= (ENET_VLAN_TBL_VPR <<
> + ENET_VLAN_TBL_SHIFT(port));
> + reg_val |= (rdc_table << ENET_VLAN_TBL_SHIFT(port));
> +
> + reg_val = vlan_entry_set_parity(reg_val);
> +
> + nw64(ENET_VLAN_TBL(index), reg_val);
> +}
> +
> +static void vlan_tbl_clear(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++)
> + nw64(ENET_VLAN_TBL(i), 0);
> +}
> +
> +static int tcam_wait_bit(struct niu *np, u64 bit)
> +{
> + int limit = 1000;
> +
> + while (--limit > 0) {
> + if (nr64(TCAM_CTL) & bit)
> + break;
> + udelay(1);
> + }
> + if (limit < 0)
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +static int tcam_flush(struct niu *np, int index)
> +{
> + nw64(TCAM_KEY_0, 0x00);
> + nw64(TCAM_KEY_MASK_0, 0xff);
> + nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));
> +
> + return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +#if 0
> +static int tcam_read(struct niu *np, int index,
> + u64 *key, u64 *mask)
> +{
> + int err;
> +
> + nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_READ | index));
> + err = tcam_wait_bit(np, TCAM_CTL_STAT);
> + if (!err) {
> + key[0] = nr64(TCAM_KEY_0);
> + key[1] = nr64(TCAM_KEY_1);
> + key[2] = nr64(TCAM_KEY_2);
> + key[3] = nr64(TCAM_KEY_3);
> + mask[0] = nr64(TCAM_KEY_MASK_0);
> + mask[1] = nr64(TCAM_KEY_MASK_1);
> + mask[2] = nr64(TCAM_KEY_MASK_2);
> + mask[3] = nr64(TCAM_KEY_MASK_3);
> + }
> + return err;
> +}
> +#endif
> +
> +static int tcam_write(struct niu *np, int index,
> + u64 *key, u64 *mask)
> +{
> + nw64(TCAM_KEY_0, key[0]);
> + nw64(TCAM_KEY_1, key[1]);
> + nw64(TCAM_KEY_2, key[2]);
> + nw64(TCAM_KEY_3, key[3]);
> + nw64(TCAM_KEY_MASK_0, mask[0]);
> + nw64(TCAM_KEY_MASK_1, mask[1]);
> + nw64(TCAM_KEY_MASK_2, mask[2]);
> + nw64(TCAM_KEY_MASK_3, mask[3]);
> + nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));
> +
> + return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +#if 0
> +static int tcam_assoc_read(struct niu *np, int index, u64 *data)
> +{
> + int err;
> +
> + nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_READ | index));
> + err = tcam_wait_bit(np, TCAM_CTL_STAT);
> + if (!err)
> + *data = nr64(TCAM_KEY_1);
> +
> + return err;
> +}
> +#endif
> +
> +static int tcam_assoc_write(struct niu *np, int index, u64 assoc_data)
> +{
> + nw64(TCAM_KEY_1, assoc_data);
> + nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_WRITE | index));
> +
> + return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +static void tcam_enable(struct niu *np, int on)
> +{
> + u64 val = nr64(FFLP_CFG_1);
> +
> + if (on)
> + val &= ~FFLP_CFG_1_TCAM_DIS;
> + else
> + val |= FFLP_CFG_1_TCAM_DIS;
> + nw64(FFLP_CFG_1, val);
> +}
> +
> +static void tcam_set_lat_and_ratio(struct niu *np, u64 latency, u64 ratio)
> +{
> + u64 val = nr64(FFLP_CFG_1);
> +
> + val &= ~(FFLP_CFG_1_FFLPINITDONE |
> + FFLP_CFG_1_CAMLAT |
> + FFLP_CFG_1_CAMRATIO);
> + val |= (latency << FFLP_CFG_1_CAMLAT_SHIFT);
> + val |= (ratio << FFLP_CFG_1_CAMRATIO_SHIFT);
> + nw64(FFLP_CFG_1, val);
> +
> + val = nr64(FFLP_CFG_1);
> + val |= FFLP_CFG_1_FFLPINITDONE;
> + nw64(FFLP_CFG_1, val);
> +}
> +
> +static int tcam_user_eth_class_enable(struct niu *np, unsigned long class,
> + int on)
> +{
> + unsigned long reg;
> + u64 val;
> +
> + if (class < CLASS_CODE_ETHERTYPE1 ||
> + class > CLASS_CODE_ETHERTYPE2)
> + return -EINVAL;
> +
> + reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1);
> + val = nr64(reg);
> + if (on)
> + val |= L2_CLS_VLD;
> + else
> + val &= ~L2_CLS_VLD;
> + nw64(reg, val);
> +
> + return 0;
> +}
> +
> +#if 0
> +static int tcam_user_eth_class_set(struct niu *np, unsigned long class,
> + u64 ether_type)
> +{
> + unsigned long reg;
> + u64 val;
> +
> + if (class < CLASS_CODE_ETHERTYPE1 ||
> + class > CLASS_CODE_ETHERTYPE2 ||
> + (ether_type & ~(u64)0xffff) != 0)
> + return -EINVAL;
> +
> + reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1);
> + val = nr64(reg);
> + val &= ~L2_CLS_ETYPE;
> + val |= (ether_type << L2_CLS_ETYPE_SHIFT);
> + nw64(reg, val);
> +
> + return 0;
> +}
> +#endif
> +
> +static int tcam_user_ip_class_enable(struct niu *np, unsigned long class,
> + int on)
> +{
> + unsigned long reg;
> + u64 val;
> +
> + if (class < CLASS_CODE_USER_PROG1 ||
> + class > CLASS_CODE_USER_PROG4)
> + return -EINVAL;
> +
> + reg = L3_CLS(class - CLASS_CODE_USER_PROG1);
> + val = nr64(reg);
> + if (on)
> + val |= L3_CLS_VALID;
> + else
> + val &= ~L3_CLS_VALID;
> + nw64(reg, val);
> +
> + return 0;
> +}
> +
> +#if 0
> +static int tcam_user_ip_class_set(struct niu *np, unsigned long class,
> + int ipv6, u64 protocol_id,
> + u64 tos_mask, u64 tos_val)
> +{
> + unsigned long reg;
> + u64 val;
> +
> + if (class < CLASS_CODE_USER_PROG1 ||
> + class > CLASS_CODE_USER_PROG4 ||
> + (protocol_id & ~(u64)0xff) != 0 ||
> + (tos_mask & ~(u64)0xff) != 0 ||
> + (tos_val & ~(u64)0xff) != 0)
> + return -EINVAL;
> +
> + reg = L3_CLS(class - CLASS_CODE_USER_PROG1);
> + val = nr64(reg);
> + val &= ~(L3_CLS_IPVER | L3_CLS_PID |
> + L3_CLS_TOSMASK | L3_CLS_TOS);
> + if (ipv6)
> + val |= L3_CLS_IPVER;
> + val |= (protocol_id << L3_CLS_PID_SHIFT);
> + val |= (tos_mask << L3_CLS_TOSMASK_SHIFT);
> + val |= (tos_val << L3_CLS_TOS_SHIFT);
> + nw64(reg, val);
> +
> + return 0;
> +}
> +#endif
> +
> +static int tcam_early_init(struct niu *np)
> +{
> + unsigned long i;
> + int err;
> +
> + tcam_enable(np, 0);
> + tcam_set_lat_and_ratio(np,
> + DEFAULT_TCAM_LATENCY,
> + DEFAULT_TCAM_ACCESS_RATIO);
> + for (i = CLASS_CODE_ETHERTYPE1; i <= CLASS_CODE_ETHERTYPE2; i++) {
> + err = tcam_user_eth_class_enable(np, i, 0);
> + if (err)
> + return err;
> + }
> + for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_USER_PROG4; i++) {
> + err = tcam_user_ip_class_enable(np, i, 0);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int tcam_flush_all(struct niu *np)
> +{
> + unsigned long i;
> +
> + for (i = 0; i < np->parent->tcam_num_entries; i++) {
> + int err = tcam_flush(np, i);
> + if (err)
> + return err;
> + }
> + return 0;
> +}
> +
> +static u64 hash_addr_regval(unsigned long index, unsigned long num_entries)
> +{
> + return ((u64)index | (num_entries == 1 ?
> + HASH_TBL_ADDR_AUTOINC : 0));
> +}
> +
> +#if 0
> +static int hash_read(struct niu *np, unsigned long partition,
> + unsigned long index, unsigned long num_entries,
> + u64 *data)
> +{
> + u64 val = hash_addr_regval(index, num_entries);
> + unsigned long i;
> +
> + if (partition >= FCRAM_NUM_PARTITIONS ||
> + index + num_entries > FCRAM_SIZE)
> + return -EINVAL;
> +
> + nw64(HASH_TBL_ADDR(partition), val);
> + for (i = 0; i < num_entries; i++)
> + data[i] = nr64(HASH_TBL_DATA(partition));
> +
> + return 0;
> +}
> +#endif
> +
> +static int hash_write(struct niu *np, unsigned long partition,
> + unsigned long index, unsigned long num_entries,
> + u64 *data)
> +{
> + u64 val = hash_addr_regval(index, num_entries);
> + unsigned long i;
> +
> + if (partition >= FCRAM_NUM_PARTITIONS ||
> + index + (num_entries * 8) > FCRAM_SIZE)
> + return -EINVAL;
> +
> + nw64(HASH_TBL_ADDR(partition), val);
> + for (i = 0; i < num_entries; i++)
> + nw64(HASH_TBL_DATA(partition), data[i]);
> +
> + return 0;
> +}
> +
> +static void fflp_reset(struct niu *np)
> +{
> + u64 val;
> +
> + nw64(FFLP_CFG_1, FFLP_CFG_1_PIO_FIO_RST);
> + udelay(10);
> + nw64(FFLP_CFG_1, 0);
> +
> + val = FFLP_CFG_1_FCRAMOUTDR_NORMAL | FFLP_CFG_1_FFLPINITDONE;
> + nw64(FFLP_CFG_1, val);
> +}
> +
> +static void fflp_set_timings(struct niu *np)
> +{
> + u64 val = nr64(FFLP_CFG_1);
> +
> + val &= ~FFLP_CFG_1_FFLPINITDONE;
> + val |= (DEFAULT_FCRAMRATIO << FFLP_CFG_1_FCRAMRATIO_SHIFT);
> + nw64(FFLP_CFG_1, val);
> +
> + val = nr64(FFLP_CFG_1);
> + val |= FFLP_CFG_1_FFLPINITDONE;
> + nw64(FFLP_CFG_1, val);
> +
> + val = nr64(FCRAM_REF_TMR);
> + val &= ~(FCRAM_REF_TMR_MAX | FCRAM_REF_TMR_MIN);
> + val |= (DEFAULT_FCRAM_REFRESH_MAX << FCRAM_REF_TMR_MAX_SHIFT);
> + val |= (DEFAULT_FCRAM_REFRESH_MIN << FCRAM_REF_TMR_MIN_SHIFT);
> + nw64(FCRAM_REF_TMR, val);
> +}
> +
> +static int fflp_set_partition(struct niu *np, u64 partition,
> + u64 mask, u64 base, int enable)
> +{
> + unsigned long reg;
> + u64 val;
> +
> + if (partition >= FCRAM_NUM_PARTITIONS ||
> + (mask & ~(u64)0x1f) != 0 ||
> + (base & ~(u64)0x1f) != 0)
> + return -EINVAL;
> +
> + reg = FLW_PRT_SEL(partition);
> +
> + val = nr64(reg);
> + val &= ~(FLW_PRT_SEL_EXT | FLW_PRT_SEL_MASK | FLW_PRT_SEL_BASE);
> + val |= (mask << FLW_PRT_SEL_MASK_SHIFT);
> + val |= (base << FLW_PRT_SEL_BASE_SHIFT);
> + if (enable)
> + val |= FLW_PRT_SEL_EXT;
> + nw64(reg, val);
> +
> + return 0;
> +}
> +
> +static int fflp_disable_all_partitions(struct niu *np)
> +{
> + unsigned long i;
> +
> + for (i = 0; i < FCRAM_NUM_PARTITIONS; i++) {
> + int err = fflp_set_partition(np, 0, 0, 0, 0);
> + if (err)
> + return err;
> + }
> + return 0;
> +}
> +
> +static void fflp_llcsnap_enable(struct niu *np, int on)
> +{
> + u64 val = nr64(FFLP_CFG_1);
> +
> + if (on)
> + val |= FFLP_CFG_1_LLCSNAP;
> + else
> + val &= ~FFLP_CFG_1_LLCSNAP;
> + nw64(FFLP_CFG_1, val);
> +}
> +
> +static void fflp_errors_enable(struct niu *np, int on)
> +{
> + u64 val = nr64(FFLP_CFG_1);
> +
> + if (on)
> + val &= ~FFLP_CFG_1_ERRORDIS;
> + else
> + val |= FFLP_CFG_1_ERRORDIS;
> + nw64(FFLP_CFG_1, val);
> +}
> +
> +static int fflp_hash_clear(struct niu *np)
> +{
> + struct fcram_hash_ipv4 ent;
> + unsigned long i;
> +
> + /* IPV4 hash entry with valid bit clear, rest is don't care. */
> + memset(&ent, 0, sizeof(ent));
> + ent.header = HASH_HEADER_EXT;
> +
> + for (i = 0; i < FCRAM_SIZE; i += sizeof(ent)) {
> + int err = hash_write(np, 0, i, 1, (u64 *) &ent);
> + if (err)
> + return err;
> + }
> + return 0;
> +}
> +
> +static int fflp_early_init(struct niu *np)
> +{
> + struct niu_parent *parent;
> + unsigned long flags;
> + int err;
> +
> + niu_lock_parent(np, flags);
> +
> + parent = np->parent;
> + err = 0;
> + if (!(parent->flags & PARENT_FLGS_CLS_HWINIT)) {
> + niudbg(PROBE, "fflp_early_init: Initting hw on port %u\n",
> + np->port);
> + if (np->parent->plat_type != PLAT_TYPE_NIU) {
> + fflp_reset(np);
> + fflp_set_timings(np);
> + err = fflp_disable_all_partitions(np);
> + if (err) {
> + niudbg(PROBE, "fflp_disable_all_partitions "
> + "failed, err=%d\n", err);
> + goto out;
> + }
> + }
> +
> + err = tcam_early_init(np);
> + if (err) {
> + niudbg(PROBE, "tcam_early_init failed, err=%d\n",
> + err);
> + goto out;
> + }
> + fflp_llcsnap_enable(np, 1);
> + fflp_errors_enable(np, 0);
> + nw64(H1POLY, 0);
> + nw64(H2POLY, 0);
> +
> + err = tcam_flush_all(np);
> + if (err) {
> + niudbg(PROBE, "tcam_flush_all failed, err=%d\n",
> + err);
> + goto out;
> + }
> + if (np->parent->plat_type != PLAT_TYPE_NIU) {
> + err = fflp_hash_clear(np);
> + if (err) {
> + niudbg(PROBE, "fflp_hash_clear failed, "
> + "err=%d\n", err);
> + goto out;
> + }
> + }
> +
> + vlan_tbl_clear(np);
> +
> + niudbg(PROBE, "fflp_early_init: Success\n");
> + parent->flags |= PARENT_FLGS_CLS_HWINIT;
> + }
> +out:
> + niu_unlock_parent(np, flags);
> + return err;
> +}
> +
> +static int niu_set_flow_key(struct niu *np, unsigned long class_code, u64 key)
> +{
> + if (class_code < CLASS_CODE_USER_PROG1 ||
> + class_code > CLASS_CODE_SCTP_IPV6)
> + return -EINVAL;
> +
> + nw64(FLOW_KEY(class_code - CLASS_CODE_USER_PROG1), key);
> + return 0;
> +}
> +
> +static int niu_set_tcam_key(struct niu *np, unsigned long class_code, u64 key)
> +{
> + if (class_code < CLASS_CODE_USER_PROG1 ||
> + class_code > CLASS_CODE_SCTP_IPV6)
> + return -EINVAL;
> +
> + nw64(TCAM_KEY(class_code - CLASS_CODE_USER_PROG1), key);
> + return 0;
> +}
> +
> +static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
> + u32 offset, u32 size)
> +{
> + int i = skb_shinfo(skb)->nr_frags;
> + skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +
> + frag->page = page;
> + frag->page_offset = offset;
> + frag->size = size;
> +
> + skb->len += size;
> + skb->data_len += size;
> + skb->truesize += size;
> +
> + skb_shinfo(skb)->nr_frags = i + 1;
> +}
> +
> +static unsigned int niu_hash_rxaddr(struct rx_ring_info *rp, u64 a)
> +{
> + a >>= PAGE_SHIFT;
> + a ^= (a >> ilog2(MAX_RBR_RING_SIZE));
> +
> + return (a & (MAX_RBR_RING_SIZE - 1));
> +}
> +
> +static struct page *niu_find_rxpage(struct rx_ring_info *rp, u64 addr,
> + struct page ***link)
> +{
> + unsigned int h = niu_hash_rxaddr(rp, addr);
> + struct page *p, **pp;
> +
> + addr &= PAGE_MASK;
> + pp = &rp->rxhash[h];
> + for (; (p = *pp) != NULL; pp = (struct page **) &p->mapping) {
> + if (p->index == addr) {
> + *link = pp;
> + break;
> + }
> + }
> +
> + return p;
> +}
> +
> +static void niu_hash_page(struct rx_ring_info *rp, struct page *page, u64 base)
> +{
> + unsigned int h = niu_hash_rxaddr(rp, base);
> +
> + page->index = base;
> + page->mapping = (struct address_space *) rp->rxhash[h];
> + rp->rxhash[h] = page;
> +}
> +
> +static int niu_rbr_add_page(struct niu *np, struct rx_ring_info *rp,
> + gfp_t mask, int start_index)
> +{
> + struct page *page;
> + u64 addr;
> + int i;
> +
> + page = alloc_page(mask);
> + if (!page)
> + return -ENOMEM;
> +
> + addr = np->ops->map_page(np->device, page, 0,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> +
> + niu_hash_page(rp, page, addr);
> + if (rp->rbr_blocks_per_page > 1)
> + atomic_add(rp->rbr_blocks_per_page - 1,
> + &compound_head(page)->_count);
> +
> + for (i = 0; i < rp->rbr_blocks_per_page; i++) {
> + __le32 *rbr = &rp->rbr[start_index + i];
> +
> + *rbr = cpu_to_le32(addr >> RBR_DESCR_ADDR_SHIFT);
> + addr += rp->rbr_block_size;
> + }
> +
> + return 0;
> +}
> +
> +static void niu_rbr_refill(struct niu *np, struct rx_ring_info *rp, gfp_t mask)
> +{
> + int index = rp->rbr_index;
> +
> + rp->rbr_pending++;
> + if ((rp->rbr_pending % rp->rbr_blocks_per_page) == 0) {
> + int err = niu_rbr_add_page(np, rp, mask, index);
> +
> + if (unlikely(err)) {
> + rp->rbr_pending--;
> + return;
> + }
> +
> + rp->rbr_index += rp->rbr_blocks_per_page;
> + BUG_ON(rp->rbr_index > rp->rbr_table_size);
> + if (rp->rbr_index == rp->rbr_table_size)
> + rp->rbr_index = 0;
> +
> + if (rp->rbr_pending >= rp->rbr_kick_thresh) {
> + nw64(RBR_KICK(rp->rx_channel), rp->rbr_pending);
> + rp->rbr_pending = 0;
> + }
> + }
> +}
> +
> +static int niu_rx_pkt_ignore(struct niu *np, struct rx_ring_info *rp)
> +{
> + unsigned int index = rp->rcr_index;
> + int num_rcr = 0;
> +
> + rp->rx_dropped++;
> + while (1) {
> + struct page *page, **link;
> + u64 addr, val;
> + u32 rcr_size;
> +
> + num_rcr++;
> +
> + val = le64_to_cpup(&rp->rcr[index]);
> + addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
> + RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
> + page = niu_find_rxpage(rp, addr, &link);
> + BUG_ON(!page);
BUG_ON checks for NULL are useless if the code following is
just going to dereference the pointer anyway.
> +
> + rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >>
> + RCR_ENTRY_PKTBUFSZ_SHIFT];
> + if ((page->index + PAGE_SIZE) - rcr_size == addr) {
> + *link = (struct page *) page->mapping;
> + np->ops->unmap_page(np->device, page->index,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> + page->index = 0;
> + page->mapping = NULL;
> + __free_page(page);
> + rp->rbr_refill_pending++;
> + }
> +
> + index = NEXT_RCR(rp, index);
> + if (!(val & RCR_ENTRY_MULTI))
> + break;
> +
> + }
> + rp->rcr_index = index;
> +
> + return num_rcr;
> +}
> +
> +static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp)
> +{
> + unsigned int index = rp->rcr_index;
> + struct sk_buff *skb;
> + int len, num_rcr;
> +
> + skb = netdev_alloc_skb(np->dev, RX_SKB_ALLOC_SIZE);
> + if (unlikely(!skb))
> + return niu_rx_pkt_ignore(np, rp);
> +
> + num_rcr = 0;
> + while (1) {
> + struct page *page, **link;
> + u32 rcr_size, append_size;
> + u64 addr, val, off;
> +
> + num_rcr++;
> +
> + val = le64_to_cpup(&rp->rcr[index]);
> +
> + len = (val & RCR_ENTRY_L2_LEN) >>
> + RCR_ENTRY_L2_LEN_SHIFT;
> + len -= ETH_FCS_LEN;
> +
> + addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
> + RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
> + page = niu_find_rxpage(rp, addr, &link);
> + BUG_ON(!page);
> +
> + rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >>
> + RCR_ENTRY_PKTBUFSZ_SHIFT];
> +
> + off = addr & ~PAGE_MASK;
> + append_size = rcr_size;
> + if (num_rcr == 1) {
> + int ptype;
> +
> + off += 2;
> + append_size -= 2;
> +
> + ptype = (val >> RCR_ENTRY_PKT_TYPE_SHIFT);
> + if ((ptype == RCR_PKT_TYPE_TCP ||
> + ptype == RCR_PKT_TYPE_UDP) &&
> + !(val & (RCR_ENTRY_NOPORT |
> + RCR_ENTRY_ERROR)))
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + else
> + skb->ip_summed = CHECKSUM_NONE;
> + }
> + if (!(val & RCR_ENTRY_MULTI))
> + append_size = len - skb->len;
> +
> + niu_rx_skb_append(skb, page, off, append_size);
> + if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
> + *link = (struct page *) page->mapping;
> + np->ops->unmap_page(np->device, page->index,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> + page->index = 0;
> + page->mapping = NULL;
> + rp->rbr_refill_pending++;
> + } else
> + get_page(page);
> +
> + index = NEXT_RCR(rp, index);
> + if (!(val & RCR_ENTRY_MULTI))
> + break;
> +
> + }
> + rp->rcr_index = index;
> +
> + skb_reserve(skb, NET_IP_ALIGN);
> + __pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX));
> +
> + rp->rx_packets++;
> + rp->rx_bytes += skb->len;
> +
> + skb->protocol = eth_type_trans(skb, np->dev);
> + netif_receive_skb(skb);
> +
> + return num_rcr;
> +}
> +
> +static int niu_rbr_fill(struct niu *np, struct rx_ring_info *rp, gfp_t mask)
> +{
> + int blocks_per_page = rp->rbr_blocks_per_page;
> + int err, index = rp->rbr_index;
> +
> + err = 0;
> + while (index < (rp->rbr_table_size - blocks_per_page)) {
> + err = niu_rbr_add_page(np, rp, mask, index);
> + if (err)
> + break;
> +
> + index += blocks_per_page;
> + }
> +
> + rp->rbr_index = index;
> + return err;
> +}
> +
> +static void niu_rbr_free(struct niu *np, struct rx_ring_info *rp)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_RBR_RING_SIZE; i++) {
> + struct page *page;
> +
> + page = rp->rxhash[i];
> + while (page) {
> + struct page *next = (struct page *) page->mapping;
> + u64 base = page->index;
> +
> + np->ops->unmap_page(np->device, base, PAGE_SIZE,
> + DMA_FROM_DEVICE);
> + page->index = 0;
> + page->mapping = NULL;
> +
> + __free_page(page);
> +
> + page = next;
> + }
> + }
> +
> + for (i = 0; i < rp->rbr_table_size; i++)
> + rp->rbr[i] = cpu_to_le32(0);
> + rp->rbr_index = 0;
> +}
> +
> +static int release_tx_packet(struct niu *np, struct tx_ring_info *rp, int idx)
> +{
> + struct tx_buff_info *tb = &rp->tx_buffs[idx];
> + struct sk_buff *skb = tb->skb;
> + struct tx_pkt_hdr *tp;
> + u64 tx_flags;
> + int i, len;
> +
> + BUG_ON(!skb);
BUG_ON checks for NULL are useless if the code following is
just going to dereference the pointer anyway.
> +
> + tp = (struct tx_pkt_hdr *) skb->data;
> + tx_flags = le64_to_cpup(&tp->flags);
> +
> + rp->tx_packets++;
> + rp->tx_bytes += (((tx_flags & TXHDR_LEN) >> TXHDR_LEN_SHIFT) -
> + ((tx_flags & TXHDR_PAD) / 2));
> +
> + len = skb_headlen(skb);
> + np->ops->unmap_single(np->device, tb->mapping,
> + len, DMA_TO_DEVICE);
> +
> + if (le64_to_cpu(rp->descr[idx]) & TX_DESC_MARK)
> + rp->mark_pending--;
> +
> + tb->skb = NULL;
> + do {
> + idx = NEXT_TX(rp, idx);
> + len -= MAX_TX_DESC_LEN;
> + } while (len > 0);
> +
> + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> + tb = &rp->tx_buffs[idx];
> + BUG_ON(tb->skb != NULL);
> + np->ops->unmap_page(np->device, tb->mapping,
> + skb_shinfo(skb)->frags[i].size,
> + DMA_TO_DEVICE);
Shouldn't this be skb_shinfo(tb->skb)->frags...
> + idx = NEXT_TX(rp, idx);
> + }
> +
> + dev_kfree_skb(skb);
Should be dev_kfree_skb_any() if you plan on supporting
netconsole.
> +
> + return idx;
> +}
> +
> +#define NIU_TX_WAKEUP_THRESH(rp) ((rp)->pending / 4)
> +
> +static void niu_tx_work(struct niu *np, struct tx_ring_info *rp)
> +{
> + u16 pkt_cnt, tmp;
> + int cons;
> + u64 cs;
> +
> + cs = rp->tx_cs;
> + if (unlikely(!(cs & (TX_CS_MK | TX_CS_MMK))))
> + goto out;
> +
> + tmp = pkt_cnt = (cs & TX_CS_PKT_CNT) >> TX_CS_PKT_CNT_SHIFT;
> + pkt_cnt = (pkt_cnt - rp->last_pkt_cnt) &
> + (TX_CS_PKT_CNT >> TX_CS_PKT_CNT_SHIFT);
> +
> + rp->last_pkt_cnt = tmp;
> +
> + cons = rp->cons;
> +
> + niudbg(TX_WORK, "%s: niu_tx_work() pkt_cnt[%u] cons[%d]\n",
> + np->dev->name, pkt_cnt, cons);
> +
> + while (pkt_cnt--)
> + cons = release_tx_packet(np, rp, cons);
> +
> + rp->cons = cons;
> + smp_mb();
> +
> +out:
> + if (unlikely(netif_queue_stopped(np->dev) &&
> + (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)))) {
> + netif_tx_lock(np->dev);
> + if (netif_queue_stopped(np->dev) &&
> + (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)))
> + netif_wake_queue(np->dev);
> + netif_tx_unlock(np->dev);
> + }
> +}
> +
> +static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
> +{
> + int qlen, rcr_done = 0, work_done = 0;
> + struct rxdma_mailbox *mbox = rp->mbox;
> + u64 stat;
> +
> +#if 1
> + stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel));
> + qlen = nr64(RCRSTAT_A(rp->rx_channel)) & RCRSTAT_A_QLEN;
> +#else
> + stat = le64_to_cpup(&mbox->rx_dma_ctl_stat);
> + qlen = (le64_to_cpup(&mbox->rcrstat_a) & RCRSTAT_A_QLEN);
> +#endif
> + mbox->rx_dma_ctl_stat = 0;
> + mbox->rcrstat_a = 0;
> +
> + niudbg(RX_WORK, "%s: niu_rx_work(chan[%d]), stat[%llx] qlen=%d\n",
> + np->dev->name, rp->rx_channel, (unsigned long long) stat, qlen);
> +
> + rcr_done = work_done = 0;
> + qlen = min(qlen, budget);
> + while (work_done < qlen) {
> + rcr_done += niu_process_rx_pkt(np, rp);
> + work_done++;
> + }
> +
> + if (rp->rbr_refill_pending >= rp->rbr_kick_thresh) {
> + unsigned int i;
> +
> + for (i = 0; i < rp->rbr_refill_pending; i++)
> + niu_rbr_refill(np, rp, GFP_ATOMIC);
> + rp->rbr_refill_pending = 0;
> + }
> +
> + stat = (RX_DMA_CTL_STAT_MEX |
> + ((u64)work_done << RX_DMA_CTL_STAT_PKTREAD_SHIFT) |
> + ((u64)rcr_done << RX_DMA_CTL_STAT_PTRREAD_SHIFT));
> +
> + nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat);
> +
> + return work_done;
> +}
> +
> +static int niu_poll_core(struct niu *np, struct niu_ldg *lp, int budget)
> +{
> + u64 v0 = lp->v0;
> + u32 tx_vec = (v0 >> 32);
> + u32 rx_vec = (v0 & 0xffffffff);
> + int i, work_done = 0;
> +
> + niudbg(POLL, "%s: niu_poll_core() v0[%016llx]\n",
> + np->dev->name, (unsigned long long) v0);
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> + if (tx_vec & (1 << rp->tx_channel))
> + niu_tx_work(np, rp);
> + nw64(LD_IM0(LDN_TXDMA(rp->tx_channel)), 0);
> + }
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + if (rx_vec & (1 << rp->rx_channel)) {
> + int this_work_done;
> +
> + this_work_done = niu_rx_work(np, rp,
> + budget);
> +
> + budget -= this_work_done;
> + work_done += this_work_done;
> + }
> + nw64(LD_IM0(LDN_RXDMA(rp->rx_channel)), 0);
> + }
> +
> + return work_done;
> +}
> +
> +static int niu_poll(struct napi_struct *napi, int budget)
> +{
> + struct niu_ldg *lp = container_of(napi, struct niu_ldg, napi);
> + struct niu *np = lp->np;
> + int work_done;
> +
> + work_done = niu_poll_core(np, lp, budget);
> +
> + if (work_done < budget) {
> + netif_rx_complete(np->dev, napi);
> + niu_ldg_rearm(np, lp, 1);
> + }
> + return work_done;
> +}
> +
> +static void niu_log_rxchan_errors(struct niu *np, struct rx_ring_info *rp,
> + u64 stat)
> +{
> + printk(KERN_ERR PFX "%s: RX channel %u errors ( ",
> + np->dev->name, rp->rx_channel);
dev_err??
> +
> + if (stat & RX_DMA_CTL_STAT_RBR_TMOUT)
> + printk("RBR_TMOUT ");
> + if (stat & RX_DMA_CTL_STAT_RSP_CNT_ERR)
> + printk("RSP_CNT ");
> + if (stat & RX_DMA_CTL_STAT_BYTE_EN_BUS)
> + printk("BYTE_EN_BUS ");
> + if (stat & RX_DMA_CTL_STAT_RSP_DAT_ERR)
> + printk("RSP_DAT ");
> + if (stat & RX_DMA_CTL_STAT_RCR_ACK_ERR)
> + printk("RCR_ACK ");
> + if (stat & RX_DMA_CTL_STAT_RCR_SHA_PAR)
> + printk("RCR_SHA_PAR ");
> + if (stat & RX_DMA_CTL_STAT_RBR_PRE_PAR)
> + printk("RBR_PRE_PAR ");
> + if (stat & RX_DMA_CTL_STAT_CONFIG_ERR)
> + printk("CONFIG ");
> + if (stat & RX_DMA_CTL_STAT_RCRINCON)
> + printk("RCRINCON ");
> + if (stat & RX_DMA_CTL_STAT_RCRFULL)
> + printk("RCRFULL ");
> + if (stat & RX_DMA_CTL_STAT_RBRFULL)
> + printk("RBRFULL ");
> + if (stat & RX_DMA_CTL_STAT_RBRLOGPAGE)
> + printk("RBRLOGPAGE ");
> + if (stat & RX_DMA_CTL_STAT_CFIGLOGPAGE)
> + printk("CFIGLOGPAGE ");
> + if (stat & RX_DMA_CTL_STAT_DC_FIFO_ERR)
> + printk("DC_FIDO ");
> +
> + printk(")\n");
> +}
> +
> +static int niu_rx_error(struct niu *np, struct rx_ring_info *rp)
> +{
> + u64 stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel));
> + int err = 0;
> +
> + printk(KERN_ERR PFX "%s: RX channel %u error, stat[%llx]\n",
> + np->dev->name, rp->rx_channel, (unsigned long long) stat);
> +
> + niu_log_rxchan_errors(np, rp, stat);
> +
> + if (stat & (RX_DMA_CTL_STAT_CHAN_FATAL |
> + RX_DMA_CTL_STAT_PORT_FATAL))
> + err = -EINVAL;
> +
> + nw64(RX_DMA_CTL_STAT(rp->rx_channel),
> + stat & RX_DMA_CTL_WRITE_CLEAR_ERRS);
> +
> + return err;
> +}
> +
> +static void niu_log_txchan_errors(struct niu *np, struct tx_ring_info *rp,
> + u64 cs)
> +{
> + printk(KERN_ERR PFX "%s: TX channel %u errors ( ",
> + np->dev->name, rp->tx_channel);
> +
> + if (cs & TX_CS_MBOX_ERR)
> + printk("MBOX ");
> + if (cs & TX_CS_PKT_SIZE_ERR)
> + printk("PKT_SIZE ");
> + if (cs & TX_CS_TX_RING_OFLOW)
> + printk("TX_RING_OFLOW ");
> + if (cs & TX_CS_PREF_BUF_PAR_ERR)
> + printk("PREF_BUF_PAR ");
> + if (cs & TX_CS_NACK_PREF)
> + printk("NACK_PREF ");
> + if (cs & TX_CS_NACK_PKT_RD)
> + printk("NACK_PKT_RD ");
> + if (cs & TX_CS_CONF_PART_ERR)
> + printk("CONF_PART ");
> + if (cs & TX_CS_PKT_PRT_ERR)
> + printk("PKT_PTR ");
> +
> + printk(")\n");
> +}
> +
> +static int niu_tx_error(struct niu *np, struct tx_ring_info *rp)
> +{
> + u64 cs, logh, logl;
> +
> + cs = nr64(TX_CS(rp->tx_channel));
> + logh = nr64(TX_RNG_ERR_LOGH(rp->tx_channel));
> + logl = nr64(TX_RNG_ERR_LOGL(rp->tx_channel));
> +
> + printk(KERN_ERR PFX "%s: TX channel %u error, "
> + "cs[%llx] logh[%llx] logl[%llx]\n",
> + np->dev->name, rp->tx_channel,
> + (unsigned long long) cs,
> + (unsigned long long) logh,
> + (unsigned long long) logl);
> +
> + niu_log_txchan_errors(np, rp, cs);
> +
> + return -ENODEV;
> +}
> +
> +static int niu_mif_interrupt(struct niu *np)
> +{
> + u64 mif_status = nr64(MIF_STATUS);
> + int phy_mdint = 0;
> +
> + if (np->flags & NIU_FLAGS_XMAC) {
> + u64 xrxmac_stat = nr64_mac(XRXMAC_STATUS);
> +
> + if (xrxmac_stat & XRXMAC_STATUS_PHY_MDINT)
> + phy_mdint = 1;
> + }
> +
> + printk(KERN_ERR PFX "%s: MIF interrupt, "
> + "stat[%llx] phy_mdint(%d)\n",
> + np->dev->name, (unsigned long long) mif_status, phy_mdint);
> +
> + return -ENODEV;
> +}
> +
> +static void niu_xmac_interrupt(struct niu *np)
> +{
> + struct niu_xmac_stats *mp = &np->mac_stats.xmac;
> + u64 val;
> +
> + val = nr64_mac(XTXMAC_STATUS);
> + if (val & XTXMAC_STATUS_FRAME_CNT_EXP)
> + mp->tx_frames += TXMAC_FRM_CNT_COUNT;
> + if (val & XTXMAC_STATUS_BYTE_CNT_EXP)
> + mp->tx_bytes += TXMAC_BYTE_CNT_COUNT;
> + if (val & XTXMAC_STATUS_TXFIFO_XFR_ERR)
> + mp->tx_fifo_errors++;
> + if (val & XTXMAC_STATUS_TXMAC_OFLOW)
> + mp->tx_overflow_errors++;
> + if (val & XTXMAC_STATUS_MAX_PSIZE_ERR)
> + mp->tx_max_pkt_size_errors++;
> + if (val & XTXMAC_STATUS_TXMAC_UFLOW)
> + mp->tx_underflow_errors++;
> +
> + val = nr64_mac(XRXMAC_STATUS);
> + if (val & XRXMAC_STATUS_LCL_FLT_STATUS)
> + mp->rx_local_faults++;
> + if (val & XRXMAC_STATUS_RFLT_DET)
> + mp->rx_remote_faults++;
> + if (val & XRXMAC_STATUS_LFLT_CNT_EXP)
> + mp->rx_link_faults += LINK_FAULT_CNT_COUNT;
> + if (val & XRXMAC_STATUS_ALIGNERR_CNT_EXP)
> + mp->rx_align_errors += RXMAC_ALIGN_ERR_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXFRAG_CNT_EXP)
> + mp->rx_frags += RXMAC_FRAG_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXMULTF_CNT_EXP)
> + mp->rx_mcasts += RXMAC_MC_FRM_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXBCAST_CNT_EXP)
> + mp->rx_bcasts += RXMAC_BC_FRM_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXBCAST_CNT_EXP)
> + mp->rx_bcasts += RXMAC_BC_FRM_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST1_CNT_EXP)
> + mp->rx_hist_cnt1 += RXMAC_HIST_CNT1_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST2_CNT_EXP)
> + mp->rx_hist_cnt2 += RXMAC_HIST_CNT2_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST3_CNT_EXP)
> + mp->rx_hist_cnt3 += RXMAC_HIST_CNT3_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST4_CNT_EXP)
> + mp->rx_hist_cnt4 += RXMAC_HIST_CNT4_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST5_CNT_EXP)
> + mp->rx_hist_cnt5 += RXMAC_HIST_CNT5_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST6_CNT_EXP)
> + mp->rx_hist_cnt6 += RXMAC_HIST_CNT6_COUNT;
> + if (val & XRXMAC_STATUS_RXHIST7_CNT_EXP)
> + mp->rx_hist_cnt7 += RXMAC_HIST_CNT7_COUNT;
> + if (val & XRXMAC_STAT_MSK_RXOCTET_CNT_EXP)
> + mp->rx_octets += RXMAC_BT_CNT_COUNT;
> + if (val & XRXMAC_STATUS_CVIOLERR_CNT_EXP)
> + mp->rx_code_violations += RXMAC_CD_VIO_CNT_COUNT;
> + if (val & XRXMAC_STATUS_LENERR_CNT_EXP)
> + mp->rx_len_errors += RXMAC_MPSZER_CNT_COUNT;
> + if (val & XRXMAC_STATUS_CRCERR_CNT_EXP)
> + mp->rx_crc_errors += RXMAC_CRC_ER_CNT_COUNT;
> + if (val & XRXMAC_STATUS_RXUFLOW)
> + mp->rx_underflows++;
> + if (val & XRXMAC_STATUS_RXOFLOW)
> + mp->rx_overflows++;
> +
> + val = nr64_mac(XMAC_FC_STAT);
> + if (val & XMAC_FC_STAT_TX_MAC_NPAUSE)
> + mp->pause_off_state++;
> + if (val & XMAC_FC_STAT_TX_MAC_PAUSE)
> + mp->pause_on_state++;
> + if (val & XMAC_FC_STAT_RX_MAC_RPAUSE)
> + mp->pause_received++;
> +}
> +
> +static void niu_bmac_interrupt(struct niu *np)
> +{
> + struct niu_bmac_stats *mp = &np->mac_stats.bmac;
> + u64 val;
> +
> + val = nr64_mac(BTXMAC_STATUS);
> + if (val & BTXMAC_STATUS_UNDERRUN)
> + mp->tx_underflow_errors++;
> + if (val & BTXMAC_STATUS_MAX_PKT_ERR)
> + mp->tx_max_pkt_size_errors++;
> + if (val & BTXMAC_STATUS_BYTE_CNT_EXP)
> + mp->tx_bytes += BTXMAC_BYTE_CNT_COUNT;
> + if (val & BTXMAC_STATUS_FRAME_CNT_EXP)
> + mp->tx_frames += BTXMAC_FRM_CNT_COUNT;
> +
> + val = nr64_mac(BRXMAC_STATUS);
> + if (val & BRXMAC_STATUS_OVERFLOW)
> + mp->rx_overflows++;
> + if (val & BRXMAC_STATUS_FRAME_CNT_EXP)
> + mp->rx_frames += BRXMAC_FRAME_CNT_COUNT;
> + if (val & BRXMAC_STATUS_ALIGN_ERR_EXP)
> + mp->rx_align_errors += BRXMAC_ALIGN_ERR_CNT_COUNT;
> + if (val & BRXMAC_STATUS_CRC_ERR_EXP)
> + mp->rx_crc_errors += BRXMAC_ALIGN_ERR_CNT_COUNT;
> + if (val & BRXMAC_STATUS_LEN_ERR_EXP)
> + mp->rx_len_errors += BRXMAC_CODE_VIOL_ERR_CNT_COUNT;
> +
> + val = nr64_mac(BMAC_CTRL_STATUS);
> + if (val & BMAC_CTRL_STATUS_NOPAUSE)
> + mp->pause_off_state++;
> + if (val & BMAC_CTRL_STATUS_PAUSE)
> + mp->pause_on_state++;
> + if (val & BMAC_CTRL_STATUS_PAUSE_RECV)
> + mp->pause_received++;
> +}
> +
> +static int niu_mac_interrupt(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_xmac_interrupt(np);
> + else
> + niu_bmac_interrupt(np);
> +
> + return 0;
> +}
> +
> +static void niu_log_device_error(struct niu *np, u64 stat)
> +{
> + printk(KERN_ERR PFX "%s: Core device errors ( ",
> + np->dev->name);
> +
> + if (stat & SYS_ERR_MASK_META2)
> + printk("META2 ");
> + if (stat & SYS_ERR_MASK_META1)
> + printk("META1 ");
> + if (stat & SYS_ERR_MASK_PEU)
> + printk("PEU ");
> + if (stat & SYS_ERR_MASK_TXC)
> + printk("TXC ");
> + if (stat & SYS_ERR_MASK_RDMC)
> + printk("RDMC ");
> + if (stat & SYS_ERR_MASK_TDMC)
> + printk("TDMC ");
> + if (stat & SYS_ERR_MASK_ZCP)
> + printk("ZCP ");
> + if (stat & SYS_ERR_MASK_FFLP)
> + printk("FFLP ");
> + if (stat & SYS_ERR_MASK_IPP)
> + printk("IPP ");
> + if (stat & SYS_ERR_MASK_MAC)
> + printk("MAC ");
> + if (stat & SYS_ERR_MASK_SMX)
> + printk("SMX ");
> +
> + printk(")\n");
> +}
> +
> +static int niu_device_error(struct niu *np)
> +{
> + u64 stat = nr64(SYS_ERR_STAT);
> +
> + printk(KERN_ERR PFX "%s: Core device error, stat[%llx]\n",
> + np->dev->name, (unsigned long long) stat);
> +
> + niu_log_device_error(np, stat);
> +
> + return -ENODEV;
> +}
> +
> +static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp)
> +{
> + u64 v0 = lp->v0;
> + u64 v1 = lp->v1;
> + u64 v2 = lp->v2;
> + int i, err = 0;
> +
> + if (v1 & 0x00000000ffffffffULL) {
> + u32 rx_vec = (v1 & 0xffffffff);
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + if (rx_vec & (1 << rp->rx_channel)) {
> + int r = niu_rx_error(np, rp);
> + if (r)
> + err = r;
> + }
> + }
> + }
> + if (v1 & 0x7fffffff00000000ULL) {
> + u32 tx_vec = (v1 >> 32) & 0x7fffffff;
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + if (tx_vec & (1 << rp->tx_channel)) {
> + int r = niu_tx_error(np, rp);
> + if (r)
> + err = r;
> + }
> + }
> + }
> + if ((v0 | v1) & 0x8000000000000000ULL) {
> + int r = niu_mif_interrupt(np);
> + if (r)
> + err = r;
> + }
> + if (v2) {
> + if (v2 & 0x01ef) {
> + int r = niu_mac_interrupt(np);
> + if (r)
> + err = r;
> + }
> + if (v2 & 0x0210) {
> + int r = niu_device_error(np);
> + if (r)
> + err = r;
> + }
> + }
> +
> + if (err)
> + niu_enable_interrupts(np, 0);
> +
> + return -EINVAL;
> +}
> +
> +static void niu_rxchan_intr(struct niu *np, struct rx_ring_info *rp,
> + int ldn)
> +{
> + struct rxdma_mailbox *mbox = rp->mbox;
> + u64 stat_write, stat = le64_to_cpup(&mbox->rx_dma_ctl_stat);
> +
> + stat_write = (RX_DMA_CTL_STAT_RCRTHRES |
> + RX_DMA_CTL_STAT_RCRTO);
> + nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat_write);
> +
> + niudbg(INTERRUPT, "%s: rxchan_intr stat[%llx]\n",
> + np->dev->name, (unsigned long long) stat);
> +}
> +
> +static void niu_txchan_intr(struct niu *np, struct tx_ring_info *rp,
> + int ldn)
> +{
> + rp->tx_cs = nr64(TX_CS(rp->tx_channel));
> +
> + niudbg(INTERRUPT, "%s: txchan_intr cs[%llx]\n",
> + np->dev->name, (unsigned long long) rp->tx_cs);
> +}
> +
> +static void __niu_fastpath_interrupt(struct niu *np, int ldg, u64 v0)
> +{
> + struct niu_parent *parent = np->parent;
> + u32 rx_vec, tx_vec;
> + int i;
> +
> + tx_vec = (v0 >> 32);
> + rx_vec = (v0 & 0xffffffff);
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> + int ldn = LDN_RXDMA(rp->rx_channel);
> +
> + if (parent->ldg_map[ldn] != ldg)
> + continue;
> +
> + nw64(LD_IM0(ldn), LD_IM0_MASK);
> + if (rx_vec & (1 << rp->rx_channel))
> + niu_rxchan_intr(np, rp, ldn);
> + }
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> + int ldn = LDN_TXDMA(rp->tx_channel);
> +
> + if (parent->ldg_map[ldn] != ldg)
> + continue;
> +
> + nw64(LD_IM0(ldn), LD_IM0_MASK);
> + if (tx_vec & (1 << rp->tx_channel))
> + niu_txchan_intr(np, rp, ldn);
> + }
> +}
> +
> +static void niu_schedule_napi(struct niu *np, struct niu_ldg *lp,
> + u64 v0, u64 v1, u64 v2)
> +{
> + if (likely(netif_rx_schedule_prep(np->dev, &lp->napi))) {
> + lp->v0 = v0;
> + lp->v1 = v1;
> + lp->v2 = v2;
> + __niu_fastpath_interrupt(np, lp->ldg_num, v0);
> + __netif_rx_schedule(np->dev, &lp->napi);
> + }
> +}
> +
> +static irqreturn_t niu_interrupt(int irq, void *dev_id)
> +{
> + struct niu_ldg *lp = dev_id;
> + struct niu *np = lp->np;
> + int ldg = lp->ldg_num;
> + unsigned long flags;
> + u64 v0, v1, v2;
> +
> + if (niu_debug & NIU_DEBUG_INTERRUPT)
> + printk(KERN_ERR PFX "niu_interrupt() ldg[%p](%d) ",
> + lp, ldg);
Why not netif_msg_intr()?
> +
> + spin_lock_irqsave(&np->lock, flags);
> +
> + v0 = nr64(LDSV0(ldg));
> + v1 = nr64(LDSV1(ldg));
> + v2 = nr64(LDSV2(ldg));
> +
> + if (niu_debug & NIU_DEBUG_INTERRUPT)
> + printk("v0[%llx] v1[%llx] v2[%llx]\n",
> + (unsigned long long) v0,
> + (unsigned long long) v1,
> + (unsigned long long) v2);
> +
> + if (unlikely(!v0 && !v1 && !v2)) {
> + spin_unlock_irqrestore(&np->lock, flags);
> + return IRQ_NONE;
> + }
> +
> + if (unlikely((v0 & ((u64)1 << LDN_MIF)) || v1 || v2)) {
> + int err = niu_slowpath_interrupt(np, lp);
> + if (err)
> + goto out;
> + }
> + if (likely(v0 & ~((u64)1 << LDN_MIF)))
> + niu_schedule_napi(np, lp, v0, v1, v2);
> + else
> + niu_ldg_rearm(np, lp, 1);
> +out:
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void niu_free_rx_ring_info(struct niu *np, struct rx_ring_info *rp)
> +{
> + if (rp->mbox) {
> + np->ops->free_coherent(np->device,
> + sizeof(struct rxdma_mailbox),
> + rp->mbox, rp->mbox_dma);
> + rp->mbox = NULL;
> + }
> + if (rp->rcr) {
> + np->ops->free_coherent(np->device,
> + MAX_RCR_RING_SIZE * sizeof(__le64),
> + rp->rcr, rp->rcr_dma);
> + rp->rcr = NULL;
> + rp->rcr_table_size = 0;
> + rp->rcr_index = 0;
> + }
> + if (rp->rbr) {
> + niu_rbr_free(np, rp);
> +
> + np->ops->free_coherent(np->device,
> + MAX_RBR_RING_SIZE * sizeof(__le32),
> + rp->rbr, rp->rbr_dma);
> + rp->rbr = NULL;
> + rp->rbr_table_size = 0;
> + rp->rbr_index = 0;
> + }
> + kfree(rp->rxhash);
> + rp->rxhash = NULL;
> +}
> +
> +static void niu_free_tx_ring_info(struct niu *np, struct tx_ring_info *rp)
> +{
> + if (rp->mbox) {
> + np->ops->free_coherent(np->device,
> + sizeof(struct txdma_mailbox),
> + rp->mbox, rp->mbox_dma);
> + rp->mbox = NULL;
> + }
> + if (rp->descr) {
> + int i;
> +
> + for (i = 0; i < MAX_TX_RING_SIZE; i++) {
> + if (rp->tx_buffs[i].skb)
> + (void) release_tx_packet(np, rp, i);
> + }
> +
> + np->ops->free_coherent(np->device,
> + MAX_TX_RING_SIZE * sizeof(__le64),
> + rp->descr, rp->descr_dma);
> + rp->descr = NULL;
> + rp->pending = 0;
> + rp->prod = 0;
> + rp->cons = 0;
> + rp->wrap_bit = 0;
> + }
> +}
> +
> +static void niu_free_channels(struct niu *np)
> +{
> + int i;
> +
> + if (np->rx_rings) {
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + niu_free_rx_ring_info(np, rp);
> + }
> + kfree(np->rx_rings);
> + np->rx_rings = NULL;
> + np->num_rx_rings = 0;
> + }
> +
> + if (np->tx_rings) {
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + niu_free_tx_ring_info(np, rp);
> + }
> + kfree(np->tx_rings);
> + np->tx_rings = NULL;
> + np->num_tx_rings = 0;
> + }
> +}
> +
> +static int niu_alloc_rx_ring_info(struct niu *np,
> + struct rx_ring_info *rp)
> +{
> + BUILD_BUG_ON(sizeof(struct rxdma_mailbox) != 64);
> +
> + rp->rxhash = kzalloc(MAX_RBR_RING_SIZE * sizeof(struct page *),
> + GFP_KERNEL);
> + if (!rp->rxhash)
> + return -ENOMEM;
> +
> + rp->mbox = np->ops->alloc_coherent(np->device,
> + sizeof(struct rxdma_mailbox),
> + &rp->mbox_dma, GFP_KERNEL);
> + if (!rp->mbox)
> + return -ENOMEM;
> + if ((unsigned long)rp->mbox & (64UL - 1)) {
> + printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> + "RXDMA mailbox %p\n", np->dev->name, rp->mbox);
> + return -EINVAL;
> + }
> +
> + rp->rcr = np->ops->alloc_coherent(np->device,
> + MAX_RCR_RING_SIZE * sizeof(__le64),
> + &rp->rcr_dma, GFP_KERNEL);
> + if (!rp->rcr)
> + return -ENOMEM;
> + if ((unsigned long)rp->rcr & (64UL - 1)) {
> + printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> + "RXDMA RCR table %p\n", np->dev->name, rp->rcr);
> + return -EINVAL;
> + }
> + rp->rcr_table_size = MAX_RCR_RING_SIZE;
> + rp->rcr_index = 0;
> +
> + rp->rbr = np->ops->alloc_coherent(np->device,
> + MAX_RBR_RING_SIZE * sizeof(__le32),
> + &rp->rbr_dma, GFP_KERNEL);
> + if (!rp->rbr)
> + return -ENOMEM;
> + if ((unsigned long)rp->rbr & (64UL - 1)) {
> + printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> + "RXDMA RBR table %p\n", np->dev->name, rp->rbr);
> + return -EINVAL;
> + }
> + rp->rbr_table_size = MAX_RBR_RING_SIZE;
> + rp->rbr_index = 0;
> + rp->rbr_pending = 0;
> +
> + return 0;
> +}
> +
> +static void niu_set_max_burst(struct niu *np, struct tx_ring_info *rp)
> +{
> + int mtu = np->dev->mtu;
> +
> + rp->max_burst = mtu + 32;
> + if (rp->max_burst > 4096)
> + rp->max_burst = 4096;
> +}
> +
> +static int niu_alloc_tx_ring_info(struct niu *np,
> + struct tx_ring_info *rp)
> +{
> + BUILD_BUG_ON(sizeof(struct txdma_mailbox) != 64);
> +
> + rp->mbox = np->ops->alloc_coherent(np->device,
> + sizeof(struct txdma_mailbox),
> + &rp->mbox_dma, GFP_KERNEL);
> + if (!rp->mbox)
> + return -ENOMEM;
> + if ((unsigned long)rp->mbox & (64UL - 1)) {
> + printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> + "TXDMA mailbox %p\n", np->dev->name, rp->mbox);
> + return -EINVAL;
> + }
> +
> + rp->descr = np->ops->alloc_coherent(np->device,
> + MAX_TX_RING_SIZE * sizeof(__le64),
> + &rp->descr_dma, GFP_KERNEL);
> + if (!rp->descr)
> + return -ENOMEM;
> + if ((unsigned long)rp->descr & (64UL - 1)) {
> + printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> + "TXDMA descr table %p\n", np->dev->name, rp->descr);
> + return -EINVAL;
> + }
> +
> + rp->pending = MAX_TX_RING_SIZE;
> + rp->prod = 0;
> + rp->cons = 0;
> + rp->wrap_bit = 0;
> +
> + /* XXX make these configurable... XXX */
> + rp->mark_freq = rp->pending / 4;
> +
> + niu_set_max_burst(np, rp);
> +
> + return 0;
> +}
> +
> +static void niu_size_rbr(struct niu *np, struct rx_ring_info *rp)
> +{
> + u16 bs;
> +
> + switch (PAGE_SIZE) {
> + case 4 * 1024:
> + case 8 * 1024:
> + case 16 * 1024:
> + case 32 * 1024:
> + rp->rbr_block_size = PAGE_SIZE;
> + rp->rbr_blocks_per_page = 1;
> + break;
> +
> + default:
> + if (PAGE_SIZE % (32 * 1024) == 0)
> + bs = 32 * 1024;
> + else if (PAGE_SIZE % (16 * 1024) == 0)
> + bs = 16 * 1024;
> + else if (PAGE_SIZE % (8 * 1024) == 0)
> + bs = 8 * 1024;
> + else if (PAGE_SIZE % (4 * 1024) == 0)
> + bs = 4 * 1024;
> + else
> + BUG();
> + rp->rbr_block_size = bs;
> + rp->rbr_blocks_per_page = PAGE_SIZE / bs;
> + }
> +
> + rp->rbr_sizes[0] = 256;
> + rp->rbr_sizes[1] = 1024;
> + if (np->dev->mtu > ETH_DATA_LEN) {
> + switch (PAGE_SIZE) {
> + case 4 * 1024:
> + rp->rbr_sizes[2] = 4096;
> + break;
> +
> + default:
> + rp->rbr_sizes[2] = 8192;
> + break;
> + }
> + } else {
> + rp->rbr_sizes[2] = 2048;
> + }
> + rp->rbr_sizes[3] = rp->rbr_block_size;
> +}
> +
> +static int niu_alloc_channels(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + int first_rx_channel, first_tx_channel;
> + int i, port, err;
> +
> + port = np->port;
> + first_rx_channel = first_tx_channel = 0;
> + for (i = 0; i < port; i++) {
> + first_rx_channel += parent->rxchan_per_port[i];
> + first_tx_channel += parent->txchan_per_port[i];
> + }
> +
> + np->num_rx_rings = parent->rxchan_per_port[port];
> + np->num_tx_rings = parent->txchan_per_port[port];
> +
> + np->rx_rings = kzalloc(np->num_rx_rings * sizeof(struct rx_ring_info),
> + GFP_KERNEL);
> + err = -ENOMEM;
> + if (!np->rx_rings)
> + goto out_err;
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + rp->np = np;
> + rp->rx_channel = first_rx_channel + i;
> +
> + err = niu_alloc_rx_ring_info(np, rp);
> + if (err)
> + goto out_err;
> +
> + niu_size_rbr(np, rp);
> +
> + /* XXX better defaults, configurable, etc... XXX */
> + rp->nonsyn_window = 64;
> + rp->nonsyn_threshold = rp->rcr_table_size - 64;
> + rp->syn_window = 64;
> + rp->syn_threshold = rp->rcr_table_size - 64;
> + rp->rcr_pkt_threshold = 16;
> + rp->rcr_timeout = 8;
> + rp->rbr_kick_thresh = RBR_REFILL_MIN;
> + if (rp->rbr_kick_thresh < rp->rbr_blocks_per_page)
> + rp->rbr_kick_thresh = rp->rbr_blocks_per_page;
> +
> + err = niu_rbr_fill(np, rp, GFP_KERNEL);
> + if (err)
> + return err;
> + }
> +
> + np->tx_rings = kzalloc(np->num_tx_rings * sizeof(struct tx_ring_info),
> + GFP_KERNEL);
> + err = -ENOMEM;
> + if (!np->tx_rings)
> + goto out_err;
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + rp->np = np;
> + rp->tx_channel = first_tx_channel + i;
> +
> + err = niu_alloc_tx_ring_info(np, rp);
> + if (err)
> + goto out_err;
> + }
> +
> + return 0;
> +
> +out_err:
> + niu_free_channels(np);
> + return err;
> +}
> +
> +static int niu_tx_cs_sng_poll(struct niu *np, int channel)
> +{
> + int limit = 1000;
> +
> + while (--limit > 0) {
> + u64 val = nr64(TX_CS(channel));
> + if (val & TX_CS_SNG_STATE)
> + return 0;
> + }
> + return -ENODEV;
> +}
> +
> +static int niu_tx_channel_stop(struct niu *np, int channel)
> +{
> + u64 val = nr64(TX_CS(channel));
> +
> + val |= TX_CS_STOP_N_GO;
> + nw64(TX_CS(channel), val);
> +
> + return niu_tx_cs_sng_poll(np, channel);
> +}
> +
> +static int niu_tx_cs_reset_poll(struct niu *np, int channel)
> +{
> + int limit = 1000;
> +
> + while (--limit > 0) {
> + u64 val = nr64(TX_CS(channel));
> + if (!(val & TX_CS_RST))
> + return 0;
> + }
> + return -ENODEV;
> +}
> +
> +static int niu_tx_channel_reset(struct niu *np, int channel)
> +{
> + u64 val = nr64(TX_CS(channel));
> + int err;
> +
> + val |= TX_CS_RST;
> + nw64(TX_CS(channel), val);
> +
> + err = niu_tx_cs_reset_poll(np, channel);
> + if (!err)
> + nw64(TX_RING_KICK(channel), 0);
> +
> + return err;
> +}
> +
> +static int niu_tx_channel_lpage_init(struct niu *np, int channel)
> +{
> + u64 val;
> +
> + nw64(TX_LOG_MASK1(channel), 0);
> + nw64(TX_LOG_VAL1(channel), 0);
> + nw64(TX_LOG_MASK2(channel), 0);
> + nw64(TX_LOG_VAL2(channel), 0);
> + nw64(TX_LOG_PAGE_RELO1(channel), 0);
> + nw64(TX_LOG_PAGE_RELO2(channel), 0);
> + nw64(TX_LOG_PAGE_HDL(channel), 0);
> +
> + val = (u64)np->port << TX_LOG_PAGE_VLD_FUNC_SHIFT;
> + val |= (TX_LOG_PAGE_VLD_PAGE0 | TX_LOG_PAGE_VLD_PAGE1);
> + nw64(TX_LOG_PAGE_VLD(channel), val);
> +
> + /* XXX TXDMA 32bit mode? XXX */
> +
> + return 0;
> +}
> +
> +static void niu_txc_enable_port(struct niu *np, int on)
> +{
> + unsigned long flags;
> + u64 val, mask;
> +
> + niu_lock_parent(np, flags);
> + val = nr64(TXC_CONTROL);
> + mask = (u64)1 << np->port;
> + if (on) {
> + val |= TXC_CONTROL_ENABLE | mask;
> + } else {
> + val &= ~mask;
> + if ((val & ~TXC_CONTROL_ENABLE) == 0)
> + val &= ~TXC_CONTROL_ENABLE;
> + }
> + nw64(TXC_CONTROL, val);
> + niu_unlock_parent(np, flags);
> +}
> +
> +static void niu_txc_set_imask(struct niu *np, u64 imask)
> +{
> + unsigned long flags;
> + u64 val;
> +
> + niu_lock_parent(np, flags);
> + val = nr64(TXC_INT_MASK);
> + val &= ~TXC_INT_MASK_VAL(np->port);
> + val |= (imask << TXC_INT_MASK_VAL_SHIFT(np->port));
> + niu_unlock_parent(np, flags);
> +}
> +
> +static void niu_txc_port_dma_enable(struct niu *np, int on)
> +{
> + u64 val = 0;
> +
> + if (on) {
> + int i;
> +
> + for (i = 0; i < np->num_tx_rings; i++)
> + val |= (1 << np->tx_rings[i].tx_channel);
> + }
> + nw64(TXC_PORT_DMA(np->port), val);
> +}
> +
> +static int niu_init_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> + int err, channel = rp->tx_channel;
> + u64 val, ring_len;
> +
> + err = niu_tx_channel_stop(np, channel);
> + if (err)
> + return err;
> +
> + err = niu_tx_channel_reset(np, channel);
> + if (err)
> + return err;
> +
> + err = niu_tx_channel_lpage_init(np, channel);
> + if (err)
> + return err;
> +
> + nw64(TXC_DMA_MAX(channel), rp->max_burst);
> + nw64(TX_ENT_MSK(channel), 0);
> +
> + if (rp->descr_dma & ~(TX_RNG_CFIG_STADDR_BASE |
> + TX_RNG_CFIG_STADDR)) {
> + printk(KERN_ERR PFX "%s: TX ring channel %d "
> + "DMA addr (%llx) is not aligned.\n",
> + np->dev->name, channel,
> + (unsigned long long) rp->descr_dma);
> + return -EINVAL;
> + }
> +
> + /* The length field in TX_RNG_CFIG is measured in 64-byte
> + * blocks. rp->pending is the number of TX descriptors in
> + * our ring, 8 bytes each, thus we divide by 8 bytes more
> + * to get the proper value the chip wants.
> + */
> + ring_len = (rp->pending / 8);
> +
> + val = ((ring_len << TX_RNG_CFIG_LEN_SHIFT) |
> + rp->descr_dma);
> + nw64(TX_RNG_CFIG(channel), val);
> +
> + if (((rp->mbox_dma >> 32) & ~TXDMA_MBH_MBADDR) ||
> + ((u32)rp->mbox_dma & ~TXDMA_MBL_MBADDR)) {
> + printk(KERN_ERR PFX "%s: TX ring channel %d "
> + "MBOX addr (%llx) is has illegal bits.\n",
> + np->dev->name, channel,
> + (unsigned long long) rp->mbox_dma);
> + return -EINVAL;
> + }
> + nw64(TXDMA_MBH(channel), rp->mbox_dma >> 32);
> + nw64(TXDMA_MBL(channel), rp->mbox_dma & TXDMA_MBL_MBADDR);
> +
> + nw64(TX_CS(channel), 0);
> +
> + rp->last_pkt_cnt = 0;
> +
> + return 0;
> +}
> +
> +static void niu_init_rdc_groups(struct niu *np)
> +{
> + struct niu_rdc_tables *tp = &np->parent->rdc_group_cfg[np->port];
> + int i, first_table_num = tp->first_table_num;
> +
> + for (i = 0; i < tp->num_tables; i++) {
> + struct rdc_table *tbl = &tp->tables[i];
> + int this_table = first_table_num + i;
> + int slot;
> +
> + for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++)
> + nw64(RDC_TBL(this_table, slot),
> + tbl->rxdma_channel[slot]);
> + }
> +
> + nw64(DEF_RDC(np->port), np->parent->rdc_default[np->port]);
> +}
> +
> +static void niu_init_drr_weight(struct niu *np)
> +{
> + int type = phy_decode(np->parent->port_phy, np->port);
> + u64 val;
> +
> + switch (type) {
> + case PORT_TYPE_10G:
> + val = PT_DRR_WEIGHT_DEFAULT_10G;
> + break;
> +
> + case PORT_TYPE_1G:
> + default:
> + val = PT_DRR_WEIGHT_DEFAULT_1G;
> + break;
> + }
> + nw64(PT_DRR_WT(np->port), val);
> +}
> +
> +static int niu_init_hostinfo(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> + int i, err, num_alt = niu_num_alt_addr(np);
> + int first_rdc_table = tp->first_table_num;
> +
> + err = niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> + if (err)
> + return err;
> +
> + err = niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> + if (err)
> + return err;
> +
> + for (i = 0; i < num_alt; i++) {
> + err = niu_set_alt_mac_rdc_table(np, i, first_rdc_table, 1);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_rx_channel_reset(struct niu *np, int channel)
> +{
> + return niu_set_and_wait_clear(np, RXDMA_CFIG1(channel),
> + RXDMA_CFIG1_RST, 1000, 10,
> + "RXDMA_CFIG1");
> +}
> +
> +static int niu_rx_channel_lpage_init(struct niu *np, int channel)
> +{
> + u64 val;
> +
> + nw64(RX_LOG_MASK1(channel), 0);
> + nw64(RX_LOG_VAL1(channel), 0);
> + nw64(RX_LOG_MASK2(channel), 0);
> + nw64(RX_LOG_VAL2(channel), 0);
> + nw64(RX_LOG_PAGE_RELO1(channel), 0);
> + nw64(RX_LOG_PAGE_RELO2(channel), 0);
> + nw64(RX_LOG_PAGE_HDL(channel), 0);
> +
> + val = (u64)np->port << RX_LOG_PAGE_VLD_FUNC_SHIFT;
> + val |= (RX_LOG_PAGE_VLD_PAGE0 | RX_LOG_PAGE_VLD_PAGE1);
> + nw64(RX_LOG_PAGE_VLD(channel), val);
> +
> + return 0;
> +}
> +
> +static void niu_rx_channel_wred_init(struct niu *np, struct rx_ring_info *rp)
> +{
> + u64 val;
> +
> + val = (((u64)rp->nonsyn_window << RDC_RED_PARA_WIN_SHIFT) |
> + ((u64)rp->nonsyn_threshold << RDC_RED_PARA_THRE_SHIFT) |
> + ((u64)rp->syn_window << RDC_RED_PARA_WIN_SYN_SHIFT) |
> + ((u64)rp->syn_threshold << RDC_RED_PARA_THRE_SYN_SHIFT));
> + nw64(RDC_RED_PARA(rp->rx_channel), val);
> +}
> +
> +static int niu_compute_rbr_cfig_b(struct rx_ring_info *rp, u64 *ret)
> +{
> + u64 val = 0;
> +
> + switch (rp->rbr_block_size) {
> + case 4 * 1024:
> + val |= (RBR_BLKSIZE_4K << RBR_CFIG_B_BLKSIZE_SHIFT);
> + break;
> + case 8 * 1024:
> + val |= (RBR_BLKSIZE_8K << RBR_CFIG_B_BLKSIZE_SHIFT);
> + break;
> + case 16 * 1024:
> + val |= (RBR_BLKSIZE_16K << RBR_CFIG_B_BLKSIZE_SHIFT);
> + break;
> + case 32 * 1024:
> + val |= (RBR_BLKSIZE_32K << RBR_CFIG_B_BLKSIZE_SHIFT);
> + break;
> + default:
> + return -EINVAL;
> + }
> + val |= RBR_CFIG_B_VLD2;
> + switch (rp->rbr_sizes[2]) {
> + case 2 * 1024:
> + val |= (RBR_BUFSZ2_2K << RBR_CFIG_B_BUFSZ2_SHIFT);
> + break;
> + case 4 * 1024:
> + val |= (RBR_BUFSZ2_4K << RBR_CFIG_B_BUFSZ2_SHIFT);
> + break;
> + case 8 * 1024:
> + val |= (RBR_BUFSZ2_8K << RBR_CFIG_B_BUFSZ2_SHIFT);
> + break;
> + case 16 * 1024:
> + val |= (RBR_BUFSZ2_16K << RBR_CFIG_B_BUFSZ2_SHIFT);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + val |= RBR_CFIG_B_VLD1;
> + switch (rp->rbr_sizes[1]) {
> + case 1 * 1024:
> + val |= (RBR_BUFSZ1_1K << RBR_CFIG_B_BUFSZ1_SHIFT);
> + break;
> + case 2 * 1024:
> + val |= (RBR_BUFSZ1_2K << RBR_CFIG_B_BUFSZ1_SHIFT);
> + break;
> + case 4 * 1024:
> + val |= (RBR_BUFSZ1_4K << RBR_CFIG_B_BUFSZ1_SHIFT);
> + break;
> + case 8 * 1024:
> + val |= (RBR_BUFSZ1_8K << RBR_CFIG_B_BUFSZ1_SHIFT);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> + val |= RBR_CFIG_B_VLD0;
> + switch (rp->rbr_sizes[0]) {
> + case 256:
> + val |= (RBR_BUFSZ0_256 << RBR_CFIG_B_BUFSZ0_SHIFT);
> + break;
> + case 512:
> + val |= (RBR_BUFSZ0_512 << RBR_CFIG_B_BUFSZ0_SHIFT);
> + break;
> + case 1 * 1024:
> + val |= (RBR_BUFSZ0_1K << RBR_CFIG_B_BUFSZ0_SHIFT);
> + break;
> + case 2 * 1024:
> + val |= (RBR_BUFSZ0_2K << RBR_CFIG_B_BUFSZ0_SHIFT);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + *ret = val;
> + return 0;
> +}
> +
> +static int niu_enable_rx_channel(struct niu *np, int channel, int on)
> +{
> + u64 val = nr64(RXDMA_CFIG1(channel));
> + int limit;
> +
> + if (on)
> + val |= RXDMA_CFIG1_EN;
> + else
> + val &= ~RXDMA_CFIG1_EN;
> + nw64(RXDMA_CFIG1(channel), val);
> +
> + limit = 1000;
> + while (--limit > 0) {
> + if (nr64(RXDMA_CFIG1(channel)) & RXDMA_CFIG1_QST)
> + break;
> + udelay(10);
> + }
> + if (limit <= 0)
> + return -ENODEV;
> + return 0;
> +}
> +
> +static int niu_init_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> + int err, channel = rp->rx_channel;
> + u64 val;
> +
> + err = niu_rx_channel_reset(np, channel);
> + if (err)
> + return err;
> +
> + err = niu_rx_channel_lpage_init(np, channel);
> + if (err)
> + return err;
> +
> + niu_rx_channel_wred_init(np, rp);
> +
> + nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_RBR_EMPTY);
> + nw64(RX_DMA_CTL_STAT(channel),
> + (RX_DMA_CTL_STAT_MEX |
> + RX_DMA_CTL_STAT_RCRTHRES |
> + RX_DMA_CTL_STAT_RCRTO |
> + RX_DMA_CTL_STAT_RBR_EMPTY));
> + nw64(RXDMA_CFIG1(channel), rp->mbox_dma >> 32);
> + nw64(RXDMA_CFIG2(channel), (rp->mbox_dma & 0x00000000ffffffc0));
> + nw64(RBR_CFIG_A(channel),
> + ((u64)rp->rbr_table_size << RBR_CFIG_A_LEN_SHIFT) |
> + (rp->rbr_dma & (RBR_CFIG_A_STADDR_BASE | RBR_CFIG_A_STADDR)));
> + err = niu_compute_rbr_cfig_b(rp, &val);
> + if (err)
> + return err;
> + nw64(RBR_CFIG_B(channel), val);
> + nw64(RCRCFIG_A(channel),
> + ((u64)rp->rcr_table_size << RCRCFIG_A_LEN_SHIFT) |
> + (rp->rcr_dma & (RCRCFIG_A_STADDR_BASE | RCRCFIG_A_STADDR)));
> + nw64(RCRCFIG_B(channel),
> + ((u64)rp->rcr_pkt_threshold << RCRCFIG_B_PTHRES_SHIFT) |
> + RCRCFIG_B_ENTOUT |
> + ((u64)rp->rcr_timeout << RCRCFIG_B_TIMEOUT_SHIFT));
> +
> + err = niu_enable_rx_channel(np, channel, 1);
> + if (err)
> + return err;
> +
> + nw64(RBR_KICK(channel), rp->rbr_index);
> +
> + val = nr64(RX_DMA_CTL_STAT(channel));
> + val |= RX_DMA_CTL_STAT_RBR_EMPTY;
> + nw64(RX_DMA_CTL_STAT(channel), val);
> +
> + return 0;
> +}
> +
> +static int niu_init_rx_channels(struct niu *np)
> +{
> + unsigned long flags;
> + u64 seed = jiffies_64;
> + int err, i;
> +
> + niu_lock_parent(np, flags);
> + nw64(RX_DMA_CK_DIV, np->parent->rxdma_clock_divider);
> + nw64(RED_RAN_INIT, RED_RAN_INIT_OPMODE | (seed & RED_RAN_INIT_VAL));
> + niu_unlock_parent(np, flags);
> +
> + /* XXX RXDMA 32bit mode? XXX */
> +
> + niu_init_rdc_groups(np);
> + niu_init_drr_weight(np);
> +
> + err = niu_init_hostinfo(np);
> + if (err)
> + return err;
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + err = niu_init_one_rx_channel(np, rp);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_set_ip_frag_rule(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + struct niu_classifier *cp = &np->clas;
> + struct niu_tcam_entry *tp;
> + int index, err;
> +
> + /* XXX fix this allocation scheme XXX */
> + index = cp->tcam_index;
> + tp = &parent->tcam[index];
> +
> + /* Note that the noport bit is the same in both ipv4 and
> + * ipv6 format TCAM entries.
> + */
> + memset(tp, 0, sizeof(*tp));
> + tp->key[1] = TCAM_V4KEY1_NOPORT;
> + tp->key_mask[1] = TCAM_V4KEY1_NOPORT;
> + tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET |
> + ((u64)0 << TCAM_ASSOCDATA_OFFSET_SHIFT));
> + err = tcam_write(np, index, tp->key, tp->key_mask);
> + if (err)
> + return err;
> + err = tcam_assoc_write(np, index, tp->assoc_data);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static int niu_init_classifier_hw(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + struct niu_classifier *cp = &np->clas;
> + int i, err;
> +
> + nw64(H1POLY, cp->h1_init);
> + nw64(H2POLY, cp->h2_init);
> +
> + err = niu_init_hostinfo(np);
> + if (err)
> + return err;
> +
> + for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++) {
> + struct niu_vlan_rdc *vp = &cp->vlan_mappings[i];
> +
> + vlan_tbl_write(np, i, np->port,
> + vp->vlan_pref, vp->rdc_num);
> + }
> +
> + for (i = 0; i < cp->num_alt_mac_mappings; i++) {
> + struct niu_altmac_rdc *ap = &cp->alt_mac_mappings[i];
> +
> + err = niu_set_alt_mac_rdc_table(np, ap->alt_mac_num,
> + ap->rdc_num, ap->mac_pref);
> + if (err)
> + return err;
> + }
> +
> + for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) {
> + int index = i - CLASS_CODE_USER_PROG1;
> +
> + err = niu_set_tcam_key(np, i, parent->tcam_key[index]);
> + if (err)
> + return err;
> + err = niu_set_flow_key(np, i, parent->flow_key[index]);
> + if (err)
> + return err;
> + }
> +
> + err = niu_set_ip_frag_rule(np);
> + if (err)
> + return err;
> +
> + tcam_enable(np, 1);
> +
> + return 0;
> +}
> +
> +static int niu_zcp_write(struct niu *np, int index, u64 *data)
> +{
> + nw64(ZCP_RAM_DATA0, data[0]);
> + nw64(ZCP_RAM_DATA1, data[1]);
> + nw64(ZCP_RAM_DATA2, data[2]);
> + nw64(ZCP_RAM_DATA3, data[3]);
> + nw64(ZCP_RAM_DATA4, data[4]);
> + nw64(ZCP_RAM_BE, ZCP_RAM_BE_VAL);
> + nw64(ZCP_RAM_ACC,
> + (ZCP_RAM_ACC_WRITE |
> + (0 << ZCP_RAM_ACC_ZFCID_SHIFT) |
> + (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT)));
> +
> + return niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> + 1000, 100);
> +}
> +
> +static int niu_zcp_read(struct niu *np, int index, u64 *data)
> +{
> + int err;
> +
> + err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> + 1000, 100);
> + if (err) {
> + printk(KERN_ERR PFX "%s: ZCP read busy won't clear, "
> + "ZCP_RAM_ACC[%llx]\n", np->dev->name,
> + (unsigned long long) nr64(ZCP_RAM_ACC));
> + return err;
> + }
> +
> + nw64(ZCP_RAM_ACC,
> + (ZCP_RAM_ACC_READ |
> + (0 << ZCP_RAM_ACC_ZFCID_SHIFT) |
> + (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT)));
> +
> + err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> + 1000, 100);
> + if (err) {
> + printk(KERN_ERR PFX "%s: ZCP read busy2 won't clear, "
> + "ZCP_RAM_ACC[%llx]\n", np->dev->name,
> + (unsigned long long) nr64(ZCP_RAM_ACC));
> + return err;
> + }
> +
> + data[0] = nr64(ZCP_RAM_DATA0);
> + data[1] = nr64(ZCP_RAM_DATA1);
> + data[2] = nr64(ZCP_RAM_DATA2);
> + data[3] = nr64(ZCP_RAM_DATA3);
> + data[4] = nr64(ZCP_RAM_DATA4);
> +
> + return 0;
> +}
> +
> +static void niu_zcp_cfifo_reset(struct niu *np)
> +{
> + u64 val = nr64(RESET_CFIFO);
> +
> + val |= RESET_CFIFO_RST(np->port);
> + nw64(RESET_CFIFO, val);
> + udelay(10);
> +
> + val &= ~RESET_CFIFO_RST(np->port);
> + nw64(RESET_CFIFO, val);
> +}
> +
> +static int niu_init_zcp(struct niu *np)
> +{
> + u64 data[5], rbuf[5];
> + int i, max, err;
> +
> + if (np->parent->plat_type != PLAT_TYPE_NIU) {
> + if (np->port == 0 || np->port == 1)
> + max = ATLAS_P0_P1_CFIFO_ENTRIES;
> + else
> + max = ATLAS_P2_P3_CFIFO_ENTRIES;
> + } else
> + max = NIU_CFIFO_ENTRIES;
> +
> + data[0] = 0;
> + data[1] = 0;
> + data[2] = 0;
> + data[3] = 0;
> + data[4] = 0;
> +
> + for (i = 0; i < max; i++) {
> + err = niu_zcp_write(np, i, data);
> + if (err)
> + return err;
> + err = niu_zcp_read(np, i, rbuf);
> + if (err)
> + return err;
> + }
> +
> + niu_zcp_cfifo_reset(np);
> + nw64(CFIFO_ECC(np->port), 0);
> + nw64(ZCP_INT_STAT, ZCP_INT_STAT_ALL);
> + (void) nr64(ZCP_INT_STAT);
> + nw64(ZCP_INT_MASK, ZCP_INT_MASK_ALL);
> +
> + return 0;
> +}
> +
> +static void niu_ipp_write(struct niu *np, int index, u64 *data)
> +{
> + u64 val = nr64_ipp(IPP_CFIG);
> +
> + nw64_ipp(IPP_CFIG, val | IPP_CFIG_DFIFO_PIO_W);
> + nw64_ipp(IPP_DFIFO_WR_PTR, index);
> + nw64_ipp(IPP_DFIFO_WR0, data[0]);
> + nw64_ipp(IPP_DFIFO_WR1, data[1]);
> + nw64_ipp(IPP_DFIFO_WR2, data[2]);
> + nw64_ipp(IPP_DFIFO_WR3, data[3]);
> + nw64_ipp(IPP_DFIFO_WR4, data[4]);
> + nw64_ipp(IPP_CFIG, val & ~IPP_CFIG_DFIFO_PIO_W);
> +}
> +
> +static void niu_ipp_read(struct niu *np, int index, u64 *data)
> +{
> + nw64_ipp(IPP_DFIFO_RD_PTR, index);
> + data[0] = nr64_ipp(IPP_DFIFO_RD0);
> + data[1] = nr64_ipp(IPP_DFIFO_RD1);
> + data[2] = nr64_ipp(IPP_DFIFO_RD2);
> + data[3] = nr64_ipp(IPP_DFIFO_RD3);
> + data[4] = nr64_ipp(IPP_DFIFO_RD4);
> +}
> +
> +static int niu_ipp_reset(struct niu *np)
> +{
> + return niu_set_and_wait_clear_ipp(np, IPP_CFIG, IPP_CFIG_SOFT_RST,
> + 1000, 100, "IPP_CFIG");
> +}
> +
> +static int niu_init_ipp(struct niu *np)
> +{
> + u64 data[5], rbuf[5], val;
> + int i, max, err;
> +
> + if (np->parent->plat_type != PLAT_TYPE_NIU) {
> + if (np->port == 0 || np->port == 1)
> + max = ATLAS_P0_P1_DFIFO_ENTRIES;
> + else
> + max = ATLAS_P2_P3_DFIFO_ENTRIES;
> + } else
> + max = NIU_DFIFO_ENTRIES;
> +
> + data[0] = 0;
> + data[1] = 0;
> + data[2] = 0;
> + data[3] = 0;
> + data[4] = 0;
> +
> + for (i = 0; i < max; i++) {
> + niu_ipp_write(np, i, data);
> + niu_ipp_read(np, i, rbuf);
> + }
> +
> + (void) nr64_ipp(IPP_INT_STAT);
> + (void) nr64_ipp(IPP_INT_STAT);
> +
> + err = niu_ipp_reset(np);
> + if (err)
> + return err;
> +
> + (void) nr64_ipp(IPP_PKT_DIS);
> + (void) nr64_ipp(IPP_BAD_CS_CNT);
> + (void) nr64_ipp(IPP_ECC);
> +
> + (void) nr64_ipp(IPP_INT_STAT);
> +
> + nw64_ipp(IPP_MSK, ~IPP_MSK_ALL);
> +
> + val = nr64_ipp(IPP_CFIG);
> + val &= ~IPP_CFIG_IP_MAX_PKT;
> + val |= (IPP_CFIG_IPP_ENABLE |
> + IPP_CFIG_DFIFO_ECC_EN |
> + IPP_CFIG_DROP_BAD_CRC |
> + IPP_CFIG_CKSUM_EN |
> + (0x1ffff << IPP_CFIG_IP_MAX_PKT_SHIFT));
> + nw64_ipp(IPP_CFIG, val);
> +
> + return 0;
> +}
> +
> +static void niu_init_xif_xmac(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u64 val;
> +
> + val = nr64_mac(XMAC_CONFIG);
> +
> + if ((np->flags & NIU_FLAGS_10G) != 0 &&
> + (np->flags & NIU_FLAGS_FIBER) != 0) {
> + if (netif_carrier_ok(np->dev)) {
> + val |= XMAC_CONFIG_LED_POLARITY;
> + val &= ~XMAC_CONFIG_FORCE_LED_ON;
> + } else {
> + val |= XMAC_CONFIG_FORCE_LED_ON;
> + val &= ~XMAC_CONFIG_LED_POLARITY;
> + }
> + }
> +
> + val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC;
> +
> + val |= XMAC_CONFIG_TX_OUTPUT_EN;
> +
> + if (lp->loopback_mode == LOOPBACK_MAC) {
> + val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC;
> + val |= XMAC_CONFIG_LOOPBACK;
> + } else {
> + val &= ~XMAC_CONFIG_LOOPBACK;
> + }
> +
> + if (np->flags & NIU_FLAGS_10G) {
> + val &= ~XMAC_CONFIG_LFS_DISABLE;
> + } else {
> + val |= XMAC_CONFIG_LFS_DISABLE;
> + if (!(np->flags & NIU_FLAGS_FIBER))
> + val |= XMAC_CONFIG_1G_PCS_BYPASS;
> + else
> + val &= ~XMAC_CONFIG_1G_PCS_BYPASS;
> + }
> +
> + val &= ~XMAC_CONFIG_10G_XPCS_BYPASS;
> +
> + if (lp->active_speed == SPEED_100)
> + val |= XMAC_CONFIG_SEL_CLK_25MHZ;
> + else
> + val &= ~XMAC_CONFIG_SEL_CLK_25MHZ;
> +
> + nw64_mac(XMAC_CONFIG, val);
> +
> + val = nr64_mac(XMAC_CONFIG);
> + val &= ~XMAC_CONFIG_MODE_MASK;
> + if (np->flags & NIU_FLAGS_10G) {
> + val |= XMAC_CONFIG_MODE_XGMII;
> + } else {
> + if (lp->active_speed == SPEED_100)
> + val |= XMAC_CONFIG_MODE_MII;
> + else
> + val |= XMAC_CONFIG_MODE_GMII;
> + }
> +
> + nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_init_xif_bmac(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u64 val;
> +
> + val = BMAC_XIF_CONFIG_TX_OUTPUT_EN;
> +
> + if (lp->loopback_mode == LOOPBACK_MAC)
> + val |= BMAC_XIF_CONFIG_MII_LOOPBACK;
> + else
> + val &= ~BMAC_XIF_CONFIG_MII_LOOPBACK;
> +
> + if (lp->active_speed == SPEED_1000)
> + val |= BMAC_XIF_CONFIG_GMII_MODE;
> + else
> + val &= ~BMAC_XIF_CONFIG_GMII_MODE;
> +
> + val &= ~(BMAC_XIF_CONFIG_LINK_LED |
> + BMAC_XIF_CONFIG_LED_POLARITY);
> +
> + if (!(np->flags & NIU_FLAGS_10G) &&
> + !(np->flags & NIU_FLAGS_FIBER) &&
> + lp->active_speed == SPEED_100)
> + val |= BMAC_XIF_CONFIG_25MHZ_CLOCK;
> + else
> + val &= ~BMAC_XIF_CONFIG_25MHZ_CLOCK;
> +
> + nw64_mac(BMAC_XIF_CONFIG, val);
> +}
> +
> +static void niu_init_xif(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_init_xif_xmac(np);
> + else
> + niu_init_xif_bmac(np);
> +}
> +
> +static void niu_pcs_mii_reset(struct niu *np)
> +{
> + u64 val = nr64_pcs(PCS_MII_CTL);
> + val |= PCS_MII_CTL_RST;
> + nw64_pcs(PCS_MII_CTL, val);
> +}
> +
> +static void niu_xpcs_reset(struct niu *np)
> +{
> + u64 val = nr64_xpcs(XPCS_CONTROL1);
> + val |= XPCS_CONTROL1_RESET;
> + nw64_xpcs(XPCS_CONTROL1, val);
> +}
> +
> +static int niu_init_pcs(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> + u64 val;
> +
> + switch (np->flags & (NIU_FLAGS_10G | NIU_FLAGS_FIBER)) {
> + case NIU_FLAGS_FIBER:
> + /* 1G fiber */
> + nw64_pcs(PCS_CONF, PCS_CONF_MASK | PCS_CONF_ENABLE);
> + nw64_pcs(PCS_DPATH_MODE, 0);
> + niu_pcs_mii_reset(np);
> + break;
> +
> + case NIU_FLAGS_10G:
> + case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
> + if (!(np->flags & NIU_FLAGS_XMAC))
> + return -EINVAL;
> +
> + /* 10G copper or fiber */
> + val = nr64_mac(XMAC_CONFIG);
> + val &= ~XMAC_CONFIG_10G_XPCS_BYPASS;
> + nw64_mac(XMAC_CONFIG, val);
> +
> + niu_xpcs_reset(np);
> +
> + val = nr64_xpcs(XPCS_CONTROL1);
> + if (lp->loopback_mode == LOOPBACK_PHY)
> + val |= XPCS_CONTROL1_LOOPBACK;
> + else
> + val &= ~XPCS_CONTROL1_LOOPBACK;
> + nw64_xpcs(XPCS_CONTROL1, val);
> +
> + nw64_xpcs(XPCS_DESKEW_ERR_CNT, 0);
> + (void) nr64_xpcs(XPCS_SYMERR_CNT01);
> + (void) nr64_xpcs(XPCS_SYMERR_CNT23);
> + break;
> +
> + case 0:
> + /* 1G copper */
> + nw64_pcs(PCS_DPATH_MODE, PCS_DPATH_MODE_MII);
> + niu_pcs_mii_reset(np);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_reset_tx_xmac(struct niu *np)
> +{
> + return niu_set_and_wait_clear_mac(np, XTXMAC_SW_RST,
> + (XTXMAC_SW_RST_REG_RS |
> + XTXMAC_SW_RST_SOFT_RST),
> + 1000, 100, "XTXMAC_SW_RST");
> +}
> +
> +static int niu_reset_tx_bmac(struct niu *np)
> +{
> + int limit;
> +
> + nw64_mac(BTXMAC_SW_RST, BTXMAC_SW_RST_RESET);
> + limit = 1000;
> + while (--limit >= 0) {
> + if (!(nr64_mac(BTXMAC_SW_RST) & BTXMAC_SW_RST_RESET))
> + break;
> + udelay(100);
> + }
> + if (limit < 0) {
> + printk(KERN_ERR PFX "Port %u TX BMAC would not reset, "
> + "BTXMAC_SW_RST[%llx]\n",
> + np->port,
> + (unsigned long long) nr64_mac(BTXMAC_SW_RST));
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_reset_tx_mac(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + return niu_reset_tx_xmac(np);
> + else
> + return niu_reset_tx_bmac(np);
> +}
> +
> +static void niu_init_tx_xmac(struct niu *np, u64 min, u64 max)
> +{
> + u64 val;
> +
> + val = nr64_mac(XMAC_MIN);
> + val &= ~(XMAC_MIN_TX_MIN_PKT_SIZE |
> + XMAC_MIN_RX_MIN_PKT_SIZE);
> + val |= (min << XMAC_MIN_RX_MIN_PKT_SIZE_SHFT);
> + val |= (min << XMAC_MIN_TX_MIN_PKT_SIZE_SHFT);
> + nw64_mac(XMAC_MIN, val);
> +
> + nw64_mac(XMAC_MAX, max);
> +
> + nw64_mac(XTXMAC_STAT_MSK, ~(u64)0);
> +
> + val = nr64_mac(XMAC_IPG);
> + if (np->flags & NIU_FLAGS_10G) {
> + val &= ~XMAC_IPG_IPG_XGMII;
> + val |= (IPG_12_15_XGMII << XMAC_IPG_IPG_XGMII_SHIFT);
> + } else {
> + val &= ~XMAC_IPG_IPG_MII_GMII;
> + val |= (IPG_12_MII_GMII << XMAC_IPG_IPG_MII_GMII_SHIFT);
> + }
> + nw64_mac(XMAC_IPG, val);
> +
> + val = nr64_mac(XMAC_CONFIG);
> + val &= ~(XMAC_CONFIG_ALWAYS_NO_CRC |
> + XMAC_CONFIG_STRETCH_MODE |
> + XMAC_CONFIG_VAR_MIN_IPG_EN |
> + XMAC_CONFIG_TX_ENABLE);
> + nw64_mac(XMAC_CONFIG, val);
> +
> + nw64_mac(TXMAC_FRM_CNT, 0);
> + nw64_mac(TXMAC_BYTE_CNT, 0);
> +}
> +
> +static void niu_init_tx_bmac(struct niu *np, u64 min, u64 max)
> +{
> + u64 val;
> +
> + nw64_mac(BMAC_MIN_FRAME, min);
> + nw64_mac(BMAC_MAX_FRAME, max);
> +
> + nw64_mac(BTXMAC_STATUS_MASK, ~(u64)0);
> + nw64_mac(BMAC_CTRL_TYPE, 0x8808);
> + nw64_mac(BMAC_PREAMBLE_SIZE, 7);
> +
> + val = nr64_mac(BTXMAC_CONFIG);
> + val &= ~(BTXMAC_CONFIG_FCS_DISABLE |
> + BTXMAC_CONFIG_ENABLE);
> + nw64_mac(BTXMAC_CONFIG, val);
> +}
> +
> +static void niu_init_tx_mac(struct niu *np)
> +{
> + u64 min, max;
> +
> + min = 64;
> + if (np->dev->mtu > ETH_DATA_LEN)
> + max = 9216;
> + else
> + max = 1522;
> +
> + /* The XMAC_MIN register only accepts values for TX min which
> + * have the low 3 bits cleared.
> + */
> + BUILD_BUG_ON(min & 0x7);
> +
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_init_tx_xmac(np, min, max);
> + else
> + niu_init_tx_bmac(np, min, max);
> +}
> +
> +static int niu_reset_rx_xmac(struct niu *np)
> +{
> + int limit;
> +
> + nw64_mac(XRXMAC_SW_RST,
> + XRXMAC_SW_RST_REG_RS | XRXMAC_SW_RST_SOFT_RST);
> + limit = 1000;
> + while (--limit >= 0) {
> + if (!(nr64_mac(XRXMAC_SW_RST) & (XRXMAC_SW_RST_REG_RS |
> + XRXMAC_SW_RST_SOFT_RST)))
> + break;
> + udelay(100);
> + }
> + if (limit < 0) {
> + printk(KERN_ERR PFX "Port %u RX XMAC would not reset, "
> + "XRXMAC_SW_RST[%llx]\n",
> + np->port,
> + (unsigned long long) nr64_mac(XRXMAC_SW_RST));
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_reset_rx_bmac(struct niu *np)
> +{
> + int limit;
> +
> + nw64_mac(BRXMAC_SW_RST, BRXMAC_SW_RST_RESET);
> + limit = 1000;
> + while (--limit >= 0) {
> + if (!(nr64_mac(BRXMAC_SW_RST) & BRXMAC_SW_RST_RESET))
> + break;
> + udelay(100);
> + }
> + if (limit < 0) {
> + printk(KERN_ERR PFX "Port %u RX BMAC would not reset, "
> + "BRXMAC_SW_RST[%llx]\n",
> + np->port,
> + (unsigned long long) nr64_mac(BRXMAC_SW_RST));
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int niu_reset_rx_mac(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + return niu_reset_rx_xmac(np);
> + else
> + return niu_reset_rx_bmac(np);
> +}
> +
> +static void niu_init_rx_xmac(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> + int first_rdc_table = tp->first_table_num;
> + unsigned long i;
> + u64 val;
> +
> + nw64_mac(XMAC_ADD_FILT0, 0);
> + nw64_mac(XMAC_ADD_FILT1, 0);
> + nw64_mac(XMAC_ADD_FILT2, 0);
> + nw64_mac(XMAC_ADD_FILT12_MASK, 0);
> + nw64_mac(XMAC_ADD_FILT00_MASK, 0);
> + for (i = 0; i < MAC_NUM_HASH; i++)
> + nw64_mac(XMAC_HASH_TBL(i), 0);
> + nw64_mac(XRXMAC_STAT_MSK, ~(u64)0);
> + niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> + niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> +
> + val = nr64_mac(XMAC_CONFIG);
> + val &= ~(XMAC_CONFIG_RX_MAC_ENABLE |
> + XMAC_CONFIG_PROMISCUOUS |
> + XMAC_CONFIG_PROMISC_GROUP |
> + XMAC_CONFIG_ERR_CHK_DIS |
> + XMAC_CONFIG_RX_CRC_CHK_DIS |
> + XMAC_CONFIG_RESERVED_MULTICAST |
> + XMAC_CONFIG_RX_CODEV_CHK_DIS |
> + XMAC_CONFIG_ADDR_FILTER_EN |
> + XMAC_CONFIG_RCV_PAUSE_ENABLE |
> + XMAC_CONFIG_STRIP_CRC |
> + XMAC_CONFIG_PASS_FLOW_CTRL |
> + XMAC_CONFIG_MAC2IPP_PKT_CNT_EN);
> + val |= (XMAC_CONFIG_HASH_FILTER_EN);
> + nw64_mac(XMAC_CONFIG, val);
> +
> + nw64_mac(RXMAC_BT_CNT, 0);
> + nw64_mac(RXMAC_BC_FRM_CNT, 0);
> + nw64_mac(RXMAC_MC_FRM_CNT, 0);
> + nw64_mac(RXMAC_FRAG_CNT, 0);
> + nw64_mac(RXMAC_HIST_CNT1, 0);
> + nw64_mac(RXMAC_HIST_CNT2, 0);
> + nw64_mac(RXMAC_HIST_CNT3, 0);
> + nw64_mac(RXMAC_HIST_CNT4, 0);
> + nw64_mac(RXMAC_HIST_CNT5, 0);
> + nw64_mac(RXMAC_HIST_CNT6, 0);
> + nw64_mac(RXMAC_HIST_CNT7, 0);
> + nw64_mac(RXMAC_MPSZER_CNT, 0);
> + nw64_mac(RXMAC_CRC_ER_CNT, 0);
> + nw64_mac(RXMAC_CD_VIO_CNT, 0);
> + nw64_mac(LINK_FAULT_CNT, 0);
> +}
> +
> +static void niu_init_rx_bmac(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> + int first_rdc_table = tp->first_table_num;
> + unsigned long i;
> + u64 val;
> +
> + nw64_mac(BMAC_ADD_FILT0, 0);
> + nw64_mac(BMAC_ADD_FILT1, 0);
> + nw64_mac(BMAC_ADD_FILT2, 0);
> + nw64_mac(BMAC_ADD_FILT12_MASK, 0);
> + nw64_mac(BMAC_ADD_FILT00_MASK, 0);
> + for (i = 0; i < MAC_NUM_HASH; i++)
> + nw64_mac(BMAC_HASH_TBL(i), 0);
> + niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> + niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> + nw64_mac(BRXMAC_STATUS_MASK, ~(u64)0);
> +
> + val = nr64_mac(BRXMAC_CONFIG);
> + val &= ~(BRXMAC_CONFIG_ENABLE |
> + BRXMAC_CONFIG_STRIP_PAD |
> + BRXMAC_CONFIG_STRIP_FCS |
> + BRXMAC_CONFIG_PROMISC |
> + BRXMAC_CONFIG_PROMISC_GRP |
> + BRXMAC_CONFIG_ADDR_FILT_EN |
> + BRXMAC_CONFIG_DISCARD_DIS);
> + val |= (BRXMAC_CONFIG_HASH_FILT_EN);
> + nw64_mac(BRXMAC_CONFIG, val);
> +
> + val = nr64_mac(BMAC_ADDR_CMPEN);
> + val |= BMAC_ADDR_CMPEN_EN0;
> + nw64_mac(BMAC_ADDR_CMPEN, val);
> +}
> +
> +static void niu_init_rx_mac(struct niu *np)
> +{
> + niu_set_primary_mac(np, np->dev->dev_addr);
> +
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_init_rx_xmac(np);
> + else
> + niu_init_rx_bmac(np);
> +}
> +
> +static void niu_enable_tx_xmac(struct niu *np, int on)
> +{
> + u64 val = nr64_mac(XMAC_CONFIG);
> +
> + if (on)
> + val |= XMAC_CONFIG_TX_ENABLE;
> + else
> + val &= ~XMAC_CONFIG_TX_ENABLE;
> + nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_tx_bmac(struct niu *np, int on)
> +{
> + u64 val = nr64_mac(BTXMAC_CONFIG);
> +
> + if (on)
> + val |= BTXMAC_CONFIG_ENABLE;
> + else
> + val &= ~BTXMAC_CONFIG_ENABLE;
> + nw64_mac(BTXMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_tx_mac(struct niu *np, int on)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_enable_tx_xmac(np, on);
> + else
> + niu_enable_tx_bmac(np, on);
> +}
> +
> +static void niu_enable_rx_xmac(struct niu *np, int on)
> +{
> + u64 val = nr64_mac(XMAC_CONFIG);
> +
> + val &= ~(XMAC_CONFIG_HASH_FILTER_EN |
> + XMAC_CONFIG_PROMISCUOUS);
> +
> + if (np->flags & NIU_FLAGS_MCAST)
> + val |= XMAC_CONFIG_HASH_FILTER_EN;
> + if (np->flags & NIU_FLAGS_PROMISC)
> + val |= XMAC_CONFIG_PROMISCUOUS;
> +
> + if (on)
> + val |= XMAC_CONFIG_RX_MAC_ENABLE;
> + else
> + val &= ~XMAC_CONFIG_RX_MAC_ENABLE;
> + nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_rx_bmac(struct niu *np, int on)
> +{
> + u64 val = nr64_mac(BRXMAC_CONFIG);
> +
> + val &= ~(BRXMAC_CONFIG_HASH_FILT_EN |
> + BRXMAC_CONFIG_PROMISC);
> +
> + if (np->flags & NIU_FLAGS_MCAST)
> + val |= BRXMAC_CONFIG_HASH_FILT_EN;
> + if (np->flags & NIU_FLAGS_PROMISC)
> + val |= BRXMAC_CONFIG_PROMISC;
> +
> + if (on)
> + val |= BRXMAC_CONFIG_ENABLE;
> + else
> + val &= ~BRXMAC_CONFIG_ENABLE;
> + nw64_mac(BRXMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_rx_mac(struct niu *np, int on)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_enable_rx_xmac(np, on);
> + else
> + niu_enable_rx_bmac(np, on);
> +}
> +
> +static int niu_init_mac(struct niu *np)
> +{
> + int err;
> +
> + niu_init_xif(np);
> + err = niu_init_pcs(np);
> + if (err)
> + return err;
> +
> + err = niu_reset_tx_mac(np);
> + if (err)
> + return err;
> + niu_init_tx_mac(np);
> + err = niu_reset_rx_mac(np);
> + if (err)
> + return err;
> + niu_init_rx_mac(np);
> +
> + /* This looks hookey but the RX MAC reset we just did will
> + * undo some of the state we setup in niu_init_tx_mac() so we
> + * have to call it again. In particular, the RX MAC reset will
> + * set the XMAC_MAX register back to it's default value.
> + */
> + niu_init_tx_mac(np);
> + niu_enable_tx_mac(np, 1);
> +
> + niu_enable_rx_mac(np, 1);
> +
> + return 0;
> +}
> +
> +static void niu_stop_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> + (void) niu_tx_channel_stop(np, rp->tx_channel);
> +}
> +
> +static void niu_stop_tx_channels(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + niu_stop_one_tx_channel(np, rp);
> + }
> +}
> +
> +static void niu_reset_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> + (void) niu_tx_channel_reset(np, rp->tx_channel);
> +}
> +
> +static void niu_reset_tx_channels(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + niu_reset_one_tx_channel(np, rp);
> + }
> +}
> +
> +static void niu_stop_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> + (void) niu_enable_rx_channel(np, rp->rx_channel, 0);
> +}
> +
> +static void niu_stop_rx_channels(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + niu_stop_one_rx_channel(np, rp);
> + }
> +}
> +
> +static void niu_reset_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> + int channel = rp->rx_channel;
> +
> + (void) niu_rx_channel_reset(np, channel);
> + nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_ALL);
> + nw64(RX_DMA_CTL_STAT(channel), 0);
> + (void) niu_enable_rx_channel(np, channel, 0);
> +}
> +
> +static void niu_reset_rx_channels(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + niu_reset_one_rx_channel(np, rp);
> + }
> +}
> +
> +static void niu_disable_ipp(struct niu *np)
> +{
> + u64 rd, wr, val;
> + int limit;
> +
> + rd = nr64_ipp(IPP_DFIFO_RD_PTR);
> + wr = nr64_ipp(IPP_DFIFO_WR_PTR);
> + limit = 100;
> + while (--limit >= 0 && (rd != wr)) {
> + rd = nr64_ipp(IPP_DFIFO_RD_PTR);
> + wr = nr64_ipp(IPP_DFIFO_WR_PTR);
> + }
> + if (limit < 0 &&
> + (rd != 0 && wr != 1)) {
> + printk(KERN_ERR PFX "%s: IPP would not quiesce, "
> + "rd_ptr[%llx] wr_ptr[%llx]\n",
> + np->dev->name,
> + (unsigned long long) nr64_ipp(IPP_DFIFO_RD_PTR),
> + (unsigned long long) nr64_ipp(IPP_DFIFO_WR_PTR));
> + }
> +
> + val = nr64_ipp(IPP_CFIG);
> + val &= ~(IPP_CFIG_IPP_ENABLE |
> + IPP_CFIG_DFIFO_ECC_EN |
> + IPP_CFIG_DROP_BAD_CRC |
> + IPP_CFIG_CKSUM_EN);
> + nw64_ipp(IPP_CFIG, val);
> +
> + (void) niu_ipp_reset(np);
> +}
> +
> +static int niu_init_hw(struct niu *np)
> +{
> + int i, err;
> +
> + niudbg(INIT_HW, "%s: Initialize TXC\n", np->dev->name);
> + niu_txc_enable_port(np, 1);
> + niu_txc_port_dma_enable(np, 1);
> + niu_txc_set_imask(np, 0);
> +
> + niudbg(INIT_HW, "%s: Initialize TX channels\n", np->dev->name);
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + err = niu_init_one_tx_channel(np, rp);
> + if (err)
> + return err;
> + }
> +
> + niudbg(INIT_HW, "%s: Initialize RX channels\n", np->dev->name);
> + err = niu_init_rx_channels(np);
> + if (err)
> + goto out_uninit_tx_channels;
> +
> + niudbg(INIT_HW, "%s: Initialize classifier\n", np->dev->name);
> + err = niu_init_classifier_hw(np);
> + if (err)
> + goto out_uninit_rx_channels;
> +
> + niudbg(INIT_HW, "%s: Initialize ZCP\n", np->dev->name);
> + err = niu_init_zcp(np);
> + if (err)
> + goto out_uninit_rx_channels;
> +
> + niudbg(INIT_HW, "%s: Initialize IPP\n", np->dev->name);
> + err = niu_init_ipp(np);
> + if (err)
> + goto out_uninit_rx_channels;
> +
> + niudbg(INIT_HW, "%s: Initialize MAC\n", np->dev->name);
> + err = niu_init_mac(np);
> + if (err)
> + goto out_uninit_ipp;
> +
> + return 0;
> +
> +out_uninit_ipp:
> + niudbg(INIT_HW, "%s: Uninit IPP\n", np->dev->name);
> + niu_disable_ipp(np);
> +
> +out_uninit_rx_channels:
> + niudbg(INIT_HW, "%s: Uninit RX channels\n", np->dev->name);
> + niu_stop_rx_channels(np);
> + niu_reset_rx_channels(np);
> +
> +out_uninit_tx_channels:
> + niudbg(INIT_HW, "%s: Uninit TX channels\n", np->dev->name);
> + niu_stop_tx_channels(np);
> + niu_reset_tx_channels(np);
> +
> + return err;
> +}
> +
> +static void niu_stop_hw(struct niu *np)
> +{
> + niudbg(STOP_HW, "%s: Disable interrupts\n", np->dev->name);
> + niu_enable_interrupts(np, 0);
> +
> + niudbg(STOP_HW, "%s: Disable RX MAC\n", np->dev->name);
> + niu_enable_rx_mac(np, 0);
> +
> + niudbg(STOP_HW, "%s: Disable IPP\n", np->dev->name);
> + niu_disable_ipp(np);
> +
> + niudbg(STOP_HW, "%s: Stop TX channels\n", np->dev->name);
> + niu_stop_tx_channels(np);
> +
> + niudbg(STOP_HW, "%s: Stop RX channels\n", np->dev->name);
> + niu_stop_rx_channels(np);
> +
> + niudbg(STOP_HW, "%s: Reset TX channels\n", np->dev->name);
> + niu_reset_tx_channels(np);
> +
> + niudbg(STOP_HW, "%s: Reset RX channels\n", np->dev->name);
> + niu_reset_rx_channels(np);
> +}
> +
> +static int niu_request_irq(struct niu *np)
> +{
> + int i, j, err;
> +
> + err = 0;
> + for (i = 0; i < np->num_ldg; i++) {
> + struct niu_ldg *lp = &np->ldg[i];
> + int err;
> +
> + err = request_irq(lp->irq, niu_interrupt,
> + IRQF_SHARED | IRQF_SAMPLE_RANDOM,
> + np->dev->name, lp);
> + if (err)
> + goto out_free_irqs;
> +
> + }
> +
> + return 0;
> +
> +out_free_irqs:
> + for (j = 0; j < i; j++) {
> + struct niu_ldg *lp = &np->ldg[j];
> +
> + free_irq(lp->irq, lp);
> + }
> + return err;
> +}
> +
> +static void niu_free_irq(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_ldg; i++) {
> + struct niu_ldg *lp = &np->ldg[i];
> +
> + free_irq(lp->irq, lp);
> + }
> +}
> +
> +static void niu_enable_napi(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_ldg; i++)
> + napi_enable(&np->ldg[i].napi);
> +}
> +
> +static void niu_disable_napi(struct niu *np)
> +{
> + int i;
> +
> + for (i = 0; i < np->num_ldg; i++)
> + napi_disable(&np->ldg[i].napi);
> +}
> +
> +static int niu_open(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> + int err;
> +
> + netif_carrier_off(dev);
> +
> + err = niu_alloc_channels(np);
> + if (err)
> + goto out_err;
> +
> + err = niu_enable_interrupts(np, 0);
> + if (err)
> + goto out_free_channels;
> +
> + err = niu_request_irq(np);
> + if (err)
> + goto out_free_channels;
> +
> + niu_enable_napi(np);
> +
> + spin_lock_irq(&np->lock);
> +
> + err = niu_init_hw(np);
> + if (!err) {
> + init_timer(&np->timer);
> + np->timer.expires = jiffies + HZ;
> + np->timer.data = (unsigned long) np;
> + np->timer.function = niu_timer;
> +
> + err = niu_enable_interrupts(np, 1);
> + if (err)
> + niu_stop_hw(np);
> + }
> +
> + spin_unlock_irq(&np->lock);
> +
> + if (err) {
> + niu_disable_napi(np);
> + goto out_free_irq;
> + }
> +
> + netif_start_queue(dev);
> +
> + if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> + netif_carrier_on(dev);
> +
> + add_timer(&np->timer);
> +
> + return 0;
> +
> +out_free_irq:
> + niu_free_irq(np);
> +
> +out_free_channels:
> + niu_free_channels(np);
> +
> +out_err:
> + return err;
> +}
> +
> +static void niu_full_shutdown(struct niu *np, struct net_device *dev)
> +{
> + cancel_work_sync(&np->reset_task);
> +
> + niu_disable_napi(np);
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&np->timer);
> +
> + spin_lock_irq(&np->lock);
> +
> + niu_stop_hw(np);
> +
> + spin_unlock_irq(&np->lock);
> +}
> +
> +static int niu_close(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> +
> + niu_full_shutdown(np, dev);
> +
> + niu_free_irq(np);
> +
> + niu_free_channels(np);
> +
> + return 0;
> +}
> +
> +static void niu_sync_xmac_stats(struct niu *np)
> +{
> + struct niu_xmac_stats *mp = &np->mac_stats.xmac;
> +
> + mp->tx_frames += nr64_mac(TXMAC_FRM_CNT);
> + mp->tx_bytes += nr64_mac(TXMAC_BYTE_CNT);
> +
> + mp->rx_link_faults += nr64_mac(LINK_FAULT_CNT);
> + mp->rx_align_errors += nr64_mac(RXMAC_ALIGN_ERR_CNT);
> + mp->rx_frags += nr64_mac(RXMAC_FRAG_CNT);
> + mp->rx_mcasts += nr64_mac(RXMAC_MC_FRM_CNT);
> + mp->rx_bcasts += nr64_mac(RXMAC_BC_FRM_CNT);
> + mp->rx_hist_cnt1 += nr64_mac(RXMAC_HIST_CNT1);
> + mp->rx_hist_cnt2 += nr64_mac(RXMAC_HIST_CNT2);
> + mp->rx_hist_cnt3 += nr64_mac(RXMAC_HIST_CNT3);
> + mp->rx_hist_cnt4 += nr64_mac(RXMAC_HIST_CNT4);
> + mp->rx_hist_cnt5 += nr64_mac(RXMAC_HIST_CNT5);
> + mp->rx_hist_cnt6 += nr64_mac(RXMAC_HIST_CNT6);
> + mp->rx_hist_cnt7 += nr64_mac(RXMAC_HIST_CNT7);
> + mp->rx_octets += nr64_mac(RXMAC_BT_CNT);
> + mp->rx_code_violations += nr64_mac(RXMAC_CD_VIO_CNT);
> + mp->rx_len_errors += nr64_mac(RXMAC_MPSZER_CNT);
> + mp->rx_crc_errors += nr64_mac(RXMAC_CRC_ER_CNT);
> +}
> +
> +static void niu_sync_bmac_stats(struct niu *np)
> +{
> + struct niu_bmac_stats *mp = &np->mac_stats.bmac;
> +
> + mp->tx_bytes += nr64_mac(BTXMAC_BYTE_CNT);
> + mp->tx_frames += nr64_mac(BTXMAC_FRM_CNT);
> +
> + mp->rx_frames += nr64_mac(BRXMAC_FRAME_CNT);
> + mp->rx_align_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT);
> + mp->rx_crc_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT);
> + mp->rx_len_errors += nr64_mac(BRXMAC_CODE_VIOL_ERR_CNT);
> +}
> +
> +static void niu_sync_mac_stats(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_sync_xmac_stats(np);
> + else
> + niu_sync_bmac_stats(np);
> +}
> +
> +static void niu_get_rx_stats(struct niu *np)
> +{
> + unsigned long pkts, dropped, errors, bytes;
> + int i;
> +
> + pkts = dropped = errors = bytes = 0;
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + pkts += rp->rx_packets;
> + bytes += rp->rx_bytes;
> + dropped += rp->rx_dropped;
> + errors += rp->rx_errors;
> + }
> + np->net_stats.rx_packets = pkts;
> + np->net_stats.rx_bytes = bytes;
> + np->net_stats.rx_dropped = dropped;
> + np->net_stats.rx_errors = errors;
> +}
> +
> +static void niu_get_tx_stats(struct niu *np)
> +{
> + unsigned long pkts, errors, bytes;
> + int i;
> +
> + pkts = errors = bytes = 0;
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + pkts += rp->tx_packets;
> + bytes += rp->tx_bytes;
> + errors += rp->tx_errors;
> + }
> + np->net_stats.tx_packets = pkts;
> + np->net_stats.tx_bytes = bytes;
> + np->net_stats.tx_errors = errors;
> +}
> +
> +static struct net_device_stats *niu_get_stats(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> +
> + niu_get_rx_stats(np);
> + niu_get_tx_stats(np);
> +
> + return &np->net_stats;
> +}
> +
> +static void niu_load_hash_xmac(struct niu *np, u16 *hash)
> +{
> + int i;
> +
> + for (i = 0; i < 16; i++)
> + nw64_mac(XMAC_HASH_TBL(i), hash[i]);
> +}
> +
> +static void niu_load_hash_bmac(struct niu *np, u16 *hash)
> +{
> + int i;
> +
> + for (i = 0; i < 16; i++)
> + nw64_mac(BMAC_HASH_TBL(i), hash[i]);
> +}
> +
> +static void niu_load_hash(struct niu *np, u16 *hash)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + niu_load_hash_xmac(np, hash);
> + else
> + niu_load_hash_bmac(np, hash);
> +}
> +
> +static void niu_set_rx_mode(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> + int i, alt_cnt, err;
> + struct dev_addr_list *addr;
> + unsigned long flags;
> + u16 hash[16] = { 0, };
> +
> + spin_lock_irqsave(&np->lock, flags);
> + niu_enable_rx_mac(np, 0);
> +
> + np->flags &= ~(NIU_FLAGS_MCAST | NIU_FLAGS_PROMISC);
> + if (dev->flags & IFF_PROMISC)
> + np->flags |= NIU_FLAGS_PROMISC;
> + if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 0))
> + np->flags |= NIU_FLAGS_MCAST;
> +
> + alt_cnt = dev->uc_count;
> + if (alt_cnt > niu_num_alt_addr(np)) {
> + alt_cnt = 0;
> + np->flags |= NIU_FLAGS_PROMISC;
> + }
> +
> + if (alt_cnt) {
> + int index = 0;
> +
> + for (addr = dev->uc_list; addr; addr = addr->next) {
> + err = niu_set_alt_mac(np, index,
> + addr->da_addr);
> + if (err)
> + printk(KERN_WARNING PFX "%s: Error %d "
> + "adding alt mac %d\n",
> + dev->name, err, index);
> + err = niu_enable_alt_mac(np, index, 1);
> + if (err)
> + printk(KERN_WARNING PFX "%s: Error %d "
> + "enabling alt mac %d\n",
> + dev->name, err, index);
> +
> + index++;
> + }
> + } else {
> + for (i = 0; i < niu_num_alt_addr(np); i++) {
> + err = niu_enable_alt_mac(np, i, 0);
> + if (err)
> + printk(KERN_WARNING PFX "%s: Error %d "
> + "disabling alt mac %d\n",
> + dev->name, err, i);
> + }
> + }
> + if (dev->flags & IFF_ALLMULTI) {
> + for (i = 0; i < 16; i++)
> + hash[i] = 0xffff;
> + } else if (dev->mc_count > 0) {
> + for (addr = dev->mc_list; addr; addr = addr->next) {
> + u32 crc = ether_crc_le(ETH_ALEN, addr->da_addr);
> +
> + crc >>= 24;
> + hash[crc >> 4] |= (1 << (15 - (crc & 0xf)));
> + }
> + }
> +
> + if (np->flags & NIU_FLAGS_MCAST)
> + niu_load_hash(np, hash);
> +
> + niu_enable_rx_mac(np, 1);
> + spin_unlock_irqrestore(&np->lock, flags);
> +}
> +
> +static int niu_set_mac_addr(struct net_device *dev, void *p)
> +{
> + struct niu *np = netdev_priv(dev);
> + struct sockaddr *addr = p;
> + unsigned long flags;
> +
> + if (!is_valid_ether_addr(addr->sa_data))
> + return -EINVAL;
> +
> + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
> +
> + if (!netif_running(dev))
> + return 0;
> +
> + spin_lock_irqsave(&np->lock, flags);
> + niu_enable_rx_mac(np, 0);
> + niu_set_primary_mac(np, dev->dev_addr);
> + niu_enable_rx_mac(np, 1);
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + return 0;
> +}
> +
> +static int niu_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static void niu_netif_stop(struct niu *np)
> +{
> + np->dev->trans_start = jiffies; /* prevent tx timeout */
> +
> + niu_disable_napi(np);
> +
> + netif_tx_disable(np->dev);
> +}
> +
> +static void niu_netif_start(struct niu *np)
> +{
> + /* NOTE: unconditional netif_wake_queue is only appropriate
> + * so long as all callers are assured to have free tx slots
> + * (such as after niu_init_hw).
> + */
> + netif_wake_queue(np->dev);
> +
> + niu_enable_napi(np);
> +
> + niu_enable_interrupts(np, 1);
> +}
> +
> +static void niu_reset_task(struct work_struct *work)
> +{
> + struct niu *np = container_of(work, struct niu, reset_task);
> + unsigned long flags;
> + int err;
> +
> + spin_lock_irqsave(&np->lock, flags);
> + if (!netif_running(np->dev)) {
> + spin_unlock_irqrestore(&np->lock, flags);
> + return;
> + }
> +
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + del_timer_sync(&np->timer);
> +
> + niu_netif_stop(np);
> +
> + spin_lock_irqsave(&np->lock, flags);
> +
> + niu_stop_hw(np);
> +
> + err = niu_init_hw(np);
> + if (!err) {
> + np->timer.expires = jiffies + HZ;
> + add_timer(&np->timer);
> + niu_netif_start(np);
> + }
> +
> + spin_unlock_irqrestore(&np->lock, flags);
> +}
> +
> +static void niu_tx_timeout(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> +
> + printk(KERN_ERR PFX "%s: Transmit timed out, resetting\n",
> + dev->name);
> +
> + schedule_work(&np->reset_task);
> +}
> +
> +static void niu_set_txd(struct tx_ring_info *rp, int index,
> + u64 mapping, u64 len, u64 mark,
> + u64 n_frags)
> +{
> + __le64 *desc = &rp->descr[index];
> +
> + *desc = cpu_to_le64(mark |
> + (n_frags << TX_DESC_NUM_PTR_SHIFT) |
> + (len << TX_DESC_TR_LEN_SHIFT) |
> + (mapping & TX_DESC_SAD));
> +}
> +
> +static u64 niu_compute_tx_flags(struct sk_buff *skb, struct ethhdr *ehdr,
> + u64 pad_bytes, u64 len)
> +{
> + u16 eth_proto, eth_proto_inner;
> + u64 csum_bits, l3off, ihl, ret;
> + u8 ip_proto;
> + int ipv6;
> +
> + eth_proto = be16_to_cpu(ehdr->h_proto);
> + eth_proto_inner = eth_proto;
> + if (eth_proto == ETH_P_8021Q) {
> + struct vlan_ethhdr *vp = (struct vlan_ethhdr *) ehdr;
> + __be16 val = vp->h_vlan_encapsulated_proto;
> +
> + eth_proto_inner = be16_to_cpu(val);
> + }
> +
> + ipv6 = ihl = 0;
> + switch (skb->protocol) {
> + case __constant_htons(ETH_P_IP):
> + ip_proto = ip_hdr(skb)->protocol;
> + ihl = ip_hdr(skb)->ihl;
> + break;
> + case __constant_htons(ETH_P_IPV6):
> + ip_proto = ipv6_hdr(skb)->nexthdr;
> + ihl = (40 >> 2);
> + ipv6 = 1;
> + break;
> + default:
> + ip_proto = ihl = 0;
> + break;
> + }
> +
> + csum_bits = TXHDR_CSUM_NONE;
> + if (skb->ip_summed == CHECKSUM_PARTIAL) {
> + u64 start, stuff;
> +
> + csum_bits = (ip_proto == IPPROTO_TCP ?
> + TXHDR_CSUM_TCP :
> + (ip_proto == IPPROTO_UDP ?
> + TXHDR_CSUM_UDP : TXHDR_CSUM_SCTP));
> +
> + start = skb_transport_offset(skb) -
> + (pad_bytes + sizeof(struct tx_pkt_hdr));
> + stuff = start + skb->csum_offset;
> +
> + csum_bits |= (start / 2) << TXHDR_L4START_SHIFT;
> + csum_bits |= (stuff / 2) << TXHDR_L4STUFF_SHIFT;
> + }
> +
> + l3off = skb_network_offset(skb) -
> + (pad_bytes + sizeof(struct tx_pkt_hdr));
> +
> + ret = (((pad_bytes / 2) << TXHDR_PAD_SHIFT) |
> + (len << TXHDR_LEN_SHIFT) |
> + ((l3off / 2) << TXHDR_L3START_SHIFT) |
> + (ihl << TXHDR_IHL_SHIFT) |
> + ((eth_proto_inner < 1536) ? TXHDR_LLC : 0) |
> + ((eth_proto == ETH_P_8021Q) ? TXHDR_VLAN : 0) |
> + (ipv6 ? TXHDR_IP_VER : 0) |
> + csum_bits);
> +
> + return ret;
> +}
> +
> +static struct tx_ring_info *tx_ring_select(struct niu *np, struct sk_buff *skb)
> +{
> + return &np->tx_rings[0];
> +}
> +
> +static int niu_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> + unsigned long align, headroom;
> + struct tx_ring_info *rp;
> + struct tx_pkt_hdr *tp;
> + unsigned int len, nfg;
> + struct ethhdr *ehdr;
> + int prod, i, tlen;
> + u64 mapping, mrk;
> +
> + rp = tx_ring_select(np, skb);
> +
> + if (niu_tx_avail(rp) <= (skb_shinfo(skb)->nr_frags + 1)) {
> + netif_stop_queue(dev);
> + printk(KERN_ERR PFX "%s: BUG! Tx ring full when "
> + "queue awake!\n", dev->name);
> + rp->tx_errors++;
> + return NETDEV_TX_BUSY;
> + }
> +
> + if (skb->len < ETH_ZLEN) {
> + unsigned int pad_bytes = ETH_ZLEN - skb->len;
> +
> + if (skb_pad(skb, pad_bytes))
> + goto out;
> + skb_put(skb, pad_bytes);
> + }
> +
> + len = sizeof(struct tx_pkt_hdr) + 15;
> + if (skb_headroom(skb) < len) {
> + struct sk_buff *skb_new;
> +
> + skb_new = skb_realloc_headroom(skb, len);
> + if (!skb_new) {
> + rp->tx_errors++;
> + goto out_drop;
> + }
> + kfree_skb(skb);
> + skb = skb_new;
> + }
> +
> + align = ((unsigned long) skb->data & (16 - 1));
> + headroom = align + sizeof(struct tx_pkt_hdr);
> +
> + ehdr = (struct ethhdr *) skb->data;
> + tp = (struct tx_pkt_hdr *) skb_push(skb, headroom);
> +
> + len = skb->len - sizeof(struct tx_pkt_hdr);
> + tp->flags = cpu_to_le64(niu_compute_tx_flags(skb, ehdr, align, len));
> + tp->resv = 0;
> +
> + len = skb_headlen(skb);
> + mapping = np->ops->map_single(np->device, skb->data,
> + len, DMA_TO_DEVICE);
> +
> + prod = rp->prod;
> +
> + rp->tx_buffs[prod].skb = skb;
> + rp->tx_buffs[prod].mapping = mapping;
> +
> + mrk = TX_DESC_SOP;
> + if (++rp->mark_counter == rp->mark_freq) {
> + rp->mark_counter = 0;
> + mrk |= TX_DESC_MARK;
> + rp->mark_pending++;
> + }
> +
> + tlen = len;
> + nfg = skb_shinfo(skb)->nr_frags;
> + while (tlen > 0) {
> + tlen -= MAX_TX_DESC_LEN;
> + nfg++;
> + }
> +
> + while (len > 0) {
> + unsigned int this_len = len;
> +
> + if (this_len > MAX_TX_DESC_LEN)
> + this_len = MAX_TX_DESC_LEN;
> +
> + niu_set_txd(rp, prod, mapping, this_len, mrk, nfg);
> + mrk = nfg = 0;
> +
> + prod = NEXT_TX(rp, prod);
> + mapping += this_len;
> + len -= this_len;
> + }
> +
> + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> + skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +
> + len = frag->size;
> + mapping = np->ops->map_page(np->device, frag->page,
> + frag->page_offset, len,
> + DMA_TO_DEVICE);
> +
> + rp->tx_buffs[prod].skb = NULL;
> + rp->tx_buffs[prod].mapping = mapping;
> +
> + niu_set_txd(rp, prod, mapping, len, 0, 0);
> +
> + prod = NEXT_TX(rp, prod);
> + }
> +
> + if (prod < rp->prod)
> + rp->wrap_bit ^= TX_RING_KICK_WRAP;
> + rp->prod = prod;
> +
> + nw64(TX_RING_KICK(rp->tx_channel), rp->wrap_bit | (prod << 3));
> +
> + if (unlikely(niu_tx_avail(rp) <= (MAX_SKB_FRAGS + 1))) {
> + netif_stop_queue(dev);
> + if (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp))
> + netif_wake_queue(dev);
> + }
> +
> + dev->trans_start = jiffies;
> +
> +out:
> + return NETDEV_TX_OK;
> +
> +out_drop:
> + rp->tx_errors++;
> + kfree_skb(skb);
> + goto out;
> +}
> +
> +static int niu_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + struct niu *np = netdev_priv(dev);
> + int err, orig_jumbo, new_jumbo;
> +
> + if (new_mtu < 68 || new_mtu > NIU_MAX_MTU)
> + return -EINVAL;
> +
> + orig_jumbo = (dev->mtu > ETH_DATA_LEN);
> + new_jumbo = (new_mtu > ETH_DATA_LEN);
> +
> + dev->mtu = new_mtu;
> +
> + if (!netif_running(dev) ||
> + (orig_jumbo == new_jumbo))
> + return 0;
> +
> + niu_full_shutdown(np, dev);
> +
> + niu_free_channels(np);
> +
> + niu_enable_napi(np);
> +
> + err = niu_alloc_channels(np);
> + if (err)
> + return err;
> +
> + spin_lock_irq(&np->lock);
> +
> + err = niu_init_hw(np);
> + if (!err) {
> + init_timer(&np->timer);
> + np->timer.expires = jiffies + HZ;
> + np->timer.data = (unsigned long) np;
> + np->timer.function = niu_timer;
> +
> + err = niu_enable_interrupts(np, 1);
> + if (err)
> + niu_stop_hw(np);
> + }
> +
> + spin_unlock_irq(&np->lock);
> +
> + if (!err) {
> + netif_start_queue(dev);
> + if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> + netif_carrier_on(dev);
> +
> + add_timer(&np->timer);
> + }
> +
> + return err;
> +}
> +
> +static void niu_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + struct niu *np = netdev_priv(dev);
> + struct niu_vpd *vpd = &np->vpd;
> +
> + strcpy(info->driver, DRV_MODULE_NAME);
> + strcpy(info->version, DRV_MODULE_VERSION);
> + sprintf(info->fw_version, "%d.%d",
> + vpd->fcode_major, vpd->fcode_minor);
> + if (np->parent->plat_type != PLAT_TYPE_NIU)
> + strcpy(info->bus_info, pci_name(np->pdev));
> +}
> +
> +static int niu_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> + struct niu *np = netdev_priv(dev);
> + struct niu_link_config *lp;
> +
> + lp = &np->link_config;
> +
> + memset(cmd, 0, sizeof(*cmd));
> + cmd->phy_address = np->phy_addr;
> + cmd->supported = lp->supported;
> + cmd->advertising = lp->advertising;
> + cmd->autoneg = lp->autoneg;
> + cmd->speed = lp->active_speed;
> + cmd->duplex = lp->active_duplex;
> +
> + return 0;
> +}
> +
> +static int niu_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> + return -EINVAL;
> +}
Why not leave function ptr null and get "-EOPNOTSUPP"
> +
> +static u32 niu_get_msglevel(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> + return np->msg_enable;
> +}
> +
> +static void niu_set_msglevel(struct net_device *dev, u32 value)
> +{
> + struct niu *np = netdev_priv(dev);
> + np->msg_enable = value;
> +}
> +
> +static int niu_get_eeprom_len(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> +
> + return np->eeprom_len;
> +}
> +
> +static int niu_get_eeprom(struct net_device *dev,
> + struct ethtool_eeprom *eeprom, u8 *data)
> +{
> + struct niu *np = netdev_priv(dev);
> + u32 offset, len, val;
> +
> + offset = eeprom->offset;
> + len = eeprom->len;
> +
> + if (offset + len < offset)
> + return -EINVAL;
> + if (offset >= np->eeprom_len)
> + return -EINVAL;
> + if (offset + len > np->eeprom_len)
> + len = eeprom->len = np->eeprom_len - offset;
> +
> + if (offset & 3) {
> + u32 b_offset, b_count;
> +
> + b_offset = offset & 3;
> + b_count = 4 - b_offset;
> + if (b_count > len)
> + b_count = len;
> +
> + val = nr64(ESPC_NCR((offset - b_offset) / 4));
> + memcpy(data, ((char *)&val) + b_offset, b_count);
> + data += b_count;
> + len -= b_count;
> + offset += b_count;
> + }
> + while (len >= 4) {
> + val = nr64(ESPC_NCR(offset / 4));
> + memcpy(data, &val, 4);
> + data += 4;
> + len -= 4;
> + offset += 4;
> + }
> + if (len) {
> + val = nr64(ESPC_NCR(offset / 4));
> + memcpy(data, &val, len);
> + }
> + return 0;
> +}
> +
> +static const struct {
> + const char string[ETH_GSTRING_LEN];
> +} niu_xmac_stat_keys[] = {
> + { "tx_frames" },
> + { "tx_bytes" },
> + { "tx_fifo_errors" },
> + { "tx_overflow_errors" },
> + { "tx_max_pkt_size_errors" },
> + { "tx_underflow_errors" },
> + { "rx_local_faults" },
> + { "rx_remote_faults" },
> + { "rx_link_faults" },
> + { "rx_align_errors" },
> + { "rx_frags" },
> + { "rx_mcasts" },
> + { "rx_bcasts" },
> + { "rx_hist_cnt1" },
> + { "rx_hist_cnt2" },
> + { "rx_hist_cnt3" },
> + { "rx_hist_cnt4" },
> + { "rx_hist_cnt5" },
> + { "rx_hist_cnt6" },
> + { "rx_hist_cnt7" },
> + { "rx_octets" },
> + { "rx_code_violations" },
> + { "rx_len_errors" },
> + { "rx_crc_errors" },
> + { "rx_underflows" },
> + { "rx_overflows" },
> + { "pause_off_state" },
> + { "pause_on_state" },
> + { "pause_received" },
> +};
> +
> +#define NUM_XMAC_STAT_KEYS ARRAY_SIZE(niu_xmac_stat_keys)
> +
> +static const struct {
> + const char string[ETH_GSTRING_LEN];
> +} niu_bmac_stat_keys[] = {
> + { "tx_underflow_errors" },
> + { "tx_max_pkt_size_errors" },
> + { "tx_bytes" },
> + { "tx_frames" },
> + { "rx_overflows" },
> + { "rx_frames" },
> + { "rx_align_errors" },
> + { "rx_crc_errors" },
> + { "rx_len_errors" },
> + { "pause_off_state" },
> + { "pause_on_state" },
> + { "pause_received" },
> +};
> +
> +#define NUM_BMAC_STAT_KEYS ARRAY_SIZE(niu_bmac_stat_keys)
> +
> +static const struct {
> + const char string[ETH_GSTRING_LEN];
> +} niu_rxchan_stat_keys[] = {
> + { "rx_channel" },
> + { "rx_packets" },
> + { "rx_bytes" },
> + { "rx_dropped" },
> + { "rx_errors" },
> +};
> +
> +#define NUM_RXCHAN_STAT_KEYS ARRAY_SIZE(niu_rxchan_stat_keys)
> +
> +static const struct {
> + const char string[ETH_GSTRING_LEN];
> +} niu_txchan_stat_keys[] = {
> + { "tx_channel" },
> + { "tx_packets" },
> + { "tx_bytes" },
> + { "tx_errors" },
> +};
> +
> +#define NUM_TXCHAN_STAT_KEYS ARRAY_SIZE(niu_txchan_stat_keys)
> +
> +static void niu_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> + struct niu *np = netdev_priv(dev);
> + int i;
> +
> + if (stringset != ETH_SS_STATS)
> + return;
> +
> + if (np->flags & NIU_FLAGS_XMAC) {
> + memcpy(data, niu_xmac_stat_keys,
> + sizeof(niu_xmac_stat_keys));
> + data += sizeof(niu_xmac_stat_keys);
> + } else {
> + memcpy(data, niu_bmac_stat_keys,
> + sizeof(niu_bmac_stat_keys));
> + data += sizeof(niu_bmac_stat_keys);
> + }
> + for (i = 0; i < np->num_rx_rings; i++) {
> + memcpy(data, niu_rxchan_stat_keys,
> + sizeof(niu_rxchan_stat_keys));
> + data += sizeof(niu_rxchan_stat_keys);
> + }
> + for (i = 0; i < np->num_tx_rings; i++) {
> + memcpy(data, niu_txchan_stat_keys,
> + sizeof(niu_txchan_stat_keys));
> + data += sizeof(niu_txchan_stat_keys);
> + }
> +}
> +
> +static int niu_get_stats_count(struct net_device *dev)
> +{
> + struct niu *np = netdev_priv(dev);
> +
> + return ((np->flags & NIU_FLAGS_XMAC ?
> + NUM_XMAC_STAT_KEYS :
> + NUM_BMAC_STAT_KEYS) +
> + (np->num_rx_rings * NUM_RXCHAN_STAT_KEYS) +
> + (np->num_tx_rings * NUM_TXCHAN_STAT_KEYS));
> +}
> +
> +static void niu_get_ethtool_stats(struct net_device *dev,
> + struct ethtool_stats *stats, u64 *data)
> +{
> + struct niu *np = netdev_priv(dev);
> + int i;
> +
> + niu_sync_mac_stats(np);
> + if (np->flags & NIU_FLAGS_XMAC) {
> + memcpy(data, &np->mac_stats.xmac,
> + sizeof(struct niu_xmac_stats));
> + data += (sizeof(struct niu_xmac_stats) / sizeof(u64));
> + } else {
> + memcpy(data, &np->mac_stats.bmac,
> + sizeof(struct niu_bmac_stats));
> + data += (sizeof(struct niu_bmac_stats) / sizeof(u64));
> + }
> + for (i = 0; i < np->num_rx_rings; i++) {
> + struct rx_ring_info *rp = &np->rx_rings[i];
> +
> + data[0] = rp->rx_channel;
> + data[1] = rp->rx_packets;
> + data[2] = rp->rx_bytes;
> + data[3] = rp->rx_dropped;
> + data[4] = rp->rx_errors;
> + data += 5;
> + }
> + for (i = 0; i < np->num_tx_rings; i++) {
> + struct tx_ring_info *rp = &np->tx_rings[i];
> +
> + data[0] = rp->tx_channel;
> + data[1] = rp->tx_packets;
> + data[2] = rp->tx_bytes;
> + data[3] = rp->tx_errors;
> + data += 4;
> + }
> +}
> +
> +static u64 niu_led_state_save(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + return nr64_mac(XMAC_CONFIG);
> + else
> + return nr64_mac(BMAC_XIF_CONFIG);
> +}
> +
> +static void niu_led_state_restore(struct niu *np, u64 val)
> +{
> + if (np->flags & NIU_FLAGS_XMAC)
> + nw64_mac(XMAC_CONFIG, val);
> + else
> + nw64_mac(BMAC_XIF_CONFIG, val);
> +}
> +
> +static void niu_force_led(struct niu *np, int on)
> +{
> + u64 val, reg, bit;
> +
> + if (np->flags & NIU_FLAGS_XMAC) {
> + reg = XMAC_CONFIG;
> + bit = XMAC_CONFIG_FORCE_LED_ON;
> + } else {
> + reg = BMAC_XIF_CONFIG;
> + bit = BMAC_XIF_CONFIG_LINK_LED;
> + }
> +
> + val = nr64_mac(reg);
> + if (on)
> + val |= bit;
> + else
> + val &= ~bit;
> + nw64_mac(reg, val);
> +}
> +
> +static int niu_phys_id(struct net_device *dev, u32 data)
> +{
> + struct niu *np = netdev_priv(dev);
> + u64 orig_led_state;
> + int i;
> +
> + if (!netif_running(dev))
> + return -EAGAIN;
> +
> + if (data == 0)
> + data = 2;
> +
> + orig_led_state = niu_led_state_save(np);
> + for (i = 0; i < (data * 2); i++) {
> + int on = ((i % 2) == 0);
> +
> + niu_force_led(np, on);
> +
> + if (msleep_interruptible(500))
> + break;
> + }
> + niu_led_state_restore(np, orig_led_state);
> +
> + return 0;
> +}
> +
> +static const struct ethtool_ops niu_ethtool_ops = {
> + .get_drvinfo = niu_get_drvinfo,
> + .get_link = ethtool_op_get_link,
> + .get_msglevel = niu_get_msglevel,
> + .set_msglevel = niu_set_msglevel,
> + .get_eeprom_len = niu_get_eeprom_len,
> + .get_eeprom = niu_get_eeprom,
> + .get_settings = niu_get_settings,
> + .set_settings = niu_set_settings,
> + .get_strings = niu_get_strings,
> + .get_stats_count = niu_get_stats_count,
> + .get_ethtool_stats = niu_get_ethtool_stats,
> + .phys_id = niu_phys_id,
> +};
> +
> +static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,
> + int ldg, int ldn)
> +{
> + if (ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX)
> + return -EINVAL;
> + if (ldn < 0 || ldn > LDN_MAX)
> + return -EINVAL;
> +
> + parent->ldg_map[ldn] = ldg;
> +
> + if (np->parent->plat_type == PLAT_TYPE_NIU) {
> + /* On N2 NIU, the ldn-->ldg assignments are setup and fixed by
> + * the firmware, and we're not supposed to change them. Validate
> + * the mapping, because if it's wrong we probably won't get any
> + * interrupts and that's painful to debug.
> + */
> + if (nr64(LDG_NUM(ldn)) != ldg) {
> + printk(KERN_ERR PFX "Port %u, mis-matched LDG assignment "
> + "for ldn %d, should be %d is %llu\n",
> + np->port, ldn, ldg,
> + (unsigned long long) nr64(LDG_NUM(ldn)));
> + return -EINVAL;
> + }
> + } else
> + nw64(LDG_NUM(ldn), ldg);
> +
> + return 0;
> +}
> +
> +static int niu_set_ldg_timer_res(struct niu *np, int res)
> +{
> + if (res < 0 || res > LDG_TIMER_RES_VAL)
> + return -EINVAL;
> +
> +
> + nw64(LDG_TIMER_RES, res);
> +
> + return 0;
> +}
> +
> +static int niu_set_ldg_sid(struct niu *np, int ldg, int func, int vector)
> +{
> + if ((ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX) ||
> + (func < 0 || func > 3) ||
> + (vector < 0 || vector > 0x1f))
> + return -EINVAL;
> +
> + nw64(SID(ldg), (func << SID_FUNC_SHIFT) | vector);
> +
> + return 0;
> +}
> +
> +static int __devinit niu_pci_eeprom_read(struct niu *np, u32 addr)
> +{
> + u64 frame, frame_base = (ESPC_PIO_STAT_READ_START |
> + (addr << ESPC_PIO_STAT_ADDR_SHIFT));
> + int limit;
> +
> + if (addr > (ESPC_PIO_STAT_ADDR >> ESPC_PIO_STAT_ADDR_SHIFT))
> + return -EINVAL;
> +
> + frame = frame_base;
> + nw64(ESPC_PIO_STAT, frame);
> + limit = 64;
> + do {
> + udelay(5);
> + frame = nr64(ESPC_PIO_STAT);
> + if (frame & ESPC_PIO_STAT_READ_END)
> + break;
> + } while (limit--);
> + if (!(frame & ESPC_PIO_STAT_READ_END)) {
> + printk(KERN_ERR PFX "EEPROM read timeout frame[%llx]\n",
> + (unsigned long long) frame);
> + return -ENODEV;
> + }
> +
> + frame = frame_base;
> + nw64(ESPC_PIO_STAT, frame);
> + limit = 64;
> + do {
> + udelay(5);
> + frame = nr64(ESPC_PIO_STAT);
> + if (frame & ESPC_PIO_STAT_READ_END)
> + break;
> + } while (limit--);
> + if (!(frame & ESPC_PIO_STAT_READ_END)) {
> + printk(KERN_ERR PFX "EEPROM read timeout frame[%llx]\n",
> + (unsigned long long) frame);
> + return -ENODEV;
> + }
> +
> + frame = nr64(ESPC_PIO_STAT);
> + return (frame & ESPC_PIO_STAT_DATA) >> ESPC_PIO_STAT_DATA_SHIFT;
> +}
> +
> +static int __devinit niu_pci_eeprom_read16(struct niu *np, u32 off)
> +{
> + int err = niu_pci_eeprom_read(np, off);
> + u16 val;
> +
> + if (err < 0)
> + return err;
> + val = (err << 8);
> + err = niu_pci_eeprom_read(np, off + 1);
> + if (err < 0)
> + return err;
> + val |= (err & 0xff);
> +
> + return val;
> +}
> +
> +static int __devinit niu_pci_eeprom_read16_swp(struct niu *np, u32 off)
> +{
> + int err = niu_pci_eeprom_read(np, off);
> + u16 val;
> +
> + if (err < 0) {
> + return err;
> + }
> + val = (err & 0xff);
> + err = niu_pci_eeprom_read(np, off + 1);
> + if (err < 0) {
> + return err;
> + }
> + val |= (err & 0xff) << 8;
> +
> + return val;
> +}
> +
> +static int __devinit niu_pci_vpd_get_propname(struct niu *np,
> + u32 off,
> + char *namebuf,
> + int namebuf_len)
> +{
> + int i;
> +
> + for (i = 0; i < namebuf_len; i++) {
> + int err = niu_pci_eeprom_read(np, off + i);
> + if (err < 0)
> + return err;
> + *namebuf++ = err;
> + if (!err)
> + break;
> + }
> + if (i >= namebuf_len)
> + return -EINVAL;
> +
> + return i + 1;
> +}
> +
> +static void __devinit niu_vpd_parse_version(struct niu *np)
> +{
> + struct niu_vpd *vpd = &np->vpd;
> + int len = strlen(vpd->version) + 1;
> + const char *s = vpd->version;
> + int i;
> +
> + for (i = 0; i < len - 5; i++) {
> + if (!strncmp(s + i, "FCode ", 5))
> + break;
> + }
> + if (i >= len - 5)
> + return;
> +
> + s += i + 5;
> + sscanf(s, "%d.%d", &vpd->fcode_major, &vpd->fcode_minor);
> +
> + niudbg(PROBE, "VPD_SCAN: FCODE major(%d) minor(%d)\n",
> + vpd->fcode_major, vpd->fcode_minor);
> + if (vpd->fcode_major > NIU_VPD_MIN_MAJOR ||
> + (vpd->fcode_major == NIU_VPD_MIN_MAJOR &&
> + vpd->fcode_minor >= NIU_VPD_MIN_MINOR))
> + np->flags |= NIU_FLAGS_VPD_VALID;
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static int __devinit niu_pci_vpd_scan_props(struct niu *np,
> + u32 start, u32 end)
> +{
> + unsigned int found_mask = 0;
> +#define FOUND_MASK_MODEL 0x00000001
> +#define FOUND_MASK_BMODEL 0x00000002
> +#define FOUND_MASK_VERS 0x00000004
> +#define FOUND_MASK_MAC 0x00000008
> +#define FOUND_MASK_NMAC 0x00000010
> +#define FOUND_MASK_PHY 0x00000020
> +#define FOUND_MASK_ALL 0x0000003f
> +
> + niudbg(PROBE, "VPD_SCAN: start[%x] end[%x]\n",
> + start, end);
> + while (start < end) {
> + int len, err, instance, type, prop_len;
> + char namebuf[64];
> + u8 *prop_buf;
> + int max_len;
> +
> + if (found_mask == FOUND_MASK_ALL) {
> + niu_vpd_parse_version(np);
> + return 1;
> + }
> +
> + err = niu_pci_eeprom_read(np, start + 2);
> + if (err < 0)
> + return err;
> + len = err;
> + start += 3;
> +
> + instance = niu_pci_eeprom_read(np, start);
> + type = niu_pci_eeprom_read(np, start + 3);
> + prop_len = niu_pci_eeprom_read(np, start + 4);
> + err = niu_pci_vpd_get_propname(np, start + 5, namebuf, 64);
> + if (err < 0)
> + return err;
> +
> + prop_buf = NULL;
> + max_len = 0;
> + if (!strcmp(namebuf, "model")) {
> + prop_buf = np->vpd.model;
> + max_len = NIU_VPD_MODEL_MAX;
> + found_mask |= FOUND_MASK_MODEL;
> + } else if (!strcmp(namebuf, "board-model")) {
> + prop_buf = np->vpd.board_model;
> + max_len = NIU_VPD_BD_MODEL_MAX;
> + found_mask |= FOUND_MASK_BMODEL;
> + } else if (!strcmp(namebuf, "version")) {
> + prop_buf = np->vpd.version;
> + max_len = NIU_VPD_VERSION_MAX;
> + found_mask |= FOUND_MASK_VERS;
> + } else if (!strcmp(namebuf, "local-mac-address")) {
> + prop_buf = np->vpd.local_mac;
> + max_len = ETH_ALEN;
> + found_mask |= FOUND_MASK_MAC;
> + } else if (!strcmp(namebuf, "num-mac-addresses")) {
> + prop_buf = &np->vpd.mac_num;
> + max_len = 1;
> + found_mask |= FOUND_MASK_NMAC;
> + } else if (!strcmp(namebuf, "phy-type")) {
> + prop_buf = np->vpd.phy_type;
> + max_len = NIU_VPD_PHY_TYPE_MAX;
> + found_mask |= FOUND_MASK_PHY;
> + }
> +
> + if (max_len && prop_len > max_len) {
> + printk(KERN_ERR PFX "Property '%s' length (%d) is "
> + "too long.\n", namebuf, prop_len);
> + return -EINVAL;
> + }
> +
> + if (prop_buf) {
> + u32 off = start + 5 + err;
> + int i;
> +
> + niudbg(PROBE, "VPD_SCAN: Reading in property [%s] "
> + "len[%d]\n", namebuf, prop_len);
> + for (i = 0; i < prop_len; i++)
> + *prop_buf++ = niu_pci_eeprom_read(np, off + i);
> + }
> +
> + start += len;
> + }
> +
> + return 0;
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static void __devinit niu_pci_vpd_fetch(struct niu *np, u32 start)
> +{
> + u32 offset;
> + int err;
> +
> + err = niu_pci_eeprom_read16_swp(np, start + 1);
> + if (err < 0)
> + return;
> +
> + offset = err + 3;
> +
> + while (start + offset < ESPC_EEPROM_SIZE) {
> + u32 here = start + offset;
> + u32 end;
> +
> + err = niu_pci_eeprom_read(np, here);
> + if (err != 0x90)
> + return;
> +
> + err = niu_pci_eeprom_read16_swp(np, here + 1);
> + if (err < 0)
> + return;
> +
> + here = start + offset + 3;
> + end = start + offset + err;
> +
> + offset += err;
> +
> + err = niu_pci_vpd_scan_props(np, here, end);
> + if (err < 0 || err == 1)
> + return;
> + }
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static u32 __devinit niu_pci_vpd_offset(struct niu *np)
> +{
> + u32 start = 0, end = ESPC_EEPROM_SIZE, ret;
> + int err;
> +
> + while (start < end) {
> + ret = start;
> +
> + /* ROM header signature? */
> + err = niu_pci_eeprom_read16(np, start + 0);
> + if (err != 0x55aa)
> + return 0;
> +
> + /* Apply offset to PCI data structure. */
> + err = niu_pci_eeprom_read16(np, start + 23);
> + if (err < 0)
> + return 0;
> + start += err;
> +
> + /* Check for "PCIR" signature. */
> + err = niu_pci_eeprom_read16(np, start + 0);
> + if (err != 0x5043)
> + return 0;
> + err = niu_pci_eeprom_read16(np, start + 2);
> + if (err != 0x4952)
> + return 0;
> +
> + /* Check for OBP image type. */
> + err = niu_pci_eeprom_read(np, start + 20);
> + if (err < 0)
> + return 0;
> + if (err != 0x01) {
> + err = niu_pci_eeprom_read(np, ret + 2);
> + if (err < 0)
> + return 0;
> +
> + start = ret + (err * 512);
> + continue;
> + }
> +
> + err = niu_pci_eeprom_read16_swp(np, start + 8);
> + if (err < 0)
> + return err;
> + ret += err;
> +
> + err = niu_pci_eeprom_read(np, ret + 0);
> + if (err != 0x82)
> + return 0;
> +
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int __devinit niu_phy_type_prop_decode(struct niu *np, const char *phy_prop)
> +{
> + if (!strcmp(phy_prop, "mif")) {
> + /* 1G copper, MII */
> + np->flags &= ~(NIU_FLAGS_FIBER |
> + NIU_FLAGS_10G);
> + np->mac_xcvr = MAC_XCVR_MII;
> + } else if (!strcmp(phy_prop, "xgf")) {
> + /* 10G fiber, XPCS */
> + np->flags |= (NIU_FLAGS_10G |
> + NIU_FLAGS_FIBER);
> + np->mac_xcvr = MAC_XCVR_XPCS;
> + } else if (!strcmp(phy_prop, "pcs")) {
> + /* 1G fiber, PCS */
> + np->flags &= ~NIU_FLAGS_10G;
> + np->flags |= NIU_FLAGS_FIBER;
> + np->mac_xcvr = MAC_XCVR_PCS;
> + } else if (!strcmp(phy_prop, "xgc")) {
> + /* 10G copper, XPCS */
> + np->flags |= NIU_FLAGS_10G;
> + np->flags &= ~NIU_FLAGS_FIBER;
> + np->mac_xcvr = MAC_XCVR_XPCS;
> + } else {
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static void __devinit niu_pci_vpd_validate(struct niu *np)
> +{
> + struct net_device *dev = np->dev;
> + struct niu_vpd *vpd = &np->vpd;
> + u8 val8;
> +
> + if (!is_valid_ether_addr(&vpd->local_mac[0])) {
> + printk(KERN_ERR PFX "VPD MAC invalid, "
> + "falling back to SPROM.\n");
> +
> + np->flags &= ~NIU_FLAGS_VPD_VALID;
> + return;
> + }
> +
> + if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
> + printk(KERN_ERR PFX "Illegal phy string [%s].\n",
> + np->vpd.phy_type);
> + printk(KERN_ERR PFX "Falling back to SPROM.\n");
> + np->flags &= ~NIU_FLAGS_VPD_VALID;
> + return;
> + }
> +
> + memcpy(dev->perm_addr, vpd->local_mac, ETH_ALEN);
> +
> + val8 = dev->perm_addr[5];
> + dev->perm_addr[5] += np->port;
> + if (dev->perm_addr[5] < val8)
> + dev->perm_addr[4]++;
> +
> + memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +}
> +
> +static int __devinit niu_pci_probe_sprom(struct niu *np)
> +{
> + struct net_device *dev = np->dev;
> + int len, i;
> + u64 val, sum;
> + u8 val8;
> +
> + val = (nr64(ESPC_VER_IMGSZ) & ESPC_VER_IMGSZ_IMGSZ);
> + val >>= ESPC_VER_IMGSZ_IMGSZ_SHIFT;
> + len = val / 4;
> +
> + np->eeprom_len = len;
> +
> + niudbg(PROBE, "SPROM: Image size %llu\n", (unsigned long long) val);
> +
> + sum = 0;
> + for (i = 0; i < len; i++) {
> + val = nr64(ESPC_NCR(i));
> + sum += (val >> 0) & 0xff;
> + sum += (val >> 8) & 0xff;
> + sum += (val >> 16) & 0xff;
> + sum += (val >> 24) & 0xff;
> + }
> + niudbg(PROBE, "SPROM: Checksum %x\n", (int)(sum & 0xff));
> + if ((sum & 0xff) != 0xab) {
> + printk(KERN_ERR PFX "Bad SPROM checksum "
> + "(%x, should be 0xab)\n", (int) (sum & 0xff));
> + return -EINVAL;
> + }
> +
> + val = nr64(ESPC_PHY_TYPE);
> + switch (np->port) {
> + case 0:
> + val = (val & ESPC_PHY_TYPE_PORT0) >>
> + ESPC_PHY_TYPE_PORT0_SHIFT;
> + break;
> + case 1:
> + val = (val & ESPC_PHY_TYPE_PORT1) >>
> + ESPC_PHY_TYPE_PORT1_SHIFT;
> + break;
> + case 2:
> + val = (val & ESPC_PHY_TYPE_PORT2) >>
> + ESPC_PHY_TYPE_PORT2_SHIFT;
> + break;
> + case 3:
> + val = (val & ESPC_PHY_TYPE_PORT3) >>
> + ESPC_PHY_TYPE_PORT3_SHIFT;
> + break;
> + default:
> + printk(KERN_ERR PFX "Bogus port number %u\n",
> + np->port);
> + return -EINVAL;
> + }
> + niudbg(PROBE, "SPROM: PHY type %llx\n", (unsigned long long) val);
> +
> + switch (val) {
> + case ESPC_PHY_TYPE_1G_COPPER:
> + /* 1G copper, MII */
> + np->flags &= ~(NIU_FLAGS_FIBER |
> + NIU_FLAGS_10G);
> + np->mac_xcvr = MAC_XCVR_MII;
> + break;
> +
> + case ESPC_PHY_TYPE_1G_FIBER:
> + /* 1G fiber, PCS */
> + np->flags &= ~NIU_FLAGS_10G;
> + np->flags |= NIU_FLAGS_FIBER;
> + np->mac_xcvr = MAC_XCVR_PCS;
> + break;
> +
> + case ESPC_PHY_TYPE_10G_COPPER:
> + /* 10G copper, XPCS */
> + np->flags |= NIU_FLAGS_10G;
> + np->flags &= ~NIU_FLAGS_FIBER;
> + np->mac_xcvr = MAC_XCVR_XPCS;
> + break;
> +
> + case ESPC_PHY_TYPE_10G_FIBER:
> + /* 10G fiber, XPCS */
> + np->flags |= (NIU_FLAGS_10G |
> + NIU_FLAGS_FIBER);
> + np->mac_xcvr = MAC_XCVR_XPCS;
> + break;
> +
> + default:
> + printk(KERN_ERR PFX "Bogus SPROM phy type %llu\n",
> + (unsigned long long) val);
> + return -EINVAL;
> + }
> +
> + val = nr64(ESPC_MAC_ADDR0);
> + niudbg(PROBE, "SPROM: MAC_ADDR0[%08llx]\n",
> + (unsigned long long) val);
> + dev->perm_addr[0] = (val >> 0) & 0xff;
> + dev->perm_addr[1] = (val >> 8) & 0xff;
> + dev->perm_addr[2] = (val >> 16) & 0xff;
> + dev->perm_addr[3] = (val >> 24) & 0xff;
> +
> + val = nr64(ESPC_MAC_ADDR1);
> + niudbg(PROBE, "SPROM: MAC_ADDR1[%08llx]\n",
> + (unsigned long long) val);
> + dev->perm_addr[4] = (val >> 0) & 0xff;
> + dev->perm_addr[5] = (val >> 8) & 0xff;
> +
> + if (!is_valid_ether_addr(&dev->perm_addr[0])) {
> + printk(KERN_ERR PFX "SPROM MAC address invalid\n");
> + printk(KERN_ERR PFX "[ \n");
> + for (i = 0; i < 6; i++)
> + printk("%02x ", dev->perm_addr[i]);
> + printk("]\n");
> + return -EINVAL;
> + }
> +
> + val8 = dev->perm_addr[5];
> + dev->perm_addr[5] += np->port;
> + if (dev->perm_addr[5] < val8)
> + dev->perm_addr[4]++;
> +
> + memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +
> + val = nr64(ESPC_MOD_STR_LEN);
> + niudbg(PROBE, "SPROM: MOD_STR_LEN[%llu]\n",
> + (unsigned long long) val);
> + if (val > 8 * 4)
> + return -EINVAL;
> +
> + for (i = 0; i < val; i += 4) {
> + u64 tmp = nr64(ESPC_NCR(5 + (i / 4)));
> +
> + np->vpd.model[i + 3] = (tmp >> 0) & 0xff;
> + np->vpd.model[i + 2] = (tmp >> 8) & 0xff;
> + np->vpd.model[i + 1] = (tmp >> 16) & 0xff;
> + np->vpd.model[i + 0] = (tmp >> 24) & 0xff;
> + }
> + np->vpd.model[val] = '\0';
> +
> + val = nr64(ESPC_BD_MOD_STR_LEN);
> + niudbg(PROBE, "SPROM: BD_MOD_STR_LEN[%llu]\n",
> + (unsigned long long) val);
> + if (val > 4 * 4)
> + return -EINVAL;
> +
> + for (i = 0; i < val; i += 4) {
> + u64 tmp = nr64(ESPC_NCR(14 + (i / 4)));
> +
> + np->vpd.board_model[i + 3] = (tmp >> 0) & 0xff;
> + np->vpd.board_model[i + 2] = (tmp >> 8) & 0xff;
> + np->vpd.board_model[i + 1] = (tmp >> 16) & 0xff;
> + np->vpd.board_model[i + 0] = (tmp >> 24) & 0xff;
> + }
> + np->vpd.board_model[val] = '\0';
> +
> + np->vpd.mac_num =
> + nr64(ESPC_NUM_PORTS_MACS) & ESPC_NUM_PORTS_MACS_VAL;
> + niudbg(PROBE, "SPROM: NUM_PORTS_MACS[%d]\n",
> + np->vpd.mac_num);
> +
> + return 0;
> +}
> +
> +static int __devinit niu_get_and_validate_port(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> +
> + if (np->port <= 1)
> + np->flags |= NIU_FLAGS_XMAC;
> +
> + if (!parent->num_ports) {
> + if (parent->plat_type == PLAT_TYPE_NIU) {
> + parent->num_ports = 2;
> + } else {
> + parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) &
> + ESPC_NUM_PORTS_MACS_VAL;
> +
> + if (!parent->num_ports)
> + parent->num_ports = 4;
> + }
> + }
> +
> + niudbg(PROBE, "niu_get_and_validate_port: port[%d] num_ports[%d]\n",
> + np->port, parent->num_ports);
> + if (np->port >= parent->num_ports)
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +static int __devinit phy_record(struct niu_parent *parent,
> + struct phy_probe_info *p,
> + int dev_id_1, int dev_id_2, u8 phy_port,
> + int type)
> +{
> + u32 id = (dev_id_1 << 16) | dev_id_2;
> + u8 idx;
> +
> + if (dev_id_1 < 0 || dev_id_2 < 0)
> + return 0;
> + if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) {
> + if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704)
> + return 0;
> + } else {
> + if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R)
> + return 0;
> + }
> +
> + printk(KERN_INFO PFX "niu%d: Found PHY %08x type %s at phy_port %u\n",
> + parent->index, id,
> + (type == PHY_TYPE_PMA_PMD ?
> + "PMA/PMD" :
> + (type == PHY_TYPE_PCS ?
> + "PCS" : "MII")),
> + phy_port);
> +
> + if (p->cur[type] >= NIU_MAX_PORTS) {
> + printk(KERN_ERR PFX "Too many PHY ports.\n");
> + return -EINVAL;
> + }
> + idx = p->cur[type];
> + p->phy_id[type][idx] = id;
> + p->phy_port[type][idx] = phy_port;
> + p->cur[type] = idx + 1;
> + return 0;
> +}
> +
> +static int __devinit port_has_10g(struct phy_probe_info *p, int port)
> +{
> + int i;
> +
> + for (i = 0; i < p->cur[PHY_TYPE_PMA_PMD]; i++) {
> + if (p->phy_port[PHY_TYPE_PMA_PMD][i] == port)
> + return 1;
> + }
> + for (i = 0; i < p->cur[PHY_TYPE_PCS]; i++) {
> + if (p->phy_port[PHY_TYPE_PCS][i] == port)
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static int __devinit count_10g_ports(struct phy_probe_info *p, int *lowest)
> +{
> + int port, cnt;
> +
> + cnt = 0;
> + *lowest = 32;
> + for (port = 8; port < 32; port++) {
> + if (port_has_10g(p, port)) {
> + if (!cnt)
> + *lowest = port;
> + cnt++;
> + }
> + }
> +
> + return cnt;
> +}
> +
> +static int __devinit count_1g_ports(struct phy_probe_info *p, int *lowest)
> +{
> + *lowest = 32;
> + if (p->cur[PHY_TYPE_MII])
> + *lowest = p->phy_port[PHY_TYPE_MII][0];
> +
> + return p->cur[PHY_TYPE_MII];
> +}
> +
> +static void __devinit niu_n2_divide_channels(struct niu_parent *parent)
> +{
> + int num_ports = parent->num_ports;
> + int i;
> +
> + for (i = 0; i < num_ports; i++) {
> + parent->rxchan_per_port[i] = (16 / num_ports);
> + parent->txchan_per_port[i] = (16 / num_ports);
> +
> + printk(KERN_INFO PFX "niu%d: Port %u [%u RX chans] "
> + "[%u TX chans]\n",
> + parent->index, i,
> + parent->rxchan_per_port[i],
> + parent->txchan_per_port[i]);
> + }
> +}
> +
> +static void __devinit niu_divide_channels(struct niu_parent *parent,
> + int num_10g, int num_1g)
> +{
> + int num_ports = parent->num_ports;
> + int rx_chans_per_10g, rx_chans_per_1g;
> + int tx_chans_per_10g, tx_chans_per_1g;
> + int i, tot_rx, tot_tx;
> +
> + if (!num_10g || !num_1g) {
> + rx_chans_per_10g = rx_chans_per_1g =
> + (NIU_NUM_RXCHAN / num_ports);
> + tx_chans_per_10g = tx_chans_per_1g =
> + (NIU_NUM_TXCHAN / num_ports);
> + } else {
> + rx_chans_per_1g = NIU_NUM_RXCHAN / 8;
> + rx_chans_per_10g = (NIU_NUM_RXCHAN -
> + (rx_chans_per_1g * num_1g)) /
> + num_10g;
> +
> + tx_chans_per_1g = NIU_NUM_TXCHAN / 6;
> + tx_chans_per_10g = (NIU_NUM_TXCHAN -
> + (tx_chans_per_1g * num_1g)) /
> + num_10g;
> + }
> +
> + tot_rx = tot_tx = 0;
> + for (i = 0; i < num_ports; i++) {
> + int type = phy_decode(parent->port_phy, i);
> +
> + if (type == PORT_TYPE_10G) {
> + parent->rxchan_per_port[i] = rx_chans_per_10g;
> + parent->txchan_per_port[i] = tx_chans_per_10g;
> + } else {
> + parent->rxchan_per_port[i] = rx_chans_per_1g;
> + parent->txchan_per_port[i] = tx_chans_per_1g;
> + }
> + printk(KERN_INFO PFX "niu%d: Port %u [%u RX chans] "
> + "[%u TX chans]\n",
> + parent->index, i,
> + parent->rxchan_per_port[i],
> + parent->txchan_per_port[i]);
> + tot_rx += parent->rxchan_per_port[i];
> + tot_tx += parent->txchan_per_port[i];
> + }
> +
> + if (tot_rx > NIU_NUM_RXCHAN) {
> + printk(KERN_ERR PFX "niu%d: Too many RX channels (%d), "
> + "resetting to one per port.\n",
> + parent->index, tot_rx);
> + for (i = 0; i < num_ports; i++)
> + parent->rxchan_per_port[i] = 1;
> + }
> + if (tot_tx > NIU_NUM_TXCHAN) {
> + printk(KERN_ERR PFX "niu%d: Too many TX channels (%d), "
> + "resetting to one per port.\n",
> + parent->index, tot_tx);
> + for (i = 0; i < num_ports; i++)
> + parent->txchan_per_port[i] = 1;
> + }
> + if (tot_rx < NIU_NUM_RXCHAN || tot_tx < NIU_NUM_TXCHAN) {
> + printk(KERN_WARNING PFX "niu%d: Driver bug, wasted channels, "
> + "RX[%d] TX[%d]\n",
> + parent->index, tot_rx, tot_tx);
> + }
> +}
> +
> +static void __devinit niu_divide_rdc_groups(struct niu_parent *parent,
> + int num_10g, int num_1g)
> +{
> + int i, num_ports = parent->num_ports;
> + int rdc_group, rdc_groups_per_port;
> + int rdc_channel_base;
> +
> + rdc_group = 0;
> + rdc_groups_per_port = NIU_NUM_RDC_TABLES / num_ports;
> +
> + rdc_channel_base = 0;
> +
> + for (i = 0; i < num_ports; i++) {
> + struct niu_rdc_tables *tp = &parent->rdc_group_cfg[i];
> + int grp, num_channels = parent->rxchan_per_port[i];
> + int this_channel_offset;
> +
> + tp->first_table_num = rdc_group;
> + tp->num_tables = rdc_groups_per_port;
> + this_channel_offset = 0;
> + for (grp = 0; grp < tp->num_tables; grp++) {
> + struct rdc_table *rt = &tp->tables[grp];
> + int slot;
> +
> + printk(KERN_INFO PFX "niu%d: Port %d RDC tbl(%d) [ ",
> + parent->index, i, tp->first_table_num + grp);
> + for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++) {
> + rt->rxdma_channel[slot] =
> + rdc_channel_base + this_channel_offset;
> +
> + printk("%d ", rt->rxdma_channel[slot]);
> +
> + if (++this_channel_offset == num_channels)
> + this_channel_offset = 0;
> + }
> + printk("]\n");
> + }
> +
> + parent->rdc_default[i] = rdc_channel_base;
> +
> + rdc_channel_base += num_channels;
> + rdc_group += rdc_groups_per_port;
> + }
> +}
> +
> +static int __devinit fill_phy_probe_info(struct niu *np,
> + struct niu_parent *parent,
> + struct phy_probe_info *info)
> +{
> + unsigned long flags;
> + int port, err;
> +
> + memset(info, 0, sizeof(*info));
> +
> + /* Port 0 to 7 are reserved for onboard Serdes, probe the rest. */
> + niu_lock_parent(np, flags);
> + err = 0;
> + for (port = 8; port < 32; port++) {
> + int dev_id_1, dev_id_2;
> +
> + dev_id_1 = mdio_read(np, port,
> + NIU_PMA_PMD_DEV_ADDR, MII_PHYSID1);
> + dev_id_2 = mdio_read(np, port,
> + NIU_PMA_PMD_DEV_ADDR, MII_PHYSID2);
> + err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> + PHY_TYPE_PMA_PMD);
> + if (err)
> + break;
> + dev_id_1 = mdio_read(np, port,
> + NIU_PCS_DEV_ADDR, MII_PHYSID1);
> + dev_id_2 = mdio_read(np, port,
> + NIU_PCS_DEV_ADDR, MII_PHYSID2);
> + err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> + PHY_TYPE_PCS);
> + if (err)
> + break;
> + dev_id_1 = mii_read(np, port, MII_PHYSID1);
> + dev_id_2 = mii_read(np, port, MII_PHYSID2);
> + err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> + PHY_TYPE_MII);
> + if (err)
> + break;
> + }
> + niu_unlock_parent(np, flags);
> +
> + return err;
> +}
> +
> +static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
> +{
> + struct phy_probe_info *info = &parent->phy_probe_info;
> + int lowest_10g, lowest_1g;
> + int num_10g, num_1g;
> + u32 val;
> + int err;
> +
> + err = fill_phy_probe_info(np, parent, info);
> + if (err)
> + return err;
> +
> + num_10g = count_10g_ports(info, &lowest_10g);
> + num_1g = count_1g_ports(info, &lowest_1g);
> +
> + switch ((num_10g << 4) | num_1g) {
> + case 0x24:
> + if (lowest_1g == 10)
> + parent->plat_type = PLAT_TYPE_VF_P0;
> + else if (lowest_1g == 26)
> + parent->plat_type = PLAT_TYPE_VF_P1;
> + else
> + goto unknown_vg_1g_port;
> +
> + /* fallthru */
> + case 0x22:
> + val = (phy_encode(PORT_TYPE_10G, 0) |
> + phy_encode(PORT_TYPE_10G, 1) |
> + phy_encode(PORT_TYPE_1G, 2) |
> + phy_encode(PORT_TYPE_1G, 3));
> + break;
> +
> + case 0x20:
> + val = (phy_encode(PORT_TYPE_10G, 0) |
> + phy_encode(PORT_TYPE_10G, 1));
> + break;
> +
> + case 0x10:
> + val = phy_encode(PORT_TYPE_10G, np->port);
> + break;
> +
> + case 0x14:
> + if (lowest_1g == 10)
> + parent->plat_type = PLAT_TYPE_VF_P0;
> + else if (lowest_1g == 26)
> + parent->plat_type = PLAT_TYPE_VF_P1;
> + else
> + goto unknown_vg_1g_port;
> +
> + /* fallthru */
> + case 0x13:
> + if ((lowest_10g & 0x7) == 0)
> + val = (phy_encode(PORT_TYPE_10G, 0) |
> + phy_encode(PORT_TYPE_1G, 1) |
> + phy_encode(PORT_TYPE_1G, 2) |
> + phy_encode(PORT_TYPE_1G, 3));
> + else
> + val = (phy_encode(PORT_TYPE_1G, 0) |
> + phy_encode(PORT_TYPE_10G, 1) |
> + phy_encode(PORT_TYPE_1G, 2) |
> + phy_encode(PORT_TYPE_1G, 3));
> + break;
> +
> + case 0x04:
> + if (lowest_1g == 10)
> + parent->plat_type = PLAT_TYPE_VF_P0;
> + else if (lowest_1g == 26)
> + parent->plat_type = PLAT_TYPE_VF_P1;
> + else
> + goto unknown_vg_1g_port;
> +
> + val = (phy_encode(PORT_TYPE_1G, 0) |
> + phy_encode(PORT_TYPE_1G, 1) |
> + phy_encode(PORT_TYPE_1G, 2) |
> + phy_encode(PORT_TYPE_1G, 3));
> + break;
> +
> + default:
> + printk(KERN_ERR PFX "Unsupported port config "
> + "10G[%d] 1G[%d]\n",
> + num_10g, num_1g);
> + return -EINVAL;
> + }
> +
> + parent->port_phy = val;
> +
> + if (parent->plat_type == PLAT_TYPE_NIU)
> + niu_n2_divide_channels(parent);
> + else
> + niu_divide_channels(parent, num_10g, num_1g);
> +
> + niu_divide_rdc_groups(parent, num_10g, num_1g);
> +
> + return 0;
> +
> +unknown_vg_1g_port:
> + printk(KERN_ERR PFX "Cannot identify platform type, 1gport=%d\n",
> + lowest_1g);
> + return -EINVAL;
> +}
> +
> +static int __devinit niu_probe_ports(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + int err, i;
> +
> + niudbg(PROBE, "niu_probe_ports(): port_phy[%08x]\n",
> + parent->port_phy);
> +
> + if (parent->port_phy == PORT_PHY_UNKNOWN) {
> + err = walk_phys(np, parent);
> + if (err)
> + return err;
> +
> + niu_set_ldg_timer_res(np, 2);
> + for (i = 0; i <= LDN_MAX; i++)
> + niu_ldn_irq_enable(np, i, 0);
> + }
> +
> + if (parent->port_phy == PORT_PHY_INVALID)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int __devinit niu_classifier_swstate_init(struct niu *np)
> +{
> + struct niu_classifier *cp = &np->clas;
> +
> + niudbg(PROBE, "niu_classifier_swstate_init: num_tcam(%d)\n",
> + np->parent->tcam_num_entries);
> +
> + cp->tcam_index = (u16) np->port;
> + cp->h1_init = 0xffffffff;
> + cp->h2_init = 0xffff;
> +
> + return fflp_early_init(np);
> +}
> +
> +static void __devinit niu_link_config_init(struct niu *np)
> +{
> + struct niu_link_config *lp = &np->link_config;
> +
> + lp->advertising = (ADVERTISED_10baseT_Half |
> + ADVERTISED_10baseT_Full |
> + ADVERTISED_100baseT_Half |
> + ADVERTISED_100baseT_Full |
> + ADVERTISED_1000baseT_Half |
> + ADVERTISED_1000baseT_Full |
> + ADVERTISED_10000baseT_Full |
> + ADVERTISED_Autoneg);
> + lp->speed = lp->active_speed = SPEED_INVALID;
> + lp->duplex = lp->active_duplex = DUPLEX_INVALID;
> +#if 0
> + lp->loopback_mode = LOOPBACK_MAC;
> + lp->active_speed = SPEED_10000;
> + lp->active_duplex = DUPLEX_FULL;
> +#else
> + lp->loopback_mode = LOOPBACK_DISABLED;
> +#endif
> +}
> +
> +static int __devinit niu_init_mac_ipp_pcs_base(struct niu *np)
> +{
> + switch (np->port) {
> + case 0:
> + np->mac_regs = np->regs + XMAC_PORT0_OFF;
> + np->ipp_off = 0x00000;
> + np->pcs_off = 0x04000;
> + np->xpcs_off = 0x02000;
> + break;
> +
> + case 1:
> + np->mac_regs = np->regs + XMAC_PORT1_OFF;
> + np->ipp_off = 0x08000;
> + np->pcs_off = 0x0a000;
> + np->xpcs_off = 0x08000;
> + break;
> +
> + case 2:
> + np->mac_regs = np->regs + BMAC_PORT2_OFF;
> + np->ipp_off = 0x04000;
> + np->pcs_off = 0x0e000;
> + np->xpcs_off = ~0UL;
> + break;
> +
> + case 3:
> + np->mac_regs = np->regs + BMAC_PORT3_OFF;
> + np->ipp_off = 0x0c000;
> + np->pcs_off = 0x12000;
> + np->xpcs_off = ~0UL;
> + break;
> +
> + default:
> + printk(KERN_ERR PFX "Port %u is invalid, cannot "
> + "compute MAC block offset.\n", np->port);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void __devinit niu_try_msix(struct niu *np, u8 *ldg_num_map)
> +{
> + struct msix_entry msi_vec[NIU_NUM_LDG];
> + struct niu_parent *parent = np->parent;
> + struct pci_dev *pdev = np->pdev;
> + int i, num_irqs, err;
> + u8 first_ldg;
> +
> + first_ldg = (NIU_NUM_LDG / parent->num_ports) * np->port;
> + for (i = 0; i < (NIU_NUM_LDG / parent->num_ports); i++)
> + ldg_num_map[i] = first_ldg + i;
> +
> + num_irqs = (parent->rxchan_per_port[np->port] +
> + parent->txchan_per_port[np->port] +
> + (np->port == 0 ? 3 : 1));
> + BUG_ON(num_irqs > (NIU_NUM_LDG / parent->num_ports));
> +
> +retry:
> + for (i = 0; i < num_irqs; i++) {
> + msi_vec[i].vector = 0;
> + msi_vec[i].entry = i;
> + }
> +
> + err = pci_enable_msix(pdev, msi_vec, num_irqs);
> + if (err < 0) {
> + np->flags &= ~NIU_FLAGS_MSIX;
> + return;
> + }
> + if (err > 0) {
> + num_irqs = err;
> + goto retry;
> + }
> +
> + np->flags |= NIU_FLAGS_MSIX;
> + for (i = 0; i < num_irqs; i++)
> + np->ldg[i].irq = msi_vec[i].vector;
> + np->num_ldg = num_irqs;
> +}
> +
> +static int __devinit niu_n2_irq_init(struct niu *np, u8 *ldg_num_map)
> +{
> +#ifdef CONFIG_SPARC64
> + struct of_device *op = np->op;
> + const u32 *int_prop;
> + int i;
> +
> + int_prop = of_get_property(op->node, "interrupts", NULL);
> + if (!int_prop)
> + return -ENODEV;
> +
> + for (i = 0; i < op->num_irqs; i++) {
> + ldg_num_map[i] = int_prop[i];
> + np->ldg[i].irq = op->irqs[i];
> + }
> +
> + np->num_ldg = op->num_irqs;
> +
> + return 0;
> +#else
> + return -EINVAL;
> +#endif
> +}
> +
> +static int __devinit niu_ldg_init(struct niu *np)
> +{
> + struct niu_parent *parent = np->parent;
> + u8 ldg_num_map[NIU_NUM_LDG];
> + int first_chan, num_chan;
> + int i, err, ldg_rotor;
> + u8 port;
> +
> + np->num_ldg = 1;
> + np->ldg[0].irq = np->dev->irq;
> + if (parent->plat_type == PLAT_TYPE_NIU) {
> + err = niu_n2_irq_init(np, ldg_num_map);
> + if (err)
> + return err;
> + } else
> + niu_try_msix(np, ldg_num_map);
> +
> + port = np->port;
> + for (i = 0; i < np->num_ldg; i++) {
> + struct niu_ldg *lp = &np->ldg[i];
> +
> + netif_napi_add(np->dev, &lp->napi, niu_poll, 64);
> +
> + lp->np = np;
> + lp->ldg_num = ldg_num_map[i];
> + lp->timer = 2; /* XXX */
> +
> + /* On N2 NIU the firmware has setup the SID mappings so they go
> + * to the correct values that will route the LDG to the proper
> + * interrupt in the NCU interrupt table.
> + */
> + if (np->parent->plat_type != PLAT_TYPE_NIU) {
> + err = niu_set_ldg_sid(np, lp->ldg_num, port, i);
> + if (err)
> + return err;
> + }
> + }
> +
> + /* We adopt the LDG assignment ordering used by the N2 NIU
> + * 'interrupt' properties because that simplifies a lot of
> + * things. This ordering is:
> + *
> + * MAC
> + * MIF (if port zero)
> + * SYSERR (if port zero)
> + * RX channels
> + * TX channels
> + */
> +
> + ldg_rotor = 0;
> +
> + err = niu_ldg_assign_ldn(np, parent, ldg_num_map[ldg_rotor],
> + LDN_MAC(port));
> + if (err)
> + return err;
> +
> + ldg_rotor++;
> + if (ldg_rotor == np->num_ldg)
> + ldg_rotor = 0;
> +
> + if (port == 0) {
> + err = niu_ldg_assign_ldn(np, parent,
> + ldg_num_map[ldg_rotor],
> + LDN_MIF);
> + if (err)
> + return err;
> +
> + ldg_rotor++;
> + if (ldg_rotor == np->num_ldg)
> + ldg_rotor = 0;
> +
> + err = niu_ldg_assign_ldn(np, parent,
> + ldg_num_map[ldg_rotor],
> + LDN_DEVICE_ERROR);
> + if (err)
> + return err;
> +
> + ldg_rotor++;
> + if (ldg_rotor == np->num_ldg)
> + ldg_rotor = 0;
> +
> + }
> +
> + first_chan = 0;
> + for (i = 0; i < port; i++)
> + first_chan += parent->rxchan_per_port[port];
> + num_chan = parent->rxchan_per_port[port];
> +
> + for (i = first_chan; i < (first_chan + num_chan); i++) {
> + err = niu_ldg_assign_ldn(np, parent,
> + ldg_num_map[ldg_rotor],
> + LDN_RXDMA(i));
> + if (err)
> + return err;
> + ldg_rotor++;
> + if (ldg_rotor == np->num_ldg)
> + ldg_rotor = 0;
> + }
> +
> + first_chan = 0;
> + for (i = 0; i < port; i++)
> + first_chan += parent->txchan_per_port[port];
> + num_chan = parent->txchan_per_port[port];
> + for (i = first_chan; i < (first_chan + num_chan); i++) {
> + err = niu_ldg_assign_ldn(np, parent,
> + ldg_num_map[ldg_rotor],
> + LDN_TXDMA(i));
> + if (err)
> + return err;
> + ldg_rotor++;
> + if (ldg_rotor == np->num_ldg)
> + ldg_rotor = 0;
> + }
> +
> + return 0;
> +}
> +
> +static void __devexit niu_ldg_free(struct niu *np)
> +{
> + if (np->flags & NIU_FLAGS_MSIX)
> + pci_disable_msix(np->pdev);
> +}
> +
> +static int __devinit niu_get_of_props(struct niu *np)
> +{
> +#ifdef CONFIG_SPARC64
> + struct net_device *dev = np->dev;
> + struct device_node *dp;
> + const char *phy_type;
> + const u8 *mac_addr;
> + int prop_len;
> +
> + if (np->parent->plat_type == PLAT_TYPE_NIU)
> + dp = np->op->node;
> + else
> + dp = pci_device_to_OF_node(np->pdev);
> +
> + phy_type = of_get_property(dp, "phy-type", &prop_len);
> + if (!phy_type) {
> + printk(KERN_ERR PFX "%s: OF node lacks phy-type property\n",
> + dp->full_name);
> + return -EINVAL;
> + }
> +
> + if (!strcmp(phy_type, "none"))
> + return -ENODEV;
> +
> + strcpy(np->vpd.phy_type, phy_type);
> +
> + if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
> + printk(KERN_ERR PFX "%s: Illegal phy string [%s].\n",
> + dp->full_name, np->vpd.phy_type);
> + return -EINVAL;
> + }
> +
> + mac_addr = of_get_property(dp, "local-mac-address", &prop_len);
> + if (!mac_addr) {
> + printk(KERN_ERR PFX "%s: OF node lacks local-mac-address property\n",
> + dp->full_name);
> + return -EINVAL;
> + }
> + if (prop_len != dev->addr_len) {
> + printk(KERN_ERR PFX "%s: OF MAC address prop len (%d) is wrong.\n",
> + dp->full_name, prop_len);
> + }
> + memcpy(dev->perm_addr, mac_addr, dev->addr_len);
> + if (!is_valid_ether_addr(&dev->perm_addr[0])) {
> + int i;
> +
> + printk(KERN_ERR PFX "%s: OF MAC address is invalid\n",
> + dp->full_name);
> + printk(KERN_ERR PFX "%s: [ \n",
> + dp->full_name);
> + for (i = 0; i < 6; i++)
> + printk("%02x ", dev->perm_addr[i]);
> + printk("]\n");
> + return -EINVAL;
> + }
> +
> + memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +
> + return 0;
> +#else
> + return -EINVAL;
> +#endif
> +}
> +
> +static int __devinit niu_get_invariants(struct niu *np)
> +{
> + int err, have_props;
> + u32 offset;
> +
> + err = niu_get_of_props(np);
> + if (err == -ENODEV)
> + return err;
> +
> + have_props = !err;
> +
> + err = niu_get_and_validate_port(np);
> + if (err)
> + return err;
> +
> + err = niu_init_mac_ipp_pcs_base(np);
> + if (err)
> + return err;
> +
> + if (!have_props) {
> + if (np->parent->plat_type == PLAT_TYPE_NIU)
> + return -EINVAL;
> +
> + nw64(ESPC_PIO_EN, ESPC_PIO_EN_ENABLE);
> + offset = niu_pci_vpd_offset(np);
> + niudbg(PROBE, "niu_get_invariants: VPD offset [%08x]\n",
> + offset);
> + if (offset)
> + niu_pci_vpd_fetch(np, offset);
> + nw64(ESPC_PIO_EN, 0);
> +
> + if (np->flags & NIU_FLAGS_VPD_VALID)
> + niu_pci_vpd_validate(np);
> +
> + if (!(np->flags & NIU_FLAGS_VPD_VALID)) {
> + err = niu_pci_probe_sprom(np);
> + if (err)
> + return err;
> + }
> + }
> +
> + err = niu_probe_ports(np);
> + if (err)
> + return err;
> +
> + niu_ldg_init(np);
> +
> + niu_classifier_swstate_init(np);
> + niu_link_config_init(np);
> +
> + err = niu_determine_phy_disposition(np);
> + if (!err)
> + err = niu_init_link(np);
> +
> + return err;
> +}
> +
> +static LIST_HEAD(niu_parent_list);
> +static DEFINE_MUTEX(niu_parent_lock);
> +static int niu_parent_index;
> +
> +static ssize_t show_port_phy(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct platform_device *plat_dev = to_platform_device(dev);
> + struct niu_parent *p = plat_dev->dev.platform_data;
> + u32 port_phy = p->port_phy;
> + char *orig_buf = buf;
> + int i;
> +
> + if (port_phy == PORT_PHY_UNKNOWN ||
> + port_phy == PORT_PHY_INVALID)
> + return 0;
> +
> + for (i = 0; i < p->num_ports; i++) {
> + const char *type_str;
> + int type;
> +
> + type = phy_decode(port_phy, i);
> + if (type == PORT_TYPE_10G)
> + type_str = "10G";
> + else
> + type_str = "1G";
> + buf += sprintf(buf,
> + (i == 0) ? "%s" : " %s",
> + type_str);
> + }
> + buf += sprintf(buf, "\n");
> + return buf - orig_buf;
> +}
> +
> +static ssize_t show_plat_type(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct platform_device *plat_dev = to_platform_device(dev);
> + struct niu_parent *p = plat_dev->dev.platform_data;
> + const char *type_str;
> +
> + switch (p->plat_type) {
> + case PLAT_TYPE_ATLAS:
> + type_str = "atlas";
> + break;
> + case PLAT_TYPE_NIU:
> + type_str = "niu";
> + break;
> + case PLAT_TYPE_VF_P0:
> + type_str = "vf_p0";
> + break;
> + case PLAT_TYPE_VF_P1:
> + type_str = "vf_p1";
> + break;
> + default:
> + type_str = "unknown";
> + break;
> + }
> +
> + return sprintf(buf, "%s\n", type_str);
> +}
> +
> +static ssize_t __show_chan_per_port(struct device *dev,
> + struct device_attribute *attr, char *buf,
> + int rx)
> +{
> + struct platform_device *plat_dev = to_platform_device(dev);
> + struct niu_parent *p = plat_dev->dev.platform_data;
> + char *orig_buf = buf;
> + u8 *arr;
> + int i;
> +
> + arr = (rx ? p->rxchan_per_port : p->txchan_per_port);
> +
> + for (i = 0; i < p->num_ports; i++) {
> + buf += sprintf(buf,
> + (i == 0) ? "%d" : " %d",
> + arr[i]);
> + }
> + buf += sprintf(buf, "\n");
> +
> + return buf - orig_buf;
> +}
> +
> +static ssize_t show_rxchan_per_port(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return __show_chan_per_port(dev, attr, buf, 1);
> +}
> +
> +static ssize_t show_txchan_per_port(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return __show_chan_per_port(dev, attr, buf, 1);
> +}
> +
> +static ssize_t show_num_ports(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct platform_device *plat_dev = to_platform_device(dev);
> + struct niu_parent *p = plat_dev->dev.platform_data;
> +
> + return sprintf(buf, "%d\n", p->num_ports);
> +}
> +
> +static struct device_attribute niu_parent_attributes[] = {
> + __ATTR(port_phy, S_IRUGO, show_port_phy, NULL),
> + __ATTR(plat_type, S_IRUGO, show_plat_type, NULL),
> + __ATTR(rxchan_per_port, S_IRUGO, show_rxchan_per_port, NULL),
> + __ATTR(txchan_per_port, S_IRUGO, show_txchan_per_port, NULL),
> + __ATTR(num_ports, S_IRUGO, show_num_ports, NULL),
> + {}
> +};
> +
> +static struct niu_parent * __devinit niu_new_parent(struct niu *np,
> + union niu_parent_id *id,
> + u8 ptype)
> +{
> + struct platform_device *plat_dev;
> + struct niu_parent *p;
> + int i;
> +
> + niudbg(PROBE, "niu_new_parent: Creating new parent.\n");
> +
> + plat_dev = platform_device_register_simple("niu", niu_parent_index,
> + NULL, 0);
> + if (!plat_dev)
> + return NULL;
> +
> + for (i = 0; attr_name(niu_parent_attributes[i]); i++) {
> + int err = device_create_file(&plat_dev->dev,
> + &niu_parent_attributes[i]);
> + if (err)
> + goto fail_unregister;
> + }
> +
> + p = kzalloc(sizeof(*p), GFP_KERNEL);
> + if (!p)
> + goto fail_unregister;
> +
> + p->index = niu_parent_index++;
> +
> + plat_dev->dev.platform_data = p;
> + p->plat_dev = plat_dev;
> +
> + memcpy(&p->id, id, sizeof(*id));
> + p->plat_type = ptype;
> + INIT_LIST_HEAD(&p->list);
> + atomic_set(&p->refcnt, 0);
> + list_add(&p->list, &niu_parent_list);
> + spin_lock_init(&p->lock);
> +
> + p->rxdma_clock_divider = 7500;
> +
> + p->tcam_num_entries = NIU_PCI_TCAM_ENTRIES;
> + if (p->plat_type == PLAT_TYPE_NIU)
> + p->tcam_num_entries = NIU_NONPCI_TCAM_ENTRIES;
> +
> + for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) {
> + int index = i - CLASS_CODE_USER_PROG1;
> +
> + p->tcam_key[index] = TCAM_KEY_TSEL;
> + p->flow_key[index] = (FLOW_KEY_IPSA |
> + FLOW_KEY_IPDA |
> + FLOW_KEY_PROTO |
> + (FLOW_KEY_L4_BYTE12 <<
> + FLOW_KEY_L4_0_SHIFT) |
> + (FLOW_KEY_L4_BYTE12 <<
> + FLOW_KEY_L4_1_SHIFT));
> + }
> +
> + for (i = 0; i < LDN_MAX + 1; i++)
> + p->ldg_map[i] = LDG_INVALID;
> +
> + return p;
> +
> +fail_unregister:
> + platform_device_unregister(plat_dev);
> + return NULL;
> +}
> +
> +static struct niu_parent * __devinit niu_get_parent(struct niu *np,
> + union niu_parent_id *id,
> + u8 ptype)
> +{
> + struct niu_parent *p, *tmp;
> + int port = np->port;
> +
> + niudbg(PROBE, "niu_get_parent: platform_type[%u] port[%u]\n",
> + ptype, port);
> +
> + mutex_lock(&niu_parent_lock);
> + p = NULL;
> + list_for_each_entry(tmp, &niu_parent_list, list) {
> + if (!memcmp(id, &tmp->id, sizeof(*id))) {
> + p = tmp;
> + break;
> + }
> + }
> + if (!p)
> + p = niu_new_parent(np, id, ptype);
> +
> + if (p) {
> + char port_name[6];
> + int err;
> +
> + sprintf(port_name, "port%d", port);
> + err = sysfs_create_link(&p->plat_dev->dev.kobj,
> + &np->device->kobj,
> + port_name);
> + if (!err) {
> + p->ports[port] = np;
> + atomic_inc(&p->refcnt);
> + }
> + }
> + mutex_unlock(&niu_parent_lock);
> +
> + return p;
> +}
> +
> +static void niu_put_parent(struct niu *np)
> +{
> + struct niu_parent *p = np->parent;
> + u8 port = np->port;
> + char port_name[6];
> +
> + BUG_ON(!p || p->ports[port] != np);
> +
> + niudbg(PROBE, "niu_put_parent: port[%u]\n", port);
> +
> + sprintf(port_name, "port%d", port);
> +
> + mutex_lock(&niu_parent_lock);
> +
> + sysfs_remove_link(&p->plat_dev->dev.kobj, port_name);
> +
> + p->ports[port] = NULL;
> + np->parent = NULL;
> +
> + if (atomic_dec_and_test(&p->refcnt)) {
> + list_del(&p->list);
> + platform_device_unregister(p->plat_dev);
> + }
> +
> + mutex_unlock(&niu_parent_lock);
> +}
> +
> +static void *niu_pci_alloc_coherent(struct device *dev, size_t size,
> + u64 *handle, gfp_t flag)
> +{
> + dma_addr_t dh;
> + void *ret;
> +
> + ret = dma_alloc_coherent(dev, size, &dh, flag);
> + if (ret)
> + *handle = dh;
> + return ret;
> +}
> +
> +static void niu_pci_free_coherent(struct device *dev, size_t size,
> + void *cpu_addr, u64 handle)
> +{
> + dma_free_coherent(dev, size, cpu_addr, handle);
> +}
> +
> +static u64 niu_pci_map_page(struct device *dev, struct page *page,
> + unsigned long offset, size_t size,
> + enum dma_data_direction direction)
> +{
> + return dma_map_page(dev, page, offset, size, direction);
> +}
> +
> +static void niu_pci_unmap_page(struct device *dev, u64 dma_address,
> + size_t size, enum dma_data_direction direction)
> +{
> + return dma_unmap_page(dev, dma_address, size, direction);
> +}
> +
> +static u64 niu_pci_map_single(struct device *dev, void *cpu_addr,
> + size_t size,
> + enum dma_data_direction direction)
> +{
> + return dma_map_single(dev, cpu_addr, size, direction);
> +}
> +
> +static void niu_pci_unmap_single(struct device *dev, u64 dma_address,
> + size_t size,
> + enum dma_data_direction direction)
> +{
> + dma_unmap_single(dev, dma_address, size, direction);
> +}
> +
> +static const struct niu_ops niu_pci_ops = {
> + .alloc_coherent = niu_pci_alloc_coherent,
> + .free_coherent = niu_pci_free_coherent,
> + .map_page = niu_pci_map_page,
> + .unmap_page = niu_pci_unmap_page,
> + .map_single = niu_pci_map_single,
> + .unmap_single = niu_pci_unmap_single,
> +};
> +
> +static void __devinit niu_driver_version(void)
> +{
> + static int niu_version_printed = 0;
> +
> + if (niu_version_printed++ == 0)
> + printk(KERN_INFO "%s", version);
> +}
> +
> +static struct net_device * __devinit niu_alloc_and_init(struct device *gen_dev,
> + struct pci_dev *pdev,
> + struct of_device *op,
> + const struct niu_ops *ops,
> + u8 port)
> +{
> + struct net_device *dev = alloc_etherdev(sizeof(struct niu));
> + struct niu *np;
> +
> + if (!dev) {
> + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
> + return NULL;
> + }
> +
> + SET_NETDEV_DEV(dev, gen_dev);
> +
> + np = netdev_priv(dev);
> + np->dev = dev;
> + np->pdev = pdev;
> + np->op = op;
> + np->device = gen_dev;
> + np->ops = ops;
> +
> + spin_lock_init(&np->lock);
> + INIT_WORK(&np->reset_task, niu_reset_task);
> +
> + np->port = port;
> +
> + return dev;
> +}
> +
> +static void __devinit niu_assign_netdev_ops(struct net_device *dev)
> +{
> + dev->open = niu_open;
> + dev->stop = niu_close;
> + dev->get_stats = niu_get_stats;
> + dev->set_multicast_list = niu_set_rx_mode;
> + dev->set_mac_address = niu_set_mac_addr;
> + dev->do_ioctl = niu_ioctl;
> + dev->tx_timeout = niu_tx_timeout;
> + dev->hard_start_xmit = niu_start_xmit;
> + dev->ethtool_ops = &niu_ethtool_ops;
> + dev->watchdog_timeo = NIU_TX_TIMEOUT;
> + dev->change_mtu = niu_change_mtu;
> +}
> +
> +static void __devinit niu_device_announce(struct niu *np)
> +{
> + struct net_device *dev = np->dev;
> + int i;
> +
> + printk(KERN_INFO "%s: NIU Ethernet ", dev->name);
> + for (i = 0; i < 6; i++)
> + printk("%2.2x%c", dev->dev_addr[i],
> + i == 5 ? '\n' : ':');
> +
> + printk(KERN_INFO "%s: Port type[%s] mode[%s:%s] XCVR[%s] phy[%s]\n",
> + dev->name,
> + (np->flags & NIU_FLAGS_XMAC ? "XMAC" : "BMAC"),
> + (np->flags & NIU_FLAGS_10G ? "10G" : "1G"),
> + (np->flags & NIU_FLAGS_FIBER ? "FIBER" : "COPPER"),
> + (np->mac_xcvr == MAC_XCVR_MII ? "MII" :
> + (np->mac_xcvr == MAC_XCVR_PCS ? "PCS" : "XPCS")),
> + np->vpd.phy_type);
> +}
> +
> +static int __devinit niu_pci_init_one(struct pci_dev *pdev,
> + const struct pci_device_id *ent)
> +{
> + unsigned long niureg_base, niureg_len;
> + union niu_parent_id parent_id;
> + struct net_device *dev;
> + struct niu *np;
> + int err, pos;
> + u64 dma_mask;
> + u16 val16;
> +
> + niu_driver_version();
> +
> + err = pci_enable_device(pdev);
> + if (err) {
> + printk(KERN_ERR PFX "Cannot enable PCI device, "
> + "aborting.\n");
> + return err;
> + }
> +
> + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
> + !(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> + printk(KERN_ERR PFX "Cannot find proper PCI device "
> + "base addresses, aborting.\n");
> + err = -ENODEV;
> + goto err_out_disable_pdev;
> + }
> +
> + err = pci_request_regions(pdev, DRV_MODULE_NAME);
> + if (err) {
> + printk(KERN_ERR PFX "Cannot obtain PCI resources, "
> + "aborting.\n");
> + goto err_out_disable_pdev;
> + }
> +
> + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
> + if (pos <= 0) {
> + printk(KERN_ERR PFX "Cannot find PCI Express capability, "
> + "aborting.\n");
> + goto err_out_free_res;
> + }
> +
> + dev = niu_alloc_and_init(&pdev->dev, pdev, NULL,
> + &niu_pci_ops, PCI_FUNC(pdev->devfn));
> + if (!dev) {
> + err = -ENOMEM;
> + goto err_out_free_res;
> + }
> + np = netdev_priv(dev);
> +
> + memset(&parent_id, 0, sizeof(parent_id));
> + parent_id.pci.domain = pci_domain_nr(pdev->bus);
> + parent_id.pci.bus = pdev->bus->number;
> + parent_id.pci.device = PCI_SLOT(pdev->devfn);
> +
> + np->parent = niu_get_parent(np, &parent_id,
> + PLAT_TYPE_ATLAS);
> + if (!np->parent) {
> + err = -ENOMEM;
> + goto err_out_free_dev;
> + }
> +
> + pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16);
> + val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
> + val16 |= (PCI_EXP_DEVCTL_CERE |
> + PCI_EXP_DEVCTL_NFERE |
> + PCI_EXP_DEVCTL_FERE |
> + PCI_EXP_DEVCTL_URRE |
> + PCI_EXP_DEVCTL_RELAX_EN);
> + pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
> +
> + dma_mask = DMA_44BIT_MASK;
> + err = pci_set_dma_mask(pdev, dma_mask);
> + if (!err) {
> + dev->features |= NETIF_F_HIGHDMA;
> + err = pci_set_consistent_dma_mask(pdev, dma_mask);
> + if (err) {
> + printk(KERN_ERR PFX "Unable to obtain 44 bit "
> + "DMA for consistent allocations, "
> + "aborting.\n");
> + goto err_out_release_parent;
> + }
> + }
> + if (err || dma_mask == DMA_32BIT_MASK) {
> + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
> + if (err) {
> + printk(KERN_ERR PFX "No usable DMA configuration, "
> + "aborting.\n");
> + goto err_out_release_parent;
> + }
> + }
> +
> + dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM);
> +
> + niureg_base = pci_resource_start(pdev, 0);
> + niureg_len = pci_resource_len(pdev, 0);
> +
> + np->regs = ioremap_nocache(niureg_base, niureg_len);
> + if (!np->regs) {
> + printk(KERN_ERR PFX "Cannot map device registers, "
> + "aborting.\n");
> + err = -ENOMEM;
> + goto err_out_release_parent;
> + }
> +
> + pci_set_master(pdev);
> + pci_save_state(pdev);
> +
> + dev->irq = pdev->irq;
> +
> + niu_assign_netdev_ops(dev);
> +
> + err = niu_get_invariants(np);
> + if (err) {
> + if (err != -ENODEV)
> + printk(KERN_ERR PFX "Problem fetching invariants "
> + "of chip, aborting.\n");
> + goto err_out_iounmap;
> + }
> +
> + err = register_netdev(dev);
> + if (err) {
> + printk(KERN_ERR PFX "Cannot register net device, "
> + "aborting.\n");
> + goto err_out_iounmap;
> + }
> +
> + pci_set_drvdata(pdev, dev);
> +
> + niu_device_announce(np);
> +
> + return 0;
> +
> +err_out_iounmap:
> + if (np->regs) {
> + iounmap(np->regs);
> + np->regs = NULL;
> + }
> +
> +err_out_release_parent:
> + niu_put_parent(np);
> +
> +err_out_free_dev:
> + free_netdev(dev);
> +
> +err_out_free_res:
> + pci_release_regions(pdev);
> +
> +err_out_disable_pdev:
> + pci_disable_device(pdev);
> + pci_set_drvdata(pdev, NULL);
> +
> + return err;
> +}
> +
> +static void __devexit niu_pci_remove_one(struct pci_dev *pdev)
> +{
> + struct net_device *dev = pci_get_drvdata(pdev);
> +
> + if (dev) {
> + struct niu *np = netdev_priv(dev);
> +
> + unregister_netdev(dev);
> + if (np->regs) {
> + iounmap(np->regs);
> + np->regs = NULL;
> + }
> +
> + niu_ldg_free(np);
> +
> + niu_put_parent(np);
> +
> + free_netdev(dev);
> + pci_release_regions(pdev);
> + pci_disable_device(pdev);
> + pci_set_drvdata(pdev, NULL);
> + }
> +}
> +
> +static int niu_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> + struct net_device *dev = pci_get_drvdata(pdev);
> + struct niu *np = netdev_priv(dev);
> + unsigned long flags;
> +
> + if (!netif_running(dev))
> + return 0;
> +
> + flush_scheduled_work();
> + niu_netif_stop(np);
> +
> + del_timer_sync(&np->timer);
> +
> + spin_lock_irqsave(&np->lock, flags);
> + niu_enable_interrupts(np, 0);
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + netif_device_detach(dev);
No need to drop lock for detach? all it does is call stop_queue()
> +
> + spin_lock_irqsave(&np->lock, flags);
> + niu_stop_hw(np);
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + pci_save_state(pdev);
> +
> + return 0;
> +}
> +
> +static int niu_resume(struct pci_dev *pdev)
> +{
> + struct net_device *dev = pci_get_drvdata(pdev);
> + struct niu *np = netdev_priv(dev);
> + unsigned long flags;
> + int err;
> +
> + if (!netif_running(dev))
> + return 0;
> +
> + pci_restore_state(pdev);
> +
> + netif_device_attach(dev);
> +
> + spin_lock_irqsave(&np->lock, flags);
> +
> + err = niu_init_hw(np);
> + if (!err) {
> + np->timer.expires = jiffies + HZ;
> + add_timer(&np->timer);
> + niu_netif_start(np);
> + }
> +
> + spin_unlock_irqrestore(&np->lock, flags);
> +
> + return err;
> +}
> +
> +static struct pci_driver niu_pci_driver = {
> + .name = DRV_MODULE_NAME,
> + .id_table = niu_pci_tbl,
> + .probe = niu_pci_init_one,
> + .remove = __devexit_p(niu_pci_remove_one),
> + .suspend = niu_suspend,
> + .resume = niu_resume,
> +};
> +
> +#ifdef CONFIG_SPARC64
> +static void *niu_phys_alloc_coherent(struct device *dev, size_t size,
> + u64 *dma_addr, gfp_t flag)
> +{
> + unsigned long order = get_order(size);
> + unsigned long page = __get_free_pages(flag, order);
> +
> + if (page == 0UL)
> + return NULL;
> + memset((char *)page, 0, PAGE_SIZE << order);
> + *dma_addr = __pa(page);
> +
> + return (void *) page;
> +}
Generic cod useful for other drivers??
> +
> +static void niu_phys_free_coherent(struct device *dev, size_t size,
> + void *cpu_addr, u64 handle)
> +{
> + unsigned long order = get_order(size);
> +
> + free_pages((unsigned long) cpu_addr, order);
> +}
> +
> +static u64 niu_phys_map_page(struct device *dev, struct page *page,
> + unsigned long offset, size_t size,
> + enum dma_data_direction direction)
> +{
> + return page_to_phys(page) + offset;
> +}
> +
> +static void niu_phys_unmap_page(struct device *dev, u64 dma_address,
> + size_t size, enum dma_data_direction direction)
> +{
> + /* Nothing to do. */
> +}
> +
> +static u64 niu_phys_map_single(struct device *dev, void *cpu_addr,
> + size_t size,
> + enum dma_data_direction direction)
> +{
> + return __pa(cpu_addr);
> +}
> +
> +static void niu_phys_unmap_single(struct device *dev, u64 dma_address,
> + size_t size,
> + enum dma_data_direction direction)
> +{
> + /* Nothing to do. */
> +}
> +
> +static const struct niu_ops niu_phys_ops = {
> + .alloc_coherent = niu_phys_alloc_coherent,
> + .free_coherent = niu_phys_free_coherent,
> + .map_page = niu_phys_map_page,
> + .unmap_page = niu_phys_unmap_page,
> + .map_single = niu_phys_map_single,
> + .unmap_single = niu_phys_unmap_single,
> +};
> +
> +static unsigned long res_size(struct resource *r)
> +{
> + return r->end - r->start + 1UL;
> +}
> +
> +static int __devinit niu_of_probe(struct of_device *op, const struct of_device_id *match)
> +{
> + union niu_parent_id parent_id;
> + struct net_device *dev;
> + struct niu *np;
> + const u32 *reg;
> + int err;
> +
> + niu_driver_version();
> +
> + reg = of_get_property(op->node, "reg", NULL);
> + if (!reg) {
> + printk(KERN_ERR PFX "%s: No 'reg' property, aborting.\n",
> + op->node->full_name);
> + return -ENODEV;
> + }
> +
> + dev = niu_alloc_and_init(&op->dev, NULL, op,
> + &niu_phys_ops, reg[0] & 0x1);
> + if (!dev) {
> + err = -ENOMEM;
> + goto err_out;
> + }
> + np = netdev_priv(dev);
> +
> + memset(&parent_id, 0, sizeof(parent_id));
> + parent_id.of = of_get_parent(op->node);
> +
> + np->parent = niu_get_parent(np, &parent_id,
> + PLAT_TYPE_NIU);
> + if (!np->parent) {
> + err = -ENOMEM;
> + goto err_out_free_dev;
> + }
> +
> + dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM);
> +
> + np->regs = of_ioremap(&op->resource[1], 0,
> + res_size(&op->resource[1]),
> + "niu regs");
> + if (!np->regs) {
> + printk(KERN_ERR PFX "Cannot map device registers, "
> + "aborting.\n");
dev_err() ??
> + err = -ENOMEM;
> + goto err_out_release_parent;
> + }
> +
> + np->vir_regs_1 = of_ioremap(&op->resource[2], 0,
> + res_size(&op->resource[2]),
> + "niu vregs-1");
> + if (!np->vir_regs_1) {
> + printk(KERN_ERR PFX "Cannot map device vir registers 1, "
> + "aborting.\n");
dev_err() ??
> + err = -ENOMEM;
> + goto err_out_iounmap;
> + }
> +
> + np->vir_regs_2 = of_ioremap(&op->resource[3], 0,
> + res_size(&op->resource[3]),
> + "niu vregs-2");
> + if (!np->vir_regs_2) {
> + printk(KERN_ERR PFX "Cannot map device vir registers 2, "
> + "aborting.\n");
dev_err() ??
> + err = -ENOMEM;
> + goto err_out_iounmap;
> + }
> +
> + niu_assign_netdev_ops(dev);
> +
> + err = niu_get_invariants(np);
> + if (err) {
> + if (err != -ENODEV)
> + printk(KERN_ERR PFX "Problem fetching invariants "
> + "of chip, aborting.\n");
dev_err() ??
> + goto err_out_iounmap;
> + }
> +
> + err = register_netdev(dev);
> + if (err) {
> + printk(KERN_ERR PFX "Cannot register net device, "
> + "aborting.\n");
dev_err() ??
> + goto err_out_iounmap;
> + }
> +
> + dev_set_drvdata(&op->dev, dev);
> +
> + niu_device_announce(np);
> +
> + return 0;
> +
> +err_out_iounmap:
> + if (np->vir_regs_1) {
> + of_iounmap(&op->resource[2], np->vir_regs_1,
> + res_size(&op->resource[2]));
> + np->vir_regs_1 = NULL;
> + }
> +
> + if (np->vir_regs_2) {
> + of_iounmap(&op->resource[3], np->vir_regs_2,
> + res_size(&op->resource[3]));
> + np->vir_regs_2 = NULL;
> + }
> +
> + if (np->regs) {
> + of_iounmap(&op->resource[1], np->regs,
> + res_size(&op->resource[1]));
> + np->regs = NULL;
> + }
> +
> +err_out_release_parent:
> + niu_put_parent(np);
> +
> +err_out_free_dev:
> + free_netdev(dev);
> +
> +err_out:
> + return err;
> +}
> +
> +static int __devexit niu_of_remove(struct of_device *op)
> +{
> + struct net_device *dev = dev_get_drvdata(&op->dev);
> +
> + if (dev) {
> + struct niu *np = netdev_priv(dev);
> +
> + unregister_netdev(dev);
> +
> + if (np->vir_regs_1) {
> + of_iounmap(&op->resource[2], np->vir_regs_1,
> + res_size(&op->resource[2]));
> + np->vir_regs_1 = NULL;
> + }
> +
> + if (np->vir_regs_2) {
> + of_iounmap(&op->resource[3], np->vir_regs_2,
> + res_size(&op->resource[3]));
> + np->vir_regs_2 = NULL;
> + }
> +
> + if (np->regs) {
> + of_iounmap(&op->resource[1], np->regs,
> + res_size(&op->resource[1]));
> + np->regs = NULL;
> + }
> +
> + niu_ldg_free(np);
> +
> + niu_put_parent(np);
> +
> + free_netdev(dev);
> + dev_set_drvdata(&op->dev, NULL);
> + }
> + return 0;
> +}
> +
> +static struct of_device_id niu_match[] = {
> + {
> + .name = "network",
> + .compatible = "SUNW,niusl",
> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, niu_match);
> +
> +static struct of_platform_driver niu_of_driver = {
> + .name = "niu",
> + .match_table = niu_match,
> + .probe = niu_of_probe,
> + .remove = __devexit_p(niu_of_remove),
> +};
> +
> +#endif /* CONFIG_SPARC64 */
> +
> +static int __init niu_init(void)
> +{
> + int err = 0;
> +
> + BUILD_BUG_ON((PAGE_SIZE < 4 * 1024) ||
> + ((PAGE_SIZE > 32 * 1024) &&
> + ((PAGE_SIZE % (32 * 1024)) != 0 &&
> + (PAGE_SIZE % (16 * 1024)) != 0 &&
> + (PAGE_SIZE % (8 * 1024)) != 0 &&
> + (PAGE_SIZE % (4 * 1024)) != 0)));
> +
> +#ifdef CONFIG_SPARC64
> + err = of_register_driver(&niu_of_driver, &of_bus_type);
> +#endif
> +
> + if (!err) {
> + err = pci_register_driver(&niu_pci_driver);
> +#ifdef CONFIG_SPARC64
> + if (err)
> + of_unregister_driver(&niu_of_driver);
> +#endif
> + }
> +
> + return err;
> +}
> +
> +static void __exit niu_exit(void)
> +{
> + pci_unregister_driver(&niu_pci_driver);
> +#ifdef CONFIG_SPARC64
> + of_unregister_driver(&niu_of_driver);
> +#endif
> +}
> +
> +module_init(niu_init);
> +module_exit(niu_exit);
> diff --git a/drivers/net/niu.h b/drivers/net/niu.h
--
Stephen Hemminger <shemminger@linux-foundation.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