vivi.c框架

内核文档: V4L2-framework.txt

UVC:usb video controll
UVC驱动框架:

system call: open   read   write

-------------------------------------------------

核心层:
        source: v4l2_dev.c
        struct: v4l2_device 
        operation:  1.分配  file_operations (v4l2_file_operations)
                    2.设置  file_operations (v4l2_file_operations)
                    2.注册  1.cdev_alloc
                            2.cdev->ops = v4l2_file_operations
                            3.cdev_add
----------------------------------------------------

硬件层:
        source: uvc_driver.c
        struct: video_device
        operation:  1.初始化 v4l2_driver_register
                    2.分配      video_device_alloc
                    3.设置   video_device
                    4.注册   video_register_device

----------------------------------------------------

如何写一个v4l2驱动:
    1.分配,设置,注册 v4l2_device
    2.分配 video_device
    3.设置 
            1. 将自己的video_device与v4l2核心层连接起来
                video_device->v4l2_dev = v4l2_device 
            2. 设置自己的video_device的fops操作函数
                video_device->fops       = xxx_fops
                video_device->ioctl_ops = xxx_ioctl 
            3. 设置自己的video_device的v4l2_ctrl_handler,并提交给核心层
                1.v4l2_ctrl_handler_init
                2.v4l2_ctrl_new_std 
                  v4l2_ctrl_new_custom
                3.v4l2_device->ctr_handler = v4l2_ctrl_handler

                
                
vivi.c 驱动框架分析:

---------------------------------------------------------------
核心层:
        source: v4l2_dev.c
        struct: v4l2_device
                        v4l2_ops(file_operations)




----------------------------------------------------------------
设备层:
        source: uvc_driver.c
        struct: video_device




-----------------------------------------------------------------
虚拟设备层: 
        source: vivi.c
        struct: vivi_dev
                        vivi_ops(v4l2_file_operations)
                        vivi_ioctl_ops(v4l2_ioctl_ops)



----------------------------------------------------------------


应用程序开启摄像头,驱动ioctl被调用流程:

//可以省略的ioctl
3.  ioctl(VIDIOC_G_FMT)
4.  for() ioctl(VIDIOC_ENUM_FMT)
5.  ioctl(VIDIOC_QUERYCAP)    // 列举性能
6.  ioctl(VIDIOC_G_INPUT)     // 获得当前使用输入源
7.  ioctl(VIDIOC_ENUMINPUT)   // 列举输入源
8.  ioctl(VIDIOC_QUERYCTRL)   // 查询属性,比如亮度、对比度
9.  ioctl(VIDIOC_QUERYCAP)
10. ioctl(VIDIOC_ENUMINPUT)


// 打开设备节点
1. fd = open
2. ioctl(fd,VIDIOC_QUERYCAP) 
  
// 获得设备容量
3. for() ioctl(VIDIOC_ENUMINPUT)     // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
4. for() ioctl(VIDIOC_ENUMSTD)       // 列举标准(制式), 不是必需的
5. for() ioctl(VIDIOC_ENUM_FMT)      // 列举格式
6. ioctl(VIDIOC_G_PARM)
7. for() ioctl(VIDIOC_QUERYCTRL)     // 查询属性(比如说亮度值最小值、最大值、默认值)

// 读取属性   
8.  ioctl(VIDIOC_G_STD)             // 获得当前使用的标准(制式), 不是必需的
9.  ioctl(VIDIOC_G_INPUT) 
10. ioctl(VIDIOC_G_CTRL )           // 获得当前属性, 比如亮度是多少
11. ioctl(VIDIOC_TRY_FMT)           // 试试能否支持某种格式
12. ioctl(VIDIOC_S_FMT)             // 设置摄像头使用某种格式

// 启动摄像头 streamon
13. ioctl(VIDIOC_REQBUFS )          // 请求系统分配缓冲区
14. for() ioctl(VIDIOC_QUERYBUF)    // 查询所分配的缓冲区   
          mmap()       
15. for() ioctl(VIDIOC_QBUF)        // 把缓冲区放入队列        
16. ioctl(VIDIOC_STREAMON)         // 启动摄像头

// 设置属性
17. for ()  ioctl(VIDIOC_S_CTRL)           // 设置属性
            ioctl(VIDIOC_S_INPUT )         // 设置输入源
            ioctl(VIDIOC_S_STD)            // 设置标准(制式), 不是必需的

// 不断地读取缓冲区数据 v4l2_nextframe > v4l2_waiton    
18. v4l2_queue_all
    v4l2_waiton    
        for ()
        {
            select(5, [4], NULL, NULL, {5, 0})      = 1 (in [4], left {4, 985979})
            ioctl(4, VIDIOC_DQBUF           // de-queue, 把缓冲区从队列中取出
                                            // 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据        
            ioctl(4, VIDIOC_QBUF            // 把缓冲区放入队列
        }

        
应用程序读取缓冲区数据流程:

1.请求分配缓冲区       ==>vidioc_reqbufs  
2.查询映射缓冲区       ==>vidioc_querybuf (mmap) 
3.把缓冲区加入队列     ==>vidioc_qbuf
4.启动摄像头           ==>vidioc_streamon
5.select查村是否有数据 ==>select
6.队列取出缓冲区数据   ==>vidioc_dqbu


摄像头数据的读取过程:

1. 请求分配缓冲区: 
ioctl(VIDIOC_REQBUFS)         // 请求系统分配缓冲区
        videobuf_reqbufs(buf_queue, v4l2_requestbuffers) // buf_queue在open函数用videobuf_queue_vmalloc_init初始化
                             // 注意:这个ioctl只是分配缓冲区的头部信息,真正的缓存还没有分配呢

2. 查询映射缓冲区:
ioctl(VIDIOC_QUERYBUF)          // 查询所分配的缓冲区
        videobuf_querybuf       // 获得缓冲区的数据格式、大小、每一行长度、高度            
mmap(参数里有"大小")            // 在这里才分配缓存
        v4l2_mmap
            vivi_mmap
                videobuf_mmap_mapper
                    videobuf-vmalloc.c里的__videobuf_mmap_mapper
                            mem->vmalloc = vmalloc_user(pages);   // 在这里才给缓冲区分配空间

3. 把缓冲区放入队列:
ioctl(VIDIOC_QBUF)                              // 把缓冲区放入队列        
    videobuf_qbuf
        q->ops->buf_prepare(q, buf, field);     // 调用驱动程序提供的函数做些预处理
        list_add_tail(&buf->stream, &q->stream);// 把缓冲区放入队列的尾部
        q->ops->buf_queue(q, buf);              // 调用驱动程序提供的"入队列函数"
        

4. 启动摄像头
ioctl(VIDIOC_STREAMON) 
    videobuf_streamon
        q->streaming = 1;
        

5. 用select查询是否有数据
          // 驱动程序里必定有: 产生数据、唤醒进程
          v4l2_poll
                vdev->fops->poll
                    vivi_poll   
                        videobuf_poll_stream
                            // 从队列的头部获得缓冲区
                            buf = list_entry(q->stream.next, struct videobuf_buffer, stream);     
                            // 如果没有数据则休眠                            
                            poll_wait(file, &buf->done, wait);

    谁来产生数据、谁来唤醒它?
    内核线程vivi_thread每30MS执行一次,它调用
    vivi_thread_tick
        vivi_fillbuff(fh, buf);  // 构造数据 
        wake_up(&buf->vb.done);  // 唤醒进程
          
6. 有数据后从队列里取出缓冲区
// 有那么多缓冲区,APP如何知道哪一个缓冲区有数据?调用VIDIOC_DQBUF
ioctl(VIDIOC_DQBUF)  
    vidioc_dqbuf   
        // 在队列里获得有数据的缓冲区
        retval = stream_next_buffer(buf_queue, &buf, nonblocking);
        
        // 把它从队列中删掉
        list_del(&buf->stream);
        
        // 把这个缓冲区的状态返回给APP
        videobuf_status(buf_queue, b, buf, buf_queue->type);
        
7. 应用程序根据VIDIOC_DQBUF所得到缓冲区状态,知道是哪一个缓冲区有数据
   就去读对应的地址(该地址来自前面的mmap)


大概流程:
1. VIDIOC_REQBUFS:
                设置缓冲区头部 buf_a_head ---> buf_b_head

2. VIDIOC_QUERYBUF:
                mmap分配空间:buf_a_head+buffer---> buf_b_head+buffer
                                
3. VIDIOC_QBUF: 
                把缓冲区放入队列:queue_head:  buf_a_head+buffer   ---> queue_last:buf_b_head+buffer              
                                    
4. streaming_on:

5. slect:
                查询队列头部的缓冲区

6.VIDIOC_DQBUF:
                返回队列头部的buf_a_head+buffer,从队列中删除头部buf_a_head+buffer,
                此时buf_b_head+buffer位队列头部,处理完把buf_a_head+buffer添加到队列尾部
                再进行第三部VIDIOC_QBUF

vivi.c源码:

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>


/* 定义一个video_device   */
static struct video_device *my_video_device;

/* 摄像头数据格式结构体 */
static struct v4l2_format my_v4l2_format;

/* videobuf-core.c维护的缓存队列 */
static struct videobuf_queue my_videobuf_queue;

/* 队列自旋锁 */
static  spinlock_t my_queue_slock;

/* 数据上报定时器*/
static struct timer_list my_timer;

/* 本地维护的缓存队列*/
static struct list_head my_local_queue;

/*包含摄像头数据构造函数*/
#include"my_fill_buf.c"

/**************************************************videobuf_queue_ops start********************************************************/

static  int my_buf_setup(struct videobuf_queue *q,unsigned int *count, unsigned int *size)
{
   /* 重新计算buff size并调整,不要浪费空间 */
   *size = my_v4l2_format.fmt.pix.sizeimage;

  /* 一个队列最多支持32缓存块 */
   if(*count == 0) *count=32;
   return 0;
}

static    int my_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field)
{
   /*设置video_buf的大小,宽度*/
   vb->size = my_v4l2_format.fmt.pix.sizeimage;
   vb->bytesperline = my_v4l2_format.fmt.pix.bytesperline;
   vb->width = my_v4l2_format.fmt.pix.width;
   vb->height = my_v4l2_format.fmt.pix.height;
   vb->field = field;

   /*摄像头数据初始化*/
   my_precalculate_bars(0);

   /*设置buf状态*/
   vb->state = VIDEOBUF_PREPARED;
   return 0;
}

static    void my_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
   /* 应用程序查询缓冲区时把本地队列的buf 放入 videobuf-core.c维护的队列 */
   list_add_tail(&vb->queue, &my_local_queue);
   
   /* 设置buf状态 */
   vb->state = VIDEOBUF_QUEUED;
}

static     void my_buf_release(struct videobuf_queue *q,struct videobuf_buffer *vb)
{
   /*释放buf*/
   videobuf_vmalloc_free(vb);
  
   /*设置buf状态*/
   vb->state = VIDEOBUF_NEEDS_INIT;
}


/*
 *    
 ops->buf_setup   - calculates the size of the video buffers and avoid they to waste more than some maximum limit of RAM;
 ops->buf_prepare - fills the video buffer structs and calls videobuf_iolock() to alloc and prepare mmaped memory;
 ops->buf_queue   - advices the driver that another buffer were requested (by read() or by QBUF);
 ops->buf_release - frees any buffer that were allocated.
 
 *
 */

static struct videobuf_queue_ops my_videobuf_queue_ops =
{
   .buf_setup = my_buf_setup,               /* app调用ioctl(REQBUF)时,此函数被调用 */
   .buf_prepare = my_buf_prepare,            /* app调用ioctl(QUERYBUF)时,此函数被调用 */
   .buf_queue = my_buf_queue,                /* app调用ioctl(QBUF)时,此函数被调用 */
   .buf_release = my_buf_release,            /* app调用ioctl(DQBUF)时,此函数被调用 */

};

/**************************************************videobuf_queue_ops end********************************************************/




/***************************************************fops  start************************************************************/
static    int my_vivi_open(struct file *file)
{
  
    /* 初始化videobuf_queue结构体
     * videobuf_queue->bufs     = videobuf_buffer     
     * videobuf_queue->ops      = videobuf_queue_ops  
     * videobuf_queue->irqlock  = my_queue_slock
     */
    videobuf_queue_vmalloc_init(&my_videobuf_queue,             // 缓冲区队列 
                                   &my_videobuf_queue_ops,         // 缓冲区队列的操作函数
                                NULL, &my_queue_slock,             // 缓冲区操作函数要用的自旋锁
                                V4L2_BUF_TYPE_VIDEO_CAPTURE,     // 单个缓冲区的类型
                                V4L2_FIELD_INTERLACED,            // 奇偶帧
                                sizeof(struct videobuf_buffer), // 缓冲头部大小
                                NULL);                             // 私有数据

    /*设置超调时间*/
    my_timer.expires = jiffies + 1;

    /*将定时器加入内核链表*/
    add_timer(&my_timer);
    return 0;
}

static    unsigned int my_vivi_poll(struct file *file, struct poll_table_struct *wait)
{

     /* 调用内核的poll函数查询数据,如果没有数据,休眠在videobuf->done上
      * poll_wait(file, &buf->done, wait);
      *
      */

   return videobuf_poll_stream(file,&my_videobuf_queue , wait);

}

static    int my_vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
    /*映射缓冲区*/
   return videobuf_mmap_mapper(&my_videobuf_queue ,  vma);
}



static    int my_vivi_release(struct file *file)
{
   /*内核链表删除定时器*/
   del_timer(&my_timer);

   /*停止队列*/
   videobuf_stop(&my_videobuf_queue);

   /*取消缓存区映射*/
   videobuf_mmap_free(&my_videobuf_queue );
   return 0;
}
/***************************************************fops  end**************************************************************/




/**************************************************ioctl  start************************************************************/

/*判断是否是摄像头设备*/
static int my_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
   /* 驱动名称  */
   strcpy(cap->driver,"zsy_vivi");
   strcpy(cap->card,"zsy_vivi");
   
   /* 摄像头版本号 */
   cap->version = 0x0011;
   
   /* 视频捕捉设备,streaming表明是app通过ioctl  来进行读写数据*/
   cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
   return 0;
}

/* 枚举当前设备支持的格式 */
static int my_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
   /* 支持几种格式 */
   if(f->index >= 1) return -EINVAL;

   /* 设置当前格式为YUYV*/
   strcpy(f->description,"4:2:2,packed,YUYV");
   f->pixelformat = V4L2_PIX_FMT_YUYV;
   return 0;
}


static int my_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
   /* 返回当前的格式 */
   memcpy(f,&my_v4l2_format,sizeof(my_v4l2_format));
   return 0;
}


static int my_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
   unsigned int maxw,maxh;
   enum v4l2_field field;

   /* 测试是否支持这个格式 */
   if(f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) return -EINVAL;
   
   field = f->fmt.pix.field;

   if (field == V4L2_FIELD_ANY)  field = V4L2_FIELD_INTERLACED;
    else if (V4L2_FIELD_INTERLACED != field)    return -EINVAL;

   /* 设置最大宽度和高度  */
    maxw  = 1024;
    maxh  = 768;

    v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0);
    /* 调整宽度 */
    f->fmt.pix.bytesperline =(f->fmt.pix.width * 16) >> 3;
    /* 调整图片大小 */
    f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;

    return 0;
}


static int my_s_fmt_vid_cap(struct file *file, void *fh,struct v4l2_format *f)
{
    int ret = my_try_fmt_vid_cap(file,NULL,f);
    if(ret < 0) return ret;

     /* 设置上个函数测试好了的格式 */
    memcpy(&my_v4l2_format,f,sizeof(my_v4l2_format));

    return ret;
}

static  int my_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
    /* 请求一个video缓存 */
    return (videobuf_reqbufs(&my_videobuf_queue, b));
}


static    int my_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
   /* 查询缓存 */
   return (videobuf_querybuf(&my_videobuf_queue,b));
}

static    int my_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
  /*把缓存挂到队列*/
  return (videobuf_qbuf(&my_videobuf_queue,b));
}

static    int my_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
  /* 把缓存拿出队列 */
  return (videobuf_dqbuf(&my_videobuf_queue,  b, file->f_flags & O_NONBLOCK));
}

static int my_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
    /* 打开摄像头 */
    return videobuf_streamon(&my_videobuf_queue);
}

static int my_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
   /* 关闭摄像头*/
    videobuf_streamoff(&my_videobuf_queue);
    return 0;
}
/*************************************************ioctl  end*************************************************************/

/* 定义一个v4l2_file_operations */
static struct v4l2_file_operations my_fops =
{
   .owner = THIS_MODULE,
   .open  = my_vivi_open,
   .release = my_vivi_release,
   .mmap  = my_vivi_mmap,
   .poll  = my_vivi_poll,
   .ioctl = video_ioctl2,

};

/* 定义一个v4l2_ioctl_ops */
static struct v4l2_ioctl_ops my_ioctl_ops = 
{
   /* 摄像头容量 */
   .vidioc_querycap = my_querycap,

   /* 数据格式 */
   .vidioc_enum_fmt_vid_cap = my_enum_fmt_vid_cap,
   .vidioc_g_fmt_vid_cap = my_g_fmt_vid_cap ,
   .vidioc_try_fmt_vid_cap = my_try_fmt_vid_cap,
   .vidioc_s_fmt_vid_cap = my_s_fmt_vid_cap,

   /* 缓冲区 */
   .vidioc_reqbufs = my_reqbufs,
   .vidioc_querybuf = my_querybuf,
   .vidioc_qbuf = my_qbuf,
   .vidioc_dqbuf = my_dqbuf,

   /* 启动/停止摄像头 */
   .vidioc_streamon = my_streamon,
   .vidioc_streamoff = my_streamoff,

};


/*定时器超时函数,填充数据*/
void my_timer_fun(unsigned long data )
{
     struct videobuf_buffer *vb;
     void *vbuf;
     struct timeval ts;
    
    /* 1.1如果队列本地队列没有数据 ,结束 */
    if(list_empty(&my_local_queue))  
        {
         mod_timer(&my_timer, jiffies + HZ/30);
         return ;
        }
    
    /* 1.2从本地队列里面取出第一个buf*/
    vb = list_entry(my_local_queue.next, struct videobuf_buffer, queue);
    
    /* 1.3如果应用程序没有等待数据 ,结束 */
    if(!waitqueue_active(&vb->done)) 
       {
         mod_timer(&my_timer, jiffies + HZ/30);
         return ;
        }
    
    /* 2.填充假数据*/
    vbuf = videobuf_to_vmalloc(vb);
    my_fill_buff(vb);
    
    vb->field_count++;
    do_gettimeofday(&ts);
    vb->ts = ts;
    vb->state = VIDEOBUF_DONE;

    /* 3.把填充了数据的buf从队列里面删除*/
    list_del(&vb->queue);

    /* 4.唤醒videobuf_buffer->done进程,
     * 当进程使用poll机制查询缓冲区时暂时没有数据时
     * 进程休眠在 videobuf_buffer->done上面

     */

      wake_up(&vb->done);

/* 5.修改超时时间,30毫秒产生一帧数据 */
    mod_timer(&my_timer, jiffies +  HZ/30);  
    
}

/* 释放函数 */
void my_video_device_release(struct video_device *vdev)
{}

int my_vivi_init(void)
{
   /* 1.分配一个video_device */
   my_video_device = video_device_alloc();

   /* 2.设置video_device */
   my_video_device->release = my_video_device_release;
   my_video_device->fops = &my_fops;
   my_video_device->ioctl_ops = &my_ioctl_ops;

   /* 3.注册video_device*/
   video_register_device(my_video_device,VFL_TYPE_GRABBER, -1);

   /* 初始化定时器*/
   init_timer(&my_timer);
   my_timer.function = my_timer_fun;

   /* 初始化自旋锁 */
   spin_lock_init(&my_queue_slock);

   /*初始化本地队列*/
   INIT_LIST_HEAD(&my_local_queue);
 
   return 0;
}

void my_vivi_exit(void)
{
   video_unregister_device(my_video_device);
   video_device_release(my_video_device);
}

module_init(my_vivi_init);
module_exit(my_vivi_exit);

MODULE_LICENSE("GPL");

fill_buffer.c源码:

/* Bars and Colors should match positions */

enum colors {
    WHITE,
    AMBAR,
    CYAN,
    GREEN,
    MAGENTA,
    RED,
    BLUE,
    BLACK,
};

    /* R   G   B */
#define COLOR_WHITE    {204, 204, 204}
#define COLOR_AMBAR    {208, 208,   0}
#define COLOR_CIAN    {  0, 206, 206}
#define    COLOR_GREEN    {  0, 239,   0}
#define COLOR_MAGENTA    {239,   0, 239}
#define COLOR_RED    {205,   0,   0}
#define COLOR_BLUE    {  0,   0, 255}
#define COLOR_BLACK    {  0,   0,   0}

struct bar_std {
    u8 bar[8][3];
};

/* Maximum number of bars are 10 - otherwise, the input print code
   should be modified */
static struct bar_std bars[] = {
    {    /* Standard ITU-R color bar sequence */
        {
            COLOR_WHITE,
            COLOR_AMBAR,
            COLOR_CIAN,
            COLOR_GREEN,
            COLOR_MAGENTA,
            COLOR_RED,
            COLOR_BLUE,
            COLOR_BLACK,
        }
    }, {
        {
            COLOR_WHITE,
            COLOR_AMBAR,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_AMBAR,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_AMBAR,
        }
    }, {
        {
            COLOR_WHITE,
            COLOR_CIAN,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_CIAN,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_CIAN,
        }
    }, {
        {
            COLOR_WHITE,
            COLOR_GREEN,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_GREEN,
            COLOR_BLACK,
            COLOR_WHITE,
            COLOR_GREEN,
        }
    },
};

#define NUM_INPUTS ARRAY_SIZE(bars)

#define TO_Y(r, g, b) 
    (((16829 * r + 33039 * g + 6416 * b  + 32768) >> 16) + 16)
/* RGB to  V(Cr) Color transform */
#define TO_V(r, g, b) 
    (((28784 * r - 24103 * g - 4681 * b  + 32768) >> 16) + 128)
/* RGB to  U(Cb) Color transform */
#define TO_U(r, g, b) 
    (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)



static unsigned char myvivi_cur_bars[8][3];



/* precalculate color bar values to speed up rendering */
static void my_precalculate_bars(int input)
{
    unsigned char r, g, b;
    int k, is_yuv;


    for (k = 0; k < 8; k++) {
        r = bars[input].bar[k][0];
        g = bars[input].bar[k][1];
        b = bars[input].bar[k][2];
        is_yuv = 0;

        switch (my_v4l2_format.fmt.pix.pixelformat) {
        case V4L2_PIX_FMT_YUYV:
        case V4L2_PIX_FMT_UYVY:
            is_yuv = 1;
            break;
        case V4L2_PIX_FMT_RGB565:
        case V4L2_PIX_FMT_RGB565X:
            r >>= 3;
            g >>= 2;
            b >>= 3;
            break;
        case V4L2_PIX_FMT_RGB555:
        case V4L2_PIX_FMT_RGB555X:
            r >>= 3;
            g >>= 3;
            b >>= 3;
            break;
        }

        if (is_yuv) {
            myvivi_cur_bars[k][0] = TO_Y(r, g, b);    /* Luma */
            myvivi_cur_bars[k][1] = TO_U(r, g, b);    /* Cb */
            myvivi_cur_bars[k][2] = TO_V(r, g, b);    /* Cr */
        } else {
            myvivi_cur_bars[k][0] = r;
            myvivi_cur_bars[k][1] = g;
            myvivi_cur_bars[k][2] = b;
        }
    }

}


static void myvivi_gen_twopix(unsigned char *buf, int colorpos)
{
    unsigned char r_y, g_u, b_v;
    unsigned char *p;
    int color;

    r_y = myvivi_cur_bars[colorpos][0]; /* R or precalculated Y */
    g_u = myvivi_cur_bars[colorpos][1]; /* G or precalculated U */
    b_v = myvivi_cur_bars[colorpos][2]; /* B or precalculated V */

    for (color = 0; color < 4; color++) {
        p = buf + color;

        switch (my_v4l2_format.fmt.pix.pixelformat) {
        case V4L2_PIX_FMT_YUYV:
            switch (color) {
            case 0:
            case 2:
                *p = r_y;
                break;
            case 1:
                *p = g_u;
                break;
            case 3:
                *p = b_v;
                break;
            }
            break;
        case V4L2_PIX_FMT_UYVY:
            switch (color) {
            case 1:
            case 3:
                *p = r_y;
                break;
            case 0:
                *p = g_u;
                break;
            case 2:
                *p = b_v;
                break;
            }
            break;
        case V4L2_PIX_FMT_RGB565:
            switch (color) {
            case 0:
            case 2:
                *p = (g_u << 5) | b_v;
                break;
            case 1:
            case 3:
                *p = (r_y << 3) | (g_u >> 3);
                break;
            }
            break;
        case V4L2_PIX_FMT_RGB565X:
            switch (color) {
            case 0:
            case 2:
                *p = (r_y << 3) | (g_u >> 3);
                break;
            case 1:
            case 3:
                *p = (g_u << 5) | b_v;
                break;
            }
            break;
        case V4L2_PIX_FMT_RGB555:
            switch (color) {
            case 0:
            case 2:
                *p = (g_u << 5) | b_v;
                break;
            case 1:
            case 3:
                *p = (r_y << 2) | (g_u >> 3);
                break;
            }
            break;
        case V4L2_PIX_FMT_RGB555X:
            switch (color) {
            case 0:
            case 2:
                *p = (r_y << 2) | (g_u >> 3);
                break;
            case 1:
            case 3:
                *p = (g_u << 5) | b_v;
                break;
            }
            break;
        }
    }
}

static void myvivi_gen_line(char *basep, int inipos, int wmax,
        int hmax, int line, int count)
{
    int  w;
    int pos = inipos;

    /* We will just duplicate the second pixel at the packet */
    wmax /= 2;

    /* Generate a standard color bar pattern */
    for (w = 0; w < wmax; w++) {
        int colorpos = ((w + count) * 8/(wmax + 1)) % 8;

        myvivi_gen_twopix(basep + pos, colorpos);
        pos += 4; /* only 16 bpp supported for now */
    }
    return;
}

static void my_fill_buff(struct videobuf_buffer *vb)
{
    int h , pos = 0;
    int hmax  = vb->height;
    int wmax  = vb->width;
    char *tmpbuf;
    void *vbuf = videobuf_to_vmalloc(vb);
    static int mv_count = 0;

    if (!vbuf)
        return;

    tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC);
    if (!tmpbuf)
        return;

    for (h = 0; h < hmax; h++) {
        myvivi_gen_line(tmpbuf, 0, wmax, hmax, h, mv_count);
        memcpy(vbuf + pos, tmpbuf, wmax * 2);
        pos += wmax*2;
    }

    mv_count++;

    kfree(tmpbuf);
}
原文地址:https://www.cnblogs.com/zsy12138/p/10548841.html