linux字符设备驱动的关键:自动创建设备及其节点

 大概步骤:

一、 注册设备号

注册函数:
regsi     te   r_chrdev_region()
alloc_chrdev_region() 或                             查看#cat /proc/devices
register_chrdev()
注销函数:
unregist_chrdev_region() 或
unregister_chrdev()

 linux字符设备驱动的关键:自动创建设备及其节点_设计制作_嵌入式技术

二、初始化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 结构,它的定义如下:
sta     ti   c struct char_device_struct
{
struct char_device_struct *next;    // 指向散列冲突链表中的下一个元素的指针
unsigned    int major;              // 主设备号
unsigned    int base     mi   nor;          // 起始次设备号
int minorct;                        // 设备编号的范围大小
char    name[64];                   // 处理该设备编号范围内的设备驱动的名称
struct file_opera  TI ons *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 fi     rs   t,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_opera  TI ons *fops)
major :要注册的设备号, 若为0则自动分配一个
name  :设备名
*fops :以后再聊
释放:
void unregister_chrdev(unsigned int major, const char *name);
void unregister_chrdev_region(dev_t f     rom   , unsigned count);
3 示例:略
4 参考:感谢原著 (有此6个函数的源码及解说)。
http://blog.csdn.net/iLetLet/ar  TI cle/det     ai   ls/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_opera  TI ons *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;
}

70
183
0
76

相关资讯

  1. 1、星空夜聊APP怎么删除孔明灯?星空夜聊APP删除孔明灯的方法1585
  2. 2、Windows7系统怎么隐藏或取消隐藏文件夹?354
  3. 3、360安全云盘怎么找回文件?360安全云盘找回文件的方法2654
  4. 4、如何修改美拍中的字幕修改美拍字幕的方法1265
  5. 5、乐心运动APP怎么添加家人?添加亲友轻松搞定4959
  6. 6、Win10蓝牙怎么开?Win10蓝牙开关不见了怎么办205
  7. 7、百度音乐下载完的音乐在哪里?百度音乐下载完歌曲在什么文件夹里2874
  8. 8、FileZilla怎么上传和下载文件?1331
  9. 9、加入Windows预览版体验计划无法获得Win11推送怎么办?4036
  10. 10、百度网盘怎么移动文件夹?百度网盘文件夹移动方法简述4858
全部评论(0)
我也有话说
0
收藏
点赞
顶部