Add support for opening multiple devices in v4l2_open(), and for
mapping controls between devices.

This is necessary for complex devices, such as Nokia N900.

Signed-off-by: Pavel Machek <pa...@ucw.cz>

diff --git a/lib/include/libv4l2.h b/lib/include/libv4l2.h
index ea1870d..a0ec0a9 100644
--- a/lib/include/libv4l2.h
+++ b/lib/include/libv4l2.h
@@ -58,6 +58,10 @@ LIBV4L_PUBLIC extern FILE *v4l2_log_file;
    invalid memory address will not lead to failure with errno being EFAULT,
    as it would with a real ioctl, but will cause libv4l2 to break, and you
    get to keep both pieces.
+
+   You can open complex pipelines by passing ".cv" file with pipeline
+   description to v4l2_open(). libv4l2 will open all the required
+   devices automatically in that case.
 */
 
 LIBV4L_PUBLIC int v4l2_open(const char *file, int oflag, ...);
diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
index 1924c91..1ee697a 100644
--- a/lib/libv4l2/libv4l2-priv.h
+++ b/lib/libv4l2/libv4l2-priv.h
@@ -104,6 +104,7 @@ struct v4l2_dev_info {
        void *plugin_library;
        void *dev_ops_priv;
        const struct libv4l_dev_ops *dev_ops;
+       struct v4l2_controls_map *map;
 };
 
 /* From v4l2-plugin.c */
@@ -130,4 +131,20 @@ static inline void v4l2_plugin_cleanup(void *plugin_lib, 
void *plugin_priv,
 extern const char *v4l2_ioctls[];
 void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
 
+
+struct v4l2_control_map {
+       unsigned long control;
+       int fd;
+};
+
+struct v4l2_controls_map {
+       int main_fd;
+       int num_fds;
+       int num_controls;
+       struct v4l2_control_map map[];
+};
+
+int v4l2_open_pipeline(struct v4l2_controls_map *map, int v4l2_flags);
+LIBV4L_PUBLIC int v4l2_get_fd_for_control(int fd, unsigned long control);
+
 #endif
diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
index 2db25d1..ac430f0 100644
--- a/lib/libv4l2/libv4l2.c
+++ b/lib/libv4l2/libv4l2.c
@@ -70,6 +70,8 @@
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <dirent.h>
+
 #include "libv4l2.h"
 #include "libv4l2-priv.h"
 #include "libv4l-plugin.h"
@@ -618,6 +620,8 @@ static void v4l2_update_fps(int index, struct 
v4l2_streamparm *parm)
                devices[index].fps = 0;
 }
 
+static int v4l2_open_complex(int fd, int v4l2_flags);
+
 int v4l2_open(const char *file, int oflag, ...)
 {
        int fd;
@@ -641,6 +645,21 @@ int v4l2_open(const char *file, int oflag, ...)
        if (fd == -1)
                return fd;
 
+       int len = strlen(file);
+       char *end = ".cv";
+       int len2 = strlen(end);
+       if ((len > len2) && (!strcmp(file + len - len2, end))) {
+               /* .cv extension */
+               struct stat sb;
+
+               if (fstat(fd, &sb) == 0) {
+                       if ((sb.st_mode & S_IFMT) == S_IFREG) {
+                               return v4l2_open_complex(fd, 0);
+                       }
+               }
+               
+       }
+
        if (v4l2_fd_open(fd, 0) == -1) {
                int saved_err = errno;
 
@@ -787,6 +806,8 @@ no_capture:
        if (index >= devices_used)
                devices_used = index + 1;
 
+       devices[index].map = NULL;
+
        /* Note we always tell v4lconvert to optimize src fmt selection for
           our default fps, the only exception is the app explicitly selecting
           a frame rate using the S_PARM ioctl after a S_FMT */
@@ -1056,12 +1077,47 @@ static int v4l2_s_fmt(int index, struct v4l2_format 
*dest_fmt)
        return 0;
 }
 
+int v4l2_get_fd_for_control(int fd, unsigned long control)
+{
+       int index = v4l2_get_index(fd);
+       struct v4l2_controls_map *map;
+       int lo = 0;
+       int hi;
+
+       if (index < 0)
+               return fd;
+
+       map = devices[index].map;
+       if (!map)
+               return fd;
+       hi = map->num_controls;
+
+       while (lo < hi) {
+               int i = (lo + hi) / 2;
+               if (map->map[i].control == control) {
+                       return map->map[i].fd;
+               }
+               if (map->map[i].control > control) {
+                       hi = i;
+                       continue;
+               }
+               if (map->map[i].control < control) {
+                       lo = i+1;
+                       continue;
+               }
+               printf("Bad: impossible condition in binary search\n");
+               exit(1);
+       }
+       return fd;
+}
+
 int v4l2_ioctl(int fd, unsigned long int request, ...)
 {
        void *arg;
        va_list ap;
        int result, index, saved_err;
-       int is_capture_request = 0, stream_needs_locking = 0;
+       int is_capture_request = 0, stream_needs_locking = 0, 
+           is_subdev_request = 0;
 
        va_start(ap, request);
        arg = va_arg(ap, void *);
@@ -1076,18 +1132,19 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
           ioctl, causing it to get sign extended, depending upon this behavior 
*/
        request = (unsigned int)request;
 
        if (devices[index].convert == NULL)
                goto no_capture_request;
 
        /* Is this a capture request and do we need to take the stream lock? */
        switch (request) {
-       case VIDIOC_QUERYCAP:
        case VIDIOC_QUERYCTRL:
        case VIDIOC_G_CTRL:
        case VIDIOC_S_CTRL:
        case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
        case VIDIOC_S_EXT_CTRLS:
+               is_subdev_request = 1;
+       case VIDIOC_QUERYCAP:
+       case VIDIOC_TRY_EXT_CTRLS:
        case VIDIOC_ENUM_FRAMESIZES:
        case VIDIOC_ENUM_FRAMEINTERVALS:
                is_capture_request = 1;
@@ -1151,10 +1209,15 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
        }
 
        if (!is_capture_request) {
+               int sub_fd;
 no_capture_request:
+               sub_fd = fd;
+               if (is_subdev_request) {
+                       sub_fd = v4l2_get_fd_for_control(index, ((struct 
v4l2_queryctrl *) arg)->id);
+               }
                result = devices[index].dev_ops->ioctl(
                                devices[index].dev_ops_priv,
-                               fd, request, arg);
+                               sub_fd, request, arg);
                saved_err = errno;
                v4l2_log_ioctl(request, arg, result);
                errno = saved_err;
@@ -1782,3 +1845,194 @@ int v4l2_get_control(int fd, int cid)
                        (qctrl.maximum - qctrl.minimum) / 2) /
                (qctrl.maximum - qctrl.minimum);
 }
+
+int v4l2_open_pipeline(struct v4l2_controls_map *map, int v4l2_flags)
+{
+       int index;
+       int i;
+
+       for (i=0; i<map->num_controls; i++) {
+               if (map->map[i].fd <= 0) {
+                       V4L2_LOG_ERR("v4l2_open_pipeline: Bad fd in map.\n");
+                       return -1;
+               }
+               if (i>=1 && map->map[i].control <= map->map[i-1].control) {
+                       V4L2_LOG_ERR("v4l2_open_pipeline: Controls not 
sorted.\n");
+                       return -1;
+               }
+       }
+
+       i = v4l2_fd_open(map->main_fd, v4l2_flags);
+       index = v4l2_get_index(map->main_fd);
+       devices[index].map = map;
+       return i;
+}
+
+static void scan_devices(char **device_names, int *device_fds, int num)
+{
+       struct dirent **namelist;
+       int n;
+       char *class_v4l = "/sys/class/video4linux";
+
+       n = scandir(class_v4l, &namelist, NULL, alphasort);
+       if (n < 0) {
+               perror("scandir");
+               return;
+       }
+       
+       while (n--) {
+               if (namelist[n]->d_name[0] != '.') {
+                       char filename[1024], content[1024];
+                       sprintf(filename, "%s/%s/name", class_v4l, 
namelist[n]->d_name);
+                       FILE *f = fopen(filename, "r");
+                       if (!f) {
+                               printf("Strange, can't open %s", filename);
+                       } else {
+                               fgets(content, 1024, f);
+                               fclose(f);
+
+                               int i;
+                               for (i = num-1; i >=0; i--) {
+                                       if (!strcmp(content, device_names[i])) {
+                                               sprintf(filename, "/dev/%s", 
namelist[n]->d_name);
+                                               device_fds[i] = open(filename, 
O_RDWR);
+                                               if (device_fds[i] < 0) {
+                                                       V4L2_LOG_ERR("Error 
opening %s: %m\n", filename);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               free(namelist[n]);
+       }
+       free(namelist);
+  
+}
+
+static int v4l2_open_complex(int fd, int v4l2_flags)
+{
+#define perr(s) V4L2_LOG_ERR("open_complex: " s "\n")
+#define BUF 256
+       FILE *f = fdopen(fd, "r");
+
+       int res = -1;
+       char buf[BUF];
+       int version, num_modes, num_devices, num_controls;
+       int dev, control;
+
+       if (!f) {
+               perr("open of .cv file failed: %m");
+               goto err;
+       }
+
+       if (fscanf(f, "Complex Video: %d\n", &version) != 1) {
+               perr(".cv file does not have required header");
+               goto close;
+       }
+
+       if (version != 0) {
+               perr(".cv file has unknown version");
+               goto close;
+       }
+  
+       if (fscanf(f, "#modes: %d\n", &num_modes) != 1) {
+               perr("could not parse modes");
+               goto close;
+       }
+
+       if (num_modes != 1) {
+               perr("only single mode is supported for now");
+               goto close;
+       }
+
+       if (fscanf(f, "Mode: %s\n", buf) != 1) {
+               perr("could not parse mode name");
+               goto close;
+       }
+
+       if (fscanf(f, " #devices: %d\n", &num_devices) != 1) {
+               perr("could not parse number of devices");
+               goto close;
+       }
+#define MAX_DEVICES 16
+       char *device_names[MAX_DEVICES] = { NULL, };
+       int device_fds[MAX_DEVICES];
+       if (num_devices > MAX_DEVICES) {
+               perr("too many devices");
+               goto close;
+       }
+  
+       for (dev = 0; dev < num_devices; dev++) {
+               int tmp;
+               if (fscanf(f, "%d: ", &tmp) != 1) {
+                       perr("could not parse device");
+                       goto free_devices;
+               }
+               if (tmp != dev) {
+                       perr("bad device number");
+                       goto free_devices;
+               }
+               fgets(buf, BUF, f);
+               device_names[dev] = strdup(buf);
+               device_fds[dev] = -1;
+       }
+
+       scan_devices(device_names, device_fds, num_devices);
+
+       for (dev = 0; dev < num_devices; dev++) {
+               if (device_fds[dev] == -1) {
+                       perr("Could not open all required devices");
+                       goto close_devices;
+               }
+       }
+
+       if (fscanf(f, " #controls: %d\n", &num_controls) != 1) {
+               perr("can not parse number of controls");
+               goto close_devices;
+       }
+
+       struct v4l2_controls_map *map = malloc(sizeof(struct v4l2_controls_map) 
+
+                                              num_controls*sizeof(struct 
v4l2_control_map));
+
+       map->num_controls = num_controls;
+       map->num_fds = num_devices;
+       map->main_fd = device_fds[0];
+  
+       for (control = 0; control < num_controls; control++) {
+               unsigned long num;
+               int dev;
+               if (fscanf(f, "0x%lx: %d\n", &num, &dev) != 2) {
+                       perr("could not parse control");
+                       goto free_map;
+               }
+               if ((dev < 0) || (dev >= num_devices)) {
+                       perr("device out of range");
+                       goto free_map;
+               }
+               map->map[control].control = num;
+               map->map[control].fd = device_fds[dev];
+       }
+       if (fscanf(f, "%s", buf) > 0) {
+               perr("junk at end of file");
+               goto free_map;
+       }
+
+       res = v4l2_open_pipeline(map, v4l2_flags);
+
+       if (res < 0) {
+free_map:
+               free(map);
+close_devices:
+               for (dev = 0; dev < num_devices; dev++)
+                       close(device_fds[dev]);
+       }
+free_devices:
+       for (dev = 0; dev < num_devices; dev++) {
+               free(device_names[dev]);
+       }
+close:
+       fclose(f);
+err:
+       return res;
+}
+
diff --git a/lib/libv4lconvert/control/libv4lcontrol.c 
b/lib/libv4lconvert/control/libv4lcontrol.c
index 59f28b1..c1e6f93 100644
--- a/lib/libv4lconvert/control/libv4lcontrol.c
+++ b/lib/libv4lconvert/control/libv4lcontrol.c
@@ -863,6 +863,7 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data 
*data, void *arg)
        struct v4l2_queryctrl *ctrl = arg;
        int retval;
        uint32_t orig_id = ctrl->id;
+       int fd;
 
        /* if we have an exact match return it */
        for (i = 0; i < V4LCONTROL_COUNT; i++)
@@ -872,8 +873,9 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data 
*data, void *arg)
                        return 0;
                }
 
+       fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
        /* find out what the kernel driver would respond. */
-       retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+       retval = data->dev_ops->ioctl(data->dev_ops_priv, fd,
                        VIDIOC_QUERYCTRL, arg);
 
        if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) &&
@@ -903,6 +905,7 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, 
void *arg)
 {
        int i;
        struct v4l2_control *ctrl = arg;
+       int fd;
 
        for (i = 0; i < V4LCONTROL_COUNT; i++)
                if ((data->controls & (1 << i)) &&
@@ -911,7 +914,8 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, 
void *arg)
                        return 0;
                }
 
-       return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+       fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
+       return data->dev_ops->ioctl(data->dev_ops_priv, fd,
                        VIDIOC_G_CTRL, arg);
 }
 
@@ -994,6 +998,7 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, 
void *arg)
 {
        int i;
        struct v4l2_control *ctrl = arg;
+       int fd;
 
        for (i = 0; i < V4LCONTROL_COUNT; i++)
                if ((data->controls & (1 << i)) &&
@@ -1008,7 +1013,8 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data 
*data, void *arg)
                        return 0;
                }
 
-       return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+       fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
+       return data->dev_ops->ioctl(data->dev_ops_priv, fd,
                        VIDIOC_S_CTRL, arg);
 }
 

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) 
http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

Attachment: signature.asc
Description: Digital signature

Reply via email to