diff --git a/doc/connectivity/networking/api/wifi.rst b/doc/connectivity/networking/api/wifi.rst index e0be448354435..4271c91f22e19 100644 --- a/doc/connectivity/networking/api/wifi.rst +++ b/doc/connectivity/networking/api/wifi.rst @@ -10,6 +10,7 @@ The Wi-Fi management API is used to manage Wi-Fi networks. It supports below mod * IEEE802.11 Station (STA) * IEEE802.11 Access Point (AP) +* IEEE802.11 P2P (Wi-Fi Direct) Only personal mode security is supported with below types: @@ -215,6 +216,19 @@ The test certificates in ``samples/net/wifi/test_certs/rsa2k`` are generated usi .. note:: These certificates are for testing only and should not be used in production. +Wi-Fi P2P (Wi-Fi Direct) +************************ + +Wi-Fi P2P or Wi-Fi Direct enables devices to communicate directly with each other without requiring +a traditional access point. This feature is particularly useful for device-to-device communication +scenarios. + +To enable and build with Wi-Fi P2P support: + +.. code-block:: bash + + $ west build -p -b samples/net/wifi -- -DCONFIG_WIFI_NM_WPA_SUPPLICANT_P2P=y + API Reference ************* diff --git a/doc/releases/release-notes-4.4.rst b/doc/releases/release-notes-4.4.rst index 95e087f86e7dc..7e36d0dd9d6cb 100644 --- a/doc/releases/release-notes-4.4.rst +++ b/doc/releases/release-notes-4.4.rst @@ -81,6 +81,12 @@ New APIs and options * :dtcompatible:`jedec,mspi-nor` now allows MSPI configuration of read, write and control commands separately via devicetree. +* Networking + + * Wi-Fi + + * Add support for Wi-Fi Direct (P2P) mode. + .. zephyr-keep-sorted-stop New Boards diff --git a/drivers/wifi/nrf_wifi/Kconfig.nrfwifi b/drivers/wifi/nrf_wifi/Kconfig.nrfwifi index cb45ea90b575c..7ae374d3c6a2c 100644 --- a/drivers/wifi/nrf_wifi/Kconfig.nrfwifi +++ b/drivers/wifi/nrf_wifi/Kconfig.nrfwifi @@ -119,6 +119,7 @@ config NRF70_ENABLE_DUAL_VIF config NRF70_P2P_MODE bool "P2P support in driver" + default y if WIFI_NM_WPA_SUPPLICANT_P2P config NRF70_SYSTEM_WITH_RAW_MODES bool "nRF70 system mode with raw modes" diff --git a/drivers/wifi/nrf_wifi/inc/fmac_main.h b/drivers/wifi/nrf_wifi/inc/fmac_main.h index 799beff1fbaf5..be8c9d7636793 100644 --- a/drivers/wifi/nrf_wifi/inc/fmac_main.h +++ b/drivers/wifi/nrf_wifi/inc/fmac_main.h @@ -75,9 +75,6 @@ struct nrf_wifi_vif_ctx_zep { #endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ struct net_stats_eth eth_stats; #endif /* CONFIG_NET_STATISTICS_ETHERNET */ -#if defined(CONFIG_NRF70_STA_MODE) || defined(CONFIG_NRF70_RAW_DATA_RX) - bool authorized; -#endif #ifdef CONFIG_NRF70_STA_MODE unsigned int assoc_freq; enum nrf_wifi_fmac_if_carr_state if_carr_state; diff --git a/drivers/wifi/nrf_wifi/inc/wpa_supp_if.h b/drivers/wifi/nrf_wifi/inc/wpa_supp_if.h index c543644e31283..25b3972d9fab7 100644 --- a/drivers/wifi/nrf_wifi/inc/wpa_supp_if.h +++ b/drivers/wifi/nrf_wifi/inc/wpa_supp_if.h @@ -123,8 +123,17 @@ int nrf_wifi_supp_get_conn_info(void *if_priv, struct wpa_conn_info *info); void nrf_wifi_supp_event_proc_get_conn_info(void *os_vif_ctx, struct nrf_wifi_umac_event_conn_info *info, unsigned int event_len); +void nrf_wifi_supp_event_roc_complete(void *os_vif_ctx, + struct nrf_wifi_event_remain_on_channel *info, + unsigned int event_len); +void nrf_wifi_supp_event_roc_cancel_complete(void *os_vif_ctx, + struct nrf_wifi_event_remain_on_channel *info, + unsigned int event_len); int nrf_wifi_supp_set_country(void *if_priv, const char *alpha2); int nrf_wifi_supp_get_country(void *if_priv, char *alpha2); +int nrf_wifi_supp_remain_on_channel(void *if_priv, unsigned int freq, unsigned int duration); +int nrf_wifi_supp_cancel_remain_on_channel(void *if_priv); +int nrf_wifi_supp_set_p2p_powersave(void *if_priv, int legacy_ps, int opp_ps, int ctwindow); #endif /* CONFIG_NRF70_STA_MODE */ #ifdef CONFIG_NRF70_AP_MODE diff --git a/drivers/wifi/nrf_wifi/src/fmac_main.c b/drivers/wifi/nrf_wifi/src/fmac_main.c index 897cb76809cb6..809547126603e 100644 --- a/drivers/wifi/nrf_wifi/src/fmac_main.c +++ b/drivers/wifi/nrf_wifi/src/fmac_main.c @@ -440,6 +440,11 @@ void nrf_wifi_event_proc_cookie_rsp(void *vif_ctx, /* TODO: When supp_callbk_fns.mgmt_tx_status is implemented, add logic * here to use the cookie and host_cookie to map requests to responses. */ + if (vif_ctx_zep->supp_drv_if_ctx && + vif_ctx_zep->supp_callbk_fns.cookie_event) { + vif_ctx_zep->supp_callbk_fns.cookie_event(vif_ctx_zep->supp_drv_if_ctx, + cookie_rsp_event->cookie); + } } #endif /* CONFIG_NRF70_STA_MODE */ @@ -831,6 +836,8 @@ static int nrf_wifi_drv_main_zep(const struct device *dev) callbk_fns.event_get_wiphy = nrf_wifi_wpa_supp_event_get_wiphy; callbk_fns.mgmt_rx_callbk_fn = nrf_wifi_wpa_supp_event_mgmt_rx_callbk_fn; callbk_fns.get_conn_info_callbk_fn = nrf_wifi_supp_event_proc_get_conn_info; + callbk_fns.roc_callbk_fn = nrf_wifi_supp_event_roc_complete; + callbk_fns.roc_cancel_callbk_fn = nrf_wifi_supp_event_roc_cancel_complete; #endif /* CONFIG_NRF70_STA_MODE */ /* The OSAL layer needs to be initialized before any other initialization @@ -951,6 +958,9 @@ static const struct zep_wpa_supp_dev_ops wpa_supp_ops = { .get_conn_info = nrf_wifi_supp_get_conn_info, .set_country = nrf_wifi_supp_set_country, .get_country = nrf_wifi_supp_get_country, + .remain_on_channel = nrf_wifi_supp_remain_on_channel, + .cancel_remain_on_channel = nrf_wifi_supp_cancel_remain_on_channel, + .set_p2p_powersave = nrf_wifi_supp_set_p2p_powersave, #ifdef CONFIG_NRF70_AP_MODE .init_ap = nrf_wifi_wpa_supp_init_ap, .start_ap = nrf_wifi_wpa_supp_start_ap, diff --git a/drivers/wifi/nrf_wifi/src/net_if.c b/drivers/wifi/nrf_wifi/src/net_if.c index 8f7750031476c..b8635e83d54ae 100644 --- a/drivers/wifi/nrf_wifi/src/net_if.c +++ b/drivers/wifi/nrf_wifi/src/net_if.c @@ -25,6 +25,7 @@ LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); #include "util.h" #include "common/fmac_util.h" +#include "system/fmac_peer.h" #include "shim.h" #include "fmac_main.h" #include "wpa_supp_if.h" @@ -388,6 +389,8 @@ int nrf_wifi_if_send(const struct device *dev, struct rpu_host_stats *host_stats = NULL; void *nbuf = NULL; bool locked = false; + unsigned char *ra = NULL; + int peer_id = -1; if (!dev || !pkt) { LOG_ERR("%s: vif_ctx_zep is NULL", __func__); @@ -436,12 +439,20 @@ int nrf_wifi_if_send(const struct device *dev, nbuf); } else { #endif /* CONFIG_NRF70_RAW_DATA_TX */ + + ra = nrf_wifi_util_get_ra(sys_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx], nbuf); + peer_id = nrf_wifi_fmac_peer_get_id(rpu_ctx_zep->rpu_ctx, ra); + if (peer_id == -1) { + nrf_wifi_osal_log_err("%s: Invalid peer", + __func__); + goto out; + } + if ((vif_ctx_zep->if_carr_state != NRF_WIFI_FMAC_IF_CARR_STATE_ON) || - (!vif_ctx_zep->authorized && !is_eapol(pkt))) { + (!sys_dev_ctx->tx_config.peers[peer_id].authorized && !is_eapol(pkt))) { ret = -EPERM; goto drop; } - ret = nrf_wifi_fmac_start_xmit(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, nbuf); diff --git a/drivers/wifi/nrf_wifi/src/wpa_supp_if.c b/drivers/wifi/nrf_wifi/src/wpa_supp_if.c index f913558ffea57..da7cc7aa1cc75 100644 --- a/drivers/wifi/nrf_wifi/src/wpa_supp_if.c +++ b/drivers/wifi/nrf_wifi/src/wpa_supp_if.c @@ -17,6 +17,7 @@ #include "common/fmac_util.h" #include "wifi_mgmt.h" #include "wpa_supp_if.h" +#include LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL); @@ -545,16 +546,16 @@ int nrf_wifi_wpa_supp_scan2(void *if_priv, struct wpa_driver_scan_params *params scan_info->scan_params.num_scan_channels = indx; } - if (params->filter_ssids) { - scan_info->scan_params.num_scan_ssids = params->num_filter_ssids; + if (params->num_ssids) { + scan_info->scan_params.num_scan_ssids = params->num_ssids; - for (indx = 0; indx < params->num_filter_ssids; indx++) { + for (indx = 0; indx < params->num_ssids; indx++) { memcpy(scan_info->scan_params.scan_ssids[indx].nrf_wifi_ssid, - params->filter_ssids[indx].ssid, - params->filter_ssids[indx].ssid_len); + params->ssids[indx].ssid, + params->ssids[indx].ssid_len); scan_info->scan_params.scan_ssids[indx].nrf_wifi_ssid_len = - params->filter_ssids[indx].ssid_len; + params->ssids[indx].ssid_len; } } @@ -1103,7 +1104,9 @@ int nrf_wifi_wpa_set_supp_port(void *if_priv, int authorized, char *bssid) struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; struct nrf_wifi_umac_chg_sta_info chg_sta_info; struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_sys_fmac_dev_ctx *sys_dev_ctx = NULL; enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int peer_id = -1; int ret = -1; if (!if_priv || !bssid) { @@ -1134,8 +1137,6 @@ int nrf_wifi_wpa_set_supp_port(void *if_priv, int authorized, char *bssid) memcpy(chg_sta_info.mac_addr, bssid, ETH_ALEN); - vif_ctx_zep->authorized = authorized; - if (authorized) { /* BIT(NL80211_STA_FLAG_AUTHORIZED) */ chg_sta_info.sta_flags2.nrf_wifi_mask = 1 << 1; @@ -1153,6 +1154,19 @@ int nrf_wifi_wpa_set_supp_port(void *if_priv, int authorized, char *bssid) goto out; } + sys_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + + peer_id = nrf_wifi_fmac_peer_get_id(rpu_ctx_zep->rpu_ctx, chg_sta_info.mac_addr); + if (peer_id == -1) { + nrf_wifi_osal_log_err("%s: Invalid peer", + __func__); + goto out; + } + + if (chg_sta_info.sta_flags2.nrf_wifi_set & NRF_WIFI_STA_FLAG_AUTHORIZED) { + sys_dev_ctx->tx_config.peers[peer_id].authorized = true; + } + ret = 0; out: k_mutex_unlock(&vif_ctx_zep->vif_lock); @@ -1435,6 +1449,10 @@ int nrf_wifi_nl80211_send_mlme(void *if_priv, const u8 *data, goto out; } + if (vif_ctx_zep->if_type == NRF_WIFI_IFTYPE_STATION) { + offchanok = 1; + } + if (offchanok) { mgmt_tx_info->nrf_wifi_flags |= NRF_WIFI_CMD_FRAME_OFFCHANNEL_TX_OK; } @@ -1698,7 +1716,7 @@ int nrf_wifi_supp_register_frame(void *if_priv, struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; struct nrf_wifi_umac_mgmt_frame_info frame_info; - if (!if_priv || !match || !match_len) { + if (!if_priv) { LOG_ERR("%s: Invalid parameters", __func__); return -1; } @@ -1719,8 +1737,14 @@ int nrf_wifi_supp_register_frame(void *if_priv, memset(&frame_info, 0, sizeof(frame_info)); frame_info.frame_type = type; - frame_info.frame_match.frame_match_len = match_len; - memcpy(frame_info.frame_match.frame_match, match, match_len); + if (match_len > 0) { + if (!match) { + LOG_ERR("%s: Invalid match parameters", __func__); + goto out; + } + frame_info.frame_match.frame_match_len = match_len; + memcpy(frame_info.frame_match.frame_match, match, match_len); + } status = nrf_wifi_sys_fmac_register_frame(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, &frame_info); @@ -1798,6 +1822,9 @@ int nrf_wifi_supp_get_capa(void *if_priv, struct wpa_driver_capa *capa) if (IS_ENABLED(CONFIG_NRF70_AP_MODE)) { capa->flags |= WPA_DRIVER_FLAGS_AP; } + if (IS_ENABLED(CONFIG_NRF70_P2P_MODE)) { + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + } capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | @@ -1983,6 +2010,192 @@ void nrf_wifi_supp_event_proc_get_conn_info(void *if_priv, k_sem_give(&wait_for_event_sem); } +void nrf_wifi_supp_event_roc_complete(void *if_priv, + struct nrf_wifi_event_remain_on_channel *roc_complete, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Missing interface context", __func__); + return; + } + + vif_ctx_zep = if_priv; + + if (!roc_complete) { + LOG_ERR("%s: Missing ROC complete event data", __func__); + return; + } + + LOG_DBG("%s: ROC complete on freq %d, dur %d, vif_idx %d", + __func__, roc_complete->frequency, + roc_complete->dur, vif_ctx_zep->vif_idx); + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.roc_complete) { + vif_ctx_zep->supp_callbk_fns.roc_complete(vif_ctx_zep->supp_drv_if_ctx, + roc_complete->frequency, + roc_complete->dur, roc_complete->cookie); + } +} + +void nrf_wifi_supp_event_roc_cancel_complete(void *if_priv, + struct nrf_wifi_event_remain_on_channel + *roc_cancel_complete, + unsigned int event_len) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Missing interface context", __func__); + return; + } + + vif_ctx_zep = if_priv; + + if (!roc_cancel_complete) { + LOG_ERR("%s: Missing ROC cancel complete event data", __func__); + return; + } + + LOG_DBG("%s: ROC cancel complete on freq %d, vif_idx %d", + __func__, roc_cancel_complete->frequency, + vif_ctx_zep->vif_idx); + + if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.roc_cancel_complete) { + vif_ctx_zep->supp_callbk_fns.roc_cancel_complete(vif_ctx_zep->supp_drv_if_ctx, + roc_cancel_complete->frequency, roc_cancel_complete->cookie); + } +} + +int nrf_wifi_supp_remain_on_channel(void *if_priv, unsigned int freq, + unsigned int duration) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; +#ifdef NRF70_P2P_MODE + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct remain_on_channel_info roc_info; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return -1; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + memset(&roc_info, 0, sizeof(roc_info)); + roc_info.nrf_wifi_freq_params.frequency = freq; + roc_info.nrf_wifi_freq_params.channel_width = NRF_WIFI_CHAN_WIDTH_20; + roc_info.nrf_wifi_freq_params.center_frequency1 = freq; + roc_info.nrf_wifi_freq_params.center_frequency2 = 0; + roc_info.nrf_wifi_freq_params.channel_type = NRF_WIFI_CHAN_HT20; + roc_info.dur = duration; + + status = nrf_wifi_sys_fmac_p2p_roc_start(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + &roc_info); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_remain_on_channel failed", __func__); + goto out; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +#endif /* NRF70_P2P_MODE */ + return status; +} + +int nrf_wifi_supp_cancel_remain_on_channel(void *if_priv) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; +#ifdef NRF70_P2P_MODE + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return -1; + } + + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + status = nrf_wifi_sys_fmac_p2p_roc_stop(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, 0); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_cancel_remain_on_channel failed", __func__); + goto out; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +#endif /* NRF70_P2P_MODE */ + return status; +} + +int nrf_wifi_supp_set_p2p_powersave(void *if_priv, int legacy_ps, int opp_ps, int ctwindow) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; +#ifdef NRF70_P2P_MODE + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + + if (!if_priv) { + LOG_ERR("%s: Invalid params", __func__); + return -1; + } + vif_ctx_zep = if_priv; + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return -1; + } + k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); + if (!rpu_ctx_zep->rpu_ctx) { + LOG_DBG("%s: RPU context not initialized", __func__); + goto out; + } + + if (legacy_ps == -1) { + status = 0; + goto out; + } + + if (legacy_ps != 0 && legacy_ps != 1) { + LOG_ERR("%s: Invalid legacy_ps value: %d", __func__, legacy_ps); + goto out; + } + + status = nrf_wifi_sys_fmac_set_power_save(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx, + legacy_ps); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: nrf_wifi_fmac_set_p2p_powersave failed", __func__); + goto out; + } +out: + k_mutex_unlock(&vif_ctx_zep->vif_lock); +#endif /* NRF70_P2P_MODE */ + return status; +} + #ifdef CONFIG_NRF70_AP_MODE static int nrf_wifi_vif_state_change(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep, enum nrf_wifi_fmac_if_op_state state) @@ -2798,7 +3011,9 @@ int nrf_wifi_wpa_supp_sta_set_flags(void *if_priv, const u8 *addr, struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; struct nrf_wifi_umac_chg_sta_info chg_sta = {0}; struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_sys_fmac_dev_ctx *sys_dev_ctx = NULL; enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + int peer_id = -1; int ret = -1; if (!if_priv || !addr) { @@ -2833,6 +3048,19 @@ int nrf_wifi_wpa_supp_sta_set_flags(void *if_priv, const u8 *addr, goto out; } + sys_dev_ctx = wifi_dev_priv(rpu_ctx_zep->rpu_ctx); + + peer_id = nrf_wifi_fmac_peer_get_id(rpu_ctx_zep->rpu_ctx, chg_sta.mac_addr); + if (peer_id == -1) { + nrf_wifi_osal_log_err("%s: Invalid peer", + __func__); + goto out; + } + + if (chg_sta.sta_flags2.nrf_wifi_set & NRF_WIFI_STA_FLAG_AUTHORIZED) { + sys_dev_ctx->tx_config.peers[peer_id].authorized = true; + } + ret = 0; out: diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index 817a4fcd6fb65..60dbe5cfc2601 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -139,6 +139,8 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_BSS_MAX_IDLE_PERIOD, /** Configure background scanning */ NET_REQUEST_WIFI_CMD_BGSCAN, + /** Wi-Fi Direct (P2P) operations*/ + NET_REQUEST_WIFI_CMD_P2P_OPER, /** @cond INTERNAL_HIDDEN */ NET_REQUEST_WIFI_CMD_MAX /** @endcond */ @@ -339,6 +341,11 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_BSS_MAX_IDLE_PERIOD); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_BGSCAN); +#define NET_REQUEST_WIFI_P2P_OPER \ + (NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_P2P_OPER) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_P2P_OPER); + /** @cond INTERNAL_HIDDEN */ enum { @@ -359,7 +366,7 @@ enum { NET_EVENT_WIFI_CMD_AP_STA_CONNECTED_VAL, NET_EVENT_WIFI_CMD_AP_STA_DISCONNECTED_VAL, NET_EVENT_WIFI_CMD_SUPPLICANT_VAL, - + NET_EVENT_WIFI_CMD_P2P_DEVICE_FOUND_VAL, NET_EVENT_WIFI_CMD_MAX, }; @@ -406,6 +413,8 @@ enum net_event_wifi_cmd { NET_MGMT_CMD(NET_EVENT_WIFI_CMD_AP_STA_DISCONNECTED), /** Supplicant specific event */ NET_MGMT_CMD(NET_EVENT_WIFI_CMD_SUPPLICANT), + /** P2P device found */ + NET_MGMT_CMD(NET_EVENT_WIFI_CMD_P2P_DEVICE_FOUND), }; /** Event emitted for Wi-Fi scan result */ @@ -468,6 +477,38 @@ enum net_event_wifi_cmd { #define NET_EVENT_WIFI_AP_STA_DISCONNECTED \ (NET_WIFI_EVENT | NET_EVENT_WIFI_CMD_AP_STA_DISCONNECTED) +/** Event emitted for P2P device found event */ +#define NET_EVENT_WIFI_P2P_DEVICE_FOUND \ + (NET_WIFI_EVENT | NET_EVENT_WIFI_CMD_P2P_DEVICE_FOUND) + +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +/** @brief Wi-Fi P2P device info */ +struct wifi_p2p_device_info { + /** Device MAC address */ + uint8_t mac[WIFI_MAC_ADDR_LEN]; + /** Device name (max 32 chars + null terminator) */ + char device_name[33]; + /** Primary device type */ + uint8_t pri_dev_type[8]; + /** Primary device type string */ + char pri_dev_type_str[32]; + /** Signal strength (RSSI) */ + int8_t rssi; + /** WPS configuration methods supported */ + uint16_t config_methods; + /** WPS configuration methods string */ + char config_methods_str[16]; + /** Device capability */ + uint8_t dev_capab; + /** Group capability */ + uint8_t group_capab; + /** Manufacturer (max 64 chars + null terminator) */ + char manufacturer[65]; + /** Model name (max 32 chars + null terminator) */ + char model_name[33]; +}; +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + /** @brief Wi-Fi version */ struct wifi_version { /** Driver version */ @@ -1114,6 +1155,9 @@ union wifi_mgmt_events { #endif /* CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS */ struct wifi_twt_params twt_params; struct wifi_ap_sta_info ap_sta_info; +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + struct wifi_p2p_device_info p2p_device_info; +#endif }; /** @endcond */ @@ -1397,6 +1441,131 @@ struct wifi_wps_config_params { char pin[WIFI_WPS_PIN_MAX_LEN + 1]; }; +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +/** Wi-Fi P2P operation */ +enum wifi_p2p_op { + /** P2P find/discovery */ + WIFI_P2P_FIND = 0, + /** P2P stop find/discovery */ + WIFI_P2P_STOP_FIND, + /** P2P query peer info use broadcast MAC (ff:ff:ff:ff:ff:ff) to list all peers, + * or specific MAC address to query a single peer + */ + WIFI_P2P_PEER, + /** P2P connect to peer */ + WIFI_P2P_CONNECT, + /** P2P group add */ + WIFI_P2P_GROUP_ADD, + /** P2P group remove */ + WIFI_P2P_GROUP_REMOVE, + /** P2P invite */ + WIFI_P2P_INVITE, + /** P2P power save */ + WIFI_P2P_POWER_SAVE, +}; + +/** Wi-Fi P2P discovery type */ +enum wifi_p2p_discovery_type { + /** Start with full scan, then only social channels */ + WIFI_P2P_FIND_START_WITH_FULL = 0, + /** Only social channels (1, 6, 11) */ + WIFI_P2P_FIND_ONLY_SOCIAL, + /** Progressive - scan through all channels one at a time */ + WIFI_P2P_FIND_PROGRESSIVE, +}; + +/** Wi-Fi P2P connection method */ +enum wifi_p2p_connection_method { + /** Push Button Configuration */ + WIFI_P2P_METHOD_PBC = 0, + /** Display PIN (device displays PIN for peer to enter) */ + WIFI_P2P_METHOD_DISPLAY, + /** Keypad PIN (user enters PIN on device) */ + WIFI_P2P_METHOD_KEYPAD, +}; + +/** Maximum number of P2P peers that can be returned in a single query */ +#define WIFI_P2P_MAX_PEERS CONFIG_WIFI_P2P_MAX_PEERS + +/** Wi-Fi P2P parameters */ +struct wifi_p2p_params { + /** P2P operation */ + enum wifi_p2p_op oper; + /** Discovery type (for find operation) */ + enum wifi_p2p_discovery_type discovery_type; + /** Timeout in seconds (0 = no timeout, run until stopped) */ + uint16_t timeout; + /** Peer device address (for peer operation) */ + uint8_t peer_addr[WIFI_MAC_ADDR_LEN]; + /** Flag to list only discovered peers (for peers operation) */ + bool discovered_only; + /** Pointer to array for peer info results */ + struct wifi_p2p_device_info *peers; + /** Actual number of peers returned */ + uint16_t peer_count; + /** Power save enabled (for power save operation) */ + bool power_save; + /** Connect specific parameters */ + struct { + /** Connection method */ + enum wifi_p2p_connection_method method; + /** PIN for display/keypad methods (8 digits) + * - For DISPLAY: Leave empty, PIN will be generated and returned + * - For KEYPAD: Provide the PIN to use for connection + */ + char pin[WIFI_WPS_PIN_MAX_LEN + 1]; + /** GO intent (0-15, higher values indicate higher willingness to be GO) */ + uint8_t go_intent; + /** Frequency in MHz (0 = not specified, use default) */ + unsigned int freq; + } connect; + /** Group add specific parameters */ + struct { + /** Frequency in MHz (0 = auto) */ + int freq; + /** Persistent group ID (-1 = not persistent) */ + int persistent; + /** Enable HT40 */ + bool ht40; + /** Enable VHT */ + bool vht; + /** Enable HE */ + bool he; + /** Enable EDMG */ + bool edmg; + /** GO BSSID (NULL = auto) */ + uint8_t go_bssid[WIFI_MAC_ADDR_LEN]; + /** GO BSSID length */ + uint8_t go_bssid_length; + } group_add; + /** Group remove specific parameters */ + struct { + /** Interface name (e.g., "wlan0") */ + char ifname[CONFIG_NET_INTERFACE_NAME_LEN + 1]; + } group_remove; + /** Invite specific parameters */ + struct { + /** Invite type: persistent or group */ + enum { + WIFI_P2P_INVITE_PERSISTENT = 0, + WIFI_P2P_INVITE_GROUP, + } type; + /** Persistent group ID (for persistent type) */ + int persistent_id; + /** Group interface name (for group type) */ + char group_ifname[CONFIG_NET_INTERFACE_NAME_LEN + 1]; + /** Peer MAC address */ + uint8_t peer_addr[WIFI_MAC_ADDR_LEN]; + /** Frequency in MHz (0 = auto) */ + int freq; + /** GO device address (for group type, NULL = auto) */ + uint8_t go_dev_addr[WIFI_MAC_ADDR_LEN]; + /** GO device address length */ + uint8_t go_dev_addr_length; + } invite; +}; +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + /** Wi-Fi AP status */ enum wifi_sap_iface_state { @@ -1785,6 +1954,17 @@ struct wifi_mgmt_ops { */ int (*set_bgscan)(const struct device *dev, struct wifi_bgscan_params *params); #endif +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + /** Wi-Fi Direct (P2P) operations for device discovery + * + * @param dev Pointer to the device structure for the driver instance. + * @param params P2P operation parameters including operation type, discovery settings, + * timeout values and peer information retrieval options + * + * @return 0 if ok, < 0 if error + */ + int (*p2p_oper)(const struct device *dev, struct wifi_p2p_params *params); +#endif }; /** Wi-Fi management offload API */ @@ -1916,6 +2096,17 @@ void wifi_mgmt_raise_ap_sta_connected_event(struct net_if *iface, void wifi_mgmt_raise_ap_sta_disconnected_event(struct net_if *iface, struct wifi_ap_sta_info *sta_info); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +/** + * @brief Raise P2P device found event + * + * @param iface Network interface + * @param device_info P2P device information + */ +void wifi_mgmt_raise_p2p_device_found_event(struct net_if *iface, + struct wifi_p2p_device_info *peer_info); +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + /** * @} */ diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index 41a50842b71a5..4989767ca2e7b 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -383,16 +383,9 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPS ${HOSTAP_SRC_BASE}/wps/wps_enrollee.c ${HOSTAP_SRC_BASE}/wps/wps_registrar.c ${HOSTAP_SRC_BASE}/crypto/dh_groups.c + ${HOSTAP_SRC_BASE}/eap_server/eap_server_wsc.c ) -if(NOT CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT) -# dh_group5 is only needed if we are not using mbedtls, as mbedtls provides -# its own definition -zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPS - ${HOSTAP_SRC_BASE}/crypto/dh_group5.c -) -endif() - zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P CONFIG_P2P CONFIG_GAS @@ -402,6 +395,7 @@ zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WPS CONFIG_WPS EAP_WSC + EAP_SERVER_WSC ) zephyr_library_sources_ifdef(CONFIG_WIFI_NM_HOSTAPD_WPS diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index ccc3b1899810a..6bb9afb7be9ee 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -53,6 +53,7 @@ config HEAP_MEM_POOL_ADD_SIZE_HOSTAP def_int 60000 if WIFI_USAGE_MODE_STA_AP def_int 55000 if WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE && WIFI_CREDENTIALS def_int 48000 if WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + def_int 80000 if WIFI_NM_WPA_SUPPLICANT_P2P def_int 41808 if WIFI_NM_WPA_SUPPLICANT_AP # 30K is mandatory, but might need more for long duration use cases def_int 30000 @@ -61,6 +62,7 @@ endif # WIFI_NM_WPA_SUPPLICANT_GLOBAL_HEAP config WIFI_NM_WPA_SUPPLICANT_THREAD_STACK_SIZE int "Stack size for wpa_supplicant thread" + default 10000 if WIFI_NM_WPA_SUPPLICANT_P2P # TODO: Providing higher stack size for Enterprise mode to fix stack # overflow issues. Need to identify the cause for higher stack usage. default 8192 if WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE || WIFI_USAGE_MODE_STA_AP diff --git a/modules/hostap/src/supp_api.c b/modules/hostap/src/supp_api.c index b5ae00e0a871e..8e7d38e4d25dd 100644 --- a/modules/hostap/src/supp_api.c +++ b/modules/hostap/src/supp_api.c @@ -60,6 +60,15 @@ enum status_thread_state { static struct wifi_enterprise_creds_params enterprise_creds; #endif +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +/* TODO: Define size with references */ +#define P2P_CMD_BUF_SIZE 256 +#define P2P_RESP_BUF_SIZE 256 +#define P2P_ADDR_SIZE 32 +#define P2P_CMD_SIZE 64 +#define P2P_PEER_INFO_SIZE 512 +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + K_MUTEX_DEFINE(wpa_supplicant_mutex); extern struct k_work_q *get_workq(void); @@ -2586,3 +2595,472 @@ int supplicant_config_params(const struct device *dev, struct wifi_config_params k_mutex_unlock(&wpa_supplicant_mutex); return ret; } + +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +static inline void extract_value(const char *src, char *dest, size_t dest_size) +{ + size_t i = 0; + + while (src[i] != '\0' && src[i] != '\n' && i < dest_size - 1) { + dest[i] = src[i]; + i++; + } + dest[i] = '\0'; +} + +static void parse_peer_info_line(const char *line, struct wifi_p2p_device_info *info) +{ + const char *pos; + + if (strncmp(line, "device_name=", 12) == 0) { + extract_value(line + 12, info->device_name, sizeof(info->device_name)); + } else if (strncmp(line, "pri_dev_type=", 13) == 0) { + extract_value(line + 13, info->pri_dev_type_str, sizeof(info->pri_dev_type_str)); + } else if (strncmp(line, "level=", 6) == 0) { + pos = line + 6; + /* Skip extraction for numeric values, atoi handles it */ + info->rssi = (int8_t)atoi(pos); + } else if (strncmp(line, "config_methods=", 15) == 0) { + pos = line + 15; + extract_value(pos, info->config_methods_str, sizeof(info->config_methods_str)); + + if (pos[0] == '0' && (pos[1] == 'x' || pos[1] == 'X')) { + info->config_methods = (uint16_t)strtol(pos, NULL, 16); + } else { + info->config_methods = (uint16_t)atoi(pos); + } + } else if (strncmp(line, "manufacturer=", 13) == 0) { + extract_value(line + 13, info->manufacturer, sizeof(info->manufacturer)); + } else if (strncmp(line, "model_name=", 11) == 0) { + extract_value(line + 11, info->model_name, sizeof(info->model_name)); + } +} + +static void parse_peer_info_response(const char *resp, const uint8_t *mac, + struct wifi_p2p_device_info *info) +{ + const char *line = resp; + const char *next_line; + + memset(info, 0, sizeof(*info)); + + if (mac) { + memcpy(info->mac, mac, WIFI_MAC_ADDR_LEN); + } + + while (line && *line) { + if (*line == '\n') { + line++; + continue; + } + next_line = strchr(line, '\n'); + parse_peer_info_line(line, info); + if (next_line) { + line = next_line + 1; + } else { + break; + } + } +} + +int supplicant_p2p_oper(const struct device *dev, struct wifi_p2p_params *params) +{ + struct wpa_supplicant *wpa_s = get_wpa_s_handle(dev); + char cmd_buf[P2P_CMD_BUF_SIZE]; + char resp_buf[P2P_RESP_BUF_SIZE]; + int ret = -1; + const char *discovery_type_str = ""; + + if (!wpa_s || !wpa_s->ctrl_conn) { + wpa_printf(MSG_ERROR, "wpa_supplicant control interface not initialized"); + return -ENOTSUP; + } + + switch (params->oper) { + case WIFI_P2P_FIND: + switch (params->discovery_type) { + case WIFI_P2P_FIND_ONLY_SOCIAL: + discovery_type_str = "type=social"; + break; + case WIFI_P2P_FIND_PROGRESSIVE: + discovery_type_str = "type=progressive"; + break; + case WIFI_P2P_FIND_START_WITH_FULL: + default: + discovery_type_str = ""; + break; + } + + if (params->timeout > 0) { + if (strlen(discovery_type_str) > 0) { + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_FIND %u %s", + params->timeout, discovery_type_str); + } else { + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_FIND %u", + params->timeout); + } + } else { + if (strlen(discovery_type_str) > 0) { + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_FIND %s", + discovery_type_str); + } else { + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_FIND"); + } + } + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_FIND command failed: %d", ret); + return -EIO; + } + ret = 0; + break; + + case WIFI_P2P_STOP_FIND: + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_STOP_FIND"); + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_STOP_FIND command failed: %d", ret); + return -EIO; + } + ret = 0; + break; + + case WIFI_P2P_PEER: + char addr[P2P_ADDR_SIZE]; + char cmd[P2P_CMD_SIZE]; + char peer_info[P2P_PEER_INFO_SIZE]; + char *pos; + size_t len; + uint16_t peer_idx = 0; + uint8_t mac[WIFI_MAC_ADDR_LEN]; + struct net_eth_addr peer_mac; + bool query_all_peers; + + if (!params->peers) { + wpa_printf(MSG_ERROR, "Peer info array not provided"); + return -EINVAL; + } + + memcpy(&peer_mac, params->peer_addr, WIFI_MAC_ADDR_LEN); + query_all_peers = net_eth_is_addr_broadcast(&peer_mac); + + if (query_all_peers) { + os_strlcpy(cmd_buf, "P2P_PEER FIRST", sizeof(cmd_buf)); + + while (peer_idx < params->peer_count) { + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, + cmd_buf, resp_buf); + + if (ret < 0 || resp_buf[0] == '\0' || + os_strncmp(resp_buf, "FAIL", 4) == 0) { + if (peer_idx == 0) { + wpa_printf(MSG_DEBUG, "No P2P peers found"); + } + break; + } + + len = 0; + pos = resp_buf; + while (*pos != '\0' && *pos != '\n' && len < sizeof(addr) - 1) { + addr[len++] = *pos++; + } + addr[len] = '\0'; + + if (os_strncmp(addr, "00:00:00:00:00:00", 17) != 0 && + sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]) == + WIFI_MAC_ADDR_LEN) { + + os_snprintf(cmd, sizeof(cmd), "P2P_PEER %s", addr); + ret = zephyr_wpa_cli_cmd_resp_noprint( + wpa_s->ctrl_conn, cmd, peer_info); + + if (ret >= 0 && + (!params->discovered_only || + os_strstr(peer_info, + "[PROBE_REQ_ONLY]") == NULL)) { + parse_peer_info_response(peer_info, + mac, + ¶ms->peers[peer_idx]); + peer_idx++; + } + } + os_snprintf(cmd_buf, sizeof(cmd_buf), "P2P_PEER NEXT-%s", addr); + } + params->peer_count = peer_idx; + } else { + char addr_str[18]; + + if (params->peer_count < 1) { + wpa_printf(MSG_ERROR, "Peer count must be at least 1"); + return -EINVAL; + } + + snprintf(addr_str, sizeof(addr_str), "%02x:%02x:%02x:%02x:%02x:%02x", + params->peer_addr[0], params->peer_addr[1], params->peer_addr[2], + params->peer_addr[3], params->peer_addr[4], params->peer_addr[5]); + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_PEER %s", addr_str); + + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, + cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_PEER command failed: %d", ret); + return -EIO; + } + if (os_strncmp(resp_buf, "FAIL", 4) == 0) { + wpa_printf(MSG_ERROR, "Peer %s not found", addr_str); + return -ENODEV; + } + parse_peer_info_response(resp_buf, params->peer_addr, + ¶ms->peers[0]); + params->peer_count = 1; + } + ret = 0; + break; + + case WIFI_P2P_CONNECT: { + char addr_str[18]; + const char *method_str = ""; + char freq_str[32] = ""; + + if (!params) { + wpa_printf(MSG_ERROR, "P2P connect params are NULL"); + return -EINVAL; + } + + snprintf(addr_str, sizeof(addr_str), "%02x:%02x:%02x:%02x:%02x:%02x", + params->peer_addr[0], params->peer_addr[1], params->peer_addr[2], + params->peer_addr[3], params->peer_addr[4], params->peer_addr[5]); + + /* Add frequency parameter if specified */ + if (params->connect.freq > 0) { + snprintf(freq_str, sizeof(freq_str), " freq=%u", params->connect.freq); + } + + switch (params->connect.method) { + case WIFI_P2P_METHOD_PBC: + method_str = "pbc"; + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_CONNECT %s %s go_intent=%d%s", + addr_str, method_str, params->connect.go_intent, freq_str); + break; + case WIFI_P2P_METHOD_DISPLAY: + method_str = "pin"; + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_CONNECT %s %s go_intent=%d%s", + addr_str, method_str, params->connect.go_intent, freq_str); + break; + case WIFI_P2P_METHOD_KEYPAD: + method_str = "keypad"; + if (params->connect.pin[0] == '\0') { + wpa_printf(MSG_ERROR, "PIN required for keypad method"); + return -EINVAL; + } + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_CONNECT %s %s %s go_intent=%d%s", + addr_str, method_str, params->connect.pin, + params->connect.go_intent, freq_str); + break; + default: + wpa_printf(MSG_ERROR, "Unknown P2P connection method: %d", + params->connect.method); + return -EINVAL; + } + + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_CONNECT command failed: %d", ret); + return -EIO; + } + if (os_strncmp(resp_buf, "FAIL", 4) == 0) { + wpa_printf(MSG_ERROR, "P2P connect failed: %s", resp_buf); + return -ENODEV; + } + + /* For DISPLAY method, capture the generated PIN from response */ + if (params->connect.method == WIFI_P2P_METHOD_DISPLAY) { + size_t len = 0; + char *pos = resp_buf; + + while (*pos == ' ' || *pos == '\t' || *pos == '\n') { + pos++; + } + + while (*pos != '\0' && *pos != '\n' && *pos != ' ' && + len < WIFI_WPS_PIN_MAX_LEN) { + params->connect.pin[len++] = *pos++; + } + params->connect.pin[len] = '\0'; + + if (params->connect.pin[0] == '\0') { + wpa_printf(MSG_ERROR, "P2P connect: No PIN returned"); + return -ENODEV; + } + } + + ret = 0; + break; + } + + case WIFI_P2P_GROUP_ADD: { + int len = 0; + + if (!params) { + wpa_printf(MSG_ERROR, "P2P group add params are NULL"); + return -EINVAL; + } + + len = snprintf(cmd_buf, sizeof(cmd_buf), "P2P_GROUP_ADD"); + + if (params->group_add.freq > 0) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " freq=%d", + params->group_add.freq); + } + + if (params->group_add.persistent >= 0) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " persistent=%d", + params->group_add.persistent); + } + + if (params->group_add.ht40) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " ht40"); + } + + if (params->group_add.vht) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " vht"); + } + + if (params->group_add.he) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " he"); + } + + if (params->group_add.edmg) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " edmg"); + } + + if (params->group_add.go_bssid_length == WIFI_MAC_ADDR_LEN) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, + " go_bssid=%02x:%02x:%02x:%02x:%02x:%02x", + params->group_add.go_bssid[0], + params->group_add.go_bssid[1], + params->group_add.go_bssid[2], + params->group_add.go_bssid[3], + params->group_add.go_bssid[4], + params->group_add.go_bssid[5]); + } + + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_GROUP_ADD command failed: %d", ret); + return -EIO; + } + ret = 0; + break; + } + + case WIFI_P2P_GROUP_REMOVE: + if (!params) { + wpa_printf(MSG_ERROR, "P2P group remove params are NULL"); + return -EINVAL; + } + + if (params->group_remove.ifname[0] == '\0') { + wpa_printf(MSG_ERROR, "Interface name required for P2P_GROUP_REMOVE"); + return -EINVAL; + } + + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_GROUP_REMOVE %s", + params->group_remove.ifname); + + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_GROUP_REMOVE command failed: %d", ret); + return -EIO; + } + ret = 0; + break; + + case WIFI_P2P_INVITE: { + char addr_str[18]; + int len = 0; + + if (!params) { + wpa_printf(MSG_ERROR, "P2P invite params are NULL"); + return -EINVAL; + } + + snprintf(addr_str, sizeof(addr_str), "%02x:%02x:%02x:%02x:%02x:%02x", + params->invite.peer_addr[0], params->invite.peer_addr[1], + params->invite.peer_addr[2], params->invite.peer_addr[3], + params->invite.peer_addr[4], params->invite.peer_addr[5]); + + if (params->invite.type == WIFI_P2P_INVITE_PERSISTENT) { + if (params->invite.persistent_id < 0) { + wpa_printf(MSG_ERROR, "Persistent group ID required"); + return -EINVAL; + } + len = snprintf(cmd_buf, sizeof(cmd_buf), "P2P_INVITE persistent=%d peer=%s", + params->invite.persistent_id, addr_str); + + if (params->invite.freq > 0) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " freq=%d", + params->invite.freq); + } + } else if (params->invite.type == WIFI_P2P_INVITE_GROUP) { + if (params->invite.group_ifname[0] == '\0') { + wpa_printf(MSG_ERROR, "Group interface name required"); + return -EINVAL; + } + len = snprintf(cmd_buf, sizeof(cmd_buf), "P2P_INVITE group=%s peer=%s", + params->invite.group_ifname, addr_str); + + if (params->invite.freq > 0) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, " freq=%d", + params->invite.freq); + } + + if (params->invite.go_dev_addr_length == WIFI_MAC_ADDR_LEN) { + len += snprintf(cmd_buf + len, sizeof(cmd_buf) - len, + " go_dev_addr=%02x:%02x:%02x:%02x:%02x:%02x", + params->invite.go_dev_addr[0], + params->invite.go_dev_addr[1], + params->invite.go_dev_addr[2], + params->invite.go_dev_addr[3], + params->invite.go_dev_addr[4], + params->invite.go_dev_addr[5]); + } + } else { + wpa_printf(MSG_ERROR, "Invalid invite type: %d", params->invite.type); + return -EINVAL; + } + + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "P2P_INVITE command failed: %d", ret); + return -EIO; + } + ret = 0; + break; + } + + case WIFI_P2P_POWER_SAVE: + snprintf(cmd_buf, sizeof(cmd_buf), "p2p_set ps %d", params->power_save ? 1 : 0); + ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn, cmd_buf, resp_buf); + if (ret < 0) { + wpa_printf(MSG_ERROR, "p2p_set ps command failed: %d", ret); + return -EIO; + } + if (os_strncmp(resp_buf, "FAIL", 4) == 0) { + wpa_printf(MSG_ERROR, "p2p_set ps command returned FAIL"); + return -EIO; + } + ret = 0; + break; + + default: + wpa_printf(MSG_ERROR, "Unknown P2P operation: %d", params->oper); + ret = -EINVAL; + break; + } + + return ret; +} +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ diff --git a/modules/hostap/src/supp_api.h b/modules/hostap/src/supp_api.h index 8ab49f177e708..692db61bf277f 100644 --- a/modules/hostap/src/supp_api.h +++ b/modules/hostap/src/supp_api.h @@ -386,4 +386,16 @@ int supplicant_dpp_dispatch(const struct device *dev, struct wifi_dpp_params *pa * @return 0 for OK; -1 for ERROR */ int supplicant_config_params(const struct device *dev, struct wifi_config_params *params); + +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +/** + * @brief P2P operation + * + * @param dev Wi-Fi interface handle to use + * @param params P2P parameters + * @return 0 for OK; -1 for ERROR + */ +int supplicant_p2p_oper(const struct device *dev, struct wifi_p2p_params *params); +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + #endif /* ZEPHYR_SUPP_MGMT_H */ diff --git a/modules/hostap/src/supp_events.c b/modules/hostap/src/supp_events.c index c4175a416bea3..ee6a05a568ec5 100644 --- a/modules/hostap/src/supp_events.c +++ b/modules/hostap/src/supp_events.c @@ -43,6 +43,9 @@ static const struct wpa_supp_event_info { { "CTRL-EVENT-NETWORK-REMOVED", SUPPLICANT_EVENT_NETWORK_REMOVED }, { "CTRL-EVENT-DSCP-POLICY", SUPPLICANT_EVENT_DSCP_POLICY }, { "CTRL-EVENT-REGDOM-CHANGE", SUPPLICANT_EVENT_REGDOM_CHANGE }, +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + { "P2P-DEVICE-FOUND", SUPPLICANT_EVENT_P2P_DEVICE_FOUND }, +#endif }; static void copy_mac_addr(const unsigned int *src, uint8_t *dst) @@ -174,6 +177,43 @@ static int supplicant_process_status(struct supplicant_int_event_data *event_dat event_data->data_len = sizeof(data->bss_removed); copy_mac_addr(tmp_mac_addr, data->bss_removed.bssid); break; +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + case SUPPLICANT_EVENT_P2P_DEVICE_FOUND: + { + char *ptr, *name_start, *name_end; + unsigned int config_methods = 0; + + memset(&data->p2p_device_found, 0, sizeof(data->p2p_device_found)); + ret = sscanf(event_no_prefix, MACSTR, MACADDR2STR(tmp_mac_addr)); + if (ret > 0) { + copy_mac_addr(tmp_mac_addr, data->p2p_device_found.mac); + } + name_start = strstr(event_no_prefix, "name='"); + if (name_start) { + name_start += 6; + name_end = strchr(name_start, '\''); + if (name_end) { + size_t name_len = name_end - name_start; + + if (name_len >= sizeof(data->p2p_device_found.device_name)) { + name_len = sizeof(data->p2p_device_found.device_name) - 1; + } + memcpy(data->p2p_device_found.device_name, name_start, name_len); + data->p2p_device_found.device_name[name_len] = '\0'; + } + } + ptr = strstr(event_no_prefix, "config_methods="); + if (ptr) { + ret = sscanf(ptr, "config_methods=%x", &config_methods); + if (ret > 0) { + data->p2p_device_found.config_methods = config_methods; + } + } + event_data->data_len = sizeof(data->p2p_device_found); + ret = 1; + break; + } +#endif case SUPPLICANT_EVENT_TERMINATING: case SUPPLICANT_EVENT_SCAN_STARTED: case SUPPLICANT_EVENT_SCAN_RESULTS: @@ -386,8 +426,18 @@ int supplicant_send_wifi_mgmt_event(const char *ifname, enum net_event_wifi_cmd case NET_EVENT_WIFI_CMD_SUPPLICANT: event_data.data = &data; if (supplicant_process_status(&event_data, (char *)supplicant_status) > 0) { - net_mgmt_event_notify_with_info(NET_EVENT_SUPPLICANT_INT_EVENT, - iface, &event_data, sizeof(event_data)); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + /* Handle P2P events directly */ + if (event_data.event == SUPPLICANT_EVENT_P2P_DEVICE_FOUND) { + wifi_mgmt_raise_p2p_device_found_event(iface, + &data.p2p_device_found); + } else { +#endif + net_mgmt_event_notify_with_info(NET_EVENT_SUPPLICANT_INT_EVENT, + iface, &event_data, sizeof(event_data)); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + } +#endif } break; default: diff --git a/modules/hostap/src/supp_events.h b/modules/hostap/src/supp_events.h index 1c74af528492f..6ff8812c2fb83 100644 --- a/modules/hostap/src/supp_events.h +++ b/modules/hostap/src/supp_events.h @@ -137,6 +137,10 @@ union supplicant_event_data { unsigned int id; } network_removed; +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + struct wifi_p2p_device_info p2p_device_found; +#endif + char supplicant_event_str[NM_WIFI_EVENT_STR_LEN]; }; @@ -158,6 +162,9 @@ enum supplicant_event_num { SUPPLICANT_EVENT_NETWORK_REMOVED, SUPPLICANT_EVENT_DSCP_POLICY, SUPPLICANT_EVENT_REGDOM_CHANGE, +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + SUPPLICANT_EVENT_P2P_DEVICE_FOUND, +#endif }; struct supplicant_int_event_data { diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index d1f8c360cb503..8afdba33b2030 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -101,6 +101,9 @@ static const struct wifi_mgmt_ops mgmt_ops = { .enterprise_creds = supplicant_add_enterprise_creds, #endif .config_params = supplicant_config_params, +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + .p2p_oper = supplicant_p2p_oper, +#endif }; DEFINE_WIFI_NM_INSTANCE(wifi_supplicant, &mgmt_ops); @@ -245,6 +248,12 @@ static void zephyr_wpa_supplicant_msg(void *ctx, const char *txt, size_t len) supplicant_send_wifi_mgmt_event(wpa_s->ifname, NET_EVENT_WIFI_CMD_NEIGHBOR_REP_RECEIVED, (void *)txt, len); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + } else if (strncmp(txt, "P2P-", 4) == 0) { + supplicant_send_wifi_mgmt_event(wpa_s->ifname, + NET_EVENT_WIFI_CMD_SUPPLICANT, + (void *)txt, len); +#endif } } diff --git a/subsys/net/l2/wifi/Kconfig b/subsys/net/l2/wifi/Kconfig index 63680dba9b30a..eddfb7d4c30a1 100644 --- a/subsys/net/l2/wifi/Kconfig +++ b/subsys/net/l2/wifi/Kconfig @@ -72,6 +72,15 @@ config WIFI_SHELL_MAX_AP_STA This option defines the maximum number of APs and STAs that can be managed in Wi-Fi shell. +config WIFI_P2P_MAX_PEERS + int "Maximum number of P2P peers that can be returned in a single query" + depends on WIFI_NM_WPA_SUPPLICANT_P2P + default 32 + range 1 256 + help + This option defines the maximum number of P2P peers that can be returned + in a single query operation. + config WIFI_NM bool "Wi-Fi Network manager support" help diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index b20e5d6674d26..3f08c6ed476be 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -1474,6 +1474,35 @@ static int wifi_set_bgscan(uint64_t mgmt_request, struct net_if *iface, void *da NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_BGSCAN, wifi_set_bgscan); #endif +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +static int wifi_p2p_oper(uint64_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_p2p_params *params = data; + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->p2p_oper == NULL) { + return -ENOTSUP; + } + + if (!data || len != sizeof(*params)) { + return -EINVAL; + } + + return wifi_mgmt_api->p2p_oper(dev, params); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_P2P_OPER, wifi_p2p_oper); + +void wifi_mgmt_raise_p2p_device_found_event(struct net_if *iface, + struct wifi_p2p_device_info *peer_info) +{ + net_mgmt_event_notify_with_info(NET_EVENT_WIFI_P2P_DEVICE_FOUND, + iface, peer_info, + sizeof(*peer_info)); +} +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ #ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS void wifi_mgmt_raise_raw_scan_result_event(struct net_if *iface, diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index e296a98d8eeee..61f9f2f701f6c 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -46,7 +46,8 @@ LOG_MODULE_REGISTER(net_wifi_shell, LOG_LEVEL_INF); NET_EVENT_WIFI_AP_STA_CONNECTED |\ NET_EVENT_WIFI_AP_STA_DISCONNECTED|\ NET_EVENT_WIFI_SIGNAL_CHANGE |\ - NET_EVENT_WIFI_NEIGHBOR_REP_COMP) + NET_EVENT_WIFI_NEIGHBOR_REP_COMP |\ + NET_EVENT_WIFI_P2P_DEVICE_FOUND) #ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS_ONLY #define WIFI_SHELL_SCAN_EVENTS ( \ @@ -519,6 +520,26 @@ static void handle_wifi_neighbor_rep_complete(struct net_mgmt_event_callback *cb } #endif +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +static void handle_wifi_p2p_device_found(struct net_mgmt_event_callback *cb) +{ + const struct wifi_p2p_device_info *peer_info = + (const struct wifi_p2p_device_info *)cb->info; + const struct shell *sh = context.sh; + + if (!peer_info || peer_info->device_name[0] == '\0') { + return; + } + + PR("Device Name: %-20s MAC Address: %02x:%02x:%02x:%02x:%02x:%02x " + "Config Methods: 0x%x\n", + peer_info->device_name, + peer_info->mac[0], peer_info->mac[1], peer_info->mac[2], + peer_info->mac[3], peer_info->mac[4], peer_info->mac[5], + peer_info->config_methods); +} +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface) { @@ -552,6 +573,11 @@ static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, handle_wifi_neighbor_rep_complete(cb, iface); break; #endif +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P + case NET_EVENT_WIFI_P2P_DEVICE_FOUND: + handle_wifi_p2p_device_found(cb); + break; +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ default: break; } @@ -3528,6 +3554,560 @@ static int cmd_wifi_dpp_reconfig(const struct shell *sh, size_t argc, char *argv } #endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_DPP */ + +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +static void print_peer_info(const struct shell *sh, int index, + const struct wifi_p2p_device_info *peer) +{ + uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")]; + const char *device_name; + const char *device_type; + const char *config_methods; + + device_name = (peer->device_name[0] != '\0') ? + peer->device_name : ""; + device_type = (peer->pri_dev_type_str[0] != '\0') ? + peer->pri_dev_type_str : "-"; + config_methods = (peer->config_methods_str[0] != '\0') ? + peer->config_methods_str : "-"; + + PR("%-4d | %-32s | %-17s | %-4d | %-20s | %s\n", + index, + device_name, + net_sprint_ll_addr_buf(peer->mac, WIFI_MAC_ADDR_LEN, + mac_string_buf, + sizeof(mac_string_buf)), + peer->rssi, + device_type, + config_methods); +} + +static int cmd_wifi_p2p_peer(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + uint8_t mac_addr[WIFI_MAC_ADDR_LEN]; + static struct wifi_p2p_device_info peers[WIFI_P2P_MAX_PEERS]; + int ret; + int max_peers = (argc < 2) ? WIFI_P2P_MAX_PEERS : 1; + + context.sh = sh; + + memset(peers, 0, sizeof(peers)); + + params.peers = peers; + params.oper = WIFI_P2P_PEER; + params.peer_count = max_peers; + + if (argc >= 2) { + if (sscanf(argv[1], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], + &mac_addr[3], &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid MAC address format. Use: XX:XX:XX:XX:XX:XX\n"); + return -EINVAL; + } + memcpy(params.peer_addr, mac_addr, WIFI_MAC_ADDR_LEN); + params.peer_count = 1; + } else { + /* Use broadcast MAC to query all peers */ + memset(params.peer_addr, 0xFF, WIFI_MAC_ADDR_LEN); + } + + ret = net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params)); + if (ret) { + PR_WARNING("P2P peer info request failed\n"); + return -ENOEXEC; + } + + if (params.peer_count > 0) { + PR("\n%-4s | %-32s | %-17s | %-4s | %-20s | %s\n", + "Num", "Device Name", "MAC Address", "RSSI", "Device Type", "Config Methods"); + for (int i = 0; i < params.peer_count; i++) { + print_peer_info(sh, i + 1, &peers[i]); + } + } else { + if (argc >= 2) { + shell_print(sh, "No information available for peer %s", argv[1]); + } else { + shell_print(sh, "No P2P peers found"); + } + } + + return 0; +} + + +static int cmd_wifi_p2p_find(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + + context.sh = sh; + + params.oper = WIFI_P2P_FIND; + params.discovery_type = WIFI_P2P_FIND_START_WITH_FULL; + params.timeout = 10; /* Default 10 second timeout */ + + if (argc > 1) { + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"timeout", required_argument, 0, 't'}, + {"type", required_argument, 0, 'T'}, + {"iface", required_argument, 0, 'i'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + long val; + + while ((opt = getopt_long(argc, argv, "t:T:i:h", + long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 't': + if (!parse_number(sh, &val, state->optarg, "timeout", 0, 65535)) { + return -EINVAL; + } + params.timeout = (uint16_t)val; + break; + case 'T': + if (!parse_number(sh, &val, state->optarg, "type", 0, 2)) { + return -EINVAL; + } + switch (val) { + case 0: + params.discovery_type = WIFI_P2P_FIND_ONLY_SOCIAL; + break; + case 1: + params.discovery_type = WIFI_P2P_FIND_PROGRESSIVE; + break; + case 2: + params.discovery_type = WIFI_P2P_FIND_START_WITH_FULL; + break; + default: + return -EINVAL; + } + break; + case 'i': + /* Unused, but parsing to avoid unknown option error */ + break; + case 'h': + shell_help(sh); + return -ENOEXEC; + default: + PR_ERROR("Invalid option %c\n", state->optopt); + return -EINVAL; + } + } + } + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P find request failed\n"); + return -ENOEXEC; + } + PR("P2P find started\n"); + return 0; +} + +static int cmd_wifi_p2p_stop_find(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + + context.sh = sh; + + params.oper = WIFI_P2P_STOP_FIND; + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P stop find request failed\n"); + return -ENOEXEC; + } + PR("P2P find stopped\n"); + return 0; +} + +static int cmd_wifi_p2p_connect(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + uint8_t mac_addr[WIFI_MAC_ADDR_LEN]; + const char *method_arg = NULL; + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"go-intent", required_argument, 0, 'g'}, + {"freq", required_argument, 0, 'f'}, + {"iface", required_argument, 0, 'i'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + long val; + + context.sh = sh; + + if (argc < 3) { + PR_ERROR("Usage: wifi p2p connect [PIN] " + "[--go-intent=<0-15>] [--freq=]\n"); + return -EINVAL; + } + + /* Parse MAC address */ + if (sscanf(argv[1], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], + &mac_addr[3], &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid MAC address format. Use: XX:XX:XX:XX:XX:XX\n"); + return -EINVAL; + } + memcpy(params.peer_addr, mac_addr, WIFI_MAC_ADDR_LEN); + + method_arg = argv[2]; + if (strcmp(method_arg, "pbc") == 0) { + params.connect.method = WIFI_P2P_METHOD_PBC; + } else if (strcmp(method_arg, "pin") == 0) { + if (argc > 3 && argv[3][0] != '-') { + params.connect.method = WIFI_P2P_METHOD_KEYPAD; + strncpy(params.connect.pin, argv[3], WIFI_WPS_PIN_MAX_LEN); + params.connect.pin[WIFI_WPS_PIN_MAX_LEN] = '\0'; + } else { + params.connect.method = WIFI_P2P_METHOD_DISPLAY; + params.connect.pin[0] = '\0'; + } + } else { + PR_ERROR("Invalid connection method. Use: pbc or pin\n"); + return -EINVAL; + } + + /* Set default GO intent */ + params.connect.go_intent = 0; + /* Set default frequency to 2462 MHz (channel 11, 2.4 GHz) */ + params.connect.freq = 2462; + + while ((opt = getopt_long(argc, argv, "g:f:i:h", long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 'g': + if (!parse_number(sh, &val, state->optarg, "go-intent", 0, 15)) { + return -EINVAL; + } + params.connect.go_intent = (uint8_t)val; + break; + case 'f': + if (!parse_number(sh, &val, state->optarg, "freq", 0, 6000)) { + return -EINVAL; + } + params.connect.freq = (unsigned int)val; + break; + case 'i': + /* Unused, but parsing to avoid unknown option error */ + break; + case 'h': + shell_help(sh); + return -ENOEXEC; + default: + PR_ERROR("Invalid option %c\n", state->optopt); + return -EINVAL; + } + } + + params.oper = WIFI_P2P_CONNECT; + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P connect request failed\n"); + return -ENOEXEC; + } + + /* Display the generated PIN for DISPLAY method */ + if (params.connect.method == WIFI_P2P_METHOD_DISPLAY && params.connect.pin[0] != '\0') { + PR("%s\n", params.connect.pin); + } else { + PR("P2P connection initiated\n"); + } + return 0; +} + +static int cmd_wifi_p2p_group_add(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"freq", required_argument, 0, 'f'}, + {"persistent", required_argument, 0, 'p'}, + {"ht40", no_argument, 0, 'h'}, + {"vht", no_argument, 0, 'v'}, + {"he", no_argument, 0, 'H'}, + {"edmg", no_argument, 0, 'e'}, + {"go-bssid", required_argument, 0, 'b'}, + {"iface", required_argument, 0, 'i'}, + {"help", no_argument, 0, '?'}, + {0, 0, 0, 0} + }; + long val; + uint8_t mac_addr[WIFI_MAC_ADDR_LEN]; + + context.sh = sh; + + params.oper = WIFI_P2P_GROUP_ADD; + params.group_add.freq = 0; + params.group_add.persistent = -1; + params.group_add.ht40 = false; + params.group_add.vht = false; + params.group_add.he = false; + params.group_add.edmg = false; + params.group_add.go_bssid_length = 0; + + while ((opt = getopt_long(argc, argv, "f:p:hvHeb:i:?", long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 'f': + if (!parse_number(sh, &val, state->optarg, "freq", 0, 6000)) { + return -EINVAL; + } + params.group_add.freq = (int)val; + break; + case 'p': + if (!parse_number(sh, &val, state->optarg, "persistent", -1, 255)) { + return -EINVAL; + } + params.group_add.persistent = (int)val; + break; + case 'h': + params.group_add.ht40 = true; + break; + case 'v': + params.group_add.vht = true; + params.group_add.ht40 = true; + break; + case 'H': + params.group_add.he = true; + break; + case 'e': + params.group_add.edmg = true; + break; + case 'b': + if (sscanf(state->optarg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], + &mac_addr[3], &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid GO BSSID format. Use: XX:XX:XX:XX:XX:XX\n"); + return -EINVAL; + } + memcpy(params.group_add.go_bssid, mac_addr, WIFI_MAC_ADDR_LEN); + params.group_add.go_bssid_length = WIFI_MAC_ADDR_LEN; + break; + case 'i': + /* Unused, but parsing to avoid unknown option error */ + break; + case '?': + shell_help(sh); + return -ENOEXEC; + default: + PR_ERROR("Invalid option %c\n", state->optopt); + return -EINVAL; + } + } + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P group add request failed\n"); + return -ENOEXEC; + } + PR("P2P group add initiated\n"); + return 0; +} + +static int cmd_wifi_p2p_group_remove(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + + context.sh = sh; + + if (argc < 2) { + PR_ERROR("Interface name required. Usage: wifi p2p group_remove \n"); + return -EINVAL; + } + + params.oper = WIFI_P2P_GROUP_REMOVE; + strncpy(params.group_remove.ifname, argv[1], + CONFIG_NET_INTERFACE_NAME_LEN); + params.group_remove.ifname[CONFIG_NET_INTERFACE_NAME_LEN] = '\0'; + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P group remove request failed\n"); + return -ENOEXEC; + } + PR("P2P group remove initiated\n"); + return 0; +} + +static int cmd_wifi_p2p_invite(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + uint8_t mac_addr[WIFI_MAC_ADDR_LEN]; + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"persistent", required_argument, 0, 'p'}, + {"group", required_argument, 0, 'g'}, + {"peer", required_argument, 0, 'P'}, + {"freq", required_argument, 0, 'f'}, + {"go-dev-addr", required_argument, 0, 'd'}, + {"iface", required_argument, 0, 'i'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + long val; + + context.sh = sh; + + params.oper = WIFI_P2P_INVITE; + params.invite.type = WIFI_P2P_INVITE_PERSISTENT; + params.invite.persistent_id = -1; + params.invite.group_ifname[0] = '\0'; + params.invite.freq = 0; + params.invite.go_dev_addr_length = 0; + memset(params.invite.peer_addr, 0, WIFI_MAC_ADDR_LEN); + + if (argc < 2) { + PR_ERROR("Usage: wifi p2p invite --persistent= OR " + "wifi p2p invite --group= --peer= [options]\n"); + return -EINVAL; + } + + while ((opt = getopt_long(argc, argv, "p:g:P:f:d:i:h", long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 'p': + if (!parse_number(sh, &val, state->optarg, "persistent", 0, 255)) { + return -EINVAL; + } + params.invite.type = WIFI_P2P_INVITE_PERSISTENT; + params.invite.persistent_id = (int)val; + break; + case 'g': + params.invite.type = WIFI_P2P_INVITE_GROUP; + strncpy(params.invite.group_ifname, state->optarg, + CONFIG_NET_INTERFACE_NAME_LEN); + params.invite.group_ifname[CONFIG_NET_INTERFACE_NAME_LEN] = '\0'; + break; + case 'P': + if (sscanf(state->optarg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], + &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid peer MAC address format\n"); + return -EINVAL; + } + memcpy(params.invite.peer_addr, mac_addr, WIFI_MAC_ADDR_LEN); + break; + case 'f': + if (!parse_number(sh, &val, state->optarg, "freq", 0, 6000)) { + return -EINVAL; + } + params.invite.freq = (int)val; + break; + case 'd': + if (sscanf(state->optarg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], + &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid GO device address format\n"); + return -EINVAL; + } + memcpy(params.invite.go_dev_addr, mac_addr, WIFI_MAC_ADDR_LEN); + params.invite.go_dev_addr_length = WIFI_MAC_ADDR_LEN; + break; + case 'i': + /* Unused, but parsing to avoid unknown option error */ + break; + case 'h': + shell_help(sh); + return -ENOEXEC; + default: + PR_ERROR("Invalid option %c\n", state->optopt); + return -EINVAL; + } + } + + if (params.invite.type == WIFI_P2P_INVITE_PERSISTENT && + params.invite.persistent_id >= 0 && + params.invite.peer_addr[0] == 0 && params.invite.peer_addr[1] == 0 && + argc > optind && argv[optind][0] != '-') { + if (sscanf(argv[optind], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], + &mac_addr[4], &mac_addr[5]) != WIFI_MAC_ADDR_LEN) { + PR_ERROR("Invalid peer MAC address format\n"); + return -EINVAL; + } + memcpy(params.invite.peer_addr, mac_addr, WIFI_MAC_ADDR_LEN); + } + + if (params.invite.type == WIFI_P2P_INVITE_PERSISTENT) { + if (params.invite.persistent_id < 0) { + PR_ERROR("Persistent group ID required. Use --persistent=\n"); + return -EINVAL; + } + if (params.invite.peer_addr[0] == 0 && params.invite.peer_addr[1] == 0) { + PR_ERROR("Peer MAC address required\n"); + return -EINVAL; + } + } else if (params.invite.type == WIFI_P2P_INVITE_GROUP) { + if (params.invite.group_ifname[0] == '\0') { + PR_ERROR("Group interface name required. Use --group=\n"); + return -EINVAL; + } + if (params.invite.peer_addr[0] == 0 && params.invite.peer_addr[1] == 0) { + PR_ERROR("Peer MAC address required. Use --peer=\n"); + return -EINVAL; + } + } + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P invite request failed\n"); + return -ENOEXEC; + } + PR("P2P invite initiated\n"); + return 0; +} + +static int cmd_wifi_p2p_power_save(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_p2p_params params = {0}; + bool power_save_enable = false; + + context.sh = sh; + + if (argc < 2) { + PR_ERROR("Usage: wifi p2p power_save \n"); + return -EINVAL; + } + + if (strcmp(argv[1], "on") == 0) { + power_save_enable = true; + } else if (strcmp(argv[1], "off") == 0) { + power_save_enable = false; + } else { + PR_ERROR("Invalid argument. Use 'on' or 'off'\n"); + return -EINVAL; + } + + params.oper = WIFI_P2P_POWER_SAVE; + params.power_save = power_save_enable; + + if (net_mgmt(NET_REQUEST_WIFI_P2P_OPER, iface, ¶ms, sizeof(params))) { + PR_WARNING("P2P power save request failed\n"); + return -ENOEXEC; + } + + PR("P2P power save %s\n", power_save_enable ? "enabled" : "disabled"); + return 0; +} +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + static int cmd_wifi_pmksa_flush(const struct shell *sh, size_t argc, char *argv[]) { struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); @@ -4232,6 +4812,91 @@ SHELL_SUBCMD_ADD((wifi), bgscan, NULL, 2, 6); #endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN */ +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P +SHELL_STATIC_SUBCMD_SET_CREATE( + wifi_cmd_p2p, + SHELL_CMD_ARG(find, NULL, + "Start P2P device discovery\n" + "[-t, --timeout=]: Discovery timeout\n" + "[-T, --type=<0|1|2>]: Discovery type\n" + " 0: Social channels only (1, 6, 11)\n" + " 1: Progressive scan (all channels)\n" + " 2: Full scan first, then social (default)\n" + "[-i, --iface=]: Interface index\n" + "[-h, --help]: Show help\n", + cmd_wifi_p2p_find, 1, 6), + SHELL_CMD_ARG(stop_find, NULL, + "Stop P2P device discovery\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_stop_find, 1, 2), + SHELL_CMD_ARG(peer, NULL, + "Show information about P2P peers\n" + "Usage: peer []\n" + ": Show detailed info for specific peer (format: XX:XX:XX:XX:XX:XX)\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_peer, 1, 3), + SHELL_CMD_ARG(connect, NULL, + "Connect to a P2P peer\n" + "Usage: connect [PIN] [options]\n" + ": Peer device MAC address (format: XX:XX:XX:XX:XX:XX)\n" + ": Use Push Button Configuration\n" + ": Use PIN method\n" + " - Without PIN: Device displays generated PIN for peer to enter\n" + " - With PIN: Device uses provided PIN to connect\n" + "[PIN]: 8-digit PIN (optional, generates if omitted)\n" + "[-g, --go-intent=<0-15>]: GO intent (0=client, 15=GO, default: 0)\n" + "[-f, --freq=]: Frequency in MHz (default: 2462)\n" + "[-i, --iface=]: Interface index\n" + "[-h, --help]: Show help\n" + "Examples:\n" + " wifi p2p connect 9c:b1:50:e3:81:96 pin -g 0 (displays PIN)\n" + " wifi p2p connect 9c:b1:50:e3:81:96 pin 12345670 -g 0 (uses PIN)\n", + cmd_wifi_p2p_connect, 3, 5), + SHELL_CMD_ARG(group_add, NULL, + "Add a P2P group (start as GO)\n" + "Usage: group_add [options]\n" + "[-f, --freq=]: Frequency in MHz (0 = auto)\n" + "[-p, --persistent=]: Persistent group ID (-1 = not persistent)\n" + "[-h, --ht40]: Enable HT40\n" + "[-v, --vht]: Enable VHT (also enables HT40)\n" + "[-H, --he]: Enable HE\n" + "[-e, --edmg]: Enable EDMG\n" + "[-b, --go-bssid=]: GO BSSID (format: XX:XX:XX:XX:XX:XX)\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_group_add, 1, 10), + SHELL_CMD_ARG(group_remove, NULL, + "Remove a P2P group\n" + "Usage: group_remove \n" + ": Interface name (e.g., wlan0)\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_group_remove, 2, 3), + SHELL_CMD_ARG(invite, NULL, + "Invite a peer to a P2P group\n" + "Usage: invite --persistent= OR\n" + " invite --group= --peer= [options]\n" + "[-p, --persistent=]: Persistent group ID\n" + "[-g, --group=]: Group interface name\n" + "[-P, --peer=]: Peer MAC address (format: XX:XX:XX:XX:XX:XX)\n" + "[-f, --freq=]: Frequency in MHz (0 = auto)\n" + "[-d, --go-dev-addr=]: GO device address (for group type)\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_invite, 2, 8), + SHELL_CMD_ARG(power_save, NULL, + "Set P2P power save mode\n" + "Usage: power_save \n" + ": Enable P2P power save\n" + ": Disable P2P power save\n" + "[-i, --iface=]: Interface index\n", + cmd_wifi_p2p_power_save, 2, 3), + SHELL_SUBCMD_SET_END +); + +SHELL_SUBCMD_ADD((wifi), p2p, &wifi_cmd_p2p, + "Wi-Fi Direct (P2P) commands.", + NULL, + 0, 0); +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */ + SHELL_SUBCMD_ADD((wifi), config, NULL, "Configure STA parameters.\n" "-o, --okc=<0/1>: Opportunistic Key Caching. 0: disable, 1: enable\n" diff --git a/west.yml b/west.yml index bf088f3690ea0..4d9b3272114a5 100644 --- a/west.yml +++ b/west.yml @@ -286,7 +286,7 @@ manifest: - hal - name: hostap path: modules/lib/hostap - revision: 6086dea5ee7406e1eede7f2ca6dff1b00b0f04e2 + revision: pull/113/head - name: liblc3 revision: 48bbd3eacd36e99a57317a0a4867002e0b09e183 path: modules/lib/liblc3 @@ -342,7 +342,7 @@ manifest: revision: 26ed181181eed53e400db8f63fa83c566a05d971 path: modules/bsim_hw_models/nrf_hw_models - name: nrf_wifi - revision: a39e9b155830461c9d1cf587afb151b894369b91 + revision: pull/85/head path: modules/lib/nrf_wifi - name: open-amp revision: c30a6d8b92fcebdb797fc1a7698e8729e250f637