/* Runs on the radio thread */
static void
initTavaruaRadio(hal::FMRadioSettings &aInfo)
{
  mozilla::ScopedClose fd(sRadioFD);
  char version[64];
  int rc;
  snprintf(version, sizeof(version), "%d", sTavaruaVersion);
  property_set("hw.fm.version", version);

  /* Set the mode for soc downloader */
  property_set("hw.fm.mode", "normal");
  /* start fm_dl service */
  property_set("ctl.start", "fm_dl");

  /*
   * Fix bug 800263. Wait until the FM radio chips initialization is done
   * then set other properties, or the system will hang and reboot. This
   * work around is from codeaurora
   * (git://codeaurora.org/platform/frameworks/base.git).
   */
  for (int i = 0; i < 4; ++i) {
    sleep(1);
    char value[PROPERTY_VALUE_MAX];
    property_get("hw.fm.init", value, "0");
    if (!strcmp(value, "1")) {
      break;
    }
  }

  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_RECV);
  if (rc < 0) {
    HAL_LOG(("Unable to turn on radio |%s|", strerror(errno)));
    return;
  }

  int preEmphasis = aInfo.preEmphasis() <= 50;
  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_EMPHASIS, preEmphasis);
  if (rc) {
    HAL_LOG(("Unable to configure preemphasis"));
    return;
  }

  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_RDS_STD, 0);
  if (rc) {
    HAL_LOG(("Unable to configure RDS"));
    return;
  }

  int spacing;
  switch (aInfo.spaceType()) {
  case 50:
    spacing = FM_CH_SPACE_50KHZ;
    break;
  case 100:
    spacing = FM_CH_SPACE_100KHZ;
    break;
  case 200:
    spacing = FM_CH_SPACE_200KHZ;
    break;
  default:
    HAL_LOG(("Unsupported space value - %d", aInfo.spaceType()));
    return;
  }

  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SPACING, spacing);
  if (rc) {
    HAL_LOG(("Unable to configure spacing"));
    return;
  }

  /*
   * Frequency conversions
   *
   * HAL uses units of 1k for frequencies
   * V4L2 uses units of 62.5kHz
   * Multiplying by (10000 / 625) converts from HAL units to V4L2.
   */

  struct v4l2_tuner tuner = {0};
  tuner.rangelow = (aInfo.lowerLimit() * 10000) / 625;
  tuner.rangehigh = (aInfo.upperLimit() * 10000) / 625;
  rc = ioctl(fd, VIDIOC_S_TUNER, &tuner);
  if (rc < 0) {
    HAL_LOG(("Unable to adjust band limits"));
    return;
  }

  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_REGION, TAVARUA_REGION_OTHER);
  if (rc < 0) {
    HAL_LOG(("Unable to configure region"));
    return;
  }

  // Some devices do not support analog audio routing. This should be
  // indicated by the 'ro.moz.fm.noAnalog' property at build time.
  char propval[PROPERTY_VALUE_MAX];
  property_get("ro.moz.fm.noAnalog", propval, "");
  bool noAnalog = !strcmp(propval, "true");

  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,
                  noAnalog ? FM_DIGITAL_PATH : FM_ANALOG_PATH);
  if (rc < 0) {
    HAL_LOG(("Unable to set audio path"));
    return;
  }

  if (!noAnalog) {
    /* Set the mode for soc downloader */
    property_set("hw.fm.mode", "config_dac");
    /* Use analog mode FM */
    property_set("hw.fm.isAnalog", "true");
    /* start fm_dl service */
    property_set("ctl.start", "fm_dl");

    for (int i = 0; i < 4; ++i) {
      sleep(1);
      char value[PROPERTY_VALUE_MAX];
      property_get("hw.fm.init", value, "0");
      if (!strcmp(value, "1")) {
        break;
      }
    }
  }

  fd.forget();
  sRadioEnabled = true;
}
Example #2
0
/* 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;
  }

  hal::FMRadioOperationInformation info;
  info.operation() = hal::FM_RADIO_OPERATION_ENABLE;
  info.status() = hal::FM_RADIO_OPERATION_STATUS_FAIL;

  mozilla::ScopedClose fd(open("/dev/radio0", O_RDWR));
  if (fd < 0) {
    HAL_LOG("Unable to open radio device");
    hal::NotifyFMRadioStatus(info);
    return;
  }

  struct v4l2_capability cap;
  int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
  if (rc < 0) {
    HAL_LOG("Unable to query radio device");
    hal::NotifyFMRadioStatus(info);
    return;
  }

  sMsmFMMode = !strcmp((char *)cap.driver, "radio-tavarua") ||
      !strcmp((char *)cap.driver, "radio-iris");
  HAL_LOG("Radio: %s (%s)\n", cap.driver, cap.card);

  if (!(cap.capabilities & V4L2_CAP_RADIO)) {
    HAL_LOG("/dev/radio0 isn't a radio");
    hal::NotifyFMRadioStatus(info);
    return;
  }

  if (!(cap.capabilities & V4L2_CAP_TUNER)) {
    HAL_LOG("/dev/radio0 doesn't support the tuner interface");
    hal::NotifyFMRadioStatus(info);
    return;
  }

  sRDSSupported = cap.capabilities & V4L2_CAP_RDS_CAPTURE;
  sRadioSettings = aInfo;

  if (sMsmFMMode) {
    sRadioFD = fd.forget();
    sMsmFMVersion = cap.version;
    if (pthread_create(&sRadioThread, nullptr, runMsmFMRadio, nullptr)) {
      HAL_LOG("Couldn't create radio thread");
      hal::NotifyFMRadioStatus(info);
    }
    return;
  }

  struct v4l2_tuner tuner = {0};
  tuner.type = V4L2_TUNER_RADIO;
  tuner.rangelow = (aInfo.lowerLimit() * 10000) / 625;
  tuner.rangehigh = (aInfo.upperLimit() * 10000) / 625;
  tuner.audmode = V4L2_TUNER_MODE_STEREO;
  rc = ioctl(fd, VIDIOC_S_TUNER, &tuner);
  if (rc < 0) {
    HAL_LOG("Unable to adjust band limits");
  }

  int emphasis;
  switch (aInfo.preEmphasis()) {
  case 0:
    emphasis = V4L2_DEEMPHASIS_DISABLED;
    break;
  case 50:
    emphasis = V4L2_DEEMPHASIS_50_uS;
    break;
  case 75:
    emphasis = V4L2_DEEMPHASIS_75_uS;
    break;
  default:
    MOZ_CRASH("Invalid preemphasis setting");
    break;
  }
  rc = setControl(V4L2_CID_TUNE_DEEMPHASIS, emphasis);
  if (rc < 0) {
    HAL_LOG("Unable to configure deemphasis");
  }

  sRadioFD = fd.forget();
  sRadioEnabled = true;

  info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
  hal::NotifyFMRadioStatus(info);
}