From cddd4591404fb4c53dc0b3c0b15b942cdbed4356 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 10 Apr 2020 10:47:05 +0800 Subject: layerscape: add patches-5.4 Add patches for linux-5.4. The patches are from NXP LSDK-20.04 release which was tagged LSDK-20.04-V5.4. https://source.codeaurora.org/external/qoriq/qoriq-components/linux/ For boards LS1021A-IOT, and Traverse-LS1043 which are not involved in LSDK, port the dts patches from 4.14. The patches are sorted into the following categories: 301-arch-xxxx 302-dts-xxxx 303-core-xxxx 701-net-xxxx 801-audio-xxxx 802-can-xxxx 803-clock-xxxx 804-crypto-xxxx 805-display-xxxx 806-dma-xxxx 807-gpio-xxxx 808-i2c-xxxx 809-jailhouse-xxxx 810-keys-xxxx 811-kvm-xxxx 812-pcie-xxxx 813-pm-xxxx 814-qe-xxxx 815-sata-xxxx 816-sdhc-xxxx 817-spi-xxxx 818-thermal-xxxx 819-uart-xxxx 820-usb-xxxx 821-vfio-xxxx Signed-off-by: Yangbo Lu --- ...link-interface-for-APP-layer-to-config-TS.patch | 5113 ++++++++++++++++++++ 1 file changed, 5113 insertions(+) create mode 100644 target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch (limited to 'target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch') diff --git a/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch b/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch new file mode 100644 index 0000000000..33de677930 --- /dev/null +++ b/target/linux/layerscape/patches-5.4/701-net-0334-net-tsn-netlink-interface-for-APP-layer-to-config-TS.patch @@ -0,0 +1,5113 @@ +From e478ab518612f1a821968e1bb5b08b01b10085b0 Mon Sep 17 00:00:00 2001 +From: Po Liu +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 +--- + 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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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"); -- cgit v1.2.3