设备驱动学习之字符设备驱动内核代码分析(一)——设备号申请接口

对应内核版本为Linux3.0.1,为OK6410开发板附带的源代码。

添加一个字符设备时首先要调用以下接口申请一个设备号

int register_chrdev_region(dev_t from, unsigned count, const char *name)

或者

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)

下面我们来看看这两个接口的内核代码,位置fs/char_dev.c:

int register_chrdev_region(dev_t from, unsigned count, const char *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);    //最终调用的申请设备号函数
 if (IS_ERR(cd))
  return PTR_ERR(cd);
 *dev = MKDEV(cd->major, cd->baseminor);
 return 0;
}


可以看到这两个接口都调用了__register_chrdev_region来申请序列号,下面我们重点讲解这个函数,先列出代码:

/*
 * Register a single major with a specified minor range.
 *
 * If major == 0 this functions will dynamically allocate a major and return
 * its number.
 *
 * If major > 0 this function will attempt to reserve the passed range of
 * minors and will return zero on success.
 *
 * Returns a -ve errno on failure.
 */
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)     //chrdevs是存储所有设备号结构体的指针数组,数组的长度为255,
				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)
		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);
}

从上面的代码可以看出,所有的设备号结构体存储在chrdevs数组中,定义如下:

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];

数组中每个元素都是一个指向struct char_device_struct对象的指针,结构体的第一个变量也是一个struct char_device_struct指针,这样的话数组的每个元素实际上是一个单向链表头。

主设备号的范围是(0~255) << 8,可以映射到数组存储的255个链表中((0<<8)号映射到数组的第一个链表,(1<<8)号映射到第二个链表。。。(254<<8)号映射到第255个链表,(255<<8)号映射到第一个链表)。

设备号结构体在链表中的位置是这样决定的:

1.先比主设备号,小的在前

2.如果主设备号相等,比次设备号,小的在前

3.如果次设备号也相等,再将次设备号加上要申请的设备号个数比较,小的在前

 

如果设备号有冲突则申请失败。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值