字符驱动设备(1)

概念:按字节流进行读写操作的设备,读写数据分先后。

举例:led、button、I2C、SPI、LCD等。

Linux应用程序驱动程序的调用:

Linux一切皆文件,驱动程序加载成功之后就会在"/dev"目录下生成一个相对应的文件,应用程序通过对这个"/dev/xxx"进行open(),read(),write(),close()操作即可实现对硬件的操作。

例如:/dev/led驱动文件,打开和关闭使用open()函数和close()函数,如果要打开led/关闭led那么就使用write()函数向驱动中写入数据,这个数据就是打开/关闭led的控制参数。如果想要获取led的状态,就使用read()函数从驱动中读取状态。

应用程序运行于用户空间,而Linux驱动属于内核的一部分,运行于内核空间

当我们在用户空间想要实现对内核的操作,比如使用open()函数打开"/dev/led"驱动文件,因为用户空间不能直接对内核进行操作,而必须使用一个叫做"系统调用"的方法来实现用户空间陷入内核空间,这样才能实现对底层驱动的操作。

 

 

应用程序使用到的函数在具体驱动中都有与之对应的函数。比如,应用程序中调用open这个函数,那么在驱动程序也得有一个open函数。每一个"系统调用",在具体驱动中都有与之对应的一个驱动函数。Linux内核下include/linux/fs.h中有个叫做file_operations的结构体,此结构体就是Linux内核驱动操作函数集合,内容如下:

struct file_operations {
    struct module *owner;//拥有该结构体模块的指针,一般设置为THIS_MODULE
    loff_t (*llseek) (struct file *, loff_t, int);//用于修改文件当前的读写位置
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//读取设备文件
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备文件写入数据
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);//轮询函数,用于查询设备是否可以进行非阻塞的读写
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);//将设备的内存映射到进程空间,也就是用户空间
    int (*open) (struct inode *, struct file *);//用于打开设备文件
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);//释放关闭设备文件
    int (*fsync) (struct file *, struct dentry *, int datasync);//用于刷新待处理的数据,将缓冲区的数据刷新到磁盘中
    int (*aio_fsync) (struct kiocb *, int datasync);//与fsync类似,用于异步刷新待处理的数据
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*dir_notify)(struct file *filp, unsigned long arg);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};

字符设备驱动开发的步骤:

1.驱动模块的加载和卸载

Linux驱动有两种运行方式,第一种就是将驱动编译进Linux内核中,当Linux启动时就会自动运行驱动程序了。第二种就是将驱动编译成模块(Linux下驱动模块的扩展名为.ko),Linux内核启动以后再使用"insmod"命令加载驱动模块。第二种常用于驱动调试,至于要不要把驱动编译进内核,根据自己的需求决定。

模块有加载和卸载两种操作,我们在编写驱动的时候需要注册这两种操作函数,模块的加载和注册函数如下:

1 module_init(xxx_init);//向内核注册模块加载函数
2 module_exit(xxx_exit);//向内核注册模块卸载函数

参数 xxx_init 和 xxx_exit 就是要注册的具体函数,当使用"insmod"命令加载驱动的时候,xxx_init 函数就会被调用,同理,当使用"rmmod"命令卸载具体驱动时,xxx_exit 就会被调用。

 

posted @ 2020-05-17 18:32  坦率  阅读(245)  评论(0编辑  收藏  举报