• 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. 硬件相关的操作

  • 相关阅读:
    PAT (Basic Level) Practice (中文)1002 写出这个数 (20 分)
    PAT (Advanced Level) Practice 1001 A+B Format (20 分)
    BP神经网络(原理及MATLAB实现)
    问题 1676: 算法2-8~2-11:链表的基本操作
    问题 1744: 畅通工程 (并查集)
    链表的基本操作(创建链表,查询元素,删除元素,插入元素等)
    问题 1690: 算法4-7:KMP算法中的模式串移动数组
    问题 1923: [蓝桥杯][算法提高VIP]学霸的迷宫 (BFS)
    Hdu1372 Knight Moves (BFS)
    Problem 2285 迷宫寻宝 (BFS)
  • 原文地址:https://www.cnblogs.com/alan666/p/8312422.html
Copyright © 2020-2023  润新知