_Linux杂项驱动.ppt

上传人:文库蛋蛋多 文档编号:2602732 上传时间:2023-02-20 格式:PPT 页数:57 大小:343KB
返回 下载 相关 举报
_Linux杂项驱动.ppt_第1页
第1页 / 共57页
_Linux杂项驱动.ppt_第2页
第2页 / 共57页
_Linux杂项驱动.ppt_第3页
第3页 / 共57页
_Linux杂项驱动.ppt_第4页
第4页 / 共57页
_Linux杂项驱动.ppt_第5页
第5页 / 共57页
点击查看更多>>
资源描述

《_Linux杂项驱动.ppt》由会员分享,可在线阅读,更多相关《_Linux杂项驱动.ppt(57页珍藏版)》请在三一办公上搜索。

1、杂项(misc)驱动,设备结点,应用程序控制驱动的链接ttyS0/ttyUSB0设备结点创建Mknod驱动自动创建,ls-l/dev/ttySAC*crw-rw-rw-1 root tty 204,64 Mar 20 09:39/dev/ttySAC0crw-rw-rw-1 root tty 204,65 Mar 20 2000/dev/ttySAC1crw-rw-rw-1 root tty 204,66 Mar 20 2000/dev/ttySAC2crw-rw-rw-1 root tty 204,67 Mar 20 2000/dev/ttySAC3,杂项(misc)驱动,传统的驱动分为字符

2、设备和块设备.字符设备硬件收到数据直接发送给应用程序,而块设备带了一个缓冲。杂项设备可以看字符设备驱动的特例。杂项一般开小硬件的模块的驱动.杂项指主设备统一为10,不同硬件用从设备区别的。杂项设备的编程接口相对是最为简单。,ls-l/dev/|grep 10crw-rw-1 root root 10,250 Mar 20 2000 s3c-cmmcrw-rw-1 root root 10,220 Mar 20 2000 s3c-g2dcrw-rw-1 root root 10,249 Mar 20 2000 s3c-g3dcrw-rw-1 root root 10,254 Mar 20 200

3、0 s3c-jpgcrw-rw-1 root root 10,252 Mar 20 2000 s3c-mfccrw-rw-1 root root 10,253 Mar 20 2000 s3c-ppcrw-rw-1 root root 10,230 Mar 20 2000 s3c-rotator,驱动与应用程序接口,驱动要全部或者部分实现下面接口,当应用程序执行调用时,内核会调驱动相应的接口函数。open readwritecloseIoctllseekmmap,struct file_operations struct module*owner;loff_t(*llseek)(struct f

4、ile*,loff_t,int);ssize_t(*read)(struct file*,char _user*,size_t,loff_t*);ssize_t(*write)(struct file*,const char _user*,size_t,loff_t*);ssize_t(*aio_read)(struct kiocb*,const struct iovec*,unsigned long,loff_t);ssize_t(*aio_write)(struct kiocb*,const struct iovec*,unsigned long,loff_t);int(*readdir)

5、(struct file*,void*,filldir_t);unsigned int(*poll)(struct file*,struct poll_table_struct*);int(*ioctl)(struct inode*,struct file*,unsigned int,unsigned long);long(*unlocked_ioctl)(struct file*,unsigned int,unsigned long);long(*compat_ioctl)(struct file*,unsigned int,unsigned long);int(*mmap)(struct

6、file*,struct vm_area_struct*);int(*open)(struct inode*,struct file*);int(*flush)(struct file*,fl_owner_t id);int(*release)(struct inode*,struct file*);int(*fsync)(struct file*,struct dentry*,int datasync);int(*aio_fsync)(struct kiocb*,int datasync);int(*fasync)(int,struct file*,int);int(*lock)(struc

7、t file*,int,struct file_lock*);ssize_t(*sendpage)(struct file*,struct page*,int,size_t,loff_t*,int);unsigned long(*get_unmapped_area)(struct file*,unsigned long,unsigned long,unsigned long,unsigned long);int(*check_flags)(int);int(*dir_notify)(struct file*filp,unsigned long arg);int(*flock)(struct f

8、ile*,int,struct file_lock*);ssize_t(*splice_write)(struct pipe_inode_info*,struct file*,loff_t*,size_t,unsigned int);ssize_t(*splice_read)(struct file*,loff_t*,struct pipe_inode_info*,size_t,unsigned int);int(*setlease)(struct file*,long,struct file_lock*);,无杂项设备还是字符设备必须要定义一个file_operations,并且把自己对应处

9、理函数放入相应的成员。比如open处理函数放open成员close处理函数放入release成员file_operations可以部分实现,关于file_operations调用,struct file*对于应用程序 int fd;当应用程序执行open().它会在系统调用的机制在内核调用do_sys_open().由它创建struct file*并且返回fd给应用程序。在它最后一步,如果驱动实现 file_operations.open.它也将调这个函数指针,SYSCALL_DEFINE3(open,const char _user*,filename,int,flags,int,mode)

10、long ret;if(force_o_largefile()flags|=O_LARGEFILE;ret=do_sys_open(AT_FDCWD,filename,flags,mode);/*avoid REGPARM breakage on x86:*/asmlinkage_protect(3,ret,filename,flags,mode);return ret;,关open参数的含义,int leds_open(struct inode*node,struct file*filp)来源于inode=dentry-d_inode;struct dentry是该文件在内存的文件树的一个描

11、述该文件结点dentry又是由文件系统的nd-path.dentry来初始化 nd-path.dentry 又是由 path-dentry;(path_to_nameidata)error=do_path_lookup(dfd,pathname,LOOKUP_PARENT,/path.dentry设定,当应用程序调用close,在最开始调用file_operations.release接口。,leds驱动的write接口,假设向驱动写入一个整数,leds根据整数来亮灭.比如0 x9=(1001)2 表示第1灯亮,第2,3灭,第4灯亮.,Leds_write实现,static struct fi

12、le_operations leds_oper=.owner=THIS_MODULE,/标准写法,表示打开这个文件的模块是谁.write=leds_write,;,static ssize_t leds_write(struct file*filp,const char _user*buf,size_t buf_len,loff_t*offset)unsigned int val;if(buf_len!=4)return-1;val=*(unsigned int*)buf);printk(KERN_DEBUG leds_write:%dn,val);leds(val);return buf_l

13、en;,file_operations注册,杂项使用如下定义注册,struct miscdevice int minor;/杂项设备从设备号,主设备号固定为10const char*name;/杂项设备名字,同时也是设备结点文件名const struct file_operations*fops;/文件操作struct list_head list;struct device*parent;struct device*this_device;extern int misc_register(struct miscdevice*misc);extern int misc_deregister(s

14、truct miscdevice*misc);,杂项设备,static struct miscdevice leds_misc=.minor=LEDS_MINOR,.name=hxyled,.fops=,static int _init mymod_init(void)int i;printk(KERN_INFOhello,this is my first leds drivern);misc_register(,应用程序代码,arm-linux-gcc test/test_leds.c-o test_leds,#define DEVICE_NAME/dev/hxyledint main()u

15、nsigned int i,val;int fd=open(DEVICE_NAME,O_RDWR);if(fd=-1)printf(open file%s failuren,DEVICE_NAME);return-1;for(i=0;i100;i+)val=0 x9;printf(test_leds:write%dn,val);write(fd,(void*),驱动测试,1。装入驱动 insmod mymod.ko2.如果udev工作正常,则会自动创建/dev/hxyled,如果没有 mknod/dev/hxyled c 10 220还有一种情况是从设备号与已安装的驱动冲突,也不会创建设备结点

16、3.测试leds,./test_leds4.移载驱动 rmmod mymod,Open流程,1.应用程序执行 open()int open(const char*pathname,int flags,mode_t mode);2.内核会切换到SWI状态,执行do_sys_open()long do_sys_open(int dfd,const char _user*filename,int flags,int mode)3.在do_sys_open()执行 do_filp_open()做真正的打开动作4.执行驱动的open()nameidata_to_filp()-_dentry_open()

17、从中取得f_ops的open指针,执行驱动的open代码,/fs/open.c:_dentry_open f-f_op=fops_get(inode-i_fop);file_move(f,open测试,rooturbetter 06#insmod mymod.kohello,this is my first leds driverrooturbetter 06#./test_ledstest_leds:write 9test_leds:write 6test_leds:write 9test_leds:write 6test_leds:write 9test_leds:write 6test_

18、leds:write 9Crooturbetter 06#dmesg|tailhello,this is my first leds driver/-mymod_init leds_open/-leds_open leds_write:9 leds_write:6 leds_write:9 leds_write:6 leds_write:9 leds_write:6 leds_write:9 leds_close/-leds_close,Module_init与file_operation.open的有什么区别?Module_init的是在安装时执行,是全局性的初始化。file_operati

19、on.open是每次打开一个文件实例的初始化,关于_user,_kernel,在LINUX每一个用户进程有4G的虚拟空间。_user就是应用程序里空间的地址。在驱动里操作是内核空间。,驱动,用户进程,Test_leds.c,val;,write,buf,Mymod.c,驱动的段错误,如果驱动直接使用用户空间的地址,如果不加检查,用户空间地址将会造成驱动的段错误,有时甚至破坏内核.write(fd,(void*)0 x123,4);/?-写入一个非法地址,如何防范非法地址?,内核规定,如果与_user空间交换数据,必须要 copy_from_user()和copy_to_user()copy_f

20、rom_user从user空间拷贝数据static inline unsigned long _must_check copy_from_user(void*to,const void _user*from,unsigned long n)copy_to_user 向user空间拷贝数据.这两个函数都首先调用access_ok来对内存做检查,如果合法才做真正的拷贝动作。,内存安全的写法,static ssize_t leds_write(struct file*filp,const char _user*buf,size_t buf_len,loff_t*offset)unsigned int

21、 val;if(buf_len!=4)return-1;/val=*(unsigned int*)buf);/非法地址会造成段错误 copy_from_user(void*),关于_must_check,在内核里如果由_must_check修饰的函数,它的返回值必须要检测,否则编译器为是错误比如直接写copy_from_user(warning:ignoring return value of copy_from_user,declared with attribute warn_unused_result必须写成,if(copy_from_user(,Ioctl的用法,Ioctl-io co

22、ntrol 的缩写.write/read一般用数据交换,ioctl用作控制功能。控制功能完成由驱动自定义int ioctl(int d,int request,.);int ioctl(int d,int cmd,int args);/更常用定义cmd命令序号args命令的参数,可以是指针cmd/args的含义完全驱动定义,在执行ioctl,内核两个参数交给驱动,fs.ioctl,int(*ioctl)(struct inode*node,struct file*filp,unsigned int cmd,unsigned long arg);,int leds_ioctl(struct in

23、ode*node,struct file*flip,unsigned int cmd,unsigned long arg)switch(cmd)case LED_CMD_ON:/args是灯的序号printk(leds_ioctl:led on,index%dn,(int)arg);led(arg,1);break;case LED_CMD_OFF:/args是灯的序号printk(leds_ioctl:led off,index%dn,(int)arg);led(arg,0);break;default:printk(leds_ioctl:unknow cmd%dn,cmd);return-

24、1;break;return 0;,ioctl的测试,rooturbetter 08#insmod mymod.kohello,this is my first leds driverrooturbetter 08#./test_ledstest leds 34520 leds_openleds_ioctl:led on,index 1leds_ioctl:led off,index 1leds_ioctl:led on,index 2leds_ioctl:led off,index 2leds_ioctl:led on,index 3leds_ioctl:led off,index 3led

25、s_ioctl:led on,index 4leds_ioctl:led off,index 4leds_ioctl:led on,index 1leds_ioctl:led off,index 1leds_ioctl:led on,index 2leds_ioctl:led off,index 2leds_ioctl:led on,index 3leds_ioctl:led off,index 3leds_ioctl:led on,index 4,Ioctl的地址安全,Ioctl的arg可以指针,这样同样会非法地址问题。这里不能使用copy_from_user,因这里没有内存长度。Ioctl

26、使用put_user(x,p)/get_user(x,p)来做拷贝。put_user(x,p)把内存里x值,放入user内存p中get_user(x,p)把数据p中对应值放入x当中get_user(val,(unsigned int _user*)arg),case LED_CMD_MULTI:/实现同write一样功能 unsigned long val;if(get_user(val,(unsigned int _user*)arg)printk(leds_ioctl:error arg%pn,(void*)arg);return-1;printk(leds_ioctl:led multi

27、,index%dn,(int)val);leds(val);break;,非法地址测试,rooturbetter 08#insmod mymod.kohello,this is my first leds driverrooturbetter 08#./test_ledstest leds3 34916 leds_openleds_ioctl:error arg 00000123leds_ioctl:error arg 00000234leds_ioctl:error arg 00000123leds_ioctl:error arg 00000234leds_ioctl:error arg 0

28、0000123leds_ioctl:error arg 00000234leds_ioctl:error arg 00000123leds_ioctl:error arg 00000234leds_ioctl:error arg 00000123leds_ioctl:error arg 00000234,多个C编译成一个模块,如果在Makefile obj-m:=lib.o mymod.o这样会有什么后果?它编译成二个*.ko,rootlocalhost 09#makemake-C/home/hxy/ut6410/linux-2.6.28_smdk6410 M=/home/hxy/ut6410

29、/rootfs/test/01_mod/09 modulesmake1:Entering directory/home/hxy/ut6410/linux-2.6.28_smdk6410 CC M/home/hxy/ut6410/rootfs/test/01_mod/09/lib.o CC M/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.o/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.c:In function leds_write:/home/hxy/ut6410/rootfs/test/01_mod/09

30、/mymod.c:120:warning:ignoring return value of copy_from_user,declared with attribute warn_unused_result Building modules,stage 2.MODPOST 2 modulesWARNING:my_add/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.ko undefined!CC/home/hxy/ut6410/rootfs/test/01_mod/09/lib.mod.o LD M/home/hxy/ut6410/rootfs/tes

31、t/01_mod/09/lib.ko CC/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.mod.o LD M/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.komake1:Leaving directory/home/hxy/ut6410/linux-2.6.28_smdk6410,多个C编译成一个模块,obj-m:=.o-objs:=,obj-m:=mymod2.omymod2-objs:=lib.o mymod.o,rootlocalhost 09#makemake-C/home/hxy/ut6410/l

32、inux-2.6.28_smdk6410 M=/home/hxy/ut6410/rootfs/test/01_mod/09 modulesmake1:Entering directory/home/hxy/ut6410/linux-2.6.28_smdk6410 CC M/home/hxy/ut6410/rootfs/test/01_mod/09/lib.o CC M/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.o/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.c:In function leds_write

33、:/home/hxy/ut6410/rootfs/test/01_mod/09/mymod.c:120:warning:ignoring return value of copy_from_user,declared with attribute warn_unused_result LD M/home/hxy/ut6410/rootfs/test/01_mod/09/mymod2.o Building modules,stage 2.MODPOST 1 modules CC/home/hxy/ut6410/rootfs/test/01_mod/09/mymod2.mod.o LD M/hom

34、e/hxy/ut6410/rootfs/test/01_mod/09/mymod2.komake1:Leaving directory/home/hxy/ut6410/linux-2.6.28_smdk6410,在模块之间调用函数,如果一个模块需要调用另外一模块的函数。要怎么做?需要把调用函数用export_symbol来声明一下比方我想使用pwm-s3c6410.c定时器。EXPORT_SYMBOL(s3c6410_timer_setup);可以理解里EXPORT_SYMBOL在声明内核中使用的全局函数,如何写标准的调试函数,一般不要直接调用printk.否则正式运行模块也会带大量输出。这样

35、会降低模块运行速度。一般是定义成一个调试宏需要调试信息时,打开这个宏定义#define MYMOD_DEBUG关闭调试信息,简单#undef MYMOD_DEBUG即可,#ifdef MYMOD_DEBUG#define MYMOD_PRINT printk#else#define MYMOD_PRINT(.)/GNU GCC里.表示变长参数#endif,宏里变长参数写法,#define dprintk(args.)dfprintk(FACILITY,#args)args 表示变长参数,#args表示把变长参数,整个连接到表达式。,内核链表,在内核中大量采用双向链表.要实现双向链表要解决如下问

36、题:1.头尾指针如何表示?2.如何把结点中数据与指针域无关?3.链表基本操作(插入,删除)4.链表的遍历5.链表的结点空间6.链表的并发访问/加锁,双向表结点,一般要有两个指针域 next,prev,struct node struct node*prev;struct node*next;,数据域与指针域,一般双向链表解决数据域与指针域无关,是进行如下设计,把固定node放在最开始,只要向后偏移sizeof(struct node)即可得到数据域 在操作链表时把具体结点(int_node)再强制转换成struct node;这样链表函数只关struct node*,struct node s

37、truct node*prev;struct node*next;strcut int_node struct node node;int data;,基本操作/结点空间,插入/删除操作,修改next/prev指针。不牵涉到空间分配.结点空间一般用malloc来分配空间。,void move_all_node(FUNC_FN handler)struct node*p,*list;for(p=list;p!=NULL;p=p-next)handler(p);,链表的并发访问,一般与链表如果不做保护,一定会有并发访问问题。如多线程不加锁,往往链表出现问题。应用程序使用操作系统的加锁机制,比如线程

38、锁,信号量来进行保护。,内核链表,定义在 linux/list.h.全部的定义和实现在这一个头文件里。内核核表的结点,一个链表以头结点表示。头结点只是头尾指示,没有数据,struct list_head struct list_head*next,*prev;,内核链表操作,链表的静态初始化,#define LIST_HEAD_INIT(name)&(name),&(name)#define LIST_HEAD(name)struct list_head name LIST_HEAD_INIT(name),struct list_head mylist,链表动态初始化。用于链表本身也是动态分配

39、的情况,static inline void INIT_LIST_HEAD(struct list_head*list)list-next=list;list-prev=list;,基本操作,_list_add是通用插入算法注意在运算中,head的地址不变的已经next-next原来prev,现在,static inline void _list_add(struct list_head*new,struct list_head*prev,struct list_head*next)next-prev=new;new-next=next;new-prev=prev;prev-next=new;

40、,假设head=next,tail=prev,new-next=next;,next-prev=new;,new-prev=prev;,prev-next=new;,static inline void _list_add(struct list_head*new,struct list_head*prev,struct list_head*next)next-prev=new;new-next=next;new-prev=prev;prev-next=new;static inline void list_add(struct list_head*new,struct list_head*h

41、ead)_list_add(new,head,head-next);static inline void list_add_tail(struct list_head*new,struct list_head*head)_list_add(new,head-prev,head);,如何把结点中数据与指针域无关?,内核核表做得非常灵活,只要结构里包含一个struct list_head 成员即可。它与链表的遍历高度相关,链表结点首地址,#define list_entry(ptr,type,member)container_of(ptr,type,member),node,struct list

42、_head member,container_of(ptr,type,member),ptr,type,offset_of,container_of已经结构成员地址,反向求总结构地址.内核链表用list_entry来使用数据域的指针域分离了。,#ifndef offset_of#define offset_of(type,memb)(unsigned long)(&(type*)0)-memb)#endif#ifndef container_of#define container_of(obj,type,memb)(type*)(char*)obj)-offset_of(type,memb)#

43、endif,链表的循历,使用一个只有循环语句,但是没有循环体的for宏来实现循历和数据分离。当实现循历时,必须完整实现循环体,#define list_for_each(pos,head)for(pos=(head)-next;prefetch(pos-next),pos!=(head);pos=pos-next),循环的实例,_list_for_each(pos,struct atm_dev*dev;struct list_head*p;list_for_each(p,链表结点分配,应用程序使用malloc/kfree,内核将使kmalloc/kfree从内核分配一个块kmalloc(size,GFP_KERNEL);释放一个块kfree(addr);,

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

当前位置:首页 > 建筑/施工/环境 > 项目建议


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号