上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
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文件里。
从入口函数开始看:
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类(图形类)。
字符设备有一个关键的成员是文件操作结构体
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函数
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
/* 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函数源码分析
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函数源码分析
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函数源码分析
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来设置各种命令,这里仅举例说明:
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源码分析:
/* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问, * 所以需要用到这里的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驱动程序?