10.重写最简单的驱动——打印驱动

    xiaoxiao2025-02-03  50

    1.模仿参考

    新建myled.c,参考Myleds.c,复制他的一些函数代码修改函数名(Myleds.c是我之前自己写的,没有的话按照下面图片写open和write函数就行)

    如:

    2.定义结构体file_operations

    3.把结构体告诉内核

    我们定义了这个结构体,但是没有用起来,所以要告诉内核,怎么告诉,需要一个系统函数register_chrdev

    原型:int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

    参数1:主设备号

    参数2:设备名

    参数3:file_operations结构体

    如:

    4.驱动的入口函数

    那么我们用register_chrdev来注册,register_chrdev被谁调用呢,被入口函数调用,怎么写入口函数

    我们想,内核中可能有很多驱动入口函数,如seconddrv,thirddrv等,内核怎么分辨这些入口函数

    ***就要修饰一下,用宏module_init

    module_init(入口函数名);

    什么是module_init

    当模块被加载时,执行moudle_init函数,该函数会调用初始化函数  

    module_init是定义一个结构体,这个结构体里面有一个函数指针,指向这个入口地址,当我们加载一个驱动,或者安装一个驱动时,内核就会自动去找到这么一个结构体,去指向这个入口函数,然后入口函数就用register_chrdev注册,告诉内核这个file_operations结构体。

    5.看 register_chrdev

    我们研究register_chrdev这个函数,major是主设备号,当我们在终端上输入:ls  /dev  -l 时,可以看到c是指字符设备,7是主设备号,0-...是次设备号

    主设备就是为了让应用程序可以找到file_operationsd的open成员的标识

    如,对于字符设备驱动:

    app:  open("/dev/xxx",...);

     

    /dev/xxx有上面图片那种属性:类似crw-rw....,major(主),mior(次)

     

    app的open函数打开/dev/xxx,就是通过主次设备号去找到register_chrdev中的file_operations的open函数,进行上层到底层的操作

     

    register_chrdev是通过在内存里用一个数组,以major为索引,把file_operations挂接上去

    6.具体流程:

    7.完善驱动程序

    (1)有入口函数,也有出口函数,参考Myleds.c

     

    (2)我们只是简答写一个驱动程序,打印即可,内核打印是用printk

     

    (2)包含头文件,参考Myleds.c

    #include <linux/module.h>

    #include <linux/kernel.h>

    #include <linux/fs.h>

    #include <linux/init.h>

    #include <linux/delay.h>

    #include <asm/uaccess.h>

    #include <asm/irq.h>

    #include <asm/io.h>

    #include <asm/arch/regs-gpio.h>

    #include <asm/hardware.h>

    9.用ftp传到虚拟机上,编写Makefile

    KERN_DIR = /work/system/linux-2.6.22.6         //内核目录 all:         make -C $(KERN_DIR) M=`pwd` modules    clean:         make -C $(KERN_DIR) M=`pwd` modules clean         rm -rf modules.order obj-m   += myled.o //依赖文件

    执行make后生成myled.ko文件

    10.代码:

    #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static int firstdrv_led_open(struct inode *inode, struct file *file) { printk("firstdrv_led_open\n"); return 0; } static ssize_t firstdrv_led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("firstdrv_led_write\n"); return 0; } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操作设备文件时所调用的open、read、write等函数, * 最终会调用这个结构中指定的对应函数 */ static struct file_operations firstdrv_led_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = firstdrv_led_open, .write = firstdrv_led_write, }; int major; int firstdrv_led_init(void) { major =register_chrdev(0,"myled",&firstdrv_led_fops);//注册设备myled,告诉内核,注冊设备时给设备号写0,则内核会自己主动分配一个主设备号返回。 return 0; } void firstdrv_led_exit(void) { unregister_chrdev(major,"first_drv");//卸载 } module_init(firstdrv_led_init); module_exit(firstdrv_led_exit); MODULE_LICENSE("GPL");

    11.测试

    编写测试程序mylec_test.c

    #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("can't open!\n"); } write(fd, &val, 4); return 0; }

    在虚拟机上用交叉编译工具生成可执行文件

    #arm-linux-gcc -o mylec_test mylec_test.c

    把mylec.ko 和mylec_test拷贝到络文件系统

    在开发版终端上执行

    发现不能输出打印信息,也没有/dev/myled的设备文件。原因是我们没有设置设备节点,有分手动设置和自动设置。这里我们先讲用mknod手动分配设备节点

    我们所写的代码只是注册了一个名为myled的驱动,并通过register_chrdev让内核帮我们分配主设备号,可以使用                    cat /proc/devices 查看内核以后主设备号

    发现内核帮我们把设备号设置为252,根据这个使用mknod手动分配设备节点

    在开发版的终端上用 mknod /dev/xxx c 主设备号  次设备号(删除设备号为 rm  /dev/xxx)

    这个时候就可以执行可执行文件了,成功打印

    这就是最简单的驱动程序了,属于手动分配设备节点。下一节讲使用udev自动分配设备节点,使用

    class_create为该设备创建一个class,再为每个设备调用 device_create创建对应的设备。

    加载模块后,会自动在/dev/下创建myled设备文件。

    最新回复(0)