您的位置:首页 > 其它

class_create(),device_create自动创建设备文件结点 .

2016-09-08 20:51 686 查看
从linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。相比devfs,udev有很多优势,在此就不罗嗦了,提醒一点,udev是应用层的东东,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,以作者所写的一个字符设备驱动为例,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。(不太明白什么是devfs,udev,对linux中的文件系统没有什么概念呢。)
大致用法如下:
struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);

class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);
这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件。

class_create()
-------------------------------------------------

linux-2.6.22/include/linux/device.h

struct class *class_create(struct module *owner, const char *name)

    class_create - create a struct class structure

    @owner: pointer to the module that is to "own"this struct class

    @name: pointer to a string for the name of this class.
在/sys/class/下创建类目录

class_device_create()

-------------------------------------------------

linux-2.6.22/include/linux/device.h

struct class_device *class_device_create(structclass       
*cls,

                                        struct class_device *parent,

                                        dev_t              devt,

                                        struct device       *device,

                                        const char          *fmt, ...)

    class_device_create - creates a class device and registersit with sysfs

    @cls: pointer to the struct class that this device should beregistered to.

    @parent: pointer to the parent struct class_device of thisnew device, if any.

    @devt: the dev_t for the char device to be added.

    @device: a pointer to a struct device that is assiociatedwith this class device.

    @fmt: string for the class device's name 
在驱动模块初始化函数中实现设备节点的自动创建

 
我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。

struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。 
在2.6.26.6内核版本中,struct class定义在头文件include/linux/device.h中

/*

 * device classes

 */

    struct class {

      const char       *name;

      struct module     *owner;

  nbsp;struct kset         subsys;

      structlist_head         devices;

      structlist_head         interfaces;

      structkset             class_dirs;

      struct semaphore sem;    /* lockschildren, devices, interfaces */

      struct class_attribute   *class_attrs;

      structdevice_attribute      *dev_attrs;

  int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);

  void (*class_release)(struct class *class);

      void (*dev_release)(struct device *dev);

  int (*suspend)(struct device *dev, pm_message_t state);

      int (*resume)(struct device *dev);

};

class_create(…)在/drivers/base/class.c中实现: 

     /**

    * class_create - create a struct class structure

    * @owner: pointer to the module that is to "own"this struct class

    * @name: pointer to a string for the name of this class.

    *

    * This is used to create a struct class pointer that canthen be used

    * in calls to device_create().

    *

    * Note, the pointer created here is to be destroyed whenfinished by

    * making a call to class_destroy().

    */

   struct class *class_create(struct module *owner, const char *name)

   {

      struct class *cls;

      int retval;

      cls = kzalloc(sizeof(*cls), GFP_KERNEL);

      if (!cls) {

           retval = -ENOMEM;

           goto error;

      }

  cls->name = name;

      cls->owner = owner;

      cls->class_release = class_create_release;

  retval = class_register(cls);

      if (retval)

           goto error;

  return cls;

error:

      kfree(cls);

      return ERR_PTR(retval);

    }

    第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 

    在class.c中,还定义了class_destroy(…)函数,用于在模块卸载时删除类。 

device_create(…)函数在/drivers/base/core.c中实现: 

    /**

     * device_create - creates a device and registers itwith sysfs

     * @class: pointer to the struct class that this deviceshould be registered to

     * @parent: pointer to the parent struct device of thisnew device, if any

     * @devt: the dev_t for the char device to be added

     * @fmt: string for the device's name

     *

     * This function can be used by char device classes. Astruct device

     * will be created in sysfs, registered to thespecified class.

     *

     * A "dev" file will be created, showing thedev_t for the device, if

     * the dev_t is not 0,0.

     * If a pointer to a parent struct device is passed in,the newly created

     * struct device will be a child of that device insysfs.

     * The pointer to the struct device will be returnedfrom the call.

     * Any further sysfs files that might be required canbe created using this

     * pointer.

     *

     * Note: the struct class passed to this function musthave previously

     * been created with a call to class_create().

     */

    struct device *device_create(struct class *class, structdevice *parent,

                       dev_t devt, const char *fmt, ...)

    {

         va_list vargs;

         struct device *dev;

     va_start(vargs, fmt);

         dev =device_create_vargs(class, parent, devt, NULL, fmt, vargs);

         va_end(vargs);

         return dev;

    }
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。 
下面以一个简单字符设备驱动来展示如何使用这几个函数 

   #include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/init.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

#include <linux/slab.h>

#include <linux/device.h>

#include "memdev.h"

static int mem_major =MEMDEV_MAJOR;

struct mem_dev *mem_devp; /*设备结构体指针*/

bool have_data = false; /*表明设备有足够数据可供读*/

struct cdev cdev; 

struct class *myclass=NULL;

/*文件打开函数*/

int mem_open(struct inode *inode, struct file *filp)

{

    struct mem_dev *dev;

    

    /*获取次设备号*/

    int num = MINOR(inode->i_rdev);

    if (num >=MEMDEV_NR_DEVS) 

            return-ENODEV;

    dev = &mem_devp[num];

    

    /*将设备描述结构指针赋值给文件私有数据指针*/

    filp->private_data = dev;

    

    return 0; 

}

/*文件释放函数*/

int mem_release(struct inode *inode, struct file *filp)

{

  return 0;

}

/*读函数*/

static ssize_t mem_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)

{

  unsigned long p =  *ppos;

  unsigned int count = size;

  int ret = 0;

  struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/

            

  /*判断读位置是否有效*/

  if (p >= MEMDEV_SIZE)

    return 0;

  if (count > MEMDEV_SIZE - p)

    count = MEMDEV_SIZE - p;

while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while,中断信号唤醒 */

{

        if (filp->f_flags &O_NONBLOCK)

            return-EAGAIN;

 

 wait_event_interruptible(dev->inq,have_data);

}

  /*读数据到用户空间*/

  if (copy_to_user(buf, (void*)(dev->data + p), count))

  {

    ret =  - EFAULT;

  }

  else

  {

    *ppos += count;

    ret = count;

    

    printk(KERN_INFO "read %d bytes(s) from %d\n",(int)count, (int)p);

  }

  have_data = false; /* 表明不再有数据可读 */

  return ret;

}

/*写函数*/

static ssize_t mem_write(struct file *filp, const char __user *buf, size_tsize, loff_t *ppos)

{

  unsigned long p =  *ppos;

  unsigned int count = size;

  int ret = 0;

  struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/

    

  /*分析和获取有效的写长度*/

  if (p >= MEMDEV_SIZE)

    return 0;

  if (count > MEMDEV_SIZE - p)

    count = MEMDEV_SIZE - p;

    

  /*从用户空间写入数据*/

  if (copy_from_user(dev->data + p, buf, count))

    ret =  - EFAULT;

  else

  {

    *ppos += count;

     ret = count;

    

    printk(KERN_INFO "written %d bytes(s) from %d\n",(int)count, (int)p);

  }

  have_data = true; /* 有新的数据可读 */

    

    /* 唤醒读进程 */

  wake_up(&(dev->inq));

  return ret;

}

/* seek文件定位函数 */

static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)



    loff_t newpos;

    switch(whence){

      case 0: /* SEEK_SET */

        newpos = offset;

        break;

     case 1: /* SEEK_CUR */

        newpos = filp->f_pos + offset;

        break;

     case 2: /* SEEK_END */

        newpos = MEMDEV_SIZE -1 + offset;

        break;

     default: /* can't happen */

        return -EINVAL;

    }

    if ((newpos<0) || (newpos>MEMDEV_SIZE))

     return -EINVAL;

     

    filp->f_pos = newpos;

    return newpos;

}

//static int mem_ioctl(struct inode * inode,struct file *flip,unsigned int cmd,

//            unsignedlong arg)

static int  mem_ioctl(struct file *flip,unsigned int cmd,

            unsignedlong arg)

{

  struct mem_dev *dev = flip->private_data; /*获得设备结构体指针*/

  

  int err = 0;

  int ret = 0;

  int ioarg = 0;

    

  /* 检测命令的有效性 */

  if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) 

      return -EINVAL;

  if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) 

        return -EINVAL;

    /* 根据命令类型,检测参数空间是否可以访问 */

    if (_IOC_DIR(cmd) & _IOC_READ)

        err = !access_ok(VERIFY_WRITE, (void*)arg, _IOC_SIZE(cmd));

    else if (_IOC_DIR(cmd) & _IOC_WRITE)

        err = !access_ok(VERIFY_READ, (void*)arg, _IOC_SIZE(cmd));

    if (err) 

        return -EFAULT;

    

    

    /* 根据命令,执行相应的操作 */

    switch(cmd) {

      /*打印当前设备信息 */

      case MEMDEV_IOCPRINT:

       printk("<--- CMD MEMDEV_IOCPRINTDone--->\n\n");

        break;

     case MEMDEV_IOCMEMEST:

    memset(dev->data, 0, MEMDEV_SIZE);   

        break;

      

      /* 获取参数 */

      case MEMDEV_IOCGETDATA:

       ioarg = 1101;

        ret = __put_user(ioarg, (int *)arg);

        break;

      

      /* 设置参数 */

      case MEMDEV_IOCSETDATA:

       ret = __get_user(ioarg, (int *)arg);

        printk("<--- In Kernel MEMDEV_IOCSETDATAioarg = %d --->\n\n",ioarg);

        break;

     default:  

        return -EINVAL;

    }

    return ret;

 

}

/*文件操作结构体*/

static const struct file_operations mem_fops =

{

  .owner = THIS_MODULE,

  .llseek = mem_llseek,

  .read = mem_read,

  .write = mem_write,

  .open = mem_open,

  .release = mem_release,

  .unlocked_ioctl = mem_ioctl,  

};

/*设备驱动模块加载函数*/

static int memdev_init(void)

{

  int result;

  int i;

  dev_t devno =MKDEV(mem_major, 0);

 

 if (mem_major)

   result = register_chrdev_region(devno,2,"ZXJ");

 else

 {

    result =alloc_chrdev_region(&devno,0,2,"ZXJ");

    mem_major = MAJOR(devno);

 }  

  

  if (result < 0)

    return result;

/*  

  cdev_init(&cdev,&mem_fops);

  cdev.owner = THIS_MODULE;

  cdev.ops = &mem_fops;

  

  cdev_add(&cdev,MKDEV(mem_major, 0),MEMDEV_NR_DEVS);

*/

  /* 为设备描述结构分配内存*/

  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);

  if (!mem_devp)    /*申请失败*/

  {

    result =  - ENOMEM;

    goto fail_malloc;

  }

  /*  清零存储器  */ 

  memset(mem_devp, 0, sizeof(struct mem_dev) * MEMDEV_NR_DEVS);  

  /*  创建类/sys文件夹下面  */ 

  myclass=class_create(THIS_MODULE,"test");

  /*为设备分配内存*/

  for (i=0; i < MEMDEV_NR_DEVS; i++) 

  {

        mem_devp[i].size = MEMDEV_SIZE;

        mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);

        if (!mem_devp[i].data)

          printk("BadKmalloc\n");

        memset(mem_devp[i].data, 0,MEMDEV_SIZE);

       sprintf(mem_devp[i].name,"memdev%d",i);

     cdev_init(&mem_devp[i].cdev,&mem_fops);

      mem_devp[i].cdev.owner = THIS_MODULE;

     result =cdev_add(&mem_devp[i].cdev,MKDEV(mem_major, i),1);                

        if (result)

        {

          printk("Badcdev\n");

          return result;

        }

               /* 初始化信号量 */

     init_waitqueue_head(&(mem_devp[i].inq)); 

     device_create(myclass,NULL,MKDEV(mem_major,i),NULL,"memdev%d",i); 

  }

  return 0;

    

  fail_malloc: 

  unregister_chrdev_region(devno, 1);

  

  return result;  

  

}

/*模块卸载函数*/

static void memdev_exit(void)

{

  int i;

  for (i=0; i < MEMDEV_NR_DEVS; i++) 

  {  

   device_destroy(myclass,MKDEV(mem_major, i));//删除设备文件

   cdev_del(&mem_devp[i].cdev);   /*注销设备*/ 

   kfree(mem_devp[i].data);     /*释放内存*/  

 } 

  class_destroy(myclass); 

  kfree(mem_devp);     /*释放设备结构体内存*/

  unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/

}

 

MODULE_AUTHOR("Smart.zhao");

MODULE_LICENSE("GPL");

module_init(memdev_init);

module_exit(memdev_exit);

 

 

当加载模块的时候,会在/dev/memdev0,/dev/memdev1两个设备文件,如图所示

本人使用的linux-3.1的内核
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: