All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] NCSI Support
@ 2016-06-30 10:27 Gavin Shan
  2016-06-30 10:27 ` [PATCH 01/10] net/ncsi: Resource management Gavin Shan
                   ` (9 more replies)
  0 siblings, 10 replies; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

This series rebases on David's linux-net git repo ("master" branch). It's
to support NCSI stack on net/farady/ftgmac100.c

The following figure gives an example about how NCSI is deployed: The NCSI is
specified by DSP0222, which can be downloaded from the following link here
(http://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.0.0.pdf).

   * The NC-SI (aka NCSI) is defined as the interface between a (Base) Management
     Controller (BMC) and one or multiple Network Controlers (NC) on host side.
     The interface is responsible for providing external network connectivity
     for BMC.
   * Each BMC can connect to multiple packages, up to 8. Each package can have
     multiple channels, up to 32. Every package and channel are identified by
     3-bits and 5-bits in NCSI packet. At one moment, one channel is active to
     provide service.
   * NCSI packet, encapsulated in ethernet frame, has 0x88F8 in the protocol
     field. The destination MAC address should be 0xFF's while the source MAC
     address can be arbitrary one.
   * NCSI packets are classified to command, response, AEN (Asynchronous Event
     Notification). Commands are sent from BMC to host for configuration and
     information retrival. Responses, corresponding to commands, are sent from
     host to BMC for confirmation and requested information. One command should
     have one and only one response. AEN is sent from host to BMC for notification
     (e.g. link down on active channel) so that BMC can take appropriate action.

   +------------------+        +----------------------------------------------+
   |                  |        |                     Host                     |
   |        BMC       |        |                                              |
   |                  |        | +-------------------+  +-------------------+ |
   |    +---------+   |        | |     Package-A     |  |     Package-B     | |
   |    |         |   |        | +---------+---------+  +-------------------+ |
   |    |   NIC   |   |        | | Channel | Channel |  | Channel | Channel | |
   +----+----+----+---+        +-+---------+---------+--+---------+---------+-+
             |                             |                      |
             |                             |                      |
             +-----------------------------+----------------------+

The design for the patchset is highlighted as below:

   * The NCSI interface is abstracted with "struct ncsi_dev". It's registered
     when net_device is created, started to work by calling ncsi_start_dev()
     when net_device is opened (ndo_open()). For the first time, NCSI packets
     are sent and received to/from the far end (host in above figure) to probe
     available NCSI packages and channels. After that, one channel is chosen as
     active one to provide service.
   * The NCSI stack is driven by workqueue and state machine internally.
   * AEN (Asychronous Event Notification) might be received from the far end
     (host). The currently active NCSI channel fails over to another available
     one if possible. Otherwise, the NCSI channel is out of service.
   * NCSI stack should be configurable through netlink or another mechanism,
     but it's not implemented in this patchset. It's something TBD.
   * The first NIC driver that is aware of NCSI: drivers/net/ethernet/faraday/ftgmac100.c

Gavin Shan (10):
  net/ncsi: Resource management
  net/ncsi: NCSI command packet handler
  net/ncsi: NCSI response packet handler
  net/ncsi: Package and channel management
  net/ncsi: NCSI AEN packet handler
  net/farady: Helper functions to create or destroy MDIO interface
  net/farady: Read MAC address from chip
  net/farady: Support NCSI mode
  net/farady: Match driver according to compatible property
  net/farady: Mask PHY interrupt with NCSI mode

 drivers/net/ethernet/faraday/ftgmac100.c |  266 +++++--
 include/uapi/linux/if_ether.h            |    1 +
 net/Kconfig                              |    1 +
 net/Makefile                             |    1 +
 net/ncsi/Kconfig                         |   10 +
 net/ncsi/Makefile                        |    4 +
 net/ncsi/internal.h                      |  308 ++++++++
 net/ncsi/ncsi-aen.c                      |  190 +++++
 net/ncsi/ncsi-cmd.c                      |  372 ++++++++++
 net/ncsi/ncsi-manage.c                   |  910 +++++++++++++++++++++++
 net/ncsi/ncsi-pkt.h                      |  395 ++++++++++
 net/ncsi/ncsi-rsp.c                      | 1169 ++++++++++++++++++++++++++++++
 12 files changed, 3563 insertions(+), 64 deletions(-)
 create mode 100644 net/ncsi/Kconfig
 create mode 100644 net/ncsi/Makefile
 create mode 100644 net/ncsi/internal.h
 create mode 100644 net/ncsi/ncsi-aen.c
 create mode 100644 net/ncsi/ncsi-cmd.c
 create mode 100644 net/ncsi/ncsi-manage.c
 create mode 100644 net/ncsi/ncsi-pkt.h
 create mode 100644 net/ncsi/ncsi-rsp.c

-- 
2.1.0

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 01/10] net/ncsi: Resource management
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:04   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 02/10] net/ncsi: NCSI command packet handler Gavin Shan
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.

   * The user (e.g. netdev driver) dereference NCSI device by
     "struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
     The later one is used by NCSI stack internally.
   * Every NCSI device can have multiple packages simultaneously, up
     to 8 packages. It's represented by "struct ncsi_package" and
     identified by 3-bits ID.
   * Every NCSI package can have multiple channels, up to 32. It's
     represented by "struct ncsi_channel" and identified by 5-bits ID.
   * Every NCSI channel has version, statistics, various modes and
     filters. They are represented by "struct ncsi_channel_version",
     "struct ncsi_channel_stats", "struct ncsi_channel_mode" and
     "struct ncsi_channel_filter" separately.
   * Apart from AEN (Asynchronous Event Notification), the NCSI stack
     works in terms of command and response. This introduces struct
     ncsi_req" to represent a complete NCSI transaction made of NCSI
     request and response.

This also introduces CONFIG_NET_NCSI to enable NCSI stack.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 net/Kconfig            |   1 +
 net/Makefile           |   1 +
 net/ncsi/Kconfig       |  10 ++
 net/ncsi/Makefile      |   4 +
 net/ncsi/internal.h    | 253 ++++++++++++++++++++++++++++
 net/ncsi/ncsi-manage.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 704 insertions(+)
 create mode 100644 net/ncsi/Kconfig
 create mode 100644 net/ncsi/Makefile
 create mode 100644 net/ncsi/internal.h
 create mode 100644 net/ncsi/ncsi-manage.c

diff --git a/net/Kconfig b/net/Kconfig
index ff40562..c2cdbce 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -237,6 +237,7 @@ source "net/hsr/Kconfig"
 source "net/switchdev/Kconfig"
 source "net/l3mdev/Kconfig"
 source "net/qrtr/Kconfig"
+source "net/ncsi/Kconfig"
 
 config RPS
 	bool
diff --git a/net/Makefile b/net/Makefile
index bdd1455..9bd20bb 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -79,3 +79,4 @@ ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
 obj-y				+= l3mdev/
 endif
 obj-$(CONFIG_QRTR)		+= qrtr/
+obj-$(CONFIG_NET_NCSI)		+= ncsi/
diff --git a/net/ncsi/Kconfig b/net/ncsi/Kconfig
new file mode 100644
index 0000000..723f0eb
--- /dev/null
+++ b/net/ncsi/Kconfig
@@ -0,0 +1,10 @@
+#
+# Configuration for NCSI support
+#
+
+config NET_NCSI
+	bool "NCSI interface support (EXPERIMENTAL)"
+	depends on INET
+	---help---
+	  This module provides NCSI (Network Controller Sideband Interface)
+	  support.
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
new file mode 100644
index 0000000..07b5625
--- /dev/null
+++ b/net/ncsi/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for NCSI API
+#
+obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
new file mode 100644
index 0000000..4a8bd76
--- /dev/null
+++ b/net/ncsi/internal.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __NCSI_INTERNAL_H__
+#define __NCSI_INTERNAL_H__
+
+enum {
+	NCSI_CAP_BASE		= 0,
+	NCSI_CAP_GENERIC	= 0,
+	NCSI_CAP_BC,
+	NCSI_CAP_MC,
+	NCSI_CAP_BUFFER,
+	NCSI_CAP_AEN,
+	NCSI_CAP_VLAN,
+	NCSI_CAP_MAX
+};
+
+enum {
+	NCSI_CAP_GENERIC_HWA	= 0x01,	/* HW arbitration             */
+	NCSI_CAP_GENERIC_HDS	= 0x02,	/* HNC driver status change   */
+	NCSI_CAP_GENERIC_FC	= 0x04,	/* HNC to MC flow control     */
+	NCSI_CAP_GENERIC_FC1	= 0x08,	/* MC to HNC flow control     */
+	NCSI_CAP_GENERIC_MC	= 0x10,	/* Global multicast filtering */
+	NCSI_CAP_GENERIC_MASK	= 0x1f,
+	NCSI_CAP_BC_ARP		= 0x01,	/* ARP packet filtering       */
+	NCSI_CAP_BC_DHCPC	= 0x02,	/* DHCP client filtering      */
+	NCSI_CAP_BC_DHCPS	= 0x04,	/* DHCP server filtering      */
+	NCSI_CAP_BC_NETBIOS	= 0x08,	/* NetBIOS packet filtering   */
+	NCSI_CAP_BC_MASK	= 0x0f,
+	NCSI_CAP_MC_NEIGHBOR	= 0x01,	/* IPv6 neighbor filtering    */
+	NCSI_CAP_MC_ROUTER	= 0x02,	/* IPv6 router filering       */
+	NCSI_CAP_MC_DHCPV6	= 0x04,	/* DHCPv6 filtering           */
+	NCSI_CAP_MC_MASK	= 0x07,
+	NCSI_CAP_AEN_LSC	= 0x01,	/* Link status change AEN     */
+	NCSI_CAP_AEN_CR		= 0x02,	/* Configuration required AEN */
+	NCSI_CAP_AEN_HDS	= 0x04,	/* HNC driver status AEN      */
+	NCSI_CAP_AEN_MASK	= 0x07,
+	NCSI_CAP_VLAN_ONLY	= 0x01,	/* VLAN is supported          */
+	NCSI_CAP_VLAN_NO	= 0x02,	/* Filter VLAN and non-VLAN   */
+	NCSI_CAP_VLAN_ANY	= 0x04,	/* Filter Any-and-non-VLAN    */
+	NCSI_CAP_VLAN_MASK	= 0x07
+};
+
+enum {
+	NCSI_MODE_BASE		= 0,
+	NCSI_MODE_ENABLE	= 0,
+	NCSI_MODE_TX_ENABLE,
+	NCSI_MODE_LINK,
+	NCSI_MODE_VLAN,
+	NCSI_MODE_BC,
+	NCSI_MODE_MC,
+	NCSI_MODE_AEN,
+	NCSI_MODE_FC,
+	NCSI_MODE_MAX
+};
+
+enum {
+	NCSI_FILTER_BASE	= 0,
+	NCSI_FILTER_VLAN	= 0,
+	NCSI_FILTER_UC,
+	NCSI_FILTER_MC,
+	NCSI_FILTER_MIXED,
+	NCSI_FILTER_MAX
+};
+
+struct ncsi_channel_version {
+	unsigned int   ncv_version;     /* Supported BCD encoded NCSI version */
+	unsigned int   ncv_alpha2;      /* Supported BCD encoded NCSI version */
+	unsigned char  ncv_fw_name[12];	/* Firware name string                */
+	unsigned int   ncv_fw_version;  /* Firmware version                   */
+	unsigned short ncv_pci_ids[4];  /* PCI identification                 */
+	unsigned int   ncv_mf_id;       /* Manufacture ID                     */
+};
+
+struct ncsi_channel_cap {
+	unsigned int ncc_index;	/* Index of channel capabilities */
+	unsigned int ncc_cap;	/* NCSI channel capability       */
+};
+
+struct ncsi_channel_mode {
+	unsigned int ncm_index;   /* Index of channel modes      */
+	unsigned int ncm_enable;  /* Enabled or disabled         */
+	unsigned int ncm_size;    /* Valid entries in ncm_data[] */
+	unsigned int ncm_data[8]; /* Data entries                */
+};
+
+struct ncsi_channel_filter {
+	unsigned int  ncf_index;  /* Index of channel filters          */
+	unsigned int  ncf_total;  /* Total entries in the filter table */
+	unsigned long ncf_bitmap; /* Bitmap of valid entries           */
+	unsigned char ncf_data[]; /* Data for the valid entries        */
+};
+
+struct ncsi_channel_stats {
+	unsigned int ncs_hnc_cnt_hi;         /* Counter cleared            */
+	unsigned int ncs_hnc_cnt_lo;         /* Counter cleared            */
+	unsigned int ncs_hnc_rx_bytes;       /* Rx bytes                   */
+	unsigned int ncs_hnc_tx_bytes;       /* Tx bytes                   */
+	unsigned int ncs_hnc_rx_uc_pkts;     /* Rx UC packets              */
+	unsigned int ncs_hnc_rx_mc_pkts;     /* Rx MC packets              */
+	unsigned int ncs_hnc_rx_bc_pkts;     /* Rx BC packets              */
+	unsigned int ncs_hnc_tx_uc_pkts;     /* Tx UC packets              */
+	unsigned int ncs_hnc_tx_mc_pkts;     /* Tx MC packets              */
+	unsigned int ncs_hnc_tx_bc_pkts;     /* Tx BC packets              */
+	unsigned int ncs_hnc_fcs_err;        /* FCS errors                 */
+	unsigned int ncs_hnc_align_err;      /* Alignment errors           */
+	unsigned int ncs_hnc_false_carrier;  /* False carrier detection    */
+	unsigned int ncs_hnc_runt_pkts;      /* Rx runt packets            */
+	unsigned int ncs_hnc_jabber_pkts;    /* Rx jabber packets          */
+	unsigned int ncs_hnc_rx_pause_xon;   /* Rx pause XON frames        */
+	unsigned int ncs_hnc_rx_pause_xoff;  /* Rx XOFF frames             */
+	unsigned int ncs_hnc_tx_pause_xon;   /* Tx XON frames              */
+	unsigned int ncs_hnc_tx_pause_xoff;  /* Tx XOFF frames             */
+	unsigned int ncs_hnc_tx_s_collision; /* Single collision frames    */
+	unsigned int ncs_hnc_tx_m_collision; /* Multiple collision frames  */
+	unsigned int ncs_hnc_l_collision;    /* Late collision frames      */
+	unsigned int ncs_hnc_e_collision;    /* Excessive collision frames */
+	unsigned int ncs_hnc_rx_ctl_frames;  /* Rx control frames          */
+	unsigned int ncs_hnc_rx_64_frames;   /* Rx 64-bytes frames         */
+	unsigned int ncs_hnc_rx_127_frames;  /* Rx 65-127 bytes frames     */
+	unsigned int ncs_hnc_rx_255_frames;  /* Rx 128-255 bytes frames    */
+	unsigned int ncs_hnc_rx_511_frames;  /* Rx 256-511 bytes frames    */
+	unsigned int ncs_hnc_rx_1023_frames; /* Rx 512-1023 bytes frames   */
+	unsigned int ncs_hnc_rx_1522_frames; /* Rx 1024-1522 bytes frames  */
+	unsigned int ncs_hnc_rx_9022_frames; /* Rx 1523-9022 bytes frames  */
+	unsigned int ncs_hnc_tx_64_frames;   /* Tx 64-bytes frames         */
+	unsigned int ncs_hnc_tx_127_frames;  /* Tx 65-127 bytes frames     */
+	unsigned int ncs_hnc_tx_255_frames;  /* Tx 128-255 bytes frames    */
+	unsigned int ncs_hnc_tx_511_frames;  /* Tx 256-511 bytes frames    */
+	unsigned int ncs_hnc_tx_1023_frames; /* Tx 512-1023 bytes frames   */
+	unsigned int ncs_hnc_tx_1522_frames; /* Tx 1024-1522 bytes frames  */
+	unsigned int ncs_hnc_tx_9022_frames; /* Tx 1523-9022 bytes frames  */
+	unsigned int ncs_hnc_rx_valid_bytes; /* Rx valid bytes             */
+	unsigned int ncs_hnc_rx_runt_pkts;   /* Rx error runt packets      */
+	unsigned int ncs_hnc_rx_jabber_pkts; /* Rx error jabber packets    */
+	unsigned int ncs_ncsi_rx_cmds;       /* Rx NCSI commands           */
+	unsigned int ncs_ncsi_dropped_cmds;  /* Dropped commands           */
+	unsigned int ncs_ncsi_cmd_type_errs; /* Command type errors        */
+	unsigned int ncs_ncsi_cmd_csum_errs; /* Command checksum errors    */
+	unsigned int ncs_ncsi_rx_pkts;       /* Rx NCSI packets            */
+	unsigned int ncs_ncsi_tx_pkts;       /* Tx NCSI packets            */
+	unsigned int ncs_ncsi_tx_aen_pkts;   /* Tx AEN packets             */
+	unsigned int ncs_pt_tx_pkts;         /* Tx packets                 */
+	unsigned int ncs_pt_tx_dropped;      /* Tx dropped packets         */
+	unsigned int ncs_pt_tx_channel_err;  /* Tx channel errors          */
+	unsigned int ncs_pt_tx_us_err;       /* Tx undersize errors        */
+	unsigned int ncs_pt_rx_pkts;         /* Rx packets                 */
+	unsigned int ncs_pt_rx_dropped;      /* Rx dropped packets         */
+	unsigned int ncs_pt_rx_channel_err;  /* Rx channel errors          */
+	unsigned int ncs_pt_rx_us_err;       /* Rx undersize errors        */
+	unsigned int ncs_pt_rx_os_err;       /* Rx oversize errors         */
+};
+
+struct ncsi_dev_priv;
+struct ncsi_package;
+
+#define NCSI_PACKAGE_INDEX(c)	(((c) >> 5) & 0x7)
+#define NCSI_CHANNEL_INDEX(c)	((c) & 0x1ffff)
+#define NCSI_TO_CHANNEL(p, c)	((((p) & 0x7) << 5) | ((c) & 0x1ffff))
+
+/* Channel state */
+enum {
+	ncsi_channel_state_deselected_initial,
+	ncsi_channel_state_selected_initial,
+	ncsi_channel_state_deselected_ready,
+	ncsi_channel_state_selected_ready,
+};
+
+struct ncsi_channel {
+	unsigned char               nc_id;
+	int                         nc_state;
+	struct ncsi_package         *nc_package;
+	struct ncsi_channel_version nc_version;
+	struct ncsi_channel_cap	    nc_caps[NCSI_CAP_MAX];
+	struct ncsi_channel_mode    nc_modes[NCSI_MODE_MAX];
+	struct ncsi_channel_filter  *nc_filters[NCSI_FILTER_MAX];
+	struct ncsi_channel_stats   nc_stats;
+	struct list_head            nc_node;
+};
+
+struct ncsi_package {
+	unsigned char        np_id;           /* NCSI package ID          */
+	struct ncsi_dev_priv *np_ndp;         /* NCSI device              */
+	atomic_t             np_channel_num;  /* Number of channels       */
+	spinlock_t           np_channel_lock; /* Protect list of channels */
+	struct list_head     np_channels;     /* List of chanels          */
+	struct list_head     np_node;
+};
+
+struct ncsi_req {
+	unsigned char        nr_id;            /* Request ID          */
+	bool                 nr_used;          /* Request used or not */
+	struct ncsi_dev_priv *nr_ndp;          /* NCSI device         */
+	struct sk_buff       *nr_cmd;          /* Command packet      */
+	struct sk_buff       *nr_rsp;          /* Response packet     */
+	struct timer_list    nr_timer;         /* Associated timer    */
+	bool                 nr_timer_enabled; /* Timer was started   */
+};
+
+struct ncsi_dev_priv {
+	struct ncsi_dev     ndp_ndev;            /* NCSI device              */
+	int                 ndp_flags;           /* NCSI device flags        */
+	struct ncsi_package *ndp_active_package; /* Active NCSI package      */
+	struct ncsi_channel *ndp_active_channel; /* Active NCSI channel      */
+	atomic_t            ndp_package_num;     /* Number of packages       */
+	spinlock_t          ndp_package_lock;    /* Protect list of packages */
+	struct list_head    ndp_packages;        /* List of packages         */
+	atomic_t            ndp_last_req_idx;    /* Last used request ID     */
+	spinlock_t          ndp_req_lock;        /* Protect request table    */
+	struct ncsi_req     ndp_reqs[256];       /* Request table            */
+	struct list_head    ndp_node;
+};
+
+extern struct list_head ncsi_dev_list;
+extern spinlock_t ncsi_dev_lock;
+
+#define TO_NCSI_DEV_PRIV(nd) \
+	container_of(nd, struct ncsi_dev_priv, ndp_ndev)
+#define NCSI_FOR_EACH_DEV(ndp) \
+	list_for_each_entry_rcu(ndp, &ncsi_dev_list, ndp_node)
+#define NCSI_FOR_EACH_PACKAGE(ndp, np) \
+	list_for_each_entry_rcu(np, &ndp->ndp_packages, np_node)
+#define NCSI_FOR_EACH_CHANNEL(np, nc) \
+	list_for_each_entry_rcu(nc, &np->np_channels, nc_node)
+
+/* Resources */
+int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index);
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np,
+				      unsigned char id);
+struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
+				       unsigned char id);
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+				      unsigned char id);
+struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
+				       unsigned char id);
+void ncsi_release_package(struct ncsi_package *np);
+void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
+				   unsigned char id,
+				   struct ncsi_package **np,
+				   struct ncsi_channel **nc);
+struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
+void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
+struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
+
+#endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
new file mode 100644
index 0000000..dab54bc
--- /dev/null
+++ b/net/ncsi/ncsi-manage.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+
+LIST_HEAD(ncsi_dev_list);
+DEFINE_SPINLOCK(ncsi_dev_lock);
+
+int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data)
+{
+	struct ncsi_channel_filter *ncf;
+	int idx, entry_size;
+	void *bitmap;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf)
+		return -ENODEV;
+
+	/* Check the valid entries one by one */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	idx = -1;
+	while ((idx = find_next_bit(bitmap, ncf->ncf_total, idx + 1))
+		< ncf->ncf_total) {
+		if (!memcmp(ncf->ncf_data + entry_size * idx, data, entry_size))
+			return idx;
+	}
+
+	return -ENOENT;
+}
+
+int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data)
+{
+	struct ncsi_channel_filter *ncf;
+	int idx, entry_size;
+	void *bitmap;
+
+	/* Needn't add it if it's already existing */
+	idx = ncsi_find_channel_filter(nc, table, data);
+	if (idx >= 0)
+		return idx;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf)
+		return -ENODEV;
+
+	/* Propagate the filter */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	do {
+		idx = find_next_zero_bit(bitmap, ncf->ncf_total, 0);
+		if (idx >= ncf->ncf_total)
+			return -ENOSPC;
+	} while (test_and_set_bit(idx, bitmap));
+
+	memcpy(ncf->ncf_data + entry_size * idx, data, entry_size);
+	return idx;
+}
+
+int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index)
+{
+	struct ncsi_channel_filter *ncf;
+	int entry_size;
+	void *bitmap;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf || index >= ncf->ncf_total)
+		return -ENODEV;
+
+	/* Check if the entry is valid */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	if (test_and_clear_bit(index, bitmap))
+		memset(ncf->ncf_data + entry_size * index, 0, entry_size);
+
+	return 0;
+}
+
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
+{
+	struct ncsi_channel *nc, *tmp;
+	int index;
+
+	nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
+	if (!nc)
+		return NULL;
+
+	nc->nc_package = np;
+	nc->nc_id = id;
+	for (index = 0; index < NCSI_CAP_MAX; index++)
+		nc->nc_caps[index].ncc_index = index;
+	for (index = 0; index < NCSI_MODE_MAX; index++)
+		nc->nc_modes[index].ncm_index = index;
+
+	spin_lock(&np->np_channel_lock);
+	tmp = ncsi_find_channel(np, id);
+	if (tmp) {
+		spin_unlock(&np->np_channel_lock);
+		kfree(nc);
+		return tmp;
+	}
+	list_add_tail_rcu(&nc->nc_node, &np->np_channels);
+	spin_unlock(&np->np_channel_lock);
+
+	atomic_inc(&np->np_channel_num);
+	return nc;
+}
+
+struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
+				       unsigned char id)
+{
+	struct ncsi_channel *nc;
+
+	NCSI_FOR_EACH_CHANNEL(np, nc) {
+		if (nc->nc_id == id)
+			return nc;
+	}
+
+	return NULL;
+}
+
+static void ncsi_release_channel(struct ncsi_channel *nc)
+{
+	struct ncsi_dev_priv *ndp = nc->nc_package->np_ndp;
+	struct ncsi_channel_filter *ncf;
+	int i;
+
+	/* Release filters */
+	for (i = 0; i < NCSI_FILTER_MAX; i++) {
+		ncf = nc->nc_filters[i];
+		if (!ncf)
+			continue;
+
+		nc->nc_filters[i] = NULL;
+		kfree(ncf);
+	}
+
+	/* Update active channel if necessary */
+	if (ndp->ndp_active_channel == nc) {
+		ndp->ndp_active_package = NULL;
+		ndp->ndp_active_channel = NULL;
+	}
+
+	/* Remove and free channel */
+	list_del_rcu(&nc->nc_node);
+	kfree(nc);
+}
+
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+				      unsigned char id)
+{
+	struct ncsi_package *np, *tmp;
+
+	np = kzalloc(sizeof(*np), GFP_ATOMIC);
+	if (!np)
+		return NULL;
+
+	np->np_id = id;
+	np->np_ndp = ndp;
+	spin_lock_init(&np->np_channel_lock);
+	INIT_LIST_HEAD(&np->np_channels);
+
+	spin_lock(&ndp->ndp_package_lock);
+	tmp = ncsi_find_package(ndp, id);
+	if (tmp) {
+		spin_unlock(&ndp->ndp_package_lock);
+		kfree(np);
+		return tmp;
+	}
+	list_add_tail_rcu(&np->np_node, &ndp->ndp_packages);
+	spin_unlock(&ndp->ndp_package_lock);
+
+	atomic_inc(&ndp->ndp_package_num);
+	return np;
+}
+
+struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
+				       unsigned char id)
+{
+	struct ncsi_package *np;
+
+	NCSI_FOR_EACH_PACKAGE(ndp, np) {
+		if (np->np_id == id)
+			return np;
+	}
+
+	return NULL;
+}
+
+void ncsi_release_package(struct ncsi_package *np)
+{
+	struct ncsi_dev_priv *ndp = np->np_ndp;
+	struct ncsi_channel *nc, *tmp;
+
+	/* Release all child channels */
+	spin_lock(&np->np_channel_lock);
+	list_for_each_entry_safe(nc, tmp, &np->np_channels, nc_node)
+		ncsi_release_channel(nc);
+	spin_unlock(&np->np_channel_lock);
+
+	/* Clear active package if necessary */
+	if (ndp->ndp_active_package == np) {
+		ndp->ndp_active_package = NULL;
+		ndp->ndp_active_channel = NULL;
+	}
+
+	/* Remove and free package */
+	list_del_rcu(&np->np_node);
+	kfree(np);
+}
+
+void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
+				   unsigned char id,
+				   struct ncsi_package **np,
+				   struct ncsi_channel **nc)
+{
+	struct ncsi_package *p;
+	struct ncsi_channel *c;
+
+	p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
+	c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
+
+	if (np)
+		*np = p;
+	if (nc)
+		*nc = c;
+}
+
+/* For two consective NCSI commands, the packet IDs shouldn't be
+ * same. Otherwise, the bogus response might be replied. So the
+ * available IDs are allocated in round-robin fasion.
+ */
+struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_req *nr = NULL;
+	int idx, limit = 256;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ndp->ndp_req_lock, flags);
+
+	/* Check if there is one available request until the ceiling */
+	for (idx = atomic_read(&ndp->ndp_last_req_idx);
+	     !nr && idx < limit; idx++) {
+		if (ndp->ndp_reqs[idx].nr_used)
+			continue;
+
+		ndp->ndp_reqs[idx].nr_used = true;
+		nr = &ndp->ndp_reqs[idx];
+		atomic_inc(&ndp->ndp_last_req_idx);
+		if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
+			atomic_set(&ndp->ndp_last_req_idx, 0);
+	}
+
+	/* Fail back to check from the starting cursor */
+	for (idx = 0; !nr && idx < atomic_read(&ndp->ndp_last_req_idx); idx++) {
+		if (ndp->ndp_reqs[idx].nr_used)
+			continue;
+
+		ndp->ndp_reqs[idx].nr_used = true;
+		nr = &ndp->ndp_reqs[idx];
+		atomic_inc(&ndp->ndp_last_req_idx);
+		if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
+			atomic_set(&ndp->ndp_last_req_idx, 0);
+	}
+
+	spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+	return nr;
+}
+
+void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
+{
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct sk_buff *cmd, *rsp;
+	unsigned long flags;
+
+	if (nr->nr_timer_enabled) {
+		nr->nr_timer_enabled = false;
+		del_timer_sync(&nr->nr_timer);
+	}
+
+	spin_lock_irqsave(&ndp->ndp_req_lock, flags);
+	cmd = nr->nr_cmd;
+	rsp = nr->nr_rsp;
+	nr->nr_cmd = NULL;
+	nr->nr_rsp = NULL;
+	nr->nr_used = false;
+	spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+
+	/* Release command and response */
+	consume_skb(cmd);
+	consume_skb(rsp);
+}
+
+struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
+{
+	struct ncsi_dev_priv *ndp;
+
+	NCSI_FOR_EACH_DEV(ndp) {
+		if (ndp->ndp_ndev.nd_dev == dev)
+			return &ndp->ndp_ndev;
+	}
+
+	return NULL;
+}
+
+static void ncsi_req_timeout(unsigned long data)
+{
+	struct ncsi_req *nr = (struct ncsi_req *)data;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	unsigned long flags;
+
+	/* If the request already had associated response,
+	 * let the response handler to release it.
+	 */
+	spin_lock_irqsave(&ndp->ndp_req_lock, flags);
+	nr->nr_timer_enabled = false;
+	if (nr->nr_rsp || !nr->nr_cmd) {
+		spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+
+	/* Release the request */
+	ncsi_free_req(nr, true, true);
+}
+
+struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
+				   void (*handler)(struct ncsi_dev *ndev))
+{
+	struct ncsi_dev_priv *ndp;
+	struct ncsi_dev *nd;
+	int idx;
+
+	/* Check if the device has been registered or not */
+	nd = ncsi_find_dev(dev);
+	if (nd)
+		return nd;
+
+	/* Create NCSI device */
+	ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
+	if (!ndp)
+		return NULL;
+
+	nd = &ndp->ndp_ndev;
+	nd->nd_state = ncsi_dev_state_registered;
+	nd->nd_dev = dev;
+	nd->nd_handler = handler;
+
+	/* Initialize private NCSI device */
+	spin_lock_init(&ndp->ndp_package_lock);
+	INIT_LIST_HEAD(&ndp->ndp_packages);
+	spin_lock_init(&ndp->ndp_req_lock);
+	atomic_set(&ndp->ndp_last_req_idx, 0);
+	for (idx = 0; idx < 256; idx++) {
+		ndp->ndp_reqs[idx].nr_id = idx;
+		ndp->ndp_reqs[idx].nr_ndp = ndp;
+		setup_timer(&ndp->ndp_reqs[idx].nr_timer, ncsi_req_timeout,
+			    (unsigned long)&ndp->ndp_reqs[idx]);
+	}
+
+	spin_lock(&ncsi_dev_lock);
+	list_add_tail_rcu(&ndp->ndp_node, &ncsi_dev_list);
+	spin_unlock(&ncsi_dev_lock);
+
+	return nd;
+}
+EXPORT_SYMBOL_GPL(ncsi_register_dev);
+
+void ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+	struct ncsi_package *np, *tmp;
+
+	spin_lock(&ndp->ndp_package_lock);
+	list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
+		ncsi_release_package(np);
+	spin_unlock(&ndp->ndp_package_lock);
+}
+EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 02/10] net/ncsi: NCSI command packet handler
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
  2016-06-30 10:27 ` [PATCH 01/10] net/ncsi: Resource management Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 03/10] net/ncsi: NCSI response " Gavin Shan
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

The NCSI command packets are sent from MC (Management Controller)
to remote end. They are used for multiple purposes: probe existing
NCSI package/channel, retrieve NCSI channel's capability, configure
NCSI channel etc.

This defines struct to represent NCSI command packets and introduces
function ncsi_xmit_cmd(), which will be used to transmit NCSI command
packet according to the request. The request is represented by struct
ncsi_cmd_arg.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 include/uapi/linux/if_ether.h |   1 +
 net/ncsi/Makefile             |   2 +-
 net/ncsi/internal.h           |  18 ++
 net/ncsi/ncsi-cmd.c           | 372 ++++++++++++++++++++++++++++++++++++++++++
 net/ncsi/ncsi-pkt.h           | 168 +++++++++++++++++++
 5 files changed, 560 insertions(+), 1 deletion(-)
 create mode 100644 net/ncsi/ncsi-cmd.c
 create mode 100644 net/ncsi/ncsi-pkt.h

diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index cec849a..7300b2d 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -87,6 +87,7 @@
 #define ETH_P_8021AH	0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_MVRP	0x88F5          /* 802.1Q MVRP                  */
 #define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
+#define ETH_P_NCSI	0x88F8		/* NCSI protocol                */
 #define ETH_P_PRP	0x88FB		/* IEC 62439-3 PRP/HSRv0	*/
 #define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
 #define ETH_P_TDLS	0x890D          /* TDLS */
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index 07b5625..abc4046 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
 #
 # Makefile for NCSI API
 #
-obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 4a8bd76..0c51c89 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -217,6 +217,21 @@ struct ncsi_dev_priv {
 	struct list_head    ndp_node;
 };
 
+struct ncsi_cmd_arg {
+	struct ncsi_dev_priv   *nca_ndp;
+	unsigned char          nca_type;
+	unsigned char          nca_id;
+	unsigned char          nca_package;
+	unsigned char          nca_channel;
+	unsigned short         nca_payload;
+	unsigned int           nca_portid;
+	union {
+		unsigned char  nca_bytes[16];
+		unsigned short nca_words[8];
+		unsigned int   nca_dwords[4];
+	};
+};
+
 extern struct list_head ncsi_dev_list;
 extern spinlock_t ncsi_dev_lock;
 
@@ -250,4 +265,7 @@ struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
 void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
 struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
 
+/* Packet handlers */
+int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
+
 #endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c
new file mode 100644
index 0000000..c64c437
--- /dev/null
+++ b/net/ncsi/ncsi-cmd.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+/* This function should be called after the data area has been
+ * populated completely.
+ */
+static int ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
+				 struct ncsi_cmd_arg *nca)
+{
+	__be32 csum, *checksum;
+	__be32 low, high;
+	unsigned char *stream;
+	int i;
+
+	h->mc_id        = 0;
+	h->revision     = NCSI_PKT_REVISION;
+	h->reserved     = 0;
+	h->id           = nca->nca_id;
+	h->type         = nca->nca_type;
+	h->channel      = NCSI_TO_CHANNEL(nca->nca_package,
+					  nca->nca_channel);
+	h->length       = htons(nca->nca_payload);
+	h->reserved1[0] = 0;
+	h->reserved1[1] = 0;
+
+	/* Calculate the checksum */
+	csum = 0;
+	stream = (unsigned char *)h;
+	for (i = 0; i < sizeof(*h) + nca->nca_payload; i += 2) {
+		high = stream[i];
+		low = stream[i + 1];
+		csum += ((high << 8) | low);
+	}
+
+	/* Fill with the calculated checksum */
+	checksum = (__be32 *)((void *)(h + 1) + nca->nca_payload);
+	csum = (~csum + 1);
+	*checksum = htonl(csum);
+
+	return 0;
+}
+
+static int ncsi_cmd_handler_default(struct sk_buff *skb,
+				    struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_pkt *cmd;
+
+	if (!nca)
+		return 0;
+
+	cmd = (struct ncsi_cmd_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_sp(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_sp_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_sp_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->hw_arbitration = nca->nca_bytes[0];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_dc(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_dc_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_dc_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->ald = nca->nca_bytes[0];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_rc(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_rc_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_rc_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_ae(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_ae_pkt *cmd;
+
+	if (!nca)
+		return 8;
+
+	cmd = (struct ncsi_cmd_ae_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mc_id = nca->nca_bytes[0];
+	cmd->mode = htonl(nca->nca_dwords[1]);
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_sl(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_sl_pkt *cmd;
+
+	if (!nca)
+		return 8;
+
+	cmd = (struct ncsi_cmd_sl_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mode = htonl(nca->nca_dwords[0]);
+	cmd->oem_mode = htonl(nca->nca_dwords[1]);
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_svf(struct sk_buff *skb,
+				struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_svf_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_svf_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->vlan = htons(nca->nca_words[0]);
+	cmd->index = nca->nca_bytes[2];
+	cmd->enable = nca->nca_bytes[3];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_ev(struct sk_buff *skb,
+			       struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_ev_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_ev_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mode = nca->nca_bytes[0];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_sma(struct sk_buff *skb,
+				struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_sma_pkt *cmd;
+	int i;
+
+	if (!nca)
+		return 8;
+
+	cmd = (struct ncsi_cmd_sma_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	for (i = 0; i < 6; i++)
+		cmd->mac[i] = nca->nca_bytes[i];
+	cmd->index = nca->nca_bytes[6];
+	cmd->at_e = nca->nca_bytes[7];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
+				struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_ebf_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_ebf_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mode = htonl(nca->nca_dwords[0]);
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
+				 struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_egmf_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_egmf_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mode = htonl(nca->nca_dwords[0]);
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
+				 struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_cmd_snfc_pkt *cmd;
+
+	if (!nca)
+		return 4;
+
+	cmd = (struct ncsi_cmd_snfc_pkt *)skb_put(skb, sizeof(*cmd));
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->mode = nca->nca_bytes[0];
+	return ncsi_cmd_build_header(&cmd->cmd.common, nca);
+}
+
+static struct ncsi_cmd_handler {
+	unsigned char	nch_type;
+	int		(*nch_handler)(struct sk_buff *skb,
+				       struct ncsi_cmd_arg *nca);
+} ncsi_cmd_handlers[] = {
+	{ NCSI_PKT_CMD_CIS,   ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_SP,    ncsi_cmd_handler_sp	 },
+	{ NCSI_PKT_CMD_DP,    ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_EC,    ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_DC,    ncsi_cmd_handler_dc      },
+	{ NCSI_PKT_CMD_RC,    ncsi_cmd_handler_rc      },
+	{ NCSI_PKT_CMD_ECNT,  ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_DCNT,  ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_AE,    ncsi_cmd_handler_ae      },
+	{ NCSI_PKT_CMD_SL,    ncsi_cmd_handler_sl      },
+	{ NCSI_PKT_CMD_GLS,   ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_SVF,   ncsi_cmd_handler_svf     },
+	{ NCSI_PKT_CMD_EV,    ncsi_cmd_handler_ev      },
+	{ NCSI_PKT_CMD_DV,    ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_SMA,   ncsi_cmd_handler_sma     },
+	{ NCSI_PKT_CMD_EBF,   ncsi_cmd_handler_ebf     },
+	{ NCSI_PKT_CMD_DBF,   ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_EGMF,  ncsi_cmd_handler_egmf    },
+	{ NCSI_PKT_CMD_DGMF,  ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_SNFC,  ncsi_cmd_handler_snfc    },
+	{ NCSI_PKT_CMD_GVI,   ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_GC,    ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_GP,    ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_GCPS,  ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_GNS,   ncsi_cmd_handler_default },
+	{ NCSI_PKT_CMD_GNPTS, ncsi_cmd_handler_default },
+	{ 0,                  NULL                     }
+};
+
+static struct ncsi_req *ncsi_alloc_cmd_req(struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_dev_priv *ndp = nca->nca_ndp;
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct net_device *dev = nd->nd_dev;
+	int hlen = LL_RESERVED_SPACE(dev);
+	int tlen = dev->needed_tailroom;
+	int len = hlen + tlen;
+	struct sk_buff *skb;
+	struct ncsi_req *nr;
+
+	nr = ncsi_alloc_req(ndp);
+	if (!nr)
+		return NULL;
+
+	/* NCSI command packet has 16-bytes header, payload,
+	 * 4-bytes checksum and optional padding.
+	 */
+	len += sizeof(struct ncsi_cmd_pkt_hdr);
+	len += 4;
+	if (nca->nca_payload < 26)
+		len += 26;
+	else
+		len += nca->nca_payload;
+
+	/* Allocate skb */
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		ncsi_free_req(nr, false, false);
+		return NULL;
+	}
+
+	nr->nr_cmd = skb;
+	skb_reserve(skb, hlen);
+	skb_reset_network_header(skb);
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_NCSI);
+
+	return nr;
+}
+
+int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
+{
+	struct ncsi_req *nr;
+	struct ethhdr *eh;
+	struct ncsi_cmd_handler *nch;
+	int i, ret;
+
+	/* Search for the handler */
+	nch = ncsi_cmd_handlers;
+	while (nch->nch_handler) {
+		if (nch->nch_type == nca->nca_type)
+			break;
+		nch++;
+	}
+
+	if (!nch->nch_handler) {
+		pr_info("%s: Cannot send packet with type 0x%x\n",
+			__func__, nca->nca_type);
+		return -ENOENT;
+	}
+
+	/* Get packet payload length and allocate the request */
+	nca->nca_payload = nch->nch_handler(NULL, NULL);
+	nr = ncsi_alloc_cmd_req(nca);
+	if (!nr)
+		return -ENOMEM;
+
+	/* Prepare the packet */
+	nca->nca_id = nr->nr_id;
+	ret = nch->nch_handler(nr->nr_cmd, nca);
+	if (ret) {
+		ncsi_free_req(nr, true, false);
+		return ret;
+	}
+
+	/* Fill the ethernet header */
+	eh = (struct ethhdr *)skb_push(nr->nr_cmd, sizeof(*eh));
+	eh->h_proto = htons(ETH_P_NCSI);
+	for (i = 0; i < ETH_ALEN; i++) {
+		eh->h_dest[i] = 0xff;
+		eh->h_source[i] = 0xff;
+	}
+
+	/* Start the timer for the request that might not have
+	 * corresponding response. I'm not sure 1 second delay
+	 * here is enough. Anyway, NCSI is internal network, so
+	 * the responsiveness should be as fast as enough.
+	 */
+	nr->nr_timer_enabled = true;
+	mod_timer(&nr->nr_timer, jiffies + 1 * HZ);
+
+	/* Send NCSI packet */
+	skb_get(nr->nr_cmd);
+	ret = dev_queue_xmit(nr->nr_cmd);
+	if (ret < 0) {
+		ncsi_free_req(nr, true, false);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
new file mode 100644
index 0000000..7f030b5
--- /dev/null
+++ b/net/ncsi/ncsi-pkt.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __NCSI_PKT_H__
+#define __NCSI_PKT_H__
+
+struct ncsi_pkt_hdr {
+	unsigned char mc_id;        /* Management controller ID */
+	unsigned char revision;     /* NCSI version - 0x01      */
+	unsigned char reserved;     /* Reserved                 */
+	unsigned char id;           /* Packet sequence number   */
+	unsigned char type;         /* Packet type              */
+	unsigned char channel;      /* Network controller ID    */
+	__be16        length;       /* Payload length           */
+	__be32        reserved1[2]; /* Reserved                 */
+};
+
+struct ncsi_cmd_pkt_hdr {
+	struct ncsi_pkt_hdr common; /* Common NCSI packet header */
+};
+
+/* NCSI common command packet */
+struct ncsi_cmd_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
+	__be32                  checksum; /* Checksum       */
+	unsigned char           pad[26];
+};
+
+/* Select Package */
+struct ncsi_cmd_sp_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;            /* Command header */
+	unsigned char           reserved[3];    /* Reserved       */
+	unsigned char           hw_arbitration; /* HW arbitration */
+	__be32                  checksum;       /* Checksum       */
+	unsigned char           pad[22];
+};
+
+/* Disable Channel */
+struct ncsi_cmd_dc_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;         /* Command header  */
+	unsigned char           reserved[3]; /* Reserved        */
+	unsigned char           ald;         /* Allow link down */
+	__be32                  checksum;    /* Checksum        */
+	unsigned char           pad[22];
+};
+
+/* Reset Channel */
+struct ncsi_cmd_rc_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
+	__be32                  reserved; /* Reserved       */
+	__be32                  checksum; /* Checksum       */
+	unsigned char           pad[22];
+};
+
+/* AEN Enable */
+struct ncsi_cmd_ae_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;         /* Command header   */
+	unsigned char           reserved[3]; /* Reserved         */
+	unsigned char           mc_id;       /* MC ID            */
+	__be32                  mode;        /* AEN working mode */
+	__be32                  checksum;    /* Checksum         */
+	unsigned char           pad[18];
+};
+
+/* Set Link */
+struct ncsi_cmd_sl_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header    */
+	__be32                  mode;     /* Link working mode */
+	__be32                  oem_mode; /* OEM link mode     */
+	__be32                  checksum; /* Checksum          */
+	unsigned char           pad[18];
+};
+
+/* Set VLAN Filter */
+struct ncsi_cmd_svf_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;       /* Command header    */
+	__be16                  reserved;  /* Reserved          */
+	__be16                  vlan;      /* VLAN ID           */
+	__be16                  reserved1; /* Reserved          */
+	unsigned char           index;     /* VLAN table index  */
+	unsigned char           enable;    /* Enable or disable */
+	__be32                  checksum;  /* Checksum          */
+	unsigned char           pad[14];
+};
+
+/* Enable VLAN */
+struct ncsi_cmd_ev_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;         /* Command header   */
+	unsigned char           reserved[3]; /* Reserved         */
+	unsigned char           mode;        /* VLAN filter mode */
+	__be32                  checksum;    /* Checksum         */
+	unsigned char           pad[22];
+};
+
+/* Set MAC Address */
+struct ncsi_cmd_sma_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header          */
+	unsigned char           mac[6];   /* MAC address             */
+	unsigned char           index;    /* MAC table index         */
+	unsigned char           at_e;     /* Addr type and operation */
+	__be32                  checksum; /* Checksum                */
+	unsigned char           pad[18];
+};
+
+/* Enable Broadcast Filter */
+struct ncsi_cmd_ebf_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
+	__be32                  mode;     /* Filter mode    */
+	__be32                  checksum; /* Checksum       */
+	unsigned char           pad[22];
+};
+
+/* Enable Global Multicast Filter */
+struct ncsi_cmd_egmf_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
+	__be32                  mode;     /* Global MC mode */
+	__be32                  checksum; /* Checksum       */
+	unsigned char           pad[22];
+};
+
+/* Set NCSI Flow Control */
+struct ncsi_cmd_snfc_pkt {
+	struct ncsi_cmd_pkt_hdr cmd;         /* Command header    */
+	unsigned char           reserved[3]; /* Reserved          */
+	unsigned char           mode;        /* Flow control mode */
+	__be32                  checksum;    /* Checksum          */
+	unsigned char           pad[22];
+};
+
+/* NCSI packet revision */
+#define NCSI_PKT_REVISION	0x01
+
+/* NCSI packet commands */
+#define NCSI_PKT_CMD_CIS	0x00	/* Clear Initial State              */
+#define NCSI_PKT_CMD_SP		0x01	/* Select Package                   */
+#define NCSI_PKT_CMD_DP		0x02	/* Deselect Package                 */
+#define NCSI_PKT_CMD_EC		0x03	/* Enable Channel                   */
+#define NCSI_PKT_CMD_DC		0x04	/* Disable Channel                  */
+#define NCSI_PKT_CMD_RC		0x05	/* Reset Channel                    */
+#define NCSI_PKT_CMD_ECNT	0x06	/* Enable Channel Network Tx        */
+#define NCSI_PKT_CMD_DCNT	0x07	/* Disable Channel Network Tx       */
+#define NCSI_PKT_CMD_AE		0x08	/* AEN Enable                       */
+#define NCSI_PKT_CMD_SL		0x09	/* Set Link                         */
+#define NCSI_PKT_CMD_GLS	0x0a	/* Get Link                         */
+#define NCSI_PKT_CMD_SVF	0x0b	/* Set VLAN Filter                  */
+#define NCSI_PKT_CMD_EV		0x0c	/* Enable VLAN                      */
+#define NCSI_PKT_CMD_DV		0x0d	/* Disable VLAN                     */
+#define NCSI_PKT_CMD_SMA	0x0e	/* Set MAC address                  */
+#define NCSI_PKT_CMD_EBF	0x10	/* Enable Broadcast Filter          */
+#define NCSI_PKT_CMD_DBF	0x11	/* Disable Broadcast Filter         */
+#define NCSI_PKT_CMD_EGMF	0x12	/* Enable Global Multicast Filter   */
+#define NCSI_PKT_CMD_DGMF	0x13	/* Disable Global Multicast Filter  */
+#define NCSI_PKT_CMD_SNFC	0x14	/* Set NCSI Flow Control            */
+#define NCSI_PKT_CMD_GVI	0x15	/* Get Version ID                   */
+#define NCSI_PKT_CMD_GC		0x16	/* Get Capabilities                 */
+#define NCSI_PKT_CMD_GP		0x17	/* Get Parameters                   */
+#define NCSI_PKT_CMD_GCPS	0x18	/* Get Controller Packet Statistics */
+#define NCSI_PKT_CMD_GNS	0x19	/* Get NCSI Statistics              */
+#define NCSI_PKT_CMD_GNPTS	0x1a	/* Get NCSI Pass-throu Statistics   */
+#define NCSI_PKT_CMD_OEM	0x50	/* OEM                              */
+
+#endif /* __NCSI_PKT_H__ */
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 03/10] net/ncsi: NCSI response packet handler
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
  2016-06-30 10:27 ` [PATCH 01/10] net/ncsi: Resource management Gavin Shan
  2016-06-30 10:27 ` [PATCH 02/10] net/ncsi: NCSI command packet handler Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 04/10] net/ncsi: Package and channel management Gavin Shan
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

The NCSI response packets are sent to MC (Management Controller)
from the remote end. They are responses of NCSI command packets
for multiple purposes: completion status of NCSI command packets,
return NCSI channel's capability or configuration etc.

This defines struct to represent NCSI response packets and introduces
function ncsi_rcv_rsp() which will be used to receive NCSI response
packets and parse them.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 net/ncsi/Makefile   |    2 +-
 net/ncsi/internal.h |    2 +
 net/ncsi/ncsi-pkt.h |  191 +++++++++
 net/ncsi/ncsi-rsp.c | 1159 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1353 insertions(+), 1 deletion(-)
 create mode 100644 net/ncsi/ncsi-rsp.c

diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index abc4046..4751819 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
 #
 # Makefile for NCSI API
 #
-obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 0c51c89..d435f91 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -267,5 +267,7 @@ struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
 
 /* Packet handlers */
 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
+int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
+		 struct packet_type *pt, struct net_device *orig_dev);
 
 #endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
index 7f030b5..95882f3 100644
--- a/net/ncsi/ncsi-pkt.h
+++ b/net/ncsi/ncsi-pkt.h
@@ -25,6 +25,12 @@ struct ncsi_cmd_pkt_hdr {
 	struct ncsi_pkt_hdr common; /* Common NCSI packet header */
 };
 
+struct ncsi_rsp_pkt_hdr {
+	struct ncsi_pkt_hdr common; /* Common NCSI packet header */
+	__be16              code;   /* Response code             */
+	__be16              reason; /* Response reason           */
+};
+
 /* NCSI common command packet */
 struct ncsi_cmd_pkt {
 	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
@@ -32,6 +38,12 @@ struct ncsi_cmd_pkt {
 	unsigned char           pad[26];
 };
 
+struct ncsi_rsp_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;      /* Response header */
+	__be32                  checksum; /* Checksum        */
+	unsigned char           pad[22];
+};
+
 /* Select Package */
 struct ncsi_cmd_sp_pkt {
 	struct ncsi_cmd_pkt_hdr cmd;            /* Command header */
@@ -133,6 +145,143 @@ struct ncsi_cmd_snfc_pkt {
 	unsigned char           pad[22];
 };
 
+/* Get Link Status */
+struct ncsi_rsp_gls_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;        /* Response header   */
+	__be32                  status;     /* Link status       */
+	__be32                  other;      /* Other indications */
+	__be32                  oem_status; /* OEM link status   */
+	__be32                  checksum;
+	unsigned char           pad[10];
+};
+
+/* Get Version ID */
+struct ncsi_rsp_gvi_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;          /* Response header */
+	__be32                  ncsi_version; /* NCSI version    */
+	unsigned char           reserved[3];  /* Reserved        */
+	unsigned char           alpha2;       /* NCSI version    */
+	unsigned char           fw_name[12];  /* f/w name string */
+	__be32                  fw_version;   /* f/w version     */
+	__be16                  pci_ids[4];   /* PCI IDs         */
+	__be32                  mf_id;        /* Manufacture ID  */
+	__be32                  checksum;
+};
+
+/* Get Capabilities */
+struct ncsi_rsp_gc_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;         /* Response header   */
+	__be32                  cap;         /* Capabilities      */
+	__be32                  bc_cap;      /* Broadcast cap     */
+	__be32                  mc_cap;      /* Multicast cap     */
+	__be32                  buf_cap;     /* Buffering cap     */
+	__be32                  aen_cap;     /* AEN cap           */
+	unsigned char           vlan_cnt;    /* VLAN filter count */
+	unsigned char           mixed_cnt;   /* Mix filter count  */
+	unsigned char           mc_cnt;      /* MC filter count   */
+	unsigned char           uc_cnt;      /* UC filter count   */
+	unsigned char           reserved[2]; /* Reserved          */
+	unsigned char           vlan_mode;   /* VLAN mode         */
+	unsigned char           channel_cnt; /* Channel count     */
+	__be32                  checksum;    /* Checksum          */
+};
+
+/* Get Parameters */
+struct ncsi_rsp_gp_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;          /* Response header       */
+	unsigned char           mac_cnt;      /* Number of MAC addr    */
+	unsigned char           reserved[2];  /* Reserved              */
+	unsigned char           mac_enable;   /* MAC addr enable flags */
+	unsigned char           vlan_cnt;     /* VLAN tag count        */
+	unsigned char           reserved1;    /* Reserved              */
+	__be16                  vlan_enable;  /* VLAN tag enable flags */
+	__be32                  link_mode;    /* Link setting          */
+	__be32                  bc_mode;      /* BC filter mode        */
+	__be32                  valid_modes;  /* Valid mode parameters */
+	unsigned char           vlan_mode;    /* VLAN mode             */
+	unsigned char           fc_mode;      /* Flow control mode     */
+	unsigned char           reserved2[2]; /* Reserved              */
+	__be32                  aen_mode;     /* AEN mode              */
+	unsigned char           mac[6];       /* Supported MAC addr    */
+	__be16                  vlan;         /* Supported VLAN tags   */
+	__be32                  checksum;     /* Checksum              */
+};
+
+/* Get Controller Packet Statistics */
+struct ncsi_rsp_gcps_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;            /* Response header            */
+	__be32                  cnt_hi;         /* Counter cleared            */
+	__be32                  cnt_lo;         /* Counter cleared            */
+	__be32                  rx_bytes;       /* Rx bytes                   */
+	__be32                  tx_bytes;       /* Tx bytes                   */
+	__be32                  rx_uc_pkts;     /* Rx UC packets              */
+	__be32                  rx_mc_pkts;     /* Rx MC packets              */
+	__be32                  rx_bc_pkts;     /* Rx BC packets              */
+	__be32                  tx_uc_pkts;     /* Tx UC packets              */
+	__be32                  tx_mc_pkts;     /* Tx MC packets              */
+	__be32                  tx_bc_pkts;     /* Tx BC packets              */
+	__be32                  fcs_err;        /* FCS errors                 */
+	__be32                  align_err;      /* Alignment errors           */
+	__be32                  false_carrier;  /* False carrier detection    */
+	__be32                  runt_pkts;      /* Rx runt packets            */
+	__be32                  jabber_pkts;    /* Rx jabber packets          */
+	__be32                  rx_pause_xon;   /* Rx pause XON frames        */
+	__be32                  rx_pause_xoff;  /* Rx XOFF frames             */
+	__be32                  tx_pause_xon;   /* Tx XON frames              */
+	__be32                  tx_pause_xoff;  /* Tx XOFF frames             */
+	__be32                  tx_s_collision; /* Single collision frames    */
+	__be32                  tx_m_collision; /* Multiple collision frames  */
+	__be32                  l_collision;    /* Late collision frames      */
+	__be32                  e_collision;    /* Excessive collision frames */
+	__be32                  rx_ctl_frames;  /* Rx control frames          */
+	__be32                  rx_64_frames;   /* Rx 64-bytes frames         */
+	__be32                  rx_127_frames;  /* Rx 65-127 bytes frames     */
+	__be32                  rx_255_frames;  /* Rx 128-255 bytes frames    */
+	__be32                  rx_511_frames;  /* Rx 256-511 bytes frames    */
+	__be32                  rx_1023_frames; /* Rx 512-1023 bytes frames   */
+	__be32                  rx_1522_frames; /* Rx 1024-1522 bytes frames  */
+	__be32                  rx_9022_frames; /* Rx 1523-9022 bytes frames  */
+	__be32                  tx_64_frames;   /* Tx 64-bytes frames         */
+	__be32                  tx_127_frames;  /* Tx 65-127 bytes frames     */
+	__be32                  tx_255_frames;  /* Tx 128-255 bytes frames    */
+	__be32                  tx_511_frames;  /* Tx 256-511 bytes frames    */
+	__be32                  tx_1023_frames; /* Tx 512-1023 bytes frames   */
+	__be32                  tx_1522_frames; /* Tx 1024-1522 bytes frames  */
+	__be32                  tx_9022_frames; /* Tx 1523-9022 bytes frames  */
+	__be32                  rx_valid_bytes; /* Rx valid bytes             */
+	__be32                  rx_runt_pkts;   /* Rx error runt packets      */
+	__be32                  rx_jabber_pkts; /* Rx error jabber packets    */
+	__be32                  checksum;       /* Checksum                   */
+};
+
+/* Get NCSI Statistics */
+struct ncsi_rsp_gns_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;           /* Response header         */
+	__be32                  rx_cmds;       /* Rx NCSI commands        */
+	__be32                  dropped_cmds;  /* Dropped commands        */
+	__be32                  cmd_type_errs; /* Command type errors     */
+	__be32                  cmd_csum_errs; /* Command checksum errors */
+	__be32                  rx_pkts;       /* Rx NCSI packets         */
+	__be32                  tx_pkts;       /* Tx NCSI packets         */
+	__be32                  tx_aen_pkts;   /* Tx AEN packets          */
+	__be32                  checksum;      /* Checksum                */
+};
+
+/* Get NCSI Pass-through Statistics */
+struct ncsi_rsp_gnpts_pkt {
+	struct ncsi_rsp_pkt_hdr rsp;            /* Response header     */
+	__be32                  tx_pkts;        /* Tx packets          */
+	__be32                  tx_dropped;     /* Tx dropped packets  */
+	__be32                  tx_channel_err; /* Tx channel errors   */
+	__be32                  tx_us_err;      /* Tx undersize errors */
+	__be32                  rx_pkts;        /* Rx packets          */
+	__be32                  rx_dropped;     /* Rx dropped packets  */
+	__be32                  rx_channel_err; /* Rx channel errors   */
+	__be32                  rx_us_err;      /* Rx undersize errors */
+	__be32                  rx_os_err;      /* Rx oversize errors  */
+	__be32                  checksum;       /* Checksum            */
+};
+
 /* NCSI packet revision */
 #define NCSI_PKT_REVISION	0x01
 
@@ -165,4 +314,46 @@ struct ncsi_cmd_snfc_pkt {
 #define NCSI_PKT_CMD_GNPTS	0x1a	/* Get NCSI Pass-throu Statistics   */
 #define NCSI_PKT_CMD_OEM	0x50	/* OEM                              */
 
+/* NCSI packet responses */
+#define NCSI_PKT_RSP_CIS	(NCSI_PKT_CMD_CIS   + 0x80)
+#define NCSI_PKT_RSP_SP		(NCSI_PKT_CMD_SP    + 0x80)
+#define NCSI_PKT_RSP_DP		(NCSI_PKT_CMD_DP    + 0x80)
+#define NCSI_PKT_RSP_EC		(NCSI_PKT_CMD_EC    + 0x80)
+#define NCSI_PKT_RSP_DC		(NCSI_PKT_CMD_DC    + 0x80)
+#define NCSI_PKT_RSP_RC		(NCSI_PKT_CMD_RC    + 0x80)
+#define NCSI_PKT_RSP_ECNT	(NCSI_PKT_CMD_ECNT  + 0x80)
+#define NCSI_PKT_RSP_DCNT	(NCSI_PKT_CMD_DCNT  + 0x80)
+#define NCSI_PKT_RSP_AE		(NCSI_PKT_CMD_AE    + 0x80)
+#define NCSI_PKT_RSP_SL		(NCSI_PKT_CMD_SL    + 0x80)
+#define NCSI_PKT_RSP_GLS	(NCSI_PKT_CMD_GLS   + 0x80)
+#define NCSI_PKT_RSP_SVF	(NCSI_PKT_CMD_SVF   + 0x80)
+#define NCSI_PKT_RSP_EV		(NCSI_PKT_CMD_EV    + 0x80)
+#define NCSI_PKT_RSP_DV		(NCSI_PKT_CMD_DV    + 0x80)
+#define NCSI_PKT_RSP_SMA	(NCSI_PKT_CMD_SMA   + 0x80)
+#define NCSI_PKT_RSP_EBF	(NCSI_PKT_CMD_EBF   + 0x80)
+#define NCSI_PKT_RSP_DBF	(NCSI_PKT_CMD_DBF   + 0x80)
+#define NCSI_PKT_RSP_EGMF	(NCSI_PKT_CMD_EGMF  + 0x80)
+#define NCSI_PKT_RSP_DGMF	(NCSI_PKT_CMD_DGMF  + 0x80)
+#define NCSI_PKT_RSP_SNFC	(NCSI_PKT_CMD_SNFC  + 0x80)
+#define NCSI_PKT_RSP_GVI	(NCSI_PKT_CMD_GVI   + 0x80)
+#define NCSI_PKT_RSP_GC		(NCSI_PKT_CMD_GC    + 0x80)
+#define NCSI_PKT_RSP_GP		(NCSI_PKT_CMD_GP    + 0x80)
+#define NCSI_PKT_RSP_GCPS	(NCSI_PKT_CMD_GCPS  + 0x80)
+#define NCSI_PKT_RSP_GNS	(NCSI_PKT_CMD_GNS   + 0x80)
+#define NCSI_PKT_RSP_GNPTS	(NCSI_PKT_CMD_GNPTS + 0x80)
+#define NCSI_PKT_RSP_OEM	(NCSI_PKT_CMD_OEM   + 0x80)
+
+/* NCSI response code/reason */
+#define NCSI_PKT_RSP_C_COMPLETED	0x0000  /* Command Completed        */
+#define NCSI_PKT_RSP_C_FAILED		0x0001  /* Command Failed           */
+#define NCSI_PKT_RSP_C_UNAVAILABLE	0x0002  /* Command Unavailable      */
+#define NCSI_PKT_RSP_C_UNSUPPORTED	0x0003  /* Command Unsupported      */
+#define NCSI_PKT_RSP_R_NO_ERROR		0x0000  /* No Error                 */
+#define NCSI_PKT_RSP_R_INTERFACE	0x0001  /* Interface not ready      */
+#define NCSI_PKT_RSP_R_PARAM		0x0002  /* Invalid Parameter        */
+#define NCSI_PKT_RSP_R_CHANNEL		0x0003  /* Channel not Ready        */
+#define NCSI_PKT_RSP_R_PACKAGE		0x0004  /* Package not Ready        */
+#define NCSI_PKT_RSP_R_LENGTH		0x0005  /* Invalid payload length   */
+#define NCSI_PKT_RSP_R_UNKNOWN		0x7fff  /* Command type unsupported */
+
 #endif /* __NCSI_PKT_H__ */
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
new file mode 100644
index 0000000..257079fc
--- /dev/null
+++ b/net/ncsi/ncsi-rsp.c
@@ -0,0 +1,1159 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+static int ncsi_validate_rsp_pkt(struct ncsi_req *nr,
+				 unsigned short payload)
+{
+	struct ncsi_rsp_pkt_hdr *h;
+	unsigned char *stream;
+	__be32 *checksum, csum;
+	__be32 high, low;
+	int i;
+
+	/* Check NCSI packet header. We don't need validate
+	 * the packet type, which should have been checked
+	 * before calling this function.
+	 */
+	h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->nr_rsp);
+	if (h->common.revision != NCSI_PKT_REVISION)
+		return -EINVAL;
+	if (ntohs(h->common.length) != payload)
+		return -EINVAL;
+
+	/* Check on code and reason */
+	if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
+	    ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
+		return -EINVAL;
+
+	/* Validate checksum, which might be zeroes if the
+	 * sender doesn't support checksum according to NCSI
+	 * specification.
+	 */
+	checksum = (__be32 *)((void *)(h + 1) + payload - 4);
+	if (ntohl(*checksum) == 0)
+		return 0;
+
+	csum = 0;
+	stream = (unsigned char *)h;
+	for (i = 0; i < sizeof(*h) + payload - 4; i += 2) {
+		high = stream[i];
+		low = stream[i + 1];
+		csum += ((high << 8) | low);
+	}
+
+	csum = ~csum + 1;
+	if (*checksum != htonl(csum))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_default(struct ncsi_req *nr)
+{
+	return ncsi_validate_rsp_pkt(nr, 0);
+}
+
+static int ncsi_rsp_handler_cis(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	unsigned char id;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
+	if (!nc) {
+		id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
+		nc = ncsi_add_channel(np, id);
+	} else if (nc->nc_state == ncsi_channel_state_deselected_initial) {
+		nc->nc_state = ncsi_channel_state_deselected_ready;
+	} else if (nc->nc_state == ncsi_channel_state_selected_initial) {
+		nc->nc_state = ncsi_channel_state_selected_ready;
+	}
+
+	return nc ? 0 : -ENODEV;
+}
+
+static int ncsi_rsp_handler_sp(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	unsigned char id;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Add the package if it's not existing. Otherwise,
+	 * to change the state of its child channels.
+	 */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      &np, NULL);
+	if (!np) {
+		id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
+		np = ncsi_add_package(ndp, id);
+		if (!np)
+			return -ENODEV;
+	}
+
+	NCSI_FOR_EACH_CHANNEL(np, nc) {
+		if (nc->nc_state == ncsi_channel_state_deselected_initial)
+			nc->nc_state = ncsi_channel_state_selected_initial;
+		else if (nc->nc_state == ncsi_channel_state_deselected_ready)
+			nc->nc_state = ncsi_channel_state_selected_ready;
+	}
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_dp(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      &np, NULL);
+	if (!np)
+		return -ENODEV;
+
+	/* Change state of all channels attached to the package */
+	NCSI_FOR_EACH_CHANNEL(np, nc) {
+		if (nc->nc_state == ncsi_channel_state_selected_initial)
+			nc->nc_state = ncsi_channel_state_deselected_initial;
+		else if (nc->nc_state == ncsi_channel_state_selected_ready)
+			nc->nc_state = ncsi_channel_state_deselected_ready;
+	}
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_ec(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	ncm = &nc->nc_modes[NCSI_MODE_ENABLE];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	ncm->ncm_enable = 1;
+	return 0;
+}
+
+static int ncsi_rsp_handler_dc(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	ncm = &nc->nc_modes[NCSI_MODE_ENABLE];
+	if (!ncm->ncm_enable)
+		return -EBUSY;
+
+	ncm->ncm_enable = 0;
+	return 0;
+}
+
+static int ncsi_rsp_handler_rc(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update state for the specified channel */
+	if (nc->nc_state == ncsi_channel_state_deselected_ready)
+		nc->nc_state = ncsi_channel_state_deselected_initial;
+	else if (nc->nc_state == ncsi_channel_state_selected_ready)
+		nc->nc_state = ncsi_channel_state_selected_initial;
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_ecnt(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	ncm = &nc->nc_modes[NCSI_MODE_TX_ENABLE];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	ncm->ncm_enable = 1;
+	return 0;
+}
+
+static int ncsi_rsp_handler_dcnt(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	ncm = &nc->nc_modes[NCSI_MODE_TX_ENABLE];
+	if (!ncm->ncm_enable)
+		return -EBUSY;
+
+	ncm->ncm_enable = 1;
+	return 0;
+}
+
+static int ncsi_rsp_handler_ae(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_ae_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if the AEN has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_AEN];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to AEN configuration */
+	cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->nr_cmd);
+	ncm->ncm_enable = 1;
+	ncm->ncm_data[0] = cmd->mc_id;
+	ncm->ncm_data[1] = ntohl(cmd->mode);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_sl(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_sl_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	cmd = (struct ncsi_cmd_sl_pkt *)skb_network_header(nr->nr_cmd);
+	ncm = &nc->nc_modes[NCSI_MODE_LINK];
+	ncm->ncm_data[0] = ntohl(cmd->mode);
+	ncm->ncm_data[1] = ntohl(cmd->oem_mode);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gls(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gls_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 16);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	ncm = &nc->nc_modes[NCSI_MODE_LINK];
+	ncm->ncm_data[2] = ntohl(rsp->status);
+	ncm->ncm_data[3] = ntohl(rsp->other);
+	ncm->ncm_data[4] = ntohl(rsp->oem_status);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_svf(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_svf_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_filter *ncf;
+	unsigned short vlan;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->nr_cmd);
+	ncf = nc->nc_filters[NCSI_FILTER_VLAN];
+	if (!ncf)
+		return -ENOENT;
+	if (cmd->index >= ncf->ncf_total)
+		return -ERANGE;
+
+	/* Add or remove the VLAN filter */
+	if (!(cmd->enable & 0x1)) {
+		ret = ncsi_del_channel_filter(nc, NCSI_FILTER_VLAN, cmd->index);
+	} else {
+		vlan = ntohs(cmd->vlan);
+		ret = ncsi_add_channel_filter(nc, NCSI_FILTER_VLAN, &vlan);
+	}
+
+	return ret;
+}
+
+static int ncsi_rsp_handler_ev(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_ev_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if VLAN mode has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_VLAN];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to VLAN mode */
+	cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->nr_cmd);
+	ncm->ncm_enable = 1;
+	ncm->ncm_data[0] = ntohl(cmd->mode);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_dv(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if VLAN mode has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_VLAN];
+	if (!ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to VLAN mode */
+	ncm->ncm_enable = 0;
+	return 0;
+}
+
+static int ncsi_rsp_handler_sma(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_sma_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_filter *ncf;
+	void *bitmap;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* According to NCSI spec 1.01, the mixed filter table
+	 * isn't supported yet.
+	 */
+	cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->nr_cmd);
+	switch (cmd->at_e >> 5) {
+	case 0x0:	/* UC address */
+		ncf = nc->nc_filters[NCSI_FILTER_UC];
+		break;
+	case 0x1:	/* MC address */
+		ncf = nc->nc_filters[NCSI_FILTER_MC];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Sanity check on the filter */
+	if (!ncf)
+		return -ENOENT;
+	else if (cmd->index >= ncf->ncf_total)
+		return -ERANGE;
+
+	bitmap = &ncf->ncf_bitmap;
+	if (cmd->at_e & 0x1) {
+		if (test_and_set_bit(cmd->index, bitmap))
+			return -EBUSY;
+		memcpy(ncf->ncf_data + 6 * cmd->index, cmd->mac, 6);
+	} else {
+		if (!test_and_clear_bit(cmd->index, bitmap))
+			return -EBUSY;
+
+		memset(ncf->ncf_data + 6 * cmd->index, 0, 6);
+	}
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_ebf(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_ebf_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the package and channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if broadcast filter has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_BC];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to broadcast filter mode */
+	cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->nr_cmd);
+	ncm->ncm_enable = 1;
+	ncm->ncm_data[0] = ntohl(cmd->mode);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_dbf(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if broadcast filter isn't enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_BC];
+	if (!ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to broadcast filter mode */
+	ncm->ncm_enable = 0;
+	ncm->ncm_data[0] = 0;
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_egmf(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_egmf_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if multicast filter has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_MC];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to multicast filter mode */
+	cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->nr_cmd);
+	ncm->ncm_enable = 1;
+	ncm->ncm_data[0] = ntohl(cmd->mode);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_dgmf(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if multicast filter has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_MC];
+	if (!ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to multicast filter mode */
+	ncm->ncm_enable = 0;
+	ncm->ncm_data[0] = 0;
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_snfc(struct ncsi_req *nr)
+{
+	struct ncsi_cmd_snfc_pkt *cmd;
+	struct ncsi_rsp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 4);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Check if flow control has been enabled */
+	ncm = &nc->nc_modes[NCSI_MODE_FC];
+	if (ncm->ncm_enable)
+		return -EBUSY;
+
+	/* Update to flow control mode */
+	cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->nr_cmd);
+	ncm->ncm_enable = 1;
+	ncm->ncm_data[0] = cmd->mode;
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gvi(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gvi_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_version *ncv;
+	int i, ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 36);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gvi_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update to channel's version info */
+	ncv = &nc->nc_version;
+	ncv->ncv_version = ntohl(rsp->ncsi_version);
+	ncv->ncv_alpha2 = rsp->alpha2;
+	memcpy(ncv->ncv_fw_name, rsp->fw_name, 12);
+	ncv->ncv_fw_version = ntohl(rsp->fw_version);
+	for (i = 0; i < 4; i++)
+		ncv->ncv_pci_ids[i] = ntohs(rsp->pci_ids[i]);
+	ncv->ncv_mf_id = ntohl(rsp->mf_id);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gc(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gc_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_filter *ncf;
+	size_t size, entry_size;
+	int cnt, i, ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 32);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update channel's capabilities */
+	nc->nc_caps[NCSI_CAP_GENERIC].ncc_cap = ntohl(rsp->cap) &
+						NCSI_CAP_GENERIC_MASK;
+	nc->nc_caps[NCSI_CAP_BC].ncc_cap = ntohl(rsp->bc_cap) &
+					   NCSI_CAP_BC_MASK;
+	nc->nc_caps[NCSI_CAP_MC].ncc_cap = ntohl(rsp->mc_cap) &
+					   NCSI_CAP_MC_MASK;
+	nc->nc_caps[NCSI_CAP_BUFFER].ncc_cap = ntohl(rsp->buf_cap);
+	nc->nc_caps[NCSI_CAP_AEN].ncc_cap = ntohl(rsp->aen_cap) &
+					    NCSI_CAP_AEN_MASK;
+	nc->nc_caps[NCSI_CAP_VLAN].ncc_cap = rsp->vlan_mode &
+					     NCSI_CAP_VLAN_MASK;
+
+	/* Build filters */
+	for (i = 0; i < NCSI_FILTER_MAX; i++) {
+		switch (i) {
+		case NCSI_FILTER_VLAN:
+			cnt = rsp->vlan_cnt;
+			entry_size = 2;
+			break;
+		case NCSI_FILTER_MIXED:
+			cnt = rsp->mixed_cnt;
+			entry_size = 6;
+			break;
+		case NCSI_FILTER_MC:
+			cnt = rsp->mc_cnt;
+			entry_size = 6;
+			break;
+		case NCSI_FILTER_UC:
+			cnt = rsp->uc_cnt;
+			entry_size = 6;
+			break;
+		default:
+			continue;
+		}
+
+		if (!cnt || nc->nc_filters[i])
+			continue;
+
+		size = sizeof(*ncf) + cnt * entry_size;
+		ncf = kzalloc(size, GFP_ATOMIC);
+		if (!ncf) {
+			pr_warn("%s: Cannot alloc filter table (%d)\n",
+				__func__, i);
+			return -ENOMEM;
+		}
+
+		ncf->ncf_index = i;
+		ncf->ncf_total = cnt;
+		ncf->ncf_bitmap = 0x0ul;
+		nc->nc_filters[i] = ncf;
+	}
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gp(struct ncsi_req *nr)
+{
+	struct ncsi_pkt_hdr *h;
+	struct ncsi_rsp_gp_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	unsigned short length, enable, vlan;
+	unsigned char *pdata;
+	int table, i, ret;
+
+	/* The get parameter response packet has variable length.
+	 * The payload should be figured out from the packet
+	 * header, instead of the fixed one we have for other types
+	 * of packets.
+	 */
+	h = (struct ncsi_pkt_hdr *)skb_network_header(nr->nr_rsp);
+	length = ntohs(h->length);
+	if (length < 32)
+		return -EINVAL;
+
+	ret = ncsi_validate_rsp_pkt(nr, length);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Modes with explicit enabled indications */
+	if (ntohl(rsp->valid_modes) & 0x1) {	/* BC filter mode */
+		nc->nc_modes[NCSI_MODE_BC].ncm_enable = 1;
+		nc->nc_modes[NCSI_MODE_BC].ncm_data[0] = ntohl(rsp->bc_mode);
+	}
+	if (ntohl(rsp->valid_modes) & 0x2)	/* Channel enabled */
+		nc->nc_modes[NCSI_MODE_ENABLE].ncm_enable = 1;
+	if (ntohl(rsp->valid_modes) & 0x4)	/* Channel Tx enabled */
+		nc->nc_modes[NCSI_MODE_TX_ENABLE].ncm_enable = 1;
+	if (ntohl(rsp->valid_modes) & 0x8)	/* MC filter mode */
+		nc->nc_modes[NCSI_MODE_MC].ncm_enable = 1;
+
+	/* Modes without explicit enabled indications */
+	nc->nc_modes[NCSI_MODE_LINK].ncm_enable = 1;
+	nc->nc_modes[NCSI_MODE_LINK].ncm_data[0] = ntohl(rsp->link_mode);
+	nc->nc_modes[NCSI_MODE_VLAN].ncm_enable = 1;
+	nc->nc_modes[NCSI_MODE_VLAN].ncm_data[0] = rsp->vlan_mode;
+	nc->nc_modes[NCSI_MODE_FC].ncm_enable = 1;
+	nc->nc_modes[NCSI_MODE_FC].ncm_data[0] = rsp->fc_mode;
+	nc->nc_modes[NCSI_MODE_AEN].ncm_enable = 1;
+	nc->nc_modes[NCSI_MODE_AEN].ncm_data[0] = ntohl(rsp->aen_mode);
+
+	/* MAC addresses filter table */
+	pdata = (unsigned char *)rsp + 48;
+	enable = rsp->mac_enable;
+	for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
+		if (i >= (nc->nc_filters[NCSI_FILTER_UC]->ncf_total +
+			  nc->nc_filters[NCSI_FILTER_MC]->ncf_total))
+			table = NCSI_FILTER_MIXED;
+		else if (i >= nc->nc_filters[NCSI_FILTER_UC]->ncf_total)
+			table = NCSI_FILTER_MC;
+		else
+			table = NCSI_FILTER_UC;
+
+		if (!(enable & (0x1 << i)))
+			continue;
+
+		if (ncsi_find_channel_filter(nc, table, pdata) >= 0)
+			continue;
+
+		ncsi_add_channel_filter(nc, table, pdata);
+	}
+
+	/* VLAN filter table */
+	enable = ntohs(rsp->vlan_enable);
+	for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
+		if (!(enable & (0x1 << i)))
+			continue;
+
+		vlan = ntohs(*(__be16 *)pdata);
+		if (ncsi_find_channel_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
+			continue;
+
+		ncsi_add_channel_filter(nc, NCSI_FILTER_VLAN, &vlan);
+	}
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gcps(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gcps_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_stats *ncs;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 172);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gcps_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update HNC's statistics */
+	ncs = &nc->nc_stats;
+	ncs->ncs_hnc_cnt_hi         = ntohl(rsp->cnt_hi);
+	ncs->ncs_hnc_cnt_lo         = ntohl(rsp->cnt_lo);
+	ncs->ncs_hnc_rx_bytes       = ntohl(rsp->rx_bytes);
+	ncs->ncs_hnc_tx_bytes       = ntohl(rsp->tx_bytes);
+	ncs->ncs_hnc_rx_uc_pkts     = ntohl(rsp->rx_uc_pkts);
+	ncs->ncs_hnc_rx_mc_pkts     = ntohl(rsp->rx_mc_pkts);
+	ncs->ncs_hnc_rx_bc_pkts     = ntohl(rsp->rx_bc_pkts);
+	ncs->ncs_hnc_tx_uc_pkts     = ntohl(rsp->tx_uc_pkts);
+	ncs->ncs_hnc_tx_mc_pkts     = ntohl(rsp->tx_mc_pkts);
+	ncs->ncs_hnc_tx_bc_pkts     = ntohl(rsp->tx_bc_pkts);
+	ncs->ncs_hnc_fcs_err        = ntohl(rsp->fcs_err);
+	ncs->ncs_hnc_align_err      = ntohl(rsp->align_err);
+	ncs->ncs_hnc_false_carrier  = ntohl(rsp->false_carrier);
+	ncs->ncs_hnc_runt_pkts      = ntohl(rsp->runt_pkts);
+	ncs->ncs_hnc_jabber_pkts    = ntohl(rsp->jabber_pkts);
+	ncs->ncs_hnc_rx_pause_xon   = ntohl(rsp->rx_pause_xon);
+	ncs->ncs_hnc_rx_pause_xoff  = ntohl(rsp->rx_pause_xoff);
+	ncs->ncs_hnc_tx_pause_xon   = ntohl(rsp->tx_pause_xon);
+	ncs->ncs_hnc_tx_pause_xoff  = ntohl(rsp->tx_pause_xoff);
+	ncs->ncs_hnc_tx_s_collision = ntohl(rsp->tx_s_collision);
+	ncs->ncs_hnc_tx_m_collision = ntohl(rsp->tx_m_collision);
+	ncs->ncs_hnc_l_collision    = ntohl(rsp->l_collision);
+	ncs->ncs_hnc_e_collision    = ntohl(rsp->e_collision);
+	ncs->ncs_hnc_rx_ctl_frames  = ntohl(rsp->rx_ctl_frames);
+	ncs->ncs_hnc_rx_64_frames   = ntohl(rsp->rx_64_frames);
+	ncs->ncs_hnc_rx_127_frames  = ntohl(rsp->rx_127_frames);
+	ncs->ncs_hnc_rx_255_frames  = ntohl(rsp->rx_255_frames);
+	ncs->ncs_hnc_rx_511_frames  = ntohl(rsp->rx_511_frames);
+	ncs->ncs_hnc_rx_1023_frames = ntohl(rsp->rx_1023_frames);
+	ncs->ncs_hnc_rx_1522_frames = ntohl(rsp->rx_1522_frames);
+	ncs->ncs_hnc_rx_9022_frames = ntohl(rsp->rx_9022_frames);
+	ncs->ncs_hnc_tx_64_frames   = ntohl(rsp->tx_64_frames);
+	ncs->ncs_hnc_tx_127_frames  = ntohl(rsp->tx_127_frames);
+	ncs->ncs_hnc_tx_255_frames  = ntohl(rsp->tx_255_frames);
+	ncs->ncs_hnc_tx_511_frames  = ntohl(rsp->tx_511_frames);
+	ncs->ncs_hnc_tx_1023_frames = ntohl(rsp->tx_1023_frames);
+	ncs->ncs_hnc_tx_1522_frames = ntohl(rsp->tx_1522_frames);
+	ncs->ncs_hnc_tx_9022_frames = ntohl(rsp->tx_9022_frames);
+	ncs->ncs_hnc_rx_valid_bytes = ntohl(rsp->rx_valid_bytes);
+	ncs->ncs_hnc_rx_runt_pkts   = ntohl(rsp->rx_runt_pkts);
+	ncs->ncs_hnc_rx_jabber_pkts = ntohl(rsp->rx_jabber_pkts);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gns(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gns_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_stats *ncs;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 172);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gns_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update HNC's statistics */
+	ncs = &nc->nc_stats;
+	ncs->ncs_ncsi_rx_cmds       = ntohl(rsp->rx_cmds);
+	ncs->ncs_ncsi_dropped_cmds  = ntohl(rsp->dropped_cmds);
+	ncs->ncs_ncsi_cmd_type_errs = ntohl(rsp->cmd_type_errs);
+	ncs->ncs_ncsi_cmd_csum_errs = ntohl(rsp->cmd_csum_errs);
+	ncs->ncs_ncsi_rx_pkts       = ntohl(rsp->rx_pkts);
+	ncs->ncs_ncsi_tx_pkts       = ntohl(rsp->tx_pkts);
+	ncs->ncs_ncsi_tx_aen_pkts   = ntohl(rsp->tx_aen_pkts);
+
+	return 0;
+}
+
+static int ncsi_rsp_handler_gnpts(struct ncsi_req *nr)
+{
+	struct ncsi_rsp_gnpts_pkt *rsp;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_stats *ncs;
+	int ret;
+
+	ret = ncsi_validate_rsp_pkt(nr, 172);
+	if (ret)
+		return ret;
+
+	/* Find the channel */
+	rsp = (struct ncsi_rsp_gnpts_pkt *)skb_network_header(nr->nr_rsp);
+	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+				      NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update HNC's statistics */
+	ncs = &nc->nc_stats;
+	ncs->ncs_pt_tx_pkts        = ntohl(rsp->tx_pkts);
+	ncs->ncs_pt_tx_dropped     = ntohl(rsp->tx_dropped);
+	ncs->ncs_pt_tx_channel_err = ntohl(rsp->tx_channel_err);
+	ncs->ncs_pt_tx_us_err      = ntohl(rsp->tx_us_err);
+	ncs->ncs_pt_rx_pkts        = ntohl(rsp->rx_pkts);
+	ncs->ncs_pt_rx_dropped     = ntohl(rsp->rx_dropped);
+	ncs->ncs_pt_rx_channel_err = ntohl(rsp->rx_channel_err);
+	ncs->ncs_pt_rx_us_err      = ntohl(rsp->rx_us_err);
+	ncs->ncs_pt_rx_os_err      = ntohl(rsp->rx_os_err);
+
+	return 0;
+}
+
+static struct ncsi_rsp_handler {
+	unsigned char	nrh_type;
+	int		(*nrh_handler)(struct ncsi_req *nr);
+} ncsi_rsp_handlers[] = {
+	{ NCSI_PKT_RSP_CIS,   ncsi_rsp_handler_cis     },
+	{ NCSI_PKT_RSP_SP,    ncsi_rsp_handler_sp      },
+	{ NCSI_PKT_RSP_DP,    ncsi_rsp_handler_dp      },
+	{ NCSI_PKT_RSP_EC,    ncsi_rsp_handler_ec      },
+	{ NCSI_PKT_RSP_DC,    ncsi_rsp_handler_dc      },
+	{ NCSI_PKT_RSP_RC,    ncsi_rsp_handler_rc      },
+	{ NCSI_PKT_RSP_ECNT,  ncsi_rsp_handler_ecnt    },
+	{ NCSI_PKT_RSP_DCNT,  ncsi_rsp_handler_dcnt    },
+	{ NCSI_PKT_RSP_AE,    ncsi_rsp_handler_ae      },
+	{ NCSI_PKT_RSP_SL,    ncsi_rsp_handler_sl      },
+	{ NCSI_PKT_RSP_GLS,   ncsi_rsp_handler_gls     },
+	{ NCSI_PKT_RSP_SVF,   ncsi_rsp_handler_svf     },
+	{ NCSI_PKT_RSP_EV,    ncsi_rsp_handler_ev      },
+	{ NCSI_PKT_RSP_DV,    ncsi_rsp_handler_dv      },
+	{ NCSI_PKT_RSP_SMA,   ncsi_rsp_handler_sma     },
+	{ NCSI_PKT_RSP_EBF,   ncsi_rsp_handler_ebf     },
+	{ NCSI_PKT_RSP_DBF,   ncsi_rsp_handler_dbf     },
+	{ NCSI_PKT_RSP_EGMF,  ncsi_rsp_handler_egmf    },
+	{ NCSI_PKT_RSP_DGMF,  ncsi_rsp_handler_dgmf    },
+	{ NCSI_PKT_RSP_SNFC,  ncsi_rsp_handler_snfc    },
+	{ NCSI_PKT_RSP_GVI,   ncsi_rsp_handler_gvi     },
+	{ NCSI_PKT_RSP_GC,    ncsi_rsp_handler_gc      },
+	{ NCSI_PKT_RSP_GP,    ncsi_rsp_handler_gp      },
+	{ NCSI_PKT_RSP_GCPS,  ncsi_rsp_handler_gcps    },
+	{ NCSI_PKT_RSP_GNS,   ncsi_rsp_handler_gns     },
+	{ NCSI_PKT_RSP_GNPTS, ncsi_rsp_handler_gnpts   },
+	{ NCSI_PKT_RSP_OEM,   ncsi_rsp_handler_default },
+	{ 0,                  NULL                     }
+};
+
+#if 0
+static void ncsi_pkt_dump(struct sk_buff *skb)
+{
+	struct skb_shared_info *info = skb_shinfo(skb);
+	skb_frag_t *frag;
+	char *data;
+	int limit, i;
+
+	pr_info("head: 0x%p data: 0x%p tail: 0x%p end: 0x%p\n",
+		skb->head, skb->data,
+		skb_tail_pointer(skb), skb_end_pointer(skb));
+	pr_info("mac_header: 0x%p network_header: 0x%p\n",
+		skb_mac_header(skb), skb_network_header(skb));
+	pr_info("len: 0x%x data_len: 0x%x truesize: 0x%x\n",
+		skb->len, skb->data_len, skb->truesize);
+
+	for (i = 0; i < info->nr_frags; i++) {
+		frag = &info->frags[i];
+		pr_info("FRAG[%d]: 0x%p offset: 0x%x size: 0x%x\n",
+			i, frag->page.p, frag->page_offset, frag->size);
+	}
+
+	data = skb_mac_header(skb);
+	limit = skb->len + sizeof(struct ethhdr);
+	for (i = 0; i < limit; data++, i++) {
+		if (i % 16 == 0)
+			printk("\n%02x ", *data);
+		else
+			printk("%02x ", *data);
+	}
+}
+#endif
+
+int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
+		 struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct ncsi_rsp_handler *nrh = NULL;
+	struct ncsi_dev *nd;
+	struct ncsi_dev_priv *ndp;
+	struct ncsi_req *nr;
+	struct ncsi_pkt_hdr *hdr;
+	unsigned long flags;
+	int ret;
+
+	/* Find the NCSI device */
+	nd = ncsi_find_dev(dev);
+	ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
+	if (!ndp)
+		return -ENODEV;
+
+	/* Find the handler */
+	hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
+	nrh = ncsi_rsp_handlers;
+	while (nrh->nrh_handler) {
+		if (nrh->nrh_type == hdr->type)
+			break;
+
+		nrh++;
+	}
+
+	if (!nrh->nrh_handler) {
+		pr_warn("NCSI: Received unrecognized packet (0x%x)\n",
+			hdr->type);
+		return -ENOENT;
+	}
+
+	/* Associate with the request */
+	nr = &ndp->ndp_reqs[hdr->id];
+	spin_lock_irqsave(&ndp->ndp_req_lock, flags);
+	if (!nr->nr_used) {
+		spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+		return -ENODEV;
+	}
+
+	nr->nr_rsp = skb;
+	if (!nr->nr_timer_enabled) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	/* Process the response */
+	ret = nrh->nrh_handler(nr);
+
+out:
+	spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
+	ncsi_free_req(nr, true, false);
+	return ret;
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 04/10] net/ncsi: Package and channel management
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (2 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 03/10] net/ncsi: NCSI response " Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

The available NCSI packages and channels are probed with help of
NCSI command/response packets. One package and channel should be
selected as active one by which the MC (Management Controller)
can communicate with the far end and external network. The currently
active channel should be disabled and another available channel should
be selected as active one when AEN (Asychronous Event Notification)
packets received.

This introduces state machine and corresponding functions for above
purposes. ncsi_start_dev() should be called after ncsi_register_dev()
in network device driver. It probes all available NCSI packages and
channels and selects the active one to provide service. ncsi_config_dev()
and ncsi_suspend_dev() are going to be used when AEN (Asychronous
Event Notification) packets are received.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 net/ncsi/internal.h    |  34 ++++
 net/ncsi/ncsi-manage.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++
 net/ncsi/ncsi-rsp.c    |   6 +
 3 files changed, 515 insertions(+)

diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index d435f91..eeb092a 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -203,9 +203,38 @@ struct ncsi_req {
 	bool                 nr_timer_enabled; /* Timer was started   */
 };
 
+enum {
+	ncsi_dev_state_major		= 0xff00,
+	ncsi_dev_state_minor		= 0x00ff,
+	ncsi_dev_state_probe_deselect	= 0x0201,
+	ncsi_dev_state_probe_package,
+	ncsi_dev_state_probe_channel,
+	ncsi_dev_state_probe_cis,
+	ncsi_dev_state_probe_gvi,
+	ncsi_dev_state_probe_gc,
+	ncsi_dev_state_probe_gls,
+	ncsi_dev_state_probe_dp,
+	ncsi_dev_state_config_sp	= 0x0301,
+	ncsi_dev_state_config_cis,
+	ncsi_dev_state_config_sma,
+	ncsi_dev_state_config_ebf,
+	ncsi_dev_state_config_ecnt,
+	ncsi_dev_state_config_ec,
+	ncsi_dev_state_config_ae,
+	ncsi_dev_state_config_gls,
+	ncsi_dev_state_config_done,
+	ncsi_dev_state_suspend_select	= 0x0401,
+	ncsi_dev_state_suspend_dcnt,
+	ncsi_dev_state_suspend_dc,
+	ncsi_dev_state_suspend_deselect,
+	ncsi_dev_state_suspend_done
+};
+
 struct ncsi_dev_priv {
 	struct ncsi_dev     ndp_ndev;            /* NCSI device              */
 	int                 ndp_flags;           /* NCSI device flags        */
+#define NCSI_DEV_FLAG_PROBED	0x1
+#define NCSI_DEV_FLAG_RESHUFFLE	0x2
 	struct ncsi_package *ndp_active_package; /* Active NCSI package      */
 	struct ncsi_channel *ndp_active_channel; /* Active NCSI channel      */
 	atomic_t            ndp_package_num;     /* Number of packages       */
@@ -214,6 +243,9 @@ struct ncsi_dev_priv {
 	atomic_t            ndp_last_req_idx;    /* Last used request ID     */
 	spinlock_t          ndp_req_lock;        /* Protect request table    */
 	struct ncsi_req     ndp_reqs[256];       /* Request table            */
+	atomic_t            ndp_pending_reqs;    /* Pending commands         */
+	struct work_struct  ndp_work;            /* Workqueue                */
+	struct packet_type  ndp_ptype;           /* NCSI receive handler     */
 	struct list_head    ndp_node;
 };
 
@@ -264,6 +296,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
 struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
 void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
 struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
+int ncsi_config_dev(struct ncsi_dev *nd);
+int ncsi_suspend_dev(struct ncsi_dev *nd);
 
 /* Packet handlers */
 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index dab54bc..37f1b3d 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -19,6 +19,7 @@
 #include <net/sock.h>
 
 #include "internal.h"
+#include "ncsi-pkt.h"
 
 LIST_HEAD(ncsi_dev_list);
 DEFINE_SPINLOCK(ncsi_dev_lock);
@@ -342,6 +343,9 @@ void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
 	nr->nr_used = false;
 	spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
 
+	if (check && cmd && atomic_dec_return(&ndp->ndp_pending_reqs) == 0)
+		schedule_work(&ndp->ndp_work);
+
 	/* Release command and response */
 	consume_skb(cmd);
 	consume_skb(rsp);
@@ -380,6 +384,417 @@ static void ncsi_req_timeout(unsigned long data)
 	ncsi_free_req(nr, true, true);
 }
 
+static void ncsi_dev_config(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct net_device *dev = nd->nd_dev;
+	struct ncsi_package *np = ndp->ndp_active_package;
+	struct ncsi_channel *nc = ndp->ndp_active_channel;
+	struct ncsi_cmd_arg nca;
+	unsigned char index;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	switch (nd->nd_state) {
+	case ncsi_dev_state_config:
+	case ncsi_dev_state_config_sp:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Select the specific package */
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_package = np->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_config_cis;
+		break;
+	case ncsi_dev_state_config_cis:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Clear initial state */
+		nca.nca_type = NCSI_PKT_CMD_CIS;
+		nca.nca_package = np->np_id;
+		nca.nca_channel = nc->nc_id;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_config_sma;
+		break;
+	case ncsi_dev_state_config_sma:
+	case ncsi_dev_state_config_ebf:
+	case ncsi_dev_state_config_ecnt:
+	case ncsi_dev_state_config_ec:
+	case ncsi_dev_state_config_ae:
+	case ncsi_dev_state_config_gls:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		nca.nca_package = np->np_id;
+		nca.nca_channel = nc->nc_id;
+
+		/* Use first entry in unicast filter table. Note that
+		 * the MAC filter table starts from entry 1 instead of
+		 * 0.
+		 */
+		if (nd->nd_state == ncsi_dev_state_config_sma) {
+			nca.nca_type = NCSI_PKT_CMD_SMA;
+			for (index = 0; index < 6; index++)
+				nca.nca_bytes[index] = dev->dev_addr[index];
+			nca.nca_bytes[6] = 0x1;
+			nca.nca_bytes[7] = 0x1;
+			nd->nd_state = ncsi_dev_state_config_ebf;
+		} else if (nd->nd_state == ncsi_dev_state_config_ebf) {
+			nca.nca_type = NCSI_PKT_CMD_EBF;
+			nca.nca_dwords[0] = nc->nc_caps[NCSI_CAP_BC].ncc_cap;
+			nd->nd_state = ncsi_dev_state_config_ecnt;
+		} else if (nd->nd_state == ncsi_dev_state_config_ecnt) {
+			nca.nca_type = NCSI_PKT_CMD_ECNT;
+			nd->nd_state = ncsi_dev_state_config_ec;
+		} else if (nd->nd_state == ncsi_dev_state_config_ec) {
+			nca.nca_type = NCSI_PKT_CMD_EC;
+			nd->nd_state = ncsi_dev_state_config_ae;
+		} else if (nd->nd_state == ncsi_dev_state_config_ae) {
+			nca.nca_type = NCSI_PKT_CMD_AE;
+			nca.nca_bytes[0] = 0;
+			nca.nca_dwords[1] = 0x7;
+			nd->nd_state = ncsi_dev_state_config_gls;
+		} else if (nd->nd_state == ncsi_dev_state_config_gls) {
+			nca.nca_type = NCSI_PKT_CMD_GLS;
+			nd->nd_state = ncsi_dev_state_config_done;
+		}
+
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+		break;
+	case ncsi_dev_state_config_done:
+		nd->nd_state = ncsi_dev_state_functional;
+		nd->nd_link_up = 0;
+		if (nc->nc_modes[NCSI_MODE_LINK].ncm_data[2] & 0x1)
+			nd->nd_link_up = 1;
+
+		nd->nd_handler(nd);
+		ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
+
+		break;
+	default:
+		pr_warn("NCSI: Wrong NCSI dev state 0x%x in config\n",
+			nd->nd_state);
+	}
+
+	return;
+
+error:
+	nd->nd_state = ncsi_dev_state_functional;
+	nd->nd_link_up = 0;
+	ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
+	nd->nd_handler(nd);
+}
+
+static void ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+
+	nd = &ndp->ndp_ndev;
+	ndp->ndp_active_package = NULL;
+	ndp->ndp_active_channel = NULL;
+	NCSI_FOR_EACH_PACKAGE(ndp, np) {
+		NCSI_FOR_EACH_CHANNEL(np, nc) {
+			if (!ndp->ndp_active_channel) {
+				ndp->ndp_active_package = np;
+				ndp->ndp_active_channel = nc;
+			}
+
+			ncm = &nc->nc_modes[NCSI_MODE_LINK];
+			if (ncm->ncm_data[2] & 0x1) {
+				ndp->ndp_active_package = np;
+				ndp->ndp_active_channel = nc;
+				goto configure;
+			}
+		}
+	}
+
+configure:
+	if (!ndp->ndp_active_channel) {
+		nd->nd_state = ncsi_dev_state_functional;
+		nd->nd_link_up = 0;
+		nd->nd_handler(nd);
+	} else {
+		nd->nd_state = ncsi_dev_state_config;
+		ncsi_dev_config(ndp);
+	}
+}
+
+static void ncsi_dev_probe(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	struct ncsi_cmd_arg nca;
+	unsigned char index;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	switch (nd->nd_state) {
+	case ncsi_dev_state_probe:
+		nd->nd_state = ncsi_dev_state_probe_deselect;
+		/* Fall through */
+	case ncsi_dev_state_probe_deselect:
+		atomic_set(&ndp->ndp_pending_reqs, 8);
+
+		/* Deselect all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		nca.nca_channel = 0x1f;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_probe_package;
+		break;
+	case ncsi_dev_state_probe_package:
+		atomic_set(&ndp->ndp_pending_reqs, 16);
+
+		/* Select all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_channel = 0x1f;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		/* Disable all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_probe_channel;
+		break;
+	case ncsi_dev_state_probe_channel:
+		if (!ndp->ndp_active_package)
+			ndp->ndp_active_package = list_first_or_null_rcu(
+							&ndp->ndp_packages,
+							struct ncsi_package,
+							np_node);
+		else if (list_is_last(&ndp->ndp_active_package->np_node,
+				      &ndp->ndp_packages))
+			ndp->ndp_active_package = NULL;
+		else
+			ndp->ndp_active_package = list_next_entry(
+							ndp->ndp_active_package,
+							np_node);
+
+		/* All available packages and channels are enumerated. The
+		 * enumeration happens for once when the NCSI interface is
+		 * started. So we need continue to start the interface after
+		 * the enumeration.
+		 *
+		 * We have to choose an active channel before configuring it.
+		 * Note that we possibly don't have active channel in extreme
+		 * situation.
+		 */
+		if (!ndp->ndp_active_package) {
+			ndp->ndp_flags |= NCSI_DEV_FLAG_PROBED;
+			ncsi_choose_active_channel(ndp);
+			return;
+		}
+
+		/* Select the active package */
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_probe_cis;
+		break;
+	case ncsi_dev_state_probe_cis:
+		atomic_set(&ndp->ndp_pending_reqs, 0x20);
+
+		/* Clear initial state */
+		nca.nca_type = NCSI_PKT_CMD_CIS;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		for (index = 0; index < 0x20; index++) {
+			nca.nca_channel = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_probe_gvi;
+		break;
+	case ncsi_dev_state_probe_gvi:
+	case ncsi_dev_state_probe_gc:
+	case ncsi_dev_state_probe_gls:
+		np = ndp->ndp_active_package;
+		atomic_set(&ndp->ndp_pending_reqs,
+			   atomic_read(&np->np_channel_num));
+
+		/* Retrieve version, capability or link status */
+		if (nd->nd_state == ncsi_dev_state_probe_gvi)
+			nca.nca_type = NCSI_PKT_CMD_GVI;
+		else if (nd->nd_state == ncsi_dev_state_probe_gc)
+			nca.nca_type = NCSI_PKT_CMD_GC;
+		else
+			nca.nca_type = NCSI_PKT_CMD_GLS;
+
+		nca.nca_package = np->np_id;
+		NCSI_FOR_EACH_CHANNEL(np, nc) {
+			nca.nca_channel = nc->nc_id;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		if (nd->nd_state == ncsi_dev_state_probe_gvi)
+			nd->nd_state = ncsi_dev_state_probe_gc;
+		else if (nd->nd_state == ncsi_dev_state_probe_gc)
+			nd->nd_state = ncsi_dev_state_probe_gls;
+		else
+			nd->nd_state = ncsi_dev_state_probe_dp;
+		break;
+	case ncsi_dev_state_probe_dp:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Deselect the active package */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		/* Scan channels in next package */
+		nd->nd_state = ncsi_dev_state_probe_channel;
+		break;
+	default:
+		pr_warn("NCSI: Wrong NCSI dev state 0x%0x in enumeration\n",
+			nd->nd_state);
+	}
+
+	return;
+error:
+	nd->nd_state = ncsi_dev_state_functional;
+	nd->nd_link_up = 0;
+	nd->nd_handler(nd);
+}
+
+static void ncsi_dev_suspend(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	struct ncsi_cmd_arg nca;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	switch (nd->nd_state) {
+	case ncsi_dev_state_suspend:
+		/* If there're no active channel, we're done */
+		if (!ndp->ndp_active_channel) {
+			nd->nd_state = ncsi_dev_state_functional;
+
+			if (ndp->ndp_flags & NCSI_DEV_FLAG_RESHUFFLE) {
+				ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
+				ncsi_choose_active_channel(ndp);
+			}
+
+			return;
+		}
+
+		nd->nd_state = ncsi_dev_state_suspend_select;
+		/* Fall through */
+	case ncsi_dev_state_suspend_select:
+	case ncsi_dev_state_suspend_dcnt:
+	case ncsi_dev_state_suspend_dc:
+	case ncsi_dev_state_suspend_deselect:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		np = ndp->ndp_active_package;
+		nc = ndp->ndp_active_channel;
+		nca.nca_package = np->np_id;
+		if (nd->nd_state == ncsi_dev_state_suspend_select) {
+			nca.nca_type = NCSI_PKT_CMD_SP;
+			nca.nca_channel = 0x1f;
+			nca.nca_bytes[0] = 1;
+			nd->nd_state = ncsi_dev_state_suspend_dcnt;
+		} else if (nd->nd_state == ncsi_dev_state_suspend_dcnt) {
+			nca.nca_type = NCSI_PKT_CMD_DCNT;
+			nca.nca_channel = nc->nc_id;
+			nd->nd_state = ncsi_dev_state_suspend_dc;
+		} else if (nd->nd_state == ncsi_dev_state_suspend_dc) {
+			nca.nca_type = NCSI_PKT_CMD_DC;
+			nca.nca_channel = nc->nc_id;
+			nca.nca_bytes[0] = 1;
+			nd->nd_state = ncsi_dev_state_suspend_deselect;
+		} else if (nd->nd_state == ncsi_dev_state_suspend_deselect) {
+			nca.nca_type = NCSI_PKT_CMD_DP;
+			nca.nca_channel = 0x1f;
+			nd->nd_state = ncsi_dev_state_suspend_done;
+		}
+
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret) {
+			nd->nd_state = ncsi_dev_state_functional;
+			return;
+		}
+
+		break;
+	case ncsi_dev_state_suspend_done:
+		nd->nd_state = ncsi_dev_state_functional;
+
+		if (ndp->ndp_flags & NCSI_DEV_FLAG_RESHUFFLE) {
+			ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
+			ncsi_choose_active_channel(ndp);
+		}
+
+		break;
+	default:
+		pr_warn("NCSI: Wrong NCSI dev state 0x%x in suspend\n",
+			nd->nd_state);
+	}
+}
+
+static void ncsi_dev_work(struct work_struct *work)
+{
+	struct ncsi_dev_priv *ndp = container_of(work, struct ncsi_dev_priv,
+						 ndp_work);
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+
+	switch (nd->nd_state & ncsi_dev_state_major) {
+	case ncsi_dev_state_probe:
+		ncsi_dev_probe(ndp);
+		break;
+	case ncsi_dev_state_suspend:
+		ncsi_dev_suspend(ndp);
+		break;
+	case ncsi_dev_state_config:
+		ncsi_dev_config(ndp);
+		break;
+	default:
+		pr_warn("NCSI: Wrong NCSI dev state 0x%x in workqueue\n",
+			nd->nd_state);
+	}
+}
+
 struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
 				   void (*handler)(struct ncsi_dev *ndev))
 {
@@ -414,19 +829,79 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
 			    (unsigned long)&ndp->ndp_reqs[idx]);
 	}
 
+	atomic_set(&ndp->ndp_pending_reqs, 0);
+	INIT_WORK(&ndp->ndp_work, ncsi_dev_work);
+
 	spin_lock(&ncsi_dev_lock);
 	list_add_tail_rcu(&ndp->ndp_node, &ncsi_dev_list);
 	spin_unlock(&ncsi_dev_lock);
 
+	/* Register NCSI packet Rx handler */
+	ndp->ndp_ptype.type = cpu_to_be16(ETH_P_NCSI);
+	ndp->ndp_ptype.func = ncsi_rcv_rsp;
+	ndp->ndp_ptype.dev = dev;
+	dev_add_pack(&ndp->ndp_ptype);
+
 	return nd;
 }
 EXPORT_SYMBOL_GPL(ncsi_register_dev);
 
+int ncsi_start_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_registered &&
+	    nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	if (!(ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)) {
+		nd->nd_state = ncsi_dev_state_probe;
+		schedule_work(&ndp->ndp_work);
+		return 0;
+	}
+
+	/* Choose active package and channel */
+	ncsi_choose_active_channel(ndp);
+	if (!ndp->ndp_active_channel)
+		return -ENXIO;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ncsi_start_dev);
+
+int ncsi_config_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	nd->nd_state = ncsi_dev_state_config;
+	schedule_work(&ndp->ndp_work);
+
+	return 0;
+}
+
+int ncsi_suspend_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	nd->nd_state = ncsi_dev_state_suspend;
+	schedule_work(&ndp->ndp_work);
+
+	return 0;
+}
+
 void ncsi_unregister_dev(struct ncsi_dev *nd)
 {
 	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
 	struct ncsi_package *np, *tmp;
 
+	dev_remove_pack(&ndp->ndp_ptype);
+
 	spin_lock(&ndp->ndp_package_lock);
 	list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
 		ncsi_release_package(np);
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index 257079fc..92dcbf4 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -88,6 +88,9 @@ static int ncsi_rsp_handler_cis(struct ncsi_req *nr)
 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
 	if (!nc) {
+		if (ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)
+			return -ENXIO;
+
 		id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
 		nc = ncsi_add_channel(np, id);
 	} else if (nc->nc_state == ncsi_channel_state_deselected_initial) {
@@ -119,6 +122,9 @@ static int ncsi_rsp_handler_sp(struct ncsi_req *nr)
 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
 				      &np, NULL);
 	if (!np) {
+		if (ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)
+			return -ENXIO;
+
 		id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
 		np = ncsi_add_package(ndp, id);
 		if (!np)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 05/10] net/ncsi: NCSI AEN packet handler
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (3 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 04/10] net/ncsi: Package and channel management Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface Gavin Shan
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

This introduces NCSI AEN packet handlers that result in (A) the
currently active channel is reconfigured; (B) Currently active
channel is deconfigured and disabled, another channel is chosen
as active one and configured.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 net/ncsi/Makefile   |   2 +-
 net/ncsi/internal.h |   1 +
 net/ncsi/ncsi-aen.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 net/ncsi/ncsi-pkt.h |  36 ++++++++++
 net/ncsi/ncsi-rsp.c |   6 +-
 5 files changed, 233 insertions(+), 2 deletions(-)
 create mode 100644 net/ncsi/ncsi-aen.c

diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index 4751819..dd12b56 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
 #
 # Makefile for NCSI API
 #
-obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index eeb092a..d284821 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -303,5 +303,6 @@ int ncsi_suspend_dev(struct ncsi_dev *nd);
 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
 int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
 		 struct packet_type *pt, struct net_device *orig_dev);
+int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb);
 
 #endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
new file mode 100644
index 0000000..82ed563
--- /dev/null
+++ b/net/ncsi/ncsi-aen.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
+				 const unsigned short payload)
+{
+	unsigned char *stream;
+	__be32 *checksum, csum;
+	__be32 high, low;
+	int i;
+
+	if (h->common.revision != NCSI_PKT_REVISION)
+		return -EINVAL;
+	if (ntohs(h->common.length) != payload)
+		return -EINVAL;
+
+	/* Validate checksum, which might be zeroes if the
+	 * sender doesn't support checksum according to NCSI
+	 * specification.
+	 */
+	checksum = (__be32 *)((void *)(h + 1) + payload - 4);
+	if (ntohl(*checksum) == 0)
+		return 0;
+
+	csum = 0;
+	stream = (unsigned char *)h;
+	for (i = 0; i < sizeof(*h) + payload - 4; i += 2) {
+		high = stream[i];
+		low = stream[i + 1];
+		csum += ((high << 8) | low);
+	}
+
+	csum = ~csum + 1;
+	if (*checksum != htonl(csum))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
+				struct ncsi_aen_pkt_hdr *h)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_aen_lsc_pkt *lsc;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	int ret;
+
+	ret = ncsi_validate_aen_pkt(h, 12);
+	if (ret)
+		return ret;
+
+	/* Find the NCSI channel */
+	ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* Update the link status */
+	ncm = &nc->nc_modes[NCSI_MODE_LINK];
+	lsc = (struct ncsi_aen_lsc_pkt *)h;
+	ncm->ncm_data[2] = ntohl(lsc->status);
+	ncm->ncm_data[4] = ntohl(lsc->oem_status);
+	if (!ndp->ndp_active_channel ||
+	    ndp->ndp_active_channel != nc)
+		return 0;
+
+	ndp->ndp_flags |= NCSI_DEV_FLAG_RESHUFFLE;
+	ncsi_suspend_dev(nd);
+
+	return 0;
+}
+
+static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
+			       struct ncsi_aen_pkt_hdr *h)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_channel *nc;
+	int ret;
+
+	ret = ncsi_validate_aen_pkt(h, 4);
+	if (ret)
+		return ret;
+
+	/* Find the NCSI channel */
+	ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* If the channel is active one, we need reconfigure it */
+	if (!ndp->ndp_active_channel ||
+	    ndp->ndp_active_channel != nc)
+		return 0;
+
+	ncsi_config_dev(nd);
+
+	return 0;
+}
+
+static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
+				   struct ncsi_aen_pkt_hdr *h)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_channel *nc;
+	struct ncsi_channel_mode *ncm;
+	struct ncsi_aen_hncdsc_pkt *hncdsc;
+	int ret;
+
+	ret = ncsi_validate_aen_pkt(h, 4);
+	if (ret)
+		return ret;
+
+	/* Find the NCSI channel */
+	ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+	if (!nc)
+		return -ENODEV;
+
+	/* If the channel is active one, we need reconfigure it */
+	ncm = &nc->nc_modes[NCSI_MODE_LINK];
+	hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
+	ncm->ncm_data[3] = ntohl(hncdsc->status);
+	if (ndp->ndp_active_channel != nc ||
+	    ncm->ncm_data[3] & 0x1)
+		return 0;
+
+	/* If this channel is the active one and the link doesn't
+	 * work, we have to choose another channel to be active one.
+	 * The logic here is exactly similar to what we do when link
+	 * is down on the active channel.
+	 */
+	ndp->ndp_flags |= NCSI_DEV_FLAG_RESHUFFLE;
+	ncsi_suspend_dev(nd);
+	return 0;
+}
+
+static struct ncsi_aen_handler {
+	unsigned char nah_type;
+	int           (*nah_handler)(struct ncsi_dev_priv *ndp,
+				     struct ncsi_aen_pkt_hdr *h);
+} ncsi_aen_handlers[] = {
+	{ NCSI_PKT_AEN_LSC,    ncsi_aen_handler_lsc    },
+	{ NCSI_PKT_AEN_CR,     ncsi_aen_handler_cr     },
+	{ NCSI_PKT_AEN_HNCDSC, ncsi_aen_handler_hncdsc },
+	{ 0,                   NULL                    }
+};
+
+int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
+{
+	struct ncsi_aen_pkt_hdr *h;
+	struct ncsi_aen_handler *nah;
+	int ret;
+
+	/* Find the handler */
+	h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
+	nah = ncsi_aen_handlers;
+	while (nah->nah_handler) {
+		if (nah->nah_type == h->type)
+			break;
+
+		nah++;
+	}
+
+	if (!nah->nah_handler) {
+		pr_warn("NCSI: Invalid AEN packet (0x%x) received\n",
+			h->type);
+		return -ENOENT;
+	}
+
+	ret = nah->nah_handler(ndp, h);
+	consume_skb(skb);
+	return ret;
+}
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
index 95882f3..6248835 100644
--- a/net/ncsi/ncsi-pkt.h
+++ b/net/ncsi/ncsi-pkt.h
@@ -31,6 +31,12 @@ struct ncsi_rsp_pkt_hdr {
 	__be16              reason; /* Response reason           */
 };
 
+struct ncsi_aen_pkt_hdr {
+	struct ncsi_pkt_hdr common;       /* Common NCSI packet header */
+	unsigned char       reserved2[3]; /* Reserved                  */
+	unsigned char       type;         /* AEN packet type           */
+};
+
 /* NCSI common command packet */
 struct ncsi_cmd_pkt {
 	struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
@@ -282,6 +288,30 @@ struct ncsi_rsp_gnpts_pkt {
 	__be32                  checksum;       /* Checksum            */
 };
 
+/* AEN: Link State Change */
+struct ncsi_aen_lsc_pkt {
+	struct ncsi_aen_pkt_hdr aen;        /* AEN header      */
+	__be32                  status;     /* Link status     */
+	__be32                  oem_status; /* OEM link status */
+	__be32                  checksum;   /* Checksum        */
+	unsigned char           pad[14];
+};
+
+/* AEN: Configuration Required */
+struct ncsi_aen_cr_pkt {
+	struct ncsi_aen_pkt_hdr aen;      /* AEN header */
+	__be32                  checksum; /* Checksum   */
+	unsigned char           pad[22];
+};
+
+/* AEN: Host Network Controller Driver Status Change */
+struct ncsi_aen_hncdsc_pkt {
+	struct ncsi_aen_pkt_hdr aen;      /* AEN header */
+	__be32                  status;   /* Status     */
+	__be32                  checksum; /* Checksum   */
+	unsigned char           pad[18];
+};
+
 /* NCSI packet revision */
 #define NCSI_PKT_REVISION	0x01
 
@@ -356,4 +386,10 @@ struct ncsi_rsp_gnpts_pkt {
 #define NCSI_PKT_RSP_R_LENGTH		0x0005  /* Invalid payload length   */
 #define NCSI_PKT_RSP_R_UNKNOWN		0x7fff  /* Command type unsupported */
 
+/* NCSI packet type: AEN */
+#define NCSI_PKT_AEN		0xFF /* AEN Packet               */
+#define NCSI_PKT_AEN_LSC	0x00 /* Link status change       */
+#define NCSI_PKT_AEN_CR		0x01 /* Configuration required   */
+#define NCSI_PKT_AEN_HNCDSC	0x02 /* HNC driver status change */
+
 #endif /* __NCSI_PKT_H__ */
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index 92dcbf4..30578d3 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -1125,8 +1125,12 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
 	if (!ndp)
 		return -ENODEV;
 
-	/* Find the handler */
+	/* Check if it is AEN packet */
 	hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
+	if (hdr->type == NCSI_PKT_AEN)
+		return ncsi_aen_handler(ndp, skb);
+
+	/* Find the handler */
 	nrh = ncsi_rsp_handlers;
 	while (nrh->nrh_handler) {
 		if (nrh->nrh_type == hdr->type)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (4 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 13:49   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 07/10] net/farady: Read MAC address from chip Gavin Shan
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

This introduces two helper functions to create or destroy MDIO
interface. No logical changes introduced.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 93 ++++++++++++++++++++------------
 1 file changed, 58 insertions(+), 35 deletions(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index e7cf313..2c22f74 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -1146,6 +1146,58 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
 	.ndo_do_ioctl		= ftgmac100_do_ioctl,
 };
 
+static int ftgmac100_setup_mdio(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+	int i, err = 0;
+
+	/* initialize mdio bus */
+	priv->mii_bus = mdiobus_alloc();
+	if (!priv->mii_bus)
+		return -EIO;
+
+	priv->mii_bus->name = "ftgmac100_mdio";
+	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
+	priv->mii_bus->priv = priv->netdev;
+	priv->mii_bus->read = ftgmac100_mdiobus_read;
+	priv->mii_bus->write = ftgmac100_mdiobus_write;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		priv->mii_bus->irq[i] = PHY_POLL;
+
+	err = mdiobus_register(priv->mii_bus);
+	if (err) {
+		dev_err(priv->dev, "Cannot register MDIO bus!\n");
+		goto err_register_mdiobus;
+	}
+
+	err = ftgmac100_mii_probe(priv);
+	if (err) {
+		dev_err(priv->dev, "MII Probe failed!\n");
+		goto err_mii_probe;
+	}
+
+	return 0;
+
+err_mii_probe:
+	mdiobus_unregister(priv->mii_bus);
+err_register_mdiobus:
+	mdiobus_free(priv->mii_bus);
+	return err;
+}
+
+static void ftgmac100_destroy_mdio(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	if (!netdev->phydev)
+		return;
+
+	phy_disconnect(netdev->phydev);
+	mdiobus_unregister(priv->mii_bus);
+	mdiobus_free(priv->mii_bus);
+}
+
 /******************************************************************************
  * struct platform_driver functions
  *****************************************************************************/
@@ -1211,31 +1263,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
 	priv->irq = irq;
 
-	/* initialize mdio bus */
-	priv->mii_bus = mdiobus_alloc();
-	if (!priv->mii_bus) {
-		err = -EIO;
-		goto err_alloc_mdiobus;
-	}
-
-	priv->mii_bus->name = "ftgmac100_mdio";
-	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
-
-	priv->mii_bus->priv = netdev;
-	priv->mii_bus->read = ftgmac100_mdiobus_read;
-	priv->mii_bus->write = ftgmac100_mdiobus_write;
-
-	err = mdiobus_register(priv->mii_bus);
-	if (err) {
-		dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
-		goto err_register_mdiobus;
-	}
-
-	err = ftgmac100_mii_probe(priv);
-	if (err) {
-		dev_err(&pdev->dev, "MII Probe failed!\n");
-		goto err_mii_probe;
-	}
+	err = ftgmac100_setup_mdio(netdev);
+	if (err)
+		goto err_setup_mdio;
 
 	/* register network device */
 	err = register_netdev(netdev);
@@ -1255,12 +1285,8 @@ static int ftgmac100_probe(struct platform_device *pdev)
 	return 0;
 
 err_register_netdev:
-	phy_disconnect(netdev->phydev);
-err_mii_probe:
-	mdiobus_unregister(priv->mii_bus);
-err_register_mdiobus:
-	mdiobus_free(priv->mii_bus);
-err_alloc_mdiobus:
+	ftgmac100_destroy_mdio(netdev);
+err_setup_mdio:
 	iounmap(priv->base);
 err_ioremap:
 	release_resource(priv->res);
@@ -1280,10 +1306,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
 	priv = netdev_priv(netdev);
 
 	unregister_netdev(netdev);
-
-	phy_disconnect(netdev->phydev);
-	mdiobus_unregister(priv->mii_bus);
-	mdiobus_free(priv->mii_bus);
+	ftgmac100_destroy_mdio(netdev);
 
 	iounmap(priv->base);
 	release_resource(priv->res);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 07/10] net/farady: Read MAC address from chip
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (5 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 08/10] net/farady: Support NCSI mode Gavin Shan
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

The device is assigned with random MAC address. It isn't reasonable.
An valid MAC address might have been in the chip. It's reasonable
to use it to maintain consistency.

This uses the MAC address in the chip if it's valid. Otherwise, a
random MAC address is given as before.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 60 ++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 2c22f74..120993e 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -141,6 +141,55 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
 	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
 }
 
+static void ftgmac100_setup_mac(struct ftgmac100 *priv)
+{
+	unsigned char mac[6];
+	unsigned int m;
+	unsigned int l;
+
+	m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
+	l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
+
+	mac[0] = (m >> 8) & 0xff;
+	mac[1] = m & 0xff;
+	mac[2] = (l >> 24) & 0xff;
+	mac[3] = (l >> 16) & 0xff;
+	mac[4] = (l >> 8) & 0xff;
+	mac[5] = l & 0xff;
+
+	if (!is_valid_ether_addr(mac)) {
+		mac[5] = (m >> 8) & 0xff;
+		mac[4] = m & 0xff;
+		mac[3] = (l >> 24) & 0xff;
+		mac[2] = (l >> 16) & 0xff;
+		mac[1] = (l >>  8) & 0xff;
+		mac[0] = l & 0xff;
+	}
+
+	if (!is_valid_ether_addr(mac)) {
+		eth_hw_addr_random(priv->netdev);
+		dev_info(priv->dev, "Generated random MAC address %pM\n",
+			 priv->netdev->dev_addr);
+	} else {
+		memcpy(priv->netdev->dev_addr, mac, 6);
+		dev_info(priv->dev, "Read MAC address from chip %pM\n", mac);
+	}
+}
+
+static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
+{
+	int ret;
+
+	ret = eth_prepare_mac_addr_change(dev, p);
+	if (ret < 0)
+		return ret;
+
+	eth_commit_mac_addr_change(dev, p);
+	ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
+
+	return 0;
+}
+
 static void ftgmac100_init_hw(struct ftgmac100 *priv)
 {
 	/* setup ring buffer base registers */
@@ -1141,7 +1190,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
 	.ndo_open		= ftgmac100_open,
 	.ndo_stop		= ftgmac100_stop,
 	.ndo_start_xmit		= ftgmac100_hard_start_xmit,
-	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_set_mac_address	= ftgmac100_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_do_ioctl		= ftgmac100_do_ioctl,
 };
@@ -1263,6 +1312,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
 	priv->irq = irq;
 
+	/* MAC address from chip or random one */
+	ftgmac100_setup_mac(priv);
+
 	err = ftgmac100_setup_mdio(netdev);
 	if (err)
 		goto err_setup_mdio;
@@ -1276,12 +1328,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
 	netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
 
-	if (!is_valid_ether_addr(netdev->dev_addr)) {
-		eth_hw_addr_random(netdev);
-		netdev_info(netdev, "generated random MAC address %pM\n",
-			    netdev->dev_addr);
-	}
-
 	return 0;
 
 err_register_netdev:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 08/10] net/farady: Support NCSI mode
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (6 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 07/10] net/farady: Read MAC address from chip Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:05   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 09/10] net/farady: Match driver according to compatible property Gavin Shan
  2016-06-30 10:27 ` [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode Gavin Shan
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

This makes ftgmac100 driver support NCSI mode. The NCSI is enabled
on the interface if property "use-nc-si" or "use-ncsi" is found from
the device node in device tree.

   * No PHY device is used when NCSI mode is enabled.
   * The NCSI device (struct ncsi_dev) is created when probing the
     device while it's enabled/started when the interface is brought
     up.
   * Hardware IP checksum dosn't work when NCSI mode is enabled. It
     is disabled on enabled NCSI.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 78 ++++++++++++++++++++++++++++----
 1 file changed, 69 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 120993e..f2205ae 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -31,6 +31,7 @@
 #include <linux/phy.h>
 #include <linux/platform_device.h>
 #include <net/ip.h>
+#include <net/ncsi.h>
 
 #include "ftgmac100.h"
 
@@ -68,10 +69,13 @@ struct ftgmac100 {
 
 	struct net_device *netdev;
 	struct device *dev;
+	struct ncsi_dev *ndev;
 	struct napi_struct napi;
 
 	struct mii_bus *mii_bus;
 	int old_speed;
+	bool use_ncsi;
+	bool enabled;
 };
 
 static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
@@ -1001,7 +1005,10 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
 	struct net_device *netdev = dev_id;
 	struct ftgmac100 *priv = netdev_priv(netdev);
 
-	if (likely(netif_running(netdev))) {
+	/* When running in NCSI mode, the interface should be ready for
+	 * receiving or transmitting NCSI packets before it's opened.
+	 */
+	if (likely(priv->use_ncsi || netif_running(netdev))) {
 		/* Disable interrupts for polling */
 		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 		napi_schedule(&priv->napi);
@@ -1114,17 +1121,33 @@ static int ftgmac100_open(struct net_device *netdev)
 		goto err_hw;
 
 	ftgmac100_init_hw(priv);
-	ftgmac100_start_hw(priv, 10);
-
-	phy_start(netdev->phydev);
+	ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
+	if (netdev->phydev)
+		phy_start(netdev->phydev);
+	else if (priv->use_ncsi)
+		netif_carrier_on(netdev);
 
 	napi_enable(&priv->napi);
 	netif_start_queue(netdev);
 
 	/* enable all interrupts */
 	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+
+	/* Start the NCSI device */
+	if (priv->use_ncsi) {
+		err = ncsi_start_dev(priv->ndev);
+		if (err)
+			goto err_ncsi;
+	}
+
+	priv->enabled = true;
+
 	return 0;
 
+err_ncsi:
+	napi_disable(&priv->napi);
+	netif_stop_queue(netdev);
+	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 err_hw:
 	free_irq(priv->irq, netdev);
 err_irq:
@@ -1137,12 +1160,17 @@ static int ftgmac100_stop(struct net_device *netdev)
 {
 	struct ftgmac100 *priv = netdev_priv(netdev);
 
+	if (!priv->enabled)
+		return 0;
+
 	/* disable all interrupts */
+	priv->enabled = false;
 	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 
 	netif_stop_queue(netdev);
 	napi_disable(&priv->napi);
-	phy_stop(netdev->phydev);
+	if (netdev->phydev)
+		phy_stop(netdev->phydev);
 
 	ftgmac100_stop_hw(priv);
 	free_irq(priv->irq, netdev);
@@ -1183,6 +1211,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
 /* optional */
 static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 {
+	if (!netdev->phydev)
+		return -ENXIO;
+
 	return phy_mii_ioctl(netdev->phydev, ifr, cmd);
 }
 
@@ -1247,6 +1278,15 @@ static void ftgmac100_destroy_mdio(struct net_device *netdev)
 	mdiobus_free(priv->mii_bus);
 }
 
+static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
+{
+	if (unlikely(nd->nd_state != ncsi_dev_state_functional))
+		return;
+
+	netdev_info(nd->nd_dev, "NCSI interface %s\n",
+		    nd->nd_link_up ? "up" : "down");
+}
+
 /******************************************************************************
  * struct platform_driver functions
  *****************************************************************************/
@@ -1256,7 +1296,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
 	int irq;
 	struct net_device *netdev;
 	struct ftgmac100 *priv;
-	int err;
+	int err = 0;
 
 	if (!pdev)
 		return -ENODEV;
@@ -1280,7 +1320,15 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
 	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
 	netdev->netdev_ops = &ftgmac100_netdev_ops;
+
+	/* We have to disable on-chip IP checksum functionality
+	 * when NCSI is enabled on the interface. It doesn't work
+	 * in that case.
+	 */
 	netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
+	if (pdev->dev.of_node &&
+	    of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
+		netdev->features &= ~NETIF_F_IP_CSUM;
 
 	platform_set_drvdata(pdev, netdev);
 
@@ -1315,9 +1363,20 @@ static int ftgmac100_probe(struct platform_device *pdev)
 	/* MAC address from chip or random one */
 	ftgmac100_setup_mac(priv);
 
-	err = ftgmac100_setup_mdio(netdev);
-	if (err)
-		goto err_setup_mdio;
+	if (pdev->dev.of_node &&
+	    (of_get_property(pdev->dev.of_node, "use-nc-si", NULL) ||
+	    of_get_property(pdev->dev.of_node, "use-ncsi", NULL))) {
+		dev_info(&pdev->dev, "Using NCSI interface\n");
+		priv->use_ncsi = true;
+		priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
+		if (!priv->ndev)
+			goto err_ncsi_dev;
+	} else {
+		priv->use_ncsi = false;
+		err = ftgmac100_setup_mdio(netdev);
+		if (err)
+			goto err_setup_mdio;
+	}
 
 	/* register network device */
 	err = register_netdev(netdev);
@@ -1330,6 +1389,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_ncsi_dev:
 err_register_netdev:
 	ftgmac100_destroy_mdio(netdev);
 err_setup_mdio:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 09/10] net/farady: Match driver according to compatible property
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (7 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 08/10] net/farady: Support NCSI mode Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:06   ` Joel Stanley
  2016-06-30 10:27 ` [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode Gavin Shan
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

This matches the driver with devices compatible with "faraday,ftgmac100"
declared in the device tree. Originally, device's name from device
tree for it.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index f2205ae..23e27f1 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -1422,14 +1422,20 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id ftgmac100_of_match[] = {
+	{ .compatible = "faraday,ftgmac100" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ftgmac100_of_match);
+
 static struct platform_driver ftgmac100_driver = {
-	.probe		= ftgmac100_probe,
-	.remove		= __exit_p(ftgmac100_remove),
-	.driver		= {
-		.name	= DRV_NAME,
+	.probe	= ftgmac100_probe,
+	.remove	= ftgmac100_remove,
+	.driver	= {
+		.name		= DRV_NAME,
+		.of_match_table	= ftgmac100_of_match,
 	},
 };
-
 module_platform_driver(ftgmac100_driver);
 
 MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode
  2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
                   ` (8 preceding siblings ...)
  2016-06-30 10:27 ` [PATCH 09/10] net/farady: Match driver according to compatible property Gavin Shan
@ 2016-06-30 10:27 ` Gavin Shan
  2016-06-30 14:06   ` Joel Stanley
  9 siblings, 1 reply; 29+ messages in thread
From: Gavin Shan @ 2016-06-30 10:27 UTC (permalink / raw)
  To: openbmc; +Cc: benh, joel, gwshan

Bogus PHY interrupts are observed. This masks the PHY interrupt
when the interface works in NCSI mode as there is no attached
PHY under the circumstance.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 23e27f1..6642071 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -74,6 +74,7 @@ struct ftgmac100 {
 
 	struct mii_bus *mii_bus;
 	int old_speed;
+	int int_mask_all;
 	bool use_ncsi;
 	bool enabled;
 };
@@ -84,14 +85,6 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
 /******************************************************************************
  * internal functions (hardware register access)
  *****************************************************************************/
-#define INT_MASK_ALL_ENABLED	(FTGMAC100_INT_RPKT_LOST	| \
-				 FTGMAC100_INT_XPKT_ETH		| \
-				 FTGMAC100_INT_XPKT_LOST	| \
-				 FTGMAC100_INT_AHB_ERR		| \
-				 FTGMAC100_INT_PHYSTS_CHG	| \
-				 FTGMAC100_INT_RPKT_BUF		| \
-				 FTGMAC100_INT_NO_RXBUF)
-
 static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
 {
 	iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
@@ -1061,8 +1054,9 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
 		ftgmac100_tx_complete(priv);
 	}
 
-	if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
-		      FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
+	if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
+			FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR |
+			FTGMAC100_INT_PHYSTS_CHG)) {
 		if (net_ratelimit())
 			netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
 				    status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
@@ -1085,7 +1079,7 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
 		napi_complete(napi);
 
 		/* enable all interrupts */
-		iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+		iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
 	}
 
 	return rx;
@@ -1131,7 +1125,7 @@ static int ftgmac100_open(struct net_device *netdev)
 	netif_start_queue(netdev);
 
 	/* enable all interrupts */
-	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+	iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
 
 	/* Start the NCSI device */
 	if (priv->use_ncsi) {
@@ -1363,10 +1357,19 @@ static int ftgmac100_probe(struct platform_device *pdev)
 	/* MAC address from chip or random one */
 	ftgmac100_setup_mac(priv);
 
+	priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
+			      FTGMAC100_INT_XPKT_ETH |
+			      FTGMAC100_INT_XPKT_LOST |
+			      FTGMAC100_INT_AHB_ERR |
+			      FTGMAC100_INT_PHYSTS_CHG |
+			      FTGMAC100_INT_RPKT_BUF |
+			      FTGMAC100_INT_NO_RXBUF);
 	if (pdev->dev.of_node &&
 	    (of_get_property(pdev->dev.of_node, "use-nc-si", NULL) ||
 	    of_get_property(pdev->dev.of_node, "use-ncsi", NULL))) {
 		dev_info(&pdev->dev, "Using NCSI interface\n");
+
+		priv->int_mask_all &= ~FTGMAC100_INT_PHYSTS_CHG;
 		priv->use_ncsi = true;
 		priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
 		if (!priv->ndev)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface
  2016-06-30 10:27 ` [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface Gavin Shan
@ 2016-06-30 13:49   ` Joel Stanley
  2016-07-01  2:59     ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 13:49 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> This introduces two helper functions to create or destroy MDIO
> interface. No logical changes introduced.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  drivers/net/ethernet/faraday/ftgmac100.c | 93 ++++++++++++++++++++------------
>  1 file changed, 58 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> index e7cf313..2c22f74 100644
> --- a/drivers/net/ethernet/faraday/ftgmac100.c
> +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> @@ -1146,6 +1146,58 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
>         .ndo_do_ioctl           = ftgmac100_do_ioctl,
>  };
>
> +static int ftgmac100_setup_mdio(struct net_device *netdev)
> +{
> +       struct ftgmac100 *priv = netdev_priv(netdev);
> +       int i, err = 0;
> +
> +       /* initialize mdio bus */
> +       priv->mii_bus = mdiobus_alloc();
> +       if (!priv->mii_bus)
> +               return -EIO;
> +
> +       priv->mii_bus->name = "ftgmac100_mdio";
> +       snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");

If we have two ftgmac100 drivers loaded (such as on the ast2500-evb),
when the second goes to create the mdiobus kobject it will have the
same name. We need to make this name unique (or fix up the mdiobus
code).

You can see a backtrace here:

  https://github.com/openbmc/linux/issues/87


> +       priv->mii_bus->priv = priv->netdev;
> +       priv->mii_bus->read = ftgmac100_mdiobus_read;
> +       priv->mii_bus->write = ftgmac100_mdiobus_write;
> +
> +       for (i = 0; i < PHY_MAX_ADDR; i++)
> +               priv->mii_bus->irq[i] = PHY_POLL;
> +
> +       err = mdiobus_register(priv->mii_bus);
> +       if (err) {
> +               dev_err(priv->dev, "Cannot register MDIO bus!\n");
> +               goto err_register_mdiobus;
> +       }
> +
> +       err = ftgmac100_mii_probe(priv);
> +       if (err) {
> +               dev_err(priv->dev, "MII Probe failed!\n");
> +               goto err_mii_probe;
> +       }
> +
> +       return 0;
> +
> +err_mii_probe:
> +       mdiobus_unregister(priv->mii_bus);
> +err_register_mdiobus:
> +       mdiobus_free(priv->mii_bus);
> +       return err;
> +}
> +
> +static void ftgmac100_destroy_mdio(struct net_device *netdev)
> +{
> +       struct ftgmac100 *priv = netdev_priv(netdev);
> +
> +       if (!netdev->phydev)
> +               return;
> +
> +       phy_disconnect(netdev->phydev);
> +       mdiobus_unregister(priv->mii_bus);
> +       mdiobus_free(priv->mii_bus);
> +}
> +
>  /******************************************************************************
>   * struct platform_driver functions
>   *****************************************************************************/
> @@ -1211,31 +1263,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
>
>         priv->irq = irq;
>
> -       /* initialize mdio bus */
> -       priv->mii_bus = mdiobus_alloc();
> -       if (!priv->mii_bus) {
> -               err = -EIO;
> -               goto err_alloc_mdiobus;
> -       }
> -
> -       priv->mii_bus->name = "ftgmac100_mdio";
> -       snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
> -
> -       priv->mii_bus->priv = netdev;
> -       priv->mii_bus->read = ftgmac100_mdiobus_read;
> -       priv->mii_bus->write = ftgmac100_mdiobus_write;
> -
> -       err = mdiobus_register(priv->mii_bus);
> -       if (err) {
> -               dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
> -               goto err_register_mdiobus;
> -       }
> -
> -       err = ftgmac100_mii_probe(priv);
> -       if (err) {
> -               dev_err(&pdev->dev, "MII Probe failed!\n");
> -               goto err_mii_probe;
> -       }
> +       err = ftgmac100_setup_mdio(netdev);
> +       if (err)
> +               goto err_setup_mdio;
>
>         /* register network device */
>         err = register_netdev(netdev);
> @@ -1255,12 +1285,8 @@ static int ftgmac100_probe(struct platform_device *pdev)
>         return 0;
>
>  err_register_netdev:
> -       phy_disconnect(netdev->phydev);
> -err_mii_probe:
> -       mdiobus_unregister(priv->mii_bus);
> -err_register_mdiobus:
> -       mdiobus_free(priv->mii_bus);
> -err_alloc_mdiobus:
> +       ftgmac100_destroy_mdio(netdev);
> +err_setup_mdio:
>         iounmap(priv->base);
>  err_ioremap:
>         release_resource(priv->res);
> @@ -1280,10 +1306,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
>         priv = netdev_priv(netdev);
>
>         unregister_netdev(netdev);
> -
> -       phy_disconnect(netdev->phydev);
> -       mdiobus_unregister(priv->mii_bus);
> -       mdiobus_free(priv->mii_bus);
> +       ftgmac100_destroy_mdio(netdev);
>
>         iounmap(priv->base);
>         release_resource(priv->res);
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 01/10] net/ncsi: Resource management
  2016-06-30 10:27 ` [PATCH 01/10] net/ncsi: Resource management Gavin Shan
@ 2016-06-30 14:04   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:04 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

Hi Gavin,

I've had a read of your code and tried to point out some things I
found. Take them as suggestions; if you disagree then please don't
feel you need to change anything.

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> NCSI spec (DSP0222) defines several objects: package, channel, mode,
> filter, version and statistics etc. This introduces the data structs
> to represent those objects and implement functions to manage them.

Link to the spec in the commit message.

>
>    * The user (e.g. netdev driver) dereference NCSI device by
>      "struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
>      The later one is used by NCSI stack internally.
>    * Every NCSI device can have multiple packages simultaneously, up
>      to 8 packages. It's represented by "struct ncsi_package" and
>      identified by 3-bits ID.
>    * Every NCSI package can have multiple channels, up to 32. It's
>      represented by "struct ncsi_channel" and identified by 5-bits ID.
>    * Every NCSI channel has version, statistics, various modes and
>      filters. They are represented by "struct ncsi_channel_version",
>      "struct ncsi_channel_stats", "struct ncsi_channel_mode" and
>      "struct ncsi_channel_filter" separately.
>    * Apart from AEN (Asynchronous Event Notification), the NCSI stack
>      works in terms of command and response. This introduces struct
>      ncsi_req" to represent a complete NCSI transaction made of NCSI

You have an unterminated quote here.

>      request and response.
>
> This also introduces CONFIG_NET_NCSI to enable NCSI stack.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  net/Kconfig            |   1 +
>  net/Makefile           |   1 +
>  net/ncsi/Kconfig       |  10 ++
>  net/ncsi/Makefile      |   4 +
>  net/ncsi/internal.h    | 253 ++++++++++++++++++++++++++++
>  net/ncsi/ncsi-manage.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 704 insertions(+)
>  create mode 100644 net/ncsi/Kconfig
>  create mode 100644 net/ncsi/Makefile
>  create mode 100644 net/ncsi/internal.h
>  create mode 100644 net/ncsi/ncsi-manage.c
>
> diff --git a/net/Kconfig b/net/Kconfig
> index ff40562..c2cdbce 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -237,6 +237,7 @@ source "net/hsr/Kconfig"
>  source "net/switchdev/Kconfig"
>  source "net/l3mdev/Kconfig"
>  source "net/qrtr/Kconfig"
> +source "net/ncsi/Kconfig"
>
>  config RPS
>         bool
> diff --git a/net/Makefile b/net/Makefile
> index bdd1455..9bd20bb 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -79,3 +79,4 @@ ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
>  obj-y                          += l3mdev/
>  endif
>  obj-$(CONFIG_QRTR)             += qrtr/
> +obj-$(CONFIG_NET_NCSI)         += ncsi/
> diff --git a/net/ncsi/Kconfig b/net/ncsi/Kconfig
> new file mode 100644
> index 0000000..723f0eb
> --- /dev/null
> +++ b/net/ncsi/Kconfig
> @@ -0,0 +1,10 @@
> +#
> +# Configuration for NCSI support
> +#
> +
> +config NET_NCSI
> +       bool "NCSI interface support (EXPERIMENTAL)"

Drop the experimental.

> +       depends on INET
> +       ---help---
> +         This module provides NCSI (Network Controller Sideband Interface)
> +         support.

Add some more details so I know if I need it:

Enable this only if your system connects to a network device via NCSI
and the ethernet driver you are using supports the protocol.

> diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
> new file mode 100644
> index 0000000..07b5625
> --- /dev/null
> +++ b/net/ncsi/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# Makefile for NCSI API
> +#
> +obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
> diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
> new file mode 100644
> index 0000000..4a8bd76
> --- /dev/null
> +++ b/net/ncsi/internal.h
> @@ -0,0 +1,253 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __NCSI_INTERNAL_H__
> +#define __NCSI_INTERNAL_H__
> +
> +enum {
> +       NCSI_CAP_BASE           = 0,
> +       NCSI_CAP_GENERIC        = 0,
> +       NCSI_CAP_BC,
> +       NCSI_CAP_MC,
> +       NCSI_CAP_BUFFER,
> +       NCSI_CAP_AEN,
> +       NCSI_CAP_VLAN,
> +       NCSI_CAP_MAX
> +};
> +
> +enum {
> +       NCSI_CAP_GENERIC_HWA    = 0x01, /* HW arbitration             */
> +       NCSI_CAP_GENERIC_HDS    = 0x02, /* HNC driver status change   */
> +       NCSI_CAP_GENERIC_FC     = 0x04, /* HNC to MC flow control     */
> +       NCSI_CAP_GENERIC_FC1    = 0x08, /* MC to HNC flow control     */
> +       NCSI_CAP_GENERIC_MC     = 0x10, /* Global multicast filtering */
> +       NCSI_CAP_GENERIC_MASK   = 0x1f,
> +       NCSI_CAP_BC_ARP         = 0x01, /* ARP packet filtering       */
> +       NCSI_CAP_BC_DHCPC       = 0x02, /* DHCP client filtering      */
> +       NCSI_CAP_BC_DHCPS       = 0x04, /* DHCP server filtering      */
> +       NCSI_CAP_BC_NETBIOS     = 0x08, /* NetBIOS packet filtering   */
> +       NCSI_CAP_BC_MASK        = 0x0f,
> +       NCSI_CAP_MC_NEIGHBOR    = 0x01, /* IPv6 neighbor filtering    */
> +       NCSI_CAP_MC_ROUTER      = 0x02, /* IPv6 router filering       */
> +       NCSI_CAP_MC_DHCPV6      = 0x04, /* DHCPv6 filtering           */
> +       NCSI_CAP_MC_MASK        = 0x07,
> +       NCSI_CAP_AEN_LSC        = 0x01, /* Link status change AEN     */
> +       NCSI_CAP_AEN_CR         = 0x02, /* Configuration required AEN */
> +       NCSI_CAP_AEN_HDS        = 0x04, /* HNC driver status AEN      */
> +       NCSI_CAP_AEN_MASK       = 0x07,
> +       NCSI_CAP_VLAN_ONLY      = 0x01, /* VLAN is supported          */
> +       NCSI_CAP_VLAN_NO        = 0x02, /* Filter VLAN and non-VLAN   */
> +       NCSI_CAP_VLAN_ANY       = 0x04, /* Filter Any-and-non-VLAN    */
> +       NCSI_CAP_VLAN_MASK      = 0x07
> +};
> +
> +enum {
> +       NCSI_MODE_BASE          = 0,
> +       NCSI_MODE_ENABLE        = 0,
> +       NCSI_MODE_TX_ENABLE,
> +       NCSI_MODE_LINK,
> +       NCSI_MODE_VLAN,
> +       NCSI_MODE_BC,
> +       NCSI_MODE_MC,
> +       NCSI_MODE_AEN,
> +       NCSI_MODE_FC,
> +       NCSI_MODE_MAX
> +};
> +
> +enum {
> +       NCSI_FILTER_BASE        = 0,
> +       NCSI_FILTER_VLAN        = 0,
> +       NCSI_FILTER_UC,
> +       NCSI_FILTER_MC,
> +       NCSI_FILTER_MIXED,
> +       NCSI_FILTER_MAX
> +};
> +
> +struct ncsi_channel_version {
> +       unsigned int   ncv_version;     /* Supported BCD encoded NCSI version */
> +       unsigned int   ncv_alpha2;      /* Supported BCD encoded NCSI version */
> +       unsigned char  ncv_fw_name[12]; /* Firware name string                */
> +       unsigned int   ncv_fw_version;  /* Firmware version                   */
> +       unsigned short ncv_pci_ids[4];  /* PCI identification                 */
> +       unsigned int   ncv_mf_id;       /* Manufacture ID                     */
> +};

Personally I find the prefixes you use on the elements in your structs
to be redundant. That's just my preference though; use what is common
in the networking stack in the kernel as a guide.

> +
> +struct ncsi_channel_cap {
> +       unsigned int ncc_index; /* Index of channel capabilities */
> +       unsigned int ncc_cap;   /* NCSI channel capability       */
> +};
> +
> +struct ncsi_channel_mode {
> +       unsigned int ncm_index;   /* Index of channel modes      */
> +       unsigned int ncm_enable;  /* Enabled or disabled         */
> +       unsigned int ncm_size;    /* Valid entries in ncm_data[] */
> +       unsigned int ncm_data[8]; /* Data entries                */
> +};
> +
> +struct ncsi_channel_filter {
> +       unsigned int  ncf_index;  /* Index of channel filters          */
> +       unsigned int  ncf_total;  /* Total entries in the filter table */
> +       unsigned long ncf_bitmap; /* Bitmap of valid entries           */
> +       unsigned char ncf_data[]; /* Data for the valid entries        */
> +};
> +
> +struct ncsi_channel_stats {
> +       unsigned int ncs_hnc_cnt_hi;         /* Counter cleared            */
> +       unsigned int ncs_hnc_cnt_lo;         /* Counter cleared            */
> +       unsigned int ncs_hnc_rx_bytes;       /* Rx bytes                   */
> +       unsigned int ncs_hnc_tx_bytes;       /* Tx bytes                   */
> +       unsigned int ncs_hnc_rx_uc_pkts;     /* Rx UC packets              */
> +       unsigned int ncs_hnc_rx_mc_pkts;     /* Rx MC packets              */
> +       unsigned int ncs_hnc_rx_bc_pkts;     /* Rx BC packets              */
> +       unsigned int ncs_hnc_tx_uc_pkts;     /* Tx UC packets              */
> +       unsigned int ncs_hnc_tx_mc_pkts;     /* Tx MC packets              */
> +       unsigned int ncs_hnc_tx_bc_pkts;     /* Tx BC packets              */
> +       unsigned int ncs_hnc_fcs_err;        /* FCS errors                 */
> +       unsigned int ncs_hnc_align_err;      /* Alignment errors           */
> +       unsigned int ncs_hnc_false_carrier;  /* False carrier detection    */
> +       unsigned int ncs_hnc_runt_pkts;      /* Rx runt packets            */
> +       unsigned int ncs_hnc_jabber_pkts;    /* Rx jabber packets          */
> +       unsigned int ncs_hnc_rx_pause_xon;   /* Rx pause XON frames        */
> +       unsigned int ncs_hnc_rx_pause_xoff;  /* Rx XOFF frames             */
> +       unsigned int ncs_hnc_tx_pause_xon;   /* Tx XON frames              */
> +       unsigned int ncs_hnc_tx_pause_xoff;  /* Tx XOFF frames             */
> +       unsigned int ncs_hnc_tx_s_collision; /* Single collision frames    */
> +       unsigned int ncs_hnc_tx_m_collision; /* Multiple collision frames  */
> +       unsigned int ncs_hnc_l_collision;    /* Late collision frames      */
> +       unsigned int ncs_hnc_e_collision;    /* Excessive collision frames */
> +       unsigned int ncs_hnc_rx_ctl_frames;  /* Rx control frames          */
> +       unsigned int ncs_hnc_rx_64_frames;   /* Rx 64-bytes frames         */
> +       unsigned int ncs_hnc_rx_127_frames;  /* Rx 65-127 bytes frames     */
> +       unsigned int ncs_hnc_rx_255_frames;  /* Rx 128-255 bytes frames    */
> +       unsigned int ncs_hnc_rx_511_frames;  /* Rx 256-511 bytes frames    */
> +       unsigned int ncs_hnc_rx_1023_frames; /* Rx 512-1023 bytes frames   */
> +       unsigned int ncs_hnc_rx_1522_frames; /* Rx 1024-1522 bytes frames  */
> +       unsigned int ncs_hnc_rx_9022_frames; /* Rx 1523-9022 bytes frames  */
> +       unsigned int ncs_hnc_tx_64_frames;   /* Tx 64-bytes frames         */
> +       unsigned int ncs_hnc_tx_127_frames;  /* Tx 65-127 bytes frames     */
> +       unsigned int ncs_hnc_tx_255_frames;  /* Tx 128-255 bytes frames    */
> +       unsigned int ncs_hnc_tx_511_frames;  /* Tx 256-511 bytes frames    */
> +       unsigned int ncs_hnc_tx_1023_frames; /* Tx 512-1023 bytes frames   */
> +       unsigned int ncs_hnc_tx_1522_frames; /* Tx 1024-1522 bytes frames  */
> +       unsigned int ncs_hnc_tx_9022_frames; /* Tx 1523-9022 bytes frames  */
> +       unsigned int ncs_hnc_rx_valid_bytes; /* Rx valid bytes             */
> +       unsigned int ncs_hnc_rx_runt_pkts;   /* Rx error runt packets      */
> +       unsigned int ncs_hnc_rx_jabber_pkts; /* Rx error jabber packets    */
> +       unsigned int ncs_ncsi_rx_cmds;       /* Rx NCSI commands           */
> +       unsigned int ncs_ncsi_dropped_cmds;  /* Dropped commands           */
> +       unsigned int ncs_ncsi_cmd_type_errs; /* Command type errors        */
> +       unsigned int ncs_ncsi_cmd_csum_errs; /* Command checksum errors    */
> +       unsigned int ncs_ncsi_rx_pkts;       /* Rx NCSI packets            */
> +       unsigned int ncs_ncsi_tx_pkts;       /* Tx NCSI packets            */
> +       unsigned int ncs_ncsi_tx_aen_pkts;   /* Tx AEN packets             */
> +       unsigned int ncs_pt_tx_pkts;         /* Tx packets                 */
> +       unsigned int ncs_pt_tx_dropped;      /* Tx dropped packets         */
> +       unsigned int ncs_pt_tx_channel_err;  /* Tx channel errors          */
> +       unsigned int ncs_pt_tx_us_err;       /* Tx undersize errors        */
> +       unsigned int ncs_pt_rx_pkts;         /* Rx packets                 */
> +       unsigned int ncs_pt_rx_dropped;      /* Rx dropped packets         */
> +       unsigned int ncs_pt_rx_channel_err;  /* Rx channel errors          */
> +       unsigned int ncs_pt_rx_us_err;       /* Rx undersize errors        */
> +       unsigned int ncs_pt_rx_os_err;       /* Rx oversize errors         */
> +};

Could you use u32 to make this shorter?

> +
> +struct ncsi_dev_priv;
> +struct ncsi_package;
> +
> +#define NCSI_PACKAGE_INDEX(c)  (((c) >> 5) & 0x7)
> +#define NCSI_CHANNEL_INDEX(c)  ((c) & 0x1ffff)
> +#define NCSI_TO_CHANNEL(p, c)  ((((p) & 0x7) << 5) | ((c) & 0x1ffff))

You could use the macros you just defined:

#define NCSI_TO_CHANNEL(p, c) (NCSI_PACKAGE_INDEX(c) | NCSI_CHANNEL_INDEX(c))

> +
> +/* Channel state */
> +enum {
> +       ncsi_channel_state_deselected_initial,
> +       ncsi_channel_state_selected_initial,
> +       ncsi_channel_state_deselected_ready,
> +       ncsi_channel_state_selected_ready,
> +};
> +
> +struct ncsi_channel {
> +       unsigned char               nc_id;
> +       int                         nc_state;
> +       struct ncsi_package         *nc_package;
> +       struct ncsi_channel_version nc_version;
> +       struct ncsi_channel_cap     nc_caps[NCSI_CAP_MAX];
> +       struct ncsi_channel_mode    nc_modes[NCSI_MODE_MAX];
> +       struct ncsi_channel_filter  *nc_filters[NCSI_FILTER_MAX];
> +       struct ncsi_channel_stats   nc_stats;
> +       struct list_head            nc_node;
> +};
> +
> +struct ncsi_package {
> +       unsigned char        np_id;           /* NCSI package ID          */
> +       struct ncsi_dev_priv *np_ndp;         /* NCSI device              */
> +       atomic_t             np_channel_num;  /* Number of channels       */
> +       spinlock_t           np_channel_lock; /* Protect list of channels */
> +       struct list_head     np_channels;     /* List of chanels          */
> +       struct list_head     np_node;
> +};
> +
> +struct ncsi_req {
> +       unsigned char        nr_id;            /* Request ID          */
> +       bool                 nr_used;          /* Request used or not */
> +       struct ncsi_dev_priv *nr_ndp;          /* NCSI device         */
> +       struct sk_buff       *nr_cmd;          /* Command packet      */
> +       struct sk_buff       *nr_rsp;          /* Response packet     */
> +       struct timer_list    nr_timer;         /* Associated timer    */
> +       bool                 nr_timer_enabled; /* Timer was started   */
> +};
> +
> +struct ncsi_dev_priv {
> +       struct ncsi_dev     ndp_ndev;            /* NCSI device              */
> +       int                 ndp_flags;           /* NCSI device flags        */
> +       struct ncsi_package *ndp_active_package; /* Active NCSI package      */
> +       struct ncsi_channel *ndp_active_channel; /* Active NCSI channel      */
> +       atomic_t            ndp_package_num;     /* Number of packages       */
> +       spinlock_t          ndp_package_lock;    /* Protect list of packages */
> +       struct list_head    ndp_packages;        /* List of packages         */
> +       atomic_t            ndp_last_req_idx;    /* Last used request ID     */
> +       spinlock_t          ndp_req_lock;        /* Protect request table    */
> +       struct ncsi_req     ndp_reqs[256];       /* Request table            */
> +       struct list_head    ndp_node;
> +};
> +
> +extern struct list_head ncsi_dev_list;
> +extern spinlock_t ncsi_dev_lock;
> +
> +#define TO_NCSI_DEV_PRIV(nd) \
> +       container_of(nd, struct ncsi_dev_priv, ndp_ndev)
> +#define NCSI_FOR_EACH_DEV(ndp) \
> +       list_for_each_entry_rcu(ndp, &ncsi_dev_list, ndp_node)
> +#define NCSI_FOR_EACH_PACKAGE(ndp, np) \
> +       list_for_each_entry_rcu(np, &ndp->ndp_packages, np_node)
> +#define NCSI_FOR_EACH_CHANNEL(np, nc) \
> +       list_for_each_entry_rcu(nc, &np->np_channels, nc_node)
> +
> +/* Resources */
> +int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data);
> +int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data);
> +int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index);
> +struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np,
> +                                     unsigned char id);
> +struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
> +                                      unsigned char id);
> +struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
> +                                     unsigned char id);
> +struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
> +                                      unsigned char id);
> +void ncsi_release_package(struct ncsi_package *np);
> +void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
> +                                  unsigned char id,
> +                                  struct ncsi_package **np,
> +                                  struct ncsi_channel **nc);
> +struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
> +void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
> +struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
> +
> +#endif /* __NCSI_INTERNAL_H__ */
> diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
> new file mode 100644
> index 0000000..dab54bc
> --- /dev/null
> +++ b/net/ncsi/ncsi-manage.c
> @@ -0,0 +1,435 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/netlink.h>
> +
> +#include <net/ncsi.h>
> +#include <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "internal.h"
> +
> +LIST_HEAD(ncsi_dev_list);
> +DEFINE_SPINLOCK(ncsi_dev_lock);
> +
> +int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data)
> +{
> +       struct ncsi_channel_filter *ncf;
> +       int idx, entry_size;
> +       void *bitmap;
> +
> +       switch (table) {
> +       case NCSI_FILTER_VLAN:
> +               entry_size = 2;
> +               break;
> +       case NCSI_FILTER_UC:
> +       case NCSI_FILTER_MC:
> +       case NCSI_FILTER_MIXED:
> +               entry_size = 6;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* Check if the filter table has been initialized */
> +       ncf = nc->nc_filters[table];
> +       if (!ncf)
> +               return -ENODEV;
> +
> +       /* Check the valid entries one by one */
> +       bitmap = (void *)&ncf->ncf_bitmap;
> +       idx = -1;
> +       while ((idx = find_next_bit(bitmap, ncf->ncf_total, idx + 1))
> +               < ncf->ncf_total) {
> +               if (!memcmp(ncf->ncf_data + entry_size * idx, data, entry_size))

It's hard to know if this is correct. I can't think how to improve it though.

> +                       return idx;
> +       }
> +
> +       return -ENOENT;
> +}
> +
> +int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data)
> +{
> +       struct ncsi_channel_filter *ncf;
> +       int idx, entry_size;
> +       void *bitmap;
> +
> +       /* Needn't add it if it's already existing */

Perhaps "Needn't add it if it already exists"

> +       idx = ncsi_find_channel_filter(nc, table, data);
> +       if (idx >= 0)
> +               return idx;
> +
> +       switch (table) {
> +       case NCSI_FILTER_VLAN:
> +               entry_size = 2;
> +               break;
> +       case NCSI_FILTER_UC:
> +       case NCSI_FILTER_MC:
> +       case NCSI_FILTER_MIXED:
> +               entry_size = 6;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* Check if the filter table has been initialized */
> +       ncf = nc->nc_filters[table];
> +       if (!ncf)
> +               return -ENODEV;
> +
> +       /* Propagate the filter */
> +       bitmap = (void *)&ncf->ncf_bitmap;
> +       do {
> +               idx = find_next_zero_bit(bitmap, ncf->ncf_total, 0);
> +               if (idx >= ncf->ncf_total)
> +                       return -ENOSPC;
> +       } while (test_and_set_bit(idx, bitmap));

I wasn't sure why this was in a loop when I first read. Is it so you
don't need to have locking around the bitmap while you search for a
free entry?

> +
> +       memcpy(ncf->ncf_data + entry_size * idx, data, entry_size);
> +       return idx;
> +}
> +
> +int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index)
> +{
> +       struct ncsi_channel_filter *ncf;
> +       int entry_size;
> +       void *bitmap;
> +
> +       switch (table) {
> +       case NCSI_FILTER_VLAN:
> +               entry_size = 2;
> +               break;
> +       case NCSI_FILTER_UC:
> +       case NCSI_FILTER_MC:
> +       case NCSI_FILTER_MIXED:
> +               entry_size = 6;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }

This is the third time you do this look up. Does it deserve it's own function?

> +
> +       /* Check if the filter table has been initialized */
> +       ncf = nc->nc_filters[table];
> +       if (!ncf || index >= ncf->ncf_total)
> +               return -ENODEV;
> +
> +       /* Check if the entry is valid */
> +       bitmap = (void *)&ncf->ncf_bitmap;
> +       if (test_and_clear_bit(index, bitmap))
> +               memset(ncf->ncf_data + entry_size * index, 0, entry_size);
> +
> +       return 0;
> +}
> +
> +struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
> +{
> +       struct ncsi_channel *nc, *tmp;
> +       int index;
> +
> +       nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
> +       if (!nc)
> +               return NULL;
> +
> +       nc->nc_package = np;
> +       nc->nc_id = id;
> +       for (index = 0; index < NCSI_CAP_MAX; index++)
> +               nc->nc_caps[index].ncc_index = index;
> +       for (index = 0; index < NCSI_MODE_MAX; index++)
> +               nc->nc_modes[index].ncm_index = index;
> +
> +       spin_lock(&np->np_channel_lock);
> +       tmp = ncsi_find_channel(np, id);
> +       if (tmp) {
> +               spin_unlock(&np->np_channel_lock);
> +               kfree(nc);
> +               return tmp;
> +       }
> +       list_add_tail_rcu(&nc->nc_node, &np->np_channels);
> +       spin_unlock(&np->np_channel_lock);
> +
> +       atomic_inc(&np->np_channel_num);
> +       return nc;
> +}
> +
> +struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
> +                                      unsigned char id)
> +{
> +       struct ncsi_channel *nc;
> +
> +       NCSI_FOR_EACH_CHANNEL(np, nc) {
> +               if (nc->nc_id == id)
> +                       return nc;
> +       }
> +
> +       return NULL;
> +}
> +
> +static void ncsi_release_channel(struct ncsi_channel *nc)
> +{
> +       struct ncsi_dev_priv *ndp = nc->nc_package->np_ndp;
> +       struct ncsi_channel_filter *ncf;
> +       int i;
> +
> +       /* Release filters */
> +       for (i = 0; i < NCSI_FILTER_MAX; i++) {
> +               ncf = nc->nc_filters[i];
> +               if (!ncf)
> +                       continue;
> +
> +               nc->nc_filters[i] = NULL;
> +               kfree(ncf);
> +       }
> +
> +       /* Update active channel if necessary */
> +       if (ndp->ndp_active_channel == nc) {
> +               ndp->ndp_active_package = NULL;
> +               ndp->ndp_active_channel = NULL;
> +       }
> +
> +       /* Remove and free channel */
> +       list_del_rcu(&nc->nc_node);
> +       kfree(nc);
> +}
> +
> +struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
> +                                     unsigned char id)
> +{
> +       struct ncsi_package *np, *tmp;
> +
> +       np = kzalloc(sizeof(*np), GFP_ATOMIC);
> +       if (!np)
> +               return NULL;
> +
> +       np->np_id = id;
> +       np->np_ndp = ndp;
> +       spin_lock_init(&np->np_channel_lock);
> +       INIT_LIST_HEAD(&np->np_channels);
> +
> +       spin_lock(&ndp->ndp_package_lock);
> +       tmp = ncsi_find_package(ndp, id);
> +       if (tmp) {
> +               spin_unlock(&ndp->ndp_package_lock);
> +               kfree(np);
> +               return tmp;
> +       }
> +       list_add_tail_rcu(&np->np_node, &ndp->ndp_packages);
> +       spin_unlock(&ndp->ndp_package_lock);
> +
> +       atomic_inc(&ndp->ndp_package_num);
> +       return np;
> +}
> +
> +struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
> +                                      unsigned char id)
> +{
> +       struct ncsi_package *np;
> +
> +       NCSI_FOR_EACH_PACKAGE(ndp, np) {
> +               if (np->np_id == id)
> +                       return np;
> +       }
> +
> +       return NULL;
> +}
> +
> +void ncsi_release_package(struct ncsi_package *np)
> +{
> +       struct ncsi_dev_priv *ndp = np->np_ndp;
> +       struct ncsi_channel *nc, *tmp;
> +
> +       /* Release all child channels */
> +       spin_lock(&np->np_channel_lock);
> +       list_for_each_entry_safe(nc, tmp, &np->np_channels, nc_node)
> +               ncsi_release_channel(nc);
> +       spin_unlock(&np->np_channel_lock);
> +
> +       /* Clear active package if necessary */
> +       if (ndp->ndp_active_package == np) {
> +               ndp->ndp_active_package = NULL;
> +               ndp->ndp_active_channel = NULL;
> +       }
> +
> +       /* Remove and free package */
> +       list_del_rcu(&np->np_node);
> +       kfree(np);
> +}
> +
> +void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
> +                                  unsigned char id,
> +                                  struct ncsi_package **np,
> +                                  struct ncsi_channel **nc)
> +{
> +       struct ncsi_package *p;
> +       struct ncsi_channel *c;
> +
> +       p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
> +       c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
> +
> +       if (np)
> +               *np = p;
> +       if (nc)
> +               *nc = c;
> +}
> +
> +/* For two consective NCSI commands, the packet IDs shouldn't be

Spelling: consecutive

> + * same. Otherwise, the bogus response might be replied. So the
> + * available IDs are allocated in round-robin fasion.

Spelling: fashion

> + */
> +struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp)
> +{
> +       struct ncsi_req *nr = NULL;
> +       int idx, limit = 256;

Instead of 256, perhaps a #define or ARRAY_SIZE(priv->ndp_reqs)?

> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&ndp->ndp_req_lock, flags);
> +
> +       /* Check if there is one available request until the ceiling */
> +       for (idx = atomic_read(&ndp->ndp_last_req_idx);
> +            !nr && idx < limit; idx++) {
> +               if (ndp->ndp_reqs[idx].nr_used)
> +                       continue;
> +
> +               ndp->ndp_reqs[idx].nr_used = true;
> +               nr = &ndp->ndp_reqs[idx];
> +               atomic_inc(&ndp->ndp_last_req_idx);
> +               if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
> +                       atomic_set(&ndp->ndp_last_req_idx, 0);

Do these need to be atomic_ variants, considering we're inside the ndp_req_lock?

> +       }
> +
> +       /* Fail back to check from the starting cursor */
> +       for (idx = 0; !nr && idx < atomic_read(&ndp->ndp_last_req_idx); idx++) {
> +               if (ndp->ndp_reqs[idx].nr_used)
> +                       continue;
> +
> +               ndp->ndp_reqs[idx].nr_used = true;
> +               nr = &ndp->ndp_reqs[idx];
> +               atomic_inc(&ndp->ndp_last_req_idx);
> +               if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
> +                       atomic_set(&ndp->ndp_last_req_idx, 0);
> +       }
> +
> +       spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +       return nr;
> +}
> +
> +void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
> +{
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct sk_buff *cmd, *rsp;
> +       unsigned long flags;
> +
> +       if (nr->nr_timer_enabled) {
> +               nr->nr_timer_enabled = false;
> +               del_timer_sync(&nr->nr_timer);
> +       }
> +
> +       spin_lock_irqsave(&ndp->ndp_req_lock, flags);
> +       cmd = nr->nr_cmd;
> +       rsp = nr->nr_rsp;
> +       nr->nr_cmd = NULL;
> +       nr->nr_rsp = NULL;
> +       nr->nr_used = false;
> +       spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +
> +       /* Release command and response */
> +       consume_skb(cmd);
> +       consume_skb(rsp);
> +}
> +
> +struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
> +{
> +       struct ncsi_dev_priv *ndp;
> +
> +       NCSI_FOR_EACH_DEV(ndp) {
> +               if (ndp->ndp_ndev.nd_dev == dev)
> +                       return &ndp->ndp_ndev;
> +       }
> +
> +       return NULL;
> +}
> +
> +static void ncsi_req_timeout(unsigned long data)
> +{
> +       struct ncsi_req *nr = (struct ncsi_req *)data;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       unsigned long flags;
> +
> +       /* If the request already had associated response,
> +        * let the response handler to release it.
> +        */
> +       spin_lock_irqsave(&ndp->ndp_req_lock, flags);
> +       nr->nr_timer_enabled = false;
> +       if (nr->nr_rsp || !nr->nr_cmd) {
> +               spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +               return;
> +       }
> +       spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +
> +       /* Release the request */
> +       ncsi_free_req(nr, true, true);
> +}
> +
> +struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
> +                                  void (*handler)(struct ncsi_dev *ndev))
> +{
> +       struct ncsi_dev_priv *ndp;
> +       struct ncsi_dev *nd;
> +       int idx;
> +
> +       /* Check if the device has been registered or not */
> +       nd = ncsi_find_dev(dev);
> +       if (nd)
> +               return nd;
> +
> +       /* Create NCSI device */
> +       ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
> +       if (!ndp)
> +               return NULL;
> +
> +       nd = &ndp->ndp_ndev;
> +       nd->nd_state = ncsi_dev_state_registered;
> +       nd->nd_dev = dev;
> +       nd->nd_handler = handler;
> +
> +       /* Initialize private NCSI device */
> +       spin_lock_init(&ndp->ndp_package_lock);
> +       INIT_LIST_HEAD(&ndp->ndp_packages);
> +       spin_lock_init(&ndp->ndp_req_lock);
> +       atomic_set(&ndp->ndp_last_req_idx, 0);
> +       for (idx = 0; idx < 256; idx++) {

As before; instead of 256, perhaps a #define or ARRAY_SIZE(priv->ndp_reqs)?

> +               ndp->ndp_reqs[idx].nr_id = idx;
> +               ndp->ndp_reqs[idx].nr_ndp = ndp;
> +               setup_timer(&ndp->ndp_reqs[idx].nr_timer, ncsi_req_timeout,
> +                           (unsigned long)&ndp->ndp_reqs[idx]);
> +       }
> +
> +       spin_lock(&ncsi_dev_lock);
> +       list_add_tail_rcu(&ndp->ndp_node, &ncsi_dev_list);
> +       spin_unlock(&ncsi_dev_lock);
> +
> +       return nd;
> +}
> +EXPORT_SYMBOL_GPL(ncsi_register_dev);
> +
> +void ncsi_unregister_dev(struct ncsi_dev *nd)
> +{
> +       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
> +       struct ncsi_package *np, *tmp;
> +
> +       spin_lock(&ndp->ndp_package_lock);
> +       list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
> +               ncsi_release_package(np);
> +       spin_unlock(&ndp->ndp_package_lock);
> +}
> +EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/10] net/ncsi: NCSI command packet handler
  2016-06-30 10:27 ` [PATCH 02/10] net/ncsi: NCSI command packet handler Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> The NCSI command packets are sent from MC (Management Controller)
> to remote end. They are used for multiple purposes: probe existing
> NCSI package/channel, retrieve NCSI channel's capability, configure
> NCSI channel etc.
>
> This defines struct to represent NCSI command packets and introduces
> function ncsi_xmit_cmd(), which will be used to transmit NCSI command
> packet according to the request. The request is represented by struct
> ncsi_cmd_arg.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  include/uapi/linux/if_ether.h |   1 +
>  net/ncsi/Makefile             |   2 +-
>  net/ncsi/internal.h           |  18 ++
>  net/ncsi/ncsi-cmd.c           | 372 ++++++++++++++++++++++++++++++++++++++++++
>  net/ncsi/ncsi-pkt.h           | 168 +++++++++++++++++++
>  5 files changed, 560 insertions(+), 1 deletion(-)
>  create mode 100644 net/ncsi/ncsi-cmd.c
>  create mode 100644 net/ncsi/ncsi-pkt.h
>
> diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
> index cec849a..7300b2d 100644
> --- a/include/uapi/linux/if_ether.h
> +++ b/include/uapi/linux/if_ether.h
> @@ -87,6 +87,7 @@
>  #define ETH_P_8021AH   0x88E7          /* 802.1ah Backbone Service Tag */
>  #define ETH_P_MVRP     0x88F5          /* 802.1Q MVRP                  */
>  #define ETH_P_1588     0x88F7          /* IEEE 1588 Timesync */
> +#define ETH_P_NCSI     0x88F8          /* NCSI protocol                */
>  #define ETH_P_PRP      0x88FB          /* IEC 62439-3 PRP/HSRv0        */
>  #define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
>  #define ETH_P_TDLS     0x890D          /* TDLS */
> diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
> index 07b5625..abc4046 100644
> --- a/net/ncsi/Makefile
> +++ b/net/ncsi/Makefile
> @@ -1,4 +1,4 @@
>  #
>  # Makefile for NCSI API
>  #
> -obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
> +obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
> diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
> index 4a8bd76..0c51c89 100644
> --- a/net/ncsi/internal.h
> +++ b/net/ncsi/internal.h
> @@ -217,6 +217,21 @@ struct ncsi_dev_priv {
>         struct list_head    ndp_node;
>  };
>
> +struct ncsi_cmd_arg {
> +       struct ncsi_dev_priv   *nca_ndp;
> +       unsigned char          nca_type;
> +       unsigned char          nca_id;
> +       unsigned char          nca_package;
> +       unsigned char          nca_channel;
> +       unsigned short         nca_payload;
> +       unsigned int           nca_portid;
> +       union {
> +               unsigned char  nca_bytes[16];
> +               unsigned short nca_words[8];
> +               unsigned int   nca_dwords[4];
> +       };
> +};
> +
>  extern struct list_head ncsi_dev_list;
>  extern spinlock_t ncsi_dev_lock;
>
> @@ -250,4 +265,7 @@ struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
>  void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
>  struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
>
> +/* Packet handlers */
> +int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
> +
>  #endif /* __NCSI_INTERNAL_H__ */
> diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c
> new file mode 100644
> index 0000000..c64c437
> --- /dev/null
> +++ b/net/ncsi/ncsi-cmd.c
> @@ -0,0 +1,372 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include <net/ncsi.h>
> +#include <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "internal.h"
> +#include "ncsi-pkt.h"
> +
> +/* This function should be called after the data area has been
> + * populated completely.
> + */
> +static int ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
> +                                struct ncsi_cmd_arg *nca)
> +{
> +       __be32 csum, *checksum;
> +       __be32 low, high;
> +       unsigned char *stream;
> +       int i;
> +
> +       h->mc_id        = 0;
> +       h->revision     = NCSI_PKT_REVISION;
> +       h->reserved     = 0;
> +       h->id           = nca->nca_id;
> +       h->type         = nca->nca_type;
> +       h->channel      = NCSI_TO_CHANNEL(nca->nca_package,
> +                                         nca->nca_channel);
> +       h->length       = htons(nca->nca_payload);
> +       h->reserved1[0] = 0;
> +       h->reserved1[1] = 0;
> +
> +       /* Calculate the checksum */
> +       csum = 0;
> +       stream = (unsigned char *)h;
> +       for (i = 0; i < sizeof(*h) + nca->nca_payload; i += 2) {
> +               high = stream[i];
> +               low = stream[i + 1];

You're assigning a uchar to a u32 here, and then arranging them into a
u16 in the next line. Perhaps just do

csum += (stream[i + 1] | stream[i]);

Or make stream be a pointer to u16s that you iterate over and add to csum?

> +               csum += ((high << 8) | low);
> +       }
> +
> +       /* Fill with the calculated checksum */
> +       checksum = (__be32 *)((void *)(h + 1) + nca->nca_payload);

Whoa. So we're pointing past the end of the ncsi_pkt_hdr, plus
nca_payload bytes? Is there a better way to describe this?

> +       csum = (~csum + 1);
> +       *checksum = htonl(csum);
> +
> +       return 0;

We always return 0. Can we drop the return type?

> +}
> +
> +static int ncsi_cmd_handler_default(struct sk_buff *skb,
> +                                   struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_pkt *cmd;
> +
> +       if (!nca)
> +               return 0;
> +
> +       cmd = (struct ncsi_cmd_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_sp(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_sp_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;

4?

https://xkcd.com/221/ ?

> +
> +       cmd = (struct ncsi_cmd_sp_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->hw_arbitration = nca->nca_bytes[0];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_dc(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_dc_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_dc_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->ald = nca->nca_bytes[0];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_rc(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_rc_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_rc_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_ae(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_ae_pkt *cmd;
> +
> +       if (!nca)
> +               return 8;
> +
> +       cmd = (struct ncsi_cmd_ae_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mc_id = nca->nca_bytes[0];
> +       cmd->mode = htonl(nca->nca_dwords[1]);
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_sl(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_sl_pkt *cmd;
> +
> +       if (!nca)
> +               return 8;
> +
> +       cmd = (struct ncsi_cmd_sl_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mode = htonl(nca->nca_dwords[0]);
> +       cmd->oem_mode = htonl(nca->nca_dwords[1]);
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_svf(struct sk_buff *skb,
> +                               struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_svf_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_svf_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->vlan = htons(nca->nca_words[0]);
> +       cmd->index = nca->nca_bytes[2];
> +       cmd->enable = nca->nca_bytes[3];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_ev(struct sk_buff *skb,
> +                              struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_ev_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_ev_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mode = nca->nca_bytes[0];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_sma(struct sk_buff *skb,
> +                               struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_sma_pkt *cmd;
> +       int i;
> +
> +       if (!nca)
> +               return 8;
> +
> +       cmd = (struct ncsi_cmd_sma_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       for (i = 0; i < 6; i++)
> +               cmd->mac[i] = nca->nca_bytes[i];
> +       cmd->index = nca->nca_bytes[6];
> +       cmd->at_e = nca->nca_bytes[7];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
> +                               struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_ebf_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_ebf_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mode = htonl(nca->nca_dwords[0]);
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
> +                                struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_egmf_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_egmf_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mode = htonl(nca->nca_dwords[0]);
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
> +                                struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_cmd_snfc_pkt *cmd;
> +
> +       if (!nca)
> +               return 4;
> +
> +       cmd = (struct ncsi_cmd_snfc_pkt *)skb_put(skb, sizeof(*cmd));
> +       memset(cmd, 0, sizeof(*cmd));
> +       cmd->mode = nca->nca_bytes[0];
> +       return ncsi_cmd_build_header(&cmd->cmd.common, nca);
> +}
> +
> +static struct ncsi_cmd_handler {
> +       unsigned char   nch_type;
> +       int             (*nch_handler)(struct sk_buff *skb,
> +                                      struct ncsi_cmd_arg *nca);
> +} ncsi_cmd_handlers[] = {
> +       { NCSI_PKT_CMD_CIS,   ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_SP,    ncsi_cmd_handler_sp        },
> +       { NCSI_PKT_CMD_DP,    ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_EC,    ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_DC,    ncsi_cmd_handler_dc      },
> +       { NCSI_PKT_CMD_RC,    ncsi_cmd_handler_rc      },
> +       { NCSI_PKT_CMD_ECNT,  ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_DCNT,  ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_AE,    ncsi_cmd_handler_ae      },
> +       { NCSI_PKT_CMD_SL,    ncsi_cmd_handler_sl      },
> +       { NCSI_PKT_CMD_GLS,   ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_SVF,   ncsi_cmd_handler_svf     },
> +       { NCSI_PKT_CMD_EV,    ncsi_cmd_handler_ev      },
> +       { NCSI_PKT_CMD_DV,    ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_SMA,   ncsi_cmd_handler_sma     },
> +       { NCSI_PKT_CMD_EBF,   ncsi_cmd_handler_ebf     },
> +       { NCSI_PKT_CMD_DBF,   ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_EGMF,  ncsi_cmd_handler_egmf    },
> +       { NCSI_PKT_CMD_DGMF,  ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_SNFC,  ncsi_cmd_handler_snfc    },
> +       { NCSI_PKT_CMD_GVI,   ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_GC,    ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_GP,    ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_GCPS,  ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_GNS,   ncsi_cmd_handler_default },
> +       { NCSI_PKT_CMD_GNPTS, ncsi_cmd_handler_default },
> +       { 0,                  NULL                     }
> +};
> +
> +static struct ncsi_req *ncsi_alloc_cmd_req(struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_dev_priv *ndp = nca->nca_ndp;
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct net_device *dev = nd->nd_dev;
> +       int hlen = LL_RESERVED_SPACE(dev);
> +       int tlen = dev->needed_tailroom;
> +       int len = hlen + tlen;
> +       struct sk_buff *skb;
> +       struct ncsi_req *nr;
> +
> +       nr = ncsi_alloc_req(ndp);
> +       if (!nr)
> +               return NULL;
> +
> +       /* NCSI command packet has 16-bytes header, payload,
> +        * 4-bytes checksum and optional padding.
> +        */
> +       len += sizeof(struct ncsi_cmd_pkt_hdr);
> +       len += 4;
> +       if (nca->nca_payload < 26)
> +               len += 26;

4 is the checksum I assume. What is 26? Can we sizeof() any structures
so these aren't just magic numbers?

> +       else
> +               len += nca->nca_payload;
> +
> +       /* Allocate skb */
> +       skb = alloc_skb(len, GFP_ATOMIC);
> +       if (!skb) {
> +               ncsi_free_req(nr, false, false);
> +               return NULL;
> +       }
> +
> +       nr->nr_cmd = skb;
> +       skb_reserve(skb, hlen);
> +       skb_reset_network_header(skb);
> +
> +       skb->dev = dev;
> +       skb->protocol = htons(ETH_P_NCSI);
> +
> +       return nr;
> +}
> +
> +int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
> +{
> +       struct ncsi_req *nr;
> +       struct ethhdr *eh;
> +       struct ncsi_cmd_handler *nch;
> +       int i, ret;
> +
> +       /* Search for the handler */
> +       nch = ncsi_cmd_handlers;
> +       while (nch->nch_handler) {
> +               if (nch->nch_type == nca->nca_type)
> +                       break;
> +               nch++;
> +       }
> +
> +       if (!nch->nch_handler) {
> +               pr_info("%s: Cannot send packet with type 0x%x\n",
> +                       __func__, nca->nca_type);
> +               return -ENOENT;
> +       }
> +
> +       /* Get packet payload length and allocate the request */
> +       nca->nca_payload = nch->nch_handler(NULL, NULL);
> +       nr = ncsi_alloc_cmd_req(nca);
> +       if (!nr)
> +               return -ENOMEM;
> +
> +       /* Prepare the packet */
> +       nca->nca_id = nr->nr_id;
> +       ret = nch->nch_handler(nr->nr_cmd, nca);
> +       if (ret) {
> +               ncsi_free_req(nr, true, false);
> +               return ret;
> +       }
> +
> +       /* Fill the ethernet header */
> +       eh = (struct ethhdr *)skb_push(nr->nr_cmd, sizeof(*eh));
> +       eh->h_proto = htons(ETH_P_NCSI);
> +       for (i = 0; i < ETH_ALEN; i++) {
> +               eh->h_dest[i] = 0xff;
> +               eh->h_source[i] = 0xff;
> +       }

These two could be eth_broadcast_addr().

> +
> +       /* Start the timer for the request that might not have
> +        * corresponding response. I'm not sure 1 second delay
> +        * here is enough. Anyway, NCSI is internal network, so
> +        * the responsiveness should be as fast as enough.
> +        */

Could this message be updated now that we are confident it works on hardware?

 /*
 * Start the timer for the request that might not have
 * corresponding response. Given NCSI is an internal
 * connection a 1 second delay should be sufficient.
 */


> +       nr->nr_timer_enabled = true;
> +       mod_timer(&nr->nr_timer, jiffies + 1 * HZ);
> +
> +       /* Send NCSI packet */
> +       skb_get(nr->nr_cmd);
> +       ret = dev_queue_xmit(nr->nr_cmd);
> +       if (ret < 0) {
> +               ncsi_free_req(nr, true, false);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
> new file mode 100644
> index 0000000..7f030b5
> --- /dev/null
> +++ b/net/ncsi/ncsi-pkt.h
> @@ -0,0 +1,168 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __NCSI_PKT_H__
> +#define __NCSI_PKT_H__
> +
> +struct ncsi_pkt_hdr {
> +       unsigned char mc_id;        /* Management controller ID */
> +       unsigned char revision;     /* NCSI version - 0x01      */
> +       unsigned char reserved;     /* Reserved                 */
> +       unsigned char id;           /* Packet sequence number   */
> +       unsigned char type;         /* Packet type              */
> +       unsigned char channel;      /* Network controller ID    */
> +       __be16        length;       /* Payload length           */
> +       __be32        reserved1[2]; /* Reserved                 */
> +};
> +
> +struct ncsi_cmd_pkt_hdr {
> +       struct ncsi_pkt_hdr common; /* Common NCSI packet header */
> +};
> +
> +/* NCSI common command packet */
> +struct ncsi_cmd_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> +       __be32                  checksum; /* Checksum       */
> +       unsigned char           pad[26];
> +};
> +
> +/* Select Package */
> +struct ncsi_cmd_sp_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;            /* Command header */
> +       unsigned char           reserved[3];    /* Reserved       */
> +       unsigned char           hw_arbitration; /* HW arbitration */
> +       __be32                  checksum;       /* Checksum       */
> +       unsigned char           pad[22];
> +};
> +
> +/* Disable Channel */
> +struct ncsi_cmd_dc_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;         /* Command header  */
> +       unsigned char           reserved[3]; /* Reserved        */
> +       unsigned char           ald;         /* Allow link down */
> +       __be32                  checksum;    /* Checksum        */
> +       unsigned char           pad[22];
> +};
> +
> +/* Reset Channel */
> +struct ncsi_cmd_rc_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> +       __be32                  reserved; /* Reserved       */
> +       __be32                  checksum; /* Checksum       */
> +       unsigned char           pad[22];
> +};
> +
> +/* AEN Enable */
> +struct ncsi_cmd_ae_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;         /* Command header   */
> +       unsigned char           reserved[3]; /* Reserved         */
> +       unsigned char           mc_id;       /* MC ID            */
> +       __be32                  mode;        /* AEN working mode */
> +       __be32                  checksum;    /* Checksum         */
> +       unsigned char           pad[18];
> +};
> +
> +/* Set Link */
> +struct ncsi_cmd_sl_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header    */
> +       __be32                  mode;     /* Link working mode */
> +       __be32                  oem_mode; /* OEM link mode     */
> +       __be32                  checksum; /* Checksum          */
> +       unsigned char           pad[18];
> +};
> +
> +/* Set VLAN Filter */
> +struct ncsi_cmd_svf_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;       /* Command header    */
> +       __be16                  reserved;  /* Reserved          */
> +       __be16                  vlan;      /* VLAN ID           */
> +       __be16                  reserved1; /* Reserved          */
> +       unsigned char           index;     /* VLAN table index  */
> +       unsigned char           enable;    /* Enable or disable */
> +       __be32                  checksum;  /* Checksum          */
> +       unsigned char           pad[14];
> +};
> +
> +/* Enable VLAN */
> +struct ncsi_cmd_ev_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;         /* Command header   */
> +       unsigned char           reserved[3]; /* Reserved         */
> +       unsigned char           mode;        /* VLAN filter mode */
> +       __be32                  checksum;    /* Checksum         */
> +       unsigned char           pad[22];
> +};
> +
> +/* Set MAC Address */
> +struct ncsi_cmd_sma_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header          */
> +       unsigned char           mac[6];   /* MAC address             */
> +       unsigned char           index;    /* MAC table index         */
> +       unsigned char           at_e;     /* Addr type and operation */
> +       __be32                  checksum; /* Checksum                */
> +       unsigned char           pad[18];
> +};
> +
> +/* Enable Broadcast Filter */
> +struct ncsi_cmd_ebf_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> +       __be32                  mode;     /* Filter mode    */
> +       __be32                  checksum; /* Checksum       */
> +       unsigned char           pad[22];
> +};
> +
> +/* Enable Global Multicast Filter */
> +struct ncsi_cmd_egmf_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> +       __be32                  mode;     /* Global MC mode */
> +       __be32                  checksum; /* Checksum       */
> +       unsigned char           pad[22];
> +};
> +
> +/* Set NCSI Flow Control */
> +struct ncsi_cmd_snfc_pkt {
> +       struct ncsi_cmd_pkt_hdr cmd;         /* Command header    */
> +       unsigned char           reserved[3]; /* Reserved          */
> +       unsigned char           mode;        /* Flow control mode */
> +       __be32                  checksum;    /* Checksum          */
> +       unsigned char           pad[22];
> +};
> +
> +/* NCSI packet revision */
> +#define NCSI_PKT_REVISION      0x01
> +
> +/* NCSI packet commands */
> +#define NCSI_PKT_CMD_CIS       0x00    /* Clear Initial State              */
> +#define NCSI_PKT_CMD_SP                0x01    /* Select Package                   */
> +#define NCSI_PKT_CMD_DP                0x02    /* Deselect Package                 */
> +#define NCSI_PKT_CMD_EC                0x03    /* Enable Channel                   */
> +#define NCSI_PKT_CMD_DC                0x04    /* Disable Channel                  */
> +#define NCSI_PKT_CMD_RC                0x05    /* Reset Channel                    */
> +#define NCSI_PKT_CMD_ECNT      0x06    /* Enable Channel Network Tx        */
> +#define NCSI_PKT_CMD_DCNT      0x07    /* Disable Channel Network Tx       */
> +#define NCSI_PKT_CMD_AE                0x08    /* AEN Enable                       */
> +#define NCSI_PKT_CMD_SL                0x09    /* Set Link                         */
> +#define NCSI_PKT_CMD_GLS       0x0a    /* Get Link                         */
> +#define NCSI_PKT_CMD_SVF       0x0b    /* Set VLAN Filter                  */
> +#define NCSI_PKT_CMD_EV                0x0c    /* Enable VLAN                      */
> +#define NCSI_PKT_CMD_DV                0x0d    /* Disable VLAN                     */
> +#define NCSI_PKT_CMD_SMA       0x0e    /* Set MAC address                  */
> +#define NCSI_PKT_CMD_EBF       0x10    /* Enable Broadcast Filter          */
> +#define NCSI_PKT_CMD_DBF       0x11    /* Disable Broadcast Filter         */
> +#define NCSI_PKT_CMD_EGMF      0x12    /* Enable Global Multicast Filter   */
> +#define NCSI_PKT_CMD_DGMF      0x13    /* Disable Global Multicast Filter  */
> +#define NCSI_PKT_CMD_SNFC      0x14    /* Set NCSI Flow Control            */
> +#define NCSI_PKT_CMD_GVI       0x15    /* Get Version ID                   */
> +#define NCSI_PKT_CMD_GC                0x16    /* Get Capabilities                 */
> +#define NCSI_PKT_CMD_GP                0x17    /* Get Parameters                   */
> +#define NCSI_PKT_CMD_GCPS      0x18    /* Get Controller Packet Statistics */
> +#define NCSI_PKT_CMD_GNS       0x19    /* Get NCSI Statistics              */
> +#define NCSI_PKT_CMD_GNPTS     0x1a    /* Get NCSI Pass-throu Statistics   */
> +#define NCSI_PKT_CMD_OEM       0x50    /* OEM                              */
> +
> +#endif /* __NCSI_PKT_H__ */
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 03/10] net/ncsi: NCSI response packet handler
  2016-06-30 10:27 ` [PATCH 03/10] net/ncsi: NCSI response " Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> The NCSI response packets are sent to MC (Management Controller)
> from the remote end. They are responses of NCSI command packets
> for multiple purposes: completion status of NCSI command packets,
> return NCSI channel's capability or configuration etc.
>
> This defines struct to represent NCSI response packets and introduces
> function ncsi_rcv_rsp() which will be used to receive NCSI response
> packets and parse them.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  net/ncsi/Makefile   |    2 +-
>  net/ncsi/internal.h |    2 +
>  net/ncsi/ncsi-pkt.h |  191 +++++++++
>  net/ncsi/ncsi-rsp.c | 1159 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1353 insertions(+), 1 deletion(-)
>  create mode 100644 net/ncsi/ncsi-rsp.c
>
> diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
> index abc4046..4751819 100644
> --- a/net/ncsi/Makefile
> +++ b/net/ncsi/Makefile
> @@ -1,4 +1,4 @@
>  #
>  # Makefile for NCSI API
>  #
> -obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
> +obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
> diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
> index 0c51c89..d435f91 100644
> --- a/net/ncsi/internal.h
> +++ b/net/ncsi/internal.h
> @@ -267,5 +267,7 @@ struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
>
>  /* Packet handlers */
>  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
> +int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
> +                struct packet_type *pt, struct net_device *orig_dev);
>
>  #endif /* __NCSI_INTERNAL_H__ */
> diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
> index 7f030b5..95882f3 100644
> --- a/net/ncsi/ncsi-pkt.h
> +++ b/net/ncsi/ncsi-pkt.h
> @@ -25,6 +25,12 @@ struct ncsi_cmd_pkt_hdr {
>         struct ncsi_pkt_hdr common; /* Common NCSI packet header */
>  };
>
> +struct ncsi_rsp_pkt_hdr {
> +       struct ncsi_pkt_hdr common; /* Common NCSI packet header */
> +       __be16              code;   /* Response code             */
> +       __be16              reason; /* Response reason           */
> +};
> +
>  /* NCSI common command packet */
>  struct ncsi_cmd_pkt {
>         struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> @@ -32,6 +38,12 @@ struct ncsi_cmd_pkt {
>         unsigned char           pad[26];
>  };
>
> +struct ncsi_rsp_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;      /* Response header */
> +       __be32                  checksum; /* Checksum        */
> +       unsigned char           pad[22];
> +};
> +
>  /* Select Package */
>  struct ncsi_cmd_sp_pkt {
>         struct ncsi_cmd_pkt_hdr cmd;            /* Command header */
> @@ -133,6 +145,143 @@ struct ncsi_cmd_snfc_pkt {
>         unsigned char           pad[22];
>  };
>
> +/* Get Link Status */
> +struct ncsi_rsp_gls_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;        /* Response header   */
> +       __be32                  status;     /* Link status       */
> +       __be32                  other;      /* Other indications */
> +       __be32                  oem_status; /* OEM link status   */
> +       __be32                  checksum;
> +       unsigned char           pad[10];
> +};
> +
> +/* Get Version ID */
> +struct ncsi_rsp_gvi_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;          /* Response header */
> +       __be32                  ncsi_version; /* NCSI version    */
> +       unsigned char           reserved[3];  /* Reserved        */
> +       unsigned char           alpha2;       /* NCSI version    */
> +       unsigned char           fw_name[12];  /* f/w name string */
> +       __be32                  fw_version;   /* f/w version     */
> +       __be16                  pci_ids[4];   /* PCI IDs         */
> +       __be32                  mf_id;        /* Manufacture ID  */
> +       __be32                  checksum;
> +};
> +
> +/* Get Capabilities */
> +struct ncsi_rsp_gc_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;         /* Response header   */
> +       __be32                  cap;         /* Capabilities      */
> +       __be32                  bc_cap;      /* Broadcast cap     */
> +       __be32                  mc_cap;      /* Multicast cap     */
> +       __be32                  buf_cap;     /* Buffering cap     */
> +       __be32                  aen_cap;     /* AEN cap           */
> +       unsigned char           vlan_cnt;    /* VLAN filter count */
> +       unsigned char           mixed_cnt;   /* Mix filter count  */
> +       unsigned char           mc_cnt;      /* MC filter count   */
> +       unsigned char           uc_cnt;      /* UC filter count   */
> +       unsigned char           reserved[2]; /* Reserved          */
> +       unsigned char           vlan_mode;   /* VLAN mode         */
> +       unsigned char           channel_cnt; /* Channel count     */
> +       __be32                  checksum;    /* Checksum          */
> +};
> +
> +/* Get Parameters */
> +struct ncsi_rsp_gp_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;          /* Response header       */
> +       unsigned char           mac_cnt;      /* Number of MAC addr    */
> +       unsigned char           reserved[2];  /* Reserved              */
> +       unsigned char           mac_enable;   /* MAC addr enable flags */
> +       unsigned char           vlan_cnt;     /* VLAN tag count        */
> +       unsigned char           reserved1;    /* Reserved              */
> +       __be16                  vlan_enable;  /* VLAN tag enable flags */
> +       __be32                  link_mode;    /* Link setting          */
> +       __be32                  bc_mode;      /* BC filter mode        */
> +       __be32                  valid_modes;  /* Valid mode parameters */
> +       unsigned char           vlan_mode;    /* VLAN mode             */
> +       unsigned char           fc_mode;      /* Flow control mode     */
> +       unsigned char           reserved2[2]; /* Reserved              */
> +       __be32                  aen_mode;     /* AEN mode              */
> +       unsigned char           mac[6];       /* Supported MAC addr    */
> +       __be16                  vlan;         /* Supported VLAN tags   */
> +       __be32                  checksum;     /* Checksum              */
> +};
> +
> +/* Get Controller Packet Statistics */
> +struct ncsi_rsp_gcps_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;            /* Response header            */
> +       __be32                  cnt_hi;         /* Counter cleared            */
> +       __be32                  cnt_lo;         /* Counter cleared            */
> +       __be32                  rx_bytes;       /* Rx bytes                   */
> +       __be32                  tx_bytes;       /* Tx bytes                   */
> +       __be32                  rx_uc_pkts;     /* Rx UC packets              */
> +       __be32                  rx_mc_pkts;     /* Rx MC packets              */
> +       __be32                  rx_bc_pkts;     /* Rx BC packets              */
> +       __be32                  tx_uc_pkts;     /* Tx UC packets              */
> +       __be32                  tx_mc_pkts;     /* Tx MC packets              */
> +       __be32                  tx_bc_pkts;     /* Tx BC packets              */
> +       __be32                  fcs_err;        /* FCS errors                 */
> +       __be32                  align_err;      /* Alignment errors           */
> +       __be32                  false_carrier;  /* False carrier detection    */
> +       __be32                  runt_pkts;      /* Rx runt packets            */
> +       __be32                  jabber_pkts;    /* Rx jabber packets          */
> +       __be32                  rx_pause_xon;   /* Rx pause XON frames        */
> +       __be32                  rx_pause_xoff;  /* Rx XOFF frames             */
> +       __be32                  tx_pause_xon;   /* Tx XON frames              */
> +       __be32                  tx_pause_xoff;  /* Tx XOFF frames             */
> +       __be32                  tx_s_collision; /* Single collision frames    */
> +       __be32                  tx_m_collision; /* Multiple collision frames  */
> +       __be32                  l_collision;    /* Late collision frames      */
> +       __be32                  e_collision;    /* Excessive collision frames */
> +       __be32                  rx_ctl_frames;  /* Rx control frames          */
> +       __be32                  rx_64_frames;   /* Rx 64-bytes frames         */
> +       __be32                  rx_127_frames;  /* Rx 65-127 bytes frames     */
> +       __be32                  rx_255_frames;  /* Rx 128-255 bytes frames    */
> +       __be32                  rx_511_frames;  /* Rx 256-511 bytes frames    */
> +       __be32                  rx_1023_frames; /* Rx 512-1023 bytes frames   */
> +       __be32                  rx_1522_frames; /* Rx 1024-1522 bytes frames  */
> +       __be32                  rx_9022_frames; /* Rx 1523-9022 bytes frames  */
> +       __be32                  tx_64_frames;   /* Tx 64-bytes frames         */
> +       __be32                  tx_127_frames;  /* Tx 65-127 bytes frames     */
> +       __be32                  tx_255_frames;  /* Tx 128-255 bytes frames    */
> +       __be32                  tx_511_frames;  /* Tx 256-511 bytes frames    */
> +       __be32                  tx_1023_frames; /* Tx 512-1023 bytes frames   */
> +       __be32                  tx_1522_frames; /* Tx 1024-1522 bytes frames  */
> +       __be32                  tx_9022_frames; /* Tx 1523-9022 bytes frames  */
> +       __be32                  rx_valid_bytes; /* Rx valid bytes             */
> +       __be32                  rx_runt_pkts;   /* Rx error runt packets      */
> +       __be32                  rx_jabber_pkts; /* Rx error jabber packets    */
> +       __be32                  checksum;       /* Checksum                   */
> +};
> +
> +/* Get NCSI Statistics */
> +struct ncsi_rsp_gns_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;           /* Response header         */
> +       __be32                  rx_cmds;       /* Rx NCSI commands        */
> +       __be32                  dropped_cmds;  /* Dropped commands        */
> +       __be32                  cmd_type_errs; /* Command type errors     */
> +       __be32                  cmd_csum_errs; /* Command checksum errors */
> +       __be32                  rx_pkts;       /* Rx NCSI packets         */
> +       __be32                  tx_pkts;       /* Tx NCSI packets         */
> +       __be32                  tx_aen_pkts;   /* Tx AEN packets          */
> +       __be32                  checksum;      /* Checksum                */
> +};
> +
> +/* Get NCSI Pass-through Statistics */
> +struct ncsi_rsp_gnpts_pkt {
> +       struct ncsi_rsp_pkt_hdr rsp;            /* Response header     */
> +       __be32                  tx_pkts;        /* Tx packets          */
> +       __be32                  tx_dropped;     /* Tx dropped packets  */
> +       __be32                  tx_channel_err; /* Tx channel errors   */
> +       __be32                  tx_us_err;      /* Tx undersize errors */
> +       __be32                  rx_pkts;        /* Rx packets          */
> +       __be32                  rx_dropped;     /* Rx dropped packets  */
> +       __be32                  rx_channel_err; /* Rx channel errors   */
> +       __be32                  rx_us_err;      /* Rx undersize errors */
> +       __be32                  rx_os_err;      /* Rx oversize errors  */
> +       __be32                  checksum;       /* Checksum            */
> +};
> +
>  /* NCSI packet revision */
>  #define NCSI_PKT_REVISION      0x01
>
> @@ -165,4 +314,46 @@ struct ncsi_cmd_snfc_pkt {
>  #define NCSI_PKT_CMD_GNPTS     0x1a    /* Get NCSI Pass-throu Statistics   */
>  #define NCSI_PKT_CMD_OEM       0x50    /* OEM                              */
>
> +/* NCSI packet responses */
> +#define NCSI_PKT_RSP_CIS       (NCSI_PKT_CMD_CIS   + 0x80)
> +#define NCSI_PKT_RSP_SP                (NCSI_PKT_CMD_SP    + 0x80)
> +#define NCSI_PKT_RSP_DP                (NCSI_PKT_CMD_DP    + 0x80)
> +#define NCSI_PKT_RSP_EC                (NCSI_PKT_CMD_EC    + 0x80)
> +#define NCSI_PKT_RSP_DC                (NCSI_PKT_CMD_DC    + 0x80)
> +#define NCSI_PKT_RSP_RC                (NCSI_PKT_CMD_RC    + 0x80)
> +#define NCSI_PKT_RSP_ECNT      (NCSI_PKT_CMD_ECNT  + 0x80)
> +#define NCSI_PKT_RSP_DCNT      (NCSI_PKT_CMD_DCNT  + 0x80)
> +#define NCSI_PKT_RSP_AE                (NCSI_PKT_CMD_AE    + 0x80)
> +#define NCSI_PKT_RSP_SL                (NCSI_PKT_CMD_SL    + 0x80)
> +#define NCSI_PKT_RSP_GLS       (NCSI_PKT_CMD_GLS   + 0x80)
> +#define NCSI_PKT_RSP_SVF       (NCSI_PKT_CMD_SVF   + 0x80)
> +#define NCSI_PKT_RSP_EV                (NCSI_PKT_CMD_EV    + 0x80)
> +#define NCSI_PKT_RSP_DV                (NCSI_PKT_CMD_DV    + 0x80)
> +#define NCSI_PKT_RSP_SMA       (NCSI_PKT_CMD_SMA   + 0x80)
> +#define NCSI_PKT_RSP_EBF       (NCSI_PKT_CMD_EBF   + 0x80)
> +#define NCSI_PKT_RSP_DBF       (NCSI_PKT_CMD_DBF   + 0x80)
> +#define NCSI_PKT_RSP_EGMF      (NCSI_PKT_CMD_EGMF  + 0x80)
> +#define NCSI_PKT_RSP_DGMF      (NCSI_PKT_CMD_DGMF  + 0x80)
> +#define NCSI_PKT_RSP_SNFC      (NCSI_PKT_CMD_SNFC  + 0x80)
> +#define NCSI_PKT_RSP_GVI       (NCSI_PKT_CMD_GVI   + 0x80)
> +#define NCSI_PKT_RSP_GC                (NCSI_PKT_CMD_GC    + 0x80)
> +#define NCSI_PKT_RSP_GP                (NCSI_PKT_CMD_GP    + 0x80)
> +#define NCSI_PKT_RSP_GCPS      (NCSI_PKT_CMD_GCPS  + 0x80)
> +#define NCSI_PKT_RSP_GNS       (NCSI_PKT_CMD_GNS   + 0x80)
> +#define NCSI_PKT_RSP_GNPTS     (NCSI_PKT_CMD_GNPTS + 0x80)
> +#define NCSI_PKT_RSP_OEM       (NCSI_PKT_CMD_OEM   + 0x80)
> +
> +/* NCSI response code/reason */
> +#define NCSI_PKT_RSP_C_COMPLETED       0x0000  /* Command Completed        */
> +#define NCSI_PKT_RSP_C_FAILED          0x0001  /* Command Failed           */
> +#define NCSI_PKT_RSP_C_UNAVAILABLE     0x0002  /* Command Unavailable      */
> +#define NCSI_PKT_RSP_C_UNSUPPORTED     0x0003  /* Command Unsupported      */
> +#define NCSI_PKT_RSP_R_NO_ERROR                0x0000  /* No Error                 */
> +#define NCSI_PKT_RSP_R_INTERFACE       0x0001  /* Interface not ready      */
> +#define NCSI_PKT_RSP_R_PARAM           0x0002  /* Invalid Parameter        */
> +#define NCSI_PKT_RSP_R_CHANNEL         0x0003  /* Channel not Ready        */
> +#define NCSI_PKT_RSP_R_PACKAGE         0x0004  /* Package not Ready        */
> +#define NCSI_PKT_RSP_R_LENGTH          0x0005  /* Invalid payload length   */
> +#define NCSI_PKT_RSP_R_UNKNOWN         0x7fff  /* Command type unsupported */
> +
>  #endif /* __NCSI_PKT_H__ */
> diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
> new file mode 100644
> index 0000000..257079fc
> --- /dev/null
> +++ b/net/ncsi/ncsi-rsp.c
> @@ -0,0 +1,1159 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include <net/ncsi.h>
> +#include <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "internal.h"
> +#include "ncsi-pkt.h"
> +
> +static int ncsi_validate_rsp_pkt(struct ncsi_req *nr,
> +                                unsigned short payload)
> +{
> +       struct ncsi_rsp_pkt_hdr *h;
> +       unsigned char *stream;
> +       __be32 *checksum, csum;
> +       __be32 high, low;
> +       int i;
> +
> +       /* Check NCSI packet header. We don't need validate
> +        * the packet type, which should have been checked
> +        * before calling this function.
> +        */
> +       h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->nr_rsp);
> +       if (h->common.revision != NCSI_PKT_REVISION)
> +               return -EINVAL;
> +       if (ntohs(h->common.length) != payload)
> +               return -EINVAL;
> +
> +       /* Check on code and reason */
> +       if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
> +           ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
> +               return -EINVAL;
> +
> +       /* Validate checksum, which might be zeroes if the
> +        * sender doesn't support checksum according to NCSI
> +        * specification.
> +        */
> +       checksum = (__be32 *)((void *)(h + 1) + payload - 4);
> +       if (ntohl(*checksum) == 0)
> +               return 0;
> +
> +       csum = 0;
> +       stream = (unsigned char *)h;
> +       for (i = 0; i < sizeof(*h) + payload - 4; i += 2) {
> +               high = stream[i];
> +               low = stream[i + 1];
> +               csum += ((high << 8) | low);
> +       }
> +
> +       csum = ~csum + 1;

This chunk is also in patch 2. Perhaps a function?

> +       if (*checksum != htonl(csum))
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_default(struct ncsi_req *nr)
> +{
> +       return ncsi_validate_rsp_pkt(nr, 0);
> +}
> +
> +static int ncsi_rsp_handler_cis(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       unsigned char id;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
> +       if (!nc) {
> +               id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
> +               nc = ncsi_add_channel(np, id);
> +       } else if (nc->nc_state == ncsi_channel_state_deselected_initial) {
> +               nc->nc_state = ncsi_channel_state_deselected_ready;
> +       } else if (nc->nc_state == ncsi_channel_state_selected_initial) {
> +               nc->nc_state = ncsi_channel_state_selected_ready;
> +       }
> +
> +       return nc ? 0 : -ENODEV;
> +}
> +
> +static int ncsi_rsp_handler_sp(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       unsigned char id;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);

What's the 4?

> +       if (ret)
> +               return ret;
> +
> +       /* Add the package if it's not existing. Otherwise,
> +        * to change the state of its child channels.
> +        */

Perhaps:

 /*
  * Add the package if it does not exist. Otherwise change the
  * state of the package's channels.
  */

> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     &np, NULL);
> +       if (!np) {
> +               id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
> +               np = ncsi_add_package(ndp, id);
> +               if (!np)
> +                       return -ENODEV;

I recall when doing bring up I think this was a location where we
would fail to find anything. Should we add some error messages instead
of returning silently?

I think you should be able to reproduce by having the pinmux incorrectly set.

> +       }
> +
> +       NCSI_FOR_EACH_CHANNEL(np, nc) {
> +               if (nc->nc_state == ncsi_channel_state_deselected_initial)
> +                       nc->nc_state = ncsi_channel_state_selected_initial;
> +               else if (nc->nc_state == ncsi_channel_state_deselected_ready)
> +                       nc->nc_state = ncsi_channel_state_selected_ready;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dp(struct ncsi_req *nr)

What's dp? Depopulate?

> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     &np, NULL);
> +       if (!np)
> +               return -ENODEV;
> +
> +       /* Change state of all channels attached to the package */
> +       NCSI_FOR_EACH_CHANNEL(np, nc) {
> +               if (nc->nc_state == ncsi_channel_state_selected_initial)
> +                       nc->nc_state = ncsi_channel_state_deselected_initial;
> +               else if (nc->nc_state == ncsi_channel_state_selected_ready)
> +                       nc->nc_state = ncsi_channel_state_deselected_ready;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_ec(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       ncm = &nc->nc_modes[NCSI_MODE_ENABLE];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       ncm->ncm_enable = 1;
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dc(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       ncm = &nc->nc_modes[NCSI_MODE_ENABLE];
> +       if (!ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       ncm->ncm_enable = 0;
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_rc(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update state for the specified channel */
> +       if (nc->nc_state == ncsi_channel_state_deselected_ready)
> +               nc->nc_state = ncsi_channel_state_deselected_initial;
> +       else if (nc->nc_state == ncsi_channel_state_selected_ready)
> +               nc->nc_state = ncsi_channel_state_selected_initial;
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_ecnt(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       ncm = &nc->nc_modes[NCSI_MODE_TX_ENABLE];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       ncm->ncm_enable = 1;
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dcnt(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       ncm = &nc->nc_modes[NCSI_MODE_TX_ENABLE];
> +       if (!ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       ncm->ncm_enable = 1;
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_ae(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_ae_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if the AEN has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_AEN];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to AEN configuration */
> +       cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm->ncm_enable = 1;
> +       ncm->ncm_data[0] = cmd->mc_id;
> +       ncm->ncm_data[1] = ntohl(cmd->mode);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_sl(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_sl_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       cmd = (struct ncsi_cmd_sl_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm = &nc->nc_modes[NCSI_MODE_LINK];
> +       ncm->ncm_data[0] = ntohl(cmd->mode);
> +       ncm->ncm_data[1] = ntohl(cmd->oem_mode);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gls(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gls_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 16);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       ncm = &nc->nc_modes[NCSI_MODE_LINK];
> +       ncm->ncm_data[2] = ntohl(rsp->status);
> +       ncm->ncm_data[3] = ntohl(rsp->other);
> +       ncm->ncm_data[4] = ntohl(rsp->oem_status);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_svf(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_svf_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_filter *ncf;
> +       unsigned short vlan;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->nr_cmd);
> +       ncf = nc->nc_filters[NCSI_FILTER_VLAN];
> +       if (!ncf)
> +               return -ENOENT;
> +       if (cmd->index >= ncf->ncf_total)
> +               return -ERANGE;
> +
> +       /* Add or remove the VLAN filter */
> +       if (!(cmd->enable & 0x1)) {
> +               ret = ncsi_del_channel_filter(nc, NCSI_FILTER_VLAN, cmd->index);
> +       } else {
> +               vlan = ntohs(cmd->vlan);
> +               ret = ncsi_add_channel_filter(nc, NCSI_FILTER_VLAN, &vlan);
> +       }
> +
> +       return ret;
> +}
> +
> +static int ncsi_rsp_handler_ev(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_ev_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if VLAN mode has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_VLAN];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to VLAN mode */
> +       cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm->ncm_enable = 1;
> +       ncm->ncm_data[0] = ntohl(cmd->mode);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dv(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if VLAN mode has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_VLAN];
> +       if (!ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to VLAN mode */
> +       ncm->ncm_enable = 0;
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_sma(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_sma_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_filter *ncf;
> +       void *bitmap;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* According to NCSI spec 1.01, the mixed filter table
> +        * isn't supported yet.
> +        */
> +       cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->nr_cmd);
> +       switch (cmd->at_e >> 5) {
> +       case 0x0:       /* UC address */
> +               ncf = nc->nc_filters[NCSI_FILTER_UC];
> +               break;
> +       case 0x1:       /* MC address */
> +               ncf = nc->nc_filters[NCSI_FILTER_MC];
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* Sanity check on the filter */
> +       if (!ncf)
> +               return -ENOENT;
> +       else if (cmd->index >= ncf->ncf_total)
> +               return -ERANGE;
> +
> +       bitmap = &ncf->ncf_bitmap;
> +       if (cmd->at_e & 0x1) {
> +               if (test_and_set_bit(cmd->index, bitmap))
> +                       return -EBUSY;
> +               memcpy(ncf->ncf_data + 6 * cmd->index, cmd->mac, 6);
> +       } else {
> +               if (!test_and_clear_bit(cmd->index, bitmap))
> +                       return -EBUSY;
> +
> +               memset(ncf->ncf_data + 6 * cmd->index, 0, 6);
> +       }
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_ebf(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_ebf_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the package and channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if broadcast filter has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_BC];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to broadcast filter mode */
> +       cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm->ncm_enable = 1;
> +       ncm->ncm_data[0] = ntohl(cmd->mode);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dbf(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if broadcast filter isn't enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_BC];
> +       if (!ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to broadcast filter mode */
> +       ncm->ncm_enable = 0;
> +       ncm->ncm_data[0] = 0;
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_egmf(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_egmf_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if multicast filter has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_MC];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to multicast filter mode */
> +       cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm->ncm_enable = 1;
> +       ncm->ncm_data[0] = ntohl(cmd->mode);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_dgmf(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if multicast filter has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_MC];
> +       if (!ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to multicast filter mode */
> +       ncm->ncm_enable = 0;
> +       ncm->ncm_data[0] = 0;
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_snfc(struct ncsi_req *nr)
> +{
> +       struct ncsi_cmd_snfc_pkt *cmd;
> +       struct ncsi_rsp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Check if flow control has been enabled */
> +       ncm = &nc->nc_modes[NCSI_MODE_FC];
> +       if (ncm->ncm_enable)
> +               return -EBUSY;
> +
> +       /* Update to flow control mode */
> +       cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->nr_cmd);
> +       ncm->ncm_enable = 1;
> +       ncm->ncm_data[0] = cmd->mode;
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gvi(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gvi_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_version *ncv;
> +       int i, ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 36);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gvi_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update to channel's version info */
> +       ncv = &nc->nc_version;
> +       ncv->ncv_version = ntohl(rsp->ncsi_version);
> +       ncv->ncv_alpha2 = rsp->alpha2;
> +       memcpy(ncv->ncv_fw_name, rsp->fw_name, 12);
> +       ncv->ncv_fw_version = ntohl(rsp->fw_version);
> +       for (i = 0; i < 4; i++)

ARRAY_SIZE(ncv->ncv_pci_ids) instead of 4

> +               ncv->ncv_pci_ids[i] = ntohs(rsp->pci_ids[i]);
> +       ncv->ncv_mf_id = ntohl(rsp->mf_id);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gc(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gc_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_filter *ncf;
> +       size_t size, entry_size;
> +       int cnt, i, ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 32);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update channel's capabilities */
> +       nc->nc_caps[NCSI_CAP_GENERIC].ncc_cap = ntohl(rsp->cap) &
> +                                               NCSI_CAP_GENERIC_MASK;
> +       nc->nc_caps[NCSI_CAP_BC].ncc_cap = ntohl(rsp->bc_cap) &
> +                                          NCSI_CAP_BC_MASK;
> +       nc->nc_caps[NCSI_CAP_MC].ncc_cap = ntohl(rsp->mc_cap) &
> +                                          NCSI_CAP_MC_MASK;
> +       nc->nc_caps[NCSI_CAP_BUFFER].ncc_cap = ntohl(rsp->buf_cap);
> +       nc->nc_caps[NCSI_CAP_AEN].ncc_cap = ntohl(rsp->aen_cap) &
> +                                           NCSI_CAP_AEN_MASK;
> +       nc->nc_caps[NCSI_CAP_VLAN].ncc_cap = rsp->vlan_mode &
> +                                            NCSI_CAP_VLAN_MASK;
> +
> +       /* Build filters */
> +       for (i = 0; i < NCSI_FILTER_MAX; i++) {
> +               switch (i) {
> +               case NCSI_FILTER_VLAN:
> +                       cnt = rsp->vlan_cnt;
> +                       entry_size = 2;
> +                       break;
> +               case NCSI_FILTER_MIXED:
> +                       cnt = rsp->mixed_cnt;
> +                       entry_size = 6;
> +                       break;
> +               case NCSI_FILTER_MC:
> +                       cnt = rsp->mc_cnt;
> +                       entry_size = 6;
> +                       break;
> +               case NCSI_FILTER_UC:
> +                       cnt = rsp->uc_cnt;
> +                       entry_size = 6;
> +                       break;
> +               default:
> +                       continue;
> +               }
> +
> +               if (!cnt || nc->nc_filters[i])
> +                       continue;
> +
> +               size = sizeof(*ncf) + cnt * entry_size;
> +               ncf = kzalloc(size, GFP_ATOMIC);
> +               if (!ncf) {
> +                       pr_warn("%s: Cannot alloc filter table (%d)\n",
> +                               __func__, i);
> +                       return -ENOMEM;
> +               }
> +
> +               ncf->ncf_index = i;
> +               ncf->ncf_total = cnt;
> +               ncf->ncf_bitmap = 0x0ul;
> +               nc->nc_filters[i] = ncf;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gp(struct ncsi_req *nr)
> +{
> +       struct ncsi_pkt_hdr *h;
> +       struct ncsi_rsp_gp_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       unsigned short length, enable, vlan;
> +       unsigned char *pdata;
> +       int table, i, ret;
> +
> +       /* The get parameter response packet has variable length.
> +        * The payload should be figured out from the packet
> +        * header, instead of the fixed one we have for other types
> +        * of packets.
> +        */
> +       h = (struct ncsi_pkt_hdr *)skb_network_header(nr->nr_rsp);
> +       length = ntohs(h->length);
> +       if (length < 32)
> +               return -EINVAL;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, length);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Modes with explicit enabled indications */
> +       if (ntohl(rsp->valid_modes) & 0x1) {    /* BC filter mode */
> +               nc->nc_modes[NCSI_MODE_BC].ncm_enable = 1;
> +               nc->nc_modes[NCSI_MODE_BC].ncm_data[0] = ntohl(rsp->bc_mode);
> +       }
> +       if (ntohl(rsp->valid_modes) & 0x2)      /* Channel enabled */
> +               nc->nc_modes[NCSI_MODE_ENABLE].ncm_enable = 1;
> +       if (ntohl(rsp->valid_modes) & 0x4)      /* Channel Tx enabled */
> +               nc->nc_modes[NCSI_MODE_TX_ENABLE].ncm_enable = 1;
> +       if (ntohl(rsp->valid_modes) & 0x8)      /* MC filter mode */
> +               nc->nc_modes[NCSI_MODE_MC].ncm_enable = 1;
> +
> +       /* Modes without explicit enabled indications */
> +       nc->nc_modes[NCSI_MODE_LINK].ncm_enable = 1;
> +       nc->nc_modes[NCSI_MODE_LINK].ncm_data[0] = ntohl(rsp->link_mode);
> +       nc->nc_modes[NCSI_MODE_VLAN].ncm_enable = 1;
> +       nc->nc_modes[NCSI_MODE_VLAN].ncm_data[0] = rsp->vlan_mode;
> +       nc->nc_modes[NCSI_MODE_FC].ncm_enable = 1;
> +       nc->nc_modes[NCSI_MODE_FC].ncm_data[0] = rsp->fc_mode;
> +       nc->nc_modes[NCSI_MODE_AEN].ncm_enable = 1;
> +       nc->nc_modes[NCSI_MODE_AEN].ncm_data[0] = ntohl(rsp->aen_mode);
> +
> +       /* MAC addresses filter table */
> +       pdata = (unsigned char *)rsp + 48;
> +       enable = rsp->mac_enable;
> +       for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
> +               if (i >= (nc->nc_filters[NCSI_FILTER_UC]->ncf_total +
> +                         nc->nc_filters[NCSI_FILTER_MC]->ncf_total))
> +                       table = NCSI_FILTER_MIXED;
> +               else if (i >= nc->nc_filters[NCSI_FILTER_UC]->ncf_total)
> +                       table = NCSI_FILTER_MC;
> +               else
> +                       table = NCSI_FILTER_UC;
> +
> +               if (!(enable & (0x1 << i)))
> +                       continue;
> +
> +               if (ncsi_find_channel_filter(nc, table, pdata) >= 0)
> +                       continue;
> +
> +               ncsi_add_channel_filter(nc, table, pdata);
> +       }
> +
> +       /* VLAN filter table */
> +       enable = ntohs(rsp->vlan_enable);
> +       for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
> +               if (!(enable & (0x1 << i)))
> +                       continue;
> +
> +               vlan = ntohs(*(__be16 *)pdata);
> +               if (ncsi_find_channel_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
> +                       continue;
> +
> +               ncsi_add_channel_filter(nc, NCSI_FILTER_VLAN, &vlan);
> +       }
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gcps(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gcps_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_stats *ncs;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 172);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gcps_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update HNC's statistics */
> +       ncs = &nc->nc_stats;
> +       ncs->ncs_hnc_cnt_hi         = ntohl(rsp->cnt_hi);
> +       ncs->ncs_hnc_cnt_lo         = ntohl(rsp->cnt_lo);
> +       ncs->ncs_hnc_rx_bytes       = ntohl(rsp->rx_bytes);
> +       ncs->ncs_hnc_tx_bytes       = ntohl(rsp->tx_bytes);
> +       ncs->ncs_hnc_rx_uc_pkts     = ntohl(rsp->rx_uc_pkts);
> +       ncs->ncs_hnc_rx_mc_pkts     = ntohl(rsp->rx_mc_pkts);
> +       ncs->ncs_hnc_rx_bc_pkts     = ntohl(rsp->rx_bc_pkts);
> +       ncs->ncs_hnc_tx_uc_pkts     = ntohl(rsp->tx_uc_pkts);
> +       ncs->ncs_hnc_tx_mc_pkts     = ntohl(rsp->tx_mc_pkts);
> +       ncs->ncs_hnc_tx_bc_pkts     = ntohl(rsp->tx_bc_pkts);
> +       ncs->ncs_hnc_fcs_err        = ntohl(rsp->fcs_err);
> +       ncs->ncs_hnc_align_err      = ntohl(rsp->align_err);
> +       ncs->ncs_hnc_false_carrier  = ntohl(rsp->false_carrier);
> +       ncs->ncs_hnc_runt_pkts      = ntohl(rsp->runt_pkts);
> +       ncs->ncs_hnc_jabber_pkts    = ntohl(rsp->jabber_pkts);
> +       ncs->ncs_hnc_rx_pause_xon   = ntohl(rsp->rx_pause_xon);
> +       ncs->ncs_hnc_rx_pause_xoff  = ntohl(rsp->rx_pause_xoff);
> +       ncs->ncs_hnc_tx_pause_xon   = ntohl(rsp->tx_pause_xon);
> +       ncs->ncs_hnc_tx_pause_xoff  = ntohl(rsp->tx_pause_xoff);
> +       ncs->ncs_hnc_tx_s_collision = ntohl(rsp->tx_s_collision);
> +       ncs->ncs_hnc_tx_m_collision = ntohl(rsp->tx_m_collision);
> +       ncs->ncs_hnc_l_collision    = ntohl(rsp->l_collision);
> +       ncs->ncs_hnc_e_collision    = ntohl(rsp->e_collision);
> +       ncs->ncs_hnc_rx_ctl_frames  = ntohl(rsp->rx_ctl_frames);
> +       ncs->ncs_hnc_rx_64_frames   = ntohl(rsp->rx_64_frames);
> +       ncs->ncs_hnc_rx_127_frames  = ntohl(rsp->rx_127_frames);
> +       ncs->ncs_hnc_rx_255_frames  = ntohl(rsp->rx_255_frames);
> +       ncs->ncs_hnc_rx_511_frames  = ntohl(rsp->rx_511_frames);
> +       ncs->ncs_hnc_rx_1023_frames = ntohl(rsp->rx_1023_frames);
> +       ncs->ncs_hnc_rx_1522_frames = ntohl(rsp->rx_1522_frames);
> +       ncs->ncs_hnc_rx_9022_frames = ntohl(rsp->rx_9022_frames);
> +       ncs->ncs_hnc_tx_64_frames   = ntohl(rsp->tx_64_frames);
> +       ncs->ncs_hnc_tx_127_frames  = ntohl(rsp->tx_127_frames);
> +       ncs->ncs_hnc_tx_255_frames  = ntohl(rsp->tx_255_frames);
> +       ncs->ncs_hnc_tx_511_frames  = ntohl(rsp->tx_511_frames);
> +       ncs->ncs_hnc_tx_1023_frames = ntohl(rsp->tx_1023_frames);
> +       ncs->ncs_hnc_tx_1522_frames = ntohl(rsp->tx_1522_frames);
> +       ncs->ncs_hnc_tx_9022_frames = ntohl(rsp->tx_9022_frames);
> +       ncs->ncs_hnc_rx_valid_bytes = ntohl(rsp->rx_valid_bytes);
> +       ncs->ncs_hnc_rx_runt_pkts   = ntohl(rsp->rx_runt_pkts);
> +       ncs->ncs_hnc_rx_jabber_pkts = ntohl(rsp->rx_jabber_pkts);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gns(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gns_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_stats *ncs;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 172);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gns_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update HNC's statistics */
> +       ncs = &nc->nc_stats;
> +       ncs->ncs_ncsi_rx_cmds       = ntohl(rsp->rx_cmds);
> +       ncs->ncs_ncsi_dropped_cmds  = ntohl(rsp->dropped_cmds);
> +       ncs->ncs_ncsi_cmd_type_errs = ntohl(rsp->cmd_type_errs);
> +       ncs->ncs_ncsi_cmd_csum_errs = ntohl(rsp->cmd_csum_errs);
> +       ncs->ncs_ncsi_rx_pkts       = ntohl(rsp->rx_pkts);
> +       ncs->ncs_ncsi_tx_pkts       = ntohl(rsp->tx_pkts);
> +       ncs->ncs_ncsi_tx_aen_pkts   = ntohl(rsp->tx_aen_pkts);
> +
> +       return 0;
> +}
> +
> +static int ncsi_rsp_handler_gnpts(struct ncsi_req *nr)
> +{
> +       struct ncsi_rsp_gnpts_pkt *rsp;
> +       struct ncsi_dev_priv *ndp = nr->nr_ndp;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_stats *ncs;
> +       int ret;
> +
> +       ret = ncsi_validate_rsp_pkt(nr, 172);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the channel */
> +       rsp = (struct ncsi_rsp_gnpts_pkt *)skb_network_header(nr->nr_rsp);
> +       ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
> +                                     NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update HNC's statistics */
> +       ncs = &nc->nc_stats;
> +       ncs->ncs_pt_tx_pkts        = ntohl(rsp->tx_pkts);
> +       ncs->ncs_pt_tx_dropped     = ntohl(rsp->tx_dropped);
> +       ncs->ncs_pt_tx_channel_err = ntohl(rsp->tx_channel_err);
> +       ncs->ncs_pt_tx_us_err      = ntohl(rsp->tx_us_err);
> +       ncs->ncs_pt_rx_pkts        = ntohl(rsp->rx_pkts);
> +       ncs->ncs_pt_rx_dropped     = ntohl(rsp->rx_dropped);
> +       ncs->ncs_pt_rx_channel_err = ntohl(rsp->rx_channel_err);
> +       ncs->ncs_pt_rx_us_err      = ntohl(rsp->rx_us_err);
> +       ncs->ncs_pt_rx_os_err      = ntohl(rsp->rx_os_err);
> +
> +       return 0;
> +}
> +
> +static struct ncsi_rsp_handler {
> +       unsigned char   nrh_type;
> +       int             (*nrh_handler)(struct ncsi_req *nr);
> +} ncsi_rsp_handlers[] = {
> +       { NCSI_PKT_RSP_CIS,   ncsi_rsp_handler_cis     },
> +       { NCSI_PKT_RSP_SP,    ncsi_rsp_handler_sp      },
> +       { NCSI_PKT_RSP_DP,    ncsi_rsp_handler_dp      },
> +       { NCSI_PKT_RSP_EC,    ncsi_rsp_handler_ec      },
> +       { NCSI_PKT_RSP_DC,    ncsi_rsp_handler_dc      },
> +       { NCSI_PKT_RSP_RC,    ncsi_rsp_handler_rc      },
> +       { NCSI_PKT_RSP_ECNT,  ncsi_rsp_handler_ecnt    },
> +       { NCSI_PKT_RSP_DCNT,  ncsi_rsp_handler_dcnt    },
> +       { NCSI_PKT_RSP_AE,    ncsi_rsp_handler_ae      },
> +       { NCSI_PKT_RSP_SL,    ncsi_rsp_handler_sl      },
> +       { NCSI_PKT_RSP_GLS,   ncsi_rsp_handler_gls     },
> +       { NCSI_PKT_RSP_SVF,   ncsi_rsp_handler_svf     },
> +       { NCSI_PKT_RSP_EV,    ncsi_rsp_handler_ev      },
> +       { NCSI_PKT_RSP_DV,    ncsi_rsp_handler_dv      },
> +       { NCSI_PKT_RSP_SMA,   ncsi_rsp_handler_sma     },
> +       { NCSI_PKT_RSP_EBF,   ncsi_rsp_handler_ebf     },
> +       { NCSI_PKT_RSP_DBF,   ncsi_rsp_handler_dbf     },
> +       { NCSI_PKT_RSP_EGMF,  ncsi_rsp_handler_egmf    },
> +       { NCSI_PKT_RSP_DGMF,  ncsi_rsp_handler_dgmf    },
> +       { NCSI_PKT_RSP_SNFC,  ncsi_rsp_handler_snfc    },
> +       { NCSI_PKT_RSP_GVI,   ncsi_rsp_handler_gvi     },
> +       { NCSI_PKT_RSP_GC,    ncsi_rsp_handler_gc      },
> +       { NCSI_PKT_RSP_GP,    ncsi_rsp_handler_gp      },
> +       { NCSI_PKT_RSP_GCPS,  ncsi_rsp_handler_gcps    },
> +       { NCSI_PKT_RSP_GNS,   ncsi_rsp_handler_gns     },
> +       { NCSI_PKT_RSP_GNPTS, ncsi_rsp_handler_gnpts   },
> +       { NCSI_PKT_RSP_OEM,   ncsi_rsp_handler_default },
> +       { 0,                  NULL                     }
> +};
> +
> +#if 0

Not sure how you will go getting this in. I assume you don't make it
run-time enabled as you're concerned with performance?

> +static void ncsi_pkt_dump(struct sk_buff *skb)
> +{
> +       struct skb_shared_info *info = skb_shinfo(skb);
> +       skb_frag_t *frag;
> +       char *data;
> +       int limit, i;
> +
> +       pr_info("head: 0x%p data: 0x%p tail: 0x%p end: 0x%p\n",
> +               skb->head, skb->data,
> +               skb_tail_pointer(skb), skb_end_pointer(skb));
> +       pr_info("mac_header: 0x%p network_header: 0x%p\n",
> +               skb_mac_header(skb), skb_network_header(skb));
> +       pr_info("len: 0x%x data_len: 0x%x truesize: 0x%x\n",
> +               skb->len, skb->data_len, skb->truesize);
> +
> +       for (i = 0; i < info->nr_frags; i++) {
> +               frag = &info->frags[i];
> +               pr_info("FRAG[%d]: 0x%p offset: 0x%x size: 0x%x\n",
> +                       i, frag->page.p, frag->page_offset, frag->size);
> +       }
> +
> +       data = skb_mac_header(skb);
> +       limit = skb->len + sizeof(struct ethhdr);
> +       for (i = 0; i < limit; data++, i++) {
> +               if (i % 16 == 0)
> +                       printk("\n%02x ", *data);
> +               else
> +                       printk("%02x ", *data);
> +       }
> +}
> +#endif
> +
> +int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
> +                struct packet_type *pt, struct net_device *orig_dev)
> +{
> +       struct ncsi_rsp_handler *nrh = NULL;
> +       struct ncsi_dev *nd;
> +       struct ncsi_dev_priv *ndp;
> +       struct ncsi_req *nr;
> +       struct ncsi_pkt_hdr *hdr;
> +       unsigned long flags;
> +       int ret;
> +
> +       /* Find the NCSI device */
> +       nd = ncsi_find_dev(dev);
> +       ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
> +       if (!ndp)
> +               return -ENODEV;
> +
> +       /* Find the handler */
> +       hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
> +       nrh = ncsi_rsp_handlers;
> +       while (nrh->nrh_handler) {
> +               if (nrh->nrh_type == hdr->type)
> +                       break;
> +
> +               nrh++;
> +       }
> +
> +       if (!nrh->nrh_handler) {
> +               pr_warn("NCSI: Received unrecognized packet (0x%x)\n",
> +                       hdr->type);
> +               return -ENOENT;
> +       }
> +
> +       /* Associate with the request */
> +       nr = &ndp->ndp_reqs[hdr->id];
> +       spin_lock_irqsave(&ndp->ndp_req_lock, flags);
> +       if (!nr->nr_used) {
> +               spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +               return -ENODEV;
> +       }
> +
> +       nr->nr_rsp = skb;
> +       if (!nr->nr_timer_enabled) {
> +               ret = -ENOENT;
> +               goto out;
> +       }
> +
> +       /* Process the response */
> +       ret = nrh->nrh_handler(nr);
> +
> +out:
> +       spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
> +       ncsi_free_req(nr, true, false);
> +       return ret;
> +}
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 04/10] net/ncsi: Package and channel management
  2016-06-30 10:27 ` [PATCH 04/10] net/ncsi: Package and channel management Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> The available NCSI packages and channels are probed with help of
> NCSI command/response packets. One package and channel should be
> selected as active one by which the MC (Management Controller)
> can communicate with the far end and external network. The currently
> active channel should be disabled and another available channel should
> be selected as active one when AEN (Asychronous Event Notification)
> packets received.
>
> This introduces state machine and corresponding functions for above
> purposes. ncsi_start_dev() should be called after ncsi_register_dev()
> in network device driver. It probes all available NCSI packages and
> channels and selects the active one to provide service. ncsi_config_dev()
> and ncsi_suspend_dev() are going to be used when AEN (Asychronous
> Event Notification) packets are received.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  net/ncsi/internal.h    |  34 ++++
>  net/ncsi/ncsi-manage.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++
>  net/ncsi/ncsi-rsp.c    |   6 +
>  3 files changed, 515 insertions(+)
>
> diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
> index d435f91..eeb092a 100644
> --- a/net/ncsi/internal.h
> +++ b/net/ncsi/internal.h
> @@ -203,9 +203,38 @@ struct ncsi_req {
>         bool                 nr_timer_enabled; /* Timer was started   */
>  };
>
> +enum {
> +       ncsi_dev_state_major            = 0xff00,
> +       ncsi_dev_state_minor            = 0x00ff,
> +       ncsi_dev_state_probe_deselect   = 0x0201,
> +       ncsi_dev_state_probe_package,
> +       ncsi_dev_state_probe_channel,
> +       ncsi_dev_state_probe_cis,
> +       ncsi_dev_state_probe_gvi,
> +       ncsi_dev_state_probe_gc,
> +       ncsi_dev_state_probe_gls,
> +       ncsi_dev_state_probe_dp,
> +       ncsi_dev_state_config_sp        = 0x0301,
> +       ncsi_dev_state_config_cis,
> +       ncsi_dev_state_config_sma,
> +       ncsi_dev_state_config_ebf,
> +       ncsi_dev_state_config_ecnt,
> +       ncsi_dev_state_config_ec,
> +       ncsi_dev_state_config_ae,
> +       ncsi_dev_state_config_gls,
> +       ncsi_dev_state_config_done,
> +       ncsi_dev_state_suspend_select   = 0x0401,
> +       ncsi_dev_state_suspend_dcnt,
> +       ncsi_dev_state_suspend_dc,
> +       ncsi_dev_state_suspend_deselect,
> +       ncsi_dev_state_suspend_done
> +};
> +
>  struct ncsi_dev_priv {
>         struct ncsi_dev     ndp_ndev;            /* NCSI device              */
>         int                 ndp_flags;           /* NCSI device flags        */
> +#define NCSI_DEV_FLAG_PROBED   0x1
> +#define NCSI_DEV_FLAG_RESHUFFLE        0x2
>         struct ncsi_package *ndp_active_package; /* Active NCSI package      */
>         struct ncsi_channel *ndp_active_channel; /* Active NCSI channel      */
>         atomic_t            ndp_package_num;     /* Number of packages       */
> @@ -214,6 +243,9 @@ struct ncsi_dev_priv {
>         atomic_t            ndp_last_req_idx;    /* Last used request ID     */
>         spinlock_t          ndp_req_lock;        /* Protect request table    */
>         struct ncsi_req     ndp_reqs[256];       /* Request table            */
> +       atomic_t            ndp_pending_reqs;    /* Pending commands         */
> +       struct work_struct  ndp_work;            /* Workqueue                */
> +       struct packet_type  ndp_ptype;           /* NCSI receive handler     */
>         struct list_head    ndp_node;
>  };
>
> @@ -264,6 +296,8 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
>  struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
>  void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
>  struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
> +int ncsi_config_dev(struct ncsi_dev *nd);
> +int ncsi_suspend_dev(struct ncsi_dev *nd);
>
>  /* Packet handlers */
>  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
> diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
> index dab54bc..37f1b3d 100644
> --- a/net/ncsi/ncsi-manage.c
> +++ b/net/ncsi/ncsi-manage.c
> @@ -19,6 +19,7 @@
>  #include <net/sock.h>
>
>  #include "internal.h"
> +#include "ncsi-pkt.h"
>
>  LIST_HEAD(ncsi_dev_list);
>  DEFINE_SPINLOCK(ncsi_dev_lock);
> @@ -342,6 +343,9 @@ void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
>         nr->nr_used = false;
>         spin_unlock_irqrestore(&ndp->ndp_req_lock, flags);
>
> +       if (check && cmd && atomic_dec_return(&ndp->ndp_pending_reqs) == 0)
> +               schedule_work(&ndp->ndp_work);
> +
>         /* Release command and response */
>         consume_skb(cmd);
>         consume_skb(rsp);
> @@ -380,6 +384,417 @@ static void ncsi_req_timeout(unsigned long data)
>         ncsi_free_req(nr, true, true);
>  }
>
> +static void ncsi_dev_config(struct ncsi_dev_priv *ndp)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct net_device *dev = nd->nd_dev;
> +       struct ncsi_package *np = ndp->ndp_active_package;
> +       struct ncsi_channel *nc = ndp->ndp_active_channel;
> +       struct ncsi_cmd_arg nca;
> +       unsigned char index;
> +       int ret;
> +
> +       nca.nca_ndp = ndp;
> +       switch (nd->nd_state) {
> +       case ncsi_dev_state_config:
> +       case ncsi_dev_state_config_sp:
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +
> +               /* Select the specific package */
> +               nca.nca_type = NCSI_PKT_CMD_SP;
> +               nca.nca_bytes[0] = 1;
> +               nca.nca_package = np->np_id;
> +               nca.nca_channel = 0x1f;
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret)
> +                       goto error;
> +
> +               nd->nd_state = ncsi_dev_state_config_cis;
> +               break;
> +       case ncsi_dev_state_config_cis:
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +
> +               /* Clear initial state */
> +               nca.nca_type = NCSI_PKT_CMD_CIS;
> +               nca.nca_package = np->np_id;
> +               nca.nca_channel = nc->nc_id;
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret)
> +                       goto error;
> +
> +               nd->nd_state = ncsi_dev_state_config_sma;
> +               break;
> +       case ncsi_dev_state_config_sma:
> +       case ncsi_dev_state_config_ebf:
> +       case ncsi_dev_state_config_ecnt:
> +       case ncsi_dev_state_config_ec:
> +       case ncsi_dev_state_config_ae:
> +       case ncsi_dev_state_config_gls:
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +
> +               nca.nca_package = np->np_id;
> +               nca.nca_channel = nc->nc_id;
> +
> +               /* Use first entry in unicast filter table. Note that
> +                * the MAC filter table starts from entry 1 instead of
> +                * 0.
> +                */
> +               if (nd->nd_state == ncsi_dev_state_config_sma) {
> +                       nca.nca_type = NCSI_PKT_CMD_SMA;
> +                       for (index = 0; index < 6; index++)
> +                               nca.nca_bytes[index] = dev->dev_addr[index];
> +                       nca.nca_bytes[6] = 0x1;
> +                       nca.nca_bytes[7] = 0x1;
> +                       nd->nd_state = ncsi_dev_state_config_ebf;
> +               } else if (nd->nd_state == ncsi_dev_state_config_ebf) {
> +                       nca.nca_type = NCSI_PKT_CMD_EBF;
> +                       nca.nca_dwords[0] = nc->nc_caps[NCSI_CAP_BC].ncc_cap;
> +                       nd->nd_state = ncsi_dev_state_config_ecnt;
> +               } else if (nd->nd_state == ncsi_dev_state_config_ecnt) {
> +                       nca.nca_type = NCSI_PKT_CMD_ECNT;
> +                       nd->nd_state = ncsi_dev_state_config_ec;
> +               } else if (nd->nd_state == ncsi_dev_state_config_ec) {
> +                       nca.nca_type = NCSI_PKT_CMD_EC;
> +                       nd->nd_state = ncsi_dev_state_config_ae;
> +               } else if (nd->nd_state == ncsi_dev_state_config_ae) {
> +                       nca.nca_type = NCSI_PKT_CMD_AE;
> +                       nca.nca_bytes[0] = 0;
> +                       nca.nca_dwords[1] = 0x7;
> +                       nd->nd_state = ncsi_dev_state_config_gls;
> +               } else if (nd->nd_state == ncsi_dev_state_config_gls) {
> +                       nca.nca_type = NCSI_PKT_CMD_GLS;
> +                       nd->nd_state = ncsi_dev_state_config_done;
> +               }
> +
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret)
> +                       goto error;
> +               break;
> +       case ncsi_dev_state_config_done:
> +               nd->nd_state = ncsi_dev_state_functional;
> +               nd->nd_link_up = 0;
> +               if (nc->nc_modes[NCSI_MODE_LINK].ncm_data[2] & 0x1)
> +                       nd->nd_link_up = 1;
> +
> +               nd->nd_handler(nd);
> +               ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
> +
> +               break;
> +       default:
> +               pr_warn("NCSI: Wrong NCSI dev state 0x%x in config\n",
> +                       nd->nd_state);
> +       }
> +
> +       return;
> +
> +error:
> +       nd->nd_state = ncsi_dev_state_functional;
> +       nd->nd_link_up = 0;
> +       ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
> +       nd->nd_handler(nd);
> +}
> +
> +static void ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
> +{
> +       struct ncsi_dev *nd;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +
> +       nd = &ndp->ndp_ndev;
> +       ndp->ndp_active_package = NULL;
> +       ndp->ndp_active_channel = NULL;
> +       NCSI_FOR_EACH_PACKAGE(ndp, np) {
> +               NCSI_FOR_EACH_CHANNEL(np, nc) {
> +                       if (!ndp->ndp_active_channel) {
> +                               ndp->ndp_active_package = np;
> +                               ndp->ndp_active_channel = nc;
> +                       }
> +
> +                       ncm = &nc->nc_modes[NCSI_MODE_LINK];
> +                       if (ncm->ncm_data[2] & 0x1) {
> +                               ndp->ndp_active_package = np;
> +                               ndp->ndp_active_channel = nc;
> +                               goto configure;
> +                       }
> +               }
> +       }
> +
> +configure:
> +       if (!ndp->ndp_active_channel) {
> +               nd->nd_state = ncsi_dev_state_functional;
> +               nd->nd_link_up = 0;
> +               nd->nd_handler(nd);
> +       } else {
> +               nd->nd_state = ncsi_dev_state_config;
> +               ncsi_dev_config(ndp);
> +       }
> +}
> +
> +static void ncsi_dev_probe(struct ncsi_dev_priv *ndp)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       struct ncsi_cmd_arg nca;
> +       unsigned char index;
> +       int ret;
> +
> +       nca.nca_ndp = ndp;
> +       switch (nd->nd_state) {
> +       case ncsi_dev_state_probe:
> +               nd->nd_state = ncsi_dev_state_probe_deselect;
> +               /* Fall through */
> +       case ncsi_dev_state_probe_deselect:
> +               atomic_set(&ndp->ndp_pending_reqs, 8);
> +
> +               /* Deselect all possible packages */
> +               nca.nca_type = NCSI_PKT_CMD_DP;
> +               nca.nca_channel = 0x1f;
> +               for (index = 0; index < 8; index++) {
> +                       nca.nca_package = index;
> +                       ret = ncsi_xmit_cmd(&nca);
> +                       if (ret)
> +                               goto error;
> +               }
> +
> +               nd->nd_state = ncsi_dev_state_probe_package;
> +               break;
> +       case ncsi_dev_state_probe_package:
> +               atomic_set(&ndp->ndp_pending_reqs, 16);
> +
> +               /* Select all possible packages */
> +               nca.nca_type = NCSI_PKT_CMD_SP;
> +               nca.nca_bytes[0] = 1;
> +               nca.nca_channel = 0x1f;
> +               for (index = 0; index < 8; index++) {
> +                       nca.nca_package = index;
> +                       ret = ncsi_xmit_cmd(&nca);
> +                       if (ret)
> +                               goto error;
> +               }
> +
> +               /* Disable all possible packages */
> +               nca.nca_type = NCSI_PKT_CMD_DP;
> +               for (index = 0; index < 8; index++) {
> +                       nca.nca_package = index;
> +                       ret = ncsi_xmit_cmd(&nca);
> +                       if (ret)
> +                               goto error;
> +               }
> +
> +               nd->nd_state = ncsi_dev_state_probe_channel;
> +               break;
> +       case ncsi_dev_state_probe_channel:
> +               if (!ndp->ndp_active_package)
> +                       ndp->ndp_active_package = list_first_or_null_rcu(
> +                                                       &ndp->ndp_packages,
> +                                                       struct ncsi_package,
> +                                                       np_node);
> +               else if (list_is_last(&ndp->ndp_active_package->np_node,
> +                                     &ndp->ndp_packages))
> +                       ndp->ndp_active_package = NULL;
> +               else
> +                       ndp->ndp_active_package = list_next_entry(
> +                                                       ndp->ndp_active_package,
> +                                                       np_node);
> +
> +               /* All available packages and channels are enumerated. The
> +                * enumeration happens for once when the NCSI interface is
> +                * started. So we need continue to start the interface after
> +                * the enumeration.
> +                *
> +                * We have to choose an active channel before configuring it.
> +                * Note that we possibly don't have active channel in extreme
> +                * situation.
> +                */
> +               if (!ndp->ndp_active_package) {
> +                       ndp->ndp_flags |= NCSI_DEV_FLAG_PROBED;
> +                       ncsi_choose_active_channel(ndp);
> +                       return;
> +               }
> +
> +               /* Select the active package */
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +               nca.nca_type = NCSI_PKT_CMD_SP;
> +               nca.nca_bytes[0] = 1;
> +               nca.nca_package = ndp->ndp_active_package->np_id;
> +               nca.nca_channel = 0x1f;
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret)
> +                       goto error;
> +
> +               nd->nd_state = ncsi_dev_state_probe_cis;
> +               break;
> +       case ncsi_dev_state_probe_cis:
> +               atomic_set(&ndp->ndp_pending_reqs, 0x20);
> +
> +               /* Clear initial state */
> +               nca.nca_type = NCSI_PKT_CMD_CIS;
> +               nca.nca_package = ndp->ndp_active_package->np_id;
> +               for (index = 0; index < 0x20; index++) {
> +                       nca.nca_channel = index;
> +                       ret = ncsi_xmit_cmd(&nca);
> +                       if (ret)
> +                               goto error;
> +               }
> +
> +               nd->nd_state = ncsi_dev_state_probe_gvi;
> +               break;
> +       case ncsi_dev_state_probe_gvi:
> +       case ncsi_dev_state_probe_gc:
> +       case ncsi_dev_state_probe_gls:
> +               np = ndp->ndp_active_package;
> +               atomic_set(&ndp->ndp_pending_reqs,
> +                          atomic_read(&np->np_channel_num));
> +
> +               /* Retrieve version, capability or link status */
> +               if (nd->nd_state == ncsi_dev_state_probe_gvi)
> +                       nca.nca_type = NCSI_PKT_CMD_GVI;
> +               else if (nd->nd_state == ncsi_dev_state_probe_gc)
> +                       nca.nca_type = NCSI_PKT_CMD_GC;
> +               else
> +                       nca.nca_type = NCSI_PKT_CMD_GLS;
> +
> +               nca.nca_package = np->np_id;
> +               NCSI_FOR_EACH_CHANNEL(np, nc) {
> +                       nca.nca_channel = nc->nc_id;
> +                       ret = ncsi_xmit_cmd(&nca);
> +                       if (ret)
> +                               goto error;
> +               }
> +
> +               if (nd->nd_state == ncsi_dev_state_probe_gvi)
> +                       nd->nd_state = ncsi_dev_state_probe_gc;
> +               else if (nd->nd_state == ncsi_dev_state_probe_gc)
> +                       nd->nd_state = ncsi_dev_state_probe_gls;
> +               else
> +                       nd->nd_state = ncsi_dev_state_probe_dp;
> +               break;
> +       case ncsi_dev_state_probe_dp:
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +
> +               /* Deselect the active package */
> +               nca.nca_type = NCSI_PKT_CMD_DP;
> +               nca.nca_package = ndp->ndp_active_package->np_id;
> +               nca.nca_channel = 0x1f;
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret)
> +                       goto error;
> +
> +               /* Scan channels in next package */
> +               nd->nd_state = ncsi_dev_state_probe_channel;
> +               break;
> +       default:
> +               pr_warn("NCSI: Wrong NCSI dev state 0x%0x in enumeration\n",
> +                       nd->nd_state);
> +       }
> +
> +       return;
> +error:
> +       nd->nd_state = ncsi_dev_state_functional;
> +       nd->nd_link_up = 0;
> +       nd->nd_handler(nd);
> +}
> +
> +static void ncsi_dev_suspend(struct ncsi_dev_priv *ndp)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct ncsi_package *np;
> +       struct ncsi_channel *nc;
> +       struct ncsi_cmd_arg nca;
> +       int ret;
> +
> +       nca.nca_ndp = ndp;
> +       switch (nd->nd_state) {
> +       case ncsi_dev_state_suspend:
> +               /* If there're no active channel, we're done */
> +               if (!ndp->ndp_active_channel) {
> +                       nd->nd_state = ncsi_dev_state_functional;
> +
> +                       if (ndp->ndp_flags & NCSI_DEV_FLAG_RESHUFFLE) {
> +                               ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
> +                               ncsi_choose_active_channel(ndp);
> +                       }
> +
> +                       return;
> +               }
> +
> +               nd->nd_state = ncsi_dev_state_suspend_select;
> +               /* Fall through */
> +       case ncsi_dev_state_suspend_select:
> +       case ncsi_dev_state_suspend_dcnt:
> +       case ncsi_dev_state_suspend_dc:
> +       case ncsi_dev_state_suspend_deselect:
> +               atomic_set(&ndp->ndp_pending_reqs, 1);
> +
> +               np = ndp->ndp_active_package;
> +               nc = ndp->ndp_active_channel;
> +               nca.nca_package = np->np_id;
> +               if (nd->nd_state == ncsi_dev_state_suspend_select) {
> +                       nca.nca_type = NCSI_PKT_CMD_SP;
> +                       nca.nca_channel = 0x1f;
> +                       nca.nca_bytes[0] = 1;
> +                       nd->nd_state = ncsi_dev_state_suspend_dcnt;
> +               } else if (nd->nd_state == ncsi_dev_state_suspend_dcnt) {
> +                       nca.nca_type = NCSI_PKT_CMD_DCNT;
> +                       nca.nca_channel = nc->nc_id;
> +                       nd->nd_state = ncsi_dev_state_suspend_dc;
> +               } else if (nd->nd_state == ncsi_dev_state_suspend_dc) {
> +                       nca.nca_type = NCSI_PKT_CMD_DC;
> +                       nca.nca_channel = nc->nc_id;
> +                       nca.nca_bytes[0] = 1;
> +                       nd->nd_state = ncsi_dev_state_suspend_deselect;
> +               } else if (nd->nd_state == ncsi_dev_state_suspend_deselect) {
> +                       nca.nca_type = NCSI_PKT_CMD_DP;
> +                       nca.nca_channel = 0x1f;
> +                       nd->nd_state = ncsi_dev_state_suspend_done;
> +               }
> +
> +               ret = ncsi_xmit_cmd(&nca);
> +               if (ret) {
> +                       nd->nd_state = ncsi_dev_state_functional;
> +                       return;
> +               }
> +
> +               break;
> +       case ncsi_dev_state_suspend_done:
> +               nd->nd_state = ncsi_dev_state_functional;
> +
> +               if (ndp->ndp_flags & NCSI_DEV_FLAG_RESHUFFLE) {
> +                       ndp->ndp_flags &= ~NCSI_DEV_FLAG_RESHUFFLE;
> +                       ncsi_choose_active_channel(ndp);
> +               }
> +
> +               break;
> +       default:
> +               pr_warn("NCSI: Wrong NCSI dev state 0x%x in suspend\n",
> +                       nd->nd_state);
> +       }
> +}
> +
> +static void ncsi_dev_work(struct work_struct *work)
> +{
> +       struct ncsi_dev_priv *ndp = container_of(work, struct ncsi_dev_priv,
> +                                                ndp_work);
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +
> +       switch (nd->nd_state & ncsi_dev_state_major) {
> +       case ncsi_dev_state_probe:
> +               ncsi_dev_probe(ndp);
> +               break;
> +       case ncsi_dev_state_suspend:
> +               ncsi_dev_suspend(ndp);
> +               break;
> +       case ncsi_dev_state_config:
> +               ncsi_dev_config(ndp);
> +               break;
> +       default:
> +               pr_warn("NCSI: Wrong NCSI dev state 0x%x in workqueue\n",
> +                       nd->nd_state);
> +       }
> +}
> +
>  struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
>                                    void (*handler)(struct ncsi_dev *ndev))
>  {
> @@ -414,19 +829,79 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
>                             (unsigned long)&ndp->ndp_reqs[idx]);
>         }
>
> +       atomic_set(&ndp->ndp_pending_reqs, 0);
> +       INIT_WORK(&ndp->ndp_work, ncsi_dev_work);
> +
>         spin_lock(&ncsi_dev_lock);
>         list_add_tail_rcu(&ndp->ndp_node, &ncsi_dev_list);
>         spin_unlock(&ncsi_dev_lock);
>
> +       /* Register NCSI packet Rx handler */
> +       ndp->ndp_ptype.type = cpu_to_be16(ETH_P_NCSI);
> +       ndp->ndp_ptype.func = ncsi_rcv_rsp;
> +       ndp->ndp_ptype.dev = dev;
> +       dev_add_pack(&ndp->ndp_ptype);
> +
>         return nd;
>  }
>  EXPORT_SYMBOL_GPL(ncsi_register_dev);
>
> +int ncsi_start_dev(struct ncsi_dev *nd)
> +{
> +       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
> +
> +       if (nd->nd_state != ncsi_dev_state_registered &&
> +           nd->nd_state != ncsi_dev_state_functional)
> +               return -ENOTTY;
> +
> +       if (!(ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)) {
> +               nd->nd_state = ncsi_dev_state_probe;
> +               schedule_work(&ndp->ndp_work);
> +               return 0;
> +       }
> +
> +       /* Choose active package and channel */
> +       ncsi_choose_active_channel(ndp);
> +       if (!ndp->ndp_active_channel)
> +               return -ENXIO;
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(ncsi_start_dev);
> +
> +int ncsi_config_dev(struct ncsi_dev *nd)
> +{
> +       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
> +
> +       if (nd->nd_state != ncsi_dev_state_functional)
> +               return -ENOTTY;
> +
> +       nd->nd_state = ncsi_dev_state_config;
> +       schedule_work(&ndp->ndp_work);
> +
> +       return 0;
> +}
> +
> +int ncsi_suspend_dev(struct ncsi_dev *nd)
> +{
> +       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
> +
> +       if (nd->nd_state != ncsi_dev_state_functional)
> +               return -ENOTTY;
> +
> +       nd->nd_state = ncsi_dev_state_suspend;
> +       schedule_work(&ndp->ndp_work);
> +
> +       return 0;
> +}
> +
>  void ncsi_unregister_dev(struct ncsi_dev *nd)
>  {
>         struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
>         struct ncsi_package *np, *tmp;
>
> +       dev_remove_pack(&ndp->ndp_ptype);
> +
>         spin_lock(&ndp->ndp_package_lock);
>         list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
>                 ncsi_release_package(np);
> diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
> index 257079fc..92dcbf4 100644
> --- a/net/ncsi/ncsi-rsp.c
> +++ b/net/ncsi/ncsi-rsp.c
> @@ -88,6 +88,9 @@ static int ncsi_rsp_handler_cis(struct ncsi_req *nr)
>         rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->nr_rsp);
>         ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
>         if (!nc) {
> +               if (ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)
> +                       return -ENXIO;
> +
>                 id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
>                 nc = ncsi_add_channel(np, id);
>         } else if (nc->nc_state == ncsi_channel_state_deselected_initial) {
> @@ -119,6 +122,9 @@ static int ncsi_rsp_handler_sp(struct ncsi_req *nr)
>         ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
>                                       &np, NULL);
>         if (!np) {
> +               if (ndp->ndp_flags & NCSI_DEV_FLAG_PROBED)
> +                       return -ENXIO;
> +
>                 id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
>                 np = ncsi_add_package(ndp, id);
>                 if (!np)
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 05/10] net/ncsi: NCSI AEN packet handler
  2016-06-30 10:27 ` [PATCH 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> This introduces NCSI AEN packet handlers that result in (A) the
> currently active channel is reconfigured; (B) Currently active
> channel is deconfigured and disabled, another channel is chosen
> as active one and configured.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  net/ncsi/Makefile   |   2 +-
>  net/ncsi/internal.h |   1 +
>  net/ncsi/ncsi-aen.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  net/ncsi/ncsi-pkt.h |  36 ++++++++++
>  net/ncsi/ncsi-rsp.c |   6 +-
>  5 files changed, 233 insertions(+), 2 deletions(-)
>  create mode 100644 net/ncsi/ncsi-aen.c
>
> diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
> index 4751819..dd12b56 100644
> --- a/net/ncsi/Makefile
> +++ b/net/ncsi/Makefile
> @@ -1,4 +1,4 @@
>  #
>  # Makefile for NCSI API
>  #
> -obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
> +obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o
> diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
> index eeb092a..d284821 100644
> --- a/net/ncsi/internal.h
> +++ b/net/ncsi/internal.h
> @@ -303,5 +303,6 @@ int ncsi_suspend_dev(struct ncsi_dev *nd);
>  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
>  int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
>                  struct packet_type *pt, struct net_device *orig_dev);
> +int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb);
>
>  #endif /* __NCSI_INTERNAL_H__ */
> diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
> new file mode 100644
> index 0000000..82ed563
> --- /dev/null
> +++ b/net/ncsi/ncsi-aen.c
> @@ -0,0 +1,190 @@
> +/*
> + * Copyright Gavin Shan, IBM Corporation 2016.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include <net/ncsi.h>
> +#include <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "internal.h"
> +#include "ncsi-pkt.h"
> +
> +static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
> +                                const unsigned short payload)
> +{
> +       unsigned char *stream;
> +       __be32 *checksum, csum;
> +       __be32 high, low;
> +       int i;
> +
> +       if (h->common.revision != NCSI_PKT_REVISION)
> +               return -EINVAL;
> +       if (ntohs(h->common.length) != payload)
> +               return -EINVAL;
> +
> +       /* Validate checksum, which might be zeroes if the
> +        * sender doesn't support checksum according to NCSI
> +        * specification.
> +        */
> +       checksum = (__be32 *)((void *)(h + 1) + payload - 4);
> +       if (ntohl(*checksum) == 0)
> +               return 0;
> +
> +       csum = 0;
> +       stream = (unsigned char *)h;
> +       for (i = 0; i < sizeof(*h) + payload - 4; i += 2) {
> +               high = stream[i];
> +               low = stream[i + 1];
> +               csum += ((high << 8) | low);
> +       }
> +
> +       csum = ~csum + 1;
> +       if (*checksum != htonl(csum))
> +               return -EINVAL;

This is checksum calculation is shared between -rsp.c and -aen.c

> +
> +       return 0;
> +}
> +
> +static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
> +                               struct ncsi_aen_pkt_hdr *h)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct ncsi_aen_lsc_pkt *lsc;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       int ret;
> +
> +       ret = ncsi_validate_aen_pkt(h, 12);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the NCSI channel */
> +       ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* Update the link status */
> +       ncm = &nc->nc_modes[NCSI_MODE_LINK];
> +       lsc = (struct ncsi_aen_lsc_pkt *)h;
> +       ncm->ncm_data[2] = ntohl(lsc->status);
> +       ncm->ncm_data[4] = ntohl(lsc->oem_status);
> +       if (!ndp->ndp_active_channel ||
> +           ndp->ndp_active_channel != nc)
> +               return 0;
> +
> +       ndp->ndp_flags |= NCSI_DEV_FLAG_RESHUFFLE;
> +       ncsi_suspend_dev(nd);
> +
> +       return 0;
> +}
> +
> +static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
> +                              struct ncsi_aen_pkt_hdr *h)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct ncsi_channel *nc;
> +       int ret;
> +
> +       ret = ncsi_validate_aen_pkt(h, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the NCSI channel */
> +       ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* If the channel is active one, we need reconfigure it */
> +       if (!ndp->ndp_active_channel ||
> +           ndp->ndp_active_channel != nc)
> +               return 0;
> +
> +       ncsi_config_dev(nd);
> +
> +       return 0;
> +}
> +
> +static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
> +                                  struct ncsi_aen_pkt_hdr *h)
> +{
> +       struct ncsi_dev *nd = &ndp->ndp_ndev;
> +       struct ncsi_channel *nc;
> +       struct ncsi_channel_mode *ncm;
> +       struct ncsi_aen_hncdsc_pkt *hncdsc;
> +       int ret;
> +
> +       ret = ncsi_validate_aen_pkt(h, 4);
> +       if (ret)
> +               return ret;
> +
> +       /* Find the NCSI channel */
> +       ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
> +       if (!nc)
> +               return -ENODEV;
> +
> +       /* If the channel is active one, we need reconfigure it */
> +       ncm = &nc->nc_modes[NCSI_MODE_LINK];
> +       hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
> +       ncm->ncm_data[3] = ntohl(hncdsc->status);
> +       if (ndp->ndp_active_channel != nc ||
> +           ncm->ncm_data[3] & 0x1)
> +               return 0;
> +
> +       /* If this channel is the active one and the link doesn't
> +        * work, we have to choose another channel to be active one.
> +        * The logic here is exactly similar to what we do when link
> +        * is down on the active channel.
> +        */
> +       ndp->ndp_flags |= NCSI_DEV_FLAG_RESHUFFLE;
> +       ncsi_suspend_dev(nd);
> +       return 0;
> +}
> +
> +static struct ncsi_aen_handler {
> +       unsigned char nah_type;
> +       int           (*nah_handler)(struct ncsi_dev_priv *ndp,
> +                                    struct ncsi_aen_pkt_hdr *h);
> +} ncsi_aen_handlers[] = {
> +       { NCSI_PKT_AEN_LSC,    ncsi_aen_handler_lsc    },
> +       { NCSI_PKT_AEN_CR,     ncsi_aen_handler_cr     },
> +       { NCSI_PKT_AEN_HNCDSC, ncsi_aen_handler_hncdsc },
> +       { 0,                   NULL                    }
> +};
> +
> +int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
> +{
> +       struct ncsi_aen_pkt_hdr *h;
> +       struct ncsi_aen_handler *nah;
> +       int ret;
> +
> +       /* Find the handler */
> +       h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
> +       nah = ncsi_aen_handlers;
> +       while (nah->nah_handler) {
> +               if (nah->nah_type == h->type)
> +                       break;
> +
> +               nah++;
> +       }
> +
> +       if (!nah->nah_handler) {
> +               pr_warn("NCSI: Invalid AEN packet (0x%x) received\n",
> +                       h->type);
> +               return -ENOENT;
> +       }
> +
> +       ret = nah->nah_handler(ndp, h);
> +       consume_skb(skb);
> +       return ret;
> +}
> diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
> index 95882f3..6248835 100644
> --- a/net/ncsi/ncsi-pkt.h
> +++ b/net/ncsi/ncsi-pkt.h
> @@ -31,6 +31,12 @@ struct ncsi_rsp_pkt_hdr {
>         __be16              reason; /* Response reason           */
>  };
>
> +struct ncsi_aen_pkt_hdr {
> +       struct ncsi_pkt_hdr common;       /* Common NCSI packet header */
> +       unsigned char       reserved2[3]; /* Reserved                  */
> +       unsigned char       type;         /* AEN packet type           */
> +};
> +
>  /* NCSI common command packet */
>  struct ncsi_cmd_pkt {
>         struct ncsi_cmd_pkt_hdr cmd;      /* Command header */
> @@ -282,6 +288,30 @@ struct ncsi_rsp_gnpts_pkt {
>         __be32                  checksum;       /* Checksum            */
>  };
>
> +/* AEN: Link State Change */
> +struct ncsi_aen_lsc_pkt {
> +       struct ncsi_aen_pkt_hdr aen;        /* AEN header      */
> +       __be32                  status;     /* Link status     */
> +       __be32                  oem_status; /* OEM link status */
> +       __be32                  checksum;   /* Checksum        */
> +       unsigned char           pad[14];
> +};
> +
> +/* AEN: Configuration Required */
> +struct ncsi_aen_cr_pkt {
> +       struct ncsi_aen_pkt_hdr aen;      /* AEN header */
> +       __be32                  checksum; /* Checksum   */
> +       unsigned char           pad[22];
> +};
> +
> +/* AEN: Host Network Controller Driver Status Change */
> +struct ncsi_aen_hncdsc_pkt {
> +       struct ncsi_aen_pkt_hdr aen;      /* AEN header */
> +       __be32                  status;   /* Status     */
> +       __be32                  checksum; /* Checksum   */
> +       unsigned char           pad[18];
> +};
> +
>  /* NCSI packet revision */
>  #define NCSI_PKT_REVISION      0x01
>
> @@ -356,4 +386,10 @@ struct ncsi_rsp_gnpts_pkt {
>  #define NCSI_PKT_RSP_R_LENGTH          0x0005  /* Invalid payload length   */
>  #define NCSI_PKT_RSP_R_UNKNOWN         0x7fff  /* Command type unsupported */
>
> +/* NCSI packet type: AEN */
> +#define NCSI_PKT_AEN           0xFF /* AEN Packet               */
> +#define NCSI_PKT_AEN_LSC       0x00 /* Link status change       */
> +#define NCSI_PKT_AEN_CR                0x01 /* Configuration required   */
> +#define NCSI_PKT_AEN_HNCDSC    0x02 /* HNC driver status change */
> +
>  #endif /* __NCSI_PKT_H__ */
> diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
> index 92dcbf4..30578d3 100644
> --- a/net/ncsi/ncsi-rsp.c
> +++ b/net/ncsi/ncsi-rsp.c
> @@ -1125,8 +1125,12 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
>         if (!ndp)
>                 return -ENODEV;
>
> -       /* Find the handler */
> +       /* Check if it is AEN packet */
>         hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
> +       if (hdr->type == NCSI_PKT_AEN)
> +               return ncsi_aen_handler(ndp, skb);
> +
> +       /* Find the handler */
>         nrh = ncsi_rsp_handlers;
>         while (nrh->nrh_handler) {
>                 if (nrh->nrh_type == hdr->type)
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 07/10] net/farady: Read MAC address from chip
  2016-06-30 10:27 ` [PATCH 07/10] net/farady: Read MAC address from chip Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  2016-07-01 11:25     ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> The device is assigned with random MAC address. It isn't reasonable.
> An valid MAC address might have been in the chip. It's reasonable
> to use it to maintain consistency.
>
> This uses the MAC address in the chip if it's valid. Otherwise, a
> random MAC address is given as before.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  drivers/net/ethernet/faraday/ftgmac100.c | 60 ++++++++++++++++++++++++++++----
>  1 file changed, 53 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> index 2c22f74..120993e 100644
> --- a/drivers/net/ethernet/faraday/ftgmac100.c
> +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> @@ -141,6 +141,55 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
>         iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
>  }
>
> +static void ftgmac100_setup_mac(struct ftgmac100 *priv)
> +{
> +       unsigned char mac[6];
> +       unsigned int m;
> +       unsigned int l;
> +
> +       m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> +       l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> +
> +       mac[0] = (m >> 8) & 0xff;
> +       mac[1] = m & 0xff;
> +       mac[2] = (l >> 24) & 0xff;
> +       mac[3] = (l >> 16) & 0xff;
> +       mac[4] = (l >> 8) & 0xff;
> +       mac[5] = l & 0xff;
> +
> +       if (!is_valid_ether_addr(mac)) {
> +               mac[5] = (m >> 8) & 0xff;
> +               mac[4] = m & 0xff;
> +               mac[3] = (l >> 24) & 0xff;
> +               mac[2] = (l >> 16) & 0xff;
> +               mac[1] = (l >>  8) & 0xff;
> +               mac[0] = l & 0xff;
> +       }
> +
> +       if (!is_valid_ether_addr(mac)) {
> +               eth_hw_addr_random(priv->netdev);
> +               dev_info(priv->dev, "Generated random MAC address %pM\n",
> +                        priv->netdev->dev_addr);
> +       } else {
> +               memcpy(priv->netdev->dev_addr, mac, 6);
> +               dev_info(priv->dev, "Read MAC address from chip %pM\n", mac);
> +       }
> +}
> +
> +static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
> +{
> +       int ret;
> +
> +       ret = eth_prepare_mac_addr_change(dev, p);
> +       if (ret < 0)
> +               return ret;
> +
> +       eth_commit_mac_addr_change(dev, p);
> +       ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
> +
> +       return 0;
> +}
> +
>  static void ftgmac100_init_hw(struct ftgmac100 *priv)
>  {
>         /* setup ring buffer base registers */
> @@ -1141,7 +1190,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
>         .ndo_open               = ftgmac100_open,
>         .ndo_stop               = ftgmac100_stop,
>         .ndo_start_xmit         = ftgmac100_hard_start_xmit,
> -       .ndo_set_mac_address    = eth_mac_addr,
> +       .ndo_set_mac_address    = ftgmac100_set_mac_addr,
>         .ndo_validate_addr      = eth_validate_addr,
>         .ndo_do_ioctl           = ftgmac100_do_ioctl,
>  };
> @@ -1263,6 +1312,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
>
>         priv->irq = irq;
>
> +       /* MAC address from chip or random one */
> +       ftgmac100_setup_mac(priv);
> +
>         err = ftgmac100_setup_mdio(netdev);
>         if (err)
>                 goto err_setup_mdio;
> @@ -1276,12 +1328,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
>
>         netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
>
> -       if (!is_valid_ether_addr(netdev->dev_addr)) {
> -               eth_hw_addr_random(netdev);
> -               netdev_info(netdev, "generated random MAC address %pM\n",
> -                           netdev->dev_addr);
> -       }
> -
>         return 0;
>
>  err_register_netdev:
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 08/10] net/farady: Support NCSI mode
  2016-06-30 10:27 ` [PATCH 08/10] net/farady: Support NCSI mode Gavin Shan
@ 2016-06-30 14:05   ` Joel Stanley
  2016-07-01 11:26     ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:05 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> This makes ftgmac100 driver support NCSI mode. The NCSI is enabled
> on the interface if property "use-nc-si" or "use-ncsi" is found from
> the device node in device tree.
>
>    * No PHY device is used when NCSI mode is enabled.
>    * The NCSI device (struct ncsi_dev) is created when probing the
>      device while it's enabled/started when the interface is brought
>      up.
>    * Hardware IP checksum dosn't work when NCSI mode is enabled. It
>      is disabled on enabled NCSI.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  drivers/net/ethernet/faraday/ftgmac100.c | 78 ++++++++++++++++++++++++++++----
>  1 file changed, 69 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> index 120993e..f2205ae 100644
> --- a/drivers/net/ethernet/faraday/ftgmac100.c
> +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> @@ -31,6 +31,7 @@
>  #include <linux/phy.h>
>  #include <linux/platform_device.h>
>  #include <net/ip.h>
> +#include <net/ncsi.h>
>
>  #include "ftgmac100.h"
>
> @@ -68,10 +69,13 @@ struct ftgmac100 {
>
>         struct net_device *netdev;
>         struct device *dev;
> +       struct ncsi_dev *ndev;
>         struct napi_struct napi;
>
>         struct mii_bus *mii_bus;
>         int old_speed;
> +       bool use_ncsi;
> +       bool enabled;
>  };
>
>  static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
> @@ -1001,7 +1005,10 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
>         struct net_device *netdev = dev_id;
>         struct ftgmac100 *priv = netdev_priv(netdev);
>
> -       if (likely(netif_running(netdev))) {
> +       /* When running in NCSI mode, the interface should be ready for
> +        * receiving or transmitting NCSI packets before it's opened.
> +        */
> +       if (likely(priv->use_ncsi || netif_running(netdev))) {
>                 /* Disable interrupts for polling */
>                 iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>                 napi_schedule(&priv->napi);
> @@ -1114,17 +1121,33 @@ static int ftgmac100_open(struct net_device *netdev)
>                 goto err_hw;
>
>         ftgmac100_init_hw(priv);
> -       ftgmac100_start_hw(priv, 10);
> -
> -       phy_start(netdev->phydev);
> +       ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
> +       if (netdev->phydev)
> +               phy_start(netdev->phydev);
> +       else if (priv->use_ncsi)
> +               netif_carrier_on(netdev);
>
>         napi_enable(&priv->napi);
>         netif_start_queue(netdev);
>
>         /* enable all interrupts */
>         iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
> +
> +       /* Start the NCSI device */
> +       if (priv->use_ncsi) {
> +               err = ncsi_start_dev(priv->ndev);
> +               if (err)
> +                       goto err_ncsi;
> +       }
> +
> +       priv->enabled = true;
> +
>         return 0;
>
> +err_ncsi:
> +       napi_disable(&priv->napi);
> +       netif_stop_queue(netdev);
> +       iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>  err_hw:
>         free_irq(priv->irq, netdev);
>  err_irq:
> @@ -1137,12 +1160,17 @@ static int ftgmac100_stop(struct net_device *netdev)
>  {
>         struct ftgmac100 *priv = netdev_priv(netdev);
>
> +       if (!priv->enabled)
> +               return 0;
> +
>         /* disable all interrupts */
> +       priv->enabled = false;
>         iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>
>         netif_stop_queue(netdev);
>         napi_disable(&priv->napi);
> -       phy_stop(netdev->phydev);
> +       if (netdev->phydev)
> +               phy_stop(netdev->phydev);
>
>         ftgmac100_stop_hw(priv);
>         free_irq(priv->irq, netdev);
> @@ -1183,6 +1211,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
>  /* optional */
>  static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
>  {
> +       if (!netdev->phydev)
> +               return -ENXIO;
> +
>         return phy_mii_ioctl(netdev->phydev, ifr, cmd);
>  }
>
> @@ -1247,6 +1278,15 @@ static void ftgmac100_destroy_mdio(struct net_device *netdev)
>         mdiobus_free(priv->mii_bus);
>  }
>
> +static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
> +{
> +       if (unlikely(nd->nd_state != ncsi_dev_state_functional))
> +               return;
> +
> +       netdev_info(nd->nd_dev, "NCSI interface %s\n",
> +                   nd->nd_link_up ? "up" : "down");

We get this message whenever the device is brought up or down. This
includes when the host reboots, filling up the bmc's kernel logs.

However, when we had the timer race it was the only way to know what
was going on. Can we detect when it's the host bringing the device
down opposed to an "ifconfig up" failing to bring it up?


> +}
> +
>  /******************************************************************************
>   * struct platform_driver functions
>   *****************************************************************************/
> @@ -1256,7 +1296,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
>         int irq;
>         struct net_device *netdev;
>         struct ftgmac100 *priv;
> -       int err;
> +       int err = 0;
>
>         if (!pdev)
>                 return -ENODEV;
> @@ -1280,7 +1320,15 @@ static int ftgmac100_probe(struct platform_device *pdev)
>
>         netdev->ethtool_ops = &ftgmac100_ethtool_ops;
>         netdev->netdev_ops = &ftgmac100_netdev_ops;
> +
> +       /* We have to disable on-chip IP checksum functionality
> +        * when NCSI is enabled on the interface. It doesn't work
> +        * in that case.
> +        */
>         netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
> +       if (pdev->dev.of_node &&
> +           of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
> +               netdev->features &= ~NETIF_F_IP_CSUM;

We set this in all of the aspeed BMC platforms, including the
firestone which uses the device with a dedicated PHY. Should we
instead match off the aspeed platform?

>
>         platform_set_drvdata(pdev, netdev);
>
> @@ -1315,9 +1363,20 @@ static int ftgmac100_probe(struct platform_device *pdev)
>         /* MAC address from chip or random one */
>         ftgmac100_setup_mac(priv);
>
> -       err = ftgmac100_setup_mdio(netdev);
> -       if (err)
> -               goto err_setup_mdio;
> +       if (pdev->dev.of_node &&
> +           (of_get_property(pdev->dev.of_node, "use-nc-si", NULL) ||
> +           of_get_property(pdev->dev.of_node, "use-ncsi", NULL))) {

I think we want to settle on one spelling when going upstream.

> +               dev_info(&pdev->dev, "Using NCSI interface\n");
> +               priv->use_ncsi = true;
> +               priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
> +               if (!priv->ndev)
> +                       goto err_ncsi_dev;

When the user (read: Joel) mistakenly puts "use-ncsi" in the device
tree, but forgets to enable CONFIG_NET_NCSI in their kernel, the
driver silently. I added this hack in my tree:

 https://github.com/shenki/linux/commit/e52b8ec4f16a6467860ee705599c7cf282272d97

Could we do something similar to protect dumb users from themselves?

> +       } else {
> +               priv->use_ncsi = false;
> +               err = ftgmac100_setup_mdio(netdev);
> +               if (err)
> +                       goto err_setup_mdio;
> +       }
>
>         /* register network device */
>         err = register_netdev(netdev);
> @@ -1330,6 +1389,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
>
>         return 0;
>
> +err_ncsi_dev:
>  err_register_netdev:
>         ftgmac100_destroy_mdio(netdev);
>  err_setup_mdio:
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 09/10] net/farady: Match driver according to compatible property
  2016-06-30 10:27 ` [PATCH 09/10] net/farady: Match driver according to compatible property Gavin Shan
@ 2016-06-30 14:06   ` Joel Stanley
  0 siblings, 0 replies; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:06 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> This matches the driver with devices compatible with "faraday,ftgmac100"
> declared in the device tree. Originally, device's name from device
> tree for it.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  drivers/net/ethernet/faraday/ftgmac100.c | 16 +++++++++++-----
>  1 file changed, 11 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> index f2205ae..23e27f1 100644
> --- a/drivers/net/ethernet/faraday/ftgmac100.c
> +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> @@ -1422,14 +1422,20 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
>         return 0;
>  }
>
> +static const struct of_device_id ftgmac100_of_match[] = {
> +       { .compatible = "faraday,ftgmac100" },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, ftgmac100_of_match);
> +
>  static struct platform_driver ftgmac100_driver = {
> -       .probe          = ftgmac100_probe,
> -       .remove         = __exit_p(ftgmac100_remove),
> -       .driver         = {
> -               .name   = DRV_NAME,
> +       .probe  = ftgmac100_probe,
> +       .remove = ftgmac100_remove,

You dropped the __exit_p annotation that was here.

> +       .driver = {
> +               .name           = DRV_NAME,
> +               .of_match_table = ftgmac100_of_match,
>         },
>  };
> -
>  module_platform_driver(ftgmac100_driver);
>
>  MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode
  2016-06-30 10:27 ` [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode Gavin Shan
@ 2016-06-30 14:06   ` Joel Stanley
  2016-07-03  5:40     ` Gavin Shan
  0 siblings, 1 reply; 29+ messages in thread
From: Joel Stanley @ 2016-06-30 14:06 UTC (permalink / raw)
  To: Gavin Shan; +Cc: OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
> Bogus PHY interrupts are observed. This masks the PHY interrupt
> when the interface works in NCSI mode as there is no attached
> PHY under the circumstance.
>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> ---
>  drivers/net/ethernet/faraday/ftgmac100.c | 27 +++++++++++++++------------
>  1 file changed, 15 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> index 23e27f1..6642071 100644
> --- a/drivers/net/ethernet/faraday/ftgmac100.c
> +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> @@ -74,6 +74,7 @@ struct ftgmac100 {
>
>         struct mii_bus *mii_bus;
>         int old_speed;
> +       int int_mask_all;
>         bool use_ncsi;
>         bool enabled;
>  };
> @@ -84,14 +85,6 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
>  /******************************************************************************
>   * internal functions (hardware register access)
>   *****************************************************************************/
> -#define INT_MASK_ALL_ENABLED   (FTGMAC100_INT_RPKT_LOST        | \
> -                                FTGMAC100_INT_XPKT_ETH         | \
> -                                FTGMAC100_INT_XPKT_LOST        | \
> -                                FTGMAC100_INT_AHB_ERR          | \
> -                                FTGMAC100_INT_PHYSTS_CHG       | \
> -                                FTGMAC100_INT_RPKT_BUF         | \
> -                                FTGMAC100_INT_NO_RXBUF)
> -
>  static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
>  {
>         iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
> @@ -1061,8 +1054,9 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
>                 ftgmac100_tx_complete(priv);
>         }
>
> -       if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
> -                     FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
> +       if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
> +                       FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR |
> +                       FTGMAC100_INT_PHYSTS_CHG)) {
>                 if (net_ratelimit())
>                         netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
>                                     status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
> @@ -1085,7 +1079,7 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
>                 napi_complete(napi);
>
>                 /* enable all interrupts */
> -               iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
> +               iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
>         }
>
>         return rx;
> @@ -1131,7 +1125,7 @@ static int ftgmac100_open(struct net_device *netdev)
>         netif_start_queue(netdev);
>
>         /* enable all interrupts */
> -       iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
> +       iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
>
>         /* Start the NCSI device */
>         if (priv->use_ncsi) {
> @@ -1363,10 +1357,19 @@ static int ftgmac100_probe(struct platform_device *pdev)
>         /* MAC address from chip or random one */
>         ftgmac100_setup_mac(priv);
>
> +       priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
> +                             FTGMAC100_INT_XPKT_ETH |
> +                             FTGMAC100_INT_XPKT_LOST |
> +                             FTGMAC100_INT_AHB_ERR |
> +                             FTGMAC100_INT_PHYSTS_CHG |
> +                             FTGMAC100_INT_RPKT_BUF |
> +                             FTGMAC100_INT_NO_RXBUF);
>         if (pdev->dev.of_node &&
>             (of_get_property(pdev->dev.of_node, "use-nc-si", NULL) ||
>             of_get_property(pdev->dev.of_node, "use-ncsi", NULL))) {

As before, lets pick single a property and stick with it.

>                 dev_info(&pdev->dev, "Using NCSI interface\n");
> +
> +               priv->int_mask_all &= ~FTGMAC100_INT_PHYSTS_CHG;
>                 priv->use_ncsi = true;
>                 priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
>                 if (!priv->ndev)
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface
  2016-06-30 13:49   ` Joel Stanley
@ 2016-07-01  2:59     ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2016-07-01  2:59 UTC (permalink / raw)
  To: Joel Stanley, Gavin Shan; +Cc: OpenBMC Maillist

On Thu, 2016-06-30 at 23:19 +0930, Joel Stanley wrote:
> +       priv->mii_bus->name = "ftgmac100_mdio";
> > +       snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE,
> "ftgmac100_mii");
> 
> If we have two ftgmac100 drivers loaded (such as on the ast2500-evb),
> when the second goes to create the mdiobus kobject it will have the
> same name. We need to make this name unique (or fix up the mdiobus
> code).
> 
> You can see a backtrace here:
> 
>   https://github.com/openbmc/linux/issues/87

Traditional way:

	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
		 priv->pdev->name, priv->pdev->id);


IE, the name here isn't the same as mii_bus->name which is a user
visible name, but the actual *device* name (platform dev) and an id.

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 07/10] net/farady: Read MAC address from chip
  2016-06-30 14:05   ` Joel Stanley
@ 2016-07-01 11:25     ` Benjamin Herrenschmidt
       [not found]       ` <CACPK8XdAiDW=PADte4ZWNumKYXCkK-8kcw0atOUQATORsRQEGA@mail.gmail.com>
  0 siblings, 1 reply; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2016-07-01 11:25 UTC (permalink / raw)
  To: Joel Stanley, Gavin Shan; +Cc: OpenBMC Maillist

On Thu, 2016-06-30 at 23:35 +0930, Joel Stanley wrote:
> On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan  wrote:
> > The device is assigned with random MAC address. It isn't reasonable.
> > An valid MAC address might have been in the chip. It's reasonable
> > to use it to maintain consistency.
> > 
> > This uses the MAC address in the chip if it's valid. Otherwise, a
> > random MAC address is given as before.

If that is a worry we could use a device-tree property. Does our
uboot update the DT ? If yes it can set the local-mac-address prop.

> > Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> > ---
> >  drivers/net/ethernet/faraday/ftgmac100.c | 60 ++++++++++++++++++++++++++++----
> >  1 file changed, 53 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
> > index 2c22f74..120993e 100644
> > --- a/drivers/net/ethernet/faraday/ftgmac100.c
> > +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> > @@ -141,6 +141,55 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
> >         iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
> >  }
> > 
> > +static void ftgmac100_setup_mac(struct ftgmac100 *priv)
> > +{
> > +       unsigned char mac[6];
> > +       unsigned int m;
> > +       unsigned int l;
> > +
> > +       m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > +       l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > +
> > +       mac[0] = (m >> 8) & 0xff;
> > +       mac[1] = m & 0xff;
> > +       mac[2] = (l >> 24) & 0xff;
> > +       mac[3] = (l >> 16) & 0xff;
> > +       mac[4] = (l >> 8) & 0xff;
> > +       mac[5] = l & 0xff;
> > +
> > +       if (!is_valid_ether_addr(mac)) {
> > +               mac[5] = (m >> 8) & 0xff;
> > +               mac[4] = m & 0xff;
> > +               mac[3] = (l >> 24) & 0xff;
> > +               mac[2] = (l >> 16) & 0xff;
> > +               mac[1] = (l >>  8) & 0xff;
> > +               mac[0] = l & 0xff;
> > +       }
> > +
> > +       if (!is_valid_ether_addr(mac)) {
> > +               eth_hw_addr_random(priv->netdev);
> > +               dev_info(priv->dev, "Generated random MAC address %pM\n",
> > +                        priv->netdev->dev_addr);
> > +       } else {
> > +               memcpy(priv->netdev->dev_addr, mac, 6);
> > +               dev_info(priv->dev, "Read MAC address from chip %pM\n", mac);
> > +       }
> > +}
> > +
> > +static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
> > +{
> > +       int ret;
> > +
> > +       ret = eth_prepare_mac_addr_change(dev, p);
> > +       if (ret < 0)
> > +               return ret;
> > +
> > +       eth_commit_mac_addr_change(dev, p);
> > +       ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
> > +
> > +       return 0;
> > +}
> > +
> >  static void ftgmac100_init_hw(struct ftgmac100 *priv)
> >  {
> >         /* setup ring buffer base registers */
> > @@ -1141,7 +1190,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
> >         .ndo_open               = ftgmac100_open,
> >         .ndo_stop               = ftgmac100_stop,
> >         .ndo_start_xmit         = ftgmac100_hard_start_xmit,
> > -       .ndo_set_mac_address    = eth_mac_addr,
> > +       .ndo_set_mac_address    = ftgmac100_set_mac_addr,
> >         .ndo_validate_addr      = eth_validate_addr,
> >         .ndo_do_ioctl           = ftgmac100_do_ioctl,
> >  };
> > @@ -1263,6 +1312,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
> > 
> >         priv->irq = irq;
> > 
> > +       /* MAC address from chip or random one */
> > +       ftgmac100_setup_mac(priv);
> > +
> >         err = ftgmac100_setup_mdio(netdev);
> >         if (err)
> >                 goto err_setup_mdio;
> > @@ -1276,12 +1328,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
> > 
> >         netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
> > 
> > -       if (!is_valid_ether_addr(netdev->dev_addr)) {
> > -               eth_hw_addr_random(netdev);
> > -               netdev_info(netdev, "generated random MAC address %pM\n",
> > -                           netdev->dev_addr);
> > -       }
> > -
> >         return 0;
> > 
> >  err_register_netdev:
> > --
> > 2.1.0
> > 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 08/10] net/farady: Support NCSI mode
  2016-06-30 14:05   ` Joel Stanley
@ 2016-07-01 11:26     ` Benjamin Herrenschmidt
  2016-07-03  5:37       ` Gavin Shan
  0 siblings, 1 reply; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2016-07-01 11:26 UTC (permalink / raw)
  To: Joel Stanley, Gavin Shan; +Cc: OpenBMC Maillist

On Thu, 2016-06-30 at 23:35 +0930, Joel Stanley wrote:
> We get this message whenever the device is brought up or down. This
> includes when the host reboots, filling up the bmc's kernel logs.
> 
> However, when we had the timer race it was the only way to know what
> was going on. Can we detect when it's the host bringing the device
> down opposed to an "ifconfig up" failing to bring it up?

What would be nice would be if we could mirror that into the "carrier"
state of the interface (which normally corresponds to the PHY link
state).

The problem however is that when the carrier is off, the network stack
will ditch packets, which doen't work for us since we still need to
talk NSCI ...

I'm thinking we should create some netdev flags so that the generic
link monitor stops bringing it down in that case. That way we can
set the carrier and eixsting stuff that knows how to read and interpret
it will work.

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 07/10] net/farady: Read MAC address from chip
       [not found]       ` <CACPK8XdAiDW=PADte4ZWNumKYXCkK-8kcw0atOUQATORsRQEGA@mail.gmail.com>
@ 2016-07-01 12:17         ` Joel Stanley
  2016-07-01 13:20           ` Benjamin Herrenschmidt
  2016-07-03  5:35           ` Gavin Shan
  0 siblings, 2 replies; 29+ messages in thread
From: Joel Stanley @ 2016-07-01 12:17 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Gavin Shan, OpenBMC Maillist

[-- Attachment #1: Type: text/plain, Size: 4735 bytes --]

On 1 Jul 2016 20:55, "Benjamin Herrenschmidt" <benh@kernel.crashing.org>
wrote:
>
> On Thu, 2016-06-30 at 23:35 +0930, Joel Stanley wrote:
> > On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan  wrote:
> > > The device is assigned with random MAC address. It isn't reasonable.
> > > An valid MAC address might have been in the chip. It's reasonable
> > > to use it to maintain consistency.
> > >
> > > This uses the MAC address in the chip if it's valid. Otherwise, a
> > > random MAC address is given as before.
>
> If that is a worry we could use a device-tree property. Does our
> uboot update the DT ? If yes it can set the local-mac-address prop.

It doesn't currently, but we will be adding that soon we need to update the
memory node for systems where board revisions change the DRAM size.

Cheers,

Joel

>
> > > Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> > > ---
> > >  drivers/net/ethernet/faraday/ftgmac100.c | 60
++++++++++++++++++++++++++++----
> > >  1 file changed, 53 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/faraday/ftgmac100.c
b/drivers/net/ethernet/faraday/ftgmac100.c
> > > index 2c22f74..120993e 100644
> > > --- a/drivers/net/ethernet/faraday/ftgmac100.c
> > > +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> > > @@ -141,6 +141,55 @@ static void ftgmac100_set_mac(struct ftgmac100
*priv, const unsigned char *mac)
> > >         iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > >  }
> > >
> > > +static void ftgmac100_setup_mac(struct ftgmac100 *priv)
> > > +{
> > > +       unsigned char mac[6];
> > > +       unsigned int m;
> > > +       unsigned int l;
> > > +
> > > +       m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > > +       l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > > +
> > > +       mac[0] = (m >> 8) & 0xff;
> > > +       mac[1] = m & 0xff;
> > > +       mac[2] = (l >> 24) & 0xff;
> > > +       mac[3] = (l >> 16) & 0xff;
> > > +       mac[4] = (l >> 8) & 0xff;
> > > +       mac[5] = l & 0xff;
> > > +
> > > +       if (!is_valid_ether_addr(mac)) {
> > > +               mac[5] = (m >> 8) & 0xff;
> > > +               mac[4] = m & 0xff;
> > > +               mac[3] = (l >> 24) & 0xff;
> > > +               mac[2] = (l >> 16) & 0xff;
> > > +               mac[1] = (l >>  8) & 0xff;
> > > +               mac[0] = l & 0xff;
> > > +       }
> > > +
> > > +       if (!is_valid_ether_addr(mac)) {
> > > +               eth_hw_addr_random(priv->netdev);
> > > +               dev_info(priv->dev, "Generated random MAC address
%pM\n",
> > > +                        priv->netdev->dev_addr);
> > > +       } else {
> > > +               memcpy(priv->netdev->dev_addr, mac, 6);
> > > +               dev_info(priv->dev, "Read MAC address from chip
%pM\n", mac);
> > > +       }
> > > +}
> > > +
> > > +static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
> > > +{
> > > +       int ret;
> > > +
> > > +       ret = eth_prepare_mac_addr_change(dev, p);
> > > +       if (ret < 0)
> > > +               return ret;
> > > +
> > > +       eth_commit_mac_addr_change(dev, p);
> > > +       ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  static void ftgmac100_init_hw(struct ftgmac100 *priv)
> > >  {
> > >         /* setup ring buffer base registers */
> > > @@ -1141,7 +1190,7 @@ static const struct net_device_ops
ftgmac100_netdev_ops = {
> > >         .ndo_open               = ftgmac100_open,
> > >         .ndo_stop               = ftgmac100_stop,
> > >         .ndo_start_xmit         = ftgmac100_hard_start_xmit,
> > > -       .ndo_set_mac_address    = eth_mac_addr,
> > > +       .ndo_set_mac_address    = ftgmac100_set_mac_addr,
> > >         .ndo_validate_addr      = eth_validate_addr,
> > >         .ndo_do_ioctl           = ftgmac100_do_ioctl,
> > >  };
> > > @@ -1263,6 +1312,9 @@ static int ftgmac100_probe(struct
platform_device *pdev)
> > >
> > >         priv->irq = irq;
> > >
> > > +       /* MAC address from chip or random one */
> > > +       ftgmac100_setup_mac(priv);
> > > +
> > >         err = ftgmac100_setup_mdio(netdev);
> > >         if (err)
> > >                 goto err_setup_mdio;
> > > @@ -1276,12 +1328,6 @@ static int ftgmac100_probe(struct
platform_device *pdev)
> > >
> > >         netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq,
priv->base);
> > >
> > > -       if (!is_valid_ether_addr(netdev->dev_addr)) {
> > > -               eth_hw_addr_random(netdev);
> > > -               netdev_info(netdev, "generated random MAC address
%pM\n",
> > > -                           netdev->dev_addr);
> > > -       }
> > > -
> > >         return 0;
> > >
> > >  err_register_netdev:
> > > --
> > > 2.1.0
> > >

[-- Attachment #2: Type: text/html, Size: 7321 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 07/10] net/farady: Read MAC address from chip
  2016-07-01 12:17         ` Joel Stanley
@ 2016-07-01 13:20           ` Benjamin Herrenschmidt
  2016-07-03  5:35           ` Gavin Shan
  1 sibling, 0 replies; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2016-07-01 13:20 UTC (permalink / raw)
  To: Joel Stanley; +Cc: Gavin Shan, OpenBMC Maillist

On Fri, 2016-07-01 at 21:47 +0930, Joel Stanley wrote:
> > If that is a worry we could use a device-tree property. Does our
> > uboot update the DT ? If yes it can set the local-mac-address prop.
> It doesn't currently, but we will be adding that soon we need to
> update the memory node for systems where board revisions change the
> DRAM size.

Ok, otherwise there's a trick with A-TAGS iirc but we should just do
DT, it's much cleaner.

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 07/10] net/farady: Read MAC address from chip
  2016-07-01 12:17         ` Joel Stanley
  2016-07-01 13:20           ` Benjamin Herrenschmidt
@ 2016-07-03  5:35           ` Gavin Shan
  1 sibling, 0 replies; 29+ messages in thread
From: Gavin Shan @ 2016-07-03  5:35 UTC (permalink / raw)
  To: Joel Stanley; +Cc: Benjamin Herrenschmidt, Gavin Shan, OpenBMC Maillist

On Fri, Jul 01, 2016 at 09:47:58PM +0930, Joel Stanley wrote:
>On 1 Jul 2016 20:55, "Benjamin Herrenschmidt" <benh@kernel.crashing.org>
>wrote:
>>
>> On Thu, 2016-06-30 at 23:35 +0930, Joel Stanley wrote:
>> > On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan  wrote:
>> > > The device is assigned with random MAC address. It isn't reasonable.
>> > > An valid MAC address might have been in the chip. It's reasonable
>> > > to use it to maintain consistency.
>> > >
>> > > This uses the MAC address in the chip if it's valid. Otherwise, a
>> > > random MAC address is given as before.
>>
>> If that is a worry we could use a device-tree property. Does our
>> uboot update the DT ? If yes it can set the local-mac-address prop.
>
>It doesn't currently, but we will be adding that soon we need to update the
>memory node for systems where board revisions change the DRAM size.
>

In the revision I sent to community just now, the MAC address is picked from
device-tree if there is a valid one. Otherwise, the one in the chip is checked.
random MAC address will be used if both are invalid. In this way, the kernel
compatible whatever uboot changes we will have.

Thanks,
Gavin

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 08/10] net/farady: Support NCSI mode
  2016-07-01 11:26     ` Benjamin Herrenschmidt
@ 2016-07-03  5:37       ` Gavin Shan
  0 siblings, 0 replies; 29+ messages in thread
From: Gavin Shan @ 2016-07-03  5:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Joel Stanley, Gavin Shan, OpenBMC Maillist

On Fri, Jul 01, 2016 at 09:26:56PM +1000, Benjamin Herrenschmidt wrote:
>On Thu, 2016-06-30 at 23:35 +0930, Joel Stanley wrote:
>> We get this message whenever the device is brought up or down. This
>> includes when the host reboots, filling up the bmc's kernel logs.
>> 
>> However, when we had the timer race it was the only way to know what
>> was going on. Can we detect when it's the host bringing the device
>> down opposed to an "ifconfig up" failing to bring it up?
>
>What would be nice would be if we could mirror that into the "carrier"
>state of the interface (which normally corresponds to the PHY link
>state).
>
>The problem however is that when the carrier is off, the network stack
>will ditch packets, which doen't work for us since we still need to
>talk NSCI ...
>
>I'm thinking we should create some netdev flags so that the generic
>link monitor stops bringing it down in that case. That way we can
>set the carrier and eixsting stuff that knows how to read and interpret
>it will work.
>

Ben and Joel, thank you very much for your comments. I'm sorry this comment
isn't covered in the revision I sent to community just. I will evaluate it
later and improve it accordingly. Note this is the only comment not covered
in the revision sent to community, all other comments should be covered there
if I didn't miss any of them.

Thanks,
Gavin

>Cheers,
>Ben.
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode
  2016-06-30 14:06   ` Joel Stanley
@ 2016-07-03  5:40     ` Gavin Shan
  0 siblings, 0 replies; 29+ messages in thread
From: Gavin Shan @ 2016-07-03  5:40 UTC (permalink / raw)
  To: Joel Stanley; +Cc: Gavin Shan, OpenBMC Maillist, Benjamin Herrenschmidt

On Thu, Jun 30, 2016 at 11:36:07PM +0930, Joel Stanley wrote:
>On Thu, Jun 30, 2016 at 7:57 PM, Gavin Shan <gwshan@linux.vnet.ibm.com> wrote:
>>         if (pdev->dev.of_node &&
>>             (of_get_property(pdev->dev.of_node, "use-nc-si", NULL) ||
>>             of_get_property(pdev->dev.of_node, "use-ncsi", NULL))) {
>
>As before, lets pick single a property and stick with it.
>

The reason why I used two strings is the change is still fine on those BMCs
where "ncsi-nc-si" is supported only. Joel, I personal prefer "use-ncsi".
Lets adjust it later in device-tree.

Thanks,
Gavin

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2016-07-03  5:40 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-30 10:27 [PATCH 00/10] NCSI Support Gavin Shan
2016-06-30 10:27 ` [PATCH 01/10] net/ncsi: Resource management Gavin Shan
2016-06-30 14:04   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 02/10] net/ncsi: NCSI command packet handler Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 03/10] net/ncsi: NCSI response " Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 04/10] net/ncsi: Package and channel management Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 06/10] net/farady: Helper functions to create or destroy MDIO interface Gavin Shan
2016-06-30 13:49   ` Joel Stanley
2016-07-01  2:59     ` Benjamin Herrenschmidt
2016-06-30 10:27 ` [PATCH 07/10] net/farady: Read MAC address from chip Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-07-01 11:25     ` Benjamin Herrenschmidt
     [not found]       ` <CACPK8XdAiDW=PADte4ZWNumKYXCkK-8kcw0atOUQATORsRQEGA@mail.gmail.com>
2016-07-01 12:17         ` Joel Stanley
2016-07-01 13:20           ` Benjamin Herrenschmidt
2016-07-03  5:35           ` Gavin Shan
2016-06-30 10:27 ` [PATCH 08/10] net/farady: Support NCSI mode Gavin Shan
2016-06-30 14:05   ` Joel Stanley
2016-07-01 11:26     ` Benjamin Herrenschmidt
2016-07-03  5:37       ` Gavin Shan
2016-06-30 10:27 ` [PATCH 09/10] net/farady: Match driver according to compatible property Gavin Shan
2016-06-30 14:06   ` Joel Stanley
2016-06-30 10:27 ` [PATCH 10/10] net/farady: Mask PHY interrupt with NCSI mode Gavin Shan
2016-06-30 14:06   ` Joel Stanley
2016-07-03  5:40     ` Gavin Shan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.