hal/gonk/GonkFMRadio.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
michael@0 2 *
michael@0 3 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 4 * you may not use this file except in compliance with the License.
michael@0 5 * You may obtain a copy of the License at
michael@0 6 *
michael@0 7 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 8 *
michael@0 9 * Unless required by applicable law or agreed to in writing, software
michael@0 10 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 12 * See the License for the specific language governing permissions and
michael@0 13 * limitations under the License.
michael@0 14 */
michael@0 15
michael@0 16 #include "Hal.h"
michael@0 17 #include "tavarua.h"
michael@0 18 #include "nsThreadUtils.h"
michael@0 19 #include "mozilla/FileUtils.h"
michael@0 20
michael@0 21 #include <cutils/properties.h>
michael@0 22 #include <errno.h>
michael@0 23 #include <fcntl.h>
michael@0 24 #include <linux/videodev2.h>
michael@0 25 #include <stdio.h>
michael@0 26 #include <stdlib.h>
michael@0 27 #include <sys/stat.h>
michael@0 28 #include <sys/types.h>
michael@0 29
michael@0 30 namespace mozilla {
michael@0 31 namespace hal_impl {
michael@0 32
michael@0 33 uint32_t GetFMRadioFrequency();
michael@0 34
michael@0 35 static int sRadioFD;
michael@0 36 static bool sRadioEnabled;
michael@0 37 static pthread_t sRadioThread;
michael@0 38 static hal::FMRadioSettings sRadioSettings;
michael@0 39 static int sMsmFMVersion;
michael@0 40 static bool sMsmFMMode;
michael@0 41
michael@0 42 static int
michael@0 43 setControl(uint32_t id, int32_t value)
michael@0 44 {
michael@0 45 struct v4l2_control control;
michael@0 46 control.id = id;
michael@0 47 control.value = value;
michael@0 48 return ioctl(sRadioFD, VIDIOC_S_CTRL, &control);
michael@0 49 }
michael@0 50
michael@0 51 class RadioUpdate : public nsRunnable {
michael@0 52 hal::FMRadioOperation mOp;
michael@0 53 hal::FMRadioOperationStatus mStatus;
michael@0 54 public:
michael@0 55 RadioUpdate(hal::FMRadioOperation op, hal::FMRadioOperationStatus status)
michael@0 56 : mOp(op)
michael@0 57 , mStatus(status)
michael@0 58 {}
michael@0 59
michael@0 60 NS_IMETHOD Run() {
michael@0 61 hal::FMRadioOperationInformation info;
michael@0 62 info.operation() = mOp;
michael@0 63 info.status() = mStatus;
michael@0 64 info.frequency() = GetFMRadioFrequency();
michael@0 65 hal::NotifyFMRadioStatus(info);
michael@0 66 return NS_OK;
michael@0 67 }
michael@0 68 };
michael@0 69
michael@0 70 /* Runs on the radio thread */
michael@0 71 static void
michael@0 72 initMsmFMRadio(hal::FMRadioSettings &aInfo)
michael@0 73 {
michael@0 74 mozilla::ScopedClose fd(sRadioFD);
michael@0 75 char version[64];
michael@0 76 int rc;
michael@0 77 snprintf(version, sizeof(version), "%d", sMsmFMVersion);
michael@0 78 property_set("hw.fm.version", version);
michael@0 79
michael@0 80 /* Set the mode for soc downloader */
michael@0 81 property_set("hw.fm.mode", "normal");
michael@0 82 /* start fm_dl service */
michael@0 83 property_set("ctl.start", "fm_dl");
michael@0 84
michael@0 85 /*
michael@0 86 * Fix bug 800263. Wait until the FM radio chips initialization is done
michael@0 87 * then set other properties, or the system will hang and reboot. This
michael@0 88 * work around is from codeaurora
michael@0 89 * (git://codeaurora.org/platform/frameworks/base.git).
michael@0 90 */
michael@0 91 for (int i = 0; i < 4; ++i) {
michael@0 92 sleep(1);
michael@0 93 char value[PROPERTY_VALUE_MAX];
michael@0 94 property_get("hw.fm.init", value, "0");
michael@0 95 if (!strcmp(value, "1")) {
michael@0 96 break;
michael@0 97 }
michael@0 98 }
michael@0 99
michael@0 100 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_RECV);
michael@0 101 if (rc < 0) {
michael@0 102 HAL_LOG(("Unable to turn on radio |%s|", strerror(errno)));
michael@0 103 return;
michael@0 104 }
michael@0 105
michael@0 106 int preEmphasis = aInfo.preEmphasis() <= 50;
michael@0 107 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_EMPHASIS, preEmphasis);
michael@0 108 if (rc) {
michael@0 109 HAL_LOG(("Unable to configure preemphasis"));
michael@0 110 return;
michael@0 111 }
michael@0 112
michael@0 113 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_RDS_STD, 0);
michael@0 114 if (rc) {
michael@0 115 HAL_LOG(("Unable to configure RDS"));
michael@0 116 return;
michael@0 117 }
michael@0 118
michael@0 119 int spacing;
michael@0 120 switch (aInfo.spaceType()) {
michael@0 121 case 50:
michael@0 122 spacing = FM_CH_SPACE_50KHZ;
michael@0 123 break;
michael@0 124 case 100:
michael@0 125 spacing = FM_CH_SPACE_100KHZ;
michael@0 126 break;
michael@0 127 case 200:
michael@0 128 spacing = FM_CH_SPACE_200KHZ;
michael@0 129 break;
michael@0 130 default:
michael@0 131 HAL_LOG(("Unsupported space value - %d", aInfo.spaceType()));
michael@0 132 return;
michael@0 133 }
michael@0 134
michael@0 135 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SPACING, spacing);
michael@0 136 if (rc) {
michael@0 137 HAL_LOG(("Unable to configure spacing"));
michael@0 138 return;
michael@0 139 }
michael@0 140
michael@0 141 /*
michael@0 142 * Frequency conversions
michael@0 143 *
michael@0 144 * HAL uses units of 1k for frequencies
michael@0 145 * V4L2 uses units of 62.5kHz
michael@0 146 * Multiplying by (10000 / 625) converts from HAL units to V4L2.
michael@0 147 */
michael@0 148
michael@0 149 struct v4l2_tuner tuner = {0};
michael@0 150 tuner.rangelow = (aInfo.lowerLimit() * 10000) / 625;
michael@0 151 tuner.rangehigh = (aInfo.upperLimit() * 10000) / 625;
michael@0 152 tuner.audmode = V4L2_TUNER_MODE_STEREO;
michael@0 153 rc = ioctl(fd, VIDIOC_S_TUNER, &tuner);
michael@0 154 if (rc < 0) {
michael@0 155 HAL_LOG(("Unable to adjust band limits"));
michael@0 156 return;
michael@0 157 }
michael@0 158
michael@0 159 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_REGION, TAVARUA_REGION_OTHER);
michael@0 160 if (rc < 0) {
michael@0 161 HAL_LOG(("Unable to configure region"));
michael@0 162 return;
michael@0 163 }
michael@0 164
michael@0 165 // Some devices do not support analog audio routing. This should be
michael@0 166 // indicated by the 'ro.moz.fm.noAnalog' property at build time.
michael@0 167 char propval[PROPERTY_VALUE_MAX];
michael@0 168 property_get("ro.moz.fm.noAnalog", propval, "");
michael@0 169 bool noAnalog = !strcmp(propval, "true");
michael@0 170
michael@0 171 rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,
michael@0 172 noAnalog ? FM_DIGITAL_PATH : FM_ANALOG_PATH);
michael@0 173 if (rc < 0) {
michael@0 174 HAL_LOG(("Unable to set audio path"));
michael@0 175 return;
michael@0 176 }
michael@0 177
michael@0 178 if (!noAnalog) {
michael@0 179 /* Set the mode for soc downloader */
michael@0 180 property_set("hw.fm.mode", "config_dac");
michael@0 181 /* Use analog mode FM */
michael@0 182 property_set("hw.fm.isAnalog", "true");
michael@0 183 /* start fm_dl service */
michael@0 184 property_set("ctl.start", "fm_dl");
michael@0 185
michael@0 186 for (int i = 0; i < 4; ++i) {
michael@0 187 sleep(1);
michael@0 188 char value[PROPERTY_VALUE_MAX];
michael@0 189 property_get("hw.fm.init", value, "0");
michael@0 190 if (!strcmp(value, "1")) {
michael@0 191 break;
michael@0 192 }
michael@0 193 }
michael@0 194 }
michael@0 195
michael@0 196 fd.forget();
michael@0 197 sRadioEnabled = true;
michael@0 198 }
michael@0 199
michael@0 200 /* Runs on the radio thread */
michael@0 201 static void *
michael@0 202 runMsmFMRadio(void *)
michael@0 203 {
michael@0 204 initMsmFMRadio(sRadioSettings);
michael@0 205 if (!sRadioEnabled) {
michael@0 206 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,
michael@0 207 hal::FM_RADIO_OPERATION_STATUS_FAIL));
michael@0 208 return nullptr;
michael@0 209 }
michael@0 210
michael@0 211 uint8_t buf[128];
michael@0 212 struct v4l2_buffer buffer = {0};
michael@0 213 buffer.index = 1;
michael@0 214 buffer.type = V4L2_BUF_TYPE_PRIVATE;
michael@0 215 buffer.length = sizeof(buf);
michael@0 216 buffer.m.userptr = (long unsigned int)buf;
michael@0 217
michael@0 218 while (sRadioEnabled) {
michael@0 219 if (ioctl(sRadioFD, VIDIOC_DQBUF, &buffer) < 0) {
michael@0 220 if (errno == EINTR)
michael@0 221 continue;
michael@0 222 break;
michael@0 223 }
michael@0 224
michael@0 225 /* The tavarua driver reports a number of things asynchronously.
michael@0 226 * In those cases, the status update comes from this thread. */
michael@0 227 for (unsigned int i = 0; i < buffer.bytesused; i++) {
michael@0 228 switch (buf[i]) {
michael@0 229 case TAVARUA_EVT_RADIO_READY:
michael@0 230 // The driver sends RADIO_READY both when we turn the radio on and when we turn
michael@0 231 // the radio off.
michael@0 232 if (sRadioEnabled) {
michael@0 233 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,
michael@0 234 hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
michael@0 235 }
michael@0 236 break;
michael@0 237
michael@0 238 case TAVARUA_EVT_SEEK_COMPLETE:
michael@0 239 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK,
michael@0 240 hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
michael@0 241 break;
michael@0 242 case TAVARUA_EVT_TUNE_SUCC:
michael@0 243 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_TUNE,
michael@0 244 hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
michael@0 245 break;
michael@0 246 default:
michael@0 247 break;
michael@0 248 }
michael@0 249 }
michael@0 250 }
michael@0 251
michael@0 252 return nullptr;
michael@0 253 }
michael@0 254
michael@0 255 /* This runs on the main thread but most of the
michael@0 256 * initialization is pushed to the radio thread. */
michael@0 257 void
michael@0 258 EnableFMRadio(const hal::FMRadioSettings& aInfo)
michael@0 259 {
michael@0 260 if (sRadioEnabled) {
michael@0 261 HAL_LOG(("Radio already enabled!"));
michael@0 262 return;
michael@0 263 }
michael@0 264
michael@0 265 mozilla::ScopedClose fd(open("/dev/radio0", O_RDWR));
michael@0 266 if (fd < 0) {
michael@0 267 HAL_LOG(("Unable to open radio device"));
michael@0 268 return;
michael@0 269 }
michael@0 270
michael@0 271 struct v4l2_capability cap;
michael@0 272 int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
michael@0 273 if (rc < 0) {
michael@0 274 HAL_LOG(("Unable to query radio device"));
michael@0 275 return;
michael@0 276 }
michael@0 277
michael@0 278 sMsmFMMode = !strcmp((char *)cap.driver, "radio-tavarua") ||
michael@0 279 !strcmp((char *)cap.driver, "radio-iris");
michael@0 280 HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card));
michael@0 281
michael@0 282 if (!(cap.capabilities & V4L2_CAP_RADIO)) {
michael@0 283 HAL_LOG(("/dev/radio0 isn't a radio"));
michael@0 284 return;
michael@0 285 }
michael@0 286
michael@0 287 if (!(cap.capabilities & V4L2_CAP_TUNER)) {
michael@0 288 HAL_LOG(("/dev/radio0 doesn't support the tuner interface"));
michael@0 289 return;
michael@0 290 }
michael@0 291 sRadioSettings = aInfo;
michael@0 292
michael@0 293 if (sMsmFMMode) {
michael@0 294 sRadioFD = fd.forget();
michael@0 295 sMsmFMVersion = cap.version;
michael@0 296 pthread_create(&sRadioThread, nullptr, runMsmFMRadio, nullptr);
michael@0 297 return;
michael@0 298 }
michael@0 299
michael@0 300 struct v4l2_tuner tuner = {0};
michael@0 301 tuner.type = V4L2_TUNER_RADIO;
michael@0 302 tuner.rangelow = (aInfo.lowerLimit() * 10000) / 625;
michael@0 303 tuner.rangehigh = (aInfo.upperLimit() * 10000) / 625;
michael@0 304 tuner.audmode = V4L2_TUNER_MODE_STEREO;
michael@0 305 rc = ioctl(fd, VIDIOC_S_TUNER, &tuner);
michael@0 306 if (rc < 0) {
michael@0 307 HAL_LOG(("Unable to adjust band limits"));
michael@0 308 }
michael@0 309
michael@0 310 sRadioFD = fd.forget();
michael@0 311 sRadioEnabled = true;
michael@0 312
michael@0 313 hal::FMRadioOperationInformation info;
michael@0 314 info.operation() = hal::FM_RADIO_OPERATION_ENABLE;
michael@0 315 info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
michael@0 316 hal::NotifyFMRadioStatus(info);
michael@0 317 }
michael@0 318
michael@0 319 void
michael@0 320 DisableFMRadio()
michael@0 321 {
michael@0 322 if (!sRadioEnabled)
michael@0 323 return;
michael@0 324
michael@0 325 sRadioEnabled = false;
michael@0 326
michael@0 327 if (sMsmFMMode) {
michael@0 328 int rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_OFF);
michael@0 329 if (rc < 0) {
michael@0 330 HAL_LOG(("Unable to turn off radio"));
michael@0 331 }
michael@0 332
michael@0 333 pthread_join(sRadioThread, nullptr);
michael@0 334 }
michael@0 335
michael@0 336 close(sRadioFD);
michael@0 337
michael@0 338 hal::FMRadioOperationInformation info;
michael@0 339 info.operation() = hal::FM_RADIO_OPERATION_DISABLE;
michael@0 340 info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
michael@0 341 hal::NotifyFMRadioStatus(info);
michael@0 342 }
michael@0 343
michael@0 344 void
michael@0 345 FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
michael@0 346 {
michael@0 347 struct v4l2_hw_freq_seek seek = {0};
michael@0 348 seek.type = V4L2_TUNER_RADIO;
michael@0 349 seek.seek_upward = aDirection == hal::FMRadioSeekDirection::FM_RADIO_SEEK_DIRECTION_UP;
michael@0 350
michael@0 351 /* ICS and older don't have the spacing field */
michael@0 352 #if ANDROID_VERSION == 15
michael@0 353 seek.reserved[0] = sRadioSettings.spaceType() * 1000;
michael@0 354 #else
michael@0 355 seek.spacing = sRadioSettings.spaceType() * 1000;
michael@0 356 #endif
michael@0 357
michael@0 358 int rc = ioctl(sRadioFD, VIDIOC_S_HW_FREQ_SEEK, &seek);
michael@0 359 if (sMsmFMMode && rc >= 0)
michael@0 360 return;
michael@0 361
michael@0 362 hal::FMRadioOperationInformation info;
michael@0 363 info.operation() = hal::FM_RADIO_OPERATION_SEEK;
michael@0 364 info.status() = rc < 0 ? hal::FM_RADIO_OPERATION_STATUS_FAIL :
michael@0 365 hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
michael@0 366 hal::NotifyFMRadioStatus(info);
michael@0 367
michael@0 368 if (rc < 0) {
michael@0 369 HAL_LOG(("Could not initiate hardware seek"));
michael@0 370 return;
michael@0 371 }
michael@0 372
michael@0 373 info.operation() = hal::FM_RADIO_OPERATION_TUNE;
michael@0 374 info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
michael@0 375 hal::NotifyFMRadioStatus(info);
michael@0 376 }
michael@0 377
michael@0 378 void
michael@0 379 GetFMRadioSettings(hal::FMRadioSettings* aInfo)
michael@0 380 {
michael@0 381 if (!sRadioEnabled) {
michael@0 382 return;
michael@0 383 }
michael@0 384
michael@0 385 struct v4l2_tuner tuner = {0};
michael@0 386 int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
michael@0 387 if (rc < 0) {
michael@0 388 HAL_LOG(("Could not query fm radio for settings"));
michael@0 389 return;
michael@0 390 }
michael@0 391
michael@0 392 aInfo->upperLimit() = (tuner.rangehigh * 625) / 10000;
michael@0 393 aInfo->lowerLimit() = (tuner.rangelow * 625) / 10000;
michael@0 394 }
michael@0 395
michael@0 396 void
michael@0 397 SetFMRadioFrequency(const uint32_t frequency)
michael@0 398 {
michael@0 399 struct v4l2_frequency freq = {0};
michael@0 400 freq.type = V4L2_TUNER_RADIO;
michael@0 401 freq.frequency = (frequency * 10000) / 625;
michael@0 402
michael@0 403 int rc = ioctl(sRadioFD, VIDIOC_S_FREQUENCY, &freq);
michael@0 404 if (rc < 0)
michael@0 405 HAL_LOG(("Could not set radio frequency"));
michael@0 406
michael@0 407 if (sMsmFMMode && rc >= 0)
michael@0 408 return;
michael@0 409
michael@0 410 hal::FMRadioOperationInformation info;
michael@0 411 info.operation() = hal::FM_RADIO_OPERATION_TUNE;
michael@0 412 info.status() = rc < 0 ? hal::FM_RADIO_OPERATION_STATUS_FAIL :
michael@0 413 hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
michael@0 414 hal::NotifyFMRadioStatus(info);
michael@0 415 }
michael@0 416
michael@0 417 uint32_t
michael@0 418 GetFMRadioFrequency()
michael@0 419 {
michael@0 420 if (!sRadioEnabled)
michael@0 421 return 0;
michael@0 422
michael@0 423 struct v4l2_frequency freq;
michael@0 424 int rc = ioctl(sRadioFD, VIDIOC_G_FREQUENCY, &freq);
michael@0 425 if (rc < 0) {
michael@0 426 HAL_LOG(("Could not get radio frequency"));
michael@0 427 return 0;
michael@0 428 }
michael@0 429
michael@0 430 return (freq.frequency * 625) / 10000;
michael@0 431 }
michael@0 432
michael@0 433 bool
michael@0 434 IsFMRadioOn()
michael@0 435 {
michael@0 436 return sRadioEnabled;
michael@0 437 }
michael@0 438
michael@0 439 uint32_t
michael@0 440 GetFMRadioSignalStrength()
michael@0 441 {
michael@0 442 struct v4l2_tuner tuner = {0};
michael@0 443 int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
michael@0 444 if (rc < 0) {
michael@0 445 HAL_LOG(("Could not query fm radio for signal strength"));
michael@0 446 return 0;
michael@0 447 }
michael@0 448
michael@0 449 return tuner.signal;
michael@0 450 }
michael@0 451
michael@0 452 void
michael@0 453 CancelFMRadioSeek()
michael@0 454 {}
michael@0 455
michael@0 456 } // hal_impl
michael@0 457 } // namespace mozilla

mercurial