/* 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; }
/* 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); }