linux lcd设备驱动剖析三
上一节文章中详细地剖析了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,
- };
问:那到底帧缓冲设备的文件操作结构体在哪里呢?
答:在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;
- }
字符设备有一个关键的成员是文件操作结构体
- 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
- };
- 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;
- }
问: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;
- }
- 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 */
- }
- 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;
- }
- 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);
- }
- 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;
- /* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,
- * 所以需要用到这里的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;
- }
1. 分配一个fb_info结构体: framebuffer_alloc
2. 设置
3. 注册: register_framebuffer
4. 硬件相关的操作