Bug 749053 - FM radio support: hal implementation, r=jlebar
authorSteven Lee <slee@mozilla.com>
Wed, 19 Sep 2012 11:17:13 -0400
changeset 107631 4e0709603af603ce900d2f3922443c2a99218cfd
parent 107630 bd797fec462dcddad5bd7e7bcacd23de29f42248
child 107632 fa535ebebed4a86bbfad0dee4bbdadc13d8543f8
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersjlebar
bugs749053
milestone18.0a1
Bug 749053 - FM radio support: hal implementation, r=jlebar Hal interface by Steven Lee (slee), gonk backend by Michael Wu (mwu).
hal/Hal.cpp
hal/Hal.h
hal/HalTypes.h
hal/Makefile.in
hal/fallback/FallbackFMRadio.cpp
hal/gonk/GonkFMRadio.cpp
hal/gonk/tavarua.h
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -776,10 +776,202 @@ SetProcessPriority(int aPid, ProcessPrio
   if (InSandbox()) {
     hal_sandbox::SetProcessPriority(aPid, aPriority);
   }
   else {
     hal_impl::SetProcessPriority(aPid, aPriority);
   }
 }
 
+typedef mozilla::ObserverList<FMRadioOperationInformation> FMRadioObserverList;
+static FMRadioObserverList sFMRadioObservers;
+
+void
+RegisterFMRadioObserver(FMRadioObserver* aFMRadioObserver) {
+  AssertMainThread();
+  sFMRadioObservers.AddObserver(aFMRadioObserver);
+}
+
+void
+UnregisterFMRadioObserver(FMRadioObserver* aFMRadioObserver) {
+  AssertMainThread();
+  sFMRadioObservers.RemoveObserver(aFMRadioObserver);
+}
+
+void
+NotifyFMRadioStatus(const FMRadioOperationInformation& aFMRadioState) {
+  sFMRadioObservers.Broadcast(aFMRadioState);
+}
+
+void
+EnableFMRadio(const FMRadioSettings& aInfo) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(EnableFMRadio(aInfo));
+}
+
+void
+DisableFMRadio() {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(DisableFMRadio());
+}
+
+void
+FMRadioSeek(const FMRadioSeekDirection& aDirection) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(FMRadioSeek(aDirection));
+}
+
+void
+GetFMRadioSettings(FMRadioSettings* aInfo) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(GetFMRadioSettings(aInfo));
+}
+
+void
+SetFMRadioFrequency(const uint32_t aFrequency) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(SetFMRadioFrequency(aFrequency));
+}
+
+uint32_t
+GetFMRadioFrequency() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetFMRadioFrequency());
+}
+
+bool
+IsFMRadioOn() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(IsFMRadioOn());
+}
+
+uint32_t
+GetFMRadioSignalStrength() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetFMRadioSignalStrength());
+}
+
+void
+CancelFMRadioSeek() {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(CancelFMRadioSeek());
+}
+
+FMRadioSettings
+GetFMBandSettings(FMRadioCountry aCountry) {
+  FMRadioSettings settings;
+
+  switch (aCountry) {
+    case FM_RADIO_COUNTRY_US:
+    case FM_RADIO_COUNTRY_EU:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87800;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_JP_STANDARD:
+      settings.upperLimit() = 76000;
+      settings.lowerLimit() = 90000;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_CY:
+    case FM_RADIO_COUNTRY_DE:
+    case FM_RADIO_COUNTRY_DK:
+    case FM_RADIO_COUNTRY_ES:
+    case FM_RADIO_COUNTRY_FI:
+    case FM_RADIO_COUNTRY_FR:
+    case FM_RADIO_COUNTRY_HU:
+    case FM_RADIO_COUNTRY_IR:
+    case FM_RADIO_COUNTRY_IT:
+    case FM_RADIO_COUNTRY_KW:
+    case FM_RADIO_COUNTRY_LT:
+    case FM_RADIO_COUNTRY_ML:
+    case FM_RADIO_COUNTRY_NO:
+    case FM_RADIO_COUNTRY_OM:
+    case FM_RADIO_COUNTRY_PG:
+    case FM_RADIO_COUNTRY_NL:
+    case FM_RADIO_COUNTRY_CZ:
+    case FM_RADIO_COUNTRY_UK:
+    case FM_RADIO_COUNTRY_RW:
+    case FM_RADIO_COUNTRY_SN:
+    case FM_RADIO_COUNTRY_SI:
+    case FM_RADIO_COUNTRY_ZA:
+    case FM_RADIO_COUNTRY_SE:
+    case FM_RADIO_COUNTRY_CH:
+    case FM_RADIO_COUNTRY_TW:
+    case FM_RADIO_COUNTRY_UA:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_VA:
+    case FM_RADIO_COUNTRY_MA:
+    case FM_RADIO_COUNTRY_TR:
+      settings.upperLimit() = 10800;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_AU:
+    case FM_RADIO_COUNTRY_BD:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_AW:
+    case FM_RADIO_COUNTRY_BS:
+    case FM_RADIO_COUNTRY_CO:
+    case FM_RADIO_COUNTRY_KR:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_EC:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 0;
+      break;
+    case FM_RADIO_COUNTRY_GM:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 0;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_QA:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_SG:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_IN:
+      settings.upperLimit() = 100000;
+      settings.lowerLimit() = 108000;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_NZ:
+      settings.upperLimit() = 100000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 50;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_USER_DEFINED:
+      break;
+    default:
+      MOZ_ASSERT(0);
+      break;
+    };
+    return settings;
+}
+
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -432,16 +432,83 @@ bool SetAlarm(int32_t aSeconds, int32_t 
  * Set the priority of the given process.
  *
  * Exactly what this does will vary between platforms.  On *nix we might give
  * background processes higher nice values.  On other platforms, we might
  * ignore this call entirely.
  */
 void SetProcessPriority(int aPid, hal::ProcessPriority aPriority);
 
+/**
+ * Register an observer for the FM radio.
+ */
+void RegisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
+
+/**
+ * Unregister the observer for the FM radio.
+ */
+void UnregisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
+
+/**
+ * Notify observers that a call to EnableFMRadio, DisableFMRadio, or FMRadioSeek
+ * has completed, and indicate what the call returned.
+ */
+void NotifyFMRadioStatus(const hal::FMRadioOperationInformation& aRadioState);
+
+/**
+ * Enable the FM radio and configure it according to the settings in aInfo.
+ */
+void EnableFMRadio(const hal::FMRadioSettings& aInfo);
+
+/**
+ * Disable the FM radio.
+ */
+void DisableFMRadio();
+
+/**
+ * Seek to an available FM radio station.
+ *
+ */
+void FMRadioSeek(const hal::FMRadioSeekDirection& aDirection);
+
+/**
+ * Get the current FM radio settings.
+ */
+void GetFMRadioSettings(hal::FMRadioSettings* aInfo);
+
+/**
+ * Set the FM radio's frequency.
+ */
+void SetFMRadioFrequency(const uint32_t frequency);
+
+/**
+ * Get the FM radio's frequency.
+ */
+uint32_t GetFMRadioFrequency();
+
+/**
+ * Get FM radio power state
+ */
+bool IsFMRadioOn();
+
+/**
+ * Get FM radio signal strength
+ */
+uint32_t GetFMRadioSignalStrength();
+
+/**
+ * Cancel FM radio seeking
+ */
+void CancelFMRadioSeek();
+
+/**
+ * Get FM radio band settings by country.
+ */
+hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry);
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #ifdef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_HAL_NAMESPACE
 #endif
 
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -76,16 +76,92 @@ enum WakeLockControl {
 
 enum SystemTimeChange {
   SYS_TIME_CHANGE_UNKNOWN = -1,
   SYS_TIME_CHANGE_CLOCK,
   SYS_TIME_CHANGE_TZ,
   SYS_TIME_CHANGE_GUARD
 };
 
+class FMRadioOperationInformation;
+
+enum FMRadioOperation {
+  FM_RADIO_OPERATION_UNKNOWN = -1,
+  FM_RADIO_OPERATION_ENABLE,
+  FM_RADIO_OPERATION_DISABLE,
+  FM_RADIO_OPERATION_SEEK,
+  NUM_FM_RADIO_OPERATION
+};
+
+enum FMRadioOperationStatus {
+  FM_RADIO_OPERATION_STATUS_UNKNOWN = -1,
+  FM_RADIO_OPERATION_STATUS_SUCCESS,
+  FM_RADIO_OPERATION_STATUS_FAIL,
+  NUM_FM_RADIO_OPERATION_STATUS
+};
+
+enum FMRadioSeekDirection {
+  FM_RADIO_SEEK_DIRECTION_UNKNOWN = -1,
+  FM_RADIO_SEEK_DIRECTION_UP,
+  FM_RADIO_SEEK_DIRECTION_DOWN,
+  NUM_FM_RADIO_SEEK_DIRECTION
+};
+
+enum FMRadioCountry {
+  FM_RADIO_COUNTRY_UNKNOWN = -1,
+  FM_RADIO_COUNTRY_US,  //USA
+  FM_RADIO_COUNTRY_EU,
+  FM_RADIO_COUNTRY_JP_STANDARD,
+  FM_RADIO_COUNTRY_JP_WIDE,
+  FM_RADIO_COUNTRY_DE,  //Germany
+  FM_RADIO_COUNTRY_AW,  //Aruba
+  FM_RADIO_COUNTRY_AU,  //Australlia
+  FM_RADIO_COUNTRY_BS,  //Bahamas
+  FM_RADIO_COUNTRY_BD,  //Bangladesh
+  FM_RADIO_COUNTRY_CY,  //Cyprus
+  FM_RADIO_COUNTRY_VA,  //Vatican
+  FM_RADIO_COUNTRY_CO,  //Colombia
+  FM_RADIO_COUNTRY_KR,  //Korea
+  FM_RADIO_COUNTRY_DK,  //Denmark
+  FM_RADIO_COUNTRY_EC,  //Ecuador
+  FM_RADIO_COUNTRY_ES,  //Spain
+  FM_RADIO_COUNTRY_FI,  //Finland
+  FM_RADIO_COUNTRY_FR,  //France
+  FM_RADIO_COUNTRY_GM,  //Gambia
+  FM_RADIO_COUNTRY_HU,  //Hungary
+  FM_RADIO_COUNTRY_IN,  //India
+  FM_RADIO_COUNTRY_IR,  //Iran
+  FM_RADIO_COUNTRY_IT,  //Italy
+  FM_RADIO_COUNTRY_KW,  //Kuwait
+  FM_RADIO_COUNTRY_LT,  //Lithuania
+  FM_RADIO_COUNTRY_ML,  //Mali
+  FM_RADIO_COUNTRY_MA,  //Morocco
+  FM_RADIO_COUNTRY_NO,  //Norway
+  FM_RADIO_COUNTRY_NZ,  //New Zealand
+  FM_RADIO_COUNTRY_OM,  //Oman
+  FM_RADIO_COUNTRY_PG,  //Papua New Guinea
+  FM_RADIO_COUNTRY_NL,  //Netherlands
+  FM_RADIO_COUNTRY_QA,  //Qatar
+  FM_RADIO_COUNTRY_CZ,  //Czech Republic
+  FM_RADIO_COUNTRY_UK,  //United Kingdom of Great Britain and Northern Ireland
+  FM_RADIO_COUNTRY_RW,  //Rwandese Republic
+  FM_RADIO_COUNTRY_SN,  //Senegal
+  FM_RADIO_COUNTRY_SG,  //Singapore
+  FM_RADIO_COUNTRY_SI,  //Slovenia
+  FM_RADIO_COUNTRY_ZA,  //South Africa
+  FM_RADIO_COUNTRY_SE,  //Sweden
+  FM_RADIO_COUNTRY_CH,  //Switzerland
+  FM_RADIO_COUNTRY_TW,  //Taiwan
+  FM_RADIO_COUNTRY_TR,  //Turkey
+  FM_RADIO_COUNTRY_UA,  //Ukraine
+  FM_RADIO_COUNTRY_USER_DEFINED,
+  NUM_FM_RADIO_COUNTRY
+};
+
+typedef Observer<FMRadioOperationInformation> FMRadioObserver;
 } // namespace hal
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Light type serializer.
  */
@@ -158,11 +234,50 @@ struct ParamTraits<mozilla::hal::Process
  */
 template <>
 struct ParamTraits<mozilla::hal::SystemTimeChange>
   : public EnumSerializer<mozilla::hal::SystemTimeChange,
                           mozilla::hal::SYS_TIME_CHANGE_UNKNOWN,
                           mozilla::hal::SYS_TIME_CHANGE_GUARD>
 {};
  
+/**
+ * Serializer for FMRadioOperation
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioOperation>:
+  public EnumSerializer<mozilla::hal::FMRadioOperation,
+                        mozilla::hal::FM_RADIO_OPERATION_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_OPERATION>
+{};
+
+/**
+ * Serializer for FMRadioOperationStatus
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioOperationStatus>:
+  public EnumSerializer<mozilla::hal::FMRadioOperationStatus,
+                        mozilla::hal::FM_RADIO_OPERATION_STATUS_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_OPERATION_STATUS>
+{};
+
+/**
+ * Serializer for FMRadioSeekDirection
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioSeekDirection>:
+  public EnumSerializer<mozilla::hal::FMRadioSeekDirection,
+                        mozilla::hal::FM_RADIO_SEEK_DIRECTION_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_SEEK_DIRECTION>
+{};
+
+/**
+ * Serializer for FMRadioCountry
+ **/
+template <>
+struct ParamTraits<mozilla::hal::FMRadioCountry>:
+  public EnumSerializer<mozilla::hal::FMRadioCountry,
+                        mozilla::hal::FM_RADIO_COUNTRY_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_COUNTRY>
+{};
 } // namespace IPC
 
 #endif // mozilla_hal_Types_h
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -52,16 +52,17 @@ CPPSRCS += \
   $(NULL)
 else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   GonkHal.cpp \
   LinuxPower.cpp \
   GonkSensor.cpp \
   UeventPoller.cpp \
   GonkSwitch.cpp \
+  GonkFMRadio.cpp \
   $(NULL)
 else ifeq (Linux,$(OS_TARGET))
 CPPSRCS += \
   LinuxPower.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackSensor.cpp \
   FallbackVibration.cpp \
   FallbackAlarm.cpp \
@@ -120,16 +121,17 @@ endif
 ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) #{
 CPPSRCS += \
   FallbackLights.cpp  \
   FallbackTime.cpp \
   FallbackWakeLocks.cpp \
   FallbackSwitch.cpp \
   FallbackScreenPower.cpp \
   FallbackProcessPriority.cpp \
+  FallbackFMRadio.cpp \
   $(NULL)
 endif #}
 
 # Fallbacks for backends implemented on Android only.
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += FallbackNetwork.cpp
 endif
 
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackFMRadio.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+EnableFMRadio(const hal::FMRadioSettings& aInfo)
+{}
+
+void
+DisableFMRadio()
+{}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{}
+
+void
+GetFMRadioSettings(hal::FMRadioSettings* aInfo)
+{
+  aInfo->type() = FM_RADIO_COUNTRY_UNKNOWN;
+  aInfo->upperLimit() = 0;
+  aInfo->lowerLimit() = 0;
+  aInfo->spaceType() = 0;
+  aInfo->preEmphasis() = 0;
+}
+
+void
+SetFMRadioFrequency(const uint32_t frequency)
+{}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  return 0;
+}
+
+bool
+IsFMRadioOn()
+{
+  return false;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  return 0;
+}
+
+void
+CancelFMRadioSeek()
+{}
+
+} // hal_impl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/hal/gonk/GonkFMRadio.cpp
@@ -0,0 +1,324 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hal.h"
+#include "tavarua.h"
+#include "nsThreadUtils.h"
+#include "mozilla/FileUtils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+namespace mozilla {
+namespace hal_impl {
+
+uint32_t GetFMRadioFrequency();
+
+static int sRadioFD;
+static bool sRadioEnabled;
+static pthread_t sMonitorThread;
+
+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;
+public:
+  RadioUpdate(hal::FMRadioOperation op)
+    : mOp(op)
+  {}
+
+  NS_IMETHOD Run() {
+    hal::FMRadioOperationInformation info;
+    info.operation() = mOp;
+    info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
+    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)
+{
+  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
+  char command[64];
+  snprintf(command, sizeof(command), "/system/bin/fm_qsoc_patches %d 0", cap.version);
+  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)));
+    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;
+  }
+
+  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;
+}
+
+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);
+
+  close(sRadioFD);
+
+  hal::FMRadioOperationInformation info;
+  info.operation() = hal::FM_RADIO_OPERATION_DISABLE;
+  info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
+  hal::NotifyFMRadioStatus(info);
+}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{
+  struct v4l2_hw_freq_seek seek = {0};
+  seek.type = V4L2_TUNER_RADIO;
+  seek.seek_upward = aDirection == hal::FMRadioSeekDirection::FM_RADIO_SEEK_DIRECTION_UP;
+  int rc = ioctl(sRadioFD, VIDIOC_S_HW_FREQ_SEEK, &seek);
+  if (rc < 0) {
+    HAL_LOG(("Could not initiate hardware seek"));
+    return;
+  }
+}
+
+void
+GetFMRadioSettings(hal::FMRadioSettings* aInfo)
+{
+  if (!sRadioEnabled) {
+    return;
+  }
+
+  struct v4l2_tuner tuner = {0};
+  int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
+  if (rc < 0) {
+    HAL_LOG(("Could not query fm radio for settings"));
+    return;
+  }
+
+  aInfo->upperLimit() = (tuner.rangehigh * 625) / 10000;
+  aInfo->lowerLimit() = (tuner.rangelow * 625) / 10000;
+}
+
+void
+SetFMRadioFrequency(const uint32_t frequency)
+{
+  struct v4l2_frequency freq = {0};
+  freq.type = V4L2_TUNER_RADIO;
+  freq.frequency = (frequency * 10000) / 625;
+
+  int rc = ioctl(sRadioFD, VIDIOC_S_FREQUENCY, &freq);
+  if (rc < 0)
+    HAL_LOG(("Could not set radio frequency"));
+}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  if (!sRadioEnabled)
+    return 0;
+
+  struct v4l2_frequency freq;
+  int rc = ioctl(sRadioFD, VIDIOC_G_FREQUENCY, &freq);
+  if (rc < 0) {
+    HAL_LOG(("Could not get radio frequency"));
+    return 0;
+  }
+
+  return (freq.frequency * 625) / 10000;
+}
+
+bool
+IsFMRadioOn()
+{
+  return sRadioEnabled;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  struct v4l2_tuner tuner = {0};
+  int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
+  if (rc < 0) {
+    HAL_LOG(("Could not query fm radio for signal strength"));
+    return 0;
+  }
+
+  return tuner.signal;
+}
+
+void
+CancelFMRadioSeek()
+{}
+
+} // hal_impl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/hal/gonk/tavarua.h
@@ -0,0 +1,484 @@
+#ifndef __LINUX_TAVARUA_H
+#define __LINUX_TAVARUA_H
+
+/* This is a Linux header generated by "make headers_install" */
+
+#include <stdint.h>
+#include <linux/ioctl.h>
+#include <linux/videodev2.h>
+
+
+#undef FM_DEBUG
+
+/* constants */
+#define  RDS_BLOCKS_NUM             (4)
+#define BYTES_PER_BLOCK             (3)
+#define MAX_PS_LENGTH              (96)
+#define MAX_RT_LENGTH              (64)
+
+#define XFRDAT0                    (0x20)
+#define XFRDAT1                    (0x21)
+#define XFRDAT2                    (0x22)
+
+#define INTDET_PEEK_MSB            (0x88)
+#define INTDET_PEEK_LSB            (0x26)
+
+#define RMSSI_PEEK_MSB             (0x88)
+#define RMSSI_PEEK_LSB             (0xA8)
+
+#define MPX_DCC_BYPASS_POKE_MSB    (0x88)
+#define MPX_DCC_BYPASS_POKE_LSB    (0xC0)
+
+#define MPX_DCC_PEEK_MSB_REG1      (0x88)
+#define MPX_DCC_PEEK_LSB_REG1      (0xC2)
+
+#define MPX_DCC_PEEK_MSB_REG2      (0x88)
+#define MPX_DCC_PEEK_LSB_REG2      (0xC3)
+
+#define MPX_DCC_PEEK_MSB_REG3      (0x88)
+#define MPX_DCC_PEEK_LSB_REG3      (0xC4)
+
+#define ON_CHANNEL_TH_MSB          (0x0B)
+#define ON_CHANNEL_TH_LSB          (0xA8)
+
+#define OFF_CHANNEL_TH_MSB         (0x0B)
+#define OFF_CHANNEL_TH_LSB         (0xAC)
+
+#define ENF_200Khz                    (1)
+#define SRCH200KHZ_OFFSET             (7)
+#define SRCH_MASK                  (1 << SRCH200KHZ_OFFSET)
+
+/* Standard buffer size */
+#define STD_BUF_SIZE               (128)
+/* Search direction */
+#define SRCH_DIR_UP                 (0)
+#define SRCH_DIR_DOWN               (1)
+
+/* control options */
+#define CTRL_ON                     (1)
+#define CTRL_OFF                    (0)
+
+#define US_LOW_BAND                (87.5)
+#define US_HIGH_BAND               (108)
+
+/* constant for Tx */
+
+#define MASK_PI                    (0x0000FFFF)
+#define MASK_PI_MSB                (0x0000FF00)
+#define MASK_PI_LSB                (0x000000FF)
+#define MASK_PTY                   (0x0000001F)
+#define MASK_TXREPCOUNT            (0x0000000F)
+
+#undef FMDBG
+#ifdef FM_DEBUG
+  #define FMDBG(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+#else
+  #define FMDBG(fmt, args...)
+#endif
+
+#undef FMDERR
+#define FMDERR(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+
+#undef FMDBG_I2C
+#ifdef FM_DEBUG_I2C
+  #define FMDBG_I2C(fmt, args...) printk(KERN_INFO "fm_i2c: " fmt, ##args)
+#else
+  #define FMDBG_I2C(fmt, args...)
+#endif
+
+/* function declarations */
+/* FM Core audio paths. */
+#define TAVARUA_AUDIO_OUT_ANALOG_OFF	(0)
+#define TAVARUA_AUDIO_OUT_ANALOG_ON	(1)
+#define TAVARUA_AUDIO_OUT_DIGITAL_OFF	(0)
+#define TAVARUA_AUDIO_OUT_DIGITAL_ON	(1)
+
+int tavarua_set_audio_path(int digital_on, int analog_on);
+
+/* defines and enums*/
+
+#define MARIMBA_A0	0x01010013
+#define MARIMBA_2_1	0x02010204
+#define BAHAMA_1_0	0x0302010A
+#define BAHAMA_2_0	0x04020205
+#define WAIT_TIMEOUT 2000
+#define RADIO_INIT_TIME 15
+#define TAVARUA_DELAY 10
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+enum v4l2_cid_private_tavarua_t {
+	V4L2_CID_PRIVATE_TAVARUA_SRCHMODE = (V4L2_CID_PRIVATE_BASE + 1),
+	V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
+	V4L2_CID_PRIVATE_TAVARUA_SRCHON,
+	V4L2_CID_PRIVATE_TAVARUA_STATE,
+	V4L2_CID_PRIVATE_TAVARUA_TRANSMIT_MODE,
+	V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
+	V4L2_CID_PRIVATE_TAVARUA_REGION,
+	V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
+	V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
+	V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
+	V4L2_CID_PRIVATE_TAVARUA_SPACING,
+	V4L2_CID_PRIVATE_TAVARUA_RDSON,
+	V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
+	V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
+	V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
+	V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
+	V4L2_CID_PRIVATE_TAVARUA_PSALL,
+	/*v4l2 Tx controls*/
+	V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+	V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
+	V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
+	V4L2_CID_PRIVATE_TAVARUA_IOVERC,
+	V4L2_CID_PRIVATE_TAVARUA_INTDET,
+	V4L2_CID_PRIVATE_TAVARUA_MPX_DCC,
+	V4L2_CID_PRIVATE_TAVARUA_AF_JUMP,
+	V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA,
+	V4L2_CID_PRIVATE_TAVARUA_HLSI,
+
+	/*
+	* Here we have IOCTl's that are specific to IRIS
+	* (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28)
+	*/
+	V4L2_CID_PRIVATE_SOFT_MUTE,/* 0x800001E*/
+	V4L2_CID_PRIVATE_RIVA_ACCS_ADDR,
+	V4L2_CID_PRIVATE_RIVA_ACCS_LEN,
+	V4L2_CID_PRIVATE_RIVA_PEEK,
+	V4L2_CID_PRIVATE_RIVA_POKE,
+	V4L2_CID_PRIVATE_SSBI_ACCS_ADDR,
+	V4L2_CID_PRIVATE_SSBI_PEEK,
+	V4L2_CID_PRIVATE_SSBI_POKE,
+	V4L2_CID_PRIVATE_TX_TONE,
+	V4L2_CID_PRIVATE_RDS_GRP_COUNTERS,
+	V4L2_CID_PRIVATE_SET_NOTCH_FILTER,/* 0x8000028 */
+
+	V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,/* 0x8000029 */
+	V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION,/* 0x800002A : IRIS */
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM,/* 0x800002B */
+	V4L2_CID_PRIVATE_IRIS_GET_SINR, /* 0x800002C : IRIS */
+	V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, /* 0x800002D */
+	V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, /* 0x800002E */
+	V4L2_CID_PRIVATE_SINR_THRESHOLD,  /* 0x800002F : IRIS */
+	V4L2_CID_PRIVATE_SINR_SAMPLES,  /* 0x8000030 : IRIS */
+
+};
+
+enum tavarua_buf_t {
+	TAVARUA_BUF_SRCH_LIST,
+	TAVARUA_BUF_EVENTS,
+	TAVARUA_BUF_RT_RDS,
+	TAVARUA_BUF_PS_RDS,
+	TAVARUA_BUF_RAW_RDS,
+	TAVARUA_BUF_AF_LIST,
+	TAVARUA_BUF_MAX
+};
+
+enum tavarua_xfr_t {
+	TAVARUA_XFR_SYNC,
+	TAVARUA_XFR_ERROR,
+	TAVARUA_XFR_SRCH_LIST,
+	TAVARUA_XFR_RT_RDS,
+	TAVARUA_XFR_PS_RDS,
+	TAVARUA_XFR_AF_LIST,
+	TAVARUA_XFR_MAX
+};
+
+enum channel_spacing {
+	FM_CH_SPACE_200KHZ,
+	FM_CH_SPACE_100KHZ,
+	FM_CH_SPACE_50KHZ
+};
+
+enum step_size {
+	NO_SRCH200khz,
+	ENF_SRCH200khz
+};
+
+enum emphasis {
+	EMP_75,
+	EMP_50
+};
+
+enum rds_std {
+	RBDS_STD,
+	RDS_STD
+};
+
+/* offsets */
+#define RAW_RDS		0x0F
+#define RDS_BLOCK 	3
+
+/* registers*/
+#define MARIMBA_XO_BUFF_CNTRL 0x07
+#define RADIO_REGISTERS 0x30
+#define XFR_REG_NUM     16
+#define STATUS_REG_NUM 	3
+
+/* TX constants */
+#define HEADER_SIZE	4
+#define TX_ON		0x80
+#define TAVARUA_TX_RT	RDS_RT_0
+#define TAVARUA_TX_PS	RDS_PS_0
+
+enum register_t {
+	STATUS_REG1 = 0,
+	STATUS_REG2,
+	STATUS_REG3,
+	RDCTRL,
+	FREQ,
+	TUNECTRL,
+	SRCHRDS1,
+	SRCHRDS2,
+	SRCHCTRL,
+	IOCTRL,
+	RDSCTRL,
+	ADVCTRL,
+	AUDIOCTRL,
+	RMSSI,
+	IOVERC,
+	AUDIOIND = 0x1E,
+	XFRCTRL,
+	FM_CTL0 = 0xFF,
+	LEAKAGE_CNTRL = 0xFE,
+};
+#define BAHAMA_RBIAS_CTL1       0x07
+#define	BAHAMA_FM_MODE_REG      0xFD
+#define	BAHAMA_FM_CTL1_REG      0xFE
+#define	BAHAMA_FM_CTL0_REG      0xFF
+#define BAHAMA_FM_MODE_NORMAL   0x00
+#define BAHAMA_LDO_DREG_CTL0    0xF0
+#define BAHAMA_LDO_AREG_CTL0    0xF4
+
+/* Radio Control */
+#define RDCTRL_STATE_OFFSET	0
+#define RDCTRL_STATE_MASK	(3 << RDCTRL_STATE_OFFSET)
+#define RDCTRL_BAND_OFFSET	2
+#define RDCTRL_BAND_MASK	(1 << RDCTRL_BAND_OFFSET)
+#define RDCTRL_CHSPACE_OFFSET	3
+#define RDCTRL_CHSPACE_MASK	(3 << RDCTRL_CHSPACE_OFFSET)
+#define RDCTRL_DEEMPHASIS_OFFSET 5
+#define RDCTRL_DEEMPHASIS_MASK	(1 << RDCTRL_DEEMPHASIS_OFFSET)
+#define RDCTRL_HLSI_OFFSET	6
+#define RDCTRL_HLSI_MASK	(3 << RDCTRL_HLSI_OFFSET)
+#define RDSAF_OFFSET		6
+#define RDSAF_MASK		(1 << RDSAF_OFFSET)
+
+/* Tune Control */
+#define TUNE_STATION	0x01
+#define ADD_OFFSET	(1 << 1)
+#define SIGSTATE	(1 << 5)
+#define MOSTSTATE	(1 << 6)
+#define RDSSYNC		(1 << 7)
+/* Search Control */
+#define SRCH_MODE_OFFSET	0
+#define SRCH_MODE_MASK		(7 << SRCH_MODE_OFFSET)
+#define SRCH_DIR_OFFSET		3
+#define SRCH_DIR_MASK		(1 << SRCH_DIR_OFFSET)
+#define SRCH_DWELL_OFFSET	4
+#define SRCH_DWELL_MASK		(7 << SRCH_DWELL_OFFSET)
+#define SRCH_STATE_OFFSET	7
+#define SRCH_STATE_MASK		(1 << SRCH_STATE_OFFSET)
+
+/* I/O Control */
+#define IOC_HRD_MUTE	0x03
+#define IOC_SFT_MUTE    (1 << 2)
+#define IOC_MON_STR     (1 << 3)
+#define IOC_SIG_BLND    (1 << 4)
+#define IOC_INTF_BLND   (1 << 5)
+#define IOC_ANTENNA     (1 << 6)
+#define IOC_ANTENNA_OFFSET	6
+#define IOC_ANTENNA_MASK     	(1 << IOC_ANTENNA_OFFSET)
+
+/* RDS Control */
+#define RDS_ON		0x01
+#define RDSCTRL_STANDARD_OFFSET 1
+#define RDSCTRL_STANDARD_MASK	(1 << RDSCTRL_STANDARD_OFFSET)
+
+/* Advanced features controls */
+#define RDSRTEN		(1 << 3)
+#define RDSPSEN		(1 << 4)
+
+/* Audio path control */
+#define AUDIORX_ANALOG_OFFSET 	0
+#define AUDIORX_ANALOG_MASK 	(1 << AUDIORX_ANALOG_OFFSET)
+#define AUDIORX_DIGITAL_OFFSET 	1
+#define AUDIORX_DIGITAL_MASK 	(1 << AUDIORX_DIGITAL_OFFSET)
+#define AUDIOTX_OFFSET		2
+#define AUDIOTX_MASK		(1 << AUDIOTX_OFFSET)
+#define I2SCTRL_OFFSET		3
+#define I2SCTRL_MASK		(1 << I2SCTRL_OFFSET)
+
+/* Search options */
+enum search_t {
+	SEEK,
+	SCAN,
+	SCAN_FOR_STRONG,
+	SCAN_FOR_WEAK,
+	RDS_SEEK_PTY,
+	RDS_SCAN_PTY,
+	RDS_SEEK_PI,
+	RDS_AF_JUMP,
+};
+
+enum audio_path {
+	FM_DIGITAL_PATH,
+	FM_ANALOG_PATH
+};
+#define SRCH_MODE	0x07
+#define SRCH_DIR	0x08 /* 0-up 1-down */
+#define SCAN_DWELL	0x70
+#define SRCH_ON		0x80
+
+/* RDS CONFIG */
+#define RDS_CONFIG_PSALL 0x01
+
+#define FM_ENABLE	0x22
+#define SET_REG_FIELD(reg, val, offset, mask) \
+	(reg = (reg & ~mask) | (((val) << offset) & mask))
+#define GET_REG_FIELD(reg, offset, mask) ((reg & mask) >> offset)
+#define RSH_DATA(val, offset)    ((val) >> (offset))
+#define LSH_DATA(val, offset)    ((val) << (offset))
+#define GET_ABS_VAL(val)        ((val) & (0xFF))
+
+enum radio_state_t {
+	FM_OFF,
+	FM_RECV,
+	FM_TRANS,
+	FM_RESET,
+};
+
+#define XFRCTRL_WRITE   (1 << 7)
+
+/* Interrupt status */
+
+/* interrupt register 1 */
+#define	READY		(1 << 0) /* Radio ready after powerup or reset */
+#define	TUNE		(1 << 1) /* Tune completed */
+#define	SEARCH		(1 << 2) /* Search completed (read FREQ) */
+#define	SCANNEXT	(1 << 3) /* Scanning for next station */
+#define	SIGNAL		(1 << 4) /* Signal indicator change (read SIGSTATE) */
+#define	INTF		(1 << 5) /* Interference cnt has fallen outside range */
+#define	SYNC		(1 << 6) /* RDS sync state change (read RDSSYNC) */
+#define	AUDIO		(1 << 7) /* Audio Control indicator (read AUDIOIND) */
+
+/* interrupt register 2 */
+#define	RDSDAT		(1 << 0) /* New unread RDS data group available */
+#define	BLOCKB		(1 << 1) /* Block-B match condition exists */
+#define	PROGID		(1 << 2) /* Block-A or Block-C matched stored PI value*/
+#define	RDSPS		(1 << 3) /* New RDS Program Service Table available */
+#define	RDSRT		(1 << 4) /* New RDS Radio Text available */
+#define	RDSAF		(1 << 5) /* New RDS AF List available */
+#define	TXRDSDAT	(1 << 6) /* Transmitted an RDS group */
+#define	TXRDSDONE	(1 << 7) /* RDS raw group one-shot transmit completed */
+
+/* interrupt register 3 */
+#define	TRANSFER	(1 << 0) /* Data transfer (XFR) completed */
+#define	RDSPROC		(1 << 1) /* Dynamic RDS Processing complete */
+#define	ERROR		(1 << 7) /* Err occurred.Read code to determine cause */
+
+
+#define	FM_TX_PWR_LVL_0		0 /* Lowest power lvl that can be set for Tx */
+#define	FM_TX_PWR_LVL_MAX	7 /* Max power lvl for Tx */
+/* Transfer */
+enum tavarua_xfr_ctrl_t {
+	RDS_PS_0 = 0x01,
+	RDS_PS_1,
+	RDS_PS_2,
+	RDS_PS_3,
+	RDS_PS_4,
+	RDS_PS_5,
+	RDS_PS_6,
+	RDS_RT_0,
+	RDS_RT_1,
+	RDS_RT_2,
+	RDS_RT_3,
+	RDS_RT_4,
+	RDS_AF_0,
+	RDS_AF_1,
+	RDS_CONFIG,
+	RDS_TX_GROUPS,
+	RDS_COUNT_0,
+	RDS_COUNT_1,
+	RDS_COUNT_2,
+	RADIO_CONFIG,
+	RX_CONFIG,
+	RX_TIMERS,
+	RX_STATIONS_0,
+	RX_STATIONS_1,
+	INT_CTRL,
+	ERROR_CODE,
+	CHIPID,
+	CAL_DAT_0 = 0x20,
+	CAL_DAT_1,
+	CAL_DAT_2,
+	CAL_DAT_3,
+	CAL_CFG_0,
+	CAL_CFG_1,
+	DIG_INTF_0,
+	DIG_INTF_1,
+	DIG_AGC_0,
+	DIG_AGC_1,
+	DIG_AGC_2,
+	DIG_AUDIO_0,
+	DIG_AUDIO_1,
+	DIG_AUDIO_2,
+	DIG_AUDIO_3,
+	DIG_AUDIO_4,
+	DIG_RXRDS,
+	DIG_DCC,
+	DIG_SPUR,
+	DIG_MPXDCC,
+	DIG_PILOT,
+	DIG_DEMOD,
+	DIG_MOST,
+	DIG_TX_0,
+	DIG_TX_1,
+	PHY_TXGAIN = 0x3B,
+	PHY_CONFIG,
+	PHY_TXBLOCK,
+	PHY_TCB,
+	XFR_PEEK_MODE = 0x40,
+	XFR_POKE_MODE = 0xC0,
+	TAVARUA_XFR_CTRL_MAX
+};
+
+enum tavarua_evt_t {
+	TAVARUA_EVT_RADIO_READY,
+	TAVARUA_EVT_TUNE_SUCC,
+	TAVARUA_EVT_SEEK_COMPLETE,
+	TAVARUA_EVT_SCAN_NEXT,
+	TAVARUA_EVT_NEW_RAW_RDS,
+	TAVARUA_EVT_NEW_RT_RDS,
+	TAVARUA_EVT_NEW_PS_RDS,
+	TAVARUA_EVT_ERROR,
+	TAVARUA_EVT_BELOW_TH,
+	TAVARUA_EVT_ABOVE_TH,
+	TAVARUA_EVT_STEREO,
+	TAVARUA_EVT_MONO,
+	TAVARUA_EVT_RDS_AVAIL,
+	TAVARUA_EVT_RDS_NOT_AVAIL,
+	TAVARUA_EVT_NEW_SRCH_LIST,
+	TAVARUA_EVT_NEW_AF_LIST,
+	TAVARUA_EVT_TXRDSDAT,
+	TAVARUA_EVT_TXRDSDONE,
+	TAVARUA_EVT_RADIO_DISABLED
+};
+
+enum tavarua_region_t {
+	TAVARUA_REGION_US,
+	TAVARUA_REGION_EU,
+	TAVARUA_REGION_JAPAN,
+	TAVARUA_REGION_JAPAN_WIDE,
+	TAVARUA_REGION_OTHER
+};
+
+#endif /* __LINUX_TAVARUA_H */
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -20,16 +20,20 @@ using mozilla::hal::SensorType;
 using mozilla::hal::SensorAccuracyType;
 using mozilla::hal::WakeLockControl;
 using mozilla::hal::SwitchState;
 using mozilla::hal::SwitchDevice;
 using mozilla::hal::ProcessPriority;
 using nsIntRect;
 using PRTime;
 using mozilla::hal::SystemTimeChange;
+using mozilla::hal::FMRadioCountry;
+using mozilla::hal::FMRadioOperation;
+using mozilla::hal::FMRadioOperationStatus;
+using mozilla::hal::FMRadioSeekDirection;
 
 namespace mozilla {
 
 namespace hal {
 struct BatteryInformation {
   double level;
   bool   charging;
   double remainingTime;
@@ -69,30 +73,45 @@ struct WakeLockInformation {
 
 struct ScreenConfiguration {
   nsIntRect rect;
   ScreenOrientation orientation;
   uint32_t colorDepth;
   uint32_t pixelDepth;
 };
 
+struct FMRadioOperationInformation {
+  FMRadioOperation operation;
+  FMRadioOperationStatus status;
+  uint32_t frequency;
+};
+
+struct FMRadioSettings {
+  FMRadioCountry country;
+  uint32_t upperLimit;
+  uint32_t lowerLimit;
+  uint32_t spaceType;
+  uint32_t preEmphasis;
+};
+
 } // namespace hal
 
 namespace hal_sandbox {
 
 sync protocol PHal {
     manager PContent;
 
 child:
     NotifyBatteryChange(BatteryInformation aBatteryInfo);
     NotifyNetworkChange(NetworkInformation aNetworkInfo);
     NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
     NotifyScreenConfigurationChange(ScreenConfiguration aScreenOrientation);
     NotifySwitchChange(SwitchEvent aEvent);
     NotifySystemTimeChange(SystemTimeChange aReason); 
+    NotifyFMRadioStatus(FMRadioOperationInformation aInfo);
 
 parent:
     Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
     CancelVibrate(uint64_t[] id, PBrowser browser);
 
     EnableBatteryNotifications();
     DisableBatteryNotifications();
     sync GetCurrentBatteryInformation()
@@ -141,20 +160,34 @@ parent:
  
     EnableSwitchNotifications(SwitchDevice aDevice);
     DisableSwitchNotifications(SwitchDevice aDevice);
     sync GetCurrentSwitchState(SwitchDevice aDevice)
       returns (SwitchState aState);
 
     SetProcessPriority(int aPid, ProcessPriority aPriority);
 
+    EnableFMRadio(FMRadioSettings aSettings);
+    DisableFMRadio();
+    FMRadioSeek(FMRadioSeekDirection aDirection);
+    sync GetFMRadioSettings()
+      returns (FMRadioSettings settings);
+    SetFMRadioFrequency(uint32_t frequency);
+    sync GetFMRadioFrequency()
+      returns (uint32_t frequency);
+    sync IsFMRadioOn()
+      returns (bool radioOn);
+    sync GetFMRadioSignalStrength()
+      returns (uint32_t strength);
+    CancelFMRadioSeek();
+
 child:
     NotifySensorChange(SensorData aSensorData);
 
-parent:    
+parent:
     EnableSensorNotifications(SensorType aSensor);
     DisableSensorNotifications(SensorType aSensor);
 
     __delete__();
 };
 
 } // namespace hal
 } // namespace mozilla
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -290,16 +290,76 @@ SetAlarm(int32_t aSeconds, int32_t aNano
 }
 
 void
 SetProcessPriority(int aPid, ProcessPriority aPriority)
 {
   Hal()->SendSetProcessPriority(aPid, aPriority);
 }
 
+void
+EnableFMRadio(const hal::FMRadioSettings& aSettings)
+{
+  Hal()->SendEnableFMRadio(aSettings);
+}
+
+void
+DisableFMRadio()
+{
+  Hal()->SendDisableFMRadio();
+}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{
+  Hal()->SendFMRadioSeek(aDirection);
+}
+
+void
+GetFMRadioSettings(FMRadioSettings* aSettings)
+{
+  Hal()->SendGetFMRadioSettings(aSettings);
+}
+
+void
+SetFMRadioFrequency(const uint32_t aFrequency)
+{
+  Hal()->SendSetFMRadioFrequency(aFrequency);
+}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  uint32_t frequency;
+  Hal()->SendGetFMRadioFrequency(&frequency);
+  return frequency;
+}
+
+bool
+IsFMRadioOn()
+{
+  bool FMRadioOn;
+  Hal()->SendIsFMRadioOn(&FMRadioOn);
+  return FMRadioOn;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  uint32_t strength;
+  Hal()->SendGetFMRadioSignalStrength(&strength);
+  return strength;
+}
+
+void
+CancelFMRadioSeek()
+{
+  Hal()->SendCancelFMRadioSeek();
+}
+
 class HalParent : public PHalParent
                 , public BatteryObserver
                 , public NetworkObserver
                 , public ISensorObserver
                 , public WakeLockObserver
                 , public ScreenConfigurationObserver
                 , public SwitchObserver
 {
@@ -660,16 +720,84 @@ public:
     hal::SetProcessPriority(aPid, aPriority);
     return true;
   }
 
   void Notify(const SystemTimeChange& aReason)
   {
     unused << SendNotifySystemTimeChange(aReason);
   }
+
+  virtual bool
+  RecvEnableFMRadio(const hal::FMRadioSettings& aSettings)
+  {
+    hal::EnableFMRadio(aSettings);
+    return true;
+  }
+
+  virtual bool
+  RecvDisableFMRadio()
+  {
+    hal::DisableFMRadio();
+    return true;
+  }
+
+  virtual bool
+  RecvFMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+  {
+    hal::FMRadioSeek(aDirection);
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioSettings(hal::FMRadioSettings* aSettings)
+  {
+    hal::GetFMRadioSettings(aSettings);
+    return true;
+  }
+
+  virtual bool
+  RecvSetFMRadioFrequency(const uint32_t& aFrequency)
+  {
+    hal::SetFMRadioFrequency(aFrequency);
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioFrequency(uint32_t* aFrequency)
+  {
+    *aFrequency = hal::GetFMRadioFrequency();
+    return true;
+  }
+
+  void Notify(const hal::FMRadioOperationInformation& aRadioStatus)
+  {
+    unused << SendNotifyFMRadioStatus(aRadioStatus);
+  }
+
+  virtual bool
+  RecvIsFMRadioOn(bool* radioOn)
+  {
+    *radioOn = hal::IsFMRadioOn();
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioSignalStrength(uint32_t* strength)
+  {
+    *strength = hal::GetFMRadioSignalStrength();
+    return true;
+  }
+
+  virtual bool
+  RecvCancelFMRadioSeek()
+  {
+    hal::CancelFMRadioSeek();
+    return true;
+  }
 };
 
 class HalChild : public PHalChild {
 public:
   virtual bool
   RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) MOZ_OVERRIDE {
     hal::NotifyBatteryChange(aBatteryInfo);
     return true;
@@ -702,16 +830,22 @@ public:
     return true;
   }
 
   virtual bool
   RecvNotifySystemTimeChange(const SystemTimeChange& aReason) {
     hal::NotifySystemTimeChange(aReason);
     return true;
   }
+
+  virtual bool
+  RecvNotifyFMRadioStatus(const FMRadioOperationInformation& aRadioStatus) {
+    hal::NotifyFMRadioStatus(aRadioStatus);
+    return true;
+  }
 };
 
 bool
 HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) {
   hal::NotifySensorChange(aSensorData);
   
   return true;
 }