net: wifi: Add Wi-Fi direct P2P discovery API support
Add supplicant api and mgmt ops support for P2P discovery. Signed-off-by: Kapil Bhatt <kapil.bhatt@nordicsemi.no>
This commit is contained in:
committed by
Henrik Brix Andersen
parent
3ac9a46aba
commit
84db77c46b
@@ -61,6 +61,14 @@ enum status_thread_state {
|
||||
static struct wifi_enterprise_creds_params enterprise_creds;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P
|
||||
#define P2P_CMD_BUF_SIZE 128
|
||||
#define P2P_RESP_BUF_SIZE 64
|
||||
#define P2P_PEER_INFO_SIZE 512
|
||||
#define P2P_ADDR_SIZE 32
|
||||
#define P2P_CMD_SIZE 64
|
||||
#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P */
|
||||
|
||||
K_MUTEX_DEFINE(wpa_supplicant_mutex);
|
||||
|
||||
extern struct k_work_q *get_workq(void);
|
||||
@@ -2607,3 +2615,249 @@ out:
|
||||
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) {
|
||||
char *endptr;
|
||||
long val;
|
||||
|
||||
pos = line + 6;
|
||||
val = strtol(pos, &endptr, 10);
|
||||
if (endptr != pos && val >= INT8_MIN && val <= INT8_MAX) {
|
||||
info->rssi = (int8_t)val;
|
||||
}
|
||||
} else if (strncmp(line, "config_methods=", 15) == 0) {
|
||||
char *endptr;
|
||||
long val;
|
||||
|
||||
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')) {
|
||||
val = strtol(pos, &endptr, 16);
|
||||
} else {
|
||||
val = strtol(pos, &endptr, 10);
|
||||
}
|
||||
if (endptr != pos && val >= 0 && val <= UINT16_MAX) {
|
||||
info->config_methods = (uint16_t)val;
|
||||
}
|
||||
} 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 != NULL) {
|
||||
memcpy(info->mac, mac, WIFI_MAC_ADDR_LEN);
|
||||
}
|
||||
|
||||
while (line != NULL && *line != '\0') {
|
||||
if (*line == '\n') {
|
||||
line++;
|
||||
continue;
|
||||
}
|
||||
next_line = strchr(line, '\n');
|
||||
parse_peer_info_line(line, info);
|
||||
if (next_line != NULL) {
|
||||
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 == NULL || wpa_s->ctrl_conn == NULL) {
|
||||
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) {
|
||||
snprintk(cmd_buf, sizeof(cmd_buf), "P2P_FIND %u %s",
|
||||
params->timeout, discovery_type_str);
|
||||
} else {
|
||||
snprintk(cmd_buf, sizeof(cmd_buf), "P2P_FIND %u",
|
||||
params->timeout);
|
||||
}
|
||||
} else {
|
||||
if (strlen(discovery_type_str) > 0) {
|
||||
snprintk(cmd_buf, sizeof(cmd_buf), "P2P_FIND %s",
|
||||
discovery_type_str);
|
||||
} else {
|
||||
snprintk(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:
|
||||
snprintk(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 == NULL) {
|
||||
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 == true) {
|
||||
snprintk(cmd_buf, sizeof(cmd_buf), "P2P_PEER FIRST");
|
||||
|
||||
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' ||
|
||||
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 (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) {
|
||||
|
||||
snprintk(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 ||
|
||||
strstr(peer_info,
|
||||
"[PROBE_REQ_ONLY]") == NULL)) {
|
||||
parse_peer_info_response(peer_info,
|
||||
mac,
|
||||
¶ms->peers[peer_idx]);
|
||||
peer_idx++;
|
||||
}
|
||||
}
|
||||
snprintk(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;
|
||||
}
|
||||
|
||||
snprintk(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]);
|
||||
snprintk(cmd_buf, sizeof(cmd_buf), "P2P_PEER %s", addr_str);
|
||||
|
||||
/* Use peer_info buffer for single peer query to avoid large resp_buf */
|
||||
ret = zephyr_wpa_cli_cmd_resp_noprint(wpa_s->ctrl_conn,
|
||||
cmd_buf, peer_info);
|
||||
if (ret < 0) {
|
||||
wpa_printf(MSG_ERROR, "P2P_PEER command failed: %d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
if (strncmp(peer_info, "FAIL", 4) == 0) {
|
||||
wpa_printf(MSG_ERROR, "Peer %s not found", addr_str);
|
||||
return -ENODEV;
|
||||
}
|
||||
parse_peer_info_response(peer_info, params->peer_addr,
|
||||
¶ms->peers[0]);
|
||||
params->peer_count = 1;
|
||||
}
|
||||
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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -100,6 +100,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);
|
||||
@@ -244,6 +247,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user