v4l2-ctl: add support for array controls
authorHans Verkuil <hans.verkuil@cisco.com>
Tue, 10 Jun 2014 14:10:17 +0000 (16:10 +0200)
committerHans Verkuil <hans.verkuil@cisco.com>
Thu, 17 Jul 2014 17:38:17 +0000 (19:38 +0200)
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/v4l2-ctl/v4l2-ctl-common.cpp

index f302eaa..bc04bc9 100644 (file)
@@ -27,7 +27,7 @@
 
 typedef std::map<unsigned, std::vector<struct v4l2_ext_control> > class2ctrls_map;
 
-typedef std::map<std::string, struct v4l2_queryctrl> ctrl_qmap;
+typedef std::map<std::string, struct v4l2_query_ext_ctrl> ctrl_qmap;
 static ctrl_qmap ctrl_str2q;
 typedef std::map<unsigned, std::string> ctrl_idmap;
 static ctrl_idmap ctrl_id2str;
@@ -194,7 +194,7 @@ static void list_devices()
        }
 }
 
-static std::string name2var(unsigned char *name)
+static std::string name2var(char *name)
 {
        std::string s;
        int add_underscore = 0;
@@ -260,63 +260,105 @@ static std::string ctrlflags2s(__u32 flags)
                { V4L2_CTRL_FLAG_SLIDER,     "slider" },
                { V4L2_CTRL_FLAG_WRITE_ONLY, "write-only" },
                { V4L2_CTRL_FLAG_VOLATILE,   "volatile" },
+               { V4L2_CTRL_FLAG_HAS_PAYLOAD,"has-payload" },
                { 0, NULL }
        };
        return flags2s(flags, def);
 }
 
-static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl,
+static void print_qctrl(int fd, struct v4l2_query_ext_ctrl *queryctrl,
                struct v4l2_ext_control *ctrl, int show_menus)
 {
        struct v4l2_querymenu qmenu;
        std::string s = name2var(queryctrl->name);
-       int i;
+       unsigned i;
 
        memset(&qmenu, 0, sizeof(qmenu));
        qmenu.id = queryctrl->id;
        switch (queryctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER:
-               printf("%31s (int)    : min=%d max=%d step=%d default=%d value=%d",
+               printf("%31s (int)    : min=%lld max=%lld step=%lld default=%lld",
                                s.c_str(),
                                queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->step, queryctrl->default_value,
-                               ctrl->value);
+                               queryctrl->step, queryctrl->default_value);
                break;
        case V4L2_CTRL_TYPE_INTEGER64:
-               printf("%31s (int64)  : value=%lld", s.c_str(), ctrl->value64);
+               printf("%31s (int64)  : min=%lld max=%lld step=%lld default=%lld",
+                               s.c_str(),
+                               queryctrl->minimum, queryctrl->maximum,
+                               queryctrl->step, queryctrl->default_value);
                break;
        case V4L2_CTRL_TYPE_STRING:
-               printf("%31s (str)    : min=%d max=%d step=%d value='%s'",
+               printf("%31s (str)    : min=%lld max=%lld step=%lld",
                                s.c_str(),
                                queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->step, safename(ctrl->string).c_str());
+                               queryctrl->step);
                break;
        case V4L2_CTRL_TYPE_BOOLEAN:
-               printf("%31s (bool)   : default=%d value=%d",
-                               s.c_str(),
-                               queryctrl->default_value, ctrl->value);
+               printf("%31s (bool)   : default=%lld",
+                               s.c_str(), queryctrl->default_value);
                break;
        case V4L2_CTRL_TYPE_MENU:
-               printf("%31s (menu)   : min=%d max=%d default=%d value=%d",
+               printf("%31s (menu)   : min=%lld max=%lld default=%lld",
                                s.c_str(),
                                queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->default_value, ctrl->value);
+                               queryctrl->default_value);
                break;
        case V4L2_CTRL_TYPE_INTEGER_MENU:
-               printf("%31s (intmenu): min=%d max=%d default=%d value=%d",
+               printf("%31s (intmenu): min=%lld max=%lld default=%lld",
                                s.c_str(),
                                queryctrl->minimum, queryctrl->maximum,
-                               queryctrl->default_value, ctrl->value);
+                               queryctrl->default_value);
                break;
        case V4L2_CTRL_TYPE_BUTTON:
                printf("%31s (button) :", s.c_str());
                break;
        case V4L2_CTRL_TYPE_BITMASK:
-               printf("%31s (bitmask): max=0x%08x default=0x%08x value=0x%08x",
+               printf("%31s (bitmask): max=0x%08llx default=0x%08llx",
                                s.c_str(), queryctrl->maximum,
-                               queryctrl->default_value, ctrl->value);
+                               queryctrl->default_value);
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               printf("%31s (u8)     : min=%lld max=%lld step=%lld default=%lld",
+                               s.c_str(),
+                               queryctrl->minimum, queryctrl->maximum,
+                               queryctrl->step, queryctrl->default_value);
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               printf("%31s (u16)    : min=%lld max=%lld step=%lld default=%lld",
+                               s.c_str(),
+                               queryctrl->minimum, queryctrl->maximum,
+                               queryctrl->step, queryctrl->default_value);
                break;
-       default: break;
+       default:
+               printf("%31s (unknown): type=%x", s.c_str(), queryctrl->type);
+               break;
+       }
+       if (queryctrl->nr_of_dims == 0) {
+               switch (queryctrl->type) {
+               case V4L2_CTRL_TYPE_INTEGER:
+               case V4L2_CTRL_TYPE_BOOLEAN:
+               case V4L2_CTRL_TYPE_MENU:
+               case V4L2_CTRL_TYPE_INTEGER_MENU:
+                       printf(" value=%d", ctrl->value);
+                       break;
+               case V4L2_CTRL_TYPE_BITMASK:
+                       printf(" value=0x%08x", ctrl->value);
+                       break;
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       printf(" value=%lld", ctrl->value64);
+                       break;
+               case V4L2_CTRL_TYPE_STRING:
+                       printf(" value='%s'", safename(ctrl->string).c_str());
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (queryctrl->nr_of_dims) {
+               printf(" ");
+               for (i = 0; i < queryctrl->nr_of_dims; i++)
+                       printf("[%u]", queryctrl->dims[i]);
        }
        if (queryctrl->flags)
                printf(" flags=%s", ctrlflags2s(queryctrl->flags).c_str());
@@ -335,7 +377,7 @@ static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl,
        }
 }
 
-static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus)
+static int print_control(int fd, struct v4l2_query_ext_ctrl &qctrl, int show_menus)
 {
        struct v4l2_control ctrl;
        struct v4l2_ext_control ext_ctrl;
@@ -356,6 +398,10 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus)
                print_qctrl(fd, &qctrl, &ext_ctrl, show_menus);
                return 1;
        }
+       if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES) {
+               print_qctrl(fd, &qctrl, NULL, show_menus);
+               return 1;
+       }
        ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id);
        ctrls.count = 1;
        ctrls.controls = &ext_ctrl;
@@ -389,57 +435,97 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus)
        return 1;
 }
 
+static int query_ext_ctrl_ioctl(int fd, struct v4l2_query_ext_ctrl &qctrl)
+{
+       struct v4l2_queryctrl qc;
+       int rc;
+
+       rc = test_ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &qctrl);
+       if (errno != ENOTTY)
+               return rc;
+       qc.id = qctrl.id;
+       rc = test_ioctl(fd, VIDIOC_QUERYCTRL, &qc);
+       if (rc == 0) {
+               qctrl.type = qc.type;
+               memcpy(qctrl.name, qc.name, sizeof(qctrl.name));
+               qctrl.minimum = qc.minimum;
+               qctrl.maximum = qc.maximum;
+               qctrl.step = qc.step;
+               qctrl.default_value = qc.default_value;
+               qctrl.flags = qc.flags;
+               qctrl.elems = 1;
+               qctrl.nr_of_dims = 0;
+               memset(qctrl.dims, 0, sizeof(qctrl.dims));
+               switch (qctrl.type) {
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       qctrl.elem_size = sizeof(__s64);
+                       break;
+               case V4L2_CTRL_TYPE_STRING:
+                       qctrl.elem_size = qc.maximum + 1;
+                       break;
+               default:
+                       qctrl.elem_size = sizeof(__s32);
+                       break;
+               }
+               memset(qctrl.reserved, 0, sizeof(qctrl.reserved));
+       }
+       qctrl.id = qc.id;
+       return rc;
+}
+
 static void list_controls(int fd, int show_menus)
 {
-       struct v4l2_queryctrl qctrl;
+       const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
+       struct v4l2_query_ext_ctrl qctrl;
        int id;
 
        memset(&qctrl, 0, sizeof(qctrl));
-       qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
-       while (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
-                       print_control(fd, qctrl, show_menus);
-               qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+       qctrl.id = next_fl;
+       while (query_ext_ctrl_ioctl(fd, qctrl) == 0) {
+               print_control(fd, qctrl, show_menus);
+               qctrl.id |= next_fl;
        }
-       if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
+       if (!(qctrl.id & next_fl))
                return;
        for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
                qctrl.id = id;
-               if (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0)
+               if (query_ext_ctrl_ioctl(fd, qctrl) == 0)
                        print_control(fd, qctrl, show_menus);
        }
        for (qctrl.id = V4L2_CID_PRIVATE_BASE;
-                       test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) {
+                       query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) {
                print_control(fd, qctrl, show_menus);
        }
 }
 
 static void find_controls(int fd)
 {
-       struct v4l2_queryctrl qctrl;
+       const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
+       struct v4l2_query_ext_ctrl qctrl;
        int id;
 
        memset(&qctrl, 0, sizeof(qctrl));
-       qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
-       while (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
+       qctrl.id = next_fl;
+       while (query_ext_ctrl_ioctl(fd, qctrl) == 0) {
                if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS &&
                    !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
                        ctrl_str2q[name2var(qctrl.name)] = qctrl;
                        ctrl_id2str[qctrl.id] = name2var(qctrl.name);
                }
-               qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+               qctrl.id |= next_fl;
        }
-       if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL)
+       if (!(qctrl.id & next_fl))
                return;
        for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
                qctrl.id = id;
-               if (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 &&
+               if (query_ext_ctrl_ioctl(fd, qctrl) == 0 &&
                    !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
                        ctrl_str2q[name2var(qctrl.name)] = qctrl;
                        ctrl_id2str[qctrl.id] = name2var(qctrl.name);
                }
        }
        for (qctrl.id = V4L2_CID_PRIVATE_BASE;
-                       test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) {
+                       query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) {
                if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
                        ctrl_str2q[name2var(qctrl.name)] = qctrl;
                        ctrl_id2str[qctrl.id] = name2var(qctrl.name);
@@ -568,24 +654,39 @@ void common_set(int fd)
                for (ctrl_set_map::iterator iter = set_ctrls.begin();
                                iter != set_ctrls.end(); ++iter) {
                        struct v4l2_ext_control ctrl;
+                       struct v4l2_query_ext_ctrl &qc = ctrl_str2q[iter->first];
 
                        memset(&ctrl, 0, sizeof(ctrl));
-                       ctrl.id = ctrl_str2q[iter->first].id;
-                       if (ctrl_str2q[iter->first].type == V4L2_CTRL_TYPE_INTEGER64)
+                       ctrl.id = qc.id;
+                       if (qc.type == V4L2_CTRL_TYPE_INTEGER64)
                                use_ext_ctrls = true;
-                       if (ctrl_str2q[iter->first].type == V4L2_CTRL_TYPE_STRING) {
-                               unsigned len = iter->second.length();
-                               unsigned maxlen = ctrl_str2q[iter->first].maximum;
+                       if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
+                               long long v;
+                               unsigned i;
 
                                use_ext_ctrls = true;
-                               ctrl.size = maxlen + 1;
-                               ctrl.string = (char *)malloc(ctrl.size);
-                               if (len > maxlen) {
-                                       memcpy(ctrl.string, iter->second.c_str(), maxlen);
-                                       ctrl.string[maxlen] = 0;
-                               }
-                               else {
-                                       strcpy(ctrl.string, iter->second.c_str());
+                               ctrl.size = qc.elems * qc.elem_size;
+                               ctrl.ptr = malloc(ctrl.size);
+
+                               switch (qc.type) {
+                               case V4L2_CTRL_TYPE_U8:
+                                       v = strtoul(iter->second.c_str(), NULL, 0);
+                                       for (i = 0; i < qc.elems; i++)
+                                               ctrl.p_u8[i] = v;
+                                       break;
+                               case V4L2_CTRL_TYPE_U16:
+                                       v = strtoul(iter->second.c_str(), NULL, 0);
+                                       for (i = 0; i < qc.elems; i++)
+                                               ctrl.p_u16[i] = v;
+                                       break;
+                               case V4L2_CTRL_TYPE_STRING:
+                                       strncpy(ctrl.string, iter->second.c_str(), qc.maximum);
+                                       ctrl.string[qc.maximum] = 0;
+                                       break;
+                               default:
+                                       fprintf(stderr, "%s: unsupported payload type\n",
+                                                       qc.name);
+                                       break;
                                }
                        } else {
                                if (V4L2_CTRL_DRIVER_PRIV(ctrl.id))
@@ -618,7 +719,7 @@ void common_set(int fd)
                                ctrls.controls = &iter->second[0];
                                if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) {
                                        if (ctrls.error_idx >= ctrls.count) {
-                                               fprintf(stderr, "Error setting MPEG controls: %s\n",
+                                               fprintf(stderr, "Error setting controls: %s\n",
                                                                strerror(errno));
                                        }
                                        else {
@@ -632,6 +733,42 @@ void common_set(int fd)
        }
 }
 
+static void print_array(const struct v4l2_query_ext_ctrl &qc, void *p)
+{
+       unsigned divide[V4L2_CTRL_MAX_DIMS] = { 0 };
+       unsigned d, i;
+
+       for (d = 0; d < qc.nr_of_dims; d++) {
+               divide[d] = qc.dims[d];
+               for (i = 0; i < d; i++)
+                       divide[i] *= divide[d];
+       }
+       for (unsigned idx = 0; idx < qc.elems / qc.dims[qc.nr_of_dims - 1]; idx++) {
+               printf("%s", qc.name);
+               for (i = 0; i < qc.nr_of_dims - 1; i++)
+                       printf("[%u]", (idx / divide[i]) % qc.dims[i]);
+               printf(": ");
+               switch (qc.type) {
+               case V4L2_CTRL_TYPE_U8:
+                       for (i = 0; i < qc.dims[qc.nr_of_dims - 1]; i++) {
+                               printf("%4d", ((__u8 *)p)[idx + i]);
+                               if (i < qc.dims[qc.nr_of_dims - 1] - 1)
+                                       printf(", ");
+                       }
+                       printf("\n");
+                       break;
+               case V4L2_CTRL_TYPE_U16:
+                       for (i = 0; i < qc.dims[qc.nr_of_dims - 1]; i++) {
+                               printf("%6d", ((__u16 *)p)[idx + i]);
+                               if (i < qc.dims[qc.nr_of_dims - 1] - 1)
+                                       printf(", ");
+                       }
+                       printf("\n");
+                       break;
+               }
+       }
+}
+
 void common_get(int fd)
 {
        if (options[OptGetCtrl] && !get_ctrls.empty()) {
@@ -643,16 +780,16 @@ void common_get(int fd)
                for (ctrl_get_list::iterator iter = get_ctrls.begin();
                                iter != get_ctrls.end(); ++iter) {
                        struct v4l2_ext_control ctrl;
+                       struct v4l2_query_ext_ctrl &qc = ctrl_str2q[*iter];
 
                        memset(&ctrl, 0, sizeof(ctrl));
-                       ctrl.id = ctrl_str2q[*iter].id;
-                       if (ctrl_str2q[*iter].type == V4L2_CTRL_TYPE_INTEGER64)
+                       ctrl.id = qc.id;
+                       if (qc.type == V4L2_CTRL_TYPE_INTEGER64)
                                use_ext_ctrls = true;
-                       if (ctrl_str2q[*iter].type == V4L2_CTRL_TYPE_STRING) {
+                       if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
                                use_ext_ctrls = true;
-                               ctrl.size = ctrl_str2q[*iter].maximum + 1;
-                               ctrl.string = (char *)malloc(ctrl.size);
-                               ctrl.string[0] = 0;
+                               ctrl.size = qc.elems * qc.elem_size;
+                               ctrl.ptr = calloc(1, ctrl.size);
                        }
                        if (V4L2_CTRL_DRIVER_PRIV(ctrl.id))
                                use_ext_ctrls = true;
@@ -679,12 +816,26 @@ void common_get(int fd)
                                doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
                                for (unsigned i = 0; i < iter->second.size(); i++) {
                                        struct v4l2_ext_control ctrl = iter->second[i];
-
-                                       if (ctrl_str2q[ctrl_id2str[ctrl.id]].type == V4L2_CTRL_TYPE_STRING)
-                                               printf("%s: '%s'\n", ctrl_id2str[ctrl.id].c_str(),
-                                                              safename(ctrl.string).c_str());
-                                       else
-                                               printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value);
+                                       std::string &name = ctrl_id2str[ctrl.id];
+                                       struct v4l2_query_ext_ctrl &qc = ctrl_str2q[name];
+
+                                       if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
+                                               switch (qc.type) {
+                                               case V4L2_CTRL_TYPE_U8:
+                                               case V4L2_CTRL_TYPE_U16:
+                                                       print_array(qc, ctrl.ptr);
+                                                       break;
+                                               case V4L2_CTRL_TYPE_STRING:
+                                                       printf("%s: '%s'\n", name.c_str(),
+                                                               safename(ctrl.string).c_str());
+                                                       break;
+                                               default:
+                                                       fprintf(stderr, "%s: unsupported payload type\n",
+                                                                       qc.name);
+                                                       break;
+                                               }
+                                       } else
+                                               printf("%s: %d\n", name.c_str(), ctrl.value);
                                }
                        }
                }