linux lcd设备驱动剖析三

上一节文章中详细地剖析了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.   
  6.     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  
  7.         printk("unable to get major %d for fb devs ", FB_MAJOR);  
  8.   
  9.     fb_class = class_create(THIS_MODULE, "graphics");  
  10.     if (IS_ERR(fb_class)) {  
  11.         printk(KERN_WARNING "Unable to create fb class; errno = %ld ", PTR_ERR(fb_class));  
  12.         fb_class = NULL;  
  13.     }  
  14.     return 0;  
  15. }  
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.   
  10.     if (fbidx >= FB_MAX)         /* 次设备号有没有大于规定的最大值32 */  
  11.         return -ENODEV;             /* 没有这样的设备 */  
  12.     info = registered_fb[fbidx];    /* 使用次设备号得到fb_info结构体 */  
  13.     if (!info)  
  14.         request_module("fb%d", fbidx);  
  15.   
  16.     /* 再次使用次设备号得到fb_info结构体 */  
  17.     info = registered_fb[fbidx];  
  18.     if (!info)  
  19.         return -ENODEV;  
  20.       
  21.     mutex_lock(&info->lock);     /* 获取mutex */  
  22.   
  23.     /* 获取模块使用计数module,成功返回非NULL */  
  24.     if (!try_module_get(info->fbops->owner)) {  
  25.         res = -ENODEV;  
  26.         goto out;  
  27.     }  
  28.   
  29.     /* 从registered_fb[]数组项里找到一个fb_info结构体保存到 
  30.      * struct file结构中的私有信息指针赋值给它呢是为了以后调用 
  31.      * read、write、ioctl等系统调用时找到这个struct fb_info结构 
  32.      */  
  33.     file->private_data = info;     
  34.   
  35.     /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */  
  36.     if (info->fbops->fb_open) {  
  37.         res = info->fbops->fb_open(info,1);     
  38.   
  39.         /* 有默认的fb_open并成功打开就删除模块计数 */  
  40.         if (res)  
  41.             module_put(info->fbops->owner);  
  42.     }  
  43. #ifdef CONFIG_FB_DEFERRED_IO        /* 这里没有定义,不用理会 */  
  44.     if (info->fbdefio)  
  45.         fb_deferred_io_open(info, inode, file);  
  46. #endif  
  47. out:  
  48.     mutex_unlock(&info->lock);   /* 释放mutex */  
  49.     return res;  
  50. }  
发现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.   
  9.     /* num_registered_fb代表注册帧缓冲设备的个数 */  
  10.     if (num_registered_fb == FB_MAX)  
  11.         return -ENXIO;  
  12.   
  13.     if (fb_check_foreignness(fb_info))  
  14.         return -ENOSYS;  
  15.   
  16.     num_registered_fb++;  
  17.   
  18.     /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */  
  19.     for (i = 0 ; i < FB_MAX; i++)  
  20.         if (!registered_fb[i])    
  21.             break;  
  22.     fb_info->node = i;  
  23.     mutex_init(&fb_info->lock);      /* 初始化mutex */  
  24.   
  25.     /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */  
  26.     fb_info->dev = device_create(fb_class, fb_info->device,  
  27.                      MKDEV(FB_MAJOR, i), NULL, "fb%d", i);  
  28.     if (IS_ERR(fb_info->dev)) {  
  29.         /* Not fatal */  
  30.         printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld ", i, PTR_ERR(fb_info->dev));  
  31.         fb_info->dev = NULL;  
  32.     } else  
  33.         fb_init_device(fb_info);    /* 对struct fb_info做一些初始化 */  
  34.   
  35.   
  36.     /* 初始化fb_info->pixmap结构体成员 */  
  37.     if (fb_info->pixmap.addr == NULL) {  
  38.         fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */  
  39.         if (fb_info->pixmap.addr) {  
  40.             fb_info->pixmap.size = FBPIXMAPSIZE; /* 8K */  
  41.             fb_info->pixmap.buf_align = 1;  
  42.             fb_info->pixmap.scan_align = 1;  
  43.             fb_info->pixmap.access_align = 32;  
  44.             fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;  
  45.         }  
  46.     }     
  47.     fb_info->pixmap.offset = 0;  
  48.   
  49.     if (!fb_info->pixmap.blit_x)  
  50.         fb_info->pixmap.blit_x = ~(u32)0;  
  51.   
  52.     if (!fb_info->pixmap.blit_y)  
  53.         fb_info->pixmap.blit_y = ~(u32)0;  
  54.   
  55.     if (!fb_info->modelist.prev || !fb_info->modelist.next)  
  56.         INIT_LIST_HEAD(&fb_info->modelist);  /* 初始化modelist链表 */  
  57.   
  58.     fb_var_to_videomode(&mode, &fb_info->var);  
  59.     fb_add_videomode(&mode, &fb_info->modelist);  
  60.   
  61.     /* registered_fb[]数组项在这里被设置 */  
  62.     registered_fb[i] = fb_info;  
  63.   
  64.     event.info = fb_info;  
  65.   
  66.       
  67.     if (!lock_fb_info(fb_info)) /* 上锁 */  
  68.         return -ENODEV;  
  69.     fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);  
  70.     unlock_fb_info(fb_info);    /* 解锁 */  
  71.     return 0;  
  72. }  
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.     /* 获取次设备号 */  
  9.     int fbidx = iminor(inode);  
  10.     /* 以次设备号为下标找到一项fb_info结构体 */  
  11.     struct fb_info *info = registered_fb[fbidx];  
  12.       
  13.     u32 *buffer, *dst;  
  14.     u32 __iomem *src;  
  15.     int c, i, cnt = 0, err = 0;  
  16.     unsigned long total_size;  
  17.   
  18.     if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */  
  19.         return -ENODEV;  
  20.   
  21.     if (info->state != FBINFO_STATE_RUNNING)  
  22.         return -EPERM;      /* 禁止操作 */  
  23.   
  24.     /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */  
  25.     if (info->fbops->fb_read)  
  26.         return info->fbops->fb_read(info, buf, count, ppos);  
  27.   
  28.     /* 没有默认的读函数就从下面的screen_base里读数据 */  
  29.     total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */  
  30.   
  31.     if (total_size == 0)  
  32.         total_size = info->fix.smem_len; /* fb缓冲区的长度 */  
  33.   
  34.     if (p >= total_size)         /* 调整读的偏移位置 */  
  35.         return 0;  
  36.   
  37.     if (count >= total_size)  
  38.         count = total_size;         /* 一次性最多读多少个字节 */  
  39.   
  40.     if (count + p > total_size)  
  41.         count = total_size - p;     /* 调整读的位置及能读多少字节 */  
  42.   
  43.     /* 分配内存,最大分配4K的大小 */  
  44.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,  
  45.              GFP_KERNEL);         
  46.     if (!buffer)  
  47.         return -ENOMEM;  
  48.   
  49.     src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */  
  50.   
  51.     /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */  
  52.     if (info->fbops->fb_sync)       
  53.         info->fbops->fb_sync(info);  
  54.   
  55.     while (count) {  
  56.         /* 读多少计数变量,单位为byte */  
  57.         c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;   
  58.           
  59.         /* buffer是指向刚分配内存的首地址的指针 */  
  60.         dst = buffer;   /* dst指针指向buffer */  
  61.   
  62.         /* 先除以4,因为每次读4个字节 */  
  63.         for (i = c >> 2; i--; )             
  64.             *dst++ = fb_readl(src++);   /* 拷贝源虚拟机基地址的数据到目标地址 */  
  65.   
  66.         /* 判断是否以字节为单位来读取 */  
  67.         if (c & 3) {                      
  68.             u8 *dst8 = (u8 *) dst;  
  69.             u8 __iomem *src8 = (u8 __iomem *) src;  
  70.   
  71.             for (i = c & 3; i--;)  
  72.                 *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */  
  73.   
  74.             src = (u32 __iomem *) src8;  
  75.         }  
  76.   
  77.         /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */  
  78.         if (copy_to_user(buf, buffer, c)) {  
  79.             err = -EFAULT;  /* 成功拷贝,则err返回值为0 */  
  80.             break;  
  81.         }  
  82.         *ppos += c;     /* 调整偏移位置 */  
  83.         buf += c;       /* 调整用户的buf */  
  84.         cnt += c;  
  85.         /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/  
  86.         count -= c;       
  87.     }  
  88.   
  89.     kfree(buffer);      /* 释放内存 */  
  90.   
  91.     return (err) ? err : cnt;   /* err = 0时,返回被拷贝成功的数量cnt */  
  92. }  
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.   
  13.     if (!info || !info->screen_base)  
  14.         return -ENODEV;  
  15.   
  16.     if (info->state != FBINFO_STATE_RUNNING)  
  17.         return -EPERM;  
  18.   
  19.     if (info->fbops->fb_write)  
  20.         return info->fbops->fb_write(info, buf, count, ppos);  
  21.       
  22.     total_size = info->screen_size;  
  23.   
  24.     if (total_size == 0)  
  25.         total_size = info->fix.smem_len;  
  26.   
  27.     if (p > total_size)  
  28.         return -EFBIG;  
  29.   
  30.     if (count > total_size) {  
  31.         err = -EFBIG;  
  32.         count = total_size;  
  33.     }  
  34.   
  35.     if (count + p > total_size) {  
  36.         if (!err)  
  37.             err = -ENOSPC;  
  38.   
  39.         count = total_size - p;  
  40.     }  
  41.   
  42.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,  
  43.              GFP_KERNEL);  
  44.     if (!buffer)  
  45.         return -ENOMEM;  
  46.   
  47.     dst = (u32 __iomem *) (info->screen_base + p);   /* 源虚拟基地址 */  
  48.   
  49.     if (info->fbops->fb_sync)  
  50.         info->fbops->fb_sync(info);  
  51.   
  52.     while (count) {  
  53.         c = (count > PAGE_SIZE) ? PAGE_SIZE : count;  
  54.         src = buffer;   /* buffer为指向刚申请的内存的指针 */  
  55.   
  56.   
  57.         /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */  
  58.         if (copy_from_user(src, buf, c)) {  
  59.             err = -EFAULT;  
  60.             break;  
  61.         }  
  62.   
  63.         for (i = c >> 2; i--; )           /* 以4字节为单位拷贝数据 */  
  64.             fb_writel(*src++, dst++);   /*     *dst++ = *src++     */  
  65.   
  66.         if (c & 3) {                    /* 以字节为单位拷贝数据 */  
  67.             u8 *src8 = (u8 *) src;  
  68.             u8 __iomem *dst8 = (u8 __iomem *) dst;  
  69.   
  70.             for (i = c & 3; i--; )  
  71.                 fb_writeb(*src8++, dst8++);  
  72.   
  73.             dst = (u32 __iomem *) dst8;  
  74.         }  
  75.   
  76.         *ppos += c;  
  77.         buf += c;  
  78.         cnt += c;  
  79.         count -= c;  
  80.     }  
  81.   
  82.     kfree(buffer);  
  83.   
  84.     return (cnt) ? cnt : err;  
  85. }  
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.   
  7.     /* 这个才是真正的fb_ioctl驱动函数 */  
  8.     return do_fb_ioctl(info, cmd, arg);   
  9. }  
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.   
  8.         /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */  
  9.         ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */  
  10.         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.   
  17.     if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  
  18.         return -EINVAL;  
  19.     off = vma->vm_pgoff << PAGE_SHIFT;  
  20.     if (!fb)  
  21.         return -ENODEV;  
  22.   
  23.     /* 如果registered_fb[]里有默认的fb_mmap就使用它 */  
  24.     if (fb->fb_mmap) {  
  25.         int res;  
  26.         mutex_lock(&info->lock);  
  27.         res = fb->fb_mmap(info, vma);  
  28.         mutex_unlock(&info->lock);  
  29.         return res;  
  30.     }  
  31.   
  32.     mutex_lock(&info->lock);  
  33.   
  34.     /* frame buffer memory */  
  35.     start = info->fix.smem_start;    /* fb缓冲内存的开始位置(物理地址) */  
  36.     len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);  
  37.     if (off >= len) {                /* 偏移值大于len长度 */  
  38.         /* memory mapped io */      /* 内存映射的IO */  
  39.         off -= len;  
  40.         if (info->var.accel_flags) {  
  41.             mutex_unlock(&info->lock);  
  42.             return -EINVAL;  
  43.         }  
  44.         start = info->fix.mmio_start;  
  45.         len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);  
  46.     }  
  47.     mutex_unlock(&info->lock);  
  48.     start &= PAGE_MASK;  
  49.     if ((vma->vm_end - vma->vm_start + off) > len)  
  50.         return -EINVAL;  
  51.     off += start;  
  52.     vma->vm_pgoff = off >> PAGE_SHIFT;  
  53.     /* This is an IO map - tell maydump to skip this VMA */  
  54.     vma->vm_flags |= VM_IO | VM_RESERVED;  
  55.     fb_pgprotect(file, vma, off);  
  56.   
  57.     /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */  
  58.     if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,  
  59.                  vma->vm_end - vma->vm_start, vma->vm_page_prot))  
  60.         return -EAGAIN;  
  61.     return 0;  
  62. }  
问:怎么写LCD驱动程序?
1. 分配一个fb_info结构体: framebuffer_alloc
2. 设置
3. 注册: register_framebuffer
4. 硬件相关的操作

原文地址:https://www.cnblogs.com/alan666/p/8312422.html