Kernel Patch for Xbox Live Camera on Linux

I’ve done some more work on getting the Xbox Live camera to work properly on Linux this evening and have submitted a patch to the development mailing list. I don’t know if it will get included in some future driver release or be considered unsuitable, but it does at least make all of the problems I have getting the camera to work with the UVC driver and V4L2 go away.

The patch is below. It’s against the v3.0.0-generic kernel which as of the time of writing is the latest available for Ubuntu 11.10. It may well apply cleanly to the driver sources in both earlier and later kernel releases, but as yet I’ve not tried.

If you wish to use the patch you’ll need to be comfortable patching the kernel and rebuilding the UVC module. I’ll try to write up some details on that later. The module should work normally for all other cameras, but if you’re using the Xbox Live camera then you’ll need to use the ctrltimeout option to increase the timeout from the default. I found that increasing it to 1000 was insufficient, but that 2000 worked fine, so for the purposes of testing I’m doing something like:

insmod uvcvideo.ko ctrltimeout=2000

The patch also forces the reported maximum exposure setting for the camera to 255 if it would otherwise be set to -1. It’s clearly wrong that the minimum is reported as 1, the maximum as -1 and the default as 166, so I suspect this is a firmware issue in the camera and as such I’ve not made the correction optional.

And here’s the patch (whitespace may have been broken, so take care when applying it):

--- drivers/media/video/uvc/uvc_ctrl.c~  2012-04-13 22:37:19.278971810 +0100
+++ drivers/media/video/uvc/uvc_ctrl.c  2012-04-13 23:22:39.840119482 +0100
@@ -963,9 +963,17 @@
     v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
 
-  if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
+  if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
     v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+    if ( v4l2_ctrl->id == V4L2_CID_EXPOSURE_ABSOLUTE &&
+        v4l2_ctrl->maximum == -1 &&
+        chain->dev->quirks &
+        UVC_QUIRK_XBOX_EXPOSURE_FIX ) {
+      uvc_trace(UVC_TRACE_CONTROL, "Xbox queryctl max exp. set to 255.");
+      v4l2_ctrl->maximum = 255;
+    }
+        }
 
   if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
     v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
@@ -1202,6 +1210,12 @@
            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
     max = mapping->get(mapping, UVC_GET_MAX,
            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+    if ( max == -1 && xctrl->id == V4L2_CID_EXPOSURE_ABSOLUTE &&
+        chain->dev->quirks &
+        UVC_QUIRK_XBOX_EXPOSURE_FIX ) {
+      uvc_trace(UVC_TRACE_CONTROL, "Xbox max exposure set to 255.");
+      max = 255;
+    }
     step = mapping->get(mapping, UVC_GET_RES,
             uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
     if (step == 0)
--- drivers/media/video/uvc/uvc_driver.c~  2012-04-15 15:36:27.858728125 +0100
+++ drivers/media/video/uvc/uvc_driver.c  2012-04-13 22:29:55.556771975 +0100
@@ -47,6 +47,7 @@
 static unsigned int uvc_quirks_param = -1;
 unsigned int uvc_trace_param;
 unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+unsigned int uvc_ctrl_timeout_param = UVC_CTRL_CONTROL_TIMEOUT;
 
 /* ------------------------------------------------------------------------
  * Video formats
@@ -2016,6 +2017,8 @@
 MODULE_PARM_DESC(trace, "Trace level bitmask");
 module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
+module_param_named(ctrltimeout, uvc_ctrl_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ctrltimeout, "Control control requests timeout");
 
 /* ------------------------------------------------------------------------
  * Driver initialization and cleanup
@@ -2351,6 +2354,15 @@
     .bInterfaceProtocol  = 0,
     .driver_info    = UVC_QUIRK_PROBE_MINMAX
         | UVC_QUIRK_IGNORE_SELECTOR_UNIT },
+        /* Microsoft XBox Live USB Web Camera */
+        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                                | USB_DEVICE_ID_MATCH_INT_INFO,
+          .idVendor             = 0x045e,
+          .idProduct            = 0x0294,
+          .bInterfaceClass      = USB_CLASS_VIDEO,
+          .bInterfaceSubClass   = 1,
+          .bInterfaceProtocol   = 0,
+          .driver_info          = UVC_QUIRK_XBOX_EXPOSURE_FIX },
   /* Generic USB Video Class */
   { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
   {}
--- drivers/media/video/uvc/uvc_video.c~  2012-04-13 22:31:47.037324898 +0100
+++ drivers/media/video/uvc/uvc_video.c  2012-04-13 22:31:04.009110557 +0100
@@ -75,7 +75,7 @@
   int ret;
 
   ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
-        UVC_CTRL_CONTROL_TIMEOUT);
+        uvc_ctrl_timeout_param);
   if (ret != size) {
     uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on "
       "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,
--- drivers/media/video/uvc/uvcvideo.h~  2012-04-13 22:27:33.248066424 +0100
+++ drivers/media/video/uvc/uvcvideo.h  2012-04-13 22:39:23.599589380 +0100
@@ -212,6 +212,7 @@
 #define UVC_QUIRK_FIX_BANDWIDTH    0x00000080
 #define UVC_QUIRK_PROBE_DEF    0x00000100
 #define UVC_QUIRK_RESTRICT_FRAME_RATE  0x00000200
+#define UVC_QUIRK_XBOX_EXPOSURE_FIX  0x00000400
 
 /* Format flags */
 #define UVC_FMT_FLAG_COMPRESSED    0x00000001
@@ -575,6 +576,7 @@
 extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_trace_param;
 extern unsigned int uvc_timeout_param;
+extern unsigned int uvc_ctrl_timeout_param;
 
 #define uvc_trace(flag, msg...) \
   do { \

This entry was posted in Computing, Linux. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *