lcd驱动框架

在linux内核中,对于通用的一些访问接口和一些子系统,必定会在内核启动的时候先初始化通用的部分,之后加载驱动的时候再注册进来,对于lcd驱动,linux内核也提供了一套通用的接口,对于一个有通用接口的设备驱动,上层访问必定会先经过通用的接口访问,然后再找到具体的硬件操作函数。分析一个驱动的框架,我比较喜欢从入口函数入手,然后以顺藤摸瓜之势贯通整个驱动框架。内核有个fbmem.c文件中的static int __init fbmem_init(void)函数为lcd设备驱动通用访问接口
进行了初始化,看下这个函数的内容:

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

这个函数主要做两件事情,通过register_chrdev函数调用把fb_fops(就是上面说的通用访问入口函数集)与主设备号]FB_MAJOR进行绑定,日后用这个主设备号去生成的设备文件,上层应用访问这些设备文件的入口就会进入这里的fb_fops
通用访问接口,然后就是创建一个class,这个生成一个设备文件的时候需要用到,不妨来看下lcd设备驱动的入口访问函数集

static const struct file_operations fb_fops = {
    .owner =    THIS_MODULE,
    .read =     fb_read,
    .write =    fb_write,
    .unlocked_ioctl = fb_ioctl,//上层用来获取lcd设备驱动的一些重要参数
#ifdef CONFIG_COMPAT
    .compat_ioctl = fb_compat_ioctl,
#endif
    .mmap =     fb_mmap,//上层应用一般不会直接对设备驱动分配的显示缓冲区进行操作,这个接口就是用来把设备驱动显示缓冲区映射到另一块内存去的作用
    .open =     fb_open,//打开一个lcd设备的时候会被调用
    .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,//提供了上层可以像操作文件一样对设备驱动的显示缓冲进行任意地方的访问
};

对于应用层,需要访问一个设备驱动,首先要打开一个设备,那就来看下这里的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;
    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;
    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;
}

这个函数的主要功能通过调用get_fb_info从registered_fb[]数组里面取出该设备驱动的struct fb_info结构,registered_fb[]是内核定义的一个全局数组,用来管理lcd设备,struct fb_info结构是以该设备的次设备号为下标存储在registered_fb[]数组中的,由上面可见调用get_fb_info传入的参数也是该设备的次设备号,得到struct fb_info结构把它放到file->private_data = info;然后判断fbops->fb_ope是否提供,是则调用info结构的fbops->fb_open函数以打开该设备,从上面已经清楚的了解的了打开一个lcd设备驱动的需要得到一个struct fb_info结构,但是暂时还不清楚struct fb_info是如何注册进registered_fb[]数组中的?有疑问,就有解决的办法,内核提供了一个接口int register_framebuffer(struct fb_info *fb_info)来注册lcd驱动,看下这个接口的实现

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])//遍历该数组,找出空闲的一项,供下面该设备把struct fb_info结构注册进来
            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);//在这里生成设备文件,fb_class的创建,上面提到
    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);
    fb_var_to_videomode(&mode, &fb_info->var);//由fb_info的struct fb_var_screeninfo var结构构造一个struct fb_videomode结构
    fb_add_videomode(&mode, &fb_info->modelist);//把struct fb_videomode结构放入链表管理
    registered_fb[i] = fb_info;//把struct 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;
}
内核提供了这个register_framebuffer接口来注册一个lcd驱动,由上面代码可以看到,进来这个函数首先是获得一个互斥锁然后调用do_register_framebuffer进行真正的注册,在这个函数里面从上面可以知道首先是从registered_fb(内核用来管理framebuffer定义的一个数组)数组中找出空的一项,然后把注册进来的struct fb_info结构存放在这一项中,然后以取出空的一项的下标作为次设备号调用device_create创建一个设备文件,这跟上面说的打开一个lcd设备驱动的时候以次设备号为下标从registered_fb数组中取出对应的struct fb_info结构对应起来了,在这里创建一个设备使用的主设备号为FB_MAJOR,这个主设备号在fbmem_init初始化的时候调用register_chrdev把FB_MAJOR跟fb_ops进行绑定了,这使得生成的所有framebuffer设备驱动文件的上层访问入口为这里的fb_ops(通过虚拟文件系统映射,应用层打开或读写一个framebuffer设备驱动文件最终是调用这里的对应函数,如应用层调用open函数打开一个framebuffer设备驱动文件,最终调用这里fb_ops中的open函数)。由上所述,都是围绕一个struct fb_info结构来进行工作,先来看看内核对这个结构的描述
struct fb_info {
    atomic_t count;
    int node;
    int flags;
    struct mutex lock;      /* Lock for open/release/ioctl funcs */
    struct mutex mm_lock;       /* Lock for fb_mmap and smem_* fields */
    struct fb_var_screeninfo var;   /* Current var 存放lcd动态的一些参数*/
    struct fb_fix_screeninfo fix;   /* Current fix 存在lcd静态的参数*/
    struct fb_monspecs monspecs;    /* Current Monitor specs */
    struct work_struct queue;   /* Framebuffer event queue */
    struct fb_pixmap pixmap;    /* Image hardware mapper */
    struct fb_pixmap sprite;    /* Cursor hardware mapper */
    struct fb_cmap cmap;        /* Current cmap */
    struct list_head modelist;      /* mode list */
    struct fb_videomode *mode;  /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
    /* assigned backlight device */
    /* set before framebuffer registration, 
       remove after unregister */
    struct backlight_device *bl_dev;//背光驱动

    /* Backlight level curve */
    struct mutex bl_curve_mutex;    
    u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    struct delayed_work deferred_work;
    struct fb_deferred_io *fbdefio;
#endif
    struct fb_ops *fbops;
    struct device *device;      /* This is the parent */
    struct device *dev;     /* This is this fb device */
    int class_flag;                    /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
    struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
    char __iomem *screen_base;  /* Virtual address 存放显示缓冲区的虚拟地址*/
    unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */ 
    void *pseudo_palette;       /* Fake palette of 16 colors  颜色表*/ 
#define FBINFO_STATE_RUNNING    0
#define FBINFO_STATE_SUSPENDED  1
    u32 state;          /* Hardware state i.e suspend */
    void *fbcon_par;                /* fbcon use-only private area */
    /* From here on everything is device dependent */
    void *par;
    /* we need the PCI or similar aperture base/size not
       smem_start/size as smem_start may just be an object
       allocated inside the aperture so may not actually overlap */
    struct apertures_struct {
        unsigned int count;
        struct aperture {
            resource_size_t base;
            resource_size_t size;
        } ranges[0];
    } *apertures;
};

这个描述结构成员很多,但是主要需要实现的是struct fb_var_screeninfo var;struct fb_fix_screeninfo fix;struct fb_ops *fbops;
struct fb_var_screeninfo {
__u32 xres; /* visible resolution 屏实际的宽度 单位为像素点*/
__u32 yres;//屏实际的高度
__u32 xres_virtual; /* virtual resolution 虚拟屏的宽度 看自己需求,不需要的设为跟实际屏的宽度就好 */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible 虚拟屏和实际屏的宽度差值*/
__u32 yoffset; /* resolution 虚拟屏和实际屏的高度差值 */
__u32 bits_per_pixel; /* guess what 用多少位数据来代表一个像素点,根据屏来设置 */
__u32 grayscale; /* 0 = color, 1 = grayscale, 1表示灰度*/
/* >1 = FOURCC */
/用来描述一个像素点颜色的参数,记录了R、G、B三个成员在一个颜色点的偏移值、长度/
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, except pixclock (of course) */
/*描述pixclock的算法
DOTCLK = fframe(hclk时钟(lcd所在总线的时钟)单位为[mhz]) × (X + HBP + HFP+HSPW) × (Y + VBP + VFP+VSPW) (单位:MHz)
pixclock = 1012/ DOTCLK=1012/ (fframe × (X + HBP + HFP+HSPW) × (Y + VBP + VFP+VSPW)) (单位:皮秒)
在上述程序中,fframe =60,X=240,Y=320,VBP=7, VFP=9,HBP=9, HFP=16,HSPW=1,VSPW=1。
pixclock = 10^12/ DOTCLK=10^12/( fframe × (X + HBP + HFP+HSPW) × (Y + VBP +VFP+VSPW) )
=10^12/ (60*(240+9+16+1)*(320+7+9+1))
= 10^12/ 5378520
=185924.75(单位为皮秒)
*/
__u32 pixclock; /* pixel clock in ps (pico seconds) */
/同步信号的一些描述 根据具体的屏参数来设置/
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};
struct fb_fix_screeninfo {
char id[16]; /* identification string eg “TT Builtin” */
unsigned long smem_start; /* Start of frame buffer mem 显示缓冲区对应的开始物理地址*/
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem 显示缓冲区的长度*/
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes 一行所占的字节数*/
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
};对于这种结构的描述没什么好说struct fb_ops {
/* open/release and usage marking */
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
/* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
/* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);//当前参数不满足显示区域要求的时候被调用
/* set the video mode according to info->var */
int (*fb_set_par)(struct fb_info *info);//设置video模式
/* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* blank display */
int (*fb_blank)(int blank, struct fb_info *info);//使能或者失能lcd
/* pan display */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);//光标
/* Rotates the display */
void (*fb_rotate)(struct fb_info *info, int angle);//用来实现屏的旋转显示
/* wait for blit idle, optional */
int (*fb_sync)(struct fb_info *info);
/* perform fb specific ioctl (optional) */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
/* Handle 32bit compat ioctl (optional) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg);
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info);
/* called at KDB enter and leave time to prepare the console */
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};
讲下应用层如何去获取驱动构造的struct fb_info结构的一些参数,应用层一般是调用ioctl函数发送命令去获取,最终会调用fb_ioctl然后转接下调用static long do_fb_ioctl回到最前面提到的通用访问接口,

static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
{
    struct fb_ops *fb;
    struct fb_var_screeninfo var;
    struct fb_fix_screeninfo fix;
    struct fb_con2fbmap con2fb;
    struct fb_cmap cmap_from;
    struct fb_cmap_user cmap;
    struct fb_event event;
    void __user *argp = (void __user *)arg;
    long ret = 0;
    switch (cmd) {
    case FBIOGET_VSCREENINFO://获取info中的struct fb_var_screeninfo结构
        if (!lock_fb_info(info))
            return -ENODEV;
        var = info->var;
        unlock_fb_info(info);
        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
        break;
    case FBIOPUT_VSCREENINFO://设置info中的struct fb_var_screeninfo结构
        if (copy_from_user(&var, argp, sizeof(var)))
            return -EFAULT;
        if (!lock_fb_info(info))
            return -ENODEV;
        console_lock();
        info->flags |= FBINFO_MISC_USEREVENT;
        ret = fb_set_var(info, &var);
        info->flags &= ~FBINFO_MISC_USEREVENT;
        console_unlock();
        unlock_fb_info(info);
        if (!ret && copy_to_user(argp, &var, sizeof(var)))
            ret = -EFAULT;
        break;
    case FBIOGET_FSCREENINFO://获取info中的struct fb_fix_screeninfo结构
        if (!lock_fb_info(info))
            return -ENODEV;
        fix = info->fix;
        unlock_fb_info(info);

        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
        break;
    case FBIOPUTCMAP:
        if (copy_from_user(&cmap, argp, sizeof(cmap)))
            return -EFAULT;
        ret = fb_set_user_cmap(&cmap, info);
        break;
    case FBIOGETCMAP:
        if (copy_from_user(&cmap, argp, sizeof(cmap)))
            return -EFAULT;
        if (!lock_fb_info(info))
            return -ENODEV;
        cmap_from = info->cmap;
        unlock_fb_info(info);
        ret = fb_cmap_to_user(&cmap_from, &cmap);
        break;
    case FBIOPAN_DISPLAY:
        if (copy_from_user(&var, argp, sizeof(var)))
            return -EFAULT;
        if (!lock_fb_info(info))
            return -ENODEV;
        console_lock();
        ret = fb_pan_display(info, &var);
        console_unlock();
        unlock_fb_info(info);
        if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
            return -EFAULT;
        break;
    case FBIO_CURSOR:
        ret = -EINVAL;
        break;
    case FBIOGET_CON2FBMAP:
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
            return -EFAULT;
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
            return -EINVAL;
        con2fb.framebuffer = -1;
        event.data = &con2fb;
        if (!lock_fb_info(info))
            return -ENODEV;
        event.info = info;
        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
        unlock_fb_info(info);
        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
        break;
    case FBIOPUT_CON2FBMAP:
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
            return -EFAULT;
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
            return -EINVAL;
        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
            return -EINVAL;
        if (!registered_fb[con2fb.framebuffer])
            request_module("fb%d", con2fb.framebuffer);
        if (!registered_fb[con2fb.framebuffer]) {
            ret = -EINVAL;
            break;
        }
        event.data = &con2fb;
        if (!lock_fb_info(info))
            return -ENODEV;
        event.info = info;
        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
        unlock_fb_info(info);
        break;
    case FBIOBLANK://lcd使能或失能的操作
        if (!lock_fb_info(info))
            return -ENODEV;
        console_lock();
        info->flags |= FBINFO_MISC_USEREVENT;
        ret = fb_blank(info, arg);
        info->flags &= ~FBINFO_MISC_USEREVENT;
        console_unlock();
        unlock_fb_info(info);
        break;
    default:
        if (!lock_fb_info(info))
            return -ENODEV;
        fb = info->fbops;
        if (fb->fb_ioctl)//对内核提供的命令不能满足要求,可以在设备驱动自己另外实现fb_ioctl函数
            ret = fb->fb_ioctl(info, cmd, arg);
        else
            ret = -ENOTTY;
        unlock_fb_info(info);
    }
    return ret;
}

内核提供了如上几种命令,另外实现自己定义的命令,可自己实现fb_ioctl函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值