Linux驱动程序的框架
Linux的外设可以分为3类:字符设备、块设备和网络设备
字符设备:是能够像字节流(比如文件)一样被访问的设备,就是说对它的读写是以字节为单位的。
块设备:块设备上的数据以块的形式存放。
1、块设备驱动程序先将用户发来的数据组织成块,在写入设备;或从设备中读取出若干块数据,再从中挑出用户需要的。
2、通常在块设备中按照一定的格式存放数据,不同的文件系统类型就是用来定义这些格式的。内核中,文件系统的层次位于块设备驱动程序上面,这意味着块设备驱动程序除了向用户层动提供与字符设备一样的接口外,还要向内核其他部件提供一些接口,这些接口用户是看不到的。这些接口使得可以在块设备上存放文件系统,挂在块设备。
网络设备:
1、应用程序调用库提供的open等函数打开设备文件
2、open函数传入的参数执行swi指令,引起cpu异常,进入内核
3、内核异常根据这些参数调用驱动程序的相关函数
一般来说,编写一个Linux设备驱动程序的大致流程如下:
1、查看原理图、数据手册,了解设备的操作方法;
2、在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始;
3、实现驱动程序的初始化:比如像内核注册这个驱动程序,这样应用程序传入文件名时,内核才能找到相应的驱动程序;
4、设计所要实现的操作,比如open、close、read、write等函数;
5、实现中断服务(中断并不是每个设备驱动所必须的);
6、编译该驱动程序到内核中,或者用insmod命令加载;
7、测试驱动程序。
Linux加载驱动程序有动态加载和静态加载两种
静态加载:就是把驱动程序直接编译到内核里
动态加载:利用Linux的module特性,可以在系统启动后用insmod命令把驱动程序添加上去,不需要时用rmmod命令卸载
使用insmod命令加载驱动模块
使用rmmod命令卸载驱动模块
使用lsmod命令查看内核中已经加载了哪些模块
当使用insmod加载模块时,会调用驱动程序中的宏module_init(my_init),它用来向内核注册驱动程序;
当使用rmmod卸载模块时,会调用驱动程序中的宏module_exit(my_cleannup),模块的清楚函数就被调用。
字符设备驱动程序中重要的数据结构和函数
extern int open (__const char *__file, int __oflag, ...) __nonnull ((1));
extern ssize_t read (int __fd, void *__buf, size_t __nbytes);
extern ssize_t write (in __fd, __const void *__buf, size_t __n);
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
...
对于上述每个系统调用,驱动程序中都有一个与之对应的函数。对于字符设备驱动程序,这些函数集合在一个 file_operations 类型的数据结构中。
Linux系统怎么知道调用那个驱动程序?
1、设备文件有主/次设备号
2、模块初始化时,将主设备号与file_operations结构一起向内核注册
注册函数:
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
卸载函数:
int unregister_chrdev(unsigned int major, const char *name);
编写字符驱动程序的程序大概如下:
1、定义file_operations结构,完成file_operations结构中成员函数的实现(包括硬件的初始化和相应的操作)
2、实现注册函数register_chrdev和卸载函数unregister_chrdev
3、通过宏module_init()和module_exit()向内核注册或者卸载
简单的驱动代码结构:
static int first_drv_open(struct inode *inode, struct file *file);
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos);
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
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)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk("first_drv_write\n");
return 0;
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");