FrameBuffer Driver Analysis of Android N Graphics

Posted by mattw on Mon, 01 Jul 2019 23:18:35 +0200

Step 1. Let's first look at the initialization of the FrameBuffer driver

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;
}

#ifdef MODULE
module_init(fbmem_init);

This is the FB-driven initialization entry. Let's first look at some of the main things that have been done here.
1 > Create a device node of FB under proc through proc_create, and register several interfaces specified by fb_proc_fops. The interfaces are as follows:

static const struct file_operations fb_proc_fops = {
        .owner          = THIS_MODULE,
        .open           = proc_fb_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = seq_release,
};

The registered interface is used to operate on the proc/fb device node.
2 > Register the character device FB module through register_chrdev, and also register the operation interface function fb_fops of the FB module, as follows:

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,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
        .get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
        .fsync =        fb_deferred_io_fsync,
#endif
        .llseek =       default_llseek,
};

This allows the external code to use these interfaces to operate the fb device.
3 > Create a graphics directory under sys/class through class_create.

The process of opening step 2. FB device
In fact, the upper code will call the open interface to open the device node of dev/fbx, so let's continue to analyze the process of opening.

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;

        info = get_fb_info(fbidx);
        if (!info) {
                request_module("fb%d", fbidx);
                info = get_fb_info(fbidx);
                if (!info)
                        return -ENODEV;
        }
        if (IS_ERR(info))
                return PTR_ERR(info);

        mutex_lock(&info->lock);
        if (!try_module_get(info->fbops->owner)) {
                res = -ENODEV;
                goto out;
        }
        file->private_data = info;
        info->file = file;
        if (info->fbops->fb_open) {
                res = info->fbops->fb_open(info,1);
                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);
        if (res)
                put_fb_info(info);
        return res;
}

The code logic is relatively simple. First, the secondary device number is obtained by passing in the file node, then the corresponding fb_info is extracted from the registered_fb array according to the secondary device number, and then the extracted fb_info is saved in the private_data of the open FB device, and then the obtained fb_info is obtained through the private_data of the open FB device. fb_open in o opens the current FB device, so far, the process of opening is completed.

Step 3. FB device registration process

int
register_framebuffer(struct fb_info *fb_info)
{
        int ret;

        mutex_lock(&registration_lock);
        ret = do_register_framebuffer(fb_info);
        mutex_unlock(&registration_lock);

        return ret;
}

static int do_register_framebuffer(struct fb_info *fb_info)
{
        int i;
        struct fb_event event;
        struct fb_videomode mode;

        if (fb_check_foreignness(fb_info))
                return -ENOSYS;

        do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
                                         fb_is_primary_device(fb_info));

        if (num_registered_fb == FB_MAX)
                return -ENXIO;

        num_registered_fb++;
        for (i = 0 ; i < FB_MAX; i++)
                if (!registered_fb[i])
                        break;
        fb_info->node = i;
        atomic_set(&fb_info->count, 1);
        mutex_init(&fb_info->lock);
        mutex_init(&fb_info->mm_lock);

        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);

        if (fb_info->pixmap.addr == NULL) {
                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
                if (fb_info->pixmap.addr) {
                        fb_info->pixmap.size = FBPIXMAPSIZE;
                        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);

        if (fb_info->skip_vt_switch)
                pm_vt_switch_required(fb_info->dev, false);
        else
                pm_vt_switch_required(fb_info->dev, true);

        fb_var_to_videomode(&mode, &fb_info->var);
        fb_add_videomode(&mode, &fb_info->modelist);
        registered_fb[i] = fb_info;

        event.info = fb_info;
        if (!lock_fb_info(fb_info))
                return -ENODEV;
        console_lock();
        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
        console_unlock();
        unlock_fb_info(fb_info);
        return 0;
}

Let's introduce the implementation of do_register_framebuffer:
First, remove the FB whose address range is conflicting, and then determine whether the number of registered FBS exceeds 32. Then create a device node of FB under dev, initialize the fb_info, register the fb_info into the registered_fb array, and finally send a FrameBuffer registration using the Linux event notification mechanism. Part FB_EVENT_FB_REGISTERED.
To be continued

Topics: Linux