目录
register_chrdev_region主动申请设备号编程实战:
新接口与老接口
(1)老接口:register_chrdev
(2)新接口:register_chrdev_region(自己指定申请设备号)/alloc_chrdev_region(系统自动分配设备号)(这两个的驱动都还没注册) + cdev(结构体,注册驱动)
模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;
通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号;
cdev介绍
去cdev.h里找
struct cdev {
struct kobject kobj; // 内嵌的内核对象,每个 cdev 都是一个 kobject
struct module *owner; // 指向实现驱动的模块
const struct file_operations *ops; // 操纵这个字符设备文件的方法
struct list_head list; // 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头,用来将已经向内核注册的所有字符设备形成链表
dev_t dev; // 字符设备的设备号,由主设备号和次设备号构成
unsigned int count; // 隶属于同一主设备号的次设备号的个数.
};
设备号
(1)=主设备号+次设备号
(2)dev_t类型
(3)MKDEV、MAJOR、MINOR三个宏
MKDEV:由主设备号和次设备号算出主次设备号
MAJOR:由设备号提取出主设备号
MINOR:由设备号提取出次设备号
相关函数:cdev_alloc、cdev_init、cdev_add(注册驱动)、cdev_del(注销驱动)
register_chrdev_region主动申请设备号编程实战:
使用register_chrdev_region + cdev_init + cdev_add进行字符设备驱动的注册
第一步:注册/分配主次设备号
#define MYMAJOR 200
#define MYCNT 1
#define MYNAME "testchar"
static dev_t mydev;
static int __init chrdev_init(void)
{
/*第一步*/
int retval;
printk(KERN_INFO "chrdev_init helloworld init\n");
mydev = MKDEV(MYMAJOR, 0);//算出主次设备号
retval = register_chrdev_region(mydev, MYCNT, MYNAME);//自己指定申请设备号(主次设备号,数量,设备名)
if (retval) {
printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
return -EINVAL;
}
printk(KERN_INFO "register_chrdev_region success\n");
/*第二步见下方*/
}
第二步:注册字符设备驱动
static struct cdev test_cdev;
static const struct file_operations test_fops = {
.owner = THIS_MODULE, // 惯例,直接写即可
.open = test_chrdev_open, // 将来应用open打开这个设备时实际调用的
.release = test_chrdev_release, // 就是这个.open对应的函数
.write = test_chrdev_write,
.read = test_chrdev_read,
};
// 第2步:注册字符设备驱动
cdev_init(&test_cdev, &test_fops);//初始化cdev
retval = cdev_add(&test_cdev, mydev, MYCNT);//注册cdev
if (retval) {
printk(KERN_ERR "Unable to cdev_add\n");
return -EINVAL;
}
printk(KERN_INFO "cdev_add success\n");
注销设备驱动:
// 注销分2步:
// 第一步真正注销字符设备驱动用cdev_del
cdev_del(&test_cdev);
// 第二步去注销申请的主次设备号
unregister_chrdev_region(mydev, MYCNT);
下面提供一个最简单的范例:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
static int major = 237;
static int minor = 0;
static dev_t devno;
static struct cdev cdev;
static int hello_open (struct inode *inode, struct file *filep)
{
printk("hello_open()\n");
return 0;
}
static struct file_operations hello_ops =
{
.open = hello_open,
};
static int hello_init(void)
{
int result;
int error;
printk("hello_init \n");
devno = MKDEV(major,minor);
result = register_chrdev_region(devno, 1, "test");
if(result<0)
{
printk("register_chrdev_region fail \n");
return result;
}
cdev_init(&cdev,&hello_ops);
error = cdev_add(&cdev,devno,1);
if(error < 0)
{
printk("cdev_add fail \n");
unregister_chrdev_region(devno,1);
return error;
}
return 0;
}
static void hello_exit(void)
{
printk("hello_exit \n");
cdev_del(cdev);
unregister_chrdev_region(devno,1);
return;
}
module_init(hello_init);
module_exit(hello_exit);
应用程序如果要想使用,还必须创建字符设备节点
mknod /dev/test c 237 0
alloc_chrdev_region自动分配设备号:
register_chrdev_region是在事先知道要使用的主、次设备号时使用的;先cat /proc/devices去看看没有使用。
更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。
自动分配的设备号,我们必须去知道他的主次设备号,否则后面没法去mknod创建他对应的设备文件。
接口分析:
老接口分析
register_chrdev
__register_chrdev
__register_chrdev_region
cdev_alloc
cdev_add
新接口分析
register_chrdev_region
__register_chrdev_region
alloc_chrdev_region
__register_chrdev_region