3.第一个字符设备驱动(虚拟设备)框架搭建、驱动模块加载、驱动函数实现、应用程序编写; |
您所在的位置:网站首页 › 内核驱动模块程序结构设计 › 3.第一个字符设备驱动(虚拟设备)框架搭建、驱动模块加载、驱动函数实现、应用程序编写; |
一、字符设备驱动框架
具体的对应关系见上一篇文章,这里只对需要实现的部分进行说明 2.字符设备驱动开发基础(一个虚拟的字符设备驱动开发流程) 字符设备驱动编写,主要工作就是驱动对应的open close read write函数的编写说白了,就是 对file_operations结构体的成员变量的实现; 在linux内核代码中,file_operations的定义在/include/linux/fs.h这在个.h文件中定义了很多对于linux文件很重要的概念,包括inode、file_operations等等;
在当前目录下创建.vscode文件夹,其下创建如下设置当前目录的头文件搜索路径(注意选择自己的内核源码路径) c_cpp_properties.json { "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/home/qjy/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include", "/home/qjy/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include", "/home/qjy/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include/generated/" ], "defines": [], "compilerPath": "/usr/bin/clang", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "clang-x64" } ], "version": 4 } 1.2 驱动模块加载卸载测试(仅含有模块注册和卸载函数的驱动测试,具体框架在1.4中)内容:编写一个仅含有驱动的注册和卸载函数的.c文件,将其编译成驱动模块后加载到内核,观察是否加载成功 目的:验证环境搭建是否存在问题; printk()的头文件:#include chrdevbase.c #include #include static int __init chrdevbase_init(void) { printk("chrdevbase_init\r\n"); return 0; } static void __exit chrdevbase_exit(void) { printk("chrdevbase_exit\r\n"); } /** * 模块入口与出口 */ module_init(chrdevbase_init); module_exit(chrdevbase_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("QJY"); 1.3 Makefile KERNELDIR := /home/qjy/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd) obj-m := chrdevbase.o build: kernel_modules kernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean编译之后的目录为:(编译——拷贝到ubuntu nfs下的rootfs中) 开发板中加载模块: 下图所示是本实验使用的字符设备驱动框架,并不完善; 驱动的加载有两种方式: 直接写进内核文件:编译内核后内核(zImage)与驱动融为一体编译成.ko模块文件,使用modprobe指令加载模块;(我们使用这种)第二种方法的好处在于,方便调试,可以随时加载和卸载模块,而不是重新编译整个内核文件; 2.1 开发板的准备工作(alpha-i.mx6ull) 启动:uboot代码编译后,烧写在sd卡中,开发板从sd卡启动内核zImage和设备树文件.dtb:使用tftp从ubuntu中读取;rootfs根文件系统在ubuntu中,使用nfs挂载;设置uboot变量 bootargs 和 bootcmd完成2、3的设置;下面是设置之后的uboot环境变量 => print baudrate=115200 board_name=EVK board_rev=14X14 boot_fdt=try bootargs=console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.5.103:/home/qjy/linux/nfs/rootfs ip=192.168.5.111:192.168.5.103:192.168.5.1:255.255.255.0::eth0:off bootcmd=tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000 bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; bootdelay=3 bootscript=echo Running bootscript from mmc ...; source console=ttymxc0 ethact=FEC1 ethaddr=00:04:9f:04:d2:35 ethprime=FEC fdt_addr=0x83000000 fdt_file=imx6ull-alientek-emmc.dtb fdt_high=0xffffffff fileaddr=83000000 filesize=8d32 findfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi; gatewayip=192.168.5.1 image=zImage initrd_addr=0x83800000 initrd_high=0xffffffff ip_dyn=yes ipaddr=192.168.5.111 loadaddr=0x80800000 loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file} loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot} mmcautodetect=yes mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; mmcdev=0 mmcpart=1 mmcroot=/dev/mmcblk0p2 rootwait rw netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; netmask=255.255.255.0 panel=TFT7016 script=boot.scr serverip=192.168.5.103如果重新配置,需要更新的环境变量为:bootcmd、bootargs、ipaddr、ethaddr、gatewayip、netmask、serverip; 2.2 驱动模块的加载卸载将编译出来的.ko模块放到根文件系统中; 使用depmod指令分析模块依赖心,为使用modprobe准备; 使用modprobe / insmod指令完成对模块的加载; 使用modprobe -r / rmmod指令完成对模块的卸载; 使用lsmod查看已经加载的模块; 如果使用modprobe出现问题,见 Linux驱动加载问题“.ko模块无法加载modprobe: module ‘xxx.ko’ not found”解决方法 Linux depmod命令用于分析可载入模块的相依性。 depmod(depend module)可检测模块的相依性,供modprobe在安装模块时使用。 3. 应用程序的编写 /** * 本文件对应驱动模块CharDevBase的测试APP * argv[1] : filename:要进行互动的驱动设备名称(通过/dev/ * argv[2] : 1:表示从驱动中读取数据操作; 2:表示向驱动中写数据操作 */ #include #include #include #include #include #include #include #include #define READ_SIZE 50 #define WRITE_SIZE 50 int main(int argc,char* argv[]) { int fd; int read_ret = 0, write_ret = 0; char read_buf[100]; // 用户空间读取数据缓冲区 char write_buf[100]; // 用户空间写入数据缓冲区 char write_data[] = "Im the written data, I come from UserSapce!\n"; memcpy(write_buf,write_data, sizeof(write_data)); if(argc != 3){ printf("参数不足!\n"); return -1; } fd = open(argv[1], O_RDWR); if(fd // 读操作 if((read_ret = read(fd, read_buf,READ_SIZE)) == -1){ printf("Now in userSpace: reading error~\n"); return errno; }else{ printf("Now in userSpace: the received data: %s\n", read_buf); } } if(atoi(argv[2]) == 2){// 写操作 if((write_ret = write(fd, write_buf,WRITE_SIZE)) == -1){ printf("Now in userSpace: reading error~\n"); return errno; } } usleep(200); if(-1 == close(fd)){ printf("Now in userSpace: close error!\n"); return errno; } return 0; } 3.1编译应用程序并将.ko与App文件拷贝到rootfs中注意,驱动模块的编译方法在之前的makefile中 arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp sudo cp ./*.ko ./chrdevbaseApp ../../../nfs/rootfs/lib/modules/4.1.15 4. 测试 4.1 首先在系统中创建设备结点 4.2 测试驱动程序 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |