xref: /aosp_15_r20/external/virtio-media/driver/virtio_media_driver.c (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
1*1b4853f5SAndroid Build Coastguard Worker // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0+
2*1b4853f5SAndroid Build Coastguard Worker 
3*1b4853f5SAndroid Build Coastguard Worker /*
4*1b4853f5SAndroid Build Coastguard Worker  * Virtio-media driver.
5*1b4853f5SAndroid Build Coastguard Worker  *
6*1b4853f5SAndroid Build Coastguard Worker  * Copyright (c) 2023-2024 Google LLC.
7*1b4853f5SAndroid Build Coastguard Worker  */
8*1b4853f5SAndroid Build Coastguard Worker 
9*1b4853f5SAndroid Build Coastguard Worker #include <linux/delay.h>
10*1b4853f5SAndroid Build Coastguard Worker #include <linux/device.h>
11*1b4853f5SAndroid Build Coastguard Worker #include <linux/mm.h>
12*1b4853f5SAndroid Build Coastguard Worker #include <linux/mutex.h>
13*1b4853f5SAndroid Build Coastguard Worker #include <linux/scatterlist.h>
14*1b4853f5SAndroid Build Coastguard Worker #include <linux/types.h>
15*1b4853f5SAndroid Build Coastguard Worker #include <linux/videodev2.h>
16*1b4853f5SAndroid Build Coastguard Worker #include <linux/vmalloc.h>
17*1b4853f5SAndroid Build Coastguard Worker #include <linux/wait.h>
18*1b4853f5SAndroid Build Coastguard Worker #include <linux/workqueue.h>
19*1b4853f5SAndroid Build Coastguard Worker #include <media/frame_vector.h>
20*1b4853f5SAndroid Build Coastguard Worker #include <media/v4l2-dev.h>
21*1b4853f5SAndroid Build Coastguard Worker #include <media/v4l2-event.h>
22*1b4853f5SAndroid Build Coastguard Worker #include <media/videobuf2-memops.h>
23*1b4853f5SAndroid Build Coastguard Worker #include <linux/module.h>
24*1b4853f5SAndroid Build Coastguard Worker #include <linux/moduleparam.h>
25*1b4853f5SAndroid Build Coastguard Worker #include <linux/version.h>
26*1b4853f5SAndroid Build Coastguard Worker #include <linux/virtio.h>
27*1b4853f5SAndroid Build Coastguard Worker #include <linux/virtio_config.h>
28*1b4853f5SAndroid Build Coastguard Worker #include <linux/virtio_ids.h>
29*1b4853f5SAndroid Build Coastguard Worker 
30*1b4853f5SAndroid Build Coastguard Worker #include <media/v4l2-device.h>
31*1b4853f5SAndroid Build Coastguard Worker #include <media/v4l2-ioctl.h>
32*1b4853f5SAndroid Build Coastguard Worker 
33*1b4853f5SAndroid Build Coastguard Worker #include "protocol.h"
34*1b4853f5SAndroid Build Coastguard Worker #include "session.h"
35*1b4853f5SAndroid Build Coastguard Worker #include "virtio_media.h"
36*1b4853f5SAndroid Build Coastguard Worker 
37*1b4853f5SAndroid Build Coastguard Worker #define VIRTIO_MEDIA_NUM_EVENT_BUFS 16
38*1b4853f5SAndroid Build Coastguard Worker 
39*1b4853f5SAndroid Build Coastguard Worker #ifndef VIRTIO_ID_MEDIA
40*1b4853f5SAndroid Build Coastguard Worker #define VIRTIO_ID_MEDIA 49
41*1b4853f5SAndroid Build Coastguard Worker #endif
42*1b4853f5SAndroid Build Coastguard Worker 
43*1b4853f5SAndroid Build Coastguard Worker /* ID of the SHM region into which MMAP buffer will be mapped. */
44*1b4853f5SAndroid Build Coastguard Worker #define VIRTIO_MEDIA_SHM_MMAP 0
45*1b4853f5SAndroid Build Coastguard Worker 
46*1b4853f5SAndroid Build Coastguard Worker /*
47*1b4853f5SAndroid Build Coastguard Worker  * Name of the driver to expose to user-space.
48*1b4853f5SAndroid Build Coastguard Worker  *
49*1b4853f5SAndroid Build Coastguard Worker  * This is configurable because v4l2-compliance has workarounds specific to
50*1b4853f5SAndroid Build Coastguard Worker  * some drivers. When proxying these directly from the host, this allows it to
51*1b4853f5SAndroid Build Coastguard Worker  * apply them as needed.
52*1b4853f5SAndroid Build Coastguard Worker  */
53*1b4853f5SAndroid Build Coastguard Worker char *driver_name = NULL;
54*1b4853f5SAndroid Build Coastguard Worker module_param(driver_name, charp, 0660);
55*1b4853f5SAndroid Build Coastguard Worker 
56*1b4853f5SAndroid Build Coastguard Worker /**
57*1b4853f5SAndroid Build Coastguard Worker  * Allocate a new session. The id and list fields must still be set by the
58*1b4853f5SAndroid Build Coastguard Worker  * caller.
59*1b4853f5SAndroid Build Coastguard Worker  */
60*1b4853f5SAndroid Build Coastguard Worker static struct virtio_media_session *
virtio_media_session_alloc(struct virtio_media * vv,u32 id,bool nonblocking_dequeue)61*1b4853f5SAndroid Build Coastguard Worker virtio_media_session_alloc(struct virtio_media *vv, u32 id,
62*1b4853f5SAndroid Build Coastguard Worker 			   bool nonblocking_dequeue)
63*1b4853f5SAndroid Build Coastguard Worker {
64*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session;
65*1b4853f5SAndroid Build Coastguard Worker 	int i;
66*1b4853f5SAndroid Build Coastguard Worker 	int ret;
67*1b4853f5SAndroid Build Coastguard Worker 
68*1b4853f5SAndroid Build Coastguard Worker 	session = kzalloc(sizeof(*session), GFP_KERNEL);
69*1b4853f5SAndroid Build Coastguard Worker 	if (!session)
70*1b4853f5SAndroid Build Coastguard Worker 		goto err_session;
71*1b4853f5SAndroid Build Coastguard Worker 
72*1b4853f5SAndroid Build Coastguard Worker 	session->shadow_buf = kzalloc(VIRTIO_SHADOW_BUF_SIZE, GFP_KERNEL);
73*1b4853f5SAndroid Build Coastguard Worker 	if (!session->shadow_buf)
74*1b4853f5SAndroid Build Coastguard Worker 		goto err_shadow_buf;
75*1b4853f5SAndroid Build Coastguard Worker 
76*1b4853f5SAndroid Build Coastguard Worker 	ret = sg_alloc_table(&session->command_sgs, DESC_CHAIN_MAX_LEN,
77*1b4853f5SAndroid Build Coastguard Worker 			     GFP_KERNEL);
78*1b4853f5SAndroid Build Coastguard Worker 	if (ret) {
79*1b4853f5SAndroid Build Coastguard Worker 		goto err_payload_sgs;
80*1b4853f5SAndroid Build Coastguard Worker 	}
81*1b4853f5SAndroid Build Coastguard Worker 
82*1b4853f5SAndroid Build Coastguard Worker 	session->id = id;
83*1b4853f5SAndroid Build Coastguard Worker 	session->nonblocking_dequeue = nonblocking_dequeue;
84*1b4853f5SAndroid Build Coastguard Worker 
85*1b4853f5SAndroid Build Coastguard Worker 	INIT_LIST_HEAD(&session->list);
86*1b4853f5SAndroid Build Coastguard Worker 	v4l2_fh_init(&session->fh, &vv->video_dev);
87*1b4853f5SAndroid Build Coastguard Worker 	v4l2_fh_add(&session->fh);
88*1b4853f5SAndroid Build Coastguard Worker 
89*1b4853f5SAndroid Build Coastguard Worker 	for (i = 0; i <= VIRTIO_MEDIA_LAST_QUEUE; i++)
90*1b4853f5SAndroid Build Coastguard Worker 		INIT_LIST_HEAD(&session->queues[i].pending_dqbufs);
91*1b4853f5SAndroid Build Coastguard Worker 	mutex_init(&session->dqbufs_lock);
92*1b4853f5SAndroid Build Coastguard Worker 
93*1b4853f5SAndroid Build Coastguard Worker 	init_waitqueue_head(&session->dqbufs_wait);
94*1b4853f5SAndroid Build Coastguard Worker 
95*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->sessions_lock);
96*1b4853f5SAndroid Build Coastguard Worker 	list_add_tail(&session->list, &vv->sessions);
97*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->sessions_lock);
98*1b4853f5SAndroid Build Coastguard Worker 
99*1b4853f5SAndroid Build Coastguard Worker 	return session;
100*1b4853f5SAndroid Build Coastguard Worker 
101*1b4853f5SAndroid Build Coastguard Worker err_payload_sgs:
102*1b4853f5SAndroid Build Coastguard Worker 	kfree(session->shadow_buf);
103*1b4853f5SAndroid Build Coastguard Worker err_shadow_buf:
104*1b4853f5SAndroid Build Coastguard Worker 	kfree(session);
105*1b4853f5SAndroid Build Coastguard Worker err_session:
106*1b4853f5SAndroid Build Coastguard Worker 	return ERR_PTR(-ENOMEM);
107*1b4853f5SAndroid Build Coastguard Worker }
108*1b4853f5SAndroid Build Coastguard Worker 
109*1b4853f5SAndroid Build Coastguard Worker /**
110*1b4853f5SAndroid Build Coastguard Worker  * Close and destroy `session`.
111*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_session_close(struct virtio_media * vv,struct virtio_media_session * session)112*1b4853f5SAndroid Build Coastguard Worker static void virtio_media_session_close(struct virtio_media *vv,
113*1b4853f5SAndroid Build Coastguard Worker 				       struct virtio_media_session *session)
114*1b4853f5SAndroid Build Coastguard Worker {
115*1b4853f5SAndroid Build Coastguard Worker 	int i;
116*1b4853f5SAndroid Build Coastguard Worker 
117*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->sessions_lock);
118*1b4853f5SAndroid Build Coastguard Worker 	list_del(&session->list);
119*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->sessions_lock);
120*1b4853f5SAndroid Build Coastguard Worker 
121*1b4853f5SAndroid Build Coastguard Worker 	v4l2_fh_del(&session->fh);
122*1b4853f5SAndroid Build Coastguard Worker 	v4l2_fh_exit(&session->fh);
123*1b4853f5SAndroid Build Coastguard Worker 
124*1b4853f5SAndroid Build Coastguard Worker 	sg_free_table(&session->command_sgs);
125*1b4853f5SAndroid Build Coastguard Worker 
126*1b4853f5SAndroid Build Coastguard Worker 	for (i = 0; i <= VIRTIO_MEDIA_LAST_QUEUE; i++)
127*1b4853f5SAndroid Build Coastguard Worker 		if (session->queues[i].buffers)
128*1b4853f5SAndroid Build Coastguard Worker 			vfree(session->queues[i].buffers);
129*1b4853f5SAndroid Build Coastguard Worker 
130*1b4853f5SAndroid Build Coastguard Worker 	kfree(session->shadow_buf);
131*1b4853f5SAndroid Build Coastguard Worker 	kfree(session);
132*1b4853f5SAndroid Build Coastguard Worker }
133*1b4853f5SAndroid Build Coastguard Worker 
134*1b4853f5SAndroid Build Coastguard Worker /**
135*1b4853f5SAndroid Build Coastguard Worker  * Lookup the session with `id`.
136*1b4853f5SAndroid Build Coastguard Worker  */
137*1b4853f5SAndroid Build Coastguard Worker static struct virtio_media_session *
virtio_media_find_session(struct virtio_media * vv,u32 id)138*1b4853f5SAndroid Build Coastguard Worker virtio_media_find_session(struct virtio_media *vv, u32 id)
139*1b4853f5SAndroid Build Coastguard Worker {
140*1b4853f5SAndroid Build Coastguard Worker 	struct list_head *p;
141*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session = NULL;
142*1b4853f5SAndroid Build Coastguard Worker 
143*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->sessions_lock);
144*1b4853f5SAndroid Build Coastguard Worker 	list_for_each(p, &vv->sessions) {
145*1b4853f5SAndroid Build Coastguard Worker 		struct virtio_media_session *s =
146*1b4853f5SAndroid Build Coastguard Worker 			list_entry(p, struct virtio_media_session, list);
147*1b4853f5SAndroid Build Coastguard Worker 		if (s->id == id) {
148*1b4853f5SAndroid Build Coastguard Worker 			session = s;
149*1b4853f5SAndroid Build Coastguard Worker 			break;
150*1b4853f5SAndroid Build Coastguard Worker 		}
151*1b4853f5SAndroid Build Coastguard Worker 	}
152*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->sessions_lock);
153*1b4853f5SAndroid Build Coastguard Worker 
154*1b4853f5SAndroid Build Coastguard Worker 	return session;
155*1b4853f5SAndroid Build Coastguard Worker }
156*1b4853f5SAndroid Build Coastguard Worker 
157*1b4853f5SAndroid Build Coastguard Worker /**
158*1b4853f5SAndroid Build Coastguard Worker  * Callback parameters to the virtio command queue.
159*1b4853f5SAndroid Build Coastguard Worker  */
160*1b4853f5SAndroid Build Coastguard Worker struct virtio_media_cmd_callback_param {
161*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv;
162*1b4853f5SAndroid Build Coastguard Worker 	/* Flag to switch once the command is completed */
163*1b4853f5SAndroid Build Coastguard Worker 	bool done_flag;
164*1b4853f5SAndroid Build Coastguard Worker 	/* Size of the received response */
165*1b4853f5SAndroid Build Coastguard Worker 	size_t resp_len;
166*1b4853f5SAndroid Build Coastguard Worker };
167*1b4853f5SAndroid Build Coastguard Worker 
168*1b4853f5SAndroid Build Coastguard Worker /**
169*1b4853f5SAndroid Build Coastguard Worker  * Callback for the command queue. This just wakes up the thread that was
170*1b4853f5SAndroid Build Coastguard Worker  * waiting on the command to complete.
171*1b4853f5SAndroid Build Coastguard Worker  */
commandq_callback(struct virtqueue * queue)172*1b4853f5SAndroid Build Coastguard Worker static void commandq_callback(struct virtqueue *queue)
173*1b4853f5SAndroid Build Coastguard Worker {
174*1b4853f5SAndroid Build Coastguard Worker 	unsigned int len;
175*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_callback_param *param;
176*1b4853f5SAndroid Build Coastguard Worker 
177*1b4853f5SAndroid Build Coastguard Worker 	while ((param = virtqueue_get_buf(queue, &len))) {
178*1b4853f5SAndroid Build Coastguard Worker 		param->done_flag = true;
179*1b4853f5SAndroid Build Coastguard Worker 		param->resp_len = len;
180*1b4853f5SAndroid Build Coastguard Worker 		wake_up(&param->vv->wq);
181*1b4853f5SAndroid Build Coastguard Worker 	}
182*1b4853f5SAndroid Build Coastguard Worker 
183*1b4853f5SAndroid Build Coastguard Worker 	virtqueue_enable_cb(queue);
184*1b4853f5SAndroid Build Coastguard Worker }
185*1b4853f5SAndroid Build Coastguard Worker 
186*1b4853f5SAndroid Build Coastguard Worker /**
187*1b4853f5SAndroid Build Coastguard Worker  * Returns 0 in case of success, or a negative error code.
188*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_kick_command(struct virtio_media * vv,struct scatterlist ** sgs,const size_t out_sgs,const size_t in_sgs,size_t * resp_len)189*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_kick_command(struct virtio_media *vv,
190*1b4853f5SAndroid Build Coastguard Worker 				     struct scatterlist **sgs,
191*1b4853f5SAndroid Build Coastguard Worker 				     const size_t out_sgs, const size_t in_sgs,
192*1b4853f5SAndroid Build Coastguard Worker 				     size_t *resp_len)
193*1b4853f5SAndroid Build Coastguard Worker {
194*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_callback_param cb_param = {
195*1b4853f5SAndroid Build Coastguard Worker 		.vv = vv,
196*1b4853f5SAndroid Build Coastguard Worker 		.done_flag = false,
197*1b4853f5SAndroid Build Coastguard Worker 		.resp_len = 0,
198*1b4853f5SAndroid Build Coastguard Worker 	};
199*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_resp_header *resp_header;
200*1b4853f5SAndroid Build Coastguard Worker 	int ret;
201*1b4853f5SAndroid Build Coastguard Worker 
202*1b4853f5SAndroid Build Coastguard Worker 	ret = virtqueue_add_sgs(vv->commandq, sgs, out_sgs, in_sgs, &cb_param,
203*1b4853f5SAndroid Build Coastguard Worker 				GFP_ATOMIC);
204*1b4853f5SAndroid Build Coastguard Worker 	if (ret) {
205*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev,
206*1b4853f5SAndroid Build Coastguard Worker 			 "failed to add sgs to command virtqueue\n");
207*1b4853f5SAndroid Build Coastguard Worker 		return ret;
208*1b4853f5SAndroid Build Coastguard Worker 	}
209*1b4853f5SAndroid Build Coastguard Worker 
210*1b4853f5SAndroid Build Coastguard Worker 	if (!virtqueue_kick(vv->commandq)) {
211*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev, "failed to kick command virtqueue\n");
212*1b4853f5SAndroid Build Coastguard Worker 		return -EINVAL;
213*1b4853f5SAndroid Build Coastguard Worker 	}
214*1b4853f5SAndroid Build Coastguard Worker 
215*1b4853f5SAndroid Build Coastguard Worker 	/* Wait for the response. */
216*1b4853f5SAndroid Build Coastguard Worker 	ret = wait_event_timeout(vv->wq, cb_param.done_flag, 5 * HZ);
217*1b4853f5SAndroid Build Coastguard Worker 	if (ret == 0) {
218*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev,
219*1b4853f5SAndroid Build Coastguard Worker 			 "timed out waiting for response to command\n");
220*1b4853f5SAndroid Build Coastguard Worker 		return -ETIMEDOUT;
221*1b4853f5SAndroid Build Coastguard Worker 	}
222*1b4853f5SAndroid Build Coastguard Worker 
223*1b4853f5SAndroid Build Coastguard Worker 	if (resp_len)
224*1b4853f5SAndroid Build Coastguard Worker 		*resp_len = cb_param.resp_len;
225*1b4853f5SAndroid Build Coastguard Worker 
226*1b4853f5SAndroid Build Coastguard Worker 	if (in_sgs > 0) {
227*1b4853f5SAndroid Build Coastguard Worker 		/*
228*1b4853f5SAndroid Build Coastguard Worker 		 * If we expect a response, make sure we have at least a response header - anything shorter is
229*1b4853f5SAndroid Build Coastguard Worker 		 * invalid.
230*1b4853f5SAndroid Build Coastguard Worker 		 */
231*1b4853f5SAndroid Build Coastguard Worker 		if (cb_param.resp_len < sizeof(*resp_header)) {
232*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(&vv->v4l2_dev,
233*1b4853f5SAndroid Build Coastguard Worker 				 "received response header is too short\n");
234*1b4853f5SAndroid Build Coastguard Worker 			return -EINVAL;
235*1b4853f5SAndroid Build Coastguard Worker 		}
236*1b4853f5SAndroid Build Coastguard Worker 
237*1b4853f5SAndroid Build Coastguard Worker 		resp_header = sg_virt(sgs[out_sgs]);
238*1b4853f5SAndroid Build Coastguard Worker 		if (resp_header->status)
239*1b4853f5SAndroid Build Coastguard Worker 			/* Host returns a positive error code. */
240*1b4853f5SAndroid Build Coastguard Worker 			return -resp_header->status;
241*1b4853f5SAndroid Build Coastguard Worker 	}
242*1b4853f5SAndroid Build Coastguard Worker 
243*1b4853f5SAndroid Build Coastguard Worker 	return 0;
244*1b4853f5SAndroid Build Coastguard Worker }
245*1b4853f5SAndroid Build Coastguard Worker 
246*1b4853f5SAndroid Build Coastguard Worker /**
247*1b4853f5SAndroid Build Coastguard Worker  * Send a command to the host and wait for its response.
248*1b4853f5SAndroid Build Coastguard Worker  * @vv: the virtio_media device to communicate with.
249*1b4853f5SAndroid Build Coastguard Worker  * @minimum_resp_len: the minimum length of the response expected by the caller
250*1b4853f5SAndroid Build Coastguard Worker  * in case the command succeeded. Anything shorter than that will result in an
251*1b4853f5SAndroid Build Coastguard Worker  * error.
252*1b4853f5SAndroid Build Coastguard Worker  *
253*1b4853f5SAndroid Build Coastguard Worker  * Returns 0 in case of success or an error code. If an error is returned,
254*1b4853f5SAndroid Build Coastguard Worker  * resp_len might not have been updated.
255*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_send_command(struct virtio_media * vv,struct scatterlist ** sgs,const size_t out_sgs,const size_t in_sgs,size_t minimum_resp_len,size_t * resp_len)256*1b4853f5SAndroid Build Coastguard Worker int virtio_media_send_command(struct virtio_media *vv, struct scatterlist **sgs,
257*1b4853f5SAndroid Build Coastguard Worker 			      const size_t out_sgs, const size_t in_sgs,
258*1b4853f5SAndroid Build Coastguard Worker 			      size_t minimum_resp_len, size_t *resp_len)
259*1b4853f5SAndroid Build Coastguard Worker {
260*1b4853f5SAndroid Build Coastguard Worker 	size_t local_resp_len = resp_len ? *resp_len : 0;
261*1b4853f5SAndroid Build Coastguard Worker 	int ret = virtio_media_kick_command(vv, sgs, out_sgs, in_sgs,
262*1b4853f5SAndroid Build Coastguard Worker 					    &local_resp_len);
263*1b4853f5SAndroid Build Coastguard Worker 	if (resp_len)
264*1b4853f5SAndroid Build Coastguard Worker 		*resp_len = local_resp_len;
265*1b4853f5SAndroid Build Coastguard Worker 
266*1b4853f5SAndroid Build Coastguard Worker 	/* If the host could not process the command, there is no valid response */
267*1b4853f5SAndroid Build Coastguard Worker 	if (ret < 0)
268*1b4853f5SAndroid Build Coastguard Worker 		return ret;
269*1b4853f5SAndroid Build Coastguard Worker 
270*1b4853f5SAndroid Build Coastguard Worker 	/* Make sure the host wrote a complete reply. */
271*1b4853f5SAndroid Build Coastguard Worker 	if (local_resp_len < minimum_resp_len) {
272*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(
273*1b4853f5SAndroid Build Coastguard Worker 			&vv->v4l2_dev,
274*1b4853f5SAndroid Build Coastguard Worker 			"received response is too short: received %zu, expected at least %zu\n",
275*1b4853f5SAndroid Build Coastguard Worker 			local_resp_len, minimum_resp_len);
276*1b4853f5SAndroid Build Coastguard Worker 		return -EINVAL;
277*1b4853f5SAndroid Build Coastguard Worker 	}
278*1b4853f5SAndroid Build Coastguard Worker 
279*1b4853f5SAndroid Build Coastguard Worker 	return 0;
280*1b4853f5SAndroid Build Coastguard Worker }
281*1b4853f5SAndroid Build Coastguard Worker 
282*1b4853f5SAndroid Build Coastguard Worker /**
283*1b4853f5SAndroid Build Coastguard Worker  * Send the event buffer to the host so it can return it back to us filled with
284*1b4853f5SAndroid Build Coastguard Worker  * the next event that occurred.
285*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_send_event_buffer(struct virtio_media * vv,void * event_buffer)286*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_send_event_buffer(struct virtio_media *vv,
287*1b4853f5SAndroid Build Coastguard Worker 					  void *event_buffer)
288*1b4853f5SAndroid Build Coastguard Worker {
289*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist *sgs[1], vresp;
290*1b4853f5SAndroid Build Coastguard Worker 	int ret;
291*1b4853f5SAndroid Build Coastguard Worker 
292*1b4853f5SAndroid Build Coastguard Worker 	sg_init_one(&vresp, event_buffer, VIRTIO_MEDIA_EVENT_MAX_SIZE);
293*1b4853f5SAndroid Build Coastguard Worker 	sgs[0] = &vresp;
294*1b4853f5SAndroid Build Coastguard Worker 
295*1b4853f5SAndroid Build Coastguard Worker 	ret = virtqueue_add_sgs(vv->eventq, sgs, 0, 1, event_buffer,
296*1b4853f5SAndroid Build Coastguard Worker 				GFP_ATOMIC);
297*1b4853f5SAndroid Build Coastguard Worker 	if (ret) {
298*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev,
299*1b4853f5SAndroid Build Coastguard Worker 			 "failed to add sgs to event virtqueue\n");
300*1b4853f5SAndroid Build Coastguard Worker 		return ret;
301*1b4853f5SAndroid Build Coastguard Worker 	}
302*1b4853f5SAndroid Build Coastguard Worker 
303*1b4853f5SAndroid Build Coastguard Worker 	if (!virtqueue_kick(vv->eventq)) {
304*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev, "failed to kick event virtqueue\n");
305*1b4853f5SAndroid Build Coastguard Worker 		return -EINVAL;
306*1b4853f5SAndroid Build Coastguard Worker 	}
307*1b4853f5SAndroid Build Coastguard Worker 
308*1b4853f5SAndroid Build Coastguard Worker 	return 0;
309*1b4853f5SAndroid Build Coastguard Worker }
310*1b4853f5SAndroid Build Coastguard Worker 
eventq_callback(struct virtqueue * queue)311*1b4853f5SAndroid Build Coastguard Worker static void eventq_callback(struct virtqueue *queue)
312*1b4853f5SAndroid Build Coastguard Worker {
313*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = queue->vdev->priv;
314*1b4853f5SAndroid Build Coastguard Worker 
315*1b4853f5SAndroid Build Coastguard Worker 	schedule_work(&vv->eventq_work);
316*1b4853f5SAndroid Build Coastguard Worker }
317*1b4853f5SAndroid Build Coastguard Worker 
318*1b4853f5SAndroid Build Coastguard Worker static void
virtio_media_process_dqbuf_event(struct virtio_media * vv,struct virtio_media_session * session,struct virtio_media_event_dqbuf * dqbuf_evt)319*1b4853f5SAndroid Build Coastguard Worker virtio_media_process_dqbuf_event(struct virtio_media *vv,
320*1b4853f5SAndroid Build Coastguard Worker 				 struct virtio_media_session *session,
321*1b4853f5SAndroid Build Coastguard Worker 				 struct virtio_media_event_dqbuf *dqbuf_evt)
322*1b4853f5SAndroid Build Coastguard Worker {
323*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_buffer *dqbuf;
324*1b4853f5SAndroid Build Coastguard Worker 	const enum v4l2_buf_type queue_type = dqbuf_evt->buffer.type;
325*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_queue_state *queue;
326*1b4853f5SAndroid Build Coastguard Worker 	typeof(dqbuf->buffer.m) buffer_m;
327*1b4853f5SAndroid Build Coastguard Worker 	typeof(dqbuf->buffer.m.planes[0].m) plane_m;
328*1b4853f5SAndroid Build Coastguard Worker 	int i;
329*1b4853f5SAndroid Build Coastguard Worker 
330*1b4853f5SAndroid Build Coastguard Worker 	if (queue_type >= ARRAY_SIZE(session->queues)) {
331*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev,
332*1b4853f5SAndroid Build Coastguard Worker 			 "unmanaged queue %d passed to dqbuf event",
333*1b4853f5SAndroid Build Coastguard Worker 			 dqbuf_evt->buffer.type);
334*1b4853f5SAndroid Build Coastguard Worker 		return;
335*1b4853f5SAndroid Build Coastguard Worker 	}
336*1b4853f5SAndroid Build Coastguard Worker 	queue = &session->queues[queue_type];
337*1b4853f5SAndroid Build Coastguard Worker 
338*1b4853f5SAndroid Build Coastguard Worker 	if (dqbuf_evt->buffer.index >= queue->allocated_bufs) {
339*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev,
340*1b4853f5SAndroid Build Coastguard Worker 			 "invalid buffer ID %d for queue %d in dqbuf event",
341*1b4853f5SAndroid Build Coastguard Worker 			 dqbuf_evt->buffer.index, dqbuf_evt->buffer.type);
342*1b4853f5SAndroid Build Coastguard Worker 		return;
343*1b4853f5SAndroid Build Coastguard Worker 	}
344*1b4853f5SAndroid Build Coastguard Worker 
345*1b4853f5SAndroid Build Coastguard Worker 	dqbuf = &queue->buffers[dqbuf_evt->buffer.index];
346*1b4853f5SAndroid Build Coastguard Worker 
347*1b4853f5SAndroid Build Coastguard Worker 	/*
348*1b4853f5SAndroid Build Coastguard Worker 	 * Preserve the 'm' union that was passed to us during QBUF so userspace
349*1b4853f5SAndroid Build Coastguard Worker 	 * gets back the information it submitted.
350*1b4853f5SAndroid Build Coastguard Worker 	 */
351*1b4853f5SAndroid Build Coastguard Worker 	buffer_m = dqbuf->buffer.m;
352*1b4853f5SAndroid Build Coastguard Worker 	memcpy(&dqbuf->buffer, &dqbuf_evt->buffer, sizeof(dqbuf->buffer));
353*1b4853f5SAndroid Build Coastguard Worker 	dqbuf->buffer.m = buffer_m;
354*1b4853f5SAndroid Build Coastguard Worker 	if (V4L2_TYPE_IS_MULTIPLANAR(dqbuf->buffer.type)) {
355*1b4853f5SAndroid Build Coastguard Worker 		if (dqbuf->buffer.length > VIDEO_MAX_PLANES) {
356*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(
357*1b4853f5SAndroid Build Coastguard Worker 				&vv->v4l2_dev,
358*1b4853f5SAndroid Build Coastguard Worker 				"invalid number of planes received from host for "
359*1b4853f5SAndroid Build Coastguard Worker 				"a multiplanar buffer\n");
360*1b4853f5SAndroid Build Coastguard Worker 			return;
361*1b4853f5SAndroid Build Coastguard Worker 		}
362*1b4853f5SAndroid Build Coastguard Worker 		for (i = 0; i < dqbuf->buffer.length; i++) {
363*1b4853f5SAndroid Build Coastguard Worker 			plane_m = dqbuf->planes[i].m;
364*1b4853f5SAndroid Build Coastguard Worker 			memcpy(&dqbuf->planes[i], &dqbuf_evt->planes[i],
365*1b4853f5SAndroid Build Coastguard Worker 			       sizeof(struct v4l2_plane));
366*1b4853f5SAndroid Build Coastguard Worker 			dqbuf->planes[i].m = plane_m;
367*1b4853f5SAndroid Build Coastguard Worker 		}
368*1b4853f5SAndroid Build Coastguard Worker 	}
369*1b4853f5SAndroid Build Coastguard Worker 
370*1b4853f5SAndroid Build Coastguard Worker 	/* Set the DONE flag as the buffer is waiting for being dequeued. */
371*1b4853f5SAndroid Build Coastguard Worker 	dqbuf->buffer.flags |= V4L2_BUF_FLAG_DONE;
372*1b4853f5SAndroid Build Coastguard Worker 
373*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&session->dqbufs_lock);
374*1b4853f5SAndroid Build Coastguard Worker 	list_add_tail(&dqbuf->list, &queue->pending_dqbufs);
375*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&session->dqbufs_lock);
376*1b4853f5SAndroid Build Coastguard Worker 	queue->queued_bufs -= 1;
377*1b4853f5SAndroid Build Coastguard Worker 	wake_up(&session->dqbufs_wait);
378*1b4853f5SAndroid Build Coastguard Worker }
379*1b4853f5SAndroid Build Coastguard Worker 
virtio_media_process_events(struct virtio_media * vv)380*1b4853f5SAndroid Build Coastguard Worker void virtio_media_process_events(struct virtio_media *vv)
381*1b4853f5SAndroid Build Coastguard Worker {
382*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_event_error *error_evt;
383*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_event_dqbuf *dqbuf_evt;
384*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_event_event *event_evt;
385*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session;
386*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_event_header *evt;
387*1b4853f5SAndroid Build Coastguard Worker 	unsigned int len;
388*1b4853f5SAndroid Build Coastguard Worker 
389*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->events_process_lock);
390*1b4853f5SAndroid Build Coastguard Worker 
391*1b4853f5SAndroid Build Coastguard Worker 	while ((evt = virtqueue_get_buf(vv->eventq, &len))) {
392*1b4853f5SAndroid Build Coastguard Worker 		/* Make sure we received enough data */
393*1b4853f5SAndroid Build Coastguard Worker 		if (len < sizeof(*evt)) {
394*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(
395*1b4853f5SAndroid Build Coastguard Worker 				&vv->v4l2_dev,
396*1b4853f5SAndroid Build Coastguard Worker 				"event is too short: got %u, expected at least %zu\n",
397*1b4853f5SAndroid Build Coastguard Worker 				len, sizeof(*evt));
398*1b4853f5SAndroid Build Coastguard Worker 			goto end_of_event;
399*1b4853f5SAndroid Build Coastguard Worker 		}
400*1b4853f5SAndroid Build Coastguard Worker 
401*1b4853f5SAndroid Build Coastguard Worker 		session = virtio_media_find_session(vv, evt->session_id);
402*1b4853f5SAndroid Build Coastguard Worker 		if (session == NULL) {
403*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(&vv->v4l2_dev, "cannot find session %d\n",
404*1b4853f5SAndroid Build Coastguard Worker 				 evt->session_id);
405*1b4853f5SAndroid Build Coastguard Worker 			goto end_of_event;
406*1b4853f5SAndroid Build Coastguard Worker 		}
407*1b4853f5SAndroid Build Coastguard Worker 
408*1b4853f5SAndroid Build Coastguard Worker 		switch (evt->event) {
409*1b4853f5SAndroid Build Coastguard Worker 		case VIRTIO_MEDIA_EVT_ERROR:
410*1b4853f5SAndroid Build Coastguard Worker 			if (len < sizeof(*error_evt)) {
411*1b4853f5SAndroid Build Coastguard Worker 				v4l2_err(
412*1b4853f5SAndroid Build Coastguard Worker 					&vv->v4l2_dev,
413*1b4853f5SAndroid Build Coastguard Worker 					"error event is too short: got %u, expected %zu\n",
414*1b4853f5SAndroid Build Coastguard Worker 					len, sizeof(*error_evt));
415*1b4853f5SAndroid Build Coastguard Worker 				break;
416*1b4853f5SAndroid Build Coastguard Worker 			}
417*1b4853f5SAndroid Build Coastguard Worker 			error_evt = (struct virtio_media_event_error *)evt;
418*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(&vv->v4l2_dev,
419*1b4853f5SAndroid Build Coastguard Worker 				 "received error %d for session %d",
420*1b4853f5SAndroid Build Coastguard Worker 				 error_evt->errno, error_evt->hdr.session_id);
421*1b4853f5SAndroid Build Coastguard Worker 			/* TODO close session! */
422*1b4853f5SAndroid Build Coastguard Worker 			break;
423*1b4853f5SAndroid Build Coastguard Worker 
424*1b4853f5SAndroid Build Coastguard Worker 		/*
425*1b4853f5SAndroid Build Coastguard Worker 		 * Dequeued buffer: put it into the right queue so user-space can dequeue
426*1b4853f5SAndroid Build Coastguard Worker 		 * it.
427*1b4853f5SAndroid Build Coastguard Worker 		 */
428*1b4853f5SAndroid Build Coastguard Worker 		case VIRTIO_MEDIA_EVT_DQBUF:
429*1b4853f5SAndroid Build Coastguard Worker 			if (len < sizeof(*dqbuf_evt)) {
430*1b4853f5SAndroid Build Coastguard Worker 				v4l2_err(
431*1b4853f5SAndroid Build Coastguard Worker 					&vv->v4l2_dev,
432*1b4853f5SAndroid Build Coastguard Worker 					"dqbuf event is too short: got %u, expected %zu\n",
433*1b4853f5SAndroid Build Coastguard Worker 					len, sizeof(*dqbuf_evt));
434*1b4853f5SAndroid Build Coastguard Worker 				break;
435*1b4853f5SAndroid Build Coastguard Worker 			}
436*1b4853f5SAndroid Build Coastguard Worker 			dqbuf_evt = (struct virtio_media_event_dqbuf *)evt;
437*1b4853f5SAndroid Build Coastguard Worker 			virtio_media_process_dqbuf_event(vv, session,
438*1b4853f5SAndroid Build Coastguard Worker 							 dqbuf_evt);
439*1b4853f5SAndroid Build Coastguard Worker 			break;
440*1b4853f5SAndroid Build Coastguard Worker 
441*1b4853f5SAndroid Build Coastguard Worker 		case VIRTIO_MEDIA_EVT_EVENT:
442*1b4853f5SAndroid Build Coastguard Worker 			if (len < sizeof(*event_evt)) {
443*1b4853f5SAndroid Build Coastguard Worker 				v4l2_err(
444*1b4853f5SAndroid Build Coastguard Worker 					&vv->v4l2_dev,
445*1b4853f5SAndroid Build Coastguard Worker 					"session event is too short: got %u expected %zu\n",
446*1b4853f5SAndroid Build Coastguard Worker 					len, sizeof(*event_evt));
447*1b4853f5SAndroid Build Coastguard Worker 				break;
448*1b4853f5SAndroid Build Coastguard Worker 			}
449*1b4853f5SAndroid Build Coastguard Worker 
450*1b4853f5SAndroid Build Coastguard Worker 			event_evt = (struct virtio_media_event_event *)evt;
451*1b4853f5SAndroid Build Coastguard Worker 			v4l2_event_queue_fh(&session->fh, &event_evt->event);
452*1b4853f5SAndroid Build Coastguard Worker 			break;
453*1b4853f5SAndroid Build Coastguard Worker 
454*1b4853f5SAndroid Build Coastguard Worker 		default:
455*1b4853f5SAndroid Build Coastguard Worker 			v4l2_err(&vv->v4l2_dev, "unknown event type %d\n",
456*1b4853f5SAndroid Build Coastguard Worker 				 evt->event);
457*1b4853f5SAndroid Build Coastguard Worker 			break;
458*1b4853f5SAndroid Build Coastguard Worker 		}
459*1b4853f5SAndroid Build Coastguard Worker 
460*1b4853f5SAndroid Build Coastguard Worker end_of_event:
461*1b4853f5SAndroid Build Coastguard Worker 		virtio_media_send_event_buffer(vv, evt);
462*1b4853f5SAndroid Build Coastguard Worker 	}
463*1b4853f5SAndroid Build Coastguard Worker 
464*1b4853f5SAndroid Build Coastguard Worker 	virtqueue_enable_cb(vv->eventq);
465*1b4853f5SAndroid Build Coastguard Worker 
466*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->events_process_lock);
467*1b4853f5SAndroid Build Coastguard Worker }
468*1b4853f5SAndroid Build Coastguard Worker 
469*1b4853f5SAndroid Build Coastguard Worker /**
470*1b4853f5SAndroid Build Coastguard Worker  * Event callback. This processes the returned event buffer and immediately
471*1b4853f5SAndroid Build Coastguard Worker  * sends it again to the host so it can send us the next event without ever
472*1b4853f5SAndroid Build Coastguard Worker  * starving.
473*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_event_work(struct work_struct * work)474*1b4853f5SAndroid Build Coastguard Worker static void virtio_media_event_work(struct work_struct *work)
475*1b4853f5SAndroid Build Coastguard Worker {
476*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv =
477*1b4853f5SAndroid Build Coastguard Worker 		container_of(work, struct virtio_media, eventq_work);
478*1b4853f5SAndroid Build Coastguard Worker 
479*1b4853f5SAndroid Build Coastguard Worker 	virtio_media_process_events(vv);
480*1b4853f5SAndroid Build Coastguard Worker }
481*1b4853f5SAndroid Build Coastguard Worker 
482*1b4853f5SAndroid Build Coastguard Worker /**
483*1b4853f5SAndroid Build Coastguard Worker  * Opens the device and create a new session.
484*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_device_open(struct file * file)485*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_device_open(struct file *file)
486*1b4853f5SAndroid Build Coastguard Worker {
487*1b4853f5SAndroid Build Coastguard Worker 	struct video_device *video_dev = video_devdata(file);
488*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = to_virtio_media(video_dev);
489*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_open *cmd_open = &vv->cmd.open;
490*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_resp_open *resp_open = &vv->resp.open;
491*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist cmd_sg = {}, resp_sg = {};
492*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist *sgs[2] = { &cmd_sg, &resp_sg };
493*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session;
494*1b4853f5SAndroid Build Coastguard Worker 	u32 session_id;
495*1b4853f5SAndroid Build Coastguard Worker 	int ret;
496*1b4853f5SAndroid Build Coastguard Worker 
497*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->vlock);
498*1b4853f5SAndroid Build Coastguard Worker 
499*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&cmd_sg, cmd_open, sizeof(*cmd_open));
500*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&cmd_sg);
501*1b4853f5SAndroid Build Coastguard Worker 
502*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&resp_sg, resp_open, sizeof(*resp_open));
503*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&resp_sg);
504*1b4853f5SAndroid Build Coastguard Worker 
505*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->bufs_lock);
506*1b4853f5SAndroid Build Coastguard Worker 	cmd_open->hdr.cmd = VIRTIO_MEDIA_CMD_OPEN;
507*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_media_send_command(vv, sgs, 1, 1, sizeof(*resp_open),
508*1b4853f5SAndroid Build Coastguard Worker 					NULL);
509*1b4853f5SAndroid Build Coastguard Worker 	session_id = resp_open->session_id;
510*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->bufs_lock);
511*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->vlock);
512*1b4853f5SAndroid Build Coastguard Worker 	if (ret < 0)
513*1b4853f5SAndroid Build Coastguard Worker 		return ret;
514*1b4853f5SAndroid Build Coastguard Worker 
515*1b4853f5SAndroid Build Coastguard Worker 	session = virtio_media_session_alloc(vv, session_id,
516*1b4853f5SAndroid Build Coastguard Worker 					     (file->f_flags & O_NONBLOCK));
517*1b4853f5SAndroid Build Coastguard Worker 	if (IS_ERR(session))
518*1b4853f5SAndroid Build Coastguard Worker 		return PTR_ERR(session);
519*1b4853f5SAndroid Build Coastguard Worker 
520*1b4853f5SAndroid Build Coastguard Worker 	file->private_data = &session->fh;
521*1b4853f5SAndroid Build Coastguard Worker 
522*1b4853f5SAndroid Build Coastguard Worker 	return 0;
523*1b4853f5SAndroid Build Coastguard Worker }
524*1b4853f5SAndroid Build Coastguard Worker 
525*1b4853f5SAndroid Build Coastguard Worker /**
526*1b4853f5SAndroid Build Coastguard Worker  * Close a previously opened session.
527*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_device_close(struct file * file)528*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_device_close(struct file *file)
529*1b4853f5SAndroid Build Coastguard Worker {
530*1b4853f5SAndroid Build Coastguard Worker 	struct video_device *video_dev = video_devdata(file);
531*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = to_virtio_media(video_dev);
532*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session =
533*1b4853f5SAndroid Build Coastguard Worker 		fh_to_session(file->private_data);
534*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_close *cmd_close = &session->cmd.close;
535*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist cmd_sg = {};
536*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist *sgs[1] = { &cmd_sg };
537*1b4853f5SAndroid Build Coastguard Worker 	int ret;
538*1b4853f5SAndroid Build Coastguard Worker 
539*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->vlock);
540*1b4853f5SAndroid Build Coastguard Worker 
541*1b4853f5SAndroid Build Coastguard Worker 	cmd_close->hdr.cmd = VIRTIO_MEDIA_CMD_CLOSE;
542*1b4853f5SAndroid Build Coastguard Worker 	cmd_close->session_id = session->id;
543*1b4853f5SAndroid Build Coastguard Worker 
544*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&cmd_sg, cmd_close, sizeof(*cmd_close));
545*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&cmd_sg);
546*1b4853f5SAndroid Build Coastguard Worker 
547*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_media_send_command(vv, sgs, 1, 0, 0, NULL);
548*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->vlock);
549*1b4853f5SAndroid Build Coastguard Worker 	if (ret < 0)
550*1b4853f5SAndroid Build Coastguard Worker 		return ret;
551*1b4853f5SAndroid Build Coastguard Worker 
552*1b4853f5SAndroid Build Coastguard Worker 	virtio_media_session_close(vv, session);
553*1b4853f5SAndroid Build Coastguard Worker 
554*1b4853f5SAndroid Build Coastguard Worker 	return 0;
555*1b4853f5SAndroid Build Coastguard Worker }
556*1b4853f5SAndroid Build Coastguard Worker 
557*1b4853f5SAndroid Build Coastguard Worker /**
558*1b4853f5SAndroid Build Coastguard Worker  * Implements poll logic for a virtio-media device.
559*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_device_poll(struct file * file,poll_table * wait)560*1b4853f5SAndroid Build Coastguard Worker static __poll_t virtio_media_device_poll(struct file *file, poll_table *wait)
561*1b4853f5SAndroid Build Coastguard Worker {
562*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session =
563*1b4853f5SAndroid Build Coastguard Worker 		fh_to_session(file->private_data);
564*1b4853f5SAndroid Build Coastguard Worker 	enum v4l2_buf_type capture_type =
565*1b4853f5SAndroid Build Coastguard Worker 		session->uses_mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
566*1b4853f5SAndroid Build Coastguard Worker 				       V4L2_BUF_TYPE_VIDEO_CAPTURE;
567*1b4853f5SAndroid Build Coastguard Worker 	enum v4l2_buf_type output_type =
568*1b4853f5SAndroid Build Coastguard Worker 		session->uses_mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
569*1b4853f5SAndroid Build Coastguard Worker 				       V4L2_BUF_TYPE_VIDEO_OUTPUT;
570*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_queue_state *capture_queue =
571*1b4853f5SAndroid Build Coastguard Worker 		&session->queues[capture_type];
572*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_queue_state *output_queue =
573*1b4853f5SAndroid Build Coastguard Worker 		&session->queues[output_type];
574*1b4853f5SAndroid Build Coastguard Worker 	__poll_t req_events = poll_requested_events(wait);
575*1b4853f5SAndroid Build Coastguard Worker 	__poll_t rc = 0;
576*1b4853f5SAndroid Build Coastguard Worker 
577*1b4853f5SAndroid Build Coastguard Worker 	poll_wait(file, &session->dqbufs_wait, wait);
578*1b4853f5SAndroid Build Coastguard Worker 	poll_wait(file, &session->fh.wait, wait);
579*1b4853f5SAndroid Build Coastguard Worker 
580*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&session->dqbufs_lock);
581*1b4853f5SAndroid Build Coastguard Worker 	if (req_events & (EPOLLIN | EPOLLRDNORM)) {
582*1b4853f5SAndroid Build Coastguard Worker 		if (!capture_queue->streaming ||
583*1b4853f5SAndroid Build Coastguard Worker 		    (capture_queue->queued_bufs == 0 &&
584*1b4853f5SAndroid Build Coastguard Worker 		     list_empty(&capture_queue->pending_dqbufs)))
585*1b4853f5SAndroid Build Coastguard Worker 			rc |= EPOLLERR;
586*1b4853f5SAndroid Build Coastguard Worker 		else if (!list_empty(&capture_queue->pending_dqbufs))
587*1b4853f5SAndroid Build Coastguard Worker 			rc |= EPOLLIN | EPOLLRDNORM;
588*1b4853f5SAndroid Build Coastguard Worker 	}
589*1b4853f5SAndroid Build Coastguard Worker 	if (req_events & (EPOLLOUT | EPOLLWRNORM)) {
590*1b4853f5SAndroid Build Coastguard Worker 		if (!output_queue->streaming)
591*1b4853f5SAndroid Build Coastguard Worker 			rc |= EPOLLERR;
592*1b4853f5SAndroid Build Coastguard Worker 		else if (output_queue->queued_bufs <
593*1b4853f5SAndroid Build Coastguard Worker 			 output_queue->allocated_bufs)
594*1b4853f5SAndroid Build Coastguard Worker 			rc |= EPOLLOUT | EPOLLWRNORM;
595*1b4853f5SAndroid Build Coastguard Worker 	}
596*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&session->dqbufs_lock);
597*1b4853f5SAndroid Build Coastguard Worker 
598*1b4853f5SAndroid Build Coastguard Worker 	if (v4l2_event_pending(&session->fh))
599*1b4853f5SAndroid Build Coastguard Worker 		rc |= EPOLLPRI;
600*1b4853f5SAndroid Build Coastguard Worker 
601*1b4853f5SAndroid Build Coastguard Worker 	return rc;
602*1b4853f5SAndroid Build Coastguard Worker }
603*1b4853f5SAndroid Build Coastguard Worker 
604*1b4853f5SAndroid Build Coastguard Worker /**
605*1b4853f5SAndroid Build Coastguard Worker  * Inform the host that a previously created MMAP mapping is no longer needed
606*1b4853f5SAndroid Build Coastguard Worker  * and can be removed.
607*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_vma_close_locked(struct vm_area_struct * vma)608*1b4853f5SAndroid Build Coastguard Worker static void virtio_media_vma_close_locked(struct vm_area_struct *vma)
609*1b4853f5SAndroid Build Coastguard Worker {
610*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = vma->vm_private_data;
611*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_munmap *cmd_munmap = &vv->cmd.munmap;
612*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_resp_munmap *resp_munmap = &vv->resp.munmap;
613*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist cmd_sg = {}, resp_sg = {};
614*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist *sgs[2] = { &cmd_sg, &resp_sg };
615*1b4853f5SAndroid Build Coastguard Worker 	int ret;
616*1b4853f5SAndroid Build Coastguard Worker 
617*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&cmd_sg, cmd_munmap, sizeof(*cmd_munmap));
618*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&cmd_sg);
619*1b4853f5SAndroid Build Coastguard Worker 
620*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&resp_sg, resp_munmap, sizeof(*resp_munmap));
621*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&resp_sg);
622*1b4853f5SAndroid Build Coastguard Worker 
623*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->bufs_lock);
624*1b4853f5SAndroid Build Coastguard Worker 	cmd_munmap->hdr.cmd = VIRTIO_MEDIA_CMD_MUNMAP;
625*1b4853f5SAndroid Build Coastguard Worker 	cmd_munmap->guest_addr =
626*1b4853f5SAndroid Build Coastguard Worker 		(vma->vm_pgoff << PAGE_SHIFT) - vv->mmap_region.addr;
627*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_media_send_command(vv, sgs, 1, 1, sizeof(*resp_munmap),
628*1b4853f5SAndroid Build Coastguard Worker 					NULL);
629*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->bufs_lock);
630*1b4853f5SAndroid Build Coastguard Worker 	if (ret < 0) {
631*1b4853f5SAndroid Build Coastguard Worker 		v4l2_err(&vv->v4l2_dev, "host failed to unmap buffer: %d\n",
632*1b4853f5SAndroid Build Coastguard Worker 			 ret);
633*1b4853f5SAndroid Build Coastguard Worker 	}
634*1b4853f5SAndroid Build Coastguard Worker }
635*1b4853f5SAndroid Build Coastguard Worker 
virtio_media_vma_close(struct vm_area_struct * vma)636*1b4853f5SAndroid Build Coastguard Worker static void virtio_media_vma_close(struct vm_area_struct *vma)
637*1b4853f5SAndroid Build Coastguard Worker {
638*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = vma->vm_private_data;
639*1b4853f5SAndroid Build Coastguard Worker 
640*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->vlock);
641*1b4853f5SAndroid Build Coastguard Worker 	virtio_media_vma_close_locked(vma);
642*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->vlock);
643*1b4853f5SAndroid Build Coastguard Worker }
644*1b4853f5SAndroid Build Coastguard Worker 
645*1b4853f5SAndroid Build Coastguard Worker static struct vm_operations_struct virtio_media_vm_ops = {
646*1b4853f5SAndroid Build Coastguard Worker 	.close = virtio_media_vma_close,
647*1b4853f5SAndroid Build Coastguard Worker };
648*1b4853f5SAndroid Build Coastguard Worker 
649*1b4853f5SAndroid Build Coastguard Worker /**
650*1b4853f5SAndroid Build Coastguard Worker  * Perform a mmap request from the guest.
651*1b4853f5SAndroid Build Coastguard Worker  *
652*1b4853f5SAndroid Build Coastguard Worker  * This requests the host to map a MMAP buffer for us, so we can make that
653*1b4853f5SAndroid Build Coastguard Worker  * mapping visible into the user-space address space.
654*1b4853f5SAndroid Build Coastguard Worker  */
virtio_media_device_mmap(struct file * file,struct vm_area_struct * vma)655*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_device_mmap(struct file *file,
656*1b4853f5SAndroid Build Coastguard Worker 				    struct vm_area_struct *vma)
657*1b4853f5SAndroid Build Coastguard Worker {
658*1b4853f5SAndroid Build Coastguard Worker 	struct video_device *video_dev = video_devdata(file);
659*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = to_virtio_media(video_dev);
660*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_session *session =
661*1b4853f5SAndroid Build Coastguard Worker 		fh_to_session(file->private_data);
662*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_cmd_mmap *cmd_mmap = &session->cmd.mmap;
663*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media_resp_mmap *resp_mmap = &session->resp.mmap;
664*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist cmd_sg = {}, resp_sg = {};
665*1b4853f5SAndroid Build Coastguard Worker 	struct scatterlist *sgs[2] = { &cmd_sg, &resp_sg };
666*1b4853f5SAndroid Build Coastguard Worker 	int ret;
667*1b4853f5SAndroid Build Coastguard Worker 
668*1b4853f5SAndroid Build Coastguard Worker 	if (!(vma->vm_flags & VM_SHARED))
669*1b4853f5SAndroid Build Coastguard Worker 		return -EINVAL;
670*1b4853f5SAndroid Build Coastguard Worker 	if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
671*1b4853f5SAndroid Build Coastguard Worker 		return -EINVAL;
672*1b4853f5SAndroid Build Coastguard Worker 
673*1b4853f5SAndroid Build Coastguard Worker 	mutex_lock(&vv->vlock);
674*1b4853f5SAndroid Build Coastguard Worker 
675*1b4853f5SAndroid Build Coastguard Worker 	cmd_mmap->hdr.cmd = VIRTIO_MEDIA_CMD_MMAP;
676*1b4853f5SAndroid Build Coastguard Worker 	cmd_mmap->session_id = session->id;
677*1b4853f5SAndroid Build Coastguard Worker 	cmd_mmap->flags =
678*1b4853f5SAndroid Build Coastguard Worker 		(vma->vm_flags & VM_WRITE) ? VIRTIO_MEDIA_MMAP_FLAG_RW : 0;
679*1b4853f5SAndroid Build Coastguard Worker 	cmd_mmap->offset = vma->vm_pgoff << PAGE_SHIFT;
680*1b4853f5SAndroid Build Coastguard Worker 
681*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&cmd_sg, cmd_mmap, sizeof(*cmd_mmap));
682*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&cmd_sg);
683*1b4853f5SAndroid Build Coastguard Worker 
684*1b4853f5SAndroid Build Coastguard Worker 	sg_set_buf(&resp_sg, resp_mmap, sizeof(*resp_mmap));
685*1b4853f5SAndroid Build Coastguard Worker 	sg_mark_end(&resp_sg);
686*1b4853f5SAndroid Build Coastguard Worker 
687*1b4853f5SAndroid Build Coastguard Worker 	/*
688*1b4853f5SAndroid Build Coastguard Worker 	 * The host performs reference counting and is smart enough to return the
689*1b4853f5SAndroid Build Coastguard Worker 	 * same guest physical address if this is called several times on the same
690*1b4853f5SAndroid Build Coastguard Worker 	 * buffer.
691*1b4853f5SAndroid Build Coastguard Worker 	 */
692*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_media_send_command(vv, sgs, 1, 1, sizeof(*resp_mmap),
693*1b4853f5SAndroid Build Coastguard Worker 					NULL);
694*1b4853f5SAndroid Build Coastguard Worker 	if (ret < 0)
695*1b4853f5SAndroid Build Coastguard Worker 		goto end;
696*1b4853f5SAndroid Build Coastguard Worker 
697*1b4853f5SAndroid Build Coastguard Worker 	vma->vm_private_data = vv;
698*1b4853f5SAndroid Build Coastguard Worker 	/*
699*1b4853f5SAndroid Build Coastguard Worker 	 * Keep the guest address at which the buffer is mapped since we will
700*1b4853f5SAndroid Build Coastguard Worker 	 * use that to unmap.
701*1b4853f5SAndroid Build Coastguard Worker 	 */
702*1b4853f5SAndroid Build Coastguard Worker 	vma->vm_pgoff = (resp_mmap->guest_addr + vv->mmap_region.addr) >>
703*1b4853f5SAndroid Build Coastguard Worker 			PAGE_SHIFT;
704*1b4853f5SAndroid Build Coastguard Worker 
705*1b4853f5SAndroid Build Coastguard Worker 	if (vma->vm_end - vma->vm_start > PAGE_ALIGN(resp_mmap->len)) {
706*1b4853f5SAndroid Build Coastguard Worker 		virtio_media_vma_close_locked(vma);
707*1b4853f5SAndroid Build Coastguard Worker 		ret = -EINVAL;
708*1b4853f5SAndroid Build Coastguard Worker 		goto end;
709*1b4853f5SAndroid Build Coastguard Worker 	}
710*1b4853f5SAndroid Build Coastguard Worker 
711*1b4853f5SAndroid Build Coastguard Worker 	ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
712*1b4853f5SAndroid Build Coastguard Worker 				 vma->vm_end - vma->vm_start,
713*1b4853f5SAndroid Build Coastguard Worker 				 vma->vm_page_prot);
714*1b4853f5SAndroid Build Coastguard Worker 	if (ret)
715*1b4853f5SAndroid Build Coastguard Worker 		goto end;
716*1b4853f5SAndroid Build Coastguard Worker 
717*1b4853f5SAndroid Build Coastguard Worker 	vma->vm_ops = &virtio_media_vm_ops;
718*1b4853f5SAndroid Build Coastguard Worker 
719*1b4853f5SAndroid Build Coastguard Worker end:
720*1b4853f5SAndroid Build Coastguard Worker 	mutex_unlock(&vv->vlock);
721*1b4853f5SAndroid Build Coastguard Worker 	return ret;
722*1b4853f5SAndroid Build Coastguard Worker }
723*1b4853f5SAndroid Build Coastguard Worker 
724*1b4853f5SAndroid Build Coastguard Worker static const struct v4l2_file_operations virtio_media_fops = {
725*1b4853f5SAndroid Build Coastguard Worker 	.owner = THIS_MODULE,
726*1b4853f5SAndroid Build Coastguard Worker 	.open = virtio_media_device_open,
727*1b4853f5SAndroid Build Coastguard Worker 	.release = virtio_media_device_close,
728*1b4853f5SAndroid Build Coastguard Worker 	.poll = virtio_media_device_poll,
729*1b4853f5SAndroid Build Coastguard Worker 	.unlocked_ioctl = virtio_media_device_ioctl,
730*1b4853f5SAndroid Build Coastguard Worker 	.mmap = virtio_media_device_mmap,
731*1b4853f5SAndroid Build Coastguard Worker };
732*1b4853f5SAndroid Build Coastguard Worker 
virtio_media_probe(struct virtio_device * virtio_dev)733*1b4853f5SAndroid Build Coastguard Worker static int virtio_media_probe(struct virtio_device *virtio_dev)
734*1b4853f5SAndroid Build Coastguard Worker {
735*1b4853f5SAndroid Build Coastguard Worker 	struct device *dev = &virtio_dev->dev;
736*1b4853f5SAndroid Build Coastguard Worker 	struct virtqueue *vqs[2];
737*1b4853f5SAndroid Build Coastguard Worker #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
738*1b4853f5SAndroid Build Coastguard Worker 	static struct virtqueue_info vq_info[2] = {
739*1b4853f5SAndroid Build Coastguard Worker 		{
740*1b4853f5SAndroid Build Coastguard Worker 			.name = "command",
741*1b4853f5SAndroid Build Coastguard Worker 			.callback = commandq_callback,
742*1b4853f5SAndroid Build Coastguard Worker 		},
743*1b4853f5SAndroid Build Coastguard Worker 		{
744*1b4853f5SAndroid Build Coastguard Worker 			.name = "event",
745*1b4853f5SAndroid Build Coastguard Worker 			.callback = eventq_callback,
746*1b4853f5SAndroid Build Coastguard Worker 		},
747*1b4853f5SAndroid Build Coastguard Worker 	};
748*1b4853f5SAndroid Build Coastguard Worker #else
749*1b4853f5SAndroid Build Coastguard Worker 	static vq_callback_t *vq_callbacks[] = {
750*1b4853f5SAndroid Build Coastguard Worker 		commandq_callback,
751*1b4853f5SAndroid Build Coastguard Worker 		eventq_callback,
752*1b4853f5SAndroid Build Coastguard Worker 	};
753*1b4853f5SAndroid Build Coastguard Worker 	static const char *const vq_names[] = { "command", "event" };
754*1b4853f5SAndroid Build Coastguard Worker #endif
755*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv;
756*1b4853f5SAndroid Build Coastguard Worker 	struct video_device *vd;
757*1b4853f5SAndroid Build Coastguard Worker 	int i;
758*1b4853f5SAndroid Build Coastguard Worker 	int ret;
759*1b4853f5SAndroid Build Coastguard Worker 
760*1b4853f5SAndroid Build Coastguard Worker 	vv = devm_kzalloc(dev, sizeof(*vv), GFP_KERNEL);
761*1b4853f5SAndroid Build Coastguard Worker 	if (!vv)
762*1b4853f5SAndroid Build Coastguard Worker 		return -ENOMEM;
763*1b4853f5SAndroid Build Coastguard Worker 
764*1b4853f5SAndroid Build Coastguard Worker 	vv->event_buffer = devm_kzalloc(
765*1b4853f5SAndroid Build Coastguard Worker 		dev, VIRTIO_MEDIA_EVENT_MAX_SIZE * VIRTIO_MEDIA_NUM_EVENT_BUFS,
766*1b4853f5SAndroid Build Coastguard Worker 		GFP_KERNEL);
767*1b4853f5SAndroid Build Coastguard Worker 	if (!vv->event_buffer) {
768*1b4853f5SAndroid Build Coastguard Worker 		return -ENOMEM;
769*1b4853f5SAndroid Build Coastguard Worker 	}
770*1b4853f5SAndroid Build Coastguard Worker 
771*1b4853f5SAndroid Build Coastguard Worker 	mutex_init(&vv->bufs_lock);
772*1b4853f5SAndroid Build Coastguard Worker 
773*1b4853f5SAndroid Build Coastguard Worker 	INIT_LIST_HEAD(&vv->sessions);
774*1b4853f5SAndroid Build Coastguard Worker 	mutex_init(&vv->sessions_lock);
775*1b4853f5SAndroid Build Coastguard Worker 	mutex_init(&vv->events_process_lock);
776*1b4853f5SAndroid Build Coastguard Worker 	mutex_init(&vv->vlock);
777*1b4853f5SAndroid Build Coastguard Worker 
778*1b4853f5SAndroid Build Coastguard Worker 	vv->virtio_dev = virtio_dev;
779*1b4853f5SAndroid Build Coastguard Worker 	virtio_dev->priv = vv;
780*1b4853f5SAndroid Build Coastguard Worker 
781*1b4853f5SAndroid Build Coastguard Worker 	init_waitqueue_head(&vv->wq);
782*1b4853f5SAndroid Build Coastguard Worker 
783*1b4853f5SAndroid Build Coastguard Worker 	ret = v4l2_device_register(dev, &vv->v4l2_dev);
784*1b4853f5SAndroid Build Coastguard Worker 	if (ret)
785*1b4853f5SAndroid Build Coastguard Worker 		return ret;
786*1b4853f5SAndroid Build Coastguard Worker 
787*1b4853f5SAndroid Build Coastguard Worker #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
788*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_find_vqs(virtio_dev, 2, vqs, vq_info, NULL);
789*1b4853f5SAndroid Build Coastguard Worker #else
790*1b4853f5SAndroid Build Coastguard Worker 	ret = virtio_find_vqs(virtio_dev, 2, vqs, vq_callbacks, vq_names, NULL);
791*1b4853f5SAndroid Build Coastguard Worker #endif
792*1b4853f5SAndroid Build Coastguard Worker 	if (ret)
793*1b4853f5SAndroid Build Coastguard Worker 		goto err_find_vqs;
794*1b4853f5SAndroid Build Coastguard Worker 
795*1b4853f5SAndroid Build Coastguard Worker 	vv->commandq = vqs[0];
796*1b4853f5SAndroid Build Coastguard Worker 	vv->eventq = vqs[1];
797*1b4853f5SAndroid Build Coastguard Worker 	INIT_WORK(&vv->eventq_work, virtio_media_event_work);
798*1b4853f5SAndroid Build Coastguard Worker 
799*1b4853f5SAndroid Build Coastguard Worker 	/* Get MMAP buffer mapping SHM region */
800*1b4853f5SAndroid Build Coastguard Worker 	virtio_get_shm_region(virtio_dev, &vv->mmap_region,
801*1b4853f5SAndroid Build Coastguard Worker 			      VIRTIO_MEDIA_SHM_MMAP);
802*1b4853f5SAndroid Build Coastguard Worker 
803*1b4853f5SAndroid Build Coastguard Worker 	virtio_device_ready(virtio_dev);
804*1b4853f5SAndroid Build Coastguard Worker 
805*1b4853f5SAndroid Build Coastguard Worker 	vd = &vv->video_dev;
806*1b4853f5SAndroid Build Coastguard Worker 
807*1b4853f5SAndroid Build Coastguard Worker 	vd->v4l2_dev = &vv->v4l2_dev;
808*1b4853f5SAndroid Build Coastguard Worker 	vd->vfl_type = VFL_TYPE_VIDEO;
809*1b4853f5SAndroid Build Coastguard Worker 	vd->ioctl_ops = &virtio_media_ioctl_ops;
810*1b4853f5SAndroid Build Coastguard Worker 	vd->fops = &virtio_media_fops;
811*1b4853f5SAndroid Build Coastguard Worker 	vd->device_caps = virtio_cread32(virtio_dev, 0);
812*1b4853f5SAndroid Build Coastguard Worker 	if (vd->device_caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE))
813*1b4853f5SAndroid Build Coastguard Worker 		vd->vfl_dir = VFL_DIR_M2M;
814*1b4853f5SAndroid Build Coastguard Worker 	else if (vd->device_caps &
815*1b4853f5SAndroid Build Coastguard Worker 		 (V4L2_CAP_VIDEO_OUTPUT | V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
816*1b4853f5SAndroid Build Coastguard Worker 		vd->vfl_dir = VFL_DIR_TX;
817*1b4853f5SAndroid Build Coastguard Worker 	else
818*1b4853f5SAndroid Build Coastguard Worker 		vd->vfl_dir = VFL_DIR_RX;
819*1b4853f5SAndroid Build Coastguard Worker 	vd->release = video_device_release_empty;
820*1b4853f5SAndroid Build Coastguard Worker 	strscpy(vd->name, "virtio-media", sizeof(vd->name));
821*1b4853f5SAndroid Build Coastguard Worker 
822*1b4853f5SAndroid Build Coastguard Worker 	video_set_drvdata(vd, vv);
823*1b4853f5SAndroid Build Coastguard Worker 
824*1b4853f5SAndroid Build Coastguard Worker 	/* TODO find out when we should enable this ioctl? */
825*1b4853f5SAndroid Build Coastguard Worker 	v4l2_disable_ioctl(vd, VIDIOC_S_HW_FREQ_SEEK);
826*1b4853f5SAndroid Build Coastguard Worker 
827*1b4853f5SAndroid Build Coastguard Worker 	ret = video_register_device(vd, virtio_cread32(virtio_dev, 4), 0);
828*1b4853f5SAndroid Build Coastguard Worker 	if (ret)
829*1b4853f5SAndroid Build Coastguard Worker 		return ret;
830*1b4853f5SAndroid Build Coastguard Worker 
831*1b4853f5SAndroid Build Coastguard Worker 	for (i = 0; i < VIRTIO_MEDIA_NUM_EVENT_BUFS; i++) {
832*1b4853f5SAndroid Build Coastguard Worker 		ret = virtio_media_send_event_buffer(
833*1b4853f5SAndroid Build Coastguard Worker 			vv, vv->event_buffer + VIRTIO_MEDIA_EVENT_MAX_SIZE * i);
834*1b4853f5SAndroid Build Coastguard Worker 		if (ret) {
835*1b4853f5SAndroid Build Coastguard Worker 			goto send_event_buffer;
836*1b4853f5SAndroid Build Coastguard Worker 		}
837*1b4853f5SAndroid Build Coastguard Worker 	}
838*1b4853f5SAndroid Build Coastguard Worker 
839*1b4853f5SAndroid Build Coastguard Worker 	return 0;
840*1b4853f5SAndroid Build Coastguard Worker 
841*1b4853f5SAndroid Build Coastguard Worker send_event_buffer:
842*1b4853f5SAndroid Build Coastguard Worker 	video_unregister_device(&vv->video_dev);
843*1b4853f5SAndroid Build Coastguard Worker 	virtio_dev->config->del_vqs(virtio_dev);
844*1b4853f5SAndroid Build Coastguard Worker err_find_vqs:
845*1b4853f5SAndroid Build Coastguard Worker 	v4l2_device_unregister(&vv->v4l2_dev);
846*1b4853f5SAndroid Build Coastguard Worker 
847*1b4853f5SAndroid Build Coastguard Worker 	return ret;
848*1b4853f5SAndroid Build Coastguard Worker }
849*1b4853f5SAndroid Build Coastguard Worker 
virtio_media_remove(struct virtio_device * virtio_dev)850*1b4853f5SAndroid Build Coastguard Worker static void virtio_media_remove(struct virtio_device *virtio_dev)
851*1b4853f5SAndroid Build Coastguard Worker {
852*1b4853f5SAndroid Build Coastguard Worker 	struct virtio_media *vv = virtio_dev->priv;
853*1b4853f5SAndroid Build Coastguard Worker 	struct list_head *p, *n;
854*1b4853f5SAndroid Build Coastguard Worker 
855*1b4853f5SAndroid Build Coastguard Worker 	virtio_reset_device(virtio_dev);
856*1b4853f5SAndroid Build Coastguard Worker 
857*1b4853f5SAndroid Build Coastguard Worker 	v4l2_device_unregister(&vv->v4l2_dev);
858*1b4853f5SAndroid Build Coastguard Worker 	virtio_dev->config->del_vqs(virtio_dev);
859*1b4853f5SAndroid Build Coastguard Worker 	video_unregister_device(&vv->video_dev);
860*1b4853f5SAndroid Build Coastguard Worker 
861*1b4853f5SAndroid Build Coastguard Worker 	list_for_each_safe(p, n, &vv->sessions) {
862*1b4853f5SAndroid Build Coastguard Worker 		struct virtio_media_session *s =
863*1b4853f5SAndroid Build Coastguard Worker 			list_entry(p, struct virtio_media_session, list);
864*1b4853f5SAndroid Build Coastguard Worker 
865*1b4853f5SAndroid Build Coastguard Worker 		virtio_media_session_close(vv, s);
866*1b4853f5SAndroid Build Coastguard Worker 	}
867*1b4853f5SAndroid Build Coastguard Worker }
868*1b4853f5SAndroid Build Coastguard Worker 
869*1b4853f5SAndroid Build Coastguard Worker static struct virtio_device_id id_table[] = {
870*1b4853f5SAndroid Build Coastguard Worker 	{ VIRTIO_ID_MEDIA, VIRTIO_DEV_ANY_ID },
871*1b4853f5SAndroid Build Coastguard Worker 	{ 0 },
872*1b4853f5SAndroid Build Coastguard Worker };
873*1b4853f5SAndroid Build Coastguard Worker 
874*1b4853f5SAndroid Build Coastguard Worker static unsigned int features[] = {};
875*1b4853f5SAndroid Build Coastguard Worker 
876*1b4853f5SAndroid Build Coastguard Worker static struct virtio_driver virtio_media_driver = {
877*1b4853f5SAndroid Build Coastguard Worker 	.feature_table = features,
878*1b4853f5SAndroid Build Coastguard Worker 	.feature_table_size = ARRAY_SIZE(features),
879*1b4853f5SAndroid Build Coastguard Worker 	.driver.name = VIRTIO_MEDIA_DEFAULT_DRIVER_NAME,
880*1b4853f5SAndroid Build Coastguard Worker 	.driver.owner = THIS_MODULE,
881*1b4853f5SAndroid Build Coastguard Worker 	.id_table = id_table,
882*1b4853f5SAndroid Build Coastguard Worker 	.probe = virtio_media_probe,
883*1b4853f5SAndroid Build Coastguard Worker 	.remove = virtio_media_remove,
884*1b4853f5SAndroid Build Coastguard Worker };
885*1b4853f5SAndroid Build Coastguard Worker 
886*1b4853f5SAndroid Build Coastguard Worker module_virtio_driver(virtio_media_driver);
887*1b4853f5SAndroid Build Coastguard Worker 
888*1b4853f5SAndroid Build Coastguard Worker MODULE_DEVICE_TABLE(virtio, id_table);
889*1b4853f5SAndroid Build Coastguard Worker MODULE_DESCRIPTION("virtio media driver");
890*1b4853f5SAndroid Build Coastguard Worker MODULE_AUTHOR("Alexandre Courbot <[email protected]>");
891*1b4853f5SAndroid Build Coastguard Worker MODULE_LICENSE("Dual BSD/GPL");
892