Return-path: Received: from wolverine01.qualcomm.com ([199.106.114.254]:19318 "EHLO wolverine01.qualcomm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750921Ab3CJPn7 (ORCPT ); Sun, 10 Mar 2013 11:43:59 -0400 From: Vladimir Kondratiev To: Johannes Berg CC: "Luis R . Rodriguez" , Jouni Malinen , "John W . Linville" , Subject: Re: [RFC] P2P find offload Date: Sun, 10 Mar 2013 17:43:55 +0200 Message-ID: <2799886.P5v870j3h2@lx-vladimir> (sfid-20130310_164405_726113_E5588312) In-Reply-To: <1628850.y8u4o7KRXH@lx-vladimir> References: <3408094.SIuA27EmQ5@lx-vladimir> <1362650938.8694.21.camel@jlt4.sipsolutions.net> <1628850.y8u4o7KRXH@lx-vladimir> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Sender: linux-wireless-owner@vger.kernel.org List-ID: On Thursday, March 07, 2013 04:10:37 PM Vladimir Kondratiev wrote: > On Thursday, March 07, 2013 11:08:58 AM Johannes Berg wrote: > > > + int n_channels; > > > + /* Up to 4 social channels: 3 in 2.4GHz band + 1 in 60GHz band */ > > > + struct ieee80211_channel *channels[4]; > > > > Hmm, is 4 really a good limit? Allocated dynamically > > > + * start_p2p_find: start P2P find phase > > > + * Parameters include IEs for probe/probe resp and channels to operate on > > > > The parameters? Documented > > > + i = 0; > > > + if (attr_freq) { > > > > i can be inside the if()? Moved to initializer > > > > > > + /* user specified, bail out if channel not found */ > > > + nla_for_each_nested(attr, attr_freq, tmp) { > > > + struct ieee80211_channel *chan; > > > + > > > + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); > > > + > > > + if (!chan) > > > + return -EINVAL; > > > + > > > + /* ignore disabled channels */ > > > + if (chan->flags & IEEE80211_CHAN_DISABLED) > > > + continue; > > > > I think you should reject them, also if they have passive scan or > > various other flags set, I think? > Passive channel may be used as social for the listening; and may be > actively scanned if beacon found on the channel. Scan do not filter > based on these flags, I suppose same reason apply here. > > > > > > + params.channels[i] = chan; > > > + i++; > > > + } > > > + if (!i) > > > + return -EINVAL; > > > + } > > > + > > > + params.n_channels = i; > > > > if you also move that assignment > > > > > + i = 0; > > > > Not needed. Sure, done > > > > > + attr = info->attrs[NL80211_ATTR_IE]; > > > + if (attr) { > > > > This is not typically done in nl80211, I'd prefer not having the > > variable I think. > It is to reasonable fit code into 80 columns. Let me know if this > argument don't play for you, I'll revert to variables. > > > > > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); > > > + if (!msg) > > > + return -ENOMEM; > > > + > > > + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, > > > + NL80211_CMD_START_P2P_FIND); > > > > Err? What's the value of sending a reply back here? It would seem maybe > > appropriate to send one when it *actually* started, but you haven't > > implemented that. > > > > > + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, > > > + NL80211_CMD_STOP_P2P_FIND); > > > > same here ... Removed; notification function added > > > > > You're also entirely missing feature advertising, so userspace can only > > guess whether it's supported or not ... Done > > >Also ... > > > > I really don't think this should be supported on IBSS/AP/... netdevs. > > Seems like the only reasonable ones are P2P_DEVICE and STATION, although > > it would probably be good to have feature advertising for both, or > > document that if P2P_DEVICE is supported at all then this doesn't have > > to be supported on STATION interfaces, or so. Done > > > > And then ... should this really be allowed to be concurrent with > > scanning/remain-on-channel? You haven't done any checking or > > documentation, so users and driver authors are left to guess. Documented I added relevant parameters for the find phase timing, accordingly to the spec. And finally, rebased to recent wireless-testing. >From c7e67726b042f6ed490858b02a6d1961be22bfa4 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 10 Mar 2013 15:52:37 +0200 Subject: [PATCH v2] cfg80211: P2P find phase offload Allow to implement P2P find phase in the driver/firmware. Offload scheme designed as follows: - Driver provide methods start_p2p_find and stop_p2p_find in the cfg80211_ops; indicates support for p2p find offload by setting feature NL80211_FEATURE_P2P_FIND_OFFLOAD - Driver indicate firmware or driver responds to the probe requests by setting feature NL80211_FEATURE_P2P_PROBE_RESP_OFFLOAD - wpa_supplicant analyses feature flags to discover p2p find offload support; to perform p2p scan, wpa_supplicant: - perform legacy scan, through driver's cfg80211_ops 'scan' method - configure rx management filter to get probe-request and probe-response frames - start p2p find via driver's cfg80211_ops start_p2p_find method - driver start p2p find with hardware and notify wpa_supplicant with cfg80211_p2p_find_notify(NL80211_CMD_START_P2P_FIND) - driver/firmware toggle search/listen states. Received probe-request and probe-response frames passed to the wpa_supplicant via cfg80211_rx_mgmt - when wpa_supplicant wants to stop p2p find, it calls driver's cfg80211_ops stop_p2p_find method. Alternatively, driver/firmware may decide to stop p2p find. In all cases, driver notifies wpa_supplicant using cfg80211_p2p_find_notify(NL80211_CMD_STOP_P2P_FIND) All driver to user space communication done through nl80211 layer. Signed-off-by: Vladimir Kondratiev --- include/net/cfg80211.h | 53 ++++++++++++++ include/uapi/linux/nl80211.h | 16 +++++ net/wireless/nl80211.c | 157 ++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 19 +++++ net/wireless/trace.h | 35 ++++++++++ 5 files changed, 280 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bdba9b6..ef276f6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1780,6 +1780,30 @@ struct cfg80211_update_ft_ies_params { }; /** + * struct cfg80211_p2p_find_params - parameters for P2P find + * @probe_ie: IE's for probe frames + * @probe_ie_len: length, bytes, of @probe_ie + * @probe_resp_ie: IE's for probe response frames + * @probe_resp_ie_len: length, bytes, of @probe_resp_ie + * @min_discoverable_interval and + * @max_discoverable_interval: min/max for random multiplier of 100TU's + * for the listen state duration + * @n_channels: number of channels to operate on + * @channels: channels to operate on + */ +struct cfg80211_p2p_find_params { + const u8 *probe_ie; + size_t probe_ie_len; + const u8 *probe_resp_ie; + size_t probe_resp_ie_len; + u32 min_discoverable_interval; + u32 max_discoverable_interval; + + int n_channels; + struct ieee80211_channel **channels; +}; + +/** * struct cfg80211_ops - backend description for wireless configuration * * This struct is registered by fullmac card drivers and/or wireless stacks @@ -1998,6 +2022,15 @@ struct cfg80211_update_ft_ies_params { * advertise the support for MAC based ACL have to implement this callback. * * @start_radar_detection: Start radar detection in the driver. + * + * start_p2p_find: start P2P find phase + * Parameters include IEs for probe/probe-resp frames; + * and channels to operate on. + * Parameters are not retained after call, driver need to copy data if + * it need it later. + * P2P find can't run concurrently with ROC or scan, and driver should + * check this. + * stop_p2p_find: stop P2P find phase */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2227,6 +2260,12 @@ struct cfg80211_ops { struct cfg80211_chan_def *chandef); int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie); + + int (*start_p2p_find)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_p2p_find_params *params); + void (*stop_p2p_find)(struct wiphy *wiphy, + struct wireless_dev *wdev); }; /* @@ -4122,6 +4161,20 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp); +/** + * cfg80211_p2p_find_notify - report p2p find state change + * @wdev: the wireless device reporting the event + * @cmd: the event, NL80211_CMD_START_P2P_FIND or NL80211_CMD_STOP_P2P_FIND + * @gfp: allocation flags + * + * This function reports p2p find state chenge for the given device + * When hardware actually starts p2p find, NL80211_CMD_START_P2P_FIND + * should be reported + * When p2p find finished, either unsolicited or in response to + * @ops->p2p_stop_find, NL80211_CMD_STOP_P2P_FIND should be reported + */ +void cfg80211_p2p_find_notify(struct wireless_dev *wdev, int cmd, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 79da871..8c87815 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -798,6 +798,9 @@ enum nl80211_commands { NL80211_CMD_UPDATE_FT_IES, NL80211_CMD_FT_EVENT, + NL80211_CMD_START_P2P_FIND, + NL80211_CMD_STOP_P2P_FIND, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1414,6 +1417,10 @@ enum nl80211_commands { * @NL80211_ATTR_IE_RIC: Resource Information Container Information * Element * + * @NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL, + * @NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL: min/max discoverable interval + * for the p2p find, represented as u32 + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1709,6 +1716,9 @@ enum nl80211_attrs { NL80211_ATTR_MDID, NL80211_ATTR_IE_RIC, + NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL, + NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3538,6 +3548,10 @@ enum nl80211_ap_sme_features { * Peering Management entity which may be implemented by registering for * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is * still generated by the driver. + * @NL80211_FEATURE_P2P_FIND_OFFLOAD: The driver supports P2P find phase + * offload. + * @NL80211_FEATURE_P2P_PROBE_RESP_OFFLOAD: When in P2P find phase, + * the device responds to probe-requests in hardware. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3557,6 +3571,8 @@ enum nl80211_feature_flags { NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, NL80211_FEATURE_USERSPACE_MPM = 1 << 16, + NL80211_FEATURE_P2P_FIND_OFFLOAD = 1 << 17, + NL80211_FEATURE_P2P_PROBE_RESP_OFFLOAD = 1 << 18, }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f924d45..d51aa2c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -378,6 +378,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MDID] = { .type = NLA_U16 }, [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL] = { .type = NLA_U32 }, + [NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -1417,6 +1419,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, } CMD(start_p2p_device, START_P2P_DEVICE); CMD(set_mcast_rate, SET_MCAST_RATE); + CMD(start_p2p_find, START_P2P_FIND); + CMD(stop_p2p_find, STOP_P2P_FIND); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); @@ -8196,6 +8200,115 @@ static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info) return rdev_update_ft_ies(rdev, dev, &ft_params); } +static int nl80211_start_p2p_find(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]; + struct wiphy *wiphy = &rdev->wiphy; + struct cfg80211_p2p_find_params params = {}; + struct nlattr *attr_freq = info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]; + struct nlattr *attr; + int err, tmp, n_channels, i = 0; + struct ieee80211_channel **channels = NULL; + + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_STATION: + break; + default: + return -EOPNOTSUPP; + + } + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP])) + return -EINVAL; + + if (!(wiphy->features & NL80211_FEATURE_P2P_FIND_OFFLOAD) || + !rdev->ops->start_p2p_find || !rdev->ops->stop_p2p_find) + return -EOPNOTSUPP; + + if (rdev->scan_req) + return -EBUSY; + + if (attr_freq) { + n_channels = validate_scan_freqs(attr_freq); + if (!n_channels) + return -EINVAL; + + channels = kzalloc(n_channels * sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + /* user specified, bail out if channel not found */ + nla_for_each_nested(attr, attr_freq, tmp) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + + if (!chan) + return -EINVAL; + + /* ignore disabled channels */ + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + params.channels[i] = chan; + i++; + } + if (!i) { + err = -EINVAL; + goto out_free; + } + + params.n_channels = i; + params.channels = channels; + } + + + attr = info->attrs[NL80211_ATTR_IE]; + if (attr) { + params.probe_ie_len = nla_len(attr); + params.probe_ie = nla_data(attr); + } + + attr = info->attrs[NL80211_ATTR_IE_PROBE_RESP]; + if (attr) { + params.probe_resp_ie_len = nla_len(attr); + params.probe_resp_ie = nla_data(attr); + } + + attr = info->attrs[NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL]; + if (attr) + params.min_discoverable_interval = nla_get_u32(attr); + + attr = info->attrs[NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL]; + if (attr) + params.max_discoverable_interval = nla_get_u32(attr); + + err = rdev_start_p2p_find(rdev, wdev, ¶ms); + +out_free: + kfree(channels); + + return err; +} + +static int nl80211_stop_p2p_find(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 (!rdev->ops->start_p2p_find || !rdev->ops->stop_p2p_find) + return -EOPNOTSUPP; + + rdev_stop_p2p_find(rdev, wdev); + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -8873,6 +8986,22 @@ static struct genl_ops nl80211_ops[] = { NL80211_FLAG_NEED_RTNL, }, { + .cmd = NL80211_CMD_START_P2P_FIND, + .doit = nl80211_start_p2p_find, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_P2P_FIND, + .doit = nl80211_stop_p2p_find, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, .doit = nl80211_get_protocol_features, .policy = nl80211_policy, @@ -10547,6 +10676,34 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, } EXPORT_SYMBOL(cfg80211_tdls_oper_request); +void cfg80211_p2p_find_notify(struct wireless_dev *wdev, int cmd, gfp_t gfp) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_p2p_find_notify(wdev, cmd); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} +EXPORT_SYMBOL(cfg80211_p2p_find_notify); + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index d77e1c1..5e43d50 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -901,4 +901,23 @@ static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_start_p2p_find(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_p2p_find_params *params) +{ + int ret; + trace_rdev_start_p2p_find(&rdev->wiphy, wdev, params); + ret = rdev->ops->start_p2p_find(&rdev->wiphy, wdev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_stop_p2p_find(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + trace_rdev_stop_p2p_find(&rdev->wiphy, wdev); + rdev->ops->stop_p2p_find(&rdev->wiphy, wdev); + trace_rdev_return_void(&rdev->wiphy); +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 23e063b..850dd3b 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1805,6 +1805,26 @@ TRACE_EVENT(rdev_update_ft_ies, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->md) ); +TRACE_EVENT(rdev_start_p2p_find, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_p2p_find_params *params), + TP_ARGS(wiphy, wdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_find, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ @@ -2459,6 +2479,21 @@ TRACE_EVENT(cfg80211_ft_event, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap)) ); +TRACE_EVENT(cfg80211_p2p_find_notify, + TP_PROTO(struct wireless_dev *wdev, int cmd), + TP_ARGS(wdev, cmd), + TP_STRUCT__entry( + WDEV_ENTRY + __field(int, cmd) + ), + TP_fast_assign( + WDEV_ASSIGN; + __entry->cmd = cmd; + ), + TP_printk(WDEV_PR_FMT ", cmd: %d", WDEV_PR_ARG, + __entry->cmd) +); + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- 1.7.10.4