使用class 自动创建设备节点

#include <linux/init.h>// __init   __exit
#include <linux/module.h>      // module_init  module_exit
#include <linux/fs.h>          //file_operations
#include <asm/uaccess.h>          //copy_from_user  copy_to_user
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <asm/string.h> 
#include <linux/ioport.h>          //request_mem_region
#include <asm/io.h> //ioremap
#include <linux/cdev.h>
#include <linux/device.h>
#define MYNAME "led_dev"
#define DEVNUM 1
#define S5PV210_PA_GPIOJ0CON 0xe0200240
volatile unsigned int *rGPJ0CON  = NULL; 
volatile unsigned int *rGPJ0DAT = NULL;  
static dev_t led_dev_no = 0;
/************ 静态使用cdev**********/
//static struct cdev led_cdev;
/************ 动态使用cdev**********/
static struct cdev *pled_cdev = NULL;
static struct class *pled_dev_class = NULL;
static int led_open(struct inode *inode, struct file *file);
ssize_t led_read(struct file *file, char __user *user, size_t count, loff_t *loff);
ssize_t led_write(struct file *file, const char __user *user, size_t count, loff_t *loff);
static int led_release(struct inode *inode, struct file *file);
static char kbuf[100] = {0};
static const struct file_operations led_fops = {
.owner= THIS_MODULE,
.open= led_open,
.read       = led_read,
.write      = led_write,
.release= led_release,
};
int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_open successful\n");
return 0;
}
ssize_t led_read(struct file *file, char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_read successful\n");
if (copy_to_user(user,kbuf , ucount))
{
printk(KERN_INFO "copy_to_user fail\n");
return -EINVAL;
}
printk(KERN_INFO "copy_to_user successful\n");
return strlen(kbuf);
}
ssize_t led_write(struct file *file, const char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_write successful\n");
memset(kbuf,0,sizeof(kbuf));
if (copy_from_user(kbuf, user, ucount))
{
printk(KERN_INFO "copy_from_user fail\n");
return -EINVAL;
}
if(!strcmp(kbuf,"on"))
{
    *rGPJ0CON &=0xff000fff;
    *rGPJ0CON |=0x00111000;
        *rGPJ0DAT &=~((0x01<<3)|(0x01<<4)|(0x01<<5));
}
else if(!strcmp(kbuf,"off"))
{
    *rGPJ0CON &=0xff000fff;
    *rGPJ0CON |=0x00111000;
        *rGPJ0DAT |=((0x01<<3)|(0x01<<4)|(0x01<<5));
}
return ucount;
printk(KERN_INFO "copy_from_user successful\n");
}
int led_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_release successful\n");
return 0;
}
// 模块安装函数
static int __init chrdev_init(void)
{
int ret = -1;
int retval = -1;
printk(KERN_INFO "chrdev_init successful\n");
/******指定主设备号注册*****/
//led_dev_no = MKDEV(250,0);
//retval = register_chrdev_region(led_dev_no, DEVNUM,MYNAME);
/******内核自动分配主设备号注册*****/
retval = alloc_chrdev_region(&led_dev_no,0,DEVNUM,MYNAME);
if (retval)
{
printk(KERN_WARNING "alloc_chrdev_region fail\n.");
ret = -EINVAL;
goto err_reg;
}
printk(KERN_INFO "alloc_chrdev_region successful major = %d,minor = %d \n",MAJOR(led_dev_no),MINOR(led_dev_no));
/****动态申请cdev实体******/
pled_cdev = cdev_alloc();
printk(KERN_INFO "cdev_alloc successful\n");
cdev_init(pled_cdev, &led_fops);
retval = cdev_add(pled_cdev, led_dev_no, DEVNUM);
if (retval)
{
printk(KERN_WARNING "cdev_add fail\n.");
ret = -EINVAL;
goto err_add;
}
printk(KERN_INFO "cdev_add successful\n");
// 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息
// 给udev,让udev自动创建和删除设备文件
pled_dev_class = class_create(THIS_MODULE, "led_musk");
if (IS_ERR(pled_dev_class))
{
printk(KERN_WARNING "class_create fail\n.");
ret = -EINVAL;
goto err_class_create;
}
device_create(pled_dev_class, NULL, led_dev_no, NULL, "led_dev");       //“led_dev”对应/dev/下的名字
if (request_mem_region(S5PV210_PA_GPIOJ0CON, 8, "GPIOJ0CON") == NULL) 
{
printk(KERN_WARNING "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}
rGPJ0CON = ioremap(S5PV210_PA_GPIOJ0CON,8);
if (rGPJ0CON ==  NULL) 
{
printk(KERN_WARNING "fail to ioremap() region\n");
ret = -ENOENT;
goto err_map;
}
rGPJ0DAT = rGPJ0CON+1;
return 0;
err_map:
release_mem_region(S5PV210_PA_GPIOJ0CON,8);
err_req:
class_destroy(pled_dev_class);
err_class_create:
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev);
err_add:
printk(KERN_INFO "err_add err\n");
unregister_chrdev_region(led_dev_no, DEVNUM);
err_reg:
return ret;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
iounmap(rGPJ0CON);
release_mem_region(S5PV210_PA_GPIOJ0CON,8);
device_destroy(pled_dev_class,led_dev_no);
class_destroy(pled_dev_class);
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev);
unregister_chrdev_region(led_dev_no, DEVNUM);
printk(KERN_INFO "chrdev_exit successful\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");// 描述模块的许可证
MODULE_AUTHOR("musk");// 描述模块的作者
MODULE_DESCRIPTION("module test");// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");// 描述模块的别名信息
View Code

一. 什么是class

     1.1. class 指的是 设备类(device classes),是对于设备的高级抽象。但实际上 class 也是一个结构体,只不过 class 结构体在声明时是按照类的思想来组织其成员的。运用 class,可以让用户空间的程序根据自己要处理的事情来调用设备,而不是根据设备被接入到系统的方式或设备的工作原理来调用

    1.2.  一个 struct class 结构体类型变量对应一个类,内核提供了class_create() 函数,可以用它来创建一个类,这个类存放于 sysfs 下面。 一旦创建了类,再调用 device_create() 函数在 /dev 目录下创建相应的设备节点

        1.2.1. sysfs虚拟文件系统

            a. sysfs是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训

            b. 当使用class_create创建时,在/sys/class/中会创建相应文件

二. linux中的mdev机制

    2.1.什么是mdev 

        2.1.1. mdev是busybox自带的一个简化版的udev。适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所

需的节点文件。在以busybox 为基础构建嵌入式linux 的根文件系统时,使用它是最优的选择

        2.1.2. mdev 的使用在busybox 中的mdev.txt 文档已经将得很详细了

    2.2. mdev 用法

        2.2.1.执行mdev前要挂载 /sys

            a. mount -t tmpfs mdev /dev

            b.mount -t sysfs sysfs /sys

        2.2.2. 命令内核在增删设备时执行/sbin/mdev,使设备节点会被创建和删除

            a. echo /sbin/mdev > /proc/sys/kernel/hotplug

        2.2.3. 设置mdev,让它在系统启动时创建所有的设备节点

            a. mdev -s

        2.2.4. 相关操作bsp 下/etc/init.d/rcS启动开发板时会设置。

PATH=/sbin:/bin:/usr/sbin:/usr/bin

runlevel=S
prevlevel=N

umask 022

export PATH runlevel prevlevel

mount -a

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

/bin/hostname -F /etc/sysconfig/HOSTNAME

ifconfig eth0 192.168.1.20
~
- /etc/init.d/rcS 1/19 5%
View Code

 三. 相关函数分析

    3.1. class_create函数分析。

        3.1.1. 函数调用层级 

            class_create

            __class_create

        __class_register

    kset_register

  kobject_uevent

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)        \
({                        \
    static struct lock_class_key __key;    \
    __class_create(owner, name, &__key);    \
})

/**
 * 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.
 * @key: the lock_class_key for this class; used by mutex lock debugging
 *
 * This is used to create a struct class pointer that can then be used
 * in calls to device_create().
 *
 * Returns &struct class pointer on success, or ERR_PTR() on error.
 *
 * Note, the pointer created here is to be destroyed when finished by
 * making a call to class_destroy().
 */
struct class *__class_create(struct module *owner, const char *name,
                 struct lock_class_key *key)
{
    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, key);
    if (retval)
        goto error;

    return cls;

error:
    kfree(cls);
    return ERR_PTR(retval);
}

int __class_register(struct class *cls, struct lock_class_key *key)
{
    struct class_private *cp;
    int error;

    pr_debug("device class '%s': registering\n", cls->name);

    cp = kzalloc(sizeof(*cp), GFP_KERNEL);
    if (!cp)
        return -ENOMEM;
    klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
    INIT_LIST_HEAD(&cp->class_interfaces);
    kset_init(&cp->class_dirs);
    __mutex_init(&cp->class_mutex, "struct class mutex", key);
    error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
    if (error) {
        kfree(cp);
        return error;
    }

    /* set the default /sys/dev directory for devices of this class */
    if (!cls->dev_kobj)
        cls->dev_kobj = sysfs_dev_char_kobj;

#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
    /* let the block class directory show up in the root of sysfs */
    if (cls != &block_class)
        cp->class_subsys.kobj.kset = class_kset;
#else
    cp->class_subsys.kobj.kset = class_kset;
#endif
    cp->class_subsys.kobj.ktype = &class_ktype;
    cp->class = cls;
    cls->p = cp;

    error = kset_register(&cp->class_subsys);
    if (error) {
        kfree(cp);
        return error;
    }
    error = add_class_attrs(class_get(cls));
    class_put(cls);
    return error;
}
/**
 * kset_register - initialize and add a kset.
 * @k: kset.
 */
int kset_register(struct kset *k)
{
    int err;

    if (!k)
        return -EINVAL;

    kset_init(k);
    err = kobject_add_internal(&k->kobj);
    if (err)
        return err;
    kobject_uevent(&k->kobj, KOBJ_ADD);
    return 0;
}
/**
 * kobject_uevent - notify userspace by ending an uevent
 *
 * @action: action that is happening
 * @kobj: struct kobject that the action is happening to
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
    return kobject_uevent_env(kobj, action, NULL);
}
View Code

    3.2. device_create函数分析。

        3.2.1. 函数调用层级          

                device_create

                device_create_vargs

                kobject_set_name_vargs

                device_register

                device_add

                kobject_add

                device_create_file

                device_create_sys_dev_entry

                devtmpfs_create_node

                device_add_class_symlinks

                device_add_attrs

                device_pm_add

                kobject_uevent

/**
 * device_create - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @fmt: string for the device's name
 *
 * This function can be used by char device classes.  A struct device
 * will be created in sysfs, registered to the specified class.
 *
 * A "dev" file will be created, showing the dev_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 in sysfs.
 * The pointer to the struct device will be returned from the call.
 * Any further sysfs files that might be required can be created using this
 * pointer.
 *
 * Returns &struct device pointer on success, or ERR_PTR() on error.
 *
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */
struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
{
    va_list vargs;
    struct device *dev;

    va_start(vargs, fmt);
    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
    va_end(vargs);
    return dev;
}
View Code

        3.2.2. 调用此函数最早会创建/sys/class/xxx 下dev        power      subsystem   uevent等文件

    3.3. device_destroy函数

        3.3.1. 函数定义:

/**
 * device_destroy - removes a device that was created with device_create()
 * @class: pointer to the struct class that this device was registered with
 * @devt: the dev_t of the device that was previously registered
 *
 * This call unregisters and cleans up a device that was created with a
 * call to device_create().
 */
void device_destroy(struct class *class, dev_t devt)
{
    struct device *dev;

    dev = class_find_device(class, NULL, &devt, __match_devt);
    if (dev) {
        put_device(dev);
        device_unregister(dev);
    }
}
View Code

    3.4. class_destroy函数

        3.4.1.函数定义

/**
 * class_destroy - destroys a struct class structure
 * @cls: pointer to the struct class that is to be destroyed
 *
 * Note, the pointer to be destroyed must have been created with a call
 * to class_create().
 */
void class_destroy(struct class *cls)
{
    if ((cls == NULL) || (IS_ERR(cls)))
        return;

    class_unregister(cls);
}
View Code

四. 应用层调用内核

    4.1. 应用层代码如下:

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <string.h>

#define DEVFILE "/dev/led_dev"

int main(void)
{
    char buf[100] = {0};
    int fd = -1;
    if((fd =open(DEVFILE, O_RDWR))<0)
    {
        perror("open");
        return -1;   
    }
    printf("open successful fd = %d\n",fd);
    if(write(fd, "on", strlen("on"))<0)
    {
        perror("write");
        return -1;
    }   
    sleep(3);
    memset(buf,0,sizeof(buf));
    if(read(fd, buf, 3)<0)
    {
        perror("read");
        return -1;
    }
    printf("read data = %s\n",buf);
    
    
    if(write(fd, "off", strlen("off"))<0)
    {
        perror("write");
        return -1;
    }   
    sleep(3);
    memset(buf,0,sizeof(buf));
    if(read(fd, buf, 4)<0)
    {
        perror("read");
        return -1;
    }
    printf("read data = %s\n",buf);
    
    close(fd);
    return 0;
}
View Code

 

posted @ 2019-01-03 16:52  三七鸽  阅读(884)  评论(0编辑  收藏  举报