首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux驱动程序开发_例解[转帖]

Linux驱动程序开发_例解[转帖]

一.基本知识


1.  驱动分类


字符设备character device:采用字符流方式访问的设备,如字符终端,串口,一般顺序访问,但也可以前后移动访问指针,如帧捕捉卡


块设备Block device:采用数据块方式访问的设备,如磁盘等,可以随意移动访问。和字符设备的差异在于内核内部管理数据的方式,如采用缓存机制等。并必须支持 mount文件系统。


上两者通过mknod来创建设备并使用


网络接口 network interface:数据包传输方式访问的设备,和上两者不同。通过ifconfig来创建和配置设备。网络驱动同块驱动最大的不同在于网络驱动异步接受外界数据,而块驱动只对内核的请求作出响应。


其他other:总线类,如USB, PCI, SCSI等,/proc接口等,一般同其他驱动联合使用


2.  模块


Linux下驱动以模块的方式展现,可以单独作为模块在运行时同内核连接,也可以直接连接进内核。模块同内核版本密切相关。通过模块计数来维持生命周期,确定是否可卸载。/proc/modules保存了当前连接入内核的模块信息。


模块和应用程序的差异:


模块运行在内核空间,应用程序在用户空间


模块只能使用内核导出的函数,不能使用其他函数库,包括glibc库。


模块必须考虑到并发,所以代码都必须是可重入的。


3.  资源


模块需要申请资源(I/O端口,I/O内存,DMA通道,中断等)


/proc下的ioports,iomem,dma,interrupts列出了已注册的资源


4.  设备


设备一般是采用设备文件方式,处于/dev下,但也并非一定需要,如网卡就没有。每个设备有设备名称,主设备号和次设备号。主设备号标识设备对应的驱动程序,驱动程序需要向系统注册一个主设备号。次设备号区分具体设备,由驱动程序管理。


5.  中断处理


驱动通过request_irq和free_irq来申请和释放中断号。


调用request_irq的正确位置应该是在设备第一次打开,硬件被告知产生中断之前;调用free_irq的正确位置应该是在最后一次关闭设备,硬件被告知不要再中断处理之后。这种方式需要为每个设备保存一个打开计数。


中断处理函数的限制


(1)       在中断发生时运行,不能向用户空间发送和接受数据,因为它不是在任何进程上下文执行的


(2)       不能做任何可能发生睡眠的操作,如sleep_on,使用不带GFP_ATOMIC标志的分配内存操作,或锁定一个信号量等


(3)       不能调用schedule函数


技巧:


执行时间尽量短,长时间计算采用tasklet或任务队列方式。一般采用Top half和Bottom half方式。Top half是实际中断处理例程,尽量短。Bottom half是一个被Top half调度,并在稍后更安全时执行的例程,一般用tasklet方式


典型情况为:顶半部程序保存设备数据到一个设备特定缓存区并调度它的底半部,并且退出。底半部执行其他必要工作,如唤醒进程,启动其他I/O操作等,此时所有的中断都处于启用状态。但底半部程序也受同样中断处理函数的限制


具体中断个数以及中断号分配等,和具体CPU相关,要参见CPU说明。每个中断都会有中断掩码位(该中断是否有效,enable_irq/disable_irq就修改该位),中断悬挂位(该中断是否生成),中断优先级(该中断的优先级)





二.模块函数


Init_module初始化函数,注册模块,连接到内核时被调用


Clean_module卸载函数,Init_module的逆操作,撤消所有注册,从内核中移出时调用


(常用方式是自定义初始化/卸载函数,使用module_init(my_init),module_exit(my_cleanup)来声明,使得直接连接进内核的驱动更容易编写,因为内核中每个驱动的初始化/卸载函数为不同名字)


驱动可以提供的其他函数,通过file_operations结构,常用的有


open打开设备,应该是对设备的第一个操作函数。如为NULL,则所有调用都成功


release关闭设备,在文件结构被释放。只有当设备文件的所有拷贝都被释放时,才进行release调用,而不是每次应用调close时都执行。同open一样,也可以为NULL


read 用来从设备接受数据。


write用来往设备发数据


ioctl是用来给设备发送命令的接口函数


mmap用来请求将设备内存映射到进程空间


poll是两个系统调用poll和select的背后支撑。如果驱动未ㄒ澹蚣偕枭璞讣瓤啥劣挚尚础?/P>



三.建议的一些技巧


1.在线参考Linux内核源码,通过“The Linux Cross-Reference project ”站点,如http://www.iglu.org.il/lxr/blurb.html,http://lxr.linux.no/,很方便查找各个Identifie


2.就是根据具体硬件功能,参考相类似的驱动,进行修改。Linux下开发的好处就在于源码共享,各种硬件基本上都能找到类似功能的驱动源码,在Linux提供的较可靠的驱动上进行修改,有利于提高开发效率和驱动的可靠性。首先采用模块方式进行调试,在调试好后根据具体情况考虑是否直接连接到内核中。


3.其他技巧包括:


多用printk打印调试信息,内核调试需要掌握日志调试技术


掌握内核定时器和tasklet,这两个也是驱动中常用的


自旋锁的使用,规范的驱动都使用自旋锁,即使在单处理器情况下仍考虑到并发处理,并要注意如在中断处理函数中使用spin_lock和spin_unlock,此时在非中断函数中必须使用spin_lock_irqsave或spin_lock_irq等禁用中断的版本,以防死锁

4.手中一本驱动开发必备之Linux Device Drivers 2nd的中文版或英文版
51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)
四.举例(以2410的触摸屏为例)

1.硬件说明

提供8通道模拟输入,能够将每个模拟输入转化成10位的数字,转换500KSPS with 2.5 MHz A/D converter clock.。


TS使用(nYPON, YMON, nXPON and XMON)和analog pads (AIN[7], AIN[5])


建议采用下面步骤


(1) 使用独立Separate或自动Auto (Sequential) X/Y位置转换模式来获得X/Y位置


(2) 设定TS接口为Waiting Interrupt Mode


(3) 如果中断发生,就会激活上面所选的X/Y位置转换模式


(4) 在得到X/Y值后,再进入中断等待


2.数据结构


struct s3c2410_ts_device { //对应于触摸屏设备


struct s3c2410_ts_general d;


struct s3c2410_ts_calibration cal; /* ts calibration parameters */


struct s3c2410_ts_event buf[MOUSEBUF_SIZE]; //触摸屏事件缓存


struct s3c2410_ts_event cur_data, //当前事件


samples[3],//多次采样事件值


last_data;//上次值


};


struct s3c2410_ts_general {触摸屏一般性信息


unsigned int head, tail; /* Position in the event buffer */


event缓存区中的head和tail,确定当前还需要处理的触摸屏事件


struct fasync_struct *async_queue; /* Asynchronous notification */


wait_queue_head_t waitq; /* Wait queue for reading */


struct semaphore lock; /* Mutex for reading */


unsigned int usage_count; /* Increment on each open */


unsigned int total; /* Total events */


unsigned int processed;


unsigned int dropped;


};


typedef struct s3c2410_ts_event {触摸屏事件,每次触摸就会产生触摸屏事件


unsigned short pressure;


unsigned short x;


unsigned short y;


unsigned short pad;


} TS_EVENT;


struct ts_pen_data {//记录每次触摸的点(笔)信息


enum pen_state state;


unsigned short x[TS_FILTER_LENGTH]; // Unfiltered data points


unsigned short y[TS_FILTER_LENGTH];


unsigned short count; // Number of points recorded in this "DOWN" or "DISCARD" series


unsigned short index; // Location in ring buffer of last stored data value


int last_cal_x; // Last reported X value to the user


int last_cal_y; // Last reported Y value to the user


};


3.基本算法:


采用等待中断模式,则每次点击都会产生一个INT_TC中断,然后开始硬件定时,会不断进入定时采样阶段,将采样值保存到设备对应的s3c2410_ts_device的samples中,并进行校验和坐标转化data_processing,最后结果保存在设备对应的s3c2410_ts_device的cur_data中,并拷贝到buf中








五.添加驱动到内核


1.修改源文件。源文件为linux /driver/char/s3c2410_ts.c。不能#define MODULE,


2. 修改makefile文件。修改 linux/driver/char/Makefile ,在适当位置添加obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o


3. 修改make配置文件。修改 linux/driver/char/ Config.in,在适当位置添加


if [ "$CONFIG_ARCH_S3C2410" = "y" ]; then


tristate 'S3C2410 TOUCH SCREEN SUPPORT ' CONFIG_TOUCHSCREEN_S3C2410


或 bool ''S3C2410 TOUCH SCREEN SUPPORT ' CONFIG_TOUCHSCREEN_S3C2410 y


fi


便于在 make menuconfig 时选择


4. 进行配置。运行make menuconfig(在menuconfig的字符设备选项里你可以看见我们刚刚添加的' S3C2410 TOUCH SCREEN SUPPORT 选项,选中


5. 重新编译内核


51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)
返回列表