Linux设备驱动模型分析
linux内核部件分析-设备驱动模型之driver

上节我们分析设备驱动模型中的device,主要是drivers/base/core.c,可以说是代码量最大的一个文件。
本节要分析的驱动driver,就要相对简单很多。
原因也很简单,对于driver,我们能定义的公共部分实在不多,能再sysfs中表达的也很少。
本节的分析将围绕drivers/base/driver.c,但头文件仍然是include/linux/device.h和drivers/base/base.h。
先让我们来看看driver的结构。
[cpp]view plaincopyprint?1.struct device_driver {2.const char *name;3.struct bus_type *bus;4.5.struct module *owner;6.const char *mod_name; /* used for built-in modules */7.8.bool suppress_bind_attrs; /* disables bind/unbind via sysfs */9.10.int (*probe) (struct device *dev);11.int (*remove) (struct device *dev);12.void (*shutdown) (struct device *dev);13.int (*suspend) (struct device *dev, pm_message_t state);14.int (*resume) (struct device *dev);15.const struct attribute_group **groups;16.17.const struct dev_pm_ops *pm;18.19.struct driver_private *p;20.};struct device_driver就是模型定义的通用驱动结构。
LINUX设备驱动程序(4)

协议简介
对于网络的正式介绍一般都采用 OSI (Open Systems Interconnection)模型, 但是Linux 中网络栈的介绍一般分为四层的 Internet 模型。
协议栈层次对比
OSI七层网络模型 应用层 表示层 会话层 传输层 网络层
数据链路层 物理层
Linux TCP/IP 四层概念模型
网络协议
网络协议层用于实现各种具体的网络协议, 如: TCP、UDP 等。
设备无关接口
设备无关接口将协议与各种网络设备驱动连接在一起。 这一层提供一组通用函数供底层网络设备驱动程序使用,让 它们可以对高层协议栈进行操作。
首先,设备驱动程序可能会通过调用 register_netdevice 或 unregister_netdevice 在内核中 进行注册或注销。调用者首先填写 net_device 结构,然后 传递这个结构进行注册。内核调用它的 init 函数(如果定义 了这种函数),然后执行一组健全性检查,并将新设备添加 到设备列表中(内核中的活动设备链表)。
驱动程序
网络栈底部是负责 管理物理网络设备 的设备驱动程序。
第二节 网卡驱动程序设计
设备注册
设备描述:
每个网络接口都由一个 net_device结构来描述
注册: 网络接口驱动的注册方式与字符驱动不同之处在于 它没有主次设备号,并使用如下函数注册。
int register_netdev(struct net_device *dev)
Linux网络子系统架构
Linux协议架构
Linux 网络子系统的顶部是系统调用接口。它为用 户空间的应用程序提供了一种访问内核网络子系统 的方法。位于其下面的是一个协议无关层,它提供 了一种通用方法来使用传输层协议。然后是具体协 议的实现,在 Linux 中包括内嵌的协议 TCP、 UDP,当然还有 IP。然后是设备无关层,它提供了 协议与设备驱动通信的通用接口,最下面是设备驱 动程序。
Linux设备模型 热插拔、mdev 与 firmware

Linux设备驱动程序学习(15)-Linux设备模型(热插拔、mdev 与firmware)热插拔有2 个不同角度来看待热插拔:从内核角度看,热插拔是在硬件、内核和内核驱动之间的交互。
从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug、udev 和mdev)的交互。
当需要通知用户内核发生了某种热插拔事件时,内核才调用这个用户空间程序。
现在的计算机系统,要求Linux 内核能够在硬件从系统中增删时,可靠稳定地运行。
这就对设备驱动作者增加了压力,因为在他们必须处理一个毫无征兆地突然出现或消失的设备。
热插拔工具当用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在/proc/sys/kernel/hotplug文件里查找处理设备连接的用户空间程序。
这个用户空间程序主要有hotplug:这个程序是一个典型的bash 脚本,只传递执行权给一系列位于/etc/hot-plug.d/ 目录树的程序。
hotplug 脚本搜索所有的有 .hotplug 后缀的可能对这个事件进行处理的程序并调用它们, 并传递给它们许多不同的已经被内核设置的环境变量。
(基本已被淘汰,具体内容请参阅《LDD3》)udev :用于linux2.6.13或更高版本的内核上,为用户空间提供使用固定设备名的动态/dev目录的解决方案。
它通过在sysfs 的/class/ 和/block/ 目录树中查找一个称为dev 的文件,以确定所创建的设备节点文件的主次设备号。
所以要使用udev,驱动必须为设备在sysfs中创建类接口及其dev属性文件,方法和sculld模块中创建dev属性相同。
udev的资料网上十分丰富,我就不在这废话了,给出以下链接有兴趣的自己研究:mdev:一个简化版的udev,是busybox所带的程序,十分适合嵌入式系统。
因为hotplug现在也在被慢慢地淘汰,udev不再依赖hotplug了,所以这里不再介绍;udev较mdev复杂,不太适合嵌入式使用。
linux驱动21页PPT

设备文件和设备驱动
设备文件和设备驱动
设备文件是文件系统上的一个 节点,是一种特殊的文件,叫 做设备文件。每个设备文件在 用户空间代表了一个设备。
设备文件一般存在/dev目录下, 用mknod命令创建。
/proc/ioports:查看设备的IO端口。 /proc/interrupts:查看正在使用的中断号。
构造和运行模块
Kernel Module的特点
模块只是预先注册自己以便服务于将来的某个请求,然后就立即 结束。
模块可以是实现驱动程序,文件系统,或者其他功能。 加载模块后,模块运行在内核空间,和内核链接为一体。
#include <linux/module.h>
int init_module(void) {
printk("<1>Hello, world\n"); return 0; } void cleanup_module(void) { printk("<1>Goodbye world\n"); }
简单的内核模块例子(2)
SUMMER TEMPLATE
linux驱动
Linux Kernel 系统架构图
设备驱动程序简介
驱动程序的特点
是应用和硬件设备之间的一个软件层 。
这个软件层一般在内核中实现
设备驱动程序的作用在于提供机制,而不是提供策略, 编写访问硬件的内核代码时不要给用户强加任何策略
○ 机制:驱动程序能实现什么功能。
1version>/modules.dep文件,其中<kernel version>
LINUX设备驱动开发详解

LINUX设备驱动开发详解概述LINUX设备驱动开发是一项非常重要的任务,它使得硬件设备能够与操作系统进行有效地交互。
本文将详细介绍LINUX设备驱动开发的基本概念、流程和常用工具,帮助读者了解设备驱动开发的要点和技巧。
设备驱动的基本概念设备驱动是连接硬件设备和操作系统的桥梁,它负责处理硬件设备的输入和输出,并提供相应的接口供操作系统调用。
设备驱动一般由设备驱动程序和设备配置信息组成。
设备驱动程序是编写解决设备驱动的代码,它负责完成设备初始化、IO操作、中断处理、设备状态管理等任务。
设备驱动程序一般由C语言编写,使用Linux内核提供的API函数进行开发。
设备配置信息是定义硬件设备的相关参数和寄存器配置的文件,它告诉操作系统如何与硬件设备进行交互。
设备配置信息一般以设备树或者直接编码在设备驱动程序中。
设备驱动的开发流程设备驱动的开发流程包括设备初始化、设备注册、设备操作函数编写和设备驱动注册等几个主要步骤。
下面将详细介绍这些步骤。
设备初始化设备初始化是设备驱动开发的第一步,它包括硬件初始化和内存分配两个主要任务。
硬件初始化是对硬件设备进行基本的初始化工作,包括寄存器配置、中断初始化等。
通过操作设备的寄存器,将设备设置为所需的状态。
内存分配是为设备驱动程序分配内存空间以便于执行。
在设备初始化阶段,通常需要为设备驱动程序分配一块连续的物理内存空间。
设备注册设备注册是将设备驱动程序与设备对象进行关联的过程,它使得操作系统能够正确地管理设备。
设备注册包括设备号分配、设备文件创建等操作。
设备号是设备在系统中的唯一标识符,通过设备号可以找到设备对象对应的设备驱动程序。
设备号分配通常由操作系统负责,设备驱动程序通过注册函数来获取设备号。
设备文件是用户通过应用程序访问设备的接口,它是操作系统中的一个特殊文件。
设备文件的创建需要通过设备号和驱动程序的注册函数来完成。
设备操作函数编写设备操作函数是设备驱动程序的核心部分,它包括设备打开、设备关闭、读和写等操作。
Linux摄像头驱动学习之:(四)UVC-摄像头驱动框架分析

Linux摄像头驱动学习之:(四)UVC-摄像头驱动框架分析UVC: USB Video ClassUVC驱动:drivers\media\video\uvc\uvc_driver.c分析:1. usb_register(&uvc_driver.driver);2. uvc_probeuvc_register_videovdev = video_device_alloc();vdev->fops = &uvc_fops;video_register_device在下载 uvc specification,UVC 1.5 Class specification.pdf : 有详细描述USB_Video_Example 1.5.pdf : 有⽰例通过VideoControl Interface来控制,通过VideoStreaming Interface来读视频数据,VC⾥含有多个Unit/Terminal等功能模块,可以通过访问这些模块进⾏控制,⽐如调亮度分析UVC驱动调⽤过程:const struct v4l2_file_operations uvc_fops = {.owner = THIS_MODULE,.open = uvc_v4l2_open,.release = uvc_v4l2_release,.ioctl = uvc_v4l2_ioctl,.read = uvc_v4l2_read,.mmap = uvc_v4l2_mmap,.poll = uvc_v4l2_poll,};1. open:uvc_v4l2_open2. VIDIOC_QUERYCAP // video->streaming->type 应该是在设备被枚举时分析描述符时设置的if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)cap->capabilities = V4L2_CAP_VIDEO_CAPTURE| V4L2_CAP_STREAMING;elsecap->capabilities = V4L2_CAP_VIDEO_OUTPUT| V4L2_CAP_STREAMING;3. VIDIOC_ENUM_FMT // format数组应是在设备被枚举时设置的format = &video->streaming->format[fmt->index];4. VIDIOC_G_FMTuvc_v4l2_get_format // USB摄像头⽀持多种格式fromat, 每种格式下有多种frame(⽐如分辨率)struct uvc_format *format = video->streaming->cur_format;struct uvc_frame *frame = video->streaming->cur_frame;5. VIDIOC_TRY_FMTuvc_v4l2_try_format/* Check if the hardware supports the requested format. *//* Find the closest image size. The distance between image sizes is* the size in pixels of the non-overlapping regions between the* requested size and the frame-specified size.*/6. VIDIOC_S_FMT // 只是把参数保存起来,还没有发给USB摄像头uvc_v4l2_set_formatuvc_v4l2_try_formatvideo->streaming->cur_format = format;video->streaming->cur_frame = frame;7. VIDIOC_REQBUFSuvc_alloc_buffersfor (; nbuffers > 0; --nbuffers) {mem = vmalloc_32(nbuffers * bufsize);if (mem != NULL)break;}8. VIDIOC_QUERYBUFuvc_query_buffer__uvc_query_buffermemcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); // 复制参数9. mmapuvc_v4l2_mmap10. VIDIOC_QBUFuvc_queue_bufferlist_add_tail(&buf->stream, &queue->mainqueue);list_add_tail(&buf->queue, &queue->irqqueue);11. VIDIOC_STREAMONuvc_video_enable(video, 1) // 把所设置的参数发给硬件,然后启动摄像头/* Commit the streaming parameters. */uvc_commit_videouvc_set_video_ctrl /* 设置格式fromat, frame */ret = __uvc_query_ctrl(video->dev /* 哪⼀个USB设备 */, SET_CUR, 0,video->streaming->intfnum /* 哪⼀个接⼝: VS */,probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,uvc_timeout_param);/* 启动:Initialize isochronous/bulk URBs and allocate transfer buffers. */uvc_init_video(video, GFP_KERNEL);uvc_init_video_isoc / uvc_init_video_bulkurb->complete = uvc_video_complete; (收到数据后此函数被调⽤,它⼜调⽤video->decode(urb, video, buf); ==>uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);)usb_submit_urb12. polluvc_v4l2_polluvc_queue_pollpoll_wait(file, &buf->wait, wait); // 休眠等待有数据13. VIDIOC_DQBUFuvc_dequeue_bufferlist_del(&buf->stream);14. VIDIOC_STREAMOFFuvc_video_enable(video, 0);usb_kill_urb(urb);usb_free_urb(urb);分析设置亮度过程:ioctl: VIDIOC_S_CTRLuvc_ctrl_setuvc_ctrl_commit__uvc_ctrl_commit(video, 0);uvc_ctrl_commit_entity(video->dev, entity, rollback);ret = uvc_query_ctrl(dev /* 哪⼀个USB设备 */, SET_CUR, ctrl->entity->id /* 哪⼀个unit/terminal */,dev->intfnum /* 哪⼀个接⼝: VC interface */, ctrl->info->selector,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),ctrl->info->size);总结:1. UVC设备有2个interface: VideoControl Interface, VideoStreaming Interface2. VideoControl Interface⽤于控制,⽐如设置亮度。
Linux的驱动开发分析

f o r ( 1 e f t= c o u n t :l e f t>O :l e f t 一 _ ) {
. .
p u t u s e r ( 1 ,b u r ,1 ) ;
.
2 驱 动程 序原 理
编写设备 驱动程序 的原 理即基于I / O 设备管理 采用的分层 模 型, l / 0 设备 管理 软件位于 内核 中的最底层 , 设备驱动程 序是
-
r e a ( V E R I F YW i f ( v e r i f ya R I T E , b u r , c o u n t ) ==
— —
E F A U L T)
r e t u r n — E F A U L T ;
性 能得到提高 。 许 多广泛应 用的嵌入 式L i n u x 系 统都 采用静态 链接 的设备驱动程序模块。
( 1 ) 工作原理 。 作为内核 的一部分, 设各驱动程 序完 成对 设
据、 读 取应用程序传 送给设备文件 的数 据和 回送应用程 序请求 的数据和 检测处理设备 出现 的错 误的功能。 L i n u x 设备主要分
s t a t i c i n t o p e n
{ i n t l e f t :
化, 尽可能地精简。 嵌入 式L i n u x 系统不能够像桌面L i n u x  ̄ g 样
灵活 地使 用i n s m o d / r m m o d 力 口 载卸载设备驱 动程序 。 从嵌 入式 系统 的整 体性能考虑 , 采用静态链接模块能够使得整 个系统 的
设计分析 ・
L i n u x 的驱动开发分析
姜远志 ( 太原师范 学院 , 山 西 太原 0 3 0 0 0 0 )
总线设备驱动

linux内核学习---总线,设备,驱动Linux 设备模型中三个很重要的概念就是总线,设备,驱动.即 bus,device,driver,而实际上内核中也定义了这么一些数据结构,他们是 struct bus_type,struct device,struct device_driver,这三个重要的数据结构都来自一个地方,include/linux/device.h.我们知道总线有很多种,pci总线,scsi 总线,usb 总线,所以我们会看到Linux 内核代码中出现pci_bus_type,scsi_bus_type,usb_bus_type,他们都是 struct bus_type 类型的变量.而struct bus_type 结构中两个非常重要的成员就是 struct kset drivers 和 struct kset devices。
kset 和另一个叫做 kobject 正是 Linux Kernel 2.6 中设备模型的基本元素。
这里我们只需要知道,drivers 和 devices 的存在,让struct bus_type 与两个链表联系了起来,一个是devices 的链表,一个是 drivers 的链表,也就是说,知道一条总线所对应的数据结构,就可以找到这条总线所关联的设备有哪些,又有哪些支持这类设备的驱动程序.而要实现这些,就要求每次出现一个设备就要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册.比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个 struct device 的变量,每一次有一个驱动程序,就要准备一个 struct device_driver 结构的变量.把这些变量统统加入相应的链表,device 插入 devices 链表,driver 插入 drivers 链表. 这样通过总线就能找到每一个设备,每一个驱动。
struct bus_type 中为 devices 和 drivers 准备了两个链表,而代表 device 的结构体 struct device 中又有两个成员,struct bus_type *bus 和 struct device_driver *driver。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
bus_register(&platform_bus_type) 在/sys/bus/目录下创建bus_name子目录 在/sys/bus/bus_name/目录下创建drivers子目录 在/sys/bus/bus_name/目录下创建devices子目录 在/sys/bus/bus_name/目录下创建uevent文件 在/sys/bus/bus_name/目录下创建drivers_probe文件 在/sys/bus/bus_name/目录下创建drivers_autoprobe文件
};
提醒:变量名字 platform_bus 实为 device{}
int device_add(struct device *dev) //dev:&platform_bus {
struct device *parent = NULL; parent = get_device(dev->parent); struct device_type *type = dev->type; ... ... ;
drivers_kset devices_kset
bus_register(&platform_bus_type):
int bus_register(struct bus_type *bus) //bus:platform_bus_type {
struct bus_type_private *priv; priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); priv->bus = bus; bus->p = priv; ... ... ;
... ... ; for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { //grp: &pm_attr_group; attrs: &power_attrs
sysfs_add_file_mode(... , *attr, SYSFS_KOBJ_ATTR, (*attr)->mode | mode); ... ... ; } }
sys文件系统注册挂载
start_kernel()→vfs_caches_init()→mnt_init()→sysfs_init()
int __init sysfs_init(void) {
register_filesystem(&sysfs_fs_type); kern_mount(&sysfs_fs_type); ... ...; }
};
//设置 priv->subsys.kobj 的名字为 platform
kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //name: "platform"
priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype;
system_bus_init()
在/sys/目录下创建system子目录
system_kset
2/20
device_register(&platform_bus) 在/sys/devices/目录下创建platform子目录 在/sys/devices/platform/目录下创建uevent文件 在/sys/devices/platform/目录下创建power子目录 在/sys/devices/platform/目录下创建control、wakeup文件
struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, ... ... ,
device_create_file(dev, &uevent_attr); static struct device_attribute uevent_attr =__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
if (MAJOR(dev->devt)) //dev->devt 无,操作不执行 ... ... ;
3/20
//dev->class 无 且 dev->parent 无,在/sys/devices/目录下创建 platform 目录
kobject_add(&dev->kobj, dev->kobj.parent, NULL);
//在/sys/devices/platform/目录下创建 uevent 属性文件
//确定 device 在/sys/device/.../中所处位置的算法思想 设备:dev
① 设备组 父设备:dev->parent
名词定义: 设备类:dev->class
② 类组 父设备类:dev->parent->class
算法思想:dev 必有,利用 dev->parent、dev->class、dev->parent->class 来计算设备所处 sysfs 文件中的位置。
//创建/sys/bus/platform 目录
kset_register(&priv->subsys);
//创建/sys/bus/platform/uevent 属性文件
bus_create_file(bus, &bus_attr_uevent);
//创建/sys/bus/platform/devices 目录
};
static struct attribute * power_attrs[] = { &dev_attr_control.attr, &dev_attr_wakeup.attr,
};
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) {
create_files(... , kobj, grp, ...);
4/20
... ... ; } int sysfs_create_subdir(struct kobject *kobj, const char *name, ...) {
create_dir(kobj, ... , name, ...); } static int create_files(... , struct kobject *kobj, const struct attribute_group *grp, ...) {
在/sys/dev/目录下创建char子目录
sysfs_dev_char_kobj
buses_init()
在/sys/目录下创建bus子目录
bus_kset
classes_init()
在/sys/目录下创建class子目录
class_kset
firmware_init()
在/sys/目录下创建firmware子目录
//在/sys/devices/platform/目录下创建 power 目录,并在 power 目录下创建 power_attrs 属性文件,包括 control、wakeup
sysfs_create_group(&dev->kobj, &pm_attr_group);
... ... ;
} }
static struct attribute_group pm_attr_group = { .name = "power", .attrs = power_attrs,
device_register(&platform_bus):
int device_register(struct device *dev) {
device_initialize(dev); device_add(dev); }
struct device platform_bus = { .init_name = "platform",
driver_init()
devtmpfs_init()
创建devtmpfs文件系统
devices_init()
在/sys/目录下创建devices子目录
devices_kset
在/sys/目录下创建dev子目录
dev_kobj
在/sys/dev/目录下创建block子目录
sysfs_dev_block_kobj
internal_create_group(kobj, ... , grp); }
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) {
if (grp->name) //name: "power" sysfs_create_subdir(kobj, grp->name, ...);
LK 设备驱动模型之 Base 子系统架构及 sysfs 组织结构
2013 年 1 月 10 日
※要点区:
① Linux驱动下面的base子系统的架构 ② sysfs文件系统的组织结构