标签:
Linux操作系统将所有的设备都看成文件,以操作文件的方式访问设备,应用程序不能直接操作硬件,而是使用统一的接口函数调用硬件驱动程序。这组接口被称为系统调用。如open,read.对于每一个系统调用,驱动程序中都有一个与之对应的函数,对于字符设备驱动程序,这些函数调用都集合在一个file_operation类型的数据结构中。当应用程序使用open函数打开某个设备时,设备驱动程序的file_operation结构中的open成员就会被调用。从这个角度来说,编写字符设备驱动程序就是为具体的硬件的file_operation结构编写各个函数。
那么当应用程序通过open,read,write等系统调用访问某个设备文件时,linux系统怎么知道去调用哪个驱动程序的file_operation结构中的open,read,write等成员呢?
驱动程序有一个初始化函数,在安装驱动程序时会调用它,在初始化函数中,会将驱动程序的file_operation结果连同其主设备号一起向内核进行注册。如下
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);//参数因此是主设备号,设备名,驱动程序的file_operation.
执行register_chrdev函数后,当应用程序操作设备文件时,linux系统就会根据设备文件的类型(是字符设备还是块设备)、主设备号找到在内核中注册的file_operation结构,
编写字符驱动程序的过程大概如下:
(1)编写驱动程序初始化函数
(2)构造file_operation结构中要用到的各个成员函数
下面仿照别人的驱动写一个小的驱动
first_drv.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>
static int first_drv_open(struct inode *inode, struct file *file)//打开,这里只是简单测试,
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct inode *inode, struct file *file)//写,这里只是简单测试
{
printk("first_drv_write\n");
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open, //指向创建的打开函数
.write = first_drv_write, //指向创建的写函数
};
int major;
static int first_drv_init(void)
{
register_chrdev(111,"first_drv",&first_drv_fops);//注册,若主设备号为0则是让系统、 //分配一个主设备号然后返回
//这个函数就是把first_drv_fops这个结构告诉内核
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(111,”first_drv”);
}
module_init(first_drv_init);//创建一个结构体,里面有first_drv_init函数的地址,当执行insmod //first_drv.ko时,系统会自动找到这个结构体,然后找到里面的函数地址,去执行first_drv_init
module_exit(first_drv_exit);/*创建一个结构体,里面有first_drv_exit函数的地址,当执行rm mod first_drv时,系统会自动找到这个结构体,然后找到里面的函数地址,去执行first_drv_exit*/
再创建一个测试文件
first_drvtest.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[]){
int fd;
int val=1;
fd=open("/dev/xxx",O_RDWR);
if(fd<0)
printf("cant open\n");
write(fd,&val,4);
return 0;
}
然后创建Makefile文件
KERN_DIR = /work/system/linux-2.6.22.6 #内核所在服务器目录,内核要先编译好,因为要依赖内核
all:
make -C $(KERN_DIR) M=`pwd` modules #make -C $(KERN_DIR)转到KERN_DIR目录,用这个目录里面的Makefile进行编译 M=`pwd`表示当前目录 modules是目标
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o #m选项说明是被编译为模块,后缀名为.ko。
然后将执行make 来编译驱动程序 执行arm-linux-gcc first_drvtest.c -o first_drvtest 来编译驱动测试程序
将会生成first_drv.ko 和first_drvtest将这两个文件考到制作的最小根文件所在目录/work/nfs_root/first_fs下。
然后再/work/nfs_root/目录下执行mkyaffs2image first_fs first_fs.yaffs2。
将生成的first_fs first_fs.yaffs2文件 烧到开发板 ,启动系统后,
执行insmod first_drv.ko 加载驱动
mknod /dev/first_drv c 111 0创建设备文件节点
然后执行first_drvtest 就可以看到输出的信息了。
从上面可以看到每次都要创建设备节点很麻烦。
可以通过udev机制自动创建设备节点(busybox中为mdev)
在first_drv.c中做如下修改
增加
static struct class *first_drv_class;//类
static struct class_device *first_drv_class_devs;//设备
static int first_drv_init(void)//加载
{
major=register_chrdev(0,"first_drv",&first_drv_fops);
first_drv_class = class_create(THIS_MODULE, "first_drv");//创建一个类
if (IS_ERR(first_drv_class))
return PTR_ERR(first_drv_class);
first_drv_class_devs = class_device_create(first_drv_class, NULL, MKDEV(major, 0), NULL, "xyz");//创建一个设备 MKDEV(major, 0),主设备,次设备。“xyz”设备名字
/*上面就会在系统sys目录下创建一个first_drv_class 类,在这个类里又会创建xyz这个设备,然后系统就会自动根据这些信息自动的创建/dev/xyz设备节点*/
if (unlikely(IS_ERR(first_drv_class_devs)))
return PTR_ERR(first_drv_class_devs);
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");//卸载;
class_device_unregister(first_drv_class_devs);
class_destroy(first_drv_class);
}
MODULE_LICENSE("GPL");//将license设为GPL
标签:
原文地址:http://blog.csdn.net/u014104588/article/details/45156517