首页 Linux设备驱动开发详解讲座

Linux设备驱动开发详解讲座

举报
开通vip

Linux设备驱动开发详解讲座     嵌入式培训专家 LinuxLinux 设备驱动开发设备驱动开发 www.farsight.com.cn     华清远见 今天的内容  Linux设备驱动的现状  从 non­os驱动到 Linux驱动  内核设施  自旋锁、信号量、互斥量、完成量  异步通知、信号  阻塞与非阻塞  内存与 I/O操作, DMA  中断, top half/bottom half  字符设备驱动  复杂设备驱动的框架  LCD设备 FRAMEBUFFER  FLASH设备MT...

Linux设备驱动开发详解讲座
    嵌入式培训专家 LinuxLinux 设备驱动开发设备驱动开发 www.farsight.com.cn     华清远见 今天的内容  Linux设备驱动的现状  从 non­os驱动到 Linux驱动  内核设施  自旋锁、信号量、互斥量、完成量  异步 通知 关于发布提成方案的通知关于xx通知关于成立公司筹建组的通知关于红头文件的使用公开通知关于计发全勤奖的通知 、信号  阻塞与非阻塞  内存与 I/O操作, DMA  中断, top half/bottom half  字符设备驱动  复杂设备驱动的框架  LCD设备 FRAMEBUFFER  FLASH设备MTD  TTY设备  块设备  用户空间的设备驱动  设备驱动开发流程  开发环境建设  调试手段  用户空间测试  设备驱动的学习方法     华清远见 Linux设备驱动的现状 高需求  Linux内核的绝大多数代码为设备驱动  新设备、新芯片、新驱动的需求 高门槛  涉及到大量硬件操作  涉及到内核基础知识  涉及到并发控制与同步  复杂的软件结构框架 高回报     华清远见 从 non­os驱动到 Linux驱动 non­os驱动  单刀直入 简单 直接提供 API Linux驱动  兵团战役 复杂 间接提供 API 应用软件 SerialSend SerialRecv 设备驱动 LightOn LightOff FlashWr FlashRd 硬件 串口 LED FLASH 硬件 操作系统 API 操作系统 驱动中独立于设备的接口 驱动中的硬件操作 用户应用程序 non­os驱动与应用 on­os驱动与应用     华清远见 并发和竞态 并发和竞态:  对称多处理器 ( SMP)的多个 CPU  单 CPU内进程与抢占它的进程  中断(硬中断、软中断、 Tasklet、底半部)与进程之间 处理思路: lock() //锁定,拿虎符 . . . critical section //临界区,调动军队 . . . unlock()  //解锁定,归还虎符 常用方法 :  中断屏蔽  原子操作  自旋锁  信号量  互斥体     华清远见 原子变量  接口 • 整型原子操作 • �  设置原子变量的值 • void atomic_set(atomic_t *v, int i); //设置原子变量的值为 i • atomic_t v = ATOMIC_INIT(0); //定义原子变量 v并初始化为 0 •   获取原子变量的值 • atomic_read(atomic_t *v);  //返回原子变量的值 • �  原子变量加 /减 • void atomic_add(int i, atomic_t *v); //原子变量增加 i • void atomic_sub(int i, atomic_t *v); //原子变量减少 i • ξ  原子变量自增 /自减 • void atomic_inc(atomic_t *v);    // 原子变量增加 1 • void atomic_dec(atomic_t *v);    // 原子变量减少 1 • η  操作并测试 • int atomic_inc_and_test(atomic_t *v); • int atomic_dec_and_test(atomic_t *v); • int atomic_sub_and_test(int i, atomic_t *v); •   操作并返回 • int atomic_add_return(int i, atomic_t *v); • int atomic_sub_return(int i, atomic_t *v); • int atomic_inc_return(atomic_t *v); • int atomic_dec_return(atomic_t *v); • 位原子操作 •   设置 /清除 /反转位 • void set_bit(nr, void *addr); • void clear_bit(nr, void *addr); • void change_bit(nr, void *addr); • �  测试位 • test_bit(nr, void *addr); • η  测试并操作位 • int test_and_set_bit(nr, void *addr); • int test_and_clear_bit(nr, void *addr); • int test_and_change_bit(nr, void *addr);     华清远见 自旋锁 VS 信号量  自旋锁:  忙等待,无调度开销  进程抢占被禁止  锁定期间不能睡觉 • spinlock_t lock; • spin_lock_init(&lock); • spin_lock (&lock) ; //获取自旋锁,保护临界区 • . . ./ /临界区 • spin_unlock (&lock) ; // 解锁  信号量  拿不到就切换进程,有调度开销  锁定期间可以睡觉,不用于中断上下文 • //定义信号量 • DECLARE_MUTEX(mount_sem); • down(&mount_sem);//获取信号量,保护临界区 • . . . • critical section //临界区 • . . . • up(&mount_sem);//释放信号量     华清远见 设备访问方式 读写函数 读写 读写系统 调用 资源不 可获得 资源可 获得 阻塞 阻塞I/O 返回 返回 用户空间 poll()或select() poll系统调用 资源状态变更 非阻塞轮询 I/O 返回 xxx_func() 唤醒 读写 yyy_func() 中断 唤醒 资源可获得 资源状态变更 唤醒 读写函数 阻塞 读写系统 调用 返回 返回 读写函数 读写系统调用 资源可 获得 signal 信号处理函数(进行读写) 异步通知 系统调用 内核空间中断 signal     华清远见 阻塞非阻塞  等待队列:进程等待被唤醒的一种机制  阻塞与非阻塞使用模板 1  static ssize_t xxx_write(struct file *file, const char *buffer, size_t count, 2    loff_t *ppos) 3  { 4    ... 5    DECLARE_WAITQUEUE(wait, current); //定义等待队列 6    add_wait_queue(&xxx_wait, &wait);  //添加等待队列 7   8    ret = count; 9    /* 等待设备缓冲区可写 */ 10   do 11   { 12     avail = device_writable(...); 13     if (avail < 0) 14       __set_current_state(TASK_INTERRUPTIBLE);//改变进程状态 15  16     if (avail < 0) 17     { 18       if (file­>f_flags &O_NONBLOCK)  //非阻塞 19       { 20         if (!ret) 21           ret =  ­ EAGAIN; 22         goto out; 23       } 24       schedule();   //调度其他进程执行 25       if (signal_pending(current))//如果是因为信号唤醒 26       { 27         if (!ret) 28           ret =  ­ ERESTARTSYS; 29         goto out; 30       } 31     } 32   }while (avail < 0); 33  34   /* 写设备缓冲区 */ 35   device_write(...) 36   out: 37   remove_wait_queue(&xxx_wait, &wait);//将等待队列移出等待队列头 38   set_current_state(TASK_RUNNING);//设置进程状态为 TASK_RUNNING 39   return ret; 40 }      华清远见 polling  驱动中 POLL模板 • 1  static unsigned int xxx_poll(struct file *filp, poll_table *wait) • 2  { • 3    unsigned int mask = 0; • 4    struct xxx_dev *dev = filp­>private_data; /*获得设备结构体指针 */ • 6    ...   • 8    poll_wait(filp, &dev­>wait, wait); • 9   • 10   if (...)//可读 • 11   { • 12     mask |= POLLIN | POLLRDNORM; /*标示数据可获得 */ • 13   }    • 15   if (...)//可写 • 16   { • 17     mask |= POLLOUT | POLLWRNORM; /*标示数据可写入 */ • 18   } • 19       • 20   ... • 21   return mask; • 22 }  用户空间 POLL模板 • fd_set fds;  • FD_ZERO(&fds); • FD_SET(fd, &fds); • select(fd + 1, &rfds, &wfds, NULL, NULL); • if (FD_ISSET(fd, &fds)) • { •     printf("Poll monitor:can be access\n"); • }     华清远见 异步 I/O  信号 :软件意义上的“中断”  驱动发出信号 • kill_fasync(&dev­>async_queue, SIGIO, POLL_IN);  用户空间应用程序处理信号 • 24   signal(SIGIO, input_handler); • 25   fcntl(STDIN_FILENO, F_SETOWN, getpid()); • 26   oflags = fcntl(STDIN_FILENO, F_GETFL); • 27   fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); • 8  void input_handler(int num) • 9  { ... • 14   len = read(STDIN_FILENO, &data, MAX_LEN); • 15 ... } f c n t l( fd,  F _S E T OWN,   ge t p i d( ) ) f c n t l( f d ,   F _GE T FL) 信号处理函数 信号 signal()绑定 内核设置 filp­>f_owner 设备驱动 fasync()函数 资源可获得 释放 导致 执行 内核空间 用户空间     华清远见 中断  两个半部 调 度 上半部 中断 (紧急的硬件操作 ) 下半部 (延缓的耗时操作 )  机制  tasklet  工作队列 • 1  /*定义 tasklet和底半部函数并关联 */ • 2  void xxx_do_tasklet(unsigned long); • 3  DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);4   • 5  /*中断处理底半部 */ • 6  void xxx_do_tasklet(unsigned long) • 7  {... • 9  } • 11 /*中断处理顶半部 */ • 12 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) • 13 { ... • 15  tasklet_schedule(&xxx_tasklet); • 16 ...}     华清远见 内存与 I/ O访问  内存空间与 I/ O空间  Linux内核地址空间 0 896M 4G 物理内存 3G 4G Linux内核空间 物理内存映射区 vmalloc分配器区 高端内存映射区 专用页面映射区 保留区  内存申请  kmalloc,get_free_pages:物理连续,线性映射  vmalloc:物理非连续,非线性映射  物理/虚拟地址映射  静态映射  ioremap, ioremap_nocache  mmap:映射到用户空间     华清远见 DMA  cache一致性问题 cache对象 DMA缓冲区  DMA缓冲区  一致性缓冲区 • void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); • void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);  流式 DMA映射  dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);  void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,enum dma_data_direction  direction);  内存地址 /总线地址     华清远见 字符设备驱动  结构  file_operations • 1 struct file_operations xxx_fops = • 2 { • 3   .owner = THIS_MODULE, • 4   .read = xxx_read, • 5   .write = xxx_write, • 6   .ioctl = xxx_ioctl, • 7   ... • 8 };     华清远见 复杂设备驱动  复杂设备驱动的 framework  层次化  结构化  上层不依赖于具体硬件,下层与硬件接口 驱动核心层 硬件操作层 Linux上层 字符设备,块设备 file_operations xxx设备 xxx_ops     华清远见 Framebuffer 应用程序 LCD控制器 用户空间 内核空间 硬件 f b_ r e a d( ) fb_ wr i t e( ) fb _mma p ( ) fb_ i o c t l( ) fbmem.c file_operations xxxfb.c fb _ i n for e g i s t e r_ f ra me bu f f e r( ) 注册 un re g i s t e r_ f r a me bu f fe r( )注销 va r f i x fb_op s fb _c h e c k_va r( ) f b_ s e t_p a r( )  从硬件无关到硬件相关:  file_operations­>fb_info­>fb_ops  从注册 cdev到注册 framebuffer     华清远见 MTD MTD nand core,主要 是nand_base.c nand chip mtd_info nand_chip  层次结构  字符/块设备 ­>mtd_info­>nand_chip     华清远见 TTY设备驱动  层次结构与数据流向 fs/char_dev.c tty_io.c /dev/ttyS0 xxx_tty.c n_tty.c 注册字符设备 struct file_operations tty_register_ldisc() struct tty_disc tty_register_driver() struct tty_driver write() read() 用户空间 tty_write() tty_read() tty核心 ldisc.write() ldisc.read() 线路规程 tty缓冲区 ldisc.receive_buf() driver.write() tty_flip_buffer_push() tty驱动 flip_buffer 中断处理函数 硬件层 数据流 函数调用 xxx_uart.c fs/char_dev.c tty_io.c /dev/ttyS0 serial_core.c n_tty.c 注册字符设备 struct file_operations tty_register_ldisc() struct tty_disc tty_register_driver() struct tty_driver u a r t_r eg i s t e r_ d r i v e r() s t r u c t   u a r t_ o p s tty­>uart     华清远见 块设备驱动  数据结构  block_device_operations  gendisk  request与 bio:表征等待进行的 I/O请求  I/O请求 请求队列 e l v_ n ex t_ r e q u e s t( ) r q_ f o r_ e a ch_b i o( ) b i o_ f o r_e a ch_ s e gmen t( )     华清远见 用户空间的设备驱动  从用户空间访问内存和 I/ O  userspace接口  User Mode SCSI  User Mode USB  User Mode I2C  UIO:drivers/uio/     华清远见 开发环境建设 服务器 Linux gdb、gcc、 samba、 sshd Windows、 ssh Linux mount nfs nfs:目标板应用 程序 使用 存放 串口 Windows、 ssh Linux Linux     华清远见 驱动调试  printk()  /proc  oops  监视工具  kcore  kdb  kgdb  仿真器     华清远见 /printk • 最简单,最常用的方法  调整打印级别: # echo 5 > /proc/sys/kernel/printk  使用宏:通过 make menuconfig选择是否包含打印信息 • #ifdef CONFIG_XXXDEBUG • #xxx_debug(fmt,arg...)     printk(KERN_DEBUG fmt,##arg) • #else • #xxx_debug(fmt,arg...) • #endif     华清远见 /proc  从用户空间获取内核信息的方法 • 7  ssize_t simple_proc_read(char *page, char **start, off_t off, int count, • 8    int*eof, void *data) • 23 ssize_t simple_proc_write(struct file *filp, const char __user *buff, unsigned • 24   long len, void *data) • 55 int __init simple_proc_init(void) • 56 { • 57   proc_entry = create_proc_entry("sim_proc", 0666, NULL); //创建 /proc • 58   if (proc_entry == NULL) • 59   {... • 62   } • 63   else • 64   { • 65     proc_entry­>read_proc = simple_proc_read; • 66     proc_entry­>write_proc = simple_proc_write; • 67     proc_entry­>owner = THIS_MODULE; • 68   } • 72 ...}  使用方法: cat, echo     华清远见 oops • 8  static ssize_t oopsexam_write(struct file  *filp, const char *buf, size_t len, loff_t • 9    *off) • 10 { • 11   int *p=0;   • 12   *p  = 1;  //故意访问 0地址 • 13   return len; • 14 } • Unable to handle kernel NULL pointer dereference at virtual address 00000000 •  printing eip: • c381a013 • *pde = 00000000 • Oops: 0002 [#1] • PREEMPT SMP  • Modules linked in: oops_example • CPU:    0 • EIP:    0060:[]    Not tainted VLI • EFLAGS: 00010286   (2.6.15.5)  • EIP is at oopsexam_write+0x4/0x11 [oops_example] • eax: 00000002   ebx: c2b35480   ecx: 00000000   edx: c381a00f • esi: 00000002   edi: 080e9408   ebp: c2007fa4   esp: c2007f68 • ds: 007b   es: 007b   ss: 0068 • Process bash (pid: 2453, threadinfo=c2006000 task=c2021570) • Stack: c015e036 c2b35480 080e9408 00000002 c2007fa4 00000000 c2b35480  fffffff7  •        080e9408 c2006000 c015e1d1 c2b35480 080e9408 00000002 c2007fa4  00000000  •        00000000 00000000 00000001 00000002 c0102f9f 00000001 080e9408  00000002  • Call Trace: •  [] vfs_write+0xc5/0x18f •  [] sys_write+0x51/0x80 •  [] sysenter_past_esp+0x54/0x75 • Code:  Bad EIP value.     华清远见 strace • 4  main() • 5  { • 6    int fd, num, pos; • 7    char wr_ch[200] = "This is a test of globalmem"; • 8    char rd_ch[200]; • 9    //打开 /dev/globalmem • 10   fd = open("/dev/globalmem", O_RDWR, S_IRUSR | S_IWUSR); • 11   if (fd != ­1 ) • 12   { • 13    //清除 globalmem • 14     if(ioctl(fd, MEM_CLEAR, 0) < 0) • 15     { • 16  printf("ioctl command failed\n"); • 17    } • 18     //读 globalmem • 19     num = read(fd, rd_ch, 200); • 20     printf("%d bytes read from globalmem\n",num); • 21      • 22    //写 globalmem • 23     num = write(fd, wr_ch, strlen(wr_ch)); • 24     printf("%d bytes written into globalmem\n",num); • 25     ... • 26     close(fd); • 27   } • 28 }   • execve("./globalmem_test", ["./globalmem_test"], [/* 24 vars */]) = 0 • ... • open("/dev/globalmem", O_RDWR)          = 3         ­­­­­­­­­打开 的 /dev/globalmem的 fd是 3 • ioctl(3, FIBMAP, 0)                     = 0 • read(3, 0xbff17920, 200)                = 200          ­­­­­­­­­读取到 200个 字节 • ... • write(1, "200 bytes read from globalmem\n", 30200 bytes read from  globalmem • ) = 30                                       ­­­­­向 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 输出设备 (fd 为 1)写入 printf中的字符串 • write(3, "This is a test of globalmem", 27) = 27 • write(1, "27 bytes written into globalmem\n", 3227 bytes written into  globalmem • ) = 32 • ...     华清远见 源代码级调试  目标机“插桩”:  打上 kgdb补丁,这样主机上的 gdb可与目标机的 kgdb通过串口或网口通信。  使用仿真器:  仿真器可直接连接目标机的 JTAG/BDM,这样主机的 gdb就可以通过与仿真器 的通信来控制目标机 Linux kgdb gdb 串口, USB等     华清远见 用户空间测试  编写用户空间的程序访问设备驱动  read  write  ioctl  做成含测试菜单的程序  每一个菜单体现一种功能     华清远见 设备驱动学习方法  牢固掌握内核编程基础知识  并发、同步  内存与 I/ O访问  中断两个半部  理解复杂设备驱动架构的特点  参考同类设备驱动的源代码  动手实践  在 PC上实践 globalmem和 globalfifo  在开发板上学习真实设备的驱动  在工作中学习     华清远见 Linux设备驱动开发详解  主要出发点:  力求用最简单的实例讲解复杂的 知识点 高中化学知识点免费下载体育概论知识点下载名人传知识点免费下载线性代数知识点汇总下载高中化学知识点免费下载 ,以免实例太复杂搅浑读者(驱 动理论部分)  对 Linux设备驱动多种复杂设备的框架结构进行了全面的介绍(驱动框架 部分)  更面向实际的嵌入式 工程 路基工程安全技术交底工程项目施工成本控制工程量增项单年度零星工程技术标正投影法基本原理 ,讲解开发必备的软硬件基础,及开发手段 (调试与移植部分)  提供讨论与交流平台(华清远见, www.linuxdriver.cn,数个 QQ群 )     华清远见 华清远见 Linux驱动课程  嵌入式 Linux驱动初级班  通过本课程的学习,学员可以掌握 Linux下字符设备、块设备、网络设 备的驱动程序开发,同时掌握嵌入式 Linux的系统开发和 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 方法。  嵌入式 Linux驱动开发高级班  本课程以案例教学为主,系统地介绍 Linux下有关 FrameBuffer、MMC 卡、 USB设备的驱动程序开发。  班级规模及环境  为了保证培训效果,增加互动环节,我们坚持小班授课,每期报名人 数限 15人,多余人员安排到下一期进行。人手一套开发板和开发用的 PC主机。     华清远见 让我们一起讨论!     www.farsight.com.cn        谢谢! The success 's road
本文档为【Linux设备驱动开发详解讲座】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_097609
暂无简介~
格式:pdf
大小:1MB
软件:PDF阅读器
页数:34
分类:互联网
上传时间:2011-10-31
浏览量:25