linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。相比devfsudev有很多优势,在此就不罗嗦了,提醒一点,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的内核

 

Logo

更多推荐