内核文档: 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); }