1 /*
2  *             (C) 2012 Mauro Carvalho Chehab
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
17  */
18 
19 #include <config.h>
20 #include <errno.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/syscall.h>
28 
29 #if defined(__OpenBSD__)
30 #include <sys/videoio.h>
31 #include <sys/ioctl.h>
32 #else
33 #include <linux/videodev2.h>
34 #endif
35 
36 #include "libv4l-plugin.h"
37 
38 #define SYS_IOCTL(fd, cmd, arg) \
39 	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
40 #define SYS_READ(fd, buf, len) \
41 	syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
42 #define SYS_WRITE(fd, buf, len) \
43 	syscall(SYS_write, (int)(fd), (const void *)(buf), (size_t)(len));
44 
45 
46 #if HAVE_VISIBILITY
47 #define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
48 #else
49 #define PLUGIN_PUBLIC
50 #endif
51 
52 struct mplane_plugin {
53 	union {
54 		struct {
55 			unsigned int		mplane_capture : 1;
56 			unsigned int		mplane_output  : 1;
57 		};
58 		unsigned int mplane;
59 	};
60 };
61 
62 #define SIMPLE_CONVERT_IOCTL(fd, cmd, arg, __struc) ({		\
63 	int __ret;						\
64 	struct __struc *req = arg;				\
65 	uint32_t type = req->type;				\
66 	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||	\
67 	    type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {	\
68 		errno = EINVAL;					\
69 		__ret = -1;					\
70 	} else {						\
71 		req->type = convert_type(type);			\
72 		__ret = SYS_IOCTL(fd, cmd, arg);		\
73 		req->type = type;				\
74 	}							\
75 	__ret;							\
76 	})
77 
plugin_init(int fd)78 static void *plugin_init(int fd)
79 {
80 	struct v4l2_capability cap;
81 	int ret;
82 	struct mplane_plugin plugin, *ret_plugin;
83 
84 	memset(&plugin, 0, sizeof(plugin));
85 
86 	/* Check if device needs mplane plugin */
87 	memset(&cap, 0, sizeof(cap));
88 	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
89 	if (ret) {
90 		perror("Failed to query video capabilities");
91 		return NULL;
92 	}
93 
94 	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
95 	    (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE))
96 		plugin.mplane_capture = 1;
97 
98 	if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
99 	    (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE))
100 		plugin.mplane_output = 1;
101 
102 	/* Device doesn't need it. return NULL to disable the plugin */
103 	if (!plugin.mplane)
104 		return NULL;
105 
106 	/* Allocate and initialize private data */
107 	ret_plugin = calloc(1, sizeof(*ret_plugin));
108 	if (!ret_plugin) {
109 		perror("Couldn't allocate memory for plugin");
110 		return NULL;
111 	}
112 	*ret_plugin = plugin;
113 
114 	printf("Using mplane plugin for %s%s\n",
115 	       plugin.mplane_capture ? "capture " : "",
116 	       plugin.mplane_output ? "output " : "");
117 
118 	return ret_plugin;
119 }
120 
plugin_close(void * dev_ops_priv)121 static void plugin_close(void *dev_ops_priv) {
122 	if (dev_ops_priv == NULL)
123 		return;
124 
125 	free(dev_ops_priv);
126 }
127 
querycap_ioctl(int fd,unsigned long int cmd,struct v4l2_capability * arg)128 static int querycap_ioctl(int fd, unsigned long int cmd,
129 			  struct v4l2_capability *arg)
130 {
131 	struct v4l2_capability *cap = arg;
132 	int ret;
133 
134 	ret = SYS_IOCTL(fd, cmd, cap);
135 	if (ret)
136 		return ret;
137 
138 	/* Report mplane as normal caps */
139 	if (cap->capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
140 		cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
141 
142 	if (cap->capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
143 		cap->capabilities |= V4L2_CAP_VIDEO_OUTPUT;
144 
145 	if (cap->device_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
146 		cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
147 
148 	if (cap->device_caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
149 		cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT;
150 
151 	cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
152 	cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT;
153 
154 	/*
155 	 * Don't report mplane caps, as this will be handled via
156 	 * this plugin
157 	 */
158 	cap->capabilities &= ~(V4L2_CAP_VIDEO_OUTPUT_MPLANE |
159 			       V4L2_CAP_VIDEO_CAPTURE_MPLANE);
160 	cap->device_caps &= ~(V4L2_CAP_VIDEO_OUTPUT_MPLANE |
161 			      V4L2_CAP_VIDEO_CAPTURE_MPLANE);
162 
163 	return 0;
164 }
165 
convert_type(int type)166 static int convert_type(int type)
167 {
168 	switch (type) {
169 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
170 		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
171 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
172 		return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
173 	default:
174 		return type;
175 	}
176 }
177 
sanitize_format(struct v4l2_format * fmt)178 static void sanitize_format(struct v4l2_format *fmt)
179 {
180 	unsigned int offset;
181 
182 	/*
183 	 * The v4l2_pix_format structure has been extended with fields that were
184 	 * not previously required to be set to zero by applications. The priv
185 	 * field, when set to a magic value, indicates the the extended fields
186 	 * are valid. We support these extended fields since struct
187 	 * v4l2_pix_format_mplane supports those fields as well.
188 	 *
189 	 * So this function will sanitize v4l2_pix_format if priv != PRIV_MAGIC
190 	 * by setting priv to that value and zeroing the remaining fields.
191 	 */
192 
193 	if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC)
194 		return;
195 
196 	fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
197 
198 	offset = offsetof(struct v4l2_pix_format, priv)
199 	       + sizeof(fmt->fmt.pix.priv);
200 	memset(((char *)&fmt->fmt.pix) + offset, 0,
201 	       sizeof(fmt->fmt.pix) - offset);
202 }
203 
try_set_fmt_ioctl(int fd,unsigned long int cmd,struct v4l2_format * arg)204 static int try_set_fmt_ioctl(int fd, unsigned long int cmd,
205 			     struct v4l2_format *arg)
206 {
207 	struct v4l2_format fmt = { 0 };
208 	struct v4l2_format *org = arg;
209 	int ret;
210 
211 	switch (arg->type) {
212 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
213 		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
214 		break;
215 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
216 		fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
217 		break;
218 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
219 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
220 		errno = EINVAL;
221 		return -1;
222 	default:
223 		return SYS_IOCTL(fd, cmd, arg);
224 	}
225 
226 	sanitize_format(org);
227 
228 	fmt.fmt.pix_mp.width = org->fmt.pix.width;
229 	fmt.fmt.pix_mp.height = org->fmt.pix.height;
230 	fmt.fmt.pix_mp.pixelformat = org->fmt.pix.pixelformat;
231 	fmt.fmt.pix_mp.field = org->fmt.pix.field;
232 	fmt.fmt.pix_mp.colorspace = org->fmt.pix.colorspace;
233 	fmt.fmt.pix_mp.xfer_func = org->fmt.pix.xfer_func;
234 	fmt.fmt.pix_mp.ycbcr_enc = org->fmt.pix.ycbcr_enc;
235 	fmt.fmt.pix_mp.quantization = org->fmt.pix.quantization;
236 	fmt.fmt.pix_mp.num_planes = 1;
237 	fmt.fmt.pix_mp.flags = org->fmt.pix.flags;
238 	fmt.fmt.pix_mp.plane_fmt[0].bytesperline = org->fmt.pix.bytesperline;
239 	fmt.fmt.pix_mp.plane_fmt[0].sizeimage = org->fmt.pix.sizeimage;
240 
241 	ret = SYS_IOCTL(fd, cmd, &fmt);
242 	if (ret)
243 		return ret;
244 
245 	org->fmt.pix.width = fmt.fmt.pix_mp.width;
246 	org->fmt.pix.height = fmt.fmt.pix_mp.height;
247 	org->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
248 	org->fmt.pix.field = fmt.fmt.pix_mp.field;
249 	org->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
250 	org->fmt.pix.xfer_func = fmt.fmt.pix_mp.xfer_func;
251 	org->fmt.pix.ycbcr_enc = fmt.fmt.pix_mp.ycbcr_enc;
252 	org->fmt.pix.quantization = fmt.fmt.pix_mp.quantization;
253 	org->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
254 	org->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
255 	org->fmt.pix.flags = fmt.fmt.pix_mp.flags;
256 
257 	return 0;
258 }
259 
create_bufs_ioctl(int fd,unsigned long int cmd,struct v4l2_create_buffers * arg)260 static int create_bufs_ioctl(int fd, unsigned long int cmd,
261 			     struct v4l2_create_buffers *arg)
262 {
263 	struct v4l2_create_buffers cbufs = { 0 };
264 	struct v4l2_format *fmt = &cbufs.format;
265 	struct v4l2_format *org = &arg->format;
266 	int ret;
267 
268 	switch (arg->format.type) {
269 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
270 		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
271 		break;
272 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
273 		fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
274 		break;
275 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
276 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
277 		errno = EINVAL;
278 		return -1;
279 	default:
280 		return SYS_IOCTL(fd, cmd, arg);
281 	}
282 
283 	cbufs.index = arg->index;
284 	cbufs.count = arg->count;
285 	cbufs.memory = arg->memory;
286 	sanitize_format(org);
287 	fmt->fmt.pix_mp.width = org->fmt.pix.width;
288 	fmt->fmt.pix_mp.height = org->fmt.pix.height;
289 	fmt->fmt.pix_mp.pixelformat = org->fmt.pix.pixelformat;
290 	fmt->fmt.pix_mp.field = org->fmt.pix.field;
291 	fmt->fmt.pix_mp.colorspace = org->fmt.pix.colorspace;
292 	fmt->fmt.pix_mp.xfer_func = org->fmt.pix.xfer_func;
293 	fmt->fmt.pix_mp.ycbcr_enc = org->fmt.pix.ycbcr_enc;
294 	fmt->fmt.pix_mp.quantization = org->fmt.pix.quantization;
295 	fmt->fmt.pix_mp.num_planes = 1;
296 	fmt->fmt.pix_mp.flags = org->fmt.pix.flags;
297 	fmt->fmt.pix_mp.plane_fmt[0].bytesperline = org->fmt.pix.bytesperline;
298 	fmt->fmt.pix_mp.plane_fmt[0].sizeimage = org->fmt.pix.sizeimage;
299 
300 	ret = SYS_IOCTL(fd, cmd, &cbufs);
301 
302 	arg->index = cbufs.index;
303 	arg->count = cbufs.count;
304 	org->fmt.pix.width = fmt->fmt.pix_mp.width;
305 	org->fmt.pix.height = fmt->fmt.pix_mp.height;
306 	org->fmt.pix.pixelformat = fmt->fmt.pix_mp.pixelformat;
307 	org->fmt.pix.field = fmt->fmt.pix_mp.field;
308 	org->fmt.pix.colorspace = fmt->fmt.pix_mp.colorspace;
309 	org->fmt.pix.xfer_func = fmt->fmt.pix_mp.xfer_func;
310 	org->fmt.pix.ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc;
311 	org->fmt.pix.quantization = fmt->fmt.pix_mp.quantization;
312 	org->fmt.pix.bytesperline = fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
313 	org->fmt.pix.sizeimage = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
314 	org->fmt.pix.flags = fmt->fmt.pix_mp.flags;
315 
316 	return ret;
317 }
318 
get_fmt_ioctl(int fd,unsigned long int cmd,struct v4l2_format * arg)319 static int get_fmt_ioctl(int fd, unsigned long int cmd, struct v4l2_format *arg)
320 {
321 	struct v4l2_format fmt = { 0 };
322 	struct v4l2_format *org = arg;
323 	int ret;
324 
325 	switch (arg->type) {
326 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
327 		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
328 		break;
329 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
330 		fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
331 		break;
332 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
333 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
334 		errno = EINVAL;
335 		return -1;
336 	default:
337 		return SYS_IOCTL(fd, cmd, arg);
338 	}
339 
340 	ret = SYS_IOCTL(fd, cmd, &fmt);
341 	if (ret)
342 		return ret;
343 
344 	memset(&org->fmt.pix, 0, sizeof(org->fmt.pix));
345 	org->fmt.pix.width = fmt.fmt.pix_mp.width;
346 	org->fmt.pix.height = fmt.fmt.pix_mp.height;
347 	org->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
348 	org->fmt.pix.field = fmt.fmt.pix_mp.field;
349 	org->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
350 	org->fmt.pix.xfer_func = fmt.fmt.pix_mp.xfer_func;
351 	org->fmt.pix.ycbcr_enc = fmt.fmt.pix_mp.ycbcr_enc;
352 	org->fmt.pix.quantization = fmt.fmt.pix_mp.quantization;
353 	org->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
354 	org->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
355 	org->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
356 	org->fmt.pix.flags = fmt.fmt.pix_mp.flags;
357 
358 	/*
359 	 * If the device doesn't support just one plane, there's
360 	 * nothing we can do, except return an error condition.
361 	 */
362 	if (fmt.fmt.pix_mp.num_planes > 1) {
363 		errno = EINVAL;
364 		return -1;
365 	}
366 
367 	return ret;
368 }
369 
buf_ioctl(int fd,unsigned long int cmd,struct v4l2_buffer * arg)370 static int buf_ioctl(int fd, unsigned long int cmd, struct v4l2_buffer *arg)
371 {
372 	struct v4l2_buffer buf = *arg;
373 	struct v4l2_plane plane = { 0 };
374 	int ret;
375 
376 	if (arg->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
377 	    arg->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
378 		errno = EINVAL;
379 		return -1;
380 	}
381 
382 	buf.type = convert_type(arg->type);
383 
384 	if (buf.type == arg->type)
385 		return SYS_IOCTL(fd, cmd, &buf);
386 
387 	memcpy(&plane.m, &arg->m, sizeof(plane.m));
388 	plane.length = arg->length;
389 	plane.bytesused = arg->bytesused;
390 
391 	buf.m.planes = &plane;
392 	buf.length = 1;
393 
394 	ret = SYS_IOCTL(fd, cmd, &buf);
395 
396 	arg->index = buf.index;
397 	arg->memory = buf.memory;
398 	arg->flags = buf.flags;
399 	arg->field = buf.field;
400 	arg->timestamp = buf.timestamp;
401 	arg->timecode = buf.timecode;
402 	arg->sequence = buf.sequence;
403 
404 	arg->length = plane.length;
405 	arg->bytesused = plane.bytesused;
406 	memcpy(&arg->m, &plane.m, sizeof(arg->m));
407 
408 	return ret;
409 }
410 
plugin_ioctl(void * dev_ops_priv,int fd,unsigned long int cmd,void * arg)411 static int plugin_ioctl(void *dev_ops_priv, int fd,
412 			unsigned long int cmd, void *arg)
413 {
414 	switch (cmd) {
415 	case VIDIOC_QUERYCAP:
416 		return querycap_ioctl(fd, cmd, arg);
417 	case VIDIOC_TRY_FMT:
418 	case VIDIOC_S_FMT:
419 		return try_set_fmt_ioctl(fd, cmd, arg);
420 	case VIDIOC_G_FMT:
421 		return get_fmt_ioctl(fd, cmd, arg);
422 	case VIDIOC_ENUM_FMT:
423 		return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_fmtdesc);
424 	case VIDIOC_S_PARM:
425 	case VIDIOC_G_PARM:
426 		return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_streamparm);
427 	case VIDIOC_QBUF:
428 	case VIDIOC_DQBUF:
429 	case VIDIOC_QUERYBUF:
430 	case VIDIOC_PREPARE_BUF:
431 		return buf_ioctl(fd, cmd, arg);
432 	case VIDIOC_CREATE_BUFS:
433 		return create_bufs_ioctl(fd, cmd, arg);
434 	case VIDIOC_REQBUFS:
435 		return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_requestbuffers);
436 	case VIDIOC_STREAMON:
437 	case VIDIOC_STREAMOFF:
438 	{
439 		int type = *(int *)arg;
440 
441 		if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
442 		    type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
443 			errno = EINVAL;
444 			return -1;
445 		}
446 		type = convert_type(type);
447 
448 		return SYS_IOCTL(fd, cmd, &type);
449 	}
450 	/* CASE VIDIOC_EXPBUF: */
451 	default:
452 		return SYS_IOCTL(fd, cmd, arg);
453 	}
454 }
455 
plugin_read(void * dev_ops_priv,int fd,void * buf,size_t len)456 static ssize_t plugin_read(void *dev_ops_priv, int fd, void *buf, size_t len)
457 {
458 	return SYS_READ(fd, buf, len);
459 }
460 
plugin_write(void * dev_ops_priv,int fd,const void * buf,size_t len)461 static ssize_t plugin_write(void *dev_ops_priv, int fd, const void *buf,
462                          size_t len)
463 {
464 	return SYS_WRITE(fd, buf, len);
465 }
466 
467 PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = {
468 	.init = &plugin_init,
469 	.close = &plugin_close,
470 	.ioctl = &plugin_ioctl,
471 	.read = &plugin_read,
472 	.write = &plugin_write,
473 };
474