1.注册主次设备号:register_chrdev_region()和 alloc_chrdev_region()
2.注册字符设备驱动:cdev_init()初始化,cdev_add()添加,注册设备驱动,cdev_alloc()申请空间,cdev_del()注销驱动
3.创建驱动的设备文件:class_create()
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/********** 宏定义变量 ***********************************/
#define my_MAJOR 230 //代表自己分配的那个设备号
#define MAX_MINORS 1 //次设备号数量
#define device_name "module_test" //驱动的名字
/**************** 全局变量 *********************************/
static struct cdev test_cdev; //驱动中重要的一个结构体变量cdev
static dev_t mydev; //注册设备号相关的结构体
static struct class *test_class; //创建驱动设备文件相关的结构体变量
static int test_chrdev_open(struct inode *inode, struct file *file)
{
printk(KERN_ERR "open is ok \n");
return 0;
}
static ssize_t test_chrdev_write(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
printk(KERN_ERR "write is ok \n");
return 0;
}
static ssize_t test_chardev_reaed(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
printk(KERN_ERR "read is ok \n");
return 0;
}
/***** 驱动的硬件实现函数 ******/
static const struct file_operations test_fops =
{
.owner = THIS_MODULE,
.open = test_chrdev_open,
.write = test_chrdev_write,
.read = test_chardev_reaed,
};
static int __init chrdev_init(void)
{
int retval;
mydev = MKDEV(my_MAJOR, 0); //由自己分配的设备号得到dev变量。
//第一步:注册主次设备号
if ((retval = register_chrdev_region(mydev,MAX_MINORS,device_name)) != 0)
{
printk(KERN_ERR "dvb-core: unable to get major \n");
return retval;
}
//第二步:注册字符设备驱动
cdev_init(&test_cdev, &test_fops);
if ((retval = cdev_add(&test_cdev, mydev, MAX_MINORS)) != 0)
{
printk(KERN_ERR "dvb-core: unable register character device\n");
goto error;
}
//第三步:创建驱动的设备文件
test_class = class_create(THIS_MODULE, "cryil_class");
if (IS_ERR(test_class))
{
retval = PTR_ERR(test_class);
goto error;
}
device_create(test_class,NULL,mydev,NULL,"test"); //test就是将来在/dev中创建的设备文件名
return 0;
//错误处理函数:先回收申请的空间,然后删除驱动
error:
cdev_del(&test_cdev);
unregister_chrdev_region(mydev, MAX_MINORS);
return retval;
}
static void __exit chardev_exit(void)
{
device_destroy(test_class,mydev); //销毁创建的驱动文件
class_destroy(test_class); //销毁创建的类
cdev_del(&test_cdev); //注销驱动
unregister_chrdev_region(MKDEV(my_MAJOR, 0), MAX_MINORS);
}
module_init(chrdev_init); //驱动的入口,insmod时会调用此函数
module_exit(chardev_exit); //驱动的出口,rmmod时会调用此函数
MODULE_DESCRIPTION("cryil test Driver"); //模块的介绍信息
MODULE_AUTHOR("cryil"); //模块的作者
MODULE_LICENSE("GPL"); //模块的许可证,固定的。
主次设备号的注册方法:
1.register_chrdev_region()自己分配:register_chrdev_region(mydev,MAX_MINORS,device_name) 。内部是调用了__register_chrdev_region函数,不过自己指定了主设备号
2.alloc_chrdev_region()使用系统分配的主次设备号:result = alloc_chrdev_region(&mydev, 0, MAX_MINORS, device_name),同样的实际上内部是调用了__register_chrdev_region函数,只不过将__register_chrdev_region函数的第一个参数设置为0,且次设备号只能从0开始分配。
3.但其实两个函数其内部都是调用了 MKDEV()函数,来指定主次设备号的。
MKDEV是将主设备号和次设备号转换成dev_t类型(dev_t类型是unsigned int 类型,32位)它是定义在
形式:MKDEV(int major,int minor) major为主设备号 minor为次设备号宏定义:#define MKDEV(major,minor) (((major) << MINORBITS) | (minor)),成功执行返回dev_t类型的设备编号,除此之外可以通过MAJOR()函数 和MINOR()函数 来获得主设备号和次设备号。
test.c中未使用cdev_alloc()函数,直接定义一个cdev结构体类型的变量,而使用cdev_alloc()函数的方法如下:
1.定义一个dev_t结构体类型的指针: static dev_t *p
2.对结构体指针进行实例化,为其分配内存: p = cdev_alloc()
3.后面的cdev_init() cdev_add() 函数就可以直接使用。
优点:cdev结构体类型占用的空间过大,在这里先定义一个指针类型的变量,然后再需要使用时动态刚分配内存,减少栈的使用。
cdev结构体分析:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
这是字符设备驱动中最重要的一个结构体,包含着绝大多数的信息。其实注册驱动的步骤就是填充这个结构体,然后在应用层操纵硬件完成具体的任务时,调用其中的函数指针完成具体的操作。
End。。。。。。