framebuffer驱动qemu仿真调试--Apple的学习笔记

一,前言

之前ubuntu18.04配置网桥支持qemu模拟开发板挂载NFS启动LCD--Apple的学习笔记
已经完成了qemu仿真LCD,并且实现了framebuffer的应用。于是想调试下源码,原因是光看代码可能会理解错误。但是vexpress a9开发板看了下设备树后发现他用的不是LCDC的framebuffer而是用了drm框架。
但是复习了下DRM框架后,发现其实framebuffer都是注册的,那么就可以当做fbdev用了。然后分析代码的话还是选简单的s3c2410fb.c来分析,看了代码后,发现和我之前分析fbtft使用framebuffer子系统类似,可以参考Linux驱动OLED屏st7735s(framebuffer学习)--Apple的学习笔记。为了能实际调试我在直接下载了一个被二次开发过的qemu,能支持mini2440仿真。

二,s3c2410fb.c关键代码分析(内核版本5.4.61)

1.设置寄存器为LCDC设置总线时序margin timer及刷新频率,LCD内存起始地址
s3c2410fb_set_par ->s3c2410fb_activate_var --->s3c2410fb_calculate_tft_lcd_regs和s3c2410fb_set_lcdaddr
2.probe函数会挂上具体芯片的操作函数
fbinfo->fbops = &s3c2410fb_ops;

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,
};
`
2.probe函数中填充fbinfo后注册register_framebuffer,然后用户空间就可以操作文件系统了。
ret = register_framebuffer(fbinfo);
```c
static const struct file_operations fb_fops = {
    .owner =    THIS_MODULE,
    .read =     fb_read,
    .write =    fb_write,
    .unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = fb_compat_ioctl,
#endif
    .mmap =     fb_mmap,
    .open =     fb_open,
    .release =  fb_release,
#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
    (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
     !defined(CONFIG_MMU))
    .get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    .fsync =    fb_deferred_io_fsync,
#endif
    .llseek =   default_llseek,
};

3.fb_mmap在s3c2410.c中没有调用。PAGE_ALIGNvm_iomap_memory为关键函数,一个是alian一个是将虚拟地址转到物理地址,具体的page MMU相关函数内容我不展开,将来做专题分析。

static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
    int fbidx = iminor(file->f_path.dentry->d_inode);
    struct fb_info *info = registered_fb[fbidx];
    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;
    mutex_lock(&info->mm_lock);
    //没有注册mmap所以没进入
    if (fb->fb_mmap) {   
        printk("mmap-1\n");
        int res;
        res = fb->fb_mmap(info, vma);
        mutex_unlock(&info->mm_lock);
        return res;
    }
    // 走如下路径
    /* frame buffer memory */
    start = info->fix.smem_start;
    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    printk("mmap-start=0x%x,len=%d\n",start,len);
    if (off >= len) {
        printk("mmap-off=0x%x\n",off);
        /* memory mapped io */
        off -= len;
        if (info->var.accel_flags) {
            mutex_unlock(&info->mm_lock);
            return -EINVAL;
        }
        start = info->fix.mmio_start;
        len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    }
    mutex_unlock(&info->mm_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);
    printk("mmap-2\n");
    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;
}

三,调试

之前我记得fbtft还使用了通用的mmap,并且开启了defio。s3c2410bf.c中没有使用自己的mmap,我添加调试信息来验证,打印输出的结果也如此,实际仿真与理解一致。见下图左下角log。


image.png

你可能感兴趣的:(framebuffer驱动qemu仿真调试--Apple的学习笔记)