diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 897dbe325b7e..8f010af419bf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5088,6 +5088,9 @@ struct mgmt_frame_regs { * links by calling cfg80211_mlo_reconf_add_done(). When calling * cfg80211_mlo_reconf_add_done() the bss pointer must be given for each * link for which MLO reconfiguration 'add' operation was requested. + * + * @start_pd: Start the PD interface. + * @stop_pd: Stop the PD interface. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -5464,6 +5467,8 @@ struct cfg80211_ops { struct cfg80211_ml_reconf_req *req); int (*set_epcs)(struct wiphy *wiphy, struct net_device *dev, bool val); + int (*start_pd)(struct wiphy *wiphy, struct wireless_dev *wdev); + void (*stop_pd)(struct wiphy *wiphy, struct wireless_dev *wdev); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 69e13cd7978a..704607824b8a 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1419,6 +1419,12 @@ * identifying the evacuated channel. * User space may reconfigure the local schedule in response to this * notification. + * @NL80211_CMD_START_PD: Start PD operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been previously + * created with %NL80211_CMD_NEW_INTERFACE. + * @NL80211_CMD_STOP_PD: Stop the PD operation, identified by + * its %NL80211_ATTR_WDEV interface. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1694,6 +1700,9 @@ enum nl80211_commands { NL80211_CMD_NAN_CHANNEL_EVAC, + NL80211_CMD_START_PD, + NL80211_CMD_STOP_PD, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 9a6f8ffe7ba9..4dd1981a3629 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -322,6 +322,28 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, return 0; } +void cfg80211_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + lockdep_assert_held(&rdev->wiphy.mtx); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_PD)) + return; + + if (!rdev->ops->stop_pd) + return; + + if (!wdev_running(wdev)) + return; + + cfg80211_pmsr_wdev_down(wdev); + + rdev_stop_pd(rdev, wdev); + wdev->is_running = false; + + rdev->opencount--; +} + void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -351,6 +373,9 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; default: break; } @@ -846,6 +871,9 @@ int wiphy_register(struct wiphy *wiphy) (!rdev->ops->tdls_channel_switch || !rdev->ops->tdls_cancel_channel_switch))) return -EINVAL; + if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_PD)) && + (!rdev->ops->start_pd || !rdev->ops->stop_pd))) + return -EINVAL; if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) && (!rdev->ops->start_nan || !rdev->ops->stop_nan || @@ -1408,6 +1436,9 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; default: break; } @@ -1516,10 +1547,12 @@ void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_NAN_DATA: - case NL80211_IFTYPE_PD: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: diff --git a/net/wireless/core.h b/net/wireless/core.h index 0e6f9bd4ba1b..df47ed6208a5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -557,6 +557,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); +void cfg80211_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 85096c8964ff..8e20fb714eff 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -16638,6 +16638,46 @@ static int nl80211_nan_change_config(struct sk_buff *skb, return rdev_nan_change_conf(rdev, wdev, &conf, changed); } +static int nl80211_start_pd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + int err; + + if (wdev->iftype != NL80211_IFTYPE_PD) + return -EOPNOTSUPP; + + if (wdev_running(wdev)) + return -EEXIST; + + if (rfkill_blocked(rdev->wiphy.rfkill)) + return -ERFKILL; + + if (!rdev->ops->start_pd) + return -EOPNOTSUPP; + + err = rdev_start_pd(rdev, wdev); + if (err) + return err; + wdev->is_running = true; + rdev->opencount++; + + return 0; +} + +static int nl80211_stop_pd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + + if (wdev->iftype != NL80211_IFTYPE_PD) + return -EOPNOTSUPP; + + cfg80211_stop_pd(rdev, wdev); + + return 0; +} + void cfg80211_nan_match(struct wireless_dev *wdev, struct cfg80211_nan_match_params *match, gfp_t gfp) { @@ -19805,6 +19845,20 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), }, + { + .cmd = NL80211_CMD_START_PD, + .doit = nl80211_start_pd, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | + NL80211_FLAG_NEED_RTNL), + }, + { + .cmd = NL80211_CMD_STOP_PD, + .doit = nl80211_stop_pd, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL), + }, { .cmd = NL80211_CMD_SET_MCAST_RATE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index d97d5c08d135..63c26e8b1139 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1093,6 +1093,25 @@ rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_start_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int ret; + + trace_rdev_start_pd(&rdev->wiphy, wdev); + ret = rdev->ops->start_pd(&rdev->wiphy, wdev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + trace_rdev_stop_pd(&rdev->wiphy, wdev); + rdev->ops->stop_pd(&rdev->wiphy, wdev); + trace_rdev_return_void(&rdev->wiphy); +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 938fea1fe9d8..a68d356fe127 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2375,6 +2375,16 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_nan, TP_ARGS(wiphy, wdev) ); +DEFINE_EVENT(wiphy_wdev_evt, rdev_start_pd, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_pd, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + TRACE_EVENT(rdev_add_nan_func, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, const struct cfg80211_nan_func *func),