linux驱动开发之字符设备--自动创建设备节点

mdev

在/dev下创建设备的方法有两种,一种是手动的使用 mknod 进行创建。另外一种是使用 mdev 进行自动的创建设备。

mdev集成在busybox中,mdev 会自动检测 /sys/class 和 /sys/block 的所有类设备目录;如果在目录中含有名为“dev”的文件,同时如果内容是设备号的话,mdev
就会利用这些信息,在/dev下创建这个设备节点。

内核API

创建类

#define class_create(owner, name)       \
({                      \
    static struct lock_class_key __key; \
    __class_create(owner, name, &__key);    \
})

参数:

  • owner: THIS_MODULE
  • name :在/sys/class/下显示的name

创建设备

struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)

参数:

  • class : 创建的类,
  • parent: 父类。通常为NULL
  • devt : 设备号
  • drvdata : 通常为NULL
  • fmt: 可变参数,通常为 设备的名字

有类、设备的创建,就有类、设备的销毁

void class_destroy(struct class *cls)
void device_destroy(struct class *class, dev_t devt)

实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>

#include <linux/uaccess.h>
//幻数
#define IOCTL_TYPE 'b'

#define COUNT   32

struct  ioctl_arg {
    int val;
    char buf[COUNT];
};
//定义的命令码
#define CMDCTL  _IO(IOCTL_TYPE,0)
#define CMDR    _IOR(IOCTL_TYPE,1,struct ioctl_arg)
#define CMDW    _IOW(IOCTL_TYPE,2,struct ioctl_arg)

#define DEVICE_CLASS_NAME   "cdev_demo_class"
#define DEVICE_NAME "cdev_demo"

static struct cdev *pdev = NULL;
static int major = 0;
static int minor = 0;
static int count = 2;

static struct  class  *cdev_demo_class;
struct kobject *cdev_obj;


#define BUF_SIZE        (1024)
static char kbuf[BUF_SIZE];
static int  buf_count = 0;

static int cdev_demo_open(struct inode * inode, struct file * file)
{
    printk("%s,%d\n",__func__,__LINE__);
    return 0;
}
static int cdev_demo_release(struct inode *inode, struct file * file)
{
    printk("%s,%d\n",__func__,__LINE__);
    return 0;
}
static ssize_t cdev_demo_read(struct file * file, char __user * buffer, size_t size, loff_t * loff)
{
    printk("%s,%d\n",__func__,__LINE__);

    if(0 == buf_count){
        return -EAGAIN;
    }

    if(buf_count < size){
        size = buf_count;
    }

    if(size == copy_to_user(buffer,kbuf,size)){
        return -EAGAIN;
    }

    buf_count  = 0;

    return size;

}

static ssize_t cdev_demo_write(struct file * file, const char __user * buffer, size_t size, loff_t * loff)
{
    printk("%s,%d\n",__func__,__LINE__);
    printk("buffer=%s   size=%d\n",buffer,size);
    if(size >BUF_SIZE){
        return -ENOMEM;
    }
    if(size == copy_from_user(kbuf,buffer,size)){
        return -EAGAIN;
    }

    buf_count = size;
    return size;
}
//ioctl
static long cdev_demo_ioctl (struct file *filep, unsigned int cmd, unsigned long arg)
{
    static struct ioctl_arg buf;
    printk("%s,%d\n",__func__,__LINE__);
    //分辨不同命令码
    switch(cmd){
        case CMDCTL:
                printk("do CMDCTL\n");
                break;
        case CMDR:
            //使用 _IOC_SIZE()获得命令码中的数据长度
            if(sizeof(buf) != _IOC_SIZE(cmd)){
                return -EINVAL;
            }

            if(sizeof(buf) == copy_to_user((struct ioctl_arg*)arg,&buf,sizeof(struct ioctl_arg))){
                return -EAGAIN;
            }
            printk("do CMDR\n");
            break;
        case CMDW:
            if(sizeof(buf)!= _IOC_SIZE(cmd)){
                 return -EINVAL;
            }

            if(sizeof(buf) == copy_from_user(&buf,(struct ioctl_arg*)arg,sizeof(buf))){
                return -EAGAIN;
            }
            printk("do CMDW\n");
            printk("%d,%s \n",buf.val,buf.buf);
            break;

        default:
            break;

    }

    return 0;
}
static struct file_operations fops ={
    .owner   = THIS_MODULE,
    .open    = cdev_demo_open,
    .release = cdev_demo_release,
    .read    = cdev_demo_read,
    .write   = cdev_demo_write,
    .unlocked_ioctl = cdev_demo_ioctl,
};

char cdev_buf[2] = "a";

static ssize_t cdev_demo_show(struct device *dev,struct device_attribute *attr, char *buf)  
{
    char *s = buf;
    s = sprintf(s,"%s",cdev_buf);
    return sizeof(s);
}
static ssize_t cdev_demo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{

    memcpy(cdev_buf,buf,count);
    return count;
}

static DEVICE_ATTR(cdev_demo, 0666, cdev_demo_show, cdev_demo_store);

static struct attribute  *g[] = {
    &dev_attr_cdev_demo.attr,
    NULL,
};
static struct attribute_group attr_group = {
    .attrs = g,
};
static int __init cdev_demo_init(void)
{
    dev_t dev;
    int ret;
    int i;
    struct device * cdemo_device;
    printk("%s,%d\n",__func__,__LINE__);

    pdev = cdev_alloc();
    if(NULL == pdev){
        printk("cdev_alloc failed.\n");
        return -ENOMEM;
    }

    cdev_init(pdev,&fops);

    ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME);
    if(ret){
        printk("alloc_chrdev_region failed.\n");
        goto ERROR_CDEV;
    }
    major = MAJOR(dev);
    ret = cdev_add(pdev, dev,count);
    if(ret) {
        printk("cdev_add failed.\n");
        goto  ERROR_ADD;
    }
    cdev_obj = kobject_create_and_add("cdev_demo",NULL);
    if(!cdev_obj){
        return -ENOMEM;
    }

    ret = sysfs_create_group(cdev_obj,&attr_group);
    if(ret)
        goto SYSFS_ERR;

    cdev_demo_class = class_create(THIS_MODULE,DEVICE_CLASS_NAME);
    if(IS_ERR(cdev_demo_class)){
        ret = PTR_ERR(cdev_demo_class);
         printk("Unable to create class, err = %d\n", ret);
        goto  CLASS_ERROR;
    }

    for(i = minor; i < minor + count; i ++){
        cdemo_device = device_create(cdev_demo_class,NULL,MKDEV(major,i),NULL,"%s%d",DEVICE_NAME,i);
        if(NULL == cdemo_device){
            ret = -EIO; 
            goto DEVICE_ERROR;
        }
    }

    return 0;
DEVICE_ERROR:
    for(i-- ; i  >= minor ; i--){
        device_destroy(cdev_demo_class, MKDEV(major,i));
    }
        class_destroy(cdev_demo_class);
CLASS_ERROR:
    sysfs_remove_group(cdev_obj,&attr_group);

SYSFS_ERR:
    kobject_del(cdev_obj);
    kobject_put(cdev_obj);

ERROR_ADD:
    unregister_chrdev_region(dev,count);
ERROR_CDEV:
    cdev_del(pdev);
    return ret;
}
static void __exit cdev_demo_exit(void)
{
    int i;
    printk("%s,%d\n",__func__,__LINE__);

    for(i = minor;i <count + minor;i ++){
    device_destroy(cdev_demo_class, MKDEV(major,i));
    }
    class_destroy(cdev_demo_class);
    sysfs_remove_group(cdev_obj,&attr_group);
    kobject_del(cdev_obj);
    kobject_put(cdev_obj);
    unregister_chrdev_region(MKDEV(major,minor),count);
    cdev_del(pdev);
}

module_init(cdev_demo_init);
module_exit(cdev_demo_exit);
MODULE_LICENSE("GPL");

结果:

//创建的类
shell@tiny4412:/sys/class/cdev_demo_class # ls -la
lrwxrwxrwx root     root              2014-01-01 20:01 cdev_demo 
->../../devices/virtual/cdev_demo_class/cdev_demo
// 类下/dev的内容
shell@tiny4412:/sys/class/cdev_demo_class/cdev_demo # cat dev
247:0
//在/dev/下创建的设备
ls -la /dev/cdev_demo                                                          
crw------- root     root     247,   0 2014-01-01 20:01 cdev_demo

在创建设备的时候,没有指定parent,则设备创建在一下目录

/sys/device/vitual/cdev_demo_class/

参考文献

浅析为什么device_create()生成文件添加到/sys/devices/virtual/目录

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值