首页
登录 | 注册

armlinux lcd驱动分析2-UP

 
 

在/kernel/drivers/video/fbmem.c 文件中:
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*/

int register_framebuffer(struct fb_info *fb_info)

/**
* unregister_framebuffer - releases a frame buffer device
* @fb_info: frame buffer info structure
* Unregisters a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*/

int unregister_framebuffer(struct fb_info *fb_info)

static int fb_open(struct inode *inode, struct file *file)

static int fb_release(struct inode *inode, struct file *file)

static ssize_t fb_read(struct file *file, char *buf, size_t count, loff_t *ppos)

static ssize_t fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos)

static int fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,  unsigned long arg)

static int fb_mmap(struct file *file, struct vm_area_struct * vma)

    在该文件中包含了所有驱动LCD 的函数。在fb_read 和fb_write 这两个函数中,都对framebuffer 进行了操作。
fb_read 函数中:
   char *base_addr;
   base_addr = info->disp->screen_base;
   count -= copy_to_user(buf, base_addr p, count);

fb_write 函数中:
char *base_addr;
base_addr = info->disp->screen_base;
count -= copy_from_user(base_addr p, buf, count);


所读写的framebuffer 的基地址就是disp->screen_base,也就是fbi->screen_cpu 所指的framebuffer 的虚拟地址。

从而得到:

      framebuffer(虚拟地址)
         |---------------|
         |      ...      |
  -------|---------------| <-- fbi->map_cpu
         |      16K    |
分配了|---------------| <-- fbi->screen_cpu = display->screen_base
  176K |              |
共11个|              |  160K = 10个PAGE
PAGE  |      160K  |  可以容下所需的150K 视频缓冲区大小
(16K)  |              |
         |               |
  -------|---------------|-------
         |      ...      |
         |---------------|

其中display->screen_base 结构在/kernel/include/video/fbcon.h 文件中定义。

    得出结论,在分配framebuffer 时一共返回两个指针,虽然是同一块内存空间,但一个返回的是实际的物理地址,另一个返回的是经过地址转换的虚拟地址。在设置LCD 控制器中framebuffer 起始地址寄存器时,用的是所分配内存的物理地址;而当要对framebuffer 进行读写操作时,用的是同一块内存的物理地址所转换后的虚拟地址。由此可以知道,内核在对每个I/O 地址进行读写操作时用的都是经过转换的虚拟地址。


------------------------------------------------------------------------
在/kernel/include/asm-arm/arch-s3c2410/memory.h 文件中:
/*
* Page offset: 3GB
*/
#define PAGE_OFFSET (0xc0000000UL)

#define PHYS_OFFSET (0x30000000UL)
/*
* We take advantage of the fact that physical and virtual address can be the
* saem. Thu NUMA code is handling the large holes that might exist between
* all memory banks.
*/
#define __virt_to_phys__is_a_macro
#define __phys_to_virt__is_a_macro
#define __virt_to_phys(x) ((x) - PAGE_OFFSET PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET PAGE_OFFSET)


    物理地址与虚拟地址的间隔为:PAGE_OFFSET - PHYS_OFFSET。这样一来,以上对物理地址和虚拟地址之间转换的宏定义就很好理解了。
虚拟地址转为物理地址宏:__virt_to_phys(x)
= (x) - (PAGE_OFFSET - PHYS_OFFSET) = (x) - PAGE_OFFSET PHYS_OFFSET
物理地址转为虚拟地址宏:__phys_to_virt(x)
= (x) (PAGE_OFFSET - PHYS_OFFSET) = (x) PAGE_OFFSET - PHYS_OFFSET

    内核虚拟地址和实际物理地址仅仅是相差一个偏移量(PAGE_OFFSET),可以很方便的将其转化为物理内存地址,同时内核也提供了virt_to_phys() 函数将内核虚拟空间中的物理影射区地址转化为物理地址。


/*
* Virtual view <-> DMA view memory address translations
* virt_to_bus: Used to translate the virtual address to an
*              address suitable to be passed to set_dma_addr
* bus_to_virt: Used to convert an address for DMA operations
*              to an address that the kernel can use.
*/
#define __virt_to_bus__is_a_macro
#define __bus_to_virt__is_a_macro
#define __virt_to_bus(x) __virt_to_phys(x)
#define __bus_to_virt(x) __phys_to_virt(x)


    这里注意:__virt_to_bus(x) 就等于__virt_to_phys(x)

------------------------------------------------------------------------
在/kernel/include/asm-arm/memory.h 文件中:
/*
* These are *only* valid on the kernel direct mapped RAM memory.
*/
static inline unsigned long virt_to_phys(volatile void *x)
{
return __virt_to_phys((unsigned long)(x));
}
/*
* Virtual <-> DMA view memory address translations
* Again, these are *only* valid on the kernel direct mapped RAM
* memory.
*/
#define virt_to_bus(x)  (__virt_to_bus((unsigned long)(x)))


    由上面的分析可知:virt_to_bus(x) 和virt_to_phys(volatile void *x) 这两个函数调用的都是__virt_to_phys(x) 即((x) - PAGE_OFFSET PHYS_OFFSET)。所以这两个调用都是将虚拟地址转换为了物理地址。


------------------------------------------------------------------------
在/kernel/arch/arm/mm/ioremap.c 文件中:
/*
* Remap an arbitrary physical address space into the kernel virtual
* address space. Needed when the kernel wants to access high addresses
* directly.
*
* NOTE! We need to allow non-page-aligned mappings too: we will obviously
* have to convert them into an offset in a page-aligned mapping, but the
* caller shouldn\'t need to know that small detail.
*
* \'flags\' are the extra L_PTE_ flags that you want to specify for this
* mapping.  See include/asm-arm/proc-armv/pgtable.h for more information.
*/
void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)

    ioremap 函数的作用是将physical address以及bus address映射为kernel的 virtual adrress。
    ioremap 的作用是把I/O 内存地址(物理地址)映射到虚拟地址空间,使用之前需要分配I/O 内存区域。但是,ioremap 函数的内部实现并不是简单的((IO的物理地址)-0x10000000 0xE4000000)。所以,得到的虚拟地址可能是不同的。
    因为linux 用的是页面映射机制,CPU 不能按物理地址来访问存储空间,而必须使用虚拟地址,所以必须反向的从物理地址出发找到一片虚存空间并建立起映射。


--  作者:asan-xu
--  发布时间:2007-10-11 8:50:17

--  
//*******************************************************
//* 2007.6.22
//*******************************************************
在/kernel/drivers/video/fbmem.c 文件中:
static struct file_operations fb_fops = {
owner:  THIS_MODULE,
read:  fb_read,
write:  fb_write,
ioctl:  fb_ioctl,
mmap:  fb_mmap,
open:  fb_open,
release: fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
get_unmapped_area: get_fb_unmapped_area,
#endif
};

这个结构中定义了对LCD 所分配的framebuffer 进行读写操作,以及一些内存映射之类的函数,这些源函数都在该文件中。


/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
*
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/

int register_framebuffer(struct fb_info *fb_info)

这个是对LCD 的framebuffer 设备进行注册时调用到的注册函数,该函数在/kernel/drivers/video/s3c2410fb.c 文件的s3c2410fb_init 函数里的最后部分被调用。

fb_info->devfs_handle =
     devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT,
       FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO,
       &fb_fops, NULL);

在注册函数的最后部分,将前面的fb_fops 结构里的各个驱动函数的入口点传入到devfs_register()设备注册函数中。


--  作者:asan-xu
--  发布时间:2007-10-11 8:50:36
--  
//*******************************************************
//* 2007.6.26
//*******************************************************
    上面这个devfs_register 函数(原型在/kernel/fs/devfs/base.c 文件中)执行前,在fbmem_init 函数中调用了函数:

devfs_handle = devfs_mk_dir (NULL, "fb", NULL);


在/kernel/fs/devfs/base.c 文件中:
/**
* devfs_mk_dir - Create a directory in the devfs namespace.
* @dir: The handle to the parent devfs directory entry. If this is %NULL the
*  new name is relative to the root of the devfs.
* @name: The name of the entry.
* @info: An arbitrary pointer which will be associated with the entry.
*
* Use of this function is optional. The devfs_register() function
* will automatically create intermediate directories as needed. This function
* is provided for efficiency reasons, as it provides a handle to a directory.
* Returns a handle which may later be used in a call to devfs_unregister().
* On failure %NULL is returned.
*/

devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)


    这个devfs_mk_dir 函数会在设备文件系统中创建一个名为fb 的目录,并返回一个带有devfs 设备文件系统目录结构的数据结构变量devfs_handle。然后把这个数据结构作为下一步调用devfs_register 函数时的参数,该参数在调用设备文件系统注册清除函数devfs_unregister 时也要作为参数传入。


/**
* devfs_register - Register a device entry.
* @dir: The handle to the parent devfs directory entry. If this is %NULL the
*  new name is relative to the root of the devfs.
* @name: The name of the entry.
* @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
* @major: The major number. Not needed for regular files.
* @minor: The minor number. Not needed for regular files.
* @mode: The default file mode.
* @ops: The &file_operations or &block_device_operations structure.
*  This must not be externally deallocated.
* @info: An arbitrary pointer which will be written to the @private_data
*  field of the &file structure passed to the device driver. You can set
*  this to whatever you like, and change it once the file is opened (the next
*  file opened will not see this change).
*
* Returns a handle which may later be used in a call to devfs_unregister().
* On failure %NULL is returned.
*/

devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
          unsigned int flags,
          unsigned int major, unsigned int minor,
          umode_t mode, void *ops, void *info)

    函数devfs_register 是设备文件系统的主册函数,会在刚才创建的目录下再创建一个名为name 的设备文件节点。返回的devfs_handle_t 数据结构变量会在调用设备文件系统注册清除函数devfs_unregister 时作为参数传入。


fb_info->devfs_handle =
     devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT,
       FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO,
       &fb_fops, NULL);


    调用该函数后,会在刚才创建的fb 目录下再创建一个名为0 (name_buf)的设备文件节点,并赋予相应的权限,以及主次设备号和file_operations 结构的函数入口。
    这样一来,Linux 设备文件的创建,删除和目录层次等都由各设备驱动程序管理,再也不用手工创建设备文件节点了,再也不需要mknod 时查找对应的主设备号了,也不用依靠复杂的脚本来管理设备文件了。


相关文章

  • 一.i2c-dev驱动分析 1.1.设备驱动注册         分析这个驱动,还是从module_init()和module_exit()开始,程序如下: static int __init i2c_dev_init(void) {    ...
  • Linux  I2C驱动分析(一)----I2C架构和总线驱动
    一.I2C总线原理         I2C是一种常用的串行总线,由串行数据线SDA 和串线时钟线SCL组成.I2C是一种多主机控制总线,它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以,而 I ...
  • 一.板级设备扫描         针对上一篇博客最后的i2c_scan_static_board_info(adap)函数处,首先先看下在系统启动的时候板级设备的注册.         针对我现在使用的开发板,对于I2C设备注册程序如下: ...
  •              为了充分发挥博客平台价值,利于更多技术爱好者使用与学习,博客将鼓励大家管理现有的文章,制作博客文集,博客将每周选择一个创建符合实用.前沿.深度内容文集推荐在博客首页,并奖励文集创作者图书一本.年终也将评选出年度最受 ...
  • 前面12节的课程,主要针对 Linux 内核中 GNU C 扩展的一些常用 C 语言语法进行了分析.GNU C 的这些扩展语法,主要用来完善 C 语言标准和编译优化.而通过 C 标准的发展过程我们又发现,对于一些编译器扩展的一些特性,或者其 ...
  • dpdk net_virtio前端驱动实现分析
    dpdk net_virtio前端驱动实现分析 --lvyilong316 和kernel中的vhost-net对应,net_virtio是dpdk中实现的网络virtio的前端.相对于kernel dpdk的net_virtio实现要简单 ...

2020 unjeep.com webmaster#unjeep.com
12 q. 0.014 s.
京ICP备10005923号