linux lcd设备驱动剖析三
上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
plain?
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
问:那到底帧缓冲设备的文件操作结构体在哪里呢?
答:在drivers/vedio/fbmem.c文件里。
从入口函数开始看:
plain?
- static int __init
- fbmem_init(void)
- {
- proc_create("fb", 0, NULL, &fb_proc_fops);
- if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
- printk("unable to get major %d for fb devs\n", FB_MAJOR);
- fb_class = class_create(THIS_MODULE, "graphics");
- if (IS_ERR(fb_class)) {
- printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
- fb_class = NULL;
- }
- return 0;
- }
fbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。
字符设备有一个关键的成员是文件操作结构体
plain?
- static const struct file_operations fb_fops = {
- .owner = THIS_MODULE,
- .read = fb_read,
- .write = fb_write,
- .unlocked_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
- };
理所当然的,我们应当首先看的函数是open函数
plain?
- static int
- fb_open(struct inode *inode, struct file *file)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- int fbidx = iminor(inode); /* 得到次设备号 */
- struct fb_info *info;
- int res = 0;
- if (fbidx >= FB_MAX) /* 次设备号有没有大于规定的最大值32 */
- return -ENODEV; /* 没有这样的设备 */
- info = registered_fb[fbidx]; /* 使用次设备号得到fb_info结构体 */
- if (!info)
- request_module("fb%d", fbidx);
- /* 再次使用次设备号得到fb_info结构体 */
- info = registered_fb[fbidx];
- if (!info)
- return -ENODEV;
- mutex_lock(&info->lock); /* 获取mutex */
- /* 获取模块使用计数module,成功返回非NULL */
- if (!try_module_get(info->fbops->owner)) {
- res = -ENODEV;
- goto out;
- }
- /* 从registered_fb[]数组项里找到一个fb_info结构体保存到
- * struct file结构中的私有信息指针赋值给它呢是为了以后调用
- * read、write、ioctl等系统调用时找到这个struct fb_info结构
- */
- file->private_data = info;
- /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */
- if (info->fbops->fb_open) {
- res = info->fbops->fb_open(info,1);
- /* 有默认的fb_open并成功打开就删除模块计数 */
- if (res)
- module_put(info->fbops->owner);
- }
- #ifdef CONFIG_FB_DEFERRED_IO /* 这里没有定义,不用理会 */
- if (info->fbdefio)
- fb_deferred_io_open(info, inode, file);
- #endif
- out:
- mutex_unlock(&info->lock); /* 释放mutex */
- return res;
- }
发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
问:registered_fb[fbidx]结构体数组是在哪里被设置?
答:register_framebuffer函数里设置registered_fb
plain?
- /* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
- int
- register_framebuffer(struct fb_info *fb_info)
- {
- int i;
- struct fb_event event;
- struct fb_videomode mode;
- /* num_registered_fb代表注册帧缓冲设备的个数 */
- if (num_registered_fb == FB_MAX)
- return -ENXIO;
- if (fb_check_foreignness(fb_info))
- return -ENOSYS;
- num_registered_fb++;
- /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
- for (i = 0 ; i < FB_MAX; i++)
- if (!registered_fb[i])
- break;
- fb_info->node = i;
- mutex_init(&fb_info->lock); /* 初始化mutex */
- /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
- fb_info->dev = device_create(fb_class, fb_info->device,
- MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
- if (IS_ERR(fb_info->dev)) {
- /* Not fatal */
- printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
- fb_info->dev = NULL;
- } else
- fb_init_device(fb_info); /* 对struct fb_info做一些初始化 */
- /* 初始化fb_info->pixmap结构体成员 */
- if (fb_info->pixmap.addr == NULL) {
- fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */
- if (fb_info->pixmap.addr) {
- fb_info->pixmap.size = FBPIXMAPSIZE; /* 8K */
- fb_info->pixmap.buf_align = 1;
- fb_info->pixmap.scan_align = 1;
- fb_info->pixmap.access_align = 32;
- fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- }
- }
- fb_info->pixmap.offset = 0;
- if (!fb_info->pixmap.blit_x)
- fb_info->pixmap.blit_x = ~(u32)0;
- if (!fb_info->pixmap.blit_y)
- fb_info->pixmap.blit_y = ~(u32)0;
- if (!fb_info->modelist.prev || !fb_info->modelist.next)
- INIT_LIST_HEAD(&fb_info->modelist); /* 初始化modelist链表 */
- fb_var_to_videomode(&mode, &fb_info->var);
- fb_add_videomode(&mode, &fb_info->modelist);
- /* registered_fb[]数组项在这里被设置 */
- registered_fb[i] = fb_info;
- event.info = fb_info;
- if (!lock_fb_info(fb_info)) /* 上锁 */
- return -ENODEV;
- fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
- unlock_fb_info(fb_info); /* 解锁 */
- return 0;
- }
fb_read函数源码分析
plain?
- static ssize_t
- fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- /* 通过file结构体的成员得到inode节点 */
- struct inode *inode = file->f_path.dentry->d_inode;
- /* 获取次设备号 */
- int fbidx = iminor(inode);
- /* 以次设备号为下标找到一项fb_info结构体 */
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *dst;
- u32 __iomem *src;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
- if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
- return -ENODEV;
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM; /* 禁止操作 */
- /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */
- if (info->fbops->fb_read)
- return info->fbops->fb_read(info, buf, count, ppos);
- /* 没有默认的读函数就从下面的screen_base里读数据 */
- total_size = info->screen_size; /* x*y*4,x,y分别为屏幕分辨率 */
- if (total_size == 0)
- total_size = info->fix.smem_len; /* fb缓冲区的长度 */
- if (p >= total_size) /* 调整读的偏移位置 */
- return 0;
- if (count >= total_size)
- count = total_size; /* 一次性最多读多少个字节 */
- if (count + p > total_size)
- count = total_size - p; /* 调整读的位置及能读多少字节 */
- /* 分配内存,最大分配4K的大小 */
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- src = (u32 __iomem *) (info->screen_base + p); /* 源虚拟基地址 */
- /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
- while (count) {
- /* 读多少计数变量,单位为byte */
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- /* buffer是指向刚分配内存的首地址的指针 */
- dst = buffer; /* dst指针指向buffer */
- /* 先除以4,因为每次读4个字节 */
- for (i = c >> 2; i--; )
- *dst++ = fb_readl(src++); /* 拷贝源虚拟机基地址的数据到目标地址 */
- /* 判断是否以字节为单位来读取 */
- if (c & 3) {
- u8 *dst8 = (u8 *) dst;
- u8 __iomem *src8 = (u8 __iomem *) src;
- for (i = c & 3; i--;)
- *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
- src = (u32 __iomem *) src8;
- }
- /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
- if (copy_to_user(buf, buffer, c)) {
- err = -EFAULT; /* 成功拷贝,则err返回值为0 */
- break;
- }
- *ppos += c; /* 调整偏移位置 */
- buf += c; /* 调整用户的buf */
- cnt += c;
- /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/
- count -= c;
- }
- kfree(buffer); /* 释放内存 */
- return (err) ? err : cnt; /* err = 0时,返回被拷贝成功的数量cnt */
- }
fb_write函数源码分析
plain?
- static ssize_t
- fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *src;
- u32 __iomem *dst;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
- if (!info || !info->screen_base)
- return -ENODEV;
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
- if (info->fbops->fb_write)
- return info->fbops->fb_write(info, buf, count, ppos);
- total_size = info->screen_size;
- if (total_size == 0)
- total_size = info->fix.smem_len;
- if (p > total_size)
- return -EFBIG;
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
- count = total_size - p;
- }
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- dst = (u32 __iomem *) (info->screen_base + p); /* 源虚拟基地址 */
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- src = buffer; /* buffer为指向刚申请的内存的指针 */
- /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */
- if (copy_from_user(src, buf, c)) {
- err = -EFAULT;
- break;
- }
- for (i = c >> 2; i--; ) /* 以4字节为单位拷贝数据 */
- fb_writel(*src++, dst++); /* *dst++ = *src++ */
- if (c & 3) { /* 以字节为单位拷贝数据 */
- u8 *src8 = (u8 *) src;
- u8 __iomem *dst8 = (u8 __iomem *) dst;
- for (i = c & 3; i--; )
- fb_writeb(*src8++, dst8++);
- dst = (u32 __iomem *) dst8;
- }
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
- kfree(buffer);
- return (cnt) ? cnt : err;
- }
fb_ioctl函数源码分析
plain?
- static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- /* 这个才是真正的fb_ioctl驱动函数 */
- return do_fb_ioctl(info, cmd, arg);
- }
do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:
plain?
- switch (cmd) {
- case FBIOGET_VSCREENINFO: /* 获得可变的屏幕参数 */
- if (!lock_fb_info(info)) /* 如果info->fbops不为空,则上锁,成功返回1 */
- return -ENODEV;
- var = info->var; /* 可变参数变量的设置 */
- unlock_fb_info(info); /* 解锁 */
- /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
- ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */
- break;
fb_mmap源码分析:
plain?
- /* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,
- * 所以需要用到这里的mmap函数,直接将这段内存空间映射到
- * 用户空间去,用户空间就能访问这段内存空间了。
- */
- static int
- fb_mmap(struct file *file, struct vm_area_struct * vma)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- int fbidx = iminor(file->f_path.dentry->d_inode);
- struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
- struct fb_ops *fb = info->fbops;
- unsigned long off;
- unsigned long start;
- u32 len;
- if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
- return -EINVAL;
- off = vma->vm_pgoff << PAGE_SHIFT;
- if (!fb)
- return -ENODEV;
- /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
- if (fb->fb_mmap) {
- int res;
- mutex_lock(&info->lock);
- res = fb->fb_mmap(info, vma);
- mutex_unlock(&info->lock);
- return res;
- }
- mutex_lock(&info->lock);
- /* frame buffer memory */
- start = info->fix.smem_start; /* fb缓冲内存的开始位置(物理地址) */
- len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
- if (off >= len) { /* 偏移值大于len长度 */
- /* memory mapped io */ /* 内存映射的IO */
- off -= len;
- if (info->var.accel_flags) {
- mutex_unlock(&info->lock);
- return -EINVAL;
- }
- start = info->fix.mmio_start;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
- }
- mutex_unlock(&info->lock);
- start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
- return -EINVAL;
- off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
- /* This is an IO map - tell maydump to skip this VMA */
- vma->vm_flags |= VM_IO | VM_RESERVED;
- fb_pgprotect(file, vma, off);
- /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
- if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot))
- return -EAGAIN;
- return 0;
- }
问:怎么写LCD驱动程序?
1. 分配一个fb_info结构体: framebuffer_alloc
2. 设置
3. 注册: register_framebuffer
4. 硬件相关的操作
linux lcd设备驱动剖析三的更多相关文章
- linux lcd设备驱动剖析四
在"linux lcd设备驱动剖析二"文章中,我们详细分析了s3c24xxfb_probe函数. 文章链接:http://blog.csdn.net/lwj103862095/ar ...
- linux lcd设备驱动剖析一
s3c2440 lcd驱动源码文件是:drivers/video/s3c2410fb.c 看驱动源码首先当然是先看入口函数,这里是s3c2410fb_init函数 [cpp] view plain? ...
- linux lcd设备驱动剖析二
上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构. 上一节文章链接:http://blog.csdn.net/lwj103862095/ ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- 深入理解Linux字符设备驱动
文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...
- linux块设备驱动之实例
1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major = register_blkdev(sbull_major, "sbull&quo ...
- Linux 视频设备驱动V4L2最常用的控制命令
http://blog.csdn.net/shaolyh/article/details/6583226 Linux 视频设备驱动V4L2最常用的控制命令使用说明(1.02) 命令 功能 VIDIOC ...
- Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】
本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...
- Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门
Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门 转载请在文首保留原文出处:EMC中文支持论坛 - https://community.emc.com/go/chines ...
随机推荐
- spring3:对JDBC的支持 之 JDBC模板类
7.2 JDBC模板类 7.2.1 概述 Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDB ...
- 51nod 1270 dp
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1270 简单的线性dp,最近狂刷水题真的是...药丸 差值最大得话要么是峰 ...
- L140
一本载有许多时装照片的杂志是用带有光泽的优质纸印制的.A glossy magazine has lots of pictures of fashionable clothes and is prin ...
- DRF 中 解决跨域 与 预检
DRF 中 解决跨域 与 预检 1 跨域 浏览器的同源策略: 对ajax请求进行阻拦 ps: 对href src属性 不限制 只有浏览器会阻止,requests模块不会存在跨域 (1)解决方案1 JS ...
- slab机制总结篇
一: slab是为了解决内部碎片提出的,还是外部碎片? 为了解决内部碎片. 内部碎片的产生:因为所有的内存分配必须起始于可被 4.8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机 ...
- sysbench安装for oracle
RHEL7.2+ 1.依赖包安装 * autoconf * automake * cdbs * debhelper (>= 9) * docbook-xml * docbook-xsl * li ...
- 你所不知道的,Java 中操作符的秘密?
在 Java 编程的过程中,我们对数据的处理,都是通过操作符来实现的.例如,用于赋值的赋值操作符.用于运算的运算操作符等.用于比较的比较操作符,还包括逻辑操作符.按位操作符.移位操作符.三元操作符等等 ...
- NGUI 学习使用
http://www.tasharen.com/forum/index.php?board=12.0
- (七)java转译字符与连接字符串
转义字符:通过“\”来改变后面字符的意义 \n空格 \t相当于table键 \b相当于回退 class Zyzf { public static void main(String[] args) { ...
- Linux部分常用命令学习(一)
什么是linux命令? 是一个可执行程序,就像我们所看到的位于目录/usr/bin 中的文件一样. 属于这一类的程序,可以编译成二进制文件,诸如用 C 和 C++语言写成的程序, 也可以是由脚本语言写 ...