3. linux 字符设备驱动框架
1.主设备号码和次设备号
dev_t
dev_t 类型是定义在
中定义用来保存设备编号(前12位用来表示主设备号,后20位表示次设备号)
注意:我们获取主设备号和次设备号应该使用内核提供的宏
MAJOR(dev_t)
:获取主设备号
MINOR(dev_t)
:获取次设备号
MKDEV(int major, int minor)
:将主设备号和次设备号转换为dev_t类型
- 分配和释放设备编号
在建立一个字符设备之前,首先需要分别一个或多个字符设备。
静态分配设备号
/*
first: 要分配的设备编号范围的初始值,次设备号为0
count: 连续编号范围
name:编号相关联的设备名称(/proc/devices)
*/
int register_chrdev_region(dev_t first, unsigned int count, char *name);
动态分配设备号
/*
dev:分配到的设备号
firstminor:被请求的第一个次设备号,通常为0
count:请求的连续设备号数
name:设备名
*/
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
释放
void unregist_chrdev_region(dev_t first,unsigned int count);
2.字符设备的注册
static void __init chrdev_init(void)
{
//构造cdev设备对象
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
//初始化cdev设备对象
cdev_init(my_cdev, &my_fops);
my_cdev->owner = THIS_MODULE;
//申请设备号,动态or静态
dev_t dev;
int ret = 0;
if(major)
{
//为字符设备静态申请第一个设备号
dev = MKDEV(major, minor);
ret = register_chrdev_region(dev, 1, "mydev");
}
else
{
//为字符设备动态申请一个设备号
ret = alloc_chrdev_region(&dev, minor, 1, "mydev");
major = MAJOR(dev);
}
//将字符设备对象注册到内核
ret = cdev_add(my_cdev, dev, 1);
}
static void __exit chrdev_exit(void)
{
//从内核注销cdev设备对象
cdev_del(my_dev);
//回收设备号
unregister_chrdev_region(dev, 1);
}
3.字符设备驱动程序样例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define HELLO_CNT 2
//主设备号为0,表示动态分配设备号
dev_t dev = 0;
static int major = 0;
static int minor = 0;
static struct cdev *hello_cdev[HELLO_CNT];
static struct class *hello_class = NULL;
static struct class_device * hello_class_dev[HELLO_CNT];
//文件操作结构体
static const struct file_operations fops =
{
.owner = THIS_MODULE,
//.open = hello_open,
//.release = hello_release,
//.read = hello_read,
//.write = hello_write,
};
static void setup_cdev(int index)
{
int err, devno = MKDEV(major, index);
cdev_init(hello_cdev[index], &fops);
hello_cdev[index]->owner = THIS_MODULE;
hello_cdev[index]->ops = &fops;
err = cdev_add(hello_cdev[index], devno, 1);
if(err)
{
printk(KERN_NOTICE "Error %d adding hello%d", err, index);
}
}
static void __init hello_init(void)
{
//申请设备号,动态or静态
int ret = 0;
if(major)
{
//为字符设备静态申请第一个设备号
dev = MKDEV(major, minor);
ret = register_chrdev_region(dev, HELLO_CNT, "hello");
}
else
{
//为字符设备动态申请一个设备号
ret = alloc_chrdev_region(&dev, minor, HELLO_CNT, "hello");
major = MAJOR(dev);
}
//构造cdev设备对象
int i = 0;
for(i = 0; i < HELLO_CNT; ++i)
{
hello_cdev[i] = cdev_alloc();
}
//初始化设备对象
for(minor = 0; minor < HELLO_CNT; ++minor)
{
setup_cdev(minor);
}
hello_class = class_create(THIS_MODULE, "hello");
for(minor = 0; minor < HELLO_CNT; ++minor)
{
hello_class_dev[minor] = device_create(hello_class, NULL, MKDEV(major, minor), NULL, "hello%d",minor);
}
}
static void __exit hello_exit(void)
{
for(minor = 0; minor < HELLO_CNT; ++minor)
{
device_destroy(hello_class, MKDEV(major, minor));
}
class_destroy(hello_class);
//从内核注销cdev设备对象
cdev_del(hello_cdev);
//回收设备号
unregister_chrdev_region(dev, HELLO_CNT);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");