码迷,mamicode.com
首页 > 其他好文 > 详细

字符设备驱动(一)

时间:2015-04-21 09:48:30      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!