diff -NaurEw ./libv4l-0.5.0/include/libv4lconvert.h ./libv4l-0.5.0_control/include/libv4lconvert.h
--- ./libv4l-0.5.0/include/libv4lconvert.h	2008-09-14 22:43:23.000000000 +0200
+++ ./libv4l-0.5.0_control/include/libv4lconvert.h	2008-09-27 21:47:03.000000000 +0200
@@ -66,7 +66,8 @@
 LIBV4L_PUBLIC int v4lconvert_convert(struct v4lconvert_data *data,
   const struct v4l2_format *src_fmt,  /* in */
   const struct v4l2_format *dest_fmt, /* in */
-  unsigned char *src, int src_size, unsigned char *dest, int dest_size);
+  unsigned char *src, int src_size, unsigned char *dest, int dest_size,
+  int rotate, int white_balance);
 
 /* get a string describing the last error*/
 LIBV4L_PUBLIC const char *v4lconvert_get_error_message(struct v4lconvert_data *data);
diff -NaurEw ./libv4l-0.5.0/include/libv4l2.h ./libv4l-0.5.0_control/include/libv4l2.h
--- ./libv4l-0.5.0/include/libv4l2.h	2008-08-26 14:32:39.000000000 +0200
+++ ./libv4l-0.5.0_control/include/libv4l2.h	2008-10-04 02:34:50.000000000 +0200
@@ -101,7 +101,8 @@
 
    Returns fd on success, -1 if the fd is not suitable for use through libv4l2
    (note the fd is left open in this case). */
-LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags);
+LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags, const char *device);
+
 
 #ifdef __cplusplus
 }
diff -NaurEw ./libv4l-0.5.0/include/v4lcontrol.h ./libv4l-0.5.0_control/include/v4lcontrol.h
--- ./libv4l-0.5.0/include/v4lcontrol.h	1970-01-01 01:00:00.000000000 +0100
+++ ./libv4l-0.5.0_control/include/v4lcontrol.h	2008-10-04 01:54:01.000000000 +0200
@@ -0,0 +1,51 @@
+/*
+#             (C) 2008 Lukas karas <lukas.karas@centrum.cz>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ for more informations about this file see control.c
+*/
+
+/*
+ for get shm memory should be use function
+ ftok( device, shm_id )
+ for example
+ ftok( "/dev/video0", SHM_DATA_ID )
+*/
+#define SHM_SEMAPTHORES_ID		0
+#define SHM_DATA_ID				1
+
+/* 
+ IDs for ipc semaphors
+*/
+#define SEMAPHORE_CLIENT_SEM_ID		0
+#define SEMAPHORE_LIB_DATA_SEM_ID		1
+#define SEMAPHORE_CLIENT_DATA_SEM_ID	2
+ 
+#define SEMAPHORES_COUNT	3
+
+/*
+ structure of shared memory
+*/
+struct v4lcontrol_shared{
+    __u64 lib_pid;
+    __u64 ioctl_request;
+    __u64 result;
+    __u8 arg[256];  
+};
+
+
diff -NaurEw ./libv4l-0.5.0/libv4lconvert/bayer.c ./libv4l-0.5.0_control/libv4lconvert/bayer.c
--- ./libv4l-0.5.0/libv4lconvert/bayer.c	2008-08-06 10:40:53.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4lconvert/bayer.c	2008-10-04 01:34:28.000000000 +0200
@@ -611,3 +611,165 @@
   v4lconvert_border_bayer_line_to_y(bayer + width, bayer, ydst, width,
     !start_with_green, !blue_line);
 }
+
+void v4lconvert_bayer_white_balance(const unsigned char *bayer,
+									int width, int height, unsigned int pixfmt)
+{
+	unsigned long r=0,g=0,b=0,tmp;
+	unsigned long exr,exg, exb;
+	unsigned char *buff;
+	int x, y;
+    
+	tmp = width*height - 0xffff;
+	if (tmp < 0)
+		return;
+    
+	buff = (unsigned char *)bayer;
+	tmp = 0;
+    
+	/* at first, we calculate average value for each color (red, green, blue) */
+	switch (pixfmt) {
+		case V4L2_PIX_FMT_SBGGR8:
+			for (y=0; (y < height) && (tmp < 0xffff); y+=2){
+				for (x=0; (x < width) && (tmp < 0xffff); x+=2){
+					b += buff[x];
+					g += buff[x+1] >> 1;
+					g += buff[x+width] >> 1;
+					r += buff[x+width+1];
+
+					tmp ++;
+				}
+				buff += (width <<1);
+			}
+			break;
+		case V4L2_PIX_FMT_SRGGB8:
+			for (y=0; (y < height) && (tmp < 0xffff); y+=2){
+				for (x=0; (x < width) && (tmp < 0xffff); x+=2){
+					r += buff[x];
+					g += buff[x+1] >> 1;
+					g += buff[x+width] >> 1;
+					b += buff[x+width+1];
+
+					tmp ++;
+				}
+				buff += (width <<1);
+			}
+			break;
+		case V4L2_PIX_FMT_SGBRG8:
+			for (y=0; (y < height) && (tmp < 0xffff); y+=2){
+				for (x=0; (x < width) && (tmp < 0xffff); x+=2){
+					g += buff[x];
+					b += buff[x+1] >> 1;
+					r += buff[x+width] >> 1;
+					g += buff[x+width+1];
+
+					tmp ++;
+				}
+				buff += (width <<1);
+			}
+			break;
+		case V4L2_PIX_FMT_SGRBG8:
+			for (y=0; (y < height) && (tmp < 0xffff); y+=2){
+				for (x=0; (x < width) && (tmp < 0xffff); x+=2){
+					g += buff[x];
+					r += buff[x+1] >> 1;
+					b += buff[x+width] >> 1;
+					g += buff[x+width+1];
+
+					tmp ++;
+				}
+				buff += (width <<1);
+			}
+			break;
+		default:
+			return;
+	}
+    
+	r = r >> 16;
+	g = g >> 16;
+	b = b >> 16;
+    
+	if (r == 0)
+		r = 1;
+	if (g == 0)
+		g = 1;
+	if (b == 0)
+		b = 1;
+    
+    // now calculate variance from average of all colors
+	exr = ((r+g+b) << 16) / (3*r);
+	exg = ((r+g+b) << 16) / (3*g);
+	exb = ((r+g+b) << 16) / (3*b);
+    
+    //printf("pomer barev je: %d %d %d (%d %d %d) \n", exr, exg, exb, r, g, b);
+    
+    
+    // change colors...
+	buff = (unsigned char *)bayer;
+    
+	switch (pixfmt) {
+		case V4L2_PIX_FMT_SBGGR8:
+			for (y=0; (y < height); y+=2){
+				for (x=0; (x < width); x+=2){
+					tmp = (buff[x]         * exb);
+					buff[x]         = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+1]       * exg);
+					buff[x+1]       = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width]   * exg);
+					buff[x+width]   = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width+1] * exr);
+					buff[x+width+1] = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+				}
+				buff += (width << 1);
+			}
+			break;
+		case V4L2_PIX_FMT_SRGGB8:
+			for (y=0; (y < height); y+=2){
+				for (x=0; (x < width); x+=2){
+					tmp = (buff[x]         * exr);
+					buff[x]         = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+1]       * exg);
+					buff[x+1]       = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width]   * exg);
+					buff[x+width]   = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width+1] * exb); 
+					buff[x+width+1] = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+				}
+				buff += (width << 1);
+			}
+			break;
+		case V4L2_PIX_FMT_SGBRG8:
+			for (y=0; (y < height); y+=2){
+				for (x=0; (x < width); x+=2){
+					tmp = (buff[x]         * exg);
+					buff[x]         = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+1]       * exb);
+					buff[x+1]       = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width]   * exr);
+					buff[x+width]   = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width+1] * exg);
+					buff[x+width+1] = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+				}
+				buff += (width << 1);
+			}
+			break;
+		case V4L2_PIX_FMT_SGRBG8:
+			for (y=0; (y < height); y+=2){
+				for (x=0; (x < width); x+=2){
+					tmp = (buff[x]         * exg);
+					buff[x]         = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+1]       * exr);
+					buff[x+1]       = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width]   * exb);
+					buff[x+width]   = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+					tmp = (buff[x+width+1] * exg);
+					buff[x+width+1] = (tmp > 0xffffff) ? 0xff : tmp >> 16; 
+				}
+				buff += (width << 1);
+			}
+			break;
+		default:
+			return;
+	}   
+    
+}
diff -NaurEw ./libv4l-0.5.0/libv4lconvert/libv4lconvert.c ./libv4l-0.5.0_control/libv4lconvert/libv4lconvert.c
--- ./libv4l-0.5.0/libv4lconvert/libv4lconvert.c	2008-09-15 13:39:59.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4lconvert/libv4lconvert.c	2008-09-29 18:00:36.000000000 +0200
@@ -272,10 +272,12 @@
 int v4lconvert_convert(struct v4lconvert_data *data,
   const struct v4l2_format *src_fmt,  /* in */
   const struct v4l2_format *dest_fmt, /* in */
-  unsigned char *src, int src_size, unsigned char *_dest, int dest_size)
+  						unsigned char *src, int src_size, 
+						unsigned char *_dest, int dest_size,
+					  	int rotate, int white_balance)
 {
   unsigned int header_width, header_height;
-  int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
+	int result, needed, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
   unsigned char *components[3];
   unsigned char *dest = _dest;
 
@@ -308,7 +310,9 @@
   }
 
   if (data->flags & V4LCONVERT_UPSIDE_DOWN) {
-    rotate = 180;
+		rotate += 180;
+		if (rotate >= 360)
+			rotate -= 360;
     dest = alloca(needed);
   }
 
@@ -343,6 +347,9 @@
 	  if (!rotate)
 	    dest = alloca(needed);
 	  rotate += 90;
+						if (rotate >= 360)
+							rotate -= 360;
+				
 	} else {
 	  V4LCONVERT_ERR("unexpected width / height in JPEG header"
 			 "expected: %ux%u, header: %ux%u\n",
@@ -398,6 +405,12 @@
     case V4L2_PIX_FMT_SGBRG8:
     case V4L2_PIX_FMT_SGRBG8:
     case V4L2_PIX_FMT_SRGGB8:
+		
+			if (white_balance){
+				v4lconvert_bayer_white_balance(src, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
+				white_balance = 0;
+			}
+		
       switch (dest_fmt->fmt.pix.pixelformat) {
       case V4L2_PIX_FMT_RGB24:
 	v4lconvert_bayer_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
diff -NaurEw ./libv4l-0.5.0/libv4lconvert/libv4lconvert-priv.h ./libv4l-0.5.0_control/libv4lconvert/libv4lconvert-priv.h
--- ./libv4l-0.5.0/libv4lconvert/libv4lconvert-priv.h	2008-09-14 22:43:23.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4lconvert/libv4lconvert-priv.h	2008-09-23 19:22:22.000000000 +0200
@@ -163,4 +163,6 @@
 void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
   int width, int height);
 
+void v4lconvert_bayer_white_balance(const unsigned char *bayer, 
+									int width, int height, unsigned int pixfmt);
 #endif
diff -NaurEw ./libv4l-0.5.0/libv4l1/libv4l1.c ./libv4l-0.5.0_control/libv4l1/libv4l1.c
--- ./libv4l-0.5.0/libv4l1/libv4l1.c	2008-09-14 22:43:23.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4l1/libv4l1.c	2008-10-04 02:37:16.000000000 +0200
@@ -320,7 +320,7 @@
 
   /* Register with libv4l2, as we use that todo format conversion and read()
      emulation for us */
-  if (v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION) == -1) {
+  if (v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION, file) == -1) {
     int saved_err = errno;
     syscall(SYS_close, fd);
     errno = saved_err;
diff -NaurEw ./libv4l-0.5.0/libv4l2/libv4l2.c ./libv4l-0.5.0_control/libv4l2/libv4l2.c
--- ./libv4l-0.5.0/libv4l2/libv4l2.c	2008-09-14 22:46:54.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4l2/libv4l2.c	2008-10-04 03:16:23.000000000 +0200
@@ -69,6 +69,8 @@
 #include "libv4l2.h"
 #include "libv4l2-priv.h"
 
+#include "v4lcontrol.c"
+
 /* Note these flags are stored together with the flags passed to v4l2_fd_open()
    in v4l2_dev_info's flags member, so care should be taken that the do not
    use the same bits! */
@@ -79,12 +81,7 @@
 
 #define V4L2_MMAP_OFFSET_MAGIC      0xABCDEF00u
 
-static pthread_mutex_t v4l2_open_mutex = PTHREAD_MUTEX_INITIALIZER;
-static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = { { .fd = -1 },
-  { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
-  { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
-  { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }};
-static int devices_used = 0;
+
 
 
 static int v4l2_request_read_buffers(int index)
@@ -276,7 +273,9 @@
 	   &devices[index].src_fmt, &devices[index].dest_fmt,
 	   devices[index].frame_pointers[buf->index],
 	   buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf +
-	     buf->index * V4L2_FRAME_BUF_SIZE), dest_size);
+					buf->index * V4L2_FRAME_BUF_SIZE), dest_size,
+					devices[index].control->value[SW_CTRL_ROTATE_INDEX],
+					devices[index].control->value[SW_CTRL_WB_INDEX]);
 
     if (result < 0) {
       int saved_err = errno;
@@ -421,7 +420,7 @@
   if (fd == -1)
     return fd;
 
-  if (v4l2_fd_open(fd, 0) == -1) {
+	 if (v4l2_fd_open(fd, 0, file) == -1) {
     int saved_err = errno;
     syscall(SYS_close, fd);
     errno = saved_err;
@@ -431,13 +430,14 @@
   return fd;
 }
 
-int v4l2_fd_open(int fd, int v4l2_flags)
+ int v4l2_fd_open(int fd, int v4l2_flags, const char *device)
 {
   int i, index;
   char *lfname;
   struct v4l2_capability cap;
   struct v4l2_format fmt;
   struct v4lconvert_data *convert;
+	 struct v4lcontrol_data *control;
 
   /* If no log file was set by the app, see if one was specified through the
      environment */
@@ -471,13 +471,19 @@
   if (!(convert = v4lconvert_create(fd)))
     return -1;
 
+	 
+	 /* init libv4lcontrol */
+	 if (!(control = v4lcontrol_init(fd, device)))
+		 return -1;
+
   /* So we have a v4l2 capture device, register it in our devices array */
   pthread_mutex_lock(&v4l2_open_mutex);
-  for (index = 0; index < V4L2_MAX_DEVICES; index++)
+	 for (index = 0; index < V4L2_MAX_DEVICES; index++){
     if(devices[index].fd == -1) {
       devices[index].fd = fd;
       break;
     }
+	 }
   pthread_mutex_unlock(&v4l2_open_mutex);
 
   if (index == V4L2_MAX_DEVICES) {
@@ -499,6 +505,7 @@
   devices[index].no_frames = 0;
   devices[index].nreadbuffers = V4L2_DEFAULT_NREADBUFFERS;
   devices[index].convert = convert;
+	devices[index].control = control;
   devices[index].convert_mmap_buf = MAP_FAILED;
   for (i = 0; i < V4L2_MAX_NO_FRAMES; i++) {
     devices[index].frame_pointers[i] = MAP_FAILED;
@@ -554,6 +561,7 @@
   /* Free resources */
   v4l2_unmap_buffers(index);
   v4lconvert_destroy(devices[index].convert);
+	 v4lcontrol_destroy(devices[index].control);
   if (devices[index].convert_mmap_buf != MAP_FAILED) {
     if (v4l2_buffers_mapped(index))
       V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n");
@@ -634,6 +642,7 @@
   if ((index = v4l2_get_index(fd)) == -1)
     return syscall(SYS_ioctl, fd, request, arg);
 
+
   /* Appearantly the kernel and / or glibc ignore the 32 most significant bits
      when long = 64 bits, and some applications pass an int holding the req to
      ioctl, causing it to get sign extended, depending upon this behavior */
@@ -689,10 +698,22 @@
   }
 
   if (!is_capture_request) {
+		 switch (request){
+			 case VIDIOC_QUERYCTRL:
+				 result = v4lcontrol_queryctrl(fd, arg);
+				 break;
+			 case VIDIOC_S_CTRL:
+				 result = v4lcontrol_setctrl(fd, arg);
+				 break;
+			 case VIDIOC_G_CTRL:
+				 result = v4lcontrol_getctrl(fd, arg);
+				 break;
+			 default:
     result = syscall(SYS_ioctl, fd, request, arg);
     saved_err = errno;
     v4l2_log_ioctl(request, arg, result);
     errno = saved_err;
+		 }
     return result;
   }
 
diff -NaurEw ./libv4l-0.5.0/libv4l2/libv4l2-priv.h ./libv4l-0.5.0_control/libv4l2/libv4l2-priv.h
--- ./libv4l-0.5.0/libv4l2/libv4l2-priv.h	2008-08-06 10:40:53.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4l2/libv4l2-priv.h	2008-10-04 02:24:21.000000000 +0200
@@ -80,6 +80,7 @@
   unsigned int no_frames;
   unsigned int nreadbuffers;
   struct v4lconvert_data *convert;
+  struct v4lcontrol_data *control;
   unsigned char *convert_mmap_buf;
   /* Frame bookkeeping is only done when in read or mmap-conversion mode */
   unsigned char *frame_pointers[V4L2_MAX_NO_FRAMES];
@@ -89,7 +90,80 @@
   unsigned char frame_map_count[V4L2_MAX_NO_FRAMES];
 };
 
+/* controls */
+int v4l2_init_control(int fd);
+
 /* From log.c */
 void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
 
+static int v4l2_get_index(int fd);
+
+
+static pthread_mutex_t v4l2_open_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = { { .fd = -1 },
+	{ .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
+ { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 },
+ { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }};
+ static int devices_used = 0;
+
+/* From control.c */
+ 
+#define SW_CTRL_WB_INDEX 		0
+#define SW_CTRL_ROTATE_INDEX 	1
+#define COUNT_SW_CTRL			2
+#define CTL_EXIT				0xffff
+
+struct v4lcontrol_data{
+	 struct v4l2_queryctrl sw_ctrl[COUNT_SW_CTRL];
+	 unsigned long int value[COUNT_SW_CTRL];
+	 unsigned long int last_ctrl_id;
+	 pthread_t event_thread;
+	 int shm_data_id;
+	 int shm_semaphores_id;
+};
+
+struct thread_arg{
+	int fd;
+	const char *device;
+	struct v4lcontrol_data *control_data;
+};
+
+static struct v4lcontrol_data v4lcontrol_data_skeleton = {
+	.sw_ctrl = {
+		{
+			.id 		= -1,
+			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name 		= "software white balance",
+			.minimum 	= 0,
+			.maximum 	= 1,
+			.step 		= 1,
+			.default_value 	= 1
+		},
+  		{
+			.id 		= -1,
+			.type 		= V4L2_CTRL_TYPE_INTEGER,
+			.name 		= "software rotate",
+			.minimum 	= 0,
+			.maximum 	= 270,
+			.step 		= 90, 
+			.default_value 	= 0
+		}
+	},
+	.value = {
+		1,
+  		0
+	},
+	.last_ctrl_id = -1
+};
+
+void get_last_and_max_ctrl_id(int fd, long unsigned int *max,long unsigned int *last);
+struct v4lcontrol_data *v4lcontrol_init(int fd, const char *device);
+int v4lcontrol_destroy(struct v4lcontrol_data *control);
+int v4lcontrol_queryctrl(int fd, struct v4l2_queryctrl *qctrl);
+int v4lcontrol_setctrl(int fd, struct v4l2_control *ctrl);
+int v4lcontrol_getctrl(int fd, struct v4l2_control *ctrl);
+int binary_semaphore_down (int semid, int counterId);
+int binary_semaphore_up(int semid, int counterId);
+void *event_thread_func(void *arg);
+
 #endif
diff -NaurEw ./libv4l-0.5.0/libv4l2/v4lcontrol.c ./libv4l-0.5.0_control/libv4l2/v4lcontrol.c
--- ./libv4l-0.5.0/libv4l2/v4lcontrol.c	1970-01-01 01:00:00.000000000 +0100
+++ ./libv4l-0.5.0_control/libv4l2/v4lcontrol.c	2008-10-04 03:16:02.000000000 +0200
@@ -0,0 +1,305 @@
+/*
+#             (C) 2008 Lukas karas <lukas.karas@centrum.cz>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ Classic model allow control video device only by application, 
+ that read stream from its.
+ 
+ But a lot of applications (for example Skype for Linux) not support all posible controls.
+ 
+ This lib allow second applicatios send CTLs to video device via shared memory.
+*/
+
+
+#include <linux/videodev2.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
+#include "../include/v4lcontrol.h"
+
+/**
+ Get maximal and latest CIDs using by device driver.
+ Driver should support V4L2_CTRL_FLAG_NEXT_CTRL for get correct values!
+*/
+void get_last_and_max_ctrl_id(int fd, long unsigned int *max,long unsigned int *last){
+	int i;
+	struct v4l2_queryctrl   qctrl = {
+		.id = V4L2_CID_BASE
+	};
+	*max = V4L2_CID_BASE;
+	*last = V4L2_CID_BASE;	
+	
+	for (i = 0; i < V4L2_CTRL_ID_MASK; i++) {
+		if (i>0){
+			qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+		}
+			
+		if (0 > v4l2_ioctl(fd,VIDIOC_QUERYCTRL,&qctrl)){
+			break;
+		}
+		if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+			continue;
+		
+		*last = qctrl.id;
+		if (*last > *max)
+				*max = * last;
+	}
+}
+
+/** 
+ Decrease value of ipc semaphore
+*/
+int binary_semaphore_down (int semid, int counterId){
+	struct sembuf operation;
+	operation.sem_num = counterId;
+	operation.sem_op = -1;
+	operation.sem_flg = 0;
+	return semop (semid, &operation, 1);
+}
+
+/** 
+ Increase value of ipc semaphore
+ */
+int binary_semaphore_up(int semid, int counterId){
+	struct sembuf operation;
+	operation.sem_num = counterId;
+	operation.sem_op = 1;
+	operation.sem_flg = 0;
+	return semop (semid, &operation, 1);
+}
+
+void *event_thread_func(void *arg){
+	int fd = ((struct thread_arg *)arg)->fd;
+	struct v4lcontrol_data *control_data;
+	key_t data_key = ftok( (char *)((struct thread_arg *)arg)->device, SHM_DATA_ID);
+	key_t semaphores_key = ftok( (char *)((struct thread_arg *)arg)->device, SHM_SEMAPTHORES_ID);
+	unsigned char exit = 0;
+	struct v4lcontrol_shared *data;
+	int i;
+	
+	control_data = ((struct thread_arg *)arg)->control_data;
+		
+	if (( control_data->shm_semaphores_id = semget(semaphores_key, SEMAPHORES_COUNT, IPC_CREAT | 0600) ) < 0) {
+		V4L2_LOG_ERR("shmget semaphores");
+	}
+	
+	semctl(control_data->shm_semaphores_id, SEMAPHORE_CLIENT_SEM_ID, SETVAL, 1);
+	semctl(control_data->shm_semaphores_id, SEMAPHORE_LIB_DATA_SEM_ID, SETVAL, 0);
+	semctl(control_data->shm_semaphores_id, SEMAPHORE_CLIENT_DATA_SEM_ID, SETVAL, 0);
+		
+	if (( control_data->shm_data_id = shmget(data_key, sizeof( struct v4lcontrol_shared ), IPC_CREAT | 0600)  ) < 0) {
+		V4L2_LOG_ERR("shmget data");
+	}
+	
+	
+	if ((data = (struct v4lcontrol_shared *)shmat(control_data->shm_data_id, NULL, 0)) == (struct v4lcontrol_shared *) -1) {
+		V4L2_LOG_ERR("shmat");
+	}
+	
+	//data->lib_pid = (int) getpid ();
+	data->ioctl_request = CTL_EXIT;
+	
+	/* do ctls */
+	while(!exit){
+		binary_semaphore_down(control_data->shm_semaphores_id, SEMAPHORE_LIB_DATA_SEM_ID);
+		printf("\ndata recieved (shm)... (ioctl 0x%x)\n", (int)data->ioctl_request);
+		switch( data->ioctl_request ){
+			case CTL_EXIT:
+				data->result = 0;
+				exit = 1;
+				break;
+			default:
+				data->result = v4l2_ioctl(fd,data->ioctl_request,&data->arg);
+		}
+		binary_semaphore_up(control_data->shm_semaphores_id, SEMAPHORE_CLIENT_DATA_SEM_ID);
+	}
+	binary_semaphore_up(control_data->shm_semaphores_id, SEMAPHORE_CLIENT_DATA_SEM_ID);
+	
+	
+	if ( shmdt( data ) < 0 ) {
+		V4L2_LOG_ERR("shmdt");
+	}
+	
+	if ( shmctl(control_data->shm_data_id, IPC_RMID, 0 ) < 0 ) {
+		V4L2_LOG_ERR("shmctl data");
+	}
+	
+	if ( shmctl(control_data->shm_semaphores_id, IPC_RMID, 0 ) < 0 ) {
+		V4L2_LOG_ERR("shmctl semaphores");
+	}
+	
+	return (void *)NULL;
+}
+
+
+struct v4lcontrol_data *v4lcontrol_init(int fd, const char *device){
+	int rc;
+	pthread_attr_t attr;
+	struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data));
+	struct thread_arg arg = {
+		.fd = fd,
+		.device = device,
+		.control_data = data
+	};
+	
+	long unsigned int max;
+	
+	memcpy(data, &v4lcontrol_data_skeleton, sizeof(struct v4lcontrol_data));
+	
+	/* assign free private CIDs for software controls */
+	get_last_and_max_ctrl_id(fd, &max, &data->last_ctrl_id);
+	if (max < V4L2_CID_PRIVATE_BASE)
+		max = V4L2_CID_PRIVATE_BASE -1;
+	
+	data->sw_ctrl[SW_CTRL_WB_INDEX].id 		= max + 1;
+	data->sw_ctrl[SW_CTRL_ROTATE_INDEX].id 	= max + 2;
+	
+	/* init event thread */
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);    
+	
+	rc = pthread_create(&data->event_thread, &attr, event_thread_func, (void *)&arg);
+	if (rc){ 
+		V4L2_LOG_ERR("Error in function pthread_create()"); 
+	}
+	
+	/* Free attribute */
+	pthread_attr_destroy(&attr);
+	
+	return data;
+}
+
+
+int v4lcontrol_destroy(struct v4lcontrol_data *control){
+	int rc;
+	struct v4lcontrol_shared *data;	
+	
+	binary_semaphore_down(control->shm_semaphores_id, SEMAPHORE_CLIENT_SEM_ID);
+	
+	if ((data = (struct v4lcontrol_shared *)shmat(control->shm_data_id, NULL, 0)) == (struct v4lcontrol_shared *) -1) {
+		V4L2_LOG_ERR("shmat");
+	}
+	data->ioctl_request = CTL_EXIT;
+	if ( shmdt( data ) < 0 ) {
+		V4L2_LOG_ERR("shmdt");
+		return -1;
+	}
+	
+	binary_semaphore_up(control->shm_semaphores_id, SEMAPHORE_LIB_DATA_SEM_ID);
+	binary_semaphore_down(control->shm_semaphores_id, SEMAPHORE_CLIENT_DATA_SEM_ID);
+	
+	binary_semaphore_up(control->shm_semaphores_id, SEMAPHORE_CLIENT_SEM_ID);
+	
+	/* wait for end of thread */
+	rc = pthread_join(control->event_thread, NULL );
+	if (rc){
+		V4L2_LOG_ERR("ERROR return code from pthread_join() is %d\n", rc);
+	}
+
+	free(control);
+	return 0;
+}
+
+int v4lcontrol_queryctrl(int fd, struct v4l2_queryctrl *qctrl){
+	int result = -1; 
+	int i, index;
+	unsigned char next = 0;
+	
+	/* first, we try software ctrls */
+	index = v4l2_get_index(fd);
+	
+	if ((qctrl->id ^ V4L2_CTRL_FLAG_NEXT_CTRL) == devices[index].control->last_ctrl_id ){
+		next = 1;
+	}
+	if (index!= -1){
+		for (i=0; i < COUNT_SW_CTRL; i++){
+			if (devices[index].control->sw_ctrl[i].id == qctrl->id || next ){
+				/* software ctrl is selected */
+				memcpy(qctrl, &devices[index].control->sw_ctrl[i], sizeof(struct v4l2_queryctrl));
+				return 0;
+			}
+			if ((qctrl->id ^ V4L2_CTRL_FLAG_NEXT_CTRL) == devices[index].control->sw_ctrl[i].id){
+				next = 1;
+			}
+		}
+	}
+	
+	/* driver (hardware) ctrl is selected */
+	result = syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, qctrl);
+	v4l2_log_ioctl(VIDIOC_QUERYCTRL, qctrl, result);
+	return result;	
+}
+
+int v4lcontrol_setctrl(int fd, struct v4l2_control *ctrl){
+	
+	struct v4l2_queryctrl qctrl = { .id = ctrl->id };
+	int result, i, index;
+	
+	/* first, try software ctrls */
+	index = v4l2_get_index(fd);
+	if (index!= -1){
+		for (i=0; i < COUNT_SW_CTRL; i++){
+			if (devices[index].control->sw_ctrl[i].id == ctrl->id){
+				devices[index].control->value[i] = ctrl->value;
+				return 0;
+			}
+		}
+	}
+	
+	if (result = v4lcontrol_queryctrl(fd, &qctrl))
+		return result;
+
+	if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) &&
+			 !(qctrl.flags & V4L2_CTRL_FLAG_GRABBED)) {
+		if (qctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
+			ctrl->value = ctrl->value? 1:0;
+
+		result = syscall(SYS_ioctl, fd, VIDIOC_S_CTRL, ctrl);
+	}
+
+	return result;
+}
+
+int v4lcontrol_getctrl(int fd, struct v4l2_control *ctrl){
+	
+	struct v4l2_queryctrl qctrl = { .id = ctrl->id };
+	//struct v4l2_control ctrl = { .id = cid };
+	int i, index, result;
+
+	/* first, we try software ctrls */
+	index = v4l2_get_index(fd);
+	if (index!= -1){
+		for (i=0; i < COUNT_SW_CTRL; i++){
+			if (devices[index].control->sw_ctrl[i].id == ctrl->id){
+				ctrl->value = devices[index].control->value[i];
+				return 0;
+			}
+		}
+	}
+		
+	if (v4lcontrol_queryctrl(fd, &qctrl))
+		return 0;
+	
+	if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+		return 0;
+
+	return  syscall(SYS_ioctl, fd, VIDIOC_G_CTRL, ctrl);
+}
diff -NaurEw ./libv4l-0.5.0/libv4l2/v4l2convert.c ./libv4l-0.5.0_control/libv4l2/v4l2convert.c
--- ./libv4l-0.5.0/libv4l2/v4l2convert.c	2008-09-14 22:43:23.000000000 +0200
+++ ./libv4l-0.5.0_control/libv4l2/v4l2convert.c	2008-10-03 22:29:12.000000000 +0200
@@ -90,9 +90,11 @@
   if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
     return fd;
 
+  printf("\n\nv4l2convert \"%s\" fd=0x%x\n\n", file, fd);  
+
   /* Try to Register with libv4l2 (in case of failure pass the fd to the
      application as is) */
-  v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION);
+  v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION, file);
 
   return fd;
 }
