Merge changes I8648956f,I49aa5b08

* changes:
  Update WPILib, roborio compilers, and CTRE Phoenix libraries
  Squashed 'third_party/allwpilib/' changes from e4b91005cf..83f1860047
diff --git a/frc971/rockpi/.config b/frc971/rockpi/.config
index 5fe3348..636a585 100644
--- a/frc971/rockpi/.config
+++ b/frc971/rockpi/.config
@@ -3366,7 +3366,7 @@
 CONFIG_VIDEO_IMX219=m
 # CONFIG_VIDEO_IMX258 is not set
 # CONFIG_VIDEO_IMX274 is not set
-# CONFIG_VIDEO_IMX290 is not set
+CONFIG_VIDEO_IMX290=m
 # CONFIG_VIDEO_IMX319 is not set
 # CONFIG_VIDEO_IMX334 is not set
 # CONFIG_VIDEO_IMX335 is not set
diff --git a/frc971/vision/media_device.cc b/frc971/vision/media_device.cc
index 1813b6f..4c227a8 100644
--- a/frc971/vision/media_device.cc
+++ b/frc971/vision/media_device.cc
@@ -365,8 +365,12 @@
 
 Link *MediaDevice::FindLink(std::string_view source, int source_pad_index,
                             std::string_view sink, int sink_pad_index) {
-  Entity *source_entity = CHECK_NOTNULL(FindEntity(source));
-  Entity *sink_entity = CHECK_NOTNULL(FindEntity(sink));
+  Entity *source_entity = FindEntity(source);
+  Entity *sink_entity = FindEntity(sink);
+
+  CHECK(source_entity != nullptr) << ": Failed to find source " << source;
+  CHECK(sink_entity != nullptr) << ": Failed to find sink " << sink;
+
   Pad *source_pad = source_entity->pads()[source_pad_index];
   Pad *sink_pad = sink_entity->pads()[sink_pad_index];
   for (size_t i = 0; i < source_pad->links_size(); ++i) {
diff --git a/frc971/vision/v4l2_reader.cc b/frc971/vision/v4l2_reader.cc
index e549ae5..9b4863e 100644
--- a/frc971/vision/v4l2_reader.cc
+++ b/frc971/vision/v4l2_reader.cc
@@ -92,7 +92,7 @@
   PCHECK(Ioctl(VIDIOC_STREAMON, &type) == 0);
 }
 
-bool V4L2ReaderBase::ReadLatestImage() {
+void V4L2ReaderBase::MaybeEnqueue() {
   // First, enqueue any old buffer we already have. This is the one which
   // may have been sent.
   if (saved_buffer_) {
@@ -100,6 +100,11 @@
     saved_buffer_.Clear();
   }
   ftrace_.FormatMessage("Enqueued previous buffer %d", saved_buffer_.index);
+}
+
+bool V4L2ReaderBase::ReadLatestImage() {
+  MaybeEnqueue();
+
   while (true) {
     const BufferInfo previous_buffer = saved_buffer_;
     saved_buffer_ = DequeueBuffer();
@@ -303,8 +308,14 @@
 
 RockchipV4L2Reader::RockchipV4L2Reader(aos::EventLoop *event_loop,
                                        aos::internal::EPoll *epoll,
-                                       const std::string &device_name)
-    : V4L2ReaderBase(event_loop, device_name), epoll_(epoll) {
+                                       const std::string &device_name,
+                                       const std::string &image_sensor_subdev)
+    : V4L2ReaderBase(event_loop, device_name),
+      epoll_(epoll),
+      image_sensor_fd_(open(image_sensor_subdev.c_str(), O_RDWR | O_NONBLOCK)) {
+  PCHECK(image_sensor_fd_.get() != -1)
+      << " Failed to open device " << device_name;
+
   StreamOn();
   epoll_->OnReadable(fd().get(), [this]() { OnImageReady(); });
 }
@@ -317,5 +328,50 @@
   SendLatestImage();
 }
 
+int RockchipV4L2Reader::ImageSensorIoctl(unsigned long number, void *arg) {
+  return ioctl(image_sensor_fd_.get(), number, arg);
+}
+
+void RockchipV4L2Reader::SetExposure(size_t duration) {
+  v4l2_control exposure_control;
+  exposure_control.id = V4L2_CID_EXPOSURE;
+  exposure_control.value = static_cast<int>(duration);
+  PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &exposure_control) == 0);
+}
+
+void RockchipV4L2Reader::SetGain(size_t gain) {
+  v4l2_control gain_control;
+  gain_control.id = V4L2_CID_GAIN;
+  gain_control.value = static_cast<int>(gain);
+  PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &gain_control) == 0);
+}
+
+void RockchipV4L2Reader::SetGainExt(size_t gain) {
+  struct v4l2_ext_controls controls;
+  memset(&controls, 0, sizeof(controls));
+  struct v4l2_ext_control control[1];
+  memset(&control, 0, sizeof(control));
+
+  controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
+  controls.count = 1;
+  controls.controls = control;
+  control[0].id = V4L2_CID_ANALOGUE_GAIN;
+  control[0].value = gain;
+
+  PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
+}
+
+void RockchipV4L2Reader::SetBlanking(size_t hblank, size_t vblank) {
+  v4l2_control hblank_control;
+  hblank_control.id = V4L2_CID_HBLANK;
+  hblank_control.value = static_cast<int>(hblank);
+  PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &hblank_control) == 0);
+
+  v4l2_control vblank_control;
+  vblank_control.id = V4L2_CID_VBLANK;
+  vblank_control.value = static_cast<int>(vblank);
+  PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &vblank_control) == 0);
+}
+
 }  // namespace vision
 }  // namespace frc971
diff --git a/frc971/vision/v4l2_reader.h b/frc971/vision/v4l2_reader.h
index 334ad81..182160d 100644
--- a/frc971/vision/v4l2_reader.h
+++ b/frc971/vision/v4l2_reader.h
@@ -31,6 +31,8 @@
   // until this method is called again.
   bool ReadLatestImage();
 
+  void MaybeEnqueue();
+
   // Sends the latest image.
   //
   // ReadLatestImage() must have returned a non-empty span the last time it was
@@ -46,7 +48,7 @@
 
   // Sets the exposure duration of the camera. duration is the number of 100
   // microsecond units.
-  void SetExposure(size_t duration);
+  virtual void SetExposure(size_t duration);
 
   // Switches from manual to auto exposure.
   void UseAutoExposure();
@@ -143,12 +145,24 @@
 class RockchipV4L2Reader : public V4L2ReaderBase {
  public:
   RockchipV4L2Reader(aos::EventLoop *event_loop, aos::internal::EPoll *epoll,
-                     const std::string &device_name);
+                     const std::string &device_name,
+                     const std::string &image_sensor_subdev);
+
+  void SetExposure(size_t duration) override;
+
+  void SetGain(size_t gain);
+  void SetGainExt(size_t gain);
+
+  void SetBlanking(size_t hblank, size_t vblank);
 
  private:
   void OnImageReady();
 
+  int ImageSensorIoctl(unsigned long number, void *arg);
+
   aos::internal::EPoll *epoll_;
+
+  aos::ScopedFD image_sensor_fd_;
 };
 
 }  // namespace vision
diff --git a/y2023/vision/camera_reader.cc b/y2023/vision/camera_reader.cc
index 7004225..560e9ee 100644
--- a/y2023/vision/camera_reader.cc
+++ b/y2023/vision/camera_reader.cc
@@ -7,6 +7,7 @@
 #include "frc971/vision/v4l2_reader.h"
 
 DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
+DEFINE_bool(lowlight_camera, false, "Switch to use imx462 image sensor.");
 
 namespace y2023 {
 namespace vision {
@@ -21,10 +22,59 @@
     media_device->Log();
   }
 
+  int width = 1296;
+  int height = 972;
+  int color_format = MEDIA_BUS_FMT_SBGGR10_1X10;
+  std::string camera_device_string = "ov5647 4-0036";
+  if (FLAGS_lowlight_camera) {
+    width = 1920;
+    height = 1080;
+    color_format = MEDIA_BUS_FMT_SRGGB10_1X10;
+    camera_device_string = "imx290 4-0036";
+  }
+
   media_device->Reset();
 
+  Entity *camera = media_device->FindEntity(camera_device_string);
+  camera->pads()[0]->SetSubdevFormat(width, height, color_format);
+
+  Entity *rkisp1_csi = media_device->FindEntity("rkisp1_csi");
+  rkisp1_csi->pads()[0]->SetSubdevFormat(width, height, color_format);
+  rkisp1_csi->pads()[1]->SetSubdevFormat(width, height, color_format);
+
+  // TODO(austin): Should we set this on the link?
+  Entity *rkisp1_isp = media_device->FindEntity("rkisp1_isp");
+  rkisp1_isp->pads(0)->SetSubdevFormat(width, height, color_format);
+  rkisp1_isp->pads(0)->SetSubdevCrop(width, height);
+
+  rkisp1_isp->pads(2)->SetSubdevFormat(width, height, MEDIA_BUS_FMT_YUYV8_2X8);
+  rkisp1_isp->pads(2)->SetSubdevCrop(width, height);
+
+  Entity *rkisp1_resizer_selfpath =
+      media_device->FindEntity("rkisp1_resizer_selfpath");
+  rkisp1_resizer_selfpath->pads(0)->SetSubdevFormat(width, height,
+                                                    MEDIA_BUS_FMT_YUYV8_2X8);
+  rkisp1_resizer_selfpath->pads(1)->SetSubdevFormat(width, height,
+                                                    MEDIA_BUS_FMT_YUYV8_2X8);
+  rkisp1_resizer_selfpath->pads(0)->SetSubdevCrop(width, height);
+
+  Entity *rkisp1_resizer_mainpath =
+      media_device->FindEntity("rkisp1_resizer_mainpath");
+  rkisp1_resizer_mainpath->pads(0)->SetSubdevFormat(width, height,
+                                                    MEDIA_BUS_FMT_YUYV8_2X8);
+
+  rkisp1_resizer_mainpath->pads(0)->SetSubdevCrop(width, height);
+  rkisp1_resizer_mainpath->pads(1)->SetSubdevFormat(width / 2, height / 2,
+                                                    MEDIA_BUS_FMT_YUYV8_2X8);
+
+  Entity *rkisp1_mainpath = media_device->FindEntity("rkisp1_mainpath");
+  rkisp1_mainpath->SetFormat(width / 2, height / 2, V4L2_PIX_FMT_YUV422P);
+
+  Entity *rkisp1_selfpath = media_device->FindEntity("rkisp1_selfpath");
+  rkisp1_selfpath->SetFormat(width, height, V4L2_PIX_FMT_YUYV);
+
   media_device->Enable(
-      media_device->FindLink("ov5647 4-0036", 0, "rkisp1_csi", 0));
+      media_device->FindLink(camera_device_string, 0, "rkisp1_csi", 0));
   media_device->Enable(
       media_device->FindLink("rkisp1_csi", 1, "rkisp1_isp", 0));
   media_device->Enable(
@@ -32,45 +82,6 @@
   media_device->Enable(
       media_device->FindLink("rkisp1_isp", 2, "rkisp1_resizer_mainpath", 0));
 
-  media_device->FindEntity("ov5647 4-0036")
-      ->pads()[0]
-      ->SetSubdevFormat(1296, 972, MEDIA_BUS_FMT_SBGGR10_1X10);
-
-  Entity *rkisp1_csi = media_device->FindEntity("rkisp1_csi");
-  rkisp1_csi->pads()[0]->SetSubdevFormat(1296, 972, MEDIA_BUS_FMT_SBGGR10_1X10);
-  rkisp1_csi->pads()[1]->SetSubdevFormat(1296, 972, MEDIA_BUS_FMT_SBGGR10_1X10);
-
-  // TODO(austin): Should we set this on the link?
-  // TODO(austin): Need to update crop too.
-  Entity *rkisp1_isp = media_device->FindEntity("rkisp1_isp");
-  rkisp1_isp->pads(0)->SetSubdevCrop(1296, 972);
-  rkisp1_isp->pads(0)->SetSubdevFormat(1296, 972, MEDIA_BUS_FMT_SBGGR10_1X10);
-
-  rkisp1_isp->pads(2)->SetSubdevCrop(1296, 972);
-  rkisp1_isp->pads(2)->SetSubdevFormat(1296, 972, MEDIA_BUS_FMT_YUYV8_2X8);
-
-  Entity *rkisp1_resizer_selfpath =
-      media_device->FindEntity("rkisp1_resizer_selfpath");
-  rkisp1_resizer_selfpath->pads(0)->SetSubdevFormat(1296, 972,
-                                                    MEDIA_BUS_FMT_YUYV8_2X8);
-  rkisp1_resizer_selfpath->pads(1)->SetSubdevFormat(1296, 972,
-                                                    MEDIA_BUS_FMT_YUYV8_2X8);
-  rkisp1_resizer_selfpath->pads(0)->SetSubdevCrop(1296, 972);
-
-  Entity *rkisp1_resizer_mainpath =
-      media_device->FindEntity("rkisp1_resizer_mainpath");
-  rkisp1_resizer_mainpath->pads(0)->SetSubdevFormat(1296, 972,
-                                                    MEDIA_BUS_FMT_YUYV8_2X8);
-  rkisp1_resizer_mainpath->pads(1)->SetSubdevFormat(1296 / 2, 972 / 2,
-                                                    MEDIA_BUS_FMT_YUYV8_2X8);
-  rkisp1_resizer_mainpath->pads(0)->SetSubdevCrop(1296 / 2, 972 / 2);
-
-  Entity *rkisp1_mainpath = media_device->FindEntity("rkisp1_mainpath");
-  rkisp1_mainpath->SetFormat(1296 / 2, 972 / 2, V4L2_PIX_FMT_YUV422P);
-
-  Entity *rkisp1_selfpath = media_device->FindEntity("rkisp1_selfpath");
-  rkisp1_selfpath->SetFormat(1296, 972, V4L2_PIX_FMT_YUYV);
-
   aos::FlatbufferDetachedBuffer<aos::Configuration> config =
       aos::configuration::ReadConfig(FLAGS_config);
 
@@ -80,22 +91,16 @@
   event_loop.SetRuntimeAffinity(aos::MakeCpusetFromCpus({2}));
 
   RockchipV4L2Reader v4l2_reader(&event_loop, event_loop.epoll(),
-                                 rkisp1_selfpath->device());
+                                 rkisp1_selfpath->device(), camera->device());
 
-  // TODO(austin): Figure out exposure and stuff.
-  /*
-  const uint32_t exposure =
-      (FLAGS_use_outdoors ? FLAGS_outdoors_exposure : FLAGS_exposure);
-  if (exposure > 0) {
-    LOG(INFO) << "Setting camera to Manual Exposure mode with exposure = "
-              << exposure << " or " << static_cast<double>(exposure) / 10.0
-              << " ms";
-    v4l2_reader.SetExposure(exposure);
+  if (FLAGS_lowlight_camera) {
+    v4l2_reader.SetGain(72);
+    v4l2_reader.SetExposure(30);
+    v4l2_reader.SetBlanking(2480, 45);
   } else {
-    LOG(INFO) << "Setting camera to use Auto Exposure";
-    v4l2_reader.UseAutoExposure();
+    v4l2_reader.SetGainExt(1000);
+    v4l2_reader.SetExposure(1000);
   }
-  */
 
   event_loop.Run();
 }