static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+#define IFIDX_ANY -1
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan);
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
- have_ifidx(drv, idx))
+ have_ifidx(drv, idx, IFIDX_ANY))
return drv;
}
return NULL;
}
wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
brid, namebuf);
- add_ifidx(drv, brid);
+ add_ifidx(drv, brid, ifi->ifi_index);
for (bss = drv->first_bss; bss; bss = bss->next) {
if (os_strcmp(ifname, bss->ifname) == 0) {
"nl80211: Remove ifindex %u for bridge %s",
brid, namebuf);
}
- del_ifidx(drv, brid);
+ del_ifidx(drv, brid, ifi->ifi_index);
}
}
static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
+ struct wpa_driver_nl80211_data *drv = ctx;
+
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+
/*
- * This may be for any interface; use ifdown event to disable
- * interface.
+ * rtnetlink ifdown handler will report interfaces other than the P2P
+ * Device interface as disabled.
*/
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
}
if (is_p2p_net_interface(drv->nlmode))
nl80211_disable_11b_rates(drv, drv->ifindex, 1);
- /* rtnetlink ifup handler will report interface as enabled */
+ /*
+ * rtnetlink ifup handler will report interfaces other than the P2P
+ * Device interface as enabled.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
}
}
+static void
+wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
+{
+ struct rfkill_config *rcfg;
+
+ if (drv->rfkill)
+ return;
+
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (!rcfg)
+ return;
+
+ rcfg->ctx = drv;
+
+ /* rfkill uses netdev sysfs for initialization. However, P2P Device is
+ * not associated with a netdev, so use the name of some other interface
+ * sharing the same wiphy as the P2P Device interface.
+ *
+ * Note: This is valid, as a P2P Device interface is always dynamically
+ * created and is created only once another wpa_s interface was added.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ struct nl80211_global *global = drv->global;
+ struct wpa_driver_nl80211_data *tmp1;
+
+ dl_list_for_each(tmp1, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
+ !tmp1->rfkill)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Use (%s) to initialize P2P Device rfkill",
+ tmp1->first_bss->ifname);
+ os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
+ sizeof(rcfg->ifname));
+ break;
+ }
+ } else {
+ os_strlcpy(rcfg->ifname, drv->first_bss->ifname,
+ sizeof(rcfg->ifname));
+ }
+
+ rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (!drv->rfkill) {
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+ os_free(rcfg);
+ }
+}
+
+
static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
void *global_priv, int hostapd,
const u8 *set_addr,
const char *driver_params)
{
struct wpa_driver_nl80211_data *drv;
- struct rfkill_config *rcfg;
struct i802_bss *bss;
if (global_priv == NULL)
drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
drv->if_indices = drv->default_if_indices;
+ drv->if_indices_reason = drv->default_if_indices_reason;
drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
if (!drv->first_bss) {
if (nl80211_init_bss(bss))
goto failed;
- rcfg = os_zalloc(sizeof(*rcfg));
- if (rcfg == NULL)
- goto failed;
- rcfg->ctx = drv;
- os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
- rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
- rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
- drv->rfkill = rfkill_init(rcfg);
- if (drv->rfkill == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
- os_free(rcfg);
- }
-
- if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0)
- drv->start_iface_up = 1;
-
if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
goto failed;
if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
bss->static_ap = 1;
+ if (first &&
+ nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+ linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
+ drv->start_iface_up = 1;
+
if (wpa_driver_nl80211_capa(drv))
return -1;
if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
nl80211_get_macaddr(bss);
+ wpa_driver_nl80211_drv_init_rfkill(drv);
+
if (!rfkill_is_blocked(drv->rfkill)) {
int ret = i802_set_iface_flags(bss, 1);
if (ret) {
} else {
wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
"interface '%s' due to rfkill", bss->ifname);
- if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
- return 0;
- drv->if_disabled = 1;
+ if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
+ drv->if_disabled = 1;
+
send_rfkill_event = 1;
}
- if (!drv->hostapd)
+ if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
- if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
- bss->addr))
- return -1;
- os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+ if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ bss->addr))
+ return -1;
+ os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+ }
if (send_rfkill_event) {
eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
if (drv->if_indices != drv->default_if_indices)
os_free(drv->if_indices);
+ if (drv->if_indices_reason != drv->default_if_indices_reason)
+ os_free(drv->if_indices_reason);
+
if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
}
#endif /* CONFIG_P2P */
+ if (params->pbss) {
+ wpa_printf(MSG_DEBUG, "nl80211: PBSS");
+ if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
if (flags & WPA_STA_AUTHENTICATED)
f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (flags & WPA_STA_ASSOCIATED)
+ f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
return f;
}
if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
goto fail;
- if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+ /*
+ * Set the below properties only in one of the following cases:
+ * 1. New station is added, already associated.
+ * 2. Set WPA_STA_TDLS_PEER station.
+ * 3. Set an already added unassociated station, if driver supports
+ * full AP client state. (Set these properties after station became
+ * associated will be rejected by the driver).
+ */
+ if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+ (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+ (params->flags & WPA_STA_ASSOCIATED))) {
wpa_hexdump(MSG_DEBUG, " * supported rates",
params->supp_rates, params->supp_rates_len);
wpa_printf(MSG_DEBUG, " * capability=0x%x",
/*
* cfg80211 validates that AID is non-zero, so we have
* to make this a non-zero value for the TDLS case where
- * a dummy STA entry is used for now.
+ * a dummy STA entry is used for now and for a station
+ * that is still not associated.
*/
- wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)");
+ wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)",
+ (params->flags & WPA_STA_TDLS_PEER) ?
+ "TDLS" : "UNASSOC_STA");
if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
goto fail;
}
wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid);
if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
goto fail;
+ } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+ (params->flags & WPA_STA_ASSOCIATED)) {
+ wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
+ wpa_printf(MSG_DEBUG, " * listen_interval=%u",
+ params->listen_interval);
+ if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
+ nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval))
+ goto fail;
}
if (params->vht_opmode_enabled) {
os_memset(&upd, 0, sizeof(upd));
upd.set = sta_flags_nl80211(params->flags);
upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+
+ /*
+ * If the driver doesn't support full AP client state, ignore ASSOC/AUTH
+ * flags, as nl80211 driver moves a new station, by default, into
+ * associated state.
+ *
+ * On the other hand, if the driver supports that feature and the
+ * station is added in unauthenticated state, set the
+ * authenticated/associated bits in the mask to prevent moving this
+ * station to associated state before it is actually associated.
+ *
+ * This is irrelevant for mesh mode where the station is added to the
+ * driver as authenticated already, and ASSOCIATED isn't part of the
+ * nl80211 API.
+ */
+ if (!is_mesh_interface(drv->nlmode)) {
+ if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
+ upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED));
+ } else if (!params->set &&
+ !(params->flags & WPA_STA_TDLS_PEER)) {
+ if (!(params->flags & WPA_STA_AUTHENTICATED))
+ upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (!(params->flags & WPA_STA_ASSOCIATED))
+ upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ }
+ }
+
wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x",
upd.set, upd.mask);
if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
/* stop listening for EAPOL on this interface */
dl_list_for_each(drv2, &drv->global->interfaces,
struct wpa_driver_nl80211_data, list)
- del_ifidx(drv2, ifidx);
+ {
+ del_ifidx(drv2, ifidx, IFIDX_ANY);
+ /* Remove all bridges learned for this iface */
+ del_ifidx(drv2, IFIDX_ANY, ifidx);
+ }
msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
iftype == NL80211_IFTYPE_WDS ||
iftype == NL80211_IFTYPE_MONITOR) {
/* start listening for EAPOL on this interface */
- add_ifidx(drv, ifidx);
+ add_ifidx(drv, ifidx, IFIDX_ANY);
}
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
goto fail;
}
- if (nl80211_ht_vht_overrides(msg, params) < 0)
- return -1;
+ ret = nl80211_ht_vht_overrides(msg, params);
+ if (ret < 0)
+ goto fail;
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (params->p2p)
wpa_printf(MSG_DEBUG, " * P2P group");
+ if (params->pbss) {
+ wpa_printf(MSG_DEBUG, " * PBSS");
+ if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+ return -1;
+ }
+
return 0;
}
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
if (stats[NL80211_STA_INFO_INACTIVE_TIME])
data->inactive_msec =
nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+ /* For backwards compatibility, fetch the 32-bit counters first. */
if (stats[NL80211_STA_INFO_RX_BYTES])
data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
if (stats[NL80211_STA_INFO_TX_BYTES])
data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+ if (stats[NL80211_STA_INFO_RX_BYTES64] &&
+ stats[NL80211_STA_INFO_TX_BYTES64]) {
+ /*
+ * The driver supports 64-bit counters, so use them to override
+ * the 32-bit values.
+ */
+ data->rx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
+ data->tx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
+ data->bytes_64bit = 1;
+ }
if (stats[NL80211_STA_INFO_RX_PACKETS])
data->rx_packets =
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
for (i = 0; i < drv->num_if_indices; i++) {
if (!drv->if_indices[i])
continue;
- res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]);
+ res = os_snprintf(pos, end - pos, " %d(%d)",
+ drv->if_indices[i],
+ drv->if_indices_reason[i]);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
{
int i;
- int *old;
+ int *old, *old_reason;
- wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
- ifidx);
- if (have_ifidx(drv, ifidx)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
+ ifidx, ifidx_reason);
+ if (have_ifidx(drv, ifidx, ifidx_reason)) {
wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
ifidx);
return;
for (i = 0; i < drv->num_if_indices; i++) {
if (drv->if_indices[i] == 0) {
drv->if_indices[i] = ifidx;
+ drv->if_indices_reason[i] = ifidx_reason;
dump_ifidx(drv);
return;
}
else
old = NULL;
+ if (drv->if_indices_reason != drv->default_if_indices_reason)
+ old_reason = drv->if_indices_reason;
+ else
+ old_reason = NULL;
+
drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
sizeof(int));
+ drv->if_indices_reason = os_realloc_array(old_reason,
+ drv->num_if_indices + 1,
+ sizeof(int));
if (!drv->if_indices) {
if (!old)
drv->if_indices = drv->default_if_indices;
else
drv->if_indices = old;
+ }
+ if (!drv->if_indices_reason) {
+ if (!old_reason)
+ drv->if_indices_reason = drv->default_if_indices_reason;
+ else
+ drv->if_indices = old_reason;
+ }
+ if (!drv->if_indices || !drv->if_indices_reason) {
wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
"interfaces");
wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
return;
- } else if (!old)
+ }
+ if (!old)
os_memcpy(drv->if_indices, drv->default_if_indices,
sizeof(drv->default_if_indices));
+ if (!old_reason)
+ os_memcpy(drv->if_indices_reason,
+ drv->default_if_indices_reason,
+ sizeof(drv->default_if_indices_reason));
drv->if_indices[drv->num_if_indices] = ifidx;
+ drv->if_indices_reason[drv->num_if_indices] = ifidx_reason;
drv->num_if_indices++;
dump_ifidx(drv);
}
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
{
int i;
for (i = 0; i < drv->num_if_indices; i++) {
- if (drv->if_indices[i] == ifidx) {
+ if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) &&
+ (drv->if_indices_reason[i] == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY)) {
drv->if_indices[i] = 0;
break;
}
}
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
{
int i;
for (i = 0; i < drv->num_if_indices; i++)
- if (drv->if_indices[i] == ifidx)
+ if (drv->if_indices[i] == ifidx &&
+ (drv->if_indices_reason[i] == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY))
return 1;
return 0;
return;
}
- if (have_ifidx(drv, lladdr.sll_ifindex))
+ if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
}
}
bss->added_bridge = 1;
br_ifindex = if_nametoindex(brname);
- add_ifidx(drv, br_ifindex);
+ add_ifidx(drv, br_ifindex, drv->ifindex);
}
bss->br_ifindex = br_ifindex;
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
params->ifname, master_ifname);
/* start listening for EAPOL on the master interface */
- add_ifidx(drv, if_nametoindex(master_ifname));
+ add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex);
+
+ /* check if master itself is under bridge */
+ if (linux_br_get(master_ifname, master_ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
+ master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ }
} else {
master_ifname[0] = '\0';
}
if (params->bridge[i]) {
ifindex = if_nametoindex(params->bridge[i]);
if (ifindex)
- add_ifidx(drv, ifindex);
+ add_ifidx(drv, ifindex, drv->ifindex);
if (ifindex == br_ifindex)
br_added = 1;
}
}
/* start listening for EAPOL on the default AP interface */
- add_ifidx(drv, drv->ifindex);
+ add_ifidx(drv, drv->ifindex, IFIDX_ANY);
if (params->num_bridge && params->bridge[0]) {
if (i802_check_bridge(drv, bss, params->bridge[0],
if (!br_added && br_ifindex &&
(params->num_bridge == 0 || !params->bridge[0]))
- add_ifidx(drv, br_ifindex);
+ add_ifidx(drv, br_ifindex, drv->ifindex);
#ifdef CONFIG_LIBNL3_ROUTE
if (bss->added_if_into_bridge) {
nlmode == NL80211_IFTYPE_AP_VLAN ||
nlmode == NL80211_IFTYPE_WDS ||
nlmode == NL80211_IFTYPE_MONITOR))
- add_ifidx(drv, ifidx);
+ add_ifidx(drv, ifidx, IFIDX_ANY);
return 0;
}
else if (ifindex > 0 && !bss->added_if) {
struct wpa_driver_nl80211_data *drv2;
dl_list_for_each(drv2, &drv->global->interfaces,
- struct wpa_driver_nl80211_data, list)
- del_ifidx(drv2, ifindex);
+ struct wpa_driver_nl80211_data, list) {
+ del_ifidx(drv2, ifindex, IFIDX_ANY);
+ del_ifidx(drv2, IFIDX_ANY, ifindex);
+ }
}
if (type != WPA_IF_AP_BSS)
if (cookie_out)
*cookie_out = no_ack ? (u64) -1 : cookie;
+
+ if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop oldest pending send action cookie 0x%llx",
+ (long long unsigned int)
+ drv->send_action_cookies[0]);
+ os_memmove(&drv->send_action_cookies[0],
+ &drv->send_action_cookies[1],
+ (MAX_SEND_ACTION_COOKIES - 1) *
+ sizeof(u64));
+ drv->num_send_action_cookies--;
+ }
+ drv->send_action_cookies[drv->num_send_action_cookies] = cookie;
+ drv->num_send_action_cookies++;
}
fail:
}
-static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
{
- struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
- (long long unsigned int) drv->send_action_cookie);
+ (long long unsigned int) cookie);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
- nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
nlmsg_free(msg);
return;
}
}
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int i;
+ u64 cookie;
+
+ /* Cancel the last pending TX cookie */
+ nl80211_frame_wait_cancel(bss, drv->send_action_cookie);
+
+ /*
+ * Cancel the other pending TX cookies, if any. This is needed since
+ * the driver may keep a list of all pending offchannel TX operations
+ * and free up the radio only once they have expired or cancelled.
+ */
+ for (i = drv->num_send_action_cookies; i > 0; i--) {
+ cookie = drv->send_action_cookies[i - 1];
+ if (cookie != drv->send_action_cookie)
+ nl80211_frame_wait_cancel(bss, cookie);
+ }
+ drv->num_send_action_cookies = 0;
+}
+
+
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
unsigned int duration)
{
os_memset(si, 0, sizeof(*si));
res = nl80211_get_link_signal(drv, si);
- if (res != 0)
- return res;
+ if (res) {
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+ return res;
+ si->current_signal = 0;
+ }
res = nl80211_get_channel_width(drv, si);
if (res != 0)
static int nl80211_set_param(void *priv, const char *param)
{
- wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
if (param == NULL)
return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
#ifdef CONFIG_P2P
if (os_strstr(param, "use_p2p_group_interface=1")) {
"capa.mac_addr_rand_scan_supported=%d\n"
"capa.conc_capab=%u\n"
"capa.max_conc_chan_2_4=%u\n"
- "capa.max_conc_chan_5_0=%u\n",
+ "capa.max_conc_chan_5_0=%u\n"
+ "capa.max_sched_scan_plans=%u\n"
+ "capa.max_sched_scan_plan_interval=%u\n"
+ "capa.max_sched_scan_plan_iterations=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
drv->capa.mac_addr_rand_scan_supported,
drv->capa.conc_capab,
drv->capa.max_conc_chan_2_4,
- drv->capa.max_conc_chan_5_0);
+ drv->capa.max_conc_chan_5_0,
+ drv->capa.max_sched_scan_plans,
+ drv->capa.max_sched_scan_plan_interval,
+ drv->capa.max_sched_scan_plan_iterations);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .abort_scan = wpa_driver_nl80211_abort_scan,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
.associate = wpa_driver_nl80211_associate,