学习驱动也有长达一年多的时间了,受益最深的就是看韦东山老师的视频,如今已经几乎将二期三期的视频全部看完,甚至已经将二期视频看过好几遍,为了再次加深印象,我将韦老师的源码自己全部编写一遍。将所有遇到的问题,记录在此。觉得看了韦老师的视频,再看其他视频都是弱爆了。由于是文章记录,不可能写的非常详细,只摘录关键点,想具体详细的深入,还请去看韦老大的视频吧。
这篇文章是主要是讲解字符驱动的框架,并没有涉及高级字符驱动。
一、字符驱动框架
------------------------------------------------------------------------
APP: open read write
------------------------------------------------------------------------
C 库
------------------------------------------------------------------------
system_open system_read system_write
------------------------------------------------------------------------
KERNEL:
led_open led_read led_wirte
------------------------------------------------------------------------
问:应用程序open如何找到驱动程序的open函数
答:应用程序的open通过C库的open函数,通过系统调用的system_open函数,进而通过swi val指令,进入内核,通过一定的办法来找到驱动程序的open函数。
问:通过什么样的方法来找到驱动程序的open函数
答:通过一个注册函数+设备节点
注册函数如下(旧的注册函数,新的以后再说):
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
参数1:主设备号(重要)
参数2:名字(不重要)
参数3:file_operations结构体(重要)
设备节点:
可以手工创建也可以自动创建,这里暂且只说手工创建
mknod /dev/xxx c 252 0
具体什么含义,我就不多说了,看视频吧,很简单。
问:应用程序一般是由main函数开始执行,那么驱动程序一般是先执行什么?
答:通过一个宏,指定驱动程序的入口函数,当装载驱动时就会执行入口函数。
例如:module_init(first_drv_init); //用于修饰入口函数
自然地,驱动程序的出口函数,则是在卸载驱动时就会执行出口函数。
例如:module_exit(first_drv_exit); //用于修饰出口函数
驱动源程序如下:
#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 <linux/module.h> int major; static int first_drv_open(struct inode * inode, struct file * filp) { printk("first_drv_open\n"); return 0; } static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos) { printk("first_drv_write\n"); return 0; } /* File operations struct for character device */ static const struct file_operations first_drv_fops = { .owner = THIS_MODULE, .open = first_drv_open, .write = first_drv_write, }; /* 驱动入口函数 */ static int first_drv_init(void) { /* 主设备号设置为0表示由系统自动分配主设备号 */ major = register_chrdev(0, "first_drv", &first_drv_fops); return 0; } /* 驱动出口函数 */ static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); } module_init(first_drv_init); //用于修饰入口函数 module_exit(first_drv_exit); //用于修饰出口函数 MODULE_AUTHOR("LWJ"); MODULE_DESCRIPTION("Just for Demon"); MODULE_LICENSE("GPL"); //遵循GPL协议
Makefile源码如下:
KERN_DIR = ~/work/s3c2440/linux/kernel/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 += first_drv.o
测试程序如下
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(void) { int fd; int val = 1; fd = open("/dev/xxx",O_RDWR); if(fd < 0) { printf("open error\n"); } write(fd,&val,4); return 0; }
编译:arm-linux-gcc first_test.c -omain
测试:insmod first_drv.ko
发现:多了一个first_drv的节点
cat /proc/devices
Character devices:
....
252 first_drv
...
这时候直接调用驱动测试程序main,发现报错
# /mnt/s3c2440/linux/driver/net/1/main
open error
解决方法解释上面的mknod /dev/xxx c 252 0,此就是创建一个主设备好252,次设备号0的设备节点
然后调用main,会提示
# /mnt/s3c2440/linux/driver/net/1/main
first_drv_open
first_drv_write
根据http://blog.csdn.net/lwj103862095/article/details/17468587测试验证修改