summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile3
-rw-r--r--net/bluetooth/Kconfig1
-rw-r--r--net/bluetooth/hci_core.c23
-rw-r--r--net/bluetooth/hci_event.c39
-rw-r--r--net/bluetooth/hci_request.c8
-rw-r--r--net/bluetooth/mgmt.c69
-rw-r--r--net/core/skbuff.c26
-rw-r--r--net/dsa/Kconfig7
-rw-r--r--net/dsa/Makefile1
-rw-r--r--net/dsa/dsa2.c4
-rw-r--r--net/dsa/port.c1
-rw-r--r--net/dsa/tag_ocelot.c241
-rw-r--r--net/ethernet/eth.c25
-rw-r--r--net/sched/sch_generic.c7
-rw-r--r--net/tsn/Kconfig15
-rw-r--r--net/tsn/Makefile1
-rw-r--r--net/tsn/genl_tsn.c3696
-rw-r--r--net/wireless/chan.c3
-rw-r--r--net/wireless/db.txt1305
-rw-r--r--net/wireless/reg.c1
-rw-r--r--net/wireless/util.c51
22 files changed, 5517 insertions, 11 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 0b2fecc83452..e0879c6b2ec4 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -231,6 +231,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"
diff --git a/net/Makefile b/net/Makefile
index 449fc0b221f8..40e08db63e81 100644
--- 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/
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 3803135c88ff..340f169f6d54 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -64,7 +64,6 @@ source "net/bluetooth/hidp/Kconfig"
config BT_HS
bool "Bluetooth High Speed (HS) features"
depends on BT_BREDR
- default y
help
Bluetooth High Speed includes support for off-loading
Bluetooth connections via 802.11 (wifi) physical layer
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2edaa601df13..a0f88081d56e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -603,6 +603,9 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[8] & 0x01)
hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
+ if (hdev->commands[18] & 0x02)
+ hci_req_add(req, HCI_OP_READ_DEF_ERR_DATA_REPORTING, 0, NULL);
+
/* Some older Broadcom based Bluetooth 1.2 controllers do not
* support the Read Page Scan Type command. Check support for
* this command in the bit mask of supported commands.
@@ -838,6 +841,26 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
sizeof(support), &support);
}
+ /* Set erroneous data reporting if supported to the wideband speech
+ * setting value
+ */
+ if (hdev->commands[18] & 0x04) {
+ bool enabled = hci_dev_test_flag(hdev,
+ HCI_WIDEBAND_SPEECH_ENABLED);
+
+ if (enabled !=
+ (hdev->err_data_reporting == ERR_DATA_REPORTING_ENABLED)) {
+ struct hci_cp_write_def_err_data_reporting cp;
+
+ cp.err_data_reporting = enabled ?
+ ERR_DATA_REPORTING_ENABLED :
+ ERR_DATA_REPORTING_DISABLED;
+
+ hci_req_add(req, HCI_OP_WRITE_DEF_ERR_DATA_REPORTING,
+ sizeof(cp), &cp);
+ }
+ }
+
/* Set Suggested Default Data Length to maximum if supported */
if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) {
struct hci_cp_le_write_def_data_len cp;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index ff6625493c9f..406c6b4eba82 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -916,6 +916,37 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
hdev->inq_tx_power = rp->tx_power;
}
+static void hci_cc_read_def_err_data_reporting(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_def_err_data_reporting *rp = (void *)skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->err_data_reporting = rp->err_data_reporting;
+}
+
+static void hci_cc_write_def_err_data_reporting(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *)skb->data);
+ struct hci_cp_write_def_err_data_reporting *cp;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_ERR_DATA_REPORTING);
+ if (!cp)
+ return;
+
+ hdev->err_data_reporting = cp->err_data_reporting;
+}
+
static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_pin_code_reply *rp = (void *) skb->data;
@@ -3294,6 +3325,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
hci_cc_read_inq_rsp_tx_power(hdev, skb);
break;
+ case HCI_OP_READ_DEF_ERR_DATA_REPORTING:
+ hci_cc_read_def_err_data_reporting(hdev, skb);
+ break;
+
+ case HCI_OP_WRITE_DEF_ERR_DATA_REPORTING:
+ hci_cc_write_def_err_data_reporting(hdev, skb);
+ break;
+
case HCI_OP_PIN_CODE_REPLY:
hci_cc_pin_code_reply(hdev, skb);
break;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 7f3f4ea56d44..a647e18b5f27 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -908,9 +908,9 @@ static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
{
struct adv_info *adv_instance;
- /* Ignore instance 0 */
+ /* Instance 0x00 always set local name */
if (instance == 0x00)
- return 0;
+ return 1;
adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
@@ -927,9 +927,9 @@ static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
u8 instance = hdev->cur_adv_instance;
struct adv_info *adv_instance;
- /* Ignore instance 0 */
+ /* Instance 0x00 always set local name */
if (instance == 0x00)
- return 0;
+ return 1;
adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0ae5d3cab4dc..070d9e7a7d4a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -106,6 +106,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_START_LIMITED_DISCOVERY,
MGMT_OP_READ_EXT_INFO,
MGMT_OP_SET_APPEARANCE,
+ MGMT_OP_SET_WIDEBAND_SPEECH,
};
static const u16 mgmt_events[] = {
@@ -765,6 +766,10 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (lmp_sc_capable(hdev))
settings |= MGMT_SETTING_SECURE_CONN;
+
+ if (test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
+ &hdev->quirks))
+ settings |= MGMT_SETTING_WIDEBAND_SPEECH;
}
if (lmp_le_capable(hdev)) {
@@ -849,6 +854,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_STATIC_ADDRESS;
}
+ if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED))
+ settings |= MGMT_SETTING_WIDEBAND_SPEECH;
+
return settings;
}
@@ -1626,6 +1634,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!IS_ENABLED(CONFIG_BT_HS))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
status = mgmt_bredr_support(hdev);
if (status)
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
@@ -3539,6 +3551,62 @@ unlock:
return err;
}
+static int set_wideband_speech(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_mode *cp = data;
+ int err;
+ bool changed = false;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (!test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks))
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (cp->val != 0x00 && cp->val != 0x01)
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ if (pending_find(MGMT_OP_SET_WIDEBAND_SPEECH, hdev)) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ if (hdev_is_powered(hdev) &&
+ !!cp->val != hci_dev_test_flag(hdev,
+ HCI_WIDEBAND_SPEECH_ENABLED)) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_STATUS_REJECTED);
+ goto unlock;
+ }
+
+ if (cp->val)
+ changed = !hci_dev_test_and_set_flag(hdev,
+ HCI_WIDEBAND_SPEECH_ENABLED);
+ else
+ changed = hci_dev_test_and_clear_flag(hdev,
+ HCI_WIDEBAND_SPEECH_ENABLED);
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_WIDEBAND_SPEECH, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@@ -6925,6 +6993,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ set_appearance, MGMT_SET_APPEARANCE_SIZE },
{ get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE },
{ set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE },
+ { set_wideband_speech, MGMT_SETTING_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5bdb3cd20d61..9d5be649ec8f 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -928,6 +928,32 @@ void napi_consume_skb(struct sk_buff *skb, int budget)
}
EXPORT_SYMBOL(napi_consume_skb);
+/**
+ * skb_recycle - clean up an skb for reuse
+ * @skb: buffer
+ *
+ * Recycles the skb to be reused as a receive buffer. This
+ * function does any necessary reference count dropping, and
+ * cleans up the skbuff as if it just came from __alloc_skb().
+ */
+void skb_recycle(struct sk_buff *skb)
+{
+ struct skb_shared_info *shinfo;
+ u8 head_frag = skb->head_frag;
+
+ skb_release_head_state(skb);
+
+ shinfo = skb_shinfo(skb);
+ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
+ atomic_set(&shinfo->dataref, 1);
+
+ memset(skb, 0, offsetof(struct sk_buff, tail));
+ skb->data = skb->head + NET_SKB_PAD;
+ skb->head_frag = head_frag;
+ skb_reset_tail_pointer(skb);
+}
+EXPORT_SYMBOL(skb_recycle);
+
/* Make sure a field is enclosed inside headers_start/headers_end section */
#define CHECK_SKB_FIELD(field) \
BUILD_BUG_ON(offsetof(struct sk_buff, field) < \
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 7dce11ab2806..13096d79a0ac 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -80,6 +80,13 @@ config NET_DSA_TAG_KSZ
Say Y if you want to enable support for tagging frames for the
Microchip 8795/9477/9893 families of switches.
+config NET_DSA_TAG_OCELOT
+ tristate "Tag driver for Ocelot family of switches"
+ select PACKING
+ help
+ Say Y or M if you want to enable support for tagging frames for the
+ Ocelot switches (VSC7511, VSC7512, VSC7513, VSC7514, VSC9959).
+
config NET_DSA_TAG_QCA
tristate "Tag driver for Qualcomm Atheros QCA8K switches"
help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 2c6d286f0511..9a482c38bdb1 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
+obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 1f27641f9cc0..0ede16508d5c 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -323,6 +323,10 @@ static int dsa_port_setup(struct dsa_port *dp)
if (err)
break;
+ /* Enable TSN function on switch port */
+ if (ds->ops->port_tsn_enable)
+ ds->ops->port_tsn_enable(dp);
+
devlink_port_type_eth_set(dlp, dp->slave);
break;
}
diff --git a/net/dsa/port.c b/net/dsa/port.c
index fa023af69bc4..057552581969 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -625,6 +625,7 @@ static int dsa_port_phylink_register(struct dsa_port *dp)
dp->pl_config.dev = ds->dev;
dp->pl_config.type = PHYLINK_DEV;
+ dp->pl_config.pcs_poll = ds->pcs_poll;
dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
mode, &dsa_port_phylink_mac_ops);
diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
new file mode 100644
index 000000000000..8e3e7283d430
--- /dev/null
+++ b/net/dsa/tag_ocelot.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2019 NXP Semiconductors
+ */
+#include <soc/mscc/ocelot.h>
+#include <linux/packing.h>
+#include "dsa_priv.h"
+
+/* The CPU injection header and the CPU extraction header can have 3 types of
+ * prefixes: long, short and no prefix. The format of the header itself is the
+ * same in all 3 cases.
+ *
+ * Extraction with long prefix:
+ *
+ * +-------------------+-------------------+------+------+------------+-------+
+ * | ff:ff:ff:ff:ff:ff | ff:ff:ff:ff:ff:ff | 8880 | 000a | extraction | frame |
+ * | | | | | header | |
+ * +-------------------+-------------------+------+------+------------+-------+
+ * 48 bits 48 bits 16 bits 16 bits 128 bits
+ *
+ * Extraction with short prefix:
+ *
+ * +------+------+------------+-------+
+ * | 8880 | 000a | extraction | frame |
+ * | | | header | |
+ * +------+------+------------+-------+
+ * 16 bits 16 bits 128 bits
+ *
+ * Extraction with no prefix:
+ *
+ * +------------+-------+
+ * | extraction | frame |
+ * | header | |
+ * +------------+-------+
+ * 128 bits
+ *
+ *
+ * Injection with long prefix:
+ *
+ * +-------------------+-------------------+------+------+------------+-------+
+ * | any dmac | any smac | 8880 | 000a | injection | frame |
+ * | | | | | header | |
+ * +-------------------+-------------------+------+------+------------+-------+
+ * 48 bits 48 bits 16 bits 16 bits 128 bits
+ *
+ * Injection with short prefix:
+ *
+ * +------+------+------------+-------+
+ * | 8880 | 000a | injection | frame |
+ * | | | header | |
+ * +------+------+------------+-------+
+ * 16 bits 16 bits 128 bits
+ *
+ * Injection with no prefix:
+ *
+ * +------------+-------+
+ * | injection | frame |
+ * | header | |
+ * +------------+-------+
+ * 128 bits
+ *
+ * The injection header looks like this (network byte order, bit 127
+ * is part of lowest address byte in memory, bit 0 is part of highest
+ * address byte):
+ *
+ * +------+------+------+------+------+------+------+------+
+ * 127:120 |BYPASS| MASQ | MASQ_PORT |REW_OP|REW_OP|
+ * +------+------+------+------+------+------+------+------+
+ * 119:112 | REW_OP |
+ * +------+------+------+------+------+------+------+------+
+ * 111:104 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 103: 96 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 95: 88 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 87: 80 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 79: 72 | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 71: 64 | RSV | DEST |
+ * +------+------+------+------+------+------+------+------+
+ * 63: 56 | DEST |
+ * +------+------+------+------+------+------+------+------+
+ * 55: 48 | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 47: 40 | RSV | SRC_PORT | RSV |TFRM_TIMER|
+ * +------+------+------+------+------+------+------+------+
+ * 39: 32 | TFRM_TIMER | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 31: 24 | RSV | DP | POP_CNT | CPUQ |
+ * +------+------+------+------+------+------+------+------+
+ * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
+ * +------+------+------+------+------+------+------+------+
+ * 15: 8 | PCP | DEI | VID |
+ * +------+------+------+------+------+------+------+------+
+ * 7: 0 | VID |
+ * +------+------+------+------+------+------+------+------+
+ *
+ * And the extraction header looks like this:
+ *
+ * +------+------+------+------+------+------+------+------+
+ * 127:120 | RSV | REW_OP |
+ * +------+------+------+------+------+------+------+------+
+ * 119:112 | REW_OP | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 111:104 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 103: 96 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 95: 88 | REW_VAL |
+ * +------+------+------+------+------+------+------+------+
+ * 87: 80 | REW_VAL | LLEN |
+ * +------+------+------+------+------+------+------+------+
+ * 79: 72 | LLEN | WLEN |
+ * +------+------+------+------+------+------+------+------+
+ * 71: 64 | WLEN | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 63: 56 | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 55: 48 | RSV |
+ * +------+------+------+------+------+------+------+------+
+ * 47: 40 | RSV | SRC_PORT | ACL_ID |
+ * +------+------+------+------+------+------+------+------+
+ * 39: 32 | ACL_ID | RSV | SFLOW_ID |
+ * +------+------+------+------+------+------+------+------+
+ * 31: 24 |ACL_HIT| DP | LRN_FLAGS | CPUQ |
+ * +------+------+------+------+------+------+------+------+
+ * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
+ * +------+------+------+------+------+------+------+------+
+ * 15: 8 | PCP | DEI | VID |
+ * +------+------+------+------+------+------+------+------+
+ * 7: 0 | VID |
+ * +------+------+------+------+------+------+------+------+
+ */
+
+static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(netdev);
+ u64 bypass, dest, src, qos_class, rew_op;
+ struct dsa_switch *ds = dp->ds;
+ int port = dp->index;
+ struct ocelot *ocelot = ds->priv;
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+ u8 *injection;
+
+ if (unlikely(skb_cow_head(skb, OCELOT_TAG_LEN) < 0)) {
+ netdev_err(netdev, "Cannot make room for tag.\n");
+ return NULL;
+ }
+
+ injection = skb_push(skb, OCELOT_TAG_LEN);
+
+ memset(injection, 0, OCELOT_TAG_LEN);
+
+ src = dsa_upstream_port(ds, port);
+ dest = BIT(port);
+ bypass = true;
+ qos_class = skb->priority;
+
+ packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
+ packing(injection, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0);
+ packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
+ packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0);
+
+ if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+ rew_op = ocelot_port->ptp_cmd;
+ if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
+ rew_op |= (ocelot_port->ts_id % 4) << 3;
+ ocelot_port->ts_id++;
+ }
+
+ packing(injection, &rew_op, 125, 117, OCELOT_TAG_LEN, PACK, 0);
+ }
+
+ return skb;
+}
+
+static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
+ struct net_device *netdev,
+ struct packet_type *pt)
+{
+ u64 src_port, qos_class;
+ u8 *start = skb->data;
+ u8 *extraction;
+
+ /* Revert skb->data by the amount consumed by the DSA master,
+ * so it points to the beginning of the frame.
+ */
+ skb_push(skb, ETH_HLEN);
+ /* We don't care about the long prefix, it is just for easy entrance
+ * into the DSA master's RX filter. Discard it now by moving it into
+ * the headroom.
+ */
+ skb_pull(skb, OCELOT_LONG_PREFIX_LEN);
+ /* And skb->data now points to the extraction frame header.
+ * Keep a pointer to it.
+ */
+ extraction = skb->data;
+ /* Now the EFH is part of the headroom as well */
+ skb_pull(skb, OCELOT_TAG_LEN);
+ /* Reset the pointer to the real MAC header */
+ skb_reset_mac_header(skb);
+ skb_reset_mac_len(skb);
+ /* And move skb->data to the correct location again */
+ skb_pull(skb, ETH_HLEN);
+
+ /* Remove from inet csum the extraction header */
+ skb_postpull_rcsum(skb, start, OCELOT_LONG_PREFIX_LEN + OCELOT_TAG_LEN);
+
+ packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
+ packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
+
+ skb->dev = dsa_master_find_slave(netdev, 0, src_port);
+ if (!skb->dev)
+ /* The switch will reflect back some frames sent through
+ * sockets opened on the bare DSA master. These will come back
+ * with src_port equal to the index of the CPU port, for which
+ * there is no slave registered. So don't print any error
+ * message here (ignore and drop those frames).
+ */
+ return NULL;
+
+ skb->offload_fwd_mark = 1;
+ skb->priority = qos_class;
+
+ return skb;
+}
+
+static struct dsa_device_ops ocelot_netdev_ops = {
+ .name = "ocelot",
+ .proto = DSA_TAG_PROTO_OCELOT,
+ .xmit = ocelot_xmit,
+ .rcv = ocelot_rcv,
+ .overhead = OCELOT_TAG_LEN + OCELOT_LONG_PREFIX_LEN,
+};
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT);
+
+module_dsa_tag_driver(ocelot_netdev_ops);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 9040fe55e0f5..3a078aeb5f96 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -578,8 +578,10 @@ EXPORT_SYMBOL(eth_platform_get_mac_address);
int nvmem_get_mac_address(struct device *dev, void *addrbuf)
{
struct nvmem_cell *cell;
- const void *mac;
+ const unsigned char *mac;
+ unsigned char macaddr[ETH_ALEN];
size_t len;
+ int i = 0;
cell = nvmem_cell_get(dev, "mac-address");
if (IS_ERR(cell))
@@ -591,14 +593,27 @@ int nvmem_get_mac_address(struct device *dev, void *addrbuf)
if (IS_ERR(mac))
return PTR_ERR(mac);
- if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
- kfree(mac);
- return -EINVAL;
+ if (len != ETH_ALEN)
+ goto invalid_addr;
+
+ if (dev->of_node &&
+ of_property_read_bool(dev->of_node, "nvmem_macaddr_swap")) {
+ for (i = 0; i < ETH_ALEN; i++)
+ macaddr[i] = mac[ETH_ALEN - i - 1];
+ } else {
+ ether_addr_copy(macaddr, mac);
}
- ether_addr_copy(addrbuf, mac);
+ if (!is_valid_ether_addr(macaddr))
+ goto invalid_addr;
+
+ ether_addr_copy(addrbuf, macaddr);
kfree(mac);
return 0;
+
+invalid_addr:
+ kfree(mac);
+ return -EINVAL;
}
EXPORT_SYMBOL(nvmem_get_mac_address);
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index ae5847de94c8..a6795e2a2ac7 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -465,6 +465,13 @@ static void dev_watchdog(struct timer_list *t)
txq->trans_timeout++;
break;
}
+
+ /* Devices with HW_ACCEL_MQ have multiple txqs
+ * but update only the first one's transmission
+ * timestamp so avoid checking the rest.
+ */
+ if (dev->features & NETIF_F_HW_ACCEL_MQ)
+ break;
}
if (some_queue_timedout) {
diff --git a/net/tsn/Kconfig b/net/tsn/Kconfig
new file mode 100644
index 000000000000..9f22807a74c9
--- /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.
diff --git a/net/tsn/Makefile b/net/tsn/Makefile
new file mode 100644
index 000000000000..ed46381bf24f
--- /dev/null
+++ b/net/tsn/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TSN) += genl_tsn.o
diff --git a/net/tsn/genl_tsn.c b/net/tsn/genl_tsn.c
new file mode 100644
index 000000000000..1fec19542308
--- /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");
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index fcac5c6366e1..68713164d79f 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -815,7 +815,8 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
- if (!c || c->flags & prohibited_flags)
+ if (!c || ((c->flags & prohibited_flags) &&
+ !(wiphy->flags & WIPHY_FLAG_DFS_OFFLOAD)))
return false;
}
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
new file mode 100644
index 000000000000..0b34e7ab4d0e
--- /dev/null
+++ b/net/wireless/db.txt
@@ -0,0 +1,1305 @@
+# This is the world regulatory domain
+country 00:
+ (2402 - 2472 @ 40), (20)
+ # Channel 12 - 13.
+ (2457 - 2482 @ 20), (20), NO-IR, AUTO-BW
+ # Channel 14. Only JP enables this and for 802.11b only
+ (2474 - 2494 @ 20), (20), NO-IR, NO-OFDM
+ # Channel 36 - 48
+ (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW
+ # Channel 52 - 64
+ (5250 - 5330 @ 80), (20), NO-IR, DFS, AUTO-BW
+ # Channel 100 - 144
+ (5490 - 5730 @ 160), (20), NO-IR, DFS
+ # Channel 149 - 165
+ (5735 - 5835 @ 80), (20), NO-IR
+ # IEEE 802.11ad (60GHz), channels 1..3
+ (57240 - 63720 @ 2160), (0)
+
+
+country AD:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20)
+ (5250 - 5330 @ 80), (20), DFS
+ (5490 - 5710 @ 80), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country AE: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://pucanguilla.org/Downloads/January2005-Anguilla%20Table%20of%20Allocations.pdf
+country AI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20.00), AUTO-BW
+ (5250 - 5330 @ 80), (20.00), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27.00), DFS
+
+country AM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (18)
+ (5250 - 5330 @ 20), (18), DFS
+
+country AN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AS: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country AU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+
+country BA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country BB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country BD: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country BE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country BF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Bulgarian rules as defined by the Communications Regulation Commission in the
+# following documents:
+#
+# Rules for carrying out electronic communications through radio equipment using
+# radio spectrum, which does not need to be individually assigned (the Rules):
+# http://www.crc.bg/files/_bg/Pravila_09_06_2015.pdf
+#
+# List of radio equipment that uses harmonized within the European Union bands
+# and electronic communications terminal equipment (the List):
+# http://www.crc.bg/files/_bg/Spisak_2015.pdf
+#
+# Note: The transmit power limits in the 5250-5350 MHz and 5470-5725 MHz bands
+# can be raised by 3 dBm if TPC is enabled. Refer to BDS EN 301 893 for details.
+country BG: DFS-ETSI
+ # Wideband data transmission systems (WDTS) in the 2.4GHz ISM band, ref:
+ # I.22 of the List, BDS EN 300 328
+ (2402 - 2482 @ 40), (20)
+ # 5 GHz Radio Local Area Networks (RLANs), ref:
+ # II.H01 of the List, BDS EN 301 893
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ # II.H01 of the List, I.54 from the List, BDS EN 301 893
+ (5490 - 5710 @ 160), (27), DFS
+ # Short range devices (SRDs) in the 5725-5875 MHz frequency range, ref:
+ # I.43 of the List, BDS EN 300 440-2, BDS EN 300 440-1
+ (5725 - 5875 @ 80), (14)
+ # 60 GHz Multiple-Gigabit RLAN Systems, ref:
+ # II.H03 of the List, BDS EN 302 567-2
+ (57000 - 66000 @ 2160), (40), NO-OUTDOOR
+
+country BH: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (20)
+ (5250 - 5330 @ 20), (20), DFS
+ (5735 - 5835 @ 20), (20)
+
+country BL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BN: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country BO: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BS: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.bicma.gov.bt/paper/publication/nrrpart4.pdf
+country BT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BZ: DFS-JP
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country CA: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5600 @ 80), (24), DFS
+ (5650 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.art-rca.org
+country CF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (17)
+ (5250 - 5330 @ 40), (24), DFS
+ (5490 - 5730 @ 40), (24), DFS
+ (5735 - 5835 @ 40), (30)
+
+country CH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country CI: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CL: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country CN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+ # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
+ (57240 - 59400 @ 2160), (28)
+ (59400 - 63720 @ 2160), (44)
+ (63720 - 65880 @ 2160), (28)
+
+country CO: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+# http://www.mincom.gob.cu/?q=marcoregulatorio
+# - Redes Informáticas
+# Resolución 127, 2011 - Reglamento Banda 2,4 GHz.
+country CU: DFS-FCC
+ (2400 - 2483.5 @ 40), (200 mW)
+
+country CX: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
+# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
+# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is
+# implemented.
+country CZ: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Allocation for the 2.4 GHz band (Vfg 10 / 2013, Allgemeinzuteilung von
+# Frequenzen für die Nutzung in lokalen Netzwerken; Wireless Local Area
+# Networks (WLAN-Funkanwendungen).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2013_10_WLAN_2,4GHz_pdf.pdf
+#
+# Allocation for the 5 GHz band (Vfg. 7 / 2010, Allgemeinzuteilung von
+# Frequenzen in den Bereichen 5150 MHz - 5350 MHz und 5470 MHz - 5725 MHz für
+# Funkanwendungen zur breitbandigen Datenübertragung, WAS/WLAN („Wireless
+# Access Systems including Wireless Local Area Networks“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2010_07_WLAN_5GHz_pdf.pdf
+# The values for the 5 GHz have been reduced by a factor of 2 (3db) for non TPC
+# devices (in other words: devices with TPC can use twice the tx power of this
+# table). Note that the docs do not require TPC for 5150--5250; the reduction
+# to 100mW thus is not strictly required -- however the conservative 100mW
+# limit is used here as the non-interference with radar and satellite
+# apps relies on the attenuation by the building walls only in the
+# absence of DFS; the neighbour countries have 100mW limit here as well.
+#
+# The ETSI EN 300 440-1 standard for short range devices in the 5 GHz band has
+# been implemented in Germany:
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2014_69_SRD_pdf.pdf
+#
+# Allocation for the 60 GHz band (Allgemeinzuteilung von Frequenzen im
+# Bereich 57 GHz - 66 GHz für Funkanwendungen für weitbandige
+# Datenübertragungssysteme; „Multiple Gigabit WAS/RLAN Systems (MGWS)“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2011_08_MGWS_pdf.pdf
+
+country DE: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (100 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # short range devices (ETSI EN 300 440-1)
+ (5725 - 5875 @ 80), (25 mW)
+ # 60 GHz band channels 1-4 (ETSI EN 302 567)
+ (57000 - 66000 @ 2160), (40)
+
+country DK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Source:
+# http://www.ntrcdom.org/index.php?option=com_content&view=category&layout=blog&id=10&Itemid=55
+country DM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DO: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DZ: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170.000 - 5250.000 @ 80.000), (23.00), AUTO-BW
+ (5250.000 - 5330.000 @ 80.000), (23.00), DFS, AUTO-BW
+ (5490.000 - 5670.000 @ 160.000), (23.00), DFS
+
+country EC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country EE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country EG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+
+# Orden IET/787/2013, de 25 de abril, por la que se aprueba
+# el cuadro nacional de atribución de frecuencias.
+# http://www.boe.es/diario_boe/txt.php?id=BOE-A-2013-4845
+#
+# more info at "Cuadro nacional de atribución de frecuencias (CNAF)":
+# http://www.minetur.gob.es/telecomunicaciones/espectro/paginas/cnaf.aspx
+
+country ES: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country ET: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country FI: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20)
+ (5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country FM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country FR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GB: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GD: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GP: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GT: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country GU: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country GY:
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country HK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HR: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20)
+ (5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country HT: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country ID: DFS-JP
+ # ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
+ (2402 - 2482 @ 20), (20)
+ (5735 - 5815 @ 20), (23)
+
+country IE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country IL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (200 mW), NO-OUTDOOR, DFS, AUTO-BW
+
+country IN:
+ (2402 - 2482 @ 40), (20)
+ (5150 - 5350 @ 160), (23)
+ (5725 - 5875 @ 80), (23)
+
+country IR: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country IS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country IT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country JM: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country JO: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5735 - 5835 @ 80), (23)
+
+country JP: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (2474 - 2494 @ 20), (20), NO-OFDM
+ (4910 - 4990 @ 40), (23)
+ (5030 - 5090 @ 40), (23)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (23), DFS
+ # 60 GHz band channels 2-4 at 10mW,
+ # ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf
+ (59000 - 66000 @ 2160), (10 mW)
+
+country KE: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5490 - 5570 @ 80), (30), DFS
+ (5735 - 5775 @ 40), (23)
+
+country KH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source
+# http://ntrc.kn/?page_id=7
+country KN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country KP: DFS-JP
+ (2402 - 2482 @ 20), (20)
+ (5170 - 5250 @ 20), (20)
+ (5250 - 5330 @ 20), (20), DFS
+ (5490 - 5630 @ 20), (30), DFS
+ (5735 - 5815 @ 20), (30)
+
+country KR: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1-4,
+ # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
+ (57000 - 66000 @ 2160), (43)
+
+country KW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country KY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country KZ:
+ (2402 - 2482 @ 40), (20)
+
+country LB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.ntrc.org.lc/operational_structures.htm
+country LC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country LI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country LK: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+# Source:
+# http://lca.org.ls/images/documents/lesotho_national_frequency_allocation_plan.pdf
+country LS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country LT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country LU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country LV: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country MC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.cnfr.md/index.php?pag=sec&id=117&l=en
+country MD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.cept.org/files/1050/Tools%20and%20Services/EFIS%20-%20ECO%20Frequency%20Information%20System/National%20frequency%20tables/Montenegro%20NAFT%20-%202010.pdf
+country ME: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MH: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MO: DFS-FCC
+ (2402 - 2482 @ 40), (23)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MP: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MQ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.are.mr/pdfs/telec_freq_TNAbf_2010.pdf
+country MR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.cam.gov.mv/docs/tech_standards/TAM-TS-100-2004-WLAN.pdf
+country MV: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), DFS, AUTO-BW
+ (5725 - 5850 @ 80), (100 mW)
+
+country MW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MX: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5650 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (24)
+
+country NG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country NI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Regulation on the use of frequency space without a license and
+# without notification 2015
+#
+# http://wetten.overheid.nl/BWBR0036378/2015-03-05
+
+country NL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+ (5250 - 5330 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # short range devices (ETSI EN 300 440-1)
+ (5725 - 5875 @ 80), (25 mW)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Data from http://www.lovdata.no/dokument/SF/forskrift/2012-01-19-77
+# Power at 5250 - 5350 MHz, 5470 - 5725 MHz and 5815 – 5850 MHz can
+# be doubled if TPC is implemented.
+# Up to 2W (or 4W with TPC) is allowed in the 5725 – 5795 MHz band
+# which has been merged with 5470 - 5725 MHz to allow wide channels
+country NO: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), DFS, AUTO-BW
+ (5470 - 5795 @ 160), (500 mW), DFS
+ (5815 - 5850 @ 35), (2000 mW), DFS
+ (17100 - 17300 @ 200), (100 mW)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country NP: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country NZ: DFS-ETSI
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country OM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PA: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country PE: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PK: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country PL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country PM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PR: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country PW: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country QA: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country RE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country RO: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+
+# Source:
+# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
+country RS: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5350 @ 40), (200 mW), NO-OUTDOOR
+ (5470 - 5725 @ 20), (1000 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country RU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5650 - 5730 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1-4, ref: Changes to NLA 124_Order №129_22042015.pdf
+ (57000 - 66000 @ 2160), (40)
+
+country RW: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country SE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country SG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country SK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Source:
+# Regulation N° 2004-005 ART/DG/DRC/D.Rég
+country SN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country SV: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (23), DFS
+ (5735 - 5835 @ 20), (30)
+
+country SY:
+ (2402 - 2482 @ 40), (20)
+
+# Source:
+# http://www.telecommission.tc/Spectrum-plan20110324-101210.html
+country TC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country TG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+ (5490 - 5710 @ 40), (27), DFS
+
+country TH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country TR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country TT: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# Table of Frequency Allocations of Republic of China (Taiwan) / Nov 2014:
+# http://www.motc.gov.tw/websitedowndoc?file=post/201411171137330.doc& \
+# filedisplay=Table+of+radio+frequency+allocation.doc
+# LP0002 Low-power Radio-frequency Devices Technical Regulations / 28 Jun 2011:
+# http://www.ncc.gov.tw/english/show_file.aspx?table_name=news&file_sn=681
+# (section 3.10.1, 4.7)
+country TW: DFS-FCC
+ (2400 - 2483.5 @ 40), (30)
+ # Follow US 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+ (5150 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5350 @ 80), (23), DFS, AUTO-BW
+ (5470 - 5725 @ 160), (23), DFS
+ (5725 - 5850 @ 80), (30)
+
+country TZ:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
+# #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
+# (appendix 8)
+# Listed 5GHz range is a lowest common denominator for all related
+# rules in the referenced laws. Such a range is used because of
+# disputable definitions there.
+country UA: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20), NO-OUTDOOR
+ (5150 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), DFS, NO-OUTDOOR, AUTO-BW
+ (5490 - 5670 @ 160), (20), DFS
+ (5735 - 5835 @ 80), (20)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country UG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country US: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ # 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (23), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60g band
+ # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
+ # channels 1,2,3, EIRP=40dBm(43dBm peak)
+ (57240 - 63720 @ 2160), (40)
+
+country UY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://cemc.uz/article/1976/
+country UZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+# Source:
+# http://www.ntrc.vc/regulations/Jun_2006_Spectrum_Managment_Regulations.pdf
+country VC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# Official Gazette (Gaceta Oficial) concerning Unlicensed transmitter use
+# (10 June 2013)
+# http://www.conatel.gob.ve/
+country VE: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country VI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country VN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17)
+ (5250 - 5330 @ 80), (24), DFS
+ (5490 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.trr.vu/attachments/category/130/GURL_for_Short-range_Radiocommunication_Devices2.pdf
+country VU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country WF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country WS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+ (5490 - 5710 @ 40), (27), DFS
+
+country YE:
+ (2402 - 2482 @ 40), (20)
+
+country YT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country ZA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30)
+
+country ZW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 0f3b57a73670..2c21921e4127 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2961,6 +2961,7 @@ int regulatory_hint_user(const char *alpha2,
return 0;
}
+EXPORT_SYMBOL(regulatory_hint_user);
int regulatory_hint_indoor(bool is_indoor, u32 portid)
{
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 95533732f9d6..5089c935c369 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -2196,3 +2196,54 @@ bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype,
return false;
}
EXPORT_SYMBOL(cfg80211_iftype_allowed);
+
+bool cfg80211_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb)
+{
+ const struct ethhdr *eth = (void *)skb->data;
+ const struct {
+ struct arphdr hdr;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+ } __packed *arp;
+ const struct ipv6hdr *ipv6;
+ const struct icmp6hdr *icmpv6;
+
+ switch (eth->h_proto) {
+ case cpu_to_be16(ETH_P_ARP):
+ /* can't say - but will probably be dropped later anyway */
+ if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*arp)))
+ return false;
+
+ arp = (void *)(eth + 1);
+
+ if ((arp->hdr.ar_op == cpu_to_be16(ARPOP_REPLY) ||
+ arp->hdr.ar_op == cpu_to_be16(ARPOP_REQUEST)) &&
+ !memcmp(arp->ar_sip, arp->ar_tip, sizeof(arp->ar_sip)))
+ return true;
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ /* can't say - but will probably be dropped later anyway */
+ if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*ipv6) +
+ sizeof(*icmpv6)))
+ return false;
+
+ ipv6 = (void *)(eth + 1);
+ icmpv6 = (void *)(ipv6 + 1);
+
+ if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT &&
+ !memcmp(&ipv6->saddr, &ipv6->daddr, sizeof(ipv6->saddr)))
+ return true;
+ break;
+ default:
+ /*
+ * no need to support other protocols, proxy service isn't
+ * specified for any others
+ */
+ break;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(cfg80211_is_gratuitous_arp_unsolicited_na);