天天看点

i.MX6ULL终结者简化版字符设备驱动框架

在上一节中介绍了常规的字符设备驱动的模板程序,发现要编写一个字符设备驱动需要分成好几步,太麻烦了,为了简化这个流程,Linux下提供了一些封装的接口函数。

/*
函数功能:注册字符设备
参数说明:major:主设备号,0表示动态分配
        name: 设备名
        fops: 操作函数
返回值: 申请的主设备号
*/
static inline int register_chrdev(unsigned int major, const char *name,
          const struct file_operations *fops)
/*
函数功能:注销字符设备
参数说明:major:主设备号
        name: 设备名
*/
static inline void unregister_chrdev(unsigned int major, const char *name)
           

通过这两个函数就不用在一步一步的去注册设备了,下面来看一下这两个函数是如何实现的,具体文件为:fs/char_dev.c。

//register_chrdev调用了__register_chrdev
int __register_chrdev(unsigned int major, unsigned int baseminor,
          unsigned int count, const char *name,
          const struct file_operations *fops)
{
  struct char_device_struct *cd;
  struct cdev *cdev;
  int err = -ENOMEM;
    //1. 申请设备号
  cd = __register_chrdev_region(major, baseminor, count, name);
  if (IS_ERR(cd))
    return PTR_ERR(cd);
    //2. 为设备结构体申请内存
  cdev = cdev_alloc();
  if (!cdev)
    goto out2;
    // 3. 初始化cdev
  cdev->owner = fops->owner;
  cdev->ops = fops;
  kobject_set_name(&cdev->kobj, "%s", name);
   //4. 向系统注册设备
  err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
  if (err)
    goto out;
 
  cd->cdev = cdev;
 
  return major ? 0 : cd->major;
out:
  kobject_put(&cdev->kobj);
out2:
  kfree(__unregister_chrdev_region(cd->major, baseminor, count));
  return err;
}
 
//unregister_chrdev调用__unregister_chrdev
void __unregister_chrdev(unsigned int major, unsigned int baseminor,
       unsigned int count, const char *name)
{
  struct char_device_struct *cd;
    //释放设备号
  cd = __unregister_chrdev_region(major, baseminor, count);
  if (cd && cd->cdev)
    cdev_del(cd->cdev); //注销设备
  kfree(cd);
}
           

通对上面的源码可以看出register_chrdev()和unregister_chrdev()函数只是对之前的步骤进行了封装,实现方法是完全一样的,以后在写驱动时直接用这两个函数就很方便了。

这样我们就可以简化一下常规字符设备框架的程序了,内容如下:

/*
设备结构体
*/
struct xxx_dev_t{
    struct cdev cdev;
    ...
};
 
struct xxx_dev_t *dev;
 
//读设备
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t* f_pos)
{
    ...
    copy_to_user(buf, ..., ...);
}
//写设备
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos)
{
    ...
    copy_from_user(..., buf, ...);
}
 
//操作函数file_operations
struct file_operations xxx_fops = {
    .owner = THIS_MODULE,
    .read = xxx_read,
    .write = xxx_write,
    ...
};
 
//设备驱动模块加载函数
static int __init xxx_init(void)
{
    ...
    //注册字符设备
    xxx_major = register_chrdev(0, "xxx_dev", &xxx_fops);
}
module_init(xxx_init);
//设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
    //注销字符设备
    unregister_chrdev(major, "xxx_dev");
    ...
}
module_exit(xxx_exit);
MODULE_LICENSE("GPL v2");
           
i.MX6ULL终结者简化版字符设备驱动框架