【文档说明】Linux设备驱动程序(同名71)课件.ppt,共(39)页,182.023 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-7170.html
以下为本文档部分文字说明:
Linux设备驱动程序docin/sundae_meng内容•设备分类•设备驱动程序的框架•字符型设备•网络设备•文件系统–UserSpaceFileSystem•USB设备•FrameBuffer例子和使用•Debug原理和Debug方法•常用设备/fb/ram/loopback/zero设
备驱动程序的任务•设备初始化•硬件操作和管理•外部硬件和内核空间的数据传递•内核空间和用户空间的数据传递设备驱动程序的功能外部硬件设备驱动程序用户程序存储缓冲用户空间内核空间用户态程序vs内核态程序用户程序•权限受限•虚拟运行环境–逻辑地址–关键资源访问受监管•函
数调用由用户控制内核程序•最高权限•实际的运行环境–物理地址–可访问所有资源•函数由内核直接调用可以运行驱动程序地址映射与物理地址访问物理地址空间用户进程1用户进程2用户进程3虚拟地址映射用户利用指针访问的是虚地址,不是物理地址,IO设备的物理地址可能是
用户进程不可触及的虚拟地址映射虚拟地址映射直接访问内核内存(/dev/kmem)kmfd=open("/dev/kmem",O_RDONLY);lseek(kmfd,offset,SEEK_SET);read(kmfd,byteArray,byteArrayLen);close(kmf
d);•直接访问内核地址(内核态的虚地址)•一般内核地址起始于0xC0000000直接访问物理地址(/dev/mem)mem_fd=open("/dev/mem",O_RDONLY);b=mmap(0,0x10000,PROT_R
EAD|PROT_WRITE,MAP_SHARED,mem_fd,0xA0000)…close(memfd);0xA00000xB0000Pointerbmmap将文件中的数据映射成数组这里是将物理内存(由特殊文件/dev/mem访问)映射成指针b指向的数组。注意,指针b的值不一定
是0xA0000,它是和物理地址0xA0000对应的用户态的虚拟地址Linux中/dev/mem主要是用于设备内存的访问(比如显卡内存),而不是普通存储器直接访问IO端口(/dev/port)port_fd=
open("/dev/port",O_RDWR);lseek(port_fd,port_addr,SEEK_SET);read(port_fd,…);write(port_fd,…);close(port_fd);•注意:不能用
fopen/fread/fwrite/fclose因为它们有数据缓冲,对读写操作不是立即完成的outb()/outw()/inb()/inw()函数#include<stdio.h>#include<unistd.h>#include<asm/io.h>#defineBA
SEPORT0x378//printerintmain(){ioperm(BASEPORT,3,1));//getaccesspermissionoutb(0,BASEPORT);usleep(100000)
;printf("status:%d\n",inb(BASEPORT+1));ioperm(BASEPORT,3,0));//giveupexit(0);}•ioperm(from,num,turn_on)•用ioperm申请的操作端口地址在0x000~0x3FF,利用iopl()可
以申请所有的端口地址•必须以root运行•用“gcc-02–oxxx.elfxxx.c”编译•outb(value,port);inb(port);//8-bit•outw(value,port);inw(port);//16-bit•访问时间大约1us设备驱动程序内访问设备地址•设备驱动
程序可以通过指针访问设备地址•设备驱动程序接触到的还是虚拟地址,但对于外界设备有固定的设备地址映射(设备的地址在移植Linux时候确定)物理内存地址空间设备驱动程序虚拟地址映射设备地址空间设备地址映射设备驱动程序虚拟地址映射设备地址映射直接
访问IO端口vs设备驱动程序IO直接访问•用户态•程序编写/调试简单•查询模式,响应慢•设备共享管理困难设备驱动访问•核心态•编程调试困难•可用中断模式访问、快•设备共享管理简单(由内核帮助完成)设备分类•字符设备–鼠标、串口、游戏杆•块设备–磁盘、打印机•网络设备–由B
SDSocket访问字符设备vs块设备字符设备•字符设备发出读/写请求时,对应的硬件I/O一般立即发生。•数据缓冲可有可无•ADC/DAC、按钮、LED、传感器等块设备•利用一块系统内存作缓冲区,一般读写由缓冲区直接提供,尽量减少IO操作•针对磁盘等慢速设备可装卸的设备驱动程序和静态连
接到内核的设备驱动程序•静态连接到内核的设备驱动程序–修改配置文件、重新编译和安装内核•可装卸的设备驱动程序–insmod装载–rmmod卸载–lsmod查询Linux对硬件设备的抽象设备文件•Open/Close/Re
ad/Write•例子–/dev/mouse–/dev/lp0驱动程序与设备文件设备驱动程序设备文件用mknod命令创建用insmod命令安装,或直接编译到内核中用户程序用open/read/write/close等命令访问通过主设备号
找到设备驱动驱动程序代码结构驱动程序注册与注销设备文件的操作函数(*open)()(*write)()(*flush)()(*llseek)()…中断服务程序LED设备驱动程序的例子CPUstructfile_operationsLED_fops={read:LED_read,write:L
ED_write,open:LED_open,release:LED_release,};intLED_init_module(void){SET_MODULE_OWNER(&LED_fops);LED_major=register_chrdev(0,"LED",&LED_fops);
LED_off();LED_status=0;return0;}voidLED_cleanup_module(void){unregister_chrdev(LED_major,"LED");}module_init(LED_init_modul
e);module_exit(LED_cleanup_module);程序列表(1)程序列表(2)intLED_open(structinode*inode,structfile*filp){printk("LED_open()\n");MOD_INC_USE_C
OUNT;return0;}intLED_release(structinode*inode,structfile*filp){printk(“LED_release()\n“);MOD_DEC_US
E_COUNT;return0;}程序列表(3)ssize_tLED_read(structfile*filp,char*buf,size_tcount,loff_t*f_pos){inti;for(i=0;i<count;i++)*((c
har*)(buf+i))=LED_Status;returncount;}ssize_tLED_write(structfile*filp,constchar*buf,size_tcount,loff_t*f_pos){inti;for(i=0;i<count;i++)if(*((c
har*)(buf+i)))Data->LED_on();elseData->LED_off();returncount;}(*((volatileunsignedint*)(0xXXXXXXXX)))|=MASK;(*((volatileunsignedint*)(0xXXXXXXXX)
))&=~MASK;#ifndef__KERNEL__#define__KERNEL__#endif#ifndefMODULE#defineMODULE#endif#include<linux/config.h>#include<linux/module.h>#include<
linux/sched.h>#include<linux/kernel.h>#include<linux/malloc.h>#include<linux/errno.h>#include<linux/types.h>#include<linux/inte
rrupt.h>#include<linux/in.h>#include<linux/netdevice.h>#include<linux/etherdevice.h>#include<linux/ip.h>#include<linux/tcp.h>#incl
ude<linux/skbuff.h>#include<sysdep.h>#include<linux/ioctl.h>#include<linux/in6.h>#include<asm/checksum.h>MO
DULE_AUTHOR("RendongYing");intLED_major,LED_status;程序列表(4)头文件程序编译(Makefile)CC=arm-elf-linux-gccLD=arm-elf-lin
ux-ldINCLUDE=/usr/local/src/bspLinux/includeLIB_INC=/usr/local/lib/gcc-lib/arm-elf-linux/2.95.3/includeCFLAGS=-O6-Wall-DCONFIG_KERNELD-DMOD
ULE-D__KERNEL__-DLinux-nostdinc-I--I.-I$(INCLUDE)-idirafter$(LIB_INC)LED.o:LED.c$(CC)$(CFLAGS)-cLED.cclean:rm-fLED.o生成o文件设备装载和
设备文件建立•chmod+x/tmp/LED.o•/sbin/insmod-f./LED.o•cat/proc/devices得到装入内核的主设备号•mknod/dev/LampcNum1Num2Num1为主设备号Num2为次设备号强制安装,忽略版本检查设备
的测试和使用•命令行echo8>/proc/sys/kernel/printkcat/dev/Lampcat>/dev/Lamp•程序voidmain(){intfd=open(“/dev/Lamp,O_RDWR);wr
ite(fd,&data,1);close(fd);}开启printk,也可以从/var/log/messages看printk的记录设备卸载/sbin/rmmodLEDrm-f/dev/LampFunctionofMOD_INC_USE_
COUNT;MOD_DEC_USE_COUNT;复杂的设备驱动程序驱动程序注册与注销(注册/注销设备、中断)设备文件的操作函数(*open)()(*write)()(*flush)()(*llseek)()…中断服务程序内核数据缓冲区用户数据空间复杂设备驱动程序的例
子(USBDevice)中断资源申请和释放•if(request_irq(USB_INTR_SOURCE1,usb_ep1_int,SA_INTERRUPT,"USBEP1",0)<0)printk("Int.req.failed!\n");•free_irq(USB_IN
TR_SOURCE0,0);cat/proc/interrupts中断服务程序•没有返回参数•简短快速voidusb_ep1_int(intirq,void*dev_id,structpt_regs*regs){//…}数据接收中断服
务程序voidusb_ep1_int(intirq,void*dev_id,structpt_regs*regs){read_data_from_hardware_FIFO();put_data_into_data_buffer();}数据发送中断服务程序voidusb_ep2_int(intir
q,void*dev_id,structpt_regs*regs){read_data_from_buffer();send_data_hardware_FIFO();}设备文件接口函数(read)ssize_tusb_ep1_read(structfile*
filp,char*buf,size_tcount,loff_t*f_pos){if(data_buffer_empty())return0;elsecopy_data_to_user_space();returndata_copyed;}copy_t
o_user(user_buf,device_driver_buf,size);设备文件接口函数(read,blockingmode)ssize_tusb_ep1_read(structfile*filp,char*bu
f,size_tcount,loff_t*f_pos){while(device_driver_buf_empty()){if(wait_event_interruptible(q_ep2,device_driver_buf_not_empty))return-ER
ESTARTSYS;}copy_data_to_user_space();returndata_copyed;}wait_queue_head_trq_EP2;init_waitqueue_head(&rq_EP2);设备文件接口函数(write)
ssize_tusb_ep2_write(structfile*filp,char*buf,size_tcount,loff_t*f_pos){if(data_buffer_full())return0
;elsecopy_data_to_device_driver_buf();if(no_transmission_now)send_1st_data();returndata_copyed;}copy_from_u
ser(device_driver_buf,user_buf,size);内存申请•malloc?X•kmalloc•kfree•vmalloc•vfree禁止设备打开多次intLED_flag;intLED_init_module(void){LED_flag=0;
…}intLED_open(structinode*inode,structfile*filp){if(LED_flag=0){LED_flag=1;MOD_INC_USE_COUNT;return0;}elsereturn-ENODEV;
}intLED_release(structinode*inode,structfile*filp){LED_flag=0;MOD_DEC_USE_COUNT;return0;}同一设备驱动管理几个接口SerialPo
rtDeviceDriverUART0UART0应用程序同一设备驱动管理几个接口intdev_open(structinode*inode,structfile*filp){intminor=MINOR(inode->i_rdev);fi
lp->private_data=sub_dev_dat[minor];…}ssize_tdev_write(structfile*filp,constchar*buf,size_tcount,loff_t*f_pos){switch(*(filp-
>private_data)){…}}