diff options
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.patch | 5113 |
1 files changed, 0 insertions, 5113 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 deleted file mode 100644 index 33de677930..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch +++ /dev/null @@ -1,5113 +0,0 @@ -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"); |