(1)LCDanalysis
基于2.6.25内核的LCD驱动程序结构体platfrom_device和platfrom_driver分析.
ntroduction
platform_device 和 platform_driver
Details
从2.6版本开始引入了platform这个概念, platform可以理解为一种设备类型,就像字符设备、块设备、网络设备一样,为了向内核添加一个platform设备,应该填写两个数据结构 一个为platform_device 另一个为platform_driver
- 先来介绍platform_device
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,定义如下:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
下面我们以s3c2410的lcd为例来具体讲解下 在 arch/arm/plat-s3c24xx/devs.c中 可以看到填写 lcd的代码 如下:
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
/* LCD Controller */
static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD,
.end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};
struct resource结构实际上描述了该设备占用的硬件资源(如地址空间,中断号等s),s3c_lcd_resource描述了内存空间和中断分配情况
从上面可以看出我们通过填写platform_device描述了s3c2410中lcd的情况
- 再看 platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
s3c2410中lcd的platfrom_driver 在那里填充的呢? 在/drivers/vedio/s3c2410fb.c中实现 代码如下:
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2410-lcd",
.owner = THIS_MODULE,
},
};
可以看到该platform设备的驱动函数有s3c2410fb_probe、s3c2410fb_remove等等 这样一个platfrom设备的两部分数据结构都填充完成了
要特别注意一点 s3c_device_lcd 结构体中的 name域 和 s3c2410fb_driver 的driver成员变量 中的name域的名字必须一致 ,这样 platfrom_device 才能和 platfrom_driver关联起来 只有这 样将来才能注册成功 才能回调 函数 s3c2410fb_probe 哈哈
(2)LCDanalysis2
LCD驱动流程实现分析.
Details
我们已经分析了一个platfrom设备的表示, 即就是要填充连个结构体 platfrom_device和platfrom_driver 下面我们着重看s3c2410的lcd驱动的流程
LCD流程分析
module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup)
根据驱动模型 我们知道 加载时调用 s3c2410fb_init 函数 注销时调用 s3c2410fb_cleanup 函数 我们主要分析 加载时的情况 s3c2410fb_init函数实现如下:
int __init s3c2410fb_init(void)
{
int ret = platform_driver_register(&s3c2410fb_driver);
if (ret == 0)
ret = platform_driver_register(&s3c2412fb_driver);;
return ret;
}
在上面的代码中 我们可以看到主要调用了platform_driver_register(&s3c2410fb_driver)函数 简单的 说就是调用platform_driver_register要将向内核注册一个platform设备的驱动 毫无疑问 这个设备是s3c2410中lcd,通过platform_driver_register函数注册该设备的过程中,它会回调s3c2410fb_driver中的s3c2410fb_probe函数
这样我们这个流程就清楚了 过程如下:
module_init() ---> s3c2410fb_init-----> platfrom_driver_register()----> s3c2410fb_driver----> s3c2410fb_probe
在s3c2410fb_probe 里面LCD驱动主要做的工作是填充结构体 struct fb_info 并使用register_framebuffer()函数在系统中进行注册
s3c2410fb_probe 函数代码
static int __init s3c24xxfb_probe(struct platform_device *pdev,
enum s3c_drv_type drv_type)
{
struct s3c2410fb_info *info;
struct s3c2410fb_display *display;
struct fb_info *fbinfo;
struct s3c2410fb_mach_info *mach_info;
struct resource *res;
int ret;
int irq;
int i;
int size;
u32 lcdcon1;
mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
if (mach_info->default_display >= mach_info->num_displays) {
dev_err(&pdev->dev, "default is %d but only %d displays\n",
mach_info->default_display, mach_info->num_displays);
return -EINVAL;
}
display = mach_info->displays + mach_info->default_display;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo)
return -ENOMEM;
platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;
info->drv_type = drv_type;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}
size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}
info->io = ioremap(res->start, size);
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
dprintk("devinit\n");
strcpy(fbinfo->fix.id, driver_name);
/* Stop the video */
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux = 0;
fbinfo->fix.xpanstep = 0;
fbinfo->fix.ypanstep = 0;
fbinfo->fix.ywrapstep = 0;
fbinfo->fix.accel = FB_ACCEL_NONE;
fbinfo->var.nonstd = 0;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_regs;
}
info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_ERR "failed to get lcd clock source\n");
ret = -ENOENT;
goto release_irq;
}
clk_enable(info->clk);
dprintk("got and enabled clock\n");
msleep(1);
/* find maximum required memory size for display */
for (i = 0; i < mach_info->num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres;
smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= 3;
if (fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len = smem_len;
}
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(fbinfo);
if (ret) {
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
dprintk("got video memory\n");
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;
s3c2410fb_init_registers(fbinfo);
s3c2410fb_check_var(&fbinfo->var, fbinfo);
ret = register_framebuffer(fbinfo);
if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n",
ret);
goto free_video_memory;
}
/* create device files */
device_create_file(&pdev->dev, &dev_attr_debug);
printk(KERN_INFO "fb%d: %s frame buffer device\n",
fbinfo->node, fbinfo->fix.id);
return 0;
free_video_memory:
s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
clk_disable(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq, info);
release_regs:
iounmap(info->io);
release_mem:
release_resource(info->mem);
kfree(info->mem);
dealloc_fb:
platform_set_drvdata(pdev, NULL);
framebuffer_release(fbinfo);
return ret;
}
我们在介绍了framebuffer相关知识后 会在重新分析该代码的实现
(3)LCDanalysis3
framebuffer相关知识介绍.
Introduction
framebuffer相关知识.
Details
- framebuffer简介
帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。 Linux为了开发FrameBuffer程序的方便,使用了分层结构。fbmem.c处于Framebuffer设备驱动技术的中心位置。它为上层应用程序提供系统调用,也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己 其中文件s3c2410fb.c就是特定硬件驱动(针对s3c2410芯片的),fbmem.c就是沟通应用层跟s3c2410fb.c的桥梁
- framebuffer中主要结构体介绍
结构体fb_info 位于 kernel/include/linux/fb.h ,fb_info中纪录了帧缓冲设备的全部信息,包括设备的设置参数,状态以及操作函数指针。每一个帧缓冲设备都必须对应一个fb_info结构 还记得在 s3c2410fb_probe 这个函数吗 在这个函数中我们所作的主要工作就是填充结构体 fb_info 结构体
struct fb_info {
int node;
//其中node成员域标示了特定的FrameBuffer,实际上也就是一个FrameBuffer设备的次设备号
int flags;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
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;
//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;
};
- fb_var_screeninfo 结构体
fb_var_screeninfo结构体成员记录用户可修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数 fb_var_screeninfo中的xres定义屏幕一行有多少个点, yres定义屏幕一列有多少个点,bits_per_pixel定义每个点用多少个字节表示 定义如下:
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 Graylevels instead of colors */
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) */
__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 reserved[5]; /* Reserved for future compatibility */
};
- fb_fix_screeninfo 结构体 kernel/include/linux/fb.h
fb_fix_screeninfo中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的 物理地址,长度。当对帧缓冲设备进行映射操作的时候,就是从fb_fix_screeninfo中取得缓冲区物理地址的 结构体如下:
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 reserved[3]; /* Reserved for future compatibility */
};
s3c2410fb_info 结构体 drivers/vedio/s3c2410fb.h 结构体如下:
struct s3c2410fb_info {
struct device *dev;
struct clk *clk;
struct resource *mem;
void __iomem *io;
void __iomem *irq_base;
enum s3c_drv_type drv_type;
struct s3c2410fb_hw regs;
unsigned int palette_ready;
/* keep these registers in case we need to re-write palette */
u32 palette_buffer[256];
u32 pseudo_pal[16];
};
s3c2410fb_mach_info 结构体 kernel/include/asm-arm/arch-s3c2410/fb.h 从字面上我们不难看出 和具体的硬件有关 结构体定义如下:
struct s3c2410fb_mach_info {
struct s3c2410fb_display *displays; /* attached diplays info */
unsigned num_displays; /* number of defined displays */
unsigned default_display;
/* GPIOs */
unsigned long gpcup;
unsigned long gpcup_mask;
unsigned long gpccon;
unsigned long gpccon_mask;
unsigned long gpdup;
unsigned long gpdup_mask;
unsigned long gpdcon;
unsigned long gpdcon_mask;
/* lpc3600 control register */
unsigned long lpcsel;
};
/* LCD description */
struct s3c2410fb_display {
/* LCD type */
unsigned type;
/* Screen size */
unsigned short width;
unsigned short height;
/* Screen info */
unsigned short xres;
unsigned short yres;
unsigned short bpp;
unsigned pixclock; /* pixclock in picoseconds */
unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */
unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */
unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */
unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */
unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */
/* lcd configuration registers */
unsigned long lcdcon5;
};
看到这个结构体应该不陌生 我们在lcd驱动移植步骤的第二步 就是填充了这个结构体 这个结构体和具体的硬件有关
(4)LCDanalysis4
Introduction
我们接着介绍framebuffer 在下面着重介绍struct file_operations 和 struct fb_ops .
Details
- struct file_operations
对于struct file_operation 我们在熟悉不过了 file_operations是提供给上层系统调用的接口,可以直接调用. 定义在 kernel/include/linux/fs.h 中 定义如下:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
对于LCD来说 该结构体的填充 位于kernel/drivers/vedio/fbmem.c中 代码如下:static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.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
};
struct fb_ops 在 kernel/include/linux/fb.h中
struct fb_ops 结构包含在fb_info结构中,指向驱动设备工作所需的函数集。fb_ops结构中包含了很多涵数指针 fb_ops是底层操作的抽象 定义如下
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);
/* 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);
/* 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);
/* save current hardware state */
void (*fb_save_state)(struct fb_info *info);
/* restore saved state */
void (*fb_restore_state)(struct fb_info *info);
/* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
};
对于LCD来说 该结构体的填充位于kernel/drivers/vedio/s3c2410fb.c 中 代码如下:
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,
};
两个结构体之间的关系
用户应用程序通过ioctl()系统调用操作硬件,fb_ops 中的函数就用于支持这些操作。 ioctl()系统调用在文件fbmem.c中实现,通过观察可以发现ioctl()命令与fb_ops’s 中函数的关系: FBIOGET_VSCREENINFO fb_get_var FBIOPUT_VSCREENINFO fb_set_var FBIOGET_FSCREENINFO fb_get_fix FBIOPUTCMAP fb_set_cmap FBIOGETCMAP fb_get_cmap FBIOPAN_DISPLAY fb_pan_display 如果我们定义了fb_XXX_XXX 方法,用户程序就可以使用FBIOXXXX宏的ioctl()操作来操作硬件。
怎么将一个设备文件 也就是dev目录下的文件和file_operation关联起来呢 ? 从上图中 我们可以知道是通过register_chrdev()函数注册实现的 位于kernel/drivers/vedio/fbmem.c中 实现如下
fbmem_init(void)
{
create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);
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);
static void __exit
fbmem_exit(void)
{
remove_proc_entry("fb", NULL);
class_destroy(fb_class);
unregister_chrdev(FB_MAJOR, "fb");
}
module_exit(fbmem_exit);
(5)LCDanalysis5
各个结构体之间的关系 和 register_framebuffer()函数的实现.Introduction
platfrom_device fb_info s3c2410fb_info s3c2410fb_mach_info 之间是怎么关联的. register_framebuffer()是如何实现注册的.
Details
- platform_device fb_info s3c2410fb_info s3c2410fb_mach_info 之间的关系
platform_device ---------------- s3c2410fb_mach_info mach_info= pdev->dev.platform_data 其中 mach_info 为s3c2410fb_info类型的指针 其实mach_info中存放的是LCD的初始化时所用的值 pdev 为结构体platform_device类 型的指针 还记得我们在驱动移植步骤中的第二步就是填充结构体 mach_info 然后 调用 s3c24xx_fb_set_platdata(&qt2410_biglcd_cfg); 就是将 platform_device同mach_info联系起来
fb_info---------------------s3c2410fb info = fbinfo->par info->fb = fbinfo 其中 info 为 结构体s3c2410fb_info类型的指针 fbinfo为结构体fb_info类型的指针
platform_device -------------- fb_info pdev->dev.driver_data = fbinfo 其中 pdev为 结构体platform_device类型的指针 fbinfo为结构体 fb_info 类型的指针
这样所有的主要结构体都串起来了platform_device ------------ platform_driver 这个在前面介绍着两个结构体的时候都说过了 是通过 name属性相互关联起来的
- 总结一下 如下图 :
结构体platform_driver
|
|
| 通过name 相互关联
|
|
|
结构体 platform_device {} 结构体 s3c2410fb_mach_info | |
| |
| |
| |
| |
| |
| |
| 上面两个通过dev. platform_data关联 |
|--------------- 结构体device---------------|
dev.data将上下| |
两个结构体关联 | |
| |
| |
| |
| |
| |
| |
| |
| |
| 你中有我 我中有你 |
结构体 fb_info---------------------------- 结构体 s3c2410fb
分析 register_framebuffer()函数的实现
fbmem.c实现了Linux FrameBuffer的中间层,任何一个FrameBuffer驱动,在系统初始化时,必须向fbmem.c注册,即需要调用register_framebuffer()函数,在这个过程中,设备驱动的信息将会存放入名称为registered_fb数组中 这个数组定义为struct fb_info*registered_fbFB_MAX、int num_registered_fb;它是类型为fb_info的数组,另外num_register_fb则存放了注册过的设备数量
代码如下:
register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;
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;
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), "fb%d", i);
建立的设备节点的次设备号就是该驱动信息在registered_fb存放的位置,即数组下标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);
fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info;
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
return 0;
}
当FrameBuffer驱动进行注册的时候,它将驱动的fb_info结构体记录到全局数组registered_fb中,并动态建立设备节点,进行设备的初始化。