LCD架构浅析&&mmap

来源:互联网 发布:药房软件 编辑:程序博客网 时间:2024/05/21 07:10


一. 关于mmap

1. linux I/O端口与I/O内存

 IO端口:当一个寄存器或者内存位于IO空间时;/*x86体系结构处理器*/
 
 IO内存:当一个内存或者寄存器位于内存空间时;/*ARM体系结构处理器*/
 
 在访问IO内存之前需要首先使用ioremap函数将设备所处的物理地址映射到虚拟地址上。
 
 ----why?
 在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。
 
 但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,
 
 驱动程序并不能直接通过物理地址访问I/O内存资源(因为要考虑考Linux系统设计分层的理念--yang提示),而必须将它们映射到核心虚地址空间内(通过页表),
 
 然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。
 
 
2. 关于ioremap和mmap函数

 ioremap主要是用来将内核空间的一段虚拟空间映射到外部设备的存取区(设备I/O地址空间).
 
 mmap主要是将用户空间的一段虚拟地址映射到设备的I/O空间中.用户空间进程就可以直接访问设备内存。
 
 驱动程序中完成的功能是在file_operations中的mmap方法.
 
3. mmap系统调用过程(应用层-->驱动中mmap实现方法)

 mmap->
 -->
  SYSCALL_DEFINE6(mmap....
  {
   
  }
   --->SYSCALL_DEFINE6(mmap_pgoff...
  
  sys_mmap_pgoff
  {
   file = fget(fd); /*通过fd获得相应的struct file文件对象指针*/
   
   do_mmap_pgoff(); /*用来完成后续的内存映射工作*/
     
  }
    --->do_mmap_pgoff 
      {
        addr = get_unmapped_area(file, addr, len, pgoff, flags);
        
        mmap_region(file, addr, len, flags, vm_flags, pgoff);
      }
     
     /*do_mmap_pgoff 根据用户进程调用mmap时,传入的参数,
    
       构造一个struct vm_area_struct对象示例,然后再调用
      
      file->f_op->mmap(),对应着驱动程序中的mmap的方法。
     */
    
     ---->
       mmap_region()
    {   
     if (file) {
      vma->vm_file = file;
      get_file(file);
      error = file->f_op->mmap(file, vma); /*设备驱动中mmap方法*/
     } 
    }
 
4. 驱动中mmap实现
 设备驱动中的mmap方法主要功能是将内核提供的用户进程空间来自MMAP区域的一段内存
 
 (内核将这段区域以struct vm_area_struct对象作为参数的方式告诉设备驱动程序)
 
 映射到设备内存上。
 
 --->驱动程序需要配置相应的页目录表项的方式来完成(linux内核提供了一些接口函数来提供映射)
 
 --->操作页目录表项建立页面映射的一些接口函数
  
  int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
      unsigned long pfn, unsigned long size, pgprot_t prot)
  
   /**
   * remap_pfn_range - remap kernel memory to userspace
   * @vma: user vma to map to
   * @addr: target user address to start at
   * @pfn: physical address of kernel memory -->页框号(page frame number)
     在页面大小为4KB的系统中,一个物理地址右移12位即可得到该物理地址对应的页框号
   * @size: size of map area
   * @prot: page protection flags for this mapping
   *
   *  Note: this is only safe if the mm semaphore is held when called.
   */


二. LCD架构分析    
/*******************************LCD架构分析****************************************/

fb.h中定义了一些主要的数据结构,Frameuffer设备在很大程度上依靠了下面的3个数据结构。

struct  fb_var_screeninfo、struct  fb_fix_screeninfo   和 struct  fb_info

1. 第一个结构体用来描述图形卡的特性,通常是被用户设置的

2. 第二个结构体定义了图形卡的硬件特性,是不能改变的,用户选定了LCD控制器和显示器后,那么硬件特性也就定下来了

3. 第三个结构体定义了当前图形卡Framebuffer设备的独立状态,一个图形卡可能有两个Framebuffer,

   在这种情况下,就需要两个fb_info结构。这个结构是唯一内核空间可见的

数据结构:

1 struct fb_cmap结构体:用来定义帧缓存区设备的颜色表(colormap)信息,
      
      可以通过ioctl()函数的FBIOGETCMAP和FBIOPUTCMAP命令来设置colormap

2 struct fb_info 结构体:包含当前显示卡的状态信息,struct  fb_info结构体只对内核可见

3 struct fb_ops结构体:应用程序使用这些函数操作底层的LCD硬件,fb_ops结构中定义的方法用于支持这些操作。
       
        这些操作需要驱动开发人员来实现

4 struct fb_fix_screeninfo结构体:定义了显卡信息,如framebuffer内存的起始地址,地址的长度

5 struct fb_var_screeninfo结构体:描述了一种显示模式的所有信息,如宽、高、颜色深度等,
         
          不同显示模式对应不同的信息

在Framebuffer设备驱动程序中,这些结构是互相关联,互相配合使用的。

只有每一个结构体起到自己的作用,才能使整个Framebuffer设备驱动程序正常工作。


/************************LCD接口层(fbmem.c)*************************************/
  
  在LCD注册的时候用的是字符设备
    register_chrdev(FB_MAJOR,"fb",&fb_fops)
 
  为控制器驱动提供统一的调用接口。

 /*在设备控制器层调用register_framebuffer注册了字函数符设备,设备号是29
 //fbmem.c
  int register_framebuffer(struct fb_info *fb_info)
  {
   for (i = 0 ; i < FB_MAX; i++)
  if (!registered_fb[i])
   break;
   
   fb_info->dev = device_create(fb_class, fb_info->device,
         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
  }
 */
 
1.当应用层调用open函数后
  -->open( )
  ->fb_open()
  {
   int fbidx = iminor(inode);
   struct fb_info *info;
   
   info = registered_fb[fbidx]; 
   
   if (info->fbops->fb_open) {
    res = info->fbops->fb_open(info,1); /*底层函数基本没有实现*/ 
   }
  }
  
  fb_open接着调用--->s3c_fb_open()//在底层控制驱动中实现
 
2.当应用层调用write函数后
  -->wirte()
 
   -->fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
      {
        if (info->fbops->fb_write) /*s3c6410底层没有实现*/
    return info->fbops->fb_write(info, buf, count, ppos);
   
   buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    GFP_KERNEL);
     
   dst = (u8 __iomem *) (info->screen_base + p); /*获取lcd缓冲区地址*/
  
   while (count) {
    c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    src = buffer;
  
    if (copy_from_user(src, buf, c)) {
     err = -EFAULT;
     break;
    }
  
    memcpy(dst, src, c);  /*往缓冲区写数据*/
    dst += c;
    src += c;
    *ppos += c;
    buf += c;
    cnt += c;
    count -= c;
   }
   
   kfree(buffer);
      }
     
3. 当调用read函数后--->过程和write相似,不分析了


/*************************LCD控制器驱动(s3c-fb.c)***********************/

lcd控制器驱动--采用平台总线形式,直接分析 .probe = s3c_fb_probe函数。
1.
 static int  s3c_fb_probe(struct platform_device *pdev)
 {
  res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  
  sfb->irq_no = res->start;
  
  ret = request_irq(sfb->irq_no, s3c_fb_irq,
      0, "s3c_fb", sfb);
     
  s3c_fb_probe_win(sfb, win, fbdrv->win[win], &sfb->windows[win]); 
 }
2.
 static int  s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
          struct s3c_fb_win_variant *variant,
          struct s3c_fb_win **res)
   {
    fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
       palette_size * sizeof(u32), sfb->dev);
      
  s3c_fb_alloc_memory(sfb, win);  /********fbi->screen_base初始化********/
  
  fbinfo->fbops = &s3c_fb_ops;  /*为fbinfo->fbops 赋值, s3c_fb_ops属于控制器操作方法*/
  
  /* create initial colour map */
  ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
  if (ret == 0)
   fb_set_cmap(&fbinfo->cmap, fbinfo);
 
  s3c_fb_set_par(fbinfo);  /*初始化fb_fix_screeninfo*/
  
  ret = register_framebuffer(fbinfo); 
   }  
   
3. static int  s3c_fb_alloc_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)   
 {
  /*Virtual address 帧缓存虚拟地址*/
  fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,
           &map_dma, GFP_KERNEL);  /******关于dma?******/
  
  memset(fbi->screen_base, 0x0, size);
  
  fbi->fix.smem_start = map_dma; /* Start of frame buffer mem (physical address)*/
 }

原创粉丝点击