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