好长时间没有安静下来想一想人生了, 每天被忙碌的工作和繁琐的家务缠身(超级奶爸, 这个光荣的头衔), 有时候一个人静下来回忆一下过去的经历, 展望一下未来, 也是一种修行.
在上一章中我简单描述了一下TCPIP, 和LWIP方面注意的重点事项, 这些也是项目的主干, 通过主干, 你们可以去研究和领悟细节, 细节这东西是说不清, 理还乱的. 需要大家的悟性. 今天我们同样是来讲一讲RTL8188eu驱动的框架结构( realtek瑞昱的无线芯片), 以及移植重点. realtek是台湾的无线芯片公司, 在深圳有分公司, 主要负责技术支持部, 曾经为了移植这个驱动也和他们讨论过, 也感谢他们的技术支持.
接下来我们来看看移植前的准备工作, 首先你得准备一块RTL8188eu的模组, 大概7块人民币, 焊上匹配的天线和一个USB接口, 这样一个无线WIFI就完成了, 插到PC端, 安装上驱动, 如果电脑能正确识别就说明你焊接的没有问题, 如果有问题, 检查一下你安装的RTL8188eu驱动是否正确, 最后检查焊接是否没有问题. 如果PC出现无线图标, 这样一个大概7元人民币的无线WIFI就完成了, 真够便宜实惠的; 最后你得找到RTL8188eu的源码驱动程序, 这样你才能开干, 一般适应LINUX环境的源码在网上可以找到, 大家可以找下, 我这边使用的LINUX rtl8188eu的源码版本是v4.1.3_5938.20121130, 版本比较低, 但是也足够用了.
得到源码后我们来看看源码的文件夹结构:
如图所示:core文件夹是WIFI芯片的核心部分, 里面有处理发送,接收, 加密等核心底层代码; hal\OUTSRC和硬件底层打交道的东西, 如设置什么寄存器, 烧写固件的二进制之类的东西了, 这个只有芯片原厂才知道的东西了; include里面包括了所有源码的头消息了; os_dep\linux这层主要是和操作系统打交道的, 操作系统通过这里面的标识符识别设备, 挂起设备, 恢复设备, TCP/IP协议通过这里面的文件, 发送以太网帧, 接收以太网帧, 由于我们移植的是USB的, 所以sdio, pci, gspi这些就可以不用了; os_dep\osdep_service.c文件是区分不同操作系统之前接口的函数, 如Windows, linux. 如果要移植到其他系统需要关注这块, 主要是不同系统提供的系统函数不一样的原因.
先来看看入口文件, usb_intf.c, 模块加载和卸载最前线的函数rtw_drv_entry()和rtw_drv_halt(), 这两个函数很简单, 我们需要关心的是里面注册的变量static struct rtw_usb_drv *usb_drv = &rtl8188e_usb_drv;
struct rtw_usb_drv rtl8188e_usb_drv = {
.usbdrv.name = (char*)"rtl8188eu", //又名有份,驱动名字
.usbdrv.probe = rtw_drv_init,<span style="white-space:pre"> </span>//USB内核枚举到设备后就加入到这里来了
.usbdrv.disconnect = rtw_dev_remove, //USB WIFI被别人拔掉了, 需要调用这个函数
.usbdrv.id_table = rtl8188e_usb_id_tbl, //USB内核怎么枚举到我呢? 就是通过这个ID表了
.usbdrv.suspend = rtw_suspend, //USB WIFI没有用到, 加入这里休息下,准备快速响应
.usbdrv.resume = rtw_resume, //USB WIFI休息介绍,开始工作;额
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
.usbdrv.reset_resume = rtw_resume,
#endif
#ifdef CONFIG_AUTOSUSPEND
.usbdrv.supports_autosuspend = 1,
#endif
};
万事开头难, 现在就开了个小头了, 这大从那里着手了, 知道入口了, 就往里面冲了.
先来看rtw_drv_init()函数, 很简单三个主要函数一个全局对象初始化,所有的私有数据, 和两个网络接口的生成.这个三个对象相辅相成彼此联系, 你中有我,我中有你,就是这样一个绯闻关系.下面我们来看看重点函数rtw_usb_if1_init(),贴出精简代码加注释说明, 不喜欢太长影响文章
_adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj,
struct usb_interface *pusb_intf, const struct usb_device_id *pdid)
{
_adapter *padapter = NULL;
struct net_device *pnetdev = NULL;
int status = _FAIL;
//分配内存
if ((padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter))) == NULL) {
goto exit;
}
//你中有我,我中有你
padapter->dvobj = dvobj;
dvobj->if1 = padapter;
//初始化赋值
if((pnetdev = rtw_init_netdev(padapter)) == NULL) {
goto handle_dualmac;
}
SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj));
padapter = rtw_netdev_priv(pnetdev);
//该函数调用rtl8188eu_set_hal_ops()函数, 将芯片底层操作函数赋值给回调函数,供系统调用.
//step 2. hook HalFunc, allocate HalData
hal_set_hal_ops(padapter);
//芯片跑起来和停下来回调 函数赋值,供系统调用
padapter->intf_start=&usb_intf_start;
padapter->intf_stop=&usb_intf_stop;
//这个函数会调用rtl8188eu_set_intf_ops()函数, 赋值些读写芯片数据的回调函数,使用USB控制传输和块传输, 很关键, 重点关注.</span>
//step init_io_priv
rtw_init_io_priv(padapter, usb_set_intf_ops);
//下面三个函数主要是读芯片的版本和软件之类的消息, 如:MAC值.....
//step read_chip_version
rtw_hal_read_chip_version(padapter);
//step usb endpoint mapping
rtw_hal_chip_configure(padapter);
//step read efuse/eeprom data and get mac_addr
rtw_hal_read_chip_info(padapter);
//这步初始化网络接口的消息,需要重点关注
//step 5.
if(rtw_init_drv_sw(padapter) ==_FAIL) {
RT_TRACE(_module_hci_intfs_c_,_drv_err_,("Initialize driver software resource Failed!\n"));
goto free_hal_data;
}
//注册给TCPIP协议栈
//step 6. /* Tell the network stack we exist */
if (register_netdev(pnetdev) != 0) {
RT_TRACE(_module_hci_intfs_c_,_drv_err_,("register_netdev() failed\n"));
goto free_hal_data;
}
//成功处理
status = _SUCCESS;
//失败处理
free_hal_data:
if(status != _SUCCESS && padapter->HalData)
rtw_mfree(padapter->HalData, sizeof(*(padapter->HalData)));
free_wdev:
if(status != _SUCCESS) {
#ifdef CONFIG_IOCTL_CFG80211
rtw_wdev_free(padapter->rtw_wdev);
#endif
}
handle_dualmac:
if (status != _SUCCESS)
rtw_handle_dualmac(padapter, 0);
free_adapter:
if (status != _SUCCESS) {
if (pnetdev)
rtw_free_netdev(pnetdev);
else if (padapter)
rtw_vmfree((u8*)padapter, sizeof(*padapter));
padapter = NULL;
}
exit:
return padapter;
}
今天就到这里了, 下一章就重点讲一下, 上面重点关注的函数,及其展开的东西. 不是很喜欢打字, 有点累了, 休息下.