Linux设备驱动程序docin/sundae_meng内容•设备分类•设备驱动程序的框架•字符型设备•网络设备•文件系统•User Space File System•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(kmfd);•直接访问内核地址〔内核态的虚地址)•一般内核地址起始于0xC0000000直接访问物理地址(/dev/mem)mem_fd = open("/dev/mem", O_RDONLY ); b=mmap(0, 0x10000, PROT_READ|PROT_WRITE,MAP_SHARED, mem_fd,0xA0000)…close(memfd);0xA00000xB0000Pointer bmmap将文件中的数据映射成数组这里是将物理内存〔由特殊文件/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 #include #include #define BASEPORT 0x378 // printerint main(){ ioperm(BASEPORT, 3, 1));// get access permission outb(0, BASEPORT); usleep(100000); printf("status: %d\n", inb(BASEPORT + 1)); ioperm(BASEPORT, 3, 0));// give up exit(0);}•ioperm(from,num,turn_on) •用ioperm申请的操作端口地址在0x000~0x3FF,利用iopl()可以申请所有的端口地址•必须以root运行•用 “gcc -02 –o xxx.elf xxx.c” 编译 •outb(value, port); inb(port); // 8-bit•outw(value, port); inw(port); // 16-bit•访问时间大约1us设备驱动程序内访问设备地址•设备驱动程序可以通过指针访问设备地址•设备驱动程序接触到的还是虚拟地址,但对于外界设备有固定的设备地址映射〔设备的地址在移植Linux时候确定)物理内存地址空间设备驱动程序虚拟地址映射虚拟地址映射设备地址空间设备地址映射设备地址映射设备驱动程序虚拟地址映射虚拟地址映射设备地址映射设备地址映射直接访问IO端口 vs 设备驱动程序IO直接访问用户态程序编写/调试简单查询模式,响应慢设备共享管理困难设备驱动访问核心态编程调试困难可用中断模式访问、快设备共享管理简单〔由内核帮助完成)设备分类•字符设备•鼠标、串口、游戏杆•块设备•磁盘、打印机•网络设备•由BSD Socket访问字符设备 vs 块设备字符设备字符设备发出读/写请求时,对应的硬件I/O一般立即发生。
数据缓冲可有可无ADC/DAC、按钮、LED、传感器等块设备利用一块系统内存作缓冲区,一般读写由缓冲区直接提供,尽量减少IO操作针对磁盘等慢速设备可装卸的设备驱动程序和静态连接到内核的设备驱动程序•静态连接到内核的设备驱动程序•修改配置文件、重新编译和安装内核•可装卸的设备驱动程序•insmod 装载•rmmod卸载•lsmod查询Linux对硬件设备的抽象设备文件Open/Close/Read/Write例子/dev/mouse/dev/lp0驱动程序与设备文件设备驱动程序设备文件用mknod命令创建用insmod命令安装,或直接编译到内核中用户程序用open/read/write/close等命令访问通过主设备号找到设备驱动驱动程序代码结构驱动程序注册与注销设备文件的操作函数(*open)()(*write)()(*flush)()(*llseek)()…中断服务程序LED设备驱动程序的例子CPUstruct file_operations LED_fops = struct file_operations LED_fops = { read: LED_read,{ read: LED_read, write: LED_write, write: LED_write, open: LED_open, open: LED_open, release: LED_release, release: LED_release,};};int LED_init_module(void) int LED_init_module(void) { SET_MODULE_OWNER(&LED_fops);{ SET_MODULE_OWNER(&LED_fops); LED_major = register_chrdev(0, "LED", &LED_fops); LED_major = register_chrdev(0, "LED", &LED_fops); LED_off(); LED_off(); LED_status=0; LED_status=0; return 0; return 0; } }void LED_cleanup_module(void) void LED_cleanup_module(void) { unregister_chrdev(LED_major, "LED"); }{ unregister_chrdev(LED_major, "LED"); }module_init(LED_init_module);module_init(LED_init_module);module_exit(LED_cleanup_module);module_exit(LED_cleanup_module);程序列表 (1)程序列表 (2)int LED_open(struct inode *inode, struct file int LED_open(struct inode *inode, struct file *filp)*filp){ printk("LED_open()\n");{ printk("LED_open()\n"); MOD_INC_USE_COUNT; MOD_INC_USE_COUNT; return 0; return 0;} }int LED_release(struct inode *inode, struct int LED_release(struct inode *inode, struct file *filp)file *filp){ printk(“LED_release()\n“);{ printk(“LED_release()\n“); MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT; return 0; return 0;} }程序列表 (3)ssize_t LED_read (struct file *filp, char *buf, size_t ssize_t LED_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)count, loff_t *f_pos){ int i; { int i; for (i=0; iLED_on(); if (*((char*)(buf+i))) Data->LED_on(); else Data->LED_off(); else Data->LED_off(); return count; return count;} }(*((volatile unsigned int *)(0xXXXXXXXX))) |= MASK;(*((volatile unsigned int *)(0xXXXXXXXX))) &=~MASK;#ifndef __KERNEL__#ifndef __KERNEL__ #define __KERNEL__ #define __KERNEL__#endif#endif#ifndef MODULE#ifndef MODULE #define MODULE #define MODULE#endif#endif#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Rendong Ying");MODULE_AUTHOR("Rendong Ying");int LED_major, LED_status;int LED_major, LED_status;程序列表 (4)头文件程序编译 (Makefile)CC = arm-elf-linux-gccCC = arm-elf-linux-gccLD = arm-elf-linux-ldLD = arm-elf-linux-ldINCLUDE = /usr/local/src/bspLinux/includeINCLUDE = /usr/local/src/bspLinux/includeLIB_INC = /usr/local/lib/gcc-lib/arm-elf-LIB_INC = /usr/local/lib/gcc-lib/arm-elf-linux/2.95.3/includelinux/2.95.3/includeCFLAGS = -O6 -Wall -DCONFIG_KERNELD -DMODULE -D__KERNEL__ CFLAGS = -O6 -Wall -DCONFIG_KERNELD -DMODULE -D__KERNEL__ -DLinux -nostdinc -I- -I . -I$(INCLUDE) -DLinux -nostdinc -I- -I . -I$(INCLUDE) -idirafter $(LIB_INC) -idirafter $(LIB_INC)LED.o: LED.cLED.o: LED.c$(CC) $(CFLAGS) -c LED.c$(CC) $(CFLAGS) -c LED.cclean:clean:rm -f LED.orm -f LED.o生成o文件设备装载和设备文件建立•chmod +x /tmp/LED.o•/sbin/insmod -f ./LED.o•cat /proc/devices得到装入内核的主设备号•mknod /dev/Lamp c Num1 Num2•Num1为主设备号Num2为次设备号强制安装,忽略版本检查设备的测试和使用•命令行•echo 8 > /proc/sys/kernel/printk•cat /dev/Lamp •cat > /dev/Lamp •程序void main(){ int fd=open(“/dev/Lamp, O_RDWR); write(fd, &data, 1); close(fd);}开启printk,也可以从/var/log/messages看printk的记录设备卸载/sbin/rmmod LEDrm -f /dev/LampFunction ofMOD_INC_USE_COUNT;MOD_DEC_USE_COUNT;复杂的设备驱动程序驱动程序注册与注销(注册/注销 设备、中断)设备文件的操作函数(*open)()(*write)()(*flush)()(*llseek)()…中断服务程序内核数据内核数据缓冲区缓冲区用户数用户数据空间据空间复杂设备驱动程序的例子(USB Device)中断资源申请和释放if (request_irq(USB_INTR_SOURCE1, usb_ep1_int, SA_INTERRUPT, "USB EP1", 0) < 0) printk("Int. req. failed !\n");free_irq(USB_INTR_SOURCE0, 0);cat /proc/interrupts中断服务程序• 没有返回参数• 简短快速•void usb_ep1_int(int irq, void *dev_id, struct pt_regs *regs) •{ //…}数据接收中断服务程序void usb_ep1_int(int irq,void usb_ep1_int(int irq, void *dev_id, void *dev_id, struct pt_regs *regs) struct pt_regs *regs) { { read_data_from_hardware_FIFO(); read_data_from_hardware_FIFO(); put_data_into_data_buffer(); put_data_into_data_buffer();} }数据发送中断服务程序void usb_ep2_int(int irq,void usb_ep2_int(int irq, void *dev_id, void *dev_id, struct pt_regs *regs) struct pt_regs *regs) { { read_data_from_buffer(); read_data_from_buffer(); send_data_hardware_FIFO (); send_data_hardware_FIFO ();} }设备文件接口函数(read)ssize_t usb_ep1_read (struct file *filp, ssize_t usb_ep1_read (struct file *filp, char *buf, char *buf, size_t count, size_t count, loff_t *f_pos) loff_t *f_pos){ { if (data_buffer_empty()) return 0; if (data_buffer_empty()) return 0; else else copy_data_to_user_space(); copy_data_to_user_space(); return data_copyed; return data_copyed;} }copy_to_user(user_buf,copy_to_user(user_buf, device_driver_buf, device_driver_buf, size); size);设备文件接口函数(read, blocking mode)ssize_t usb_ep1_read (struct file *filp, ssize_t usb_ep1_read (struct file *filp, char *buf, char *buf, size_t count, size_t count, loff_t *f_pos) loff_t *f_pos){ { while(device_driver_buf_empty()) while(device_driver_buf_empty()) { { if (wait_event_interruptible(q_ep2, if (wait_event_interruptible(q_ep2, device_driver_buf_not_empty)) device_driver_buf_not_empty)) return -ERESTARTSYS; return -ERESTARTSYS; } } copy_data_to_user_space(); copy_data_to_user_space(); return data_copyed; return data_copyed;} }wait_queue_head_t rq_EP2;wait_queue_head_t rq_EP2;init_waitqueue_head(&rq_EP2);init_waitqueue_head(&rq_EP2);设备文件接口函数(write)ssize_t usb_ep2_write (struct file *filp, ssize_t usb_ep2_write (struct file *filp, char *buf, char *buf, size_t count, size_t count, loff_t *f_pos) loff_t *f_pos){ { if (data_buffer_full()) return 0; if (data_buffer_full()) return 0; else else copy_data_to_device_driver_buf(); copy_data_to_device_driver_buf(); if (no_transmission_now) if (no_transmission_now) send_1st_data(); send_1st_data(); return data_copyed; return data_copyed;} }copy_from_user(device_driver_buf, copy_from_user(device_driver_buf, user_buf, user_buf, size); size);内存申请•malloc ? X•kmalloc•kfree•vmalloc•vfree禁止设备打开多次int LED_flag;int LED_flag;int LED_init_module(void)int LED_init_module(void){ LED_flag=0;{ LED_flag=0; … …} } int LED_open(struct inode *inode, struct file *filp)int LED_open(struct inode *inode, struct file *filp){ if (LED_flag=0){ if (LED_flag=0) { LED_flag=1; { LED_flag=1; MOD_INC_USE_COUNT; MOD_INC_USE_COUNT; return 0; return 0; } } else else return -ENODEV; return -ENODEV;} }int LED_release(struct inode *inode, struct file *filp)int LED_release(struct inode *inode, struct file *filp){ LED_flag=0;{ LED_flag=0; MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT; return 0; return 0;} }同一设备驱动管理几个接口Serial Port Device DriverUART 0UART 0应用程序同一设备驱动管理几个接口int dev_open(struct inode *inode, int dev_open(struct inode *inode, struct file *filp) struct file *filp){ int minor = MINOR(inode->i_rdev);{ int minor = MINOR(inode->i_rdev); filp->private_data=sub_dev_dat[minor]; filp->private_data=sub_dev_dat[minor]; … …} }ssize_t dev_write(struct file *filp, ssize_t dev_write(struct file *filp, const char *buf, const char *buf, size_t count, size_t count, loff_t *f_pos) loff_t *f_pos){ { switch(*(filp->private_data)) switch(*(filp->private_data)) { … } { … }} }。