camera: Support manual focus distance control on USB camera

This CL adds manual focus distance control in USB HAL.

BUG=b:151048287, 1068176
TEST=Test focus distance on logitech camera on eve.
Pass CtsCameraTestCases on morphius

Change-Id: Icbe92186daaaee1e8b87a6346f15055d5644f1f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2359065
Tested-by: Hsu Wei-Cheng <mojahsu@chromium.org>
Commit-Queue: Hsu Wei-Cheng <mojahsu@chromium.org>
Reviewed-by: Ren-Pei Zeng <kamesan@chromium.org>
diff --git a/camera/hal/usb/metadata_handler.cc b/camera/hal/usb/metadata_handler.cc
index 7279099..0d10d0d 100644
--- a/camera/hal/usb/metadata_handler.cc
+++ b/camera/hal/usb/metadata_handler.cc
@@ -91,6 +91,24 @@
               vert_crop_factor};
 }
 
+// The unit of ANDROID_LENS_FOCUS_DISTANCE is diopters (1/meter), but the unit
+// of V4L2_CID_FOCUS_ABSOLUTE is undefined. We map the V4L2 value to diopters by
+// (value - minimum) / normalize_factor where |value| is in [minimum, maximum].
+// We calculate a proper |normalize_factor| by assuming the minimum focus
+// distance of USB cameras is >= 1cm (tested on real webcams), i.e. max diopter
+// is <= 100, and only use power of 10s for readability.
+// For example, V4L2 range [0, 250] will map to Android range [0, 25], where the
+// minimum focus distance is 1m/25=4cm. This function takes |v4l2_range|
+// (== maximum - minimum) and returns |normalize_factor|.
+uint32_t GetNormalizeFactorForV4l2FocusRange(float v4l2_range) {
+  uint32_t normalize_factor = 1;
+
+  while (v4l2_range / normalize_factor > 100.0)
+    normalize_factor *= 10;
+
+  return normalize_factor;
+}
+
 class MetadataUpdater {
  public:
   explicit MetadataUpdater(android::CameraMetadata* metadata)
@@ -178,7 +196,10 @@
                                  const DeviceInfo& device_info,
                                  V4L2CameraDevice* device,
                                  const SupportedFormats& supported_formats)
-    : device_info_(device_info), device_(device), af_trigger_(false) {
+    : device_info_(device_info),
+      device_(device),
+      af_trigger_(false),
+      focus_distance_normalize_factor_(0) {
   // MetadataBase::operator= will make a copy of camera_metadata_t.
   static_metadata_ = &static_metadata;
   request_template_ = &request_template;
@@ -220,6 +241,14 @@
   is_zoom_control_supported_ = V4L2CameraDevice::IsControlSupported(
       device_info.device_path, kControlZoom);
 
+  if (V4L2CameraDevice::IsFocusDistanceSupported(device_info.device_path,
+                                                 &focus_distance_range_)) {
+    float full_range =
+        focus_distance_range_.maximum - focus_distance_range_.minimum;
+    focus_distance_normalize_factor_ =
+        GetNormalizeFactorForV4l2FocusRange(full_range);
+  }
+
   thread_checker_.DetachFromThread();
 }
 
@@ -769,6 +798,7 @@
       ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
       ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
       ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+      ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
       ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
       ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
       ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
@@ -806,7 +836,6 @@
         available_characteristics_keys.end(),
         {
             ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
-            ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
             ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
         });
   }
@@ -831,12 +860,6 @@
     update_request(ANDROID_LENS_FOCAL_LENGTH,
                    device_info.lens_info_available_focal_lengths[0]);
 
-    update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-                  device_info.lens_info_minimum_focus_distance);
-
-    update_request(ANDROID_LENS_FOCUS_DISTANCE,
-                   device_info.lens_info_optimal_focus_distance);
-
     update_static(
         ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
         std::vector<float>{device_info.sensor_info_physical_size_width,
@@ -867,21 +890,6 @@
       update_request(ANDROID_LENS_FOCAL_LENGTH, kDefaultAvailableFocalLength);
     }
 
-    if (device_info.lens_info_minimum_focus_distance > 0) {
-      update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-                    device_info.lens_info_minimum_focus_distance);
-    } else {
-      update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
-                    kDefaultMinimumFocusDistance);
-    }
-
-    if (device_info.lens_info_optimal_focus_distance > 0) {
-      update_request(ANDROID_LENS_FOCUS_DISTANCE,
-                     device_info.lens_info_optimal_focus_distance);
-    } else {
-      update_request(ANDROID_LENS_FOCUS_DISTANCE, kDefaultLensFocusDistance);
-    }
-
     if (device_info.sensor_info_pixel_array_size_width > 0 &&
         device_info.sensor_info_pixel_array_size_height > 0) {
       update_static(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
@@ -896,31 +904,11 @@
                   ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
   }
 
-  update_static(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
-                ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
-
   update_static(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
                 ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO);
   update_request(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
                  ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO);
 
-  bool support_af =
-      V4L2CameraDevice::IsAutoFocusSupported(device_info.device_path);
-  if (support_af) {
-    update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
-                  std::vector<uint8_t>{ANDROID_CONTROL_AF_MODE_OFF,
-                                       ANDROID_CONTROL_AF_MODE_AUTO});
-    update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_AUTO);
-  } else {
-    update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
-                  ANDROID_CONTROL_AF_MODE_OFF);
-    update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_OFF);
-    // If auto focus is not supported, the minimum focus distance should be 0.
-    // Overwrite the value here since there are many camera modules have wrong
-    // config.
-    update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0.0f);
-  }
-
   // Set vendor tags for specified boards.
   if (device_info.quirks & kQuirkMonocle) {
     int32_t timestamp_sync =
@@ -1008,6 +996,38 @@
     available_request_keys.push_back(ANDROID_SENSOR_EXPOSURE_TIME);
   }
 
+  // The unit of V4L2 focus distance is undefined, so set it to uncalibrated.
+  update_static(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+                ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
+  if (V4L2CameraDevice::IsControlSupported(device_info.device_path,
+                                           kControlFocusAuto)) {
+    update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                  std::vector<uint8_t>{ANDROID_CONTROL_AF_MODE_OFF,
+                                       ANDROID_CONTROL_AF_MODE_AUTO});
+    update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_AUTO);
+    update_request(ANDROID_LENS_FOCUS_DISTANCE, 0.0f);
+    if (V4L2CameraDevice::IsFocusDistanceSupported(device_info.device_path,
+                                                   &range)) {
+      float full_range = range.maximum - range.minimum;
+      uint32_t factor = GetNormalizeFactorForV4l2FocusRange(full_range);
+      update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+                    full_range / factor);
+    } else {
+      if (device_info.lens_info_minimum_focus_distance > 0) {
+        update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+                      1.0f / device_info.lens_info_minimum_focus_distance);
+      } else {
+        update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+                      1.0f / kDefaultMinimumFocusDistance);
+      }
+    }
+  } else {
+    update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+                  ANDROID_CONTROL_AF_MODE_OFF);
+    update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_OFF);
+    update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0.0f);
+  }
+
   update_static(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
                 available_characteristics_keys);
   update_static(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, available_request_keys);
@@ -1133,9 +1153,34 @@
   if (metadata->exists(ANDROID_CONTROL_AF_MODE)) {
     camera_metadata_entry entry = metadata->find(ANDROID_CONTROL_AF_MODE);
     if (entry.data.u8[0] == ANDROID_CONTROL_AF_MODE_OFF) {
-      device_->SetAutoFocus(false);
+      if (metadata->exists(ANDROID_LENS_FOCUS_DISTANCE) &&
+          focus_distance_normalize_factor_ > 0) {
+        entry = metadata->find(ANDROID_LENS_FOCUS_DISTANCE);
+        int32_t distance =
+            static_cast<int32_t>(entry.data.f[0] *
+                                 focus_distance_normalize_factor_) +
+            focus_distance_range_.minimum;
+        distance = std::min(distance, focus_distance_range_.maximum);
+        device_->SetAutoFocus(false);
+        device_->SetFocusDistance(distance);
+        int32_t focus_distance;
+        device_->GetControlValue(kControlFocusDistance, &focus_distance);
+        float simulate_diopters =
+            static_cast<float>(focus_distance - focus_distance_range_.minimum) /
+            focus_distance_normalize_factor_;
+        update_request(ANDROID_LENS_FOCUS_DISTANCE, simulate_diopters);
+      } else {
+        device_->SetAutoFocus(false);
+      }
     } else if (entry.data.u8[0] == ANDROID_CONTROL_AF_MODE_AUTO) {
       device_->SetAutoFocus(true);
+      float diopters;
+      if (device_info_.lens_info_optimal_focus_distance > 0) {
+        diopters = 1.0 / device_info_.lens_info_optimal_focus_distance;
+      } else {
+        diopters = 1.0 / kDefaultLensFocusDistance;
+      }
+      update_request(ANDROID_LENS_FOCUS_DISTANCE, diopters);
     }
   }
 
diff --git a/camera/hal/usb/metadata_handler.h b/camera/hal/usb/metadata_handler.h
index de11d9c..e9e3b4c 100644
--- a/camera/hal/usb/metadata_handler.h
+++ b/camera/hal/usb/metadata_handler.h
@@ -139,6 +139,9 @@
   bool is_sharpness_control_supported_;
   bool is_tilt_control_supported_;
   bool is_zoom_control_supported_;
+
+  uint32_t focus_distance_normalize_factor_;
+  ControlRange focus_distance_range_;
 };
 
 }  // namespace cros
diff --git a/camera/hal/usb/v4l2_camera_device.cc b/camera/hal/usb/v4l2_camera_device.cc
index c529128..cb63cf0 100644
--- a/camera/hal/usb/v4l2_camera_device.cc
+++ b/camera/hal/usb/v4l2_camera_device.cc
@@ -64,6 +64,12 @@
     case kControlExposureTime:
       return V4L2_CID_EXPOSURE_ABSOLUTE;
 
+    case kControlFocusAuto:
+      return V4L2_CID_FOCUS_AUTO;
+
+    case kControlFocusDistance:
+      return V4L2_CID_FOCUS_ABSOLUTE;
+
     case kControlPan:
       return V4L2_CID_PAN_ABSOLUTE;
 
@@ -108,6 +114,12 @@
     case kControlExposureTime:
       return "exposure time";
 
+    case kControlFocusAuto:
+      return "auto focus";
+
+    case kControlFocusDistance:
+      return "focus distance";
+
     case kControlPan:
       return "pan";
 
@@ -152,6 +164,12 @@
     case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
       return "V4L2_CID_EXPOSURE_AUTO_PRIORITY";
 
+    case V4L2_CID_FOCUS_ABSOLUTE:
+      return "V4L2_CID_FOCUS_ABSOLUTE";
+
+    case V4L2_CID_FOCUS_AUTO:
+      return "V4L2_CID_FOCUS_AUTO";
+
     case V4L2_CID_PAN_ABSOLUTE:
       return "V4L2_CID_PAN_ABSOLUTE";
 
@@ -244,20 +262,23 @@
   }
 
   // Initial autofocus state.
-  struct v4l2_control control;
-  control.id = V4L2_CID_FOCUS_AUTO;
-  ret = TEMP_FAILURE_RETRY(ioctl(device_fd_.get(), VIDIOC_G_CTRL, &control));
-  if (ret < 0) {
-    LOGF(WARNING) << "Failed to get V4L2_CID_FOCUS_AUTO";
-    autofocus_supported_ = false;
-    autofocus_on_ = false;
-  } else {
-    autofocus_supported_ = true;
-    autofocus_on_ = control.value;
+  int32_t value;
+  focus_auto_supported_ = IsControlSupported(kControlFocusAuto) &&
+                          GetControlValue(kControlFocusAuto, &value) == 0;
+  if (focus_auto_supported_) {
+    LOGF(INFO) << "Device supports auto focus control, current mode is "
+               << (value == 0 ? "Off" : "Auto");
+  }
+  focus_distance_supported_ = IsControlSupported(kControlFocusDistance);
+  if (focus_distance_supported_) {
+    LOGF(INFO) << "Device supports focus distance control";
+    // Focus distance is valid when focus mode is off.
+    if (value == 0 && GetControlValue(kControlFocusDistance, &value) == 0) {
+      LOGF(INFO) << "Current distance is " << value;
+    }
   }
 
   // Query the initial auto white balance state.
-  int32_t value;
   white_balance_control_supported_ =
       IsControlSupported(kControlAutoWhiteBalance) &&
       IsControlSupported(kControlWhiteBalanceTemperature);
@@ -633,19 +654,30 @@
 }
 
 int V4L2CameraDevice::SetAutoFocus(bool enable) {
-  if (!autofocus_supported_ || enable == autofocus_on_)
+  if (!focus_auto_supported_) {
+    // Off mode is default supported
+    if (enable) {
+      LOGF(WARNING)
+          << "Setting auto focus while device doesn't support. Ignored";
+    }
     return 0;
-  int ret;
-  struct v4l2_control control;
-  control.id = V4L2_CID_FOCUS_AUTO;
-  control.value = enable ? 1 : 0;
-  ret = TEMP_FAILURE_RETRY(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
-  if (ret < 0) {
-    LOGF(WARNING) << "Failed to set V4L2_CID_FOCUS_AUTO";
-  } else {
-    autofocus_on_ = enable;
   }
-  return ret;
+
+  if (enable && control_values_.count(kControlFocusDistance)) {
+    control_values_.erase(kControlFocusDistance);
+  }
+
+  return SetControlValue(kControlFocusAuto, enable ? 1 : 0);
+}
+
+int V4L2CameraDevice::SetFocusDistance(int32_t distance) {
+  if (!focus_distance_supported_) {
+    LOGF(WARNING) << "Setting focus distance while devcie doesn't support. "
+                  << "Ignored.";
+    return 0;
+  }
+
+  return SetControlValue(kControlFocusDistance, distance);
 }
 
 int V4L2CameraDevice::SetExposureTimeHundredUs(uint32_t exposure_time) {
@@ -1281,19 +1313,21 @@
 }
 
 // static
-bool V4L2CameraDevice::IsAutoFocusSupported(const std::string& device_path) {
-  base::ScopedFD fd(RetryDeviceOpen(device_path, O_RDONLY));
-  if (!fd.is_valid()) {
-    PLOGF(ERROR) << "Failed to open " << device_path;
+bool V4L2CameraDevice::IsFocusDistanceSupported(
+    const std::string& device_path, ControlRange* focus_distance_range) {
+  DCHECK(focus_distance_range != nullptr);
+
+  if (!IsControlSupported(device_path, kControlFocusAuto))
+    return false;
+
+  ControlInfo info;
+  if (QueryControl(device_path, kControlFocusDistance, &info) != 0) {
     return false;
   }
-  struct v4l2_queryctrl query_ctrl;
-  query_ctrl.id = V4L2_CID_FOCUS_AUTO;
-  if (TEMP_FAILURE_RETRY(ioctl(fd.get(), VIDIOC_QUERYCTRL, &query_ctrl)) < 0) {
-    LOGF(WARNING) << "Failed to query V4L2_CID_FOCUS_AUTO";
-    return false;
-  }
-  return !(query_ctrl.flags & V4L2_CTRL_FLAG_DISABLED);
+
+  *focus_distance_range = info.range;
+
+  return true;
 }
 
 // static
diff --git a/camera/hal/usb/v4l2_camera_device.h b/camera/hal/usb/v4l2_camera_device.h
index 58a8765..89c6a13 100644
--- a/camera/hal/usb/v4l2_camera_device.h
+++ b/camera/hal/usb/v4l2_camera_device.h
@@ -39,6 +39,8 @@
 enum ControlType {
   kControlAutoWhiteBalance,
   kControlBrightness,
+  kControlFocusAuto,
+  kControlFocusDistance,
   kControlContrast,
   kControlExposureAuto,
   kControlExposureAutoPriority,  // 0 for constant frame rate
@@ -117,6 +119,9 @@
   // |-errno|.
   int SetAutoFocus(bool enable);
 
+  // Return 0 if focus distance is set successfully. Otherwise, return |-errno|.
+  int SetFocusDistance(int32_t distance);
+
   // Return 0 if device sets color tepmerature successfully. Otherwise, return
   // |-errno|. Set |color_temperature| to |kColorTemperatureAuto| means auto
   // white balance mode.
@@ -160,7 +165,10 @@
   static PowerLineFrequency GetPowerLineFrequency(
       const std::string& device_path);
 
-  static bool IsAutoFocusSupported(const std::string& device_path);
+  // If the device supports manual focus distance, returns the focus distance
+  // range to |focus_distance_range|.
+  static bool IsFocusDistanceSupported(const std::string& device_path,
+                                       ControlRange* focus_distance_range);
 
   // If the device supports manual exposure time, returns the exposure time
   // range to |exposure_time_range|.
@@ -239,8 +247,8 @@
   bool stream_on_;
 
   // AF state
-  bool autofocus_on_;
-  bool autofocus_supported_;
+  bool focus_auto_supported_;
+  bool focus_distance_supported_;
 
   bool white_balance_control_supported_;