Linux驱动编程 step-by-step (四)

来源:互联网 发布:清理mac系统垃圾 编辑:程序博客网 时间:2024/05/17 01:29
 

似乎每一章介绍的内容比较少,但学习是一个循序渐进的过程,不在于一天学多少,重要的一天能真正的学懂多少,所以我主张一步一步来,从多个渠道去学习知识,实现互补。

本节测试代码传到此处了:char_step1 大家可以下载测试一下。

字符设备的注册与设备节点的自动创建

cdev 结构

内核内部使用struct cdev<linux/cdev.h>来表示一个字符设备

struct cdev {
    struct kobject kobj; //kobj设备模型以后介绍
    struct module *owner;
    const struct file_operations *ops;//文件操作
    struct list_head list;
    dev_t dev;//设备号
    unsigned int count; //设备个数
};

注册字符设备

动态初始化:

struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_ops;

静态初始化:

struct cdev my_cdev;
cdev_init(&my_dev, &my_ops);

向内核注册(添加设备):

int cdev_add(struct cdev *dev, dev_t num, unsigned count);
dev: 指向以初始化的字符设备结构
num: 设备号
count: 所要添加的设备个数
返回值:
成功返回0 失败返回错误码,设备被注册时候可能立即会呗调用,所以在驱动程序还没有准备好处理设备操作时候不要先调用cdev_add

注销字符设备:

在不需要用西设备时候需要注销设备
void cdev_del(struct cdev *);

早期的方法

在2.6的内核之前注册字符设备有一个简单易用的方法,现在也可以使用但是不建议使用,因为到最后这个方法会从内核消失

注册字符设备:

static inline int register_chrdev(unsigned int major, 
    const char *name, const struct file_operations *fops);
major: 主设备号 (当major为0时表示有内核分配一个可用的主设备号并返回)
name: 驱动名
fops: 文件操作
缺点: 不能同时注册多个设备, 他是用一种默认的方式去建立一个cdev结构,而这样的话它不能使用大于255的主次设备号

注销设备:

static inline void unregister_chrdev(unsigned int major, const char *name)

设备节点的自动创建

在上一个程序中,虽然能够争取的得到一个设备号,但是并不会在/dev/目录下生成一个设备节点。

使用手动创建

mknod /dev/device_name c major minor
device_name: 指设备名,
c: 表述创建字符设备节点(同理b 表示创建块设备文件节点)
major: 主设备号
minor: 次设备号
在做测试时候这样做事可以理解的,而在作为一个模块要发行时候,这样就太不方面了,①要先查看设备是否成功创建,②查看注册设备的主设备号,③输入命令创建文件节点
当然可以写一个shell脚本来完成这些事情。

自动创建设备节点

需要的纤体内核空间支持udev,在内核配置,及busybox配置时候需要指定
内核中定义了struct class 与 struct devic结构<linux/device.h>
创建一个class结构
#define class_create(owner, name)    \
({            \
    static struct lock_class_key __key;   \
    __class_create(owner, name, &__key); \
})
owner表示拥有这个模块的指针
name 表示设备名
调用此函数后 创建了一个struct class结构放在sysfs下边,而后调用
struct device *device_create(struct class *cls, 
    struct device *parent,
                    dev_t devt, void *drvdata,
                    const char *fmt, ...)
在/dev目录下创建设备节点
cls : 是上边函数的创建的struct class结构指针
parent: 指向设备的父节点设备(如果没有填NULL
devt: 被添加设备的设备号
drvdata: 回调函数参数
fmt ,args:一个或者多个设备名
至此我们就自动创建了一个设备节点

删除设备节点

在设备不需要使用时候我们需要删除设备节点:
void device_destroy(struct class *class, dev_t devt);//删除设备节点
void class_destroy(struct class *cls);//删除sysfs下得struct class结构
至于深层的一些实现机制,没有细细研究,大家干兴趣可以参考相关资料

实例:

初始化 添加设备到内核

  1.     simple_cdev = cdev_alloc(); //动态初始化字符设备  
  2.     if(simple_cdev != NULL)   
  3.     {   
  4.         simple_cdev->ops   = &simple_fops; //文件操作  
  5.         simple_cdev->owner = THIS_MODULE;   
  6.     }   
  7.     else  
  8.     {   
  9.         printk(KERN_ERR "alloc cdev err no memory");   
  10.         unregister_chrdev_region(dev, DEV_COUNT); //如果分配资源出错则释放设备号  
  11.         return -ENOMEM;   
  12.     }   
  13.   
  14.     err = cdev_add(simple_cdev, dev, DEV_COUNT); //向内核添加设备  
  15.     if(err < 0)   
  16.     {   
  17.         printk(KERN_ERR "add cdev err \n");   
  18.         goto error1;   
  19.     }   
  20.     else  
  21.     {   
  22. #if SIMPLE_DEBUG   
  23.         printk(KERN_INFO "add char dev OK!\n");   
  24. #endif   
  25.     }   

自动创建设备节点

  1.     simple_class = class_create(THIS_MODULE, SIMPLE_NAME);   
  2.     if(simple_class == NULL)   
  3.     {   
  4.         printk(KERN_ERR "create simple class error\n");   
  5.         goto error2;   
  6.     }   
  7.     else  
  8.     {   
  9. #if SIMPLE_DEBUG   
  10.         printk(KERN_INFO "create simple class  OK!\n");   
  11. #endif   
  12.     }   
  13.   
  14.     simple_dev = device_create(simple_class, NULL, dev, NULL, SIMPLE_NAME);   
  15.     if(simple_dev == NULL)   
  16.     {   
  17.         printk(KERN_ERR "create device error");   
  18.         goto error3;   
  19.     }   
  20.     else  
  21.     {   
  22. #if SIMPLE_DEBUG   
  23.         printk(KERN_INFO "create simple device  OK!\n");   
  24. #endif   
  25.     }  

删除设备节点,注销字符设备

  1. dev = MKDEV(simple_major, simple_minor); //计算出设备号  
  2. device_destroy(simple_class, dev); //删除设备节点  
  3. class_destroy(simple_class);//删除并释放class结构  
  4. cdev_del(simple_cdev);//注销设备   
  5. cdev_put(simple_cdev);//对动态的设备需要使用cdev_put‘来释放资源