Linux中Framebuffer驱动详解2

“上回书”说到kernel源代码/drivers/video/fbmem.c中有一些函数和结构需要弄清楚,我们在这里一个一个地了解。

        module_init(fbmem_init);

        subsys_initcall(fbmem_init);

无论是模块中还是子系统中初始化函数都调用了fbmem_init(),说明它对于framebuffer驱动来说极其重要,应该是所有程序的源头。

fbmem_init()在同文件中实现:

/**
 * fbmem_init - init frame buffer subsystem
 *
 * Initialize the frame buffer subsystem.
 *
 * NOTE: This function is _only_ to be called by drivers/char/mem.c.
 *
 */

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

根据其函数说明,可知fbmen_init()的作用是初始化frame buffer子系统。下面来分析其具体内容:

其中调用了3个函数,先说第一个:proc_create()

创建一个 proc 文件
struct proc_dir_entry *proc_create(const char *name, mode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops)

参数 :
name - 要创建的 proc 文件名;
mode - 该文件权限值,例如 S_IRUGO,可传入0表示采用系统默认值;
parent - 指定该文件的上层 proc 目录项,如果为 NULL,表示创建在 /proc 根目录下;
proc_fops -  如果要创建一个 proc 文件,并且不用 proc_fs 默认提供的 file_operations 的话,可以使用 proc_create() 函数,通过最后一个参数来指定要创建的 proc 文件的 file_operations。

根据上述解释,proc_create("fb", 0, NULL, &fb_proc_fops);这句的作用是在"/proc"下建立一个名为"fb"的文件,不使用proc文件系统默认提供的file_operation,而是单独提供名为"fb_proc_fops"的file_operation结构。此结构体在同文件中定义:

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

其中proc_fb_open()在同文件中实现:

static int proc_fb_open(struct inode *inode, struct file *file)
{
 return seq_open(file, &proc_fb_seq_ops);
}

proc_fb_seq_ops结构体也在同文件中实现,就在proc_fb_open()的上头:

static const struct seq_operations proc_fb_seq_ops = {
 .start = fb_seq_start,
 .next = fb_seq_next,
 .stop = fb_seq_stop,
 .show = fb_seq_show,
};

 

proc_fb_seq_ops结构体中定义的4个函数在同文件中实现:


static void *fb_seq_start(struct seq_file *m, loff_t *pos)
{
 mutex_lock(&registration_lock);
 return (*pos < FB_MAX) ? pos : NULL;
}

 

static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
 (*pos)++;
 return (*pos < FB_MAX) ? pos : NULL;
}

 

static void fb_seq_stop(struct seq_file *m, void *v)
{
 mutex_unlock(&registration_lock);
}

 

static int fb_seq_show(struct seq_file *m, void *v)
{
 int i = *(loff_t *)v;
 struct fb_info *fi = registered_fb[i];

 if (fi)
  seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
 return 0;
}

FB_MAX的定义在kernel源码/include/linux/fb.h中:

#define FB_MAX   32 /* sufficient for now */

即系统中最多有32个framebuffer设备。

上述内容中,有关seq_xx的函数被跳过了,因为seq_xx()的实现都不在本文件中,而是在kernel源代码/fs/seq_file.c中:


/**
 * seq_open - initialize sequential file
 * @file: file we initialize
 * @op: method table describing the sequence
 *
 * seq_open() sets @file, associating it with a sequence described
 * by @op.  @op->start() sets the iterator up and returns the first
 * element of sequence. @op->stop() shuts it down.  @op->next()
 * returns the next element of sequence.  @op->show() prints element
 * into the buffer.  In case of error ->start() and ->next() return
 * ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
 * returns 0 in case of success and negative number in case of error.
 * Returning SEQ_SKIP means "discard this element and move on".
 */
int seq_open(struct file *file, const struct seq_operations *op)
{
 struct seq_file *p = file->private_data;

 if (!p) {
  p = kmalloc(sizeof(*p), GFP_KERNEL);
  if (!p)
   return -ENOMEM;
  file->private_data = p;
 }
 memset(p, 0, sizeof(*p));
 mutex_init(&p->lock);
 p->op = op;

 /*
  * Wrappers around seq_open(e.g. swaps_open) need to be
  * aware of this. If they set f_version themselves, they
  * should call seq_open first and then set f_version.
  */
 file->f_version = 0;

 /*
  * seq_files support lseek() and pread().  They do not implement
  * write() at all, but we clear FMODE_PWRITE here for historical
  * reasons.
  *
  * If a client of seq_files a) implements file.write() and b) wishes to
  * support pwrite() then that client will need to implement its own
  * file.open() which calls seq_open() and then sets FMODE_PWRITE.
  */
 file->f_mode &= ~FMODE_PWRITE;
 return 0;
}
EXPORT_SYMBOL(seq_open);

 /**
 * seq_release - free the structures associated with sequential file.
 * @file: file in question
 * @inode: file->f_path.dentry->d_inode
 *
 * Frees the structures associated with sequential file; can be used
 * as ->f_op->release() if you don't have private data to destroy.
 */
int seq_release(struct inode *inode, struct file *file)
{
 struct seq_file *m = file->private_data;
 kfree(m->buf);
 kfree(m);
 return 0;
}
EXPORT_SYMBOL(seq_release);

 

/**
 * seq_read - ->read() method for sequential files.
 * @file: the file to read from
 * @buf: the buffer to read to
 * @size: the maximum number of bytes to read
 * @ppos: the current position in the file
 *
 * Ready-made ->f_op->read()
 */
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
 struct seq_file *m = file->private_data;
 size_t copied = 0;
 loff_t pos;
 size_t n;
 void *p;
 int err = 0;

 mutex_lock(&m->lock);

 /* Don't assume *ppos is where we left it */
 if (unlikely(*ppos != m->read_pos)) {
  m->read_pos = *ppos;
  while ((err = traverse(m, *ppos)) == -EAGAIN)
   ;
  if (err) {
   /* With prejudice... */
   m->read_pos = 0;
   m->version = 0;
   m->index = 0;
   m->count = 0;
   goto Done;
  }
 }

 /*
  * seq_file->op->..m_start/m_stop/m_next may do special actions
  * or optimisations based on the file->f_version, so we want to
  * pass the file->f_version to those methods.
  *
  * seq_file->version is just copy of f_version, and seq_file
  * methods can treat it simply as file version.
  * It is copied in first and copied out after all operations.
  * It is convenient to have it as  part of structure to avoid the
  * need of passing another argument to all the seq_file methods.
  */
 m->version = file->f_version;
 /* grab buffer if we didn't have one */
 if (!m->buf) {
  m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
  if (!m->buf)
   goto Enomem;
 }
 /* if not empty - flush it first */
 if (m->count) {
  n = min(m->count, size);
  err = copy_to_user(buf, m->buf + m->from, n);
  if (err)
   goto Efault;
  m->count -= n;
  m->from += n;
  size -= n;
  buf += n;
  copied += n;
  if (!m->count)
   m->index++;
  if (!size)
   goto Done;
 }
 /* we need at least one record in buffer */
 pos = m->index;
 p = m->op->start(m, &pos);
 while (1) {
  err = PTR_ERR(p);
  if (!p || IS_ERR(p))
   break;
  err = m->op->show(m, p);
  if (err < 0)
   break;
  if (unlikely(err))
   m->count = 0;
  if (unlikely(!m->count)) {
   p = m->op->next(m, p, &pos);
   m->index = pos;
   continue;
  }
  if (m->count < m->size)
   goto Fill;
  m->op->stop(m, p);
  kfree(m->buf);
  m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
  if (!m->buf)
   goto Enomem;
  m->count = 0;
  m->version = 0;
  pos = m->index;
  p = m->op->start(m, &pos);
 }
 m->op->stop(m, p);
 m->count = 0;
 goto Done;
Fill:
 /* they want more? let's try to get some more */
 while (m->count < size) {
  size_t offs = m->count;
  loff_t next = pos;
  p = m->op->next(m, p, &next);
  if (!p || IS_ERR(p)) {
   err = PTR_ERR(p);
   break;
  }
  err = m->op->show(m, p);
  if (m->count == m->size || err) {
   m->count = offs;
   if (likely(err <= 0))
    break;
  }
  pos = next;
 }
 m->op->stop(m, p);
 n = min(m->count, size);
 err = copy_to_user(buf, m->buf, n);
 if (err)
  goto Efault;
 copied += n;
 m->count -= n;
 if (m->count)
  m->from = n;
 else
  pos++;
 m->index = pos;
Done:
 if (!copied)
  copied = err;
 else {
  *ppos += copied;
  m->read_pos += copied;
 }
 file->f_version = m->version;
 mutex_unlock(&m->lock);
 return copied;
Enomem:
 err = -ENOMEM;
 goto Done;
Efault:
 err = -EFAULT;
 goto Done;
}
EXPORT_SYMBOL(seq_read);

 

/**
 * seq_lseek - ->llseek() method for sequential files.
 * @file: the file in question
 * @offset: new position
 * @origin: 0 for absolute, 1 for relative position
 *
 * Ready-made ->f_op->llseek()
 */
loff_t seq_lseek(struct file *file, loff_t offset, int origin)
{
 struct seq_file *m = file->private_data;
 loff_t retval = -EINVAL;

 mutex_lock(&m->lock);
 m->version = file->f_version;
 switch (origin) {
  case 1:
   offset += file->f_pos;
  case 0:
   if (offset < 0)
    break;
   retval = offset;
   if (offset != m->read_pos) {
    while ((retval=traverse(m, offset)) == -EAGAIN)
     ;
    if (retval) {
     /* with extreme prejudice... */
     file->f_pos = 0;
     m->read_pos = 0;
     m->version = 0;
     m->index = 0;
     m->count = 0;
    } else {
     m->read_pos = offset;
     retval = file->f_pos = offset;
    }
   }
 }
 file->f_version = m->version;
 mutex_unlock(&m->lock);
 return retval;
}
EXPORT_SYMBOL(seq_lseek);


发布了34 篇原创文章 · 获赞 12 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/38706505