嵌入式设备WIFI应用开发(2) - wpa_cli源码分析

嵌入式设备WIFI应用开发(2) - wpa_cli源码分析

概述

使用wpa_cli可执行程序和相关的命令可以做到连接wifi,在实际的项目中更进一步的想法是怎么样将wpa_cli的源代码整合到自己的实际项目中来。为了达到这个目的,需要从wap_cli的源码开始分析,了解清楚了wpa_cli的工作过程中后,就可以开始进行源代码层面的整合了。

wpa_cli源码分析

wpa_cli的源码包含在了wpa_supplicant的源码包中,解压wpa_supplicant后,在wpa_supplicant目录下找到wap_cli.c这个文件,该文件就是wpa_cli的主要实现问题,在该文件中能找wpa_cli的入口main函数。
wpa_cli的main函数分析
wpa_cli代码执行流程通过对main函数执行流程的分析,最后得到的结论我们进一步分析的只要两个函数:wpa_cli_open_connection()和wpa_request()
wpa_cli_open_connection()函数分析

wpa_cli_open_connection执行分析wpa_request() 函数分析
wpa_request()wpa_cli_commands分析

struct wpa_cli_cmd {
	const char *cmd;
	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
	char ** (*completion)(const char *str, int pos);
	enum wpa_cli_cmd_flags flags;
	const char *usage;
};
static const struct wpa_cli_cmd wpa_cli_commands[] = {
	{ "status", wpa_cli_cmd_status, NULL,
	  cli_cmd_flag_none,
	  "[verbose] = get current WPA/EAPOL/EAP status" },
	{ "ifname", wpa_cli_cmd_ifname, NULL,
	  cli_cmd_flag_none,
	  "= get current interface name" },
	{ "ping", wpa_cli_cmd_ping, NULL,
	  cli_cmd_flag_none,
	  "= pings wpa_supplicant" },
	{ "relog", wpa_cli_cmd_relog, NULL,
	  cli_cmd_flag_none,
	  "= re-open log-file (allow rolling logs)" },
	{ "note", wpa_cli_cmd_note, NULL,
	  cli_cmd_flag_none,
	  "<text> = add a note to wpa_supplicant debug log" },
	{ "mib", wpa_cli_cmd_mib, NULL,
	  cli_cmd_flag_none,
	  "= get MIB variables (dot1x, dot11)" },
	{ "help", wpa_cli_cmd_help, wpa_cli_complete_help,
	  cli_cmd_flag_none,
	  "[command] = show usage help" },
	{ "interface", wpa_cli_cmd_interface, NULL,
	  cli_cmd_flag_none,
	  "[ifname] = show interfaces/select interface" },
	{ "level", wpa_cli_cmd_level, NULL,
	  cli_cmd_flag_none,
	  "<debug level> = change debug level" },
	  ....
	  //后面还有很多

wpa_cli_commands数组中每个元素对应一个wpa_cli_cmd ,wpa_cli_cmd结构体定义了wpa_cli命令及命令执行函数。已

{
	  "status", 
	  wpa_cli_cmd_status, 
	  NULL,
	  cli_cmd_flag_none,
	  "[verbose] = get current WPA/EAPOL/EAP status" 
}

为例:
“status”:命令字,
wpa_cli_cmd_status:执行status命令的函数,
进入wpa_cli_cmd_status函数:

static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
	if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
		return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
	if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
		return wpa_ctrl_command(ctrl, "STATUS-WPS");
	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
		return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
#ifdef ANDROID
	if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
		return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
#endif /* ANDROID */
	return wpa_ctrl_command(ctrl, "STATUS");
}

最终进入

static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
{
	return _wpa_ctrl_command(ctrl, cmd, 1);
}

下一层


static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
{
	char buf[4096];
	size_t len;
	int ret;

	if (ctrl_conn == NULL) {
		printf("Not connected to wpa_supplicant - command dropped.\n");
		return -1;
	}
	if (ifname_prefix) {
		os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
			    ifname_prefix, cmd);
		buf[sizeof(buf) - 1] = '\0';
		cmd = buf;
	}
	len = sizeof(buf) - 1;
	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
			       wpa_cli_msg_cb);
	if (ret == -2) {
		printf("'%s' command timed out.\n", cmd);
		return -2;
	} else if (ret < 0) {
		printf("'%s' command failed.\n", cmd);
		return -1;
	}
	if (print) {
		buf[len] = '\0';
		printf("%s", buf);
		if (interactive && len > 0 && buf[len - 1] != '\n')
			printf("\n");
	}
	return 0;
}

下一层

#ifdef CTRL_IFACE_SOCKET
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
		     char *reply, size_t *reply_len,
		     void (*msg_cb)(char *msg, size_t len))
{
	struct timeval tv;
	struct os_reltime started_at;
	int res;
	fd_set rfds;
	const char *_cmd;
	char *cmd_buf = NULL;
	size_t _cmd_len;

#ifdef CONFIG_CTRL_IFACE_UDP
	if (ctrl->cookie) {
		char *pos;
		_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
		cmd_buf = os_malloc(_cmd_len);
		if (cmd_buf == NULL)
			return -1;
		_cmd = cmd_buf;
		pos = cmd_buf;
		os_strlcpy(pos, ctrl->cookie, _cmd_len);
		pos += os_strlen(ctrl->cookie);
		*pos++ = ' ';
		os_memcpy(pos, cmd, cmd_len);
	} else
#endif /* CONFIG_CTRL_IFACE_UDP */
	{
		_cmd = cmd;
		_cmd_len = cmd_len;
	}

	errno = 0;
	started_at.sec = 0;
	started_at.usec = 0;
retry_send:
	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
		if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
		{
			/*
			 * Must be a non-blocking socket... Try for a bit
			 * longer before giving up.
			 */
			if (started_at.sec == 0)
				os_get_reltime(&started_at);
			else {
				struct os_reltime n;
				os_get_reltime(&n);
				/* Try for a few seconds. */
				if (os_reltime_expired(&n, &started_at, 5))
					goto send_err;
			}
			os_sleep(1, 0);
			goto retry_send;
		}
	send_err:
		os_free(cmd_buf);
		return -1;
	}
	os_free(cmd_buf);

	for (;;) {
		tv.tv_sec = 10;
		tv.tv_usec = 0;
		FD_ZERO(&rfds);
		FD_SET(ctrl->s, &rfds);
		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
		if (res < 0)
			return res;
		if (FD_ISSET(ctrl->s, &rfds)) {
			res = recv(ctrl->s, reply, *reply_len, 0);
			if (res < 0)
				return res;
			if (res > 0 && reply[0] == '<') {
				/* This is an unsolicited message from
				 * wpa_supplicant, not the reply to the
				 * request. Use msg_cb to report this to the
				 * caller. */
				if (msg_cb) {
					/* Make sure the message is nul
					 * terminated. */
					if ((size_t) res == *reply_len)
						res = (*reply_len) - 1;
					reply[res] = '\0';
					msg_cb(reply, res);
				}
				continue;
			}
			*reply_len = res;
			break;
		} else {
			return -2;
		}
	}
	return 0;
}
#endif /* CTRL_IFACE_SOCKET */

最终走到wpa_ctrl_request,wpa_ctrl_request完成与wpa_supplicant之间的通行,发送命令,接收命令执行结果。
在这里插入图片描述

接口封装

明确了wpa_cli命令执行过程后,从后往前倒推执行过程,在连接上一篇《嵌入式设备WIFI应用开发(1)- wpa_supplicant和wpa_cli的移植和使用》中,我们使用了“scan”,“scan_r”,“add_network”,“set_network”,“enable_network”,“status”命令完成了wifi的整个连接过程。现在只需要针对上面的命令封装出调用接口就可以了。
scan命令封装

int my_wpa_cmd_scan(struct wpa_ctrl *ctrl, char *buf, size_t bufsize)
{
    return my_wpa_ctrl_command(ctrl, "SCAN", buf, bufsize);
}
int my_wpa_scan(const char *ifname)
{
    int ret = 0;
    char reply[64] = {0};
    
    if (!ifname)
        return -1;
	
	if (wpa_cli_open_connection(ifname, 0) < 0)
    {
		TRACE("> failed to connect to ifname: %s error: %s. %s %d\r\n", ifname ? ifname : "(nil)",
			strerror(errno), MDL);
        
		return -1;
	}

    my_wpa_cmd_scan(ctrl_conn, reply, sizeof(reply));
    if (os_strncmp(reply, "OK", 2) != 0) 
    {
		TRACE("> failed to scan network in wpa_supplicant, %s. %s %d\r\n", reply, MDL);
        ret = -1;
        if (os_strncmp(reply, "FAIL-BUSY", os_strlen("FAIL-BUSY")) == 0) 
        {
            ret = -2;
        }
	}
   
    wpa_cli_cleanup();
    
	return ret;
}

scan_r命令封装

int my_wpa_cmd_scan_results(struct wpa_ctrl *ctrl, char *buf, size_t bufsize)
{
    return my_wpa_ctrl_command(ctrl, "SCAN_RESULTS", buf, bufsize);    
}
int my_wpa_scan_results(const char *ifname, struct my_wpa_scan_result *result)
{
    int ret = 0;
    char reply[4096] = {0};
    size_t reply_len = sizeof(reply);
    
    if (ifname == NULL || result == NULL)
        return -1;
    
	if (wpa_cli_open_connection(ifname, 0) < 0) 
    {
		TRACE("> failed to connect to ifname: %s error: %s. %s %d\r\n", ifname ? ifname : "(nil)",
			strerror(errno), MDL);
        
		return -1;
	}

    ret = my_wpa_cmd_scan_results(ctrl_conn, reply, reply_len);
    reply[reply_len - 1] = '\0';
    if (0 == ret) 
    {   
        /* reply保存所有扫描的结果 */
        ret = my_wpa_parse_scan_result(reply, result);
    }
    
    wpa_cli_cleanup();
    
	return ret;
}

其它指令可以按同样的方法进行封装。

libwifi.a

通过wpa_cli的源码分析,更进一步,将相关的源文件抽取出来,编译成一个libwifi.a文件,移植到自己的项目中。
通过libwifi.h对外提供调用接口。
libwifi.h

#ifdef __cplusplus
extern "C" {
#endif

#define MAX_SCAN_RESULT_NUM     50

typedef struct wpa_cli_scan_result 
{
    char bssid[24];         /* wifi热点MAC地址 */
    int freq;               /* wifi信道值 */
    int level;              /* 信号强度,值越大信号越强 */
    char flags[128];        /* 加密相关信息 */
    char ssid[128];         /* ssid */
    
} wpa_cli_scan_result;

typedef struct my_wpa_scan_result 
{
    int count;              /* 扫描wifi总数 */
    wpa_cli_scan_result results[MAX_SCAN_RESULT_NUM];   /* 扫描wifi结果 */
    
} my_wpa_scan_result;

/* ssid 搜素 */
int wifi_connect_scan(const char *ifname, const char *utf8ssid, int *auth, int *encr, int *signal);

/* 获取指定ssid WiFi连接状态 */
int wifi_connect_status(const char *ifname, const char *utf8ssid);

/* 连接指定ssid WiFi */
int wifi_connect_by_ssid(const char* ifname, const char* ssid, const char* passwd, int auth, int encr);

#ifdef __cplusplus
}
#endif

#endif /* LIB_WIFI_H */

  • 6
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
### 回答1: ST-Link_CLI是ST公司提供的一个用于程序下载和调试的命令行工具。ELF文件是一种可执行文件格式,通常用于嵌入式系统的开发。而hex文件是一种十六进制格式的文件,用于存储程序的机器码和数据。 要将ELF文件转换为hex文件,可以使用ST-Link_CLI提供的相关命令。以下是具体的步骤: 1. 打开命令行窗口,并导航至ST-Link_CLI的安装目录。 2. 输入以下命令来将ELF文件转换为hex文件: st-link_cli.exe -p <芯片类型> -s -H -NoPrompt -bin <ELF文件路径> <hex文件路径> 其中,“<芯片类型>”应该替换为具体的芯片型号,而“<ELF文件路径>”和“<hex文件路径>”分别是ELF文件和生成的hex文件的路径。 3. 执行命令后,ST-Link_CLI将读取ELF文件并生成相应的hex文件。 在转换过程中,ST-Link_CLI会解析ELF文件,提取其中包含的机器码和数据,并按照hex文件的格式进行存储。转换成功后,生成的hex文件可以用于在目标芯片上进行下载和调试。 需要注意的是,在转换之前,确保ST-Link_CLI已正确安装并设置好系统的环境变量。此外,还需要确保连接的ST-Link调试器与目标芯片已正确配置,并与计算机连接。 通过上述步骤,就可以使用ST-Link_CLI将ELF文件转换为hex文件,以用于嵌入式系统的开发和调试。 ### 回答2: ST-Link_CLI是一个用于ST-Link调试和编程工具的命令行界面。它提供了很多功能,可以用于将ELF文件转换为HEX文件。 ELF文件是一种可执行文件格式,而HEX文件是一种十六进制文件格式。ELF文件包含了程序的可执行代码、数据和其他信息,而HEX文件只包含了程序的机器码指令。 使用ST-Link_CLI将ELF文件转换为HEX文件可以方便地在特定的嵌入式系统上进行编程和调试。以下是一个基本的命令示例: 1. 打开命令行终端,并进入ST-Link_CLI工具所在的目录。 2. 输入以下命令来将ELF文件转换为HEX文件: `ST-Link_CLI.exe -c SWD -ME -P my_application.elf -O my_application.hex` 其中,"-c"参数指定了接口类型,"SWD"表示使用SWD接口,"-ME"参数指定了可执行文件的大小端模式,"-P"参数指定了输入的ELF文件名,"-O"参数指定了输出的HEX文件名。 3. 按下回车键执行命令,ST-Link_CLI将会读取ELF文件,并将其转换为HEX文件。 执行完上述命令后,我们就可以在指定的目录下找到生成的HEX文件。这个HEX文件可以被用于将程序烧录到嵌入式系统的闪存中,以便进行调试和测试。 总结起来,ST-Link_CLI是一个强大的工具,可以将ELF文件转换为HEX文件,以便在特定的嵌入式系统上进行编程和调试。 ### 回答3: st-link_cli命令是ST-Link系列调试器的命令行接口工具,elf转hex是指将程序文件的elf格式转换为hex格式。 使用st-link_cli命令进行elf转hex的步骤如下: 1. 首先,将ST-Link调试器连接到目标设备的调试接口上,并保证硬件连接正确。 2. 打开命令行终端,进入st-link_cli的安装目录。 3. 输入以下命令,将elf文件转换为hex文件: st-link_cli.exe --convert=bin --format=hex <elf文件路径> <hex文件输出路径> 其中,--convert=bin参数指定要进行转换操作,--format=hex参数指定输出文件的格式为hex格式。 例如,如果elf文件路径为C:\your\elf\path\file.elf,hex文件输出路径为C:\your\hex\path\file.hex,则命令应为: st-link_cli.exe --convert=bin --format=hex C:\your\elf\path\file.elf C:\your\hex\path\file.hex 4. 按下回车键执行命令,等待转换过程完成。 5. 转换完成后,可以在指定的hex文件输出路径中找到生成的hex文件,该文件可以用于烧录到目标设备中。 需要注意的是,st-link_cli命令需要正确配置并与目标设备连接,而且需要提供正确的elf文件路径和hex文件输出路径。是在ST-Link调试器的命令行接口中实现elf转hex功能的一种方法。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值