上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体

[cpp] view
plain
?
  1. static struct fb_ops s3c2410fb_ops = {
  2. .owner          = THIS_MODULE,
  3. .fb_check_var   = s3c2410fb_check_var,
  4. .fb_set_par     = s3c2410fb_set_par,
  5. .fb_blank       = s3c2410fb_blank,
  6. .fb_setcolreg   = s3c2410fb_setcolreg,
  7. .fb_fillrect    = cfb_fillrect,
  8. .fb_copyarea    = cfb_copyarea,
  9. .fb_imageblit   = cfb_imageblit,
  10. };

这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765

问:那到底帧缓冲设备的文件操作结构体在哪里呢?

答:在drivers/vedio/fbmem.c文件里。

从入口函数开始看:

[cpp] view
plain
?
  1. static int __init
  2. fbmem_init(void)
  3. {
  4. proc_create("fb", 0, NULL, &fb_proc_fops);
  5. if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
  6. printk("unable to get major %d for fb devs\n", FB_MAJOR);
  7. fb_class = class_create(THIS_MODULE, "graphics");
  8. if (IS_ERR(fb_class)) {
  9. printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
  10. fb_class = NULL;
  11. }
  12. return 0;
  13. }

fbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。

字符设备有一个关键的成员是文件操作结构体

[cpp] view
plain
?
  1. static const struct file_operations fb_fops = {
  2. .owner          = THIS_MODULE,
  3. .read           = fb_read,
  4. .write          = fb_write,
  5. .unlocked_ioctl = fb_ioctl,
  6. .mmap           = fb_mmap,
  7. .open           = fb_open,
  8. .release        = fb_release,
  9. #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
  10. .get_unmapped_area = get_fb_unmapped_area,
  11. #endif
  12. };

理所当然的,我们应当首先看的函数是open函数

[cpp] view
plain
?
  1. static int
  2. fb_open(struct inode *inode, struct file *file)
  3. __acquires(&info->lock)
  4. __releases(&info->lock)
  5. {
  6. int fbidx = iminor(inode);      /* 得到次设备号 */
  7. struct fb_info *info;
  8. int res = 0;
  9. if (fbidx >= FB_MAX)         /* 次设备号有没有大于规定的最大值32 */
  10. return -ENODEV;             /* 没有这样的设备 */
  11. info = registered_fb[fbidx];    /* 使用次设备号得到fb_info结构体 */
  12. if (!info)
  13. request_module("fb%d", fbidx);
  14. /* 再次使用次设备号得到fb_info结构体 */
  15. info = registered_fb[fbidx];
  16. if (!info)
  17. return -ENODEV;
  18. mutex_lock(&info->lock);     /* 获取mutex */
  19. /* 获取模块使用计数module,成功返回非NULL */
  20. if (!try_module_get(info->fbops->owner)) {
  21. res = -ENODEV;
  22. goto out;
  23. }
  24. /* 从registered_fb[]数组项里找到一个fb_info结构体保存到
  25. * struct file结构中的私有信息指针赋值给它呢是为了以后调用
  26. * read、write、ioctl等系统调用时找到这个struct fb_info结构
  27. */
  28. file->private_data = info;
  29. /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */
  30. if (info->fbops->fb_open) {
  31. res = info->fbops->fb_open(info,1);
  32. /* 有默认的fb_open并成功打开就删除模块计数 */
  33. if (res)
  34. module_put(info->fbops->owner);
  35. }
  36. #ifdef CONFIG_FB_DEFERRED_IO        /* 这里没有定义,不用理会 */
  37. if (info->fbdefio)
  38. fb_deferred_io_open(info, inode, file);
  39. #endif
  40. out:
  41. mutex_unlock(&info->lock);   /* 释放mutex */
  42. return res;
  43. }

发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]

问:registered_fb[fbidx]结构体数组是在哪里被设置?

答:register_framebuffer函数里设置registered_fb

[cpp] view
plain
?
  1. /* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
  2. int
  3. register_framebuffer(struct fb_info *fb_info)
  4. {
  5. int i;
  6. struct fb_event event;
  7. struct fb_videomode mode;
  8. /* num_registered_fb代表注册帧缓冲设备的个数 */
  9. if (num_registered_fb == FB_MAX)
  10. return -ENXIO;
  11. if (fb_check_foreignness(fb_info))
  12. return -ENOSYS;
  13. num_registered_fb++;
  14. /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
  15. for (i = 0 ; i < FB_MAX; i++)
  16. if (!registered_fb[i])
  17. break;
  18. fb_info->node = i;
  19. mutex_init(&fb_info->lock);      /* 初始化mutex */
  20. /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
  21. fb_info->dev = device_create(fb_class, fb_info->device,
  22. MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
  23. if (IS_ERR(fb_info->dev)) {
  24. /* Not fatal */
  25. printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
  26. fb_info->dev = NULL;
  27. } else
  28. fb_init_device(fb_info);    /* 对struct fb_info做一些初始化 */
  29. /* 初始化fb_info->pixmap结构体成员 */
  30. if (fb_info->pixmap.addr == NULL) {
  31. fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */
  32. if (fb_info->pixmap.addr) {
  33. fb_info->pixmap.size = FBPIXMAPSIZE; /* 8K */
  34. fb_info->pixmap.buf_align = 1;
  35. fb_info->pixmap.scan_align = 1;
  36. fb_info->pixmap.access_align = 32;
  37. fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
  38. }
  39. }
  40. fb_info->pixmap.offset = 0;
  41. if (!fb_info->pixmap.blit_x)
  42. fb_info->pixmap.blit_x = ~(u32)0;
  43. if (!fb_info->pixmap.blit_y)
  44. fb_info->pixmap.blit_y = ~(u32)0;
  45. if (!fb_info->modelist.prev || !fb_info->modelist.next)
  46. INIT_LIST_HEAD(&fb_info->modelist);  /* 初始化modelist链表 */
  47. fb_var_to_videomode(&mode, &fb_info->var);
  48. fb_add_videomode(&mode, &fb_info->modelist);
  49. /* registered_fb[]数组项在这里被设置 */
  50. registered_fb[i] = fb_info;
  51. event.info = fb_info;
  52. if (!lock_fb_info(fb_info)) /* 上锁 */
  53. return -ENODEV;
  54. fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
  55. unlock_fb_info(fb_info);    /* 解锁 */
  56. return 0;
  57. }

fb_read函数源码分析

[cpp] view
plain
?
  1. static ssize_t
  2. fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  3. {
  4. unsigned long p = *ppos;
  5. /* 通过file结构体的成员得到inode节点 */
  6. struct inode *inode = file->f_path.dentry->d_inode;
  7. /* 获取次设备号 */
  8. int fbidx = iminor(inode);
  9. /* 以次设备号为下标找到一项fb_info结构体 */
  10. struct fb_info *info = registered_fb[fbidx];
  11. u32 *buffer, *dst;
  12. u32 __iomem *src;
  13. int c, i, cnt = 0, err = 0;
  14. unsigned long total_size;
  15. if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
  16. return -ENODEV;
  17. if (info->state != FBINFO_STATE_RUNNING)
  18. return -EPERM;      /* 禁止操作 */
  19. /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */
  20. if (info->fbops->fb_read)
  21. return info->fbops->fb_read(info, buf, count, ppos);
  22. /* 没有默认的读函数就从下面的screen_base里读数据 */
  23. total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */
  24. if (total_size == 0)
  25. total_size = info->fix.smem_len; /* fb缓冲区的长度 */
  26. if (p >= total_size)         /* 调整读的偏移位置 */
  27. return 0;
  28. if (count >= total_size)
  29. count = total_size;         /* 一次性最多读多少个字节 */
  30. if (count + p > total_size)
  31. count = total_size - p;     /* 调整读的位置及能读多少字节 */
  32. /* 分配内存,最大分配4K的大小 */
  33. buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  34. GFP_KERNEL);
  35. if (!buffer)
  36. return -ENOMEM;
  37. src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */
  38. /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */
  39. if (info->fbops->fb_sync)
  40. info->fbops->fb_sync(info);
  41. while (count) {
  42. /* 读多少计数变量,单位为byte */
  43. c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  44. /* buffer是指向刚分配内存的首地址的指针 */
  45. dst = buffer;   /* dst指针指向buffer */
  46. /* 先除以4,因为每次读4个字节 */
  47. for (i = c >> 2; i--; )
  48. *dst++ = fb_readl(src++);   /* 拷贝源虚拟机基地址的数据到目标地址 */
  49. /* 判断是否以字节为单位来读取 */
  50. if (c & 3) {
  51. u8 *dst8 = (u8 *) dst;
  52. u8 __iomem *src8 = (u8 __iomem *) src;
  53. for (i = c & 3; i--;)
  54. *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
  55. src = (u32 __iomem *) src8;
  56. }
  57. /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
  58. if (copy_to_user(buf, buffer, c)) {
  59. err = -EFAULT;  /* 成功拷贝,则err返回值为0 */
  60. break;
  61. }
  62. *ppos += c;     /* 调整偏移位置 */
  63. buf += c;       /* 调整用户的buf */
  64. cnt += c;
  65. /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/
  66. count -= c;
  67. }
  68. kfree(buffer);      /* 释放内存 */
  69. return (err) ? err : cnt;   /* err = 0时,返回被拷贝成功的数量cnt */
  70. }

fb_write函数源码分析

[cpp] view
plain
?
  1. static ssize_t
  2. fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  3. {
  4. unsigned long p = *ppos;
  5. struct inode *inode = file->f_path.dentry->d_inode;
  6. int fbidx = iminor(inode);
  7. struct fb_info *info = registered_fb[fbidx];
  8. u32 *buffer, *src;
  9. u32 __iomem *dst;
  10. int c, i, cnt = 0, err = 0;
  11. unsigned long total_size;
  12. if (!info || !info->screen_base)
  13. return -ENODEV;
  14. if (info->state != FBINFO_STATE_RUNNING)
  15. return -EPERM;
  16. if (info->fbops->fb_write)
  17. return info->fbops->fb_write(info, buf, count, ppos);
  18. total_size = info->screen_size;
  19. if (total_size == 0)
  20. total_size = info->fix.smem_len;
  21. if (p > total_size)
  22. return -EFBIG;
  23. if (count > total_size) {
  24. err = -EFBIG;
  25. count = total_size;
  26. }
  27. if (count + p > total_size) {
  28. if (!err)
  29. err = -ENOSPC;
  30. count = total_size - p;
  31. }
  32. buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  33. GFP_KERNEL);
  34. if (!buffer)
  35. return -ENOMEM;
  36. dst = (u32 __iomem *) (info->screen_base + p);   /* 源虚拟基地址 */
  37. if (info->fbops->fb_sync)
  38. info->fbops->fb_sync(info);
  39. while (count) {
  40. c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  41. src = buffer;   /* buffer为指向刚申请的内存的指针 */
  42. /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */
  43. if (copy_from_user(src, buf, c)) {
  44. err = -EFAULT;
  45. break;
  46. }
  47. for (i = c >> 2; i--; )           /* 以4字节为单位拷贝数据 */
  48. fb_writel(*src++, dst++);   /*     *dst++ = *src++     */
  49. if (c & 3) {                    /* 以字节为单位拷贝数据 */
  50. u8 *src8 = (u8 *) src;
  51. u8 __iomem *dst8 = (u8 __iomem *) dst;
  52. for (i = c & 3; i--; )
  53. fb_writeb(*src8++, dst8++);
  54. dst = (u32 __iomem *) dst8;
  55. }
  56. *ppos += c;
  57. buf += c;
  58. cnt += c;
  59. count -= c;
  60. }
  61. kfree(buffer);
  62. return (cnt) ? cnt : err;
  63. }

fb_ioctl函数源码分析

[cpp] view
plain
?
  1. static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  2. {
  3. struct inode *inode = file->f_path.dentry->d_inode;
  4. int fbidx = iminor(inode);
  5. struct fb_info *info = registered_fb[fbidx];
  6. /* 这个才是真正的fb_ioctl驱动函数 */
  7. return do_fb_ioctl(info, cmd, arg);
  8. }

do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:

[cpp] view
plain
?
  1. switch (cmd) {
  2. case FBIOGET_VSCREENINFO:       /* 获得可变的屏幕参数 */
  3. if (!lock_fb_info(info))    /* 如果info->fbops不为空,则上锁,成功返回1 */
  4. return -ENODEV;
  5. var = info->var;         /* 可变参数变量的设置 */
  6. unlock_fb_info(info);       /* 解锁 */
  7. /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
  8. ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */
  9. break;

fb_mmap源码分析:

[cpp] view
plain
?
  1. /* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,
  2. * 所以需要用到这里的mmap函数,直接将这段内存空间映射到
  3. * 用户空间去,用户空间就能访问这段内存空间了。
  4. */
  5. static int
  6. fb_mmap(struct file *file, struct vm_area_struct * vma)
  7. __acquires(&info->lock)
  8. __releases(&info->lock)
  9. {
  10. int fbidx = iminor(file->f_path.dentry->d_inode);
  11. struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
  12. struct fb_ops *fb = info->fbops;
  13. unsigned long off;
  14. unsigned long start;
  15. u32 len;
  16. if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
  17. return -EINVAL;
  18. off = vma->vm_pgoff << PAGE_SHIFT;
  19. if (!fb)
  20. return -ENODEV;
  21. /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
  22. if (fb->fb_mmap) {
  23. int res;
  24. mutex_lock(&info->lock);
  25. res = fb->fb_mmap(info, vma);
  26. mutex_unlock(&info->lock);
  27. return res;
  28. }
  29. mutex_lock(&info->lock);
  30. /* frame buffer memory */
  31. start = info->fix.smem_start;    /* fb缓冲内存的开始位置(物理地址) */
  32. len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
  33. if (off >= len) {                /* 偏移值大于len长度 */
  34. /* memory mapped io */      /* 内存映射的IO */
  35. off -= len;
  36. if (info->var.accel_flags) {
  37. mutex_unlock(&info->lock);
  38. return -EINVAL;
  39. }
  40. start = info->fix.mmio_start;
  41. len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
  42. }
  43. mutex_unlock(&info->lock);
  44. start &= PAGE_MASK;
  45. if ((vma->vm_end - vma->vm_start + off) > len)
  46. return -EINVAL;
  47. off += start;
  48. vma->vm_pgoff = off >> PAGE_SHIFT;
  49. /* This is an IO map - tell maydump to skip this VMA */
  50. vma->vm_flags |= VM_IO | VM_RESERVED;
  51. fb_pgprotect(file, vma, off);
  52. /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
  53. if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
  54. vma->vm_end - vma->vm_start, vma->vm_page_prot))
  55. return -EAGAIN;
  56. return 0;
  57. }

问:怎么写LCD驱动程序?

1. 分配一个fb_info结构体: framebuffer_alloc

2. 设置

3. 注册: register_framebuffer

4. 硬件相关的操作

linux lcd设备驱动剖析三的更多相关文章

  1. linux lcd设备驱动剖析四

    在"linux lcd设备驱动剖析二"文章中,我们详细分析了s3c24xxfb_probe函数. 文章链接:http://blog.csdn.net/lwj103862095/ar ...

  2. linux lcd设备驱动剖析一

    s3c2440 lcd驱动源码文件是:drivers/video/s3c2410fb.c 看驱动源码首先当然是先看入口函数,这里是s3c2410fb_init函数 [cpp] view plain? ...

  3. linux lcd设备驱动剖析二

    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构. 上一节文章链接:http://blog.csdn.net/lwj103862095/ ...

  4. linux 块设备驱动 (三)块设备驱动开发

    一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...

  5. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  6. linux块设备驱动之实例

    1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major  =  register_blkdev(sbull_major, "sbull&quo ...

  7. Linux 视频设备驱动V4L2最常用的控制命令

    http://blog.csdn.net/shaolyh/article/details/6583226 Linux 视频设备驱动V4L2最常用的控制命令使用说明(1.02) 命令 功能 VIDIOC ...

  8. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  9. Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门

    Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门 转载请在文首保留原文出处:EMC中文支持论坛 - https://community.emc.com/go/chines ...

随机推荐

  1. spring3:对JDBC的支持 之 JDBC模板类

    7.2  JDBC模板类 7.2.1  概述 Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDB ...

  2. 51nod 1270 dp

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1270 简单的线性dp,最近狂刷水题真的是...药丸 差值最大得话要么是峰 ...

  3. L140

    一本载有许多时装照片的杂志是用带有光泽的优质纸印制的.A glossy magazine has lots of pictures of fashionable clothes and is prin ...

  4. DRF 中 解决跨域 与 预检

    DRF 中 解决跨域 与 预检 1 跨域 浏览器的同源策略: 对ajax请求进行阻拦 ps: 对href src属性 不限制 只有浏览器会阻止,requests模块不会存在跨域 (1)解决方案1 JS ...

  5. slab机制总结篇

    一: slab是为了解决内部碎片提出的,还是外部碎片? 为了解决内部碎片. 内部碎片的产生:因为所有的内存分配必须起始于可被 4.8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机 ...

  6. sysbench安装for oracle

    RHEL7.2+ 1.依赖包安装 * autoconf * automake * cdbs * debhelper (>= 9) * docbook-xml * docbook-xsl * li ...

  7. 你所不知道的,Java 中操作符的秘密?

    在 Java 编程的过程中,我们对数据的处理,都是通过操作符来实现的.例如,用于赋值的赋值操作符.用于运算的运算操作符等.用于比较的比较操作符,还包括逻辑操作符.按位操作符.移位操作符.三元操作符等等 ...

  8. NGUI 学习使用

    http://www.tasharen.com/forum/index.php?board=12.0

  9. (七)java转译字符与连接字符串

    转义字符:通过“\”来改变后面字符的意义 \n空格 \t相当于table键 \b相当于回退 class Zyzf { public static void main(String[] args) { ...

  10. Linux部分常用命令学习(一)

    什么是linux命令? 是一个可执行程序,就像我们所看到的位于目录/usr/bin 中的文件一样. 属于这一类的程序,可以编译成二进制文件,诸如用 C 和 C++语言写成的程序, 也可以是由脚本语言写 ...