linux 设备驱动模型

inux2.6提供了新的设备模型:总线、驱动、设备。基本关系简要的概括如下:

驱动核心可以注册多种类型的总线。

每种总线下面可以挂载许多设备。(通过kset devices)

每种总线下可以用很多设备驱动。(通过包含一个kset drivers)}

每个驱动可以处理一组设备。按照我的理解就是所有的设备都挂载到总线上,当加载驱动时,驱动就支总线上找到自己对应的设备。或者先把驱动加载上,来了一个设备就去总线找驱动。

一:总线

总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连。

(1)总线结构

bus_type:

 struct bus_type {
const char * name;//设备名称
struct subsystem subsys;//代表自身
struct kset drivers; //当前总线的设备驱动集合
struct kset devices; //所有设备集合
struct klist klist_devices;
struct klist klist_drivers;
struct bus_attribute * bus_attrs;//总线属性
struct device_attribute * dev_attrs;//设备属性
struct driver_attribute * drv_attrs;
int (*match)(struct device * dev, struct device_driver * drv);//设备驱动匹配函数
int (*uevent)(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);//热拔插事件
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
};

在后面的实例当中用到了里面的两个成员

1:const char *name;
2:int (*match)(struct device * dev, struct device_driver * drv);

设备驱动匹配函数,这个匹配函数是很关键的东西,这是建立总线上设备与驱动的桥梁,当一个新的设备或驱动被添加到一个总线上时被调用。

(2)总线的操作:

注册:int bus_register(struct bus_type * bus)

注销:void bus_unregister(struct bus_type *bus);

(3)总线属性 bus_attribute

struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf,size_t count);
};
BUS_ATTR(name, mode, show, store);

这个宏声明一个结构, 产生它的名子通过前缀字符串 bus_attr_ 到给定的名子(bus_attr_name),然后利用bus_create_file来创建总线属性

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);

参数中的attr,即为bus_attr_name。

另外, 就是参数中的 show 方法,设置方法如下

static ssize_t show_bus_version(struct bus_type *bus, char *buf) {
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

总线属性的删除, 使用:

void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

(4)总线实例:

1:首先是要准备一个总线bus_type.也就是定义一个bus_type,然后给它填上一些成员。定义如下:

struct bus_type my_bus_type = {

.name = "my_bus",

.match = my_match,

};

这里就对其两个成员赋值了。一个是名称。另一个则是匹配函数:

2,总线本身也是要对应一个设备的。还要为总线创建设备。

struct device my_bus = {

.bus_id = "my_bus0",

.release = my_bus_release

};

源代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
static char *version = "version 1.0";
//用于判断指定的驱动程序是否能处理指定的设备。
static int my_match(struct device *dev, struct device_driver *driver) {
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
static int my_bus_release(struct device *dev) {
return 0;
}
static ssize_t show_bus_version(struct bus_type *bus, char *buf) {
return sprintf(buf, PAGE_SIZE, "%s\n", version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); struct device my_bus = {//定义总线设备
.bus_id = "my_bus0",
.release = my_bus_release,
};
EXPORT_SYMBOL(my_bus);
struct bus_type my_bus_type = { //定义总线类型
.name = "my_bus",
.match = my_match,
};
EXPORT_SYMBOL(my_bus_type);
static int __init my_bus_init(void){
int ret;
ret = bus_register(&my_bus_type);//注册总线
if(ret)
printk("bus_register failed!\n");
if(bus_create_file(&my_bus_type, &bus_attr_version))//创建总线属性
printk("Creat bus failed!\n");
ret = device_register(&my_bus);//注册总线设备
if (ret)
printk("device_register failed!\n");
return ret;
}
static void __exit my_bus_exit(void) {
bus_unregister(&my_bus_type);//删除总线属性
device_unregister(&my_bus);//删除总线设备
}
module_init(my_bus_init);
module_exit(my_bus_exit);
MODULE_AUTHOR("Fany");
MODULE_LICENSE("GPL");

(5)测试

将bus.c以动态加载的方式加载到内核,insmod bus.ko,在/sys/bus/目录下会有一个my_bus目录,这就是我们添加的总线。该目录下有devices,drivers目录,因为该总线上没有挂载任何设备和驱动,所以这两个目录都为空;同时,在/sy/devices目录下,还可看到my_bus0设备(总线本身也是一种设备)。

二:设备:

设备结构体

关于设备的一些常用结构体:device,

1:struct device {

struct device * parent; //父设备,一般一个bus也对应一个设备。

struct kobject kobj;//代表自身

char bus_id[BUS_ID_SIZE];

struct bus_type * bus; /* 所属的总线 /

struct device_driver driver; / 匹配的驱动
/

void driver_data; / data private to the driver 指向驱动 /

void platform_data; / Platform specific data,由驱动定义并使用
/

...........更多字段忽略了

};

注册设备:int device_register(sruct device *dev)

注销设备:void device_unregister(struct device *dev);

2:设备属性:

sysfs 中的设备入口可有属性. 相关的结构是:

struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char *buf);
ssize_t (*store)(struct device *dev, const char *buf,
size_t count);
};

这些属性结构可在编译时建立, 使用这些宏:

DEVICE_ATTR(name, mode, show, store);

结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函数对来处理:

int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);

3:创建设备实例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
extern struct device my_bus; //这里用到了总线设备中定义的结构体
extern struct bus_type my_bus_type;
static int my_device_release() {
return 0;
}
struct device my_dev={ //创建设备属性
.bus = &my_bus_type,//定义总线类型
.parent = &my_bus,//定义my_dev的父设备。
.release = my_device_release,
};
static ssize_t mydev_show(struct device *dev, char *buf) {
return sprintf(buf, "%s\n", "This is my device!");
}
static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
static int __init my_device_init(void){
int ret;
strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE); //初始化设备
ret = device_register(&my_dev); //注册设备
if (ret)
printk("device register!\n");
device_create_file(&my_dev, &dev_attr_dev); //创建设备文件
return ret;
}
static void __exit my_device_exit(void) {
device_unregister(&my_dev);//卸载设备
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_AUTHOR("Fany");
MODULE_LICENSE("GPL");

4:测试

将设备device.c,编译成模块,以动态加载的方式加载到内核。会发现在sys/bus/my_bus/devices/目录下有一个my_dev设备,查看属性,它是挂在/sys/devices/my_bus0/my_dev目录下,至此添加设备成功。

三:设备驱动:

1:数据结构

关于驱动的常用结构体:device_driver

struct device_driver {

const char name; /驱动程序的名字( 在 sysfs 中出现 )/

struct bus_type bus; /驱动程序所操作的总线类型
/

struct module *owner;

const char mod_name; / used for built-in modules /

int (
probe) (struct device dev);

int (
remove) (struct device dev);

void (
shutdown) (struct device dev);

int (
suspend) (struct device dev, pm_message_t state);

int (
resume) (struct device *dev);

struct attribute_group **groups;

struct pm_ops *pm;

struct driver_private *p;

};

2:驱动程序的注册和注销

/注册device_driver 结构的函数是:/

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

3:驱动程序的属性

/driver的属性结构在:/

struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *drv, char *buf);
ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(_name,_mode,_show,_store) /*属性文件创建的方法:*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);//创建设备驱动的属性
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);//删除设备驱动的属性

4:驱动实例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
extern struct bus_type my_bus_type;
static int my_probe(struct device *dev) {
printk("Driver found device!\n");
return 0;
};
static int my_remove(struct device *dev) {
printk("Driver unpluged!\n");
return 0;
};
struct device_driver my_driver = {
.name = "my_dev",
.bus = &my_bus_type,
.probe = my_probe,
.remove = my_remove,
};
//定义设备驱动属性
static ssize_t my_driver_show(struct device_driver *driver, char *buf) {
return sprintf(buf, "%s\n", "This is my driver!");
};
static DRIVER_ATTR(drv, S_IRUGO, my_driver_show, NULL);
static int __init my_driver_init(void){
int ret;
//注册设备驱动
ret = driver_register(&my_driver);
if(ret)
printk("driver_register failed!\n");
//创建设备驱动属性
ret = driver_create_file(&my_driver, &driver_attr_drv);
if(ret)
printk("create_driver_file failed!\n"); return ret;
}
static void __exit my_driver_exit(void){
driver_unregister(&my_driver);//注销设备驱动
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_AUTHOR("Fany");
MODULE_LICENSE("GPL");

5:测试

当加载驱动程序时,终端界面上打印

Driver found device!

说明驱动找到了匹配的设备,看到打印这个时,想到在windows下插U盘,立马弹出“发现可移动设备”,有点相像!

再看看相应的目录:/sys/bus/my_bus/drivers/,多了一个my_dev。

说明添加驱动成功!

linux驱动之设备模型的更多相关文章

  1. Linux内核(7) - 设备模型(上)

    对于驱动开发来说,设备模型的理解是根本,毫不夸张得说,理解了设备模型,再去看那些五花八门的驱动程序,你会发现自己站在了另一个高度,从而有了一种俯视的感觉,就像凤姐俯视知音和故事会,韩峰同志俯视女下属. ...

  2. Linux内核(8) - 设备模型(下)

    设备模型拍得再玄幻,它也只是个模型,必须得落实在具体的子系统,否则就只能抱着个最佳技术奖空遗恨.既然前面已经以USB子系统的实现分析示例了分析内核源码应该如何入手,那么这里就仍然以USB子系统为例,看 ...

  3. Linux驱动之设备树的基础知识

    前期知识   1. 如何编写一个简单的Linux驱动(一)--驱动的基本框架   2. 如何编写一个简单的Linux驱动(二)--设备操作集file_operations   3. 如何编写一个简单的 ...

  4. Linux内核系列设备模型(一) Kobject与Kset

    1.Kobject Kobject是设备驱动模型的核心结构,它使所有设备在底层都有统一的接口.在内核注册的kobject对象都会对应sysfs文件系统中的一个目录(目录名称有Kobject结构中k_n ...

  5. 【Linux 驱动】设备驱动程序再理解

    学习设备驱动编程也有一段时间了,也写过了几个驱动程序,因此有对设备驱动程序有了一些新的理解和认识,总结一下.学习设备驱动编程也有一段时间了,也写过了几个驱动程序.因此有对设备驱动程序有了一些新的理解和 ...

  6. Linux 驱动框架---设备文件devfs

    设备文件系统 Linux引入了虚拟文件系统,从而使设备的访问可以像访问普通文件系统一样.因此在内核中描述打开文件的数据inode中的rdev成员用来记录设备文件对应到的设备号.设备文件也由一个对应的f ...

  7. linux驱动---字符设备的注册register_chrdev说起

    首先我们在注册函数里面调用了register_chrdev(MEM_MAJOR,"mem",&memory_fops),向内核注册了一个字符设备. 第一个参数是主设备号,0 ...

  8. linux驱动之设备号与创建设备节点

    设备号: 1.自己主动分配 major = register_chrdev(0,"first_drv",&first_sdv_fops);//注冊 注冊设备时给设备号写0, ...

  9. Linux设备模型 学习总结

    看LDD3中设备模型一章,觉得思维有些混乱.这里从整体的角度来理理思路.本文从四个方面来总结一些内容: 1.底层数据结构:kobject,kset.2.linux设备模型层次关系:bus_type,d ...

随机推荐

  1. 用conda创建一个tensorflow 虚拟环境

    创建your——user——name = tensorflow 的虚拟环境 xinpingdeMacBook-Pro:~ xinpingbao$ conda create -n tensorflow ...

  2. 4.python 系统批量运维管理器之paramiko模块

    paramiko paramiko是ssh服务最经常使用的模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. paramiko实现ssh2不外乎两个角度:SSH客户端与服务端 SS ...

  3. 时间日期控件的处理-Selenium

    很多人问时间日期的空间怎么处理,但是时间日期控件各种各样,你可能遇到正常点的像这样: 当然也可能遇到难点的,像这样: 当然,也不排除会遇到变态的,像这样: 呵呵,真要一个个想着怎么去选择,简直是非人类 ...

  4. 认识HttpContext.User

    HttpContext.User,即IPrincipal .net源代码 namespace System.Security.Principal { /// <summary>Define ...

  5. MongoDB整理笔记の走进MongoDB世界

    本人学习mongodb时间不长,但是鉴于工作的需要以及未来发展的趋势,本人想更深层的认识mongodb底层的原理以及更灵活的应用mongodb,边学边工作实践.  mongodb属于nosql中算是最 ...

  6. jQuery事件(持续更新中)

    方法 描述 bind() 向匹配元素附加一个或更多事件处理器 blur() 触发.或将函数绑定到指定元素的 blur 事件 change() 触发.或将函数绑定到指定元素的 change 事件 cli ...

  7. wp后台更新瓷片

    下载源码 还有一种方式,更新瓷片方式 1. /// <summary> /// 定时更新磁贴 /// </summary> public class ShellUpdate { ...

  8. c#读取数据库内容

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. Windows + python + pywinauto 搭建自动化测试环境

    最近公司在搞测试, 单纯的人工去测试需要花费太多的人力物力以及时间, 所以准备用Python做一套自动化测试来使用. 本文中使用的是Python3.6.8  和 pywin32-224.win-amd ...

  10. 【leetcode 94. 二叉树的中序遍历】解题报告

    前往二叉树的:前序,中序,后序 遍历算法 方法一:递归 vector<int> res; vector<int> inorderTraversal(TreeNode* root ...