首页资源分类嵌入式系统Linux > Linux驱动程序设计全文详解

Linux驱动程序设计全文详解

已有 445117个资源

下载专区

上传者其他资源

    文档信息举报收藏

    标    签:Linux驱动程序设计

    分    享:

    文档简介

    这个是一个Linux驱动程序设计全文详解

    文档预览

    Linux驱动程序设计全文详解 [日期:2007-10-19] 来源:Linux公社  作者:Linuxidc linux驱动程序设计   PROGRAM FOR BLOCK DEVICE DRIVER OF DEVFS TYPE 对linux的devfs类型的驱动程序的编写可以从以下几大内容理解和入手: 通过分析驱动程序源代码可以发现驱动程序一般可分三部分: 核心数据结构;核心数据和资源的初始化,注册以及注消,释放;底层设备操作函数; A.核心数据结构 struct file_operations fops 设备驱动程序接口 struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); 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); }; block_device_operations 块设备驱动程序接口 { int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); int (*check_media_change) (kdev_t); int (*revalidate) (kdev_t); struct module *owner; };块设备的READ().WRITE()不在这里注册,而是在设备的读写请求队列里注册,内核在这里将调用通用的blk_read(),blk_write().向读写队列 发出读写请求. Linux 利用这些数据结构向内核注册open(),release(),ioctl(),check_media_change(),rvalidate()等函数的入口句柄. 我们将要编写的open(),release(),ioctl(),check_media_change(),revalidate()等函数,将在驱动初始化的时候, 通过一个此结构类型的变量向内核提供函数的 入口. struct request_queue_t 设备请求队列的数据结构 struct request_list { unsigned int count; unsigned int pending[2]; struct list_head free; }; struct request { struct list_head queue; int elevator_sequence; kdev_t rq_dev; int cmd; /* READ or WRITE */ int errors; unsigned long start_time; unsigned long sector; unsigned long nr_sectors; unsigned long hard_sector, hard_nr_sectors; unsigned int nr_segments; unsigned int nr_hw_segments; unsigned long current_nr_sectors, hard_cur_sectors; void * special; char * buffer; struct completion * waiting; struct buffer_head * bh; struct buffer_head * bhtail; request_queue_t *q; }; struct request_queue { /* * the queue request freelist, one for reads and one for writes */ struct request_list rq; /* * The total number of requests on each queue */ int nr_requests; /* * Batching threshold for sleep/wakeup decisions */ int batch_requests; /* * The total number of 512byte blocks on each queue */ atomic_t nr_sectors; /* * Batching threshold for sleep/wakeup decisions */ int batch_sectors; /* * The max number of 512byte blocks on each queue */ int max_queue_sectors; /* * Together with queue_head for cacheline sharing */ struct list_head queue_head; elevator_t elevator; request_fn_proc * request_fn; merge_request_fn * back_merge_fn; merge_request_fn * front_merge_fn; merge_requests_fn * merge_requests_fn; make_request_fn * make_request_fn; plug_device_fn * plug_device_fn; /* * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. */ void * queuedata; /* * This is used to remove the plug when tq_disk runs. */ struct tq_struct plug_tq; /* * Boolean that indicates whether this queue is plugged or not. */ int plugged:1; /* * Boolean that indicates whether current_request is active or * not. */ int head_active:1; /* * Boolean that indicates you will use blk_started_sectors * and blk_finished_sectors in addition to blk_started_io * and blk_finished_io. It enables the throttling code to * help keep the sectors in flight to a reasonable value */ int can_throttle:1; unsigned long bounce_pfn; /* * Is meant to protect the queue in the future instead of * io_request_lock */ spinlock_t queue_lock; /* * Tasks wait here for free read and write requests */ wait_queue_head_t wait_for_requests; struct request *last_request; }; 缓冲区和对缓冲区相应的I/O操作在此任务队列中相关联,等待内核的调度.如果是字符设备就不需要此数据结构.而 块设备的read(),write()函数则在buffer_queue的initize和设备请求队列进行处理请求时候传递给request_fn(). struct request_queue_t{}设备请求队列的变量类型,驱动程序在初始化的时候需要填写request_fn(). 其他的数据结构还有 I/O port,Irq,DMA 资源分配,符合POSIX标准的ioctl的cmd的构造和定义,以及描述设备自身的 相关数据结构定义-如设备的控制寄存器的相关数据结构定义,BIOS里的参数定义,设备类型定义等. B.初始化和注册和注消,模块方式驱动程序的加载和卸载. 设备驱动程序在定义了数据结构后 ,首先开始初始化: 如I/O 端口的检查和登记,内核对 I/O PORT的检查和登记提供了两个 函数check_region(int io_port, int off_set) 和request_region(int io_port, int off_set,char *devname).I/O Port登记后,就可以用inb()和outb()进行操作了 . 还有DMA和Irq的初始化检查和 登记, int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags, const char *device); irq: 是要申请的中断。 handle:中断处理函数指针。 flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。 device:设备名。 如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。 DMA主要是在内存中分配交换内存空间.还有缓冲区,设备请求队列的初始化. 还有设备控制寄存器的检查和初始化,还有对设备自身相关的数据结构的初始化,填写一些设备特定的数据等. 然后,开始注册 devfs_register()向VFS注册统一的设备操作函数. static struct file_operations XXX_fops = { owner: THIS_MODULE, XXX_fops所属的设备模块 read: XXX_read, 读设备操作 write: XXX_write, 写设备操作 ioctl: XXX_ioctl, 控制设备操作 mmap: XXX_mmap, 内存重映射操作 open: XXX_open, 打开设备操作 release: XXX_release 释放设备操作 /* ... */ }; blk_init_queue()队列初始化函数. request_irq()中断注册函数 相应的注消函数: devfs_unregister (devfs_handle_t de){}; free_irq()释放中断,I/O资源,释放缓冲区,释放设备,请求队列,VFS节点等. 模块方式驱动程序的加载和卸载. static int __init _init_module (void) { /* ... */ } static void __exit _cleanup_module (void) { } /* 加载驱动程序模块入口 */ module_init(_init_module); /* 卸载驱动程序模块入口 */ module_exit(_cleanup_module); _intrrupt() 设备发生中断时的处理程序. { 1.对共享中断的处理; 2.对spinlock以及其他的事务的处理; } C. 底层设备操作函数的编写 read().write(),open(),release(),check_media_change(),revalidate()等. open()和release() 打开设备是通过调用file_operations结构中的函数open( )来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中,open( )通常需要完成下列工作: 1. 检查设备相关错误,如设备尚未准备好等。 2. 如果是第一次打开,则初始化硬件设备。 3. 识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。 4. 分配和填写要放在file->private_data里的数据结构。 5. 使用计数增1。 释放设备是通过调用file_operations结构中的函数release( )来完成的,这个设备方法有时也被称为close( ),它的作用正好与open( )相反,通常要完成下列工作: 1. 使用计数减1。 2. 释放在file->private_data中分配的内存。 3. 如果使用计算为0,则关闭设备。 read()和 write() 字符设备的读写操作相对比较简单,直接使用函数read( )和write( )就可以了。但如果是块设备的话,则需要调用函数block_read( )和block_write( )来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作的,因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用数据结构blk_dev_struct中的函数request_fn( )来完成的。 ioctl()--将cmd进行解释,并送到设备的控制寄存器.事实上,read()和write()也要通过ioctl()来完成操作的 . ioctl(){ CASE CMD{ SWITCH CASE1:{...}; SWITCH CASE2:{...}; SWITCH CASE N:{...}; . . DEFAULT : {...}; } END CASE 总结: 我们可以看出一个linux的驱动程序通常包含如下: 初始化设备模块、 {I/O port ,DMA.Irq,内存 buffer,初始化并且填写具体设备数据结构,注册 fops的具体函数等等 } 中断处理模块、设备释放模块、设备卸载模块 设备打开模块、数据读写和控制模块、 驱动装载模块、驱动释放模块. 浙江省城乡规划设计研究院计算机中心 陈刚 2004.07.21 linux驱动问题讲解 [日期:2007-06-02] 来源:Linux公社  作者:Linux   问题澄清:   1. 凡是调用dev_queue_xmit的对象都是一个网络设备。   解答:这个思路是错误的。因为dev_queue_xmit是有网络设备无关层调用的函数,调用对象调用该函数之后,函数会判断skb中的dev字段,根据这个字段指示的设备调用该设备的发送函数hard_start_xmit来对skb进行转发。   2. 凡是由dev_queue_xmit调用hard_start_xmit发送出来的帧都是封装好的以太帧。   解答:这个思路还是有问题,因为对于是否将skb进一步封装成为以太帧的形式,其实是由hard_start_smit函数的具体实现来决定的,如果在hard_start_xmit中调用了hard_header函数,那么就会将skb进一步封装,如果没有调用,那么就没有将skb进行进一步封装。也就是说,上层在需要发送skb的时候会选择调用dev_queue_xmit,那么至于下层是怎么传递该skb的,上层根本就不用关心,这就是所谓的各层的独立性原理。所以对skb具体的发送处理过程,可以由下层网络接口的hard_queue_xmit来处理。比如说上层需要发送一个广播帧,那么它就将skb->pkt_type赋值为PACKET_BROADCAST,然后调用dev_queue_xmit将其发送出去之后就不管下层是否将这个广播帧真的放到网络中进行广播。而下层如果是一个与上层绑定好了的虚拟网络设备的话,它可以在自己的hard_start_xmit中对skb->pkt_type字段为PACKET_BROADCAST的skb进行特定的处理,这里指的特定就是说,不一定非要将这个skb放到网络中进行广播。   3. 对于注册的协议类型。   解答:注册协议类型是由具有该协议类型的本层来注册的,当netif_rx判断是某层注册的协议时,则会主动调用该协议的处理函数来对接收的帧进行处理。

    Top_arrow
    回到顶部
    EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。