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