怎样创建char device driver
1 动态分配设备号
从代码看有可能分配失败,所以要对返回值进行检查
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
/*如果是动态分配设备号,__register_chrdev_region的major参数是0*/
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 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;
/*the standard way of allocating kernel memory and initialized to 0*/
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
/* temporary, the auto allocate flag: major == 0 */
if (major == 0) {/*start from the end to get empty one*/
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {/*cannot get one in the array*/
ret = -EBUSY;
goto out;
}/*if get one, then value the major and ret*/
major = i;
ret = major;
}
/*value the char_device_struct*/
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major);
------
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
2 cdev_init初始化file_operations
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
3 cdev_add建立dev_t,kobject and cdev之间的关系到cdev_map变量
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
4 由class and dev_t创建一个device object
为什么需要这么做见char 设备驱动程序(一)
device_create(alb_context->alb_class, NULL,
MKDEV(MAJOR(dev_id),0),
NULL,
"abcd");
5 根据inode的cdev成员传递关联内容
static int alb_open(struct inode *inode, struct file *filp)
{
int err = 0;
/* From the inode to get the alb_driver_context and others from the driver */
struct alb_driver_context *alb_context = container_of(
inode->i_cdev,
struct alb_driver_context,
cdev);
----
}
从得到的inode,可以知道对应的cdev的地址,从而知道cdev 所在结构体的地址,从而知道各变量的内容。
这层关系是怎样建立起来的那?
当根据设备文件名进行系统调用的时候,可以得到对应的inode, 从inode可以得到 dev_id.根据dev_id通过函数
kobj_lookup(cdev_map, inode->i_rdev, &idx);可以得到dev_id 对应的kobject.dev_id和kobject的对应关系是由
cdev_add -> kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);建立的。
又kobject嵌入在cdev中,进而而到cdev的地址。对以上的例子而言,cdev又嵌入在alb_driver_context,进而知道该
结构体的地址。