// SPDX-License-Identifier: GPL-2.0-only /* * This is the new netlink-based wireless configuration interface. * * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH */ #include #include #include #include #include #include #include #include "img_host_rpu_umac_iface.h" #include "img_module_iface.h" #include "core.h" #include "nl80211.h" #include "reg.h" #include "rdev-ops.h" #include #include #include #include #include "umac_debugs.h" #include "img_host_rpu_umac_iface.h" #include "ieee80211_i.h" /*Mapped Scan command*/ RETENTION_MEM_SECTION_UNINITIALIZED struct mapped_scan_channels_info *mapped_scan_ch_gp; RETENTION_MEM_SECTION_UNINITIALIZED unsigned char duration_probe_cnt; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int sleepEnable; RETENTION_MEM_SECTION_UNINITIALIZED extern struct non_trans_mbssid nontrans_mbssid; int global_channel_width_g; extern unsigned char umac_wait_for_get_scan; int send_rx_buffs_to_host(void *event_buff, int size); extern int get_cmd(int host_cmd); int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct nrf_wifi_connect_common_info *info, struct cfg80211_crypto_settings *settings, int cipher_limit); bool hal_check_hpqm_events_free_queue_empty(); #define NLM_F_MULTI 2 int handle_get_event_in_patch(enum nl80211_commands cmd) { return -1; } int get_event(enum nl80211_commands cmd) { int ret = 0; ret = handle_get_event_in_patch(cmd); if (ret >= 0) { return ret; } switch(cmd) { case NL80211_CMD_TRIGGER_SCAN: return NRF_WIFI_UMAC_EVENT_TRIGGER_SCAN_START; case NL80211_CMD_SCAN_ABORTED: return NRF_WIFI_UMAC_EVENT_SCAN_ABORTED; case NL80211_CMD_NEW_SCAN_RESULTS: return NRF_WIFI_UMAC_EVENT_SCAN_RESULT; case NL80211_CMD_AUTHENTICATE: return NRF_WIFI_UMAC_EVENT_AUTHENTICATE; case NL80211_CMD_ASSOCIATE: return NRF_WIFI_UMAC_EVENT_ASSOCIATE; case NL80211_CMD_CONNECT: return NRF_WIFI_UMAC_EVENT_CONNECT; case NL80211_CMD_DEAUTHENTICATE: return NRF_WIFI_UMAC_EVENT_DEAUTHENTICATE; case NL80211_CMD_DISASSOCIATE: return NRF_WIFI_UMAC_EVENT_DISASSOCIATE; case NL80211_CMD_JOIN_IBSS: return NRF_WIFI_UMAC_CMD_JOIN_IBSS; case NL80211_CMD_SET_BSS: return NRF_WIFI_UMAC_CMD_SET_BSS; case NL80211_CMD_START_AP: return NRF_WIFI_UMAC_CMD_START_AP; case NL80211_CMD_STOP_AP: return NRF_WIFI_UMAC_CMD_STOP_AP; case NL80211_CMD_SET_INTERFACE: return NRF_WIFI_UMAC_EVENT_SET_INTERFACE; case IMG_CMD_SET_IFFLAGS: return NRF_WIFI_UMAC_CMD_SET_IFFLAGS; case NL80211_CMD_FRAME: return NRF_WIFI_UMAC_EVENT_FRAME; case IMG_CMD_COOKIE_RESP: return NRF_WIFI_UMAC_EVENT_COOKIE_RESP; case NL80211_CMD_FRAME_TX_STATUS: return NRF_WIFI_UMAC_EVENT_FRAME_TX_STATUS; case NL80211_CMD_NEW_STATION: return NRF_WIFI_UMAC_EVENT_NEW_STATION; case NL80211_CMD_GET_STATION: return NRF_WIFI_UMAC_EVENT_GET_STATION; case IMG_CMD_GET_CHANNEL: return NRF_WIFI_UMAC_EVENT_GET_CHANNEL; case IMG_CMD_GET_TX_POWER: return NRF_WIFI_UMAC_EVENT_GET_TX_POWER; case NL80211_CMD_DEL_STATION: return NRF_WIFI_UMAC_EVENT_DEL_STATION; case NL80211_CMD_UNPROT_DEAUTHENTICATE: return NRF_WIFI_UMAC_EVENT_UNPROT_DEAUTHENTICATE; case NL80211_CMD_UNPROT_DISASSOCIATE: return NRF_WIFI_UMAC_EVENT_UNPROT_DISASSOCIATE; case NL80211_CMD_REMAIN_ON_CHANNEL: return NRF_WIFI_UMAC_EVENT_REMAIN_ON_CHANNEL; case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: return NRF_WIFI_UMAC_EVENT_CANCEL_REMAIN_ON_CHANNEL; case NL80211_CMD_DISCONNECT: return NRF_WIFI_UMAC_EVENT_DISCONNECT; case NL80211_CMD_NEW_INTERFACE: return NRF_WIFI_UMAC_EVENT_NEW_INTERFACE; case NL80211_CMD_NEW_WIPHY: return NRF_WIFI_UMAC_EVENT_NEW_WIPHY; case IMG_CMD_GET_IFHWADDR: return NRF_WIFI_UMAC_EVENT_GET_IFHWADDR; case NL80211_CMD_GET_REG: return NRF_WIFI_UMAC_EVENT_GET_REG; case NL80211_CMD_SET_REG: return NRF_WIFI_UMAC_EVENT_SET_REG; case NL80211_CMD_REQ_SET_REG: return NRF_WIFI_UMAC_EVENT_REQ_SET_REG; case NL80211_CMD_NEW_KEY: return NRF_WIFI_UMAC_EVENT_GET_KEY; case NL80211_CMD_REG_BEACON_HINT: return NRF_WIFI_UMAC_EVENT_BEACON_HINT; case NL80211_CMD_REG_CHANGE: return NRF_WIFI_UMAC_EVENT_REG_CHANGE; case NL80211_CMD_WIPHY_REG_CHANGE: return NRF_WIFI_UMAC_EVENT_WIPHY_REG_CHANGE; #ifdef BSS_OPTIMIZATION case NL80211_CMD_NEW_SCAN_DISPLAY_RESULTS: return NRF_WIFI_UMAC_EVENT_SCAN_DISPLAY_RESULT; #endif default: { printk("%s:%d: UNHANDLED EVENT IN HOST cmd= %d\n", __FUNCTION__, __LINE__, cmd); return -1; } } } void img_nl80211hdr_put(struct nrf_wifi_umac_hdr *cmd_hdr, u32 portid, u32 seq, int flags, int cmd) { (void)flags; cmd_hdr->cmd_evnt = get_event(cmd); cmd_hdr->portid = portid; cmd_hdr->seq = seq; } int nl80211_msg_put_channel(struct nrf_wifi_event_channel *event_chan, struct ieee80211_channel *chan, bool large) { /* Some channels must be completely excluded from the * list to protect old user-space tools from breaking */ if (!large && chan->flags & (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ)) { event_chan->ch_valid = false; return 0; } event_chan->ch_valid = true; event_chan->center_frequency = chan->center_freq; if (chan->flags & IEEE80211_CHAN_DISABLED) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_DISABLED; if (chan->flags & IEEE80211_CHAN_NO_IR) { event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_IR; event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_IBSS; } if (chan->flags & IEEE80211_CHAN_RADAR) { event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_RADAR; if (large) { u32 time; time = elapsed_jiffies_msecs(chan->dfs_state_entered); event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_DFS_VALID; event_chan->dfs_state = chan->dfs_state; event_chan->nrf_wifi_time = time; event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_DFS_CAC_TIME_VALID; event_chan->dfs_cac_msec = chan->dfs_cac_ms; } } if (large) { if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_HT40_MINUS; if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_HT40_PLUS; if (chan->flags & IEEE80211_CHAN_NO_80MHZ) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_80MHZ; if (chan->flags & IEEE80211_CHAN_NO_160MHZ) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_160MHZ; if (chan->flags & IEEE80211_CHAN_INDOOR_ONLY) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_INDOOR_ONLY; if (chan->flags & IEEE80211_CHAN_IR_CONCURRENT) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_GO_CONCURRENT; if (chan->flags & IEEE80211_CHAN_NO_20MHZ) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_20MHZ; if (chan->flags & IEEE80211_CHAN_NO_10MHZ) event_chan->nrf_wifi_flags |= NRF_WIFI_CHAN_FLAG_FREQUENCY_ATTR_NO_10MHZ; } event_chan->nrf_wifi_max_power = DBM_TO_MBM(chan->max_power); return 0; return -ENOBUFS; } /* netlink command implementations */ void umac_event_free(char *msg) { free(msg - sizeof(struct host_rpu_msg)); } void *umac_event_alloc(enum img_module_iface_port_id port_id, unsigned int *len) { void *event; char *buf; *len = *len + sizeof(struct host_rpu_msg); buf = kzalloc(*len, GFP_ATOMIC); if(buf == NULL) { return NULL; } event = (void *)(buf + sizeof(struct host_rpu_msg)); return event; } void img_genlmsg_reply(void *msg, unsigned int msg_len) { char *buf; struct host_rpu_msg *host_rpu_msg_ref; host_rpu_msg_ref =(struct host_rpu_msg *)((char *)msg - sizeof(struct host_rpu_msg)); host_rpu_msg_ref->type = NRF_WIFI_HOST_RPU_MSG_TYPE_UMAC; host_rpu_msg_ref->hdr.len = msg_len + sizeof(struct host_rpu_msg); host_rpu_msg_ref->hdr.resubmit = true; send_rx_buffs_to_host(host_rpu_msg_ref, msg_len + sizeof(struct host_rpu_msg)); } void img_genlmsg_multicast_netns(void *msg, unsigned int msg_len) { char *buf; struct host_rpu_msg *host_rpu_msg_ref; host_rpu_msg_ref =(struct host_rpu_msg *)((char *)msg - sizeof(struct host_rpu_msg)); host_rpu_msg_ref->type = NRF_WIFI_HOST_RPU_MSG_TYPE_UMAC; host_rpu_msg_ref->hdr.len = msg_len + sizeof(struct host_rpu_msg); host_rpu_msg_ref->hdr.resubmit = true; send_rx_buffs_to_host(host_rpu_msg_ref, msg_len + sizeof(struct host_rpu_msg)); } struct key_parse { struct key_params p; int idx; int type; bool def, defmgmt; bool def_uni, def_multi; }; int nl80211_parse_key_new(struct nrf_wifi_umac_key_info *key_info, struct key_parse *k) { k->def = !!(key_info->nrf_wifi_flags & NRF_WIFI_KEY_DEFAULT); k->defmgmt = !!(key_info->nrf_wifi_flags & NRF_WIFI_KEY_DEFAULT_MGMT); if (k->def) { k->def_uni = true; k->def_multi = true; } if (k->defmgmt) k->def_multi = true; if (key_info->valid_fields & NRF_WIFI_KEY_IDX_VALID) k->idx = key_info->key_idx; if (key_info->valid_fields & NRF_WIFI_KEY_VALID){ k->p.key = key_info->key.nrf_wifi_key; k->p.key_len = key_info->key.nrf_wifi_key_len; } if (key_info->valid_fields & NRF_WIFI_SEQ_VALID){ k->p.seq = key_info->seq.nrf_wifi_seq; k->p.seq_len = key_info->seq.nrf_wifi_seq_len; } if (key_info->valid_fields & NRF_WIFI_CIPHER_SUITE_VALID) k->p.cipher = key_info->cipher_suite; if (key_info->valid_fields & NRF_WIFI_KEY_TYPE_VALID) { k->type = key_info->key_type; if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) return -EINVAL; } if(key_info->key_idx) { if (key_info->nrf_wifi_flags & NRF_WIFI_KEY_DEFAULT_TYPE_UNICAST) { k->def_uni = true; } else { k->def_uni = false; } if (key_info->nrf_wifi_flags & NRF_WIFI_KEY_DEFAULT_TYPE_MULTICAST ) { k->def_multi = true; } else { k->def_multi = false; } } return 0; } void nl80211_process_twt_sleep_event(struct wiphy *wiphy,u8 type) { struct nrf_wifi_umac_event_twt_sleep *twt_sleep_event; unsigned int req_len; req_len = sizeof(*twt_sleep_event); twt_sleep_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); memset(twt_sleep_event, 0, sizeof(struct nrf_wifi_umac_event_twt_sleep)); twt_sleep_event->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_TWT_SLEEP; twt_sleep_event->umac_hdr.ids.nrf_wifi_wiphy_idx = get_wiphy_idx(wiphy); twt_sleep_event->info.type = type; img_genlmsg_reply(twt_sleep_event, sizeof(struct nrf_wifi_umac_event_twt_sleep)); } void nl80211_process_twt_teardown_event(struct wiphy *wiphy,u8 flow_id,unsigned char reason) { struct nrf_wifi_umac_cmd_teardown_twt *twt_teardown_event; unsigned int req_len; req_len = sizeof(*twt_teardown_event); twt_teardown_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); memset(twt_teardown_event, 0, sizeof(struct nrf_wifi_umac_cmd_teardown_twt)); twt_teardown_event->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_TEARDOWN_TWT; twt_teardown_event->umac_hdr.ids.nrf_wifi_wiphy_idx = get_wiphy_idx(wiphy); twt_teardown_event->info.twt_flow_id = flow_id; twt_teardown_event->info.reason_code = reason; img_genlmsg_reply(twt_teardown_event, sizeof(struct nrf_wifi_umac_cmd_teardown_twt)); } void nl80211_process_twt_event(struct ieee80211_sub_if_data *sdata,struct ieee80211_twt_setup *twt, struct ieee80211_twt_params *twt_resp) { struct nrf_wifi_umac_cmd_config_twt *twt_event; struct wireless_dev *wdev = &sdata->wdev; struct wiphy *wiphy = wdev->wiphy; unsigned int req_len; req_len = sizeof(*twt_event); twt_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); memset(twt_event, 0, sizeof(struct nrf_wifi_umac_cmd_config_twt)); twt_event->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_CONFIG_TWT; twt_event->umac_hdr.ids.nrf_wifi_wiphy_idx = get_wiphy_idx(wiphy); twt_event->info.twt_flow_id = ((twt_resp->req_type>>7) & 0x7); twt_event->info.neg_type = ((twt->control>>2) & 0x3); twt_event->info.setup_cmd = ((twt_resp->req_type>>1) & 0x7); if (twt_resp->req_type & IEEE80211_TWT_REQTYPE_TRIGGER) twt_event->info.ap_trigger_frame = 1; if (twt_resp->req_type & IEEE80211_TWT_REQTYPE_IMPLICIT) twt_event->info.is_implicit = 1; if (twt_resp->req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE) twt_event->info.twt_flow_type = 1; twt_event->info.twt_target_wake_interval_exponent = ((twt_resp->req_type>>10) & 0x1F); twt_event->info.twt_target_wake_interval_mantissa = twt_resp->mantissa; twt_event->info.target_wake_time = twt_resp->twt; twt_event->info.nominal_min_twt_wake_duration = twt_resp->min_twt_dur; img_genlmsg_reply(twt_event, sizeof(struct nrf_wifi_umac_cmd_config_twt)); } int nl80211_parse_key(struct nrf_wifi_umac_key_info *key_info, struct key_parse *k) { int err; memset(k, 0, sizeof(*k)); k->idx = -1; k->type = -1; err = nl80211_parse_key_new(key_info, k); if (err) return err; if (k->def && k->defmgmt) return -EINVAL; if (k->defmgmt) { if (k->def_uni || !k->def_multi) return -EINVAL; } if (k->idx != -1) { if (k->defmgmt) { if (k->idx < 4 || k->idx > 5) return -EINVAL; } else if (k->def) { if (k->idx < 0 || k->idx > 3) return -EINVAL; } else { if (k->idx < 0 || k->idx > 5) return -EINVAL; } } return 0; } struct cfg80211_cached_keys * nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, struct nrf_wifi_umac_key_info *key_info, unsigned int key_info_num, bool *no_ht) { struct key_parse parse; struct cfg80211_cached_keys *result; int err, i, def = 0; result = kzalloc(sizeof(*result), GFP_KERNEL); if (!result) return ERR_PTR(-ENOMEM); result->def = -1; for (i = 0; i < key_info_num; i++) { memset(&parse, 0, sizeof(parse)); parse.idx = -1; err = nl80211_parse_key_new((key_info+i), &parse); if (err) goto error; err = -EINVAL; if (!parse.p.key) goto error; if (parse.idx < 0 || parse.idx > 3) goto error; if (parse.def) { if (def) goto error; def = 1; result->def = parse.idx; if (!parse.def_uni || !parse.def_multi) goto error; } else if (parse.defmgmt) goto error; err = cfg80211_validate_key_settings(rdev, &parse.p, parse.idx, false, NULL); if (err) goto error; if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 && parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) { err = -EINVAL; goto error; } result->params[parse.idx].cipher = parse.p.cipher; result->params[parse.idx].key_len = parse.p.key_len; result->params[parse.idx].key = result->data[parse.idx]; memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); /* must be WEP key if we got here */ if (no_ht) *no_ht = true; } if (result->def < 0) { err = -EINVAL; goto error; } return result; error: kfree(result); return ERR_PTR(err); } int nl80211_key_allowed(struct wireless_dev *wdev) { ASSERT_WDEV_LOCK(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_MESH_POINT: break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!wdev->current_bss) return -ENOLINK; break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: return -EINVAL; } return 0; } struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy, unsigned int freq) { struct ieee80211_channel *chan; chan = ieee80211_get_channel(wiphy, freq); if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) return NULL; return chan; } int nl80211_put_iftypes(unsigned short int *interface_modes, u16 ifmodes) { *interface_modes = ifmodes; return 0; } int nl80211_put_iface_combinations(struct wiphy *wiphy, struct nrf_wifi_event_iface_combination *iface_comb, bool large) { (void)large; int i, j; struct nrf_wifi_event_iface_combination *event_c; for (i = 0; i < wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; c = &wiphy->iface_combinations[i]; event_c = &iface_comb[i]; event_c->nrf_wifi_n_limits = c->n_limits; for (j = 0; j < c->n_limits; j++) { event_c->limits[j].nrf_wifi_max = c->limits[j].max; nl80211_put_iftypes(&event_c->limits[j].nrf_wifi_types, c->limits[j].types); } if (c->beacon_int_infra_match) event_c->beacon_int_infra_match = true; event_c->nrf_wifi_num_different_channels = c->num_different_channels; event_c->nrf_wifi_max_interfaces = c->max_interfaces; event_c->comb_valid |= NRF_WIFI_EVENT_GET_WIPHY_VALID_RADAR_DETECT_WIDTHS; event_c->nrf_wifi_radar_detect_widths = c->radar_detect_widths; event_c->comb_valid |= NRF_WIFI_EVENT_GET_WIPHY_VALID_RADAR_DETECT_REGIONS; event_c->nrf_wifi_radar_detect_regions = c->radar_detect_regions; } return 0; return -ENOBUFS; } #ifdef CONFIG_PM int nl80211_send_wowlan( struct nrf_wifi_event_get_wiphy *event_msg, struct cfg80211_registered_device *rdev, bool large) { (void)event_msg; (void)rdev; (void)large; return 0; } #endif int nl80211_send_band_rateinfo(struct nrf_wifi_event_supported_band *event_band, struct ieee80211_supported_band *sband) { struct ieee80211_rate *rate; int i; /* add HT info */ if (sband->ht_cap.ht_supported) { event_band->ht_cap.nrf_wifi_ht_supported = true; memcpy(&event_band->ht_cap.mcs, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); event_band->ht_cap.nrf_wifi_cap = sband->ht_cap.cap; event_band->ht_cap.nrf_wifi_ampdu_factor = sband->ht_cap.ampdu_factor; event_band->ht_cap.nrf_wifi_ampdu_density = sband->ht_cap.ampdu_density; } /* add VHT info */ if (sband->vht_cap.vht_supported) { event_band->vht_cap.nrf_wifi_vht_supported = true; memcpy(&event_band->vht_cap.vht_mcs, &sband->vht_cap.vht_mcs, sizeof(sband->vht_cap.vht_mcs)); event_band->vht_cap.nrf_wifi_cap = sband->vht_cap.cap; } /* add bitrates */ event_band->nrf_wifi_n_bitrates = sband->n_bitrates; for (i = 0; i < sband->n_bitrates; i++) { rate = &sband->bitrates[i]; event_band->bitrates[i].nrf_wifi_bitrate = rate->bitrate; if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) event_band->bitrates[i].nrf_wifi_flags |= NRF_WIFI_EVENT_GET_WIPHY_FLAG_RATE_SHORT_PREAMBLE; } return 0; } struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; long split_start, band_start, chan_start; bool split; }; int nl80211_send_wiphy_case0(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { (void)band; (void)chan; (void)features; event_msg->retry_short = rdev->wiphy.retry_short; event_msg->retry_long = rdev->wiphy.retry_long; event_msg->nrf_wifi_frag_threshold = rdev->wiphy.frag_threshold; event_msg->nrf_wifi_rts_threshold = rdev->wiphy.rts_threshold; event_msg->coverage_class = rdev->wiphy.coverage_class; event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_MAX_NUM_SCAN_SSIDS; event_msg->max_scan_ssids = rdev->wiphy.max_scan_ssids; event_msg->umac_hdr.ids.valid_fields |= NRF_WIFI_GET_WIPHY_VALID_NUM_SCHED_SCAN_SSIDS; event_msg->max_sched_scan_ssids = rdev->wiphy.max_sched_scan_ssids; event_msg->max_scan_ie_len = rdev->wiphy.max_scan_ie_len; event_msg->max_sched_scan_ie_len = rdev->wiphy.max_sched_scan_ie_len; event_msg->umac_hdr.ids.valid_fields |= NRF_WIFI_GET_WIPHY_VALID_MAX_MATCH_SETS; event_msg->max_match_sets = rdev->wiphy.max_match_sets; if (rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_IBSS_RSN; if (rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_MESH_AUTH; if (rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_AP_UAPSD; if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_SUPPORTS_FW_ROAM; if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_SUPPORTS_TDLS; if (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_TDLS_EXTERNAL_SETUP; return 0; } int nl80211_send_wiphy_case1(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { event_msg->n_cipher_suites = rdev->wiphy.n_cipher_suites; memcpy(event_msg->cipher_suites, rdev->wiphy.cipher_suites, (sizeof(unsigned int) * rdev->wiphy.n_cipher_suites)); event_msg->max_num_pmkids = rdev->wiphy.max_num_pmkids; if (rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_CONTROL_PORT_ETHERTYPE; event_msg->nrf_wifi_available_antennas_tx = rdev->wiphy.available_antennas_tx; event_msg->nrf_wifi_available_antennas_rx = rdev->wiphy.available_antennas_rx; if (rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) { event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_PROBE_RESP_OFFLOAD; event_msg->nrf_wifi_probe_resp_offload = rdev->wiphy.probe_resp_offload; } if ((rdev->wiphy.available_antennas_tx || rdev->wiphy.available_antennas_rx) && rdev->ops->get_antenna) { u32 tx_ant = 0, rx_ant = 0; int res; res = rdev_get_antenna(rdev, &tx_ant, &rx_ant); if (!res) { event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_TX_ANT; event_msg->tx_ant = tx_ant; event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_RX_ANT; event_msg->rx_ant = rx_ant; } } return 0; } int nl80211_send_wiphy_case3(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { int i; for (band = state->band_start; band < NUM_NL80211_BANDS; band++) { struct ieee80211_supported_band *sband; sband = rdev->wiphy.bands[band]; if (!sband) continue; event_msg->sband[band].band = band; switch (state->chan_start) { case 0: if (nl80211_send_band_rateinfo(&event_msg->sband[band], sband)) goto nla_put_failure; state->chan_start++; if (state->split) break; default: event_msg->sband[band].nrf_wifi_n_channels = sband->n_channels; for (i = state->chan_start - 1; i < sband->n_channels; i++) { chan = &sband->channels[i]; if (nl80211_msg_put_channel( &event_msg->sband[band].channels[i], chan, state->split)) goto nla_put_failure; if (state->split) break; } if (i < sband->n_channels) state->chan_start = i + 2; else state->chan_start = 0; } if (state->split) { /* start again here */ if (state->chan_start) band--; break; } } if (band < NUM_NL80211_BANDS) state->band_start = band + 1; else state->band_start = 0; /* if bands & channels are done, continue outside */ return 0; nla_put_failure: return -EMSGSIZE; } int nl80211_send_wiphy_case4(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { int i; i = 0; #define CMD(op, n) \ do { \ if (rdev->ops->op) { \ i++; \ event_msg->supp_commands[i] = NL80211_CMD_ ## n; \ } \ } while (0) CMD(add_virtual_intf, NEW_INTERFACE); CMD(change_virtual_intf, SET_INTERFACE); CMD(add_key, NEW_KEY); CMD(start_ap, START_AP); CMD(add_station, NEW_STATION); CMD(add_mpath, NEW_MPATH); CMD(update_mesh_config, SET_MESH_CONFIG); CMD(change_bss, SET_BSS); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); CMD(join_mesh, JOIN_MESH); CMD(set_pmksa, SET_PMKSA); CMD(del_pmksa, DEL_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA); if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) CMD(remain_on_channel, REMAIN_ON_CHANNEL); CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); CMD(mgmt_tx, FRAME); CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { i++; event_msg->supp_commands[i] = NL80211_CMD_SET_WIPHY_NETNS; } if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || rdev->ops->join_mesh) { i++; event_msg->supp_commands[i] = NL80211_CMD_SET_CHANNEL; } CMD(set_wds_peer, SET_WDS_PEER); if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { CMD(tdls_mgmt, TDLS_MGMT); CMD(tdls_oper, TDLS_OPER); } if (rdev->wiphy.max_sched_scan_reqs) CMD(sched_scan_start, START_SCHED_SCAN); CMD(probe_client, PROBE_CLIENT); CMD(set_noack_map, SET_NOACK_MAP); if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { i++; event_msg->supp_commands[i] = NL80211_CMD_REGISTER_BEACONS; } CMD(start_p2p_device, START_P2P_DEVICE); CMD(set_mcast_rate, SET_MCAST_RATE); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); #endif if (state->split) { CMD(crit_proto_start, CRIT_PROTOCOL_START); CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) CMD(channel_switch, CHANNEL_SWITCH); CMD(set_qos_map, SET_QOS_MAP); if (rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); } /* add into the if now */ #undef CMD if (rdev->ops->connect || rdev->ops->auth) { i++; event_msg->supp_commands[i] = NL80211_CMD_CONNECT; } if (rdev->ops->disconnect || rdev->ops->deauth) { i++; event_msg->supp_commands[i] = NL80211_CMD_CONNECT; } return 0; } int nl80211_send_wiphy_case5(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { if (rdev->ops->remain_on_channel && (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) event_msg->max_remain_on_channel_duration = rdev->wiphy.max_remain_on_channel_duration; if (rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) { event_msg->get_wiphy_flags |= NRF_WIFI_EVENT_GET_WIPHY_OFFCHANNEL_TX_OK; } return 0; } int nl80211_send_wiphy_case7(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { event_msg->num_iface_com = rdev->wiphy.n_iface_combinations; if (nl80211_put_iface_combinations(&rdev->wiphy, event_msg->iface_com, state->split)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } int nl80211_send_wiphy_case8(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) { event_msg->get_wiphy_flags |= NRF_WIFI_GET_WIPHY_VALID_HAVE_AP_SME; event_msg->ap_sme_capa = rdev->wiphy.ap_sme_capa; } features = rdev->wiphy.features; /* * We can only add the per-channel limit information if the * dump is split, otherwise it makes it too big. Therefore * only advertise it in that case. */ if (state->split) features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; event_msg->features = features; if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && rdev->wiphy.max_acl_mac_addrs) { event_msg->umac_hdr.ids.valid_fields |= NRF_WIFI_GET_WIPHY_VALID_MAC_ACL_MAX; event_msg->max_acl_mac_addresses = rdev->wiphy.max_acl_mac_addrs; } return 0; } int nl80211_send_wiphy_case9(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { if (rdev->wiphy.extended_capabilities) { event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_EXTENDED_CAPABILITIES; event_msg->extended_capabilities_len = rdev->wiphy.extended_capabilities_len; memcpy(event_msg->extended_capabilities, rdev->wiphy.extended_capabilities, rdev->wiphy.extended_capabilities_len); memcpy(event_msg->extended_capabilities_mask, rdev->wiphy.extended_capabilities_mask, rdev->wiphy.extended_capabilities_len); } return 0; } int nl80211_send_wiphy_case10(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { return 0; } int nl80211_send_wiphy_case11(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { return 0; } int nl80211_send_wiphy_case12(struct cfg80211_registered_device *rdev, struct nrf_wifi_event_get_wiphy *event_msg, enum nl80211_band band, struct ieee80211_channel *chan, u32 features, struct nl80211_dump_wiphy_state *state) { if(rdev->wiphy.ext_features){ event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_EXTENDED_FEATURES; event_msg->ext_features_len = sizeof(rdev->wiphy.ext_features); memcpy(event_msg->ext_features,rdev->wiphy.ext_features,sizeof(rdev->wiphy.ext_features)); } return 0; } int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, enum nl80211_commands cmd, struct nrf_wifi_event_get_wiphy *event_msg, u32 portid, u32 seq, int flags, struct nl80211_dump_wiphy_state *state) { enum nl80211_band band = 0; struct ieee80211_channel *chan = NULL; int i; int ret = 0; /* const struct ieee80211_txrx_stypes *mgmt_stypes = rdev->wiphy.mgmt_stypes; */ u32 features = 0; img_nl80211hdr_put(&event_msg->umac_hdr, portid, seq, flags, cmd); if (WARN_ON(!state)) return -EINVAL; event_msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; event_msg->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; event_msg->params_valid |= NRF_WIFI_GET_WIPHY_VALID_WIPHY_NAME; strcpy((char *)event_msg->wiphy_name, wiphy_name(&rdev->wiphy)); if (cmd != NL80211_CMD_NEW_WIPHY) goto finish; switch (state->split_start) { case 0: ret = nl80211_send_wiphy_case0(rdev, event_msg, band, chan, features, state); state->split_start++; if (state->split) break; case 1: ret = nl80211_send_wiphy_case1(rdev, event_msg, band, chan, features, state); state->split_start++; if (state->split) break; case 2: nl80211_put_iftypes(&event_msg->interface_modes, rdev->wiphy.interface_modes); state->split_start++; if (state->split) break; case 3: ret = nl80211_send_wiphy_case3(rdev, event_msg, band, chan, features, state); if (state->band_start == 0 && state->chan_start == 0) state->split_start++; if (state->split) break; case 4: ret = nl80211_send_wiphy_case4(rdev, event_msg, band, chan, features, state); state->split_start++; if (state->split) break; case 5: ret = nl80211_send_wiphy_case5(rdev, event_msg, band, chan, features, state); state->split_start++; if (state->split) break; case 6: #ifdef CONFIG_PM if (nl80211_send_wowlan(event_msg, rdev, state->split)) goto nla_put_failure; state->split_start++; if (state->split) break; #else state->split_start++; #endif case 7: ret = nl80211_send_wiphy_case7(rdev, event_msg, band, chan, features, state); state->split_start++; if (state->split) break; case 8: ret = nl80211_send_wiphy_case8(rdev, event_msg, band, chan, features, state); /* * Any information below this point is only available to * applications that can deal with it being split. This * helps ensure that newly added capabilities don't break * older tools by overrunning their buffers. * * We still increment split_start so that in the split * case we'll continue with more data in the next round, * but break unconditionally so unsplit data stops here. */ state->split_start++; if(state->split) break; case 9: ret = nl80211_send_wiphy_case9(rdev, event_msg, band, chan, features, state); state->split_start++; if(state->split) break; case 10: ret = nl80211_send_wiphy_case10(rdev, event_msg, band, chan, features, state); state->split_start++; if(state->split) break; case 11: ret = nl80211_send_wiphy_case11(rdev, event_msg, band, chan, features, state); state->split_start++; if(state->split) break; case 12: ret = nl80211_send_wiphy_case12(rdev, event_msg, band, chan, features, state); /* done */ state->split_start = 0; break; } finish: return ret; nla_put_failure: return -EMSGSIZE; } int nl80211_get_wiphy(struct nrf_wifi_cmd_get_wiphy *cmd, struct cfg80211_registered_device *r_dev) { struct cfg80211_registered_device *rdev = r_dev; struct nl80211_dump_wiphy_state state = {0, 0, 0, 0, 0, 0}; struct nrf_wifi_event_get_wiphy *event_msg; unsigned int req_len; req_len = sizeof(*event_msg); event_msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!event_msg) return -ENOMEM; memset(event_msg, 0, sizeof(*event_msg)); if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, event_msg, cmd->umac_hdr.portid, cmd->umac_hdr.seq, 0, &state) < 0) { umac_event_free((char *)event_msg); return -ENOBUFS; } img_genlmsg_reply(event_msg, sizeof(*event_msg)); return 0; } bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) { /* * You can only set the channel explicitly for WDS interfaces, * all others have their channel managed via their respective * "establish a connection" command (connect, join, ...) * * For AP/GO and mesh mode, the channel can be set with the * channel userspace API, but is only stored and passed to the * low-level driver when the AP starts or the mesh is joined. * This is for backward compatibility, userspace can also give * the channel in the start-ap or join-mesh commands instead. * * Monitors are special as they are normally slaved to * whatever else is going on, so they have their own special * operation to set the monitor channel if possible. */ return !wdev || wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_MESH_POINT || wdev->iftype == NL80211_IFTYPE_MONITOR || wdev->iftype == NL80211_IFTYPE_P2P_GO; } int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct freq_params *freq_params, struct cfg80211_chan_def *chandef) { u32 control_freq = 0; control_freq = freq_params->frequency; chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); chandef->width = freq_params->channel_width; chandef->center_freq1 = control_freq; chandef->center_freq2 = 0; if (freq_params->channel_width == NRF_WIFI_CHAN_WIDTH_40) { global_channel_width_g = 40; } else if(freq_params->channel_width == NRF_WIFI_CHAN_WIDTH_80) { global_channel_width_g = 80; } /* Primary channel not allowed */ if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) { return -EINVAL; } if (freq_params->valid_fields & NRF_WIFI_SET_FREQ_PARAMS_CHANNEL_TYPE_VALID) { enum nl80211_channel_type chantype; chantype = freq_params->channel_type; switch (chantype) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: case NL80211_CHAN_HT40PLUS: case NL80211_CHAN_HT40MINUS: cfg80211_chandef_create(chandef, chandef->chan, chantype); break; default: return -EINVAL; } } else if (freq_params->valid_fields & NRF_WIFI_SET_FREQ_PARAMS_CHANNEL_WIDTH_VALID) { chandef->width = freq_params->channel_width; if (freq_params->valid_fields & NRF_WIFI_SET_FREQ_PARAMS_CENTER_FREQ1_VALID) chandef->center_freq1 = freq_params->center_frequency1; if (freq_params->valid_fields & NRF_WIFI_SET_FREQ_PARAMS_CENTER_FREQ2_VALID) chandef->center_freq2 = freq_params->center_frequency2; } if (!cfg80211_chandef_valid(chandef)){ return -EINVAL; } if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, IEEE80211_CHAN_DISABLED)){ return -EINVAL; } if ((chandef->width == NL80211_CHAN_WIDTH_5 || chandef->width == NL80211_CHAN_WIDTH_10) && !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)){ return -EINVAL; } return 0; } int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct net_device *dev, struct nrf_wifi_umac_cmd_set_channel *set_ch) { struct cfg80211_chan_def chandef; int result; enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; struct wireless_dev *wdev = NULL; if (dev) wdev = dev->ieee80211_ptr; if (!nl80211_can_set_dev_channel(wdev)) return -EOPNOTSUPP; if (wdev) iftype = wdev->iftype; result = nl80211_parse_chandef(rdev, &set_ch->freq_params, &chandef); if (result) return result; switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) { result = -EINVAL; break; } if (wdev->beacon_interval) { if (!dev || !rdev->ops->set_ap_chanwidth || !(rdev->wiphy.features & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) { result = -EBUSY; break; } /* Only allow dynamic channel width changes */ if (chandef.chan != wdev->preset_chandef.chan) { result = -EBUSY; break; } result = rdev_set_ap_chanwidth(rdev, dev, &chandef); if (result) break; } wdev->preset_chandef = chandef; result = 0; break; case NL80211_IFTYPE_MESH_POINT: result = 0; break; case NL80211_IFTYPE_MONITOR: #ifdef MONITOR_MODE_SUPPORT result = cfg80211_set_monitor_channel(rdev, &chandef); #endif break; default: result = -EINVAL; } return result; } int nl80211_set_channel(struct nrf_wifi_umac_cmd_set_channel *set_ch, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *netdev = net_dev; return __nl80211_set_channel(rdev, netdev, set_ch); } int nl80211_set_wiphy(struct nrf_wifi_umac_cmd_set_wiphy *set_wiphy, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev; struct net_device *netdev = NULL; struct wireless_dev *wdev; int result = 0; u32 changed; u8 retry_short = 0, retry_long = 0; u32 frag_threshold = 0, rts_threshold = 0; u8 coverage_class = 0; /* * Try to find the wiphy and netdev. Normally this * function shouldn't need the netdev, but this is * done for backward compatibility -- previously * setting the channel was done per wiphy, but now * it is per netdev. Previous userland like hostapd * also passed a netdev to set_wiphy, so that it is * possible to let that go to the right netdev! */ rdev = r_dev; netdev = net_dev; wdev = net_dev->ieee80211_ptr; /* * end workaround code, by now the rdev is available * and locked, and wdev may or may not be NULL. */ if(set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_WIPHY_NAME_VALID) result = cfg80211_dev_rename(rdev, (char *)set_wiphy->info.wiphy_name); if (result) return result; if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_TXQ_PARAMS_VALID) { struct ieee80211_txq_params txq_params; txq_params.ac = set_wiphy->info.txq_params.ac; txq_params.txop = set_wiphy->info.txq_params.txop; txq_params.cwmin = set_wiphy->info.txq_params.cwmin; txq_params.cwmax = set_wiphy->info.txq_params.cwmax; txq_params.aifs = set_wiphy->info.txq_params.aifs; result = rdev_set_txq_params(rdev, netdev, &txq_params); } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_FREQ_PARAMS_VALID) { struct nrf_wifi_umac_cmd_set_channel set_ch; memcpy(&set_ch.freq_params, &set_wiphy->info.freq_params, sizeof(struct freq_params)); result = __nl80211_set_channel( rdev, nl80211_can_set_dev_channel(wdev) ? netdev : NULL, &set_ch); } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_TX_POWER_SETTING_VALID) { struct wireless_dev *txp_wdev = wdev; enum nl80211_tx_power_setting type = NL80211_TX_POWER_AUTOMATIC; int mbm = 0; if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) txp_wdev = NULL; if(set_wiphy->info.tx_power_setting.valid_fields & NRF_WIFI_TX_POWER_SETTING_TYPE_VALID) type = set_wiphy->info.tx_power_setting.type; mbm = set_wiphy->info.tx_power_setting.tx_power_level; result = rdev_set_tx_power(rdev, txp_wdev, type, mbm); if (result) return result; } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_ANTENNA_TX_VALID && set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_ANTENNA_RX_VALID) { u32 tx_ant, rx_ant; tx_ant = set_wiphy->info.antenna_tx; rx_ant = set_wiphy->info.antenna_rx; tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; result = rdev_set_antenna(rdev, tx_ant, rx_ant); } changed = 0; if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_RETRY_SHORT_VALID) { retry_short = set_wiphy->info.retry_short; changed |= WIPHY_PARAM_RETRY_SHORT; } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_RETRY_LONG_VALID) { retry_long = set_wiphy->info.retry_long; changed |= WIPHY_PARAM_RETRY_LONG; } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_FRAG_THRESHOLD_VALID) { frag_threshold = set_wiphy->info.frag_threshold; if (frag_threshold != (u32) -1) { /* * Fragments (apart from the last one) are required to * have even length. Make the fragmentation code * simpler by stripping LSB should someone try to use * odd threshold value. */ frag_threshold &= ~0x1; } changed |= WIPHY_PARAM_FRAG_THRESHOLD; } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_RTS_THRESHOLD_VALID) { rts_threshold = set_wiphy->info.rts_threshold; changed |= WIPHY_PARAM_RTS_THRESHOLD; } if (set_wiphy->valid_fields & NRF_WIFI_CMD_SET_WIPHY_COVERAGE_CLASS_VALID) { coverage_class = set_wiphy->info.coverage_class; changed |= WIPHY_PARAM_COVERAGE_CLASS; } if (changed) { u8 old_retry_short, old_retry_long; u32 old_frag_threshold, old_rts_threshold; u8 old_coverage_class; old_retry_short = rdev->wiphy.retry_short; old_retry_long = rdev->wiphy.retry_long; old_frag_threshold = rdev->wiphy.frag_threshold; old_rts_threshold = rdev->wiphy.rts_threshold; old_coverage_class = rdev->wiphy.coverage_class; if (changed & WIPHY_PARAM_RETRY_SHORT) rdev->wiphy.retry_short = retry_short; if (changed & WIPHY_PARAM_RETRY_LONG) rdev->wiphy.retry_long = retry_long; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) rdev->wiphy.frag_threshold = frag_threshold; if (changed & WIPHY_PARAM_RTS_THRESHOLD) rdev->wiphy.rts_threshold = rts_threshold; if (changed & WIPHY_PARAM_COVERAGE_CLASS) rdev->wiphy.coverage_class = coverage_class; result = rdev_set_wiphy_params(rdev, changed); if (result) { rdev->wiphy.retry_short = old_retry_short; rdev->wiphy.retry_long = old_retry_long; rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.rts_threshold = old_rts_threshold; rdev->wiphy.coverage_class = old_coverage_class; } } return 0; } int nl80211_send_chandef(struct nrf_wifi_chan_definition *chan_def, const struct cfg80211_chan_def *chandef) { if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL; chan_def->chan.center_frequency = chandef->chan->center_freq; switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_40: break; default: break; } chan_def->width = chandef->width; chan_def->center_frequency1 = chandef->center_freq1; chan_def->center_frequency2 = chandef->center_freq2; return 0; } int nl80211_send_iface(struct nrf_wifi_interface_info *interface_info, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, bool removal) { (void)portid; (void)seq; (void)flags; struct net_device *dev = wdev->netdev; u8 cmd = NL80211_CMD_NEW_INTERFACE; if (removal) cmd = NL80211_CMD_DEL_INTERFACE; img_nl80211hdr_put(&interface_info->umac_hdr, 0, 0, 0, cmd); if (dev) { interface_info->umac_hdr.ids.ifaceindex = dev->ifindex; interface_info->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; strcpy((char *)interface_info->ifacename, dev->name); interface_info->valid_fields |= NRF_WIFI_INTERFACE_INFO_IFNAME_VALID; } else { goto nla_put_failure; } interface_info->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; interface_info->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; interface_info->umac_hdr.ids.wdev_id = wdev_id(wdev); interface_info->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; interface_info->nrf_wifi_iftype = wdev->iftype; memcpy(interface_info->nrf_wifi_eth_addr, wdev_address(wdev), ETH_ALEN); if (rdev->ops->get_channel) { int ret; struct cfg80211_chan_def chandef; ret = rdev_get_channel(rdev, wdev, &chandef); if (ret == 0) { interface_info->valid_fields |= NRF_WIFI_INTERFACE_INFO_CHAN_DEF_VALID; if (nl80211_send_chandef(&interface_info->chan_def, &chandef)) goto nla_put_failure; } } if (wdev->ssid_len) { interface_info->valid_fields |= NRF_WIFI_INTERFACE_INFO_SSID_VALID; interface_info->ssid.nrf_wifi_ssid_len = wdev->ssid_len; memcpy(interface_info->ssid.nrf_wifi_ssid, wdev->ssid, wdev->ssid_len); } return 0; nla_put_failure: return -EMSGSIZE; } int nl80211_get_interface(struct nrf_wifi_cmd_get_interface *cmd, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { struct nrf_wifi_interface_info *interface_info; struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; unsigned int req_len; req_len = sizeof(*interface_info); interface_info = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!interface_info) return -ENOMEM; if (nl80211_send_iface(interface_info, cmd->umac_hdr.portid, cmd->umac_hdr.seq, 0, rdev, wdev, false) < 0) { umac_event_free((char *)interface_info); return -ENOBUFS; } img_genlmsg_reply((void *)interface_info, sizeof(*interface_info)); return 0; } int nl80211_set_interface(struct nrf_wifi_umac_cmd_chg_vif_attr *set_if, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct vif_params params; int err; enum nl80211_iftype otype, ntype; struct net_device *dev = net_dev; u32 *flags = NULL; bool change = false; unsigned int req_len; struct nrf_wifi_umac_event_set_interface *event_msg; memset(¶ms, 0, sizeof(params)); otype = ntype = dev->ieee80211_ptr->iftype; if (set_if->valid_fields & NRF_WIFI_SET_INTERFACE_IFTYPE_VALID) { ntype = set_if->info.iftype; if (otype != ntype) change = true; } if (set_if->valid_fields & NRF_WIFI_SET_INTERFACE_USE_4ADDR_VALID) { params.use_4addr = set_if->info.nrf_wifi_use_4addr; change = true; } else { params.use_4addr = -1; } if (change) err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); else err = 0; if (!err && params.use_4addr != -1) dev->ieee80211_ptr->use_4addr = params.use_4addr; req_len = sizeof(*event_msg); event_msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!event_msg) return -ENOBUFS; memset(event_msg, 0, sizeof(*event_msg)); img_nl80211hdr_put(&event_msg->umac_hdr, set_if->umac_hdr.portid, set_if->umac_hdr.seq, 0, NL80211_CMD_SET_INTERFACE); event_msg->return_value = err; img_genlmsg_reply(event_msg, sizeof(*event_msg)); return 0; } int nl80211_new_interface(struct nrf_wifi_umac_cmd_add_vif *cmd_new_if, struct cfg80211_registered_device *r_dev) { struct cfg80211_registered_device *rdev = r_dev; struct vif_params params; struct wireless_dev *wdev; struct nrf_wifi_interface_info *msg, *event; int err=0; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; unsigned int req_len; /* to avoid failing a new interface creation due to pending removal */ cfg80211_destroy_ifaces(rdev); memset(¶ms, 0, sizeof(params)); if (cmd_new_if->valid_fields & NRF_WIFI_CMD_NEW_INTERFACE_IFTYPE_VALID) { type = cmd_new_if->info.iftype; } if ((type == NL80211_IFTYPE_P2P_DEVICE || rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && cmd_new_if->valid_fields & NRF_WIFI_CMD_NEW_INTERFACE_MAC_ADDR_VALID) { memcpy(params.macaddr, cmd_new_if->info.mac_addr, ETH_ALEN); } if (cmd_new_if->valid_fields & NRF_WIFI_CMD_NEW_INTERFACE_USE_4ADDR_VALID) { params.use_4addr = !!cmd_new_if->info.nrf_wifi_use_4addr; } flags = 0; wdev = rdev_add_virtual_intf(rdev, (char *)cmd_new_if->info.ifacename, NET_NAME_USER, type, ¶ms); switch (type) { case NL80211_IFTYPE_P2P_DEVICE: /* * P2P Device doesn't have a netdev, so doesn't go * through the netdev notifier and must be added here */ mutex_init(&wdev->mtx); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list); rdev->devlist_generation++; break; default: break; } return 0; } int umac_proc_cmd_get_channel(struct nrf_wifi_umac_cmd_get_channel *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct nrf_wifi_umac_event_get_channel *event; unsigned int req_len; struct net_device *dev = wdev->netdev; req_len = sizeof(*event); event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!event) return -ENOMEM; img_nl80211hdr_put(&event->umac_hdr, cmd->umac_hdr.portid, cmd->umac_hdr.seq, 0, IMG_CMD_GET_CHANNEL); event->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; event->umac_hdr.ids.ifaceindex = dev->ifindex; if (rdev->ops->get_channel) { int ret; struct cfg80211_chan_def chandef; ret = rdev_get_channel(rdev, wdev, &chandef); if (ret == 0) { event->chan_def.chan.center_frequency = chandef.chan->center_freq; event->chan_def.width = chandef.width; event->chan_def.center_frequency1 = chandef.center_freq1; event->chan_def.center_frequency2 = chandef.center_freq2; } } img_genlmsg_reply(event, sizeof(*event)); return 0; } int umac_proc_cmd_get_tx_power(struct nrf_wifi_umac_cmd_get_tx_power *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct nrf_wifi_umac_event_get_tx_power *event; unsigned int req_len; int err; int txpwr_level=0; struct net_device *dev = wdev->netdev; req_len = sizeof(*event); event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!event) return -ENOMEM; img_nl80211hdr_put(&event->umac_hdr, cmd->umac_hdr.portid, cmd->umac_hdr.seq, 0, IMG_CMD_GET_TX_POWER); event->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; event->umac_hdr.ids.ifaceindex = dev->ifindex; if (!rdev->ops->get_tx_power) return -EOPNOTSUPP; err = rdev_get_tx_power(rdev, wdev, &txpwr_level); if (err) { return err; } event->txpwr_level = txpwr_level; img_genlmsg_reply(event, sizeof(*event)); return 0; } int nl80211_del_interface(struct nrf_wifi_umac_cmd_del_vif *cmd_del_if, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { (void)cmd_del_if; struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; int status; status = rdev_del_virtual_intf(rdev, wdev); return status; } struct get_key_cookie { struct nrf_wifi_umac_key_info *msg; int error; int idx; }; void get_key_callback(void *c, struct key_params *params) { struct nrf_wifi_umac_key_info *key_info; struct get_key_cookie *cookie = c; key_info = (struct nrf_wifi_umac_key_info *)cookie->msg; if (!key_info) goto nla_put_failure; if (params->key) { key_info->valid_fields |= NRF_WIFI_KEY_VALID; key_info->key.nrf_wifi_key_len = params->key_len; memcpy(key_info->key.nrf_wifi_key, params->key, params->key_len); } if (params->seq) { key_info->valid_fields |= NRF_WIFI_SEQ_VALID; key_info->seq.nrf_wifi_seq_len = params->seq_len; memcpy(key_info->seq.nrf_wifi_seq, params->seq, params->seq_len); } if (params->cipher) { key_info->valid_fields |= NRF_WIFI_CIPHER_SUITE_VALID; key_info->cipher_suite = params->cipher; } key_info->valid_fields |= NRF_WIFI_KEY_IDX_VALID; key_info->key_idx = cookie->idx; return; nla_put_failure: cookie->error = 1; } int nl80211_get_key(struct nrf_wifi_umac_cmd_get_key *cmd_key, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; int err; struct net_device *dev = net_dev; u8 key_idx = 0; const u8 *mac_addr = NULL; bool pairwise; struct get_key_cookie cookie = { .error = 0, }; struct nrf_wifi_umac_event_get_key *get_key_rsp; unsigned int req_len; if (cmd_key->valid_fields & NRF_WIFI_CMD_GET_KEY_KEY_IDX_VALID) { key_idx = cmd_key->key_idx; } if(cmd_key->valid_fields & NRF_WIFI_CMD_GET_KEY_MAC_ADDR_VALID) { mac_addr = cmd_key->mac_addr; } pairwise = !!mac_addr; req_len = sizeof(*get_key_rsp); get_key_rsp = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!get_key_rsp) return -ENOMEM; img_nl80211hdr_put(&get_key_rsp->umac_hdr, cmd_key->umac_hdr.portid, cmd_key->umac_hdr.seq, 0, NL80211_CMD_NEW_KEY); cookie.msg = &get_key_rsp->key_info; cookie.idx = key_idx; get_key_rsp->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; get_key_rsp->umac_hdr.ids.ifaceindex = dev->ifindex; get_key_rsp->key_info.valid_fields |= NRF_WIFI_KEY_IDX_VALID; get_key_rsp->key_info.key_idx = key_idx; if (mac_addr) { get_key_rsp->valid_fields |= NRF_WIFI_EVENT_GET_KEY_MAC_ADDR_VALID; memcpy(get_key_rsp->mac_addr, mac_addr, ETH_ALEN); } err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie, get_key_callback); if (err) goto free_msg; img_genlmsg_reply(get_key_rsp, sizeof(*get_key_rsp)); free_msg: umac_event_free((char *)get_key_rsp); return err; } int nl80211_set_key(struct nrf_wifi_umac_cmd_set_key *set_key, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct key_parse key; int err; struct net_device *dev = net_dev; err = nl80211_parse_key(&set_key->key_info, &key); if (err) return err; wdev_lock(dev->ieee80211_ptr); if (key.def) { err = nl80211_key_allowed(dev->ieee80211_ptr); if (err) goto out; err = rdev_set_default_key(rdev, dev, key.idx, key.def_uni, key.def_multi); if (err) goto out; } else { err = nl80211_key_allowed(dev->ieee80211_ptr); if (err) goto out; err = rdev_set_default_mgmt_key(rdev, dev, key.idx); if (err) goto out; } out: wdev_unlock(dev->ieee80211_ptr); return err; } int nl80211_new_key(struct nrf_wifi_umac_cmd_key *cmd_key, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; int err; struct net_device *dev= net_dev; struct key_parse key; const u8 *mac_addr = NULL; err = nl80211_parse_key(&cmd_key->key_info, &key); if (err) return err; if (cmd_key->valid_fields & NRF_WIFI_CMD_KEY_MAC_ADDR_VALID) mac_addr = cmd_key->mac_addr; if (key.type == -1) { if (mac_addr) key.type = NL80211_KEYTYPE_PAIRWISE; else key.type = NL80211_KEYTYPE_GROUP; } if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr)) return -EINVAL; wdev_lock(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr); if (!err) err = rdev_add_key(rdev, dev, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr, &key.p); wdev_unlock(dev->ieee80211_ptr); return err; } int nl80211_del_key(struct nrf_wifi_umac_cmd_key *cmd_key, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; int err; struct net_device *dev = net_dev; u8 *mac_addr = NULL; struct key_parse key; err = nl80211_parse_key(&cmd_key->key_info, &key); if (err) return err; if (cmd_key->valid_fields & NRF_WIFI_CMD_KEY_MAC_ADDR_VALID) mac_addr = cmd_key->mac_addr; if (key.type == -1) { if (mac_addr) key.type = NL80211_KEYTYPE_PAIRWISE; else key.type = NL80211_KEYTYPE_GROUP; } wdev_lock(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr); if (!err) err = rdev_del_key(rdev, dev, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr); wdev_unlock(dev->ieee80211_ptr); return err; } int nl80211_parse_beacon(struct nrf_wifi_beacon_data beacon_info, struct cfg80211_beacon_data *bcn) { bool haveinfo = false; memset(bcn, 0, sizeof(*bcn)); if (beacon_info.head_len) { bcn->head = beacon_info.head; bcn->head_len = beacon_info.head_len; if (!bcn->head_len) return -EINVAL; haveinfo = true; } if (beacon_info.tail_len) { bcn->tail = beacon_info.tail; bcn->tail_len = beacon_info.tail_len; haveinfo = true; } if (!haveinfo) return -EINVAL; if (beacon_info.probe_resp_len) { bcn->probe_resp = beacon_info.probe_resp; bcn->probe_resp_len = beacon_info.probe_resp_len; } return 0; } bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, struct cfg80211_ap_settings *params) { struct wireless_dev *wdev; bool ret = false; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; if (!wdev->preset_chandef.chan) continue; params->chandef = wdev->preset_chandef; ret = true; break; } return ret; } int nl80211_start_ap(struct nrf_wifi_umac_cmd_start_ap *cmd_start_ap, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_ap_settings params; int err; memset(¶ms, 0, sizeof(params)); err = nl80211_parse_beacon(cmd_start_ap->info.beacon_data, ¶ms.beacon); if (err){ return err; } params.beacon_interval = cmd_start_ap->info.beacon_interval; params.dtim_period = cmd_start_ap->info.dtim_period; err = cfg80211_validate_beacon_int(rdev,dev->ieee80211_ptr->iftype, params.beacon_interval); if (err) return err; /* * In theory, some of these attributes should be required here * but since they were not used when the command was originally * added, keep them optional for old user space programs to let * them continue to work with drivers that do not need the * additional information -- drivers must check! */ params.ssid = (u8 *)cmd_start_ap->info.ssid.nrf_wifi_ssid; params.ssid_len = cmd_start_ap->info.ssid.nrf_wifi_ssid_len; params.hidden_ssid = cmd_start_ap->info.hidden_ssid; params.privacy = !!(cmd_start_ap->info.nrf_wifi_flags & NRF_WIFI_CMD_BEACON_INFO_PRIVACY); if (cmd_start_ap->valid_fields & NRF_WIFI_CMD_BEACON_INFO_AUTH_TYPE_VALID) { params.auth_type = cmd_start_ap->info.auth_type; } else params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; err = nl80211_crypto_settings(rdev, &cmd_start_ap->info.connect_common_info, ¶ms.crypto, NL80211_MAX_NR_CIPHER_SUITES); if (err) { return err; } if (cmd_start_ap->valid_fields & NRF_WIFI_CMD_BEACON_INFO_INACTIVITY_TIMEOUT_VALID) { params.inactivity_timeout = cmd_start_ap->info.inactivity_timeout; } if (cmd_start_ap->valid_fields & NRF_WIFI_CMD_BEACON_INFO_P2P_CTWINDOW_VALID) { params.p2p_ctwindow = cmd_start_ap->info.p2p_go_ctwindow; } if (cmd_start_ap->valid_fields & NRF_WIFI_CMD_BEACON_INFO_P2P_OPPPS_VALID) { params.p2p_opp_ps = cmd_start_ap->info.p2p_opp_ps; } if (cmd_start_ap->info.freq_params.valid_fields & NRF_WIFI_SET_FREQ_PARAMS_FREQ_VALID) { err = nl80211_parse_chandef(rdev, &cmd_start_ap->info.freq_params, ¶ms.chandef); if (err) return err; } else if (wdev->preset_chandef.chan) { params.chandef = wdev->preset_chandef; } else if (!nl80211_get_ap_channel(rdev, ¶ms)){ printk("%s %d\n", __func__, __LINE__ ); return -EINVAL; } if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef, wdev->iftype)){ printk("%s %d\n", __func__, __LINE__ ); return -EINVAL; } params.smps_mode = cmd_start_ap->info.smps_mode; switch (params.smps_mode) { case NL80211_SMPS_OFF: break; case NL80211_SMPS_STATIC: break; case NL80211_SMPS_DYNAMIC: break; default: return -EINVAL; } wdev_lock(wdev); err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { wdev->preset_chandef = params.chandef; wdev->beacon_interval = params.beacon_interval; wdev->chandef = params.chandef; wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } wdev_unlock(wdev); return err; } int nl80211_set_beacon(struct nrf_wifi_umac_cmd_set_beacon *cmd_set_bcn, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_beacon_data params; int err; err = nl80211_parse_beacon(cmd_set_bcn->info.beacon_data, ¶ms); if (err) return err; wdev_lock(wdev); err = rdev_change_beacon(rdev, dev, ¶ms); wdev_unlock(wdev); return err; } int img_nl80211_stop_ap(struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; return cfg80211_stop_ap(rdev, dev, false); } int parse_station_flags(struct nrf_wifi_sta_flag_update *sta_flags2, enum nl80211_iftype iftype, struct station_parameters *params) { int flag; /* * Try parsing the new attribute first so userspace * can specify both for older kernels. */ struct nl80211_sta_flag_update *sta_flags; /*flags are typecasted to the nl80211_sta_flag_update structure, the length field is not required*/ sta_flags = (struct nl80211_sta_flag_update *)sta_flags2; params->sta_flags_mask = sta_flags->mask; params->sta_flags_set = sta_flags->set; params->sta_flags_set &= params->sta_flags_mask; if ((params->sta_flags_mask | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) return -EINVAL; return 0; /* * Only allow certain flags for interface types so that * other attributes are silently ignored. Remember that * this is backward compatibility code with old userspace * and shouldn't be hit in other cases anyway. */ switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_TDLS_PEER); break; #ifdef MESH_POINT_MODE_SUPPORT case NL80211_IFTYPE_MESH_POINT: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHORIZED); #endif default: return -EINVAL; } for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) { params->sta_flags_set |= (1< NL80211_STA_FLAG_MAX_OLD_API) return -EINVAL; } return 0; } bool nl80211_put_sta_rate(struct nrf_wifi_rate_info *rate_info, struct rate_info *info, int attr) { (void)attr; u32 bitrate; u16 bitrate_compat; /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ bitrate = cfg80211_calculate_bitrate(info); /* report 16-bit bitrate only if we can */ bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; if (bitrate > 0) { rate_info->valid_fields |= NRF_WIFI_RATE_INFO_BITRATE_VALID; rate_info->bitrate = bitrate; } if (bitrate_compat > 0) { rate_info->valid_fields |= NRF_WIFI_RATE_INFO_BITRATE_COMPAT_VALID; rate_info->bitrate_compat = bitrate_compat; } switch (info->bw) { case RATE_INFO_BW_5: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_5_MHZ_WIDTH; break; case RATE_INFO_BW_10: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_10_MHZ_WIDTH; break; default: WARN_ON(1); /* fall through */ case RATE_INFO_BW_20: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_0_MHZ_WIDTH; break; case RATE_INFO_BW_40: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_40_MHZ_WIDTH; break; case RATE_INFO_BW_80: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_80_MHZ_WIDTH; break; case RATE_INFO_BW_160: rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_160_MHZ_WIDTH; break; case RATE_INFO_BW_HE_RU: rate_info->nrf_wifi_flags = 0; } if (info->flags & RATE_INFO_FLAGS_MCS) { rate_info->valid_fields |= NRF_WIFI_RATE_INFO_BITRATE_MCS_VALID; rate_info->nrf_wifi_mcs = info->mcs; if (info->flags & RATE_INFO_FLAGS_SHORT_GI) { rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_SHORT_GI; } } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { rate_info->valid_fields |= NRF_WIFI_RATE_INFO_BITRATE_VHT_MCS_VALID; rate_info->vht_mcs = info->mcs; rate_info->valid_fields |= NRF_WIFI_RATE_INFO_BITRATE_VHT_NSS_VALID; rate_info->vht_nss = info->nss; if (info->flags & RATE_INFO_FLAGS_SHORT_GI) { rate_info->nrf_wifi_flags |= NRF_WIFI_RATE_INFO_SHORT_GI; } } return true; } bool nl80211_put_signal(unsigned char *msg, unsigned int *msg_mask, u8 mask, s8 *signal, int id) { (void)id; int i = 0; if (!mask) return true; for (i = 0; i < IEEE80211_MAX_CHAINS; i++) { if (!(mask & BIT(i))) continue; *msg_mask |= (1 << i); msg[i] = signal[i]; } return true; } int nl80211_send_station(struct nrf_wifi_umac_event_new_station *send_station, u32 cmd, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { img_nl80211hdr_put(&send_station->umac_hdr, portid, seq, flags, cmd); send_station->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; send_station->umac_hdr.ids.ifaceindex = dev->ifindex; send_station->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; send_station->umac_hdr.ids.wdev_id = dev->ieee80211_ptr->identifier; memcpy(send_station->mac_addr, mac_addr, ETH_ALEN); if(sinfo == NULL) return 0; send_station->generation = sinfo->generation; #define PUT_SINFO(attr, memb, type) do { \ send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_ ## attr ## _VALID; \ send_station->sta_info.memb = sinfo->memb; \ } while (0) PUT_SINFO(CONNECTED_TIME, connected_time, u32); PUT_SINFO(INACTIVE_TIME, inactive_time, u32); if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_RX_BYTES64))) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_RX_BYTES_VALID; send_station->sta_info.rx_bytes = (u32)sinfo->rx_bytes; } if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES64))) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_TX_BYTES_VALID; send_station->sta_info.tx_bytes = (u32)sinfo->tx_bytes; } PUT_SINFO(RX_BYTES, rx_bytes, u64); PUT_SINFO(TX_BYTES, tx_bytes, u64); PUT_SINFO(LLID, llid, u16); PUT_SINFO(PLID, plid, u16); PUT_SINFO(PLINK_STATE, plink_state, u8); switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: PUT_SINFO(SIGNAL, signal, u8); PUT_SINFO(SIGNAL_AVG, signal_avg, u8); break; default: break; } if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_CHAIN_SIGNAL_VALID; if (!nl80211_put_signal(send_station->sta_info.chain_signal, &send_station->sta_info.chain_signal_mask, sinfo->chains, sinfo->chain_signal, NL80211_STA_INFO_CHAIN_SIGNAL)) goto nla_put_failure; } if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_CHAIN_SIGNAL_AVG_VALID; if (!nl80211_put_signal(send_station->sta_info.chain_signal_avg, &send_station->sta_info.chain_signal_avg_mask, sinfo->chains, sinfo->chain_signal_avg, NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) goto nla_put_failure; } if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_TX_BITRATE_VALID; if (!nl80211_put_sta_rate(&send_station->sta_info.tx_bitrate, &sinfo->txrate, NL80211_STA_INFO_TX_BITRATE)) goto nla_put_failure; } if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_RX_BITRATE_VALID; if (!nl80211_put_sta_rate(&send_station->sta_info.rx_bitrate, &sinfo->rxrate, NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; } PUT_SINFO(RX_PACKETS, rx_packets, u32); PUT_SINFO(TX_PACKETS, tx_packets, u32); PUT_SINFO(TX_RETRIES, tx_retries, u32); PUT_SINFO(TX_FAILED, tx_failed, u32); PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); PUT_SINFO(BEACON_LOSS_COUNT, beacon_loss_count, u32); PUT_SINFO(LOCAL_PM, local_pm, u32); PUT_SINFO(PEER_PM, peer_pm, u32); PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); send_station->sta_info.bss_param.nrf_wifi_flags = sinfo->bss_param.flags; send_station->sta_info.bss_param.dtim_period = sinfo->bss_param.dtim_period; send_station->sta_info.bss_param.beacon_interval = sinfo->bss_param.beacon_interval; send_station->sta_info.valid_fields |=NRF_WIFI_STA_INFO_STA_BSS_PARAMS_VALID; if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS))) { send_station->sta_info.valid_fields |= NRF_WIFI_STA_INFO_STA_FLAGS_VALID; memcpy(&send_station->sta_info.sta_flags, &sinfo->sta_flags, sizeof(struct nl80211_sta_flag_update)); } PUT_SINFO(T_OFFSET, t_offset, u64); PUT_SINFO(RX_DROPPED_MISC, rx_dropped_misc, u64); PUT_SINFO(RX_BEACON, rx_beacon, u64); PUT_SINFO(RX_BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); #undef PUT_SINFO if (sinfo->assoc_req_ies_len) { send_station->valid_fields |= NRF_WIFI_CMD_SEND_STATION_ASSOC_REQ_IES_VALID; send_station->assoc_req_ies.ie_len = sinfo->assoc_req_ies_len; memcpy(send_station->assoc_req_ies.ie, sinfo->assoc_req_ies, sinfo->assoc_req_ies_len); } return 0; nla_put_failure: return -EMSGSIZE; } int nl80211_get_station(struct nrf_wifi_umac_cmd_get_sta *get_station, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct station_info sinfo; struct nrf_wifi_umac_event_new_station *msg; u8 *mac_addr = NULL; int err; unsigned int req_len; memset(&sinfo, 0, sizeof(sinfo)); mac_addr = get_station->info.mac_addr; if (!rdev->ops->get_station) return -EOPNOTSUPP; err = rdev_get_station(rdev, dev, mac_addr, &sinfo); if (err) return err; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!msg) return -ENOMEM; if (nl80211_send_station(msg, NL80211_CMD_GET_STATION, get_station->umac_hdr.portid, get_station->umac_hdr.seq, 0, rdev, dev, mac_addr, &sinfo) < 0) { umac_event_free((char *)msg); return -ENOBUFS; } img_genlmsg_reply(msg, sizeof(*msg)); return 0; } int cfg80211_check_station_change(struct wiphy *wiphy, struct station_parameters *params, enum cfg80211_station_type statype) { if (params->listen_interval != -1){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->aid && !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } /* When you run into this, adjust the code below for the new flag */ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); switch (statype) { case CFG80211_STA_MESH_PEER_KERNEL: case CFG80211_STA_MESH_PEER_USER: /* * No ignoring the TDLS flag here -- the userspace mesh * code doesn't have the bug of including TDLS in the * mask everywhere. */ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHORIZED))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; case CFG80211_STA_TDLS_PEER_SETUP: case CFG80211_STA_TDLS_PEER_ACTIVE: if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } /* ignore since it can't change */ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); break; default: /* disallow mesh-specific things */ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->local_pm){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } } if (statype != CFG80211_STA_TDLS_PEER_SETUP && statype != CFG80211_STA_TDLS_PEER_ACTIVE) { /* TDLS can't be set, ... */ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } /* * ... but don't bother the driver with it. This works around * a hostapd/wpa_supplicant issue -- it always includes the * TLDS_PEER flag in the mask even for AP mode. */ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); } if (statype != CFG80211_STA_TDLS_PEER_SETUP && statype != CFG80211_STA_AP_CLIENT_UNASSOC) { /* reject other things that can't change */ if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->supported_rates){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->ext_capab || params->ht_capa || params->vht_capa){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } } if (statype != CFG80211_STA_AP_CLIENT && statype != CFG80211_STA_AP_CLIENT_UNASSOC) { if (params->vlan){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } } switch (statype) { case CFG80211_STA_AP_MLME_CLIENT: /* Use this only for authorizing/unauthorizing a station */ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))){ printk("%s %d\n", __func__, __LINE__); return -EOPNOTSUPP; } break; case CFG80211_STA_AP_CLIENT: case CFG80211_STA_AP_CLIENT_UNASSOC: /* accept only the listed bits */ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } /* but authenticated/associated only if driver handles it */ if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) && params->sta_flags_mask & (BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_ASSOCIATED))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; case CFG80211_STA_IBSS: case CFG80211_STA_AP_STA: /* reject any changes other than AUTHORIZED */ if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; case CFG80211_STA_TDLS_PEER_SETUP: /* reject any changes other than AUTHORIZED or WME */ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_WME))){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } /* force (at least) rates when authorizing */ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) && !params->supported_rates){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; case CFG80211_STA_TDLS_PEER_ACTIVE: /* reject any changes */ return -EINVAL; case CFG80211_STA_MESH_PEER_KERNEL: if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; case CFG80211_STA_MESH_PEER_USER: if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION && params->plink_action != NL80211_PLINK_ACTION_BLOCK) { printk("%s %d\n", __func__, __LINE__); return -EINVAL; } break; } /* * Older kernel versions ignored this attribute entirely, so don't * reject attempts to update it but mark it as unused instead so the * driver won't look at the data. */ if (statype != CFG80211_STA_AP_CLIENT_UNASSOC && statype != CFG80211_STA_TDLS_PEER_SETUP) params->opmode_notif_used = false; return 0; } EXPORT_SYMBOL(cfg80211_check_station_change); int nl80211_parse_sta_wme(char wme_uapsd_queues, char wme_max_sp, struct station_parameters *params) { if((wme_uapsd_queues == 0) && (wme_max_sp == 0)) return 0; if (wme_uapsd_queues) params->uapsd_queues = wme_uapsd_queues; if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (wme_max_sp) params->max_sp = wme_max_sp; if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; return 0; } int nl80211_parse_sta_channel_info(struct nrf_wifi_supported_channels *supp_ch, struct nrf_wifi_supported_oper_classes *op_class, struct station_parameters *params) { if (supp_ch) { params->supported_channels = supp_ch->supported_channels; params->supported_channels_len = supp_ch->supported_channels_len; /* * Need to include at least one (first channel, number of * channels) tuple for each subband, and must have proper * tuples for the rest of the data as well. */ if (params->supported_channels_len < 2){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } if (params->supported_channels_len % 2){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } } if (op_class) { params->supported_oper_classes = op_class->supported_oper_classes; params->supported_oper_classes_len = op_class->supported_oper_classes_len; /* * The value of the Length field of the Supported Operating * Classes element is between 2 and 253. */ if (params->supported_oper_classes_len < 2 || params->supported_oper_classes_len > 253){ printk("%s %d\n", __func__, __LINE__); return -EINVAL; } } return 0; } int nl80211_set_station(struct nrf_wifi_umac_cmd_chg_sta *set_stn, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct station_parameters params; u8 *mac_addr; int err; memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; mac_addr = set_stn->info.mac_addr; if (set_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_SUPP_RATES_VALID) { params.supported_rates = set_stn->info.supp_rates.rates; params.supported_rates_len = set_stn->info.supp_rates.nrf_wifi_num_rates; } if (set_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_STA_CAPABILITY_VALID) { params.capability = set_stn->info.sta_capability; params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; } if (set_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_EXT_CAPABILITY_VALID) { params.ext_capab = set_stn->info.ext_capability.ext_capability; params.ext_capab_len = set_stn->info.ext_capability.ext_capability_len; } if(set_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_STA_FLAGS2_VALID) if (parse_station_flags(&set_stn->info.sta_flags2, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; /* driver will call cfg80211_check_station_change() */ err = rdev_change_station(rdev, dev, mac_addr, ¶ms); return err; } int nl80211_new_station(struct nrf_wifi_umac_cmd_add_sta *cmd_new_stn, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; int err = 0; struct net_device *dev = net_dev; struct station_parameters params; u8 *mac_addr = NULL; unsigned char wme_uapsd_queues = 0, wme_max_sp = 0; #ifdef BSS_OPTIMIZATION u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_ASSOCIATED); #endif memset(¶ms, 0, sizeof(params)); mac_addr = cmd_new_stn->info.mac_addr; params.supported_rates = cmd_new_stn->info.supp_rates.rates; params.supported_rates_len = cmd_new_stn->info.supp_rates.nrf_wifi_num_rates; params.listen_interval = cmd_new_stn->info.nrf_wifi_listen_interval; if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_PEER_AID_VALID) params.aid = cmd_new_stn->info.nrf_wifi_peer_aid; else params.aid = cmd_new_stn->info.aid; if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_STA_CAPABILITY_VALID) { params.capability = cmd_new_stn->info.sta_capability; params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; } if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_EXT_CAPABILITY_VALID) { params.ext_capab = cmd_new_stn->info.ext_capability.ext_capability; params.ext_capab_len = cmd_new_stn->info.ext_capability.ext_capability_len; } if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_HT_CAPABILITY_VALID) params.ht_capa = (struct ieee80211_ht_cap *)cmd_new_stn->info.ht_capability; if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_VHT_CAPABILITY_VALID) params.vht_capa = (struct ieee80211_vht_cap *)cmd_new_stn->info.vht_capability; if (cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_OPMODE_NOTIF_VALID) { params.opmode_notif_used = true; params.opmode_notif = cmd_new_stn->info.opmode_notif; } if(cmd_new_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_SUPPORTED_CHANNELS_VALID){ err = nl80211_parse_sta_channel_info(&cmd_new_stn->info.supported_channels, NULL, ¶ms); if (err) return err; } if(cmd_new_stn->valid_fields & NRF_WIFI_CMD_NEW_STATION_SUPPORTED_OPER_CLASSES_VALID){ err = nl80211_parse_sta_channel_info(NULL,&cmd_new_stn->info.supported_oper_classes, ¶ms); if (err) return err; } if(cmd_new_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_STA_WME_UAPSD_QUEUES_VALID) wme_uapsd_queues = cmd_new_stn->info.wme_uapsd_queues; if(cmd_new_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_STA_WME_MAX_SP_VALID) wme_max_sp = cmd_new_stn->info.wme_max_sp; err = nl80211_parse_sta_wme(wme_uapsd_queues, wme_max_sp, ¶ms); if (err) return err; if(cmd_new_stn->valid_fields & NRF_WIFI_CMD_SET_STATION_STA_FLAGS2_VALID) if (parse_station_flags(&cmd_new_stn->info.sta_flags2, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT * as userspace might just pass through the capabilities from the IEs * directly, rather than enforcing this restriction and returning an * error in this case. */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) { params.ht_capa = NULL; params.vht_capa = NULL; } /* When you run into this, adjust the code below for the new flag */ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: /* ignore WME attributes if iface/sta is not capable */ if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) || !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; /* but don't bother the driver with it */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); #ifdef BSS_OPTIMIZATION /* allow authenticated/associated only if driver handles it */ if (!(rdev->wiphy.features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) && params.sta_flags_mask & auth_assoc) return -EINVAL; /* Older userspace, or userspace wanting to be compatible with * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth * and assoc flags in the mask, but assumes the station will be * added as associated anyway since this was the required driver * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was * introduced. * In order to not bother drivers with this quirk in the API * set the flags in both the mask and set for new stations in * this case. */ if (!(params.sta_flags_mask & auth_assoc)) { params.sta_flags_mask |= auth_assoc; params.sta_flags_set |= auth_assoc; } #endif break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: /* ignore uAPSD data */ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; /* * Older wpa_supplicant versions always mark the TDLS peer * as authorized, but it shouldn't yet be. */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); break; default: return -EOPNOTSUPP; } /* be aware of params.vlan when changing code here */ err = rdev_add_station(rdev, dev, mac_addr, ¶ms); if (params.vlan) dev_put(params.vlan); return err; } int nl80211_del_station(struct nrf_wifi_umac_cmd_del_sta *cmd_del_stn, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct station_del_parameters params; memset(¶ms, 0, sizeof(params)); if (cmd_del_stn->valid_fields & NRF_WIFI_CMD_DEL_STATION_MAC_ADDR_VALID) params.mac = cmd_del_stn->info.mac_addr; if (cmd_del_stn->valid_fields & NRF_WIFI_CMD_DEL_STATION_MGMT_SUBTYPE_VALID) { params.subtype = cmd_del_stn->info.mgmt_subtype; } else { /* Default to Deauthentication frame */ params.subtype = IEEE80211_STYPE_DEAUTH >> 4; } if (cmd_del_stn->valid_fields & NRF_WIFI_CMD_DEL_STATION_REASON_CODE_VALID) { params.reason_code = cmd_del_stn->info.reason_code; } else { /* Default to reason code 2 */ params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; } return rdev_del_station(rdev, dev, ¶ms); } int nl80211_set_bss(struct nrf_wifi_umac_cmd_set_bss *cmd_set_bss, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct bss_parameters params; int err; memset(¶ms, 0, sizeof(params)); /* default to not changing parameters */ params.use_cts_prot = -1; params.use_short_preamble = -1; params.use_short_slot_time = -1; params.ap_isolate = -1; params.ht_opmode = -1; params.p2p_ctwindow = -1; params.p2p_opp_ps = -1; if(cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_CTS_VALID) params.use_cts_prot = cmd_set_bss->bss_info.nrf_wifi_cts; if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_PREAMBLE_VALID) params.use_short_preamble = cmd_set_bss->bss_info.preamble; if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_SLOT_VALID) params.use_short_slot_time = cmd_set_bss->bss_info.nrf_wifi_slot; if (cmd_set_bss->bss_info.num_basic_rates) { params.basic_rates = cmd_set_bss->bss_info.basic_rates; params.basic_rates_len = cmd_set_bss->bss_info.num_basic_rates; } if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_AP_ISOLATE_VALID) params.ap_isolate = !!cmd_set_bss->bss_info.ap_isolate; if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_HT_OPMODE_VALID) params.ht_opmode = cmd_set_bss->bss_info.ht_opmode; if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_P2P_CTWINDOW_VALID){ params.p2p_ctwindow = cmd_set_bss->bss_info.p2p_go_ctwindow; } if (cmd_set_bss->valid_fields & NRF_WIFI_CMD_SET_BSS_P2P_OPPPS_VALID){ params.p2p_opp_ps = cmd_set_bss->bss_info.p2p_opp_ps; } wdev_lock(wdev); err = rdev_change_bss(rdev, dev, ¶ms); wdev_unlock(wdev); return err; } int parse_reg_rule(struct nrf_wifi_reg_rules *rule, struct ieee80211_reg_rule *reg_rule) { struct ieee80211_freq_range *freq_range = ®_rule->freq_range; struct ieee80211_power_rule *power_rule = ®_rule->power_rule; if (!rule->valid_fields & REG_RULE_FLAGS_VALID) return -EINVAL; if (!(rule->valid_fields & FREQ_RANGE_START_VALID)) return -EINVAL; if (!(rule->valid_fields & FREQ_RANGE_END_VALID)) return -EINVAL; if (!(rule->valid_fields & FREQ_RANGE_MAX_BW_VALID)) return -EINVAL; if (!(rule->valid_fields & POWER_RULE_MAX_EIRP_VALID)) return -EINVAL; reg_rule->flags = rule->rule_flags; freq_range->start_freq_khz = rule->freq_range_start; freq_range->end_freq_khz = rule->freq_range_end; freq_range->max_bandwidth_khz = rule->freq_range_max_bw; power_rule->max_eirp = rule->pwr_max_eirp; return 0; } int nl80211_req_set_reg(struct nrf_wifi_cmd_req_set_reg *cmd) { char *data = NULL; enum nl80211_user_reg_hint_type user_reg_hint_type; /* * You should only get this when cfg80211 hasn't yet initialized * completely when built-in to the kernel right between the time * window between nl80211_init() and regulatory_init(), if that is * even possible. */ if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) return -EINPROGRESS; if (cmd->valid_fields & NRF_WIFI_CMD_REQ_SET_REG_USER_REG_HINT_TYPE_VALID) user_reg_hint_type = cmd->nrf_wifi_user_reg_hint_type; else user_reg_hint_type = NL80211_USER_REG_HINT_USER; switch (user_reg_hint_type) { case NL80211_USER_REG_HINT_USER: case NL80211_USER_REG_HINT_CELL_BASE: if (!cmd->valid_fields & NRF_WIFI_CMD_REQ_SET_REG_ALPHA2_VALID) return -EINVAL; data = (char *)cmd->nrf_wifi_alpha2; return regulatory_hint_user(data, user_reg_hint_type); default: return -EINVAL; } } int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, struct nrf_wifi_reg *msg) { unsigned int i; msg->valid_fields |= NRF_WIFI_CMD_SET_REG_ALPHA2_VALID | NRF_WIFI_CMD_SET_REG_RULES_VALID; if(regdom->dfs_region){ msg->valid_fields |= NRF_WIFI_CMD_SET_REG_DFS_REGION_VALID; } strcpy((char *)msg->nrf_wifi_alpha2, regdom->alpha2); msg->dfs_region = regdom->dfs_region; msg->num_reg_rules= regdom->n_reg_rules; for (i = 0; i < regdom->n_reg_rules; i++) { const struct ieee80211_reg_rule *reg_rule; const struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule; unsigned int max_bandwidth_khz; reg_rule = ®dom->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; max_bandwidth_khz = freq_range->max_bandwidth_khz; if (!max_bandwidth_khz) max_bandwidth_khz = reg_get_max_bandwidth(regdom, reg_rule); msg->nrf_wifi_reg_rules[i].rule_flags = reg_rule->flags; msg->nrf_wifi_reg_rules[i].freq_range_start = freq_range->start_freq_khz; msg->nrf_wifi_reg_rules[i].freq_range_end = freq_range->end_freq_khz; msg->nrf_wifi_reg_rules[i].freq_range_max_bw = max_bandwidth_khz; msg->nrf_wifi_reg_rules[i].pwr_max_eirp = power_rule->max_eirp; msg->nrf_wifi_reg_rules[i].valid_fields |= REG_RULE_FLAGS_VALID | FREQ_RANGE_START_VALID | FREQ_RANGE_END_VALID | FREQ_RANGE_MAX_BW_VALID | POWER_RULE_MAX_EIRP_VALID; } return 0; } int nl80211_get_reg_do(struct cfg80211_registered_device *rdev) { (void)rdev; const struct ieee80211_regdomain *regdom = NULL; struct nrf_wifi_reg *msg; unsigned int req_len; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!msg) return -ENOBUFS; img_nl80211hdr_put(&msg->umac_hdr, 0, 0, 0, NL80211_CMD_GET_REG); rcu_read_lock(); if (!regdom) regdom = rcu_dereference(cfg80211_regdomain); nl80211_put_regdom(regdom, msg); rcu_read_unlock(); img_genlmsg_reply((void *)msg, sizeof(*msg)); return 0; } int nl80211_set_reg(struct nrf_wifi_reg *cmd) { char *alpha2; int r; u32 rule_idx = 0, size_of_regd; enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; struct ieee80211_regdomain *rd; if (!cmd->valid_fields & NRF_WIFI_CMD_SET_REG_ALPHA2_VALID) return -EINVAL; if (!(cmd->valid_fields & NRF_WIFI_CMD_SET_REG_RULES_VALID)) return -EINVAL; alpha2 = (char *)cmd->nrf_wifi_alpha2; if (cmd->valid_fields & NRF_WIFI_CMD_SET_REG_DFS_REGION_VALID) dfs_region = cmd->dfs_region; if (!reg_is_valid_request(alpha2)) return -EINVAL; size_of_regd = sizeof(struct ieee80211_regdomain) + cmd->num_reg_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) return -ENOMEM; rd->n_reg_rules = cmd->num_reg_rules; rd->alpha2[0] = alpha2[0]; rd->alpha2[1] = alpha2[1]; /* * Disable DFS master mode if the DFS region was * not supported or known on this kernel. */ if (reg_supported_dfs_region(dfs_region)) rd->dfs_region = dfs_region; for(rule_idx = 0; rule_idx < cmd->num_reg_rules; rule_idx++){ r = parse_reg_rule(&cmd->nrf_wifi_reg_rules[rule_idx], &rd->reg_rules[rule_idx]); if (r) goto bad_reg; rule_idx++; if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { r = -EINVAL; goto bad_reg; } } r = set_regdom(rd, REGD_SOURCE_CRDA); /* set_regdom took ownership */ rd = NULL; bad_reg: kfree(rd); return r; } RETENTION_MEM_SECTION_UNINITIALIZED unsigned long long scan_wdev_id_g; int nl80211_trigger_scan(struct nrf_wifi_umac_cmd_scan *scan_cmd, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; struct cfg80211_scan_request *request; struct wiphy *wiphy; int err, n_ssids = 0, n_channels, i; size_t ie_len; #ifdef BSS_OPTIMIZATION struct umac_display_bss *display_scan, *tmp1; struct cfg80211_internal_bss *scan, *tmp2; #endif wiphy = &rdev->wiphy; if (rdev->scan_req || rdev->scan_msg) { IMG_CMD_EVENT_DBG_PARAM_INCR(umac_scan_busy,1); err = -EBUSY; goto unlock; } if(scan_cmd->info.scan_params.num_scan_channels){ n_channels = scan_cmd->info.scan_params.num_scan_channels; if(CHANNEL_MAPPING_SCAN == scan_cmd->info.scan_mode){ mapped_scan_ch_gp = kzalloc(sizeof(struct mapped_scan_channels_info), GFP_KERNEL); for(i=0; ichannels[i], &scan_cmd->info.scan_params.channels[i],sizeof(struct nrf_wifi_channel)); mapped_scan_ch_gp->probe_cnt[i] = scan_cmd->info.scan_params.probe_cnt[i]; mapped_scan_ch_gp->scan_duration[i] = scan_cmd->info.scan_params.scan_duration[i]; } mapped_scan_ch_gp->oper_ch_duration = scan_cmd->info.scan_params.oper_ch_duration; } } else { n_channels = ieee80211_get_num_supported_channels(wiphy); } n_ssids = scan_cmd->info.scan_params.num_scan_ssids; ie_len = scan_cmd->info.scan_params.ie.ie_len; scan_wdev_id_g = scan_cmd->umac_hdr.ids.wdev_id; request = kzalloc(sizeof(*request) + sizeof(*request->ssids) * n_ssids + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (n_ssids) request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = n_ssids; if (ie_len) { if (n_ssids) request->ie = (void *)(request->ssids + n_ssids); else request->ie = (void *)(request->channels + n_channels); } i = 0; if(scan_cmd->info.scan_params.num_scan_channels){ struct ieee80211_channel *chan; for(i=0; iinfo.scan_mode){ chan = &mapped_scan_ch_gp->channels[i]; }else{ chan = ieee80211_get_channel(wiphy, scan_cmd->info.scan_params.channels[i].center_frequency); } /* ignore disabled channels */ if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; } } else { enum nl80211_band band; /* all channels */ for (band = 0; band < NUM_NL80211_BANDS; band++) { int j; if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { struct ieee80211_channel *chan; chan = &wiphy->bands[band]->channels[j]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; i++; } } } request->n_channels = i; i = 0; if (n_ssids) { for(i=0; issids[i].ssid_len = scan_cmd->info.scan_params.scan_ssids[i].nrf_wifi_ssid_len; memcpy(request->ssids[i].ssid, scan_cmd->info.scan_params.scan_ssids[i].nrf_wifi_ssid, scan_cmd->info.scan_params.scan_ssids[i].nrf_wifi_ssid_len); } } if (scan_cmd->info.scan_params.ie.ie_len){ request->ie_len = ie_len; memcpy((void *)request->ie,scan_cmd->info.scan_params.ie.ie, request->ie_len); } for (i = 0; i < NUM_NL80211_BANDS; i++) if (wiphy->bands[i]) request->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; request->flags = scan_cmd->info.scan_params.scan_flags; memcpy(request->mac_addr, scan_cmd->info.scan_params.mac_addr, ETH_ALEN); memcpy(request->mac_addr_mask, scan_cmd->info.scan_params.mac_addr_mask, ETH_ALEN); request->no_cck = scan_cmd->info.scan_params.no_cck; #ifdef BSS_OPTIMIZATION request->scan_reason = scan_cmd->info.scan_reason; if (request->scan_reason == SCAN_DISPLAY) { list_for_each_entry_safe(display_scan, tmp1, &rdev->display_bss_list, list) { list_del(&display_scan->list); kfree(display_scan); } } else { memset(&nontrans_mbssid, 0, sizeof(struct non_trans_mbssid)); list_for_each_entry_safe(scan, tmp2, &rdev->bss_list, list) { cfg80211_unlink_bss(&rdev->wiphy, &scan->pub); } } #endif /* BSS_DTATBASE2_TEST */ request->wdev = wdev; request->wiphy = &rdev->wiphy; request->scan_start = jiffies; rdev->scan_req = request; nl80211_send_scan_start(rdev, wdev); if (wdev->netdev) dev_hold(wdev->netdev); err = rdev_scan(rdev, request); unlock: return err; } int nl80211_abort_scan(struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { if (r_dev->scan_msg) return 0; if (!r_dev->scan_req) return -ENOENT; rdev_abort_scan(r_dev, w_dev); return 0; } int fill_wdev_validfields(struct nrf_wifi_umac_hdr *umac_hdr, struct wireless_dev *wdev) { if (wdev->netdev) { umac_hdr->ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; umac_hdr->ids.ifaceindex = wdev->netdev->ifindex; } umac_hdr->ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; umac_hdr->ids.wdev_id = wdev_id(wdev); return 0; } int nl80211_send_bss(struct nrf_wifi_umac_event_new_scan_results *new_scan_results, unsigned int portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_internal_bss *intbss) { (void)flags; struct cfg80211_bss *res = &intbss->pub; const struct cfg80211_bss_ies *ies; ASSERT_WDEV_LOCK(wdev); img_nl80211hdr_put(&new_scan_results->umac_hdr , portid, seq, 0, NL80211_CMD_NEW_SCAN_RESULTS); new_scan_results->generation = rdev->bss_generation; fill_wdev_validfields(&new_scan_results->umac_hdr, wdev); if (!is_zero_ether_addr(res->bssid)) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_MAC_ADDR_VALID; memcpy(new_scan_results->mac_addr, res->bssid, ETH_ALEN); } if (rcu_access_pointer(res->proberesp_ies)) { new_scan_results->nrf_wifi_flags |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BSS_PRESP_DATA; } /* this pointer prefers to be pointed to probe response data * but is always valid */ ies = rcu_dereference(res->ies); if (ies) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_TSF_VALID; new_scan_results->ies_tsf = ies->tsf; if (ies->len) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_VALID; new_scan_results->ies.ie_len = ies->len; memcpy(new_scan_results->ies.ie, ies->data, ies->len); } } /* and this pointer is always (unless driver didn't know) beacon data */ ies = rcu_dereference(res->beacon_ies); if (ies && ies->from_beacon) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_TSF_VALID; new_scan_results->beacon_ies_tsf = ies->tsf; if (ies->len) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_VALID; new_scan_results->beacon_ies.ie_len = ies->len; memcpy(new_scan_results->beacon_ies.ie, ies->data, ies->len); } } if (res->beacon_interval) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_INTERVAL_VALID; new_scan_results->beacon_interval = res->beacon_interval; } new_scan_results->capability = res->capability; new_scan_results->frequency = res->channel->center_freq; new_scan_results->chan_width = res->scan_width; new_scan_results->seen_ms_ago = jiffies_to_msecs(jiffies - intbss->ts); switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_SIGNAL_VALID; new_scan_results->signal.signal_type = NRF_WIFI_SIGNAL_TYPE_MBM; new_scan_results->signal.signal.mbm_signal = res->signal; break; case CFG80211_SIGNAL_TYPE_UNSPEC: new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_SIGNAL_VALID; new_scan_results->signal.signal_type = NRF_WIFI_SIGNAL_TYPE_UNSPEC; new_scan_results->signal.signal.mbm_signal = res->signal; break; default: break; } switch (wdev->iftype) { case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: if (intbss == wdev->current_bss) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_STATUS_VALID; new_scan_results->status = NL80211_BSS_STATUS_ASSOCIATED; } break; #ifdef ADHOC_MODE_SUPPORT case NL80211_IFTYPE_ADHOC: if (intbss == wdev->current_bss) { new_scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_STATUS_VALID; new_scan_results->status = NL80211_BSS_STATUS_IBSS_JOINED; } break; #endif default: break; } return 0; } int send_connect_scan_results_event(struct nrf_wifi_umac_cmd_get_scan_results *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_internal_bss *scan = NULL; int bss_count = 0, i = 0; unsigned int req_len; SEMAPHORE_T u_event_task_sem; struct nrf_wifi_umac_event_new_scan_results *scan_results; #ifdef BSS_OPTIMIZATION list_for_each_entry(scan, &rdev->bss_list, list) { bss_count++; } #else bss_count = 0; #endif if(bss_count == 0) { req_len = sizeof(struct nrf_wifi_umac_event_new_scan_results); scan_results = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!scan_results){ return -ENOBUFS; } memset(scan_results, 0, sizeof(struct nrf_wifi_umac_event_new_scan_results)); img_nl80211hdr_put(&scan_results->umac_hdr, cmd->umac_hdr.portid, 0, 0, NL80211_CMD_NEW_SCAN_RESULTS); fill_wdev_validfields(&scan_results->umac_hdr, wdev); img_genlmsg_reply(scan_results, sizeof(struct nrf_wifi_umac_event_new_scan_results)); } else { UMAC_OUTMSG("##scan_results = %d\n", bss_count); list_for_each_entry(scan, &rdev->bss_list, list) { retry_buf: if(hal_check_hpqm_events_free_queue_empty()) { mutex_lock_timedout(&u_event_task_sem, 1, 1*1000); goto retry_buf; } req_len = sizeof(struct nrf_wifi_umac_event_new_scan_results); scan_results = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!scan_results){ return -ENOBUFS; } i++; nl80211_send_bss(scan_results, cmd->umac_hdr.portid, (bss_count - i), NLM_F_MULTI, rdev, wdev, scan); img_genlmsg_reply(scan_results, sizeof(struct nrf_wifi_umac_event_new_scan_results)); } } return 0; } int send_display_scan_results_event(struct nrf_wifi_umac_cmd_get_scan_results *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { int display_bss_count = 0; int alloc_event, entry_count = 0, j = 0 ; unsigned int req_len; SEMAPHORE_T u_event_task_sem; struct umac_display_bss *display_scan; struct nrf_wifi_umac_event_new_scan_display_results *scan_display_results; alloc_event = true; umac_wait_for_get_scan = 0; list_for_each_entry(display_scan, &rdev->display_bss_list, list) { display_bss_count++; } mutex_lock_timedout_init(&u_event_task_sem, 0); list_for_each_entry(display_scan, &rdev->display_bss_list, list) { if (alloc_event == true) { retry_buf2: if(hal_check_hpqm_events_free_queue_empty()) { mutex_lock_timedout(&u_event_task_sem, 1, 1*1000); goto retry_buf2; } req_len = sizeof(struct nrf_wifi_umac_event_new_scan_display_results); scan_display_results = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!scan_display_results) { return -ENOBUFS; } img_nl80211hdr_put(&scan_display_results->umac_hdr , cmd->umac_hdr.portid, DISPLAY_BSS_TOHOST_PEREVNT, 0, NL80211_CMD_NEW_SCAN_DISPLAY_RESULTS); fill_wdev_validfields(&scan_display_results->umac_hdr, wdev); } alloc_event = false; memcpy(&scan_display_results->display_results[entry_count], &display_scan->display_bss_info, sizeof(struct umac_display_results)); j++; entry_count++; if ((entry_count == DISPLAY_BSS_TOHOST_PEREVNT) || (j == display_bss_count)) { scan_display_results->event_bss_count = entry_count; if (j == display_bss_count) { scan_display_results->umac_hdr.seq = 0; } img_genlmsg_reply(scan_display_results, sizeof(struct nrf_wifi_umac_event_new_scan_display_results)); entry_count = 0; alloc_event = true; } } return 0; } int nl80211_dump_scan(struct nrf_wifi_umac_cmd_get_scan_results *cmd, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; int ret = 0; wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); #ifdef BSS_OPTIMIZATION if(SCAN_DISPLAY == cmd->scan_reason) { ret = send_display_scan_results_event(cmd, rdev, wdev); } else #endif /* BSS_DTATBASE2_TEST */ { cfg80211_bss_expire(rdev); ret = send_connect_scan_results_event(cmd, rdev, wdev); } spin_unlock_bh(&rdev->bss_lock); wdev_unlock(wdev); return ret; } int nl80211_update_ssid_matched_bss(struct nrf_wifi_umac_event_new_scan_results *scan_results, struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_channel *channel, unsigned long ts) { unsigned short beacon_interval; struct ieee80211_sub_if_data *sdata1; struct cfg80211_registered_device *rdev; struct wiphy *wiphy; struct wireless_dev *wdev; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); wiphy = local->hw.wiphy; rdev = wiphy_to_rdev(wiphy); sdata1 = rcu_dereference(local->scan_sdata); wdev = &sdata1->wdev; scan_results->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_BSS_INFO; scan_results->umac_hdr.portid = 0; scan_results->umac_hdr.seq = 1; scan_results->generation = rdev->bss_generation; if (wdev->netdev) { scan_results->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; scan_results->umac_hdr.ids.ifaceindex = wdev->netdev->ifindex; } scan_results->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; scan_results->umac_hdr.ids.wdev_id = wdev_id(wdev); scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_TSF_VALID; scan_results->ies_tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); if (ielen) { scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_IES_VALID; scan_results->ies.ie_len = ielen; memcpy(scan_results->ies.ie, mgmt->u.probe_resp.variable, ielen); } if (!is_zero_ether_addr(mgmt->bssid)) { scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_MAC_ADDR_VALID; memcpy(scan_results->mac_addr, mgmt->bssid, ETH_ALEN); } if (ieee80211_is_probe_resp(mgmt->frame_control)) { scan_results->nrf_wifi_flags |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BSS_PRESP_DATA; } if (ieee80211_is_beacon(mgmt->frame_control)) { ielen = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_TSF_VALID; scan_results->beacon_ies_tsf = le64_to_cpu(mgmt->u.beacon.timestamp); if (ielen) { scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_IES_VALID; scan_results->beacon_ies.ie_len = ielen; memcpy(scan_results->beacon_ies.ie, mgmt->u.beacon.variable, ielen); } } beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); if (beacon_interval) { scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_BEACON_INTERVAL_VALID; scan_results->beacon_interval = beacon_interval; } scan_results->capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); scan_results->frequency = channel->center_freq; scan_results->seen_ms_ago = jiffies_to_msecs(jiffies - ts); switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_SIGNAL_VALID; scan_results->signal.signal_type = NRF_WIFI_SIGNAL_TYPE_MBM; scan_results->signal.signal.mbm_signal = rx_status->signal * 100; break; case CFG80211_SIGNAL_TYPE_UNSPEC: scan_results->valid_fields |= NRF_WIFI_EVENT_NEW_SCAN_RESULTS_SIGNAL_VALID; scan_results->signal.signal_type = NRF_WIFI_SIGNAL_TYPE_UNSPEC; scan_results->signal.signal.mbm_signal = (rx_status->signal * 100) / local->hw.max_signal; break; default: break; } scan_results->chan_width = NL80211_BSS_CHAN_WIDTH_20; if (rx_status->bw == RATE_INFO_BW_5) scan_results->chan_width = NL80211_BSS_CHAN_WIDTH_5; else if (rx_status->bw == RATE_INFO_BW_10) scan_results->chan_width = NL80211_BSS_CHAN_WIDTH_10; return 0; } int send_ssid_matched_bss_to_host(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status,struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_channel *channel, unsigned long ts) { unsigned int req_len; struct nrf_wifi_umac_event_new_scan_results *scan_results; req_len = sizeof(struct nrf_wifi_umac_event_new_scan_results); scan_results = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); nl80211_update_ssid_matched_bss(scan_results, local, rx_status, mgmt, len, channel, ts); img_genlmsg_reply(scan_results, sizeof(struct nrf_wifi_umac_event_new_scan_results)); return 0; } #ifndef BSS_OPTIMIZATION int send_bss_to_host(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_bss *cbss) { unsigned int req_len; struct cfg80211_internal_bss *scan; struct nrf_wifi_umac_event_new_scan_results *scan_results; wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); scan = bss_from_pub(cbss); req_len = sizeof(struct nrf_wifi_umac_event_new_scan_results); scan_results = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); nl80211_send_bss(scan_results, 0, 1, NLM_F_MULTI, rdev, wdev, scan); img_genlmsg_reply(scan_results, sizeof(struct nrf_wifi_umac_event_new_scan_results)); spin_unlock_bh(&rdev->bss_lock); wdev_unlock(wdev); return 0; } #endif int umac_set_ifhwaddr_byname(struct nrf_wifi_cmd_set_ifhwaddr *cmd, struct cfg80211_registered_device *rdev) { struct wireless_dev *wdev; struct net_device *net_dev; struct sockaddr sa; int found = 0; int err = 0; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!strncmp(wdev->netdev->name, (char *)cmd->ifacename, IFACENAMSIZ)) { found = 1; break; } } if(found == 1) { memcpy(sa.sa_data, cmd->nrf_wifi_hwaddr, ETH_ALEN); net_dev = wdev->netdev; if (!net_dev->netdev_ops->ndo_set_mac_address) { UMAC_OUTMSG("ndo_set_mac_address operation not supported\n"); return -EOPNOTSUPP; } err = net_dev->netdev_ops->ndo_set_mac_address(net_dev, &sa); if (err) { UMAC_OUTMSG("ndo_set_mac_address failed\n"); return err; } } return 0; } int umac_set_ifflags(struct nrf_wifi_umac_cmd_chg_vif_state *cmd, struct cfg80211_registered_device *rdev) { struct wireless_dev *wdev; struct net_device *net_dev; int found = 0; int err = 0; unsigned int req_len; struct nrf_wifi_umac_event_vif_state *event; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!strncmp(wdev->netdev->name, (char *)cmd->info.ifacename, IFACENAMSIZ)) { found = 1; break; } } if (found == 1) { net_dev = wdev->netdev; if (cmd->info.state) { if (!net_dev->netdev_ops->ndo_open) { UMAC_OUTMSG("ndo_open operation not supported\n"); return -EOPNOTSUPP; } err = net_dev->netdev_ops->ndo_open(net_dev); if (err) { UMAC_OUTMSG("ndo_open failed\n"); return err; } } else { if (!net_dev->netdev_ops->ndo_stop) { UMAC_OUTMSG("ndo_stop operation not supported\n"); return -EOPNOTSUPP; } err = net_dev->netdev_ops->ndo_stop(net_dev); if (err) { UMAC_OUTMSG("ndo_stop failed\n"); return err; } } } else { UMAC_OUTMSG("ifacename not found: %s", cmd->ifacename); } req_len = sizeof(*event); event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); event->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_IFFLAGS_STATUS; event->umac_hdr.ids.wdev_id = cmd->umac_hdr.ids.wdev_id; event->status = err; img_genlmsg_reply(event, sizeof(*event)); return 0; } void create_bss_entry(struct net_device *dev, struct nrf_wifi_umac_cmd_auth *cmd_auth, struct ieee80211_channel *chan); int nl80211_authenticate(struct nrf_wifi_umac_cmd_auth *cmd_auth, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL; int err, ssid_len, ie_len = 0, sae_data_len = 0; enum nl80211_auth_type auth_type; struct key_parse key; bool local_state_change; err = nl80211_parse_key(&cmd_auth->info.key_info, &key); if (err) return err; if(key.idx >= 0){ } else { key.p.key_len = 0; key.p.key = NULL; } bssid = cmd_auth->info.nrf_wifi_bssid; chan = nl80211_get_valid_chan(&rdev->wiphy, cmd_auth->info.frequency); if (!chan) return -EINVAL; ssid = (u8 *)cmd_auth->info.ssid.nrf_wifi_ssid; ssid_len = cmd_auth->info.ssid.nrf_wifi_ssid_len; if(cmd_auth->valid_fields & NRF_WIFI_CMD_AUTHENTICATE_IE_VALID) { ie = (u8 *)cmd_auth->info.ie.ie; } auth_type = cmd_auth->info.auth_type; if (cmd_auth->valid_fields & NRF_WIFI_CMD_AUTHENTICATE_SAE_VALID) { sae_data = cmd_auth->info.sae.sae_data; sae_data_len = cmd_auth->info.sae.sae_data_len; } local_state_change = !!cmd_auth->info.nrf_wifi_flags & NRF_WIFI_CMD_AUTHENTICATE_LOCAL_STATE_CHANGE; wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key.p.key, key.p.key_len, key.idx, sae_data, sae_data_len); wdev_unlock(dev->ieee80211_ptr); return err; } int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct nrf_wifi_connect_common_info *info, struct cfg80211_crypto_settings *settings, int cipher_limit) { memset(settings, 0, sizeof(*settings)); if (info->control_port) { settings->control_port = true; } if (info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_CONTROL_PORT_ETHER_TYPE) { u16 proto; proto = info->control_port_ether_type; settings->control_port_ethertype = cpu_to_be16(proto); if ((info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_CONTROL_PORT_NO_ENCRYPT) == NRF_WIFI_CONNECT_COMMON_INFO_CONTROL_PORT_NO_ENCRYPT) settings->control_port_no_encrypt = true; } else settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); if (info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_CIPHER_SUITES_PAIRWISE_VALID) { void *data; int len, i; data = info->cipher_suites_pairwise; len = info->num_cipher_suites_pairwise; settings->n_ciphers_pairwise = len / sizeof(u32); memcpy(settings->ciphers_pairwise, data, len); for (i = 0; i < settings->n_ciphers_pairwise; i++){ settings->ciphers_pairwise[i] = info->cipher_suites_pairwise[i]; } } if (info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_CIPHER_SUITE_GROUP_VALID) { settings->cipher_group = info->cipher_suite_group; } if (info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_WPA_VERSIONS_VALID) { settings->wpa_versions = info->wpa_versions; } if (info->valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_AKM_SUITES_VALID) { void *data; int len; data = info->akm_suites; len = info->num_akm_suites; settings->n_akm_suites = len / sizeof(u32); memcpy(settings->akm_suites, data, len); } return 0; } int nl80211_associate(struct nrf_wifi_umac_cmd_assoc *cmd_assoc, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct ieee80211_channel *chan; struct cfg80211_assoc_request req = {(struct cfg80211_bss *)NULL, (u8 *)NULL, (u8 *)NULL, 0, {0,0,0,{0,0,0,0,0},0,{0,0},0,0,0}, 0, 0, {0,0,{{0,0,0,0,0,0,0,0,0,0},0,0,{0,0,0}},0,0,0}, {0,0,{{0,0,0,0,0,0,0,0,0,0},0,0,{0,0,0}},0,0,0}, {0,{0,0,0,0}}, {0,{0,0,0,0}}}; const u8 *bssid, *ssid; int err, ssid_len = 0; bssid = cmd_assoc->connect_common_info.mac_addr; chan = nl80211_get_valid_chan(&rdev->wiphy, cmd_assoc->connect_common_info.frequency); if (!chan){ return -EINVAL; } ssid = (u8 *)cmd_assoc->connect_common_info.ssid.nrf_wifi_ssid; ssid_len = cmd_assoc->connect_common_info.ssid.nrf_wifi_ssid_len; if(cmd_assoc->connect_common_info.valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_WPA_IE_VALID) { req.ie = (u8 *)cmd_assoc->connect_common_info.wpa_ie.ie; req.ie_len = cmd_assoc->connect_common_info.wpa_ie.ie_len; } if (cmd_assoc->connect_common_info.valid_fields & NRF_WIFI_CONNECT_COMMON_INFO_USE_MFP_VALID) { enum nl80211_mfp mfp = cmd_assoc->connect_common_info.use_mfp; if (mfp == NL80211_MFP_REQUIRED) req.use_mfp = true; else if (mfp != NL80211_MFP_NO) return -EINVAL; } if(cmd_assoc->connect_common_info.nrf_wifi_flags & NRF_WIFI_CONNECT_COMMON_INFO_PREV_BSSID) { req.prev_bssid = cmd_assoc->connect_common_info.prev_bssid; } if (cmd_assoc->connect_common_info.nrf_wifi_flags & NRF_WIFI_CMD_CONNECT_COMMON_INFO_USE_RRM) { req.flags |= ASSOC_REQ_USE_RRM; } err = nl80211_crypto_settings(rdev, &cmd_assoc->connect_common_info, &req.crypto, 1); if (!err) { wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, ssid_len, &req); wdev_unlock(dev->ieee80211_ptr); } return err; } int nl80211_deauthenticate(struct nrf_wifi_umac_cmd_disconn *cmd_deauth, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; const u8 *ie = NULL, *bssid; int ie_len = 0, err; u16 reason_code; bool local_state_change; bssid = (u8 *)&cmd_deauth->info.mac_addr; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; reason_code = cmd_deauth->info.reason_code; local_state_change = !!(cmd_deauth->info.nrf_wifi_flags & NRF_WIFI_CMD_MLME_LOCAL_STATE_CHANGE); wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); wdev_unlock(dev->ieee80211_ptr); return err; } int nl80211_disassociate(struct nrf_wifi_umac_cmd_disconn *cmd_disassoc, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; const u8 *ie = NULL, *bssid; int ie_len = 0, err; u16 reason_code; bool local_state_change; bssid = (u8 *)&cmd_disassoc->info.mac_addr; reason_code = cmd_disassoc->info.reason_code; local_state_change = !!(cmd_disassoc->info.nrf_wifi_flags & NRF_WIFI_CMD_MLME_LOCAL_STATE_CHANGE); wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); wdev_unlock(dev->ieee80211_ptr); return err; } #ifdef ADHOC_MODE_SUPPORT int nl80211_join_ibss(struct umac_cmd_ibss_join *join_ibss_cmd, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&ibss, 0, sizeof(ibss)); ibss.beacon_interval = 100; if (!rdev->ops->join_ibss) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; wiphy = &rdev->wiphy; ibss.bssid = join_ibss_cmd->bssid; ibss.ssid = join_ibss_cmd->ssid.ssid; ibss.ssid_len = join_ibss_cmd->ssid.ssid_len; err = nl80211_parse_chandef(rdev, &join_ibss_cmd->freq_params, &ibss.chandef); if (err) return err; if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef, NL80211_IFTYPE_ADHOC)) return -EINVAL; switch (ibss.chandef.width) { case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: break; case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_40: if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) return -EINVAL; break; case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) return -EINVAL; if (!wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_VHT_IBSS)) return -EINVAL; break; default: return -EINVAL; } ibss.channel_fixed = true; err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kzfree(connkeys); return err; } #endif /* ADHOC_MODE_SUPPORT */ int nl80211_disconnect(struct nrf_wifi_umac_cmd_disconn *cmd_discon, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_registered_device *rdev = r_dev; struct net_device *dev = net_dev; u16 reason; int ret; reason = cmd_discon->info.reason_code; if (reason == 0) return -EINVAL; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; wdev_lock(dev->ieee80211_ptr); ret = cfg80211_disconnect(rdev, dev, reason, true); wdev_unlock(dev->ieee80211_ptr); return ret; } int nl80211_remain_on_channel(struct nrf_wifi_umac_cmd_remain_on_channel *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_chan_def chandef; struct nrf_wifi_umac_event_cookie_rsp *msg; u64 cookie; u32 duration; int err; unsigned int req_len; duration = cmd->info.dur; err = nl80211_parse_chandef(rdev, &cmd->info.nrf_wifi_freq_params, &chandef); if (err) return err; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!msg) return -ENOMEM; img_nl80211hdr_put(&msg->umac_hdr, cmd->umac_hdr.portid, cmd->umac_hdr.seq, 0, IMG_CMD_COOKIE_RESP); err = rdev_remain_on_channel(rdev, wdev, chandef.chan, duration, &cookie); if (err) goto free_msg; msg->cookie = cookie; msg->host_cookie = cmd->info.host_cookie; img_genlmsg_reply((void *)msg, sizeof(*msg)); return 0; free_msg: umac_event_free((char *)msg); return err; } int nl80211_cancel_remain_on_channel(struct nrf_wifi_umac_cmd_cancel_remain_on_channel *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { u64 cookie; cookie = cmd->cookie; return rdev_cancel_remain_on_channel(rdev, wdev, cookie); } int nl80211_register_mgmt(struct nrf_wifi_umac_cmd_mgmt_frame_reg *cmd_reg, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; frame_type = cmd_reg->info.frame_type; return cfg80211_mlme_register_mgmt(wdev, /*info->snd_portid*/ cmd_reg->umac_hdr.portid, frame_type, cmd_reg->info.frame_match.frame_match, cmd_reg->info.frame_match.frame_match_len); } int nl80211_tx_mgmt(struct nrf_wifi_umac_cmd_mgmt_tx *cmd_frame, struct cfg80211_registered_device *r_dev, struct wireless_dev *w_dev) { struct cfg80211_registered_device *rdev = r_dev; struct wireless_dev *wdev = w_dev; struct cfg80211_chan_def chandef; int err; u64 cookie; struct nrf_wifi_umac_event_cookie_rsp *cookie_rsp = NULL; struct cfg80211_mgmt_tx_params params = { .dont_wait_for_ack = cmd_frame->info.nrf_wifi_flags & NRF_WIFI_CMD_FRAME_DONT_WAIT_FOR_ACK, }; if (cmd_frame->valid_fields & NRF_WIFI_CMD_FRAME_DURATION_VALID) { params.wait = cmd_frame->info.dur; } params.offchan = cmd_frame->info.nrf_wifi_flags & NRF_WIFI_CMD_FRAME_OFFCHANNEL_TX_OK; params.no_cck = cmd_frame->info.nrf_wifi_flags & NRF_WIFI_CMD_FRAME_TX_NO_CCK_RATE; /* get the channel if any has been specified, otherwise pass NULL to * the driver. The latter will use the current one */ chandef.chan = NULL; chandef.chan = ieee80211_get_channel(&rdev->wiphy, cmd_frame->info.frequency); params.buf = (u8 *)cmd_frame->info.frame.frame; params.len = cmd_frame->info.frame.frame_len; if (!params.dont_wait_for_ack) { unsigned int req_len = sizeof(*cookie_rsp); cookie_rsp = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if (!cookie_rsp) return -ENOMEM; img_nl80211hdr_put(&cookie_rsp->umac_hdr, cmd_frame->umac_hdr.portid, cmd_frame->umac_hdr.seq, 0, IMG_CMD_COOKIE_RESP); } params.chan = chandef.chan; err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); if(cookie_rsp) { cookie_rsp->cookie = cookie; cookie_rsp->host_cookie = cmd_frame->info.host_cookie; img_genlmsg_reply(cookie_rsp, sizeof(*cookie_rsp)); } return 0; } int img_nl80211_set_power_save(struct nrf_wifi_umac_cmd_set_power_save *cmd, struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev; u8 ps_state; bool state; int err; ps_state = cmd->info.ps_state; wdev = dev->ieee80211_ptr; state = (ps_state == NL80211_PS_ENABLED) ? true : false; if (state == wdev->ps) return 0; err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout); if (!err) wdev->ps = state; return err; } int nl80211_start_p2p_device(struct nrf_wifi_cmd_start_p2p *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { (void)cmd; int err; err = rdev_start_p2p_device(rdev, wdev); if (err) return err; wdev->is_running = true; rdev->opencount++; return 0; } int nl80211_stop_p2p_device(struct nrf_wifi_umac_cmd_stop_p2p_dev *cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { (void)cmd; cfg80211_stop_p2p_device(rdev, wdev); return 0; } /* static int nl80211_set_qos_map(struct sk_buff *skb, struct genl_info *info) */ int nl80211_set_qos_map(struct nrf_wifi_umac_cmd_set_qos_map *cmd_set_qos_map, struct cfg80211_registered_device *r_dev, struct net_device *net_dev) { struct cfg80211_qos_map *qos_map = NULL; u8 *pos, len, num_des, des_len, des; int ret; if (!r_dev->ops->set_qos_map) return -EOPNOTSUPP; if(cmd_set_qos_map->info.qos_map_info_len){ qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL); if (!qos_map) return -ENOMEM; memcpy(qos_map, cmd_set_qos_map->info.qos_map_info, cmd_set_qos_map->info.qos_map_info_len); } wdev_lock(net_dev->ieee80211_ptr); ret = nl80211_key_allowed(net_dev->ieee80211_ptr); if (!ret){ ret = rdev_set_qos_map(r_dev, net_dev, qos_map); } wdev_unlock(net_dev->ieee80211_ptr); if (qos_map) kfree(qos_map); return ret; } int nl80211_add_scan_req(struct nrf_wifi_umac_event_trigger_scan *scan_event, struct cfg80211_registered_device *rdev) { struct cfg80211_scan_request *req = rdev->scan_req; int i; if (WARN_ON(!req)) return 0; scan_event->num_scan_ssid = req->n_ssids; for (i = 0; i < req->n_ssids; i++) { scan_event->scan_ssid[i].nrf_wifi_ssid_len = req->ssids[i].ssid_len; memcpy(scan_event->scan_ssid[i].nrf_wifi_ssid, req->ssids[i].ssid, req->ssids[i].ssid_len); } scan_event->num_scan_frequencies = req->n_channels; for (i = 0; i < req->n_channels; i++) { scan_event->scan_frequencies[i] = req->channels[i]->center_freq; } if(req->ie) { scan_event->valid_fields |= NRF_WIFI_EVENT_TRIGGER_SCAN_IE_VALID; scan_event->ie.ie_len = req->ie_len; memcpy(scan_event->ie.ie, req->ie, req->ie_len); } if(req->flags) { scan_event->valid_fields |= NRF_WIFI_EVENT_TRIGGER_SCAN_SCAN_FLAGS_VALID; scan_event->nrf_wifi_scan_flags = req->flags; } return 0; } int nl80211_send_scan_msg(struct nrf_wifi_umac_event_trigger_scan *scan_event, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 portid, u32 seq, int flags, u32 cmd) { img_nl80211hdr_put(&scan_event->umac_hdr, portid, seq, flags, cmd); scan_event->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; scan_event->umac_hdr.ids.ifaceindex = wdev->netdev->ifindex; scan_event->umac_hdr.ids.wdev_id = wdev_id(wdev); nl80211_add_scan_req(scan_event, rdev); return 0; } void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct nrf_wifi_umac_event_trigger_scan *scan_event; unsigned int req_len; req_len = sizeof(*scan_event); scan_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); nl80211_send_scan_msg(scan_event, rdev, wdev, 0, 0, 0, NL80211_CMD_TRIGGER_SCAN); img_genlmsg_multicast_netns(scan_event, sizeof(*scan_event)); } struct nrf_wifi_umac_event_trigger_scan *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, bool aborted) { struct nrf_wifi_umac_event_trigger_scan *scan_event; unsigned int req_len; req_len = sizeof(*scan_event); scan_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (nl80211_send_scan_msg(scan_event, rdev, wdev, 0, 0, 0, aborted ? NL80211_CMD_SCAN_ABORTED : NL80211_CMD_NEW_SCAN_RESULTS) < 0) { umac_event_free((char *)scan_event); return NULL; } return scan_event; } void nl80211_send_scan_result(struct cfg80211_registered_device *rdev, struct nrf_wifi_umac_event_trigger_scan *scan_event) { (void)rdev; if (!scan_event) return; img_genlmsg_multicast_netns(scan_event, sizeof(*scan_event)); } void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd) { (void)req, (void)cmd; } bool nl80211_reg_change_event_fill(struct nrf_wifi_event_regulatory_change *msg, struct regulatory_request *request) { /* Userspace can always count this one always being set */ msg->intr = request->initiator; if (request->alpha2[0] == '0' && request->alpha2[1] == '0') { msg->regulatory_type = NRF_WIFI_REGDOM_TYPE_WORLD; } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') { msg->regulatory_type = NRF_WIFI_REGDOM_TYPE_CUSTOM_WORLD; } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || request->intersect) { msg->regulatory_type = NRF_WIFI_REGDOM_TYPE_INTERSECTION; } else { msg->regulatory_type = NRF_WIFI_REGDOM_TYPE_COUNTRY; memcpy(msg->nrf_wifi_alpha2, request->alpha2, 2); } if (request->wiphy_idx != WIPHY_IDX_INVALID) { struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx); } return true; } /* * This can happen on global regulatory changes or device specific settings * based on custom regulatory domains. */ void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, struct regulatory_request *request) { struct nrf_wifi_event_regulatory_change *msg; unsigned int req_len; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!msg) return; memset(msg, 0, sizeof(*msg)); img_nl80211hdr_put(&msg->umac_hdr, 0, 0, 0, cmd_id); msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; msg->umac_hdr.ids.nrf_wifi_wiphy_idx = request->wiphy_idx; rcu_read_lock(); if (nl80211_reg_change_event_fill(msg, request) == false) return; rcu_read_unlock(); if (cmd_id == NL80211_CMD_REG_CHANGE) IMG_CMD_EVENT_DBG_PARAM_INCR(event_reg_change, 1); else IMG_CMD_EVENT_DBG_PARAM_INCR(event_wiphy_reg_change, 1); img_genlmsg_reply(msg, sizeof(*msg)); return; } void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, enum nl80211_commands cmd, gfp_t gfp, int uapsd_queues, const u8 *req_ies, size_t req_ies_len) { (void)gfp; struct nrf_wifi_umac_event_mlme *mlme_event; unsigned int req_len; req_len = sizeof(struct nrf_wifi_umac_event_mlme); mlme_event = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (!mlme_event) return; memset(mlme_event, 0, sizeof(struct nrf_wifi_umac_event_mlme)); img_nl80211hdr_put(&mlme_event->umac_hdr, 0, 0, 0, cmd); mlme_event->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; mlme_event->umac_hdr.ids.ifaceindex = netdev->ifindex; mlme_event->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; mlme_event->umac_hdr.ids.wdev_id = netdev->ieee80211_ptr->identifier; mlme_event->frame.frame_len = len; memcpy(mlme_event->frame.frame, buf, len); mlme_event->valid_fields |= NRF_WIFI_EVENT_MLME_FRAME_VALID; if (uapsd_queues >= 0) { mlme_event->valid_fields |= NRF_WIFI_EVENT_MLME_WME_UAPSD_QUEUES_VALID; mlme_event->wme_uapsd_queues = (unsigned char)uapsd_queues; } img_genlmsg_multicast_netns(mlme_event, sizeof(*mlme_event)); return; } void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_AUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp, int uapsd_queues, const u8 *req_ies, size_t req_ies_len) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE, gfp, uapsd_queues, req_ies, req_ies_len); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_DEAUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_DISASSOCIATE, gfp, -1, NULL, 0); } void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); const struct ieee80211_mgmt *mgmt = (void *)buf; u32 cmd; if (WARN_ON(len < 2)) return; if (ieee80211_is_deauth(mgmt->frame_control)) cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE; else cmd = NL80211_CMD_UNPROT_DISASSOCIATE; nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1, NULL, 0); } EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, int cmd, const u8 *addr, gfp_t gfp) { (void)rdev; (void)netdev; (void)cmd; (void)addr; (void)gfp; } void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, addr, gfp); } void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr, gfp); } void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct net_device *netdev, struct cfg80211_connect_resp_params *cr, gfp_t gfp) { (void)gfp; struct nrf_wifi_umac_event_connect *connect_result; unsigned int req_len; struct wireless_dev *wdev = netdev->ieee80211_ptr; req_len = sizeof(*connect_result); connect_result = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if(!connect_result) return; img_nl80211hdr_put(&connect_result->umac_hdr, 0, 0, 0, NL80211_CMD_CONNECT); connect_result->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; connect_result->umac_hdr.ids.wdev_id = wdev_id(wdev); connect_result->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; connect_result->umac_hdr.ids.ifaceindex = netdev->ifindex; memcpy(connect_result->mac_addr, cr->bssid, ETH_ALEN); connect_result->status_code = cr->status; if(cr->req_ie) { connect_result->req_ie = true; connect_result->connect_ie.ie_len = cr->req_ie_len; memcpy(connect_result->connect_ie.ie, cr->req_ie, cr->req_ie_len); } if(cr->resp_ie) { connect_result->resp_ie = true; connect_result->connect_ie.ie_len = cr->resp_ie_len; memcpy(connect_result->connect_ie.ie, cr->resp_ie, cr->resp_ie_len); } img_genlmsg_multicast_netns(connect_result, sizeof(*connect_result)); return; } void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, struct cfg80211_roam_info *info, gfp_t gfp) { (void)rdev; (void)netdev; (void)info; (void)gfp; } void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid) { return; } void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap) { (void)rdev; (void)netdev; (void)reason; (void)ie; (void)ie_len; (void)from_ap; } void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc, gfp_t gfp) { (void)rdev; (void)netdev; (void)addr; (void)key_type; (void)key_id; (void)tsc; (void)gfp; } void nl80211_send_beacon_hint_event(struct wiphy *wiphy, struct ieee80211_channel *channel_before, struct ieee80211_channel *channel_after) { struct nrf_wifi_event_send_beacon_hint *msg; unsigned int req_len; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_RESP_PORT, &req_len); if(!msg) return; memset(msg, 0, sizeof(*msg)); img_nl80211hdr_put(&msg->umac_hdr, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT); msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; msg->umac_hdr.ids.nrf_wifi_wiphy_idx = get_wiphy_idx(wiphy); rcu_read_lock(); /* Before */ nl80211_msg_put_channel(&msg->channel_before, channel_before, false); /* After */ nl80211_msg_put_channel(&msg->channel_after, channel_after, false); rcu_read_unlock(); IMG_CMD_EVENT_DBG_PARAM_INCR(event_beacon_hint, 1); img_genlmsg_reply(msg, sizeof(*msg)); return; } void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, unsigned int duration, gfp_t gfp) { (void)gfp; struct nrf_wifi_event_remain_on_channel *msg; unsigned int req_len; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (!msg) return; img_nl80211hdr_put(&msg->umac_hdr, 0, 0, 0, cmd); msg->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; if(wdev->netdev){ msg->umac_hdr.ids.ifaceindex = wdev->netdev->ifindex; msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; } msg->umac_hdr.ids.wdev_id = wdev_id(wdev); msg->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; msg->frequency = chan->center_freq; msg->valid_fields |= NRF_WIFI_EVENT_ROC_FREQ_VALID; msg->ch_type = NL80211_CHAN_NO_HT; msg->valid_fields |= NRF_WIFI_EVENT_ROC_CH_TYPE_VALID; msg->cookie = cookie; msg->valid_fields |= NRF_WIFI_EVENT_ROC_COOKIE_VALID; if(cmd == NL80211_CMD_REMAIN_ON_CHANNEL){ msg->dur = duration; msg->valid_fields |= NRF_WIFI_EVENT_ROC_DURATION_VALID; } img_genlmsg_multicast_netns(msg, sizeof(*msg)); return; } void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, unsigned int duration, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, duration, gfp); } EXPORT_SYMBOL(cfg80211_ready_on_channel); void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, 0, gfp); } EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); void cfg80211_tx_mgmt_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, gfp_t gfp) { return; } EXPORT_SYMBOL(cfg80211_tx_mgmt_expired); void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, u8 is_sta_legacy, bool wme) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct nrf_wifi_umac_event_new_station *msg; unsigned int req_len; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (!msg) return; if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0, rdev, dev, mac_addr, NULL) < 0) { umac_event_free((char *)msg); return; } msg->is_sta_legacy = is_sta_legacy; msg->wme = wme; img_genlmsg_multicast_netns(msg, sizeof(*msg)); } EXPORT_SYMBOL(cfg80211_new_sta); void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp) { (void)gfp; struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct nrf_wifi_umac_event_new_station *msg; struct station_info empty_sinfo = {}; unsigned int req_len; if (!sinfo) sinfo = &empty_sinfo; req_len = sizeof(*msg); msg = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (!msg) return; if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, rdev, dev, mac_addr, NULL) < 0) { umac_event_free((char *)msg); return; } img_genlmsg_multicast_netns(msg, sizeof(*msg)); } EXPORT_SYMBOL(cfg80211_del_sta_sinfo); bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, const u8 *addr, gfp_t gfp) { (void)gfp; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); return true; } bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; bool ret; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO)) { return false; } ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, addr, gfp); return ret; } EXPORT_SYMBOL(cfg80211_rx_spurious_frame); bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; bool ret; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO && wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { return false; } ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_4ADDR_FRAME, addr, gfp); return ret; } EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 nlportid, int freq, int sig_dbm, const u8 *buf, size_t len, u32 flags, gfp_t gfp) { (void)gfp; struct net_device *netdev = wdev->netdev; struct nrf_wifi_umac_event_mlme *send_mgmt; unsigned int req_len; req_len = sizeof(*send_mgmt); send_mgmt = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if (!send_mgmt) return -ENOMEM; memset(send_mgmt, 0, sizeof(*send_mgmt)); img_nl80211hdr_put(&send_mgmt->umac_hdr, nlportid, 0, 0, NL80211_CMD_FRAME); send_mgmt->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; send_mgmt->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; send_mgmt->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; send_mgmt->umac_hdr.ids.ifaceindex = netdev->ifindex; send_mgmt->umac_hdr.ids.wdev_id = wdev_id(wdev); send_mgmt->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; send_mgmt->frequency = freq; send_mgmt->valid_fields |= NRF_WIFI_EVENT_MLME_FREQ_VALID; if(sig_dbm) { send_mgmt->rx_signal_dbm = sig_dbm; send_mgmt->valid_fields |= NRF_WIFI_EVENT_MLME_RX_SIGNAL_DBM_VALID; } send_mgmt->frame.frame_len = len; memcpy(send_mgmt->frame.frame, buf, len); send_mgmt->valid_fields |= NRF_WIFI_EVENT_MLME_FRAME_VALID; if(flags) { send_mgmt->nrf_wifi_flags = flags; send_mgmt->valid_fields |= NRF_WIFI_EVENT_MLME_RXMGMT_FLAGS_VALID; } img_genlmsg_multicast_netns(send_mgmt, sizeof(*send_mgmt)); return 0; } void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { (void)gfp; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct net_device *netdev = wdev->netdev; struct nrf_wifi_umac_event_mlme *tx_status; unsigned int req_len; req_len = sizeof(*tx_status); tx_status = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); if(!tx_status) return; img_nl80211hdr_put(&tx_status->umac_hdr, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS); tx_status->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WIPHY_IDX_VALID; tx_status->umac_hdr.ids.nrf_wifi_wiphy_idx = rdev->wiphy_idx; if(netdev) { tx_status->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_IFINDEX_VALID; tx_status->umac_hdr.ids.ifaceindex = netdev->ifindex; } tx_status->umac_hdr.ids.wdev_id = wdev_id(wdev); tx_status->umac_hdr.ids.valid_fields |= NRF_WIFI_INDEX_IDS_WDEV_ID_VALID; tx_status->frame.frame_len = len; memcpy(tx_status->frame.frame, buf, len); tx_status->valid_fields |= NRF_WIFI_EVENT_MLME_FRAME_VALID; tx_status->cookie = cookie; if(ack) { tx_status->nrf_wifi_flags = NRF_WIFI_EVENT_MLME_ACK; tx_status->valid_fields |= NRF_WIFI_EVENT_MLME_RXMGMT_FLAGS_VALID; } img_genlmsg_multicast_netns(tx_status, sizeof(*tx_status)); return; } EXPORT_SYMBOL(cfg80211_mgmt_tx_status); void cfg80211_cqm_rssi_notify(struct net_device *dev, enum nl80211_cqm_rssi_threshold_event rssi_event, s32 rssi_level, gfp_t gfp) { (void)dev; (void)rssi_event; (void)gfp; } EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); void cfg80211_cqm_pktloss_notify(struct net_device *dev, const u8 *peer, u32 num_packets, gfp_t gfp) { (void)dev; (void)peer; (void)num_packets; (void)gfp; } EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp) { (void)dev; (void)gfp; } EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify); void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { (void)rdev; (void)netdev; (void)bssid; (void)replay_ctr; (void)gfp; } void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, const u8 *bssid, bool preauth, gfp_t gfp) { (void)dev; (void)index; (void)bssid; (void)preauth; (void)gfp; } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); void cfg80211_ch_switch_notify(struct net_device *dev, struct cfg80211_chan_def *chandef) { (void)dev; (void)chandef; } EXPORT_SYMBOL(cfg80211_ch_switch_notify); void cfg80211_ch_switch_started_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, u8 count) { (void)dev; (void)chandef; (void)count; } EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); void nl80211_radar_notify(struct cfg80211_registered_device *rdev, const struct cfg80211_chan_def *chandef, enum nl80211_radar_event event, struct net_device *netdev, gfp_t gfp) { (void)rdev, (void)chandef; (void)event; (void)netdev; (void)gfp; } void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac, struct sta_opmode_info *sta_opmode, gfp_t gfp) { return; } EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, s32 ack_signal, bool is_valid_ack_signal, gfp_t gfp) { (void)dev; (void)addr; (void)cookie; (void)acked; (void)ack_signal; (void)is_valid_ack_signal; (void)gfp; } EXPORT_SYMBOL(cfg80211_probe_status); void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, int freq, int sig_dbm) { (void)wiphy; (void)frame; (void)len; (void)freq; (void)sig_dbm; } EXPORT_SYMBOL(cfg80211_report_obss_beacon); void nl80211_send_ap_stopped(struct wireless_dev *wdev) { (void)wdev; } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int status) { struct nrf_wifi_umac_event_scan_done *scan_done; unsigned int req_len = sizeof(*scan_done); if(NULL != mapped_scan_ch_gp) { kfree(mapped_scan_ch_gp); mapped_scan_ch_gp = NULL; duration_probe_cnt = 0; } scan_done = umac_event_alloc(IMG_MODULE_IFACE_UMAC_WPAS_EVENT_PORT, &req_len); scan_done->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_EVENT_SCAN_DONE; scan_done->umac_hdr.portid = 0; scan_done->umac_hdr.seq = 0; scan_done->umac_hdr.ids.wdev_id = scan_wdev_id_g; scan_done->scan_type = rdev->scan_req->scan_reason; scan_done->status = status; img_genlmsg_multicast_netns(scan_done, sizeof(struct nrf_wifi_umac_event_scan_done)); }