diff --git a/src/scan.c b/src/scan.c index 6fa18c7f..6748db8b 100644 --- a/src/scan.c +++ b/src/scan.c @@ -52,12 +52,24 @@ struct scan_periodic { uint16_t interval; scan_notify_func_t callback; void *userdata; + bool rearm:1; + bool retry:1; +}; + +struct scan_request { + scan_trigger_func_t trigger; + scan_notify_func_t callback; + void *userdata; + scan_destroy_func_t destroy; + bool passive:1; /* Active or Passive scan? */ + bool triggered:1; }; struct scan_context { uint32_t ifindex; enum scan_state state; struct scan_periodic sp; + struct l_queue *requests; }; struct scan_results { @@ -67,8 +79,11 @@ struct scan_results { struct scan_freq_set *freqs; scan_notify_func_t callback; void *userdata; + scan_destroy_func_t destroy; }; +static void scan_done(struct l_genl_msg *msg, void *userdata); + static bool scan_context_match(const void *a, const void *b) { const struct scan_context *sc = a; @@ -77,6 +92,13 @@ static bool scan_context_match(const void *a, const void *b) return (sc->ifindex == ifindex); } +static void scan_request_free(void *data) +{ + struct scan_request *sr = data; + + free(sr); +} + static struct scan_context *scan_context_new(uint32_t ifindex) { struct scan_context *sc; @@ -85,6 +107,7 @@ static struct scan_context *scan_context_new(uint32_t ifindex) sc->ifindex = ifindex; sc->state = SCAN_STATE_NOT_RUNNING; + sc->requests = l_queue_new(); return sc; } @@ -93,6 +116,8 @@ static void scan_context_free(struct scan_context *sc) { l_debug("sc: %p", sc); + l_queue_destroy(sc->requests, scan_request_free); + if (sc->sp.timeout) l_timeout_remove(sc->sp.timeout); @@ -131,14 +156,127 @@ bool scan_ifindex_remove(uint32_t ifindex) return true; } -void scan_start(struct l_genl_family *nl80211, uint32_t ifindex, - scan_func_t callback, void *user_data) +static bool __scan_passive_start(struct l_genl_family *nl80211, + uint32_t ifindex, + scan_func_t callback, void *user_data) { struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_TRIGGER_SCAN, 16); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); - l_genl_family_send(nl80211, msg, callback, user_data, NULL); + + if (!l_genl_family_send(nl80211, msg, callback, user_data, NULL)) { + l_error("Starting scheduled scan failed"); + return false; + } + + return true; +} + +static void start_next_scan_request(void *userdata) +{ + uint32_t ifindex = L_PTR_TO_UINT(userdata); + struct scan_context *sc; + struct scan_request *sr; + bool r; + + sc = l_queue_find(scan_contexts, scan_context_match, + L_UINT_TO_PTR(ifindex)); + + if (!sc) + return; + + sr = l_queue_peek_head(sc->requests); + + r = __scan_passive_start(nl80211, ifindex, scan_done, sc); + + if (!r) { + l_error("Could not send CMD_TRIGGER_SCAN"); + + if (sr->destroy) + sr->destroy(sr->userdata); + + sr = l_queue_pop_head(sc->requests); + scan_request_free(sr); + + if (!l_queue_isempty(sc->requests)) + l_idle_oneshot(start_next_scan_request, + L_UINT_TO_PTR(sc->ifindex), NULL); + } +} + +static void scan_done(struct l_genl_msg *msg, void *userdata) +{ + struct scan_context *sc = userdata; + struct scan_request *sr = l_queue_peek_head(sc->requests); + int err; + + l_debug(""); + + err = l_genl_msg_get_error(msg); + if (err < 0) { + /* Scan in progress, defer */ + if (err == -EBUSY) + return; + + if (sr->trigger) + sr->trigger(err, sr->userdata); + + if (sr->destroy) + sr->destroy(sr->userdata); + + l_queue_pop_head(sc->requests); + scan_request_free(sr); + + l_error("Received an error during CMD_TRIGGER_SCAN"); + + if (!l_queue_isempty(sc->requests)) + l_idle_oneshot(start_next_scan_request, + L_UINT_TO_PTR(sc->ifindex), NULL); + + return; + } + + sc->state = sr->passive ? SCAN_STATE_PASSIVE : SCAN_STATE_ACTIVE; + l_debug("Passive scan triggered for ifindex: %u", sc->ifindex); + sr->triggered = true; +} + +bool scan_passive(uint32_t ifindex, scan_trigger_func_t trigger, + scan_notify_func_t notify, void *userdata, + scan_destroy_func_t destroy) +{ + struct scan_context *sc; + struct scan_request *sr; + + sc = l_queue_find(scan_contexts, scan_context_match, + L_UINT_TO_PTR(ifindex)); + + if (!sc) + return false; + + sr = l_new(struct scan_request, 1); + sr->trigger = trigger; + sr->callback = notify; + sr->userdata = userdata; + sr->destroy = destroy; + sr->passive = true; + + if (l_queue_length(sc->requests) > 0) + goto done; + + if (sc->state != SCAN_STATE_NOT_RUNNING) + goto done; + + if (!__scan_passive_start(nl80211, ifindex, scan_done, sc)) { + scan_request_free(sr); + return false; + } + +done: + l_queue_push_tail(sc->requests, sr); + + return true; } void scan_sched_start(struct l_genl_family *nl80211, uint32_t ifindex, @@ -165,6 +303,7 @@ static void scan_periodic_done(struct l_genl_msg *msg, void *user_data) int err; l_debug(""); + sc->sp.rearm = true; err = l_genl_msg_get_error(msg); if (err < 0) { @@ -173,6 +312,8 @@ static void scan_periodic_done(struct l_genl_msg *msg, void *user_data) l_warn("Periodic scan could not be triggered: %s (%d)", strerror(-err), -err); + sc->sp.retry = true; + return; } @@ -201,8 +342,10 @@ void scan_periodic_start(uint32_t ifindex, scan_notify_func_t func, sc->sp.interval = SCAN_INIT_INTERVAL; sc->sp.callback = func; sc->sp.userdata = userdata; + sc->sp.retry = false; + sc->sp.rearm = false; - scan_start(nl80211, ifindex, scan_periodic_done, sc); + __scan_passive_start(nl80211, ifindex, scan_periodic_done, sc); } bool scan_periodic_stop(uint32_t ifindex) @@ -224,6 +367,8 @@ bool scan_periodic_stop(uint32_t ifindex) sc->sp.interval = 0; sc->sp.callback = NULL; sc->sp.userdata = NULL; + sc->sp.rearm = false; + sc->sp.retry = false; return true; } @@ -235,7 +380,7 @@ static void scan_periodic_timeout(struct l_timeout *timeout, void *user_data) l_debug("scan_periodic_timeout: %u", sc->ifindex); sc->sp.interval *= 2; - scan_start(nl80211, sc->ifindex, scan_periodic_done, sc); + __scan_passive_start(nl80211, sc->ifindex, scan_periodic_done, sc); } static void scan_periodic_rearm(struct scan_context *sc) @@ -247,6 +392,8 @@ static void scan_periodic_rearm(struct scan_context *sc) else sc->sp.timeout = l_timeout_create(sc->sp.interval, scan_periodic_timeout, sc, NULL); + + sc->sp.rearm = false; } enum scan_ssid_security scan_get_ssid_security( @@ -706,6 +853,7 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data) { struct l_genl_msg *scan_msg; struct scan_results *results; + struct scan_request *sr; results = l_new(struct scan_results, 1); results->wiphy = attr_wiphy; @@ -713,11 +861,20 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data) scan_parse_new_scan_results(msg, results); - /* - * Can't use active_scan here, since NEW_SCAN_RESULTS always - * contains this attribute - */ - if (sc->state == SCAN_STATE_PASSIVE) { + sr = l_queue_peek_head(sc->requests); + if (sr && sr->triggered) { + results->callback = sr->callback; + results->userdata = sr->userdata; + results->destroy = sr->destroy; + + scan_request_free(sr); + l_queue_pop_head(sc->requests); + } else if (sc->state == SCAN_STATE_PASSIVE && + sc->sp.interval != 0) { + /* + * Can't use active_scan here, since + * NEW_SCAN_RESULTS always contains this attribute + */ results->callback = sc->sp.callback; results->userdata = sc->sp.userdata; } @@ -728,11 +885,19 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data) l_genl_family_dump(nl80211, scan_msg, get_scan_callback, results, get_scan_done); - if (sc->state == SCAN_STATE_PASSIVE && sc->sp.interval != 0) - scan_periodic_rearm(sc); - sc->state = SCAN_STATE_NOT_RUNNING; + if (!l_queue_isempty(sc->requests)) { + l_idle_oneshot(start_next_scan_request, + L_UINT_TO_PTR(sc->ifindex), NULL); + } else if (sc->sp.retry) { + __scan_passive_start(nl80211, sc->ifindex, + scan_periodic_done, sc); + sc->sp.retry = false; + } else if (sc->sp.rearm) { + scan_periodic_rearm(sc); + } + break; } diff --git a/src/scan.h b/src/scan.h index 27adf28b..3dbaa3a3 100644 --- a/src/scan.h +++ b/src/scan.h @@ -39,9 +39,11 @@ enum scan_state { }; typedef void (*scan_func_t)(struct l_genl_msg *msg, void *user_data); +typedef void (*scan_trigger_func_t)(int, void *); typedef bool (*scan_notify_func_t)(uint32_t wiphy, uint32_t ifindex, struct l_queue *bss_list, void *userdata); +typedef void (*scan_destroy_func_t)(void *userdata); struct scan_freq_set; struct ie_rsn_info; @@ -61,8 +63,9 @@ struct scan_bss { uint16_t rank; }; -void scan_start(struct l_genl_family *nl80211, uint32_t ifindex, - scan_func_t callback, void *user_data); +bool scan_passive(uint32_t ifindex, scan_trigger_func_t trigger, + scan_notify_func_t notify, void *userdata, + scan_destroy_func_t destroy); void scan_periodic_start(uint32_t ifindex, scan_notify_func_t func, void *userdata); bool scan_periodic_stop(uint32_t ifindex); diff --git a/src/wiphy.c b/src/wiphy.c index cbb676dd..a7dd3d04 100644 --- a/src/wiphy.c +++ b/src/wiphy.c @@ -723,14 +723,14 @@ static struct l_dbus_message *device_get_properties(struct l_dbus *dbus, return reply; } -static void device_scan_callback(struct l_genl_msg *msg, void *user_data) +static void device_scan_triggered(int err, void *user_data) { struct netdev *netdev = user_data; struct l_dbus_message *reply; - l_debug("Scan callback"); + l_debug("device_scan_triggered: %i", err); - if (l_genl_msg_get_error(msg) < 0) { + if (err < 0) { dbus_pending_reply(&netdev->scan_pending, dbus_error_failed(netdev->scan_pending)); return; @@ -756,7 +756,9 @@ static struct l_dbus_message *device_scan(struct l_dbus *dbus, netdev->scan_pending = l_dbus_message_ref(message); - scan_start(nl80211, netdev->index, device_scan_callback, netdev); + if (!scan_passive(netdev->index, device_scan_triggered, + new_scan_results, netdev, NULL)) + return dbus_error_failed(message); return NULL; }