




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、知识结构:Linux驱动程序设计模式(40%) 2.内核相关知识(30%) 3,硬件相关知识(30%) 驱动分类:字符,网络,块字符设备:以字节为最小单位块设备,一次传送一个整体数据(512字节),Linux可以以字节访问块设备(仅仅是驱动与 内核的接口不同,访问的顺序的不同(字符只可顺序访问,块驱动可随机访问) 网络接口:硬件(eth0),纯软件(lo)驱动的安装:模块,编译进内核(Linux启动的时候会自动加载init段)使用驱动程序:字符设备文件一字符设备驱动一字符设备文件系统一块设备文件一块设备驱动一块设备套接字一协议栈一网络设备驱动一网络接口设备主设备号用来标示与设备文件相连的驱动程
2、序,次编号被驱动程序用来辨别操作哪个设备 主设备号反映设备类型,此设备号区分同类型的设备dev_t高12位为主设备号,低20位为次设备号MAJOR( dev_t dev)从dev_t分解出主设备号MINOR( dev_t dev)从dev_t分解出此设备号MKDEV(major,minor)构造设备号;静态申请:1.根据Documentation/devices.txt驱动没有使用的主设备号2,使用 register_chrdev_region (dev_t form, unsigned count ,const char *name)函数注册(容易冲突) From希望使用的设备号,count
3、希望申请使用设备号的数 目,name设备名(体现在 /proc/devices)动态分配alloc_chardev_region (安装驱动前无法创建设备文件)创建设备文件后,通过/proc/devices 察看alloc_chardev_region ( dev_t *dev,unsigned baseminor, unsigned count, const char *name)dev分配的设配号,baseminor起始的此设备号,count要分配的设备数目,name设备名主要设备号:unregister_chrdev_region(dev_t dev, unsigned basemino
4、r)Mknod filename type major minor type 是字符或块 mknod serial0 c 1000Linux字符设备驱动3个重要数据结构Struct file每打开一次都有一个关联的struct file重要结构loff_t f_pos文件读写位置struct file_operations *f_opStruct inode记录文件的物理上的信息(设备号等),一个文件可以有多个file,但只有一个 inodeStruct file_operation *f_op 函数指针的集合struct file_operations mem_fops = (.owner
5、= THIS_MODULE,.llseek = mem_seek,.read = mem_read 读内核代码应用程序怎样访问驱动程序(read_write,c)系统条用read找到vfs_read根据file结构中找到file_operations中的read 字符设备使用struct cdev来描述字符设备注册可分为如下3个部分:分配 cdev struct cdev *cdev_alloc(void)初始化 cdev cdev_init(struct cedev *p, const struct file_operations *fops)添加 cdev cdev_add(struct
6、cdev *p ,dev_t dev ,unsigned count ) dev 设备号 设备号的数目字符设备的注销:cdev_dev(struct cdev *p)设备操作int (*open)(struct *inode , struct file *)如果该项为NULL,设备打开永远成功void( *release)(struct *inode , struct file *)ssize_t (*read)(struct file *, char _user *buff , size_t , loff_t *)ssize_t (*write)(struct file *, char _u
7、ser *, size_t , loff_t *)file是文件指针(来与内核),*buff是数据缓冲(用户空间),count传输的数据量(用户空间),offp访问位置(来与内核)*buff是用户空间的,不能直接使用int copy_from_user(void *to , const void _user *from, int n)int copy_to_user(void _user *to ,const void *to , int n)loff_t llseek( struct *file ,loff_t offset , int whence)open方法主要完成如下工作:1.初始化
8、设备2.标明此设备号在 open(struct inode *inode, struct fil)2 *函数可使用 MINOR(inode-i_rdev;获取此 设备号filp-private_data = dev;将设备描述指针赋值给私有文件指针,区分出了那种设备在read()函数使用struct mem_dev *dev = filp-private_data可根据私有文件指针指定找到 具体的设备,read函数参数没有inode,无法获取此设备号。kmalloc分配内存,返回地址,根据其返回的地址就可操作内存中的数据copy_to_user(buf,(void*)(dev-data+p),
9、count)这里的(dev-data+p)为什么要用(void *)强 制转换呢? copy_to_user 是这么定义的 int copy_to_user(void _user *to ,const void *to , int n), 但还是不理解。驱动程序调试分类:打印调试,调试器调试(kgdb),查询调试(proc文件系统)合理的使用printk可以全局的打开或关闭它们。并发:多个执行单元同时被执行竟态:并发的执行单元对共享资源(硬件资源或全局变量等)的共享访问通过semaphore机制和spin_lock机制实现获取信号量不成功该阻塞或者睡眠定义信号量 struct semaphor
10、e sem;初始化信号量 void sema_init(struct semaphore *sem ,int val)初始化信号量的初值为valvoid init_MUTEX(struct semaphore *sem)初始化一个互斥锁,把 sem 的值设为 1void init_MUTEX_LOCKED(struct semaphore *sem)初始化一个互斥锁,把 sem 的值设 为0定义与初始化工作可由如下宏一步完成DECLARE_MUTEX(name )定义一个信号量,并初始化为1DECLARE_MUTEX_LOCK(name)定义一个信号量,并初始化为0,为已锁状态获取信号量voi
11、d down(struct semaphore *sem)可能会导致进程睡眠,故不能在中断上下文 中使用,如果sem非负直接返回,否则挂起(TASK_UNINTERRUPTIBLE)不建议使用void down_interrruptible(struct semaphore *sem)信 号量不 可用 , 置为 TASK_INTERRUPTIBLEvoid down_killable(struct semaphore *sem)信号量不可用,置为 TASK_KILLABLEvoid up(struct semaphore *sem) 释放信号量自旋锁不会引起调用者的睡眠,线程会移植忙循环,移植
12、等待下去spin_lock_init(x)初始化自旋锁spin_lock(lock)获取自旋锁,不成功自旋在那spin_trylock(lock)不会一直等待spin_unlock信号量可以有多个持有者(1个互斥信号量),自旋锁只有一个持有者信号量适合保持时间较长,自旋锁适合保持时间较短Ioctl对硬件进行控制(改变波特率,报告错误信息)用户使用方法:int ioctl(int fd, unsigned long cmd,)点表示可选参数int(*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
13、long arg)cmd用户空间传下来的,arg用户传下来的参数ioctl命令实现方法:1.定义命令2.实现命令Documentation/ioctl-number.txt 定义了使用的幻数ioctl被划分为几个位段,include/asm/ioctl.h定义了这些字段:类型(幻数):8位宽,属于哪一类设备序号:表明设备命令的第几个传送方向:可能的值是_IOC_NONE , _IOC_READ, _IOC_WRITE是从应用程序的观点 来看的4 .参数的大小(数据的类型)内核提供下列宏来帮助定义命令_IO(type,nr)没有参数传递_IOR(type, nr, datatype)从驱动中读数
14、据_I0W(type, nr, datatype)从数据到驱动_IOWR(type, nr, datatype)type 和 number 成员作为参数被传递Ioctl函数的实现1.返回值2.参数使用3.命令操作 通常是个switch语句,不支持的返回-EINVAL使用ioctl中的参数:整数可以直接使用,指针则使用前需进行正确的检查 参数检查不需要检测的函数:copy_from_ user, copy_to_user, get_user, put_user需要检测的函数:get_user, _put_userint access_ok(int type, const void *addr,
15、unsingned long size)第一参数是VERIFY_READ或者VERIFY_WRITE, addr是要操作的用户内存的地址,size 是操作的长度。access_ok返回一个布尔值:1.存取没问题0.失败,如果返回失败, 则ioctl应当返回-EFAULT.等待队列:实现进程的阻塞,保存进程的容器,阻塞时放入等待队列,唤醒时,取出进程 定义等待队歹 Uwait_queue_head_t my_queue 初始化等待队列wait_waitqueue_head(&my_queue) 定义并初始化等待队列DECLARE_WAIT_QUEUE_HEAD(my_queue)有条件睡眠wai
16、t_event(queue, condition)当 condition 为真, 返 回。当 condition 为假,进 入TASK_UNINTERRUTIBLE睡眠,并挂在queue指定的等待队列上wait_event_interruptible(queue, condition)wait_event_killable(queue, conditon)无条件睡眠(老版本,不建议使用)sleep_on(wait_queue_head_t *q) interruptible_sleep_on(wait_queue_head_t *q)等待队列中唤醒进程Wake_up(wait_queue_t
17、*q)唤醒等待队列中的所有进程都唤醒Wake_up_interruptible(wait_queue_t *q)唤醒为 TASK_INTERRUPTIBLE进程阻塞方式是文件读写的默认方式,应用程序可以使用O_NONBLOCK标志非阻塞的设置了 O_NONBLOCK,系统只是简单的返回-EAGAINSelect系统调用对应于pollSelect用于多路监控,如没有一个文件满足要求,select将阻塞进程Int select(int maxfd, fd_set *reedfds, fd_set *writefds, fd_set *exceptfds, const struct timeval
18、*timeout)maxfd:文件描述符的范围,比检测的最大文件描述符大1Readfds:被读监控的文件描述符Writefds:被写监控的Exceptfds:被异常监控的Timeout:定时器Timeout为0时不管有没有文件满足要求,立即返回,无文件满足,返回0为NULL时,select将阻塞进程,直到文件满足要求为止为正整数的时候,等待的最长时间,即select在timeout时间内阻塞进程Select返回值正常返回满足要求的文件描述符个数没有满足的返回0select被某个信号打断,返回-1 , errno为EINTRSelect系统调用: 1.将要监控的文件添加到文件描述符集调用sele
19、ct开始监控判断文件是否满足要求将fd添加到fdset中在fdset中清楚fd清空fdset检测文件描述集中的需判断的fd发生变化VOid FD_SET(int fd, fd_set *fdset)VOid FD_CLR(int fd, fd_set *fdset)VOid FD_ZERO(fd_set *fdset)VOid FD_ISSET(int fd, fd_set *fdset)驱动通常由poll实现Unsigned int(*poll)(struct file *filp, poll_table *wait)负责完成:使用poll_wait将等待队列添加到poll_table中返回
20、描述设备是否可读可写的掩码 位掩码:POLLIN设备可读,POLLRDNORM数据可读,POLLOUT设备可写,POLLWRNORM数据 可写设备可读通常返回(POLLIN I POLLRDNORM)设备可写通常返回(POLLOUT I POLLWRNORM)Poll方法只是做一个登记,真正的阻塞发生在select.c中的do_select函数(分析了 do_select 函数)自动创建设备文件2.4 内核使用 devfs_register(devfs_handle_t dir, const char *name, unsigned int flags, unsigned int major
21、unsigned int minor, umode_t mode, void *ops, void *info)Dir:目录名,name:文件名;flags:创建标志;major:主设备号;minor此设备号;mode:创建模式;ops:操作函数集;info:通常为空从2.6.13开始,devfs不复存在,udev成为替代使用class_create 为设备创建一个 class,使用device_create创建对应的设备例:struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);Device_create(m
22、yclass, NULL, MKDEV(major_num, 0), NULL, “my_device”) void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件 的读取和修改,而不需要再调用read,write等操作。addr:映射的起始地址,通常为NULL,由系统指定length:映射文件的长度prot:映射区的保护方式PROT_EXEC:映射区可被执行,PROT_READ:映射区可被读取, PROT_WRITE
23、 :映射区可被写入flags:映射区的特性MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该 文件的进程共享。MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制 (copy-on-write),对此区域的修改不会写回源文件。fd:由open返回的文件描述符,代表要映射的文件offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映 射。注意mmap不能映像原有文件的长度int munmap(void *start, size_t length)成功返回0,失败返回-1. start的取值一般是mmap返回的地址虚拟内存区域:是虚拟地
24、址空间的一个同质区间,即有同样特性的连续地址范围。一个进程 的内存映像由以下几部分组成:程序代码,数据,BSS和栈区域,以及内存映射区域一个进程的内存区域可以通过查看/proc/pid/maps每一行的域为:start_end perm offset major:minor inodelinux内核使用结构vm_area_struct来描述虚拟内存区域,其中主要成员如下:unsigned long vm_start虚拟内存区域起始地址unsigned long vm_end虚拟内存区域结束地址unsigned long vm_flags映射一个设备是指把用户空间的一段地址关联到设备内存上。mm
25、ap做了三件事:1.找到用户空间地址(内核完成)2.找到设备的物理地址(原理图)3. 关联(通过页式管理)mmap设备方法需要做的就是建立虚拟地址到物理地址的页表。int( *mmap)( struct file *, struct vm_area_struct *)建立页表有两种方法:使用remap_pfn_range 一次建立所有页表;使用nopage VMA方法每次建立一个也表。int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size,
26、 pgprot_t prot)vma:虚拟内存区域指针virt_addr:虚拟地址的起始值pfn:要映射的物理地址所在的物理页 祯号,可将物理地址PAGE_SHIFT得到(PAGE_SHIFT为12,相当于除以4KB) size:要映射的区域的大小prot: VMA的保护属性硬件访问寄存器和内存的区别:寄存器和RAM主要不同在于寄存器操作由副作用(sideeffect或边际效果):读取某刻地址可能导致该地址内容变化,读中断状态寄存器,便自动清 零内存与IO在X86存在IO空间(串口并口等),在32为x86IO空间是64K,内存空间是4G, ARM, PowerPC只有内存地址空间的IO端口:一
27、个寄存器或内存位于IO空间时,称为IO端口IO内存:一个寄存器或内存位于内存空间时,称为IO内存对IO端口的操作需要按如下步骤完成:1,申请2,访问3,释放 申请:struct resource *request_region(unsigned long first, unsigned long n, const char *name) 从first开始的n个端口,name设备名字系统中端口的分配情况记录在/proc/ioports中访问:intb outb intw outw intl outl释放:void release_region(unsigned long start, unsig
28、ned long n)IO内存有4步:1.申请2,映射3,访问4,释放申请:struct resource *request_mem_region(unsigned long start, unsigned long len,char*name)从 start开始,长度为len字节的内存区。成功,返回非NULL,否则返回NULL。可在/proc/iomem中列出访问:在访问IO内存之前,必须进行物理地址到虚拟地址的转化,使用下面函数void *ioremap(unsigned long phys_addr, unsigned long size)访问:ioread8(void *addr)io
29、write8(u8 value, void *addr) 老版本 使用 readb writeb释放: 1, void iounmap(void *addr) 2,void release_mem_region(unsigned longstart, unsigned long len)混杂设备驱动:共享一个主设备号10,成为混杂设备Linux内核使用struct miscdevice描述混杂设备struct miscdevice(int minor;const char *name;const struct file_operations *fops;struct list_head lis
30、t;struct device *parent;struct device *this_device;使用 misc_register 函数来注册一个混杂设备驱动 misc_register(struct miscdevice *misc)使用上拉下拉避免悬浮Linux总线设备驱动模型(2.6内核难点)Sysfs文件系统(基于内存,展示内核数据结构属性,关系),与proc同类别的文件系统同 类别的文件系统,sysfs把连接在系统上的设备和总线组织成分级的文件,使其从用户空间 可以访问到sysfs 在/sys/目录下block目录:块设备信息 bus:总线(ide pci scsi sbpcmc
31、ia)里边还有devices和drivers目录,devices目录下都是软链接class目录:按照功能进行分类(网络)devices:包含系统所有的设备kernel:内核中的配置参数Module:系统中所有模块信息 firmware:系统中的固件fs:描述系统中的文件系统power:系统电源选项Kobject实现了基本的面向对象管理机制,与sysfs文件系统紧密相连,在内核中注册的每个 kobject对象对应sysfs文件系统中的一个目录(作用:在sys下创建一个目录)类似于C+中 的基类。void kobject_init(struct kobject *kobj)初始化 kobject
32、结构int kobject_add(strut kobject *kobj)将 kobject 对象注册到 Linux 系统int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, structkobject *parent, bonst char *fmt, .)初始化 kobject,并注册到 Linuxvoid kobject_del(struct kobject *kobj)从 Linux 系统中删除 kobject 对象struct kobject *kobject_get ( struct kob
33、ject*kobj)将kobject对象引用计数加1,同时返回该对象指针void kobject_put(struct kobject*kobj)将 kobject对象的引用计数减1,如果引用计数降为0,则调用release方法释放该kobject 对象kobject的ktype成员是一个特指向kobj_type结构的指针,该结构记录了 kobject对象的一些 属性struct kobj_type(void(*release)(struct kobject *kobj);struct sysfs_ops *sysfs_ops;struct attribute *default_attrs;r
34、elease:用于释放kobject占用的资源,当kobject的引用为0时被调用struct attribute(char *name;struct module *owner;mode_t mode; /*属性的保护位*/对应于kobject的目录下的一个文件,name成员就是文件名struct sysfs_opsssize_t(*show)(struct kobject *, struct attribute*, char *) ssize_t(*store)(struct kobject *, struct attribute *, const char *,size_t)show :
35、当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态 store :当用户写属性文件时,该函数被调用,用于存储用户传入的属性值(学习函数指针三点参数是怎么样的(从哪来到哪去)2.这个函数指针什么时候被调用3.这个函数用来做什么) kset是具有相同类型的kobject的结合,在sysfs中体现一个目录,kobject里边只能是文件不 能是目录struct ksetstruct list_head list; 链接该 kset 中所有 kobject 的链表头spinlock_t list_losk;struct kobject kobj; /内嵌的 kobjectst
36、ruct kset_uevent_ops *uevent_ops /处理热插拔事件的操作集合kset操作:int kset_register(struct kset *kset) 在内核中注册一个 ksetvoid kset_unregister(struct kset *kset) 在内核中注销一个 kset热插拔事件:当系统配置发生变化时,如:添加kset到系统;移动kobject,一个通知会从 内核空间发送到用户空间,这就是热插拔事件。热插拔事件导致用户空间相应的处理程序(如 udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事 件操作集合struct
37、 kset_uevent_opsint(*filter)(struct kset *kset, struct kobject *kobj);const char *(*name)(struct kset *kset, struct kobject *kobj);int(*uevent)(struct kset *kset, struct kobject *kobj, structkobj_uevent_env *env);当该kset所管理的kobject和kset状态发生变化时(如被加入,移动),这三个函数将被调 用。filter函数:决定是否将事件传递到用户空间。如果filter返回0,不
38、传递事件name函数:用于将字符串传递给用户空间的热插拔处理程序uevent函数:将用户空间需要的参数添加到环境变量中linux2.6内核提供了全新的内核设备模型设备模型元素:总线,设备,驱动总线:处理器和设备之间的通道,所有的设备都通过总线相连,包括platform总线(虚拟平 台总线),总线由bus_type结构表示总线的注册使用:bus_register(struct bus_type *bus)若成功,可在sysfs的/sys/bus下看到总线的删除使用:void bus_unregister(struct bus_type *bus)int(*match)(struct device
39、 *dev, struct device_driver *drv)当一个新设备或者驱动添加到这个总线时,用于判断指定的驱动程序是否能处理指定的设 备。可以返回非零值int (*uevent)(struct device *dev, char *envp, int num_envp, char *buffer,int buffer_size)在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量总线的属性由bus_attribute描述,定义如下:struct bus_attribute(struct attribute attr;ssize_t(*show)(struct bus-typ
40、e *, char *buf);ssize_t(*store)(struct bus_type *, const char *buf, size_t count);int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)仓0建属性void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) 删除属性Linux系统中每个设备由一个struct device描述int device_register(struct device *dev)
41、 注册设备void device_unregister(struct device *dev) 注销设备一条总线也是个设备,也必须按设备注册struct device_attributestruct attribute attr;ssize_t(*show)(struct device *dev, struct device_attribute *attr, char*buf);ssize_t(*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);int device_
42、create_file(struct device *device, struct device_attribute *entry)创建属性void device_remove_file(struct device *dev, struct device_attribute *attr)删除属性驱动程序由struct device_driver描述int driver_register(struct device_driver *drv) 注册驱动void driver_unregister(struct device_driver *drv) 注销驱动驱动的属性使用struct driver
43、_attribute来描述stryct druver_attributestruct attribute attr;ssize_t(*show)(struct device_driver *drv, char *buf);ssize_t(*store)(struct device_driver *drv, const char *buf, size_tcount);int driver_create_file(struct device_driver *drv, struct driver_attribute*attr)创建属性void driver_remove_file(struct de
44、vice_driver *drv, struct driver_attribute*attr)删除属性platform总线:2.6内核加入的虚拟总线,由platform_device和platform_driver两部分组成 platform优势在于platform机制将设备本身的资源注册进内核,由内核同意管理,在驱动程 序使用这些资源提供统一的接口,提高可以执行。通过 platform 流程:1.定义 platform_device 2.注册 platform_device3,定义 platform_driver 4.注册 platform_driver平台设备使用struct platfo
45、rm_device来描述struct platform_device(const char *name; 设备名int id;设备编号,配合设备名使用struct device dev ;u32 num_resources;struct resource resource;设备资源(中断号,基地址)platform_device 的分配使用:struct platform_device *platform_device_alloc(const char *name, int id)name:设备名id:设备id,一般为-1注册平台设备,使用函数int platform_device_add(struct platform_device *pdev)平台设备资源使
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 12 慧眼看交通 教学设计-2023-2024学年道德与法治三年级下册统编版
- 牛羊进口合同范本
- 外包员工顾问合同范本
- 亲属买房合同范本
- 12总也倒不了的老屋教学设计2024-2025学年统编版语文三年级上册
- 2023年浙江省中考科学一轮专题辅导教学设计:观察生物
- 3《欢欢喜喜庆国庆》(教学设计)2023-2024学年统编版道德与法治二年级上册
- Module 5 Unit 2 On Monday,I'll go swimming (教学设计)-2023-2024学年外研版(一起)英语三年级下册
- 玉米买卖居间合同范本
- 收购的合同范本
- 法社会学教程(第三版)教学
- AQ6111-2023个体防护装备安全管理规范
- 医院评审工作临床科室资料盒目录(15个盒子)
- 压力性损伤指南解读
- 汤姆走丢了 详细版课件
- 大学学院学生心理危机预防与干预工作预案
- 国有土地上房屋征收与补偿条例 课件
- 铁路建设项目施工企业信用评价办法(铁总建设〔2018〕124号)
- 叉形件加工设计与分析论文
- 高强螺栓质保书
- 市政工程施工进度网络图
评论
0/150
提交评论