uvc调用select在内核中的数据处理流程,

1、控制传输VC

应用层用于控制传输代码,内核中主要通过usb端点0进行通信

int run_uvc_device()
{
    fd_set efds;
    struct timeval tv;
    int r;
    tv.tv_sec  = 1;
    tv.tv_usec = 0;
    FD_ZERO(&efds);
    FD_SET(__uvc_device->fd, &efds);
    r = select(__uvc_device->fd + 1, NULL, NULL, &efds, &tv);
    if (r > 0)
    {
        if (FD_ISSET(__uvc_device->fd, &efds))
        {
            uvc_events_process(__uvc_device);
        }
    }
    return r;
}
static void uvc_events_process(struct uvc_device* dev)
{
    struct v4l2_event v4l2_event;
    struct uvc_event* uvc_event = (struct uvc_event*)(void*)&v4l2_event.u.data;
    struct uvc_request_data resp;
    int ret;
    ret = ioctl(dev->fd, VIDIOC_DQEVENT, &v4l2_event);
    if (ret < 0)
    {
        printf("VIDIOC_DQEVENT failed: %s (%d)\n", strerror(errno),
            errno);
        return;
    }
    memset(&resp, 0, sizeof resp);
    resp.length = -EL2HLT;
    switch (v4l2_event.type)
    {
        //0x08000000
    case UVC_EVENT_CONNECT:
        printf("handle connect event \n");
        return;
        //0x08000001
    case UVC_EVENT_DISCONNECT:
        printf("handle disconnect event\n");
        return;
        //0x08000004   UVC class
    case UVC_EVENT_SETUP:
        printf("UVC_EVENT_SETUP\n");
        uvc_events_process_setup(dev, &uvc_event->req, &resp);
        break;
        //0x08000005
    case UVC_EVENT_DATA:
        printf("UVC_EVENT_DATA\n");
        uvc_events_process_data(dev, &uvc_event->data);
        return;
        //0x08000002
    case UVC_EVENT_STREAMON:
        printf("UVC_EVENT_STREAMON\n");
        if (!dev->bulk)
        {
            enable_uvc_video(dev);
        }
        return;
        //0x08000003
    case UVC_EVENT_STREAMOFF:
        printf("UVC_EVENT_STREAMOFF\n");
        if (!dev->bulk)
        {
            disable_uvc_video(dev);
        }
        return;
    }

    ret = ioctl(dev->fd, UVCIOC_SEND_RESPONSE, &resp);
    if (ret < 0)
    {
        printf("UVCIOC_S_EVENT failed: %s (%d)\n", strerror(errno),
            errno);
        return;
    }
}

内核代码
内核select是如何知道有数据要处理的呢?如下

#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI)

int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
{
					f_op = f.file->f_op;
					mask = DEFAULT_POLLMASK;
					if (f_op->poll) {//这里调poll,下面主要说明这个函数
						wait_key_set(wait, in, out,
							     bit, busy_flag);
						mask = (*f_op->poll)(f.file, wait);
					}
					fdput(f);
					if ((mask & POLLIN_SET) && (in & bit)) {
						res_in |= bit;
						retval++;
						wait->_qproc = NULL;
					}
					if ((mask & POLLOUT_SET) && (out & bit)) {//这里是uvc建立通信之后会传输数据
						res_out |= bit;
						retval++;
						wait->_qproc = NULL;
					}
					if ((mask & POLLEX_SET) && (ex & bit)) {//控制传输的时候按异常处理
						res_ex |= bit;
						retval++;
						wait->_qproc = NULL;
					}
}
v4l2_poll--》uvc_v4l2_poll--》uvcg_queue_poll--》vb2_poll
unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
{
	struct video_device *vfd = video_devdata(file);
	unsigned long req_events = poll_requested_events(wait);
	unsigned int res = 0;
	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
		struct v4l2_fh *fh = file->private_data;

		if (v4l2_event_pending(fh))
			res = POLLPRI;//控制传输的时候这里会赋值,#define POLLEX_SET (POLLPRI)上面定义了,select返回的时候会体现在异常描述符集合里面
		else if (req_events & POLLPRI)
			poll_wait(file, &fh->wait, wait);
	}

	return res | vb2_core_poll(q, file, wait);
}

int v4l2_event_pending(struct v4l2_fh *fh)
{
	return fh->navailable;//这个值在中断处理函数中调用端点0的处理函数赋值
}

static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
		const struct timespec *ts)
{
	struct v4l2_subscribed_event *sev;
	struct v4l2_kevent *kev;
	bool copy_payload = true;

	/* Are we subscribed? */
	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
	if (sev == NULL)
		return;

	/*
	 * If the event has been added to the fh->subscribed list, but its
	 * add op has not completed yet elems will be 0, treat this as
	 * not being subscribed.
	 */
	if (!sev->elems)
		return;

	/* Increase event sequence number on fh. */
	fh->sequence++;

	/* Do we have any free events? */
	if (sev->in_use == sev->elems) {
		/* no, remove the oldest one */
		kev = sev->events + sev_pos(sev, 0);
		list_del(&kev->list);
		sev->in_use--;
		sev->first = sev_pos(sev, 1);
		fh->navailable--;
		if (sev->elems == 1) {
			if (sev->ops && sev->ops->replace) {
				sev->ops->replace(&kev->event, ev);
				copy_payload = false;
			}
		} else if (sev->ops && sev->ops->merge) {
			struct v4l2_kevent *second_oldest =
				sev->events + sev_pos(sev, 0);
			sev->ops->merge(&kev->event, &second_oldest->event);
		}
	}

	/* Take one and fill it. */
	//这里开始填充一个 struct v4l2_event结构体类型的事件加到对应的链表,应用会通过ioctl获取对应 struct v4l2_event的事件
	kev = sev->events + sev_pos(sev, sev->in_use);
	kev->event.type = ev->type;
	if (copy_payload)
		kev->event.u = ev->u;
	kev->event.id = ev->id;
	kev->event.timestamp = *ts;
	kev->event.sequence = fh->sequence;
	sev->in_use++;
	list_add_tail(&kev->list, &fh->available);

	fh->navailable++;
	<....>
}

直白点说明,usb中断来了之后调用usb注册的中断处理底半步函数调用__v4l2_event_queue_fh设置navailable该参数,具体调用流程看下方图片,不细说,然后select的时候通过poll一层层掉v4l2_event_pending会读到该标志增加,select就会检测到有异常事件要处理,应用调用uvc_events_process处理对应事件
在这里插入图片描述

2、视频流传输VS

应用层代码,数据用的不再是usb的0端点,而是其他端点
应用代码

int run_uvc_data()
{
    fd_set wfds;
    struct timeval tv;
    int r;
    tv.tv_sec  = 1;
    tv.tv_usec = 0;
    FD_ZERO(&wfds);

    if (__uvc_device->streaming == 1)
    {
        FD_SET(__uvc_device->fd, &wfds);
    }
    r = select(__uvc_device->fd + 1, NULL, &wfds, NULL, &tv);
    if (r > 0)
    {
        if (FD_ISSET(__uvc_device->fd, &wfds))
        {
            uvc_video_process_userptr(__uvc_device);
        }
    }
    return r;
}
static int uvc_video_process_userptr(struct uvc_device* dev)
{
    struct v4l2_buffer buf;
    int ret;


    memset(&buf, 0, sizeof buf);
    buf.type   = V4L2_BUF_TYPE_VIDEO_OUTPUT;
    buf.memory = V4L2_MEMORY_USERPTR;

    if ((ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf)) < 0)
    {
        return ret;
    }

    uvc_video_fill_buffer_userptr(dev, &buf);//取视频流数据

    if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0)//发送数据
    {
        printf("Unable to requeue buffer: %s (%d).\n", strerror(errno), errno);
        return ret;
    }

    return 0;
}

内核代码
内核select处理和上面一样,下面具体说明工作流程
do_select–》v4l2_poll–》uvc_v4l2_poll–》uvcg_queue_poll–》vb2_poll–》vb2_core_poll

unsigned int vb2_core_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
{
	struct vb2_buffer *vb = NULL;

<....>
	/*
	 * Take first buffer available for dequeuing.
	 */
	spin_lock_irqsave(&q->done_lock, flags);
	if (!list_empty(&q->done_list))  //在链表中获取vb2_buffer ,这里是获取vb,vb哪里加的呢,接着往下看
		vb = list_first_entry(&q->done_list, struct vb2_buffer,
					done_entry);
	spin_unlock_irqrestore(&q->done_lock, flags);

	if (vb && (vb->state == VB2_BUF_STATE_DONE
			|| vb->state == VB2_BUF_STATE_ERROR)) {
		return (q->is_output) ?  //这个output在初始化队列的时候就初始化为1了
				POLLOUT | POLLWRNORM :
				POLLIN | POLLRDNORM;
	}
	return 0;
}
//q->is_output在这里初始化vb2_queue_init
int vb2_queue_init(struct vb2_queue *q)
{
	q->buf_ops = &v4l2_buf_ops;
	q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type);
	q->is_output = V4L2_TYPE_IS_OUTPUT(q->type);
}

//这个函数会往链表q->done_list里面加成员,select就知道out的描述符集合里对应的描述符需要写数据
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{

	spin_lock_irqsave(&q->done_lock, flags);
	if (state == VB2_BUF_STATE_QUEUED ||
	    state == VB2_BUF_STATE_REQUEUEING) {
		vb->state = VB2_BUF_STATE_QUEUED;
	} else {
		/* Add the buffer to the done buffers list */
		list_add_tail(&vb->done_entry, &q->done_list);
		vb->state = state;
	}
	atomic_dec(&q->owned_by_drv_count);
	spin_unlock_irqrestore(&q->done_lock, flags);
}

在这里插入图片描述

通过内核调度WORK队列调用uvcg_video_pump函数一步步往下走最后调用vb2_buffer_done,这样子q->done_list这个链表就非空了,select就返回out的描述符,应用层开始传输数据
在这里插入图片描述

int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
	INIT_LIST_HEAD(&video->req_free);
	spin_lock_init(&video->req_lock);
	INIT_WORK(&video->pump, uvcg_video_pump);//这里将uvcg_video_pump注册给work工作队列
	return 0;
}

那么哪里调度了这个uvcg_video_pump函数呢
两个地方
1、stream_on的时候调这个uvcg_video_enable
int uvcg_video_enable(struct uvc_video *video, int enable)
{
unsigned int i;
int ret;
static int pp=0;

if (video->ep == NULL) {
	printk(KERN_INFO "Video enable failed, device is "
		"uninitialized.\n");
	return -ENODEV;
}

if (!enable) {
	cancel_work_sync(&video->pump);
	uvcg_queue_cancel(&video->queue, 0);

	for (i = 0; i < UVC_NUM_REQUESTS; ++i)
		if (video->req[i])
			usb_ep_dequeue(video->ep, video->req[i]);

	uvc_video_free_requests(video);
	uvcg_queue_enable(&video->queue, 0);
	return 0;
}

if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
	return ret;

if ((ret = uvc_video_alloc_requests(video)) < 0)
	return ret;

if (video->max_payload_size) {
	video->encode = uvc_video_encode_bulk;
	video->payload_size = 0;
} else
	video->encode = uvc_video_encode_isoc;
schedule_work(&video->pump);//这里cpu会调度使能注册的work队列函数

}
2、数据传输的时候调用uvc_video_complete

static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct uvc_video *video = req->context;
	struct uvc_video_queue *queue = &video->queue;
	unsigned long flags;
    static int pp=0;

	switch (req->status) {
	case 0:
		break;

	case -ESHUTDOWN:	/* disconnect from host. */
		printk(KERN_DEBUG "VS request cancelled.\n");
		uvcg_queue_cancel(queue, 1);
		break;

	default:
		printk(KERN_INFO "VS request completed with status %d.\n",
			req->status);
		uvcg_queue_cancel(queue, 0);
	}

	spin_lock_irqsave(&video->req_lock, flags);
	list_add_tail(&req->list, &video->req_free);
	spin_unlock_irqrestore(&video->req_lock, flags);
	schedule_work(&video->pump);//这里调用
}

应用层收到端点0发来的stream_on指令的时候会调用一次 if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0)传输视频流
这一次调用的是uvcg_video_enable
在这里插入图片描述

数据传输的时候都是通过中断底半步处理函数调用uvc_video_complete
在这里插入图片描述
当调用完之后select就知道要写数据,返回给应用

3、实际调用过程

端点0的控制传输
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000005_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_DATA
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000005_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_DATA
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000004_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_SETUP
_______________kev->event.type:0x8000005_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_DATA
_______________kev->event.type:0x8000002_______func=__v4l2_event_queue_fh,line=156
UVC_EVENT_STREAMON
端点0经过几次setup和data之后stream on,之后就交给usb的其他端点传输数据

中断底半部处理函数中的端点处理函数

static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
		const struct dwc3_event_depevt *event)
{
	struct dwc3_ep		*dep;
	u8			epnum = event->endpoint_number;

	dep = dwc->eps[epnum];

	if (!(dep->flags & DWC3_EP_ENABLED))
		return;

	if (epnum == 0 || epnum == 1) {
		dwc3_ep0_interrupt(dwc, event);
		return;
	}
	switch (event->endpoint_event) {
	case DWC3_DEPEVT_XFERCOMPLETE:
		dep->resource_index = 0;

		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
			dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
			return;
		}

		dwc3_endpoint_transfer_complete(dep, event);
		break;
	case DWC3_DEPEVT_XFERINPROGRESS:
		dwc3_endpoint_transfer_complete(dep, event);
		break;
	case DWC3_DEPEVT_XFERNOTREADY:
		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
			dwc3_gadget_start_isoc(dep, event);
		} else {
			int ret;

			ret = __dwc3_gadget_kick_transfer(dep, 0);
			if (!ret || ret == -EBUSY)
				return;
		}

		break;
	case DWC3_DEPEVT_STREAMEVT:
		if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
			dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
					dep->name);
			return;
		}
		break;
	case DWC3_DEPEVT_RXTXFIFOEVT:
	case DWC3_DEPEVT_EPCMDCMPLT:
		break;
	}
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值