Allow external programs to request wpa_radio work items
The new control interface command RADIO_WORK can be used by external programs to request radio allocation slots from wpa_supplicant if exclusive radio control is needed, e.g., for offchannel operations. If such operations are done directly to the driver, wpa_supplicant may not have enough information to avoid conflicting operations. This new command can be used to provide enough information and radio scheduling to avoid issues with such cases. Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
e766f56643
commit
1f965e622a
7 changed files with 283 additions and 3 deletions
|
@ -162,6 +162,9 @@ extern "C" {
|
||||||
/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
|
/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
|
||||||
#define GAS_QUERY_DONE "GAS-QUERY-DONE "
|
#define GAS_QUERY_DONE "GAS-QUERY-DONE "
|
||||||
|
|
||||||
|
#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
|
||||||
|
#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
|
||||||
|
|
||||||
/* hostapd control interface - fixed message prefixes */
|
/* hostapd control interface - fixed message prefixes */
|
||||||
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
|
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
|
||||||
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
|
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
|
||||||
|
|
|
@ -983,3 +983,71 @@ directory could be created before starting the wpa_supplicant and set to
|
||||||
suitable mode to allow wpa_supplicant to create sockets
|
suitable mode to allow wpa_supplicant to create sockets
|
||||||
there. Alternatively, other directory or abstract socket namespace could
|
there. Alternatively, other directory or abstract socket namespace could
|
||||||
be used for the control interface.
|
be used for the control interface.
|
||||||
|
|
||||||
|
|
||||||
|
External requests for radio control
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
External programs can request wpa_supplicant to not start offchannel
|
||||||
|
operations during other tasks that may need exclusive control of the
|
||||||
|
radio. The RADIO_WORK control interface command can be used for this.
|
||||||
|
|
||||||
|
"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be
|
||||||
|
used to reserve a slot for radio access. If freq is specified, other
|
||||||
|
radio work items on the same channel may be completed in
|
||||||
|
parallel. Otherwise, all other radio work items are blocked during
|
||||||
|
execution. Timeout is set to 10 seconds by default to avoid blocking
|
||||||
|
wpa_supplicant operations for excessive time. If a longer (or shorter)
|
||||||
|
safety timeout is needed, that can be specified with the optional
|
||||||
|
timeout parameter. This command returns an identifier for the radio work
|
||||||
|
item.
|
||||||
|
|
||||||
|
Once the radio work item has been started, "EXT-RADIO-WORK-START <id>"
|
||||||
|
event message is indicated that the external processing can start. Once
|
||||||
|
the operation has been completed, "RADIO_WORK done <id>" is used to
|
||||||
|
indicate that to wpa_supplicant. This allows other radio works to be
|
||||||
|
performed. If this command is forgotten (e.g., due to the external
|
||||||
|
program terminating), wpa_supplicant will time out the radio owrk item
|
||||||
|
and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has
|
||||||
|
happened. "RADIO_WORK done <id>" can also be used to cancel items that
|
||||||
|
have not yet been started.
|
||||||
|
|
||||||
|
For example, in wpa_cli interactive mode:
|
||||||
|
|
||||||
|
> radio_work add test
|
||||||
|
1
|
||||||
|
<3>EXT-RADIO-WORK-START 1
|
||||||
|
> radio_work show
|
||||||
|
ext:test@wlan0:0:1:2.487797
|
||||||
|
> radio_work done 1
|
||||||
|
OK
|
||||||
|
> radio_work show
|
||||||
|
|
||||||
|
|
||||||
|
> radio_work done 3
|
||||||
|
OK
|
||||||
|
> radio_work show
|
||||||
|
ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483
|
||||||
|
<3>EXT-RADIO-WORK-TIMEOUT 2
|
||||||
|
|
||||||
|
|
||||||
|
> radio_work add test2 freq=2412 timeout=60
|
||||||
|
5
|
||||||
|
<3>EXT-RADIO-WORK-START 5
|
||||||
|
> radio_work add test3
|
||||||
|
6
|
||||||
|
> radio_work add test4
|
||||||
|
7
|
||||||
|
> radio_work show
|
||||||
|
ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844
|
||||||
|
ext:test3@wlan0:0:0:5.071812
|
||||||
|
ext:test4@wlan0:0:0:3.143870
|
||||||
|
> radio_work done 6
|
||||||
|
OK
|
||||||
|
> radio_work show
|
||||||
|
ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869
|
||||||
|
ext:test4@wlan0:0:0:9.679895
|
||||||
|
> radio_work done 5
|
||||||
|
OK
|
||||||
|
<3>EXT-RADIO-WORK-START 7
|
||||||
|
<3>EXT-RADIO-WORK-TIMEOUT 7
|
||||||
|
|
|
@ -5221,6 +5221,186 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
struct wpa_radio_work *work;
|
||||||
|
char *pos, *end;
|
||||||
|
struct os_reltime now, diff;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
end = buf + buflen;
|
||||||
|
|
||||||
|
os_get_reltime(&now);
|
||||||
|
|
||||||
|
dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
os_reltime_sub(&now, &work->time, &diff);
|
||||||
|
ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
|
||||||
|
work->type, work->wpa_s->ifname, work->freq,
|
||||||
|
work->started, diff.sec, diff.usec);
|
||||||
|
if (ret < 0 || ret >= end - pos)
|
||||||
|
break;
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos - buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
|
||||||
|
{
|
||||||
|
struct wpa_radio_work *work = eloop_ctx;
|
||||||
|
struct wpa_external_work *ework = work->ctx;
|
||||||
|
|
||||||
|
wpa_dbg(work->wpa_s, MSG_DEBUG,
|
||||||
|
"Timing out external radio work %u (%s)",
|
||||||
|
ework->id, work->type);
|
||||||
|
wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
|
||||||
|
os_free(ework);
|
||||||
|
radio_work_done(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
|
||||||
|
{
|
||||||
|
struct wpa_external_work *ework = work->ctx;
|
||||||
|
|
||||||
|
if (deinit) {
|
||||||
|
os_free(ework);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
|
||||||
|
ework->id, ework->type);
|
||||||
|
wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
|
||||||
|
if (!ework->timeout)
|
||||||
|
ework->timeout = 10;
|
||||||
|
eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
|
||||||
|
work, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
struct wpa_external_work *ework;
|
||||||
|
char *pos, *pos2;
|
||||||
|
size_t type_len;
|
||||||
|
int ret;
|
||||||
|
unsigned int freq = 0;
|
||||||
|
|
||||||
|
/* format: <name> [freq=<MHz>] [timeout=<seconds>] */
|
||||||
|
|
||||||
|
ework = os_zalloc(sizeof(*ework));
|
||||||
|
if (ework == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pos = os_strchr(cmd, ' ');
|
||||||
|
if (pos) {
|
||||||
|
type_len = pos - cmd;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
pos2 = os_strstr(pos, "freq=");
|
||||||
|
if (pos2)
|
||||||
|
freq = atoi(pos2 + 5);
|
||||||
|
|
||||||
|
pos2 = os_strstr(pos, "timeout=");
|
||||||
|
if (pos2)
|
||||||
|
ework->timeout = atoi(pos2 + 8);
|
||||||
|
} else {
|
||||||
|
type_len = os_strlen(cmd);
|
||||||
|
}
|
||||||
|
if (4 + type_len >= sizeof(ework->type))
|
||||||
|
type_len = sizeof(ework->type) - 4 - 1;
|
||||||
|
os_strlcpy(ework->type, "ext:", sizeof(ework->type));
|
||||||
|
os_memcpy(ework->type + 4, cmd, type_len);
|
||||||
|
ework->type[4 + type_len] = '\0';
|
||||||
|
|
||||||
|
wpa_s->ext_work_id++;
|
||||||
|
if (wpa_s->ext_work_id == 0)
|
||||||
|
wpa_s->ext_work_id++;
|
||||||
|
ework->id = wpa_s->ext_work_id;
|
||||||
|
|
||||||
|
if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
|
||||||
|
ework) < 0) {
|
||||||
|
os_free(ework);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = os_snprintf(buf, buflen, "%u", ework->id);
|
||||||
|
if (ret < 0 || (size_t) ret >= buflen)
|
||||||
|
return -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
|
||||||
|
{
|
||||||
|
struct wpa_radio_work *work;
|
||||||
|
unsigned int id = atoi(cmd);
|
||||||
|
|
||||||
|
dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
|
||||||
|
{
|
||||||
|
struct wpa_external_work *ework;
|
||||||
|
|
||||||
|
if (os_strncmp(work->type, "ext:", 4) != 0)
|
||||||
|
continue;
|
||||||
|
ework = work->ctx;
|
||||||
|
if (id && ework->id != id)
|
||||||
|
continue;
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||||
|
"Completed external radio work %u (%s)",
|
||||||
|
ework->id, ework->type);
|
||||||
|
eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
|
||||||
|
os_free(ework);
|
||||||
|
radio_work_done(work);
|
||||||
|
return 3; /* "OK\n" */
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
if (os_strcmp(cmd, "show") == 0)
|
||||||
|
return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
|
||||||
|
if (os_strncmp(cmd, "add ", 4) == 0)
|
||||||
|
return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
|
||||||
|
if (os_strncmp(cmd, "done ", 5) == 0)
|
||||||
|
return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
struct wpa_radio_work *work, *tmp;
|
||||||
|
|
||||||
|
dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
|
||||||
|
struct wpa_radio_work, list) {
|
||||||
|
struct wpa_external_work *ework;
|
||||||
|
|
||||||
|
if (os_strncmp(work->type, "ext:", 4) != 0)
|
||||||
|
continue;
|
||||||
|
ework = work->ctx;
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||||
|
"Flushing %sexternal radio work %u (%s)",
|
||||||
|
work->started ? " started" : "", ework->id,
|
||||||
|
ework->type);
|
||||||
|
if (work->started)
|
||||||
|
eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
|
||||||
|
work, NULL);
|
||||||
|
os_free(ework);
|
||||||
|
radio_work_done(work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
|
static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
|
||||||
{
|
{
|
||||||
struct wpa_supplicant *wpa_s = eloop_ctx;
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
||||||
|
@ -5873,6 +6053,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
#endif /* CONFIG_WNM */
|
#endif /* CONFIG_WNM */
|
||||||
} else if (os_strcmp(buf, "FLUSH") == 0) {
|
} else if (os_strcmp(buf, "FLUSH") == 0) {
|
||||||
wpa_supplicant_ctrl_iface_flush(wpa_s);
|
wpa_supplicant_ctrl_iface_flush(wpa_s);
|
||||||
|
} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
|
||||||
|
reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
|
||||||
|
reply_size);
|
||||||
} else {
|
} else {
|
||||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
||||||
reply_len = 16;
|
reply_len = 16;
|
||||||
|
|
|
@ -113,6 +113,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
|
||||||
void wpa_supplicant_global_ctrl_iface_deinit(
|
void wpa_supplicant_global_ctrl_iface_deinit(
|
||||||
struct ctrl_iface_global_priv *priv);
|
struct ctrl_iface_global_priv *priv);
|
||||||
|
|
||||||
|
void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
|
||||||
|
|
||||||
#else /* CONFIG_CTRL_IFACE */
|
#else /* CONFIG_CTRL_IFACE */
|
||||||
|
|
||||||
static inline struct ctrl_iface_priv *
|
static inline struct ctrl_iface_priv *
|
||||||
|
@ -148,6 +150,10 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_CTRL_IFACE */
|
#endif /* CONFIG_CTRL_IFACE */
|
||||||
|
|
||||||
#endif /* CTRL_IFACE_H */
|
#endif /* CTRL_IFACE_H */
|
||||||
|
|
|
@ -2426,6 +2426,12 @@ static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum wpa_cli_cmd_flags {
|
enum wpa_cli_cmd_flags {
|
||||||
cli_cmd_flag_none = 0x00,
|
cli_cmd_flag_none = 0x00,
|
||||||
cli_cmd_flag_sensitive = 0x01
|
cli_cmd_flag_sensitive = 0x01
|
||||||
|
@ -2893,6 +2899,8 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
|
||||||
{ "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
|
{ "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
|
||||||
"<command> = driver private commands" },
|
"<command> = driver private commands" },
|
||||||
#endif /* ANDROID */
|
#endif /* ANDROID */
|
||||||
|
{ "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none,
|
||||||
|
"= radio_work <show/add/done>" },
|
||||||
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3185,12 +3185,15 @@ void radio_work_done(struct wpa_radio_work *work)
|
||||||
{
|
{
|
||||||
struct wpa_supplicant *wpa_s = work->wpa_s;
|
struct wpa_supplicant *wpa_s = work->wpa_s;
|
||||||
struct os_reltime now, diff;
|
struct os_reltime now, diff;
|
||||||
|
unsigned int started = work->started;
|
||||||
|
|
||||||
os_get_reltime(&now);
|
os_get_reltime(&now);
|
||||||
os_reltime_sub(&now, &work->time, &diff);
|
os_reltime_sub(&now, &work->time, &diff);
|
||||||
wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p done in %ld.%06ld seconds",
|
wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
|
||||||
work->type, work, diff.sec, diff.usec);
|
work->type, work, started ? "done" : "canceled",
|
||||||
|
diff.sec, diff.usec);
|
||||||
radio_work_free(work);
|
radio_work_free(work);
|
||||||
|
if (started)
|
||||||
radio_work_check_next(wpa_s);
|
radio_work_check_next(wpa_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3514,6 +3517,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_P2P */
|
#endif /* CONFIG_P2P */
|
||||||
|
|
||||||
|
wpas_ctrl_radio_work_flush(wpa_s);
|
||||||
radio_remove_interface(wpa_s);
|
radio_remove_interface(wpa_s);
|
||||||
|
|
||||||
if (wpa_s->drv_priv)
|
if (wpa_s->drv_priv)
|
||||||
|
|
|
@ -317,6 +317,12 @@ int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
|
||||||
void wpas_connect_work_free(struct wpa_connect_work *cwork);
|
void wpas_connect_work_free(struct wpa_connect_work *cwork);
|
||||||
void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
|
void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
|
||||||
|
|
||||||
|
struct wpa_external_work {
|
||||||
|
unsigned int id;
|
||||||
|
char type[100];
|
||||||
|
unsigned int timeout;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* offchannel_send_action_result - Result of offchannel send Action frame
|
* offchannel_send_action_result - Result of offchannel send Action frame
|
||||||
*/
|
*/
|
||||||
|
@ -788,6 +794,8 @@ struct wpa_supplicant {
|
||||||
|
|
||||||
unsigned int num_multichan_concurrent;
|
unsigned int num_multichan_concurrent;
|
||||||
struct wpa_radio_work *connect_work;
|
struct wpa_radio_work *connect_work;
|
||||||
|
|
||||||
|
unsigned int ext_work_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue