嵌入式Linux驅動筆記(十八)------淺析V4L2框架之ioctl
你好!這裡是風箏的部落格,
歡迎和我一起交流。
上一章寫了V4L2框架:嵌入式Linux驅動筆記(十七)——詳解V4L2框架(UVC驅動)
現在來寫V4L2的重點,他的使用者空間操作函式集合:
const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
#ifndef CONFIG_MMU
.get_unmapped_area = uvc_v4l2_get_unmapped_area,
#endif
};
看下open函式:
static int uvc_v4l2_open(struct file *file)
{
/*部分函式省略*/
struct uvc_streaming *stream;
struct uvc_fh *handle;
stream = video_drvdata(file);//獲取uvc視訊流
ret = usb_autopm_get_interface(stream->dev->intf);//喚醒裝置
handle = kzalloc(sizeof *handle, GFP_KERNEL);//建立uvc控制程式碼
if (stream->dev->users == 0) {//第一次時
ret = uvc_status_start(stream->dev, GFP_KERNEL);//uvc狀態開始,裡面提交urb
}
stream->dev->users++;
v4l2_fh_init(&handle->vfh, &stream->vdev);
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;//捆綁uvc控制程式碼和uvc視訊鏈
handle->stream = stream;//捆綁uvc控制程式碼和uvc視訊流
handle->state = UVC_HANDLE_PASSIVE;//設定uvc狀態為未啟用
file->private_data = handle;//將uvc控制程式碼作為檔案的私有資料
return 0;
}
open函式不是我們這章的重點,使用者空間對V4L2裝置的操作基本都是ioctl來實現的,我們看下ioctl函式:
long video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(file, cmd, arg, __video_do_ioctl);
}
可以看出video_usercopy函式就是從user空間copy複製ioctl的cmd和arg引數,然後進入__video_do_ioctl函式:
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
/*部分內容省略*/
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
const struct v4l2_ioctl_info *info;
if (v4l2_is_known_ioctl(cmd)) {
info = &v4l2_ioctls[_IOC_NR(cmd)];//判斷是INFO_FL_STD還是INFO_FL_FUNC
}
if (info->flags & INFO_FL_STD) {//如果是INFO_FL_STD
typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
const void *p = vfd->ioctl_ops;//呼叫到ioctl_ops真正的ioctrl操作集
const vidioc_op *vidioc = p + info->u.offset;//通過偏移值找到要執行函式的地址
ret = (*vidioc)(file, fh, arg);//直接呼叫到視訊裝置驅動中video_device->ioctl_ops
} else if (info->flags & INFO_FL_FUNC) {//如果是INFO_FL_FUNC
ret = info->u.func(ops, file, fh, arg);//呼叫到v4l2自己實現的標準回撥函式
}
}
我們可以看出,如果info->flags是INFO_FL_FUNC,會呼叫vfd->ioctl_ops函式集合裡的某個函式(通過info->u.offset偏移值確定),那vfd->ioctl_ops是什麼呢?其實就是上一章說的,uvc_register_video函式裡的vdev->ioctl_ops = &uvc_ioctl_ops了。
如果info->flags是INFO_FL_FUNC,直接呼叫info->u.func(ops, file, fh, arg)函式
那info又是怎麼確定的呢?當然是:info = &v4l2_ioctls[_IOC_NR(cmd)];
static struct v4l2_ioctl_info v4l2_ioctls[] = {//.ioctl, .u.func, .debug, .flags
IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),//列舉效能
IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),//列舉格式
IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),//設定攝像頭使用某種格式
IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),//請求系統分配緩衝區
IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),//查詢所分配的緩衝區
/*太長了,省略後續*/
}
其實,雖然是呼叫info->u.func(ops, file, fh, arg)函式,但是ops是vfd->ioctl_ops;,以v4l2_ioctls[]陣列裡的v4l_querycap函式(就是u.func欄位)為例,裡面也會呼叫:ops->vidioc_querycap(file, fh, cap);
所以,最終還是會回到uvc_ioctl_ops函式集合裡。其實和info->flags 是 INFO_FL_STD的情況沒什麼大的差別。
我們看下uvc_ioctl_ops 這個真正的ioctl操作函式集合:
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_querycap = uvc_ioctl_querycap,
.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,//列舉支援哪種格式
.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,//獲取格式、解析度
.vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,//先try測試,然後把要設定的格式/解析度存起來
.vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,//檢測是否支援使用者輸入的格式
.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
.vidioc_reqbufs = uvc_ioctl_reqbufs,//請求分配快取,APP將從這些快取中讀到視訊資料
.vidioc_querybuf = uvc_ioctl_querybuf,//查詢快取狀態, 比如地址資訊(APP可以用mmap進行對映)
.vidioc_qbuf = uvc_ioctl_qbuf,//把緩衝區放入佇列,底層的硬體操作函式將會把資料放入這個佇列的快取
.vidioc_expbuf = uvc_ioctl_expbuf,
.vidioc_dqbuf = uvc_ioctl_dqbuf,//APP通過poll/select確定有資料後,把快取從佇列中取出來
.vidioc_create_bufs = uvc_ioctl_create_bufs,
.vidioc_streamon = uvc_ioctl_streamon,//啟動視訊傳輸
.vidioc_streamoff = uvc_ioctl_streamoff,
.vidioc_enum_input = uvc_ioctl_enum_input,
.vidioc_g_input = uvc_ioctl_g_input,
.vidioc_s_input = uvc_ioctl_s_input,
/*太長了,後續省略......*/
};
可以看到非常的多,帶_cap的是捕獲裝置,帶_out的是輸出裝置。
雖然這些ioctl非常多,但是在韋東山第三期視訊裡說道,簡化的攝像頭驅動程式至少需要11個ioctl,我能力不大,所以也就按照這個來分析:
.vidioc_querycap = uvc_ioctl_querycap,
.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
.vidioc_reqbufs = uvc_ioctl_reqbufs,
.vidioc_querybuf = uvc_ioctl_querybuf,
.vidioc_qbuf = uvc_ioctl_qbuf,
.vidioc_dqbuf = uvc_ioctl_dqbuf,
.vidioc_streamon = uvc_ioctl_streamon,
.vidioc_streamoff = uvc_ioctl_streamoff,
我們先看第【1】個ioctl:.vidioc_querycap = uvc_ioctl_querycap函式:
static int uvc_ioctl_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
struct uvc_fh *handle = file->private_data;
struct uvc_video_chain *chain = handle->chain;
struct uvc_streaming *stream = handle->stream;
strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver));
strlcpy(cap->card, vdev->name, sizeof(cap->card));
usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
| chain->caps;//獲取這是一個什麼裝置:
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)//如果是視訊捕獲裝置
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
else
cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
return 0;
}
很簡單的函式,.vidioc_querycap 裡的回撥函式主要就是說明這個是什麼裝置而已,輸入裝置?輸出裝置?
接下來看第【2】個ioctl:.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap函式:
uvc_ioctl_enum_fmt_vid_cap函式裡又會呼叫uvc_ioctl_enum_fmt函式:
static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,struct v4l2_fmtdesc *fmt)
{
/*省略部分內容*/
struct uvc_format *format;
format = &stream->format[fmt->index];//從uvc_fmts找出支援的格式
strlcpy(fmt->description, format->name, sizeof(fmt->description));//存放到description
fmt->description[sizeof(fmt->description) - 1] = 0;
fmt->pixelformat = format->fcc;
}
.vidioc_enum_fmt_vid_cap 裡的回撥函式主要就是列舉出支援的格式,把他放到fmt->description描述符中,然後返回給使用者空間傳進來的fmt。
繼續看第【3】個ioctl:.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap函式:
uvc_ioctl_g_fmt_vid_cap函式裡又會呼叫uvc_v4l2_get_format函式:
static int uvc_v4l2_get_format(struct uvc_streaming *stream,struct v4l2_format *fmt)
{
/*部分內容省略*/
format = stream->cur_format;//從stream獲取當前格式
frame = stream->cur_frame;//從stream獲取當前解析度
fmt->fmt.pix.pixelformat = format->fcc;
fmt->fmt.pix.width = frame->wWidth;
fmt->fmt.pix.height = frame->wHeight;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
fmt->fmt.pix.colorspace = format->colorspace;
fmt->fmt.pix.priv = 0;
}
.vidioc_g_fmt_vid_cap裡的回撥函式主要是獲取格式、解析度,把他存放在fmt->fmt.pix裡。
第【4】個ioctl:.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap函式:
uvc_ioctl_try_fmt_vid_cap函式裡面又呼叫uvc_v4l2_try_format函式:
static int uvc_v4l2_try_format(struct uvc_streaming *stream,
struct v4l2_format *fmt, struct uvc_streaming_control *probe,
struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
{
/*部分內容省略*/
for (i = 0; i < stream->nformats; ++i) {//在format查詢支援的格式是否有請求的格式
format = &stream->format[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
if (i == stream->nformats) {//如果沒有
format = stream->def_format;//使用預設的格式
fmt->fmt.pix.pixelformat = format->fcc;
}
rw = fmt->fmt.pix.width;
rh = fmt->fmt.pix.height;
maxd = (unsigned int)-1;
for (i = 0; i < format->nframes; ++i) {//查詢最接近的影象解析度
__u16 w = format->frame[i].wWidth;
__u16 h = format->frame[i].wHeight;
d = min(w, rw) * min(h, rh);
d = w*h + rw*rh - 2*d;
if (d < maxd) {
maxd = d;
frame = &format->frame[i];
}
if (maxd == 0)
break;
}
interval = frame->dwDefaultFrameInterval;//每一幀的間隙
probe->bmHint = 1; /* dwFrameInterval */
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
ret = uvc_probe_video(stream, probe);//裡面呼叫uvc_set_video_ctrl
fmt->fmt.pix.width = frame->wWidth;//設定具體的引數
fmt->fmt.pix.height = frame->wHeight;//解析度
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;//一楨大小
fmt->fmt.pix.colorspace = format->colorspace;
fmt->fmt.pix.priv = 0;
}
我們看下.vidioc_try_fmt_vid_cap的回撥函式做了什麼:
1.檢測硬體是否支援請求的格式,否則使用預設格式
2.然後在format->frame[i]查詢最近的解析度來使用
3.設定每一幀的時間間隙,也就是一秒多少幀
4.填充probe,然後在uvc_probe_video裡設定video控制
5.最後把嘗試好的資料填充到fmt->fmt.pix
所以.vidioc_try_fmt_vid_cap的回撥函式主要是起一個測試作用。
第【5】個ioctl:.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap函式:
uvc_ioctl_s_fmt_vid_cap函式裡呼叫uvc_v4l2_set_format函式:
static int uvc_v4l2_set_format(struct uvc_streaming *stream,
struct v4l2_format *fmt)
{
/*部分內容省略*/
ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);//測試好引數
/*把引數儲存在stream,供uvc_v4l2_get_format函式呼叫*/
stream->ctrl = probe;
stream->cur_format = format;//把格式foramt儲存起來
stream->cur_frame = frame;//把解析度frame儲存起來
}
其實我感覺這個函式都沒啥啥,大部分都在uvc_v4l2_try_format函式裡做了,最後就是把引數存起來到stream裡而已。
第【6】個ioctl:.vidioc_reqbufs = uvc_ioctl_reqbufs函式:
呼叫關係:
uvc_ioctl_reqbufs
uvc_request_buffers
vb2_reqbufs
vb2_core_reqbufs
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,unsigned int *count)
{
/*部分內容省略*/
__vb2_queue_cancel(q);//清理任何緩衝的準備或排隊佇列狀態
memset(q->alloc_devs, 0, sizeof(q->alloc_devs));//初始化清零
q->memory = memory;//指向記憶體
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
plane_sizes, q->alloc_devs);//查詢需要多少緩衝區buffers,設定快取區佇列
allocated_buffers =
__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);//申請快取區
}
其中call_qop(q, queue_setup, q, &num_buffers, &num_planes,plane_sizes, q->alloc_devs)裡面其實就是呼叫:(q)->ops->queue_setup(q, &num_buffers, &num_planes,plane_sizes, q->alloc_devs)
那這個(q)->ops又是在哪設定呢?
其實就是我們上一章裡的uvc_queue_init函式裡:
queue->queue.ops = &uvc_queue_qops;
static struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
.buf_finish = uvc_buffer_finish,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.start_streaming = uvc_start_streaming,
.stop_streaming = uvc_stop_streaming,
};
這個結構體很重要,之後我們還會呼叫到。
知道需要多少快取之後,就會呼叫__vb2_queue_alloc函式申請空間:
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
unsigned int num_buffers, unsigned int num_planes,
const unsigned plane_sizes[VB2_MAX_PLANES])
{
/*部分內容省略*/
unsigned int buffer, plane;
struct vb2_buffer *vb;
for (buffer = 0; buffer < num_buffers; ++buffer) {
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);//分配空間
vb->state = VB2_BUF_STATE_DEQUEUED;//設定狀態
vb->vb2_queue = q;
vb->num_planes = num_planes;
vb->index = q->num_buffers + buffer;
vb->type = q->type;//設定型別,比如捕獲之類的
vb->memory = memory;//設定記憶體
for (plane = 0; plane < num_planes; ++plane) {
vb->planes[plane].length = plane_sizes[plane];
vb->planes[plane].min_length = plane_sizes[plane];
}
q->bufs[vb->index] = vb;//申請的空間裝入bufs
if (memory == VB2_MEMORY_MMAP) {//如果使用的是VB2_MEMORY_MMAP型別
ret = __vb2_buf_mem_alloc(vb);//mmap出來
if (ret) {
dprintk(1, "failed allocating memory for "
"buffer %d\n", buffer);
q->bufs[vb->index] = NULL;
kfree(vb);
break;
}
__setup_offsets(vb);//設定偏移量
ret = call_vb_qop(vb, buf_init, vb);//buf初始化
}
}
}
這裡面就是申請num_buffers個快取了,設定好然後放到q->bufs[]裡,再mmap到使用者空間。
為什麼mmap呢?
read和write,是基本幀IO訪問方式,每一幀都要通過IO操作,通過read讀取每一幀資料,資料需要在核心和使用者之間拷貝,這種方式訪問速度可能會非常慢;
但是通過mmap在核心空間開闢緩衝區,這些緩衝區可以是大而連續DMA緩衝區、通過vmalloc()建立的虛擬緩衝區,或者直接在裝置的IO記憶體中開闢的緩衝區(如果硬體支援);是流IO訪問方式,不需要記憶體拷貝,訪問速度比較快。
設定好偏移值就對buf進行初始化了。
所以.vidioc_reqbufs的回撥函式主要是請求分配快取。
第【7】個.ioctl:.vidioc_querybuf = uvc_ioctl_querybuf函式:
呼叫關係:
uvc_query_buffer
vb2_querybuf
vb2_core_querybuf
call_void_bufop(q, fill_user_buffer, q->bufs[index], pb);
可以看出,這裡根據傳進來的引數,查詢到使用到的快取,會呼叫fill_user_buffer返回bufs[]給使用者空間。
所以.vidioc_querybuf 的回撥函式主要是查詢快取狀態,把他返回給使用者空間。
第【8】個.vidioc_qbuf = uvc_ioctl_qbuf函式:
呼叫關係為:
uvc_ioctl_qbuf
uvc_queue_buffer
vb2_qbuf
vb2_core_qbuf
int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
{
/*部分內容省略*/
struct vb2_buffer *vb;
vb = q->bufs[index];
list_add_tail(&vb->queued_entry, &q->queued_list);//新增到queued_list連結串列
q->queued_count++;//佇列數目加一
q->waiting_for_buffers = false;
vb->state = VB2_BUF_STATE_QUEUED;//標記已入佇列
if (q->start_streaming_called)//如果呼叫過vb2_start_streaming
__enqueue_in_driver(vb);//將vb->planes[plane].mem_priv(即是我們申請的mmap)調入我們寫的驅動中
if (pb)
call_void_bufop(q, fill_user_buffer, vb, pb);//填充buffers到使用者空間
if (q->streaming && !q->start_streaming_called &&
q->queued_count >= q->min_buffers_needed) {
ret = vb2_start_streaming(q);
}
}
這裡面,將對應的vb2_buffer 新增到 q->queued_list 連結串列中,並改變state狀態。
然後呼叫__enqueue_in_driver把緩衝區放入佇列。
然後就是ret = vb2_start_streaming(q)函式,這個函式在uvc_ioctl_streamon函式裡也會呼叫有,所以放在後面寫吧,這裡雖然也會呼叫,但是是有條件的:
只有start_streaming沒被呼叫且到達最小需要的buffers數目,才嘗試啟動
第【9】個.vidioc_dqbuf = uvc_ioctl_dqbuf,函式:
呼叫關係為:
uvc_ioctl_dqbuf
uvc_dequeue_buffer
vb2_dqbuf
vb2_core_dqbuf
int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
bool nonblocking)
{
/*部分內容省略*/
struct vb2_buffer *vb = NULL;
ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
call_void_vb_qop(vb, buf_finish, vb);
call_void_bufop(q, fill_user_buffer, vb, pb);
list_del(&vb->queued_entry);
q->queued_count--;
}
這裡面,就是在__vb2_get_done_vb函式裡,將q->done_list 中的vb2_buffer中提出來,然後 將vb2_buffer中的v4l2_buffer資訊返回,並將其從q->done_list 中刪除。
然後就呼叫(q)->vb2_queue->ops->buf_finish代表buf操作完成。
接著呼叫fill_user_buffer函式把把資料返回給使用者空間,最後list_del(&vb->queued_entry)把他移除掉。
第【10】個.vidioc_streamon = uvc_ioctl_streamon,,函式:
呼叫關係為:
uvc_ioctl_streamon
uvc_queue_streamon
vb2_streamon
vb2_core_streamon
vb2_start_streaming
static int vb2_start_streaming(struct vb2_queue *q)
{
/*部分內容省略*/
struct vb2_buffer *vb;
q->start_streaming_called = 1;//標誌以使用streaming
ret = call_qop(q, start_streaming, q,
atomic_read(&q->owned_by_drv_count));//呼叫q->ops->start_streaming
q->start_streaming_called = 0;
for (i = 0; i < q->num_buffers; ++i) {
vb = q->bufs[i];
if (vb->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);//資料完成後
}
}
函式裡面q->ops->start_streaming,也就是queue->queue.ops = &uvc_queue_qops裡的函式:uvc_start_streaming
但是uvc_start_streaming函式裡又會呼叫uvc_video_enable(stream, 1);
uvc_video_enable函式裡面就有這兩句:
ret = uvc_commit_video(stream, &stream->ctrl);//uvc提交視訊引數
ret = uvc_init_video(stream, GFP_KERNEL);//uvc初始化視訊,同時呼叫uvc_init_video_isoc分配urb
這樣就成功的啟動視訊傳輸了。
然後資料完成後就是呼叫vb2_buffer_done函式:函式裡面就是:
list_add_tail(&vb->done_entry, &q->done_list);//將資料放入q->done_list中
vb->state = state;
wake_up(&q->done_wq);//喚醒poll休眠的程式
資料完成就要把buffers放到done_list這個完成的連結串列中,然後改變狀態,最後就喚醒poll函式裡的休眠程式。
第【11】個.vidioc_streamoff = uvc_ioctl_streamoff,,函式:
其實就和uvc_ioctl_streamoff是相反的,uvc_ioctl_streamoff是呼叫uvc_video_enable(stream, 1);
uvc_ioctl_streamoff就是呼叫uvc_video_enable(stream, 0)了,進行關閉視訊傳輸。
好了,十一個ioctl就馬馬虎虎的講完了。還有一些其他的ioctl,比如設定亮度之類的,就自己去看了,是在太多了。
其實一路寫來,我對這個buffers這塊輸出還是很模糊的,不知道怎麼寫,,,,,,,,,還在研究中。不過大致的流程還是瞭解了一點。
最後,附上一位大佬的blog:http://www.cnblogs.com/surpassal/archive/2012/12/22/zed_webcam_lab2.html
相關文章
- 嵌入式Linux驅動筆記(十七)------詳解V4L2框架(UVC驅動)Linux筆記框架
- Linux 驅動之IoctlLinux
- 嵌入式Linux驅動筆記(十二)------通俗易懂式分析瞭解spi框架Linux筆記框架
- 嵌入式Linux驅動筆記(十六)------裝置驅動模型(kobject、kset、ktype)Linux筆記模型Object
- 嵌入式Linux驅動學習筆記(十六)------裝置驅動模型(kobject、kset、ktype)Linux筆記模型Object
- 嵌入式Linux驅動筆記(十一)------i2c裝置之mpu6050驅動Linux筆記
- 嵌入式Linux驅動筆記(十三)------spi裝置之RFID-rc522驅動Linux筆記
- 嵌入式Linux驅動筆記(十)------通俗易懂式分析瞭解i2c框架Linux筆記框架
- 嵌入式Linux驅動筆記(十四)------詳解clock時鐘(CCF)框架及clk_get函式Linux筆記框架函式
- Vuex 原理淺析筆記Vue筆記
- 嵌入式Linux驅動筆記(九)------dts裝置樹在2440使用Linux筆記
- 深入淺出:Linux裝置驅動之字元裝置驅動Linux字元
- MySQL 非同步驅動淺析 (一):效能分析MySql非同步
- 應用呼叫驅動的ioctl函式函式
- linux驅動之LED驅動Linux
- 【Camera專題】Qcom-Camera驅動框架淺析(Hal層->Driver層)框架
- ArmSoM系列板卡 嵌入式Linux驅動開發實戰指南 之 字元裝置驅動Linux字元
- 關於驅動程式中的ioctl (轉貼)
- 【演算法學習筆記】博弈論淺析之遊戲類演算法筆記遊戲
- Linux 筆記分享十八:網路配置Linux筆記
- 淺析微服務框架微服務框架
- Android框架淺析Android框架
- Linux LED驅動原始碼簡析Linux原始碼
- 多執行緒系列(十八) -AQS原理淺析執行緒AQS
- 嵌入式Linux驅動筆記(十五)------編譯使用tslib支援LCD觸控式螢幕Linux筆記編譯
- webrtc QOS筆記四 Nack機制淺析Web筆記
- WebMagic 爬蟲框架淺析Web爬蟲框架
- 淺析Spring Framework框架容器啟動過程SpringFramework框架
- Django筆記三十八之傳送郵件Django筆記
- 嵌入式Linux中platform平臺裝置模型的框架(實現LED驅動)LinuxPlatform模型框架
- Linux 記憶體池原始碼淺析Linux記憶體原始碼
- 嵌入式之Makefile學習筆記筆記
- 嵌入式Linux中的LED驅動控制(續)Linux
- linux裝置驅動歸納總結(三):4.ioctl的實現【轉】Linux
- MySQL service啟動指令碼淺析(r12筆記第59天)MySql指令碼筆記
- 嵌入式Linux驅動學習筆記(十五)------編譯使用tslib支援LCD觸控式螢幕Linux筆記編譯
- Linux驅動開發筆記(一):helloworld驅動原始碼編寫、makefile編寫以及驅動編譯Linux筆記原始碼編譯
- vue.js框架原理淺析Vue.js框架