- struct idr {
- struct idr_layer *top;
- struct idr_layer *id_free;
- int layers;
- int id_free_cnt;
- spinlock_t lock;
- };
/* idr是idr机制的核心结构体 */
(2)为idr分配内存
int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
每次通过idr获得ID号之前,需要先分配内存。
返回0表示错误,非零值代表正常
(3)分配ID号并将ID号和指针关联
int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);
idp: 之前通过idr_init初始化的idr指针
id: 由内核自动分配的ID号
ptr: 和ID号相关联的指针
start_id: 起始ID号。内核在分配ID号时,会从start_id开始。如果为I2C节点分配ID号,可以将设备地址作为start_id
函数调用正常返回0,如果没有ID可以分配,则返回-ENOSPC
在实际中,上述函数常常采用如下方式使用:
- again:
- if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) {
- /* No memory, give up entirely */
- }
- spin_lock(&my_lock);
- result = idr_get_new(&my_idr, &target, &id);
- if (result == -EAGAIN) {
- sigh();
- spin_unlock(&my_lock);
- goto again;
- }
(4)通过ID号搜索对应的指针
void *idr_find(struct idr *idp, int id);
返回值是和给定id相关联的指针,如果没有,则返回NULL
(5)删除ID
要删除一个ID,使用:
void idr_remove(struct idr *idp, int id);
通过上面这些方法,内核代码可以为子设备,inode生成对应的ID号。这些函数都定义在<linux-2.6.xx/lib/idr.c>中
下面,我们通过分析I2C协议的核心代码,来看一看idr机制的实际应用:
- <linux-2.6.23/drivers/i2c/i2c-core.c>
- ...
- <linux/idr.h> /* idr头文件 */
- ...
- static DEFINE_IDR(i2c_adapter_idr); /* 声明idr */
- ...
- /*
- 采用动态总线号声明并注册一个i2c适配器(adapter),可睡眠
- 针对总线号可动态指定的设备,如基于USB的i2c设备或pci卡
- */
- int i2c_add_adapter(struct i2c_adapter *adapter)
- {
- int id, res = 0;
- retry:
- if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
- mutex_lock(&core_lists);
- /* __i2c_first_dynamic_bus_num是当前系统允许的动态总线号的最大值 */
- res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id);
- mutex_unlock(&core_lists);
- if (res < 0) {
- if (res == -EAGAIN)
- goto retry;
- return res;
- }
- adapter->nr = id;
- return i2c_register_adapter(adapter);
- }
- EXPORT_SYMBOL(i2c_add_adapter);
-
- /*
- 采用静态总线号声明并注册一个i2c适配器(adapter)
- */
- int i2c_add_numbered_adapter(struct i2c_adapter *adap)
- {
- int id;
- int status;
- if (adap->nr & ~MAX_ID_MASK)
- return -EINVAL;
- retry:
- if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
- mutex_lock(&core_lists);
- /* "above" here means "above or equal to", sigh;
- * we need the "equal to" result to force the result
- */
- status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
- if (status == 0 && id != adap->nr) {
- status = -EBUSY;
- idr_remove(&i2c_adapter_idr, id);
- }
- mutex_unlock(&core_lists);
- if (status == -EAGAIN)
- goto retry;
- if (status == 0)
- status = i2c_register_adapter(adap);
- return status;
- }
- EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
-
- /* 注销一个i2c适配器 */
- int i2c_del_adapter(struct i2c_adapter *adap)
- {
- ...
- /* free bus id */
- idr_remove(&i2c_adapter_idr, adap->nr);
- ...
- return res;
- }
- EXPORT_SYMBOL(i2c_del_adapter);
-
- /* 通过ID号获得i2c_adapter设备结构体 */
- struct i2c_adapter* i2c_get_adapter(int id)
- {
- struct i2c_adapter *adapter;
- mutex_lock(&core_lists);
- adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
- if (adapter && !try_module_get(adapter->owner))
- adapter = NULL;
- mutex_unlock(&core_lists);
- return adapter;
- }
- EXPORT_SYMBOL(i2c_get_adapter);