1 // SPDX-License-Identifier: GPL-2.0-only
7 struct linkstate_req_info
{
8 struct ethnl_req_info base
;
11 struct linkstate_reply_data
{
12 struct ethnl_reply_data base
;
16 bool link_ext_state_provided
;
17 struct ethtool_link_ext_state_info ethtool_link_ext_state_info
;
20 #define LINKSTATE_REPDATA(__reply_base) \
21 container_of(__reply_base, struct linkstate_reply_data, base)
23 const struct nla_policy ethnl_linkstate_get_policy
[] = {
24 [ETHTOOL_A_LINKSTATE_HEADER
] =
25 NLA_POLICY_NESTED(ethnl_header_policy
),
28 static int linkstate_get_sqi(struct net_device
*dev
)
30 struct phy_device
*phydev
= dev
->phydev
;
36 mutex_lock(&phydev
->lock
);
37 if (!phydev
->drv
|| !phydev
->drv
->get_sqi
)
40 ret
= phydev
->drv
->get_sqi(phydev
);
41 mutex_unlock(&phydev
->lock
);
46 static int linkstate_get_sqi_max(struct net_device
*dev
)
48 struct phy_device
*phydev
= dev
->phydev
;
54 mutex_lock(&phydev
->lock
);
55 if (!phydev
->drv
|| !phydev
->drv
->get_sqi_max
)
58 ret
= phydev
->drv
->get_sqi_max(phydev
);
59 mutex_unlock(&phydev
->lock
);
64 static int linkstate_get_link_ext_state(struct net_device
*dev
,
65 struct linkstate_reply_data
*data
)
69 if (!dev
->ethtool_ops
->get_link_ext_state
)
72 err
= dev
->ethtool_ops
->get_link_ext_state(dev
, &data
->ethtool_link_ext_state_info
);
76 data
->link_ext_state_provided
= true;
81 static int linkstate_prepare_data(const struct ethnl_req_info
*req_base
,
82 struct ethnl_reply_data
*reply_base
,
83 struct genl_info
*info
)
85 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
86 struct net_device
*dev
= reply_base
->dev
;
89 ret
= ethnl_ops_begin(dev
);
92 data
->link
= __ethtool_get_link(dev
);
94 ret
= linkstate_get_sqi(dev
);
95 if (ret
< 0 && ret
!= -EOPNOTSUPP
)
99 ret
= linkstate_get_sqi_max(dev
);
100 if (ret
< 0 && ret
!= -EOPNOTSUPP
)
104 if (dev
->flags
& IFF_UP
) {
105 ret
= linkstate_get_link_ext_state(dev
, data
);
106 if (ret
< 0 && ret
!= -EOPNOTSUPP
&& ret
!= -ENODATA
)
112 ethnl_ops_complete(dev
);
116 static int linkstate_reply_size(const struct ethnl_req_info
*req_base
,
117 const struct ethnl_reply_data
*reply_base
)
119 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
122 len
= nla_total_size(sizeof(u8
)) /* LINKSTATE_LINK */
125 if (data
->sqi
!= -EOPNOTSUPP
)
126 len
+= nla_total_size(sizeof(u32
));
128 if (data
->sqi_max
!= -EOPNOTSUPP
)
129 len
+= nla_total_size(sizeof(u32
));
131 if (data
->link_ext_state_provided
)
132 len
+= nla_total_size(sizeof(u8
)); /* LINKSTATE_EXT_STATE */
134 if (data
->ethtool_link_ext_state_info
.__link_ext_substate
)
135 len
+= nla_total_size(sizeof(u8
)); /* LINKSTATE_EXT_SUBSTATE */
140 static int linkstate_fill_reply(struct sk_buff
*skb
,
141 const struct ethnl_req_info
*req_base
,
142 const struct ethnl_reply_data
*reply_base
)
144 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
146 if (data
->link
>= 0 &&
147 nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_LINK
, !!data
->link
))
150 if (data
->sqi
!= -EOPNOTSUPP
&&
151 nla_put_u32(skb
, ETHTOOL_A_LINKSTATE_SQI
, data
->sqi
))
154 if (data
->sqi_max
!= -EOPNOTSUPP
&&
155 nla_put_u32(skb
, ETHTOOL_A_LINKSTATE_SQI_MAX
, data
->sqi_max
))
158 if (data
->link_ext_state_provided
) {
159 if (nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_EXT_STATE
,
160 data
->ethtool_link_ext_state_info
.link_ext_state
))
163 if (data
->ethtool_link_ext_state_info
.__link_ext_substate
&&
164 nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE
,
165 data
->ethtool_link_ext_state_info
.__link_ext_substate
))
172 const struct ethnl_request_ops ethnl_linkstate_request_ops
= {
173 .request_cmd
= ETHTOOL_MSG_LINKSTATE_GET
,
174 .reply_cmd
= ETHTOOL_MSG_LINKSTATE_GET_REPLY
,
175 .hdr_attr
= ETHTOOL_A_LINKSTATE_HEADER
,
176 .req_info_size
= sizeof(struct linkstate_req_info
),
177 .reply_data_size
= sizeof(struct linkstate_reply_data
),
179 .prepare_data
= linkstate_prepare_data
,
180 .reply_size
= linkstate_reply_size
,
181 .fill_reply
= linkstate_fill_reply
,