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;
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;
}
cdemo_device = device_create(cdev_demo_class,NULL,dev,NULL,DEVICE_NAME);
if(NULL == cdemo_device){
ret = -EIO;
goto DEVICE_ERROR;
}
return 0;
DEVICE_ERROR:
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)
{
printk("%s,%d\n",__func__,__LINE__);
device_destroy(cdev_demo_class, MKDEV(major,minor));
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/目录
作者:u013377887 发表于2017/5/26 21:40:34 原文链接
阅读:200 评论:0 查看评论