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 ...
随机推荐
- Java 注解指导手册 – 终极向导
原文链接 原文作者:Dani Buiza 译者:Toien Liu 校对:深海 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Gee ...
- 【LABVIEW到C#】4》String的操作之Search and Replace.vi
C#封装如下: public class SearchAndRepalce : Darrenstring { public bool replaced; private string stringou ...
- 牛客练习赛13D
定义一个数字为幸运数字当且仅当它的所有数位都是4或者7.比如说,47.744.4都是幸运数字而5.17.467都不是.现在想知道在1...n的第k小的排列(permutation,https://en ...
- IOS-支付宝
一.使用支付宝进行一个完整的支付功能,大致有以下步骤: 与支付宝签约,获得商户ID(partner)和账号ID(seller) 下载相应的公钥私钥文件(加密签名用) 下载支付宝SDK 生成订单信息 ...
- DGA短域名(360样本) mark下 下次分析可以参考
一共100万,按照长度排序后的前2000个: aagst.cnacyke.wsaefrd.ccaiqxg.ukakplh.pwalurx.pwamsmz.ccaogtp.inawwgf.inayveg ...
- MVC 框架中的缓存
在程序中加入缓存的目的很多是为了提高程序的性能,提高数据的查找效率,在MVC框架中也引入了非常多的缓存,比如Controller的匹配查找,Controller,ControllerDescripto ...
- dateTimePicker编辑状态下,取值不正确的问题
当对dateTimePicker进行编辑,回车,调用函数处理dateTimePicker的value值时,其取值结果是你编辑之前的值,而不是你编辑后的值,虽然dateTimePicker.text的值 ...
- 2 秒杀系统模拟基础实现,使用Redis实现
这一篇,我们来使用redis进行数据存储. 新建一个redis的service实现类 package com.tianyalei.service; import com.tianyalei.model ...
- 类里边的构造函数可以被private修饰,在类的内部创建对象。利用这种特性创建单类模式
- HDU 3720 Arranging Your Team
先分组,然后暴力:注意 初始化时不要为0 会有负数:我直接二进制枚举: dfs是正解:呵呵 #include <iostream> #include <cstdio> #in ...