LCD驱动程序—框架分析

LCD驱动程序—框架分析

在写lcd驱动程序之前,我们必须先要明白lcd驱动程序编写的框架,首先从linux帧缓冲设备驱动的上层建筑fbmem.c文件开始。

LCD驱动程序—框架分析

(1)我们先找到入口函数:

static int __init fbmem_init(void)

{

 proc_create("fb", 0, NULL, &fb_proc_fops);

 

 if (register_chrdev(FB_MAJOR,"fb",&fb_fops))//注册字符设备,FB_MAJOR为主设备号,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;

}

(2)接下来当然要看操作函数集fb_fops

static const struct file_operations fb_fops = {

 .owner = THIS_MODULE,

 .read =  fb_read,

 .write = fb_write,

 .unlocked_ioctl = fb_ioctl,

..............................................................

 .mmap =  fb_mmap,

 .open =  fb_open,

 .release = fb_release,

..............................................................

};

我们选两个来分析:open和read

首先是open函数:

static int fb_open(struct inode *inode, struct file *file)

__acquires(&info->lock)

__releases(&info->lock)

{

 int fbidx = iminor(inode);//根据次设备号得到fbidx

 struct fb_info *info;

 int res = 0;

 

 if (fbidx >= FB_MAX)

  return -ENODEV;

 info = registered_fb[fbidx];//这个东西很重要,从下面的注释中我们可以看到,帧缓冲区操作函数集定义在这个结构体里,我们将在注释1里来具体分析

 if (!info)

  request_module("fb%d", fbidx);

 info = registered_fb[fbidx];

 if (!info)

  return -ENODEV;

 mutex_lock(&info->lock);

 if (!try_module_get(info->fbops->owner)) {

  res = -ENODEV;

  goto out;

 }

 file->private_data = info;

 if (info->fbops->fb_open)//如果在在帧缓冲区操作函数集里定义了open函数,就会调用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);

 return res;

}

 

接下来我们看看read函数

static ssize_t

fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

{

 unsigned long p = *ppos;

 struct inode *inode = file->f_path.dentry->d_inode;

 int fbidx = iminor(inode);

 struct fb_info *info = registered_fb[fbidx];//熟悉吧,我们又看到这个数组了,看来它很重要,待会一定好好款待一下它

 u32 *buffer, *dst;

 u32 __iomem *src;

 int c, i, cnt = 0, err = 0;

 unsigned long total_size;

 

 if (!info || ! info->screen_base)//跟那个数组有关

  return -ENODEV;

 

 if (info->state != FBINFO_STATE_RUNNING)//跟那个数组有关

  return -EPERM;

 

 if (info->fbops->fb_read)//如果操作函数集里定义了read函数,就调用,否则就算了

  return info->fbops->fb_read(info, buf, count, ppos);

 

 total_size = info->screen_size;//跟那个数组有关

 

 if (total_size == 0)

  total_size = info->fix.smem_len;//跟那个数组有关

 

 if (p >= total_size)

  return 0;

 

 if (count >= total_size)

  count = total_size;

 

 if (count + p > total_size)

  count = total_size - p;

 

 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,//开辟一个buffer

    GFP_KERNEL);

 if (!buffer)

  return -ENOMEM;

 

 src = (u32 __iomem *) (info->screen_base + p);//显存基地址也在里面

 

 if (info->fbops->fb_sync)

  info->fbops->fb_sync(info);

 

 while (count) {

  c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;

  dst = buffer;//目的指针指向一个buffer(我们在上面开辟的)

  for (i = c >> 2; i--; )

   *dst++ = fb_readl(src++);//从基地址读取数据放进buffer中

  if (c & 3) {

   u8 *dst8 = (u8 *) dst;

   u8 __iomem *src8 = (u8 __iomem *) src;

 

   for (i = c & 3; i--;)

    *dst8++ = fb_readb(src8++);

 

   src = (u32 __iomem *) src8;

  }

 

  if (copy_to_user(buf, buffer, c))//将buffer中的数据拷贝到用户空间,这样在用户空间调用read函数时就把显存内容读出来了

              {

   err = -EFAULT;

   break;

  }

  *ppos += c;

  buf += c;

  cnt += c;

  count -= c;

 }

 

 kfree(buffer);

 

 return (err) ? err : cnt;

}

注释1:registered_fb[]这个东东哪里来的呢?我们收索了一下,并在本文件的register_framebuffer函数中找到了对它的赋值

int

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;

 

 if (fb_check_foreignness(fb_info))

  return -ENOSYS;

 

 num_registered_fb++;

 for (i = 0 ; i < FB_MAX; i++)

  if (!registered_fb[i])//寻找registered_fb数组中空的一项

   break;

 fb_info->node = i;

 mutex_init(&fb_info->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)) {

 

  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;

}

结合之前的知识我们就可以将整个框架大致搭建起来了,我们在驱动程序中调用register_framebuffer(struct fb_info *fb_info)这个函数,并将帧缓冲设备描述结构体struct fb_info *fb_info传递了进来,在这个结构体里就定义了帧缓冲操作函数集,以及各种跟硬件相关的东西。现在我们就清楚了registered_fb[]这个数组只是起到了一个中间变量的作用,在注册函数中将struct fb_info结构体存进去,然后在操作函数中再取出来。

(3)现在框架就搭建起来了,不过为了进一步理解,我们在内核里查看一下有哪些文件调用了register_framebuffer(struct fb_info *fb_info),并适当分析其各种硬件相关设置。其实我们搜索到的这些文件就是我们编写驱动程序应当编写的东西,下面我们以/driver/video/arcfb.c来分析一下:

platform_driver_register(&arcfb_driver);

          int __init arcfb_probe(struct platform_device *dev)

                    register_framebuffer(info);//struct fb_info *info;这个结构体里有各种东东的,包括file_operations结构体,注册前已经设置了

这样一注册的话,fb_info 里设置的东东就都传过去了,内核就可以调用我们自己写的东东了。

我们稍微总结一下驱动程序的流程:

分配fb_info

设置

注册

硬件相关设置

关于驱动程序的编写我们在接下来程序中会涉及。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值