1、嵌入式系统城人式系疣软件开技术主要内容Ji 版级支持包BSP-嵌入式系统初始化以及BSP的设计-Linux系统驱动程序开发-嵌入式联网BSP的概念 BSP全称“板级支持包”(Board Support Packages),说的简单一点,就是一段启 动代码,和计算机主板的BIOS差不多,但提供的功能区别就相差很大在Windows CE中,BSP是驱动程序、OEM适 应层(OEM Adaptation Layers,AL)、硬件抽象层(HAL)以及启动 设备和使外设正常工作所需BIOS文件的 集合。&BSP和Bl OS区另 BIOS主要是负责在电脑开启时检测、初始化系 统设备(设置栈指针,中断分配
2、,内存初始 化.)、装入操作系统并调度操作系统向硬件 麦出的指令。BSP是和操作系统绑在起运行,尽管BSP的开 始部分和BIOS所做的工作类似,但是BSP还包 含和系统有关的基本驱动 BIOS程序是用户不能更改,编译编程的,只能 对参数进行修改设置,但是程序员还可以编程 修改BSP,在BSP中任意添加一些和系统无关的 驱动或程序,甚至可以把上层开发的统统放到 BSP中4不同系统中的BSP ST每年都有大量新 品推出 新的芯片功能总是需要相应的驱动程序支持Linux驱动开发流程熟悉设备的特性 确定设备驱动程序类别 编写测试用例 搜集可重用的代码 编写自己的驱动程序代码 编码、调试、测试S Lin
3、ux驱动程序的开发环境本机编译调试 开发环境配置简单 无需网络环境 适用于配置较高的x86机器 主机+目标机 主机可以自由选择Linux或1Windows+Cygwin 主机和目标机通过网络共享文件系统 内核崩溃不会影响主机Linux驱动程序的开发环境(续)主机+目标机环境包括 主机运行的工具链:cross gcc+glibc+gdb,如果是windows主机还要有cygwin仿真环境 主机运行远程服务,常用的有tftp用来传送内 核映像、initrd,NFS用来共享文件系统 目标机运行ssh或telnet等远程登陆服务,用来 调试驱动程序Linux驱动程序的加载方式驱动程序直接编译入内核驱动
4、程序在内核启动时就已经在内存中-可以保留专用存储器空间驱动程序以模块形式存储在文件系 统里,需要时动态载入内核驱动程序按需加载,不用时节省内存驱动程序相对独立于内核,升级灵活.Linux驱动程序模块加载Linux驱动程序开发的任务 规划硬件资源的使用 分离硬件相关和硬件无关的代码 划分驱动程序的抽象层次 移植驱动程序到新的平台&Linux驱动程序开发的任务规划硬件资源的使用 CPU时间片分配 中断处理 系统存储器空间映射J 设备存储器的映射decoderRFIf ReaderKxt UARTZOU PlashLinux驱动程序开发的任务分离硬件相关和硬件无关的代码划分驱动程序的抽象层次Linu
5、x驱动程序开发的任务-移植驱动程序到新的平台Platform IPlatform 2GPL对驱动程序开发的影响实现非GPL授权的方法模块形 式动态加载驱动程序可以以私有产权形式进行 商业授权设备驱动程序的代码-驱动程序的注册与注销 re gister_chrdevO re gister_blkdev()设备的打开与释放 openQ releaseQ设备的读写操作 read。write 设备的控制操作 ioctlQ设备驱动的加载 使用模块的方式动态加载驱动 int func_init(void)Makefile:insmod xx.o Ismod rmmod xx.o 将驱动静态编译到内核里面
6、int init func_init(void)Makefile:启动时自动加载.Linux驱动程序模块加载编写驱动程序的些基本概念无论是什么操作系统的驱动程序,都有 些通用的概念操作系统提供给驱动程序的支持也大致 相同以下简单介绍网络设备驱动程序的些 基本要求编写网络驱动程序发送和接收这是一个网络设备最基本的功能 如块网卡所做的无非就是收发工作。所以驱动程序 里要告诉系统发送函数在哪里,系统在有数据要发送 时就会调用发送程序。驱动程序由于是直接操纵硬件的,所以网络硬件有数 据收到,最先能得到这个数据的也就是驱动程序,它 负责把这些原始数据进行必要的处理,然后送给系 统。这里,操作系统必须要提
7、供两个机制:找到驱动程序的发送函数驱动程序把收到的数据送给系统1编写驱动程序读写 几乎所有设备都有输入和输出。每个驱动程 序要负责本设备的读写操作。操作系统不需要知道对设备的具体读写操作 怎样进行,这些都由驱动程序屏蔽掉了 操作系统定义好一些读写接口,由驱动程序 完成具体的功能 在驱动程序初始化时,需要把具有这种接口 的读写函数注册进操作系统d编写驱动程序中断 中断在现代计算机结构中有重要的地位 操作系统必须提供驱动程序响应中断的能力 一般是把个中断处理程序注册到系统中去 操作系统在硬件中断发生后调用驱动程序的处理程序 Linux支持中断的共享,即多个设备共享一个中断编写驱动程序时钟 在实现驱
8、动程序时,很多地方会用到时钟。如某些协 议里的超时处理,没有中断机制的硬件的轮询等。操作系统应为驱动程序提供定时机制 一般是在预定的时间过了以后回调注册的时钟函数内核模块 模块是内核的一部分,但是并没有被编译到内 核里去。它们被分别编译和连接成目标文件。用命令insmod插入一个模块到内核中,用命令 rmmod卸载个模块 在Linux内核中,以下内容一般编译成模块:大多数的驱动程序。包括SCSI设备,CD-ROM,网 络设备,不常用的字符设备,如打印机,watchdog 等。大多数文件系统,理论上除了根文件系统不能是模 块,其他文件系统都可以是模块。些内核支持的不常用的可执行文件格式,如 bi
9、nfmt_ miscokmod和高级模块化 Linux提供了对模块自动加载和卸载的支持 要利用这一特性,在编译内核前进行的配置 中,必须打开对kmod的支持选项。一旦内核试图访问某种资源并发现该资源不可 用时,它会对kmod子系统进行次特殊的调 用而不仅仅是返回一个错误 按需加载的例子:ALSA(Advanced Linux Sound Architecture)声卡驱动程序组的实现1常用的系统支持 内存申请和释放 中断 时钟 I/O 中断打开关闭 输出信息 注册驱动程序内存申请和释放 include/linux/kernel.h里 尸 明 J kmalloc。和 kfreeQ用于在内核模式下
10、申请和释放内 存。与用户模式下的malloc。不同,kmalloc。申 请空间有大小限制。长度是2的整次方。可 以申请的最大长度也有限制。另外kmalloc。有priority参 数 Kfrce。释放的内存必须是kmalloc。申请的申请中断和释放中断 rcqucst_irqOfree_irqQ是驱动程序申请 中断和放中断面调用。在 include/linux/sched.h里 声明时钟 时钟的处理类似中断,也是登记个时 间处理函数,在预定的时间过后,系统 会调用这个函数。在includc/linux/timer.h 里 戸 明 使用时钟,先声明一个timcjlist结构,调 用inijtim
11、er对它进行初始化。Time_list结 构里expires是标明这个时钟的周期,单位 采用jiffies的单位。I/O端口的存取使用:inline unsigned int inb(unsigned short port);inline unsigned int inb_p(unsigned short port);inline void outb(char value,unsigned short port);inline void outb_p(char value,unsigned short port);在 include/adm/io.h里 定 义中断打开关闭 系统提供给驱动程序开
12、放和关闭响应中 断的能力 是社 include/asm/sy stem,h#define cliQ asm volatile(cli:)#define stiQ _asm volatile(nstin:)输出信息-驱动程序要输出信息使用printkO include/linux/kernel.h M 严明注册驱动程序 如果使用模块(module)方式加载驱动程 序,需要在模块初始化时把设备注册到 系统设备表里去 不再使用时,把设备从系统中卸除 定义在drivers/net/net_init.h里的两个函数 完成这个工作 Int register_netdev(struct device*dev
13、);void unregister_netdev(struet device*dev);网络驱动程序的结构 所有的Linux网络驱动程序遵循通用的接口 设计时采用的是面向对象的方法 个设备就是个对象(device结构),它内部有自己的数据 和方法 每个设备的方法被调用时的第一个参数都是这个设备对 象本身。这样这个方法就可以存取自身的数据(类似面向对 象程序设计时的this引用)个网络设备最基本的方法有初始化、发送和接收 初始化程序完成硬件的初始化、device中变量的初始化和 系统资源的申请 发送程序是在驱动程序的上层协议层有数据要发送时自动 调用的。一般驱动程序中不对发送数据进行缓存,而是直
14、 接使用硬件的发送功能把数据发送出去 接收数据一般是通过硬件中断来通知的。在中断处理程序 里,把硬件帧信息填入个skbuff结构中,然后调用 netirx()传递给上层处理网络驱动程序的基本方法 初始化(initialize)打开(pen)关闭(stop)发送(hard_start_xmit)接收(reception)硬件帧头(hard_hcadcr)地址解析(xarp)参数设置和统计数据网络驱动程序的基本方法一初始化驱动程序必须有一个初始化方法在把驱动程序载入系统的时候会调用这个初始化程序。它做以下几方面的工作:检测设备:在初始化程序里你可以根据硬件的特征 检查硬件是否存在,然后决定是否启动
15、这个驱动程 序。配置和初始化硬件:在初始化程序里可以完成对硬 件资源的配置,比如即插即用的硬件就可以在这个 时候壮行配置(Linux内核PnP功能没有很好的支 持,可以在驱动程序里完成这个功能)。配置或协商好硬件占用的资源以后,就可以向系统 申请这些资源。有些资源是可以和别的设备共享 的,如中断。有些是不能共享的,如1、DMA初始化device结构中的变量 让硬件正式开始工作j网络驱动程序的基本方法一打开打开(open)pen这个方法在网络设备驱动程序里是在网络设备被激 活的时候被调用(即设备状态由down-up)实际上很多在initialize中的工作可以放到这里来做。比如 资源的申请、硬件
16、的激活。如果dev-open返回非。(error),则硬件的状态还是down pen方法另个作用是如果驱动程序做为个模块被装 入,则要防止模块卸载时设备处于打开状态。在open方法里要调用MODNC_USE_COUNT宏网络驱动程序的基本方法一关闭关闭(stop)close方法做和open相反的工作。可以释放某些资源以减 少系统负担。close是在设备状态由up转为down时被调用的 如果是做为模块装入的驱动程序,close里应该调用MOD_DEC_USE_COUNT,减少设备被引用的 次数,以使驱动程序可以被卸教。close方法必须返回成功(0=success)网络驱动程序的基本方法一发送爱
17、送(hard_start_xmit)所有的向络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit时,发送的数据放在个 sk_buff结构中。一般的驱动程序把数据传给硬件发出去。也有一些特殊的 设备比如loopb ack把数据组成一个接收数据再回送给系 统,或者dummy设备直接丢弃数据。如果发送成功,hard_start_xmit方法里释放sk_bu任,返回 0(发送成功)如果设备暂时无法处理,比如硬件忙,则返回1。这时如 果dev-tbusy置为非0,则系统认为硬件忙,要等到dev-tbusy置。以后会再次发送。tbusy的置任务一般由中断 完成。网络驱动程序的基本方法一发送(续
18、)硬件在发送结束后产生中断,这时可以把tbusy置,然后 用mark_bh。调用通知系统可以再次发送。在发送不成功的情况下,也可以不置dev-tbusy为非0,这 样系统会不断尝试重发。如果hard_start_xmit发送不成功,则不要释放sk_buff。传送下来的sk_buff中的数据已经包含硬件需要的帧头。所 以在发送方法里不需要再填充硬件帧头,数据可以直接提 交给硬件发送。sk_buff是被锁住的(locked),确保其他程序不会存取它。网络驱动程序的基本方法一接收接收(reception)驱动程序并不存在个接收方法。有数据收到应 该是驱动程序来通知系统的。一般设备收到数据后都会产生一
19、个中断,在中断 处理程序中驱动程序申请块sk_buff(skb),从硬 件读出数据放置到申请好的缓冲区里。接下来填 无sk_buff中的一些信息。skb-dev=dev,判断收 到帧的协双类型,填入skb-protocol(多协 岐的主痔)。把指针skb-mac.raw指向硬件数据然后丢弃硬件帧 头(skb_pull)网络驱动程序的基本方法一接收(续)设置skb-pkjtype,标明第二层(链路层)数据类型。可以是以 下类型:PACKET_BROADCAST:链路层 广播 PACKET_MULTICAST:链路层组播 PACKET_SELF:发给自己的帧PACKET_OTHERHOST:发给别
20、人的帧(监听模式时会有这种 帧)最后调用netifjx。把数据传送给协议层。netifjx。里数据放入 处理队列然后返回,真正的处理是在中断返回以后,这样可 以减少中断时间。调用netifjx。以后,驱动程序就不能再存取数据缓冲区skb。网络驱动程序的基本方法一硬件帧头 硬件一般都会在上层数据发送之前加上自己的硬件 帧头,比如以太网(Ethernet)就有14字节的帧头。这 个帧头是加在上层ip、ipx等数搪包的前面的。驱动程序提供个hard_hcader方法,协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。硬件帧头的长度必须填在dcv-hard_headcr_len,这样 协议
21、层回在数据之前保留好硬件标头的空间。这样 hard_header程序只要调用skb_push然后正确填入硬 件帧头就可以了。网络驱动程序的基本方法一硬件帧头(续1)在协议层调用hard_hcader时,传送的参数包括(2.0.xx):数据的sk_buff device扌旨针 Protocol 目的地址(daddr)源地址(saddr)数据长度Qcn)-数据长度不要使用sk_buff中的参数,因为调用hardjeader 时数据可能还没完全组织好 saddr是NULL的话是使用缺省地址(default)daddr是NULL表明协议层不知道硬件目的地址如果harcLhcadcr完全填好了硬件帧头,
22、则返回添加的字节 数。网络驱动程序的基本方法一硬件帧头(续2)如果硬件帧头中的信息还不完全(比如daddr为NULL,但是帧头中需要目的硬件地址。典型的情况是以太网 需要地址解析(arp),则返回负字节数。hard_header返 回负数的情况下,协议层会做进步的build header的 工作。目前Linux系统里就是做arp(如果hard_header返回正,dev-arp=l,表明不需要做arp,返回负,dev-arp=O,做 arp)对hard_header的调用在每个协议层的处理程序里。如 ip_output 网络驱动程序的基本方法一地址解析址解析(xarp)有些网络有硬件地址(比如
23、Ethernet),并且在发送硬件 帧时需要知道目的硬件地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。这个对应是通过地址 解标完成的。需要做arp的的设备在发送之前会调用驱动程序的 rebuild_header方法。调用的主要参数包括:指通硬件帧头的指针协议层地址如果驱动程序能够解析硬件地址,就返回1,如果不能,返回。对 rebuild_header 的 调用 在 net/core/dev.c 的 do_dev_queue_xmitQ 里 网络驱动程序的基本方法一参数设置和统计数据参数设置和统计数据在驱动程序里还提供一些方法供系统对设备 的参数进行设置和读取信息。一般只有超级用
24、户(。司权限才能对设备参数进 行设置。设置方法有:dev-set_mac_addressQ当用户痼用ioct!类型为SIOCSIFHWADDR时是要设 置这个设备的mac地址。一般对mac地址的设置没有 太大意义的。网络驱动程序的基本方法一参数设置和统计数据(续)dev-set_configQ当用户调用ioct!时类型为SIOCSIFMAP时,系统会调用驱动程序的 sejconfig方法。用户会传递个ifmap结构包含需要的I/O、中断等参 数。dev-do_ioctlQ如果用户调用ioctl时类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,系统会调用驱动程序的这个
25、方法。一般 是设置设备的专用数据。读取信息也是通过ioct!调用进行。除次之外驱动程序还可以提供个dev-get_stats方法,返回一个 enet_statistics结构,包含发送接收的统计信息。ioctl 的处理在 net/core/dev.c 的 dev_ioctlQ 和 dev_ifsioc。里网络驱动程序中用到的数据结构最重要的是网络设备的数据结构。它定义社 include/linux/netdevice.h sk_buff Linux网络各层之间的数据传送都是通过sk buff编写inux网络驱动程序中需要注意的问题 中断共享硬件发送忙时的处理 流量控制(flow control
26、)调试中断共享 Linux系统运行几个设备共享同一个中断。需要共享的 话,在申请的时候指明共享方式。,系统提供的request_irqQ调用的定义:int request_irq(unsigned int irq,void(handler)(int irq,void*dev_id,struct pt_regs*regs),unsigned long irqflags,const char*devname,void*dev_id);如果共享中断,irqflags设置SA_SHIRQ属性,这样就允 许别的设备申请同一个中断。需要注意所有用到这个 中断的设备调用request_irq()都必须设置这
27、个属性。系统在回调每个中断处理程序时,可以用dev_id这个参 数找到相应的设备。一般dev_id就设为device结构本 身。系统处理共享中断是用各自的dev_id参数依次调用 每个中断处理程序。硬件发送忙时的处理主CPU的处理能般比网络发送要快,所以经常会遇到 系统有数据要发,但上一包数据网络设备还没发送完。因 为在Linux里网络设备驱动程序一般不做数据缓存,不能发 送的数据都是通知系统发送不成功,所以必须要有一个机 制在硬件不忙时及时通知系统接着发送下面的数据。一般对发送忙的处理在前面设备的发送方法(hard_start_xmit)里已经描述,即如果发送忙,置tbusy为 lo处理完发
28、送数据后,在发送结束中断里清tbusy,同时 用mark_bh()调用通知统继续发接。但在具体实现驱动程序时发现,这样的处理系统好象并不 能及时地知道硬件已经空闲了,即在mark_bh()以后,系统 要等一段时间会接着发送。造成发送效率很低。实现时不把tbusy置1,让系统始终认为硬件空闲,但是报 告发送不成功。系统会一直尝试重发。这样处理就运行正 常了。:流量控制F%络数据的发送和接收都需要流量控制。这些控制是在系统里实现 的,不需要驱动程序做工作。每个设备数据结构里都有一个参数dev-tx_queueen,这个参数 标明发送时最多缓存的数据包。在Linux系统里以太网设备(10/100Mb
29、ps)tx_queue_len一般设置为100,串行线路(异步串口)为10。实际上,设置了dev-tx_queueen并不是为缓存这些数据申请了空 间。这个参数只是在收到协议层的数楣包时判断发送队列里的数据 是不是到了的限度,贞定这庖数糖加加进麦垓 队列。发送时易个方面的流控是更高层协议的发送窗口(TCP协议 里就有发送窗口)。达到了窗口大小,高层协议就不会再发送数据。接收流控也分两个层次。netifLrxO缓存的数据包有限制。另外高层 协议也会有1个最大的等待处理的数据量。发送和接收流控处理在net/core/dev.c的do_dcv_queue_xmit()和 netif_rx()中。调试
30、很多Linux的驱动程序都是编译进内核的,形成一人大的内核文件。但对 调试来说,这是相当麻烦的。调试驱动程序可以用module方式加载。支持模块方式的驱动程序必须提供两个函数:intinit_module(void)Pvoid cleanup_module(void)init_module()在加载此模块时调用,在这个函数里可以registcjnetdevO注 册设备。inijmoduleO返回。表示成功,返回负表示失败。cleanup_moduleO在驱动程序被卸载时调用,清除占用的资源,调用 unregister_netdevO 模块可以动态地加载、卸载。在2Oxx版本里,还有kernel
31、d自动加载模 决,但是22xx中已卷取消Tkerneldo手工加戴使用insmod命令卸载用 rmmod命令)看内核中的模块用Ismod命令 编译驱动程序用gcj主要命令行参数一DKERNEL.DMODULE。并且作 为模块加载的驱动程序,只编译成。可形式(加一c参数)。编译好而目标文件 放在/lib/modules/2.x.xxmisc卞)在启动文件里用insmod加载Linux驱动程序可利用资源互联网上有很多驱动程序资源:www.kernel.or g www.linuxdevice.org J驱动源代码_#inlude.#deHne SimpleLED_MAJOR 97static lo
32、ng ioremap_addr;ssize_t SimpleLE D_read struct file*file 9char*buf size_t county lo仃1 f_opprintk(tfs3c2410:device file-read operation!nft);return count;ssize_t Simp leL E D_wr ite(s7/7/ct file*file cotist char*buf size J count,3 fops)printk(tfs3c2410:device file-write operation!nM);return count;驱动源代
33、码ssize_t SimpleLED_ioctl struct inode 伕 inode9 struct file*file,unsigned int cnid,long data)printk(Ms3c2410:device ioctl operation!nft);issize_t SimpleLED_open struct inode*inode,struct file*file)return 0;issize_t SimpleLED_release struct inode*inode,struct file*file)outw(0 x0000aoremap_addr);return
34、 0;驱动源代码struct fileoperations SiinpleLED_ops=open:SinipleLEDopen,read:SinipleLED_read9write:SiinpleLED ritqioctl:SiinpleLEDjoctl,release:SimpleLEDel ease,/其他b二驱动源代码1.static int _init HVV_SinipleLED_init(void2.13.intret=-ENODEV;4.int delay;5.ret=devfs_register_chrdev(SimpleLED_MAJOR,nSimpleLEDf&Simpl
35、eLED_ops);6.if(ret 0)7.printk(“3c241:iniCmodule failed with%dnf ret);8.return ret;9.else10.printk(KERN_INFOM SimpleLE D register success!nM);11.dev_handle=devfs_register(NULLJSinipleLED,DEVFS_FL_DEFAULLSimpleLED_MAJORAS_IFCHR.&SimpleLED_ops NULL);12.ioreniap_addr=ioremap(0 x20000000,0 x0f);13.oiitw(
36、0 x55aa,ioremap_addr);14.printk(Mremap address=%xnH joremap_add,15.return ret;,驱动源代码1.inf_init s3c2410_SimpleLED_init(void)2.int ret=ENODEV;3.ret=HW_SimpleLED_init();4.if(ret)return ret;5.return 0;6.int init_module()7.8.s3c2410_SimpleLED_init();9.10.void cleanup_module()1112.devfs_unregister_chrdev(
37、SimpleLED_MA,JOR,MSimpleLEDn);13.devfs_unregister(dev_handle);14.编译驱动程序Makefile文件内容:/opt/host/armv41/bi n/arm v41-un k no wn-1 inux-gcc n-D n_KERNEL_ n-I/s3c2410_linux/kernel/include n-DMODULE n-c n-o nSimpleLED.o SimpleLED.c应用程序int main()SimpleLE D_openint fdl,fd2,i,ret,buf;fdl=opef*7(Iev/SimpleLED
38、n,0_RDWR);if(fdl=0)read。和 write。struct inode binode:指向代表要访问的设 备的特殊文件的指针。sturct file*file:指向该设备的文件结构 的指针。Char*buf:个读写的字符缓冲区。位于用 户空间内存中,可以用get_存(),put_fs*()和!nemcpy*f s()访问 Int count:缓冲区中读或写的字符的计数。它是buf的大小,也是知道怎样到达buf的末尾 的手段,因为buf是没有保证以NULL结尾的。Select()struct inode*inode:指向该设备的inode结构的指针。-Struct file*f
39、ile:指向设备的文件结构的指 针。Int sel_type:可以执行的选择类型SEL_I N readSEL_OUT writeSELEX exceptionSelect_table*wait如果设备没有准备好,调用select_wait(),并且 返回。如果设备准备好,返回1。ioctl()函数:处理ioctl调用。J|_结构:首先差错检查,然后用个大的switch语句来处 理所有可能的ioct。参数:Struct inode*inodeStruct file*fileUnsigned int cmd:ioctl命令。一般用于做case语句的 switch 参数。Unsigned int
40、arg这是此命令的参数,由用户定义。返回:出错返回-error。其他情况下返回由用户定义。mmap()函数Struct inbde*inodeStruct file*fileUnsigned long addr需要映射进入的主存开始地址。Size_t len需要映射的存储空间长度。I nt prot 下面中的个:PROT_READ 可以读的区域。PROT_WRITE可写的区域PROT_EXEC 可执行的区域PROT_NONE 不可访问的区域Unsigned long off 需要映射的文件偏移地址。这个 地址将被映射到addr。open。和 release。函数Struct inode*ino
41、de指向止匕设备的inode结构的指针。Struct file*file指向此设备的文件结构的指针。pen()在设备特殊文件打开时调用。是用来保 证一致性的策略机制。Release。只在进程关闭它打开的最后个文件 描述子的时候调用?init()函数内核第一次启动时调用:在正确的位置调用irdt():字符设备 drivers/chai71Tlem.c 中的chr_devjnit()把 file_operation 注册到 VFS 中:对于字符设备register_chrdev()打印关于设备的信息,并且报告找到的硬件printk()中断处理 文件/proc nterrupts 函数 reques
42、tjrq。函数 freejrq()-睡眠与唤醒-中断共享 ISR的上咅B和下咅B(bttmhalf)辅助函数请求调度 Static void add_request(struct blk_dev_struct*dev,struct request*req)Static void end_request(struct blk_dev_struct*dev,struct request*req)static void make_request(int major,int rw5 struct buffer_head*bh)void ll_rw_block(int rw5 int nr5 struc
43、t buffer_head*bh)m ake_req u est()调用 add_req uest()llw_block()才请求队列排序,只能通过buffer cache调用。电梯算法:读在写前低次设备号请求在高次设备号前 低块号在高块drivers/block/ll_ rw_ block.c,辅助函数定时器管理 void add_timer(struct timerjist*timer)void del_timer(struct timerjist*timer)void initjimer(struct timerjist*timer)struct timerjist辅助函数中断管理 ex
44、tern int request_irq(unsigned int irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void*devjd);void free_irq(unsigned int irq)-cli()sti()辅助函数端口读写 void out*()unsigned char byte,unsigned port unsigned in*()参数:unsigned port*:b,w,I 用in*()来清空某些状态值?辅助函数内存管理&设备号 kmalloc()
45、kfree()kfree_s()设备号由主、次设备号拼接而成。#define MAJOR(dev)(dev)8)#define MINOR(dev)(dev)&Oxff)辅助函数设备的注册和注销int register_chrdev(unsigned int major,const charname,struct file_operations*fops)int register_blkdev(unsigned int major,const char*name,struct file_operations*fops)int unregister_chrdev(unsigned int maj
46、or,const char*name)int unregister_blkdev(unsigned int major,const char*name)#include#include 辅助函数内存空间转换 inline void memcpy_tofs(void*to5 const void*from,unsigned long n)inline void memcpy_fromfs(void*to5 const void*from,unsigned long n)#include#define get_user(ptr)(_typeof_(*(ptr)_get_user(ptr)5size
47、of(*(ptr)#define put_user(x,ptr)_put_user(unsigned long)(x),(ptr),sizeof(*(ptr)#include 在用户空间和内核空间拷贝内存 属于隐含的I/O操作,不要再临界代码中使用,因为它可能冲破 cli()和sti()的保护辅助函数缓冲区管理 fs.cstruct buffer_head*getblk(kdev_t dev,int block,int size);void brelse(struct buffer_head*buf);void ll_rw_block(int rw,int nr,structbuff er_h
48、ead*bh口);4辅助函数其他 int printk(const char*fmt,.)#include 在内核中打印信息,是printf的内核版本可能导致隐含的I/O操作。不要在cli()保护的代 码段中使用,因为它可能导致开中断。些与进程相关的系统调用模块编程基础 模块的基本概念:可以动态的加载到内核中成为kernel的一部 分;加载后可以访问内核的数据结构;用户空间的程序或进程可以通过某个模块和内 核交互。module在需要的时可通过符号表(symbol table)使用核心资源。而且module一般需要调用核心的资源,所以必须注意module的版本和核心的版 本的相配问题。一般在mo
49、dule的装入过 程中检查module的版本信息。模块之间的函数调用内核可以使用其它模块或内核的函数,也可以export一 些函数供其他模块或内核使用。模块栈:如果模块A使用了模块B的函数,那么B必须 在A之前加载,否则加载A的命令不成功。模块可以使用的函数:自身定义;其他module提供;内核提供 命令ksyms-a:列出已经加载的模块的函数或变量。Symbol table:记录module导出的函数或变量。所有声明为global的函数或变量都意味着被导出,可以 被其他模块使用。模块编程基础常用命令 Ismod 把现在kernel中已经安装的modules 列出来 insmod 把某个mod
50、ule安装至Ukernel中。rmmod 把某个没在用的module从kerne中 卸载。depmod 制造 module dependency file,以告 诉标来的insmod要去哪儿找modules来安 装。这个 dependency file放在/lib/modules/当冃 kernel 版本/modules.dep。I模块基础装入 Insmod 命令 内核kerneld守护进程(daemon)自动装入。守护进程:在超级用户下运行的一个用户进程,与核 心建立一个I PC通道。Kerneld调用insmod和rmmond来装入和移出模块。kerneld 装入的module,般放在/l
©2010-2024 宁波自信网络信息技术有限公司 版权所有
客服电话:4008-655-100 投诉/维权电话:4009-655-100