写点什么

Linux 驱动开发 _ 数码相册项目、360WIFI 驱动移植介绍

作者:DS小龙哥
  • 2022 年 6 月 05 日
  • 本文字数:9528 字

    阅读完需:约 31 分钟

这篇文章介绍两个知识点: 数码相册要求介绍、贴出数码相册案例代码、介绍 360 随身 WIFI 的驱动移植注意事项。

任务 1: 文件系统本地挂载

下面这是 Linux 下 NFS 文件系统服务器端共享目录配置文件的编写案例。


任务 2:数码相册项目代码

数码相册功能:


【1】支持两种格式图片显示: bmp、jpg


区分两种图片格式,通过后缀名称区分。


【2】支持触摸屏、按键方式翻页(支持前后翻页)


建立双向链表,调用读取目录的函数(opendir),将目录下所有符合要求的图片加入到链表里。


【3】支持三轴加速度计,实现姿态感应。根据三轴加速度的姿态,调整图片的显示方向。


【4】支持图片的自适应: 居中显示,超大尺寸的图片需要自动缩小到屏幕能够显示的大小。


【5】居中显示。


【6】数码相册需要有状态栏: 当前系统的时间信息,当前图片的名称、数量。




倒车影像、USB 摄像头网页视频控制、广告机。


案例代码:


#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>#include <linux/fb.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <string.h>#include <sys/types.h>#include <dirent.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <pthread.h>#include <signal.h>
#include "touch_input.h"#include "key_input.h"#include "mma7660_input.h"#include "framebuffer.h"#include "image_zoom.h"#include "list.h"#include "Imagedecoding.h"
struct PHOTOA_LBUM_INFO photo_album_info; /*重要结构*/
/*函数功能: 处理捕获到的信号*/void exit_sighandler(int sig){ printf("捕获到信号_%d\n",sig); exit(0); /*结束进程*/}
/*函数功能: 图片类型过滤函数返回值: 0表示支持,其他值表示不支持*/int ImageTypeFiltering(char *name){ /*1. 查找后缀*/ char *jpg_p=strstr(name,".jpg"); char *jpeg_p=strstr(name,".jpeg"); char *bmp_p=strstr(name,".bmp"); char *JPG_p=strstr(name,".JPG"); char *JPEG_p=strstr(name,".JPEG"); char *BMP_p=strstr(name,".BMP");
/*2. 过滤后缀*/ if(jpg_p==NULL&&jpeg_p==NULL&&bmp_p==NULL&& JPG_p==NULL&&JPEG_p==NULL&&BMP_p==NULL) { return -1; } /* 123.mp4.mp3 123.c.txt p=strstr("123.c.txt",".c"); p执行 *p=. */ /*3. 名称比较,二次过滤*/ int jpg_p_cmp=1; int jpeg_p_cmp=1; int bmp_p_cmp=1; int JPG_p_cmp=1; int JPEG_p_cmp=1; int BMP_p_cmp=1; if(jpg_p)jpg_p_cmp=strcmp(jpg_p,".jpg"); if(jpeg_p)jpeg_p_cmp=strcmp(jpeg_p,".jpeg"); if(bmp_p)bmp_p_cmp=strcmp(bmp_p,".bmp"); if(JPG_p)JPG_p_cmp=strcmp(JPG_p,".JPG"); if(JPEG_p)JPEG_p_cmp=strcmp(JPEG_p,".JPEG"); if(BMP_p)BMP_p_cmp=strcmp(BMP_p,".BMP");
if(jpg_p_cmp!=0&&jpeg_p_cmp!=0&&bmp_p_cmp!=0&& JPG_p_cmp!=0&&JPEG_p_cmp!=0&&BMP_p_cmp!=0) { return -2; } return 0;}

/*函数功能: 遍历指定的目录,将图片的绝对路径加入到链表函数参数: 传入目录名称函数返回值: 负数表示失败, 大于0表示加入到链表里节点的数量*/int Traverse_Directory(char *dir_name){ int cnt=0; /*1. 先清空链表*/ Remove_ALL_Node(); /*2. 打开目录*/ DIR *dir=opendir(dir_name); if(dir==NULL) { DBUG_PRINTF("%s 目录打开失败!\n",dir_name); return -1; } /*3. 循环遍历目录*/ struct dirent *dir_info=NULL; struct IMAGE_ListStruct *temp=NULL; int len; while(dir_info=readdir(dir)) { /*过滤图片*/ if(ImageTypeFiltering(dir_info->d_name)!=0) { DBUG_PRINTF("不符合要求的文件: %s\n",dir_info->d_name); continue; } cnt++; //给新节点申请空间 temp=malloc(sizeof(struct IMAGE_ListStruct)); //计算文件名称长度(绝对路径): len=strlen(dir_name)+strlen(dir_info->d_name)+1; //给存放文件名称指针申请空间 temp->file_name=malloc(len); strcpy(temp->file_name,dir_name); strcat(temp->file_name,dir_info->d_name); /*4. 添加链表节点*/ AddListNode(ListHead,temp); } return cnt;}
/*函数功能: 删除链表里所有的节点*/int Remove_ALL_Node(void){ struct IMAGE_ListStruct *p=ListHead; //保存头地址 struct IMAGE_ListStruct *tmp=NULL;
if(p==NULL) /*头为空就创建一个头*/ { p=ListHead=CreateListHead(ListHead); } if(p->next==NULL)return 0;/*节点为空不需要删除*/ else { tmp=p; //保存上一个节点的地址---也就是头的地址,因为链表头不需要删除 p=p->next; /*移动到下一个数据节点*/ tmp->next=NULL; /*指定头的下一个节点为NULL*/ } while(p!=NULL) { tmp=p; //保存上一个节点的地址 p=p->next; /*偏移到下一个节点*/
/*删除节点*/ free(tmp->file_name); free(tmp); } return 0;}
/*函数功能: 在屏幕左下角实时显示当前时间*/void *time_pthread_func(void *dev){ time_t sec_time; time_t sec_tmp; struct tm *system_time; char timebuff[20]; while(1) { /*获取本地秒单位时间*/ sec_time=time(NULL); if(sec_tmp!=sec_time) { sec_tmp=sec_time; /*使用时间函数将秒单位时间转为标准时间返回*/ system_time=localtime(&sec_time); /*格式化打印时间*/ strftime(timebuff,20,"%Y-%m-%d %H:%M:%S",system_time); /*清除底状态栏*/ framebuffer_Fill(0,photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,19*GBK_FONT_SIZE/2,photo_album_info.framebuffer_var.yres,0xD1EEEE); /*在指定位置进行显示时间*/ framebuffer_String(0,photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,timebuff); } }}
/*函数功能: GUI界面初始化代码*/void GUI_Init(void){ /*1. 清屏初始化*/ framebuffer_Fill(0,0,photo_album_info.framebuffer_var.xres,photo_album_info.framebuffer_var.yres,0x33ffcc); /*清除整个屏幕*/ framebuffer_Fill(0,0,photo_album_info.framebuffer_var.xres,GBK_FONT_SIZE,0xD1EEEE); /*清除顶状态栏*/ framebuffer_Fill(0,photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,photo_album_info.framebuffer_var.xres,photo_album_info.framebuffer_var.yres,0xD1EEEE); /*清除底状态栏*/ /*2. 项目名称显示(居中显示)*/ framebuffer_String((photo_album_info.framebuffer_var.xres-strlen(PROJECT_NAME)*(GBK_FONT_SIZE/2))/2, 0,PROJECT_NAME);}
int main(int argc,char **argv){ int current_cnt=1; /*保存当前图片的位置*/ struct IMAGE_ListStruct *List_current_p=NULL; /*用于保存节点的位置*/ struct ImageDecodingInfo image_info={0,0,NULL}; /*保存当前图片解码信息*/ int current_x=0; /*显示图片时,x坐标起始位置*/ int current_y=0; /*显示图片时,y坐标起始位置*/ char CurrentNumberStr[50]; /*显示当前数量字符串*/
photo_album_info.lcd_direction=1; /*屏幕默认方向*/ if(argc!=2) { printf("传入的参数格式: ./app <目录名称>\n"); return 0; } /*1. 注册将要捕获的信号*/ signal(SIGINT,exit_sighandler); /*Ctrl+C发出*/ signal(SIGSEGV,exit_sighandler); /*进程产生非法内存访问时发出*/ /*2. 初始化帧缓冲设备*/ if(framebuffer_Device_init(&photo_album_info)!=0)exit(1);
/*3. 字库初始化*/ if(Fontlib_Init()!=0)exit(1); /*4. 创建读取触摸屏坐标的线程*/ pthread_t touch_thread_id; pthread_create(&touch_thread_id,NULL,touch_pthread_func,NULL); pthread_detach(touch_thread_id);
/*5. 创建读取按键值的线程*/ pthread_t key_thread_id; pthread_create(&key_thread_id,NULL,key_pthread_func,NULL); pthread_detach(key_thread_id); /*6. 创建读取三轴加速度计姿态的线程*/ pthread_t mma7660_thread_id; pthread_create(&mma7660_thread_id,NULL,mma7660_pthread_func,NULL); pthread_detach(mma7660_thread_id);
/*7. 创建显示时间的线程*/ pthread_t time_thread_id; pthread_create(&time_thread_id,NULL,time_pthread_func,NULL); pthread_detach(time_thread_id); /*8. 创建链表头*/ ListHead=CreateListHead(ListHead); if(ListHead==NULL) { printf("创建链表头失败!\n"); exit(1); }

while(1) { List_current_p=ListHead; /*保存头地址*/ current_cnt=1; /*位置置1*/ /*遍历指定目录,保存图片名称到链表,并返回图片的总数量*/ photo_album_info.image_number=Traverse_Directory(argv[1]); /*打印链表里的节点信息*/ PintListInfo(ListHead);
/*偏移到数据节点*/ List_current_p=List_current_p->next; while(List_current_p) { /*1. 等待加速计输入事件*/ /*2. 判断图片类型,开始显示图片*/ if(strstr(List_current_p->file_name,".jpg")||strstr(List_current_p->file_name,".jpeg")|| strstr(List_current_p->file_name,".JPG")||strstr(List_current_p->file_name,".JPEG")) { /*2.1 解码JPEG图片*/ if(Decoding_JPEGImage(List_current_p->file_name,&image_info)) { printf("%s图片解码失败!\n",List_current_p->file_name); } } else if(strstr(List_current_p->file_name,".bmp")||strstr(List_current_p->file_name,".BMP")) { /*2.2 解码BMP图片,得到图片信息*/ if(Decoding_BmpImage(List_current_p->file_name,&image_info)) { printf("%s图片解码失败!\n",List_current_p->file_name); } }
/*3. 判断是否解码成功*/ if(image_info.rgb==NULL || image_info.Height==0 || image_info.Width==0) { goto FREE_MEM; /*解码失败,继续下一张*/ }
/*4. 解码成功,在LCD屏上显示*/ /*判断LCD屏剩余尺寸是否足够显示图片,不够显示就缩小*/ if(image_info.Height>photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE*2|| image_info.Width>photo_album_info.framebuffer_var.xres) { int maximum_height; int zoom_proportion; int maximum_width; if(image_info.Height>=image_info.Width) /*如果图片高度大于宽度,那么缩放就以高度为准*/ { maximum_height=photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE*2;/*屏幕最大能够显示的高度*/ zoom_proportion=image_info.Height-maximum_height; /*缩放比例*/ maximum_width=image_info.Width-zoom_proportion; /*宽度根据高度进行比例缩放*/ DBUG_PRINTF("Height:缩放之后的实际尺寸(宽*高):%d*%d\n",maximum_width,maximum_height);
/*再次判断缩放之后图片宽度在LCD屏是否足够显示,如果不够,则进行缩小*/ if(maximum_width>photo_album_info.framebuffer_var.xres) { } } else/*如果图片宽度大于高度,那么缩放就以宽度为准*/ { maximum_width=photo_album_info.framebuffer_var.xres; /*屏幕最大能够显示的宽度*/ zoom_proportion=image_info.Width-maximum_width; /*缩放比例*/ maximum_height=image_info.Height-zoom_proportion; /*高度根据宽度进行比例缩放*/
/*再次判断缩放之后图片高度在LCD屏是否足够显示,如果不够,则进行缩小*/ if(maximum_height>photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE*2) { /*高度以屏幕最大高度为准*/ maximum_height=photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE*2; /*高度缩小了,那么宽度也要按照比例缩小,否则会导致图片变形*/ maximum_width=maximum_height-(maximum_height-photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE*2); } DBUG_PRINTF("Width:缩放之后的实际尺寸(宽*高):%d*%d\n",maximum_width,maximum_height); } /*申请空间存放缩放之后的数据*/ unsigned char *rgb_zoom=malloc(maximum_width*maximum_height*3); if(rgb_zoom==NULL) { perror("存放图片缩放数据的空间申请失败!"); goto FREE_MEM; }
/*将图片按照指定的尺寸进行缩小*/ if(Image_Zoom(image_info.rgb,image_info.Width,image_info.Height,rgb_zoom,maximum_width,maximum_height)!=0) { goto FREE_MEM; }
/*缩放成功,保存缩放之后图片实际参数*/ image_info.Height=maximum_height; image_info.Width=maximum_width; free(image_info.rgb); /*释放源RGB空间*/ image_info.rgb=rgb_zoom; /*指针指向缩放之后的RGB数据空间*/
/*显示图片的坐标起始位置*/ current_x=(photo_album_info.framebuffer_var.xres-maximum_width)/2;; current_y=(photo_album_info.framebuffer_var.yres-maximum_height)/2; } else /*尺寸足够显示,进行居中显示*/ { /*计算显示图片时,xy坐标位置--保证居中显示*/ current_x=(photo_album_info.framebuffer_var.xres-image_info.Width)/2; current_y=(photo_album_info.framebuffer_var.yres-image_info.Height)/2; }
/*清空显示区域*/ framebuffer_Fill(0,GBK_FONT_SIZE,photo_album_info.framebuffer_var.xres,photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,0x33ffcc);
/*在LCD指定位置显示图片*/ DBUG_PRINTF("在LCD显示的位置:x=%d,y=%d\n",current_x,current_y); framebuffer_DisplayImages(current_x, /*横坐标位置*/ current_y, /*纵坐标位置*/ image_info.Width, image_info.Height, image_info.rgb); DBUG_PRINTF("显示: 第%d个节点的数据=%s\n",current_cnt,List_current_p->file_name);
/*图片显示的数量情况*/ sprintf(CurrentNumberStr,"%d/%d",photo_album_info.image_number,current_cnt); /*清除底状态栏*/ framebuffer_Fill((photo_album_info.framebuffer_var.xres-(strlen(CurrentNumberStr)*GBK_FONT_SIZE/2))/2-32, photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE, photo_album_info.framebuffer_var.xres, photo_album_info.framebuffer_var.yres,0xD1EEEE); /*在指定位置显示当前数量*/ framebuffer_String((photo_album_info.framebuffer_var.xres-(strlen(CurrentNumberStr)*GBK_FONT_SIZE/2))/2, photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,CurrentNumberStr); /*在右下角显示当前图片名称*/ framebuffer_String(photo_album_info.framebuffer_var.xres/2+(strlen(CurrentNumberStr)*GBK_FONT_SIZE/2)/2 +GBK_FONT_SIZE*2, photo_album_info.framebuffer_var.yres-GBK_FONT_SIZE,basename(List_current_p->file_name));FREE_MEM: if(image_info.rgb) /*判断空间是否申请成功*/ { free(image_info.rgb); /*释放空间*/ image_info.rgb=NULL; } image_info.Height=0; image_info.Width=0;
/*等待按键信号和触摸屏信号进行翻页 或者有切换屏幕的操作时进行切换屏幕方向*/ while(!photo_album_info.page_direction&&!photo_album_info.lcd_state){} if(photo_album_info.page_direction==1) /*右翻页*/ { List_current_p=List_current_p->next; current_cnt++; /*记录图片位置*/ } else if(photo_album_info.page_direction==2)/*左翻页*/ { if(current_cnt>1) { List_current_p=List_current_p->old; current_cnt--; /*记录图片位置*/ } } photo_album_info.page_direction=0; /*状态清0*/ photo_album_info.lcd_state=0; /*状态清0*/ } } return 0;}
复制代码

任务 3: 360 WIFI 驱动测试、无线工具使用

将 360 WIFI 的驱动资料包拷贝到 Linux 开发共享文件系统路径下。


资料包的下载看下面的链接地址。



将 360WIFI 资料包解压之后得到的文件如下, 每个文件的含义解释如下。



Udhcpc 自动分配 IP 地址的命令,需要的脚本和使用方法




两个脚本里的代码。




WIFI 的配置文件



一键移植文件布局。



[root@wbyq 360wifi]# ls
360wifi_1.sh 360wifi_2.sh bin etc lib mt7601Usta.ko simple.script
[root@wbyq 360wifi]# tree
.
├── 360wifi_1.sh
├── 360wifi_2.sh
├── bin
│ ├── ifrename
│ ├── iwconfig
│ ├── iwevent
│ ├── iwgetid
│ ├── iwlist
│ ├── iwpriv
│ ├── iwspy
│ ├── wpa_cli
│ ├── wpa_supplicant
│ └── wpa_supplicant.conf
├── etc
│ ├── Wireless
│ │ └── RT2870STA
│ │ ├── RT2870STA.dat
│ │ └── RT2870STA.dat~
│ └── wpa_supplicant.conf
├── lib
│ └── libiw.so.29
├── mt7601Usta.ko
└── simple.script
5 directories, 18 files
[root@wbyq 360wifi]#
复制代码


USB 设备匹配的流程


当前 USB 设备插到电脑的 USB 口上之后,USB 产生 USB 中断,USB 主机就会发生特定命令,询问插入


进来的设备,是什么设备。设备会按照 USB 协议的规定,回应一个数据包---结构体。


主机收到数据包之后会进行解析,会按照设备的类型或者设备 ID 进行在内核里寻找与之匹配的驱动,


寻找成功就会调用 USB 驱动程序。


启动脚本连接 WIFI


[root@tiny4412 etc]#vi wpa_supplicant.conf //修改将要连接的路由器名称和密码
[root@tiny4412 etc]#cd /work/360wifi/
[root@tiny4412 360wifi]#ls
360wifi_1.sh bin lib simple.script
360wifi_2.sh etc mt7601Usta.ko
[root@tiny4412 360wifi]#./360wifi_2.sh //运行脚本2,安装WIFI驱动,启动WIFI连接热点,连接上之后再分配IP地址
[ 917.120000] rtusb init rt2870 --->
[ 917.120000] <-- RTMPAllocTxRxRingMemory, Status=0
[ 917.120000] <-- RTMPAllocAdapterBlock, Status=0
[ 917.120000] BULK IN MaxPacketSize = 512
[ 917.120000] EP address = 0x84
[ 917.120000] BULK IN MaxPacketSize = 512
[ 917.120000] EP address = 0x85
[ 917.120000] BULK OUT MaxPacketSize = 512
[ 917.125000] EP address = 0x 8
[ 917.130000] BULK OUT MaxPacketSize = 512
[ 917.130000] EP address = 0x 4
[ 917.135000] BULK OUT MaxPacketSize = 512
[ 917.140000] EP address = 0x 5
[ 917.145000] BULK OUT MaxPacketSize = 512
[ 917.145000] EP address = 0x 6
[ 917.150000] BULK OUT MaxPacketSize = 512
[ 917.155000] EP address = 0x 7
[ 917.155000] BULK OUT MaxPacketSize = 512
[ 917.160000] EP address = 0x 9
[ 917.170000] usbcore: registered new interface driver rt2870
[ 917.350000] Current MAC: =b0:d5:9d:0c:51:29
[ 917.355000] NICReadEEPROMParameters: RxPath = 1, TxPath = 1
[ 917.360000] 20MHz BW, 2.4G band-03030505, Adata = 03030505, Gdata = 03030505
[ 917.365000] 20MHz BW, 2.4G band-00000004, Adata = 00000004, Gdata = 00000004
[ 917.365000] 20MHz BW, 2.4G band-00000002, Adata = 00000002, Gdata = 00000002
[ 917.365000] 20MHz BW, 2.4G band-00000002, Adata = 00000002, Gdata = 00000002
[ 917.370000] 20MHz BW, 2.4G band-ffff0002, Adata = ffff0002, Gdata = ffff0002
[ 917.610000] BuildChannel # 1 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 2 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 3 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 4 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 5 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 6 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 7 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 8 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 9 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 10 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 11 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 12 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 13 :: Pwr0 = 6, Pwr1 =0, Flags = 0
BuildChannel # 14 :: Pwr0 = 6, Pwr1 =0, Flags = 0
<==== rt28xx_init, Status=0
[ 917.665000] 0x1300 = 00064300
udhcpc (v1.23.2) started
Setting IP address 0.0.0.0 on ra0
Sending discover...
[ 920.025000] RSN_IE: f0b15003, len = 22
[ 920.025000] 0x0000 : 30 14 01 00 00 0f ac 04 01 00 00 0f ac 04 01 00
[ 920.025000] 0x0010 : 00 0f ac 02 0c 00
[ 920.035000] /work/360_WIFI/DPO_MT7601U_LinuxSTA_3.0.0.4_20130913/os/linux/../../sta/rtmp_data.c:540 assert pRxWI->RxWIWirelessCliID == BSSID_WCIDfailed
[ 920.090000] Key = 28:fd:b8:58:c5:ce:85:e5:33:97:d8:09:8d:46:c2:6a
[ 920.090000] Rx MIC Key = 00:00:00:00:00:00:00:00
[ 920.090000] Tx MIC Key = 00:00:00:00:00:00:00:00 (表示WIFI已经成功连接热点)
Sending discover...
[ 923.340000] CmdThread : CMDTHREAD_SET_ASIC_WCID : WCID = 1, SetTid = 10000, DeleteTid = ffffffff.
[ 923.340000] 1-MACValue= e02d6594,
[ 923.340000] 2-MACValue= 1701d,
Sending select for 192.168.43.240...
Lease of 192.168.43.240 obtained, lease time 3600
Setting IP address 192.168.43.240 on ra0
Deleting routers
route: SIOCDELRT: No such process
Adding router 192.168.43.1
Recreating /etc/resolv.conf
Adding DNS server 192.168.43.1 //表示WIFI已经成功分配了IP地址
复制代码


查看网卡 IP 地址,ping 百度测试是否可以访问外网



查看当前 WIFI 连接的网卡信息



通过 WIFI 管理工具,扫描周边的热点


[root@tiny4412 360wifi]#iwlist scanning
复制代码


发布于: 刚刚阅读数: 3
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
Linux驱动开发_数码相册项目、360WIFI驱动移植介绍_6月月更_DS小龙哥_InfoQ写作社区