summaryrefslogtreecommitdiff
path: root/net/ethtool/ts.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ethtool/ts.c')
-rw-r--r--net/ethtool/ts.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/net/ethtool/ts.c b/net/ethtool/ts.c
index bd219512b8de..357265e74e08 100644
--- a/net/ethtool/ts.c
+++ b/net/ethtool/ts.c
@@ -59,6 +59,102 @@ static int ts_fill_reply(struct sk_buff *skb,
return nla_put_u32(skb, ETHTOOL_A_TS_LAYER, data->ts_layer);
}
+/* TS_SET */
+const struct nla_policy ethnl_ts_set_policy[] = {
+ [ETHTOOL_A_TS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_TS_LAYER] = NLA_POLICY_RANGE(NLA_U32, 0,
+ __TIMESTAMPING_COUNT - 1)
+};
+
+static int ethnl_set_ts_validate(struct ethnl_req_info *req_info,
+ struct genl_info *info)
+{
+ struct nlattr **tb = info->attrs;
+ const struct net_device_ops *ops = req_info->dev->netdev_ops;
+
+ if (!ops->ndo_hwtstamp_set)
+ return -EOPNOTSUPP;
+
+ if (!tb[ETHTOOL_A_TS_LAYER])
+ return 0;
+
+ return 1;
+}
+
+static int ethnl_set_ts(struct ethnl_req_info *req_info, struct genl_info *info)
+{
+ struct net_device *dev = req_info->dev;
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct kernel_hwtstamp_config config = {0};
+ struct nlattr **tb = info->attrs;
+ enum timestamping_layer ts_layer;
+ bool mod = false;
+ int ret;
+
+ ts_layer = dev->ts_layer;
+ ethnl_update_u32(&ts_layer, tb[ETHTOOL_A_TS_LAYER], &mod);
+
+ if (!mod)
+ return 0;
+
+ if (ts_layer == SOFTWARE_TIMESTAMPING) {
+ struct ethtool_ts_info ts_info = {0};
+
+ if (!ops->get_ts_info) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_TS_LAYER],
+ "this net device cannot support timestamping");
+ return -EINVAL;
+ }
+
+ ops->get_ts_info(dev, &ts_info);
+ if ((ts_info.so_timestamping &
+ SOF_TIMESTAMPING_SOFTWARE_MASK) !=
+ SOF_TIMESTAMPING_SOFTWARE_MASK) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_TS_LAYER],
+ "this net device cannot support software timestamping");
+ return -EINVAL;
+ }
+ } else if (ts_layer == MAC_TIMESTAMPING) {
+ struct ethtool_ts_info ts_info = {0};
+
+ if (!ops->get_ts_info) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_TS_LAYER],
+ "this net device cannot support timestamping");
+ return -EINVAL;
+ }
+
+ ops->get_ts_info(dev, &ts_info);
+ if ((ts_info.so_timestamping &
+ SOF_TIMESTAMPING_HARDWARE_MASK) !=
+ SOF_TIMESTAMPING_HARDWARE_MASK) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_TS_LAYER],
+ "this net device cannot support hardware timestamping");
+ return -EINVAL;
+ }
+ } else if (ts_layer == PHY_TIMESTAMPING && !phy_has_tsinfo(dev->phydev)) {
+ NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_TS_LAYER],
+ "this phy device cannot support timestamping");
+ return -EINVAL;
+ }
+
+ /* Disable time stamping in the current layer. */
+ if (netif_device_present(dev) &&
+ (dev->ts_layer == PHY_TIMESTAMPING ||
+ dev->ts_layer == MAC_TIMESTAMPING)) {
+ ret = dev_set_hwtstamp_phylib(dev, &config, info->extack);
+ if (ret < 0)
+ return ret;
+ }
+
+ dev->ts_layer = ts_layer;
+
+ return 1;
+}
+
const struct ethnl_request_ops ethnl_ts_request_ops = {
.request_cmd = ETHTOOL_MSG_TS_GET,
.reply_cmd = ETHTOOL_MSG_TS_GET_REPLY,
@@ -69,6 +165,9 @@ const struct ethnl_request_ops ethnl_ts_request_ops = {
.prepare_data = ts_prepare_data,
.reply_size = ts_reply_size,
.fill_reply = ts_fill_reply,
+
+ .set_validate = ethnl_set_ts_validate,
+ .set = ethnl_set_ts,
};
/* TS_LIST_GET */