设备表结构与源码分析
一。设备结构体
#define CHRDEV_MAJOR_HASH_SIZE 255
static DEFINE_MUTEX(chrdevs_lock);
static struct char_device_struct {
struct char_device_struct *next; //结构体指针
unsigned int major; //主设备号
unsigned int baseminor; //次设备起始号
int minorct; //次备号个数
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; //只能挂255个字符主设备
二。申请设备号操作函数源码分析
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock); //互斥访问
/* temporary */
if (major == 0) { //如果主设备号为0,则系统自动分配一个没有使用的设备号
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
ret = major;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major); /获得主设备号
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) //找到比NEW的第一个大的结构体指针
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor; //判断两个是否有得叠
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
cd->next = *cp; //插入
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
================================================
static struct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
struct char_device_struct *cd = NULL, **cp;
int i = major_to_index(major);
mutex_lock(&chrdevs_lock);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
(*cp)->minorct == minorct)
break;
if (*cp) {
cd = *cp;
*cp = cd->next;
}
mutex_unlock(&chrdevs_lock);
return cd;
}
int register_chrdev_region(dev_t from, unsigned count, const char *name) //from:设备号 count:次设备个数 name:设备名
{
struct char_device_struct *cd;
dev_t to = from + count; //设备号结束数
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0); //下一个设备号
if (next > to) //
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name); //获取设备结构体指针所注册位置指针
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); //如果失败,则删除刚才分的设备结构体
}
return PTR_ERR(cd);
}
=============================================================
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name); //主设备号为0。系统自动分配一个
if (IS_ERR(cd))
return PTR_ERR(cd); //如果失败,返回一个错误号
*dev = MKDEV(cd->major, cd->baseminor); //如果成功返回设备号
return 0;
}
void unregister_chrdev_region(dev_t from, unsigned count) //注销
{
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); //回收分配的内存及设备号
}
}
==============================================================
三。cdev的初始化与添加与操作的连接的源码分析
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev); //初始化cdev
INIT_LIST_HEAD(&cdev->list); //初始化链表
kobject_init(&cdev->kobj, &ktype_cdev_default); //后期分析
cdev->ops = fops; //将文件操作与cdev连接起来
}
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); //分配一个cdev结构内存
if (p) {
INIT_LIST_HEAD(&p->list); //初始化一个链表
kobject_init(&p->kobj, &ktype_cdev_dynamic); //后期分析
}
return p;
}
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev; //初始化cdev
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); //后期分析
}
===========================================================
void cdev_del(struct cdev *p) //删除cdev结构体
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj); //减少引用计数
}
一。设备结构体
#define CHRDEV_MAJOR_HASH_SIZE 255
static DEFINE_MUTEX(chrdevs_lock);
static struct char_device_struct {
struct char_device_struct *next; //结构体指针
unsigned int major; //主设备号
unsigned int baseminor; //次设备起始号
int minorct; //次备号个数
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; //只能挂255个字符主设备
二。申请设备号操作函数源码分析
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock); //互斥访问
/* temporary */
if (major == 0) { //如果主设备号为0,则系统自动分配一个没有使用的设备号
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
ret = major;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major); /获得主设备号
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) //找到比NEW的第一个大的结构体指针
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor; //判断两个是否有得叠
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
cd->next = *cp; //插入
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
================================================
static struct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
struct char_device_struct *cd = NULL, **cp;
int i = major_to_index(major);
mutex_lock(&chrdevs_lock);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
(*cp)->minorct == minorct)
break;
if (*cp) {
cd = *cp;
*cp = cd->next;
}
mutex_unlock(&chrdevs_lock);
return cd;
}
int register_chrdev_region(dev_t from, unsigned count, const char *name) //from:设备号 count:次设备个数 name:设备名
{
struct char_device_struct *cd;
dev_t to = from + count; //设备号结束数
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0); //下一个设备号
if (next > to) //
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name); //获取设备结构体指针所注册位置指针
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); //如果失败,则删除刚才分的设备结构体
}
return PTR_ERR(cd);
}
=============================================================
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name); //主设备号为0。系统自动分配一个
if (IS_ERR(cd))
return PTR_ERR(cd); //如果失败,返回一个错误号
*dev = MKDEV(cd->major, cd->baseminor); //如果成功返回设备号
return 0;
}
void unregister_chrdev_region(dev_t from, unsigned count) //注销
{
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); //回收分配的内存及设备号
}
}
==============================================================
三。cdev的初始化与添加与操作的连接的源码分析
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev); //初始化cdev
INIT_LIST_HEAD(&cdev->list); //初始化链表
kobject_init(&cdev->kobj, &ktype_cdev_default); //后期分析
cdev->ops = fops; //将文件操作与cdev连接起来
}
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); //分配一个cdev结构内存
if (p) {
INIT_LIST_HEAD(&p->list); //初始化一个链表
kobject_init(&p->kobj, &ktype_cdev_dynamic); //后期分析
}
return p;
}
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev; //初始化cdev
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); //后期分析
}
===========================================================
void cdev_del(struct cdev *p) //删除cdev结构体
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj); //减少引用计数
}