aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch')
-rw-r--r--target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch5113
1 files changed, 5113 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch b/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch
new file mode 100644
index 0000000000..33de677930
--- /dev/null
+++ b/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch
@@ -0,0 +1,5113 @@
+From e478ab518612f1a821968e1bb5b08b01b10085b0 Mon Sep 17 00:00:00 2001
+From: Po Liu <Po.Liu@nxp.com>
+Date: Tue, 15 Oct 2019 16:11:40 +0800
+Subject: [PATCH] net:tsn: netlink interface for APP layer to config TSN
+ capability hardware ports
+
+This patch provids netlink method to configure the TSN protocols hardwares.
+TSN guaranteed packet transport with bounded low latency, low packet delay
+variation, and low packet loss by hardware and software methods.
+
+The three basic components of TSN are:
+
+1. Time synchronization: This was implement by 8021AS which base on the
+ IEEE1588 precision Time Protocol. This is configured by the other way
+ in kernel.
+ 8021AS not included in this patch.
+
+2. Scheduling and traffic shaping and per-stream filter policing:
+ This patch support Qbv/Qci/Qbu/8021CB/Qav etc.
+
+3. Selection of communication paths:
+ This patch not support the pure software only TSN protocols(like Qcc)
+ but hardware related configuration.
+
+TSN Protocols supports by this patch: Qbv/Qci/Qbu/Credit-base Shaper(Qav).
+This patch verified on NXP ls1028ardb board.
+
+Signed-off-by: Po Liu <Po.Liu@nxp.com>
+---
+ include/net/tsn.h | 114 ++
+ include/uapi/linux/tsn.h | 1207 +++++++++++++++
+ net/Kconfig | 1 +
+ net/Makefile | 3 +
+ net/tsn/Kconfig | 15 +
+ net/tsn/Makefile | 1 +
+ net/tsn/genl_tsn.c | 3696 ++++++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 5037 insertions(+)
+ create mode 100644 include/net/tsn.h
+ create mode 100644 include/uapi/linux/tsn.h
+ create mode 100644 net/tsn/Kconfig
+ create mode 100644 net/tsn/Makefile
+ create mode 100644 net/tsn/genl_tsn.c
+
+--- /dev/null
++++ b/include/net/tsn.h
+@@ -0,0 +1,114 @@
++/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
++/* Copyright 2017-2019 NXP */
++
++#ifndef __TSN_H__
++#define __TSN_H__
++
++#include <linux/notifier.h>
++#include <uapi/linux/tsn.h>
++
++enum tsn_notifier_type {
++ TSN_QBV_CONFIGCHANGETIME_ARRIVE = 1,
++};
++
++struct tsn_notifier_info {
++ struct net_device *dev;
++ union {
++ struct tsn_qbv_conf qbv_notify;
++ struct tsn_qci_psfp_sgi_conf qci_notify;
++ } ntdata;
++};
++
++static inline struct net_device *
++tsn_notifier_info_to_dev(const struct tsn_notifier_info *info)
++{
++ return info->dev;
++}
++
++struct tsn_ops {
++ void (*device_init)(struct net_device *ndev);
++ void (*device_deinit)(struct net_device *ndev);
++ u32 (*get_capability)(struct net_device *ndev);
++ /* Qbv standard */
++ int (*qbv_set)(struct net_device *ndev, struct tsn_qbv_conf *qbvconf);
++ int (*qbv_get)(struct net_device *ndev, struct tsn_qbv_conf *qbvconf);
++ int (*qbv_get_status)(struct net_device *ndev,
++ struct tsn_qbv_status *qbvstat);
++ int (*cb_streamid_set)(struct net_device *ndev, u32 index,
++ bool enable, struct tsn_cb_streamid *sid);
++ int (*cb_streamid_get)(struct net_device *ndev, u32 index,
++ struct tsn_cb_streamid *sid);
++ int (*cb_streamid_counters_get)(struct net_device *ndev, u32 index,
++ struct tsn_cb_streamid_counters *sidcounter);
++ int (*qci_get_maxcap)(struct net_device *ndev,
++ struct tsn_qci_psfp_stream_param *qcicapa);
++ int (*qci_sfi_set)(struct net_device *ndev, u32 index, bool enable,
++ struct tsn_qci_psfp_sfi_conf *sficonf);
++ /* return: 0 stream filter instance not valid
++ * 1 stream filter instance valid
++ * -1 error happened
++ */
++ int (*qci_sfi_get)(struct net_device *ndev, u32 index,
++ struct tsn_qci_psfp_sfi_conf *sficonf);
++ int (*qci_sfi_counters_get)(struct net_device *ndev, u32 index,
++ struct tsn_qci_psfp_sfi_counters *sficounter);
++ int (*qci_sgi_set)(struct net_device *ndev, u32 index,
++ struct tsn_qci_psfp_sgi_conf *sgiconf);
++ int (*qci_sgi_get)(struct net_device *ndev, u32 index,
++ struct tsn_qci_psfp_sgi_conf *sgiconf);
++ int (*qci_sgi_status_get)(struct net_device *ndev, u16 index,
++ struct tsn_psfp_sgi_status *sgistat);
++ int (*qci_fmi_set)(struct net_device *ndev, u32 index, bool enable,
++ struct tsn_qci_psfp_fmi *fmi);
++ int (*qci_fmi_get)(struct net_device *ndev, u32 index,
++ struct tsn_qci_psfp_fmi *fmi,
++ struct tsn_qci_psfp_fmi_counters *counters);
++ int (*cbs_set)(struct net_device *ndev, u8 tc, u8 bw);
++ int (*cbs_get)(struct net_device *ndev, u8 tc);
++ /* To set a 8 bits vector shows 8 traffic classes
++ * preemtable(1) or express(0)
++ */
++ int (*qbu_set)(struct net_device *ndev, u8 ptvector);
++ /* To get port preemtion status */
++ int (*qbu_get)(struct net_device *ndev,
++ struct tsn_preempt_status *preemptstat);
++ int (*tsd_set)(struct net_device *ndev, struct tsn_tsd *tsd);
++ int (*tsd_get)(struct net_device *ndev, struct tsn_tsd_status *stats);
++ int (*ct_set)(struct net_device *ndev, u8 cut_thru);
++ int (*cbgen_set)(struct net_device *ndev, u32 index,
++ struct tsn_seq_gen_conf *seqgen);
++ int (*cbrec_set)(struct net_device *ndev, u32 index,
++ struct tsn_seq_rec_conf *seqrec);
++ int (*cb_get)(struct net_device *ndev, u32 index,
++ struct tsn_cb_status *c);
++ int (*dscp_set)(struct net_device *ndev, bool enable,
++ const u8 dscp_ix,
++ struct tsn_qos_switch_dscp_conf *c);
++};
++
++enum ethdev_type {
++ TSN_SWITCH,
++ TSN_ENDPOINT,
++};
++
++#define GROUP_OFFSET_SWITCH 256
++
++struct tsn_port {
++ u16 groupid;
++ struct tsn_ops *tsnops;
++ struct net_device *netdev;
++ struct list_head list;
++ enum ethdev_type type;
++ u8 tc_nums;
++ struct tsn_notifier_info nd;
++};
++
++struct tsn_port *tsn_get_port(struct net_device *ndev);
++int register_tsn_notifier(struct notifier_block *nb);
++int unregister_tsn_notifier(struct notifier_block *nb);
++int call_tsn_notifiers(unsigned long val, struct net_device *dev,
++ struct tsn_notifier_info *info);
++int tsn_port_register(struct net_device *netdev,
++ struct tsn_ops *tsnops, u16 groupid);
++void tsn_port_unregister(struct net_device *netdev);
++#endif
+--- /dev/null
++++ b/include/uapi/linux/tsn.h
+@@ -0,0 +1,1207 @@
++/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
++/* Copyright 2017-2019 NXP */
++
++#ifndef __UAPI_GENL_TSN_H
++#define __UAPI_GENL_TSN_H
++
++#define TSN_GENL_NAME "TSN_GEN_CTRL"
++#define TSN_GENL_VERSION 0x1
++
++#define MAX_USER_SIZE 0
++#define MAX_ATTR_SIZE 3072
++#define MAX_TOTAL_MSG_SIZE (MAX_USER_SIZE + MAX_ATTR_SIZE)
++#define MAX_ENTRY_SIZE 2048
++#define MAX_ENTRY_NUMBER 128
++#define MAX_IFNAME_COUNT 64
++
++#define TSN_MULTICAST_GROUP_QBV "qbv"
++#define TSN_MULTICAST_GROUP_QCI "qci"
++
++/* multicast groups */
++enum tsn_multicast_groups {
++ TSN_MCGRP_QBV,
++ TSN_MCGRP_QCI,
++ TSN_MCGRP_MAX
++};
++
++enum tsn_capability {
++ TSN_CAP_QBV = 0x1,
++ TSN_CAP_QCI = 0x2,
++ TSN_CAP_QBU = 0x4,
++ TSN_CAP_CBS = 0x8, /* Credit-based Shapter Qav */
++ TSN_CAP_CB = 0x10, /* 8021CB redundancy and replication */
++ TSN_CAP_TBS = 0x20, /* Time Based schedule */
++ TSN_CAP_CTH = 0x40, /* cut through */
++};
++
++/*
++ * Commands sent from userspace
++ * Not versioned. New commands should only be inserted at the enum's end
++ * prior to __TSN_CMD_MAX
++ */
++
++enum {
++ TSN_CMD_UNSPEC = 0, /* Reserved */
++ TSN_CMD_QBV_SET,
++ TSN_CMD_QBV_GET,
++ TSN_CMD_QBV_GET_STATUS,
++ TSN_CMD_CB_STREAMID_SET,
++ TSN_CMD_CB_STREAMID_GET,
++ TSN_CMD_CB_STREAMID_GET_COUNTS,
++ TSN_CMD_QCI_CAP_GET, /* Qci capability get length capability get */
++ TSN_CMD_QCI_SFI_SET,
++ TSN_CMD_QCI_SFI_GET,
++ TSN_CMD_QCI_SFI_GET_COUNTS,
++ TSN_CMD_QCI_SGI_SET,
++ TSN_CMD_QCI_SGI_GET,
++ TSN_CMD_QCI_SGI_GET_STATUS,
++ TSN_CMD_QCI_FMI_SET,
++ TSN_CMD_QCI_FMI_GET,
++ TSN_CMD_CBS_SET,
++ TSN_CMD_CBS_GET,
++ TSN_CMD_QBU_SET,
++ TSN_CMD_QBU_GET_STATUS,
++ TSN_CMD_QAV_SET_CBS,
++ TSN_CMD_QAV_GET_CBS,
++ TSN_CMD_TSD_SET,
++ TSN_CMD_TSD_GET,
++ TSN_CMD_CT_SET,
++ TSN_CMD_CBGEN_SET,
++ TSN_CMD_CBREC_SET,
++ TSN_CMD_CBSTAT_GET,
++ TSN_CMD_PCPMAP_SET_UNUSE,
++ TSN_CMD_DSCP_SET,
++ TSN_CMD_ECHO, /* user->kernel request/get-response */
++ TSN_CMD_REPLY, /* kernel->user event */
++ TSN_CMD_CAP_GET,
++ __TSN_CMD_MAX,
++};
++#define TSN_CMD_MAX (__TSN_CMD_MAX - 1)
++
++
++enum {
++ TSN_CMD_ATTR_UNSPEC = 0,
++ TSN_CMD_ATTR_MESG, /* demo message */
++ TSN_CMD_ATTR_DATA, /* demo data */
++ TSN_ATTR_IFNAME,
++ TSN_ATTR_PORT_NUMBER,
++ TSN_ATTR_QBV,
++ TSN_ATTR_STREAM_IDENTIFY, /* stream identify */
++ TSN_ATTR_QCI_SP, /* psfp port capbility parameters */
++ TSN_ATTR_QCI_SFI, /* psfp stream filter instance */
++ TSN_ATTR_QCI_SGI, /* psfp stream gate instance */
++ TSN_ATTR_QCI_FMI, /* psfp flow meter instance */
++ TSN_ATTR_CBS, /* credit-based shaper */
++ TSN_ATTR_TSD, /* Time Specific Departure */
++ TSN_ATTR_QBU, /* preemption */
++ TSN_ATTR_CT, /* cut through */
++ TSN_ATTR_CBGEN, /* 802.1CB sequence generate */
++ TSN_ATTR_CBREC, /* 802.1CB sequence recover */
++ TSN_ATTR_CBSTAT, /* 802.1CB status */
++ TSN_ATTR_PCPMAP_UNUSE,
++ TSN_ATTR_DSCP,
++ TSN_ATTR_CAP, /* TSN capbility */
++ __TSN_CMD_ATTR_MAX,
++};
++#define TSN_CMD_ATTR_MAX (__TSN_CMD_ATTR_MAX - 1)
++
++enum {
++ TSN_CAP_ATTR_UNSPEC,
++ TSN_CAP_ATTR_QBV,
++ TSN_CAP_ATTR_QCI,
++ TSN_CAP_ATTR_QBU,
++ TSN_CAP_ATTR_CBS,
++ TSN_CAP_ATTR_CB,
++ TSN_CAP_ATTR_TBS,
++ TSN_CAP_ATTR_CTH,
++ __TSN_CAP_ATTR_MAX,
++ TSN_CAP_ATTR_MAX = __TSN_CAP_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QBU_ATTR_UNSPEC,
++ TSN_QBU_ATTR_ADMIN_STATE,
++ TSN_QBU_ATTR_HOLD_ADVANCE,
++ TSN_QBU_ATTR_RELEASE_ADVANCE,
++ TSN_QBU_ATTR_ACTIVE,
++ TSN_QBU_ATTR_HOLD_REQUEST,
++ __TSN_QBU_ATTR_MAX,
++ TSN_QBU_ATTR_MAX = __TSN_QBU_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_CBS_ATTR_UNSPEC,
++ TSN_CBS_ATTR_TC_INDEX,
++ TSN_CBS_ATTR_BW,
++ __TSN_CBS_ATTR_MAX,
++ TSN_CBS_ATTR_MAX = __TSN_CBS_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_TSD_ATTR_UNSPEC,
++ TSN_TSD_ATTR_DISABLE,
++ TSN_TSD_ATTR_ENABLE,
++ TSN_TSD_ATTR_PERIOD,
++ TSN_TSD_ATTR_MAX_FRM_NUM,
++ TSN_TSD_ATTR_CYCLE_NUM,
++ TSN_TSD_ATTR_LOSS_STEPS,
++ TSN_TSD_ATTR_SYN_IMME,
++ __TSN_TSD_ATTR_MAX,
++ TSN_TSD_ATTR_MAX = __TSN_TSD_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_STREAMID_ATTR_UNSPEC,
++ TSN_STREAMID_ATTR_INDEX,
++ TSN_STREAMID_ATTR_ENABLE,
++ TSN_STREAMID_ATTR_DISABLE,
++ TSN_STREAMID_ATTR_STREAM_HANDLE,
++ TSN_STREAMID_ATTR_IFOP,
++ TSN_STREAMID_ATTR_OFOP,
++ TSN_STREAMID_ATTR_IFIP,
++ TSN_STREAMID_ATTR_OFIP,
++ TSN_STREAMID_ATTR_TYPE,
++ TSN_STREAMID_ATTR_NDMAC,
++ TSN_STREAMID_ATTR_NTAGGED,
++ TSN_STREAMID_ATTR_NVID,
++ TSN_STREAMID_ATTR_SMAC,
++ TSN_STREAMID_ATTR_STAGGED,
++ TSN_STREAMID_ATTR_SVID,
++ TSN_STREAMID_ATTR_COUNTERS_PSI,
++ TSN_STREAMID_ATTR_COUNTERS_PSO,
++ TSN_STREAMID_ATTR_COUNTERS_PSPPI,
++ TSN_STREAMID_ATTR_COUNTERS_PSPPO,
++ __TSN_STREAMID_ATTR_MAX,
++ TSN_STREAMID_ATTR_MAX = __TSN_STREAMID_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QCI_STREAM_ATTR_UNSPEC = 0,
++ TSN_QCI_STREAM_ATTR_MAX_SFI,
++ TSN_QCI_STREAM_ATTR_MAX_SGI,
++ TSN_QCI_STREAM_ATTR_MAX_FMI,
++ TSN_QCI_STREAM_ATTR_SLM,
++ __TSN_QCI_STREAM_ATTR_MAX,
++ TSN_QCI_STREAM_ATTR_MAX = __TSN_QCI_STREAM_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QCI_SFI_ATTR_UNSPEC = 0,
++ TSN_QCI_SFI_ATTR_INDEX,
++ TSN_QCI_SFI_ATTR_ENABLE,
++ TSN_QCI_SFI_ATTR_DISABLE,
++ TSN_QCI_SFI_ATTR_STREAM_HANDLE,
++ TSN_QCI_SFI_ATTR_PRIO_SPEC,
++ TSN_QCI_SFI_ATTR_GATE_ID,
++ TSN_QCI_SFI_ATTR_FILTER_TYPE,
++ TSN_QCI_SFI_ATTR_FLOW_ID,
++ TSN_QCI_SFI_ATTR_MAXSDU,
++ TSN_QCI_SFI_ATTR_COUNTERS,
++ TSN_QCI_SFI_ATTR_OVERSIZE_ENABLE,
++ TSN_QCI_SFI_ATTR_OVERSIZE,
++ __TSN_QCI_SFI_ATTR_MAX,
++ TSN_QCI_SFI_ATTR_MAX = __TSN_QCI_SFI_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QCI_SFI_ATTR_COUNTERS_UNSPEC = 0,
++ TSN_QCI_SFI_ATTR_MATCH,
++ TSN_QCI_SFI_ATTR_PASS,
++ TSN_QCI_SFI_ATTR_DROP,
++ TSN_QCI_SFI_ATTR_SDU_DROP,
++ TSN_QCI_SFI_ATTR_SDU_PASS,
++ TSN_QCI_SFI_ATTR_RED,
++ __TSN_QCI_SFI_ATTR_COUNT_MAX,
++ TSN_QCI_SFI_ATTR_COUNT_MAX = __TSN_QCI_SFI_ATTR_COUNT_MAX - 1,
++};
++
++enum {
++ TSN_QCI_SGI_ATTR_UNSPEC = 0,
++ TSN_QCI_SGI_ATTR_INDEX,
++ TSN_QCI_SGI_ATTR_ENABLE,
++ TSN_QCI_SGI_ATTR_DISABLE,
++ TSN_QCI_SGI_ATTR_CONFCHANGE,
++ TSN_QCI_SGI_ATTR_IRXEN, /* Invalid rx enable*/
++ TSN_QCI_SGI_ATTR_IRX,
++ TSN_QCI_SGI_ATTR_OEXEN, /* Octet exceed enable */
++ TSN_QCI_SGI_ATTR_OEX,
++ TSN_QCI_SGI_ATTR_ADMINENTRY,
++ TSN_QCI_SGI_ATTR_OPERENTRY,
++ TSN_QCI_SGI_ATTR_CCTIME, /* config change time */
++ TSN_QCI_SGI_ATTR_TICKG,
++ TSN_QCI_SGI_ATTR_CUTIME,
++ TSN_QCI_SGI_ATTR_CPENDING,
++ TSN_QCI_SGI_ATTR_CCERROR,
++ __TSN_QCI_SGI_ATTR_MAX,
++ TSN_QCI_SGI_ATTR_MAX = __TSN_QCI_SGI_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_SGI_ATTR_CTRL_UNSPEC = 0,
++ TSN_SGI_ATTR_CTRL_INITSTATE,
++ TSN_SGI_ATTR_CTRL_LEN,
++ TSN_SGI_ATTR_CTRL_CYTIME,
++ TSN_SGI_ATTR_CTRL_CYTIMEEX,
++ TSN_SGI_ATTR_CTRL_BTIME,
++ TSN_SGI_ATTR_CTRL_INITIPV,
++ TSN_SGI_ATTR_CTRL_GCLENTRY,
++ __TSN_SGI_ATTR_CTRL_MAX,
++ TSN_SGI_ATTR_CTRL_MAX = __TSN_SGI_ATTR_CTRL_MAX - 1,
++};
++
++enum {
++ TSN_SGI_ATTR_GCL_UNSPEC = 0,
++ TSN_SGI_ATTR_GCL_GATESTATE,
++ TSN_SGI_ATTR_GCL_IPV,
++ TSN_SGI_ATTR_GCL_INTERVAL,
++ TSN_SGI_ATTR_GCL_OCTMAX,
++ __TSN_SGI_ATTR_GCL_MAX,
++ TSN_SGI_ATTR_GCL_MAX = __TSN_SGI_ATTR_GCL_MAX - 1,
++};
++
++enum {
++ TSN_QCI_FMI_ATTR_UNSPEC = 0,
++ TSN_QCI_FMI_ATTR_INDEX,
++ TSN_QCI_FMI_ATTR_ENABLE,
++ TSN_QCI_FMI_ATTR_DISABLE,
++ TSN_QCI_FMI_ATTR_CIR,
++ TSN_QCI_FMI_ATTR_CBS,
++ TSN_QCI_FMI_ATTR_EIR,
++ TSN_QCI_FMI_ATTR_EBS,
++ TSN_QCI_FMI_ATTR_CF,
++ TSN_QCI_FMI_ATTR_CM,
++ TSN_QCI_FMI_ATTR_DROPYL,
++ TSN_QCI_FMI_ATTR_MAREDEN,
++ TSN_QCI_FMI_ATTR_MARED,
++ TSN_QCI_FMI_ATTR_COUNTERS,
++ __TSN_QCI_FMI_ATTR_MAX,
++ TSN_QCI_FMI_ATTR_MAX = __TSN_QCI_FMI_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QBV_ATTR_UNSPEC,
++ TSN_QBV_ATTR_ENABLE,
++ TSN_QBV_ATTR_DISABLE,
++ TSN_QBV_ATTR_CONFIGCHANGE,
++ TSN_QBV_ATTR_CONFIGCHANGETIME,
++ TSN_QBV_ATTR_MAXSDU,
++ TSN_QBV_ATTR_GRANULARITY,
++ TSN_QBV_ATTR_CURRENTTIME,
++ TSN_QBV_ATTR_CONFIGPENDING,
++ TSN_QBV_ATTR_CONFIGCHANGEERROR,
++ TSN_QBV_ATTR_ADMINENTRY,
++ TSN_QBV_ATTR_OPERENTRY,
++ TSN_QBV_ATTR_LISTMAX,
++ __TSN_QBV_ATTR_MAX,
++ TSN_QBV_ATTR_MAX = __TSN_QBV_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_QBV_ATTR_CTRL_UNSPEC,
++ TSN_QBV_ATTR_CTRL_LISTCOUNT,
++ TSN_QBV_ATTR_CTRL_GATESTATE,
++ TSN_QBV_ATTR_CTRL_CYCLETIME,
++ TSN_QBV_ATTR_CTRL_CYCLETIMEEXT,
++ TSN_QBV_ATTR_CTRL_BASETIME,
++ TSN_QBV_ATTR_CTRL_LISTENTRY,
++ __TSN_QBV_ATTR_CTRL_MAX,
++ TSN_QBV_ATTR_CTRL_MAX = __TSN_QBV_ATTR_CTRL_MAX - 1,
++};
++
++enum {
++ TSN_QBV_ATTR_ENTRY_UNSPEC,
++ TSN_QBV_ATTR_ENTRY_ID,
++ TSN_QBV_ATTR_ENTRY_GC,
++ TSN_QBV_ATTR_ENTRY_TM,
++ __TSN_QBV_ATTR_ENTRY_MAX,
++ TSN_QBV_ATTR_ENTRY_MAX = __TSN_QBV_ATTR_ENTRY_MAX - 1,
++};
++
++enum {
++ TSN_CT_ATTR_UNSPEC,
++ TSN_CT_ATTR_QUEUE_STATE,
++ __TSN_CT_ATTR_MAX,
++ TSN_CT_ATTR_MAX = __TSN_CT_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_CBGEN_ATTR_UNSPEC,
++ TSN_CBGEN_ATTR_INDEX,
++ TSN_CBGEN_ATTR_PORT_MASK,
++ TSN_CBGEN_ATTR_SPLIT_MASK,
++ TSN_CBGEN_ATTR_SEQ_LEN,
++ TSN_CBGEN_ATTR_SEQ_NUM,
++ __TSN_CBGEN_ATTR_MAX,
++ TSN_CBGEN_ATTR_MAX = __TSN_CBGEN_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_CBREC_ATTR_UNSPEC,
++ TSN_CBREC_ATTR_INDEX,
++ TSN_CBREC_ATTR_SEQ_LEN,
++ TSN_CBREC_ATTR_HIS_LEN,
++ TSN_CBREC_ATTR_TAG_POP_EN,
++ __TSN_CBREC_ATTR_MAX,
++ TSN_CBREC_ATTR_MAX = __TSN_CBREC_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_CBSTAT_ATTR_UNSPEC,
++ TSN_CBSTAT_ATTR_INDEX,
++ TSN_CBSTAT_ATTR_GEN_REC,
++ TSN_CBSTAT_ATTR_ERR,
++ TSN_CBSTAT_ATTR_SEQ_NUM,
++ TSN_CBSTAT_ATTR_SEQ_LEN,
++ TSN_CBSTAT_ATTR_SPLIT_MASK,
++ TSN_CBSTAT_ATTR_PORT_MASK,
++ TSN_CBSTAT_ATTR_HIS_LEN,
++ TSN_CBSTAT_ATTR_SEQ_HIS,
++ __TSN_CBSTAT_ATTR_MAX,
++ TSN_CBSTAT_ATTR_MAX = __TSN_CBSTAT_ATTR_MAX - 1,
++};
++
++enum {
++ TSN_DSCP_ATTR_UNSPEC,
++ TSN_DSCP_ATTR_DISABLE,
++ TSN_DSCP_ATTR_INDEX,
++ TSN_DSCP_ATTR_COS,
++ TSN_DSCP_ATTR_DPL,
++ __TSN_DSCP_ATTR_MAX,
++ TSN_DSCP_ATTR_MAX = __TSN_DSCP_ATTR_MAX - 1,
++};
++
++#define ptptime_t __u64
++
++#define MAX_QUEUE_CNT 8
++
++struct tsn_preempt_status {
++ /* The value of admin_state shows a 8-bits vector value for showing
++ * the framePreemptionAdminStatus parameter and PreemptionPriority
++ * for the traffic class. Bit-7 is the highest priority traffic class
++ * and the bit-0 is the lowest priority traffic class.
++ * The bit is express (0) and is preemptible (1).
++ */
++ __u8 admin_state;
++ /* The value of the holdAdvance parameter for the port in nanoseconds.
++ * There is no default value; the holdAdvance is a property of the
++ * underlying MAC." This parameter corresponds to the holdAdvance
++ * parameter in 802.1Qbu.
++ */
++ __u32 hold_advance;
++
++ /* The value of the releaseAdvance parameter for the port in
++ * nanoseconds. There is no default value; the releaseAdvance is a
++ * property of the underlying MAC." This parameter corresponds to the
++ * releaseAdvance parameter in 802.1Qbu.
++ */
++ __u32 release_advance;
++
++ /* The value is active (TRUE) when preemption is operationally active
++ * for the port, and idle (FALSE) otherwise. This parameter corresponds
++ * to the preemptionActive parameter in 802.1Qbu.
++ */
++ __u8 preemption_active;
++
++ /* The value is hold (1) when the sequence of gate operations for
++ * the port has executed a Set-And-Hold-MAC operation, and release
++ * (2) when the sequence of gate operations has executed a
++ * Set-And-Release-MAC operation. The value of this object is release
++ * (FALSE) on system initialization. This parameter corresponds to the
++ * holdRequest parameter in 802.1Qbu.
++ */
++ __u8 hold_request;
++};
++
++enum tsn_tx_mode {
++ TX_MODE_STRICT,
++ TX_MODE_CBS,
++ TX_MODE_ETS,
++ TX_MODE_VENDOR_DEFINE = 255,
++};
++
++#define QUEUE_TX_MASK ((1 << TX_MODE_STRICT) | (1 << TX_MODE_CBS) \
++ | (1 << TX_MODE_ETS) | (1 << TX_MODE_VENDOR_DEFINE))
++
++struct cbs_status {
++ __u8 delta_bw; /* percentage, 0~100 */
++ __u32 idleslope;
++ __s32 sendslope;
++ __u32 maxframesize;
++ __u32 hicredit;
++ __s32 locredit;
++ __u32 maxninference;
++};
++
++struct tx_queue {
++ /* tx_queue_capbility shows the queue's capability mask.
++ * refer the enum tsn_tx_mode
++ */
++ __u8 capability;
++
++ /* tx_queue_mode is current queue working mode */
++ __u8 mode;
++
++ /* prio is showing the queue priority */
++ __u8 prio;
++
++ /* mstat shows the status data of cbs or priority */
++ union {
++ struct cbs_status cbs;
++ };
++};
++
++struct port_status {
++ /* txqueue_cnt shows how many queues in this port */
++ __u8 queue_cnt;
++
++ /* max_rate(Mbit/s) is the port transmit rate current port is setting */
++ __u32 max_rate;
++
++ /* tsn_capability mask the tsn capability */
++ __u32 tsn_capability;
++};
++
++enum tsn_cb_streamid_type {
++ STREAMID_RESERVED = 0,
++ /* Null Stream identification */
++ STREAMID_NULL,
++ /* Source MAC and VLAN Stream identification */
++ STREAMID_SMAC_VLAN,
++ /* Active Destination MAC and VLAN stream identification */
++ STREAMID_DMAC_VLAN,
++ /* IP stream identification */
++ STREAMID_IP,
++};
++
++/* When instantiating an instance of the Null Stream identification function
++ * 8021CB(6.4) for a particular input Stream, the managed objects in the
++ * following subsections serve as the tsnStreamIdParameters managed object
++ * 8021CB claus(9.1.1.7).
++ */
++struct tsn_cb_null_streamid {
++ /* tsnCpeNullDownDestMac. Specifies the destination_address that
++ * identifies a packet in an Enhanced Internal Sublayer Service (EISS)
++ * indication primitive, to the Null Stream identification function.
++ */
++ __u64 dmac;
++
++ /* tsnCpeNullDownTagged. It can take the following values:
++ * 1 tagged: A frame must have a VLAN tag to be recognized as belonging
++ * to the Stream.
++ * 2 priority: A frame must be untagged, or have a VLAN tag with a VLAN
++ * ID = 0 to be recognized as belonging to the Stream.
++ * 3 all: A frame is recognized as belonging to the Stream whether
++ * tagged or not.
++ */
++ __u8 tagged;
++
++ /* tsnCpeNullDownVlan. Specifies the vlan_identifier parameter that
++ * identifies a packet in an EISS indication primitive to the Null
++ * Stream identification function. A value of 0 indicates that the vlan
++ * _identifier parameter is ignored on EISS indication primitives.
++ */
++ __u16 vid;
++};
++
++struct tsn_cb_source_streamid {
++ __u64 smac;
++ __u8 tagged;
++ __u16 vid;
++};
++
++struct tsn_cb_dest_streamid {
++ __u64 down_dmac;
++ __u8 down_tagged;
++ __u16 down_vid;
++ __u8 down_prio;
++ __u64 up_dmac;
++ __u8 up_tagged;
++ __u16 up_vid;
++ __u8 up_prio;
++};
++
++struct tsn_cb_ip_streamid {
++ __u64 dmac;
++ __u8 tagged;
++ __u16 vid;
++ __u64 siph;
++ __u64 sipl;
++ __u64 diph;
++ __u64 dipl;
++ __u8 dscp;
++ __u8 npt;
++ __u16 sport;
++ __u16 dport;
++};
++
++/* 802.1CB stream identify table clause 9.1 */
++struct tsn_cb_streamid {
++ /* The objects in a given entry of the Stream identity table are used
++ * to control packets whose stream_handle subparameter is equal to the
++ * entry tsnStreamIdHandle object.
++ */
++ __s32 handle;
++
++ /* The list of ports on which an in-facing Stream identification
++ * function in the output (towards the system forwarding function)
++ * direction Only Active Destination MAC and VLAN Stream identification
++ * (or nothing) can be configured.
++ */
++ __u32 ifac_oport;
++
++ /* The list of ports on which an out-facing Stream identification
++ * function in the output (towards the physical interface) direction.
++ * Only Active Destination MAC and VLAN Stream identification
++ * (or nothing) can be configured.
++ */
++ __u32 ofac_oport;
++
++ /* The list of ports on which an in-facing Stream identification
++ * function in the input (coming from the system forwarding function)
++ * direction
++ */
++ __u32 ifac_iport;
++
++ /* The list of ports on which an out-facing Stream identification
++ * function in the input (coming from the physical interface) direction
++ * .
++ */
++ __u32 ofac_iport;
++
++ /* An enumerated value indicating the method used to identify packets
++ * belonging to the Stream.
++ * The Organizationally Unique Identifier (OUI) or Company Identifier
++ * (CID) to identify the organization defining the enumerated type
++ * should be: 00-80-C2
++ * 1: null stream identification
++ * 2: source mac and vlan stream identification
++ * 3: activ destination mac and vlan stream identification
++ * 4: ip stream identifaciton
++ */
++ __u8 type;
++
++ /* tsnStreamIdParameters The number of controlling parameters for a
++ * Stream identification method, their types and values, are specific
++ * to the tsnStreamIdIdentificationType
++ */
++ union {
++ struct tsn_cb_null_streamid nid;
++ struct tsn_cb_source_streamid sid;
++ struct tsn_cb_dest_streamid did;
++ struct tsn_cb_ip_streamid iid;
++ } para;
++};
++
++/* Following counters are instantiated for each port on which the Stream
++ * identification function (6.2) is configured. The counters are indexed by
++ * port number, facing (in-facing or out-facing), and stream_handle value
++ * (tsnStreamIdHandle, 9.1.1.1).
++ */
++struct tsn_cb_streamid_counters {
++ struct {
++ __u64 input;
++ __u64 output;
++ } per_stream;
++
++ struct {
++ __u64 input;
++ __u64 output;
++ } per_streamport[32];
++};
++
++/* 802.1Qci Stream Parameter Table, read from port */
++struct tsn_qci_psfp_stream_param {
++ /* MaxStreamFilterInstances.
++ * The maximum number of Stream Filter instances supported by this
++ * Bridge component.
++ */
++ __s32 max_sf_instance;
++
++ /* MaxStreamGateInstances
++ * The maximum number of Stream Gate instances supported by this Bridge
++ * component.
++ */
++ __s32 max_sg_instance;
++
++ /* MaxFlowMeterInstances
++ * The maximum number of Flow Meter instances supported by this Bridge
++ * component.
++ */
++ __s32 max_fm_instance;
++
++ /* SupportedListMax
++ * The maximum value supported by this Bridge component of the
++ * AdminControlListLength and OperControlListLength parameters.
++ */
++ __s32 supported_list_max;
++};
++
++/* 802.1Qci Stream Filter Instance Table, counters part only. */
++struct tsn_qci_psfp_sfi_counters {
++ /* The MatchingFramesCount counter counts received frames that match
++ * this stream filter.
++ */
++ __u64 matching_frames_count;
++
++ /* The PassingFramesCount counter counts received frames that pass the
++ * gate associated with this stream filter.
++ */
++ __u64 passing_frames_count;
++
++ /* The NotPassingFramesCount counter counts received frames that do not
++ * pass the gate associated with this stream filter.
++ */
++ __u64 not_passing_frames_count;
++
++ /* The PassingSDUCount counter counts received frames that pass the SDU
++ * size filter specification associated with this stream filter.
++ */
++ __u64 passing_sdu_count;
++
++ /* The NotPassingSDUCount counter counts received frames that do not
++ * pass the SDU size filter specification associated with this stream
++ * filter.
++ */
++ __u64 not_passing_sdu_count;
++
++ /* The REDFramesCount counter counts received random early detection
++ * (RED) frames associated with this stream filter.
++ */
++ __u64 red_frames_count;
++};
++
++/* 802.1Qci Stream Filter Instance Table, configuration part only. */
++struct tsn_qci_psfp_sfi_conf {
++
++ /* The StreamHandleSpec parameter contains a stream identifier
++ * specification value. A value of -1 denotes the wild card value; zero
++ * or positive values denote stream identifier values.
++ */
++ __s32 stream_handle_spec;
++
++ /* The PrioritySpec parameter contains a priority specification value.
++ * A value of -1 denotes the wild card value; zero or positive values
++ * denote priority values.
++ */
++ __s8 priority_spec;
++
++ /* The StreamGateInstanceID parameter contains the index of an entry in
++ * the Stream Gate Table.
++ */
++ __u32 stream_gate_instance_id;
++
++ /* The filter specifications. The actions specified in a filter
++ * specification can result in a frame passing or failing the specified
++ * filter. Frames that fail a filter are discarded.
++ */
++ struct {
++ /* The MaximumSDUSize parameter specifies the maximum allowed
++ * frame size for the stream. Any frame exceeding this value
++ * will be dropped. A value of 0 denote that the MaximumSDUSize
++ * filter is disabled for this stream.
++ */
++ __u16 maximum_sdu_size;
++
++ /* The FlowMeterInstanceID parameter contains the index of an
++ * entry in the Flow Meter Table. A value of -1 denotes that
++ * no flow meter is assigned; zero or positive values denote
++ * flow meter IDs.
++ */
++ __s32 flow_meter_instance_id;
++ } stream_filter;
++
++ /* The StreamBlockedDueToOversizeFrameEnable object contains a Boolean
++ * value that indicates whether the StreamBlockedDueToOversizeFrame
++ * function is enabled (TRUE) or disabled (FALSE).
++ */
++ __u8 block_oversize_enable;
++
++ /* The StreamBlockedDueToOversizeFrame object contains a Boolean value
++ * that indicates whether, if the StreamBlockedDueToOversizeFrame
++ * function is enabled, all frames are to be discarded (TRUE) or not
++ * (FALSE).
++ */
++ __u8 block_oversize;
++};
++
++/* 802.1Qci Stream Gate Control List Entry. */
++struct tsn_qci_psfp_gcl {
++ /* The GateState parameter specifies a desired state, open (true) or
++ * closed (false), for the stream gate.
++ */
++ __u8 gate_state;
++
++ /* An IPV is encoded as a signed integer. A negative denotes the null
++ * value; zero or positive values denote internal priority values.
++ */
++ __s8 ipv;
++
++ /* A TimeInterval is encoded in 4 octets as a 32-bit unsigned integer,
++ * representing a number of nanoseconds.
++ */
++ __u32 time_interval;
++
++ /* The maximum number of octets that are permitted to pass the gate
++ * during the specified TimeInterval. If zero, there is no maximum.
++ */
++ __u32 octet_max;
++
++};
++
++/* 802.1Qci Stream Gate Admin/Operation common list control parameters */
++struct tsn_qci_sg_control {
++ /* The administrative/operation value of the GateStates parameter
++ * for the stream gate. A value of false indicates closed;
++ * a value of true indicates open.
++ */
++ __u8 gate_states;
++
++ /* The administrative/operation value of the ListMax parameter for the
++ * gate. The integer value indicates the number of entries (TLVs) in
++ * the AdminControlList/OperControlList.
++ */
++ __u8 control_list_length;
++
++ /* The administrative/operation value of the CycleTime parameter for
++ * the gate. The value is an unsigned integer number of nanoseconds.
++ */
++ __u32 cycle_time;
++
++ /* The administrative/operation value of the CycleTimeExtension
++ * parameter for the gate. The value is an unsigned integer number
++ * of nanoseconds.
++ */
++ __u32 cycle_time_extension;
++
++ /* The administrative/operation value of the BaseTime parameter for the
++ * gate. The value is a representation of a PTPtime value, consisting
++ * of a 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds.
++ */
++ ptptime_t base_time;
++
++ /* The administrative/operation value of the IPV parameter for the gate.
++ * A value of -1 denotes the null value; zero or positive values denote
++ * internal priority values.
++ */
++ __s8 init_ipv;
++
++ /* control_list contend the gate control list of
++ * administrative/operation
++ */
++ struct tsn_qci_psfp_gcl *gcl;
++};
++
++/* 802.1Qci Stream Gate Instance Table, configuration part only. */
++struct tsn_qci_psfp_sgi_conf {
++ /* The GateEnabled parameter determines whether the stream gate is
++ * active (true) or inactive (false).
++ */
++ __u8 gate_enabled;
++
++ /* The ConfigChange parameter signals the start of a configuration
++ * change when it is set to TRUE. This should only be done when the
++ * various administrative parameters are all set to appropriate values.
++ */
++ __u8 config_change;
++
++ /* admin control parameters with admin control list */
++ struct tsn_qci_sg_control admin;
++
++ /* The GateClosedDueToInvalidRxEnable object contains a Boolean value
++ * that indicates whether the GateClosedDueToInvalidRx function is
++ * enabled (TRUE) or disabled (FALSE).
++ */
++ __u8 block_invalid_rx_enable;
++
++ /* The GateClosedDueToInvalidRx object contains a Boolean value that
++ * indicates whether, if the GateClosedDueToInvalidRx function is
++ * enabled, all frames are to be discarded (TRUE) or not (FALSE).
++ */
++ __u8 block_invalid_rx;
++
++ /* The GateClosedDueToOctetsExceededEnable object contains a Boolean
++ * value that indicates whether the GateClosedDueToOctetsExceeded
++ * function is enabled (TRUE) or disabled (FALSE).
++ */
++ __u8 block_octets_exceeded_enable;
++
++ /* The GateClosedDueToOctetsExceeded object contains a Boolean value
++ * that indicates whether, if the GateClosedDueToOctetsExceeded
++ * function is enabled, all frames are to be discarded (TRUE) or not
++ * (FALSE).
++ */
++ __u8 block_octets_exceeded;
++};
++
++/* 802.1Qci Stream Gate Instance Table, status part only. */
++struct tsn_psfp_sgi_status {
++
++ /* admin control parameters with admin control list */
++ struct tsn_qci_sg_control oper;
++
++ /* The PTPtime at which the next config change is scheduled to occur.
++ * The value is a representation of a PTPtime value, consisting of a
++ * 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds.
++ */
++ ptptime_t config_change_time;
++
++ /* The granularity of the cycle time clock, represented as an unsigned
++ * number of tenths of nanoseconds.
++ */
++ __u32 tick_granularity;
++
++ /* The current time, in PTPtime, as maintained by the local system.
++ * The value is a representation of a PTPtime value, consisting of a
++ * 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds.
++ */
++ ptptime_t current_time;
++
++ /* The value of the ConfigPending state machine variable. The value is
++ * TRUE if a configuration change is in progress but has not yet
++ * completed.
++ */
++ __u8 config_pending;
++
++ /* A counter of the number of times that a re-configuration of the
++ * traffic schedule has been requested with the old schedule still
++ * running and the requested base time was in the past.
++ */
++ __u64 config_change_error;
++
++};
++
++/* 802.1Qci Flow Meter Instance Table. */
++struct tsn_qci_psfp_fmi {
++ /* The FlowMeterCIR parameter contains an integer value that represents
++ * the CIR value for the flow meter, in kbit/s.
++ */
++ __u32 cir;
++
++ /* The FlowMeterCBS parameter contains an integer value that represents
++ * the CBS value for the flow meter, in octets.
++ */
++ __u32 cbs;
++
++ /* The FlowMeterEIR parameter contains an integer value that represents
++ * the EIR value for the flow meter, in kbit/s.
++ */
++ __u32 eir;
++
++ /* The FlowMeterEBS parameter contains an integer value that represents
++ * the EBS value for the flow meter, in octets.
++ */
++ __u32 ebs;
++
++ /* The FlowMeterCF parameter contains a Boolean value that represents
++ * the CF value for the flow meter, as a Boolean value indicating no
++ * coupling (FALSE) or coupling (TRUE).
++ */
++ __u8 cf;
++
++ /* The FlowMeterCM parameter contains a Boolean value that represents
++ * the CM value for the flow meter, as a Boolean value indicating
++ * colorBlind (FALSE) or colorAware (TRUE).
++ */
++ __u8 cm;
++
++ /* The FlowMeterDropOnYellow parameter contains a Boolean value that
++ * indicates whether yellow frames are dropped (TRUE) or have
++ * drop_eligible set to TRUE (FALSE).
++ */
++ __u8 drop_on_yellow;
++
++ /* The FlowMeterMarkAllFramesRedEnable parameter contains a Boolean
++ * value that indicates whether the MarkAllFramesRed function
++ * is enabled (TRUE) or disabled (FALSE).
++ */
++ __u8 mark_red_enable;
++
++ /* The FlowMeterMarkAllFramesRed parameter contains a Boolean value
++ * that indicates whether, if the MarkAllFramesRed function is enabled,
++ * all frames are to be discarded (TRUE) or not (FALSE).
++ */
++ __u8 mark_red;
++};
++
++struct tsn_qci_psfp_fmi_counters {
++ __u64 bytecount;
++ __u64 drop;
++ __u64 dr0_green;
++ __u64 dr1_green;
++ __u64 dr2_yellow;
++ __u64 remark_yellow;
++ __u64 dr3_red;
++ __u64 remark_red;
++};
++
++/* 802.1cb */
++struct tsn_seq_gen_conf {
++
++ /* The InputPortMask parameter contains a port mask.
++ * If the packet is from input port belonging to this
++ * port mask then it's on known stream and sequence
++ * generation parameters can be applied.
++ */
++ __u8 iport_mask;
++
++ /* The SplitMask parameter contains a output port mask
++ * used to add redundant paths.
++ */
++ __u8 split_mask;
++
++ /* The SequenceSpaceLenLog parameter is a value to specifies
++ * number of bits to be used for sequence number.
++ */
++ __u8 seq_len;
++
++ /* The SequenceNumber parameter is a value to used for
++ * outgoing packet's sequence number generation.
++ */
++ __u32 seq_num;
++};
++
++struct tsn_seq_rec_conf {
++
++ /* The SequenceSpaceLenLog parameter is a value to specifies
++ * number of bits to be used for sequence number.
++ */
++ __u8 seq_len;
++
++ /* The HistorySpaceLenLog parameter is a value to specifies
++ * number of bits to be used for history register.
++ */
++ __u8 his_len;
++
++ /* The RTagPopEnable parameter contains a __u8 to enable removal
++ * of redundancy tag from the packet.
++ */
++ __u8 rtag_pop_en;
++};
++
++struct tsn_cb_status {
++
++ /* The GenRecover parameter contains a value specifies type
++ * of stream sequence parameters:
++ * 0: Stream sequence parameters are for generation.
++ * 1: Stream sequence parameters are for recovery.
++ */
++ __u8 gen_rec;
++
++ /* The ErrStatus parameter indicates stream's error status
++ * 1: This switch is expected to sequence the stream,
++ * but the incoming packet has sequence number.
++ * 2: This switch is expected to recover the stream,
++ * but the incoming packet is NONSEQ.
++ */
++ __u8 err;
++
++ /* The SequenceNumber parameter is a value to used for
++ * outgoing packet's sequence number generation.
++ */
++ __u32 seq_num;
++
++ /* The SequenceSpaceLenLog parameter is a value to specifies
++ * number of bits to be used for sequence number.
++ */
++ __u8 seq_len;
++
++ /* The SplitMask parameter contains a output port mask
++ * used to add redundant paths.
++ */
++ __u8 split_mask;
++
++ /* The InputPortMask parameter contains a port mask.
++ * If the packet is from input port belonging to this
++ * port mask then it's on known stream and sequence
++ * generation parameters can be applied.
++ */
++ __u8 iport_mask;
++
++ /* The HistorySpaceLenLog parameter is a value to specifies
++ * number of bits to be used for history register.
++ */
++ __u8 his_len;
++
++ /* The SequenceHistory parameter Maintains history of sequence
++ * numbers of received packets.
++ */
++ __u32 seq_his;
++};
++
++/* An entry for gate control list */
++struct tsn_qbv_entry {
++ /* Octet represent the gate states for the corresponding traffic
++ * classes.
++ * The MS bit corresponds to traffic class 7.
++ * The LS bit to traffic class 0.
++ * A bit value of 0 indicates closed;
++ * A bit value of 1 indicates open.
++ */
++ __u8 gate_state;
++
++ /* A TimeInterval is encoded in 4 octets as a 32-bit unsigned integer,
++ * representing a number of nanoseconds.
++ */
++ __u32 time_interval;
++};
++
++/* The administrative/operation time and gate list */
++struct tsn_qbv_basic {
++ /* The administrative/operation value of the GateStates parameter for
++ * the Port.
++ * The bits of the octet represent the gate states for the
++ * corresponding traffic classes; the MS bit corresponds to traffic
++ * class 7, the LS bit to traffic class 0. A bit value of 0 indicates
++ * closed; a bit value of 1 indicates open.
++ * The value of this object MUST be retained
++ * across reinitializations of the management system.
++ */
++ __u8 gate_states;
++
++ /* The administrative/operation value of the ListMax parameter for the
++ * port. The integer value indicates the number of entries (TLVs) in
++ * the AdminControlList. The value of this object MUST be retained
++ * across reinitializations of the management system.
++ */
++ __u32 control_list_length;
++
++ /* The administrative/operation value of the AdminCycleTime
++ * parameter for the Port. The numerator and denominator together
++ * represent the cycle time as a rational number of seconds. The value
++ * of this object MUST be retained across reinitializations of the
++ * management system.
++ */
++ __u32 cycle_time;
++
++ /* The administrative/operation value of the CycleTimeExtension
++ * parameter for the Port. The value is an unsigned integer number of
++ * nanoseconds.
++ * The value of this object MUST be retained across reinitializations
++ * of the management system.
++ */
++
++ __u32 cycle_time_extension;
++
++ /* The administrative/operation value of the BaseTime parameter for the
++ * Port. The value is a representation of a PTPtime value, consisting
++ * of a 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds.
++ * The value of this object MUST be retained across reinitializations of
++ * the management system.
++ */
++ ptptime_t base_time;
++
++ /* admin_control_list represent the AdminControlList/OperControlList.
++ * The administrative version of the gate control list for the Port.
++ */
++ struct tsn_qbv_entry *control_list;
++};
++
++struct tsn_qbv_conf {
++ /* The GateEnabled parameter determines whether traffic scheduling is
++ * active (true) or inactive (false). The value of this object MUST be
++ * retained across reinitializations of the management system.
++ */
++ __u8 gate_enabled;
++
++ /* The maxsdu parameter denoting the maximum SDU size supported by the
++ * queue.
++ */
++ __u32 maxsdu;
++
++ /* The ConfigChange parameter signals the start of a configuration
++ * change when it is set to TRUE. This should only be done when the
++ * various administrative parameters are all set to appropriate values.
++ */
++ __u8 config_change;
++
++ /* The admin parameter signals the admin relate cycletime, basictime,
++ * gatelist paraters.
++ */
++ struct tsn_qbv_basic admin;
++};
++
++/* 802.1Qbv (Time Aware Shaper) port status */
++struct tsn_qbv_status {
++ /* The PTPtime at which the next config change is scheduled to occur.
++ * The value is a representation of a PTPtime value, consisting of a
++ * 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds. The value of this object MUST be retained across
++ * reinitializations of the management system.
++ */
++ ptptime_t config_change_time;
++
++ /* The granularity of the cycle time clock, represented as an unsigned
++ * number of tenths of nanoseconds. The value of this object MUST be
++ * retained across reinitializations of the management system.
++ */
++ __u32 tick_granularity;
++
++ /* The current time, in PTPtime, as maintained by the local system.
++ * The value is a representation of a PTPtime value, consisting of a
++ * 48-bit integer number of seconds and a 32-bit integer number of
++ * nanoseconds.
++ */
++ ptptime_t current_time;
++
++ /* The value of the ConfigPending state machine variable. The value is
++ * TRUE if a configuration change is in progress but has not yet
++ * completed.
++ */
++ __u8 config_pending;
++
++ /* A counter of the number of times that a re-configuration of the
++ * traffic schedule has been requested with the old schedule still
++ * running and the requested base time was in the past.
++ */
++ __u64 config_change_error;
++
++ /* The maximum value supported by this Port of the
++ * AdminControlListLength and OperControlListLength parameters.
++ */
++ __u32 supported_list_max;
++
++ /* Operation settings parameters and Oper gate list */
++ struct tsn_qbv_basic oper;
++};
++
++/* Time Specific Departure parameters */
++struct tsn_tsd {
++ __u8 enable;
++
++ /* The cycle time, in units of microsecond(us)*/
++ __u32 period;
++
++ /* The maximum number of frames which could be transmitted on one cycle
++ * The exceeding frames will be transmitted on next cycle.
++ */
++ __u32 maxFrameNum;
++
++ /* Specify the time of the first cycle begins.
++ * 1: begin when the queue get the first frame to transmit.
++ * 2: begin immediately at the end of setting function.
++ */
++ __u32 syn_flag;
++};
++
++struct tsn_tsd_status {
++ __u8 enable;
++ __u32 period;
++ __u32 maxFrameNum;
++ __u32 flag;
++ __u32 cycleNum;
++ __u32 loss_steps;
++};
++
++struct tsn_qos_switch_dscp_conf {
++ __u8 trust;
++ __u8 cos;
++ __u8 dpl;
++ __u8 remark;
++ __u8 dscp; /* New ingress translated DSCP value */
++};
++
++#endif /* _UAPI_GENL_TSN_H */
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -240,6 +240,7 @@ source "net/ieee802154/Kconfig"
+ source "net/mac802154/Kconfig"
+ source "net/sched/Kconfig"
+ source "net/dcb/Kconfig"
++source "net/tsn/Kconfig"
+ source "net/dns_resolver/Kconfig"
+ source "net/batman-adv/Kconfig"
+ source "net/openvswitch/Kconfig"
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -59,6 +59,9 @@ obj-$(CONFIG_CAIF) += caif/
+ ifneq ($(CONFIG_DCB),)
+ obj-y += dcb/
+ endif
++ifneq ($(CONFIG_TSN),)
++obj-y += tsn/
++endif
+ obj-$(CONFIG_6LOWPAN) += 6lowpan/
+ obj-$(CONFIG_IEEE802154) += ieee802154/
+ obj-$(CONFIG_MAC802154) += mac802154/
+--- /dev/null
++++ b/net/tsn/Kconfig
+@@ -0,0 +1,15 @@
++config TSN
++ bool "802.1 Time-Sensitive Networking support"
++ default n
++ depends on VLAN_8021Q && PTP_1588_CLOCK
++ help
++ This enables support for TSN(time sensitive networking)
++ TSN features include:
++ 802.1Qav:
++ 802.1Qbv:
++ 802.1Qci:
++ 802.1Qbu:
++ 802.1AS:
++ 802.1CB:
++
++ If unsure, say N.
+--- /dev/null
++++ b/net/tsn/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_TSN) += genl_tsn.o
+--- /dev/null
++++ b/net/tsn/genl_tsn.c
+@@ -0,0 +1,3696 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
++/* Copyright 2017-2019 NXP */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/netdevice.h>
++#include <linux/if_vlan.h>
++#include <net/genetlink.h>
++#include <net/netlink.h>
++#include <linux/version.h>
++#include <net/tsn.h>
++
++#define NLA_PARSE_NESTED(a, b, c, d) \
++ nla_parse_nested_deprecated(a, b, c, d, NULL)
++#define NLA_PUT_U64(a, b, c) nla_put_u64_64bit(a, b, c, NLA_U64)
++
++static struct genl_family tsn_family;
++
++LIST_HEAD(port_list);
++
++static const struct nla_policy tsn_cmd_policy[TSN_CMD_ATTR_MAX + 1] = {
++ [TSN_CMD_ATTR_MESG] = { .type = NLA_STRING },
++ [TSN_CMD_ATTR_DATA] = { .type = NLA_S32 },
++ [TSN_ATTR_IFNAME] = { .type = NLA_STRING },
++ [TSN_ATTR_PORT_NUMBER] = { .type = NLA_U8 },
++ [TSN_ATTR_CAP] = { .type = NLA_NESTED },
++ [TSN_ATTR_QBV] = { .type = NLA_NESTED },
++ [TSN_ATTR_STREAM_IDENTIFY] = { .type = NLA_NESTED },
++ [TSN_ATTR_QCI_SP] = { .type = NLA_NESTED },
++ [TSN_ATTR_QCI_SFI] = { .type = NLA_NESTED },
++ [TSN_ATTR_QCI_SGI] = { .type = NLA_NESTED },
++ [TSN_ATTR_QCI_FMI] = { .type = NLA_NESTED },
++ [TSN_ATTR_CBS] = { .type = NLA_NESTED },
++ [TSN_ATTR_TSD] = { .type = NLA_NESTED },
++ [TSN_ATTR_QBU] = { .type = NLA_NESTED },
++ [TSN_ATTR_CT] = { .type = NLA_NESTED },
++ [TSN_ATTR_CBGEN] = { .type = NLA_NESTED },
++ [TSN_ATTR_CBREC] = { .type = NLA_NESTED },
++ [TSN_ATTR_CBSTAT] = { .type = NLA_NESTED },
++ [TSN_ATTR_DSCP] = { .type = NLA_NESTED },
++};
++
++static const struct nla_policy tsn_cap_policy[TSN_CAP_ATTR_MAX + 1] = {
++ [TSN_CAP_ATTR_QBV] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_QCI] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_QBU] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_CBS] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_CB] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_TBS] = { .type = NLA_FLAG },
++ [TSN_CAP_ATTR_CTH] = { .type = NLA_FLAG },
++};
++
++static const struct nla_policy qci_cap_policy[TSN_QCI_STREAM_ATTR_MAX + 1] = {
++ [TSN_QCI_STREAM_ATTR_MAX_SFI] = { .type = NLA_U32 },
++ [TSN_QCI_STREAM_ATTR_MAX_SGI] = { .type = NLA_U32 },
++ [TSN_QCI_STREAM_ATTR_MAX_FMI] = { .type = NLA_U32 },
++ [TSN_QCI_STREAM_ATTR_SLM] = { .type = NLA_U32 },
++};
++
++static const struct nla_policy ct_policy[TSN_CT_ATTR_MAX + 1] = {
++ [TSN_CT_ATTR_QUEUE_STATE] = { .type = NLA_U8 }
++};
++
++static const struct nla_policy cbgen_policy[TSN_CBGEN_ATTR_MAX + 1] = {
++ [TSN_CBGEN_ATTR_INDEX] = { .type = NLA_U32 },
++ [TSN_CBGEN_ATTR_PORT_MASK] = { .type = NLA_U8 },
++ [TSN_CBGEN_ATTR_SPLIT_MASK] = { .type = NLA_U8 },
++ [TSN_CBGEN_ATTR_SEQ_LEN] = { .type = NLA_U8 },
++ [TSN_CBGEN_ATTR_SEQ_NUM] = { .type = NLA_U32 },
++};
++
++static const struct nla_policy cbrec_policy[TSN_CBREC_ATTR_MAX + 1] = {
++ [TSN_CBREC_ATTR_INDEX] = { .type = NLA_U32 },
++ [TSN_CBREC_ATTR_SEQ_LEN] = { .type = NLA_U8 },
++ [TSN_CBREC_ATTR_HIS_LEN] = { .type = NLA_U8 },
++ [TSN_CBREC_ATTR_TAG_POP_EN] = { .type = NLA_FLAG },
++};
++
++static const struct nla_policy cbstat_policy[TSN_CBSTAT_ATTR_MAX + 1] = {
++ [TSN_CBSTAT_ATTR_INDEX] = { .type = NLA_U32 },
++ [TSN_CBSTAT_ATTR_GEN_REC] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_ERR] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_SEQ_NUM] = { .type = NLA_U32 },
++ [TSN_CBSTAT_ATTR_SEQ_LEN] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_SPLIT_MASK] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_PORT_MASK] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_HIS_LEN] = { .type = NLA_U8 },
++ [TSN_CBSTAT_ATTR_SEQ_HIS] = { .type = NLA_U32 },
++};
++
++static const struct nla_policy qbu_policy[TSN_QBU_ATTR_MAX + 1] = {
++ [TSN_QBU_ATTR_ADMIN_STATE] = { .type = NLA_U8 },
++ [TSN_QBU_ATTR_HOLD_ADVANCE] = { .type = NLA_U32},
++ [TSN_QBU_ATTR_RELEASE_ADVANCE] = { .type = NLA_U32},
++ [TSN_QBU_ATTR_ACTIVE] = { .type = NLA_FLAG},
++ [TSN_QBU_ATTR_HOLD_REQUEST] = { .type = NLA_U8},
++};
++
++static const struct nla_policy cbs_policy[TSN_CBS_ATTR_MAX + 1] = {
++ [TSN_CBS_ATTR_TC_INDEX] = { .type = NLA_U8},
++ [TSN_CBS_ATTR_BW] = { .type = NLA_U8},
++};
++
++static const struct nla_policy tsd_policy[TSN_TSD_ATTR_MAX + 1] = {
++ [TSN_TSD_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_TSD_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_TSD_ATTR_PERIOD] = { .type = NLA_U32},
++ [TSN_TSD_ATTR_MAX_FRM_NUM] = { .type = NLA_U32},
++ [TSN_TSD_ATTR_CYCLE_NUM] = { .type = NLA_U32},
++ [TSN_TSD_ATTR_LOSS_STEPS] = { .type = NLA_U32},
++ [TSN_TSD_ATTR_SYN_IMME] = { .type = NLA_FLAG},
++};
++
++static const struct nla_policy qbv_policy[TSN_QBV_ATTR_MAX + 1] = {
++ [TSN_QBV_ATTR_ADMINENTRY] = { .type = NLA_NESTED},
++ [TSN_QBV_ATTR_OPERENTRY] = { .type = NLA_NESTED},
++ [TSN_QBV_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_QBV_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_QBV_ATTR_CONFIGCHANGE] = { .type = NLA_FLAG},
++ [TSN_QBV_ATTR_CONFIGCHANGETIME] = { .type = NLA_U64},
++ [TSN_QBV_ATTR_MAXSDU] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_GRANULARITY] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_CURRENTTIME] = { .type = NLA_U64},
++ [TSN_QBV_ATTR_CONFIGPENDING] = {.type = NLA_FLAG},
++ [TSN_QBV_ATTR_CONFIGCHANGEERROR] = { .type = NLA_U64},
++ [TSN_QBV_ATTR_LISTMAX] = { .type = NLA_U32},
++};
++
++static const struct nla_policy qbv_ctrl_policy[TSN_QBV_ATTR_CTRL_MAX + 1] = {
++ [TSN_QBV_ATTR_CTRL_LISTCOUNT] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_CTRL_GATESTATE] = { .type = NLA_U8},
++ [TSN_QBV_ATTR_CTRL_CYCLETIME] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_CTRL_CYCLETIMEEXT] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_CTRL_BASETIME] = { .type = NLA_U64},
++ [TSN_QBV_ATTR_CTRL_LISTENTRY] = { .type = NLA_NESTED},
++};
++
++static const struct nla_policy qbv_entry_policy[TSN_QBV_ATTR_ENTRY_MAX + 1] = {
++ [TSN_QBV_ATTR_ENTRY_ID] = { .type = NLA_U32},
++ [TSN_QBV_ATTR_ENTRY_GC] = { .type = NLA_U8},
++ [TSN_QBV_ATTR_ENTRY_TM] = { .type = NLA_U32},
++};
++
++static const struct nla_policy cb_streamid_policy[TSN_STREAMID_ATTR_MAX + 1] = {
++ [TSN_STREAMID_ATTR_INDEX] = { .type = NLA_U32},
++ [TSN_STREAMID_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_STREAMID_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_STREAMID_ATTR_STREAM_HANDLE] = { .type = NLA_S32},
++ [TSN_STREAMID_ATTR_IFOP] = { .type = NLA_U32},
++ [TSN_STREAMID_ATTR_OFOP] = { .type = NLA_U32},
++ [TSN_STREAMID_ATTR_IFIP] = { .type = NLA_U32},
++ [TSN_STREAMID_ATTR_OFIP] = { .type = NLA_U32},
++ [TSN_STREAMID_ATTR_TYPE] = { .type = NLA_U8},
++ [TSN_STREAMID_ATTR_NDMAC] = { .type = NLA_U64},
++ [TSN_STREAMID_ATTR_NTAGGED] = { .type = NLA_U8},
++ [TSN_STREAMID_ATTR_NVID] = { .type = NLA_U16},
++ [TSN_STREAMID_ATTR_SMAC] = { .type = NLA_U64},
++ [TSN_STREAMID_ATTR_STAGGED] = { .type = NLA_U8},
++ [TSN_STREAMID_ATTR_SVID] = { .type = NLA_U16},
++ [TSN_STREAMID_ATTR_COUNTERS_PSI] = { .type = NLA_U64},
++ [TSN_STREAMID_ATTR_COUNTERS_PSO] = { .type = NLA_U64},
++ [TSN_STREAMID_ATTR_COUNTERS_PSPPI] = { .type = NLA_U64},
++ [TSN_STREAMID_ATTR_COUNTERS_PSPPO] = { .type = NLA_U64},
++};
++
++static const struct nla_policy qci_sfi_policy[TSN_QCI_SFI_ATTR_MAX + 1] = {
++ [TSN_QCI_SFI_ATTR_INDEX] = { .type = NLA_U32},
++ [TSN_QCI_SFI_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_SFI_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_SFI_ATTR_STREAM_HANDLE] = { .type = NLA_S32},
++ [TSN_QCI_SFI_ATTR_PRIO_SPEC] = { .type = NLA_S8},
++ [TSN_QCI_SFI_ATTR_GATE_ID] = { .type = NLA_U32},
++ [TSN_QCI_SFI_ATTR_FILTER_TYPE] = { .type = NLA_U8},
++ [TSN_QCI_SFI_ATTR_FLOW_ID] = { .type = NLA_S32},
++ [TSN_QCI_SFI_ATTR_MAXSDU] = { .type = NLA_U16},
++ [TSN_QCI_SFI_ATTR_COUNTERS] = {
++ .len = sizeof(struct tsn_qci_psfp_sfi_counters)},
++ [TSN_QCI_SFI_ATTR_OVERSIZE_ENABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_SFI_ATTR_OVERSIZE] = { .type = NLA_FLAG},
++};
++
++static const struct nla_policy qci_sgi_policy[] = {
++ [TSN_QCI_SGI_ATTR_INDEX] = { .type = NLA_U32},
++ [TSN_QCI_SGI_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_CONFCHANGE] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_IRXEN] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_IRX] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_OEXEN] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_OEX] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_ADMINENTRY] = { .type = NLA_NESTED},
++ [TSN_QCI_SGI_ATTR_OPERENTRY] = { .type = NLA_NESTED},
++ [TSN_QCI_SGI_ATTR_CCTIME] = { .type = NLA_U64},
++ [TSN_QCI_SGI_ATTR_TICKG] = { .type = NLA_U32},
++ [TSN_QCI_SGI_ATTR_CUTIME] = { .type = NLA_U64},
++ [TSN_QCI_SGI_ATTR_CPENDING] = { .type = NLA_FLAG},
++ [TSN_QCI_SGI_ATTR_CCERROR] = { .type = NLA_U64},
++};
++
++static const struct nla_policy qci_sgi_ctrl_policy[] = {
++ [TSN_SGI_ATTR_CTRL_INITSTATE] = { .type = NLA_FLAG},
++ [TSN_SGI_ATTR_CTRL_LEN] = { .type = NLA_U8},
++ [TSN_SGI_ATTR_CTRL_CYTIME] = { .type = NLA_U32},
++ [TSN_SGI_ATTR_CTRL_CYTIMEEX] = { .type = NLA_U32},
++ [TSN_SGI_ATTR_CTRL_BTIME] = { .type = NLA_U64},
++ [TSN_SGI_ATTR_CTRL_INITIPV] = { .type = NLA_S8},
++ [TSN_SGI_ATTR_CTRL_GCLENTRY] = { .type = NLA_NESTED},
++};
++
++static const struct nla_policy qci_sgi_gcl_policy[] = {
++ [TSN_SGI_ATTR_GCL_GATESTATE] = { .type = NLA_FLAG},
++ [TSN_SGI_ATTR_GCL_IPV] = { .type = NLA_S8},
++ [TSN_SGI_ATTR_GCL_INTERVAL] = { .type = NLA_U32},
++ [TSN_SGI_ATTR_GCL_OCTMAX] = { .type = NLA_U32},
++};
++
++static const struct nla_policy qci_fmi_policy[] = {
++ [TSN_QCI_FMI_ATTR_INDEX] = { .type = NLA_U32},
++ [TSN_QCI_FMI_ATTR_ENABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_CIR] = { .type = NLA_U32},
++ [TSN_QCI_FMI_ATTR_CBS] = { .type = NLA_U32},
++ [TSN_QCI_FMI_ATTR_EIR] = { .type = NLA_U32},
++ [TSN_QCI_FMI_ATTR_EBS] = { .type = NLA_U32},
++ [TSN_QCI_FMI_ATTR_CF] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_CM] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_DROPYL] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_MAREDEN] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_MARED] = { .type = NLA_FLAG},
++ [TSN_QCI_FMI_ATTR_COUNTERS] = {
++ .len = sizeof(struct tsn_qci_psfp_fmi_counters)},
++};
++
++static const struct nla_policy dscp_policy[] = {
++ [TSN_DSCP_ATTR_INDEX] = { .type = NLA_U32},
++ [TSN_DSCP_ATTR_DISABLE] = { .type = NLA_FLAG},
++ [TSN_DSCP_ATTR_COS] = { .type = NLA_U8},
++ [TSN_DSCP_ATTR_DPL] = { .type = NLA_U8},
++};
++
++static ATOMIC_NOTIFIER_HEAD(tsn_notif_chain);
++
++/**
++ * register_tsn_notifier - Register notifier
++ * @nb: notifier_block
++ *
++ * Register switch device notifier.
++ */
++int register_tsn_notifier(struct notifier_block *nb)
++{
++ return atomic_notifier_chain_register(&tsn_notif_chain, nb);
++}
++EXPORT_SYMBOL_GPL(register_tsn_notifier);
++
++/**
++ * unregister_tsn_notifier - Unregister notifier
++ * @nb: notifier_block
++ *
++ * Unregister switch device notifier.
++ */
++int unregister_tsn_notifier(struct notifier_block *nb)
++{
++ return atomic_notifier_chain_unregister(&tsn_notif_chain, nb);
++}
++EXPORT_SYMBOL_GPL(unregister_tsn_notifier);
++
++/**
++ * call_tsn_notifiers - Call notifiers
++ * @val: value passed unmodified to notifier function
++ * @dev: port device
++ * @info: notifier information data
++ *
++ * Call all network notifier blocks.
++ */
++int call_tsn_notifiers(unsigned long val, struct net_device *dev,
++ struct tsn_notifier_info *info)
++{
++ info->dev = dev;
++ return atomic_notifier_call_chain(&tsn_notif_chain, val, info);
++}
++EXPORT_SYMBOL_GPL(call_tsn_notifiers);
++
++struct tsn_port *tsn_get_port(struct net_device *ndev)
++{
++ struct tsn_port *port;
++ bool tsn_found = false;
++
++ list_for_each_entry(port, &port_list, list) {
++ if (port->netdev == ndev) {
++ tsn_found = true;
++ break;
++ }
++ }
++
++ if (!tsn_found)
++ return NULL;
++
++ return port;
++}
++EXPORT_SYMBOL_GPL(tsn_get_port);
++
++static int tsn_prepare_reply(struct genl_info *info, u8 cmd,
++ struct sk_buff **skbp, size_t size)
++{
++ struct sk_buff *skb;
++ void *reply;
++
++ /* If new attributes are added, please revisit this allocation
++ */
++ skb = genlmsg_new(size, GFP_KERNEL);
++ if (!skb)
++ return -ENOMEM;
++
++ if (!info) {
++ nlmsg_free(skb);
++ return -EINVAL;
++ }
++
++ reply = genlmsg_put_reply(skb, info, &tsn_family, 0, cmd);
++ if (!reply) {
++ nlmsg_free(skb);
++ return -EINVAL;
++ }
++
++ *skbp = skb;
++ return 0;
++}
++
++static int tsn_mk_reply(struct sk_buff *skb, int aggr, void *data, int len)
++{
++ /* add a netlink attribute to a socket buffer */
++ return nla_put(skb, aggr, len, data);
++}
++
++static int tsn_send_reply(struct sk_buff *skb, struct genl_info *info)
++{
++ struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
++ void *reply = genlmsg_data(genlhdr);
++
++ genlmsg_end(skb, reply);
++
++ return genlmsg_reply(skb, info);
++}
++
++static int cmd_attr_echo_message(struct genl_info *info)
++{
++ struct nlattr *na;
++ char *msg;
++ struct sk_buff *rep_skb;
++ size_t size;
++ int ret;
++
++ na = info->attrs[TSN_CMD_ATTR_MESG];
++ if (!na)
++ return -EINVAL;
++
++ msg = (char *)nla_data(na);
++ pr_info("tsn generic netlink receive echo mesg %s\n", msg);
++
++ size = nla_total_size(strlen(msg) + 1);
++
++ ret = tsn_prepare_reply(info, TSN_CMD_REPLY, &rep_skb,
++ size + NLMSG_ALIGN(MAX_USER_SIZE));
++ if (ret < 0)
++ return ret;
++
++ ret = tsn_mk_reply(rep_skb, TSN_CMD_ATTR_MESG, msg, size);
++ if (ret < 0)
++ goto err;
++
++ return tsn_send_reply(rep_skb, info);
++
++err:
++ nlmsg_free(rep_skb);
++ return ret;
++}
++
++static int cmd_attr_echo_data(struct genl_info *info)
++{
++ struct nlattr *na;
++ s32 data;
++ struct sk_buff *rep_skb;
++ size_t size;
++ int ret;
++
++ /*read data */
++ na = info->attrs[TSN_CMD_ATTR_DATA];
++ if (!na)
++ return -EINVAL;
++
++ data = nla_get_s32(info->attrs[TSN_CMD_ATTR_DATA]);
++ pr_info("tsn generic netlink receive echo data %d\n", data);
++
++ /* send back */
++ size = nla_total_size(sizeof(s32));
++
++ ret = tsn_prepare_reply(info, TSN_CMD_REPLY, &rep_skb,
++ size + NLMSG_ALIGN(MAX_USER_SIZE));
++ if (ret < 0)
++ return ret;
++
++ /* netlink lib func */
++ ret = nla_put_s32(rep_skb, TSN_CMD_ATTR_DATA, data);
++ if (ret < 0)
++ goto err;
++
++ return tsn_send_reply(rep_skb, info);
++
++err:
++ nlmsg_free(rep_skb);
++ return ret;
++}
++
++static int tsn_echo_cmd(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_CMD_ATTR_MESG])
++ return cmd_attr_echo_message(info);
++ else if (info->attrs[TSN_CMD_ATTR_DATA])
++ return cmd_attr_echo_data(info);
++
++ return -EINVAL;
++}
++
++static int tsn_simple_reply(struct genl_info *info, u32 cmd,
++ char *portname, s32 retvalue)
++{
++ struct sk_buff *rep_skb;
++ size_t size;
++ int ret;
++
++ /* send back */
++ size = nla_total_size(strlen(portname) + 1);
++ size += nla_total_size(sizeof(s32));
++
++ ret = tsn_prepare_reply(info, cmd,
++ &rep_skb, size + NLMSG_ALIGN(MAX_USER_SIZE));
++ if (ret < 0)
++ return ret;
++
++ /* netlink lib func */
++ ret = nla_put_string(rep_skb, TSN_ATTR_IFNAME, portname);
++ if (ret < 0)
++ return ret;
++
++ ret = nla_put_s32(rep_skb, TSN_CMD_ATTR_DATA, retvalue);
++ if (ret < 0)
++ return ret;
++
++ return tsn_send_reply(rep_skb, info);
++}
++
++struct tsn_port *tsn_init_check(struct genl_info *info,
++ struct net_device **ndev)
++{
++ struct nlattr *na;
++ char *portname;
++ struct net_device *netdev;
++ struct tsn_port *port;
++ bool tsn_found = false;
++
++ if (!info->attrs[TSN_ATTR_IFNAME]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ "no portname", -EINVAL);
++ return NULL;
++ }
++
++ na = info->attrs[TSN_ATTR_IFNAME];
++
++ portname = (char *)nla_data(na);
++
++ netdev = __dev_get_by_name(genl_info_net(info), portname);
++ if (!netdev) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ "error device", -ENODEV);
++ return NULL;
++ }
++
++ list_for_each_entry(port, &port_list, list) {
++ if (port->netdev == netdev) {
++ tsn_found = true;
++ break;
++ }
++ }
++
++ if (!tsn_found) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -ENODEV);
++ return NULL;
++ }
++
++ *ndev = netdev;
++
++ return port;
++}
++
++static int tsn_cap_get(struct sk_buff *skb, struct genl_info *info)
++{
++ struct sk_buff *rep_skb;
++ struct nlattr *tsn_cap_attr;
++ int ret;
++ u32 cap = 0;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port) {
++ ret = -ENODEV;
++ goto out;
++ }
++
++ tsnops = port->tsnops;
++ genlhdr = info->genlhdr;
++ if (!tsnops->get_capability) {
++ ret = -EOPNOTSUPP;
++ goto out;
++ }
++
++ cap = tsnops->get_capability(netdev);
++ if (cap < 0) {
++ ret = cap;
++ goto out;
++ }
++
++ /* Pad netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ goto out;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name)) {
++ ret = -EMSGSIZE;
++ goto err;
++ }
++
++ tsn_cap_attr = nla_nest_start_noflag(rep_skb, TSN_ATTR_CAP);
++ if (!tsn_cap_attr) {
++ ret = -EMSGSIZE;
++ goto err;
++ }
++
++ if (cap & TSN_CAP_QBV) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_QBV))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_QCI) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_QCI))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_QBU) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_QBU))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_CBS) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_CBS))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_CB) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_CB))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_TBS) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_TBS))
++ goto err;
++ }
++
++ if (cap & TSN_CAP_CTH) {
++ if (nla_put_flag(rep_skb, TSN_CAP_ATTR_CTH))
++ goto err;
++ }
++
++ nla_nest_end(rep_skb, tsn_cap_attr);
++
++ tsn_send_reply(rep_skb, info);
++ return 0;
++err:
++ nlmsg_free(rep_skb);
++out:
++ if (ret < 0)
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++}
++
++static int cmd_cb_streamid_set(struct genl_info *info)
++{
++ struct nlattr *na, *sid[TSN_STREAMID_ATTR_MAX + 1];
++ u32 sid_index;
++ u8 iden_type = 1;
++ bool enable;
++ int ret;
++ struct net_device *netdev;
++ struct tsn_cb_streamid sidconf;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&sidconf, 0, sizeof(struct tsn_cb_streamid));
++
++ if (!info->attrs[TSN_ATTR_STREAM_IDENTIFY])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_STREAM_IDENTIFY];
++
++ ret = NLA_PARSE_NESTED(sid, TSN_STREAMID_ATTR_MAX,
++ na, cb_streamid_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sid[TSN_STREAMID_ATTR_INDEX])
++ return -EINVAL;
++
++ sid_index = nla_get_u32(sid[TSN_STREAMID_ATTR_INDEX]);
++
++ if (sid[TSN_STREAMID_ATTR_ENABLE])
++ enable = true;
++ else if (sid[TSN_STREAMID_ATTR_DISABLE])
++ enable = false;
++ else
++ return -EINVAL;
++
++ if (!enable)
++ goto loaddev;
++
++ if (sid[TSN_STREAMID_ATTR_TYPE])
++ iden_type = nla_get_u8(sid[TSN_STREAMID_ATTR_TYPE]);
++ else
++ return -EINVAL;
++
++ sidconf.type = iden_type;
++ switch (iden_type) {
++ case STREAMID_NULL:
++ if (!sid[TSN_STREAMID_ATTR_NDMAC] ||
++ !sid[TSN_STREAMID_ATTR_NTAGGED] ||
++ !sid[TSN_STREAMID_ATTR_NVID]) {
++ return -EINVAL;
++ }
++
++ sidconf.para.nid.dmac =
++ nla_get_u64(sid[TSN_STREAMID_ATTR_NDMAC]);
++ sidconf.para.nid.tagged =
++ nla_get_u8(sid[TSN_STREAMID_ATTR_NTAGGED]);
++ sidconf.para.nid.vid =
++ nla_get_u16(sid[TSN_STREAMID_ATTR_NVID]);
++ break;
++ case STREAMID_SMAC_VLAN:
++ /* TODO: not supportted yet */
++ if (!sid[TSN_STREAMID_ATTR_SMAC] ||
++ !sid[TSN_STREAMID_ATTR_STAGGED] ||
++ !sid[TSN_STREAMID_ATTR_SVID]) {
++ return -EINVAL;
++ }
++
++ sidconf.para.sid.smac =
++ nla_get_u64(sid[TSN_STREAMID_ATTR_SMAC]);
++ sidconf.para.sid.tagged =
++ nla_get_u8(sid[TSN_STREAMID_ATTR_STAGGED]);
++ sidconf.para.sid.vid =
++ nla_get_u16(sid[TSN_STREAMID_ATTR_SVID]);
++ break;
++ case STREAMID_DMAC_VLAN:
++
++ case STREAMID_IP:
++
++ default:
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (sid[TSN_STREAMID_ATTR_STREAM_HANDLE])
++ sidconf.handle =
++ nla_get_s32(sid[TSN_STREAMID_ATTR_STREAM_HANDLE]);
++
++ if (sid[TSN_STREAMID_ATTR_IFOP])
++ sidconf.ifac_oport = nla_get_u32(sid[TSN_STREAMID_ATTR_IFOP]);
++ if (sid[TSN_STREAMID_ATTR_OFOP])
++ sidconf.ofac_oport = nla_get_u32(sid[TSN_STREAMID_ATTR_OFOP]);
++ if (sid[TSN_STREAMID_ATTR_IFIP])
++ sidconf.ifac_iport = nla_get_u32(sid[TSN_STREAMID_ATTR_IFIP]);
++ if (sid[TSN_STREAMID_ATTR_OFIP])
++ sidconf.ofac_iport = nla_get_u32(sid[TSN_STREAMID_ATTR_OFIP]);
++
++loaddev:
++ if (!tsnops->cb_streamid_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EOPNOTSUPP;
++ }
++
++ ret = tsnops->cb_streamid_set(netdev, sid_index, enable, &sidconf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++ }
++
++ /* simple reply here. To be continue */
++ if (tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, 0))
++ return -1;
++
++ return 0;
++}
++
++static int tsn_cb_streamid_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_cb_streamid_set(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_cb_streamid_get(struct genl_info *info)
++{
++ struct nlattr *na, *sidattr, *sid[TSN_STREAMID_ATTR_MAX + 1];
++ u32 sid_index;
++ struct genlmsghdr *genlhdr;
++ struct sk_buff *rep_skb;
++ int ret, i;
++ int valid;
++ struct net_device *netdev;
++ struct tsn_cb_streamid sidconf;
++ struct tsn_cb_streamid_counters sidcounts;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&sidconf, 0, sizeof(struct tsn_cb_streamid));
++ memset(&sidcounts, 0, sizeof(struct tsn_cb_streamid_counters));
++
++ if (!info->attrs[TSN_ATTR_STREAM_IDENTIFY])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_STREAM_IDENTIFY];
++
++ ret = NLA_PARSE_NESTED(sid, TSN_STREAMID_ATTR_MAX,
++ na, cb_streamid_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sid[TSN_STREAMID_ATTR_INDEX])
++ return -EINVAL;
++
++ sid_index = nla_get_u32(sid[TSN_STREAMID_ATTR_INDEX]);
++
++ if (!tsnops->cb_streamid_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ ret = -EINVAL;
++ goto exit;
++ } else {
++ valid = tsnops->cb_streamid_get(netdev, sid_index, &sidconf);
++ if (valid < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, valid);
++ return valid;
++ }
++ }
++
++ /* send back */
++ genlhdr = info->genlhdr;
++ ret = tsn_prepare_reply(info, genlhdr->cmd, &rep_skb,
++ NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ /* input netlink the parameters */
++ sidattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_STREAM_IDENTIFY);
++ if (!sidattr) {
++ ret = -EINVAL;
++ goto err;
++ }
++
++ if (nla_put_u32(rep_skb, TSN_STREAMID_ATTR_INDEX, sid_index))
++ return -EMSGSIZE;
++
++ if (valid == 1) {
++ nla_put_flag(rep_skb, TSN_STREAMID_ATTR_ENABLE);
++ } else if (valid == 0) {
++ nla_put_flag(rep_skb, TSN_STREAMID_ATTR_DISABLE);
++ } else {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ goto err;
++ }
++
++ if (nla_put_s32(rep_skb,
++ TSN_STREAMID_ATTR_STREAM_HANDLE, sidconf.handle) ||
++ nla_put_u32(rep_skb, TSN_STREAMID_ATTR_IFOP, sidconf.ifac_oport) ||
++ nla_put_u32(rep_skb, TSN_STREAMID_ATTR_OFOP, sidconf.ofac_oport) ||
++ nla_put_u32(rep_skb, TSN_STREAMID_ATTR_IFIP, sidconf.ifac_iport) ||
++ nla_put_u32(rep_skb, TSN_STREAMID_ATTR_OFIP, sidconf.ofac_iport) ||
++ nla_put_u8(rep_skb, TSN_STREAMID_ATTR_TYPE, sidconf.type))
++ return -EMSGSIZE;
++
++ switch (sidconf.type) {
++ case STREAMID_NULL:
++ if (NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_NDMAC,
++ sidconf.para.nid.dmac) ||
++ nla_put_u16(rep_skb, TSN_STREAMID_ATTR_NVID,
++ sidconf.para.nid.vid) ||
++ nla_put_u8(rep_skb, TSN_STREAMID_ATTR_NTAGGED,
++ sidconf.para.nid.tagged))
++ return -EMSGSIZE;
++ break;
++ case STREAMID_SMAC_VLAN:
++ if (NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_SMAC,
++ sidconf.para.sid.smac) ||
++ nla_put_u16(rep_skb, TSN_STREAMID_ATTR_SVID,
++ sidconf.para.sid.vid) ||
++ nla_put_u8(rep_skb, TSN_STREAMID_ATTR_STAGGED,
++ sidconf.para.sid.tagged))
++ return -EMSGSIZE;
++ break;
++ case STREAMID_DMAC_VLAN:
++ case STREAMID_IP:
++ default:
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ goto err;
++ }
++
++ if (!tsnops->cb_streamid_counters_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ goto err;
++ } else {
++ ret = tsnops->cb_streamid_counters_get(netdev,
++ sid_index,
++ &sidcounts);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ goto err;
++ }
++ }
++
++ if (NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_COUNTERS_PSI,
++ sidcounts.per_stream.input) ||
++ NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_COUNTERS_PSO,
++ sidcounts.per_stream.output))
++ return -EMSGSIZE;
++
++ for (i = 0; i < 32; i++) {
++ if (NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_COUNTERS_PSPPI,
++ sidcounts.per_streamport[i].input) ||
++ NLA_PUT_U64(rep_skb, TSN_STREAMID_ATTR_COUNTERS_PSPPO,
++ sidcounts.per_streamport[i].output))
++ return -EMSGSIZE;
++ }
++
++ nla_nest_end(rep_skb, sidattr);
++ /* end netlink input the parameters */
++
++ /* netlink lib func */
++ ret = nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name);
++ if (ret < 0)
++ goto err;
++
++ ret = nla_put_s32(rep_skb, TSN_CMD_ATTR_DATA, 0);
++ if (ret < 0)
++ goto err;
++
++ return tsn_send_reply(rep_skb, info);
++
++err:
++ nlmsg_free(rep_skb);
++exit:
++ return ret;
++}
++
++static int tsn_cb_streamid_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_cb_streamid_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmb_cb_streamid_counters_get(struct genl_info *info)
++{
++ return 0;
++}
++
++static int tsn_cb_streamid_counters_get(struct sk_buff *skb,
++ struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmb_cb_streamid_counters_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int tsn_qci_cap_get(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *qci_cap;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qci_psfp_stream_param qci_cap_status;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ tsnops = port->tsnops;
++
++ genlhdr = info->genlhdr;
++
++ memset(&qci_cap_status, 0, sizeof(qci_cap_status));
++
++ if (!tsnops->qci_get_maxcap) {
++ ret = -EOPNOTSUPP;
++ goto out;
++ }
++
++ ret = tsnops->qci_get_maxcap(netdev, &qci_cap_status);
++ if (ret < 0)
++ goto out;
++
++ /* Pad netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ goto out;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name)) {
++ ret = -EMSGSIZE;
++ goto err;
++ }
++
++ qci_cap = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_SP);
++ if (!qci_cap) {
++ ret = -EMSGSIZE;
++ goto err;
++ }
++
++ if (nla_put_u32(rep_skb, TSN_QCI_STREAM_ATTR_MAX_SFI,
++ qci_cap_status.max_sf_instance) ||
++ nla_put_u32(rep_skb, TSN_QCI_STREAM_ATTR_MAX_SGI,
++ qci_cap_status.max_sg_instance) ||
++ nla_put_u32(rep_skb, TSN_QCI_STREAM_ATTR_MAX_FMI,
++ qci_cap_status.max_fm_instance) ||
++ nla_put_u32(rep_skb, TSN_QCI_STREAM_ATTR_SLM,
++ qci_cap_status.supported_list_max)) {
++ ret = -EMSGSIZE;
++ goto err;
++ }
++
++ nla_nest_end(rep_skb, qci_cap);
++
++ tsn_send_reply(rep_skb, info);
++
++ return 0;
++err:
++ nlmsg_free(rep_skb);
++out:
++ if (ret < 0)
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++
++ return ret;
++}
++
++static int cmd_qci_sfi_set(struct genl_info *info)
++{
++ struct nlattr *na, *sfi[TSN_QCI_SFI_ATTR_MAX + 1];
++ u32 sfi_handle;
++ bool enable;
++ int ret;
++ struct net_device *netdev;
++ struct tsn_qci_psfp_sfi_conf sficonf;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&sficonf, 0, sizeof(struct tsn_qci_psfp_sfi_conf));
++
++ if (!info->attrs[TSN_ATTR_QCI_SFI])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QCI_SFI];
++
++ ret = NLA_PARSE_NESTED(sfi, TSN_QCI_SFI_ATTR_MAX, na, qci_sfi_policy);
++ if (ret) {
++ pr_info("tsn: parse value TSN_QCI_SFI_ATTR_MAX error.");
++ return -EINVAL;
++ }
++
++ if (!sfi[TSN_QCI_SFI_ATTR_INDEX])
++ return -EINVAL;
++
++ sfi_handle = nla_get_u32(sfi[TSN_QCI_SFI_ATTR_INDEX]);
++
++ if (sfi[TSN_QCI_SFI_ATTR_ENABLE]) {
++ enable = true;
++ } else if (sfi[TSN_QCI_SFI_ATTR_DISABLE]) {
++ enable = false;
++ goto loaddrive;
++ } else {
++ pr_err("tsn: must provde ENABLE or DISABLE attribute.\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!sfi[TSN_QCI_SFI_ATTR_GATE_ID]) {
++ pr_err("tsn: must provide stream gate index\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!sfi[TSN_QCI_SFI_ATTR_STREAM_HANDLE])
++ sficonf.stream_handle_spec = -1;
++ else
++ sficonf.stream_handle_spec =
++ nla_get_s32(sfi[TSN_QCI_SFI_ATTR_STREAM_HANDLE]);
++
++ if (!sfi[TSN_QCI_SFI_ATTR_PRIO_SPEC])
++ sficonf.priority_spec = -1;
++ else
++ sficonf.priority_spec =
++ nla_get_s8(sfi[TSN_QCI_SFI_ATTR_PRIO_SPEC]);
++
++ sficonf.stream_gate_instance_id =
++ nla_get_u32(sfi[TSN_QCI_SFI_ATTR_GATE_ID]);
++
++ if (sfi[TSN_QCI_SFI_ATTR_MAXSDU])
++ sficonf.stream_filter.maximum_sdu_size =
++ nla_get_u16(sfi[TSN_QCI_SFI_ATTR_MAXSDU]);
++ else
++ sficonf.stream_filter.maximum_sdu_size = 0;
++
++ if (sfi[TSN_QCI_SFI_ATTR_FLOW_ID])
++ sficonf.stream_filter.flow_meter_instance_id =
++ nla_get_s32(sfi[TSN_QCI_SFI_ATTR_FLOW_ID]);
++ else
++ sficonf.stream_filter.flow_meter_instance_id = -1;
++
++ if (sfi[TSN_QCI_SFI_ATTR_OVERSIZE_ENABLE])
++ sficonf.block_oversize_enable = true;
++
++ if (sfi[TSN_QCI_SFI_ATTR_OVERSIZE])
++ sficonf.block_oversize = true;
++
++loaddrive:
++ if (!tsnops->qci_sfi_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EINVAL;
++ }
++
++ ret = tsnops->qci_sfi_set(netdev, sfi_handle, enable, &sficonf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++ }
++
++ ret = tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, 0);
++
++ if (ret)
++ return ret;
++ return 0;
++}
++
++static int tsn_qci_sfi_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sfi_set(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_sfi_get(struct genl_info *info)
++{
++ struct nlattr *na, *sfiattr;
++ struct nlattr *sfi[TSN_QCI_SFI_ATTR_MAX + 1];
++ u32 sfi_handle;
++ struct sk_buff *rep_skb;
++ int ret, valid = 0;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qci_psfp_sfi_conf sficonf;
++ struct tsn_qci_psfp_sfi_counters sficount;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ genlhdr = info->genlhdr;
++
++ if (!info->attrs[TSN_ATTR_QCI_SFI])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QCI_SFI];
++
++ ret = NLA_PARSE_NESTED(sfi, TSN_QCI_SFI_ATTR_MAX,
++ na, qci_sfi_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sfi[TSN_QCI_SFI_ATTR_INDEX])
++ return -EINVAL;
++
++ sfi_handle = nla_get_u32(sfi[TSN_QCI_SFI_ATTR_INDEX]);
++
++ memset(&sficonf, 0, sizeof(struct tsn_qci_psfp_sfi_conf));
++ memset(&sficount, 0, sizeof(struct tsn_qci_psfp_sfi_counters));
++
++ if (!tsnops->qci_sfi_get || !tsnops->qci_sfi_counters_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ ret = -EINVAL;
++ goto exit;
++ } else {
++ valid = tsnops->qci_sfi_get(netdev, sfi_handle, &sficonf);
++ if (valid < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, valid);
++ return valid;
++ }
++
++ valid = tsnops->qci_sfi_counters_get(netdev, sfi_handle,
++ &sficount);
++ if (valid < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, valid);
++ return valid;
++ }
++ }
++
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ goto err;
++
++ sfiattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_SFI);
++ if (!sfiattr) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ ret = -EINVAL;
++ goto err;
++ }
++
++ if (nla_put_u32(rep_skb, TSN_QCI_SFI_ATTR_INDEX, sfi_handle))
++ return -EMSGSIZE;
++
++ if (valid) {
++ if (nla_put_flag(rep_skb, TSN_QCI_SFI_ATTR_ENABLE))
++ return -EMSGSIZE;
++ } else {
++ if (nla_put_flag(rep_skb, TSN_QCI_SFI_ATTR_DISABLE))
++ return -EMSGSIZE;
++ }
++
++ if (nla_put_s32(rep_skb, TSN_QCI_SFI_ATTR_STREAM_HANDLE,
++ sficonf.stream_handle_spec) ||
++ nla_put_s8(rep_skb, TSN_QCI_SFI_ATTR_PRIO_SPEC,
++ sficonf.priority_spec) ||
++ nla_put_u32(rep_skb, TSN_QCI_SFI_ATTR_GATE_ID,
++ sficonf.stream_gate_instance_id))
++ return -EMSGSIZE;
++
++ if (sficonf.stream_filter.maximum_sdu_size)
++ if (nla_put_u16(rep_skb, TSN_QCI_SFI_ATTR_MAXSDU,
++ sficonf.stream_filter.maximum_sdu_size))
++ return -EMSGSIZE;
++
++ if (sficonf.stream_filter.flow_meter_instance_id >= 0)
++ if (nla_put_s32(rep_skb, TSN_QCI_SFI_ATTR_FLOW_ID,
++ sficonf.stream_filter.flow_meter_instance_id))
++ return -EMSGSIZE;
++
++ if (sficonf.block_oversize_enable)
++ if (nla_put_flag(rep_skb, TSN_QCI_SFI_ATTR_OVERSIZE_ENABLE))
++ return -EMSGSIZE;
++ if (sficonf.block_oversize)
++ if (nla_put_flag(rep_skb, TSN_QCI_SFI_ATTR_OVERSIZE))
++ return -EMSGSIZE;
++
++ if (nla_put(rep_skb, TSN_QCI_SFI_ATTR_COUNTERS,
++ sizeof(struct tsn_qci_psfp_sfi_counters), &sficount))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, sfiattr);
++
++ return tsn_send_reply(rep_skb, info);
++err:
++ nlmsg_free(rep_skb);
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++exit:
++ return ret;
++}
++
++static int tsn_qci_sfi_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sfi_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_sfi_counters_get(struct genl_info *info)
++{
++ struct nlattr *na, *sfiattr;
++ struct nlattr *sfi[TSN_QCI_SFI_ATTR_MAX + 1];
++ u32 sfi_handle;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qci_psfp_sfi_counters sficount;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ genlhdr = info->genlhdr;
++
++ if (!info->attrs[TSN_ATTR_QCI_SFI])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QCI_SFI];
++
++ ret = NLA_PARSE_NESTED(sfi, TSN_QCI_SFI_ATTR_MAX,
++ na, qci_sfi_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sfi[TSN_QCI_SFI_ATTR_INDEX])
++ return -EINVAL;
++
++ sfi_handle = nla_get_u32(sfi[TSN_QCI_SFI_ATTR_INDEX]);
++
++ memset(&sficount, 0, sizeof(struct tsn_qci_psfp_sfi_counters));
++ if (!tsnops->qci_sfi_counters_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->qci_sfi_counters_get(netdev, sfi_handle, &sficount);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ ret = tsn_prepare_reply(info, genlhdr->cmd, &rep_skb,
++ NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ goto err;
++
++ sfiattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_SFI);
++ if (!sfiattr) {
++ ret = -EINVAL;
++ goto err;
++ }
++
++ if (nla_put_u32(rep_skb, TSN_QCI_SFI_ATTR_INDEX, sfi_handle))
++ return -EMSGSIZE;
++
++ ret = tsnops->qci_sfi_counters_get(netdev, sfi_handle, &sficount);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++ }
++
++ if (nla_put(rep_skb, TSN_QCI_SFI_ATTR_COUNTERS,
++ sizeof(struct tsn_qci_psfp_sfi_counters), &sficount))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, sfiattr);
++
++ return tsn_send_reply(rep_skb, info);
++err:
++ nlmsg_free(rep_skb);
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, -EINVAL);
++ return ret;
++}
++
++static int tsn_qci_sfi_counters_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sfi_counters_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_sgi_set(struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *sgia[TSN_QCI_SGI_ATTR_MAX + 1];
++ struct nlattr *admin[TSN_SGI_ATTR_CTRL_MAX + 1];
++ int ret = 0;
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct tsn_qci_psfp_sgi_conf sgi;
++ struct tsn_qci_psfp_gcl *gcl = NULL;
++ u16 sgi_handle = 0;
++ u16 listcount = 0;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&sgi, 0, sizeof(struct tsn_qci_psfp_sgi_conf));
++
++ if (!info->attrs[TSN_ATTR_QCI_SGI]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_QCI_SGI];
++
++ ret = NLA_PARSE_NESTED(sgia, TSN_QCI_SGI_ATTR_MAX,
++ na, qci_sgi_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (sgia[TSN_QCI_SGI_ATTR_ENABLE] && sgia[TSN_QCI_SGI_ATTR_DISABLE]) {
++ pr_err("tsn: enable or disable?\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -1;
++ }
++
++ if (sgia[TSN_QCI_SGI_ATTR_INDEX])
++ sgi_handle = nla_get_u32(sgia[TSN_QCI_SGI_ATTR_INDEX]);
++
++ if (sgia[TSN_QCI_SGI_ATTR_DISABLE]) {
++ sgi.gate_enabled = 0;
++ goto loaddev;
++ } else {
++ /* set default to be enable*/
++ sgi.gate_enabled = 1;
++ }
++
++ if (sgia[TSN_QCI_SGI_ATTR_CONFCHANGE])
++ sgi.config_change = 1;
++
++ if (sgia[TSN_QCI_SGI_ATTR_IRXEN])
++ sgi.block_invalid_rx_enable = 1;
++
++ if (sgia[TSN_QCI_SGI_ATTR_IRX])
++ sgi.block_invalid_rx = 1;
++
++ if (sgia[TSN_QCI_SGI_ATTR_OEXEN])
++ sgi.block_octets_exceeded_enable = 1;
++
++ if (sgia[TSN_QCI_SGI_ATTR_OEX])
++ sgi.block_octets_exceeded = 1;
++
++ if (sgia[TSN_QCI_SGI_ATTR_ADMINENTRY]) {
++ struct nlattr *entry;
++ int rem;
++ int count = 0;
++
++ na = sgia[TSN_QCI_SGI_ATTR_ADMINENTRY];
++ ret = NLA_PARSE_NESTED(admin, TSN_SGI_ATTR_CTRL_MAX,
++ na, qci_sgi_ctrl_policy);
++
++ /* Other parameters in admin control */
++ if (admin[TSN_SGI_ATTR_CTRL_INITSTATE])
++ sgi.admin.gate_states = 1;
++
++ if (admin[TSN_SGI_ATTR_CTRL_CYTIME])
++ sgi.admin.cycle_time =
++ nla_get_u32(admin[TSN_SGI_ATTR_CTRL_CYTIME]);
++
++ if (admin[TSN_SGI_ATTR_CTRL_CYTIMEEX])
++ sgi.admin.cycle_time_extension =
++ nla_get_u32(admin[TSN_SGI_ATTR_CTRL_CYTIMEEX]);
++
++ if (admin[TSN_SGI_ATTR_CTRL_BTIME])
++ sgi.admin.base_time =
++ nla_get_u64(admin[TSN_SGI_ATTR_CTRL_BTIME]);
++
++ if (admin[TSN_SGI_ATTR_CTRL_INITIPV])
++ sgi.admin.init_ipv =
++ nla_get_s8(admin[TSN_SGI_ATTR_CTRL_INITIPV]);
++ else
++ sgi.admin.init_ipv = -1;
++
++ if (admin[TSN_SGI_ATTR_CTRL_LEN]) {
++ sgi.admin.control_list_length =
++ nla_get_u8(admin[TSN_SGI_ATTR_CTRL_LEN]);
++ listcount = sgi.admin.control_list_length;
++ }
++
++ if (!listcount)
++ goto loaddev;
++
++ gcl = kmalloc_array(listcount, sizeof(*gcl), GFP_KERNEL);
++
++ memset(gcl, 0, listcount * sizeof(struct tsn_qci_psfp_gcl));
++
++ /* Check the whole admin attrs,
++ * checkout the TSN_SGI_ATTR_CTRL_GCLENTRY attributes
++ */
++ nla_for_each_nested(entry, na, rem) {
++ struct nlattr *gcl_entry[TSN_SGI_ATTR_GCL_MAX + 1];
++ struct nlattr *ti, *om;
++
++ if (nla_type(entry) != TSN_SGI_ATTR_CTRL_GCLENTRY)
++ continue;
++
++ /* parse each TSN_SGI_ATTR_CTRL_GCLENTRY */
++ ret = NLA_PARSE_NESTED(gcl_entry, TSN_SGI_ATTR_GCL_MAX,
++ entry, qci_sgi_gcl_policy);
++ /* Parse gate control list */
++ if (gcl_entry[TSN_SGI_ATTR_GCL_GATESTATE])
++ (gcl + count)->gate_state = 1;
++
++ if (gcl_entry[TSN_SGI_ATTR_GCL_IPV])
++ (gcl + count)->ipv =
++ nla_get_s8(gcl_entry[TSN_SGI_ATTR_GCL_IPV]);
++
++ if (gcl_entry[TSN_SGI_ATTR_GCL_INTERVAL]) {
++ ti = gcl_entry[TSN_SGI_ATTR_GCL_INTERVAL];
++ (gcl + count)->time_interval = nla_get_u32(ti);
++ }
++
++ if (gcl_entry[TSN_SGI_ATTR_GCL_OCTMAX]) {
++ om = gcl_entry[TSN_SGI_ATTR_GCL_OCTMAX];
++ (gcl + count)->octet_max = nla_get_u32(om);
++ }
++
++ count++;
++
++ if (count >= listcount)
++ break;
++ }
++
++ if (count < listcount) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ pr_err("tsn: count less than TSN_SGI_ATTR_CTRL_LEN\n");
++ kfree(gcl);
++ return -EINVAL;
++ }
++
++ } else {
++ pr_info("tsn: no admin list parameters setting\n");
++ }
++
++loaddev:
++ if (!tsnops->qci_sgi_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ kfree(gcl);
++ return -EINVAL;
++ }
++
++ sgi.admin.gcl = gcl;
++
++ ret = tsnops->qci_sgi_set(netdev, sgi_handle, &sgi);
++ kfree(gcl);
++ if (!ret)
++ return tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++}
++
++static int tsn_qci_sgi_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sgi_set(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_sgi_get(struct genl_info *info)
++{
++ struct nlattr *na, *sgiattr, *adminattr, *sglattr;
++ struct nlattr *sgi[TSN_QCI_SGI_ATTR_MAX + 1];
++ struct sk_buff *rep_skb;
++ int ret;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qci_psfp_sgi_conf sgiadmin;
++ struct tsn_qci_psfp_gcl *gcl = NULL;
++ const struct tsn_ops *tsnops;
++ u16 sgi_handle;
++ u8 listcount, i;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_QCI_SGI]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ pr_err("tsn: no sgi handle input\n");
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_QCI_SGI];
++
++ ret = NLA_PARSE_NESTED(sgi, TSN_QCI_SGI_ATTR_MAX,
++ na, qci_sgi_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sgi[TSN_QCI_SGI_ATTR_INDEX]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ pr_err("tsn: no sgi handle input\n");
++ return -EINVAL;
++ }
++
++ sgi_handle = nla_get_u32(sgi[TSN_QCI_SGI_ATTR_INDEX]);
++
++ /* Get config data from device */
++ genlhdr = info->genlhdr;
++
++ memset(&sgiadmin, 0, sizeof(struct tsn_qci_psfp_sgi_conf));
++
++ if (!tsnops->qci_sgi_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->qci_sgi_get(netdev, sgi_handle, &sgiadmin);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ sgiattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_SGI);
++ if (!sgiattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_QCI_SGI_ATTR_INDEX, sgi_handle))
++ return -EMSGSIZE;
++
++ /* Gate enable? sgiadmin.gate_enabled */
++ if (sgiadmin.gate_enabled) {
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_ENABLE))
++ return -EMSGSIZE;
++ } else {
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_DISABLE))
++ return -EMSGSIZE;
++ }
++
++ if (sgiadmin.config_change)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_CONFCHANGE))
++ return -EMSGSIZE;
++
++ if (sgiadmin.block_invalid_rx_enable)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_IRXEN))
++ return -EMSGSIZE;
++
++ if (sgiadmin.block_invalid_rx)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_IRX))
++ return -EMSGSIZE;
++
++ if (sgiadmin.block_octets_exceeded_enable)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_OEXEN))
++ return -EMSGSIZE;
++
++ if (sgiadmin.block_octets_exceeded)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_OEX))
++ return -EMSGSIZE;
++
++ /* Administration */
++ adminattr = nla_nest_start_noflag(rep_skb, TSN_QCI_SGI_ATTR_ADMINENTRY);
++ if (!adminattr)
++ return -EMSGSIZE;
++
++ if (sgiadmin.admin.gate_states)
++ if (nla_put_flag(rep_skb, TSN_SGI_ATTR_CTRL_INITSTATE))
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_SGI_ATTR_CTRL_CYTIME,
++ sgiadmin.admin.cycle_time) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_CTRL_CYTIMEEX,
++ sgiadmin.admin.cycle_time_extension) ||
++ NLA_PUT_U64(rep_skb, TSN_SGI_ATTR_CTRL_BTIME,
++ sgiadmin.admin.base_time) ||
++ nla_put_u8(rep_skb, TSN_SGI_ATTR_CTRL_INITIPV,
++ sgiadmin.admin.init_ipv))
++ return -EMSGSIZE;
++
++ listcount = sgiadmin.admin.control_list_length;
++ if (!listcount)
++ goto out1;
++
++ if (!sgiadmin.admin.gcl) {
++ pr_err("error: no gate control list\n");
++ ret = -EINVAL;
++ goto err;
++ }
++
++ gcl = sgiadmin.admin.gcl;
++
++ /* loop list */
++ for (i = 0; i < listcount; i++) {
++ s8 ipv;
++ u32 ti, omax;
++
++ if (!(gcl + i)) {
++ pr_err("error: list count too big\n");
++ ret = -EINVAL;
++ kfree(sgiadmin.admin.gcl);
++ goto err;
++ }
++
++ /* Adminastration entry */
++ sglattr = nla_nest_start_noflag(rep_skb,
++ TSN_SGI_ATTR_CTRL_GCLENTRY);
++ if (!sglattr)
++ return -EMSGSIZE;
++ ipv = (gcl + i)->ipv;
++ ti = (gcl + i)->time_interval;
++ omax = (gcl + i)->octet_max;
++
++ if ((gcl + i)->gate_state)
++ if (nla_put_flag(rep_skb, TSN_SGI_ATTR_GCL_GATESTATE))
++ return -EMSGSIZE;
++
++ if (nla_put_s8(rep_skb, TSN_SGI_ATTR_GCL_IPV, ipv) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_GCL_INTERVAL, ti) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_GCL_OCTMAX, omax))
++ return -EMSGSIZE;
++
++ /* End administration entry */
++ nla_nest_end(rep_skb, sglattr);
++ }
++
++ kfree(sgiadmin.admin.gcl);
++ if (nla_put_u8(rep_skb, TSN_SGI_ATTR_CTRL_LEN, listcount))
++ return -EMSGSIZE;
++
++out1:
++ /* End adminastration */
++ nla_nest_end(rep_skb, adminattr);
++
++ nla_nest_end(rep_skb, sgiattr);
++
++ return tsn_send_reply(rep_skb, info);
++err:
++ nlmsg_free(rep_skb);
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++}
++
++static int tsn_qci_sgi_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sgi_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_sgi_status_get(struct genl_info *info)
++{
++ struct nlattr *na, *sgiattr, *operattr, *sglattr;
++ struct nlattr *sgi[TSN_QCI_SGI_ATTR_MAX + 1];
++ struct sk_buff *rep_skb;
++ int ret;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_psfp_sgi_status sgistat;
++ struct tsn_qci_psfp_gcl *gcl = NULL;
++ const struct tsn_ops *tsnops;
++ u16 sgi_handle;
++ u8 listcount;
++ int valid, i;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_QCI_SGI]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ pr_err("tsn: no sgi handle input\n");
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_QCI_SGI];
++
++ ret = NLA_PARSE_NESTED(sgi, TSN_QCI_SGI_ATTR_MAX,
++ na, qci_sgi_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (!sgi[TSN_QCI_SGI_ATTR_INDEX]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ pr_err("tsn: no sgi handle input\n");
++ return -EINVAL;
++ }
++
++ sgi_handle = nla_get_u32(sgi[TSN_QCI_SGI_ATTR_INDEX]);
++
++ /* Get status data from device */
++ genlhdr = info->genlhdr;
++
++ memset(&sgistat, 0, sizeof(struct tsn_psfp_sgi_status));
++
++ if (!tsnops->qci_sgi_status_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ valid = tsnops->qci_sgi_status_get(netdev, sgi_handle, &sgistat);
++ if (valid < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, valid);
++ return valid;
++ }
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ /* Down one netlink attribute level */
++ sgiattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_SGI);
++ if (!sgiattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_QCI_SGI_ATTR_INDEX, sgi_handle))
++ return -EMSGSIZE;
++
++ /* Gate enable */
++ if (valid == 1) {
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_ENABLE))
++ return -EMSGSIZE;
++ } else {
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_DISABLE))
++ return -EMSGSIZE;
++ }
++
++ if (nla_put_u32(rep_skb, TSN_QCI_SGI_ATTR_TICKG,
++ sgistat.tick_granularity) ||
++ NLA_PUT_U64(rep_skb, TSN_QCI_SGI_ATTR_CCTIME,
++ sgistat.config_change_time) ||
++ NLA_PUT_U64(rep_skb, TSN_QCI_SGI_ATTR_CUTIME,
++ sgistat.current_time) ||
++ NLA_PUT_U64(rep_skb, TSN_QCI_SGI_ATTR_CCERROR,
++ sgistat.config_change_error))
++ return -EMSGSIZE;
++
++ if (sgistat.config_pending)
++ if (nla_put_flag(rep_skb, TSN_QCI_SGI_ATTR_CPENDING))
++ return -EMSGSIZE;
++
++ /* operation data */
++ operattr = nla_nest_start_noflag(rep_skb, TSN_QCI_SGI_ATTR_OPERENTRY);
++ if (!operattr)
++ return -EMSGSIZE;
++
++ if (sgistat.oper.gate_states)
++ if (nla_put_flag(rep_skb, TSN_SGI_ATTR_CTRL_INITSTATE))
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_SGI_ATTR_CTRL_CYTIME,
++ sgistat.oper.cycle_time) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_CTRL_CYTIMEEX,
++ sgistat.oper.cycle_time_extension) ||
++ NLA_PUT_U64(rep_skb, TSN_SGI_ATTR_CTRL_BTIME,
++ sgistat.oper.base_time) ||
++ nla_put_u8(rep_skb, TSN_SGI_ATTR_CTRL_INITIPV,
++ sgistat.oper.init_ipv))
++ return -EMSGSIZE;
++
++ /* Loop list */
++ listcount = sgistat.oper.control_list_length;
++ if (!listcount)
++ goto out1;
++
++ if (!sgistat.oper.gcl) {
++ pr_err("error: list lenghth is not zero!\n");
++ ret = -EINVAL;
++ goto err;
++ }
++
++ gcl = sgistat.oper.gcl;
++
++ /* loop list */
++ for (i = 0; i < listcount; i++) {
++ s8 ipv;
++ u32 ti, omax;
++
++ if (!(gcl + i)) {
++ pr_err("error: list count too big\n");
++ ret = -EINVAL;
++ kfree(sgistat.oper.gcl);
++ goto err;
++ }
++
++ /* Operation entry */
++ sglattr = nla_nest_start_noflag(rep_skb,
++ TSN_SGI_ATTR_CTRL_GCLENTRY);
++ if (!sglattr)
++ return -EMSGSIZE;
++ ipv = (gcl + i)->ipv;
++ ti = (gcl + i)->time_interval;
++ omax = (gcl + i)->octet_max;
++
++ if ((gcl + i)->gate_state)
++ if (nla_put_flag(rep_skb, TSN_SGI_ATTR_GCL_GATESTATE))
++ return -EMSGSIZE;
++
++ if (nla_put_s8(rep_skb, TSN_SGI_ATTR_GCL_IPV, ipv) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_GCL_INTERVAL, ti) ||
++ nla_put_u32(rep_skb, TSN_SGI_ATTR_GCL_OCTMAX, omax))
++ return -EMSGSIZE;
++
++ /* End operation entry */
++ nla_nest_end(rep_skb, sglattr);
++ }
++
++ kfree(sgistat.oper.gcl);
++ if (nla_put_u8(rep_skb, TSN_SGI_ATTR_CTRL_LEN, listcount))
++ return -EMSGSIZE;
++out1:
++ /* End operation */
++ nla_nest_end(rep_skb, operattr);
++
++ nla_nest_end(rep_skb, sgiattr);
++
++ return tsn_send_reply(rep_skb, info);
++err:
++ nlmsg_free(rep_skb);
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++}
++
++static int tsn_qci_sgi_status_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_sgi_status_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_fmi_set(struct genl_info *info)
++{
++ struct nlattr *na, *fmi[TSN_QCI_FMI_ATTR_MAX + 1];
++ u32 index;
++ int ret;
++ struct net_device *netdev;
++ struct tsn_qci_psfp_fmi fmiconf;
++ const struct tsn_ops *tsnops;
++ bool enable = 0;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&fmiconf, 0, sizeof(struct tsn_qci_psfp_fmi));
++
++ if (!info->attrs[TSN_ATTR_QCI_FMI])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QCI_FMI];
++
++ ret = NLA_PARSE_NESTED(fmi, TSN_QCI_FMI_ATTR_MAX, na, qci_fmi_policy);
++ if (ret) {
++ pr_info("tsn: parse value TSN_QCI_FMI_ATTR_MAX error.");
++ return -EINVAL;
++ }
++
++ if (!fmi[TSN_QCI_FMI_ATTR_INDEX])
++ return -EINVAL;
++
++ index = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_INDEX]);
++
++ if (fmi[TSN_QCI_FMI_ATTR_DISABLE])
++ goto loaddev;
++
++ enable = 1;
++
++ if (fmi[TSN_QCI_FMI_ATTR_CIR])
++ fmiconf.cir = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_CIR]);
++
++ if (fmi[TSN_QCI_FMI_ATTR_CBS])
++ fmiconf.cbs = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_CBS]);
++
++ if (fmi[TSN_QCI_FMI_ATTR_EIR])
++ fmiconf.eir = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_EIR]);
++
++ if (fmi[TSN_QCI_FMI_ATTR_EBS])
++ fmiconf.ebs = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_EBS]);
++
++ if (fmi[TSN_QCI_FMI_ATTR_CF])
++ fmiconf.cf = 1;
++
++ if (fmi[TSN_QCI_FMI_ATTR_CM])
++ fmiconf.cm = 1;
++
++ if (fmi[TSN_QCI_FMI_ATTR_DROPYL])
++ fmiconf.drop_on_yellow = 1;
++
++ if (fmi[TSN_QCI_FMI_ATTR_MAREDEN])
++ fmiconf.mark_red_enable = 1;
++
++ if (fmi[TSN_QCI_FMI_ATTR_MARED])
++ fmiconf.mark_red = 1;
++
++loaddev:
++
++ if (!tsnops->qci_fmi_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EINVAL;
++ }
++
++ ret = tsnops->qci_fmi_set(netdev, index, enable, &fmiconf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++ }
++
++ ret = tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, 0);
++
++ if (ret)
++ return ret;
++ return 0;
++}
++
++static int tsn_qci_fmi_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_fmi_set(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qci_fmi_get(struct genl_info *info)
++{
++ struct nlattr *na, *fmi[TSN_QCI_FMI_ATTR_MAX + 1], *fmiattr;
++ u32 index;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct net_device *netdev;
++ struct tsn_qci_psfp_fmi fmiconf;
++ struct tsn_qci_psfp_fmi_counters counters;
++ const struct tsn_ops *tsnops;
++ struct genlmsghdr *genlhdr;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_QCI_FMI])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QCI_FMI];
++
++ ret = NLA_PARSE_NESTED(fmi, TSN_QCI_FMI_ATTR_MAX,
++ na, qci_fmi_policy);
++ if (ret) {
++ pr_info("tsn: parse value TSN_QCI_FMI_ATTR_MAX error.");
++ return -EINVAL;
++ }
++
++ if (!fmi[TSN_QCI_FMI_ATTR_INDEX])
++ return -EINVAL;
++
++ index = nla_get_u32(fmi[TSN_QCI_FMI_ATTR_INDEX]);
++
++ /* Get data from device */
++ memset(&fmiconf, 0, sizeof(struct tsn_qci_psfp_fmi));
++ memset(&counters, 0, sizeof(struct tsn_qci_psfp_fmi_counters));
++
++ if (!tsnops->qci_fmi_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EINVAL;
++ }
++
++ ret = tsnops->qci_fmi_get(netdev, index, &fmiconf, &counters);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, ret);
++ return ret;
++ }
++
++ genlhdr = info->genlhdr;
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ fmiattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QCI_FMI);
++ if (!fmiattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_QCI_FMI_ATTR_INDEX, index) ||
++ nla_put_u32(rep_skb, TSN_QCI_FMI_ATTR_CIR, fmiconf.cir) ||
++ nla_put_u32(rep_skb, TSN_QCI_FMI_ATTR_CBS, fmiconf.cbs) ||
++ nla_put_u32(rep_skb, TSN_QCI_FMI_ATTR_EIR, fmiconf.eir) ||
++ nla_put_u32(rep_skb, TSN_QCI_FMI_ATTR_EBS, fmiconf.ebs))
++ return -EMSGSIZE;
++
++ if (fmiconf.cf)
++ if (nla_put_flag(rep_skb, TSN_QCI_FMI_ATTR_CF))
++ return -EMSGSIZE;
++
++ if (fmiconf.cm)
++ if (nla_put_flag(rep_skb, TSN_QCI_FMI_ATTR_CM))
++ return -EMSGSIZE;
++
++ if (fmiconf.drop_on_yellow)
++ if (nla_put_flag(rep_skb, TSN_QCI_FMI_ATTR_DROPYL))
++ return -EMSGSIZE;
++
++ if (fmiconf.mark_red_enable)
++ if (nla_put_flag(rep_skb, TSN_QCI_FMI_ATTR_MAREDEN))
++ return -EMSGSIZE;
++
++ if (fmiconf.mark_red)
++ if (nla_put_flag(rep_skb, TSN_QCI_FMI_ATTR_MAREDEN))
++ return -EMSGSIZE;
++
++ if (nla_put(rep_skb, TSN_QCI_FMI_ATTR_COUNTERS,
++ sizeof(struct tsn_qci_psfp_fmi_counters), &counters))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, fmiattr);
++
++ tsn_send_reply(rep_skb, info);
++
++ return 0;
++}
++
++static int tsn_qci_fmi_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qci_fmi_get(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qbv_set(struct genl_info *info)
++{
++ struct nlattr *na, *na1;
++ struct nlattr *qbv_table;
++ struct nlattr *qbv[TSN_QBV_ATTR_MAX + 1];
++ struct nlattr *qbvctrl[TSN_QBV_ATTR_CTRL_MAX + 1];
++ int rem;
++ int ret = 0;
++ struct net_device *netdev;
++ struct tsn_qbv_conf qbvconfig;
++ const struct tsn_ops *tsnops;
++ struct tsn_qbv_entry *gatelist = NULL;
++ int count = 0;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&qbvconfig, 0, sizeof(struct tsn_qbv_conf));
++
++ if (!info->attrs[TSN_ATTR_QBV])
++ return -EINVAL;
++
++ na = info->attrs[TSN_ATTR_QBV];
++
++ ret = NLA_PARSE_NESTED(qbv, TSN_QBV_ATTR_MAX, na, qbv_policy);
++ if (ret)
++ return -EINVAL;
++
++ if (qbv[TSN_QBV_ATTR_ENABLE])
++ qbvconfig.gate_enabled = 1;
++ else
++ goto setdrive;
++
++ if (qbv[TSN_QBV_ATTR_CONFIGCHANGE])
++ qbvconfig.config_change = 1;
++
++ if (!qbv[TSN_QBV_ATTR_ADMINENTRY]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -1;
++ }
++
++ na1 = qbv[TSN_QBV_ATTR_ADMINENTRY];
++ NLA_PARSE_NESTED(qbvctrl, TSN_QBV_ATTR_CTRL_MAX,
++ na1, qbv_ctrl_policy);
++
++ if (qbvctrl[TSN_QBV_ATTR_CTRL_CYCLETIME]) {
++ qbvconfig.admin.cycle_time =
++ nla_get_u32(qbvctrl[TSN_QBV_ATTR_CTRL_CYCLETIME]);
++ }
++
++ if (qbvctrl[TSN_QBV_ATTR_CTRL_CYCLETIMEEXT]) {
++ qbvconfig.admin.cycle_time_extension =
++ nla_get_u32(qbvctrl[TSN_QBV_ATTR_CTRL_CYCLETIMEEXT]);
++ }
++
++ if (qbvctrl[TSN_QBV_ATTR_CTRL_BASETIME]) {
++ qbvconfig.admin.base_time =
++ nla_get_u64(qbvctrl[TSN_QBV_ATTR_CTRL_BASETIME]);
++ }
++
++ if (qbvctrl[TSN_QBV_ATTR_CTRL_GATESTATE]) {
++ qbvconfig.admin.gate_states =
++ nla_get_u8(qbvctrl[TSN_QBV_ATTR_CTRL_GATESTATE]);
++ }
++
++ if (qbvctrl[TSN_QBV_ATTR_CTRL_LISTCOUNT]) {
++ int listcount;
++
++ listcount = nla_get_u32(qbvctrl[TSN_QBV_ATTR_CTRL_LISTCOUNT]);
++
++ qbvconfig.admin.control_list_length = listcount;
++
++ gatelist = kmalloc_array(listcount,
++ sizeof(*gatelist),
++ GFP_KERNEL);
++
++ nla_for_each_nested(qbv_table, na1, rem) {
++ struct nlattr *qbv_entry[TSN_QBV_ATTR_ENTRY_MAX + 1];
++
++ if (nla_type(qbv_table) != TSN_QBV_ATTR_CTRL_LISTENTRY)
++ continue;
++
++ ret = NLA_PARSE_NESTED(qbv_entry,
++ TSN_QBV_ATTR_ENTRY_MAX,
++ qbv_table, qbv_entry_policy);
++ if (ret)
++ return -EINVAL;
++
++ (gatelist + count)->gate_state =
++ nla_get_u8(qbv_entry[TSN_QBV_ATTR_ENTRY_GC]);
++ (gatelist + count)->time_interval =
++ nla_get_u32(qbv_entry[TSN_QBV_ATTR_ENTRY_TM]);
++ count++;
++ if (count > listcount)
++ break;
++ }
++ }
++
++ if (gatelist)
++ qbvconfig.admin.control_list = gatelist;
++
++setdrive:
++ if (!tsnops->qbv_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ goto err;
++ }
++
++ ret = tsnops->qbv_set(netdev, &qbvconfig);
++
++ /* send back */
++ if (ret < 0)
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ else
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++
++err:
++ kfree(gatelist);
++ return ret;
++}
++
++static int tsn_qbv_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME]) {
++ cmd_qbv_set(info);
++ return 0;
++ }
++
++ return -1;
++}
++
++static int cmd_qbv_get(struct genl_info *info)
++{
++ struct nlattr *qbv, *qbvadminattr;
++ struct sk_buff *rep_skb;
++ int ret;
++ int len = 0, i = 0;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qbv_conf qbvconf;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ genlhdr = info->genlhdr;
++
++ memset(&qbvconf, 0, sizeof(struct tsn_qbv_conf));
++
++ if (!tsnops->qbv_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->qbv_get(netdev, &qbvconf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ qbv = nla_nest_start_noflag(rep_skb, TSN_ATTR_QBV);
++ if (!qbv)
++ return -EMSGSIZE;
++
++ qbvadminattr = nla_nest_start_noflag(rep_skb, TSN_QBV_ATTR_ADMINENTRY);
++ if (!qbvadminattr)
++ return -EMSGSIZE;
++
++ if (qbvconf.admin.control_list) {
++ len = qbvconf.admin.control_list_length;
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_LISTCOUNT, len))
++ return -EMSGSIZE;
++
++ for (i = 0; i < len; i++) {
++ struct nlattr *qbv_table;
++ u8 gs;
++ u32 tp;
++ int glisttype = TSN_QBV_ATTR_CTRL_LISTENTRY;
++
++ gs = (qbvconf.admin.control_list + i)->gate_state;
++ tp = (qbvconf.admin.control_list + i)->time_interval;
++
++ qbv_table =
++ nla_nest_start_noflag(rep_skb, glisttype);
++ if (!qbv_table)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_ENTRY_ID, i) ||
++ nla_put_u8(rep_skb, TSN_QBV_ATTR_ENTRY_GC, gs) ||
++ nla_put_u32(rep_skb, TSN_QBV_ATTR_ENTRY_TM, tp))
++ return -EMSGSIZE;
++ nla_nest_end(rep_skb, qbv_table);
++ }
++
++ if (qbvconf.admin.gate_states)
++ if (nla_put_u8(rep_skb, TSN_QBV_ATTR_CTRL_GATESTATE,
++ qbvconf.admin.gate_states))
++ return -EMSGSIZE;
++
++ if (qbvconf.admin.cycle_time)
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_CYCLETIME,
++ qbvconf.admin.cycle_time))
++ return -EMSGSIZE;
++
++ if (qbvconf.admin.cycle_time_extension)
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_CYCLETIMEEXT,
++ qbvconf.admin.cycle_time_extension))
++ return -EMSGSIZE;
++
++ if (qbvconf.admin.base_time)
++ if (NLA_PUT_U64(rep_skb, TSN_QBV_ATTR_CTRL_BASETIME,
++ qbvconf.admin.base_time))
++ return -EMSGSIZE;
++
++ kfree(qbvconf.admin.control_list);
++
++ } else {
++ pr_info("tsn: error get administrator data.");
++ }
++
++ nla_nest_end(rep_skb, qbvadminattr);
++
++ if (qbvconf.gate_enabled) {
++ if (nla_put_flag(rep_skb, TSN_QBV_ATTR_ENABLE))
++ return -EMSGSIZE;
++ } else {
++ if (nla_put_flag(rep_skb, TSN_QBV_ATTR_DISABLE))
++ return -EMSGSIZE;
++ }
++
++ if (qbvconf.maxsdu)
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_MAXSDU, qbvconf.maxsdu))
++ return -EMSGSIZE;
++
++ if (qbvconf.config_change)
++ if (nla_put_flag(rep_skb, TSN_QBV_ATTR_CONFIGCHANGE))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, qbv);
++
++ tsn_send_reply(rep_skb, info);
++
++ return ret;
++}
++
++static int cmd_qbv_status_get(struct genl_info *info)
++{
++ struct nlattr *qbv, *qbvoperattr;
++ struct sk_buff *rep_skb;
++ int ret;
++ int len = 0, i = 0;
++ struct net_device *netdev;
++ struct genlmsghdr *genlhdr;
++ struct tsn_qbv_status qbvstatus;
++ const struct tsn_ops *tsnops;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ genlhdr = info->genlhdr;
++
++ memset(&qbvstatus, 0, sizeof(struct tsn_qbv_status));
++
++ if (!tsnops->qbv_get_status) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->qbv_get_status(netdev, &qbvstatus);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ qbv = nla_nest_start_noflag(rep_skb, TSN_ATTR_QBV);
++ if (!qbv)
++ return -EMSGSIZE;
++
++ qbvoperattr = nla_nest_start_noflag(rep_skb, TSN_QBV_ATTR_OPERENTRY);
++ if (!qbvoperattr)
++ return -EMSGSIZE;
++
++ if (qbvstatus.oper.control_list) {
++ len = qbvstatus.oper.control_list_length;
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_LISTCOUNT, len)) {
++ nla_nest_cancel(rep_skb, qbvoperattr);
++ return -EMSGSIZE;
++ }
++
++ for (i = 0; i < len; i++) {
++ struct nlattr *qbv_table;
++ u8 gs;
++ u32 tp;
++ int glisttype = TSN_QBV_ATTR_CTRL_LISTENTRY;
++
++ gs = (qbvstatus.oper.control_list + i)->gate_state;
++ tp = (qbvstatus.oper.control_list + i)->time_interval;
++
++ qbv_table = nla_nest_start_noflag(rep_skb, glisttype);
++ if (!qbv_table)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_ENTRY_ID, i) ||
++ nla_put_u8(rep_skb, TSN_QBV_ATTR_ENTRY_GC, gs) ||
++ nla_put_u32(rep_skb, TSN_QBV_ATTR_ENTRY_TM, tp)) {
++ nla_nest_cancel(rep_skb, qbv_table);
++ return -EMSGSIZE;
++ }
++
++ nla_nest_end(rep_skb, qbv_table);
++ }
++
++ if (qbvstatus.oper.gate_states) {
++ if (nla_put_u8(rep_skb, TSN_QBV_ATTR_CTRL_GATESTATE,
++ qbvstatus.oper.gate_states))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.oper.cycle_time) {
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_CYCLETIME,
++ qbvstatus.oper.cycle_time))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.oper.cycle_time_extension) {
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_CTRL_CYCLETIMEEXT,
++ qbvstatus.oper.cycle_time_extension))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.oper.base_time) {
++ if (NLA_PUT_U64(rep_skb, TSN_QBV_ATTR_CTRL_BASETIME,
++ qbvstatus.oper.base_time))
++ return -EMSGSIZE;
++ }
++
++ kfree(qbvstatus.oper.control_list);
++ } else {
++ pr_info("tsn: error get operation list data.");
++ }
++
++ nla_nest_end(rep_skb, qbvoperattr);
++
++ if (qbvstatus.config_change_time) {
++ if (NLA_PUT_U64(rep_skb, TSN_QBV_ATTR_CONFIGCHANGETIME,
++ qbvstatus.config_change_time))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.tick_granularity) {
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_GRANULARITY,
++ qbvstatus.tick_granularity))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.current_time) {
++ if (NLA_PUT_U64(rep_skb, TSN_QBV_ATTR_CURRENTTIME,
++ qbvstatus.current_time))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.config_pending) {
++ if (nla_put_flag(rep_skb, TSN_QBV_ATTR_CONFIGPENDING))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.config_change_error) {
++ if (NLA_PUT_U64(rep_skb, TSN_QBV_ATTR_CONFIGCHANGEERROR,
++ qbvstatus.config_change_error))
++ return -EMSGSIZE;
++ }
++
++ if (qbvstatus.supported_list_max) {
++ if (nla_put_u32(rep_skb, TSN_QBV_ATTR_LISTMAX,
++ qbvstatus.supported_list_max))
++ return -EMSGSIZE;
++ }
++
++ nla_nest_end(rep_skb, qbv);
++
++ tsn_send_reply(rep_skb, info);
++
++ return ret;
++}
++
++static int tsn_qbv_status_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME])
++ cmd_qbv_status_get(info);
++
++ return 0;
++}
++
++static int tsn_qbv_get(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME])
++ cmd_qbv_get(info);
++
++ return 0;
++}
++
++static int tsn_cbs_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *cbsa[TSN_CBS_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ u8 tc, bw;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_CBS]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_CBS];
++
++ if (!tsnops->cbs_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = NLA_PARSE_NESTED(cbsa, TSN_CBS_ATTR_MAX, na, cbs_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!cbsa[TSN_CBS_ATTR_TC_INDEX]) {
++ pr_err("tsn: no TSN_CBS_ATTR_TC_INDEX input\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++ tc = nla_get_u8(cbsa[TSN_CBS_ATTR_TC_INDEX]);
++
++ if (!cbsa[TSN_CBS_ATTR_BW]) {
++ pr_err("tsn: no TSN_CBS_ATTR_BW input\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ bw = nla_get_u8(cbsa[TSN_CBS_ATTR_BW]);
++ if (bw > 100) {
++ pr_err("tsn: TSN_CBS_ATTR_BW isn't in the range of 0~100\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ ret = tsnops->cbs_set(netdev, tc, bw);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_cbs_get(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na, *cbsattr;
++ struct nlattr *cbsa[TSN_CBS_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct genlmsghdr *genlhdr;
++ u8 tc;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_CBS]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!tsnops->cbs_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ na = info->attrs[TSN_ATTR_CBS];
++ ret = NLA_PARSE_NESTED(cbsa, TSN_CBS_ATTR_MAX, na, cbs_policy);
++ if (ret) {
++ pr_err("tsn: parse value TSN_CBS_ATTR_MAX error.");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ /* Get status data from device */
++ genlhdr = info->genlhdr;
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd, &rep_skb,
++ NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ cbsattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_CBS);
++ if (!cbsattr)
++ return -EMSGSIZE;
++
++ if (!cbsa[TSN_CBS_ATTR_TC_INDEX]) {
++ pr_err("tsn: must to specify the TSN_CBS_ATTR_TC_INDEX\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++ tc = nla_get_u8(cbsa[TSN_CBS_ATTR_TC_INDEX]);
++
++ ret = tsnops->cbs_get(netdev, tc);
++ if (ret < 0) {
++ pr_err("tsn: cbs_get return error\n");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ if (nla_put_u8(rep_skb, TSN_CBS_ATTR_BW, ret & 0XF))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, cbsattr);
++ return tsn_send_reply(rep_skb, info);
++}
++
++static int cmd_qbu_set(struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *qbua[TSN_QBU_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ u8 preemptible = 0;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_QBU]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_QBU];
++
++ ret = NLA_PARSE_NESTED(qbua, TSN_QBU_ATTR_MAX, na, qbu_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (qbua[TSN_QBU_ATTR_ADMIN_STATE])
++ preemptible = nla_get_u8(qbua[TSN_QBU_ATTR_ADMIN_STATE]);
++ else
++ pr_info("No preemptible TSN_QBU_ATTR_ADMIN_STATE config!\n");
++
++ if (!tsnops->qbu_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EINVAL;
++ }
++
++ ret = tsnops->qbu_set(netdev, preemptible);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_qbu_set(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME])
++ return cmd_qbu_set(info);
++
++ return -1;
++}
++
++static int cmd_qbu_get_status(struct genl_info *info)
++{
++ struct nlattr *qbuattr;
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct genlmsghdr *genlhdr;
++ struct tsn_preempt_status pps;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ /* Get status data from device */
++ genlhdr = info->genlhdr;
++
++ memset(&pps, 0, sizeof(struct tsn_preempt_status));
++
++ if (!tsnops->qbu_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->qbu_get(netdev, &pps);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ qbuattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_QBU);
++ if (!qbuattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u8(rep_skb, TSN_QBU_ATTR_ADMIN_STATE, pps.admin_state) ||
++ nla_put_u32(rep_skb,
++ TSN_QBU_ATTR_HOLD_ADVANCE, pps.hold_advance) ||
++ nla_put_u32(rep_skb,
++ TSN_QBU_ATTR_RELEASE_ADVANCE, pps.release_advance))
++ return -EMSGSIZE;
++
++ if (pps.preemption_active) {
++ if (nla_put_flag(rep_skb, TSN_QBU_ATTR_ACTIVE))
++ return -EMSGSIZE;
++ }
++
++ if (nla_put_u8(rep_skb, TSN_QBU_ATTR_HOLD_REQUEST, pps.hold_request))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, qbuattr);
++
++ return tsn_send_reply(rep_skb, info);
++}
++
++static int tsn_qbu_get_status(struct sk_buff *skb, struct genl_info *info)
++{
++ if (info->attrs[TSN_ATTR_IFNAME])
++ return cmd_qbu_get_status(info);
++
++ return -1;
++}
++
++static int tsn_tsd_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *ntsd[TSN_TSD_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct tsn_tsd tsd;
++ int ret;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ memset(&tsd, 0, sizeof(struct tsn_tsd));
++
++ if (!info->attrs[TSN_ATTR_TSD]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_TSD];
++
++ ret = NLA_PARSE_NESTED(ntsd, TSN_TSD_ATTR_MAX, na, tsd_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!tsnops->tsd_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -EINVAL;
++ }
++
++ if (nla_get_flag(ntsd[TSN_TSD_ATTR_DISABLE])) {
++ tsd.enable = false;
++ } else {
++ if (ntsd[TSN_TSD_ATTR_PERIOD])
++ tsd.period = nla_get_u32(ntsd[TSN_TSD_ATTR_PERIOD]);
++
++ if (!tsd.period) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (ntsd[TSN_TSD_ATTR_MAX_FRM_NUM])
++ tsd.maxFrameNum =
++ nla_get_u32(ntsd[TSN_TSD_ATTR_MAX_FRM_NUM]);
++
++ if (ntsd[TSN_TSD_ATTR_SYN_IMME])
++ tsd.syn_flag = 2;
++ else
++ tsd.syn_flag = 1;
++
++ tsd.enable = true;
++ }
++
++ ret = tsnops->tsd_set(netdev, &tsd);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_tsd_get(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na, *tsdattr;
++ struct nlattr *tsda[TSN_TSD_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct sk_buff *rep_skb;
++ int ret;
++ struct genlmsghdr *genlhdr;
++ struct tsn_tsd_status tts;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_TSD]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ if (!tsnops->tsd_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = tsnops->tsd_get(netdev, &tts);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ na = info->attrs[TSN_ATTR_TSD];
++
++ ret = NLA_PARSE_NESTED(tsda, TSN_TSD_ATTR_MAX,
++ na, tsd_policy);
++ if (ret) {
++ pr_err("tsn: parse value TSN_TSD_ATTR_MAX error.");
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ /* Get status data from device */
++ genlhdr = info->genlhdr;
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd, &rep_skb,
++ NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ tsdattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_TSD);
++ if (!tsdattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u32(rep_skb, TSN_TSD_ATTR_PERIOD, tts.period) ||
++ nla_put_u32(rep_skb, TSN_TSD_ATTR_MAX_FRM_NUM, tts.maxFrameNum) ||
++ nla_put_u32(rep_skb, TSN_TSD_ATTR_CYCLE_NUM, tts.cycleNum) ||
++ nla_put_u32(rep_skb, TSN_TSD_ATTR_LOSS_STEPS, tts.loss_steps) ||
++ nla_put_u32(rep_skb, TSN_TSD_ATTR_MAX_FRM_NUM, tts.maxFrameNum))
++ return -EMSGSIZE;
++
++ if (!tts.enable) {
++ if (nla_put_flag(rep_skb, TSN_TSD_ATTR_DISABLE))
++ return -EMSGSIZE;
++ } else {
++ if (nla_put_flag(rep_skb, TSN_TSD_ATTR_ENABLE))
++ return -EMSGSIZE;
++ }
++
++ if (tts.flag == 2)
++ if (nla_put_flag(rep_skb, TSN_TSD_ATTR_SYN_IMME))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, tsdattr);
++ return tsn_send_reply(rep_skb, info);
++}
++
++static int tsn_ct_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *cta[TSN_CT_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ u8 queue_stat;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_CT]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_CT];
++
++ if (!tsnops->ct_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = NLA_PARSE_NESTED(cta, TSN_CT_ATTR_MAX,
++ na, ct_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ queue_stat = nla_get_u8(cta[TSN_CT_ATTR_QUEUE_STATE]);
++
++ ret = tsnops->ct_set(netdev, queue_stat);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_cbgen_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *cbgena[TSN_CBGEN_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ u32 index;
++ struct tsn_seq_gen_conf sg_conf;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_CBGEN]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_CBGEN];
++
++ if (!tsnops->cbgen_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = NLA_PARSE_NESTED(cbgena, TSN_CBGEN_ATTR_MAX,
++ na, cbgen_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ index = nla_get_u32(cbgena[TSN_CBGEN_ATTR_INDEX]);
++
++ memset(&sg_conf, 0, sizeof(struct tsn_seq_gen_conf));
++ sg_conf.iport_mask = nla_get_u8(cbgena[TSN_CBGEN_ATTR_PORT_MASK]);
++ sg_conf.split_mask = nla_get_u8(cbgena[TSN_CBGEN_ATTR_SPLIT_MASK]);
++ sg_conf.seq_len = nla_get_u8(cbgena[TSN_CBGEN_ATTR_SEQ_LEN]);
++ sg_conf.seq_num = nla_get_u32(cbgena[TSN_CBGEN_ATTR_SEQ_NUM]);
++
++ ret = tsnops->cbgen_set(netdev, index, &sg_conf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_cbrec_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *cbreca[TSN_CBREC_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ u32 index;
++ struct tsn_seq_rec_conf sr_conf;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_CBREC]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_CBREC];
++
++ if (!tsnops->cbrec_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = NLA_PARSE_NESTED(cbreca, TSN_CBREC_ATTR_MAX,
++ na, cbrec_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ index = nla_get_u32(cbreca[TSN_CBREC_ATTR_INDEX]);
++
++ memset(&sr_conf, 0, sizeof(struct tsn_seq_rec_conf));
++ sr_conf.seq_len = nla_get_u8(cbreca[TSN_CBREC_ATTR_SEQ_LEN]);
++ sr_conf.his_len = nla_get_u8(cbreca[TSN_CBREC_ATTR_HIS_LEN]);
++ sr_conf.rtag_pop_en = nla_get_flag(cbreca[TSN_CBREC_ATTR_TAG_POP_EN]);
++
++ ret = tsnops->cbrec_set(netdev, index, &sr_conf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++ return 0;
++}
++
++static int tsn_cbstatus_get(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *cba[TSN_CBSTAT_ATTR_MAX + 1];
++ struct nlattr *cbattr;
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ struct sk_buff *rep_skb;
++ int ret;
++ unsigned int index;
++ struct genlmsghdr *genlhdr;
++ struct tsn_cb_status cbstat;
++ struct tsn_port *port;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ /* Get status data from device */
++ genlhdr = info->genlhdr;
++
++ memset(&cbstat, 0, sizeof(struct tsn_cb_status));
++
++ if (!tsnops->cb_get) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ na = info->attrs[TSN_ATTR_CBSTAT];
++ ret = NLA_PARSE_NESTED(cba, TSN_CBSTAT_ATTR_MAX,
++ na, cbstat_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ index = nla_get_u32(cba[TSN_CBSTAT_ATTR_INDEX]);
++
++ ret = tsnops->cb_get(netdev, index, &cbstat);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ /* Form netlink reply data */
++ ret = tsn_prepare_reply(info, genlhdr->cmd,
++ &rep_skb, NLMSG_ALIGN(MAX_ATTR_SIZE));
++ if (ret < 0)
++ return ret;
++
++ if (nla_put_string(rep_skb, TSN_ATTR_IFNAME, netdev->name))
++ return -EMSGSIZE;
++
++ cbattr = nla_nest_start_noflag(rep_skb, TSN_ATTR_CBSTAT);
++ if (!cbattr)
++ return -EMSGSIZE;
++
++ if (nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_GEN_REC, cbstat.gen_rec) ||
++ nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_ERR, cbstat.err) ||
++ nla_put_u32(rep_skb, TSN_CBSTAT_ATTR_SEQ_NUM,
++ cbstat.seq_num) ||
++ nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_SEQ_LEN, cbstat.seq_len) ||
++ nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_SPLIT_MASK,
++ cbstat.split_mask) ||
++ nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_PORT_MASK,
++ cbstat.iport_mask) ||
++ nla_put_u8(rep_skb, TSN_CBSTAT_ATTR_HIS_LEN, cbstat.his_len) ||
++ nla_put_u32(rep_skb, TSN_CBSTAT_ATTR_SEQ_HIS,
++ cbstat.seq_his))
++ return -EMSGSIZE;
++
++ nla_nest_end(rep_skb, cbattr);
++
++ return tsn_send_reply(rep_skb, info);
++}
++
++static int tsn_dscp_set(struct sk_buff *skb, struct genl_info *info)
++{
++ struct nlattr *na;
++ struct nlattr *dscpa[TSN_DSCP_ATTR_MAX + 1];
++ struct net_device *netdev;
++ const struct tsn_ops *tsnops;
++ int ret;
++ bool enable = 0;
++ struct tsn_port *port;
++ int dscp_ix;
++ struct tsn_qos_switch_dscp_conf dscp_conf;
++
++ port = tsn_init_check(info, &netdev);
++ if (!port)
++ return -ENODEV;
++
++ tsnops = port->tsnops;
++
++ if (!info->attrs[TSN_ATTR_DSCP]) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ na = info->attrs[TSN_ATTR_DSCP];
++
++ if (!tsnops->dscp_set) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EPERM);
++ return -1;
++ }
++
++ ret = NLA_PARSE_NESTED(dscpa, TSN_DSCP_ATTR_MAX,
++ na, dscp_policy);
++ if (ret) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, -EINVAL);
++ return -EINVAL;
++ }
++
++ enable = 1;
++ if (dscpa[TSN_DSCP_ATTR_DISABLE])
++ enable = 0;
++ dscp_ix = nla_get_u32(dscpa[TSN_DSCP_ATTR_INDEX]);
++ dscp_conf.cos = nla_get_u32(dscpa[TSN_DSCP_ATTR_COS]);
++ dscp_conf.dpl = nla_get_u32(dscpa[TSN_DSCP_ATTR_DPL]);
++ ret = tsnops->dscp_set(netdev, enable, dscp_ix, &dscp_conf);
++ if (ret < 0) {
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, ret);
++ return ret;
++ }
++
++ tsn_simple_reply(info, TSN_CMD_REPLY,
++ netdev->name, 0);
++
++ return 0;
++}
++
++static const struct genl_ops tsnnl_ops[] = {
++ {
++ .cmd = TSN_CMD_ECHO,
++ .doit = tsn_echo_cmd,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CAP_GET,
++ .doit = tsn_cap_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QBV_SET,
++ .doit = tsn_qbv_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QBV_GET,
++ .doit = tsn_qbv_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QBV_GET_STATUS,
++ .doit = tsn_qbv_status_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CB_STREAMID_SET,
++ .doit = tsn_cb_streamid_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CB_STREAMID_GET,
++ .doit = tsn_cb_streamid_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CB_STREAMID_GET_COUNTS,
++ .doit = tsn_cb_streamid_counters_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_CAP_GET,
++ .doit = tsn_qci_cap_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SFI_SET,
++ .doit = tsn_qci_sfi_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SFI_GET,
++ .doit = tsn_qci_sfi_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SFI_GET_COUNTS,
++ .doit = tsn_qci_sfi_counters_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SGI_SET,
++ .doit = tsn_qci_sgi_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SGI_GET,
++ .doit = tsn_qci_sgi_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_SGI_GET_STATUS,
++ .doit = tsn_qci_sgi_status_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_FMI_SET,
++ .doit = tsn_qci_fmi_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QCI_FMI_GET,
++ .doit = tsn_qci_fmi_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CBS_SET,
++ .doit = tsn_cbs_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CBS_GET,
++ .doit = tsn_cbs_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QBU_SET,
++ .doit = tsn_qbu_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_QBU_GET_STATUS,
++ .doit = tsn_qbu_get_status,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_TSD_SET,
++ .doit = tsn_tsd_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_TSD_GET,
++ .doit = tsn_tsd_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CT_SET,
++ .doit = tsn_ct_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CBGEN_SET,
++ .doit = tsn_cbgen_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CBREC_SET,
++ .doit = tsn_cbrec_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_CBSTAT_GET,
++ .doit = tsn_cbstatus_get,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = TSN_CMD_DSCP_SET,
++ .doit = tsn_dscp_set,
++ .flags = GENL_ADMIN_PERM,
++ },
++};
++
++static const struct genl_multicast_group tsn_mcgrps[] = {
++ [TSN_MCGRP_QBV] = { .name = TSN_MULTICAST_GROUP_QBV},
++ [TSN_MCGRP_QCI] = { .name = TSN_MULTICAST_GROUP_QCI},
++};
++
++static struct genl_family tsn_family = {
++ .name = TSN_GENL_NAME,
++ .version = TSN_GENL_VERSION,
++ .maxattr = TSN_CMD_ATTR_MAX,
++ .module = THIS_MODULE,
++ .netnsok = true,
++ .ops = tsnnl_ops,
++ .n_ops = ARRAY_SIZE(tsnnl_ops),
++ .mcgrps = tsn_mcgrps,
++ .n_mcgrps = ARRAY_SIZE(tsn_mcgrps),
++};
++
++int tsn_port_register(struct net_device *netdev,
++ struct tsn_ops *tsnops, u16 groupid)
++{
++ struct tsn_port *port;
++
++ if (list_empty(&port_list)) {
++ INIT_LIST_HEAD(&port_list);
++ } else {
++ list_for_each_entry(port, &port_list, list) {
++ if (port->netdev == netdev) {
++ pr_info("TSN device already registered!\n");
++ return -1;
++ }
++ }
++ }
++
++ port = kzalloc(sizeof(*port), GFP_KERNEL);
++ if (!port)
++ return -1;
++
++ port->netdev = netdev;
++ port->groupid = groupid;
++ port->tsnops = tsnops;
++ port->nd.dev = netdev;
++
++ if (groupid < GROUP_OFFSET_SWITCH)
++ port->type = TSN_ENDPOINT;
++ else
++ port->type = TSN_SWITCH;
++
++ list_add_tail(&port->list, &port_list);
++
++ if (tsnops && tsnops->device_init)
++ port->tsnops->device_init(netdev);
++
++ return 0;
++}
++EXPORT_SYMBOL(tsn_port_register);
++
++void tsn_port_unregister(struct net_device *netdev)
++{
++ struct tsn_port *p;
++
++ list_for_each_entry(p, &port_list, list) {
++ if (!p || !p->netdev)
++ continue;
++ if (p->netdev == netdev) {
++ if (p->tsnops->device_deinit)
++ p->tsnops->device_deinit(netdev);
++ list_del(&p->list);
++ kfree(p);
++ break;
++ }
++ }
++}
++EXPORT_SYMBOL(tsn_port_unregister);
++
++static int tsn_multicast_to_user(unsigned long event,
++ struct tsn_notifier_info *tsn_info)
++{
++ struct sk_buff *skb;
++ struct genlmsghdr *nlh;
++ int res;
++ struct tsn_qbv_conf *qbvdata;
++
++ /* If new attributes are added, please revisit this allocation */
++ skb = genlmsg_new(sizeof(*tsn_info), GFP_KERNEL);
++ if (!skb) {
++ pr_err("Allocation failure.\n");
++ return -ENOMEM;
++ }
++
++ switch (event) {
++ case TSN_QBV_CONFIGCHANGETIME_ARRIVE:
++ nlh = genlmsg_put(skb, 0, 1, &tsn_family,
++ GFP_KERNEL, TSN_CMD_QBV_SET);
++ qbvdata = &tsn_info->ntdata.qbv_notify;
++ res = NLA_PUT_U64(skb, TSN_QBV_ATTR_CTRL_BASETIME,
++ qbvdata->admin.base_time);
++
++ if (res) {
++ pr_err("put data failure!\n");
++ goto done;
++ }
++
++ res = nla_put_u32(skb, TSN_QBV_ATTR_CTRL_CYCLETIME,
++ qbvdata->admin.cycle_time);
++ if (res) {
++ pr_err("put data failure!\n");
++ goto done;
++ }
++
++ if (qbvdata->gate_enabled)
++ res = nla_put_flag(skb, TSN_QBV_ATTR_ENABLE +
++ TSN_QBV_ATTR_CTRL_MAX);
++ else
++ res = nla_put_flag(skb, TSN_QBV_ATTR_DISABLE +
++ TSN_QBV_ATTR_CTRL_MAX);
++ if (res) {
++ pr_err("put data failure!\n");
++ goto done;
++ }
++
++ res = nla_put_u32(skb, TSN_QBV_ATTR_CTRL_UNSPEC,
++ tsn_info->dev->ifindex);
++ if (res) {
++ pr_err("put data failure!\n");
++ goto done;
++ }
++
++ break;
++ default:
++ pr_info("event not supportted!\n");
++ break;
++ }
++
++ (void)genlmsg_end(skb, nlh);
++
++ res = genlmsg_multicast_allns(&tsn_family, skb, 0,
++ TSN_MCGRP_QBV, GFP_KERNEL);
++ skb = NULL;
++ if (res && res != -ESRCH) {
++ pr_err("genlmsg_multicast_allns error: %d\n", res);
++ goto done;
++ }
++
++ if (res == -ESRCH)
++ res = 0;
++
++done:
++ if (skb) {
++ nlmsg_free(skb);
++ skb = NULL;
++ }
++
++ return res;
++}
++
++/* called with RTNL or RCU */
++static int tsn_event(struct notifier_block *unused,
++ unsigned long event, void *ptr)
++{
++ struct tsn_notifier_info *tsn_info;
++ int err = NOTIFY_DONE;
++
++ switch (event) {
++ case TSN_QBV_CONFIGCHANGETIME_ARRIVE:
++ tsn_info = ptr;
++ err = tsn_multicast_to_user(event, tsn_info);
++ if (err) {
++ err = notifier_from_errno(err);
++ break;
++ }
++ break;
++ default:
++ pr_info("event not supportted!\n");
++ break;
++ }
++
++ return err;
++}
++
++static struct notifier_block tsn_notifier = {
++ .notifier_call = tsn_event,
++};
++
++static int __init tsn_genetlink_init(void)
++{
++ int ret;
++
++ pr_info("tsn generic netlink module v%d init...\n", TSN_GENL_VERSION);
++
++ ret = genl_register_family(&tsn_family);
++
++ if (ret != 0) {
++ pr_info("failed to init tsn generic netlink example module\n");
++ return ret;
++ }
++
++ register_tsn_notifier(&tsn_notifier);
++
++ return 0;
++}
++
++static void __exit tsn_genetlink_exit(void)
++{
++ int ret;
++
++ ret = genl_unregister_family(&tsn_family);
++ if (ret != 0)
++ pr_info("failed to unregister family:%i\n", ret);
++
++ unregister_tsn_notifier(&tsn_notifier);
++}
++
++module_init(tsn_genetlink_init);
++module_exit(tsn_genetlink_exit);
++MODULE_LICENSE("GPL");