Bug 793809 - Take FM radio initialization off main thread, r=jlebar
authorMichael Wu <mwu@mozilla.com>
Wed, 26 Sep 2012 15:35:40 -0400
changeset 108177 a25086637d1fcb604d2dbe6aebc189a657cf994b
parent 108176 1a989993c992618fe16f0339c37554ee68142075
child 108178 4a080f2c3091466eb606dbf546fd8b44e6dc0d56
push idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs793809
milestone18.0a1
Bug 793809 - Take FM radio initialization off main thread, r=jlebar
hal/gonk/GonkFMRadio.cpp
--- a/hal/gonk/GonkFMRadio.cpp
+++ b/hal/gonk/GonkFMRadio.cpp
@@ -28,114 +28,56 @@
 
 namespace mozilla {
 namespace hal_impl {
 
 uint32_t GetFMRadioFrequency();
 
 static int sRadioFD;
 static bool sRadioEnabled;
-static pthread_t sMonitorThread;
+static pthread_t sRadioThread;
+static hal::FMRadioSettings sRadioSettings;
+static int sTavaruaVersion;
 
 static int
 setControl(uint32_t id, int32_t value)
 {
   struct v4l2_control control;
   control.id = id;
   control.value = value;
   return ioctl(sRadioFD, VIDIOC_S_CTRL, &control);
 }
 
 class RadioUpdate : public nsRunnable {
   hal::FMRadioOperation mOp;
+  hal::FMRadioOperationStatus mStatus;
 public:
-  RadioUpdate(hal::FMRadioOperation op)
+  RadioUpdate(hal::FMRadioOperation op, hal::FMRadioOperationStatus status)
     : mOp(op)
+    , mStatus(status)
   {}
 
   NS_IMETHOD Run() {
     hal::FMRadioOperationInformation info;
     info.operation() = mOp;
-    info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
+    info.status() = mStatus;
     info.frequency() = GetFMRadioFrequency();
     hal::NotifyFMRadioStatus(info);
     return NS_OK;
   }
 };
 
-static void *
-pollTavaruaRadio(void *)
-{
-  uint8_t buf[128];
-  struct v4l2_buffer buffer = {0};
-  buffer.index = 1;
-  buffer.type = V4L2_BUF_TYPE_PRIVATE;
-  buffer.length = sizeof(buf);
-  buffer.m.userptr = (long unsigned int)buf;
-
-  while (sRadioEnabled) {
-    int rc = ioctl(sRadioFD, VIDIOC_DQBUF, &buffer);
-    if (rc)
-      return NULL;
-
-    for (unsigned int i = 0; i < buffer.bytesused; i++) {
-      switch (buf[i]) {
-      case TAVARUA_EVT_RADIO_READY:
-        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE));
-        break;
-      case TAVARUA_EVT_SEEK_COMPLETE:
-        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK));
-        break;
-      default:
-        break;
-      }
-    }
-  }
-
-  return NULL;
-}
-
-void
-EnableFMRadio(const hal::FMRadioSettings& aInfo)
+/* Runs on the radio thread */
+static void
+initTavaruaRadio(hal::FMRadioSettings &aInfo)
 {
-  if (sRadioEnabled) {
-    HAL_LOG(("Radio already enabled!"));
-    return;
-  }
-
-  mozilla::ScopedClose fd(open("/dev/radio0", O_RDWR));
-  if (fd < 0) {
-    HAL_LOG(("Unable to open radio device"));
-    return;
-  }
-
-  struct v4l2_capability cap;
-  int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
-  if (rc < 0) {
-    HAL_LOG(("Unable to query radio device"));
-    return;
-  }
-
-  HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card));
-
-  if (!(cap.capabilities & V4L2_CAP_RADIO)) {
-    HAL_LOG(("/dev/radio0 isn't a radio"));
-    return;
-  }
-
-  if (!(cap.capabilities & V4L2_CAP_TUNER)) {
-    HAL_LOG(("/dev/radio0 doesn't support the tuner interface"));
-    return;
-  }
-  sRadioFD = fd;
-
-  // Tavarua specific start
+  mozilla::ScopedClose fd(sRadioFD);
   char command[64];
-  snprintf(command, sizeof(command), "/system/bin/fm_qsoc_patches %d 0", cap.version);
-  rc = system(command);
+  snprintf(command, sizeof(command), "/system/bin/fm_qsoc_patches %d 0", sTavaruaVersion);
+  int rc = system(command);
   if (rc) {
     HAL_LOG(("Unable to initialize radio"));
     return;
   }
 
   rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_RECV);
   if (rc < 0) {
     HAL_LOG(("Unable to turn on radio |%s|", strerror(errno)));
@@ -201,39 +143,119 @@ EnableFMRadio(const hal::FMRadioSettings
   }
 
   rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH, FM_DIGITAL_PATH);
   if (rc < 0) {
     HAL_LOG(("Unable to set audio path"));
     return;
   }
 
-  pthread_create(&sMonitorThread, NULL, pollTavaruaRadio, NULL);
-  // Tavarua specific end
-
   fd.forget();
   sRadioEnabled = true;
 }
 
+/* Runs on the radio thread */
+static void *
+runTavaruaRadio(void *)
+{
+  initTavaruaRadio(sRadioSettings);
+  if (!sRadioEnabled) {
+    NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,
+                                            hal::FM_RADIO_OPERATION_STATUS_FAIL));
+    return NULL;
+  }
+
+  uint8_t buf[128];
+  struct v4l2_buffer buffer = {0};
+  buffer.index = 1;
+  buffer.type = V4L2_BUF_TYPE_PRIVATE;
+  buffer.length = sizeof(buf);
+  buffer.m.userptr = (long unsigned int)buf;
+
+  while (sRadioEnabled) {
+    if (ioctl(sRadioFD, VIDIOC_DQBUF, &buffer) < 0)
+      break;
+
+    for (unsigned int i = 0; i < buffer.bytesused; i++) {
+      switch (buf[i]) {
+      case TAVARUA_EVT_RADIO_READY:
+        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,                                                 hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
+        break;
+      case TAVARUA_EVT_SEEK_COMPLETE:
+        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK,
+                                                hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
+        break;
+      default:
+        break;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/* This runs on the main thread but most of the
+ * initialization is pushed to the radio thread. */
+void
+EnableFMRadio(const hal::FMRadioSettings& aInfo)
+{
+  if (sRadioEnabled) {
+    HAL_LOG(("Radio already enabled!"));
+    return;
+  }
+
+  mozilla::ScopedClose fd(open("/dev/radio0", O_RDWR));
+  if (fd < 0) {
+    HAL_LOG(("Unable to open radio device"));
+    return;
+  }
+
+  struct v4l2_capability cap;
+  int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+  if (rc < 0) {
+    HAL_LOG(("Unable to query radio device"));
+    return;
+  }
+
+  HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card));
+
+  if (!(cap.capabilities & V4L2_CAP_RADIO)) {
+    HAL_LOG(("/dev/radio0 isn't a radio"));
+    return;
+  }
+
+  if (!(cap.capabilities & V4L2_CAP_TUNER)) {
+    HAL_LOG(("/dev/radio0 doesn't support the tuner interface"));
+    return;
+  }
+  sRadioFD = fd.forget();
+  sRadioSettings = aInfo;
+
+  // Tavarua specific start
+  sTavaruaVersion = cap.version;
+  pthread_create(&sRadioThread, NULL, runTavaruaRadio, NULL);
+  // Tavarua specific end
+}
+
 void
 DisableFMRadio()
 {
   if (!sRadioEnabled)
     return;
 
   sRadioEnabled = false;
 
   // Tavarua specific start
   int rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_OFF);
   if (rc < 0) {
     HAL_LOG(("Unable to turn off radio"));
   }
   // Tavarua specific end
 
-  pthread_join(sMonitorThread, NULL);
+  pthread_join(sRadioThread, NULL);
 
   close(sRadioFD);
 
   hal::FMRadioOperationInformation info;
   info.operation() = hal::FM_RADIO_OPERATION_DISABLE;
   info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
   hal::NotifyFMRadioStatus(info);
 }