本文代码参考 RT-Thread 官方 BSP
实验功能
例程源码:(main 函数)
WiFi 扫描和连接的底层代码并未开源,所以这个实验只能用来学习它的代码框架。。。
int main(void)
{
int result = RT_EOK;
struct rt_wlan_info info;
struct rt_wlan_scan_result *scan_result;
rt_hw_wlan_wait_init_done(500);
LOG_D("start to scan ap ...");
scan_result = rt_wlan_scan_sync();
if (scan_result)
{
LOG_D("the scan is complete, results is as follows: ");
print_scan_result(scan_result);
rt_wlan_scan_result_clean();
}
else
{
LOG_E("not found ap information ");
return -1;
}
LOG_D("start to connect ap ...");
rt_sem_init(&net_ready, "net_ready", 0, RT_IPC_FLAG_FIFO);
rt_wlan_register_event_handler(RT_WLAN_EVT_READY, wlan_ready_handler, RT_NULL);
rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED, wlan_station_disconnect_handler, RT_NULL);
result = rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);
if (result == RT_EOK)
{
rt_memset(&info, 0, sizeof(struct rt_wlan_info));
rt_wlan_get_info(&info);
LOG_D("station information:");
print_wlan_information(&info);
result = rt_sem_take(&net_ready, NET_READY_TIME_OUT);
if (result == RT_EOK)
{
LOG_D("networking ready!");
msh_exec("ifconfig", rt_strlen("ifconfig"));
}
else
{
LOG_D("wait ip got timeout!");
}
rt_wlan_unregister_event_handler(RT_WLAN_EVT_READY);
rt_sem_detach(&net_ready);
}
else
{
LOG_E("The AP(%s) is connect failed!", WLAN_SSID);
}
rt_thread_mdelay(5000);
LOG_D("ready to disconect from ap ...");
rt_wlan_disconnect();
LOG_D("start to autoconnect ...");
wlan_autoconnect_init();
rt_wlan_config_autoreconnect(RT_TRUE);
return 0;
}
代码剖析
rt_hw_wlan_wait_init_done()
这只是一个判断 WiFi 模块是否初始化完成的函数。
int rt_hw_wlan_wait_init_done(rt_uint32_t time_ms)
{
rt_uint32_t time_cnt = 0;
while (time_cnt <= (time_ms / 100))
{
time_cnt++;
rt_thread_mdelay(100);
if (rt_hw_wlan_get_initialize_status() == 1)
{
break;
}
}
if (time_cnt > (time_ms / 100))
{
return -RT_ETIMEOUT;
}
return RT_EOK;
}
rt_hw_wlan_get_initialize_status()
int rt_hw_wlan_get_initialize_status(void)
{
return init_flag;
}
LOG_D()
本实验中,我们可以将 LOG_D() 视为 rt_kprintf() ,
#define dbg_log_line(lvl, color_n, fmt, ...) \
do \
{ \
_DBG_LOG_HDR(lvl, color_n); \
rt_kprintf(fmt, ##__VA_ARGS__); \
_DBG_LOG_X_END; \
} \
while (0)
LOG_D 是 RT-Thread 内核里的一个日志打印函数,详情可见:《RT-Thread 文档中心——ulog 日志》
RT-Thread 的日志 API 包括:
rt_wlan_scan_sync()
RT-Thread 的 wifi 同步扫描函数,位于 wlan_mgnt.c 文件中,
struct rt_wlan_scan_result *rt_wlan_scan_sync(void)
{
struct rt_wlan_scan_result *result;
MGNT_LOCK();
result = rt_wlan_scan_with_info(RT_NULL);
MGNT_UNLOCK();
return result;
}
rt_wlan_scan_with_info()
该函数可以根据指定的 WiFi 信息进行扫描,信息放在 struct rt_wlan_info 结构体成员中,
struct rt_wlan_scan_result *rt_wlan_scan_with_info(struct rt_wlan_info *info)
{
rt_err_t err = RT_EOK;
struct rt_wlan_complete_des *complete;
rt_uint32_t set = 0, recved = 0;
if (_sta_is_null())
{
return RT_NULL;
}
RT_WLAN_LOG_D("%s is run", __FUNCTION__);
if (info != RT_NULL && info->ssid.len > RT_WLAN_SSID_MAX_LENGTH)
{
RT_WLAN_LOG_E("ssid is to long!");
return RT_NULL;
}
MGNT_LOCK();
complete = rt_wlan_complete_create("scan");
if (complete == RT_NULL)
{
MGNT_UNLOCK();
return &scan_result;
}
err = rt_wlan_dev_scan(STA_DEVICE(), info);
if (err != RT_EOK)
{
rt_wlan_complete_delete(complete);
RT_WLAN_LOG_E("scan sync fail");
MGNT_UNLOCK();
return RT_NULL;
}
set |= 0x1 << RT_WLAN_DEV_EVT_SCAN_DONE;
rt_wlan_complete_wait(complete, set, RT_WLAN_CONNECT_WAIT_MS, &recved);
rt_wlan_complete_delete(complete);
set = 0x1 << RT_WLAN_DEV_EVT_SCAN_DONE;
if (!(recved & set))
{
RT_WLAN_LOG_E("scan wait timeout!");
MGNT_UNLOCK();
return &scan_result;
}
MGNT_UNLOCK();
return &scan_result;
}
rt_wlan_complete_create()
struct rt_wlan_complete_des 里面封装了一个 RT-Thread 事件(通信方式),该函数的作用是创建(初始化)一个 WiFi 事件的结构体变量,比如下面代码中创建了一个名为 “scan” 的 WiFi 事件。
static struct rt_wlan_complete_des *rt_wlan_complete_create(const char *name)
{
struct rt_wlan_complete_des *complete;
int i;
complete = rt_malloc(sizeof(struct rt_wlan_complete_des));
if (complete == RT_NULL)
{
RT_WLAN_LOG_E("complete event create failed");
MGNT_UNLOCK();
return complete;
}
rt_event_init(&complete->complete, name, RT_IPC_FLAG_FIFO);
complete->event_flag = 0;
COMPLETE_LOCK();
for (i = 0; i < sizeof(complete_tab) / sizeof(complete_tab[0]); i++)
{
if (complete_tab[i] == RT_NULL)
{
complete->index = i;
complete_tab[i] = complete;
break;
}
}
COMPLETE_UNLOCK();
if (i >= sizeof(complete_tab) / sizeof(complete_tab[0]))
{
rt_event_detach(&complete->complete);
rt_free(complete);
complete = RT_NULL;
}
return complete;
}
rt_wlan_dev_scan()
该函数最后会调用 rt_device_control() 函数,这是一个命令控制函数,可以根据命令参数的不同而执行不同的任务。
rt_err_t rt_wlan_dev_scan(struct rt_wlan_device *device, struct rt_wlan_info *info)
{
struct rt_scan_info scan_info = { 0 };
struct rt_scan_info *p_scan_info = RT_NULL;
rt_err_t result = 0;
if (device == RT_NULL)
{
return -RT_EIO;
}
if (info != RT_NULL)
{
if (info->ssid.len >= RT_WLAN_SSID_MAX_LENGTH)
{
LOG_E("L:%d ssid is to long", __LINE__);
return -RT_EINVAL;
}
rt_memcpy(&scan_info.ssid, &info->ssid, sizeof(rt_wlan_ssid_t));
rt_memcpy(scan_info.bssid, info->bssid, RT_WLAN_BSSID_MAX_LENGTH);
scan_info.channel_min = -1;
scan_info.channel_max = -1;
p_scan_info = &scan_info;
}
result = rt_device_control(RT_DEVICE(device), RT_WLAN_CMD_SCAN, p_scan_info);
return result;
}
在 wlan_dev.c 中,定义了设备驱动的函数操作集合(结构体):
#ifdef RT_USING_DEVICE_OPS
wlan->device.ops = &wlan_ops;
#else
wlan->device.init = _rt_wlan_dev_init;
wlan->device.open = RT_NULL;
wlan->device.close = RT_NULL;
wlan->device.read = RT_NULL;
wlan->device.write = RT_NULL;
wlan->device.control = _rt_wlan_dev_control;
#endif
_rt_wlan_dev_control()
该函数太大,只贴出了一部分代码,当命令为 RT_WLAN_CMD_SCAN 时,会调用 wlan 设备的底层驱动 wlan_scan(),
static rt_err_t _rt_wlan_dev_control(rt_device_t dev, int cmd, void *args)
{
struct rt_wlan_device *wlan = (struct rt_wlan_device *)dev;
rt_err_t err = RT_EOK;
RT_ASSERT(dev != RT_NULL);
WLAN_DEV_LOCK(wlan);
switch (cmd)
{
case RT_WLAN_CMD_MODE:
{
rt_wlan_mode_t mode = *((rt_wlan_mode_t *)args);
LOG_D("%s %d cmd[%d]:%s run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_MODE, "RT_WLAN_CMD_MODE");
if (wlan->ops->wlan_mode)
err = wlan->ops->wlan_mode(wlan, mode);
break;
}
case RT_WLAN_CMD_SCAN:
{
struct rt_scan_info *scan_info = args;
LOG_D("%s %d cmd[%d]:%s run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_SCAN, "RT_WLAN_CMD_SCAN");
if (wlan->ops->wlan_scan)
err = wlan->ops->wlan_scan(wlan, scan_info);
break;
}
...
...
...
wlan->ops->wlan_scan(wlan, mode) 的具体实现应该是闭源的。
rt_wlan_connect()
WiFi 连接函数,主要进行也参数检查和事件初始化(连接和断开事件)。
rt_err_t rt_wlan_connect(const char *ssid, const char *password)
{
rt_err_t err = RT_EOK;
int ssid_len = 0;
struct rt_wlan_info info;
struct rt_wlan_complete_des *complete;
rt_uint32_t set = 0, recved = 0;
if (_sta_is_null())
{
return -RT_EIO;
}
RT_WLAN_LOG_D("%s is run ssid:%s password:%s", __FUNCTION__, ssid, password);
if (ssid == RT_NULL)
{
RT_WLAN_LOG_E("ssid is null!");
return -RT_EINVAL;
}
ssid_len = rt_strlen(ssid);
if (ssid_len > RT_WLAN_SSID_MAX_LENGTH)
{
RT_WLAN_LOG_E("ssid is to long! ssid:%s len:%d", ssid, ssid_len);
return -RT_EINVAL;
}
if ((rt_wlan_is_connected() == RT_TRUE) &&
(rt_strcmp((char *)&_sta_mgnt.info.ssid.val[0], ssid) == 0))
{
RT_WLAN_LOG_I("wifi is connect ssid:%s", ssid);
return RT_EOK;
}
INVALID_INFO(&info);
MGNT_LOCK();
if (rt_wlan_find_best_by_cache(ssid, &info) != RT_TRUE)
{
rt_wlan_scan_sync();
rt_wlan_find_best_by_cache(ssid, &info);
rt_wlan_scan_result_clean();
}
if (info.ssid.len <= 0)
{
RT_WLAN_LOG_W("not find ap! ssid:%s", ssid);
MGNT_UNLOCK();
return -RT_ERROR;
}
RT_WLAN_LOG_D("find best info ssid:%s mac: %02x %02x %02x %02x %02x %02x",
info.ssid.val, info.bssid[0], info.bssid[1], info.bssid[2], info.bssid[3], info.bssid[4], info.bssid[5]);
complete = rt_wlan_complete_create("join");
if (complete == RT_NULL)
{
MGNT_UNLOCK();
return -RT_ENOMEM;
}
err = rt_wlan_connect_adv(&info, password);
if (err != RT_EOK)
{
rt_wlan_complete_delete(complete);
MGNT_UNLOCK();
return err;
}
set |= 0x1 << RT_WLAN_DEV_EVT_CONNECT;
set |= 0x1 << RT_WLAN_DEV_EVT_CONNECT_FAIL;
rt_wlan_complete_wait(complete, set, RT_WLAN_CONNECT_WAIT_MS, &recved);
rt_wlan_complete_delete(complete);
set = 0x1 << RT_WLAN_DEV_EVT_CONNECT;
if (!(recved & set))
{
RT_WLAN_LOG_I("wifi connect failed!");
MGNT_UNLOCK();
return -RT_ERROR;
}
MGNT_UNLOCK();
return err;
}
rt_wlan_connect_adv()
更底层的 WiFi 连接函数,首先进行了参数检查,然后判断 WiFi 热点是否已经连接(已连接就直接退出),保存热点名和密码,最后调用真正的 WiFi 连接函数 rt_wlan_dev_connect(),
rt_err_t rt_wlan_connect_adv(struct rt_wlan_info *info, const char *password)
{
int password_len = 0;
rt_err_t err = RT_EOK;
if (_sta_is_null())
{
return -RT_EIO;
}
if (info == RT_NULL)
{
RT_WLAN_LOG_E("info is null!");
return -RT_EINVAL;
}
RT_WLAN_LOG_D("%s is run ssid:%s password:%s", __FUNCTION__, info->ssid.val, password);
if (password != RT_NULL)
{
password_len = rt_strlen(password);
if (password_len > RT_WLAN_PASSWORD_MAX_LENGTH)
{
RT_WLAN_LOG_E("password is to long! password:%s len:%d", password, password_len);
return -RT_EINVAL;
}
}
if (info->ssid.len == 0 || info->ssid.len > RT_WLAN_SSID_MAX_LENGTH)
{
RT_WLAN_LOG_E("ssid is zero or to long! ssid:%s len:%d", info->ssid.val, info->ssid.len);
return -RT_EINVAL;
}
MGNT_LOCK();
if (rt_wlan_is_connected())
{
if ((_sta_mgnt.info.ssid.len == info->ssid.len) &&
(_sta_mgnt.key.len == password_len) &&
(rt_memcmp(&_sta_mgnt.info.ssid.val[0], &info->ssid.val[0], info->ssid.len) == 0) &&
(rt_memcmp(&_sta_mgnt.info.bssid[0], &info->bssid[0], RT_WLAN_BSSID_MAX_LENGTH) == 0) &&
(rt_memcmp(&_sta_mgnt.key.val[0], password, password_len) == 0))
{
RT_WLAN_LOG_I("wifi Already Connected");
MGNT_UNLOCK();
return RT_EOK;
}
err = rt_wlan_disconnect();
if (err != RT_EOK)
{
MGNT_UNLOCK();
return err;
}
}
rt_enter_critical();
_sta_mgnt.info = *info;
rt_memcpy(&_sta_mgnt.key.val, password, password_len);
_sta_mgnt.key.len = password_len;
_sta_mgnt.key.val[password_len] = '\0';
rt_exit_critical();
_sta_mgnt.state |= RT_WLAN_STATE_CONNECTING;
err = rt_wlan_dev_connect(_sta_mgnt.device, info, password, password_len);
if (err != RT_EOK)
{
rt_enter_critical();
rt_memset(&_sta_mgnt.info, 0, sizeof(struct rt_wlan_ssid));
rt_memset(&_sta_mgnt.key, 0, sizeof(struct rt_wlan_key));
rt_exit_critical();
_sta_mgnt.state &= ~RT_WLAN_STATE_CONNECTING;
MGNT_UNLOCK();
return err;
}
MGNT_UNLOCK();
return err;
}
rt_wlan_dev_connect()
该函数在参数检查后,使用 rt_device_control() 来实现 WiFi 的连接,这个函数上文已经介绍,只是这里的命令换成了 RT_WLAN_CMD_JOIN,
rt_err_t rt_wlan_dev_connect(struct rt_wlan_device *device, struct rt_wlan_info *info, const char *password, int password_len)
{
rt_err_t result = RT_EOK;
struct rt_sta_info sta_info;
if (device == RT_NULL)
{
return -RT_EIO;
}
if (info == RT_NULL)
{
return -RT_ERROR;
}
if ((password_len > RT_WLAN_PASSWORD_MAX_LENGTH) ||
(info->ssid.len > RT_WLAN_SSID_MAX_LENGTH))
{
LOG_E("L:%d password or ssid is to long", __LINE__);
return -RT_ERROR;
}
rt_memset(&sta_info, 0, sizeof(struct rt_sta_info));
rt_memcpy(&sta_info.ssid, &info->ssid, sizeof(rt_wlan_ssid_t));
rt_memcpy(sta_info.bssid, info->bssid, RT_WLAN_BSSID_MAX_LENGTH);
if (password != RT_NULL)
{
rt_memcpy(sta_info.key.val, password, password_len);
sta_info.key.len = password_len;
}
sta_info.channel = info->channel;
sta_info.security = info->security;
result = rt_device_control(RT_DEVICE(device), RT_WLAN_CMD_JOIN, &sta_info);
return result;
}
和 WiFi 扫描一样,WiFi 连接的底层代码未开源。
case RT_WLAN_CMD_JOIN:
{
struct rt_sta_info *sta_info = args;
LOG_D("%s %d cmd[%d]:%s run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_JOIN, "RT_WLAN_CMD_JOIN");
if (wlan->ops->wlan_join)
err = wlan->ops->wlan_join(wlan, sta_info);
break;
}
rt_thread_mdelay()
这是 RT-Thread 的毫秒级延时函数,定义如下:
rt_err_t rt_thread_mdelay(rt_int32_t ms)
{
rt_tick_t tick;
tick = rt_tick_from_millisecond(ms);
return rt_thread_sleep(tick);
}
rt_tick_from_millisecond()
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER;
}
else
{
tick = RT_TICK_PER_SECOND * (ms / 1000);
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
}
return tick;
}
|