国嵌驱动笔记.docx

上传人:b****6 文档编号:7623376 上传时间:2023-01-25 格式:DOCX 页数:16 大小:26.46KB
下载 相关 举报
国嵌驱动笔记.docx_第1页
第1页 / 共16页
国嵌驱动笔记.docx_第2页
第2页 / 共16页
国嵌驱动笔记.docx_第3页
第3页 / 共16页
国嵌驱动笔记.docx_第4页
第4页 / 共16页
国嵌驱动笔记.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

国嵌驱动笔记.docx

《国嵌驱动笔记.docx》由会员分享,可在线阅读,更多相关《国嵌驱动笔记.docx(16页珍藏版)》请在冰豆网上搜索。

国嵌驱动笔记.docx

国嵌驱动笔记

知识结构:

1.Linux驱动程序设计模式(40%)2.内核相关知识(30%)3.硬件相关知识(30%)

驱动分类:

字符,网络,块

字符设备:

以字节为最小单位

块设备,一次传送一个整体数据(512字节),Linux可以以字节访问块设备(仅仅是驱动与内核的接口不同,访问的顺序的不同(字符只可顺序访问,块驱动可随机访问))

网络接口:

硬件(eth0),纯软件(lo)

驱动的安装:

模块,编译进内核(Linux启动的时候会自动加载init段)

使用驱动程序:

字符设备文件—〉字符设备驱动—〉字符设备

文件系统—〉块设备文件—〉块设备驱动—〉块设备

套接字—〉协议栈—〉网络设备驱动—网络接口设备

主设备号用来标示与设备文件相连的驱动程序,次编号被驱动程序用来辨别操作哪个设备

主设备号反映设备类型,此设备号区分同类型的设备

dev_t高12位为主设备号,低20位为次设备号

MAJOR(dev_tdev)从dev_t分解出主设备号

MINOR(dev_tdev)从dev_t分解出此设备号

MKDEV(major,minor)构造设备号;

静态申请:

1.根据Documentation/devices.txt驱动没有使用的主设备号2.使用register_chrdev_region(dev_tform,unsignedcount,constchar*name)函数注册(容易冲突)

From希望使用的设备号,count希望申请使用设备号的数目,name设备名(体现在/proc/devices)

2.动态分配alloc_chardev_region(安装驱动前无法创建设备文件)创建设备文件后,通过/proc/devices察看

alloc_chardev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name)

dev分配的设配号,baseminor起始的此设备号,count要分配的设备数目,name设备名

主要设备号:

unregister_chrdev_region(dev_tdev,unsignedbaseminor)

Mknodfilenametypemajorminortype是字符或块mknodserial0c1000

Linux字符设备驱动3个重要数据结构

Structfile每打开一次都有一个关联的structfile重要结构loff_tf_pos文件读写位置structfile_operations*f_op

Structinode记录文件的物理上的信息(设备号等),一个文件可以有多个file,但只有一个inode

Structfile_operation*f_op函数指针的集合

structfile_operationsmem_fops={

.owner=THIS_MODULE,

.llseek=mem_seek,

.read=mem_read

…….

}

读内核代码应用程序怎样访问驱动程序(read_write,c)

系统条用read找到vfs_read根据file结构中找到file_operations中的read

字符设备使用structcdev来描述

字符设备注册可分为如下3个部分:

1.分配cdevstructcdev*cdev_alloc(void)

2.初始化cdevcdev_init(structcedev*p,conststructfile_operations*fops)

3.添加cdevcdev_add(structcdev*p,dev_tdev,unsignedcount)dev设备号设备号的数目

字符设备的注销:

cdev_dev(structcdev*p)

设备操作

int(*open)(struct*inode,structfile*)

如果该项为NULL,设备打开永远成功

void(*release)(struct*inode,structfile*)

ssize_t(*read)(structfile*,char__user*buff,size_t,loff_t*)

ssize_t(*write)(structfile*,char__user*,size_t,loff_t*)

file是文件指针(来与内核),*buff是数据缓冲(用户空间),count传输的数据量(用户空间),offp访问位置(来与内核)

*buff是用户空间的,不能直接使用

intcopy_from_user(void*to,constvoid__user*from,intn)

intcopy_to_user(void__user*to,constvoid*to,intn)

loff_tllseek(struct*file,loff_toffset,intwhence)

open方法主要完成如下工作:

1.初始化设备2.标明此设备号

在open(structinode*inode,structfile*filp)函数可使用MINOR(inode->i_rdev;获取此设备号

filp->private_data=dev;将设备描述指针赋值给私有文件指针,区分出了那种设备

在read()函数使用structmem_dev*dev=filp->private_data可根据私有文件指针指定找到具体的设备,read函数参数没有inode,无法获取此设备号。

kmalloc分配内存,返回地址,根据其返回的地址就可操作内存中的数据

copy_to_user(buf,(void*)(dev->data+p),count)这里的(dev->data+p)为什么要用(void*)强制转换呢?

copy_to_user是这么定义的intcopy_to_user(void__user*to,constvoid*to,intn),但还是不理解。

驱动程序调试分类:

打印调试,调试器调试(kgdb),查询调试(proc文件系统)

合理的使用printk可以全局的打开或关闭它们。

并发:

多个执行单元同时被执行

竟态:

并发的执行单元对共享资源(硬件资源或全局变量等)的共享访问

通过semaphore机制和spin_lock机制实现

获取信号量不成功该阻塞或者睡眠

1.定义信号量structsemaphoresem;

2.初始化信号量voidsema_init(structsemaphore*sem,intval)初始化信号量的初值为val

3.voidinit_MUTEX(structsemaphore*sem)初始化一个互斥锁,把sem的值设为1

4.voidinit_MUTEX_LOCKED(structsemaphore*sem)初始化一个互斥锁,把sem的值设为0

定义与初始化工作可由如下宏一步完成

DECLARE_MUTEX(name)定义一个信号量,并初始化为1

DECLARE_MUTEX_LOCK(name)定义一个信号量,并初始化为0,为已锁状态

5.获取信号量voiddown(structsemaphore*sem)可能会导致进程睡眠,故不能在中断上下文中使用,如果sem非负直接返回,否则挂起(TASK_UNINTERRUPTIBLE),不建议使用

6.voiddown_interrruptible(structsemaphore*sem)信号量不可用,置为TASK_INTERRUPTIBLE

7.voiddown_killable(structsemaphore*sem)信号量不可用,置为TASK_KILLABLE

8.voidup(structsemaphore*sem)释放信号量

自旋锁不会引起调用者的睡眠,线程会移植忙循环,移植等待下去

1.spin_lock_init(x)初始化自旋锁

2.spin_lock(lock)获取自旋锁,不成功自旋在那

3.spin_trylock(lock)不会一直等待

4.spin_unlock

信号量可以有多个持有者(1个互斥信号量),自旋锁只有一个持有者

信号量适合保持时间较长,自旋锁适合保持时间较短

Ioctl对硬件进行控制(改变波特率,报告错误信息)

用户使用方法:

intioctl(intfd,unsignedlongcmd,…)点表示可选参数

int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg)

cmd用户空间传下来的,arg用户传下来的参数

ioctl命令实现方法:

1.定义命令2.实现命令

Documentation/ioctl-number.txt定义了使用的幻数

ioctl被划分为几个位段,include/asm/ioctl.h定义了这些字段:

1.类型(幻数):

8位宽,属于哪一类设备

2.序号:

表明设备命令的第几个

3.传送方向:

可能的值是_IOC_NONE,_IOC_READ,_IOC_WRITE是从应用程序的观点来看的

4.参数的大小(数据的类型)

内核提供下列宏来帮助定义命令

_IO(type,nr)没有参数传递

_IOR(type,nr,datatype)从驱动中读数据

_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_user

intaccess_ok(inttype,constvoid*addr,unsingnedlongsize)

第一参数是VERIFY_READ或者VERIFY_WRITE,addr是要操作的用户内存的地址,size是操作的长度。

access_ok返回一个布尔值:

1.存取没问题0.失败,如果返回失败,

则ioctl应当返回-EFAULT.

等待队列:

实现进程的阻塞,保存进程的容器,阻塞时放入等待队列,唤醒时,取出进程

1.定义等待队列wait_queue_head_tmy_queue

2.初始化等待队列wait_waitqueue_head(&my_queue)

3.定义并初始化等待队列DECLARE_WAIT_QUEUE_HEAD(my_queue)

有条件睡眠

wait_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*q)唤醒等待队列中的所有进程都唤醒

Wake_up_interruptible(wait_queue_t*q)唤醒为TASK_INTERRUPTIBLE进程

阻塞方式是文件读写的默认方式,应用程序可以使用O_NONBLOCK标志非阻塞的

设置了O_NONBLOCK,系统只是简单的返回-EAGAIN

Select系统调用对应于poll

Select用于多路监控,如没有一个文件满足要求,select将阻塞进程

Intselect(intmaxfd,fd_set*reedfds,fd_set*writefds,fd_set*exceptfds,conststructtimeval*timeout)

maxfd:

文件描述符的范围,比检测的最大文件描述符大1

Readfds:

被读监控的文件描述符

Writefds:

被写监控的

Exceptfds:

被异常监控的

Timeout:

定时器

Timeout

1.为0时不管有没有文件满足要求,立即返回,无文件满足,返回0

2.为NULL时,select将阻塞进程,直到文件满足要求为止

3.为正整数的时候,等待的最长时间,即select在timeout时间内阻塞进程

Select返回值

1.正常返回满足要求的文件描述符个数

2.没有满足的返回0

3.select被某个信号打断,返回-1,errno为EINTR

Select系统调用:

1.将要监控的文件添加到文件描述符集

2.调用select开始监控

3.判断文件是否满足要求

VoidFD_SET(intfd,fd_set*fdset)将fd添加到fdset中

VoidFD_CLR(intfd,fd_set*fdset)在fdset中清楚fd

VoidFD_ZERO(fd_set*fdset)清空fdset

VoidFD_ISSET(intfd,fd_set*fdset)检测文件描述集中的需判断的fd发生变化

驱动通常由poll实现

Unsignedint(*poll)(structfile*filp,poll_table*wait)

负责完成:

1.使用poll_wait将等待队列添加到poll_table中

2.返回描述设备是否可读可写的掩码

位掩码:

POLLIN设备可读,POLLRDNORM数据可读,POLLOUT设备可写,POLLWRNORM数据可写

设备可读通常返回(POLLIN|POLLRDNORM)

设备可写通常返回(POLLOUT|POLLWRNORM)

Poll方法只是做一个登记,真正的阻塞发生在select.c中的do_select函数(分析了do_select函数)

自动创建设备文件

2.4内核使用devfs_register(devfs_handle_tdir,constchar*name,unsignedintflags,unsignedintmajorunsignedintminor,umode_tmode,void*ops,void*info)

Dir:

目录名,name:

文件名;flags:

创建标志;major:

主设备号;minor此设备号;mode:

创建模式;ops:

操作函数集;info:

通常为空

从2.6.13开始,devfs不复存在,udev成为替代

使用

1.class_create为设备创建一个class,

2.使用device_create创建对应的设备

例:

structclass*myclass=class_create(THIS_MODULE,“my_device_driver”);

Device_create(myclass,NULL,MKDEV(major_num,0),NULL,“my_device”)

void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset)

负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。

addr:

映射的起始地址,通常为NULL,由系统指定

length:

映射文件的长度

prot:

映射区的保护方式PROT_EXEC:

映射区可被执行,PROT_READ:

映射区可被读取,PROT_WRITE:

映射区可被写入

flags:

映射区的特性MAP_SHARED:

写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享。

MAP_PRIVATE:

对映射区的写入操作会产生一个映射区的复制(copy-on-write),对此区域的修改不会写回源文件。

fd:

由open返回的文件描述符,代表要映射的文件

offset:

以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射。

注意mmap不能映像原有文件的长度

intmunmap(void*start,size_tlength)

成功返回0,失败返回-1.start的取值一般是mmap返回的地址

虚拟内存区域:

是虚拟地址空间的一个同质区间,即有同样特性的连续地址范围。

一个进程的内存映像由以下几部分组成:

程序代码,数据,BSS和栈区域,以及内存映射区域

一个进程的内存区域可以通过查看/proc/pid/maps

每一行的域为:

start_endpermoffsetmajor:

minorinode

linux内核使用结构vm_area_struct来描述虚拟内存区域,其中主要成员如下:

unsignedlongvm_start虚拟内存区域起始地址

unsignedlongvm_end虚拟内存区域结束地址

unsignedlongvm_flags

映射一个设备是指把用户空间的一段地址关联到设备内存上。

mmap做了三件事:

1.找到用户空间地址(内核完成)2.找到设备的物理地址(原理图)3.关联(通过页式管理)

mmap设备方法需要做的就是建立虚拟地址到物理地址的页表。

int(*mmap)(structfile*,structvm_area_struct*)

建立页表有两种方法:

1.使用remap_pfn_range一次建立所有页表;

2.使用nopageVMA方法每次建立一个也表。

intremap_pfn_range(structvm_area_struct*vma,unsignedlongaddr,unsignedlongpfn,unsignedlongsize,pgprot_tprot)

vma:

虚拟内存区域指针virt_addr:

虚拟地址的起始值pfn:

要映射的物理地址所在的物理页祯号,可将物理地址>>PAGE_SHIFT得到(PAGE_SHIFT为12,相当于除以4KB)

size:

要映射的区域的大小prot:

VMA的保护属性

硬件访问

寄存器和内存的区别:

寄存器和RAM主要不同在于寄存器操作由副作用(side

effect或边际效果):

读取某刻地址可能导致该地址内容变化,读中断状态寄存器,便自动清零

内存与IO

在X86存在IO空间(串口并口等),在32为x86

IO空间是64K,内存空间是4G,ARM,PowerPC只有内存地址空间的

IO端口:

一个寄存器或内存位于IO空间时,称为IO端口

IO内存:

一个寄存器或内存位于内存空间时,称为IO内存

对IO端口的操作需要按如下步骤完成:

1,申请2,访问3,释放

申请:

structresource*request_region(unsignedlongfirst,unsignedlongn,

constchar*name)从first开始的n个端口,name设备名字

系统中端口的分配情况记录在/proc/ioports中

访问:

intboutbintwoutwintloutl

释放:

voidrelease_region(unsignedlongstart,unsignedlongn)

IO内存有4步:

1.申请2,映射3,访问4,释放

申请:

structresource*request_mem_region(unsignedlongstart,unsignedlong

len,char

*name)从start开始,长度为len字节的内存区。

成功,返回非NULL,否则返回NULL。

可在/proc/iomem中列出

访问:

在访问IO内存之前,必须进行物理地址到虚拟地址的转化,使用下面函数

void*ioremap(unsignedlongphys_addr,unsignedlongsize)

访问:

ioread8(void*addr)iowrite8(u8value,void*addr)老版本使用readbwriteb

释放:

1,voidiounmap(void*addr)2,voidrelease_mem_region(unsignedlong

start,unsignedlonglen)

混杂设备驱动:

共享一个主设备号10,成为混杂设备

Linux内核使用structmiscdevice描述混杂设备

structmiscdevice{

intminor;

constchar*name;

conststructfile_operations*fops;

structlist_headlist;

structdevice*parent;

structdevice*this_device;

}

使用misc_register函数来注册一个混杂设备驱动misc_register(structmiscdevice*misc)

使用上拉下拉避免悬浮

Linux总线设备驱动模型(2.6内核难点)

Sysfs文件系统(基于内存,展示内核数据结构属性,关系),与proc同类别的文件系统同类别的文件系统,sysfs把连接在系统上的设备和总线组织成分级的文件,使其从用户空间可以访问到

sysfs在/sys/目录下

block目录:

块设备信息bus:

总线(idepciscsisb

pcmcia)里边还有devices和drivers目录,devices目录下都是软链接

class目录:

按照功能进行分类(网络)

devices:

包含系统所有的设备kernel:

内核中的配置参数Module:

系统中所有模块信息

firmware:

系统中的固件fs:

描述系统中的文件系统power:

系统电源选项

Kobject实现了基本的面向对象管理机制,与sysfs文件系统紧密相连,在内核中注册的每个kobject对象对应sysfs文件系统中的一个目录(作用:

在sys下创建一个目录)类似于C++中的基类。

voidkobject_init(structkobject*kobj)初始化kobject结构

intkobject_add(strutkobject*kobj)将kobject对象注册到Linux系统

intkobject_init_and_add(stru

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 调查报告

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

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