ImageVerifierCode 换一换
格式:DOCX , 页数:28 ,大小:537.23KB ,
资源ID:8060389      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/8060389.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(linux驱动编程初级+makefile.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

linux驱动编程初级+makefile.docx

1、linux驱动编程初级+makefile驱动编程1 模块的概述 22 source insight 加载内核源码方法 23 模块makefile的编写 34 模块makefile编写方法 45 在X86上运行模块: 56 编写模块 57 模块的加载进内核命令 58 最简单的上层调用 + 调用驱动方法 69 复杂框架上层应用+驱动调用方法 710 复杂框架字符设备创建并注册过程 711 file_operations常用函数 912 同步互斥操作 1013 同步互斥函数总结 1014 阻塞IO编程流程 1115 轮询操作 上层select 下层poll 1216 信号处理 1217 中断 131

2、8 中断新模型-上半部中断和下半部中断的实现 1419 内核定时器编程 1520 内核延时函数 1521 内核源代码中头文件分配方式 1522 linux内核管理和内核的内存管理 1623 设备io端口和io内存访问 如何控制led的亮灭 1624 * 驱动-设备分离思想编程 内核进阶 1825 驱动-设备分离-核心最小架构 1826 驱动设备分离思想- 上层架构(基于封装) 2027 头文件总结 2328 设置系统自启动命令 u-boot 24第一天需要理清的东西1)模块的概念,模块与应用的区别2)模块主要的组成 头文件、module_init() modoule_exit() module

3、_lisence()3) 模块的如何编辑,如何编译,如何加载到内核中运行 使用makefile4) 模块驱动编写,必须通过上层应用程序调用。1 模块的概述模块是内核的一部分,为了防止内核太大,把它放在文件系统里面。也可以在编译内核的直接编译进内核。1,存储位置 可以在开始时编译进内核,也可以编译进模块,最后加载 2、运行时环境 在哪个内核树下编译,就对应这个运行环境 3、模块的编译问题:前提条件是需要对应的内核源码树,或者必须有对应的内核版本匹配4、模块编译使用makefile 注意makefile的编写2 source insight 加载内核源码方法在windows下创建工程,使用sour

4、ce insight查看内核代码:2.1 先将内核源码拷到对应的文件夹2.2 在source insight 里添加工程,筛选需要添加的文件注意选择 按照树来添加,然后按照remove来踢出不需要的文件夹主要添加arm 下 s5pc100 和s5p平台的 drives include 文件夹等,后面的许多不需要添加。2.3 最后同步3 模块makefile的编写模块的编译: 1)、模块编译的核心语句: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules -C : 进入内核源码树 M= : 返回到当前目录,再次执行该目录下的makefile eg: /tnt/abc

5、.c - abc.ko 1、在/tnt目录下敲make,只有/tnt目录下的Makefile被调用 2、目的是要进入到内核源码树中,一进一回,-C来进,M=带着内核源码树中的makefile的功能回来了 - 内核源码树中Makefile的目标: obj-y:汇集了所有编译进内核的目标文件 obj-m:汇集了所有编译成模块的目标文件 3、回来过后,我们只有确定obj-m变量的集合 4、make modules告诉内核的makefile,只做编译模块的功能4 模块makefile编写方法ifeq ($(KERNELRELEASE),)KERNELDIR := /work/linux-2.6.35-

6、farsightPWD := $(shell pwd)modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesinstall: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean: rm -rf .tmp_versions *.ko *.o .*.cmd *.mod.c *.order *.symvers.PHONY: modules cleanelseobj-m := ex1.oendif以上是makefile的内容, 注意原来的内核目录树不要进行 make clean 或者 make dist

7、clean KERNELDIR 表示模块加载在哪个内核的文件夹(又叫内核源码树), $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 表示进入该内核文件夹,将顶层makefile 中的内容带回,再重新执行一次该makefile 将obj-m := ex1.o 编译,并执行make modules (并只编译 ex1.c ,不编译其它模块 ) $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 表示执行顶层makefile的 modules install 标签下的命令 安装的位置原来默认在 /lib 下面,所以需要修

8、改其到我们制作的根文件系统下 /work/rootfs/在顶层Makefile位置搜索: MODLIB修改为: obj-m := ex1.o 你需要编译的.c的文件名*此时简单的编译环境已经搭建完毕*执行 make *执行 make install *在 /work/rootfs/lib/modules/2.6.35/extra即可找到该模块 .ko*搭建好环境,保证虚拟机与板子与计算机网络连通,并设置板子u-boot 从nfs挂载,启动内核,并成功通过nfs 加载rootfs,此时环境完毕,进入/work/rootfs/lib/modules/2.6.35/extra ,找到模块,加载卸装模

9、块操纵5 在X86上运行模块:修改Makefile中的内核源码树的目录X86下的内核源码树:/lib/modules/2.6.35-22-generic/build如果没有在控制台上交互,默认是看不到信息的,需要dmesg这个命令去查看6 编写模块模块最小组成如下: 注意 : module_init module_exit 必须放在末尾 注意: 函数的原型 返回值 头文件7 模块的加载进内核命令insmod rmmodlsmod8 最简单的上层调用 + 调用驱动方法8.1 首先在module_init(abc) abc函数中注册设备register_chrdev(注册设备号,上层可见的设备名,

10、操作封装)该函数完成设备注册,在板子上用 cat /proc/devices 便可以看见该设备8.2 完成fops 操作的封装 注意格式 必须在函数后面声明该结构体 头文件 #include 8.3 查看到该字符设备后,创建设备节点,则上层通过设备字符名与该设备号绑定mknod /dev/hf_char c 245 0ls /dev/ 可以查看注册的所有设备节点8.4 此时上层应用的open(”hf_char”,O_RDWR),即可完成该设备的打开 ,即可以完成上层应用于下层驱动相关 fops 的操作。由此可以理解: 上层应用通过设备名称在目录/dev/下查找设备, 下层驱动通过设备号如254

11、,在内核注册,在/porc/devices查看, 两者通过 mknod /dev/abc c 245 0 进行连接9 复杂框架上层应用+驱动调用方法9.1 知识前期准备9.1.1 设备号的操作设备号是由两部分组成的号,一个是主设备号,一个是次设备号。操作的主要函数:MKDEV();MINOR();MAJOR();注册的两者方式: 动态注册,静态注册1)、静态注册: register_chrdev_region(dev_t from, unsigned count, const char * name)2)、动态注册: alloc_chrdev_region(dev_t * dev, unsig

12、ned baseminor, unsigned count, const char * name)设备号:衔接应用空间到内核空间的符号 dev_t 32bit dev_t num; - 得到主设备号:MAJOR(XXX) XXX:必须是主次设备号的一个整体 #define MAJOR(dev) (unsigned int) (dev) MINORBITS) #define MINOR(dev) (unsigned int) (dev) & MINORMASK) #define MKDEV(ma,mi) (ma) MINORBITS) | (mi)10 复杂框架字符设备创建并注册过程10.1 完

13、成基本模块框架如:module_init() module_exit() module_licence() 头文件10.2 申请cdev 结构体、申请 file_operations 结构体、创建设备号变量major1) 在文件开头创建设备号,模块代码中创建 cdev 变量和fops变量2) 初始化fops 结构体中的实现操作函数10.3 在函数中完成设备号注册与设备注册 1) 使用regester_chrdev_region() 完成设备号注册,动态注册于静态注册两者,注意: 出错处理 ,设备号与整形数的转化 MKDEV()2) 使用 cdev_init() 完成fops 与cdev 结构体

14、绑定3) 使用cdev_add() 完成内核的注册10.4 模块驱动的卸载1) 设备的注销2) 设备号的注销*需要记住所有操作的函数名,不知用法可以使用source insight 搜索例子,照抄例子完成见下图: 一个是设备号注册子系统 ,一个是操作子系统 11 file_operations常用函数1)头文件 #include #include 2)(*read)对应上层的 read函数copy_to_usercopy_to_user(void _user *to, const void *from, unsigned long n)3)(*write)对应上层的write函数copy_fr

15、om_usercopy_from_user(void *to, const void _user *from, unsigned long n)4)(*ioctl)多用于设备控制,如lcd等 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);常用系统封装好的宏定义,完成命令的命名 如下: #define WDIOC_GETSUPPORT _IOR(WATCHDO

16、G_IOCTL_BASE, 0, struct watchdog_info) #define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int) #define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int) #define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int) #define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int) #define _IOR(type,nr,size)

17、 _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size) #define _IOC(dir,type,nr,size) (dir) _IOC_DIRSHIFT) | (type) _IOC_TYPESHIFT) | (nr) _IOC_NRSHIFT) | (size) local_irq_save(flags)local_irq_enable() - local_irq_restore(flags)2)、单CPU具备抢占内核进程上下文中,中断上下文都要考虑解决办法: 同步互斥+开关中断3)、多CPUcpu可以同时操作某个资源,所以必须考虑同步互斥问题

18、,并且中断和进程都要考虑解决办法: 同步互斥+开关中断13 同步互斥函数总结1) 整型原子2) 自旋锁3) 信号量13.1 自旋锁当等待时候执行while(1),cpu一直占用,所以一般内部执行时间必须很短,如果没有解锁前又取锁,就会导致系统崩溃,所以使用时请慎行!spinlock_t lock; 定义spin_lock_init(lock); 初始化spin_lock(lock); 获得锁spin_unlock(lock) 释放锁spin_lock_irq() = spin_lock() + local_irq_disable()spin_unlock_irq() = spin_unlock

19、() + local_irq_enablespin_lock_irq() = spin_lock() + local_irq_save()spin_unlock_irq() = spin_unlock()+local_irq_restore()13.2 信号量struct semaphore sem;sema_init()DECLARE_MUTEX(name) 完成两步:初始化并设置初始值为1DECLARE_MUTEX_LOCKED(name) : 初始化并设置初始值为0down(&sem); 获得信号,消耗信号up(&sem); 释放信号14 阻塞IO编程流程1、队列头结点的定义和初始化2、

20、人为的定义一个逻辑表达式作为我们的条件3、wait_event(队列名)唤醒4、wake_up(队列名的地址)1) DECLARE_WAIT_HEAD(NAME) 定义初始化队列头2) wait_event()3) wait_up()在全局声明 DECLARE_WAIT_HEAD(my_wait) wait_event(my_wait,(i=0); 当i=0 的时候则运行(等到i=0 的时候) wait_up(&my_wait) 用于唤醒该队列还有其它函数:wait_event_interruptible(queue,conditon) 可以中断唤醒15 轮询操作 上层select 下层pol

21、l上层应用使用select 下层内核调用poll,实现非阻塞方式,上层通过select访问内核子系统(内核有专门的子系统负责这功能),下次值需要将连个队列注册,则一旦有变化将mask放回内核子系统,再有内核子系统返回给上层的select函数fd。unsigned int(*poll) (struct file *filp, struct poll_table *table);主要结构:static unsigned int globalfifo_poll(struct file *filp, poll_table *wait) unsigned int mask = 0; struct glo

22、balfifo_dev *dev = filp-private_data; /*获得设备结构体指针*/ down(&dev-sem); poll_wait(filp, &dev-r_wait, wait); poll_wait(filp, &dev-w_wait, wait); /*fifo非空*/ if (dev-current_len != 0) mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/ /*fifo非满*/ if (dev-current_len != GLOBALFIFO_SIZE) mask |= POLLOUT | POLLWRNORM; /

23、*标示数据可写入*/ up(&dev-sem); return mask;16 信号处理16.1 上层应用:#if 1 fcntl(fd, F_SETOWN, getpid(); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); 即调用下层的globalfifo_fasync#endif#if 0 fcntl(0, F_SETOWN, getpid(); oflags = fcntl(0, F_GETFL);fcntl(0, F_SETFL, oflags | FASYNC);16.2 下层:kill_fasyn

24、c(&dev-async_queue, SIGIO, POLL_IN);static int globalfifo_fasync(int fd, struct file *filp, int mode) struct globalfifo_dev *dev = filp-private_data; return fasync_helper(fd, filp, mode, &dev-async_queue);17 中断17.1 中断简介中断与内核调度的重要方式,中断可以抢cpu。内核对每一个中断源,都使用struct irqdesc irq_desc结构体来描述,每一个SOC芯片,都有很多的中断

25、源,内核通过一个叫做NR_IRQS的宏定义这些中断源的数量struct irqdesc irq_descNR_IRQS这个中断源不等于芯片厂商直接提供的中断源数量S5PC100:在内核中arch/arm/mach-s5pc100/include/mach/irqs.h这个文件描述了该SOC芯片在内核的中断子系统的源数量17.2 中断程序编写步骤和方式17.2.1 查找硬件你产生中断的引脚由原理图找出key1在ENIT1上,然后找到该引脚对应的中断号,17.2.2 申请irq request_irq(unsigned int irq, irq_handler_t handler, unsigne

26、d long flags, const char *name, void *dev) 使用这个函数即可向内核子系统申请irq,注意参数的填写。分别是。irq号,irq处理函数,irq标志(有宏定义),可以看见的名称,传给处理函数的参数注意:中断号不是我们查得的中断号,是系统通过一种线性映射维护的一张中断向量表,我们去差,在内核源代码中:/work/linux_kernel_copy/arch/arm/mach-s5pc100/include/mach/irqs.h (没有) /work/linux_kernel_copy/arch/arm/plat-s5p/include/plat/irqs.

27、h (找到)rootubuntu:/work/linux_kernel_copy/include/linux# vi interrupt.hirq_eint(1)表示 外部中断号1 17.2.3 编写中断处理函数static irqreturn_t my_irq(int irq, void * my_arg) printk(irq_yes.=n); return IRQ_HANDLED;17.2.4 exit的时候释放中断free_irq(IRQ_EINT(1),NULL);17.3 具体编程实现截图1) 注册,在init里面 注意:flages2) 释放,在exit里面3) 中断服务程序 注意返回值,*此时中断即可完成,但中断里面不能执行阻塞和循环锁,时间不能太长,所以中断有几种处理机制*见书:19718 中断新模型-上半部中断和下半部中断的实现1, 软中断和tasklet 是中断处理机制,只是比紧急中断慢一点执行。2, 工作队列 是将剩余的处理函数压入进程,与进程同级别受cpu调度。

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1