Add the two ops that install a stream classification on a peer, the extended feature flags that advertise them, and a wiphy_register() check that a wiphy does not advertise what it cannot do. One SCS Request frame can carry several descriptors with different request types, so set_scs takes a set and each descriptor carries its own status back. A peer has at most one active MSCS, so set_mscs needs only a return value. Signed-off-by: Felix Fietkau --- include/net/cfg80211.h | 28 +++++++++++++++++++++- include/uapi/linux/nl80211.h | 10 ++++++++- net/wireless/core.c | 10 ++++++++- net/wireless/rdev-ops.h | 32 ++++++++++++++++++++++++- net/wireless/trace.h | 49 +++++++++++++++++++++++++++++++++++++- 5 files changed, 129 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f140c3c2a5b8..73ccb6e3173c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4237,6 +4237,18 @@ struct cfg80211_scs_desc { }; /** + * struct cfg80211_scs_result - what one SCS descriptor is answered with + * + * Parallel to the descriptor array, so a descriptor holds what was asked for + * and never what came of it. + * + * @status: IEEE status code + */ +struct cfg80211_scs_result { + u16 status; +}; + +/** * struct cfg80211_mscs_desc - the MSCS of one peer * * @req_type: add, remove or change @@ -5359,6 +5371,16 @@ struct mgmt_frame_regs { * * @set_qos_map: Set QoS mapping information to the driver * + * @set_scs: Add, change or remove SCS descriptors of one peer. The action + * frame exchange has been handled by userspace, so this just has to make + * the transmit path classify matching MSDUs. The op answers every + * descriptor in the result entry of the same index, and a declined change + * leaves the previously accepted classification of that SCSID in force. + * A negative return fails the request as a whole and answers nothing. + * @set_mscs: Install, change or remove the MSCS of one peer. A peer has at + * most one active MSCS, so the result is a single return value rather + * than a per descriptor status. + * * @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the * given interface This is used e.g. for dynamic HT 20/40 MHz channel width * changes during the lifetime of the BSS. @@ -5763,6 +5785,12 @@ struct cfg80211_ops { struct net_device *dev, struct cfg80211_qos_map *qos_map); + int (*set_scs)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, struct cfg80211_scs_desc * const *desc, + struct cfg80211_scs_result *res, u8 n_desc); + int (*set_mscs)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, struct cfg80211_mscs_desc *desc); + int (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id, struct cfg80211_chan_def *chandef); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 86e1855c8475..f3e085c3c34c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -7127,6 +7127,14 @@ enum nl80211_feature_flags { * * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP * in STA mode using @NL80211_CMD_PROBE_PEER. + * @NL80211_EXT_FEATURE_SCS: Driver or device supports the stream + * classification service, so it classifies transmitted MSDUs against the + * descriptors given with %NL80211_CMD_SET_SCS. Userspace sets the SCS + * field of the Extended Capabilities element only when this is reported. + * @NL80211_EXT_FEATURE_MSCS: Driver or device supports the mirrored stream + * classification service, configured with %NL80211_CMD_SET_MSCS. + * Userspace sets the Mirrored SCS field of the Extended Capabilities + * element only when this is reported. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -7210,6 +7218,8 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, NL80211_EXT_FEATURE_PROBE_AP, + NL80211_EXT_FEATURE_SCS, + NL80211_EXT_FEATURE_MSCS, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/core.c b/net/wireless/core.c index d13310fef691..cc2cff43ef75 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -974,6 +974,16 @@ int wiphy_register(struct wiphy *wiphy) rdev->ops->update_connect_params)) return -EINVAL; + if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SCS) && + !rdev->ops->set_scs)) + return -EINVAL; + + if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_MSCS) && + !rdev->ops->set_mscs)) + return -EINVAL; + if (wiphy->addresses) memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 46849fe8d0b3..1287c3542d93 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1185,6 +1185,38 @@ static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_set_scs(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + struct cfg80211_scs_desc * const *desc, + struct cfg80211_scs_result *res, u8 n_desc) +{ + int ret = -EOPNOTSUPP; + + if (rdev->ops->set_scs) { + trace_rdev_set_scs(&rdev->wiphy, dev, peer, n_desc); + ret = rdev->ops->set_scs(&rdev->wiphy, dev, peer, desc, res, + n_desc); + trace_rdev_return_int(&rdev->wiphy, ret); + } + + return ret; +} + +static inline int rdev_set_mscs(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + struct cfg80211_mscs_desc *desc) +{ + int ret = -EOPNOTSUPP; + + if (rdev->ops->set_mscs) { + trace_rdev_set_mscs(&rdev->wiphy, dev, peer, desc); + ret = rdev->ops->set_mscs(&rdev->wiphy, dev, peer, desc); + trace_rdev_return_int(&rdev->wiphy, ret); + } + + return ret; +} + static inline int rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev, struct net_device *dev, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 8c2a91b85c39..81c0899cb212 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2636,6 +2636,55 @@ TRACE_EVENT(rdev_set_ap_chanwidth, __entry->link_id) ); +TRACE_EVENT(rdev_set_scs, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *peer, u8 n_desc), + TP_ARGS(wiphy, netdev, peer, n_desc), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(u8, n_desc) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->n_desc = n_desc; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %pM, %d descriptors", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->peer, __entry->n_desc) +); + +TRACE_EVENT(rdev_set_mscs, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *peer, const struct cfg80211_mscs_desc *desc), + TP_ARGS(wiphy, netdev, peer, desc), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(u32, fields) + __field(u32, stream_timeout) + __field(u8, up_bitmap) + __field(u8, up_limit) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->fields = desc ? desc->fields : 0; + __entry->stream_timeout = desc ? desc->stream_timeout : 0; + __entry->up_bitmap = desc ? desc->up_bitmap : 0; + __entry->up_limit = desc ? desc->up_limit : 0; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT + ", %pM, fields %x, UP bitmap %x, UP limit %d, timeout %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->peer, __entry->fields, + __entry->up_bitmap, __entry->up_limit, + __entry->stream_timeout) +); + TRACE_EVENT(rdev_add_tx_ts, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 tsid, const u8 *peer, u8 user_prio, u16 admitted_time), -- git-series 0.9.1