Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2675901
  • 博文数量: 877
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5921
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 12:25
个人简介

技术的乐趣在于分享,欢迎多多交流,多多沟通。

文章分类

全部博文(877)

文章存档

2021年(2)

2016年(20)

2015年(471)

2014年(358)

2013年(26)

分类: LINUX

2014-03-28 14:40:44

#include
#include
#include
#include
static struct class *led_class;

int led_open(struct inode *inode, struct file *file)
{
    printk("led_open\n");
    return 0;
}
ssize_t led_write(struct file *file, const char __user *user, size_t size, loff_t *off)
{
    printk("led_write\n");
    return 0;
}

static struct file_operations led_operation = {
    .owner=THIS_MODULE,
    .open=led_open,
    .write=led_write,   
};
int major;
int led_init(void)
{
    major=register_chrdev(0,"led",&led_operation);

    led_class = class_create(THIS_MODULE, "led_class");

    device_create(led_class, NULL, MKDEV(major, 0), NULL,"led_device");
    return 0;
}

void led_exit(void)
{
    unregister_chrdev(major,"led");
    device_destroy(led_class,MKDEV(major, 0));
    class_destroy(led_class);
    
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

主要思路:先创建一个类,在类下创建设备!这样我们就不需要在开发板上查看主设备号,然后手动创建设备节点了!在开发板上:ls /sys/class/会看到我们创建的类,ls /sys/class/led_class/会看到我们在类下创建的设备!不过真正的设备节点在/dev目录下面,通过命令:ls /dev/led_device可以查看到!
需要注意的是,我们在编写应用程序的时候,open函数里面的设备名字要跟驱动里面device_create指定的设备名字相一致!

但是,系统做了什么呢?在开发板的/etc/init.d/rcS文件里面有如下的信息:
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
这两行信息就是说,一旦发生了热插拔事件(比如创建了类,在类下创建了设备),就会调用/sbin/mdev命令,mdev命令会通过环境变量中的 ACTION 和 DEVPATH来判断此次热插拔事件影响了/sys目录下的那个文件,一旦发现了这个文件,就会进入这个文件里面去查找dev的属性文件,并根据属性创建设备节点!比如我们加载驱动的的时候,会在 /sys/class/目录下创建类,在 /sys/class/led_class目录下创建设备,在
/sys/class/led_class目录下有个dev文件,dev文件里面就有设备的主次设备号,mdev就会根据主次设备号在/dev/目录下创建设备节点!

 /***************************************************************************************************
                         linux字符设备驱动总结之:全自动创建设备及节点
看了LDD3,深入浅出LDD,以及各个博文,还是需要总结下的。
张永辉 2012年10月9日
***************************************************************************************************/
概览:
    第一步:注册设备号                                              信息#tail -f /var/log/message
        注册函数:
            register_chrdev_region() 或                             查看#lsmod
            alloc_chrdev_region()    或                             查看#cat /proc/devices
            register_chrdev()
        注销函数:
            unregist_chrdev_region() 或
            unregister_chrdev()
    
    第二步:初始化cdev并添加到系统
        初始化cdev
            静态初始化 cdev_init() 或
            动态初始化 cdev_alloc()
        添加到系统函数
            cdev_add()
        从系统删除函数
            cdev_del()
            
    第三步:创建设备节点
        创建类
            class_create()          将放于/sysfs                    查看#ls /sys/class
        删除类
            class_destroy()
        
        创建节点
            device_create() 或 class_device_create()  将存放于/dev  查看#ls /dev
        删除节点
            device_destroy() 或 class_device_destroy()
    
    第四步:简单示例(待续...)

/***************************************************************************************************
                                   第一步:注册设备号
***************************************************************************************************/
Linux内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。
    该散列表中的每一个元素是一个 char_device_struct 结构,它的定义如下:
    static struct char_device_struct
    {
        struct char_device_struct *next;    // 指向散列冲突链表中的下一个元素的指针
        unsigned    int major;              // 主设备号
        unsigned    int baseminor;          // 起始次设备号
        int minorct;                        // 设备编号的范围大小
        char    name[64];                   // 处理该设备编号范围内的设备驱动的名称
        struct file_operations *fops;       // 没有使用
        struct cdev *cdev;                  // 指向字符设备驱动程序描述符的指针
    }*chrdevs[CHRDEV_MAJOR_HASH_SIZE];

    1 每一个主设备有一个会分配一个此结构,可以有多个次设备号。次设备是依次递增的。
    2 内核提供了5个函数来来管理字符设备编号。
    
            register_chrdev_region()        指定初始值
            alloc_chrdev_region()           动态分配
            register_chrdev()               指定设备号
        他们都会调用 __register_chrdev_region() 来注册一组设备编号范围(一个char_device_struct结构),我们使用其中一个即可。
            
            unregist_chrdev_region()        释放都用此函数
            unregister_chrdev()             都调用了 __unregister_chrdev_region() 来注销设备

        注册:
            register_chrdev_region(dev_t first,unsigned int count,char *name)
                first :要分配的设备编号范围的初始值(次设备号常设为0);
                count :连续编号范围.
                Name  :编号相关联的设备名称. (/proc/devices);


            int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
                *dev        :存放返回的设备号
                firstminor  :第一个次设备号的号数,常为0;

            int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
                major :要注册的设备号, 若为0则自动分配一个
                name  :设备名
                *fops :以后再聊

        释放:
            void unregister_chrdev(unsigned int major, const char *name);
            void unregister_chrdev_region(dev_t from, unsigned count);
    3 示例:略
    4 参考:感谢原著 (有此6个函数的源码及解说)。
        
http://blog.csdn.net/iLetLet/article/details/6180314

/***************************************************************************************************
                            第二步:初始化 cdev 并添加到系统
***************************************************************************************************/
1.内核中每个字符设备都对应一个 cdev 结构的变量,定义如下:
    linux-2.6.22/include/linux/cdev.h
    struct cdev 
    {
        struct kobject kobj;                //每个 cdev 都是一个 kobject
        struct module *owner;               //指向实现驱动的模块
        const struct file_operations *ops;  //操纵这个字符设备文件的方法
        struct list_head list;              //与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
        dev_t dev;                          //起始设备编号
        unsigned int count;                 //设备范围号大小
    };

2. 初始化cdev :有两种定义初始化方式:
    
    方式1:静态内存定义初始化:
        struct cdev my_cdev;
        cdev_init(&my_cdev, &fops);
        my_cdev.owner = THIS_MODULE;
        
    方式2:动态内存定义初始化:
        struct cdev *my_cdev = cdev_alloc();
        my_cdev->ops = &fops;
        my_cdev->owner = THIS_MODULE;


    下面是2函数的具体代码:
        struct cdev *cdev_alloc(void)       //它主要完成了空间的申请和简单的初始化操作;
        {
            struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
            if (p)
            {
                INIT_LIST_HEAD(&p->list);
                kobject_init(&p->kobj, &ktype_cdev_dynamic);
            }
            return p;
        }
    
        void cdev_init(struct cdev *cdev, const struct file_operations *fops)
        {   
            memset(cdev, 0, sizeof *cdev);  //主要是对空间起到一个清零作用并较之cdev_alloc多了一个ops的赋值操作
            INIT_LIST_HEAD(&cdev->list);
            kobject_init(&cdev->kobj, &ktype_cdev_default);
            cdev->ops = fops;
        }

3. 添加cdev到系统
    为此可以调用 cdev_add() 函数。传入cdev结构的指针,起始设备编号,以及设备编号范围。
    int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    {
        p->dev = dev;
        p->count = count;
        return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
    }

    释放时使用 cdev_del()函数来释放cdev占用的内存。
    void cdev_del(struct cdev *p)
    {
        cdev_unmap(p->dev, p->count);   //释放 cdev_map 散列表中的对象
        kobject_put(&p->kobj);          //释放 cdev 结构本身。
    }

4.关于kobject_init() kobj_map()
    内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。
    这个结构的变量中包含一个散列表用来快速存取所有的对象。
    kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。
    当后续要打开一个字符设备文件时,通过调用 kobj_lookup()  函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

/***************************************************************************************************
                            第三步:创建设备节点
***************************************************************************************************/
    方法一:利用mknod命令手动创建设备节点。
    方法二:实际上Linux内核为我们提供了一组函数,可以在模块加载的时候在/dev目录下创建相应设备节点,在卸载时可删除该节点。

    原理:
        1 内核中定义了struct class结构体,它对应一个类。
        2 先调用class_create()函数,可以用它来创建一个类,这个类将存放于sysfs下面.
        3 再调用device_create()函数,从而在/dev目录下创建相应的设备节点。
        4 卸载模块对应的函数是 device_destroy 和 class_destroy()
        注:2.6 以后的版本使用device_create(),之前的版本使用的class_device_create()。

    详解:
        1:class结构:
            include/linux/device.h
            struct class
            {
                const   char        *name;
                struct module       *owner;
                struct kset         subsys;
                struct list_head    devices;
                struct list_head    interfaces;
                struct kset         class_dirs;
                struct semaphore sem;       /* locks    children, devices, interfaces */
                struct class_attribute  *class_attrs;
                struct device_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);
            };

        2:class_create() 
            class_create()在/drivers/base/class.c中实现:
            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);
            }
            
        3:device_create()函数在/drivers/base/core.c中实现:
            struct device *device_create(struct class *class,   //指定所要创建的设备所从属的类
                                        struct devicev *parent, //这个设备的父设备,如果没有就指定为NULL
                                        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;
            }

http://blog.csdn.net/zyhui65/article/details/8053116
阅读(853) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~