Linux设备驱动之USB网卡驱动程序

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 */

你可能感兴趣的:(linux)