diff --git a/Android.mk b/Android.mk index 2761fcb..f99adb0 100644 --- a/Android.mk +++ b/Android.mk @@ -27,6 +27,14 @@ CFLAGS += -DCONFIG_WLANTEST OBJS += wlantest.c endif +### MIRACAST ### +OBJS += miracast.c +CFLAGS += -DMIRACAST +dhcpver = $(filter N%,$(PLATFORM_VERSION)) +dhcpver += $(filter 7.%,$(PLATFORM_VERSION)) +ifeq (,$(strip $(dhcpver))) + CFLAGS += -DMIRACAST_DHCP_M +endif CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" CFLAGS += -DSIGMA_TMPDIR=\"/data\" @@ -54,8 +62,14 @@ LOCAL_MODULE_TAGS := optional LOCAL_C_INCLUDES += \ $(LOCAL_PATH) frameworks/base/cmds/keystore system/security/keystore \ $(LOCAL_PATH) hardware/qcom/wlan/qcwcn/wifi_hal \ - $(LOCAL_PATH) hardware/libhardware_legacy/include/hardware_legacy + $(LOCAL_PATH) system/core/include/cutils \ + $(LOCAL_PATH) hardware/libhardware_legacy/include/hardware_legacy \ + $(TARGET_OUT_HEADERS)/common/inc LOCAL_SHARED_LIBRARIES := libc libcutils +ifneq (,$(strip $(dhcpver))) +LOCAL_SHARED_LIBRARIES += libnetutils +LOCAL_C_INCLUDES += $(LOCAL_PATH) system/core/include/netutils +endif LOCAL_SHARED_LIBRARIES += libhardware_legacy ifeq ($(BOARD_WLAN_DEVICE),qcwcn) ifneq ($(wildcard hardware/qcom/wlan/qcwcn/wifi_hal/nan_cert.h),) diff --git a/Makefile b/Makefile index 6af2a38..966b2a5 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,12 @@ CFLAGS += -DCONFIG_SERVER OBJS += server.o endif +ifdef MIRACAST +OBJS += miracast.o +CFLAGS += -DMIRACAST -DMIRACAST_DHCP_M +LIBS += -ldl +endif + sigma_dut: $(OBJS) $(LDO) $(LDFLAGS) -o sigma_dut $(OBJS) $(LIBS) diff --git a/cmds_reg.c b/cmds_reg.c index b4bc2ba..762d860 100644 --- a/cmds_reg.c +++ b/cmds_reg.c @@ -53,4 +53,9 @@ void sigma_dut_register_cmds(void) void server_register_cmds(void); server_register_cmds(); #endif /* CONFIG_SERVER */ + +#ifdef MIRACAST + void miracast_register_cmds(void); + miracast_register_cmds(); +#endif /* MIRACAST */ } diff --git a/dev.c b/dev.c index 7fc17fd..0ef3ce5 100644 --- a/dev.c +++ b/dev.c @@ -1,16 +1,25 @@ /* * Sigma Control API DUT (station/AP/sniffer) - * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2013, 2017, Qualcomm Atheros, Inc. * All Rights Reserved. * Licensed under the Clear BSD license. See README for more details. */ #include "sigma_dut.h" +#include "miracast.h" static int cmd_dev_send_frame(struct sigma_dut *dut, struct sigma_conn *conn, struct sigma_cmd *cmd) { +#ifdef MIRACAST + const char *program = get_param(cmd, "Program"); + + if (program && (strcasecmp(program, "WFD") == 0 || + strcasecmp(program, "DisplayR2") == 0)) + return miracast_dev_send_frame(dut, conn, cmd); +#endif /* MIRACAST */ + if (dut->mode == SIGMA_MODE_STATION || dut->mode == SIGMA_MODE_UNKNOWN) { sigma_dut_print(dut, DUT_MSG_DEBUG, "Convert " @@ -51,6 +60,21 @@ static int cmd_dev_set_parameter(struct sigma_dut *dut, struct sigma_conn *conn, } +static int cmd_dev_exec_action(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ +#ifdef MIRACAST + const char *program = get_param(cmd, "Program"); + + if (program && (strcasecmp(program, "WFD") == 0 || + strcasecmp(program, "DisplayR2") == 0)) + return miracast_dev_exec_action(dut, conn, cmd); +#endif /* MIRACAST */ + + return -2; +} + + static int req_intf_prog(struct sigma_cmd *cmd) { if (get_param(cmd, "interface") == NULL) @@ -66,4 +90,6 @@ void dev_register_cmds(void) sigma_dut_reg_cmd("dev_send_frame", req_intf_prog, cmd_dev_send_frame); sigma_dut_reg_cmd("dev_set_parameter", req_intf_prog, cmd_dev_set_parameter); + sigma_dut_reg_cmd("dev_exec_action", req_intf_prog, + cmd_dev_exec_action); } diff --git a/miracast.c b/miracast.c new file mode 100644 index 0000000..4d67f81 --- /dev/null +++ b/miracast.c @@ -0,0 +1,1589 @@ +/* + * Sigma Control API DUT - Miracast interface + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * All Rights Reserved. + * Licensed under the Clear BSD license. See README for more details. + * + * Implementation of MIRACAST specific functionality. + * For example, starting wfd session. +*/ + +#include "sigma_dut.h" +#include +#include +#include +#include "wpa_ctrl.h" +#include "wpa_helpers.h" +#include "miracast.h" +#ifdef ANDROID +#include "properties.h" +#ifndef MIRACAST_DHCP_M +#include +#endif /* MIRACAST_DHCP_M */ +#endif /* ANDROID */ + +#define HUNDRED_SECOND_TIMEOUT 100 /* 100 seconds */ +#define DHCP_LEASE_FILE_PATH "/data/misc/dhcp/dnsmasq.leases" +#define MIRACAST_CMD_LEN 512 + +extern char *sigma_main_ifname; +extern char *sigma_station_ifname; + +static int session_management_control_port = 7236; +/* Followingng stores p2p interface name after P2P group formation */ +static char wfd_ifname[32]; + + +#ifndef ANDROID + +static size_t strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} + + +static size_t strlcat(char *dst, const char *str, size_t size) +{ + char *pos; + size_t dstlen, srclen, copy; + + srclen = strlen(str); + for (pos = dst; pos - dst < size && *dst; pos++) + ; + dstlen = pos - dst; + if (*dst) + return dstlen + srclen; + if (dstlen + srclen + 1 > size) + copy = size - dstlen - 1; + else + copy = srclen; + memcpy(pos, str, copy); + pos[copy] = '\0'; + return dstlen + srclen; +} + +#endif /* ANDROID */ + + +static int miracast_load(struct sigma_dut *dut) +{ + static int once = 1; + + if (!once) + return 0; + + once = 0; + dlerror(); + dut->miracast_lib = dlopen(dut->miracast_lib_path ? + dut->miracast_lib_path : "libmiracast.so", + RTLD_LAZY); + if (!dut->miracast_lib) { + sigma_dut_print(dut, DUT_MSG_INFO, + "Fail to load Miracast library %s", + dlerror()); + return -1; + } + sigma_dut_print(dut, DUT_MSG_INFO, + "Miracast Wi-Fi Display library found - starting service"); + return 0; +} + + +static int miracast_unload(struct sigma_dut *dut) +{ + int err; + + if (!dut->miracast_lib) + return -1; + + dlerror(); + sigma_dut_print(dut, DUT_MSG_INFO, "Unloading Miracast library"); + err = dlclose(dut->miracast_lib); + dut->miracast_lib = NULL; + if (err == 0) { + sigma_dut_print(dut, DUT_MSG_INFO, + "Miracast library successfully unloaded"); + } else { + sigma_dut_print(dut, DUT_MSG_INFO, + "Failed to unload Miracast library"); + } + return err; +} + + +static int get_peer_ip_p2p_go(struct sigma_dut *dut, char *ipaddr, + const char *macaddr, unsigned int wait_limit) +{ + + FILE *fp; + + fp = fopen(DHCP_LEASE_FILE_PATH, "r"); + if (!fp) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Could not open DHCP lease file"); + return -1; + } + + sigma_dut_print(dut, DUT_MSG_INFO, "macaddress %s", macaddr); + while (wait_limit > 0) { + char line[100] = { 0 }; + char *str1 = NULL; + char *dummy_str = NULL; + char dummy_macaddress[32]; + int ip_found = 0; + int len; + + fseek(fp, 0, SEEK_SET); + while (fgets(line, sizeof(line), fp) != NULL) { + len = strlen(line); + if (len == 0) + continue; + + str1 = strtok_r(line, " ", &dummy_str); + if (str1 == NULL) + break; + + /* Look for mac address */ + str1 = strtok_r(NULL, " ", &dummy_str); + if (str1 == NULL) + break; + + strlcpy(dummy_macaddress, str1, + sizeof(dummy_macaddress)); + + /* Look for ip address */ + str1 = strtok_r(NULL, " ", &dummy_str); + if (str1 == NULL) + break; + + strlcpy(ipaddr,str1,32); + + sigma_dut_print(dut, DUT_MSG_INFO, + "Peer IP Address obtained and mac %s %s", + ipaddr, dummy_macaddress); + + /* + * The idea is that the p2p mac address may differ by 1 + * nibble mostly it is the first byte, hence try the + * middle two octets. + */ + if (strncasecmp(macaddr + 6, dummy_macaddress + 6, + 5) == 0) { + ip_found = 1; + sigma_dut_print(dut, DUT_MSG_INFO, + "Obtained the IP address %s", + ipaddr); + break; + } + } + + if (ip_found) + break; + + sigma_dut_print(dut, DUT_MSG_INFO, + "Failed to find IP from DHCP lease file"); + sleep(1); + wait_limit--; + } + fclose(fp); + return 0; +} + + +static int miracast_start_dhcp_client(struct sigma_dut *dut, const char *ifname) +{ +#ifdef MIRACAST_DHCP_M + start_dhcp(dut, ifname, 0); +#else /* MIRACAST_DHCP_M */ + int ret = ifc_init(); + + sigma_dut_print(dut, DUT_MSG_DEBUG, "ifc init returned %d", ret); + ret = do_dhcp((char *) ifname); + sigma_dut_print(dut, DUT_MSG_DEBUG, "do dhcp returned %d", ret); +#endif /* MIRACAST_DHCP_M */ + return 0; +} + + +static void miracast_stop_dhcp_client(struct sigma_dut *dut, char *ifname) +{ +#ifdef MIRACAST_DHCP_M + stop_dhcp(dut, ifname, 0); +#else /* MIRACAST_DHCP_M */ + ifc_close(); +#endif /* MIRACAST_DHCP_M */ +} + + +#ifdef MIRACAST_DHCP_M + +static int get_local_ip_address(struct sigma_dut *dut, + char *local_ip_addr, size_t buflen, + const char *intf, int size) +{ + struct ifreq ifr; + int s; + + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, intf, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + sigma_dut_print(dut, DUT_MSG_INFO, + "%s: Error in creating socket", __func__); + return -1; + } + + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { + sigma_dut_print(dut, DUT_MSG_INFO, "ioctl failed: %s", + strerror(errno)); + close(s); + return -1; + } + + strlcpy(local_ip_addr, + inet_ntoa(((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr), + buflen); + close(s); + return 0; +} + + +static int get_peer_ip_p2p_client(struct sigma_dut *dut, char *ip_addr, + const char *intf, unsigned int wait_limit) +{ + char prop_name[128]; + char prop_name_self[128]; + char self_ip[128]; + + memset(self_ip, 0, sizeof(self_ip)); + /* For P2P Client read the server property */ + snprintf(prop_name, sizeof(prop_name), "%s.%s.server", "dhcp", "p2p"); + snprintf(prop_name_self, sizeof(prop_name_self), + "%s.%s.ipaddress", "dhcp", "p2p"); + + while (wait_limit > 0) { +#ifdef ANDROID + property_get(prop_name, ip_addr, NULL); +#else /* ANDROID */ + ip_addr[0] = '\0'; +#endif /* ANDROID */ + get_local_ip_address(dut, self_ip, sizeof(self_ip), intf, 20); + sigma_dut_print(dut, DUT_MSG_INFO, "Peer IP, self IP: %s %s", + ip_addr, self_ip); + if (strlen(ip_addr) > 8 && + ip_addr[0] == '1' && ip_addr[1] == '9' && + self_ip[0] == '1' && self_ip[1] == '9') + break; /* connected */ + + /* What if DHCP server was started before Client was started? + * Request DHCP yet again */ + miracast_start_dhcp_client(dut, intf); + sleep(5); /* Sleep always helps */ + wait_limit--; + } + + return wait_limit > 0 ? 0 : -1; +} + +#else /* MIRACAST_DHCP_M */ + +extern void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, + uint32_t *prefixLength, uint32_t *dns1, + uint32_t *dns2, uint32_t *server, + uint32_t *lease); + +extern int do_dhcp(char *); + +const char *ipaddr (in_addr_t addr) +{ + struct in_addr in_addr; + in_addr.s_addr = addr; + return inet_ntoa(in_addr); +} + + +static int get_peer_ip_p2p_client(struct sigma_dut *dut, char *ipAddr, + const char *intf, unsigned int wait_limit) +{ + uint32_t ipaddress, gateway, prefixLength, + dns1, dns2, serveraddr, lease; + + get_dhcp_info(&ipaddress, &gateway, &prefixLength, &dns1, &dns2, + &serveraddr, &lease); + while (wait_limit > 0) { + sigma_dut_print(dut, DUT_MSG_INFO, "Peer IP: %u", ipaddress); + if (strlen(ipaddr(serveraddr)) > 8) { + /* connected */ + strncpy(ipAddr, ipaddr(serveraddr), 16); + break; + } + sleep(1); + wait_limit--; + } + return wait_limit == 0 ? -1 : 0; +} + +#endif /* MIRACAST_DHCP_M */ + + +static int get_p2p_connection_event(struct sigma_dut *dut, + const char *input_intf, + char *output_intf, + int size_output_intf, + int *is_group_owner) +{ + /* + * Poll for the P2P Connection + * Then poll for IP + * Then poll for WFD session ID and exit + * P2P connection done + * Loop till connection is ready + */ + struct wpa_ctrl *ctrl; + char *mode_string; + char event_buf[256]; + char *ifname; + char *pos; + int res = 0; + const char *events[] = { + "P2P-GROUP-STARTED", + "P2P-GO-NEG-FAILURE", + "P2P-GROUP-FORMATION-FAILURE", + NULL + }; + + /* Wait for WPA CLI EVENTS */ + /* Default timeout is 120s */ + ctrl = open_wpa_mon(input_intf); + if (!ctrl) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Failed to open wpa_supplicant monitor connection"); + return -1; + } + + res = get_wpa_cli_events(dut, ctrl, events, event_buf, + sizeof(event_buf)); + + wpa_ctrl_detach(ctrl); + wpa_ctrl_close(ctrl); + + if (res < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Group formation did not complete"); + return -1; + } + + sigma_dut_print(dut, DUT_MSG_DEBUG, "Received event %s", event_buf); + + if (strstr(event_buf, "P2P-GROUP-FORMATION-FAILURE") || + strstr(event_buf, "P2P-GO-NEG-FAILURE")) + return -1; + + sigma_dut_print(dut, DUT_MSG_INFO, "P2P connection done"); + ifname = strchr(event_buf, ' '); + if (!ifname) { + sigma_dut_print(dut, DUT_MSG_INFO, "No P2P interface found"); + return -1; + } + ifname++; + pos = strchr(ifname, ' '); + if (!pos) { + sigma_dut_print(dut, DUT_MSG_ERROR, "No P2P interface found"); + return -1; + } + *pos++ = '\0'; + sigma_dut_print(dut, DUT_MSG_DEBUG, "Group interface %s", ifname); + + strlcpy(output_intf, ifname, size_output_intf); + + mode_string = pos; + pos = strchr(mode_string, ' '); + if (!pos) { + sigma_dut_print(dut, DUT_MSG_ERROR, "No group role found"); + return -1; + } + + *pos++ = '\0'; + sigma_dut_print(dut, DUT_MSG_DEBUG, "Group Role %s", mode_string); + + if (strcmp(mode_string, "GO") == 0) + *is_group_owner = 1; + sigma_dut_print(dut, DUT_MSG_DEBUG, "Value of is_group_owner %d", + *is_group_owner); + return 0; +} + + +/* Following serves as an entry point function to perform rtsp tasks */ +static void * miracast_rtsp_thread_entry(void *ptr) +{ + struct sigma_dut *dut = ptr; + char output_ifname[16]; + int is_group_owner = 0; + const char *intf = sigma_station_ifname; + unsigned int wait_limit; + char peer_ip_address[32]; + char rtsp_session_id[12]; + int (*extn_start_wfd_connection)(const char *, + const char *, /* Peer IP */ + int, /* RTSP port number */ + int, /* WFD Device Type; 0-Source, + 1-P-Sink, 2-Secondary Sink */ + char *); /* for returning session ID */ + + if (!dut) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Bail out, RTSP thread has invalid parameters"); + goto EXIT; + } + + miracast_load(dut); + + if (sigma_main_ifname) { + intf = sigma_main_ifname; + sigma_dut_print(dut, DUT_MSG_DEBUG, + "miracast_rtsp_thread_entry: sigma_main_ifname = [%s]", + intf); + } else { + sigma_dut_print(dut, DUT_MSG_DEBUG, + "miracast_rtsp_thread_entry: sigma_main_ifname is NULL"); + } + + if (get_p2p_connection_event(dut, intf, output_ifname, + sizeof(output_ifname), + &is_group_owner) < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, "P2P connection failure"); + goto EXIT; + } + + sigma_dut_print(dut, DUT_MSG_DEBUG, "Waiting to start dhcp"); + + /* Calling WFD APIs now */ + /* If you are a source, go ahead and start the RTSP server */ + if (dut->wfd_device_type != 0) + wait_limit = HUNDRED_SECOND_TIMEOUT; + else + wait_limit = 500; + + if (!is_group_owner) { + sigma_dut_print(dut, DUT_MSG_INFO, + "Waiting to start dhcp client"); + sleep(5); /* Wait for IP */ + miracast_start_dhcp_client(dut, output_ifname); + sleep(5); /* Wait for IP */ + if (get_peer_ip_p2p_client(dut, peer_ip_address, output_ifname, + wait_limit) < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Could not get peer IP"); + goto EXIT; + } + } else { + stop_dhcp(dut, output_ifname, 1); + /* For GO read the DHCP Lease File */ + sigma_dut_print(dut, DUT_MSG_INFO, + "Waiting to start dhcp server"); + start_dhcp(dut, output_ifname, 1); + sleep(5); + if (get_peer_ip_p2p_go(dut, peer_ip_address, + dut->peer_mac_address, wait_limit) < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Could not get peer IP"); + goto EXIT; + } + } + + extn_start_wfd_connection = dlsym(dut->miracast_lib, + "start_wfd_connection"); + if (extn_start_wfd_connection) { + extn_start_wfd_connection(NULL, peer_ip_address, + session_management_control_port, + 1 - dut->wfd_device_type, + rtsp_session_id); + } else { + sigma_dut_print(dut, DUT_MSG_INFO, + "dlsym seems to have error %p %p", + dut->miracast_lib, extn_start_wfd_connection); + } + +EXIT: + sigma_dut_print(dut, DUT_MSG_INFO, "Reached Miracast thread exit"); + + return NULL; +} + + +/*---------------------------------------------------------------------- + WFD Source IE: 000601101c440036 + len WFD device info control port throughput + 110] [00000 00100010 000] [00011 10001000 100] [00000 00000110 110] + = 7236 + + WFD Sink IE: 000601511c440036 + len WFD device info control port throughput + 110] [00000 00101010 001] [00011 10001000 100] [00000 00000110 110] + = 7236 + + WFD device info: + BITS NAME DESCRIPTION + ------------------------------------------- + 1:0 WFD Device Type 0b00: WFD Source + 0b01: Primary Sink + 0b10: Secondary Sink + 0b11: Dual Role, either WFD Source/Primary sink + + 5:4 WFD Session 0b00: Not available for WFD Session + Availibility 0b01: Available for WFD Session + 0b10, 0b11: Reserved + + 6 WSD Support Bit 0b0: WFD Service Discovery not supported + 0b1: WFD Service Discovery supported + + 8 CP Support Bit 0b0: Content Protection via HDCP not supported + 0b1: Content Protection via HDCP supported +--------------------------------------------------------------------------- +*/ + +static void miracast_set_wfd_ie(struct sigma_dut *sigma_dut) +{ + char *intf = sigma_station_ifname; + + if (sigma_main_ifname != NULL) + intf = sigma_main_ifname; + + sigma_dut_print(sigma_dut, DUT_MSG_DEBUG, "miracast_set_wfd_ie() = intf = %s", + intf); + wpa_command(intf, "SET wifi_display 1"); + + if (sigma_dut->wfd_device_type == 0) { + wpa_command(intf, "WFD_SUBELEM_SET 0 000601101c440036"); + wpa_command(intf, "WFD_SUBELEM_SET 11 00020000"); + } else { + wpa_command(intf, "WFD_SUBELEM_SET 0 000601511c440036"); + wpa_command(intf, "WFD_SUBELEM_SET 11 00020001"); + } +} + + +void miracast_init(struct sigma_dut *dut) +{ + sigma_dut_print(dut, DUT_MSG_DEBUG, "Create thread pool for VDS"); + miracast_set_wfd_ie(dut); + sigma_dut_print(dut, DUT_MSG_DEBUG, "Clear groupID @ start"); +} + + +void miracast_deinit(struct sigma_dut *dut) +{ + (void) miracast_unload(dut); +} + + +static void miracast_generate_string_cmd(struct sigma_cmd *cmd, char *strcmd, + size_t size) +{ + int i = 0; + char *pos, *end; + int ret; + + if (!strcmd) + return; + strcmd[0] = '\0'; + pos = strcmd; + end = strcmd + size; + for (i = 0; i < cmd->count; i++) { + ret = snprintf(pos, end - pos, "%s,%s,", cmd->params[i], + cmd->values[i]); + if (ret < 0 || ret < end - pos) + break; + pos += ret; + } + + pos = strrchr(strcmd, ','); + if (pos) + *pos = '\0'; + printf("Miracast: generated command: %s\n", strcmd); +} + + +static void * auto_go_thread_entry(void *ptr) +{ + struct sigma_dut *dut = ptr; + struct wpa_ctrl *ctrl; + char event_buf[64]; + char *peer = NULL; + int res = 0; + char macaddress[32]; + char peer_ip_address[32]; + char rtsp_session_id[12]; + int (*extn_start_wfd_connection)(const char *, + const char *, /* Peer IP */ + int, /* RTSP port number */ + int, /* WFD Device Type; 0-Source, + 1-P-Sink, 2-Secondary Sink */ + char *); /* for returning session ID */ + + if (!dut) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Bail out, RTSP thread has invalid parameters"); + goto THR_EXIT; + } + stop_dhcp(dut, wfd_ifname, 1); + /* For auto-GO, start the DHCP server and wait for 5 seconds */ + start_dhcp(dut, wfd_ifname, 1); + sleep(5); /* Wait for IP */ + + sigma_dut_print(dut, DUT_MSG_INFO, "Wait for AP-STA-CONNECTED"); + ctrl = open_wpa_mon(wfd_ifname); /* Refer to wfd_ifname */ + if (!ctrl) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Failed to open wpa_supplicant monitor connection"); + goto THR_EXIT; + } + res = get_wpa_cli_event(dut, ctrl, "AP-STA-CONNECTED", + event_buf, sizeof(event_buf)); + wpa_ctrl_detach(ctrl); + wpa_ctrl_close(ctrl); + + if (res < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Could not get event before timeout"); + goto THR_EXIT; + } + + sigma_dut_print(dut, DUT_MSG_DEBUG, "STA Connected Event: '%s'", + event_buf); + peer = strchr(event_buf, ' '); + if (!peer) { + sigma_dut_print(dut, DUT_MSG_ERROR, "Could not find STA MAC"); + goto THR_EXIT; + } + + peer++; + strncpy(macaddress, peer, 17 /* Size of MAC */); + macaddress[17] = '\0'; + if (get_peer_ip_p2p_go(dut, peer_ip_address, macaddress, 30) < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, "Could not get peer IP"); + goto THR_EXIT; + } + + sigma_dut_print(dut, DUT_MSG_INFO, "dlsym %p", dut->miracast_lib); + extn_start_wfd_connection = dlsym(dut->miracast_lib, + "start_wfd_connection"); + if (!extn_start_wfd_connection) + sigma_dut_print(dut, DUT_MSG_INFO, "dlsym function NULL"); + else + extn_start_wfd_connection(NULL, peer_ip_address, + session_management_control_port, + 1 - dut->wfd_device_type, + rtsp_session_id); + +THR_EXIT: + sigma_dut_print(dut, DUT_MSG_INFO, "Reached auto GO thread exit"); + return NULL; +} + + +void miracast_sta_reset_default(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + char *intf = sigma_station_ifname; + int (*extn_sta_reset_default)(char *); + char string_cmd[MIRACAST_CMD_LEN] = { 0 }; + + if (sigma_main_ifname != NULL) + intf = sigma_main_ifname; + sigma_dut_print(dut, DUT_MSG_DEBUG, + "miracast_sta_reset_default() = intf = %s", intf); + stop_dhcp(dut, intf, 1); /* IFNAME argument is ignored */ + miracast_stop_dhcp_client(dut, intf); + + /* This is where vendor Miracast library is loaded and function pointers + * to Miracast functions (defined by CAPI) are loaded. */ + + if (miracast_load(dut) != 0) { + sigma_dut_print(dut, DUT_MSG_INFO, + "Fail to load Miracast library"); + return; + } + + if (!dut->miracast_lib) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Miracast library is absent"); + return; + } + + dlerror(); + + miracast_generate_string_cmd(cmd, string_cmd, sizeof(string_cmd)); + extn_sta_reset_default = dlsym(dut->miracast_lib, "sta_reset_default"); + if (extn_sta_reset_default) + extn_sta_reset_default(string_cmd); + + /* delete threads if any */ + /* TODO: if dut->rtsp_thread_handle running, call + * miracast_release_rtsp_thread_resources(dut); */ +} + + +void miracast_start_autonomous_go(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd, char *ifname) +{ + strlcpy(wfd_ifname, ifname, sizeof(wfd_ifname)); + (void) pthread_create(&dut->rtsp_thread_handle, NULL, + auto_go_thread_entry, dut); +} + + +static void miracast_rtsp_thread_create(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + (void) pthread_create(&dut->rtsp_thread_handle, NULL, + miracast_rtsp_thread_entry, dut); +} + + +int miracast_dev_send_frame(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *frame_name = get_param(cmd, "FrameName"); + /* const char *source = get_param(cmd, "Source"); */ + /* const char *destination = get_param(cmd, "Destination"); */ + /* const char *dev_type = get_param(cmd, "DevType"); */ + const char *rtsp_msg_type = get_param(cmd, "RtspMsgType"); + /* const char *wfd_session_id = get_param(cmd, "WfdSessionID"); */ + int (*dev_send_frame)(const char *); + char string_cmd[MIRACAST_CMD_LEN] = { 0 }; + + dev_send_frame = dlsym(dut->miracast_lib, "dev_send_frame"); + if (!dev_send_frame) + return -1; + sigma_dut_print(dut, DUT_MSG_DEBUG, "miracast_dev_send_frame 1"); + miracast_generate_string_cmd(cmd, string_cmd, sizeof(string_cmd)); + if (strcasecmp(frame_name, "RTSP") != 0) + return 0; + + if (strcasecmp(rtsp_msg_type, "PAUSE") == 0 || + strcasecmp(rtsp_msg_type, "TRIGGER-PAUSE") == 0) { + /* Call RTSP Pause */ + dev_send_frame(string_cmd); + return 1; + } + + if (strcasecmp(rtsp_msg_type, "PLAY") == 0 || + strcasecmp(rtsp_msg_type, "TRIGGER-PLAY") == 0) { + /* Call RTSP Play */; + dev_send_frame(string_cmd); /* Not for secure playback */ + return 1; + } + + if (strcasecmp(rtsp_msg_type, "TEARDOWN") == 0 || + strcasecmp(rtsp_msg_type, "TRIGGER-TEARDOWN") == 0) { + dev_send_frame(string_cmd); /* RTSP Teardown */ + return 1; + } + + if (strcasecmp(rtsp_msg_type,"SET_PARAMETER") == 0) { + const char *set_parameter = get_param(cmd, "SetParameter"); + const char *transportType = get_param(cmd, "TransportType"); + + if (set_parameter == NULL && transportType == NULL) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Invalid Set Parameter value"); + return 0; + } + + if (1) /* (strcasecmp(set_parameter, "Standby") == 0) */ { + dev_send_frame(string_cmd); + return 1; + } + /* TODO More needs to be implemented when the spec is clearer */ + return 1; + } + + if (strcasecmp(rtsp_msg_type, "SETUP") == 0) { + dev_send_frame(string_cmd); + /* TODO More needs to be implemented when the spec is clearer */ + return 1; + } + + if (strcasecmp(frame_name, "WFD_ProbeReq") == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Unsupported WFD Probe Request"); + return 0; + } + + if (strcasecmp(frame_name, "WFD_ServiceDiscReq") == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Unsupported WFD Service Discovery"); + return 0; + } + + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Unsupported dev_send_frame"); + return 0; +} + + +int miracast_dev_exec_action(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *service_type = get_param(cmd,"ServiceType"); + int (*dev_exec_action)(const char *); + char string_cmd[MIRACAST_CMD_LEN] = { 0 }; + + sigma_dut_print(dut, DUT_MSG_DEBUG, "miracast_dev_exec_frame"); + + if (service_type) { + char resp_buf[128]; + + sigma_dut_print(dut, DUT_MSG_DEBUG, "MDNS Instance Name = %s", + dut->mdns_instance_name); + strlcpy(resp_buf, "InstanceName,", sizeof(resp_buf)); + strlcat(resp_buf + strlen(resp_buf), dut->mdns_instance_name, + sizeof(resp_buf) - strlen(resp_buf)); + send_resp(dut, conn, SIGMA_COMPLETE, resp_buf); + return 0; + } + + miracast_generate_string_cmd(cmd, string_cmd, sizeof(string_cmd)); + dev_exec_action = dlsym(dut->miracast_lib, "dev_exec_action"); + if (!dev_exec_action) + return -2; + return dev_exec_action(string_cmd); +} + + +int miracast_preset_testparameters(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *mdns_disc = get_param(cmd, "mdns_disc"); + const char *mdns_role = get_param(cmd, "mdns_role"); + char string_cmd[MIRACAST_CMD_LEN]; + int ret = 0; + char string_resp[64] = { 0 }; + int (*extn_sta_preset_test_parameter)(const char *, char *, int); + + miracast_load(dut); + miracast_generate_string_cmd(cmd, string_cmd, sizeof(string_cmd)); + extn_sta_preset_test_parameter = + dlsym(dut->miracast_lib, "sta_preset_testparameters"); + if (!extn_sta_preset_test_parameter) + return -1; + ret = extn_sta_preset_test_parameter(string_cmd, string_resp, + sizeof(string_resp)); + if (ret == SIGMA_ERROR) { + send_resp(dut, conn, SIGMA_ERROR, + "Miracast extension reported error in the command sta_preset_testparameters"); + return 0; + } + + if (mdns_disc && mdns_role) { + if (strlen(string_resp)) + strlcpy(dut->mdns_instance_name, string_resp, + sizeof(dut->mdns_instance_name)); + else + dut->mdns_instance_name[0] = '\0'; + } + + return 1; +} + + +static int get_p2p_peers(char *respbuf, size_t bufsize) +{ + char addr[1024], cmd[64]; + char *intf = get_main_ifname(); + int ret; + char *pos, *end; + + pos = respbuf; + end = respbuf + bufsize; + + if (wpa_command_resp(intf, "P2P_PEER FIRST", addr, 128) >= 0) { + strlcpy(respbuf, addr, bufsize); + respbuf[17] = '\0'; + addr[17] = '\0'; + snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr); + memset(addr, 0, sizeof(addr)); + while (wpa_command_resp(intf, cmd, addr, sizeof(addr)) >= 0) { + if (memcmp(addr, "FAIL", 4) == 0) + break; + addr[17] = '\0'; + ret = snprintf(pos, end - pos, " %s", addr); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr); + memset(addr, 0, sizeof(addr)); + } + } + + return 0; +} + + +int miracast_cmd_sta_get_parameter(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + /* const char *intf = get_param(cmd, "Interface"); */ + /* const char *program = get_param(cmd, "Program"); */ + const char *parameter = get_param(cmd, "Parameter"); + char resp_buf[1024]; /* may need to change depending on number of peer + devices found */ + + if (!parameter) { + send_resp(dut, conn, SIGMA_COMPLETE, "NULL"); + return 0; + } + + if (strcasecmp(parameter, "DiscoveredDevList") == 0) { + int len = strlen("DeviceList,"); + + snprintf(resp_buf, sizeof(resp_buf), "DeviceList,"); + get_p2p_peers(resp_buf + len, 1024 - len); + } else { + send_resp(dut, conn, SIGMA_ERROR, "Invalid Parameter"); + return 0; + } + + send_resp(dut, conn, SIGMA_COMPLETE, resp_buf); + return 0; +} + + +int miracast_mdns_start_wfd_connection(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *init_wfd = get_param(cmd, "init_wfd"); + int int_init_wfd = -1; + char rtsp_session_id[12]; + int (*extn_start_wfd_connection)(const char *, + const char *, /* Peer IP */ + int, /* RTSP port number */ + int, /* WFD Device Type; 0-Source, + 1-P-Sink, 2-Secondary Sink */ + char *); /* for returning session ID */ + int count = 0; + char *sig_resp = NULL; + + if (init_wfd) + int_init_wfd = atoi(init_wfd); + + extn_start_wfd_connection = dlsym(dut->miracast_lib, + "start_wfd_connection"); + if (!extn_start_wfd_connection) + return -1; + rtsp_session_id[0] = '\0'; + if (int_init_wfd != 0) { + extn_start_wfd_connection(NULL, NULL, -100, + 1 - dut->wfd_device_type, + rtsp_session_id); + while (strlen(rtsp_session_id) == 0 && count < 60) { + count++; + sleep(1); + } + if (count == 60) + strlcpy(rtsp_session_id, "-1", 12); + sig_resp = rtsp_session_id; + } else { + extn_start_wfd_connection(NULL, NULL, -100, + 1 - dut->wfd_device_type, NULL); + sig_resp = "result,NULL,GroupID,NULL,WFDSessionID,NULL"; + } + + send_resp(dut, conn, SIGMA_COMPLETE, sig_resp); + return 0; +} + + +static int cmd_start_wfd_connection(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *intf = get_param(cmd, "Interface"); + const char *peer_address = get_param(cmd, "PeerAddress"); + const char *init_wfd = get_param(cmd, "init_wfd"); + const char *intent_val = get_param(cmd, "intent_val"); + const char *oper_chan = get_param(cmd, "oper_chn"); + const char *coupled_session = get_param(cmd, "coupledSession"); + const char *tdls = get_param(cmd, "TDLS"); + const char *r2_connection = get_param(cmd, "R2ConnectionType"); + char ssid[128]; + char p2p_dev_address[18]; + char output_intf[16]; + char sig_resp_buf[1024]; + char cmd_buf[256]; /* Command buffer */ + char resp_buf[256]; /* Response buffer to UCC */ + int go_intent = 0; + int freq; + int res = 0; + char buf_peer[4096]; + char *availability = NULL; + char command[64]; + int avail_bit; + char ctemp[2]; + char rtspport[5] = { '7', '2', '3', '6', '\0' }; + int is_group_owner = 0; + char peer_ip_address[32]; + int sm_control_port = 7236; + char rtsp_session_id[12] = { '\0' }; + int (*extn_start_wfd_connection)(const char *, + const char *, /* Peer IP */ + int, /* RTSP port number */ + int, /* WFD Device Type; 0-Source, + 1-P-Sink, 2-Secondary Sink */ + char *); /* for returning session ID */ + int count = 0; + + if (r2_connection) { + if (strcasecmp(r2_connection, "Infrastructure") == 0) + return miracast_mdns_start_wfd_connection(dut, conn, + cmd); + } + + if (coupled_session && atoi(coupled_session) == 1) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Coupled Session is unsupported"); + return 0; + } + + if (tdls && atoi(tdls) == 1) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,TDLS is unsupported"); + return 0; + } + + if (intent_val) { + go_intent = atoi(intent_val); + if (go_intent > 15) + go_intent = 1; + } + + if (p2p_discover_peer(dut, intf, peer_address, 1) < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Could not find peer"); + return 0; + } + + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_CONNECT %s", peer_address); + + switch (dut->wps_method) { + case WFA_CS_WPS_PIN_DISPLAY: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pin display"); + break; + case WFA_CS_WPS_PIN_LABEL: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pin label"); + break; + case WFA_CS_WPS_PIN_KEYPAD: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " %s keypad", + dut->wps_pin); + break; + case WFA_CS_WPS_PBC: + default: /* Configuring default to PBC */ + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pbc"); + break; + } + + snprintf(cmd_buf + strlen(cmd_buf), sizeof(cmd_buf) - strlen(cmd_buf), + " go_intent=%d", go_intent); + + if (init_wfd && atoi(init_wfd) == 0) { + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " auth"); + } + + if (oper_chan) { + int chan; + + chan = atoi(oper_chan); + if (chan >= 1 && chan <= 13) + freq = 2407 + chan * 5; + else if (chan == 14) + freq = 2484; + else + freq = 5000 + chan * 5; + + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " freq=%d", freq); + } + + /* WFD SESSION AVAILABILITY CHECK */ + + memset(buf_peer, 0, sizeof(buf_peer)); + snprintf(command, sizeof(command), "P2P_PEER %s", peer_address); + strlcpy(dut->peer_mac_address, peer_address, + sizeof(dut->peer_mac_address)); + wpa_command_resp(intf, command, buf_peer, sizeof(buf_peer)); + + if (strlen(buf_peer) != 0) + availability = strstr(buf_peer, "wfd_subelems="); + + if (!availability || strlen(availability) < 21) { + sigma_dut_print(dut, DUT_MSG_INFO, "Did not get WFD SUBELEMS"); + send_resp(dut, conn, SIGMA_COMPLETE, + "result,NULL,GroupID,NULL,WFDSessionID,NULL"); + return 0; + } + + /* Extracting Availability Bit */ + ctemp[0] = availability[21]; + ctemp[1] = '\0'; + avail_bit = (int) strtol(ctemp, NULL, 16); + + if ((avail_bit & 0x3) == 0) { + send_resp(dut, conn, SIGMA_COMPLETE, + "result,NULL,GroupID,NULL,WFDSessionID,NULL"); + return 0; + } + + /* Extract RTSP Port for Sink */ + + if (dut->wfd_device_type != 0) { + if (strlen(availability) >= 23) { + availability += 23; + if (availability[0]) + snprintf(rtspport, 5, "%s", availability); + } + sigma_dut_print(dut, DUT_MSG_INFO, + "rtsp_port = %s, availability = %s ", + rtspport, availability); + session_management_control_port = (int) strtol(rtspport, NULL, + 16); + sigma_dut_print(dut, DUT_MSG_INFO, + "SessionManagementControlPort = %d", + session_management_control_port); + } + + memset(resp_buf, 0, sizeof(resp_buf)); + res = wpa_command_resp(intf, cmd_buf, resp_buf, sizeof(resp_buf)); + if (strncmp(resp_buf, "FAIL", 4) == 0) { + sigma_dut_print(dut, DUT_MSG_INFO, + "wpa_command: Command failed (FAIL received)"); + return 1; + } + + if (init_wfd && atoi(init_wfd) == 0) { + /* Start thread to wait for P2P connection */ + miracast_rtsp_thread_create(dut, conn, cmd); + send_resp(dut, conn, SIGMA_COMPLETE, + "result,NULL,GroupID,NULL,WFDSessionID,NULL"); + return 0; + } + + res = get_p2p_connection_event(dut, intf, output_intf, + sizeof(output_intf), &is_group_owner); + sigma_dut_print(dut, DUT_MSG_DEBUG, "p2p connection done %d", + is_group_owner); + if (res < 0) { + sigma_dut_print(dut, DUT_MSG_ERROR, + "Group Formation did not complete"); + return 1; + } + + snprintf(sig_resp_buf, sizeof(sig_resp_buf), "result"); + + if (is_group_owner) { + stop_dhcp(dut, output_intf,1); + snprintf(sig_resp_buf + strlen(sig_resp_buf), + sizeof(sig_resp_buf) - strlen(sig_resp_buf), ",GO"); + start_dhcp(dut, output_intf,1); + sleep(5); + } else { + snprintf(sig_resp_buf + strlen(sig_resp_buf), + sizeof(sig_resp_buf) - strlen(sig_resp_buf), + ",CLIENT"); + miracast_start_dhcp_client(dut, output_intf); + sleep(5); + } + + snprintf(sig_resp_buf + strlen(sig_resp_buf), + sizeof(sig_resp_buf) - strlen(sig_resp_buf), ",GroupID,"); + + res = get_wpa_status(output_intf, "p2p_device_address", + p2p_dev_address, sizeof(p2p_dev_address)); + if (res < 0) + return -1; + sigma_dut_print(dut, DUT_MSG_INFO, "p2p_dev_address %s", + p2p_dev_address); + strlcpy(sig_resp_buf + strlen(sig_resp_buf), p2p_dev_address, + sizeof(sig_resp_buf) - strlen(sig_resp_buf)); + + res = get_wpa_status(output_intf, "ssid", ssid, sizeof(ssid)); + if (res < 0) { + sigma_dut_print(dut, DUT_MSG_DEBUG, + "get_wpa_status failed to get ssid"); + return -1; + } + + snprintf(sig_resp_buf + strlen(sig_resp_buf), + sizeof(sig_resp_buf) - strlen(sig_resp_buf), + " %s,WFDSessionId,", ssid); + + if (!is_group_owner) { + if (get_peer_ip_p2p_client(dut, peer_ip_address, output_intf, + 60) < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "Could not get remote IP"); + return 0; + } + } else { + if (get_peer_ip_p2p_go(dut, peer_ip_address, peer_address, + 30) < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "Could not get remote IP"); + return 0; + } + } + + if (dut->wfd_device_type != 0) + sm_control_port = (int) strtol(rtspport, NULL, 16); + else + sm_control_port = 7236; + + extn_start_wfd_connection = dlsym(dut->miracast_lib, + "start_wfd_connection"); + if (!extn_start_wfd_connection) + return -1; + extn_start_wfd_connection(NULL, peer_ip_address, sm_control_port, + 1 - dut->wfd_device_type, rtsp_session_id); + + while (strlen(rtsp_session_id) == 0 && count < 60) { + count++; + sleep(1); + } + + if (count == 60) + strlcpy(rtsp_session_id, "00000000", sizeof(rtsp_session_id)); + + strlcat(sig_resp_buf, rtsp_session_id, + sizeof(sig_resp_buf) - strlen(sig_resp_buf)); + send_resp(dut, conn, SIGMA_COMPLETE, sig_resp_buf); + return 0; +} + + +static int cmd_connect_go_start_wfd(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *intf = get_param(cmd, "Interface"); + const char *p2p_dev_id = get_param(cmd, "P2PdevID"); + /* const char *p2p_group_id = get_param(cmd, "GroupID"); */ + char sig_resp_buf[1024]; + char method[12]; + char cmd_buf[256]; + char buf[256]; + char resp_buf[256]; + int go = 0; + int res = 0; + char output_ifname[32]; + char peer_ip_address[32]; + char rtsp_session_id[12]; + int (*extn_connect_go_start_wfd)(const char *, + const char * /* Peer IP */, + int /* RTSP port number */, + int /* WFD Device Type; 0-Source, + 1-P-Sink, 2-Secondary Sink */, + char *); /* for returning session ID */ + + snprintf(cmd_buf, sizeof(cmd_buf), "P2P_CONNECT %s", p2p_dev_id); + + switch (dut->wps_method) { + case WFA_CS_WPS_PBC: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pbc"); + strlcpy(method, "pbc", sizeof(method)); + break; + case WFA_CS_WPS_PIN_DISPLAY: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pin display"); + strlcpy(method, "display", sizeof(method)); + break; + case WFA_CS_WPS_PIN_LABEL: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pin label"); + strlcpy(method, "label", sizeof(method)); + break; + case WFA_CS_WPS_PIN_KEYPAD: + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " %s keypad", + dut->wps_pin); + strlcpy(method, "keypad", sizeof(method)); + break; + default: /* Configuring to PBC */ + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " pbc"); + strlcpy(method, "pbc", sizeof(method)); + break; + } + snprintf(cmd_buf + strlen(cmd_buf), + sizeof(cmd_buf) - strlen(cmd_buf), " join"); + + /* run provisional discovery */ + if (p2p_discover_peer(dut, intf, p2p_dev_id, 0) < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "ErrorCode,Could not discover the requested peer"); + return 0; + } + + snprintf(buf, sizeof(buf), "P2P_PROV_DISC %s %s", p2p_dev_id, method); + if (wpa_command(intf, buf) < 0) { + sigma_dut_print(dut, DUT_MSG_INFO, + "Failed to send provision discovery request"); + return -2; + } + + res = wpa_command_resp(intf, cmd_buf, resp_buf, sizeof(resp_buf)); + if (strncmp(resp_buf, "FAIL", 4) == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,failed P2P connection"); + return 0; + } + + res = get_p2p_connection_event(dut, intf, output_ifname, + sizeof(output_ifname), &go); + if (res < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,failed P2P connection"); + return 0; + } + + snprintf(sig_resp_buf + strlen(sig_resp_buf), + sizeof(sig_resp_buf) - strlen(sig_resp_buf), "WFDSessionId,"); + + miracast_start_dhcp_client(dut, output_ifname); + + if (get_peer_ip_p2p_client(dut, peer_ip_address, output_ifname, + 30) < 0) { + send_resp(dut, conn, SIGMA_ERROR, "Could not get remote IP"); + return 0; + } + + if (dut->wfd_device_type != 0) { + char rtsp_buff[1000], cmd_buff[32]; + char *sub_elem = NULL; + char rtspport[5] = { '7', '2', '3', '6', '\0' }; + + sigma_dut_print(dut, DUT_MSG_DEBUG, + "Log --- p2p address = %s", p2p_dev_id); + snprintf(cmd_buff, sizeof(cmd_buff), "P2P_PEER %s", p2p_dev_id); + wpa_command_resp(output_ifname, cmd_buff, rtsp_buff, + sizeof(rtsp_buff)); + + if (strlen(rtsp_buff) != 0) + sub_elem = strstr(rtsp_buff, "wfd_subelems="); + + /* Extract RTSP Port for Sink */ + if (sub_elem && strlen(sub_elem) >= 23) { + sub_elem += 23; + snprintf(rtspport, 5, "%s", sub_elem); + sigma_dut_print(dut, DUT_MSG_DEBUG, + "rtsp_port = %s, subElem = %s", + rtspport, sub_elem); + } + session_management_control_port = (int) strtol(rtspport, NULL, + 16); + sigma_dut_print(dut, DUT_MSG_DEBUG, + "SessionManagementControlPort = %d", + session_management_control_port); + + } else { + session_management_control_port = 7236; + } + + extn_connect_go_start_wfd = dlsym(dut->miracast_lib, + "connect_go_start_wfd"); + if (!extn_connect_go_start_wfd) + return -1; + extn_connect_go_start_wfd(NULL, peer_ip_address, + session_management_control_port, + 1 - dut->wfd_device_type, rtsp_session_id); + strlcat(sig_resp_buf, rtsp_session_id, + sizeof(sig_resp_buf) - strlen(sig_resp_buf)); + + send_resp(dut, conn, SIGMA_COMPLETE, sig_resp_buf); + return 0; +} + + +static int cmd_sta_generate_event(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + + /* const char *intf = get_param(cmd, "Interface"); */ + /* const char *program = get_param(cmd, "Program"); */ + const char *type = get_param(cmd, "Type"); + char string_cmd[MIRACAST_CMD_LEN]; + int (*extn_sta_generate_event)(const char *); + + if (!type) { + send_resp(dut, conn, SIGMA_INVALID, + "errorCode, Invalid Type for Generate Event"); + return 0; + } + miracast_generate_string_cmd(cmd, string_cmd, sizeof(string_cmd)); + extn_sta_generate_event = dlsym(dut->miracast_lib, + "sta_generate_event"); + if (!extn_sta_generate_event) + return -1; + if (strcasecmp(type, "UIBC_Gen") == 0 || + strcasecmp(type, "UIBC_HID") == 0) { + extn_sta_generate_event(string_cmd); + } else if (strcasecmp(type, "FrameSkip") == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Unsupported Type for Generate Event"); + return 0; + } else if (strcasecmp(type, "InputContent") == 0) { + send_resp(dut, conn, SIGMA_COMPLETE, NULL); + return 0; + } else if (strcasecmp(type, "I2cRead") == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode,Unsupported Type for Generate Event"); + return 0; + } else if (strcasecmp(type, "I2cWrite") == 0) { + send_resp(dut, conn, SIGMA_ERROR, + "errorCode, Unsupported Type for Generate Event"); + return 0; + } else if (strcasecmp(type, "IdrReq") == 0) { + if (dut->wfd_device_type == 0) { /* Source */ + send_resp(dut, conn, SIGMA_ERROR, + "errorCode, Unsupported Type for Generate Event"); + } else { + send_resp(dut, conn, SIGMA_COMPLETE, NULL); + extn_sta_generate_event(string_cmd); + } + return 0; + } + return 1; +} + + +static int cmd_reinvoke_wfd_session(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd) +{ + const char *intf = get_param(cmd, "Interface"); + const char *grp_id = get_param(cmd, "GroupID"); + const char *peer_address = get_param(cmd, "peeraddress"); + const char *invitation_action = get_param(cmd, "InvitationAction"); + char buf[256]; + + /* All are compulsory parameters */ + if (!intf || !grp_id || !invitation_action || !peer_address) { + send_resp(dut, conn, SIGMA_INVALID, + "errorCode,Invalid parameters for Reinvoke WFD Session"); + return 0; + } + + if (strcmp(invitation_action, "accept") == 0) { + /* + * In a client-joining-a-running-group case, we need to + * separately authorize the invitation. + */ + miracast_stop_dhcp_client(dut, NULL); + sigma_dut_print(dut, DUT_MSG_DEBUG, "Trying to discover GO %s", + peer_address); + if (p2p_discover_peer(dut, intf, peer_address, 1) < 0) { + send_resp(dut, conn, SIGMA_ERROR, + "ErrorCode,Could not discover the requested peer"); + return 0; + } + + snprintf(buf, sizeof(buf), "P2P_CONNECT %s %s join auth", + peer_address, dut->wps_method == WFA_CS_WPS_PBC ? + "pbc" : dut->wps_pin); + if (wpa_command(intf, buf) < 0) + return -2; + + miracast_rtsp_thread_create(dut, conn, cmd); + return 1; + } + + send_resp(dut, conn, SIGMA_INVALID, + "errorCode,Unsupported Invitation Action"); + return 0; +} + + +static int req_intf_peer(struct sigma_cmd *cmd) +{ + if (!get_param(cmd, "interface") || + !get_param(cmd, "PeerAddress")) + return -1; + return 0; +} + + +static int req_intf_p2pdev_grpid(struct sigma_cmd *cmd) +{ + if (!get_param(cmd, "interface") || + !get_param(cmd, "P2pdevID") || + !get_param(cmd, "GroupID")) + return -1; + return 0; +} + + +static int req_intf_prog_type(struct sigma_cmd *cmd) +{ + const char *prog = get_param(cmd, "Program"); + + if (!get_param(cmd, "interface") || + !get_param(cmd, "Type") || + !prog || strcmp(prog, "WFD") != 0) + return -1; + return 0; +} + + +static int req_intf_peeradd_inv(struct sigma_cmd *cmd) +{ + if (!get_param(cmd, "interface") || + !get_param(cmd, "peerAddress") || + !get_param(cmd, "InvitationAction")) + return -1; + return 0; +} + + +void miracast_register_cmds(void) +{ + sigma_dut_reg_cmd("start_wfd_connection", req_intf_peer, + cmd_start_wfd_connection); + sigma_dut_reg_cmd("connect_go_start_wfd", req_intf_p2pdev_grpid, + cmd_connect_go_start_wfd); + sigma_dut_reg_cmd("sta_generate_event", req_intf_prog_type, + cmd_sta_generate_event); + sigma_dut_reg_cmd("reinvoke_wfd_session", req_intf_peeradd_inv, + cmd_reinvoke_wfd_session); +} diff --git a/miracast.h b/miracast.h new file mode 100644 index 0000000..6d1aab3 --- /dev/null +++ b/miracast.h @@ -0,0 +1,57 @@ +/* + * Sigma Control API DUT - Miracast interface + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * All Rights Reserved. + * Licensed under the Clear BSD license. See README for more details. +*/ +#ifndef SIGMA_MIRACAST_H +#define SIGMA_MIRACAST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ANDROID +#include "properties.h" +#endif /* ANDROID */ + + +struct sigma_dut; +struct sigma_conn; +struct sigma_cmd; + +void miracast_init(struct sigma_dut *sigma_dut); + +void miracast_deinit(struct sigma_dut *sigma_dut); + +void miracast_start_autonomous_go(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd, char *ifname); + +int miracast_dev_send_frame(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd); + +int miracast_dev_exec_action(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd); + +int miracast_preset_testparameters(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd); + +int miracast_cmd_sta_get_parameter(struct sigma_dut *dut, + struct sigma_conn *conn, + struct sigma_cmd *cmd); + +void miracast_sta_reset_default(struct sigma_dut *dut, struct sigma_conn *conn, + struct sigma_cmd *cmd); + +#endif /* SIGMA_MIRACAST_H */ diff --git a/p2p.c b/p2p.c index 8d743da..bf6e844 100644 --- a/p2p.c +++ b/p2p.c @@ -1,7 +1,7 @@ /* * Sigma Control API DUT (station/AP) * Copyright (c) 2010-2011, Atheros Communications, Inc. - * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. * All Rights Reserved. * Licensed under the Clear BSD license. See README for more details. */ @@ -10,6 +10,7 @@ #include #include "wpa_ctrl.h" #include "wpa_helpers.h" +#include "miracast.h" int run_system(struct sigma_dut *dut, const char *cmd) @@ -795,6 +796,9 @@ static int cmd_sta_start_autonomous_go(struct sigma_dut *dut, const char *intf = get_param(cmd, "Interface"); const char *oper_chn = get_param(cmd, "OPER_CHN"); const char *ssid_param = get_param(cmd, "SSID"); +#ifdef MIRACAST + const char *rtsp = get_param(cmd, "RTSP"); +#endif /* MIRACAST */ int freq, chan, res; char buf[256], grpid[100], resp[200]; struct wpa_ctrl *ctrl; @@ -916,6 +920,14 @@ static int cmd_sta_start_autonomous_go(struct sigma_dut *dut, p2p_group_add(dut, ifname, strcmp(gtype, "GO") == 0, grpid, ssid); snprintf(resp, sizeof(resp), "GroupID,%s", grpid); + +#ifdef MIRACAST + if (rtsp && atoi(rtsp) == 1) { + /* Start RTSP Thread for incoming connections */ + miracast_start_autonomous_go(dut, conn, cmd, ifname); + } +#endif /* MIRACAST */ + send_resp(dut, conn, SIGMA_COMPLETE, resp); return 0; } @@ -1057,10 +1069,14 @@ static int cmd_sta_p2p_start_group_formation(struct sigma_dut *dut, int freq = 0, chan = 0, init; char buf[256]; struct wpa_ctrl *ctrl; + int intent; if (devid == NULL || intent_val == NULL) return -1; + intent = atoi(intent_val); + if (intent > 15) + intent = 1; if (init_go_neg) init = atoi(init_go_neg); else @@ -1134,7 +1150,7 @@ static int cmd_sta_p2p_start_group_formation(struct sigma_dut *dut, " keypad" )), dut->persistent ? " persistent" : "", init ? "" : " auth", - atoi(intent_val)); + intent); if (freq > 0) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " freq=%d", freq); @@ -1712,6 +1728,12 @@ int cmd_sta_p2p_reset(struct sigma_dut *dut, struct sigma_conn *conn, struct wfa_cs_p2p_group *grp, *prev; char buf[256]; +#ifdef MIRACAST + if (dut->program == PROGRAM_WFD || + dut->program == PROGRAM_DISPLAYR2) + miracast_sta_reset_default(dut, conn, cmd); +#endif /* MIRACAST */ + dut->go = 0; dut->p2p_client = 0; dut->wps_method = WFA_CS_WPS_NOT_READY; diff --git a/sigma_dut.c b/sigma_dut.c index 15b6aa9..98ca085 100644 --- a/sigma_dut.c +++ b/sigma_dut.c @@ -1,7 +1,7 @@ /* * Sigma Control API DUT (station/AP) * Copyright (c) 2010-2011, Atheros Communications, Inc. - * Copyright (c) 2011-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. * All Rights Reserved. * Licensed under the Clear BSD license. See README for more details. */ @@ -13,6 +13,7 @@ #endif /* __linux__ */ #include "wpa_ctrl.h" #include "wpa_helpers.h" +#include "miracast.h" #define SIGMA_DUT_PORT 9000 #define MAX_CONNECTIONS 4 @@ -764,7 +765,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "aAb:Bc:C:dDE:e:fghH:i:Ik:l:L:m:M:nN:o:O:p:P:qr:R:s:S:tT:uv:VWw:"); + "aAb:Bc:C:dDE:e:fghH:i:Ik:l:L:m:M:nN:o:O:p:P:qr:R:s:S:tT:uv:VWw:x:y:"); if (c < 0) break; switch (c) { @@ -920,6 +921,22 @@ int main(int argc, char *argv[]) case 'A': sigma_dut.sim_no_username = 1; break; +#ifdef MIRACAST + case 'x': + if (strcmp(optarg, "sink") == 0) { + sigma_dut.wfd_device_type = 1; + sigma_dut_print(&sigma_dut, DUT_MSG_INFO, + "Device Type is SINK"); + } else if (strcmp(optarg, "source") == 0) { + sigma_dut.wfd_device_type = 0; + sigma_dut_print(&sigma_dut, DUT_MSG_INFO, + "Device Type is SOURCE"); + } + break; + case 'y': + sigma_dut.miracast_lib_path = optarg; + break; +#endif /* MIRACAST */ case 'h': default: printf("usage: sigma_dut [-aABdfqDIntuVW] [-p] " @@ -943,6 +960,10 @@ int main(int argc, char *argv[]) " [-N ] \\\n" " [-o ] \\\n" " [-O ] \\\n" +#ifdef MIRACAST + " [-x ] \\\n" + " [-y ] \\\n" +#endif /* MIRACAST */ " [-r ]\n"); printf("local command: sigma_dut [-p] " "<-l>\n"); @@ -952,6 +973,9 @@ int main(int argc, char *argv[]) } sigma_dut.p2p_ifname = determine_sigma_p2p_ifname(); +#ifdef MIRACAST + miracast_init(&sigma_dut); +#endif /* MIRACAST */ if (local_cmd) return run_local_cmd(port, local_cmd); @@ -1022,6 +1046,9 @@ int main(int argc, char *argv[]) free(sigma_p2p_ifname_buf); close_socket(&sigma_dut); +#ifdef MIRACAST + miracast_deinit(&sigma_dut); +#endif /* MIRACAST */ sigma_dut_unreg_cmds(&sigma_dut); return 0; diff --git a/sigma_dut.h b/sigma_dut.h index eae9512..9e05110 100644 --- a/sigma_dut.h +++ b/sigma_dut.h @@ -1,7 +1,7 @@ /* * Sigma Control API DUT (station/AP) * Copyright (c) 2010-2011, Atheros Communications, Inc. - * Copyright (c) 2011-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. * All Rights Reserved. * Licensed under the Clear BSD license. See README for more details. */ @@ -517,6 +517,14 @@ struct sigma_dut { #endif /* CONFIG_SNIFFER */ int last_set_ip_config_ipv6; +#ifdef MIRACAST + pthread_t rtsp_thread_handle; + int wfd_device_type; /* 0 for source, 1 for sink */ + char peer_mac_address[32]; + void *miracast_lib; + const char *miracast_lib_path; + char mdns_instance_name[64]; +#endif /* MIRACAST */ int tid_to_handle[8]; /* Mapping of TID to handle */ int dialog_token; /* Used for generating unique handle for an addTs */ diff --git a/sta.c b/sta.c index a9597e5..c8cb4a1 100644 --- a/sta.c +++ b/sta.c @@ -29,6 +29,7 @@ #endif /* __QNXNTO__ */ #include "wpa_ctrl.h" #include "wpa_helpers.h" +#include "miracast.h" /* Temporary files for sta_send_addba */ #define VI_QOS_TMP_FILE "/tmp/vi-qos.tmp" @@ -2937,6 +2938,11 @@ static int cmd_sta_preset_testparameters(struct sigma_dut *dut, if (val && strcasecmp(val, "NAN") == 0) return nan_cmd_sta_preset_testparameters(dut, conn, cmd); #endif /* ANDROID_NAN */ +#ifdef MIRACAST + if (val && (strcasecmp(val, "WFD") == 0 || + strcasecmp(val, "DisplayR2") == 0)) + return miracast_preset_testparameters(dut, conn, cmd); +#endif /* MIRACAST */ #if 0 val = get_param(cmd, "Supplicant"); @@ -4300,6 +4306,12 @@ static int cmd_sta_get_parameter(struct sigma_dut *dut, struct sigma_conn *conn, return nan_cmd_sta_get_parameter(dut, conn, cmd); #endif /* ANDROID_NAN */ +#ifdef MIRACAST + if (strcasecmp(program, "WFD") == 0 || + strcasecmp(program, "DisplayR2") == 0) + return miracast_cmd_sta_get_parameter(dut, conn, cmd); +#endif /* MIRACAST */ + send_resp(dut, conn, SIGMA_ERROR, "ErrorCode,Unsupported parameter"); return 0; } @@ -4450,8 +4462,11 @@ static int cmd_sta_reset_default(struct sigma_dut *dut, struct sigma_cmd *cmd); const char *intf = get_param(cmd, "Interface"); const char *type; + const char *program = get_param(cmd, "program"); - dut->program = sigma_program_to_enum(get_param(cmd, "prog")); + if (!program) + program = get_param(cmd, "prog"); + dut->program = sigma_program_to_enum(program); dut->device_type = STA_unknown; type = get_param(cmd, "type"); if (type && strcasecmp(type, "Testbed") == 0) @@ -4472,6 +4487,12 @@ static int cmd_sta_reset_default(struct sigma_dut *dut, } } +#ifdef MIRACAST + if (dut->program == PROGRAM_WFD || + dut->program == PROGRAM_DISPLAYR2) + miracast_sta_reset_default(dut, conn, cmd); +#endif /* MIRACAST */ + switch (get_driver_type()) { case DRIVER_ATHEROS: sta_reset_default_ath(dut, intf, type);