精华内容
下载资源
问答
  • linux 设备驱动

    千次阅读 2011-12-20 14:00:59
    设备驱动最通俗的解释就是“驱使硬件设备行动“。操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并提供统一的操作方式。系统调用是操作系统内核和...

    设备驱动最通俗的解释就是“驱使硬件设备行动“。操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并提供统一的操作方式。系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能:

    1 对设备初始化和释放。

    2 把数据从内核传送到硬件和从硬件读取数据。

    3 读取应用程序传送给设备文件的数据和回送应用程序请求的数据。

    4 检测和处理设备出现的错误。

    一.驱动设备分类

    以 Linux 的方式看待设备可区分为 3 种基本设备类型字符设备、块设备、网络接口。

    字符设备 

    一个字符( char ) 设备是一种可以当作一个字节流来存取的设备( 如同一个文件 ); 一个字符驱动负责实现这种行为. 这样的驱动常常至少实现 open, close, read, 和 write 系统调用. 文本控制台( /dev/console )和串口( /dev/ttyS0 及其友 )是字符设备的例子, 因为它们很好地展现了流的抽象. 字符设备通过文件系统结点来存取, 例如 /dev/tty1 和 /dev/lp0. 在一个字符设备和一个普通文件之间唯一有关的不同就是, 你经常可以在普通文件中移来移去, 但是大部分字符设备仅仅是数据通道, 你只能顺序存取.然而, 存在看起来象数据区的字符设备, 你可以在里面移来移去. 例如, frame grabber 经常这样, 应用程序可以使用 mmap 或者 lseek 存取整个要求的图像.

    块设备 

    如同字符设备, 块设备通过位于 /dev 目录的文件系统结点来存取. 一个块设备(例如一个磁盘)应该是可以驻有一个文件系统的. 在大部分的 Unix 系统, 一个块设备只能处理这样的 I/O 操作, 传送一个或多个长度经常是 512 字节( 或一个更大的 2 的幂的数 )的整块. Linux, 相反, 允许应用程序读写一个块设备象一个字符设备一样 -- 它允许一次传送任意数目的字节. 结果就是, 块和字符设备的区别仅仅在内核在内部管理数据的方式上, 并且因此在内核/驱动的软件接口上不同. 如同一个字符设备, 每个块设备都通过一个文件系统结点被存取的, 它们之间的区别对用户是透明的. 块驱动和字符驱动相比, 与内核的接口完全不同.  

    网络接口

     

    任何网络事务都通过一个接口来进行, 就是说, 一个能够与其他主机交换数据的设备. 通常, 一个接口是一个硬件设备, 但是它也可能是一个纯粹的软件设备, 比如环回接口. 一个网络接口负责发送和接收数据报文, 在内核网络子系统的驱动下, 不必知道单个事务是如何映射到实际的被发送的报文上的. 很多网络连接( 特别那些使用 TCP 的)是面向流的, 但是网络设备却常常设计成处理报文的发送和接收. 一个网络驱动对单个连接一无所知; 它只处理报文.

    二.驱动设备设备号

     Linux设备驱动中,设备号设一个很重要的概念和变量。每个设备在 /dev目录下都有一个对应的文件(节点)。可以通过 cat /proc/devices 命令查看当前已经加载的设备驱动程序的主设备号。内核能够识别的所有设备都记录在原码树下的 documentation/devices.txt 文件中。在 /dev 目录下除了字符设备和块设备节点之外还通常还会存在:fifo管道、socket、软/硬连接、目录。这些东西没有主/次设备号。
    $ ls -l /dev/rfd0 /dev/fd0
    brw-r-----  9 root operator 2, 0 nov 12 13:32 /dev/fd0
    crw-r-----  9 root operator 9, 0 nov 12 13:32 /dev/rfd0
       
    可以看到原来显示文件大小的地方,现在改为显示两个用逗号分隔的数字。这是系统用来表示设备的两个重要的序号,第一个为主设备号(major number),用来表示设备使用的硬件驱动程序在系统中的序号;第二个为从设备号(minor number)。

    $ ls -l /dev/rfd0 /dev/fd0

    brw-r-----  9 root operator 2, 0 nov 12 13:32 /dev/fd0

    crw-r-----  9 root operator 9, 0 nov 12 13:32 /dev/rfd0

        可以看到原来显示文件大小的地方,现在改为显示两个用逗号分隔的数字。这是系统用来表示设备的两个重要的序号,第一个为主设备号(major number),用来表示设备使用的硬件驱动程序在系统中的序号;第二个为从设备号(minor number)。

    了解这些设备的最基本要求就是对 每个设备文件的含义了如指掌,下面就医列表的形式列出常见的设备文件以及相应的含义:
    ----------------------------------------------------------------------
    主 设备号        设备类型
                   次设备号=文件名          简要说明
    ----------------------------------------------------------------------
     0                未命名设备(例如:挂载的非设备)
                     0 = 未空设备号保留
     1 char        内存设备
                     1 = /dev/mem                  直接存取物理内存
                     2 = /dev/kmem               存取经过内核虚拟之后的内存
                     3 = /dev/null                   空设备。任何写入都将被直接丢弃,任何读取都将得到eof。
                     4 = /dev/port                   存取 i/o 端口
                     5 = /dev/zero                 零字节源,只能读取到无限多的零字节。
                     7 = /dev/full                    满设备。任何写入都将失败,并把errno设为enospc以表示没有剩余空间。
                     8 = /dev/random           随机数发生器。完全由用户的输入来产生随机数。 如果用户停止所有动作,则停止产生新的随机数。                                    
                     9 = /dev/urandom       更快,但是不够安全的随机数发生器。尽可能由用户的输入来产生随机数, 如果用户停止所有动作,则把已经产生的随机数做为种子来产生新的随机数。
                    10 = /dev/aio                   异 步 i/o 通知接口
                    11 = /dev/kmsg              任何对该文件的写入都将作为 printk 的输出
     1 block        ram disk
                     0 = /dev/ram0               第1个 ram disk (initrd只能使用ram0)
                     1 = /dev/ram1              第2个 ram disk
                       ...
                   200 = /dev/ram200      第200个 ramdisk
     4 char        tty(终端)设备
                     0 = /dev/tty0               当前虚拟控制台
                     1 = /dev/tty1              第1个虚拟控制台
                       ...
                    63 = /dev/tty63       第63个虚拟控制台
     4 block        如果根文件系统以是以只读方式挂载的,那么就不可能创建真正的设备节点,
                   此时就使用该设备作为动态分配的主(major)设备的别名
                     0 = /dev/root
     5 char        其他 tty 设备
                     0 = /dev/tty              当前 tty 设备
                     1 = /dev/console       系统控制台
                     2 = /dev/ptmx               所有 pty master 的复用器
     7 char        虚拟控制台捕捉设备(这些设备既允许读也允许写)
                     0 = /dev/vcs               当前虚拟控制台(vc)的文本内容
                     1 = /dev/vcs1               tty1 的文本内容
                       ...
                    63 = /dev/vcs63       tty63 的文本内容
                   128 = /dev/vcsa              当前虚拟控制台(vc)的文本/属性内容
                   129 = /dev/vcsa1       tty1 的文本/属性内容
                       ...
                   191 = /dev/vcsa63      tty63 的文本/属性内容
    代码:  7 block        回环设备(用一个普通的磁盘文件来模拟一个块设备)
                   对回环设备的绑定由 mount(8) 或 losetup(8) 处理
                     0 = /dev/loop0      第1个回环设备
                     1 = /dev/loop1       第2个回环设备
                       ...
     8 block        scsi 磁盘(0-15)
                     0 = /dev/sda              第1个 scsi 磁盘(整个磁盘)
                    16 = /dev/sdb              第2个 scsi 磁盘(整个磁盘)
                    32 = /dev/sdc              第3个 scsi 磁盘(整个磁盘)
                       ...
                   240 = /dev/sdp               第16个 scsi 磁盘(整个磁盘)
                   分区表示方法如下(以第3个 scsi 磁盘为例)
                    33 = /dev/sdc1              第1个分区
                    34 = /dev/sdc2              第2个分区
                       ...
                    47 = /dev/sdc15         第15个分区 对于linux/i386来说,分区1-4是主分区,5-15是逻辑分区。
     9 block        metadisk(raid)设备
                     0 = /dev/md0               第1组 metadisk
                     1 = /dev/md1               第2组 metadisk
                       ...
                   metadisk 驱动用于将同一个文件系统分割到多个物理磁盘上。
    10 char        非串口鼠标,各种杂项设备和特性
                     1 = /dev/psaux      ps/2鼠标
                   131 = /dev/temperature       机器内部温度
                   134 = /dev/apm_bios       apm(高级电源管理) bios
                   135 = /dev/rtc              实时时钟(realtime clock)
                   144 = /dev/nvram       非易失配置 ram
                   162 = /dev/smbus       系统管理总线(system management bus)
                   164 = /dev/ipmo              intel的智能平台管理(intelligent platform management)接口
                   173 = /dev/ipmikcs       智能平台管理(intelligent platformmanagement)接口
                   175 = /dev/agpgart      agp图形地址重映射表(graphics addressremapping table)
                   182 = /dev/perfctr       性能监视计数器
                   183 = /dev/hwrng      通用硬件随机数发生器
                   184 = /dev/cpu/microcodecpu微代码更新接口
                   186 = /dev/atomicps      进程状态数据的原子快照
                   188 = /dev/smbusbios      smbus(系统管理总线) bios
                   200 = /dev/net/tun      tap/tun 网络设备(tap/tun以软件的方式实现了网络设备)
                                          tap模拟了以太网帧(第二层),tun模拟了ip包(第三层)。
                   202 = /dev/emd/ctl      增强型 metadisk raid (emd) 控制器
                   220 = /dev/mptctl      message passing technology (mpt) control
                   223 = /dev/input/uinput      用户层输入设备驱动支持
                   227 = /dev/mcelog       x86_64 machine check exception driver
                   228 = /dev/hpet              hpet driver
                   229 = /dev/fuse               fuse(用户空间的虚拟文件系统)
                   231 = /dev/snapshot      系统内存快照
                   232 = /dev/kvm              基于内核的虚构机(基于amd svm和intel vt硬件虚拟技术)
    11 block        scsi cd-rom 设备
                     0 = /dev/scd0              第1个 scsi cd-rom
                     1 = /dev/scd1              第2个 scsi cd-rom
                       ...
    13 char        核心输入设备
                    32 = /dev/input/mouse0               第1个鼠标
                    33 = /dev/input/mouse1              第2个鼠标
                       ...
                    62 = /dev/input/mouse30      第31个鼠标
                    63 = /dev/input/mice               所有鼠标的统一
                    64 = /dev/input/event0              第1个事件队列
                    65 = /dev/input/event1               第2个事件队列
                       ...
                    95 = /dev/input/event1               第32个事件队列
    21 char        通用 scsi 设备(通常是scsi光驱)
                     0 = /dev/sg0               第1个通用 scsi 设备
                     1 = /dev/sg1              第2个通用 scsi 设备
                       ...
    29 char        通用帧缓冲(frame buffer)设备
                     0 = /dev/fb0              第1个帧缓冲设备
                     1 = /dev/fb1               第2个帧缓冲设备
                       ...
                    31 = /dev/fb31              第32个帧缓冲设备
    30 char        ibcs-2 兼容设备
                     0 = /dev/socksys      套接字访问接口
                     1 = /dev/spx               svr3 本地 x 接口
                    32 = /dev/inet/ip       网络访问接口
                    33 = /dev/inet/icmp
                    34 = /dev/inet/ggp
                    35 = /dev/inet/ipip
                    36 = /dev/inet/tcp
                    37 = /dev/inet/egp
                    38 = /dev/inet/pup
                    39 = /dev/inet/udp
                    40 = /dev/inet/idp
                    41 = /dev/inet/rawip
                   此外,ibcs-2 还需要下面的连接必须存在
                   /dev/ip ->/dev/inet/ip
                   /dev/icmp ->/dev/inet/icmp
                   /dev/ggp -> /dev/inet/ggp
                   /dev/ipip ->/dev/inet/ipip
                   /dev/tcp ->/dev/inet/tcp
                   /dev/egp ->/dev/inet/egp
                   /dev/pup ->/dev/inet/pup
                   /dev/udp ->/dev/inet/udp
                   /dev/idp ->/dev/inet/idp
                   /dev/rawip ->/dev/inet/rawip
                   /dev/inet/arp ->/dev/inet/udp
                   /dev/inet/rip ->/dev/inet/udp
                   /dev/nfsd ->/dev/socksys
                   /dev/x0r -> /dev/null
    代码: 36 char        netlink 支持
                     0 = /dev/route       路由, 设备更新, kernel to user
                     3 =/dev/fwmonitor       firewall packet 复制
    59 char        sf 防火墙模块
                     0 = /dev/firewall      与 sf 内核模块通信
    65 block        scsi 磁盘(16-31)
                     0 = /dev/sdq               第17个 scsi 磁盘(整个磁盘)
                    16 = /dev/sdr               第18个 scsi 磁盘(整个磁盘)
                    32 = /dev/sds               第19个 scsi 磁盘(整个磁盘)
                       ...
                   240 = /dev/sdaf               第32个 scsi 磁盘(整个磁盘)
    66 block        scsi 磁盘(32-47)
                     0 = /dev/sdag              第33个 scsi 磁盘(整个磁盘)
                    16 = /dev/sdah              第34个 scsi 磁盘(整个磁盘)
                    32 = /dev/sdai              第35个 scsi 磁盘(整个磁盘)
                       ...
                   240 = /dev/sdav               第48个 scsi 磁盘(整个磁盘)
    89 char        i2c 总线接口
                     0 = /dev/i2c-0      第1个i2c 适配器
                     1 = /dev/i2c-1       第2个i2c 适配器
                       ...
    98 block        用户模式下的虚拟块设备(分区处理方式与 scsi 磁盘相同)
                     0 = /dev/ubda               第1个用户模式块设备
                    16 = /dev/udbb              第2个用户模式块设备
                       ...
    103 block        审计(audit)设备
                     0 = /dev/audit      审计(audit)设备
    128-135 char        unix98 pty master
                   这些设备不应当存在设备节点,而应当通过 /dev/ptmx 接口访问。
    136-143 char        unix98 pty slave
                   这些设备节点是自动生成的(伴有适当的权限和模式),不能手动创建。
                   方法是通过使用适当的 mount 选项(通常是:mode=0620,gid=)
                   将 devpts 文件系统挂载到 /dev/pts 目录即可。
                     0 = /dev/pts/0       第1个unix98 pty slave
                     1 = /dev/pts/1       第2个unix98 pty slave
                       ...
    代码:153 block        enhancedmetadisk raid (emd) 存储单元(分区处理方式与 scsi 磁盘相同)
                     0 = /dev/emd/0         第1个存储单元
                     1 = /dev/emd/0p1      第1个存储单元的第1个分区
                     2 = /dev/emd/0p2       第1个存储单元的第2个分区
                       ...
                    15 = /dev/emd/0p15      第1个存储单元的第15个分区
                    16 = /dev/emd/1       第2个存储单元
                    32 = /dev/emd/2      第3个存储单元
                       ...
                   240 = /dev/emd/15      第16个存储单元
    180 char        usb 字符设备
                    96 = /dev/usb/hiddev0      第1个usb人机界面设备(鼠标/键盘/游戏杆/手写版等人操作计算机的设备)
                       ...
                   111 = /dev/usb/hiddev15       第16个usb人机界面设备
    180 block        usb 块设备(u盘之类)
                     0 = /dev/uba               第1个usb 块设备
                     8 = /dev/ubb                第2个usb 块设备
                    16 = /dev/ubc              第3个usb 块设备
                        ...
    192 char        内核 profiling 接口
                     0 = /dev/profile          profiling 控制设备
                     1 = /dev/profile0      cpu 0 的 profiling 设备
                     2 = /dev/profile1       cpu 1 的 profiling 设备
                       ...
    193 char        内核事件跟踪接口
                     0 = /dev/trace       跟踪控制设备
                     1 = /dev/trace0      cpu 0 的跟踪设备
                     2 = /dev/trace1       cpu 1 的跟踪设备
                       ...
    195 char        nvidia 图形设备(比如显卡)
                     0 = /dev/nvidia0               第1个 nvidia 卡
                     1 = /dev/nvidia1              第2个 nvidia 卡
                       ...
                   255 = /dev/nvidiactl               nvidia 卡控制设备
    202 char        特定于cpu模式的寄存器(model-specific register,msr)
                     0 =/dev/cpu/0/msr                cpu 0 的 msrs
                     1 =/dev/cpu/1/msr               cpu 1 的 msrs
                       ...
    203 char        cpu cpuid 信息
                     0 =/dev/cpu/0/cpuid                cpu 0 的 cpuid

                     1 =/dev/cpu/1/cpuid               cpu 1 的 cpuid

    展开全文
  • 本博实时更新《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)的最新进展。 目前已经完成稿件。 2015年8月9日,china-pub开始上线预售: ... 2015年8月20日,各路朋友报喜...

    本博实时更新《Linux设备驱动开发详解(第3版)》的最新进展。 目前已经完成稿件。

    2015年8月9日,china-pub开始上线预售:

    http://product.china-pub.com/4733972

    2015年8月20日,各路朋友报喜说已经拿到了书。

    本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台

    本书微信公众号二维码, 如需联系请扫描下方二维码

    [F]是修正或升级;[N]是新增知识点;[D]是删除的内容

     

    第1章 《Linux设备驱动概述及开发环境构建》
    [D]删除关于LDD6410开发板的介绍
    [F]更新新的Ubuntu虚拟机
    [N]添加关于QEMU模拟vexpress板的描述

     

    第2章 《驱动设计的硬件基础》

    [N]增加关于SoC的介绍;
    [N]增加关于eFuse的内容;
    [D]删除ISA总线的内容了;
    [N]增加关于SPI总线的介绍;
    [N]增加USB 3.0的介绍;
    [F]修正USB同步传输方式英文名;
    [D]删除关于cPCI介绍;
    [N]增加关于PCI Express介绍;
    [N]增加关于Xilinx ZYNQ的介绍;
    [N]增加SD/SDIO/eMMC的章节;
    [D]删除“原理图分析的内容”一节;
    [N]增加通过逻辑分析仪看I2C总线的例子;

     

    第3章 《Linux内核及内核编程》

    [N]新增关于3.X内核版本和2015年2月23日 Linux 4.0-rc1
    [N]新增关于内核版本升级流程以及Linux社区开发模式讲解
    [N]新增关于Linux内核调度时间的图式讲解
    [N]新增关于Linux 3.0后ARM架构的变更的讲解
    [N]新增关于TASK_KILLABLE状态的简介
    [N]新增Linux内存管理图式讲解

    [F]修正Kconfig和Makefile中的一些表述
    [D]删除关于x86启动过程讲解
    [N]新增ARM Linux启动过程讲解
    [N]新增关于likely()和unlikely()讲解
    [N]新增toolchain的讲解,以及toolchain的几种浮点模式


    第4章 《Linux内核模块》
    [F]改正关于模块使用非GPL license的问题;
    [F]修正关于__exit修饰函数的内存管理

    第5章 《Linux文件系统与设备文件》
    [F]修正关于文件系统与块设备驱动关系图;
    [N]增加应用到驱动的file操作调用图;
    [N]增加通过netlink接受内核uevent的范例;
    [N]增加遍历sysfs的范例;

    [N]增加为kingston U盘编写udev规则的范例;
    [F]更新udev规则,以符合新版本;
    [N]增加udevadm的讲解;
    [N]高亮Android vold
     
    第6章 《字符设备驱动》
    [F]更新file_operations的定义,升级ioctl()原型;
    [N]增加关于Linux access_ok()的讲解以及Linux内核安全漏洞的说明;
    [F]修正globalmem的编码风格;
    [F]在globalmem支持2个以上实例的时候,从直接2个实例,升级为支持N个实例;

    第7章 《Linux设备驱动中的并发控制》
    [N]绘图深入讲解单核和多核下的各种竞态;
    [N]增加关于编译乱序,执行乱序,编译屏障和内存屏障的讲解;
    [N]增加关于ARM LDREX/STREX指令的讲解;
    [N]对spin_lock单核和多核的使用场景进行深入分析;

    [F]重新整理RCU的讲解方法和实例;
    [F]明确指明信号量已过时;
    [F]将globalmem中使用的信号量换为mutex。


    第8章 《Linux设备驱动中的阻塞与非阻塞I/O》
    [N]新增阻塞和非组塞的时序图
    [F]修正关于等待队列头部、等待队列元素的一些中文说法
    [N]添加等待队列的图形描述
    [F]修正globalfifo的编码风格
    [F]修正globalfifo可读可写的if判断为while判断
    [N]新增select的时序图
    [N]新增EPOLL的章节


    第9章 《Linux设备驱动中的异步通知与异步I/O》
    [F]修正关于glibc AIO支持
    [F]修正关于内核AIO支持
    [F]修正驱动AIO接口
    [D]删除关于驱动AIO支持的错误实例
    [N]高亮C10问题

    第10章 《中断与时钟》
    [N]增加关于ARM GIC的讲解
    [N]增加关于irq_set_affinity() API的讲解
    [N]增加关于devm_request_irq() API的讲解
    [N]增加关于request_any_context_irq() API的讲解

    [F]修正interrupt handler原型
    [F]修正work queue原型
    [N]新增关于Concurrency-managed workqueues讲解
    [N]增加关于ksoftirqd讲解
    [N]增加关于request_threaded_irq()讲解
    [D]删除s3c6410 rtc驱动中断实例
    [N]新增GPIO按键驱动中断实例
    [N]新增hrtimer讲解和实例
    [F]修正second设备编码风格

    第11章 《内存与I/O访问》
    [F]修正关于页表级数的描述,添加PUD
    [F]修正page table walk的案例,使用ARM Linux pin_page_for_write
    [N]新增关于ARM Linux内核空间虚拟地址分布
    [F]修正关于内核空间与用户空间界限
    [N]新增关于DMA、NORMAL和HIGHMEM ZONE的几种可能分布
    [N]新增关于buddy的介绍
    [F]修正关于用户空间malloc的讲解
    [N]增加mallopt()的案例
    [N]增加关于devm_ioremap、devm_request_region()和devm_request_mem_region()的讲解
    [N]增加关于readl()与readl_relaxed()的区别,writel()与writel_relaxed()的区别

    [F]更新vm_area_struct的定义
    [F]修正nopage() callback为fault() callback
    [N]增加io_remap_pfn_range()、vm_iomap_memory()讲解
    [F]强调iotable_init()静态映射目前已不太推荐
    [N]增加关于coherent_dma_mask的讲解
    [N]讲解dma_alloc_coherent()与DMA ZONE关系
    [N]提及了一致性DMA缓冲区与CMA的关系
    [N]增加关于dmaengine驱动和API的讲解

     

    第12章 《工程中的Linux设备驱动》
    [F]更名为《Linux设备驱动的软件架构思想》;
    [N]本章新增多幅图片讲解Linux设备驱动模型;
    [N]新增内容详细剖析为什么要进行设备与驱动的分离,platform的意义;
    [N]新增内容详细剖析为什么Linux设备驱动要分层,枚举多个分层实例;
    [N]新增内容详细剖析Linux驱动框架如何解耦,为什么主机侧驱动要和外设侧驱动分离;
    [N]DM9000实例新增关于在dts中填充平台信息的内容;
    [N]新增内容讲解驱动核心层的3大功能;
    [N]新增内容以面向对象类泛化对比Linux驱动;
    [N]SPI案例部分新增通过dts填充外设信息;

    [F]从tty, LCD章节移出架构部分到本章

     

    第13章 《Linux块设备驱动》
    [N]介绍关于block_device_operations的check_events成员函数
    [N]添加关于磁盘文件系统,I/O调度关系的图形
    [F]更新关于request_queue、request、bio、segment关系的图形
    [F]淘汰elv_next_request
    [F]淘汰blkdev_dequeue_request
    [N]添加关于blk_start_request描述
    [F]淘汰Anticipatory I/O scheduler
    [N]添加关于ZRAM块设备驱动实例
    [F]更新针对内核4.0-rc1的vmem_disk
    [N]添加关于vmem_disk处理I/O过程的图形
    [N]增加关于Linux MMC子系统的描述

     

    第14章 《Linux终端设备驱动》
    [D]整章全部删除,部分架构内容前移到第12章作为驱动分层实例

     

    第15章 《Linux I2C核心、总线与设备驱动》

    [F]修正i2c_adpater驱动的案例
    [N]增加关于在device tree中增加i2c设备的方法的描述

     

    第16章 《Linux网络设备驱动》

    [F]本章顺序从第16章前移到第14章
    [N]澄清sk_buff head、data、tail、end指针关系
    [F]更新sk_buff定义
    [F]澄清skb_put、skb_push、skb_reserve
    [N]增加netdev_priv的讲解,加入实例
    [N]增加关于get_stats()可以从硬件读统计信息的描述

    [F]修正关于net_device_stats结构体的定义位置
    [F]修正关于统计信息的更新方法

     

    第17章 《Linux音频设备驱动》

    [D] 本章直接删除

     

    第18章 《LCD设备驱动》

     

    [D] 本章直接删除,部分架构内容前移到第12章

     

    第19章 《Flash设备驱动》

    [D] 本章直接删除

     

    第20章 《USB主机与设备驱动》
    [F]前移到第16章;
    [F]更名为《USB主机、设备与Gadget驱动》;
    [N]增加关于xHCI的介绍;
    [F]修正usb gadget驱动为function驱动;
    [D]删除OHCI实例;
    [N]添加EHCI讲解和Chipidea EHCI实例;
    [F]修正iso传输实例;
    [F]修正usb devices信息到/sys/kernel/debug/usb/devices
    [N]介绍module_usb_driver;
    [N]介绍usb_function;
    [N]介绍usb_ep_autoconfig;
    [N]介绍usb_otg;

    [D]删除otg_transceiver;

     

    第21章 《PCI设备驱动》
    [D]整章删除

     

    第22章 《Linux设备驱动的调试》

    [F]变为第21章;

    [D]把实验室环境建设相关的节移到第3章;

    [F]修正关于gdb的set step-mode的含义讲解;

    [F]增加关于gdb的set命令的讲解;

    [F]增加gdb call命令的案例

    [D/N]删除手动编译工具链的代码,使用crosstool-ng;
    [N]更新toolchain的下载地址(codesourcery -> memtor),加入linaro下载地址;
    [N]增加pr_fmt的讲解;
    [N]增加关于ignore_loglevel bootargs的讲解;
    [N]增加EARLY_PRINTK和DEBUG_LL的讲解;

    [F]调整proc的范例,创建/proc/test_dir/test_rw;
    [N]修正关于3.10后内核proc实现框架和API的变更;
    [N]增加关于BUG_ON和WARN_ON的讲解
    [F]不再以BDI-2000为例,改为ARM DS-5;
    [N]增加关于ARM streamline性能分析器的介绍;
    [N]增加使用qemu调试linux内核的案例;
    [F]调整Oops的例子,使用globalmem,平台改为ARM;
    [F]更新LTT为LTTng。

     

    第23章 《Linux设备驱动的移植》
    [D]整章删除

     

    全新的章节

     

    第17章  《I2C、SPI、USB架构类比》

     

    本章导读

    本章类比I2C、SPI、USB的结构,从而更进一步帮助读者理解本书第12章的内容,也进一步实证Linux驱动万变不离其宗的道理。

     

    第18章  《Linux设备树(Device Tree)》

     

    本章导读

    本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP变更。

    18.1节阐明了ARM Linux为什么要采用设备树。

    18.2节详细剖析了设备树的结构、结点和属性,设备树的编译方法以及如何用设备树来描述板上的设备、设备的地址、设备的中断号、时钟等信息。

    18.3节讲解了采用设备树后,驱动和BSP的代码需要怎么改,哪些地方变了。

    18.4节补充了一些设备树相关的API定义以及用法。

    本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是步入内核3.x时代后,嵌入式Linux工程师必备的知识体系。

     

     

     

    第19章  《Linux的电源管理》

     

    本章导读

    Linux在消费电子领域的应用已经铺天盖地,而对于消费电子产品而言,省电是一个重要的议题。

    本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP变更。

    19.1节阐述了Linux电源管理的总体架构。

    19.2~19.8节分别论述了CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator、OPP以及电源管理的调试工具PowerTop。

    19.9节讲解了系统Suspend to RAM的过程以及设备驱动如何提供对Suspend to RAM的支持。

    19.10节讲解了设备驱动的Runtimesuspend。

    本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是Linux设备驱动工程师必备的知识体系。

     

     

     

    第20章 《Linux芯片级移植与底层驱动》

     

    本章导读

    本章主要讲解,在一个新的ARM SoC上,如何移植Linux。当然,本章的内容也适合MIPS、PowerPC等其他的体系架构。

    第20.1节先总体上介绍了Linux 3.x之后的内核在底层BSP上进行了哪些优化。

    第20.2节讲解了如何提供操作系统的运行节拍。

    第20.3节讲解了中断控制器驱动,以及它是如何为驱动提供标准接口的。

    第20.4节讲解多核SMP芯片的启动。

    第20.6~20.9节分别讲解了作为Linux运行底层基础设施的GPIO、pinctrl、clock和dmaengine驱动。

    本章相对《Linux设备驱动开发详解(第2版)》几乎是全新的一章内容,有助于工程师理解驱动调用的底层API的来源,以及直接有助于进行Linux的平台移植。

     

    展开全文
  • LINUX设备驱动程序

    千次下载 热门讨论 2010-10-08 23:42:17
    LINUX设备驱动程序 嵌入式开发资料
  • 深入Linux设备驱动程序内核机制 你懂的 好书 今年新书 驱动
  • Linux设备驱动

    千次阅读 2006-01-09 11:30:00
    简介 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI...
        

    一. 简介

        操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕 上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本 的组成部分,在Linux内核源程序中也占有较高的比例。


    二. 基本原理

        设备由两部分组成,一个是被称为控制器的电器部分,另一个是机械部分。控制器通过系统总线加载到 电脑上。典型的方式是,一组互不冲突的寄存器组被赋予到各个控制器。I/O端口包含4组寄存器,即状态寄存器,控制寄存器,数据输入寄存器,数据输出寄存器。状态寄存器拥有可以被主机读取的(状态)位,用来 指示当前命令是否执行完毕,或者字节是否可以被读出或写入,以及任何错误提示。控制寄存器则被主机写 操作以启动一条命令或者改变设备的(工作)模式。数据输入寄存器用于获取输入而数据输出寄存器则向主机 发送结果。所以,处理器和设备之间的基本界面是控制和状态寄存器。当处理器执行程序并且遇到与设备相关的指令 时,它通过向相应的设备发送一条命令来执行该指令。控制器执行所要求的动作并设置状态寄存器的特定位, 然后进入等待。处理器有责任检查设备的状态直到发现操作完成。例如并口驱动程序(打印机使用的)一般会 轮询打印机以知道打印机是否准备好。如果打印机没有准备好,驱动程序会睡眠一段时间(处理器此时会做其 他有用的工作),该过程将重复直到打印机准备好。这种轮询的机制能够改进系统的性能。另外一种方式则是 系统进行不必要的"死等"(unnecessarily waiting)而不做任何有用的工作。
        寄存器拥有在I/O空间明确定义的地址范围。通常这些地址在启动时被分配,使用一组在配置文件中定义 的参数。如果设备是静态加载的,各个设备的地址范围可能被预分配。这意味内核包含了已存在设备的驱动 程序,已分配的I/O端口能被存放在Proc目录下。你可以在系统使用这些设备时,通过运行“cat /proc/ioports” 命令同步的检查其所使用的地址范围。第一列输出显示了端口的范围而第二列则是拥用这些端口的设备。一些操作系统具备在运行时动态加载设备驱动模块的特性。所以任何新的设备都能通过动态加载模块在系统运行 时加载到系统中,并且能够被控制和访问。
        设备驱动的概念是非常抽象的并且处于一台计算上所运行软件的最低层。由于直接到设备的硬件特性的限 制。每个设备驱动都只管理一种单一类型的设备。这些类型可能是字符型,快设备型或网络型。如果一个应用 程序向设备提出(操作)要求。内核会联系到对应的设备驱动,设备驱动接着向特定的设备发出命令。设备驱 动是一个函数集合:包含了许多调用入口,类似于open,close,read,write,ioctl,llseek 等。当你插入 你的模块时,init_module ( ) 函数会被调用,而模块被移出时,cleanup_module ( ) 函数会被调用。设备是在 设备驱动的init_module ( ) 例程中被登记的。
        当设备在 init_module ( ) 中登记时,设备的资源如I/O端口,内存和中断号也在这个函数被分配,这也 是驱动程序能够正确操作设备的需要。如果你分配了任何错误的内存地址,系统会显示错误信息segmentation fault。 而对于I/O端口,系统不会给出任何类似wrong I/O port的信息,但是指派任何现有设备已使用的端 口将会造成系统崩溃。当你移出模块时,设备应当被注销,更确切的说,主(设备)号和资源将在cleanup_module ( ) 函数中被释放.
        设备驱动最频繁的工作时读写IO端口。所以你的驱动应当是确信完美的,被设备使用的端口地址是独占的。 任何其他设备都不会使用这段地址范围。为了确认这点,首先驱动应当查明这段地址是否在使用,当驱动发现 这段地址未被使用时,可以申请内核为设备分配这段地址。

    三. 内核与驱动模块

        Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。
       
    我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM (高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱 动。
       
    因此,加载驱动时就是加载内核模块。

        有关模块的命令有:lsmod、modprob、insmod、rmmod、modinfo

        lsmod 列出当前系统中加载的模块
        rmmod卸载已加载的模块
        insmod
    ,插入模块,但是它不会自动解决依存关系,所以一般加载内核模块时使用的命令为modprobe。

        modprobe,智能插入模块,即根据模块间依存关系,以及/etc/modules.conf文件中的内容智能插入模块

        modinfo,查看模块信息

        系统的模块文件保存在/lib/modules/2.4.XXX/kerne目录中,根据分类分别在fs、net等子目录中,他们的互相依存关系则保存在/lib/modules/2.4.XXX/modules.dep 文件中

    四. 设备文件

        当加载了设备驱动模块后,应该怎样访问这些设备呢?Linux是一种类Unix系统,Unix的一个基本特点是“一切皆为文件”,它抽象了设备的处理,将所有的硬件设备都像普通文件一样看待,也就是说硬件可以跟普通文件一样来打开、关闭和读写。

        系统中的设备都用一个设备特殊文件代表,叫做设备文件,设备文件又分为Block(块)型设备文件、Character(字符)型设备文件和Socket (网络插件)型设备文件。Block设备文件常常指定哪些需要以块(如512字节)的方式写入的设备,比如IDE硬盘、SCSI硬盘、光驱等。 而Character型设备文件常指定直接读写,没有缓冲区的设备,比如并口、虚拟控制台等。Socket(网络插件)型设备文件指定的是网络设备访问的BSD socket 接口。

        设备文件都放在/dev目录下

    五. 使用/proc目录中的文件监视驱动程序的状态

        通过设备文件怎样访问到相应的驱动程序呢?它们中间有一个桥梁,那就是proc文件系统,它一般会被加载到/proc目录。访问设备文件时,操作系统通常 会通过查找/proc目录下的值,确定由哪些驱动模块来完成任务。如果proc文件系统没有加载,访问设备文件时就会出现错误。
       
    Linux系统中proc文件系统是内核虚拟的文件系统,其中所有的文件都是内核中虚拟出来的,各种文件实际上是当前内核在内存中的参数。它就像是专门为 访问内核而打开的一扇门,比如访问/proc/cpuinfo文件,实际上就是访问目前的CPU的参数,每一次系统启动时系统都会通过 /etc/fstab中设置的信息自动将proc文件系统加载到/proc目录下.
       
    通过/proc目录下的文件可以访问或更改内核参数,可以通过/proc目录查询驱动程序的信息。
       
    /proc/sys目录下的文件一般可以直接更改,相当于直接更改内核的运行参数,
        free
    等命令一般都是查询/proc目录下的文件,并返回结果。

    参考:
        http://www.linuxfocus.org/ChineseGB/November2002/article264.shtml

    展开全文
  • linux设备驱动模型

    千次阅读 2016-04-16 15:17:52
    基于一个虚拟总线分析linux设备驱动模型

    1、总线概念

    总线是内核与一个或者多个设备之间的通道。在设备模型中,所有的设备都是通过总线连接

    2、sysfs

    • 作用:


      一个用于内核对象表述的文件系统
      sysfs是一个基于ramfs的内存文件系统. 描述内核数据结构,属性以及与它们用户空间的链接关系

    • sys文件夹内含的顶层文件夹(使用ls -l /sys可以查看)

      1. block/
      2. bus/ :包含内核中所有总线类型的描述. 每一个总线文件夹下面都有下面两类文件夹
      3. devices/ :内核中所有注册的设备,内含的设备都是指向sys目录下面devices目录内设备的链接文件
      4. drivers/ :所有与注册设备相匹配的驱动程序
        总线设备驱动总是会分出设备与驱动,也就是设备、驱动分离分层的概念,在注册的时候总会进行设备与驱动的匹配,内核总线会提供各自的match函数以供此类总线设备驱动调用
      5. class/
      6. devices/ : 设备树的文件系统表述. 映射了内核的设备结构层次,包含内核所有的注册设备
      7. firmware/
      8. net/
      9. fs/ : 包含一些文件系统的目录.想要表述其属性的文件系统都必须在fs目录下创建自己的层次

    3、kobject

    每个加载到内核的kobject结构体都具体化为一个sys或者其子目录下的一个文件夹

    Kobject

    一个基础结构,用于保持设备模型在一起,Kobject最初是用来简单的引用计数的,但是随着时间的发展已经增加了很多的功能

    • 对象的引用计数
      当一个内核对象被创建的时候,往往不能够确定这个对象的声明周期,Kobject被用作对象声明周期的跟踪,当对象计数完毕之后且没有内核进行引用,则该对象就可以被删除(正在使用的模块不能够删除应该与此有关)
    • sysfs的表示
      在sys下面的每一个对象都对应一个Kobject结构,用来创建其可视化的表示
    • 数据结构粘和
      整体来看, 设备模型是一个极端复杂的由多级组成的数据结构, 各级之间有许多连接。kobject 实现这个结构并且保持它在一起
    • 热拔插事件的处理
      Kobject负责事件的产生,而事件则负责通知内核用户空间硬件的接入与卸载

    Kobject结构体原型(不同版本的内核原型可能不一样)

    struct kobject {
        const char      * k_name;               //Kobject静态名字
        char            name[KOBJ_NAME_LEN];    //Kobject名字 
        struct kref     kref;
        struct list_head    entry;              //list_head结构体
        struct kobject      * parent;           //父结构体,往往是指向容纳kobject的kset中的kobject结构体
        struct kset     * kset;                 //kset结构体
        struct kobj_type    * ktype;        //ktype,内包含kobject的relase函数指针
        struct dentry       * dentry;
        wait_queue_head_t   poll;               //等待队列的头部
    };

    使用Kobject

    由于C语言没有继承这一特性,所以需要另一种方法实现,就是在结构体里面嵌入另一个结构体来实现对上一个对象的继承,要从嵌入的结构体找到被嵌入的结构体需要使用container_of宏,这个是与list_head一样的原理,详见list_head结构体详解

    • 初始化清零Kobject
      使用memset函数对Kobject结构体进行初始化清零操作,否则会产生意想不到的程序崩溃
    • 初始化一个Kobject
    void kobject_init(struct kobject *kobj);
    • 设置Kobject的名字(必须的,需要用在sysfs的入口)
    int kobject_set_name(struct kobject *kobj, const char *fmt, ...);
    va_start(args,fmt); //初始化一个char型指针,指向本函数最后一个参数的结尾处并且32字节对齐
    (void)((args) = (((char *) &(fmt)) + (((sizeof (fmt)) + (31)) & (~(31)))))
    • 设置其他应当设置的 kobject 成员
      ktype, kset, parent.

    Kobject引用计数

    struct kobject *kobject_get(struct kobject *kobj);  //增加一个计数
    void kobject_put(struct kobject *kobj);             //减掉一个计数

    当一个Kobject计数为0时,代表此对象已经到达生命周期的尽头,此时就需要一个Kobject的释放函数,而这个释放函数必须存在到其被调用为止,但是又由于这个Kobject释放函数并不能嵌入kobject结构体本身,所以就有了kobj_type这个结构体,此结构体往往可能会在两个地方给出,一个是kobject内部的kobj_type,另一个是包含该kobject的kset给出,已知该kobject的话,可以使用struct kobj_type *get_ktype(struct kobject *kobj);来获取其kobj_type结构体

    4、kset

    很多情况下,kset被看做是kobj_type的扩展,用来表示被嵌入到同一类型结构的kobject集合。但是kobj_type关注的是特定一个对象,而kset关注的是一整个集合

    struct kset {
        struct kobj_type    * ktype;        //内含的kobj_type,用于指示kobject的kobj_type,优先于kobject自身含有的kobj_type
        struct list_head    list;           //用于串联kset的list_head双向循环链表
        spinlock_t      list_lock;  
        struct kobject      kobj;           //内含的kobject
        struct kset_uevent_ops  * uevent_ops;   //
    };

    增加一个kobject到kset

    此时该objetc计数会增加1,因为这是对kobject的一个引用

    extern int kobject_register(struct kobject *kobj);
    //此函数是kobject_init 和 kobject_add 的结合

    从kset删掉一个kobject

    void kobject_del(struct kobject *kobj); //或者 
    extern void kset_unregister(struct kset * k);
    //后者是下述两个函数的结合
    extern void kobject_del(struct kobject *);
    extern void kobject_put(struct kobject *);

    kset提供类似于kobject的接口函数

    extern void kset_init(struct kset * k);
    extern int __must_check kset_add(struct kset * k);
    extern int __must_check kset_register(struct kset * k);
    extern void kset_unregister(struct kset * k);
    
    /* 减少一个计数 */
    static inline struct kset * kset_get(struct kset * k)
    {
        return k ? to_kset(kobject_get(&k->kobj)) : NULL;
    }
    
    /* 增加一个计数 */
    static inline void kset_put(struct kset * k)
    {
        kobject_put(&k->kobj);
    }

    5、低级sysfs操作

    kobjects 的 sysfs 入口一直为目录, 因此一个对 kobject_add 的调用导致在sysfs 中创建一个目录. 常常地, 这个目录包含一个或多个属性

    • 分配给 kobject 的名字( 用 kobject_set_name ) 是给 sysfs 目录使用的名字. 因此, 出现在 sysfs 层次的相同部分的 kobjects必须有唯一的名字. 分配给 kobjects 的名字也应当是合理的文件名字: 它们不能包含斜线字符, 不能为空

    • sysfs 入口位于对应 kobject 的 parent 指针的目录中. 如果 parent 是 NULL 当 kobject_add 被调用时, 它被设置为嵌在新 kobject 的kset 中的 kobject; 因此, sysfs 层级常常匹配使用 kset 创建的内部层次. 如果 parent 和 kset 都是 NULL, sysfs 目录将在顶级被创建

    //每个kobject被创建的时候都会有一个缺省的属性,由kobj_type里面的struct attribute   ** default_attrs;指定
    /* struct attribute原型 */
    struct attribute {
        const char      * name;
        struct module       * owner;
        mode_t          mode;
    };
    //属性类别
    #define S_IRWXUGO   (S_IRWXU|S_IRWXG|S_IRWXO)
    #define S_IALLUGO   (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
    #define S_IRUGO     (S_IRUSR|S_IRGRP|S_IROTH)
    #define S_IWUGO     (S_IWUSR|S_IWGRP|S_IWOTH)
    #define S_IXUGO     (S_IXUSR|S_IXGRP|S_IXOTH)
    /* attrbute只是负责指明属性的类型,而实现这些属性的工作就交给了sysfs_ops */
    struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *,char *);
        ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
    };
    • 添加一个属性到kobject
    int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
    • 删除属性
    int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
    • 创建一个符号链接
    int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
    • 删除一个符号链接
    void sysfs_remove_link(struct kobject *kobj, char *name);

    6、mybus的编写实现

    头文件

    #ifndef _MYBUS_H_
    #define _MYBUS_H_
    
    #include <linux/device.h>
    
    /* 定义mybus的设备结构体 */
    struct mybus_device {
        const char  * name;
        struct device   dev;
        struct mybus_driver *drv;
    };
    /* 由mybus设备里面的设备结构体找到该mybus,详见list_head结构体注解 */
    #define to_mybus_device(_dev) container_of(_dev, struct mybus_device, dev)
    
    /* 定义mybus驱动 */
    struct mybus_driver {
        int (*probe)(struct mybus_device *);
        int (*remove)(struct mybus_device *);
        void (*shutdown)(struct mybus_device *);
        int (*suspend)(struct mybus_device *, pm_message_t state);
        int (*resume)(struct mybus_device *);
        struct device_driver driver;
    };
    /* 同上面的 to_mybus_device */
    #define to_mybus_driver(drv) container_of(drv, struct mybus_driver, driver)
    
    extern int register_mybus_device(struct mybus_device *mybusdev);
    extern void unregister_mybus_device(struct mybus_device *mybusdev);
    extern int register_mybus_driver(struct mybus_driver *mybusdrv);
    extern void unregister_mybus_driver(struct mybus_driver *mybusdrv);
    
    #endif /* end _MYBUS_H_ */

    C文件

    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    /* mybus卸载时候会调用到 */
    static void mybus_relase(struct device * dev)
    {
        printk("mybus relase \n");
    }
    
    /* 总线设备 */
    struct device my_bus = {
        .bus_id   = "mybus",
        .release  = mybus_relase
    };
    
    /* 设备与设备驱动匹配函数 */
    static int mybus_match(struct device * dev, struct device_driver * drv)
    {
        struct mybus_device *pdev = to_mybus_device(dev);
    
        printk("mybus_match is called\n");  //输出打印信息,说明此函数被调用
    
        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
    
    /* mybus总线热拔插事件 */
    static int mybus_event(struct device *dev, char **envp, int num_envp,
            char *buffer, int buffer_size)
    {
        envp[0] = buffer;
        snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", mybus_version);
    
        return 0;
    }
    
    /* 定义一个总线结构体 */
    struct bus_type mybus_type = {
        .name = "mybus",
        .match = mybus_match,
        .uevent  = mybus_event,
    };
    
    /* mybus 的设备与设备驱动操作函数 */
    /* mybus 设备相关函数 */
    static void mybus_device_release(struct device * dev)
    {
        printk("mybus_device relase \n");
    }
    
    int register_mybus_device(struct mybus_device *mybusdev)
    {
        mybusdev->dev.bus = &mybus_type;    //设置该设备的总线为mybus总线类型
        mybusdev->dev.parent = &my_bus;     //设置该设备的父设备为my_bus设备
        mybusdev->dev.release = mybus_device_release;   //设备释放
        strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
    
        return device_register(&mybusdev->dev); //注册该设备
    }
    
    EXPORT_SYMBOL(register_mybus_device);
    
    void unregister_mybus_device(struct mybus_device *mybusdev)
    {
        device_unregister(&mybusdev->dev);  //卸载设备
    }
    
    EXPORT_SYMBOL(unregister_mybus_device);
    
    /* mybus设备驱动相关的函数 */
    static int mybus_driver_probe(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->probe(dev);
    }
    
    static int mybus_driver_remove(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->remove(dev);
    }
    
    static void mybus_driver_shutdown(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        drv->shutdown(dev);
    }
    
    static int mybus_driver_suspend(struct device *_dev, pm_message_t state)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->suspend(dev, state);
    }
    
    static int mybus_driver_resume(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->resume(dev);
    }
    
    int register_mybus_driver(struct mybus_driver *mybusdrv)
    {
        if(mybusdrv->probe)
            mybusdrv->driver.probe = mybus_driver_probe;
        if(mybusdrv->remove)
            mybusdrv->driver.remove = mybus_driver_remove;
        if(mybusdrv->shutdown)
            mybusdrv->driver.shutdown = mybus_driver_shutdown;
        if(mybusdrv->suspend)
            mybusdrv->driver.suspend = mybus_driver_suspend;
        if(mybusdrv->resume)
            mybusdrv->driver.resume = mybus_driver_resume;
    
        mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
    
        return driver_register(&mybusdrv->driver);  //注册设备驱动
    }
    
    EXPORT_SYMBOL(register_mybus_driver);
    
    void unregister_mybus_driver(struct mybus_driver *mybusdrv)
    {
        driver_unregister(&mybusdrv->driver);   //卸载设备驱动
    }
    
    EXPORT_SYMBOL(unregister_mybus_driver);
    
    /* mybus总线初始化 */
    static int mybus_init(void)
    {
        int error;
    
        error = bus_register(&mybus_type);  //注册mybus总线
        if(error){
            bus_unregister(&mybus_type);
            printk("Can't register mybus_type bus_type\n");
            return error;
        }
    //  此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
    //  if(bus_create_file(&mybus_type, &mybus_attribute)){
    //      printk("Can't create mybus version attribute\n");
    //  }
    
        error = device_register(&my_bus);   //mybus设备注册,所有的从属于mybus总线的设备都会被挂入此设备链表
        if(error){
            device_unregister(&my_bus);
            printk("Can't register my_bus device\n");
            return error;
        }
    
        return 0;
    }
    
    /* mybus总线卸载 */
    static void mybus_exit(void)
    {
        device_unregister(&my_bus);
    //  bus_remove_file(&mybus_type, &mybus_attribute);
    //  此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
        bus_unregister(&mybus_type);
    }
    
    module_init(mybus_init);
    module_exit(mybus_exit);
    MODULE_LICENSE("GPL");
    
    MODULE_AUTHOR("YellowMax");

    8、mybus与驱动的联合测试

    led_dev测试程序

    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    static void led_dev_release(struct device * dev)
    {
        printk("led_dev relase \n");
    }
    
    static struct mybus_device led_dev = {
        .name   =   "by_led",
        .dev    = {
            .release    = led_dev_release,
        },
    };
    
    static int led_dev_init(void)
    {
        int error;
    
        error = register_mybus_device(&led_dev);
        if(error){
            printk("register led_dev failed\n");
            unregister_mybus_device(&led_dev);
            return error;
        }
        return 0;
    }
    
    static void led_dev_exit(void)
    {
        unregister_mybus_device(&led_dev);
    }
    
    module_init(led_dev_init);
    module_exit(led_dev_exit);
    MODULE_LICENSE("GPL");

    led_drv测试程序

    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    static int led_drv_probe(struct platform_device *p_dev)
    {
        /* 输出打印信息,说明两者成功匹配 */
        printk("Found by_led, Connect success\n");
    
        return 0;
    }
    
    static int led_drv_remove(struct platform_device *p_dev)
    {
        /* 输出打印信息,说明成功卸载设备 */
        printk("Found by_led, Remove success\n");
    
        return 0;
    }
    
    static struct mybus_driver led_drv = {
        .probe  = led_drv_probe,
        .remove = led_drv_remove,
        .driver = {
            .name   = "by_led",
        },
    };
    
    static int led_drv_init(void)
    {
        register_mybus_driver(&led_drv);
        return 0;
    }
    
    static void led_drv_exit(void)
    {
        unregister_mybus_driver(&led_drv);
    }
    
    module_init(led_drv_init);
    module_exit(led_drv_exit);
    MODULE_LICENSE("GPL");

    联合测试

    • 装载led_drv.ko查看现象(由于没有注册总线设备,所以会出现函数未定义错误)
    insmod led_drv.ko led_drv: Unknown symbol register_mybus_driver led_drv: Unknown symbol unregister_mybus_driver insmod: cannot insert 'led_drv.ko': Unknown symbol in module (-1): No such file or directory
    • 装载mybus.ko并且查看/sys/bus/
    insmod mybus.ko ls -l /sys/bus/ drwxr-xr-x 4 0 0 0 Jan 1 00:01 mybus
    • 查看设备
    ls -l /sys/devices/ drwxr-xr-x 3 0 0 0 Jan 1 00:01 mybus
    • 再次装载led_drv.ko并查看mybus里面的驱动
    insmod led_drv.ko ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led
    • 装载led_dev.ko
    insmod led_dev.ko mybus_match is called //调用到了mybus.c里面的相关match函数 Found by_led, Connect success //调用到led_drv.c里面的probe相关函数
    • 装载其他模块
    insmod led_drv1.ko insmod led_drv2.ko insmod led_dev2.ko mybus_match is called mybus_match is called mybus_match is called Found by_led2, Connect success

    由此可见,mybus.c里面的match相关函数会被内核不断地调用,直到找到匹配的设备或者驱动(测试函数同上,只不过修改一下设备以及驱动名字)。我装载了3个驱动,一共找了三次,基本可以说明这些设备是挂载在mybus总线设备上面的,找的时候直接去mybus总线找,

    insmod led_dev1.ko mybus_match is called mybus_match is called Found by_led1, Connect success
    • 全部装载完毕并且查看相关的设备以及设备驱动
    ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led1 drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led2 ls -l /sys/bus/mybus/devices/ lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led -> ../../../devices/mybus/by_led lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led1 -> ../../../devices/mybus/by_led1 lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led2 -> ../../../devices/mybus/by_led2
    • 尝试移除mybus
    rmmod mybus rmmod: mybus: Resource temporarily unavailable rmmod led_dev1 Found by_led, Remove success mybus_device relase rmmod led_dev Found by_led, Remove success mybus_device relase rmmod led_dev2 Found by_led, Remove success mybus_device relase rmmod led_drv rmmod led_drv1 rmmod led_drv2
    • 再次尝试移除mybus,调用到了mybus.c里面的static void mybus_relase(struct device * dev)函数,mybus设备引用计数达到0
    rmmod mybus mybus relase

    9、分析mybus过程

    /* 初始化注册一个bus总线 */
    int bus_register(struct bus_type * bus)
        retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);  //将总线对应为mybus子系统的Kobject结构
        /* 将Kobject放入一个kset容器,并且注册 */
        subsys_set_kset(bus, bus_subsys);
        retval = subsystem_register(&bus->subsys);
    
        /* 设置一个名字为devices的Kobject,然后将其父结构指向上面构建的mybus的Kobject */
        kobject_set_name(&bus->devices.kobj, "devices");
        bus->devices.kobj.parent = &bus->subsys.kobj;
    
        /* 初始化bus总线的device链表与driver链表,device链表添加get(增加一个设备引用计数),put(减少一个设备引用计数) 
         * 之后在此bus总线里面加入设备以及驱动的时候就可以通过该bus总线的设备链与设备驱动链进行查找了 
         */
        klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
        klist_init(&bus->klist_drivers, NULL, NULL);
    /* 提供该bus总线的设备注册 */
    int register_mybus_device(struct mybus_device *mybusdev)
        /* 结构体的填充 */
        mybusdev->dev.bus = &mybus_type;    //设置该设备的总线为mybus总线类型
        mybusdev->dev.parent = &my_bus;     //设置该设备的父设备为my_bus设备
        mybusdev->dev.release = mybus_device_release;   //设备释放
        strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
    
        return device_register(&mybusdev->dev); //注册该设备
    /* 提供该bus总线的设备驱动注册 */   
    int register_mybus_driver(struct mybus_driver *mybusdrv)
        if(mybusdrv->probe)
            mybusdrv->driver.probe = mybus_driver_probe;
        if(mybusdrv->remove)
            mybusdrv->driver.remove = mybus_driver_remove;
    
        //省略若干
    
        mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
    
        return driver_register(&mybusdrv->driver);  //注册设备驱动

    10、总线设备以及总线设备驱动的注册

    分别由device_registerdriver_register实际完成注册
    在两者的函数里面都会有试图获得该注册对象的bus的语句,如果获得成功的话,不仅在系统顶层的sys/devices里面创建对象Kobject,而且在对应的bus里面也创建Kobject用来计数以及以文件形式显示出来设备。同一个被注册对象可能会被不同的链表链接,在任意一个链表里面都可以找到该对象。就比如一个总线设备,在其bus总线的设备列表里面可以找得到,在系统的devices链表里面也可以找得到

    展开全文
  • Linux设备驱动程序和设备文件

    千次阅读 2018-01-14 18:25:10
    Linux设备驱动程序和设备文件 设备驱动程序 一个设备驱动程序是一个管理着系统与某种特定硬件之间交互作用的程序。驱动程序在设备可理解的硬件指令和内核使用的固定编程接口之间起转换作用。驱动程序层的存在有...
  • 前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习。   一、platform 驱动的工作过程  platform模型驱动编程,需要实现platform_device...
  • 本课程是linux驱动开发的第5个课程,主要内容是linux设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式...
  • 精通LINUX设备驱动程序开发 中文扫描

    千次下载 热门讨论 2012-09-05 16:08:21
    精通LINUX设备驱动程序开发 Linux 中文 扫描 高清
  • (二)Linux设备驱动的模块化编程 (三)写一个完整的Linux驱动程序访问硬件并写应用程序进行测试 (四)Linux设备驱动之多个同类设备共用一套驱动 (五)Linux设备驱动模型介绍 (六)Linux驱动子系统-I2C子...
  • 5.linux设备驱动模型

    万次阅读 多人点赞 2018-10-10 10:19:43
    1.linux设备驱动模型简介 1.1、什么是设备驱动模型 (1)类class、总线bus、设备device、驱动driver (2)kobject和对象生命周期 (3)sysfs (4)udev 1.2、为什么需要设备驱动模型 (1)早期内核(2.4之前)没有统一的...
  • Linux设备驱动程序教程将为您提供有关如何为Linux操作系统编写设备驱动程序的所有必要信息。 本文包含一个易于遵循的实用Linux驱动程序开发示例。 我们将讨论以下内容: 内核日志系统 如何使用角色设备 如何使用...
  • Linux设备驱动开发详解:基于最新的Linux 4.0内核》
  • linux设备驱动例子,编译过,通过,用于驱动学习
  • 在整个linux设备驱动的学习中,字符设备的驱动是最基础的,下来我们就先看下字符设备驱动程序的框架。 在linux中,字符设备驱动是由下面几个部分组成的。 首先我们先来看一下字符设备的内核模块是如何加载与卸载的...
  • 嵌入式linux设备驱动开发详解

    热门讨论 2010-10-08 23:22:56
    嵌入式linux设备驱动开发详解 嵌入式开发资料
  • (五)Linux设备驱动模型介绍

    千次阅读 2019-05-24 16:48:00
    (二)Linux设备驱动的模块化编程 (三)写一个完整的Linux驱动程序访问硬件并写应用程序进行测试 (四)Linux设备驱动之多个同类设备共用一套驱动 (五)Linux设备驱动模型介绍 (六)Linux驱动子系统-I2C子...
  • 1 Linux设备驱动概述及开发环境构建 1.1 设备驱动的作用 驱使硬件设备行动 1.2 无操作系统时的设备驱动 典型架构:一个无限循环中夹杂着对设备中断的检测或者对设备的轮询 1.3 有操作系统时的设备...
  • LINUX设备驱动程序第三版.pdf免费下载链接(.pdf书籍的优点是便于直接在电脑中保存有电脑就可以阅读,如果觉得这本书给你提供到了很大的帮助,可以去书店补一本纸质版) 资源保存在腾讯微云上,下载不需要微云...
  • Linux 设备驱动模型

    千次阅读 2013-10-17 22:54:56
    Linux设备驱动模型是一个相当复杂的系统,对于初学者来说真有些无从入手。而且更加困难的是,随着新的Linux Kernel的release,Linux的设备驱动模型总会有或大或小的变化,我们将尽量展现 Linux Kernel 的这种变化。...
  • Linux设备驱动开发入门

    万人学习 2016-01-20 10:28:16
    本课程讲解Linux驱动程序开发基本知识,程序架构,字符设备编程,杂项设备编程,具体硬件模块驱动开发。
  • linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存。 下面...
  • linux设备驱动框架

    万次阅读 多人点赞 2018-05-26 11:32:00
    一.Linux设备分类字符设备: 以字节为单位读写的设备。块设备 : 以块为单位(效率最高)读写的设备。网络设备 : 用于网络通讯设备。字符设备: 字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,...
  • linux设备驱动的实现与理解

    千次阅读 2018-03-31 12:40:40
    linux设备驱动的实现与理解 在linux中对字符设备的驱动编写,驱动插入以及使用驱动文件进行逻辑控制,其中这份代码写在嵌入式板中,通过控制io来实现灯的亮灭,但是设备驱动的实现流程与灯无关,大致的流程都体现在...
  • 裸机驱动与Linux设备驱动的区别

    千次阅读 2016-05-05 10:26:39
    裸机驱动一般针对没有操作系统支持的...总之,Linux设备驱动就是比裸机驱动多了一些框架。 裸机底层驱动设计方法: 所谓裸机在这里主要是指系统软件平台没有用到操作系统。在基于ARM处理器平台的软件设计中,如
  • linux设备驱动makefile入门解析

    千次阅读 2016-08-24 15:07:15
    linux设备驱动makefile入门解析 对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以 完成大部分驱动的编译,使用时只需要修改一下要编译生成的驱动名称即可。只需修改...
  • 前面我们学习I2C、USB、SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: ...这样结构清晰,大大地有利于我们的驱动开发,这其中就是利用了Linux设备驱动开发
  • linux驱动基础开发1——linux 设备驱动基本概念

    万次阅读 热门讨论 2011-09-22 17:27:15
    学习linux设备驱动首先我们必须明确以下几个概念,为我们接下来学习linux驱动打下坚实的基础: 应用程序、库、内核、驱动程序的关系 设备类型 设备文件、主设备号与从设备号 驱动程序与应用程序的...
  • linux设备驱动归纳总结

    千次阅读 2014-10-16 15:45:26
    我的总结是根据学习时的笔记(李杨老师授课)、《linux内核设计与实现》第三版、《linux设备驱动程序》第三版和《linux设备驱动开发详解》第一版来归纳的。文章中涉及一些自己的想法,并不能保证所说的一定正确。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,584
精华内容 17,433
关键字:

linux设备驱动

linux 订阅