一. 上层与内核的交互
1. camera驱动的目录结构
cong@ubuntu:/tmp/media/video$ tree
├── generic_sensor.c
├── generic_sensor.h
├── ir-kbd-i2c.c
├── Kconfig
├── Makefile
├── rk30_camera.c //设备:platform_device_register RK29_CAM_DRV_NAME "rk3066b-camera"
├── rk30_camera_oneframe.c //驱动: RK29_CAM_DRV_NAME "rk3066b-camera"
├── soc_camera.c
├── soc_mediabus.c
├── sp0a19.c
├── v4l2-common.c
├── v4l2-ctrls.c
├── v4l2-dev.c
├── v4l2-device.c
├── v4l2-event.c
├── v4l2-fh.c
├── v4l2-int-device.c
├── v4l2-ioctl.c
├── v4l2-subdev.c
├── videobuf2-core.c
├── videobuf-core.c
└── videobuf-dma-contig.c
2. 用户空间的调用过程以VIDIOC_REQBUFS为例
在hardware/rk29/camera/CameraHal.cpp中
CameraHal.cpp: if (ioctl(iCamFd, VIDIOC_REQBUFS, &creqbuf) < 0) //用户空间
-------------------------------------------------------------------------------------
v4l2_ioctl 没干啥事,只是调用video_ioctl2 //v4l2-dev.c //内核空间
--> video_ioctl2 没干啥事,只是调用video_usercopy //v4l2-ioctl.c
--> video_usercopy 将用户空间的cmd 和参数拷贝到内核空间 //v4l2-ioctl.c
--> __video_do_ioctl 进行cmd解析,并调用相应的函数处理 //v4l2_ioctl.c
--> soc_camera_reqbufs //soc_camra.c
--> videobuf_reqbufs //videobuf-core.c
--> __videobuf_mmap_setup //videobuf-core.c
-->
videobuf_alloc_vb //videobuf-core.c
-->
__videobuf_alloc_vb //videobuf-core.c
在soc_camera.c中
-
static int soc_camera_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
-
{
-
struct soc_camera_device *icd = file->private_data;
-
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-
-
videobuf_reqbufs(&icd->vb_vidq, p);
-
ici->ops->reqbufs(icd, p); //rk_camera_reqbufs
-
-
if (!ret && !icd->streamer)
-
icd->streamer = file;
-
return ret;
-
}
-
int videobuf_reqbufs(struct videobuf_queue *q, struct v4l2_requestbuffers *req)
-
{
-
unsigned int size, count;
-
videobuf_queue_lock(q);
-
count = req->count; //count=4
-
q->ops->buf_setup(q, &count, &size); //调用rk_videobuf_setup设转置count与size
-
retval = __videobuf_mmap_setup(q, count, size, req->memory); //count=4 size=0x71000=452K
-
req->count = retval;
-
-
videobuf_queue_unlock(q);
-
return 0;
-
}
-
EXPORT_SYMBOL_GPL(videobuf_reqbufs);
在rk30_camera_oneframe.c中
-
static int rk_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
-
{
-
struct soc_camera_device *icd = vq->priv_data;
-
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-
struct rk_camera_dev *pcdev = ici->priv;
-
struct rk_camera_work *wk;
-
struct soc_mbus_pixelfmt fmt;
-
-
//icd->user_width=640
-
//bytes_pre_line = 640*3/2= 960
-
//bytes_per_line_host=960
-
//输出格式 NV12
-
//size = 960*480= 460800 = 0x70800 = PAGE(0x71000)
-
*size = PAGE_ALIGN(bytes_per_line*icd->user_height);
-
//vipmem_bsize = 960*480 = 0x70800 = PAGE(0x71000)
-
pcdev->vipmem_bsize = PAGE_ALIGN(bytes_per_line_host * pcdev->host_height);
-
-
pcdev->camera_work = wk = kzalloc(sizeof(struct rk_camera_work)*(*count), GFP_KERNEL);
-
INIT_LIST_HEAD(&pcdev->camera_work_queue);
-
for (i=0; i<(*count); i++) {
-
wk->index = i;
-
list_add_tail(&wk->queue, &pcdev->camera_work_queue);
-
wk++;
-
}
-
pcdev->camera_work_count = (*count);
-
pcdev->vbinfo = kzalloc(sizeof(struct rk29_camera_vbinfo)*(*count), GFP_KERNEL);
-
memset(pcdev->vbinfo,0,sizeof(struct rk29_camera_vbinfo)*(*count));
-
pcdev->vbinfo_count = *count;
-
}
-
pcdev->video_vq = vq;
-
return 0;
-
}
在videobuf-core.c中
-
int __videobuf_mmap_setup(struct videobuf_queue *q, unsigned int bcount, unsigned int bsize, enum v4l2_memory memory)
-
{
-
unsigned int i;
-
//这个函数只执行一次,所以开始时先释放以前分配的内存队列
-
__videobuf_free(q);
-
//bcount=4
-
for (i = 0; i < bcount; i++) {
-
q->bufs[i] = videobuf_alloc_vb(q);
-
q->bufs[i]->i = i;
-
q->bufs[i]->input = UNSET;
-
q->bufs[i]->memory = memory;
-
q->bufs[i]->bsize = bsize;
-
}
-
return i;
-
}
-
EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
在videobuf-core.c中
-
struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
-
{
-
struct videobuf_buffer *vb;
-
vb = q->int_ops->alloc_vb(q->msize);
-
if (NULL != vb) {
-
init_waitqueue_head(&vb->done);
-
vb->magic = MAGIC_BUFFER;
-
}
-
return vb;
-
}
-
EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
在videobuf-dma-contig.c中
分配一块内存,真正分配的内存大小是size+sizeof(mem)180个字节,返回分配内存的首地址
-
static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
-
{
-
struct videobuf_dma_contig_memory *mem;
-
struct videobuf_buffer *vb;
-
-
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); //注意:大小为size+mem,既分配size的内存还有mem的地方
-
if (vb) {
-
mem = vb->priv = ((char *)vb) + size; //priv指向刚分配内存的mem处
-
mem->magic = MAGIC_DC_MEM;
-
}
-
-
return vb;
-
}
二. camera 每一帧数据的获取
1. 先看一下用户空间是如何调用的
在hardware/rk29/camera/CameraHal.cpp中
-
int CameraHal::cameraStart()
-
{
-
struct v4l2_format format;
-
enum v4l2_buf_type type;
-
struct v4l2_requestbuffers creqbuf;
-
struct v4l2_buffer buffer;
-
-
-
creqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
creqbuf.memory = mCamDriverV4l2MemType;
-
creqbuf.count = CONFIG_CAMERA_PRVIEW_BUF_CNT;
-
ioctl(iCamFd, VIDIOC_REQBUFS, &creqbuf); //1. VIDIOC_REQBUFS在内核中会申请内存空间
-
-
memset(mCamDriverV4l2Buffer, 0x00, sizeof(mCamDriverV4l2Buffer));
-
for (int i = 0; i < mPreviewBufferCount; i++) {
-
if (CAMERA_PREVIEWBUF_ALLOW_WRITE(mPreviewBufferMap[i]->buf_state)) {
-
memset(&buffer, 0, sizeof(struct v4l2_buffer));
-
buffer.type = creqbuf.type;
-
buffer.memory = creqbuf.memory;
-
buffer.flags = 0;
-
buffer.index = i;
-
-
ioctl(iCamFd, VIDIOC_QUERYBUF, &buffer); //2. VIDIOC_QUERYBUF
-
//不管是overlay 还是 mmap 都是要获取 camera数据帧的首地址
-
if (buffer.memory == V4L2_MEMORY_OVERLAY) {
-
buffer.m.offset = mPreviewBufferMap[i]->phy_addr;
-
mCamDriverV4l2Buffer[i] = (char*)mPreviewBufferMap[i]->vir_addr;
-
} else if (buffer.memory == V4L2_MEMORY_MMAP) {
-
mCamDriverV4l2Buffer[i] = (char*)mmap(0, buffer.length,
-
PROT_READ, MAP_SHARED, iCamFd,buffer.m.offset);
-
}
-
mCamDriverV4l2BufferLen = buffer.length;
-
cameraPreviewBufferSetSta(mPreviewBufferMap[i], CMD_PREVIEWBUF_WRITING, 1);
-
ioctl(iCamFd, VIDIOC_QBUF, &buffer); //3. VIDIOC_QBUF
-
}
-
}
-
-
if(!mPreviewMemory) {
-
mPreviewMemory = mRequestMemory(-1, mPreviewFrame2AppSize, CONFIG_CAMERA_PRVIEW_BUF_CNT, NULL);
-
} else if (mPreviewMemory->size != (unsigned int)(mPreviewFrame2AppSize*CONFIG_CAMERA_PRVIEW_BUF_CNT)) {
-
mPreviewMemory->release(mPreviewMemory);
-
mPreviewMemory = mRequestMemory(-1, mPreviewFrame2AppSize, CONFIG_CAMERA_PRVIEW_BUF_CNT, NULL);
-
}
-
-
if (mPreviewMemory) {
-
for (int i=0; i < CONFIG_CAMERA_PRVIEW_BUF_CNT; i++) {
-
mPreviewBufs[i] = (unsigned char*) mPreviewMemory->data + (i*mPreviewFrame2AppSize);
-
}
-
} else {
-
LOGE("%s(%d): mPreviewMemory create failed",__FUNCTION__,__LINE__);
-
}
-
-
int *addr;
-
-
for (int i=0; i < CONFIG_CAMERA_PRVIEW_BUF_CNT; i++) {
-
if(!mVideoBufs[i])
-
mVideoBufs[i] = mRequestMemory(-1, 4, 1, NULL);
-
if( (NULL == mVideoBufs[i]) || ( NULL == mVideoBufs[i]->data)) {
-
mVideoBufs[i] = NULL;
-
LOGE("%s(%d): video buffer %d create failed",__FUNCTION__,__LINE__,i);
-
}
-
if (mVideoBufs[i]) {
-
addr = (int*)mVideoBufs[i]->data;
-
*addr = mPreviewBufferMap[i]->phy_addr;
-
}
-
}
-
mPreviewErrorFrameCount = 0;
-
mPreviewFrameIndex = 0;
-
-
cameraStream(true);
-
LOG_FUNCTION_NAME_EXIT
-
return 0;
-
}
VIDIOC_REQBUFS: 分配内存
VIDIOC_QUERYBUF: 把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP: 查询驱动功能
VIDIOC_ENUM_FMT: 获取当前驱动支持的视频格式
VIDIOC_S_FMT: 设置当前驱动的频捕获格式
VIDIOC_G_FMT: 读取当前驱动的频捕获格式
VIDIOC_TRY_FMT: 验证当前驱动的显示格式
VIDIOC_CROPCAP: 查询驱动的修剪能力
VIDIOC_S_CROP: 设置视频信号的边框
VIDIOC_G_CROP: 读取视频信号的边框
VIDIOC_QBUF: 把数据从缓存中读取出来
VIDIOC_DQBUF: 把数据放回缓存队列
VIDIOC_STREAMON: 开始视频显示函数
VIDIOC_STREAMOFF: 结束视频显示函数
VIDIOC_QUERYSTD: 检查当前视频设备支持的标准,例如PAL或NTSC
在soc_camera.c中
-
static int soc_camera_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-
{
-
struct soc_camera_device *icd = file->private_data;
-
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-
return videobuf_querybuf(&icd->vb_vidq, p);
-
}
在videobuf-core.c中
-
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-
{
-
videobuf_queue_lock(q);
-
videobuf_status(q, b, q->bufs[b->index], q->type);
-
videobuf_queue_unlock(q);
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(videobuf_querybuf);
在soc_camera.c中
-
static int soc_camera_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-
{
-
struct soc_camera_device *icd = file->private_data;
-
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-
videobuf_qbuf(&icd->vb_vidq, p);
-
}
-
int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-
{
-
struct videobuf_buffer *buf;
-
enum v4l2_field field;
-
unsigned long flags = 0;
-
-
videobuf_queue_lock(q);
-
-
buf = q->bufs[b->index]; //这个index值是 0-3 一直循环
-
buf->input = UNSET;
-
-
buf->boff = b->m.offset; //这个buf->boff是用户空间传入的buffer
-
field = videobuf_next_field(q);
-
retval = q->ops->buf_prepare(q, buf, field);
-
list_add_tail(&buf->stream, &q->stream);
-
if (q->streaming) {
-
spin_lock_irqsave(q->irqlock, flags);
-
q->ops->buf_queue(q, buf);
-
spin_unlock_irqrestore(q->irqlock, flags);
-
}
-
wake_up_interruptible_sync(&q->wait);
-
videobuf_queue_unlock(q);
-
return 0;
-
}
-
EXPORT_SYMBOL_GPL(videobuf_qbuf);
-
static int rk_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field)
-
{
-
struct soc_camera_device *icd = vq->priv_data;
-
struct rk_camera_buffer *buf;
-
int ret;
-
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt);
-
-
buf = container_of(vb, struct rk_camera_buffer, vb);
-
-
if (buf->code != icd->current_fmt->code || vb->width != icd->user_width ||
-
vb->height != icd->user_height || vb->field != field) {
-
buf->code = icd->current_fmt->code;
-
vb->width = icd->user_width;
-
vb->height = icd->user_height;
-
vb->field = field;
-
vb->state = VIDEOBUF_NEEDS_INIT;
-
}
-
-
vb->size = bytes_per_line*vb->height;
-
if (vb->state == VIDEOBUF_NEEDS_INIT) {
-
ret = videobuf_iolock(vq, vb, NULL);
-
vb->state = VIDEOBUF_PREPARED;
-
}
-
return 0;
-
}
在drivers/media/video/videobuf-core.c中
-
int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b, int nonblocking)
-
{
-
struct videobuf_buffer *buf = NULL;
-
memset(b, 0, sizeof(*b));
-
videobuf_queue_lock(q);
-
stream_next_buffer(q, &buf, nonblocking);
-
videobuf_status(q, b, buf, q->type); //将结果保存在buf中
-
list_del(&buf->stream);
-
buf->state = VIDEOBUF_IDLE;
-
b->flags &= ~V4L2_BUF_FLAG_DONE;
-
videobuf_queue_unlock(q);
-
return retval;
-
}
-
EXPORT_SYMBOL_GPL(videobuf_dqbuf);
videobuf_dqbuf
--> stream_next_buffer
-
static int stream_next_buffer(struct videobuf_queue *q, struct videobuf_buffer **vb, int nonblocking)
-
{
-
struct videobuf_buffer *buf = NULL;
-
-
retval = stream_next_buffer_check_queue(q, nonblocking);
-
-
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-
retval = videobuf_waiton(q, buf, nonblocking, 1);
-
*vb = buf;
-
return retval;
-
}
阅读(5159) | 评论(0) | 转发(0) |