wiphy: introduce new radio management APIs
PATCH v3 1/6 wiphy: introduce new radio management APIs
These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.).
Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item.
A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting).
Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
--- src/wiphy.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wiphy.h | 22 +++++++++++ 2 files changed, 134 insertions(+)
v2:
- Better optimized wiphy_radio_work_done to not iterate the queue more than it needs to.
src/wiphy.c +112 -0
@@ -32,6 +32,7 @@#include <fnmatch.h> #include <unistd.h> #include <string.h> +#include <limits.h> #include <ell/ell.h>
@@ -62,6 +63,7 @@ static char **whitelist_filter;static char **blacklist_filter;static int mac_randomize_bytes =6 ;static char regdom_country[2 ]; +static uint32_t work_ids;struct wiphy {uint32_t id;
@@ -85,6 +87,8 @@ struct wiphy {uint8_t rm_enabled_capabilities[7 ];/* 5 size max + header */ struct l_genl_family *nl80211;char regdom_country[2 ]; +/* Work queue for this radio */ +struct l_queue *work; bool support_scheduled_scan:1; bool support_rekey_offload:1;
@@ -216,6 +220,14 @@ static struct wiphy *wiphy_new(uint32_t id)return wiphy; } +static void destroy_work (void *user_data) +{ +struct wiphy_radio_work_item *work = user_data; + +if (work->ops && work->ops->destroy) + work->ops->destroy (work); +} +static void wiphy_free (void *data) {struct wiphy *wiphy = data;
@@ -235,6 +247,7 @@ static void wiphy_free(void *data)l_free (wiphy->vendor_str);l_free (wiphy->driver_str);l_genl_family_free (wiphy->nl80211); +l_queue_destroy (wiphy->work, destroy_work);l_free (wiphy); }
@@ -1072,6 +1085,8 @@ struct wiphy *wiphy_create(uint32_t wiphy_id, const char *name)if (!wiphy_is_managed (name)) wiphy->blacklisted = true; + wiphy->work =l_queue_new (); +return wiphy; }
@@ -1449,6 +1464,103 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data) } } +static void wiphy_radio_work_next (struct wiphy *wiphy) +{ +struct wiphy_radio_work_item *work; +bool done; + + work =l_queue_peek_head (wiphy->work); +if (!work) +return ; + +/* +* Ensures no other work item will get inserted before this one while +* the work is being done. +*/ + work->priority =INT_MIN ; + +l_debug ("Starting work item %u" , work->id); + done = work->ops->do_work (work); + +if (done) { + work->id =0 ; + +l_queue_remove (wiphy->work, work); + +destroy_work (work); + +wiphy_radio_work_next (wiphy); + } +} + +static int insert_by_priority (const void *a,const void *b,void *user_data) +{ +const struct wiphy_radio_work_item *new = a; +const struct wiphy_radio_work_item *work = b; + +if (work->priority <= new->priority) +return 1 ; + +return -1 ; +} + +uint32_t wiphy_radio_work_insert (struct wiphy *wiphy, +struct wiphy_radio_work_item *item, +int priority, +const struct wiphy_radio_work_item_ops *ops) +{ + item->priority = priority; + item->ops = ops; + item->id = ++work_ids; + +l_debug ("Inserting work item %u" , item->id); + +l_queue_insert (wiphy->work, item, insert_by_priority,NULL ); + +if (l_queue_length (wiphy->work) ==1 ) +wiphy_radio_work_next (wiphy); + +return item->id; +} + +static bool match_id (const void *a,const void *b) +{ +const struct wiphy_radio_work_item *item = a; + +if (item->id ==L_PTR_TO_UINT (b)) +return true; + +return false; +} + +void wiphy_radio_work_done (struct wiphy *wiphy,uint32_t id) +{ +struct wiphy_radio_work_item *item; +bool next = false; + + item =l_queue_peek_head (wiphy->work); +if (!item) +return ; + +if (item->id == id) { + next = true; +l_queue_pop_head (wiphy->work); + }else + item =l_queue_remove_if (wiphy->work, match_id, +L_UINT_TO_PTR (id)); +if (!item) +return ; + +l_debug ("Work item %u done" , id); + + item->id =0 ; + +destroy_work (item); + +if (next) +wiphy_radio_work_next (wiphy); +} +static int wiphy_init (void ) {struct l_genl *genl =iwd_get_genl ();
src/wiphy.h +22 -0
@@ -26,6 +26,22 @@struct wiphy ;struct scan_bss ;struct scan_freq_set ; +struct wiphy_radio_work_item ; + +typedef bool (*wiphy_radio_work_func_t )(struct wiphy_radio_work_item *item); +typedef void (*wiphy_radio_work_destroy_func_t )( +struct wiphy_radio_work_item *item); + +struct wiphy_radio_work_item_ops { +wiphy_radio_work_func_t do_work; +wiphy_radio_work_destroy_func_t destroy; +}; + +struct wiphy_radio_work_item { +uint32_t id; +int priority; +const struct wiphy_radio_work_item_ops *ops; +};enum wiphy_state_watch_event {WIPHY_STATE_WATCH_EVENT_POWERED ,
@@ -92,3 +108,9 @@ uint32_t wiphy_state_watch_add(struct wiphy *wiphy, wiphy_state_watch_func_t func, void *user_data, wiphy_destroy_func_t destroy);bool wiphy_state_watch_remove (struct wiphy *wiphy,uint32_t id); + +uint32_t wiphy_radio_work_insert (struct wiphy *wiphy, +struct wiphy_radio_work_item *item, +int priority, +const struct wiphy_radio_work_item_ops *ops); +void wiphy_radio_work_done (struct wiphy *wiphy,uint32_t id);
Signature · 1 line
2.21.1
PATCH v3 2/6 frame-xchg: refactor to use wiphy work queue
In order to first integrate frame-xchg some refactoring needed to be done. First it is useful to allow queueing frames up rather than requiring the module (p2p, anqp etc) to wait for the last frame to finish. This can be aided by radio management but frame-xchg needed some refactoring as well.
First was getting rid of this fx pointer re-use. It looks like this was done to save a bit of memory but things get pretty complex needed to check if the pointer is stale or has been reset. Instead of this we now just allocate a new pointer each frame-xchg. This allows for the module to queue multiple requests as well as removes the complexity of needed to check if the fx pointer is stale.
Next was adding the ability to track frame-xchgs by ID. If a module can queue up multiple requests it also needs to be able to cancel them individually vs per-wdev. This comes free with the wiphy work queue since it returns an ID which can be given directly to the caller.
Then radio management was simply piped in by adding the insert/done APIs.
--- src/frame-xchg.c | 121 +++++++++++++++++++++++------------------------ src/frame-xchg.h | 6 +-- src/p2p.c | 22 +++++++-- 3 files changed, 81 insertions(+), 68 deletions(-)
src/frame-xchg.c +60 -61
@@ -41,6 +41,7 @@#include "src/nl80211util.h" #include "src/netdev.h" #include "src/frame-xchg.h" +#include "src/wiphy.h" #ifndef SOL_NETLINK #define SOL_NETLINK 270
@@ -113,8 +114,7 @@ struct frame_xchg_data {unsigned int retry_interval;unsigned int resp_timeout;unsigned int retries_on_ack; - bool in_frame_cb : 1; - bool stale : 1; +struct wiphy_radio_work_item work; };struct frame_xchg_watch_data {
@@ -726,7 +726,7 @@ static bool frame_watch_remove_by_handler(uint64_t wdev_id, uint32_t group_id, &handler_info) > 0; } -static void frame_xchg_tx_retry (struct frame_xchg_data *fx); +static bool frame_xchg_tx_retry (struct wiphy_radio_work_item *item);static bool frame_xchg_resp_handle (const struct mmpdu_header *mpdu,const void *body,size_t body_len,int rssi,void *user_data);
@@ -753,9 +753,6 @@ static void frame_xchg_wait_cancel(struct frame_xchg_data *fx)static void frame_xchg_reset (struct frame_xchg_data *fx) { - fx->in_frame_cb = false; - fx->stale = false; -frame_xchg_wait_cancel (fx);if (fx->timeout)
@@ -771,9 +768,10 @@ static void frame_xchg_reset(struct frame_xchg_data *fx) frame_xchg_resp_cb, fx); } -static void frame_xchg_destroy (void *user_data) +static void frame_xchg_destroy (struct wiphy_radio_work_item *item) { -struct frame_xchg_data *fx = user_data; +struct frame_xchg_data *fx =l_container_of (item, + struct frame_xchg_data, work);if (fx->destroy) fx->destroy (fx->user_data);
@@ -789,7 +787,7 @@ static void frame_xchg_done(struct frame_xchg_data *fx, int err)if (fx->cb) fx->cb (err, fx->user_data); -frame_xchg_destroy (fx); +wiphy_radio_work_done (wiphy_find_by_wdev (fx->wdev_id), fx->work.id); }static void frame_xchg_timeout_destroy (void *user_data)
@@ -805,7 +803,7 @@ static void frame_xchg_timeout_cb(struct l_timeout *timeout,struct frame_xchg_data *fx = user_data;l_timeout_remove (fx->timeout); -frame_xchg_tx_retry (fx); +frame_xchg_tx_retry (&fx->work); } static void frame_xchg_listen_end_cb(struct l_timeout *timeout,
@@ -821,7 +819,7 @@ static void frame_xchg_listen_end_cb(struct l_timeout *timeout,l_timeout_remove (fx->timeout); fx->retries_on_ack--; fx->retry_cnt =0 ; -frame_xchg_tx_retry (fx); +frame_xchg_tx_retry (&fx->work); }static void frame_xchg_tx_status (struct frame_xchg_data *fx,bool acked)
@@ -918,8 +916,10 @@ error:frame_xchg_done (fx, error); } -static void frame_xchg_tx_retry (struct frame_xchg_data *fx) +static bool frame_xchg_tx_retry (struct wiphy_radio_work_item *item) { +struct frame_xchg_data *fx =l_container_of (item, + struct frame_xchg_data, work);struct l_genl_msg *msg;uint32_t cmd_id;uint32_t duration = fx->resp_timeout;
@@ -951,13 +951,15 @@ static void frame_xchg_tx_retry(struct frame_xchg_data *fx)l_error ("Error sending frame" );l_genl_msg_unref (msg);frame_xchg_done (fx, -EIO ); -return ; +return true; } fx->tx_acked = false; fx->have_cookie = false; fx->early_status = false; fx->retry_cnt++; + +return false; } static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
@@ -999,20 +1001,10 @@ static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,if (!fx->tx_acked) goto early_frame; - fx->in_frame_cb = true; done = watch->cb (mpdu, body, body_len, rssi, fx->user_data); -/* -* If the callback has started a new frame exchange it will -* have reset and taken over the state variables and we need -* to just exit without touching anything. -*/ -if (!fx->in_frame_cb) -return true; - - fx->in_frame_cb = false; - -if (done || fx->stale) { +if (done) { +/* NULL callback here since the caller is done */ fx->cb =NULL ;frame_xchg_done (fx,0 );return true;
@@ -1070,22 +1062,29 @@ static bool frame_xchg_match(const void *a, const void *b)* @resp_timeout was 0. @frame is an iovec array terminated by an iovec * struct with NULL-iov_base. */ -void frame_xchg_start (uint64_t wdev_id,struct iovec *frame,uint32_t freq, +uint32_t frame_xchg_start (uint64_t wdev_id,struct iovec *frame,uint32_t freq,unsigned int retry_interval,unsigned int resp_timeout,unsigned int retries_on_ack,uint32_t group_id,frame_xchg_cb_t cb,void *user_data,frame_xchg_destroy_func_t destroy, ...) { +uint32_t id;va_list args;va_start (args, destroy); -frame_xchg_startv (wdev_id, frame, freq, retry_interval, resp_timeout, + id =frame_xchg_startv (wdev_id, frame, freq, retry_interval, resp_timeout, retries_on_ack, group_id, cb, user_data, destroy, args);va_end (args); +return id; } -void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, +static const struct wiphy_radio_work_item_ops work_ops = { + .do_work = frame_xchg_tx_retry, + .destroy = frame_xchg_destroy, +}; + +uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, unsigned int retry_interval, unsigned int resp_timeout, unsigned int retries_on_ack, uint32_t group_id, frame_xchg_cb_t cb, void *user_data,
@@ -1108,31 +1107,13 @@ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, frame[0].iov_base)) {l_error ("Frame too short" );cb (-EMSGSIZE , user_data); -return ; +return 0 ; } - fx =l_queue_find (frame_xchgs, frame_xchg_match, &wdev_id); - -if (fx) { -/* -* If a frame callback calls us assume it's the end of -* that earlier frame exchange and the start of a new one. -*/ -if (fx->in_frame_cb) -frame_xchg_reset (fx); -else { -l_error ("Frame exchange in progress" ); -cb (-EBUSY , user_data); -return ; - } - }else { - fx = l_new(struct frame_xchg_data, 1); + fx = l_new(struct frame_xchg_data, 1); -if (!frame_xchgs) - frame_xchgs =l_queue_new (); - -l_queue_push_tail (frame_xchgs, fx); - } +if (!frame_xchgs) + frame_xchgs =l_queue_new (); fx->wdev_id = wdev_id; fx->freq = freq;
@@ -1177,24 +1158,35 @@ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, } fx->retry_cnt =0 ; -frame_xchg_tx_retry (fx); + +l_queue_push_tail (frame_xchgs, fx); + +/* +* TODO: Assume any offchannel frames are a high priority (0). This may +* need to be re-examined in the future if other operations (e.g. +* wait on channel) are introduced. +*/ +return wiphy_radio_work_insert (wiphy_find_by_wdev (wdev_id), + &fx->work,0 , &work_ops); +} + +static bool frame_xchg_match_id (const void *a,const void *b) +{ +const struct frame_xchg_data *fx = a; +const uint32_t *id = b; + +return fx->work.id == *id; } -void frame_xchg_stop (uint64_t wdev_id) +void frame_xchg_cancel (uint32_t id) {struct frame_xchg_data *fx = -l_queue_remove_if (frame_xchgs, frame_xchg_match, &wdev_id); +l_queue_remove_if (frame_xchgs, frame_xchg_match_id, &id);if (!fx)return ; -if (fx->in_frame_cb) { - fx->stale = true; -return ; - } - -frame_xchg_reset (fx); -l_free (fx); +wiphy_radio_work_done (wiphy_find_by_wdev (fx->wdev_id), id); }static void frame_xchg_mlme_notify (struct l_genl_msg *msg,void *user_data)
@@ -1341,13 +1333,20 @@ error:return -EIO ; } +static void destroy_xchg_data (void *user_data) +{ +struct frame_xchg_data *fx = user_data; + +frame_xchg_destroy (&fx->work); +} +static void frame_xchg_exit (void ) {struct l_queue *groups = watch_groups;struct l_queue *xchgs = frame_xchgs; frame_xchgs =NULL ; -l_queue_destroy (xchgs, frame_xchg_destroy); +l_queue_destroy (xchgs, destroy_xchg_data); watch_groups =NULL ;l_queue_destroy (groups, frame_watch_group_destroy);
src/frame-xchg.h +3 -3
@@ -43,14 +43,14 @@ bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type,bool frame_watch_group_remove (uint64_t wdev_id,uint32_t group);bool frame_watch_wdev_remove (uint64_t wdev_id); -void frame_xchg_start (uint64_t wdev_id,struct iovec *frame,uint32_t freq, +uint32_t frame_xchg_start (uint64_t wdev_id,struct iovec *frame,uint32_t freq,unsigned int retry_interval,unsigned int resp_timeout,unsigned int retries_on_ack,uint32_t group_id,frame_xchg_cb_t cb,void *user_data,frame_xchg_destroy_func_t destroy, ...); -void frame_xchg_startv (uint64_t wdev_id,struct iovec *frame,uint32_t freq, +uint32_t frame_xchg_startv (uint64_t wdev_id,struct iovec *frame,uint32_t freq,unsigned int retry_interval,unsigned int resp_timeout,unsigned int retries_on_ack,uint32_t group_id,frame_xchg_cb_t cb,void *user_data,frame_xchg_destroy_func_t destroy,va_list resp_args); -void frame_xchg_stop (uint64_t wdev_id); +void frame_xchg_cancel (uint32_t id);
src/p2p.c +18 -4
@@ -68,6 +68,7 @@ struct p2p_device {uint32_t start_stop_cmd_id;l_dbus_property_complete_cb_t pending_complete;struct l_dbus_message *pending_message; +uint32_t xchg_id;uint8_t listen_country[3 ];uint8_t listen_oper_class;
@@ -383,7 +384,11 @@ static void p2p_connection_reset(struct p2p_device *dev)netdev_watch_remove (dev->conn_netdev_watch_id);frame_watch_group_remove (dev->wdev_id,FRAME_GROUP_CONNECT ); -frame_xchg_stop (dev->wdev_id); + +if (dev->xchg_id) { +frame_xchg_cancel (dev->xchg_id); + dev->xchg_id =0 ; + }if (!dev->enabled || (dev->enabled && dev->start_stop_cmd_id)) {/*
@@ -458,9 +463,10 @@ static void p2p_peer_frame_xchg(struct p2p_peer *peer, struct iovec *tx_body, peer->bss->frequency;va_start (args, cb); -frame_xchg_startv (dev->wdev_id, frame, freq, retry_interval, - resp_timeout, retries_on_ack, group_id, - cb, dev,NULL , args); + + dev->xchg_id =frame_xchg_startv (dev->wdev_id, frame, freq, + retry_interval, resp_timeout, retries_on_ack, + group_id, cb, dev,NULL , args);va_end (args);l_free (frame);
@@ -1175,11 +1181,17 @@ static void p2p_go_negotiation_resp_done(int error, void *user_data)else l_error ("No GO Negotiation Confirmation frame received" ); + dev->xchg_id =0 ; +p2p_connect_failed (dev); }static void p2p_go_negotiation_resp_err_done (int error,void *user_data) { +struct p2p_device *dev = user_data; + + dev->xchg_id =0 ; +if (error)l_error ("Sending the GO Negotiation Response failed: %s (%i)" ,strerror (-error), -error);
@@ -1272,6 +1284,8 @@ static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu,l_debug ("" ); + dev->xchg_id =0 ; +if (body_len <8 ) {l_error ("GO Negotiation Confirmation frame too short" );p2p_connect_failed (dev);
Signature · 1 line
2.21.1
PATCH v3 3/6 anqp: refactor to use frame-xchg
In reply to James PrestwoodThe new frame-xchg module now handles a lot of what ANQP used to do. ANQP now does not need to depend on nl80211/netdev for building and sending frames. It also no longer needs any of the request lookups, frame watches or to maintain a queue of requests because frame-xchg filters this for us.
From an API perspective:
- anqp_request() was changed to take the wdev_id rather than ifindex.
- anqp_cancel() was added so that station can properly clean up ANQP requests if the device disappears.
During testing a bug was also fixed in station on the timeout path where the request queue would get popped twice.
--- src/anqp.c | 443 +++++++++++--------------------------------------- src/anqp.h | 3 +- src/station.c | 19 +-- 3 files changed, 103 insertions(+), 362 deletions(-)
src/anqp.c +92 -351
@@ -31,105 +31,61 @@#include "src/module.h" #include "src/anqp.h" #include "src/util.h" -#include "src/eap-private.h" #include "src/ie.h" -#include "src/nl80211util.h" -#include "src/nl80211cmd.h" #include "src/scan.h" -#include "src/netdev.h" #include "src/iwd.h" #include "src/mpdu.h" -#include "src/wiphy.h" +#include "src/frame-xchg.h" #include "linux/nl80211.h" +#define ANQP_GROUP 0 +struct anqp_request { -uint32_t ifindex; +uint64_t wdev_id;anqp_response_func_t anqp_cb;anqp_destroy_func_t anqp_destroy;void *anqp_data; -uint64_t anqp_cookie;uint8_t anqp_token; +uint32_t frequency; +uint8_t *frame; +size_t frame_len; +uint32_t id; }; -static struct l_genl_family *nl80211 =NULL ; - -static struct l_queue *anqp_requests;static uint8_t anqp_token =0 ; -static uint32_t netdev_watch; -static uint32_t unicast_watch; -static void anqp_destroy (void *user_data) {struct anqp_request *request = user_data;if (request->anqp_destroy) request->anqp_destroy (request->anqp_data); -} - -static void netdev_gas_request_cb (struct l_genl_msg *msg,void *user_data) -{ -struct anqp_request *request = user_data; - -if (l_genl_msg_get_error (msg) !=0 ) - goto error; - -if (nl80211_parse_attrs (msg,NL80211_ATTR_COOKIE , &request->anqp_cookie, -NL80211_ATTR_UNSPEC ) <0 ) - goto error; - -return ; - -error: -l_debug ("Error sending CMD_FRAME (%d)" ,l_genl_msg_get_error (msg)); - -if (request->anqp_cb) - request->anqp_cb (ANQP_FAILED ,NULL ,0 , request->anqp_data); - -if (request->anqp_destroy) - request->anqp_destroy (request->anqp_data); +l_free (request->frame);l_free (request); } -static bool match_token (const void *a,const void *b) -{ -const struct anqp_request *request = a; -const struct token_match { -uint32_t ifindex; -uint8_t token; - - } *match = b; - -if (request->ifindex != match->ifindex) -return false; - -if (request->anqp_token != match->token) -return false; - -return true; -} - -static void anqp_response_frame_event (uint32_t ifindex, -const struct mmpdu_header *hdr, -const void *body,size_t body_len) +/* +* By using frame-xchg we should get called back here for any frame matching our +* prefix until the duration expires. If frame-xchg is never signalled 'done' +* (returning true) we should get a timeout in anqp_frame_timeout. This is +* why we drop any improperly formatted frames without cleaning up the request. +*/ +static bool anqp_response_frame_event (const struct mmpdu_header *hdr, +const void *body,size_t body_len, +int rssi,void *user_data) { -struct anqp_request *request; +struct anqp_request *request = user_data;const uint8_t *ptr = body;uint16_t status_code;uint16_t delay;uint16_t qrlen;uint8_t adv_proto_len;uint8_t token; -struct token_match { -uint32_t ifindex; -uint8_t token; - - } match;if (body_len <9 ) -return ; +return false;/* Skip past category/action since this frame was prefixed matched */ ptr +=2 ;
@@ -138,12 +94,8 @@ static void anqp_response_frame_event(uint32_t ifindex,/* dialog token */ token = *ptr++; - match.ifindex = ifindex; - match.token = token; - - request =l_queue_find (anqp_requests, match_token, &match); -if (!request) -return ; +if (request->anqp_token != token) +return false; status_code =l_get_le16 (ptr); ptr +=2 ;
@@ -151,7 +103,7 @@ static void anqp_response_frame_event(uint32_t ifindex,if (status_code !=0 ) {l_error ("Bad status code on GAS response %u" , status_code); -return ; +return false; } delay =l_get_le16 (ptr);
@@ -166,12 +118,12 @@ static void anqp_response_frame_event(uint32_t ifindex,*/ if (delay !=0 ) {l_error ("GAS comeback delay was not zero" ); -return ; +return false; }if (*ptr !=IE_TYPE_ADVERTISEMENT_PROTOCOL ) {l_error ("GAS request not advertisement protocol" ); -return ; +return false; } ptr++;
@@ -181,339 +133,128 @@ static void anqp_response_frame_event(uint32_t ifindex, body_len--;if (body_len < adv_proto_len) -return ; +return false; ptr += adv_proto_len; body_len -= adv_proto_len;if (body_len <2 ) -return ; +return false; qrlen =l_get_le16 (ptr); ptr +=2 ;if (body_len < qrlen) -return ; - -l_queue_remove (anqp_requests, request); +return false;l_debug ("ANQP response received from " MAC ,MAC_STR (hdr->address_2));if (request->anqp_cb) - request->anqp_cb (ANQP_SUCCESS , ptr, qrlen, - request->anqp_data); - -if (request->anqp_destroy) - request->anqp_destroy (request->anqp_data); + request->anqp_cb (ANQP_SUCCESS , ptr, qrlen, request->anqp_data); -l_free (request); +anqp_destroy (request); -return ; +return true; } -static void netdev_gas_timeout_cb (void *user_data) +static const struct frame_xchg_prefix anqp_frame_prefix = { + .data = (uint8_t []) { +0x04 ,0x0b , + }, + .len =2 , +}; + +static void anqp_frame_timeout (int error,void *user_data) {struct anqp_request *request = user_data; +enum anqp_result result =ANQP_TIMEOUT ; -l_debug ("GAS request timed out" ); +if (error <0 ) { + result =ANQP_FAILED ; +l_error ("Sending ANQP request failed: %s (%i)" , +strerror (-error), -error); + }if (request->anqp_cb) - request->anqp_cb (ANQP_TIMEOUT ,NULL ,0 , - request->anqp_data); + request->anqp_cb (result,NULL ,0 , request->anqp_data); -/* allows anqp_request to be re-entrant */ if (request->anqp_destroy) request->anqp_destroy (request->anqp_data); -l_queue_remove (anqp_requests, request); -l_free (request); +anqp_destroy (request); } -static bool match_cookie (const void *a,const void *b) +static uint8_t *anqp_build_frame (const uint8_t *addr,struct scan_bss *bss, +const uint8_t *anqp,size_t len, +size_t *len_out) { -const struct anqp_request *request = a; -const struct cookie_match { -uint64_t cookie; -uint32_t ifindex; - } *match = b; +uint8_t *frame =l_malloc (len +33 ); +uint8_t *ptr; -if (match->ifindex != request->ifindex) -return false; +memset (frame,0 , len +33 ); -if (match->cookie != request->anqp_cookie) -return false; +l_put_le16 (0x00d0 , frame +0 ); +memcpy (frame +4 , bss->addr,6 ); +memcpy (frame +10 , addr,6 ); +memcpy (frame +16 , bss->addr,6 ); -return true; -} + ptr = frame +24 ; -static void anqp_frame_wait_cancel_event (struct l_genl_msg *msg, -uint32_t ifindex) -{ -uint64_t cookie; -struct anqp_request *request; -struct cookie_match { -uint64_t cookie; -uint32_t ifindex; - } match; + *ptr++ =0x04 ;/* Category: Public */ + *ptr++ =0x0a ;/* Action: GAS initial Request */ + *ptr++ = anqp_token++;/* Dialog Token */ + *ptr++ =IE_TYPE_ADVERTISEMENT_PROTOCOL ; + *ptr++ =2 ; -l_debug ("" ); - -if (nl80211_parse_attrs (msg,NL80211_ATTR_COOKIE , &cookie, -NL80211_ATTR_UNSPEC ) <0 ) -return ; - - match.cookie = cookie; - match.ifindex = ifindex; + *ptr++ =0x7f ; + *ptr++ =IE_ADVERTISEMENT_ANQP ; +l_put_le16 (len, ptr); + ptr +=2 ; - request =l_queue_find (anqp_requests, match_cookie, &match); -if (!request) -return ; +memcpy (ptr, anqp, len); + ptr += len; -if (cookie != request->anqp_cookie) -return ; + *len_out = ptr - frame; -netdev_gas_timeout_cb (request); +return frame; } -uint32_t anqp_request (uint32_t ifindex,const uint8_t *addr, -struct scan_bss *bss,const uint8_t *anqp, -size_t len,anqp_response_func_t cb, -void *user_data,anqp_destroy_func_t destroy) +uint32_t anqp_request (uint64_t wdev_id,const uint8_t *addr, +struct scan_bss *bss,const uint8_t *anqp, +size_t len,anqp_response_func_t cb, +void *user_data,anqp_destroy_func_t destroy) {struct anqp_request *request; -uint8_t frame[512 ]; -struct l_genl_msg *msg;struct iovec iov[2 ]; -uint32_t id; -uint32_t duration =300 ; -struct netdev *netdev =netdev_find (ifindex); - -if (!netdev) -return 0 ; - -/* -* TODO: Netdev dependencies will eventually be removed so we need -* another way to figure out wiphy capabilities. -*/ -if (!wiphy_can_offchannel_tx (netdev_get_wiphy (netdev))) { -l_error ("ANQP failed, driver does not support offchannel TX" ); -return 0 ; - } - - frame[0 ] =0x04 ;/* Category: Public */ - frame[1 ] =0x0a ;/* Action: GAS initial Request */ - frame[2 ] = anqp_token;/* Dialog Token */ - frame[3 ] =IE_TYPE_ADVERTISEMENT_PROTOCOL ; - frame[4 ] =2 ; - frame[5 ] =0x7f ; - frame[6 ] =IE_ADVERTISEMENT_ANQP ; -l_put_le16 (len, frame +7 ); - - iov[0 ].iov_base = frame; - iov[0 ].iov_len =9 ; - iov[1 ].iov_base = (void *)anqp; - iov[1 ].iov_len = len; request = l_new(struct anqp_request, 1); - request->ifindex = ifindex; + request->wdev_id = wdev_id; + request->frequency = bss->frequency; request->anqp_cb = cb; request->anqp_destroy = destroy; - request->anqp_token = anqp_token++; + request->anqp_token = anqp_token; request->anqp_data = user_data; - msg =nl80211_build_cmd_frame (ifindex, addr, bss->addr, - bss->frequency, iov,2 ); - -l_genl_msg_append_attr (msg,NL80211_ATTR_OFFCHANNEL_TX_OK ,0 ,"" ); -l_genl_msg_append_attr (msg,NL80211_ATTR_DURATION ,4 , &duration); - - id =l_genl_family_send (nl80211, msg, netdev_gas_request_cb, - request,NULL ); - -if (!id) { -l_debug ("Failed to send ANQP request" ); -l_genl_msg_unref (msg); -l_free (request); -return 0 ; - } - -l_debug ("ANQP request sent to " MAC ,MAC_STR (bss->addr)); - -l_queue_push_head (anqp_requests, request); - -return id; -} - -static void netdev_frame_cb (struct l_genl_msg *msg,void *user_data) -{ -if (l_genl_msg_get_error (msg) <0 ) -l_error ("Could not register frame watch type %04x: %i" , -L_PTR_TO_UINT (user_data),l_genl_msg_get_error (msg)); -} - -static void anqp_register_frame (uint32_t ifindex) -{ -struct l_genl_msg *msg; -uint16_t frame_type =0x00d0 ; -uint8_t prefix[] = {0x04 ,0x0b }; - - msg =l_genl_msg_new_sized (NL80211_CMD_REGISTER_FRAME ,34 ); - -l_genl_msg_append_attr (msg,NL80211_ATTR_IFINDEX ,4 , &ifindex); -l_genl_msg_append_attr (msg,NL80211_ATTR_FRAME_TYPE ,2 , &frame_type); -l_genl_msg_append_attr (msg,NL80211_ATTR_FRAME_MATCH , -sizeof (prefix), prefix); - -l_genl_family_send (nl80211, msg, netdev_frame_cb, -L_UINT_TO_PTR (frame_type),NULL ); -} - -static void anqp_netdev_watch (struct netdev *netdev, -enum netdev_watch_event event,void *user_data) -{ -switch (event) { -case NETDEV_WATCH_EVENT_NEW : -if (netdev_get_iftype (netdev) ==NETDEV_IFTYPE_STATION ) -anqp_register_frame (netdev_get_ifindex (netdev)); - -return ; -default : -break ; - } -} - -static void anqp_unicast_notify (struct l_genl_msg *msg,void *user_data) -{ -const struct mmpdu_header *mpdu =NULL ; -const uint8_t *body; -struct l_genl_attr attr; -uint16_t type, len; -uint16_t frame_len =0 ; -const void *data; -uint8_t cmd; -uint32_t ifindex =0 ; - -if (l_queue_isempty (anqp_requests)) -return ; - - cmd =l_genl_msg_get_command (msg); -if (!cmd) -return ; - -if (!l_genl_attr_init (&attr, msg)) -return ; - -while (l_genl_attr_next (&attr, &type, &len, &data)) { -switch (type) { -case NL80211_ATTR_IFINDEX : -if (len !=sizeof (uint32_t )) { -l_warn ("Invalid interface index attribute" ); -return ; - } - - ifindex = *((uint32_t *) data); - -break ; -case NL80211_ATTR_FRAME : -if (mpdu) -return ; - - mpdu =mpdu_validate (data, len); -if (!mpdu) -l_error ("Frame didn't validate as MMPDU" ); - - frame_len = len; -break ; - } - } - -if (!ifindex || !mpdu) -return ; - - body =mmpdu_body (mpdu); - -anqp_response_frame_event (ifindex, mpdu, body, - (const uint8_t *) mpdu + frame_len - body); -} - -static void anqp_mlme_notify (struct l_genl_msg *msg,void *user_data) -{ -struct l_genl_attr attr; -uint16_t type, len; -const void *data; -uint8_t cmd; -uint32_t ifindex =0 ; - -if (l_queue_isempty (anqp_requests)) -return ; - - cmd =l_genl_msg_get_command (msg); - -l_debug ("MLME notification %s(%u)" ,nl80211cmd_to_string (cmd), cmd); - -if (!l_genl_attr_init (&attr, msg)) -return ; - -while (l_genl_attr_next (&attr, &type, &len, &data)) { -switch (type) { -case NL80211_ATTR_IFINDEX : -if (len !=sizeof (uint32_t )) { -l_warn ("Invalid interface index attribute" ); -return ; - } - - ifindex = *((uint32_t *) data); -break ; - } - } + request->frame =anqp_build_frame (addr, bss, anqp, len, + &request->frame_len); -if (!ifindex) { -l_warn ("MLME notification is missing ifindex attribute" ); -return ; - } - -switch (cmd) { -case NL80211_CMD_FRAME_WAIT_CANCEL : -anqp_frame_wait_cancel_event (msg, ifindex); -break ; - } -} - -static int anqp_init (void ) -{ -struct l_genl *genl =iwd_get_genl (); - - nl80211 =l_genl_family_new (genl,NL80211_GENL_NAME ); - - anqp_requests =l_queue_new (); + iov[0 ].iov_base = request->frame; + iov[0 ].iov_len = request->frame_len; + iov[1 ].iov_base =NULL ; - netdev_watch =netdev_watch_add (anqp_netdev_watch,NULL ,NULL ); +l_debug ("Sending ANQP request" ); - unicast_watch =l_genl_add_unicast_watch (genl,NL80211_GENL_NAME , - anqp_unicast_notify, -NULL ,NULL ); + request->id =frame_xchg_start (request->wdev_id, iov, + request->frequency,0 ,300 ,0 , +ANQP_GROUP , anqp_frame_timeout, request,NULL , + &anqp_frame_prefix, anqp_response_frame_event, +NULL ); -if (!l_genl_family_register (nl80211,"mlme" , anqp_mlme_notify, -NULL ,NULL )) -l_error ("Registering for MLME notification failed" ); - -return 0 ; +return true; } -static void anqp_exit (void ) +void anqp_cancel (uint32_t id) { -struct l_genl *genl =iwd_get_genl (); - -l_genl_family_free (nl80211); - nl80211 =NULL ; - -l_queue_destroy (anqp_requests, anqp_destroy); - -netdev_watch_remove (netdev_watch); - -l_genl_remove_unicast_watch (genl, unicast_watch); +frame_xchg_cancel (id); } - -IWD_MODULE (anqp, anqp_init, anqp_exit); -IWD_MODULE_DEPENDS (anqp, netdev);
src/anqp.h +2 -1
@@ -34,7 +34,8 @@ typedef void (*anqp_response_func_t)(enum anqp_result result, const void *anqp, size_t len, void *user_data); -uint32_t anqp_request (uint32_t ifindex,const uint8_t *addr, +uint32_t anqp_request (uint64_t wdev_id,const uint8_t *addr,struct scan_bss *bss,const uint8_t *anqp,size_t len,anqp_response_func_t cb,void *user_data,anqp_destroy_func_t destroy); +void anqp_cancel (uint32_t id);
src/station.c +9 -10
@@ -447,6 +447,9 @@ static void remove_anqp(void *data) {struct anqp_entry *entry = data; +if (entry->pending) +anqp_cancel (entry->pending); +l_free (entry); }
@@ -476,12 +479,9 @@ static void station_anqp_response_cb(enum anqp_result result,char **realms =NULL ;struct nai_search search; - entry->pending =0 ; -l_debug ("" ); -if (result ==ANQP_TIMEOUT ) { -l_queue_remove (station->anqp_pending, entry); +if (result !=ANQP_SUCCESS ) {/* TODO: try next BSS */ goto request_done; }
@@ -583,11 +583,10 @@ static bool station_start_anqp(struct station *station, struct network *network,* these are checked in hs20_find_settings_file. */ - entry->pending =anqp_request (netdev_get_ifindex (station->netdev), -netdev_get_address (station->netdev), - bss, anqp, ptr - anqp, - station_anqp_response_cb, - entry, l_free); + entry->pending =anqp_request (netdev_get_wdev_id (station->netdev), +netdev_get_address (station->netdev), bss, anqp, + ptr - anqp, station_anqp_response_cb, + entry,NULL );if (!entry->pending) {l_free (entry);return false;
@@ -3252,7 +3251,7 @@ static void station_free(struct station *station)watchlist_destroy (&station->state_watches); -l_queue_destroy (station->anqp_pending, l_free); +l_queue_destroy (station->anqp_pending, remove_anqp);l_free (station); }
Signature · 1 line
2.21.1
PATCH v3 4/6 scan: refactor to use wiphy radio work queue
In reply to James PrestwoodTo use the wiphy radio work queue, scanning mostly remained the same. start_next_scan_request was modified to be used as the work callback, as well as not start the next scan if the current one was done (since this is taken care of by wiphy work queue now). All calls to start_next_scan_request were removed, and more or less replaced with wiphy_radio_work_done.
scan_{suspend,resume} were both removed since radio management priorities solve this for us. ANQP requests can be inserted ahead of scan requests, which accomplishes the same thing.
--- src/scan.c | 130 +++++++++++++++----------------------------------- src/scan.h | 3 -- src/station.c | 16 +------ 3 files changed, 40 insertions(+), 109 deletions(-)
v3:
- Added back in calling destroy() immediately if an already triggered scan was canceled. This is safe since destroy() gets NULL'ed so once the scan results come back in the work will get signaled as done but destroy() wont get called twice.
src/scan.c +39 -91
@@ -56,7 +56,8 @@static struct l_queue *scan_contexts;static struct l_genl_family *nl80211; -static uint32_t next_scan_request_id; + +struct scan_context ;struct scan_periodic {struct l_timeout *timeout;
@@ -70,7 +71,7 @@ struct scan_periodic { };struct scan_request { -uint32_t id; +struct scan_context *sc;scan_trigger_func_t trigger;scan_notify_func_t callback;void *userdata;
@@ -79,6 +80,7 @@ struct scan_request {struct l_queue *cmds;/* The time the current scan was started. Reported in TRIGGER_SCAN */ uint64_t start_time_tsf; +struct wiphy_radio_work_item work; };struct scan_context {
@@ -106,7 +108,6 @@ struct scan_context { bool triggered:1;/* Whether any commands from current request's queue have started */ bool started:1; - bool suspended:1;struct wiphy *wiphy; };
@@ -118,7 +119,7 @@ struct scan_results {struct scan_request *sr; }; -static bool start_next_scan_request (struct scan_context *sc); +static bool start_next_scan_request (struct wiphy_radio_work_item *item);static void scan_periodic_rearm (struct scan_context *sc);static bool scan_context_match (const void *a,const void *b)
@@ -134,12 +135,13 @@ static bool scan_request_match(const void *a, const void *b)const struct scan_request *sr = a;uint32_t id =L_PTR_TO_UINT (b); -return sr->id == id; +return sr->work.id == id; } -static void scan_request_free (void *data) +static void scan_request_free (struct wiphy_radio_work_item *item) { -struct scan_request *sr = data; + struct scan_request *sr = l_container_of(item, struct scan_request, + work);if (sr->destroy) sr->destroy (sr->userdata);
@@ -159,12 +161,12 @@ static void scan_request_failed(struct scan_context *sc,else if (sr->callback) sr->callback (err,NULL , sr->userdata); -scan_request_free (sr); +wiphy_radio_work_done (sc->wiphy, sr->work.id); }static struct scan_context *scan_context_new (uint64_t wdev_id) { -struct wiphy *wiphy =wiphy_find (wdev_id >>32 ); +struct wiphy *wiphy =wiphy_find_by_wdev (wdev_id);struct scan_context *sc;if (!wiphy)
@@ -180,11 +182,18 @@ static struct scan_context *scan_context_new(uint64_t wdev_id)return sc; } +static void scan_request_cancel (void *data) +{ +struct scan_request *sr = data; + +wiphy_radio_work_done (sr->sc->wiphy, sr->work.id); +} +static void scan_context_free (struct scan_context *sc) {l_debug ("sc: %p" , sc); -l_queue_destroy (sc->requests, scan_request_free); +l_queue_destroy (sc->requests, scan_request_cancel);if (sc->sp.timeout)l_timeout_remove (sc->sp.timeout);
@@ -215,7 +224,6 @@ static void scan_request_triggered(struct l_genl_msg *msg, void *userdata) }l_queue_remove (sc->requests, sr); -start_next_scan_request (sc);scan_request_failed (sc, sr, err);
@@ -517,6 +525,11 @@ static int scan_request_send_trigger(struct scan_context *sc,return -EIO ; } +static const struct wiphy_radio_work_item_ops work_ops = { + .do_work = start_next_scan_request, + .destroy = scan_request_free, +}; + static uint32_t scan_common(uint64_t wdev_id, bool passive, const struct scan_parameters *params, scan_trigger_func_t trigger,
@@ -532,36 +545,19 @@ static uint32_t scan_common(uint64_t wdev_id, bool passive,return 0 ; sr = l_new(struct scan_request, 1); + sr->sc = sc; sr->trigger = trigger; sr->callback = notify; sr->userdata = userdata; sr->destroy = destroy; sr->passive = passive; - sr->id = ++next_scan_request_id; sr->cmds =l_queue_new ();scan_cmds_add (sr->cmds, sc, passive, params); -/* Queue empty implies !sc->triggered && !sc->start_cmd_id */ -if (!l_queue_isempty (sc->requests)) - goto done; - -if (sc->suspended) - goto done; - -if (sc->state !=SCAN_STATE_NOT_RUNNING ) - goto done; - -if (!scan_request_send_trigger (sc, sr)) - goto done; - - sr->destroy =NULL ;/* Don't call destroy when returning error */ -scan_request_free (sr); -return 0 ; -done:l_queue_push_tail (sc->requests, sr); -return sr->id; +return wiphy_radio_work_insert (sc->wiphy, &sr->work,2 , &work_ops); } uint32_t scan_passive(uint64_t wdev_id, struct scan_freq_set *freqs,
@@ -622,7 +618,7 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id)if (!sr)return false; -/* If already triggered, just zero out the callback */ +/* If already triggered, just zero out the callback. */ if (sr ==l_queue_peek_head (sc->requests) && sc->triggered) {l_debug ("Scan is at the top of the queue and triggered" );
@@ -649,11 +645,11 @@ bool scan_cancel(uint64_t wdev_id, uint32_t id) sc->start_cmd_id =0 ;l_queue_remove (sc->requests, sr); sc->started = false; -start_next_scan_request (sc); }else l_queue_remove (sc->requests, sr); -scan_request_free (sr); +wiphy_radio_work_done (sc->wiphy, sr->work.id); +return true; }
@@ -832,34 +828,23 @@ static void scan_periodic_rearm(struct scan_context *sc) scan_periodic_timeout_destroy); } -static bool start_next_scan_request (struct scan_context *sc) +static bool start_next_scan_request (struct wiphy_radio_work_item *item) { -struct scan_request *sr =l_queue_peek_head (sc->requests); - -if (sc->suspended) -return true; - -if (sc->state !=SCAN_STATE_NOT_RUNNING ) -return true; - -if (sc->start_cmd_id || sc->get_scan_cmd_id) -return true; - -while (sr) { -if (!scan_request_send_trigger (sc, sr)) -return true; +struct scan_request *sr =l_container_of (item, + struct scan_request, work); +struct scan_context *sc = sr->sc; -scan_request_failed (sc, sr, -EIO ); +if (!scan_request_send_trigger (sc, sr)) +return false; - sr =l_queue_peek_head (sc->requests); - } +scan_request_failed (sc, sr, -EIO );if (sc->sp.retry) { sc->sp.retry = false;scan_periodic_queue (sc); } -return false; +return true; } static bool scan_parse_vendor_specific(struct scan_bss *bss, const void *data,
@@ -1511,9 +1496,7 @@ static void scan_finished(struct scan_context *sc,* taken care of sending the next command for a new or ongoing * scan, or scheduling the next periodic scan. */ -start_next_scan_request (sc); - -scan_request_free (sr); +wiphy_radio_work_done (sc->wiphy, sr->work.id); }else if (sc->sp.callback) new_owner = sc->sp.callback (err, bss_list, sc->sp.userdata);
@@ -1679,7 +1662,7 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)/* Send the next command of a new or an ongoing request */ if (send_next) -start_next_scan_request (sc); +start_next_scan_request (&sr->work);if (!get_results)break ;
@@ -1722,15 +1705,6 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data) sc->triggered = false;scan_finished (sc, -ECANCELED ,NULL , sr); - }else { -/* -* If this was an external scan that got aborted -* we may be able to now queue our own scan although -* the abort could also have been triggered by the -* hardware or the driver because of another activity -* starting in which case we should just get an EBUSY. -*/ -start_next_scan_request (sc); }break ;
@@ -2133,32 +2107,6 @@ bool scan_wdev_remove(uint64_t wdev_id)return true; } -bool scan_suspend (uint64_t wdev_id) -{ -struct scan_context *sc; - - sc =l_queue_find (scan_contexts, scan_context_match, &wdev_id); -if (!sc) -return false; - - sc->suspended = true; - -return true; -} - -void scan_resume (uint64_t wdev_id) -{ -struct scan_context *sc; - - sc =l_queue_find (scan_contexts, scan_context_match, &wdev_id); -if (!sc) -return ; - - sc->suspended = false; - -start_next_scan_request (sc); -} -static int scan_init (void ) {const struct l_settings *config =iwd_get_config ();
src/scan.h +0 -3
@@ -170,6 +170,3 @@ bool scan_freq_set_isempty(const struct scan_freq_set *set);bool scan_wdev_add (uint64_t wdev_id);bool scan_wdev_remove (uint64_t wdev_id); - -bool scan_suspend (uint64_t wdev_id); -void scan_resume (uint64_t wdev_id);
src/station.c +1 -15
@@ -531,8 +531,6 @@ request_done:station_network_foreach (station, network_add_foreach, station);station_autoconnect_next (station); } - -scan_resume (netdev_get_wdev_id (station->netdev)); } static bool station_start_anqp(struct station *station, struct network *network,
@@ -662,19 +660,7 @@ void station_set_scan_results(struct station *station,l_hashmap_foreach_remove (station->networks, process_network, station); -/* -* ANQP requests are scheduled in the same manor as scans, and cannot -* be done simultaneously. To avoid long queue times (waiting for a -* scan to finish) its best to stop scanning, do ANQP, then resume -* scanning. -* -* TODO: It may be possible for some hardware to actually scan and do -* ANQP at the same time. Detecting this could allow us to continue -* scanning. -*/ -if (wait_for_anqp) -scan_suspend (netdev_get_wdev_id (station->netdev)); -else if (add_to_autoconnect) { +if (!wait_for_anqp && add_to_autoconnect) {station_network_foreach (station, network_add_foreach, station);station_autoconnect_next (station); }
Signature · 1 line
2.21.1
PATCH v3 5/6 station: cancel hidden network scan when connecting
In reply to James PrestwoodBefore connecting to a hidden network we must scan. During this scan if another connection attempt comes in the expected behavior is to abort the original connection. Rather than waiting for the scan to complete, then canceling the original hidden connection we can just cancel the hidden scan immediately, reply to dbus, and continue with the new connection attempt.
--- src/station.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
src/station.c +15 -0
@@ -2474,6 +2474,19 @@ void station_connect_network(struct station *station, struct network *network,struct l_dbus *dbus =dbus_get_bus ();int err; +/* +* If a hidden scan is not completed, station_is_busy would not +* indicate anything is going on so we need to cancel the scan and +* fail the connection now. +*/ +if (station->hidden_network_scan_id) { +scan_cancel (netdev_get_wdev_id (station->netdev), + station->hidden_network_scan_id); + +dbus_pending_reply (&station->hidden_pending, +dbus_error_failed (station->hidden_pending)); + } +if (station_is_busy (station)) {station_disconnect_onconnect (station, network, bss, message);
@@ -2525,6 +2538,8 @@ static bool station_hidden_network_scan_results(int err, msg = station->hidden_pending; station->hidden_pending =NULL ; +/* Zero this now so station_connect_network knows the scan is done */ + station->hidden_network_scan_id =0 ;if (err) {dbus_pending_reply (&msg,dbus_error_failed (msg));
Signature · 1 line
2.21.1
PATCH v3 6/6 netdev: use wiphy radio work queue for connections
In reply to James PrestwoodThis adds connection/FT attempts to the radio work queue. This will ensure that connections aren't delayed or done concurrently with scanning.
--- src/netdev.c | 86 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 20 deletions(-)
v3:
- Added check for netdev->work.id before signaling work as done. This is because AP/AdHoc both use some netdev APIs and these modules do not yet use the work queue.
src/netdev.c +66 -20
@@ -136,6 +136,9 @@ struct netdev {struct l_io *pae_io;/* for drivers without EAPoL over NL80211 */ +struct l_genl_msg *connect_cmd; +struct wiphy_radio_work_item work; + bool connected : 1; bool operational : 1; bool rekey_offload_support : 1;
@@ -485,6 +488,9 @@ static void netdev_preauth_destroy(void *data)static void netdev_connect_free (struct netdev *netdev) { +if (netdev->work.id) +wiphy_radio_work_done (netdev->wiphy, netdev->work.id); +if (netdev->sm) {eapol_sm_free (netdev->sm); netdev->sm =NULL ;
@@ -529,6 +535,7 @@ static void netdev_connect_free(struct netdev *netdev) netdev->in_ft = false; netdev->ignore_connect_event = false; netdev->expect_connect_failure = false; + netdev->connect_cmd =NULL ;netdev_rssi_polling_update (netdev);
@@ -625,12 +632,12 @@ static void netdev_free(void *data) netdev->mac_change_cmd_id =0 ; } +scan_wdev_remove (netdev->wdev_id); +if (netdev->events_ready)WATCHLIST_NOTIFY (&netdev_watches, netdev_watch_func_t, netdev,NETDEV_WATCH_EVENT_DEL ); -scan_wdev_remove (netdev->wdev_id); -watchlist_destroy (&netdev->station_watches);l_io_destroy (netdev->pae_io);
@@ -1019,6 +1026,9 @@ static void netdev_connect_ok(struct netdev *netdev) }netdev_rssi_polling_update (netdev); + +if (netdev->work.id) +wiphy_radio_work_done (netdev->wiphy, netdev->work.id); } static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
@@ -1487,6 +1497,9 @@ void netdev_handshake_failed(struct handshake_state *hs, uint16_t reason_code)if (!l_genl_family_send (nl80211, msg,NULL ,NULL ,NULL ))l_error ("error sending DEL_STATION" ); } + +if (netdev->work.id) +wiphy_radio_work_done (netdev->wiphy, netdev->work.id); }static void hardware_rekey_cb (struct l_genl_msg *msg,void *data)
@@ -2426,25 +2439,60 @@ struct rtnl_data {int ref; }; -static int netdev_begin_connection (struct netdev *netdev, -struct l_genl_msg *cmd_connect) +static bool netdev_connection_ready (struct wiphy_radio_work_item *item) { -if (cmd_connect) { + struct netdev *netdev = l_container_of(item, struct netdev, work); + +if (netdev->connect_cmd) { netdev->connect_cmd_id =l_genl_family_send (nl80211, - cmd_connect, netdev_cmd_connect_cb, - netdev,NULL ); + netdev->connect_cmd, + netdev_cmd_connect_cb, netdev,NULL );if (!netdev->connect_cmd_id) { -l_genl_msg_unref (cmd_connect); -return -EIO ; +l_genl_msg_unref (netdev->connect_cmd); + netdev->connect_cmd =NULL ; + +netdev_connect_failed (netdev,NETDEV_RESULT_ABORTED , +MMPDU_STATUS_CODE_UNSPECIFIED ); +return true; } + + netdev->connect_cmd =NULL ; } -handshake_state_set_supplicant_address (netdev->handshake, netdev->addr); +if (netdev->ap) { +if (!auth_proto_start (netdev->ap)) { +/* Restore original nonce if this was an FT attempt */ +if (netdev->in_ft) +memcpy (netdev->handshake->snonce, + netdev->prev_snonce,32 ); + +netdev_connect_failed (netdev,NETDEV_RESULT_ABORTED , +MMPDU_STATUS_CODE_UNSPECIFIED ); +return true; + } +/* +* set connected since the auth protocols cannot do +* so internally +*/ -/* set connected since the auth protocols cannot do so internally */ -if (netdev->ap &&auth_proto_start (netdev->ap)) netdev->connected = true; + } + +return false; +} + +static const struct wiphy_radio_work_item_ops connect_work_ops = { + .do_work = netdev_connection_ready, +}; + +static int netdev_begin_connection (struct netdev *netdev, +struct l_genl_msg *cmd_connect) +{ + netdev->connect_cmd = cmd_connect; + +wiphy_radio_work_insert (netdev->wiphy, &netdev->work,1 , + &connect_work_ops);return 0 ; }
@@ -2514,6 +2562,8 @@ static void netdev_mac_power_up_cb(int error, uint16_t type,return ; } +handshake_state_set_supplicant_address (netdev->handshake, netdev->addr); +/* * Pick up where we left off in netdev_connect_commmon. */
@@ -2649,6 +2699,8 @@ static int netdev_connect_common(struct netdev *netdev,return ret; } +handshake_state_set_supplicant_address (netdev->handshake, netdev->addr); +return netdev_begin_connection (netdev, cmd_connect); }
@@ -3092,7 +3144,6 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss, netdev_connect_cb_t cb) {struct netdev_handshake_state *nhs; -int err = -EINVAL ;if (!netdev->operational)return -ENOTCONN ;
@@ -3173,15 +3224,10 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss, netdev_ft_over_ds_tx_authenticate, netdev_ft_tx_associate, netdev); -if (!auth_proto_start (netdev->ap)) - goto restore_snonce; +wiphy_radio_work_insert (netdev->wiphy, &netdev->work,1 , + &connect_work_ops);return 0 ; - -restore_snonce: -memcpy (netdev->handshake->snonce, netdev->prev_snonce,32 ); - -return err; } int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
Signature · 1 line
2.21.1
Hi James,
Quoted 26 lines · James Prestwood
These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.).
Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (
wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item.A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting).
Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
Quoted 9 lines
--- src/wiphy.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wiphy.h | 22 +++++++++++ 2 files changed, 134 insertions(+)v2:
- Better optimizedwiphy_radio_work_doneto not iterate the queue more than it needs to.
Patches 1-3, 5 applied now, thanks.
Regards,
-Denis