1、字符设备驱动简介 
 
 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI, LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。 
 
 
 
如果在之前有学习RTOS的相关知识在这方面应该非常好理解,也就多了些IO文件操作而已。 
 
 在 Linux  中一切皆为文件,驱动加载成功以后会在“ /dev ”目录下生成一个相应的文件,应  
 
 用程序通过对这个名为“ /dev/xxx ” (xxx  是具体的驱动文件名字 ) 的文件进行相应的操作即可实  
 
 现对硬件的操作。比如现在有个叫做 /dev/led  的驱动文件,此文件是  led  灯的驱动文件。应用程  
 
 序使用  open  函数来打开文件 /dev/led ,使用完成以后使用  close  函数关闭 /dev/led  这个文件。 open 和 close  就是打开和关闭  led  驱动的函数,如果要点亮或关闭  led ,那么就使用  write  函数来操作,也就是向此驱动写入数据,这个数据就是要关闭还是要打开 led  的控制参数。如果要获取  
 
 led  灯的状态,就用  read  函数从驱动中读取相应的状态。  
 
 应用程序运行在用户空间,而  Linux  驱动属于内核的一部分,因此驱动运行于内核空间。  
 
 当我们在用户空间想要实现对内核的操作,比如使用  open  函数打开 /dev/led  这个驱动,因为用  
 
 户空间不能直接对内核进行操作,因此必须使用一个叫做“系统调用”的方法来实现从用户空  
 
 间“陷入”到内核空间,这样才能实现对底层驱动的操作。 
 
 
如下是我们在编写字符驱动中的一个 最具有核心的函数,在驱动中都有与之对应的一个驱动函数,在 Linux 内核文件 include/linux/fs.h 中有个叫做 file_operations 的结构体, 此结构体就是 Linux 内核驱动操作函数集合。 
 
struct file_operations {struct module *owner;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 (*read_iter) (struct kiocb *, struct iov_iter *);ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iterate) (struct file *, struct dir_context *);int (*iterate_shared) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);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 *, loff_t, loff_t, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);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 (*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);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);
#endifssize_t (*copy_file_range)(struct file *, loff_t, struct file *,loff_t, size_t, unsigned int);int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,u64);ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,u64);
};
 简单介绍一下  file_operation  结构体中比较重要的、常用的函数:  
  owner  拥有该结构体的模块的指针,一般设置为  THIS_MODULE 。  
  llseek  函数用于修改文件当前的读写位置。  
  read  函数用于读取设备文件。  
  write  函数用于向设备文件写入 ( 发送 ) 数据。  
  poll  是个轮询函数,用于查询设备是否可以进行非阻塞的读写。  
  unlocked_ioctl  函数提供对于设备的控制功能,与应用程序中的  ioctl  函数对应。  
  compat_ioctl  函数与  unlocked_ioctl  函数功能一样,区别在于在  64  位系统上,  
  32  位的应用程序调用将会使用此函数。在  32  位的系统上运行  32  位的应用程序调用的是  
  unlocked_ioctl 。  
  mmap  函数用于将设备的内存映射到进程空间中 ( 也就是用户空间 ) ,一般帧缓  
  冲设备会使用此函数,比如  LCD  驱动的显存,将帧缓冲 (LCD  显存 ) 映射到用户空间中以后应用  
  程序就可以直接操作显存了,这样就不用在用户空间和内核空间之间来回复制。  
  open  函数用于打开设备文件。  
  release  函数用于释放 ( 关闭 ) 设备文件,与应用程序中的  close  函数对应。  
  fasync  函数用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中。  
  aio_fsync  函数与  fasync  函数的功能类似,只是  aio_fsync  是异步刷新待处理的  
  数据。  
  在字符设备驱动开发中最常用的就是上面这些函数,关于其他的函数大家可以查阅相关文  
  档。我们在字符设备驱动开发中最主要的工作就是实现上面这些函数,不一定全部都要实现,  
  但是像 open、release、write、read 等都是需要实现的,当然了,具体需要实现哪些函数还是要  
  看具体的驱动要求。  
   2、字符设备驱动开发基础 
  直接观察下述代码即可。 
 #include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>static int chrtest_open (struct inode *inodp, struct file *filp)
{return 0;
}static ssize_t chrtest_read (struct file *filp, char __user *buf, size_t, loff_t *offt)
{return 0;
}static ssize_t chrtest_write (struct file *filp, const char __user *buf, size_t, loff_t *offt)
{return 0;
}static int chrtest_release (struct inode *inodp, struct file *filp)
{return 0;
}static struct file_operations test_fops = {.owner   = THIS_MODULE,.open    = chrtest_open,.read    = chrtest_read,.write   = chrtest_write,.release = chrtest_release,
};static int __init xxx_init(void){int retvalue = 0;//设备号 名字 字符模型驱动的一个结构体retvalue = register_chrdev(200,"chrtest",&test_fops);if(retvalue < 0){// exit}return 0;
}static void __exit xxx_exit(void){unregister_chrdev(200,"chrtest");
}module_init(xxx_init);
module_exit(xxx_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("7yewh");
代码架构很简单,相信都能看懂,就不做具体解析了。