1 / 39

Linux 设备驱动程序

Linux 设备驱动程序. 04/05/2006 应忍冬. 内容. 设备分类 设备驱动程序的框架 字符型设备 网络设备 文件系统 User Space File System USB 设备 FrameBuffer 例子和使用 Debug 原理和 Debug 方法 常用设备 /fb/ram/loopback/zero. 设备驱动程序的任务. 设备初始化 硬件操作和管理 外部硬件和内核空间的数据传递 内核空间和用户空间的数据传递. 设备驱动程序的功能. 用户空间. 用户程序. 内核空间. 设备驱动程序. 存储缓冲. 外部硬件. 用户程序

ray
Download Presentation

Linux 设备驱动程序

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Linux设备驱动程序 04/05/2006 应忍冬

  2. 内容 • 设备分类 • 设备驱动程序的框架 • 字符型设备 • 网络设备 • 文件系统 • User Space File System • USB设备 • FrameBuffer例子和使用 • Debug原理和Debug方法 • 常用设备/fb/ram/loopback/zero

  3. 设备驱动程序的任务 • 设备初始化 • 硬件操作和管理 • 外部硬件和内核空间的数据传递 • 内核空间和用户空间的数据传递

  4. 设备驱动程序的功能 用户空间 用户程序 内核空间 设备驱动程序 存储缓冲 外部硬件

  5. 用户程序 权限受限 虚拟运行环境 逻辑地址 关键资源访问受监管 函数调用由用户控制 内核程序 最高权限 实际的运行环境 物理地址 可访问所有资源 函数由内核直接调用 可以运行驱动程序 用户态程序 vs 内核态程序 设备操作和管理能运行在用户态吗?

  6. 地址映射与物理地址访问 用户进程1 用户进程2 用户进程3 虚拟地址映射 虚拟地址映射 虚拟地址映射 物理地址空间 用户利用指针访问的是虚地址,不是物理地址,IO设备的物理地址可能是用户进程不可触及的

  7. 直接访问内核内存(/dev/kmem) kmfd = open("/dev/kmem", O_RDONLY ); lseek( kmfd, offset, SEEK_SET ); read( kmfd, byteArray, byteArrayLen ); close(kmfd); • 直接访问内核地址(内核态的虚地址) • 一般内核地址起始于0xC0000000

  8. 直接访问物理地址(/dev/mem) mem_fd = open("/dev/mem", O_RDONLY ); b=mmap(0, 0x10000, PROT_READ|PROT_WRITE,MAP_SHARED, mem_fd,0xA0000) … close(memfd); 0xA0000 Pointer b mmap将文件中的数据映射成数组 这里是将物理内存(由特殊文件/dev/mem访问)映射成指针b指向的数组。 注意,指针b的值不一定是0xA0000,它是和物理地址0xA0000对应的用户态的虚拟地址 Linux中/dev/mem主要是用于设备内存的访问(比如显卡内存),而不是普通存储器 0xB0000

  9. 直接访问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因为它们有数据缓冲,对读写操作不是立即完成的

  10. outb()/outw()/inb()/inw()函数 • outb(value, port); inb(port); // 8-bit • outw(value, port); inw(port); // 16-bit • 访问时间大约1us #include <stdio.h> #include <unistd.h> #include <asm/io.h> #define BASEPORT 0x378 // printer int 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” 编译

  11. 设备驱动程序内访问设备地址 • 设备驱动程序可以通过指针访问设备地址 • 设备驱动程序接触到的还是虚拟地址,但对于外界设备有固定的设备地址映射(设备的地址在移植Linux时候确定) 设备驱动程序 设备驱动程序 虚拟地址映射 设备地址映射 虚拟地址映射 设备地址映射 物理内存地址空间 设备地址空间

  12. IO直接访问 用户态 程序编写/调试简单 查询模式,响应慢 设备共享管理困难 设备驱动访问 核心态 编程调试困难 可用中断模式访问、快 设备共享管理简单(由内核帮助完成) 直接访问IO端口 vs 设备驱动程序

  13. 设备分类 • 字符设备 • 鼠标、串口、游戏杆 • 块设备 • 磁盘、打印机 • 网络设备 • 由BSD Socket访问

  14. 字符设备 字符设备发出读/写请求时,对应的硬件I/O一般立即发生。 数据缓冲可有可无 ADC/DAC、按钮、LED、传感器等 块设备 利用一块系统内存作缓冲区,一般读写由缓冲区直接提供,尽量减少IO操作 针对磁盘等慢速设备 字符设备 vs 块设备

  15. 可装卸的设备驱动程序和静态连接到内核的设备驱动程序可装卸的设备驱动程序和静态连接到内核的设备驱动程序 • 静态连接到内核的设备驱动程序 • 修改配置文件、重新编译和安装内核 • 可装卸的设备驱动程序 • insmod 装载 • rmmod卸载 • lsmod查询

  16. Linux对硬件设备的抽象 设备文件 • Open/Close/Read/Write • 例子 • /dev/mouse • /dev/lp0

  17. 驱动程序与设备文件 用open/read/write/close等命令访问 用户程序 设备 文件 用mknod命令创建 设备驱动程序 通过主设备号找到设备驱动 用insmod命令安装, 或直接编译到内核中

  18. 驱动程序代码结构 驱动程序注册与注销 设备文件的操作函数 (*open)() (*write)() (*flush)() (*llseek)() … 中断服务程序

  19. LED设备驱动程序的例子 CPU

  20. 程序列表 (1) struct file_operations LED_fops = { read: LED_read, write: LED_write, open: LED_open, release: LED_release, }; int LED_init_module(void) { SET_MODULE_OWNER(&LED_fops); LED_major = register_chrdev(0, "LED", &LED_fops); LED_off(); LED_status=0; return 0; } void LED_cleanup_module(void) { unregister_chrdev(LED_major, "LED"); } module_init(LED_init_module); module_exit(LED_cleanup_module);

  21. 程序列表 (2) int LED_open(struct inode *inode, struct file *filp){ printk("LED_open()\n"); MOD_INC_USE_COUNT; return 0;} int LED_release(struct inode *inode, struct file *filp){ printk(“LED_release()\n“); MOD_DEC_USE_COUNT; return 0;}

  22. 程序列表 (3) ssize_t LED_read (struct file *filp, char *buf, size_t count, loff_t *f_pos){ int i; for (i=0; i<count; i++) *((char*)(buf+i)) = LED_Status; return count;}ssize_t LED_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos){ int i; for (i=0; i<count; i++) if (*((char*)(buf+i)))Data->LED_on(); else Data->LED_off(); return count;} (*((volatile unsigned int *)(0xXXXXXXXX))) |= MASK;(*((volatile unsigned int *)(0xXXXXXXXX))) &=~MASK;

  23. #ifndef __KERNEL__ #define __KERNEL__#endif#ifndef MODULE #define MODULE#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/interrupt.h>#include <linux/in.h>#include <linux/netdevice.h>#include <linux/etherdevice.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/skbuff.h>#include <sysdep.h>#include <linux/ioctl.h> #include <linux/in6.h>#include <asm/checksum.h>MODULE_AUTHOR("Rendong Ying");int LED_major, LED_status; 程序列表 (4) 头文件

  24. 程序编译 (Makefile) CC = arm-elf-linux-gcc LD = arm-elf-linux-ld INCLUDE = /usr/local/src/bspLinux/include LIB_INC = /usr/local/lib/gcc-lib/arm-elf-linux/2.95.3/include CFLAGS = -O6 -Wall -DCONFIG_KERNELD -DMODULE -D__KERNEL__ -DLinux -nostdinc -I- -I . -I$(INCLUDE) -idirafter $(LIB_INC) LED.o: LED.c $(CC) $(CFLAGS) -c LED.c clean: rm -f LED.o 生成o文件

  25. 设备装载和设备文件建立 • chmod +x /tmp/LED.o • /sbin/insmod -f ./LED.o • cat /proc/devices得到装入内核的主设备号 • mknod /dev/Lamp c Num1 Num2 Num1为主设备号Num2为次设备号 强制安装,忽略版本检查

  26. 设备的测试和使用 • 命令行 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的记录

  27. 设备卸载 /sbin/rmmod LEDrm -f /dev/Lamp Function of MOD_INC_USE_COUNT; MOD_DEC_USE_COUNT;

  28. 复杂的设备驱动程序 驱动程序注册与注销 (注册/注销 设备、中断) 设备文件的操作函数 (*open)() (*write)() (*flush)() (*llseek)() … 用户数 据空间 内核数据 缓冲区 中断服务程序

  29. 复杂设备驱动程序的例子(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

  30. 中断服务程序 • 没有返回参数 • 简短快速 void usb_ep1_int(int irq, void *dev_id, struct pt_regs *regs) { //…}

  31. 数据接收中断服务程序 void usb_ep1_int(int irq, void *dev_id, struct pt_regs *regs) { read_data_from_hardware_FIFO(); put_data_into_data_buffer(); }

  32. 数据发送中断服务程序 void usb_ep2_int(int irq, void *dev_id, struct pt_regs *regs) { read_data_from_buffer(); send_data_hardware_FIFO (); }

  33. 设备文件接口函数(read) ssize_t usb_ep1_read (struct file *filp, char *buf, size_t count, loff_t *f_pos){ if (data_buffer_empty()) return 0; elsecopy_data_to_user_space(); return data_copyed;} copy_to_user(user_buf, device_driver_buf, size);

  34. 设备文件接口函数(read, blocking mode) ssize_t usb_ep1_read (struct file *filp, char *buf, size_t count, loff_t *f_pos){ while(device_driver_buf_empty()) { if (wait_event_interruptible(q_ep2, device_driver_buf_not_empty)) return -ERESTARTSYS; } copy_data_to_user_space(); return data_copyed;} wait_queue_head_t rq_EP2;init_waitqueue_head(&rq_EP2);

  35. 设备文件接口函数(write) ssize_t usb_ep2_write (struct file *filp, char *buf, size_t count, loff_t *f_pos){ if (data_buffer_full()) return 0; else copy_data_to_device_driver_buf(); if (no_transmission_now) send_1st_data(); return data_copyed;} copy_from_user(device_driver_buf, user_buf, size);

  36. 内存申请 • malloc ? X • kmalloc • kfree • vmalloc • vfree

  37. 禁止设备打开多次 int LED_flag;int LED_init_module(void){ LED_flag=0; … } int LED_open(struct inode *inode, struct file *filp){ if (LED_flag=0) { LED_flag=1; MOD_INC_USE_COUNT; return 0; } else return -ENODEV;}int LED_release(struct inode *inode, struct file *filp){ LED_flag=0; MOD_DEC_USE_COUNT; return 0;}

  38. 同一设备驱动管理几个接口 应用程序 Serial Port Device Driver UART 0 UART 0

  39. 同一设备驱动管理几个接口 int dev_open(struct inode *inode, struct file *filp){ int minor = MINOR(inode->i_rdev);filp->private_data=sub_dev_dat[minor]; …} ssize_t dev_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { switch(*(filp->private_data)) { … } }

More Related