From: Dmitry V. Levin Date: Sun, 1 May 2016 17:25:24 +0000 (+0000) Subject: Fix decoding of VIDIOC_* ioctls X-Git-Tag: v4.12~230 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0a47ff79cae7476ed47bd74ec62eeee7224fa382;p=strace Fix decoding of VIDIOC_* ioctls * v4l2.c: Fix numerous bugs in decoding of VIDIOC_* ioctls. * tests/ioctl.c (main): Update. --- diff --git a/tests/ioctl.c b/tests/ioctl.c index 68ac8cad..f05ff5ce 100644 --- a/tests/ioctl.c +++ b/tests/ioctl.c @@ -69,7 +69,7 @@ main(void ) " = -1 EBADF (%m)\n", &data); (void) ioctl(-1, VIDIOC_ENUMINPUT, 0); - printf("ioctl(-1, VIDIOC_ENUMINPUT, 0)" + printf("ioctl(-1, VIDIOC_ENUMINPUT, NULL)" " = -1 EBADF (%m)\n"); (void) ioctl(-1, HIDIOCGVERSION, &data); diff --git a/v4l2.c b/v4l2.c index 5b2426b2..bbf94366 100644 --- a/v4l2.c +++ b/v4l2.c @@ -33,6 +33,7 @@ #include #include #include + /* some historical constants */ #ifndef V4L2_CID_HCENTER #define V4L2_CID_HCENTER (V4L2_CID_BASE+22) @@ -44,58 +45,42 @@ #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) #endif -#include "xlat/v4l2_device_capabilities_flags.h" -#include "xlat/v4l2_buf_types.h" -#include "xlat/v4l2_buf_flags.h" -#include "xlat/v4l2_framesize_types.h" -#include "xlat/v4l2_frameinterval_types.h" -#include "xlat/v4l2_fields.h" -#include "xlat/v4l2_colorspaces.h" -#include "xlat/v4l2_format_description_flags.h" -#include "xlat/v4l2_memories.h" -#include "xlat/v4l2_control_ids.h" -#include "xlat/v4l2_control_types.h" -#include "xlat/v4l2_control_flags.h" -#include "xlat/v4l2_control_classes.h" -#include "xlat/v4l2_streaming_capabilities.h" -#include "xlat/v4l2_capture_modes.h" -#include "xlat/v4l2_input_types.h" - #define FMT_FRACT "%u/%u" #define ARGS_FRACT(x) ((x).numerator), ((x).denominator) -#define FMT_RECT "{left=%i, top=%i, width=%i, height=%i}" +#define FMT_RECT "{left=%d, top=%d, width=%u, height=%u}" #define ARGS_RECT(x) (x).left, (x).top, (x).width, (x).height -static void print_pixelformat(uint32_t fourcc) +static void +print_pixelformat(uint32_t fourcc) { - union { + const union { uint32_t pixelformat; unsigned char cc[sizeof(uint32_t)]; - } u = { - .pixelformat = -#if WORDS_BIGENDIAN - htole32(fourcc) -#else - fourcc -#endif - }; + } u = { .pixelformat = htole32(fourcc) }; unsigned int i; tprints("v4l2_fourcc("); for (i = 0; i < sizeof(u.cc); ++i) { - unsigned int c = u.cc[i]; + unsigned char c = u.cc[i]; if (i) tprints(", "); - if (c == ' ' || - (c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z')) { + if (c == '\'' || c == '\\') { + char sym[] = { + '\'', + '\\', + c, + '\'', + '\0' + }; + tprints(sym); + } else if (c >= ' ' && c <= 0x7e) { char sym[] = { '\'', - u.cc[i], - '\'' + c, + '\'', + '\0' }; tprints(sym); } else { @@ -114,588 +99,841 @@ static void print_pixelformat(uint32_t fourcc) tprints(")"); } -static void print_v4l2_format_fmt(const struct v4l2_format *f) +#include "xlat/v4l2_device_capabilities_flags.h" + +static int +print_v4l2_capability(struct tcb *tcp, const long arg) +{ + struct v4l2_capability caps; + + if (entering(tcp)) + return 0; + tprints(", "); + if (umove_or_printaddr(tcp, arg, &caps)) + return 1; + tprints("{driver="); + print_quoted_string((const char *) caps.driver, + sizeof(caps.driver), QUOTE_0_TERMINATED); + tprints(", card="); + print_quoted_string((const char *) caps.card, + sizeof(caps.card), QUOTE_0_TERMINATED); + tprints(", bus_info="); + print_quoted_string((const char *) caps.bus_info, + sizeof(caps.bus_info), QUOTE_0_TERMINATED); + tprintf(", version=%u.%u.%u, capabilities=", + (caps.version >> 16) & 0xFF, + (caps.version >> 8) & 0xFF, + caps.version & 0xFF); + printflags(v4l2_device_capabilities_flags, caps.capabilities, + "V4L2_CAP_???"); +#ifdef V4L2_CAP_DEVICE_CAPS + tprints(", device_caps="); + printflags(v4l2_device_capabilities_flags, caps.device_caps, + "V4L2_CAP_???"); +#endif + tprints("}"); + return 1; +} + +#include "xlat/v4l2_buf_types.h" +#include "xlat/v4l2_format_description_flags.h" + +static int +print_v4l2_fmtdesc(struct tcb *tcp, const long arg) +{ + struct v4l2_fmtdesc f; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &f)) + return RVAL_DECODED | 1; + tprintf("{index=%u, type=", f.index); + printxval(v4l2_buf_types, f.type, "V4L2_BUF_TYPE_???"); + return 0; + } + + if (!syserror(tcp) && !umove(tcp, arg, &f)) { + tprints(", flags="); + printflags(v4l2_format_description_flags, f.flags, + "V4L2_FMT_FLAG_???"); + tprints(", description="); + print_quoted_string((const char *) f.description, + sizeof(f.description), + QUOTE_0_TERMINATED); + tprints(", pixelformat="); + print_pixelformat(f.pixelformat); + } + tprints("}"); + return 1; +} + +#include "xlat/v4l2_fields.h" +#include "xlat/v4l2_colorspaces.h" + +static void +print_v4l2_format_fmt(const char *prefix, const struct v4l2_format *f) { - tprints("fmt."); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: { - const struct v4l2_pix_format *pix = &f->fmt.pix; - - tprintf("pix={width=%u, height=%u, pixelformat=", - pix->width, pix->height); - print_pixelformat(pix->pixelformat); + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + tprints(prefix); + tprintf("fmt.pix={width=%u, height=%u, pixelformat=", + f->fmt.pix.width, f->fmt.pix.height); + print_pixelformat(f->fmt.pix.pixelformat); tprints(", field="); - printxval(v4l2_fields, pix->field, "V4L2_FIELD_???"); + printxval(v4l2_fields, f->fmt.pix.field, "V4L2_FIELD_???"); tprintf(", bytesperline=%u, sizeimage=%u, colorspace=", - pix->bytesperline, pix->sizeimage); - printxval(v4l2_colorspaces, pix->colorspace, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + printxval(v4l2_colorspaces, f->fmt.pix.colorspace, "V4L2_COLORSPACE_???"); tprints("}"); break; - } #if HAVE_DECL_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: { - const struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; unsigned int i, max; - tprintf("pix_mp={width=%u, height=%u, pixelformat=", - pix_mp->width, pix_mp->height); - print_pixelformat(pix_mp->pixelformat); + tprints(prefix); + tprintf("fmt.pix_mp={width=%u, height=%u, pixelformat=", + f->fmt.pix_mp.width, f->fmt.pix_mp.height); + print_pixelformat(f->fmt.pix_mp.pixelformat); tprints(", field="); - printxval(v4l2_fields, pix_mp->field, "V4L2_FIELD_???"); + printxval(v4l2_fields, f->fmt.pix_mp.field, "V4L2_FIELD_???"); tprints(", colorspace="); - printxval(v4l2_colorspaces, pix_mp->colorspace, + printxval(v4l2_colorspaces, f->fmt.pix_mp.colorspace, "V4L2_COLORSPACE_???"); - tprints("plane_fmt=["); - max = pix_mp->num_planes; + tprints(", plane_fmt=["); + max = f->fmt.pix_mp.num_planes; if (max > VIDEO_MAX_PLANES) max = VIDEO_MAX_PLANES; for (i = 0; i < max; i++) { if (i > 0) tprints(", "); tprintf("{sizeimage=%u, bytesperline=%u}", - pix_mp->plane_fmt[i].sizeimage, - pix_mp->plane_fmt[i].bytesperline); + f->fmt.pix_mp.plane_fmt[i].sizeimage, + f->fmt.pix_mp.plane_fmt[i].bytesperline); } - tprintf("], num_planes=%u}", (unsigned) pix_mp->num_planes); + tprintf("], num_planes=%u}", (unsigned) f->fmt.pix_mp.num_planes); break; } #endif /* TODO: Complete this switch statement */ +#if 0 case V4L2_BUF_TYPE_VIDEO_OVERLAY: #if HAVE_DECL_V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: #endif - tprints("win={???}"); + tprints(prefix); + tprints("fmt.win={???}"); break; case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: - tprints("vbi={???}"); + tprints(prefix); + tprints("fmt.vbi={???}"); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - tprints("sliced={???}"); + tprints(prefix); + tprints("fmt.sliced={???}"); break; - default: - tprints("???"); +#if HAVE_DECL_V4L2_BUF_TYPE_SDR_CAPTURE + case V4L2_BUF_TYPE_SDR_CAPTURE: + case V4L2_BUF_TYPE_SDR_OUTPUT: + tprints(prefix); + tprints("fmt.sdr={???}"); break; +#endif +#endif } } -int -v4l2_ioctl(struct tcb *tcp, const unsigned int code, long arg) +static int +print_v4l2_format(struct tcb *tcp, const long arg, const bool is_get) { - if (!verbose(tcp)) - return 0; + struct v4l2_format f; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &f)) + return RVAL_DECODED | 1; + tprints("{type="); + printxval(v4l2_buf_types, f.type, "V4L2_BUF_TYPE_???"); + if (is_get) + return 0; + print_v4l2_format_fmt(", ", &f); + } else { + if (!syserror(tcp) && !umove(tcp, arg, &f)) { + const char *delim = is_get ? ", " : " => "; + print_v4l2_format_fmt(delim, &f); + } + tprints("}"); + } + return 1; +} - switch (code) { - case VIDIOC_QUERYCAP: /* decode on exit */ { - struct v4l2_capability caps; +#include "xlat/v4l2_memories.h" - if (entering(tcp) || syserror(tcp) || umove(tcp, arg, &caps) < 0) - return 0; - tprints(", {driver="); - print_quoted_string((const char *) caps.driver, - sizeof(caps.driver), QUOTE_0_TERMINATED); - tprints(", card="); - print_quoted_string((const char *) caps.card, - sizeof(caps.card), QUOTE_0_TERMINATED); - tprints(", bus_info="); - print_quoted_string((const char *) caps.bus_info, - sizeof(caps.bus_info), QUOTE_0_TERMINATED); - tprintf(", version=%u.%u.%u, capabilities=", - (caps.version >> 16) & 0xFF, - (caps.version >> 8) & 0xFF, - caps.version & 0xFF); - printflags(v4l2_device_capabilities_flags, caps.capabilities, - "V4L2_CAP_???"); -#ifdef V4L2_CAP_DEVICE_CAPS - tprints(", device_caps="); - printflags(v4l2_device_capabilities_flags, caps.device_caps, - "V4L2_CAP_???"); -#endif +static int +print_v4l2_requestbuffers(struct tcb *tcp, const long arg) +{ + struct v4l2_requestbuffers reqbufs; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &reqbufs)) + return RVAL_DECODED | 1; + tprintf("{count=%u, type=", reqbufs.count); + printxval(v4l2_buf_types, reqbufs.type, "V4L2_BUF_TYPE_???"); + tprints(", memory="); + printxval(v4l2_memories, reqbufs.memory, "V4L2_MEMORY_???"); tprints("}"); - return 1; + return 0; + } else { + static char outstr[sizeof("{count=}") + sizeof(int) * 3]; + + if (syserror(tcp) || umove(tcp, arg, &reqbufs) < 0) + return 1; + sprintf(outstr, "{count=%u}", reqbufs.count); + tcp->auxstr = outstr; + return 1 + RVAL_STR; } +} -#ifdef VIDIOC_ENUM_FRAMESIZES - case VIDIOC_ENUM_FRAMESIZES: /* decode on exit */ { - struct v4l2_frmsizeenum s; +#include "xlat/v4l2_buf_flags.h" - if (entering(tcp) || umove(tcp, arg, &s) < 0) - return 0; - tprintf(", {index=%u, pixel_format=", s.index); - print_pixelformat(s.pixel_format); +static int +print_v4l2_buffer(struct tcb *tcp, const unsigned int code, const long arg) +{ + struct v4l2_buffer b; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &b)) + return RVAL_DECODED | 1; + tprints("{type="); + printxval(v4l2_buf_types, b.type, "V4L2_BUF_TYPE_???"); + if (code != VIDIOC_DQBUF) + tprintf(", index=%u", b.index); + } else { + if (!syserror(tcp) && umove(tcp, arg, &b) == 0) { + if (code == VIDIOC_DQBUF) + tprintf(", index=%u", b.index); + tprints(", memory="); + printxval(v4l2_memories, b.memory, "V4L2_MEMORY_???"); - if (!syserror(tcp)) { - tprints(", type="); - printxval(v4l2_framesize_types, s.type, "V4L2_FRMSIZE_TYPE_???"); - switch (s.type) { - case V4L2_FRMSIZE_TYPE_DISCRETE: - tprintf(", discrete={width=%u, height=%u}", - s.discrete.width, s.discrete.height); - break; - case V4L2_FRMSIZE_TYPE_STEPWISE: - tprintf(", stepwise={min_width=%u, max_width=%u, " - "step_width=%u, min_height=%u, max_height=%u, " - "step_height=%u}", - s.stepwise.min_width, s.stepwise.max_width, - s.stepwise.step_width, s.stepwise.min_height, - s.stepwise.max_height, s.stepwise.step_height); - break; + if (b.memory == V4L2_MEMORY_MMAP) { + tprintf(", m.offset=%#x", b.m.offset); + } else if (b.memory == V4L2_MEMORY_USERPTR) { + tprintf(", m.userptr=%#lx", + (unsigned long) b.m.userptr); } + + tprintf(", length=%u, bytesused=%u, flags=", + b.length, b.bytesused); + printflags(v4l2_buf_flags, b.flags, "V4L2_BUF_FLAG_???"); + if (code == VIDIOC_DQBUF) + tprintf(", timestamp = {%ju.%06ju}", + (uintmax_t)b.timestamp.tv_sec, + (uintmax_t)b.timestamp.tv_usec); + tprints(", ..."); } tprints("}"); - return 1; } -#endif /* VIDIOC_ENUM_FRAMESIZES */ + return 1; +} - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: { - struct v4l2_format f; +static int +print_v4l2_framebuffer(struct tcb *tcp, const long arg) +{ + struct v4l2_framebuffer b; - if (umove(tcp, arg, &f) < 0) - return 0; - if (entering(tcp)) { - tprints(", {type="); - printxval(v4l2_buf_types, f.type, "V4L2_BUF_TYPE_???"); - } - if ((entering(tcp) && code != VIDIOC_G_FMT) - || (exiting(tcp) && !syserror(tcp))) { - tprints(exiting(tcp) && code != VIDIOC_G_FMT ? " => " : ", "); - print_v4l2_format_fmt(&f); + tprints(", "); + if (!umove_or_printaddr(tcp, arg, &b)) { + tprintf("{capability=%#x, flags=%#x, base=%#lx}", + b.capability, b.flags, (unsigned long) b.base); + } + + return RVAL_DECODED | 1; +} + +static int +print_v4l2_buf_type(struct tcb *tcp, const long arg) +{ + int type; + + tprints(", "); + if (!umove_or_printaddr(tcp, arg, &type)) { + tprints("["); + printxval(v4l2_buf_types, type, "V4L2_BUF_TYPE_???"); + tprints("]"); + } + return RVAL_DECODED | 1; +} + +#include "xlat/v4l2_streaming_capabilities.h" +#include "xlat/v4l2_capture_modes.h" + +static int +print_v4l2_streamparm(struct tcb *tcp, const long arg, const bool is_get) +{ + struct v4l2_streamparm s; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &s)) + return RVAL_DECODED | 1; + tprints("{type="); + printxval(v4l2_buf_types, s.type, "V4L2_BUF_TYPE_???"); + switch (s.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (is_get) + return 0; + tprints(", "); + break; + default: + tprints("}"); + return RVAL_DECODED | 1; } - if (exiting(tcp)) + } else { + if (syserror(tcp) || umove(tcp, arg, &s) < 0) { tprints("}"); - return 1; + return 1; + } + tprints(is_get ? ", " : " => "); } - case VIDIOC_ENUM_FMT: { - struct v4l2_fmtdesc f; + if (s.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + tprints("parm.capture={capability="); + printflags(v4l2_streaming_capabilities, + s.parm.capture.capability, "V4L2_CAP_???"); - if (entering(tcp) || umove(tcp, arg, &f) < 0) - return 0; + tprints(", capturemode="); + printflags(v4l2_capture_modes, + s.parm.capture.capturemode, "V4L2_MODE_???"); - tprintf(", {index=%u", f.index); - if (!syserror(tcp)) { - tprints(", type="); - printxval(v4l2_buf_types, f.type, "V4L2_BUF_TYPE_???"); - tprints(", flags="); - printflags(v4l2_format_description_flags, f.flags, - "V4L2_FMT_FLAG_???"); - tprints(", description="); - print_quoted_string((const char *) f.description, - sizeof(f.description), + tprintf(", timeperframe=" FMT_FRACT, + ARGS_FRACT(s.parm.capture.timeperframe)); + + tprintf(", extendedmode=%u, readbuffers=%u}", + s.parm.capture.extendedmode, + s.parm.capture.readbuffers); + } else { + tprints("parm.output={capability="); + printflags(v4l2_streaming_capabilities, + s.parm.output.capability, "V4L2_CAP_???"); + + tprintf(", outputmode=%u", s.parm.output.outputmode); + + tprintf(", timeperframe=" FMT_FRACT, + ARGS_FRACT(s.parm.output.timeperframe)); + + tprintf(", extendedmode=%u, writebuffers=%u}", + s.parm.output.extendedmode, + s.parm.output.writebuffers); + } + if (exiting(tcp)) + tprints("}"); + return 1; +} + +static int +print_v4l2_standard(struct tcb *tcp, const long arg) +{ + struct v4l2_standard s; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &s)) + return RVAL_DECODED | 1; + tprintf("{index=%u", s.index); + } else { + if (!syserror(tcp) && !umove(tcp, arg, &s)) { + tprints(", name="); + print_quoted_string((const char *) s.name, + sizeof(s.name), QUOTE_0_TERMINATED); - tprints(", pixelformat="); - print_pixelformat(f.pixelformat); + tprintf(", frameperiod=" FMT_FRACT, + ARGS_FRACT(s.frameperiod)); + tprintf(", framelines=%d", s.framelines); } tprints("}"); - return 1; } + return 1; +} - case VIDIOC_G_PARM: - case VIDIOC_S_PARM: { - struct v4l2_streamparm s; +#include "xlat/v4l2_input_types.h" - if (entering(tcp) && code == VIDIOC_G_PARM) - return 1; - if (exiting(tcp) && syserror(tcp)) - return code == VIDIOC_S_PARM; - if (umove(tcp, arg, &s) < 0) - return 0; - if (entering(tcp)) { - tprints(", {type="); - printxval(v4l2_buf_types, s.type, "V4L2_BUF_TYPE_???"); +static int +print_v4l2_input(struct tcb *tcp, const long arg) +{ + struct v4l2_input i; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &i)) + return RVAL_DECODED | 1; + tprintf("{index=%u", i.index); + } else { + if (!syserror(tcp) && !umove(tcp, arg, &i)) { + tprints(", name="); + print_quoted_string((const char *) i.name, + sizeof(i.name), + QUOTE_0_TERMINATED); + tprints(", type="); + printxval(v4l2_input_types, i.type, + "V4L2_INPUT_TYPE_???"); } + tprints("}"); + } + return 1; +} - tprints(exiting(tcp) && code == VIDIOC_S_PARM ? " => {" : ", {"); - if (s.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - struct v4l2_captureparm *cap = &s.parm.capture; +#include "xlat/v4l2_control_ids.h" - tprints("capability="); - printflags(v4l2_streaming_capabilities, - cap->capability, "V4L2_CAP_???"); +static int +print_v4l2_control(struct tcb *tcp, const long arg, const bool is_get) +{ + struct v4l2_control c; - tprints(", capturemode="); - printflags(v4l2_capture_modes, - cap->capturemode, "V4L2_MODE_???"); + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &c)) + return RVAL_DECODED | 1; + tprints("{id="); + printxval(v4l2_control_ids, c.id, "V4L2_CID_???"); + if (!is_get) + tprintf(", value=%d", c.value); + return 0; + } - tprintf(", timeperframe=" FMT_FRACT, - ARGS_FRACT(cap->timeperframe)); + if (!syserror(tcp) && !umove(tcp, arg, &c)) { + tprints(is_get ? ", " : " => "); + tprintf("value=%d", c.value); + } - tprintf(", extendedmode=%u, readbuffers=%u", - cap->extendedmode, - cap->readbuffers); - } else - tprints("..."); - tprints("}"); - if (exiting(tcp)) + tprints("}"); + return 1; +} + +#include "xlat/v4l2_control_types.h" +#include "xlat/v4l2_control_flags.h" + +static int +print_v4l2_queryctrl(struct tcb *tcp, const long arg) +{ + struct v4l2_queryctrl c; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &c)) + return RVAL_DECODED | 1; + tprints("{id="); + } else { + if (syserror(tcp) || umove(tcp, arg, &c) < 0) { tprints("}"); - return 1; + return 1; + } + if (tcp->auxstr) + tprints(" => "); } - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl c; - - if (umove(tcp, arg, &c) < 0) - return 0; - /* 'id' field must be printed : - * on enter - * on exit if !syserror(tcp) && V4L2_CTRL_FLAG_NEXT_CTRL was set - */ - if (entering(tcp) - || (exiting(tcp) && tcp->auxstr && !syserror(tcp))) { - tprints(exiting(tcp) ? " => " : ", {id="); + if (entering(tcp) || tcp->auxstr) { #ifdef V4L2_CTRL_FLAG_NEXT_CTRL - tcp->auxstr = (c.id & V4L2_CTRL_FLAG_NEXT_CTRL) ? "" : NULL; - if (tcp->auxstr) { - tprints("V4L2_CTRL_FLAG_NEXT_CTRL|"); - c.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; - } -#endif - printxval(v4l2_control_ids, c.id, "V4L2_CID_???"); - } - if (exiting(tcp)) { - if (!syserror(tcp)) { - tprints(", type="); - printxval(v4l2_control_types, c.type, - "V4L2_CTRL_TYPE_???"); - tprints(", name="); - print_quoted_string((const char *) c.name, - sizeof(c.name), - QUOTE_0_TERMINATED); - tprintf(", minimum=%i, maximum=%i, step=%i, " - "default_value=%i, flags=", - c.minimum, c.maximum, - c.step, c.default_value); - printflags(v4l2_control_flags, c.flags, - "V4L2_CTRL_FLAG_???"); - } - tprints("}"); + tcp->auxstr = (c.id & V4L2_CTRL_FLAG_NEXT_CTRL) ? "" : NULL; + if (tcp->auxstr) { + tprints("V4L2_CTRL_FLAG_NEXT_CTRL|"); + c.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; } - return 1; +#endif + printxval(v4l2_control_ids, c.id, "V4L2_CID_???"); } - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: { - struct v4l2_control c; + if (exiting(tcp)) { + tprints(", type="); + printxval(v4l2_control_types, c.type, "V4L2_CTRL_TYPE_???"); + tprints(", name="); + print_quoted_string((const char *) c.name, + sizeof(c.name), + QUOTE_0_TERMINATED); + tprintf(", minimum=%d, maximum=%d, step=%d" + ", default_value=%d, flags=", + c.minimum, c.maximum, c.step, c.default_value); + printflags(v4l2_control_flags, c.flags, "V4L2_CTRL_FLAG_???"); + tprints("}"); + } + return 1; +} + +static int +print_v4l2_cropcap(struct tcb *tcp, const long arg) +{ + struct v4l2_cropcap c; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &c)) + return RVAL_DECODED | 1; + tprints("{type="); + printxval(v4l2_buf_types, c.type, "V4L2_BUF_TYPE_???"); + return 0; + } + if (!syserror(tcp) && !umove(tcp, arg, &c)) { + tprintf(", bounds=" FMT_RECT + ", defrect=" FMT_RECT + ", pixelaspect=" FMT_FRACT, + ARGS_RECT(c.bounds), + ARGS_RECT(c.defrect), + ARGS_FRACT(c.pixelaspect)); + } + tprints("}"); + return 1; +} + +static int +print_v4l2_crop(struct tcb *tcp, const long arg, const bool is_get) +{ + struct v4l2_crop c; - if (entering(tcp) || umove(tcp, arg, &c) < 0) + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &c)) + return RVAL_DECODED | 1; + tprints("{type="); + printxval(v4l2_buf_types, c.type, "V4L2_BUF_TYPE_???"); + if (is_get) return 0; - tprints(", {id="); - printxval(v4l2_control_ids, c.id, "V4L2_CID_???"); - if (!syserror(tcp) || code != VIDIOC_G_CTRL) - tprintf(", value=%i", c.value); - tprints("}"); - return 1; + tprintf(", c=" FMT_RECT, ARGS_RECT(c.c)); + } else { + if (!syserror(tcp) && !umove(tcp, arg, &c)) + tprintf(", c=" FMT_RECT, ARGS_RECT(c.c)); } + tprints("}"); + return RVAL_DECODED | 1; +} + #ifdef VIDIOC_S_EXT_CTRLS - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - case VIDIOC_G_EXT_CTRLS: { - struct v4l2_ext_controls c; - unsigned int n; - bool must_print_values; - - if (entering(tcp) && code == VIDIOC_G_EXT_CTRLS) - return 0; - if (exiting(tcp) && syserror(tcp) && code != VIDIOC_G_EXT_CTRLS) - return 0; - must_print_values = ((entering(tcp) && code != VIDIOC_G_EXT_CTRLS) - || (exiting(tcp) && !syserror(tcp))); - if (umove(tcp, arg, &c) < 0) - return 0; - tprints(code != VIDIOC_G_EXT_CTRLS && exiting(tcp) ? " => " : ", "); - tprints("{ctrl_class="); - printxval(v4l2_control_classes, c.ctrl_class, - "V4L2_CTRL_CLASS_???"); - tprintf(", count=%u", c.count); - if (exiting(tcp) && syserror(tcp)) - tprintf(", error_idx=%u", c.error_idx); - tprints(", controls=["); - for (n = 0; n < c.count; ++n) { - struct v4l2_ext_control ctrl; +static int +print_v4l2_ext_control_array(struct tcb *tcp, const unsigned long addr, + const unsigned count) +{ + struct v4l2_ext_control ctrl; + const unsigned long size = count * sizeof(ctrl); + const unsigned long end = addr + size; + int rc = 0; + + if (!addr || end <= addr || size / sizeof(ctrl) != count) { + printaddr(addr); + return -1; + } + const unsigned long abbrev_end = + (abbrev(tcp) && max_strlen < count) ? + addr + max_strlen * sizeof(ctrl) : end; + unsigned long cur; + for (cur = addr; cur < end; cur += sizeof(ctrl)) { + if (cur != addr) + tprints(", "); - if (n > 0) - tprints(", "); - if (umove(tcp, (long) (c.controls + n), &ctrl) < 0) - break; - if (abbrev(tcp) && n == 2) { - tprints("..."); - break; - } - tprints("{id="); - printxval(v4l2_control_ids, ctrl.id, "V4L2_CID_???"); + if (umove(tcp, cur, &ctrl)) { + printaddr(cur); + rc = -1; + break; + } + + if (cur == addr) + tprints("["); + + if (cur >= abbrev_end) { + tprints("..."); + cur = end; + break; + } + + tprints("{id="); + printxval(v4l2_control_ids, ctrl.id, "V4L2_CID_???"); # if HAVE_DECL_V4L2_CTRL_TYPE_STRING - tprintf(", size=%u", ctrl.size); - if (ctrl.size > 0) { - if (must_print_values) { - tprints(", string="); - printstr(tcp, (long) ctrl.string, ctrl.size); - } - } else + tprintf(", size=%u", ctrl.size); + if (ctrl.size > 0) { + tprints(", string="); + printstr(tcp, (long) ctrl.string, ctrl.size); + } else # endif - { - if (must_print_values) { - tprintf(", value=%i, value64=%lld", ctrl.value, - (long long) ctrl.value64); - } - } - tprints("}"); - } - tprints("]}"); - return 1; + tprintf(", value=%d, value64=%lld", ctrl.value, + (long long) ctrl.value64); + tprints("}"); } -#endif /* VIDIOC_S_EXT_CTRLS */ + if (cur != addr) + tprints("]"); + return rc; +} - case VIDIOC_ENUMSTD: { - struct v4l2_standard s; +#include "xlat/v4l2_control_classes.h" + +static int +print_v4l2_ext_controls(struct tcb *tcp, const long arg, const bool is_get) +{ + struct v4l2_ext_controls c; - if (umove(tcp, arg, &s) < 0) + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &c)) + return RVAL_DECODED | 1; + tprints("{ctrl_class="); + printxval(v4l2_control_classes, c.ctrl_class, + "V4L2_CTRL_CLASS_???"); + tprintf(", count=%u", c.count); + if (!c.count) { + tprints("}"); + return RVAL_DECODED | 1; + } + if (is_get) return 0; - if (entering(tcp)) - tprintf(", {index=%i", s.index); - else { - if (!syserror(tcp)) { - tprints(", name="); - print_quoted_string((const char *) s.name, - sizeof(s.name), - QUOTE_0_TERMINATED); - tprintf(", frameperiod=" FMT_FRACT, ARGS_FRACT(s.frameperiod)); - tprintf(", framelines=%i", s.framelines); - } + tprints(", "); + } else { + if (umove(tcp, arg, &c) < 0) { tprints("}"); + return 1; } - return 1; + tprints(is_get ? ", " : " => "); } - case VIDIOC_G_STD: - case VIDIOC_S_STD: { - v4l2_std_id s; - - if (code == VIDIOC_G_STD && exiting(tcp) && syserror(tcp)) - return 0; - if (umove(tcp, arg, &s) < 0) - return 0; - if ((code == VIDIOC_S_STD) == entering(tcp)) - tprintf(", std=%#llx", (unsigned long long) s); - return 1; - } + tprints("controls="); + int fail = print_v4l2_ext_control_array(tcp, + (unsigned long) c.controls, + c.count); - case VIDIOC_ENUMINPUT: { - struct v4l2_input i; + if (exiting(tcp) && syserror(tcp)) + tprintf(", error_idx=%u", c.error_idx); - if (entering(tcp) || umove(tcp, arg, &i) < 0) - return 0; - tprintf(", {index=%i", i.index); - if (!syserror(tcp)) { - tprints(", name="); - print_quoted_string((const char *) i.name, - sizeof(i.name), QUOTE_0_TERMINATED); - tprints(", type="); - printxval(v4l2_input_types, i.type, - "V4L2_INPUT_TYPE_???"); - } + if (exiting(tcp) || fail) { tprints("}"); - return 1; + return RVAL_DECODED | 1; } + return 1; +} +#endif /* VIDIOC_S_EXT_CTRLS */ - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: { - int index; +#ifdef VIDIOC_ENUM_FRAMESIZES +# include "xlat/v4l2_framesize_types.h" - if (entering(tcp) || syserror(tcp) || umove(tcp, arg, &index) < 0) - return 0; +static int +print_v4l2_frmsizeenum(struct tcb *tcp, const long arg) +{ + struct v4l2_frmsizeenum s; - tprintf(", index=%i", index); - return 1; + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &s)) + return RVAL_DECODED | 1; + tprintf("{index=%u, pixel_format=", s.index); + print_pixelformat(s.pixel_format); + return 0; } + if (!syserror(tcp) && !umove(tcp, arg, &s)) { + tprints(", type="); + printxval(v4l2_framesize_types, s.type, "V4L2_FRMSIZE_TYPE_???"); + switch (s.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + tprintf(", discrete={width=%u, height=%u}", + s.discrete.width, s.discrete.height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + tprintf(", stepwise={min_width=%u, max_width=%u, " + "step_width=%u, min_height=%u, max_height=%u, " + "step_height=%u}", + s.stepwise.min_width, s.stepwise.max_width, + s.stepwise.step_width, s.stepwise.min_height, + s.stepwise.max_height, s.stepwise.step_height); + break; + } + } + tprints("}"); + return 1; +} +#endif /* VIDIOC_ENUM_FRAMESIZES */ + #ifdef VIDIOC_ENUM_FRAMEINTERVALS - case VIDIOC_ENUM_FRAMEINTERVALS: { - struct v4l2_frmivalenum f; +# include "xlat/v4l2_frameinterval_types.h" - if (entering(tcp) || umove(tcp, arg, &f) < 0) - return 0; - tprintf(", {index=%i, pixel_format=", f.index); +static int +print_v4l2_frmivalenum(struct tcb *tcp, const long arg) +{ + struct v4l2_frmivalenum f; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &f)) + return RVAL_DECODED | 1; + tprintf("{index=%u, pixel_format=", f.index); print_pixelformat(f.pixel_format); tprintf(", width=%u, height=%u", f.width, f.height); - if (!syserror(tcp)) { - tprints(", type="); - printxval(v4l2_frameinterval_types, f.type, - "V4L2_FRMIVAL_TYPE_???"); - switch (f.type) { - case V4L2_FRMIVAL_TYPE_DISCRETE: - tprintf(", discrete=" FMT_FRACT, - ARGS_FRACT(f.discrete)); - break; - case V4L2_FRMIVAL_TYPE_STEPWISE: - case V4L2_FRMSIZE_TYPE_CONTINUOUS: - tprintf(", stepwise={min=" FMT_FRACT ", max=" - FMT_FRACT ", step=" FMT_FRACT "}", - ARGS_FRACT(f.stepwise.min), - ARGS_FRACT(f.stepwise.max), - ARGS_FRACT(f.stepwise.step)); - break; - } + return 0; + } + if (!syserror(tcp) && !umove(tcp, arg, &f)) { + tprints(", type="); + printxval(v4l2_frameinterval_types, f.type, + "V4L2_FRMIVAL_TYPE_???"); + switch (f.type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + tprintf(", discrete=" FMT_FRACT, + ARGS_FRACT(f.discrete)); + break; + case V4L2_FRMIVAL_TYPE_STEPWISE: + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + tprintf(", stepwise={min=" FMT_FRACT ", max=" + FMT_FRACT ", step=" FMT_FRACT "}", + ARGS_FRACT(f.stepwise.min), + ARGS_FRACT(f.stepwise.max), + ARGS_FRACT(f.stepwise.step)); + break; } - tprints("}"); - return 1; } + tprints("}"); + return 1; +} #endif /* VIDIOC_ENUM_FRAMEINTERVALS */ - case VIDIOC_CROPCAP: { - struct v4l2_cropcap c; +#ifdef VIDIOC_CREATE_BUFS +static int +print_v4l2_create_buffers(struct tcb *tcp, const long arg) +{ + struct v4l2_create_buffers b; + + if (entering(tcp)) { + tprints(", "); + if (umove_or_printaddr(tcp, arg, &b)) + return RVAL_DECODED | 1; + tprintf("{count=%u, memory=", b.count); + printxval(v4l2_memories, b.memory, "V4L2_MEMORY_???"); + tprints(", format={type="); + printxval(v4l2_buf_types, b.format.type, + "V4L2_BUF_TYPE_???"); + print_v4l2_format_fmt(", ", + (struct v4l2_format *) &b.format); + tprints("}}"); + return 0; + } else { + static const char fmt[] = "{index=%u, count=%u}"; + static char outstr[sizeof(fmt) + sizeof(int) * 6]; - if (entering(tcp) || umove(tcp, arg, &c) < 0) - return 0; - tprints(", {type="); - printxval(v4l2_buf_types, c.type, "V4L2_BUF_TYPE_???"); - if (!syserror(tcp)) - tprintf(", bounds=" FMT_RECT ", defrect=" FMT_RECT ", " - "pixelaspect=" FMT_FRACT, ARGS_RECT(c.bounds), - ARGS_RECT(c.defrect), ARGS_FRACT(c.pixelaspect)); - tprints("}"); - return 1; + if (syserror(tcp) || umove(tcp, arg, &b) < 0) + return 1; + sprintf(outstr, fmt, b.index, b.count); + tcp->auxstr = outstr; + return 1 + RVAL_STR; } +} +#endif /* VIDIOC_CREATE_BUFS */ - case VIDIOC_S_CROP: { - struct v4l2_crop c; +int +v4l2_ioctl(struct tcb *tcp, const unsigned int code, const long arg) +{ + if (!verbose(tcp)) + return RVAL_DECODED; - if (exiting(tcp) || umove(tcp, arg, &c) < 0) - return 0; - tprints(", {type="); - printxval(v4l2_buf_types, c.type, "V4L2_BUF_TYPE_???"); - tprintf(", c=" FMT_RECT "}", ARGS_RECT(c.c)); - return 1; - } + switch (code) { + case VIDIOC_QUERYCAP: /* R */ + return print_v4l2_capability(tcp, arg); - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: { - struct v4l2_framebuffer b; + case VIDIOC_ENUM_FMT: /* RW */ + return print_v4l2_fmtdesc(tcp, arg); - if (syserror(tcp) && code == VIDIOC_G_FBUF) - return 0; - if (entering(tcp) || umove(tcp, arg, &b) < 0) - return 0; - tprintf(", {capability=%x, flags=%x, base=%p}", - b.capability, b.flags, b.base); - return 1; - } + case VIDIOC_G_FMT: /* RW */ + case VIDIOC_S_FMT: /* RW */ + case VIDIOC_TRY_FMT: /* RW */ + return print_v4l2_format(tcp, arg, code == VIDIOC_G_FMT); -#ifdef VIDIOC_CREATE_BUFS - case VIDIOC_CREATE_BUFS: { - struct v4l2_create_buffers b; + case VIDIOC_REQBUFS: /* RW */ + return print_v4l2_requestbuffers(tcp, arg); - if (exiting(tcp) && syserror(tcp)) - return 1; - if (umove(tcp, arg, &b) < 0) + case VIDIOC_QUERYBUF: /* RW */ + case VIDIOC_QBUF: /* RW */ + case VIDIOC_DQBUF: /* RW */ + return print_v4l2_buffer(tcp, code, arg); + + case VIDIOC_G_FBUF: /* R */ + if (entering(tcp)) return 0; - if (entering(tcp)) { - tprintf(", {count=%u, memory=", b.count); - printxval(v4l2_memories, b.memory, "V4L2_MEMORY_???"); - tprints(", format={type="); - printxval(v4l2_buf_types, b.format.type, "V4L2_BUF_TYPE_???"); - tprints(", "); - print_v4l2_format_fmt(&b.format); - tprints("}}"); - return 1; - } else { - static const char fmt[] = "{index=%u, count=%u}"; - static char outstr[sizeof(fmt) + sizeof(int) * 6]; + /* fall through */ + case VIDIOC_S_FBUF: /* W */ + return print_v4l2_framebuffer(tcp, arg); - sprintf(outstr, fmt, b.index, b.count); - tcp->auxstr = outstr; - return 1 + RVAL_STR; - } - } -#endif /* VIDIOC_CREATE_BUFS */ + case VIDIOC_STREAMON: /* W */ + case VIDIOC_STREAMOFF: /* W */ + return print_v4l2_buf_type(tcp, arg); - case VIDIOC_REQBUFS: { - struct v4l2_requestbuffers reqbufs; + case VIDIOC_G_PARM: /* RW */ + case VIDIOC_S_PARM: /* RW */ + return print_v4l2_streamparm(tcp, arg, code == VIDIOC_G_PARM); - if (umove(tcp, arg, &reqbufs) < 0) + case VIDIOC_G_STD: /* R */ + if (entering(tcp)) return 0; - if (entering(tcp)) { - tprintf(", {count=%u, type=", reqbufs.count); - printxval(v4l2_buf_types, reqbufs.type, "V4L2_BUF_TYPE_???"); - tprints(", memory="); - printxval(v4l2_memories, reqbufs.memory, "V4L2_MEMORY_???"); - tprints("}"); - return 1; - } else if (syserror(tcp)) - return 1; - else { - static char outstr[sizeof("{count=}") + sizeof(int) * 3]; + /* fall through */ + case VIDIOC_S_STD: /* W */ + tprints(", "); + printnum_int64(tcp, arg, "%#" PRIx64); + return RVAL_DECODED | 1; - sprintf(outstr, "{count=%u}", reqbufs.count); - tcp->auxstr = outstr; - return 1 + RVAL_STR; - } - } + case VIDIOC_ENUMSTD: /* RW */ + return print_v4l2_standard(tcp, arg); - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: { - struct v4l2_buffer b; + case VIDIOC_ENUMINPUT: /* RW */ + return print_v4l2_input(tcp, arg); - if (umove(tcp, arg, &b) < 0) - return 0; - if (entering(tcp)) { - tprints(", {type="); - printxval(v4l2_buf_types, b.type, "V4L2_BUF_TYPE_???"); - if (code != VIDIOC_DQBUF) - tprintf(", index=%u", b.index); - } else { - if (!syserror(tcp)) { - if (code == VIDIOC_DQBUF) - tprintf(", index=%u", b.index); - tprints(", memory="); - printxval(v4l2_memories, b.memory, "V4L2_MEMORY_???"); - - if (b.memory == V4L2_MEMORY_MMAP) { - tprintf(", m.offset=%#x", b.m.offset); - } else if (b.memory == V4L2_MEMORY_USERPTR) { - tprintf(", m.userptr=%#lx", b.m.userptr); - } - - tprintf(", length=%u, bytesused=%u, flags=", - b.length, b.bytesused); - printflags(v4l2_buf_flags, b.flags, "V4L2_BUF_FLAG_???"); - if (code == VIDIOC_DQBUF) - tprintf(", timestamp = {%ju.%06ju}", - (uintmax_t)b.timestamp.tv_sec, - (uintmax_t)b.timestamp.tv_usec); - tprints(", ..."); - } - tprints("}"); - } - return 1; - } + case VIDIOC_G_CTRL: /* RW */ + case VIDIOC_S_CTRL: /* RW */ + return print_v4l2_control(tcp, arg, code == VIDIOC_G_CTRL); - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: { - int type; + case VIDIOC_QUERYCTRL: /* RW */ + return print_v4l2_queryctrl(tcp, arg); - if (umove(tcp, arg, &type) < 0) + case VIDIOC_G_INPUT: /* R */ + if (entering(tcp)) return 0; - if (entering(tcp)) { - tprints(", "); - printxval(v4l2_buf_types, type, "V4L2_BUF_TYPE_???"); - } - return 1; - } + /* fall through */ + case VIDIOC_S_INPUT: /* RW */ + tprints(", "); + printnum_int(tcp, arg, "%u"); + return RVAL_DECODED | 1; - default: /* decode on exit */ - return 0; + case VIDIOC_CROPCAP: /* RW */ + return print_v4l2_cropcap(tcp, arg); + + case VIDIOC_G_CROP: /* RW */ + case VIDIOC_S_CROP: /* W */ + return print_v4l2_crop(tcp, arg, code == VIDIOC_G_CROP); + +#ifdef VIDIOC_S_EXT_CTRLS + case VIDIOC_S_EXT_CTRLS: /* RW */ + case VIDIOC_TRY_EXT_CTRLS: /* RW */ + case VIDIOC_G_EXT_CTRLS: /* RW */ + return print_v4l2_ext_controls(tcp, arg, + code == VIDIOC_G_EXT_CTRLS); +#endif /* VIDIOC_S_EXT_CTRLS */ + +#ifdef VIDIOC_ENUM_FRAMESIZES + case VIDIOC_ENUM_FRAMESIZES: /* RW */ + return print_v4l2_frmsizeenum(tcp, arg); +#endif /* VIDIOC_ENUM_FRAMESIZES */ + +#ifdef VIDIOC_ENUM_FRAMEINTERVALS + case VIDIOC_ENUM_FRAMEINTERVALS: /* RW */ + return print_v4l2_frmivalenum(tcp, arg); +#endif /* VIDIOC_ENUM_FRAMEINTERVALS */ + +#ifdef VIDIOC_CREATE_BUFS + case VIDIOC_CREATE_BUFS: /* RW */ + return print_v4l2_create_buffers(tcp, arg); +#endif /* VIDIOC_CREATE_BUFS */ + + default: + return RVAL_DECODED; } }