1、select函数
定义函数 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
- FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
- FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
- FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
- FD_ZERO(fd_set *set); 用来清除描述词组set的全部位
参数 timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
struct timeval
{
time_t tv_sec;
time_t tv_usec;
};
返回值 如果参数timeout设为NULL则表示select()没有timeout
通过模拟手机wifi模式,写一个Linux下wifi的驱动程序
摘取重要部分函数:
/* * WPA Supplicant - command line interface for wpa_supplicant daemon * Copyright (c) 2004-2012, Jouni Malinen <[email protected]> * * This software may be distributed under the terms of the BSD license. * See README for more details. */
static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
{
return _wpa_ctrl_command(ctrl, cmd, 0);
}
static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
int argc, char *argv[])
{
char buf[256];
if (argc < min_args) {
printf("Invalid %s command - at least %d argument%s "
"required.\n", cmd, min_args,
min_args > 1 ? "s are" : " is");
return -1;
}
if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, buf);
}
/*Garmen:WPA命令请求函数*/
static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
struct wpa_cli_cmd *cmd, *match = NULL;
int count;
int ret = 0;
count = 0;
cmd = wpa_cli_commands;
while (cmd->cmd) {
if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
{
match = cmd;
if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
/* we have an exact match */
count = 1;
break;
}
count++;
}
cmd++;
}
if (count > 1) {
printf("Ambiguous command '%s'; possible commands:", argv[0]);
cmd = wpa_cli_commands;
while (cmd->cmd) {
if (os_strncasecmp(cmd->cmd, argv[0],
os_strlen(argv[0])) == 0) {
printf(" %s", cmd->cmd);
}
cmd++;
}
printf("\n");
ret = 1;
} else if (count == 0) {
printf("Unknown command '%s'\n", argv[0]);
ret = 1;
} else {
ret = match->handler(ctrl, argc - 1, &argv[1]);
}
return ret;
}
#define CFG_MAXARGS 10
/*Garmen:行解析函数,从U-boot中提取的这个函数,返回的是该行命令有的字段的个数,以空格和tab为分界,进行提取*/
static int parse_line (char *line, char *argv[])
{
int nargs = 0;
while (nargs < CFG_MAXARGS) {
/* skip any white space:跳过任何的空白 包括TAB和space空格,但是假如不是TAB或者SPACE证明就有字符串了,就到下面的函数,此时记录为++line*/
while ((*line == ' ') || (*line == '\t')) {
++line;
}
/*Garmen:假如没有空格键也没有TAB,但是一上来就是回车换行,就证明是结束行了*/
if (*line == '\0') { /* end of line, no more args:结束行,没有更多参数了 */
argv[nargs] = NULL;
return (nargs);
}
/* begin of argument string:参数串的开始,此时的line是由上面的while函数判断可得*/
argv[nargs++] = line;
/* find end of string :找到最后的字符串;\t表示一个tab的距离既一个大空格 * 满足的条件是:line有数据而且line不等于空格或者TAB,等到没数据了或者是空格TAB,此时的line已经被记录下来了 */
while (*line && (*line != ' ') && (*line != '\t')) {
++line;
}
if (*line == '\0') { /* end of line, no more args:结束行,没有更多参数了 */
argv[nargs] = NULL;
return (nargs);
}
*line++ = '\0'; /* terminate current arg:终止目前的arg */
}
return (nargs);
}
/* eg. * cmd: set_network 2 ssid "dswei" * 则: * argc = 4 * argv[0] = "set_network" * argv[1] = "2" * argv[2] = "ssid" * argv[3] = "\"dswei\"" */
static int wpa_command(struct wpa_ctrl *ctrl, char *cmd)
{
int argc;
char buf[1024];
char *argv[CFG_MAXARGS];
/*Garemn:将cmd复制到buf*/
strncpy(buf, cmd, 1024);
buf[1023] = '\0';
/*Garmen:从前面的例子便可以看出,parse_line就是进行命令的提取,将buf的数据一个个提取出来存入argv里面*/
argc = parse_line(buf, argv);
return wpa_request(ctrl, argc, argv);
}
#define max_args 10
/*Garmen:通过你要找哪行(index),假如找到了你想要找的行数,就将此时的buf数据传递给line*/
static int get_line_from_buf(int index, char *line, char *buf)
{
int i = 0;
int j;
int endcnt = -1;/*Garmen:每一行结束的标志*/
char *linestart = buf;
int len;
while (1)
{
/*Garmen:回车或者换行或者已经达到尾部*/
if (buf[i] == '\n' || buf[i] == '\r' || buf[i] == '\0')
{
endcnt++;
/*index等于endcnt表明我们已经找到我们想要的那行*/
if (index == endcnt)
{
len = &buf[i] - linestart;
strncpy(line, linestart, len);//Garmen:从linestart开始拷贝,长度为len
line[len] = '\0';
return 0; //返回0证明我们的目的已经达到了
}
/*Garmen:还得继续找,因为这行不是我们想要的*/
else
{
/* 更新linestart */
for (j = i + 1; buf[j]; )
{
if (buf[j] == '\n' || buf[j] == '\r')
j++;
else
break;
}
/*Garmen:跳出的原因是因buf[j]已经等于0的话已经结束,返回-1*/
if (!buf[j])
return -1;
/*Garmen:否则的话,进行更新,意思就是下一新的行的起始位置等于这个空格前镜哪歉鲎址?/ linestart = &buf[j]; i = j;/*Garmen:同时对i也进行更新*/
}
}
/*Garmen:结束-只是判断buf[i]有无数据了,无数据就证明到底了,结束*/
if (!buf[i])
return -1;
i++;/*Garmen:假如没有到底,就不断的对buf[i]进行判断,然后i就不断的增加*/
}
}
static int StdinDevInit(void)
{
struct termios tTTYState;
//get the terminal state
tcgetattr(STDIN_FILENO, &tTTYState);
//turn off canonical mode
tTTYState.c_lflag &= ~ICANON;
//minimum of number input read.
tTTYState.c_cc[VMIN] = 1; /* 有一个数据时就立刻返回 */
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &tTTYState);
return 0;
}
static int StdinDevExit(void)
{
struct termios tTTYState;
//get the terminal state
tcgetattr(STDIN_FILENO, &tTTYState);
//turn on canonical mode
tTTYState.c_lflag |= ICANON;
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &tTTYState);
return 0;
}
static int StdinGetChar(int timeout)
{
/* 如果有数据就读取、处理、返回 * 如果没有数据, 立刻返回, 不等待 */
/* select, poll 可以参数 UNIX环境高级编程 */
struct timeval tTV;
fd_set tFDs;
char c;
tTV.tv_sec = timeout;
tTV.tv_usec = 0;
FD_ZERO(&tFDs);
FD_SET(STDIN_FILENO, &tFDs); //STDIN_FILENO is 0
/*Garmen: 如果tv_sec和tv_usec都是0,那么就是超时时间为0,那么select就会立刻返回了。 如果timeout这里是个NULL,那么超时就未被启用,会一直阻塞在监视文件描述符的地方。 *而且整个select函数最终要的就是文件句柄STDIN_FILENO,通过它我们才可以启动函数之间的监听和初始化及注销 */
if (timeout == -1)
select(STDIN_FILENO+1, &tFDs, NULL, NULL, NULL);
else
select(STDIN_FILENO+1, &tFDs, NULL, NULL, &tTV);
if (FD_ISSET(STDIN_FILENO, &tFDs))
{
/* 处理数据 */
c = fgetc(stdin);
return c;
}
else
{
return 0;
}
}
static int StdinGetString(char *buf, int timeout)
{
/* 如果获得了第1个字符,以后的字符没有时间限制 */
int c;
int i = 0;
c = StdinGetChar(timeout);
if (!c)
return -1;
if (c == '\n' || c == '\r')
return -1;
buf[i++] = c;
while (c = StdinGetChar(-1))
{
if (c == '\n' || c == '\r')
{
buf[i] = '\0';
return 0;
}
else
{
buf[i++] = c;
}
}
return 0;
}
/* wpa_cli status */
int main(int argc, char *argv[])
{
int c;
int daemonize = 0;
int ret = 0;
const char *global = NULL;
int i;
char line[1024];
char bssid[1024];
char freq[1024];
char signal[1024];
char flags[1024];
char ssid[1024];
char input[1024];
int ap;
char cmd[1024];
char ret_buf_bak[1024];
if (os_program_init())
return -1;
if (ctrl_ifname == NULL)
ctrl_ifname = wpa_cli_get_default_ifname();
if (wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
fprintf(stderr, "Failed to connect to non-global "
"ctrl_ifname: %s error: %s\n",
ctrl_ifname, strerror(errno));
return -1;
}
StdinDevInit();
#if 0
ret = wpa_request(ctrl_conn, argc - optind,
&argv[optind]);
#endif
while (1)
{
/* 查看当前是否已经连接了AP */
ret = wpa_command(ctrl_conn, "status");
if (ret)
{
continue;
}
if (strstr(ret_buf, "COMPLETED"))//Garmen:strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
{
/* 连接上了某个AP */
get_line_from_buf(1, line, ret_buf);
sscanf(line+5, "%s", ssid);//Garmen:sscanf 读取格式化的字符串中的数据。sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。
printf("***************** %s connected! *****************\n", ssid);
/* select_network命令会把其他的network禁止掉, 我们要重新开启它们 */
ret = wpa_command(ctrl_conn, "list_network");
if (ret)
{
continue;
}
strncpy(ret_buf_bak, ret_buf, 1024);
ret_buf_bak[1023] = '\0';
for (i = 1; !get_line_from_buf(i, line, ret_buf_bak); i++)
{
sscanf(line, "%d", &ap);
sprintf(cmd, "enable_network %d", ap);
ret = wpa_command(ctrl_conn, cmd);
}
/* 把密码等信息保存起来: save_config */
/* 前提是配置文件里有"update_config=1" */
ret = wpa_command(ctrl_conn, "save_config");
}
/* 扫描AP: scan */
ret = wpa_command(ctrl_conn, "scan");
if (ret)
{
continue;
}
/* 获得结果: scan_results */
ret = wpa_command(ctrl_conn, "scan_results");
if (ret)
{
continue;
}
/* 打印出来 */
//printf("scan_results: \n %s\n", ret_buf);
/*sscanf() - 从一个字符串中读进与指定格式相符的数据.*/
/*Garmen,为什么i=1?因为第0行我们不需要,我们需要的是从第1行开始*/
for (i = 1; !get_line_from_buf(i, line, ret_buf); i++)
{
sscanf(line, "%s %s %s %s %s", bssid, freq, signal, flags, ssid);
printf("%02d %-32s %s\n", i, ssid, flags);
}
printf("Q: Quit\n");
/* 让用户选择某个AP */
printf("Select the AP to connect: ");
fflush(stdout);
//scanf();
ret = StdinGetString(input, 3);
if (ret)
{
printf("\n");
continue;
}
if (input[0] == 'q' || input[0] == 'Q')
break;
sscanf(input, "%d", &ap);
get_line_from_buf(ap, line, ret_buf);
sscanf(line, "%s %s %s %s %s", bssid, freq, signal, flags, ssid);
/* 创建一个network */
ret = wpa_command(ctrl_conn, "add_network");
if (ret)
{
continue;
}
sscanf(ret_buf, "%d", &ap);
sprintf(cmd, "set_network %d ssid \"%s\"", ap, ssid);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
/* 让用户输入密码 */
if (strstr(flags, "WPA"))
{
printf("Enter password: ");
fflush(stdout);
ret = StdinGetString(input, -1);
if (ret)
continue;
sprintf(cmd, "set_network %d psk \"%s\"", ap, input);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
sprintf(cmd, "select_network %d", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
}
else if (strstr(flags, "WEP"))
{
printf("Enter password: ");
fflush(stdout);
ret = StdinGetString(input, -1);
if (ret)
continue;
sprintf(cmd, "set_network %d key_mgmt NONE", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
sprintf(cmd, "set_network %d wep_key0 \"%s\"", ap, input);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
sprintf(cmd, "set_network %d wep_tx_keyidx 0", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
sprintf(cmd, "select_network %d", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
}
else
{
/* OPEN */
sprintf(cmd, "set_network %d key_mgmt NONE", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
sprintf(cmd, "select_network %d", ap);
ret = wpa_command(ctrl_conn, cmd);
if (ret)
{
continue;
}
}
}
StdinDevExit();
os_free(ctrl_ifname);
wpa_cli_cleanup();
return ret;
}
#else /* CONFIG_CTRL_IFACE */
int main(int argc, char *argv[])
{
printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
return -1;
}
#endif /* CONFIG_CTRL_IFACE */