net: eth: Add Qbv configuration support

Allow caller to either set or receive various Ethernet Qbv
configuration options defined in IEEE Std 802.1Qbv-2015
specification.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
diff --git a/include/net/ethernet.h b/include/net/ethernet.h
index 194d802..d6dcfb7 100644
--- a/include/net/ethernet.h
+++ b/include/net/ethernet.h
@@ -28,6 +28,7 @@
 #include <sys/util.h>
 #include <net/net_if.h>
 #include <net/ethernet_vlan.h>
+#include <net/ptp_time.h>
 
 #if defined(CONFIG_NET_DSA)
 #include <net/dsa.h>
@@ -150,6 +151,9 @@
 	/** DSA switch */
 	ETHERNET_DSA_SLAVE_PORT	= BIT(15),
 	ETHERNET_DSA_MASTER_PORT	= BIT(16),
+
+	/** IEEE 802.1Qbv (scheduled traffic) supported */
+	ETHERNET_QBV			= BIT(17),
 };
 
 /** @cond INTERNAL_HIDDEN */
@@ -160,9 +164,11 @@
 	ETHERNET_CONFIG_TYPE_DUPLEX,
 	ETHERNET_CONFIG_TYPE_MAC_ADDRESS,
 	ETHERNET_CONFIG_TYPE_QAV_PARAM,
+	ETHERNET_CONFIG_TYPE_QBV_PARAM,
 	ETHERNET_CONFIG_TYPE_PROMISC_MODE,
 	ETHERNET_CONFIG_TYPE_PRIORITY_QUEUES_NUM,
 	ETHERNET_CONFIG_TYPE_FILTER,
+	ETHERNET_CONFIG_TYPE_PORTS_NUM,
 };
 
 enum ethernet_qav_param_type {
@@ -196,6 +202,70 @@
 
 /** @cond INTERNAL_HIDDEN */
 
+enum ethernet_qbv_param_type {
+	ETHERNET_QBV_PARAM_TYPE_STATUS,
+	ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST,
+	ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN,
+	ETHERNET_QBV_PARAM_TYPE_TIME,
+};
+
+enum ethernet_qbv_state_type {
+	ETHERNET_QBV_STATE_TYPE_ADMIN,
+	ETHERNET_QBV_STATE_TYPE_OPER,
+};
+
+enum ethernet_gate_state_operation {
+	ETHERNET_SET_GATE_STATE,
+};
+
+/** @endcond */
+
+struct ethernet_qbv_param {
+	/** Port id */
+	int port_id;
+	/** Type of Qbv parameter */
+	enum ethernet_qbv_param_type type;
+	/** What state (Admin/Oper) parameters are these */
+	enum ethernet_qbv_state_type state;
+	union {
+		/** True if Qbv is enabled or not */
+		bool enabled;
+
+		struct {
+			/** True = open, False = closed */
+			bool gate_status[NET_TC_TX_COUNT];
+
+			/** GateState operation */
+			enum ethernet_gate_state_operation operation;
+
+			/** Time interval ticks (nanoseconds) */
+			uint32_t time_interval;
+
+			/** Gate control list row */
+			uint16_t row;
+		} gate_control;
+
+		/** Number of entries in gate control list */
+		uint32_t gate_control_list_len;
+
+		/* The time values are set in one go when type is set to
+		 * ETHERNET_QBV_PARAM_TYPE_TIME
+		 */
+		struct {
+			/** Base time */
+			struct net_ptp_extended_time base_time;
+
+			/** Cycle time */
+			struct net_ptp_time cycle_time;
+
+			/** Extension time (nanoseconds) */
+			uint32_t extension_time;
+		};
+	};
+};
+
+/** @cond INTERNAL_HIDDEN */
+
 enum ethernet_filter_type {
 	ETHERNET_FILTER_TYPE_SRC_MAC_ADDRESS,
 	ETHERNET_FILTER_TYPE_DST_MAC_ADDRESS,
@@ -228,8 +298,10 @@
 		struct net_eth_addr mac_address;
 
 		struct ethernet_qav_param qav_param;
+		struct ethernet_qbv_param qbv_param;
 
 		int priority_queues_num;
+		int ports_num;
 
 		struct ethernet_filter filter;
 	};
diff --git a/include/net/ethernet_mgmt.h b/include/net/ethernet_mgmt.h
index c9a7500..a3d84e1 100644
--- a/include/net/ethernet_mgmt.h
+++ b/include/net/ethernet_mgmt.h
@@ -41,9 +41,12 @@
 	NET_REQUEST_ETHERNET_CMD_SET_DUPLEX,
 	NET_REQUEST_ETHERNET_CMD_SET_MAC_ADDRESS,
 	NET_REQUEST_ETHERNET_CMD_SET_QAV_PARAM,
+	NET_REQUEST_ETHERNET_CMD_SET_QBV_PARAM,
 	NET_REQUEST_ETHERNET_CMD_SET_PROMISC_MODE,
 	NET_REQUEST_ETHERNET_CMD_GET_PRIORITY_QUEUES_NUM,
 	NET_REQUEST_ETHERNET_CMD_GET_QAV_PARAM,
+	NET_REQUEST_ETHERNET_CMD_GET_PORTS_NUM,
+	NET_REQUEST_ETHERNET_CMD_GET_QBV_PARAM,
 };
 
 #define NET_REQUEST_ETHERNET_SET_AUTO_NEGOTIATION			\
@@ -71,6 +74,16 @@
 
 NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QAV_PARAM);
 
+#define NET_REQUEST_ETHERNET_GET_PORTS_NUM				\
+	(_NET_ETHERNET_BASE | NET_REQUEST_ETHERNET_CMD_GET_PORTS_NUM)
+
+NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PORTS_NUM);
+
+#define NET_REQUEST_ETHERNET_SET_QBV_PARAM				\
+	(_NET_ETHERNET_BASE | NET_REQUEST_ETHERNET_CMD_SET_QBV_PARAM)
+
+NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBV_PARAM);
+
 #define NET_REQUEST_ETHERNET_SET_PROMISC_MODE				\
 	(_NET_ETHERNET_BASE | NET_REQUEST_ETHERNET_CMD_SET_PROMISC_MODE)
 
@@ -86,8 +99,14 @@
 
 NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QAV_PARAM);
 
+#define NET_REQUEST_ETHERNET_GET_QBV_PARAM				\
+	(_NET_ETHERNET_BASE | NET_REQUEST_ETHERNET_CMD_GET_QBV_PARAM)
+
+NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBV_PARAM);
+
 struct net_eth_addr;
 struct ethernet_qav_param;
+struct ethernet_qbv_param;
 
 struct ethernet_req_params {
 	union {
@@ -104,8 +123,10 @@
 		struct net_eth_addr mac_address;
 
 		struct ethernet_qav_param qav_param;
+		struct ethernet_qbv_param qbv_param;
 
 		int priority_queues_num;
+		int ports_num;
 	};
 };
 
diff --git a/subsys/net/l2/ethernet/ethernet_mgmt.c b/subsys/net/l2/ethernet/ethernet_mgmt.c
index 92ab4e8..7e13f57 100644
--- a/subsys/net/l2/ethernet/ethernet_mgmt.c
+++ b/subsys/net/l2/ethernet/ethernet_mgmt.c
@@ -139,6 +139,26 @@
 		memcpy(&config.qav_param, &params->qav_param,
 		       sizeof(struct ethernet_qav_param));
 		type = ETHERNET_CONFIG_TYPE_QAV_PARAM;
+	} else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QBV_PARAM) {
+		if (!is_hw_caps_supported(dev, ETHERNET_QBV)) {
+			return -ENOTSUP;
+		}
+
+		/* Validate params which need global validating */
+		if (params->qbv_param.state == ETHERNET_QBV_STATE_TYPE_OPER) {
+			/* Read-only parameters */
+			return -EINVAL;
+		}
+
+		if (params->qbv_param.type == ETHERNET_QBV_PARAM_TYPE_TIME &&
+		    (params->qbv_param.cycle_time.nanosecond >= 1000000000 ||
+		     params->qbv_param.base_time.fract_nsecond >= 1000000000)) {
+			return -EINVAL;
+		}
+
+		memcpy(&config.qbv_param, &params->qbv_param,
+		       sizeof(struct ethernet_qbv_param));
+		type = ETHERNET_CONFIG_TYPE_QBV_PARAM;
 	} else if (mgmt_request == NET_REQUEST_ETHERNET_SET_PROMISC_MODE) {
 		if (!is_hw_caps_supported(dev, ETHERNET_PROMISC_MODE)) {
 			return -ENOTSUP;
@@ -168,6 +188,9 @@
 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QAV_PARAM,
 				  ethernet_set_config);
 
+NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBV_PARAM,
+				  ethernet_set_config);
+
 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_PROMISC_MODE,
 				  ethernet_set_config);
 
@@ -244,6 +267,63 @@
 			break;
 		}
 
+	} else if (mgmt_request == NET_REQUEST_ETHERNET_GET_PORTS_NUM) {
+		type = ETHERNET_CONFIG_TYPE_PORTS_NUM;
+
+		ret = api->get_config(dev, type, &config);
+		if (ret) {
+			return ret;
+		}
+
+		params->ports_num = config.ports_num;
+
+	} else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QBV_PARAM) {
+		if (!is_hw_caps_supported(dev, ETHERNET_QBV)) {
+			return -ENOTSUP;
+		}
+
+		config.qbv_param.port_id = params->qbv_param.port_id;
+		config.qbv_param.type = params->qbv_param.type;
+		config.qbv_param.state = params->qbv_param.state;
+
+		if (config.qbv_param.type ==
+		    ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST) {
+			config.qbv_param.gate_control.row =
+				params->qbv_param.gate_control.row;
+		}
+
+		type = ETHERNET_CONFIG_TYPE_QBV_PARAM;
+
+		ret = api->get_config(dev, type, &config);
+		if (ret) {
+			return ret;
+		}
+
+		switch (config.qbv_param.type) {
+		case ETHERNET_QBV_PARAM_TYPE_STATUS:
+			params->qbv_param.enabled = config.qbv_param.enabled;
+			break;
+		case ETHERNET_QBV_PARAM_TYPE_TIME:
+			memcpy(&params->qbv_param.cycle_time,
+			       &config.qbv_param.cycle_time,
+			       sizeof(params->qbv_param.cycle_time));
+			memcpy(&params->qbv_param.base_time,
+			       &config.qbv_param.base_time,
+			       sizeof(params->qbv_param.base_time));
+			params->qbv_param.extension_time =
+				config.qbv_param.extension_time;
+			break;
+		case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN:
+			params->qbv_param.gate_control_list_len =
+				config.qbv_param.gate_control_list_len;
+			break;
+		case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST:
+			memcpy(&params->qbv_param.gate_control,
+			       &config.qbv_param.gate_control,
+			       sizeof(params->qbv_param.gate_control));
+			break;
+		}
+
 	} else {
 		return -EINVAL;
 	}
@@ -257,6 +337,12 @@
 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QAV_PARAM,
 				  ethernet_get_config);
 
+NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PORTS_NUM,
+				  ethernet_get_config);
+
+NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBV_PARAM,
+				  ethernet_get_config);
+
 void ethernet_mgmt_raise_carrier_on_event(struct net_if *iface)
 {
 	net_mgmt_event_notify(NET_EVENT_ETHERNET_CARRIER_ON, iface);