在学习linux字符设备驱动的时候,我发现其中需要用到的函数比较多,而我经常是记不住其中各种函数的参数,就在这里整理一下,加深一下对字符设备框架理解。
首先就从基础的开始,就是模块许可声明,加载函数和卸载函数的缺省模式:
MODULE_LICENSE("GPL");
int init_module(void); 加载函数的缺省模式
void cleanup_module(void); 卸载函数的缺省模式
非缺省模式下的加载和卸载可以写成这样:
static int __init xxx_mmt_init(void);
static void __exit xxx_mmt_exit(void);
module_init();
module_exit();
模块参数和模块导出符号,这两个可以不用:
module_param(参数名,参数类型,参数读/写权限)
EXPORT_SYMBOL(符号名);
注册字符驱动设备相关操作:
关于设备号的创建就不再说了,比较简单,然后是动态注册设备:
alloc_chrdev_region()
动态注册一般不常用,主要记一下静态注册:
register_chrdev_region(devno, number_of_devices, "xxx");
之后是注销设备号:
unregister_chrdev_region(devno, number_of_devices);
我自己在练习驱动编写的时候,之前会碰到这样的问题,把dev_t devno = MKDEV(xxx_major, xxx_minor);当成全局变量来定义的时候编译器会报错,后来问过之后知道MKDEV作为一个c语言函数是需要类似于main()函数入口才能执行的,所以我们可以这样做,把dev_t devno放在全局变量那里,然后MKDEV放在加载函数和卸载函数里面就不会有问题了。
与操作集合file_operations有关的函数:
static int xxx_open(struct inode *inode, struct file *filp);
static int xxx_release(struct inode *inode, struct file *filp);
static ssize_t xxx_read(struct file *filp, const char *buf, size_t count, loff_t *fops);
static ssize_t xxx_write(struct file *filp, const char *buf, size_t count, loff_t *fops);
下面是实现对设备的操作集合,你用了哪个函数,就把那个函数加入到操作集合里面,具体实现情况如下:
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_release,
.read = xxx_read,
.write = xxx_write,
};
对于我们来说,了解到驱动整体的框架的话,我们现在所要才去的操作就是把操作集合跟要想注册的设备联系起来,那么我们就需要这个struct cdev这个结构体来实现,结构体具体作用和用法如下:
struct cdev cdev; 首先是定义一个cdev结构体;
cdev_init(&cdev, &xxx_fops); 初始化函数,把cdev跟file_operations联系在一起;
cdev_add(&cdev, devno, 1); 把设备号加入到cdev里面;
就这样我们就把设备号跟操作集合联系在一起了,其实这里面的联系还是比较复杂,在这里就不在深入探讨了。
read中驱动会调用copy_to_user()将数据返回给应用层,而且用这个函数时,最好定义一个变量去接受它的返回值,不然的话编译器会报警告:
int ret;
ret = copy_to_user(buf,data,count);
write中,我们也可以调用copy_from_user()将数据从应用层接受过来:
int ret;
ret = copy_from_user(data,buf,count);
大体上就是这样,一个字符设备驱动的基本框架就构建起来了,然后再来一个例子让我加深一下理解:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h> MODULE_LICENSE ("GPL"); int hello_major = 250; int hello_minor = 0; int number_of_devices = 1; char data[128] = "foobar not equal to barfoo"; struct cdev cdev; static int hello_open (struct inode *inode, struct file *file) { printk (KERN_INFO "Hey! device opened\n"); return 0; } static int hello_release (struct inode *inode, struct file *file) { printk (KERN_INFO "Hmmm... device closed\n"); return 0; } ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp) { ssize_t result = 0; if (count < 0) return -EINVAL; if (count > 127) count = 127; if (copy_to_user (buff, data, count)) { result = -EFAULT; } else { printk (KERN_INFO "wrote %d bytes\n", count); result = count; } return result; } struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, }; static void char_reg_setup_cdev (void) { int error; dev_t devno; devno = MKDEV (hello_major, hello_minor); cdev_init (&cdev, &hello_fops); cdev.owner = THIS_MODULE; error = cdev_add (&cdev, devno , 1); if (error) printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error); } static int __init hello_2_init (void) { int result; dev_t devno; devno = MKDEV (hello_major, hello_minor); result = register_chrdev_region (devno, number_of_devices, "hello"); if (result < 0) { printk (KERN_WARNING "hello: can't get major number %d\n", hello_major); return result; } char_reg_setup_cdev (); printk (KERN_INFO "char device registered\n"); return 0; } static void __exit hello_2_exit (void) { dev_t devno = MKDEV (hello_major, hello_minor); cdev_del (&cdev); unregister_chrdev_region (devno, number_of_devices); } module_init (hello_2_init); module_exit (hello_2_exit);