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.

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

mercurial