分析内种LCD驱动程序框架
LCD在fbmem.c文件中
1.找到init.函数
static int __initfbmem_init(void)
{
做的工作: (1).if (register_chrdev(FB_MAJOR,"fb",&fb_fops))注册字符处理函数结构体,以及生成设备号,
(2)fb_class = class_create(THIS_MODULE, "graphics");生成一个类
}
2.打开注册设备中的字符函数结构体fb_fops
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.ioctl = fb_ioctl,
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
};
APP层如果要打开这个设备,就会执行经过系统调用执行fb_open函数
3.分析这个函数
static int fb_open(struct inode *inode, struct file *file)
{
int fbidx = iminor(inode);生成一个次设备号
struct fb_info *info;定义一个fb_info结构体
if (!(info = registered_fb[fbidx]))把registered_fb[]结构体数组中以这个设备的次设备号为索引,把数组中的值赋值给info
}
4.如果应用程序要对这个驱动进行读写操作,就会调用read函数
static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
那么读函数主要做的工作:
int fbidx = iminor(inode);获得设备的次设备号
struct fb_info *info = registered_fb[fbidx];在fb_info结构体中以此设备号为索引找到一个成员赋值给info变量。
if (info->fbops->fb_read)如果字符结构体函数定义fb_read,则执行相应处理
return info->fbops->fb_read(info, buf, count, ppos);
total_size = info->screen_size; 虚拟屏的大小
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,分配页大小的缓冲区
src = (u32 __iomem *) (info->screen_base + p);//显存基地址,作为源地址
dst = buffer;把分配的缓冲区作为目的地址
*dst++ = fb_readl(src++);把源地址中的数据拷贝到目的地址中
if (copy_to_user(buf, buffer, c))把数据传递到应用程序
kfree(buffer);释放缓冲区
}
不管是读还是写都用到了registered_fb[];
5.那么这个结构体数组是怎么赋值的呢registered_fb[];
在这个函数中有对这个结构体进行赋值
int register_framebuffer(struct fb_info *fb_info)
{
分析这个函数
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), "fb%d", i);在fb_class类下面创建一个类的设备,
fb_init_device(fb_info);初始化这个设备
......对fb_info进行初始化操作
registered_fb[i] = fb_info;把这个传进来的变量存入数组中,那么是由谁调用这个函数,并传进参数的呢
可以发现有很多不同厂家的触摸屏.c函数都会调用这个函数,这说明这个函数是抽象出来的,大家都在用它,那么根据什么来区别他们呢,那就根据registered_fb数组中的成员,
}
6.比如打开s3c2410fb.c这个函数,
找到init函数
int __devinit s3c2410fb_init(void)
{
return platform_driver_register(&s3c2410fb_driver);
}
进入到int platform_driver_register(struct platform_driver *drv)里面
{
drv->driver.bus = &platform_bus_type;
....
return driver_register(&drv->driver);
}
}
进入int driver_register(struct device_driver * drv)函数里面
{....
return bus_add_driver(drv);
int bus_add_driver(struct device_driver *drv)
error = driver_attach(drv);
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
driver_probe_device(drv, dev);
if (drv->bus->match && !drv->bus->match(dev, drv))
return ret;如果设备和驱动的名字匹配,那么就会调用probe函数,也就是会调用
.probe = s3c2410fb_probe,这个函数
7.分析这个函数static int __init s3c2410fb_probe(struct platform_device *pdev)
{
主要工作:struct fb_info *fbinfo;定义一个fb_info结构体指针变量
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);为这个结构体指针变量分配大小
....初始化这个结构体
ret = register_framebuffer(fbinfo);注册这个结构体,就把这个结构体注册到内核中,也就是存在registered_fb数组中去,
}
总结:整个LCD驱动架构就是设备和驱动分离
驱动是对所有的LCD抽出来的共性
设备是根据不同厂家的LCD来进注册,如果设备的名字和驱动的名字相同,那么就可以调用probe函数对着设备进行注册;从而达到了设备和驱动分离的目的。