import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,324 @@
/* 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"
#include <sys/syscall.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include <errno.h>
#include "base/message_loop.h"
#include "base/task.h"
#include "DiskSpaceWatcher.h"
#include "fanotify.h"
#include "nsIObserverService.h"
#include "nsIDiskSpaceWatcher.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/ModuleUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
using namespace mozilla;
namespace mozilla { namespace hal_impl { class GonkDiskSpaceWatcher; } }
using namespace mozilla::hal_impl;
namespace mozilla {
namespace hal_impl {
// NOTE: this should be unnecessary once we no longer support ICS.
#ifndef __NR_fanotify_init
#if defined(__ARM_EABI__)
#define __NR_fanotify_init 367
#define __NR_fanotify_mark 368
#elif defined(__i386__)
#define __NR_fanotify_init 338
#define __NR_fanotify_mark 339
#else
#error "Unhandled architecture"
#endif
#endif
// fanotify_init and fanotify_mark functions are syscalls.
// The user space bits are not part of bionic so we add them here
// as well as fanotify.h
int fanotify_init (unsigned int flags, unsigned int event_f_flags)
{
return syscall(__NR_fanotify_init, flags, event_f_flags);
}
// Add, remove, or modify an fanotify mark on a filesystem object.
int fanotify_mark (int fanotify_fd, unsigned int flags,
uint64_t mask, int dfd, const char *pathname)
{
// On 32 bits platforms we have to convert the 64 bits mask into
// two 32 bits ints.
if (sizeof(void *) == 4) {
union {
uint64_t _64;
uint32_t _32[2];
} _mask;
_mask._64 = mask;
return syscall(__NR_fanotify_mark, fanotify_fd, flags,
_mask._32[0], _mask._32[1], dfd, pathname);
}
return syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname);
}
class GonkDiskSpaceWatcher final : public MessageLoopForIO::Watcher
{
public:
GonkDiskSpaceWatcher();
~GonkDiskSpaceWatcher() {};
virtual void OnFileCanReadWithoutBlocking(int aFd);
// We should never write to the fanotify fd.
virtual void OnFileCanWriteWithoutBlocking(int aFd)
{
MOZ_CRASH("Must not write to fanotify fd");
}
void DoStart();
void DoStop();
private:
void NotifyUpdate();
uint64_t mLowThreshold;
uint64_t mHighThreshold;
TimeDuration mTimeout;
TimeStamp mLastTimestamp;
uint64_t mLastFreeSpace;
uint32_t mSizeDelta;
bool mIsDiskFull;
uint64_t mFreeSpace;
int mFd;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
};
static GonkDiskSpaceWatcher* gHalDiskSpaceWatcher = nullptr;
#define WATCHER_PREF_LOW "disk_space_watcher.low_threshold"
#define WATCHER_PREF_HIGH "disk_space_watcher.high_threshold"
#define WATCHER_PREF_TIMEOUT "disk_space_watcher.timeout"
#define WATCHER_PREF_SIZE_DELTA "disk_space_watcher.size_delta"
static const char kWatchedPath[] = "/data";
// Helper class to dispatch calls to xpcom on the main thread.
class DiskSpaceNotifier : public Runnable
{
public:
DiskSpaceNotifier(const bool aIsDiskFull, const uint64_t aFreeSpace) :
mIsDiskFull(aIsDiskFull),
mFreeSpace(aFreeSpace) {}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
DiskSpaceWatcher::UpdateState(mIsDiskFull, mFreeSpace);
return NS_OK;
}
private:
bool mIsDiskFull;
uint64_t mFreeSpace;
};
// Helper runnable to delete the watcher on the main thread.
class DiskSpaceCleaner : public Runnable
{
public:
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (gHalDiskSpaceWatcher) {
delete gHalDiskSpaceWatcher;
gHalDiskSpaceWatcher = nullptr;
}
return NS_OK;
}
};
GonkDiskSpaceWatcher::GonkDiskSpaceWatcher() :
mLastFreeSpace(UINT64_MAX),
mIsDiskFull(false),
mFreeSpace(UINT64_MAX),
mFd(-1)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(gHalDiskSpaceWatcher == nullptr);
// Default values: 5MB for low threshold, 10MB for high threshold, and
// a timeout of 5 seconds.
mLowThreshold = Preferences::GetInt(WATCHER_PREF_LOW, 5) * 1024 * 1024;
mHighThreshold = Preferences::GetInt(WATCHER_PREF_HIGH, 10) * 1024 * 1024;
mTimeout = TimeDuration::FromSeconds(Preferences::GetInt(WATCHER_PREF_TIMEOUT, 5));
mSizeDelta = Preferences::GetInt(WATCHER_PREF_SIZE_DELTA, 1) * 1024 * 1024;
}
void
GonkDiskSpaceWatcher::DoStart()
{
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
"Not on the correct message loop");
mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC | O_LARGEFILE);
if (mFd == -1) {
if (errno == ENOSYS) {
// Don't change these printf_stderr since we need these logs even
// in opt builds.
printf_stderr("Warning: No fanotify support in this device's kernel.\n");
#if ANDROID_VERSION >= 19
MOZ_CRASH("Fanotify support must be enabled in the kernel.");
#endif
} else {
printf_stderr("Error calling fanotify_init()");
}
return;
}
if (fanotify_mark(mFd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE,
0, kWatchedPath) < 0) {
NS_WARNING("Error calling fanotify_mark");
close(mFd);
mFd = -1;
return;
}
if (!MessageLoopForIO::current()->WatchFileDescriptor(
mFd, /* persistent = */ true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher, gHalDiskSpaceWatcher)) {
NS_WARNING("Unable to watch fanotify fd.");
close(mFd);
mFd = -1;
}
}
void
GonkDiskSpaceWatcher::DoStop()
{
NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
"Not on the correct message loop");
if (mFd != -1) {
mReadWatcher.StopWatchingFileDescriptor();
fanotify_mark(mFd, FAN_MARK_FLUSH, 0, 0, kWatchedPath);
close(mFd);
mFd = -1;
}
// Dispatch the cleanup to the main thread.
nsCOMPtr<nsIRunnable> runnable = new DiskSpaceCleaner();
NS_DispatchToMainThread(runnable);
}
// We are called off the main thread, so we proxy first to the main thread
// before calling the xpcom object.
void
GonkDiskSpaceWatcher::NotifyUpdate()
{
mLastTimestamp = TimeStamp::Now();
mLastFreeSpace = mFreeSpace;
nsCOMPtr<nsIRunnable> runnable =
new DiskSpaceNotifier(mIsDiskFull, mFreeSpace);
NS_DispatchToMainThread(runnable);
}
void
GonkDiskSpaceWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
struct fanotify_event_metadata* fem = nullptr;
char buf[4096];
struct statfs sfs;
int32_t len, rc;
do {
len = read(aFd, buf, sizeof(buf));
} while(len == -1 && errno == EINTR);
// Bail out if the file is busy.
if (len < 0 && errno == ETXTBSY) {
return;
}
// We should get an exact multiple of fanotify_event_metadata
if (len <= 0 || (len % FAN_EVENT_METADATA_LEN != 0)) {
MOZ_CRASH("About to crash: fanotify_event_metadata read error.");
}
fem = reinterpret_cast<fanotify_event_metadata *>(buf);
while (FAN_EVENT_OK(fem, len)) {
rc = fstatfs(fem->fd, &sfs);
if (rc < 0) {
NS_WARNING("Unable to stat fan_notify fd");
} else {
bool firstRun = mFreeSpace == UINT64_MAX;
mFreeSpace = sfs.f_bavail * sfs.f_bsize;
// We change from full <-> free depending on the free space and the
// low and high thresholds.
// Once we are in 'full' mode we send updates for all size changes with
// a minimum of time between messages or when we cross a size change
// threshold.
if (firstRun) {
mIsDiskFull = mFreeSpace <= mLowThreshold;
// Always notify the current state at first run.
NotifyUpdate();
} else if (!mIsDiskFull && (mFreeSpace <= mLowThreshold)) {
mIsDiskFull = true;
NotifyUpdate();
} else if (mIsDiskFull && (mFreeSpace > mHighThreshold)) {
mIsDiskFull = false;
NotifyUpdate();
} else if (mIsDiskFull) {
if (mTimeout < TimeStamp::Now() - mLastTimestamp ||
mSizeDelta < llabs(mFreeSpace - mLastFreeSpace)) {
NotifyUpdate();
}
}
}
close(fem->fd);
fem = FAN_EVENT_NEXT(fem, len);
}
}
void
StartDiskSpaceWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
// Bail out if called several times.
if (gHalDiskSpaceWatcher != nullptr) {
return;
}
gHalDiskSpaceWatcher = new GonkDiskSpaceWatcher();
XRE_GetIOMessageLoop()->PostTask(
NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStart));
}
void
StopDiskSpaceWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
if (!gHalDiskSpaceWatcher) {
return;
}
XRE_GetIOMessageLoop()->PostTask(
NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStop));
}
} // namespace hal_impl
} // namespace mozilla

2045
hal/gonk/GonkHal.cpp Normal file

File diff suppressed because it is too large Load diff

861
hal/gonk/GonkSensor.cpp Normal file
View file

@ -0,0 +1,861 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <pthread.h>
#include <stdio.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/Saturate.h"
#include "base/basictypes.h"
#include "base/thread.h"
#include "base/task.h"
#include "GonkSensorsInterface.h"
#include "GonkSensorsPollInterface.h"
#include "GonkSensorsRegistryInterface.h"
#include "Hal.h"
#include "HalLog.h"
#include "HalSensor.h"
#include "hardware/sensors.h"
#include "nsThreadUtils.h"
using namespace mozilla::hal;
namespace mozilla {
//
// Internal implementation
//
// The value from SensorDevice.h (Android)
#define DEFAULT_DEVICE_POLL_RATE 200000000 /*200ms*/
// ProcessOrientation.cpp needs smaller poll rate to detect delay between
// different orientation angles
#define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/
// This is present in Android from API level 18 onwards, which is 4.3. We might
// be building on something before 4.3, so use a local define for its value
#define MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR 15
double radToDeg(double a) {
return a * (180.0 / M_PI);
}
static SensorType
HardwareSensorToHalSensor(int type)
{
switch(type) {
case SENSOR_TYPE_ORIENTATION:
return SENSOR_ORIENTATION;
case SENSOR_TYPE_ACCELEROMETER:
return SENSOR_ACCELERATION;
case SENSOR_TYPE_PROXIMITY:
return SENSOR_PROXIMITY;
case SENSOR_TYPE_LIGHT:
return SENSOR_LIGHT;
case SENSOR_TYPE_GYROSCOPE:
return SENSOR_GYROSCOPE;
case SENSOR_TYPE_LINEAR_ACCELERATION:
return SENSOR_LINEAR_ACCELERATION;
case SENSOR_TYPE_ROTATION_VECTOR:
return SENSOR_ROTATION_VECTOR;
case MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR:
return SENSOR_GAME_ROTATION_VECTOR;
default:
return SENSOR_UNKNOWN;
}
}
static SensorAccuracyType
HardwareStatusToHalAccuracy(int status) {
return static_cast<SensorAccuracyType>(status);
}
static int
HalSensorToHardwareSensor(SensorType type)
{
switch(type) {
case SENSOR_ORIENTATION:
return SENSOR_TYPE_ORIENTATION;
case SENSOR_ACCELERATION:
return SENSOR_TYPE_ACCELEROMETER;
case SENSOR_PROXIMITY:
return SENSOR_TYPE_PROXIMITY;
case SENSOR_LIGHT:
return SENSOR_TYPE_LIGHT;
case SENSOR_GYROSCOPE:
return SENSOR_TYPE_GYROSCOPE;
case SENSOR_LINEAR_ACCELERATION:
return SENSOR_TYPE_LINEAR_ACCELERATION;
case SENSOR_ROTATION_VECTOR:
return SENSOR_TYPE_ROTATION_VECTOR;
case SENSOR_GAME_ROTATION_VECTOR:
return MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR;
default:
return -1;
}
}
static int
SensorseventStatus(const sensors_event_t& data)
{
int type = data.type;
switch(type) {
case SENSOR_ORIENTATION:
return data.orientation.status;
case SENSOR_LINEAR_ACCELERATION:
case SENSOR_ACCELERATION:
return data.acceleration.status;
case SENSOR_GYROSCOPE:
return data.gyro.status;
}
return SENSOR_STATUS_UNRELIABLE;
}
class SensorRunnable : public Runnable
{
public:
SensorRunnable(const sensors_event_t& data, const sensor_t* sensors, ssize_t size)
{
mSensorData.sensor() = HardwareSensorToHalSensor(data.type);
mSensorData.accuracy() = HardwareStatusToHalAccuracy(SensorseventStatus(data));
mSensorData.timestamp() = data.timestamp;
if (mSensorData.sensor() == SENSOR_GYROSCOPE) {
// libhardware returns gyro as rad. convert.
mSensorValues.AppendElement(radToDeg(data.data[0]));
mSensorValues.AppendElement(radToDeg(data.data[1]));
mSensorValues.AppendElement(radToDeg(data.data[2]));
} else if (mSensorData.sensor() == SENSOR_PROXIMITY) {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(0);
// Determine the maxRange for this sensor.
for (ssize_t i = 0; i < size; i++) {
if (sensors[i].type == SENSOR_TYPE_PROXIMITY) {
mSensorValues.AppendElement(sensors[i].maxRange);
}
}
} else if (mSensorData.sensor() == SENSOR_LIGHT) {
mSensorValues.AppendElement(data.data[0]);
} else if (mSensorData.sensor() == SENSOR_ROTATION_VECTOR) {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
mSensorValues.AppendElement(data.data[2]);
if (data.data[3] == 0.0) {
// data.data[3] was optional in Android <= API level 18. It can be computed from 012,
// but it's better to take the actual value if one is provided. The computation is
// v = 1 - d[0]*d[0] - d[1]*d[1] - d[2]*d[2]
// d[3] = v > 0 ? sqrt(v) : 0;
// I'm assuming that it will be 0 if it's not passed in. (The values form a unit
// quaternion, so the angle can be computed from the direction vector.)
float sx = data.data[0], sy = data.data[1], sz = data.data[2];
float v = 1.0f - sx*sx - sy*sy - sz*sz;
mSensorValues.AppendElement(v > 0.0f ? sqrt(v) : 0.0f);
} else {
mSensorValues.AppendElement(data.data[3]);
}
} else if (mSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
mSensorValues.AppendElement(data.data[2]);
mSensorValues.AppendElement(data.data[3]);
} else {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
mSensorValues.AppendElement(data.data[2]);
}
mSensorData.values() = mSensorValues;
}
~SensorRunnable() {}
NS_IMETHOD Run() override
{
NotifySensorChange(mSensorData);
return NS_OK;
}
private:
SensorData mSensorData;
AutoTArray<float, 4> mSensorValues;
};
namespace hal_impl {
static DebugOnly<int> sSensorRefCount[NUM_SENSOR_TYPE];
static base::Thread* sPollingThread;
static sensors_poll_device_t* sSensorDevice;
static sensors_module_t* sSensorModule;
static void
PollSensors()
{
const size_t numEventMax = 16;
sensors_event_t buffer[numEventMax];
const sensor_t* sensors;
int size = sSensorModule->get_sensors_list(sSensorModule, &sensors);
do {
// didn't check sSensorDevice because already be done on creating pollingThread.
int n = sSensorDevice->poll(sSensorDevice, buffer, numEventMax);
if (n < 0) {
HAL_ERR("Error polling for sensor data (err=%d)", n);
break;
}
for (int i = 0; i < n; ++i) {
// FIXME: bug 802004, add proper support for the magnetic field sensor.
if (buffer[i].type == SENSOR_TYPE_MAGNETIC_FIELD)
continue;
// Bug 938035, transfer HAL data for orientation sensor to meet w3c spec
// ex: HAL report alpha=90 means East but alpha=90 means West in w3c spec
if (buffer[i].type == SENSOR_TYPE_ORIENTATION) {
buffer[i].orientation.azimuth = 360 - buffer[i].orientation.azimuth;
buffer[i].orientation.pitch = -buffer[i].orientation.pitch;
buffer[i].orientation.roll = -buffer[i].orientation.roll;
}
if (HardwareSensorToHalSensor(buffer[i].type) == SENSOR_UNKNOWN) {
// Emulator is broken and gives us events without types set
int index;
for (index = 0; index < size; index++) {
if (sensors[index].handle == buffer[i].sensor) {
break;
}
}
if (index < size &&
HardwareSensorToHalSensor(sensors[index].type) != SENSOR_UNKNOWN) {
buffer[i].type = sensors[index].type;
} else {
HAL_LOG("Could not determine sensor type of event");
continue;
}
}
NS_DispatchToMainThread(new SensorRunnable(buffer[i], sensors, size));
}
} while (true);
}
static void
SwitchSensor(bool aActivate, sensor_t aSensor, pthread_t aThreadId)
{
int index = HardwareSensorToHalSensor(aSensor.type);
MOZ_ASSERT(sSensorRefCount[index] || aActivate);
sSensorDevice->activate(sSensorDevice, aSensor.handle, aActivate);
if (aActivate) {
if (aSensor.type == SENSOR_TYPE_ACCELEROMETER) {
sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
ACCELEROMETER_POLL_RATE);
} else {
sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
DEFAULT_DEVICE_POLL_RATE);
}
}
if (aActivate) {
sSensorRefCount[index]++;
} else {
sSensorRefCount[index]--;
}
}
static void
SetSensorState(SensorType aSensor, bool activate)
{
int type = HalSensorToHardwareSensor(aSensor);
const sensor_t* sensors = nullptr;
int size = sSensorModule->get_sensors_list(sSensorModule, &sensors);
for (ssize_t i = 0; i < size; i++) {
if (sensors[i].type == type) {
SwitchSensor(activate, sensors[i], pthread_self());
break;
}
}
}
static void
EnableSensorNotificationsInternal(SensorType aSensor)
{
if (!sSensorModule) {
hw_get_module(SENSORS_HARDWARE_MODULE_ID,
(hw_module_t const**)&sSensorModule);
if (!sSensorModule) {
HAL_ERR("Can't get sensor HAL module\n");
return;
}
sensors_open(&sSensorModule->common, &sSensorDevice);
if (!sSensorDevice) {
sSensorModule = nullptr;
HAL_ERR("Can't get sensor poll device from module \n");
return;
}
sensor_t const* sensors;
int count = sSensorModule->get_sensors_list(sSensorModule, &sensors);
for (size_t i=0 ; i<size_t(count) ; i++) {
sSensorDevice->activate(sSensorDevice, sensors[i].handle, 0);
}
}
if (!sPollingThread) {
sPollingThread = new base::Thread("GonkSensors");
MOZ_ASSERT(sPollingThread);
// sPollingThread never terminates because poll may never return
sPollingThread->Start();
sPollingThread->message_loop()->PostTask(
NewRunnableFunction(PollSensors));
}
SetSensorState(aSensor, true);
}
static void
DisableSensorNotificationsInternal(SensorType aSensor)
{
if (!sSensorModule) {
return;
}
SetSensorState(aSensor, false);
}
//
// Daemon
//
typedef detail::SaturateOp<uint32_t> SaturateOpUint32;
/**
* The poll notification handler receives all events about sensors and
* sensor events.
*/
class SensorsPollNotificationHandler final
: public GonkSensorsPollNotificationHandler
{
public:
SensorsPollNotificationHandler(GonkSensorsPollInterface* aPollInterface)
: mPollInterface(aPollInterface)
{
MOZ_ASSERT(mPollInterface);
mPollInterface->SetNotificationHandler(this);
}
void EnableSensorsByType(SensorsType aType)
{
if (SaturateOpUint32(mClasses[aType].mActivated)++) {
return;
}
SensorsDeliveryMode deliveryMode = DefaultSensorsDeliveryMode(aType);
// Old ref-count for the sensor type was 0, so we
// activate all sensors of the type.
for (size_t i = 0; i < mSensors.Length(); ++i) {
if (mSensors[i].mType == aType &&
mSensors[i].mDeliveryMode == deliveryMode) {
mPollInterface->EnableSensor(mSensors[i].mId, nullptr);
mPollInterface->SetPeriod(mSensors[i].mId, DefaultSensorPeriod(aType),
nullptr);
}
}
}
void DisableSensorsByType(SensorsType aType)
{
if (SaturateOpUint32(mClasses[aType].mActivated)-- != 1) {
return;
}
SensorsDeliveryMode deliveryMode = DefaultSensorsDeliveryMode(aType);
// Old ref-count for the sensor type was 1, so we
// deactivate all sensors of the type.
for (size_t i = 0; i < mSensors.Length(); ++i) {
if (mSensors[i].mType == aType &&
mSensors[i].mDeliveryMode == deliveryMode) {
mPollInterface->DisableSensor(mSensors[i].mId, nullptr);
}
}
}
void ClearSensorClasses()
{
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mClasses); ++i) {
mClasses[i] = SensorsSensorClass();
}
}
void ClearSensors()
{
mSensors.Clear();
}
// Methods for SensorsPollNotificationHandler
//
void ErrorNotification(SensorsError aError) override
{
// XXX: Bug 1206056: Try to repair some of the errors or restart cleanly.
}
void SensorDetectedNotification(int32_t aId, SensorsType aType,
float aRange, float aResolution,
float aPower, int32_t aMinPeriod,
int32_t aMaxPeriod,
SensorsTriggerMode aTriggerMode,
SensorsDeliveryMode aDeliveryMode) override
{
auto i = FindSensorIndexById(aId);
if (i == -1) {
// Add a new sensor...
i = mSensors.Length();
mSensors.AppendElement(SensorsSensor(aId, aType, aRange, aResolution,
aPower, aMinPeriod, aMaxPeriod,
aTriggerMode, aDeliveryMode));
} else {
// ...or update an existing one.
mSensors[i] = SensorsSensor(aId, aType, aRange, aResolution, aPower,
aMinPeriod, aMaxPeriod, aTriggerMode,
aDeliveryMode);
}
mClasses[aType].UpdateFromSensor(mSensors[i]);
if (mClasses[aType].mActivated &&
mSensors[i].mDeliveryMode == DefaultSensorsDeliveryMode(aType)) {
// The new sensor's type is enabled, so enable sensor.
mPollInterface->EnableSensor(aId, nullptr);
mPollInterface->SetPeriod(mSensors[i].mId, DefaultSensorPeriod(aType),
nullptr);
}
}
void SensorLostNotification(int32_t aId) override
{
auto i = FindSensorIndexById(aId);
if (i != -1) {
mSensors.RemoveElementAt(i);
}
}
void EventNotification(int32_t aId, const SensorsEvent& aEvent) override
{
auto i = FindSensorIndexById(aId);
if (i == -1) {
HAL_ERR("Sensor %d not registered", aId);
return;
}
SensorData sensorData;
auto rv = CreateSensorData(aEvent, mClasses[mSensors[i].mType],
sensorData);
if (NS_FAILED(rv)) {
return;
}
NotifySensorChange(sensorData);
}
private:
ssize_t FindSensorIndexById(int32_t aId) const
{
for (size_t i = 0; i < mSensors.Length(); ++i) {
if (mSensors[i].mId == aId) {
return i;
}
}
return -1;
}
uint64_t DefaultSensorPeriod(SensorsType aType) const
{
return aType == SENSORS_TYPE_ACCELEROMETER ? ACCELEROMETER_POLL_RATE
: DEFAULT_DEVICE_POLL_RATE;
}
SensorsDeliveryMode DefaultSensorsDeliveryMode(SensorsType aType) const
{
if (aType == SENSORS_TYPE_PROXIMITY ||
aType == SENSORS_TYPE_SIGNIFICANT_MOTION) {
return SENSORS_DELIVERY_MODE_IMMEDIATE;
}
return SENSORS_DELIVERY_MODE_BEST_EFFORT;
}
SensorType HardwareSensorToHalSensor(SensorsType aType) const
{
// FIXME: bug 802004, add proper support for the magnetic-field sensor.
switch (aType) {
case SENSORS_TYPE_ORIENTATION:
return SENSOR_ORIENTATION;
case SENSORS_TYPE_ACCELEROMETER:
return SENSOR_ACCELERATION;
case SENSORS_TYPE_PROXIMITY:
return SENSOR_PROXIMITY;
case SENSORS_TYPE_LIGHT:
return SENSOR_LIGHT;
case SENSORS_TYPE_GYROSCOPE:
return SENSOR_GYROSCOPE;
case SENSORS_TYPE_LINEAR_ACCELERATION:
return SENSOR_LINEAR_ACCELERATION;
case SENSORS_TYPE_ROTATION_VECTOR:
return SENSOR_ROTATION_VECTOR;
case SENSORS_TYPE_GAME_ROTATION_VECTOR:
return SENSOR_GAME_ROTATION_VECTOR;
default:
NS_NOTREACHED("Invalid sensors type");
}
return SENSOR_UNKNOWN;
}
SensorAccuracyType HardwareStatusToHalAccuracy(SensorsStatus aStatus) const
{
return static_cast<SensorAccuracyType>(aStatus - 1);
}
nsresult CreateSensorData(const SensorsEvent& aEvent,
const SensorsSensorClass& aSensorClass,
SensorData& aSensorData) const
{
AutoTArray<float, 4> sensorValues;
auto sensor = HardwareSensorToHalSensor(aEvent.mType);
if (sensor == SENSOR_UNKNOWN) {
return NS_ERROR_ILLEGAL_VALUE;
}
aSensorData.sensor() = sensor;
aSensorData.accuracy() = HardwareStatusToHalAccuracy(aEvent.mStatus);
aSensorData.timestamp() = aEvent.mTimestamp;
if (aSensorData.sensor() == SENSOR_ORIENTATION) {
// Bug 938035: transfer HAL data for orientation sensor to meet W3C spec
// ex: HAL report alpha=90 means East but alpha=90 means West in W3C spec
sensorValues.AppendElement(360.0 - radToDeg(aEvent.mData.mFloat[0]));
sensorValues.AppendElement(-radToDeg(aEvent.mData.mFloat[1]));
sensorValues.AppendElement(-radToDeg(aEvent.mData.mFloat[2]));
} else if (aSensorData.sensor() == SENSOR_ACCELERATION) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
sensorValues.AppendElement(aEvent.mData.mFloat[1]);
sensorValues.AppendElement(aEvent.mData.mFloat[2]);
} else if (aSensorData.sensor() == SENSOR_PROXIMITY) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
sensorValues.AppendElement(aSensorClass.mMinValue);
sensorValues.AppendElement(aSensorClass.mMaxValue);
} else if (aSensorData.sensor() == SENSOR_LINEAR_ACCELERATION) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
sensorValues.AppendElement(aEvent.mData.mFloat[1]);
sensorValues.AppendElement(aEvent.mData.mFloat[2]);
} else if (aSensorData.sensor() == SENSOR_GYROSCOPE) {
sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[0]));
sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[1]));
sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[2]));
} else if (aSensorData.sensor() == SENSOR_LIGHT) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
} else if (aSensorData.sensor() == SENSOR_ROTATION_VECTOR) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
sensorValues.AppendElement(aEvent.mData.mFloat[1]);
sensorValues.AppendElement(aEvent.mData.mFloat[2]);
sensorValues.AppendElement(aEvent.mData.mFloat[3]);
} else if (aSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) {
sensorValues.AppendElement(aEvent.mData.mFloat[0]);
sensorValues.AppendElement(aEvent.mData.mFloat[1]);
sensorValues.AppendElement(aEvent.mData.mFloat[2]);
sensorValues.AppendElement(aEvent.mData.mFloat[3]);
}
aSensorData.values() = sensorValues;
return NS_OK;
}
GonkSensorsPollInterface* mPollInterface;
nsTArray<SensorsSensor> mSensors;
SensorsSensorClass mClasses[SENSORS_NUM_TYPES];
};
static StaticAutoPtr<SensorsPollNotificationHandler> sPollNotificationHandler;
/**
* This is the notifiaction handler for the Sensors interface. If the backend
* crashes, we can restart it from here.
*/
class SensorsNotificationHandler final : public GonkSensorsNotificationHandler
{
public:
SensorsNotificationHandler(GonkSensorsInterface* aInterface)
: mInterface(aInterface)
{
MOZ_ASSERT(mInterface);
mInterface->SetNotificationHandler(this);
}
void BackendErrorNotification(bool aCrashed) override
{
// XXX: Bug 1206056: restart sensorsd
}
private:
GonkSensorsInterface* mInterface;
};
static StaticAutoPtr<SensorsNotificationHandler> sNotificationHandler;
/**
* |SensorsRegisterModuleResultHandler| implements the result-handler
* callback for registering the Poll service and activating the first
* sensors. If an error occures during the process, the result handler
* disconnects and closes the backend.
*/
class SensorsRegisterModuleResultHandler final
: public GonkSensorsRegistryResultHandler
{
public:
SensorsRegisterModuleResultHandler(
uint32_t* aSensorsTypeActivated,
GonkSensorsInterface* aInterface)
: mSensorsTypeActivated(aSensorsTypeActivated)
, mInterface(aInterface)
{
MOZ_ASSERT(mSensorsTypeActivated);
MOZ_ASSERT(mInterface);
}
void OnError(SensorsError aError) override
{
GonkSensorsRegistryResultHandler::OnError(aError); // print error message
Disconnect(); // Registering failed, so close the connection completely
}
void RegisterModule(uint32_t aProtocolVersion) override
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sPollNotificationHandler);
// Init, step 3: set notification handler for poll service and vice versa
auto pollInterface = mInterface->GetSensorsPollInterface();
if (!pollInterface) {
Disconnect();
return;
}
if (NS_FAILED(pollInterface->SetProtocolVersion(aProtocolVersion))) {
Disconnect();
return;
}
sPollNotificationHandler =
new SensorsPollNotificationHandler(pollInterface);
// Init, step 4: activate sensors
for (int i = 0; i < SENSORS_NUM_TYPES; ++i) {
while (mSensorsTypeActivated[i]) {
sPollNotificationHandler->EnableSensorsByType(
static_cast<SensorsType>(i));
--mSensorsTypeActivated[i];
}
}
}
public:
void Disconnect()
{
class DisconnectResultHandler final : public GonkSensorsResultHandler
{
public:
void OnError(SensorsError aError)
{
GonkSensorsResultHandler::OnError(aError); // print error message
sNotificationHandler = nullptr;
}
void Disconnect() override
{
sNotificationHandler = nullptr;
}
};
mInterface->Disconnect(new DisconnectResultHandler());
}
private:
uint32_t* mSensorsTypeActivated;
GonkSensorsInterface* mInterface;
};
/**
* |SensorsConnectResultHandler| implements the result-handler
* callback for starting the Sensors backend.
*/
class SensorsConnectResultHandler final : public GonkSensorsResultHandler
{
public:
SensorsConnectResultHandler(
uint32_t* aSensorsTypeActivated,
GonkSensorsInterface* aInterface)
: mSensorsTypeActivated(aSensorsTypeActivated)
, mInterface(aInterface)
{
MOZ_ASSERT(mSensorsTypeActivated);
MOZ_ASSERT(mInterface);
}
void OnError(SensorsError aError) override
{
GonkSensorsResultHandler::OnError(aError); // print error message
sNotificationHandler = nullptr;
}
void Connect() override
{
MOZ_ASSERT(NS_IsMainThread());
// Init, step 2: register poll service
auto registryInterface = mInterface->GetSensorsRegistryInterface();
if (!registryInterface) {
return;
}
registryInterface->RegisterModule(
GonkSensorsPollModule::SERVICE_ID,
new SensorsRegisterModuleResultHandler(mSensorsTypeActivated,
mInterface));
}
private:
uint32_t* mSensorsTypeActivated;
GonkSensorsInterface* mInterface;
};
static uint32_t sSensorsTypeActivated[SENSORS_NUM_TYPES];
static const SensorsType sSensorsType[] = {
[SENSOR_ORIENTATION] = SENSORS_TYPE_ORIENTATION,
[SENSOR_ACCELERATION] = SENSORS_TYPE_ACCELEROMETER,
[SENSOR_PROXIMITY] = SENSORS_TYPE_PROXIMITY,
[SENSOR_LINEAR_ACCELERATION] = SENSORS_TYPE_LINEAR_ACCELERATION,
[SENSOR_GYROSCOPE] = SENSORS_TYPE_GYROSCOPE,
[SENSOR_LIGHT] = SENSORS_TYPE_LIGHT,
[SENSOR_ROTATION_VECTOR] = SENSORS_TYPE_ROTATION_VECTOR,
[SENSOR_GAME_ROTATION_VECTOR] = SENSORS_TYPE_GAME_ROTATION_VECTOR
};
void
EnableSensorNotificationsDaemon(SensorType aSensor)
{
if ((aSensor < 0) ||
(aSensor > static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sSensorsType)))) {
HAL_ERR("Sensor type %d not known", aSensor);
return; // Unsupported sensor type
}
auto interface = GonkSensorsInterface::GetInstance();
if (!interface) {
return;
}
if (sPollNotificationHandler) {
// Everythings already up and running; enable sensor type.
sPollNotificationHandler->EnableSensorsByType(sSensorsType[aSensor]);
return;
}
++SaturateOpUint32(sSensorsTypeActivated[sSensorsType[aSensor]]);
if (sNotificationHandler) {
// We are in the middle of a pending start up; nothing else to do.
return;
}
// Start up
MOZ_ASSERT(!sPollNotificationHandler);
MOZ_ASSERT(!sNotificationHandler);
sNotificationHandler = new SensorsNotificationHandler(interface);
// Init, step 1: connect to Sensors backend
interface->Connect(
sNotificationHandler,
new SensorsConnectResultHandler(sSensorsTypeActivated, interface));
}
void
DisableSensorNotificationsDaemon(SensorType aSensor)
{
if ((aSensor < 0) ||
(aSensor > static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sSensorsType)))) {
HAL_ERR("Sensor type %d not known", aSensor);
return; // Unsupported sensor type
}
if (sPollNotificationHandler) {
// Everthings up and running; disable sensors type
sPollNotificationHandler->DisableSensorsByType(sSensorsType[aSensor]);
return;
}
// We might be in the middle of a startup; decrement type's ref-counter.
--SaturateOpUint32(sSensorsTypeActivated[sSensorsType[aSensor]]);
// TODO: stop sensorsd if all sensors are disabled
}
//
// Public interface
//
// TODO: Remove in-Gecko sensors code. Until all devices' base
// images come with sensorsd installed, we have to support the
// in-Gecko implementation as well. So we test for the existance
// of the binary. If it's there, we use it. Otherwise we run the
// old code.
static bool
HasDaemon()
{
static bool tested;
static bool hasDaemon;
if (MOZ_UNLIKELY(!tested)) {
hasDaemon = !access("/system/bin/sensorsd", X_OK);
tested = true;
}
return hasDaemon;
}
void
EnableSensorNotifications(SensorType aSensor)
{
if (HasDaemon()) {
EnableSensorNotificationsDaemon(aSensor);
} else {
EnableSensorNotificationsInternal(aSensor);
}
}
void
DisableSensorNotifications(SensorType aSensor)
{
if (HasDaemon()) {
DisableSensorNotificationsDaemon(aSensor);
} else {
DisableSensorNotificationsInternal(aSensor);
}
}
} // hal_impl
} // mozilla

View file

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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 "GonkSensorsHelpers.h"
namespace mozilla {
namespace hal {
//
// Unpacking
//
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsEvent& aOut)
{
nsresult rv = UnpackPDU(aPDU, aOut.mType);
if (NS_FAILED(rv)) {
return rv;
}
rv = UnpackPDU(aPDU, aOut.mTimestamp);
if (NS_FAILED(rv)) {
return rv;
}
rv = UnpackPDU(aPDU, aOut.mStatus);
if (NS_FAILED(rv)) {
return rv;
}
size_t i = 0;
switch (aOut.mType) {
case SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
case SENSORS_TYPE_GYROSCOPE_UNCALIBRATED:
/* 6 data values */
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
/* fall through */
case SENSORS_TYPE_ROTATION_VECTOR:
case SENSORS_TYPE_GAME_ROTATION_VECTOR:
case SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
/* 5 data values */
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
/* fall through */
case SENSORS_TYPE_ACCELEROMETER:
case SENSORS_TYPE_GEOMAGNETIC_FIELD:
case SENSORS_TYPE_ORIENTATION:
case SENSORS_TYPE_GYROSCOPE:
case SENSORS_TYPE_GRAVITY:
case SENSORS_TYPE_LINEAR_ACCELERATION:
/* 3 data values */
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
/* fall through */
case SENSORS_TYPE_LIGHT:
case SENSORS_TYPE_PRESSURE:
case SENSORS_TYPE_TEMPERATURE:
case SENSORS_TYPE_PROXIMITY:
case SENSORS_TYPE_RELATIVE_HUMIDITY:
case SENSORS_TYPE_AMBIENT_TEMPERATURE:
case SENSORS_TYPE_HEART_RATE:
case SENSORS_TYPE_TILT_DETECTOR:
case SENSORS_TYPE_WAKE_GESTURE:
case SENSORS_TYPE_GLANCE_GESTURE:
case SENSORS_TYPE_PICK_UP_GESTURE:
case SENSORS_TYPE_WRIST_TILT_GESTURE:
case SENSORS_TYPE_SIGNIFICANT_MOTION:
case SENSORS_TYPE_STEP_DETECTED:
/* 1 data value */
rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
if (NS_FAILED(rv)) {
return rv;
}
break;
case SENSORS_TYPE_STEP_COUNTER:
/* 1 data value */
rv = UnpackPDU(aPDU, aOut.mData.mUint[0]);
if (NS_FAILED(rv)) {
return rv;
}
break;
default:
if (MOZ_HAL_IPC_UNPACK_WARN_IF(true, SensorsEvent)) {
return NS_ERROR_ILLEGAL_VALUE;
}
}
rv = UnpackPDU(aPDU, aOut.mDeliveryMode);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
} // namespace hal
} // namespace mozilla

View file

@ -0,0 +1,226 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef hal_gonk_GonkSensorsHelpers_h
#define hal_gonk_GonkSensorsHelpers_h
#include <mozilla/ipc/DaemonSocketPDU.h>
#include <mozilla/ipc/DaemonSocketPDUHelpers.h>
#include "SensorsTypes.h"
namespace mozilla {
namespace hal {
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketPDUHelpers::Convert;
using mozilla::ipc::DaemonSocketPDUHelpers::PackPDU;
using mozilla::ipc::DaemonSocketPDUHelpers::UnpackPDU;
using namespace mozilla::ipc::DaemonSocketPDUHelpers;
//
// Conversion
//
// The functions below convert the input value to the output value's
// type and perform extension tests on the validity of the result. On
// success the output value will be returned in |aOut|. The functions
// return NS_OK on success, or an XPCOM error code otherwise.
//
// See the documentation of the HAL IPC framework for more information
// on conversion functions.
//
nsresult
Convert(int32_t aIn, SensorsStatus& aOut)
{
static const uint8_t sStatus[] = {
[0] = SENSORS_STATUS_NO_CONTACT, // '-1'
[1] = SENSORS_STATUS_UNRELIABLE, // '0'
[2] = SENSORS_STATUS_ACCURACY_LOW, // '1'
[3] = SENSORS_STATUS_ACCURACY_MEDIUM, // '2'
[4] = SENSORS_STATUS_ACCURACY_HIGH // '3'
};
static const int8_t sOffset = -1; // '-1' is the lower bound of the status
if (MOZ_HAL_IPC_CONVERT_WARN_IF(aIn < sOffset, int32_t, SensorsStatus) ||
MOZ_HAL_IPC_CONVERT_WARN_IF(
aIn >= (static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sStatus)) + sOffset),
int32_t, SensorsStatus)) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = static_cast<SensorsStatus>(sStatus[aIn - sOffset]);
return NS_OK;
}
nsresult
Convert(uint8_t aIn, SensorsDeliveryMode& aOut)
{
static const uint8_t sMode[] = {
[0x00] = SENSORS_DELIVERY_MODE_BEST_EFFORT,
[0x01] = SENSORS_DELIVERY_MODE_IMMEDIATE
};
if (MOZ_HAL_IPC_CONVERT_WARN_IF(
aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, SensorsDeliveryMode)) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = static_cast<SensorsDeliveryMode>(sMode[aIn]);
return NS_OK;
}
nsresult
Convert(uint8_t aIn, SensorsError& aOut)
{
static const uint8_t sError[] = {
[0x00] = SENSORS_ERROR_NONE,
[0x01] = SENSORS_ERROR_FAIL,
[0x02] = SENSORS_ERROR_NOT_READY,
[0x03] = SENSORS_ERROR_NOMEM,
[0x04] = SENSORS_ERROR_BUSY,
[0x05] = SENSORS_ERROR_DONE,
[0x06] = SENSORS_ERROR_UNSUPPORTED,
[0x07] = SENSORS_ERROR_PARM_INVALID
};
if (MOZ_HAL_IPC_CONVERT_WARN_IF(
aIn >= MOZ_ARRAY_LENGTH(sError), uint8_t, SensorsError)) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = static_cast<SensorsError>(sError[aIn]);
return NS_OK;
}
nsresult
Convert(uint8_t aIn, SensorsTriggerMode& aOut)
{
static const uint8_t sMode[] = {
[0x00] = SENSORS_TRIGGER_MODE_CONTINUOUS,
[0x01] = SENSORS_TRIGGER_MODE_ON_CHANGE,
[0x02] = SENSORS_TRIGGER_MODE_ONE_SHOT,
[0x03] = SENSORS_TRIGGER_MODE_SPECIAL
};
if (MOZ_HAL_IPC_CONVERT_WARN_IF(
aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, SensorsTriggerMode)) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = static_cast<SensorsTriggerMode>(sMode[aIn]);
return NS_OK;
}
nsresult
Convert(uint32_t aIn, SensorsType& aOut)
{
static const uint8_t sType[] = {
[0x00] = 0, // invalid, required by gcc
[0x01] = SENSORS_TYPE_ACCELEROMETER,
[0x02] = SENSORS_TYPE_GEOMAGNETIC_FIELD,
[0x03] = SENSORS_TYPE_ORIENTATION,
[0x04] = SENSORS_TYPE_GYROSCOPE,
[0x05] = SENSORS_TYPE_LIGHT,
[0x06] = SENSORS_TYPE_PRESSURE,
[0x07] = SENSORS_TYPE_TEMPERATURE,
[0x08] = SENSORS_TYPE_PROXIMITY,
[0x09] = SENSORS_TYPE_GRAVITY,
[0x0a] = SENSORS_TYPE_LINEAR_ACCELERATION,
[0x0b] = SENSORS_TYPE_ROTATION_VECTOR,
[0x0c] = SENSORS_TYPE_RELATIVE_HUMIDITY,
[0x0d] = SENSORS_TYPE_AMBIENT_TEMPERATURE,
[0x0e] = SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
[0x0f] = SENSORS_TYPE_GAME_ROTATION_VECTOR,
[0x10] = SENSORS_TYPE_GYROSCOPE_UNCALIBRATED,
[0x11] = SENSORS_TYPE_SIGNIFICANT_MOTION,
[0x12] = SENSORS_TYPE_STEP_DETECTED,
[0x13] = SENSORS_TYPE_STEP_COUNTER,
[0x14] = SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR,
[0x15] = SENSORS_TYPE_HEART_RATE,
[0x16] = SENSORS_TYPE_TILT_DETECTOR,
[0x17] = SENSORS_TYPE_WAKE_GESTURE,
[0x18] = SENSORS_TYPE_GLANCE_GESTURE,
[0x19] = SENSORS_TYPE_PICK_UP_GESTURE,
[0x1a] = SENSORS_TYPE_WRIST_TILT_GESTURE
};
if (MOZ_HAL_IPC_CONVERT_WARN_IF(
!aIn, uint32_t, SensorsType) ||
MOZ_HAL_IPC_CONVERT_WARN_IF(
aIn >= MOZ_ARRAY_LENGTH(sType), uint32_t, SensorsType)) {
return NS_ERROR_ILLEGAL_VALUE;
}
aOut = static_cast<SensorsType>(sType[aIn]);
return NS_OK;
}
nsresult
Convert(nsresult aIn, SensorsError& aOut)
{
if (NS_SUCCEEDED(aIn)) {
aOut = SENSORS_ERROR_NONE;
} else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
aOut = SENSORS_ERROR_NOMEM;
} else if (aIn == NS_ERROR_ILLEGAL_VALUE) {
aOut = SENSORS_ERROR_PARM_INVALID;
} else {
aOut = SENSORS_ERROR_FAIL;
}
return NS_OK;
}
//
// Packing
//
// Pack functions store a value in PDU. See the documentation of the
// HAL IPC framework for more information.
//
// There are currently no sensor-specific pack functions necessary. If
// you add one, put it below.
//
//
// Unpacking
//
// Unpack function retrieve a value from a PDU. The functions return
// NS_OK on success, or an XPCOM error code otherwise. On sucess, the
// returned value is stored in the second argument |aOut|.
//
// See the documentation of the HAL IPC framework for more information
// on unpack functions.
//
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsDeliveryMode& aOut)
{
return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsDeliveryMode>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsError& aOut)
{
return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsError>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsEvent& aOut);
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsStatus& aOut)
{
return UnpackPDU(aPDU, UnpackConversion<int32_t, SensorsStatus>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsTriggerMode& aOut)
{
return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsTriggerMode>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, SensorsType& aOut)
{
return UnpackPDU(aPDU, UnpackConversion<uint32_t, SensorsType>(aOut));
}
} // namespace hal
} // namespace mozilla
#endif // hal_gonk_GonkSensorsHelpers_h

View file

@ -0,0 +1,494 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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 "GonkSensorsInterface.h"
#include "GonkSensorsPollInterface.h"
#include "GonkSensorsRegistryInterface.h"
#include "HalLog.h"
#include <mozilla/ipc/DaemonSocket.h>
#include <mozilla/ipc/DaemonSocketConnector.h>
#include <mozilla/ipc/ListenSocket.h>
namespace mozilla {
namespace hal {
using namespace mozilla::ipc;
//
// GonkSensorsResultHandler
//
void
GonkSensorsResultHandler::OnError(SensorsError aError)
{
HAL_ERR("Received error code %d", static_cast<int>(aError));
}
void
GonkSensorsResultHandler::Connect()
{ }
void
GonkSensorsResultHandler::Disconnect()
{ }
GonkSensorsResultHandler::~GonkSensorsResultHandler()
{ }
//
// GonkSensorsNotificationHandler
//
void
GonkSensorsNotificationHandler::BackendErrorNotification(bool aCrashed)
{
if (aCrashed) {
HAL_ERR("Sensors backend crashed");
} else {
HAL_ERR("Error in sensors backend");
}
}
GonkSensorsNotificationHandler::~GonkSensorsNotificationHandler()
{ }
//
// GonkSensorsProtocol
//
class GonkSensorsProtocol final
: public DaemonSocketIOConsumer
, public GonkSensorsRegistryModule
, public GonkSensorsPollModule
{
public:
GonkSensorsProtocol();
void SetConnection(DaemonSocket* aConnection);
already_AddRefed<DaemonSocketResultHandler> FetchResultHandler(
const DaemonSocketPDUHeader& aHeader);
// Methods for |SensorsRegistryModule| and |SensorsPollModule|
//
nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) override;
// Methods for |DaemonSocketIOConsumer|
//
void Handle(DaemonSocketPDU& aPDU) override;
void StoreResultHandler(const DaemonSocketPDU& aPDU) override;
private:
void HandleRegistrySvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandlePollSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
DaemonSocket* mConnection;
nsTArray<RefPtr<DaemonSocketResultHandler>> mResultHandlerQ;
};
GonkSensorsProtocol::GonkSensorsProtocol()
{ }
void
GonkSensorsProtocol::SetConnection(DaemonSocket* aConnection)
{
mConnection = aConnection;
}
already_AddRefed<DaemonSocketResultHandler>
GonkSensorsProtocol::FetchResultHandler(const DaemonSocketPDUHeader& aHeader)
{
MOZ_ASSERT(!NS_IsMainThread());
if (aHeader.mOpcode & 0x80) {
return nullptr; // Ignore notifications
}
RefPtr<DaemonSocketResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
return res.forget();
}
void
GonkSensorsProtocol::HandleRegistrySvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
GonkSensorsRegistryModule::HandleSvc(aHeader, aPDU, aRes);
}
void
GonkSensorsProtocol::HandlePollSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
GonkSensorsPollModule::HandleSvc(aHeader, aPDU, aRes);
}
// |SensorsRegistryModule|, |SensorsPollModule|
nsresult
GonkSensorsProtocol::Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes)
{
MOZ_ASSERT(mConnection);
MOZ_ASSERT(aPDU);
aPDU->SetConsumer(this);
aPDU->SetResultHandler(aRes);
aPDU->UpdateHeader();
if (mConnection->GetConnectionStatus() == SOCKET_DISCONNECTED) {
HAL_ERR("Sensors socket is disconnected");
return NS_ERROR_FAILURE;
}
mConnection->SendSocketData(aPDU); // Forward PDU to data channel
return NS_OK;
}
// |DaemonSocketIOConsumer|
void
GonkSensorsProtocol::Handle(DaemonSocketPDU& aPDU)
{
static void (GonkSensorsProtocol::* const HandleSvc[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[GonkSensorsRegistryModule::SERVICE_ID] =
&GonkSensorsProtocol::HandleRegistrySvc,
[GonkSensorsPollModule::SERVICE_ID] =
&GonkSensorsProtocol::HandlePollSvc
};
DaemonSocketPDUHeader header;
if (NS_FAILED(UnpackPDU(aPDU, header))) {
return;
}
if (!(header.mService < MOZ_ARRAY_LENGTH(HandleSvc)) ||
!HandleSvc[header.mService]) {
HAL_ERR("Sensors service %d unknown", header.mService);
return;
}
RefPtr<DaemonSocketResultHandler> res = FetchResultHandler(header);
(this->*(HandleSvc[header.mService]))(header, aPDU, res);
}
void
GonkSensorsProtocol::StoreResultHandler(const DaemonSocketPDU& aPDU)
{
MOZ_ASSERT(!NS_IsMainThread());
mResultHandlerQ.AppendElement(aPDU.GetResultHandler());
}
//
// GonkSensorsInterface
//
GonkSensorsInterface*
GonkSensorsInterface::GetInstance()
{
static GonkSensorsInterface* sGonkSensorsInterface;
if (sGonkSensorsInterface) {
return sGonkSensorsInterface;
}
sGonkSensorsInterface = new GonkSensorsInterface();
return sGonkSensorsInterface;
}
void
GonkSensorsInterface::SetNotificationHandler(
GonkSensorsNotificationHandler* aNotificationHandler)
{
MOZ_ASSERT(NS_IsMainThread());
mNotificationHandler = aNotificationHandler;
}
/*
* The connect procedure consists of several steps.
*
* (1) Start listening for the command channel's socket connection: We
* do this before anything else, so that we don't miss connection
* requests from the Sensors daemon. This step will create a listen
* socket.
*
* (2) Start the Sensors daemon: When the daemon starts up it will open
* a socket connection to Gecko and thus create the data channel.
* Gecko already opened the listen socket in step (1). Step (2) ends
* with the creation of the data channel.
*
* (3) Signal success to the caller.
*
* If any step fails, we roll-back the procedure and signal an error to the
* caller.
*/
void
GonkSensorsInterface::Connect(GonkSensorsNotificationHandler* aNotificationHandler,
GonkSensorsResultHandler* aRes)
{
#define BASE_SOCKET_NAME "sensorsd"
static unsigned long POSTFIX_LENGTH = 16;
// If we could not cleanup properly before and an old
// instance of the daemon is still running, we kill it
// here.
mozilla::hal::StopSystemService("sensorsd");
mNotificationHandler = aNotificationHandler;
mResultHandlerQ.AppendElement(aRes);
if (!mProtocol) {
mProtocol = MakeUnique<GonkSensorsProtocol>();
}
if (!mListenSocket) {
mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
}
// Init, step 1: Listen for data channel... */
if (!mDataSocket) {
mDataSocket = new DaemonSocket(mProtocol.get(), this, DATA_SOCKET);
} else if (mDataSocket->GetConnectionStatus() == SOCKET_CONNECTED) {
// Command channel should not be open; let's close it.
mDataSocket->Close();
}
// The listen socket's name is generated with a random postfix. This
// avoids naming collisions if we still have a listen socket from a
// previously failed cleanup. It also makes it hard for malicious
// external programs to capture the socket name or connect before
// the daemon can do so. If no random postfix can be generated, we
// simply use the base name as-is.
nsresult rv = DaemonSocketConnector::CreateRandomAddressString(
NS_LITERAL_CSTRING(BASE_SOCKET_NAME), POSTFIX_LENGTH, mListenSocketName);
if (NS_FAILED(rv)) {
mListenSocketName.AssignLiteral(BASE_SOCKET_NAME);
}
rv = mListenSocket->Listen(new DaemonSocketConnector(mListenSocketName),
mDataSocket);
if (NS_FAILED(rv)) {
OnConnectError(DATA_SOCKET);
return;
}
// The protocol implementation needs a data channel for
// sending commands to the daemon. We set it here, because
// this is the earliest time when it's available.
mProtocol->SetConnection(mDataSocket);
}
/*
* Disconnecting is inverse to connecting.
*
* (1) Close data socket: We close the data channel and the daemon will
* will notice. Once we see the socket's disconnect, we continue with
* the cleanup.
*
* (2) Close listen socket: The listen socket is not active any longer
* and we simply close it.
*
* (3) Signal success to the caller.
*
* We don't have to stop the daemon explicitly. It will cleanup and quit
* after it noticed the closing of the data channel
*
* Rolling back half-completed cleanups is not possible. In the case of
* an error, we simply push forward and try to recover during the next
* initialization.
*/
void
GonkSensorsInterface::Disconnect(GonkSensorsResultHandler* aRes)
{
mNotificationHandler = nullptr;
// Cleanup, step 1: Close data channel
mDataSocket->Close();
mResultHandlerQ.AppendElement(aRes);
}
GonkSensorsRegistryInterface*
GonkSensorsInterface::GetSensorsRegistryInterface()
{
if (mRegistryInterface) {
return mRegistryInterface.get();
}
mRegistryInterface = MakeUnique<GonkSensorsRegistryInterface>(mProtocol.get());
return mRegistryInterface.get();
}
GonkSensorsPollInterface*
GonkSensorsInterface::GetSensorsPollInterface()
{
if (mPollInterface) {
return mPollInterface.get();
}
mPollInterface = MakeUnique<GonkSensorsPollInterface>(mProtocol.get());
return mPollInterface.get();
}
GonkSensorsInterface::GonkSensorsInterface()
: mNotificationHandler(nullptr)
{ }
GonkSensorsInterface::~GonkSensorsInterface()
{ }
void
GonkSensorsInterface::DispatchError(GonkSensorsResultHandler* aRes,
SensorsError aError)
{
DaemonResultRunnable1<GonkSensorsResultHandler, void,
SensorsError, SensorsError>::Dispatch(
aRes, &GonkSensorsResultHandler::OnError,
ConstantInitOp1<SensorsError>(aError));
}
void
GonkSensorsInterface::DispatchError(
GonkSensorsResultHandler* aRes, nsresult aRv)
{
SensorsError error;
if (NS_FAILED(Convert(aRv, error))) {
error = SENSORS_ERROR_FAIL;
}
DispatchError(aRes, error);
}
// |DaemonSocketConsumer|, |ListenSocketConsumer|
void
GonkSensorsInterface::OnConnectSuccess(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aIndex) {
case LISTEN_SOCKET: {
// Init, step 2: Start Sensors daemon
nsCString args("-a ");
args.Append(mListenSocketName);
mozilla::hal::StartSystemService("sensorsd", args.get());
}
break;
case DATA_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
// Init, step 3: Signal success
RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
if (res) {
res->Connect();
}
}
break;
}
}
void
GonkSensorsInterface::OnConnectError(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aIndex) {
case DATA_SOCKET:
// Stop daemon and close listen socket
mozilla::hal::StopSystemService("sensorsd");
mListenSocket->Close();
// fall through
case LISTEN_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
// Signal error to caller
RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
if (res) {
DispatchError(res, SENSORS_ERROR_FAIL);
}
}
break;
}
}
/*
* Disconnects can happend
*
* (a) during startup,
* (b) during regular service, or
* (c) during shutdown.
*
* For cases (a) and (c), |mResultHandlerQ| contains an element. For
* case (b) |mResultHandlerQ| will be empty. This distinguishes a crash in
* the daemon. The following procedure to recover from crashes consists of
* several steps for case (b).
*
* (1) Close listen socket.
* (2) Wait for all sockets to be disconnected and inform caller about
* the crash.
* (3) After all resources have been cleaned up, let the caller restart
* the daemon.
*/
void
GonkSensorsInterface::OnDisconnect(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
switch (aIndex) {
case DATA_SOCKET:
// Cleanup, step 2 (Recovery, step 1): Close listen socket
mListenSocket->Close();
break;
case LISTEN_SOCKET:
// Cleanup, step 3: Signal success to caller
if (!mResultHandlerQ.IsEmpty()) {
RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
if (res) {
res->Disconnect();
}
}
break;
}
/* For recovery make sure all sockets disconnected, in order to avoid
* the remaining disconnects interfere with the restart procedure.
*/
if (mNotificationHandler && mResultHandlerQ.IsEmpty()) {
if (mListenSocket->GetConnectionStatus() == SOCKET_DISCONNECTED &&
mDataSocket->GetConnectionStatus() == SOCKET_DISCONNECTED) {
// Recovery, step 2: Notify the caller to prepare the restart procedure.
mNotificationHandler->BackendErrorNotification(true);
mNotificationHandler = nullptr;
}
}
}
} // namespace hal
} // namespace mozilla

View file

@ -0,0 +1,191 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
/*
* The sensors interface gives you access to the low-level sensors code
* in a platform-independent manner. The interfaces in this file allow
* for starting an stopping the sensors driver. Specific functionality
* is implemented in sub-interfaces.
*/
#ifndef hal_gonk_GonkSensorsInterface_h
#define hal_gonk_GonkSensorsInterface_h
#include <mozilla/ipc/DaemonSocketConsumer.h>
#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
#include <mozilla/ipc/ListenSocketConsumer.h>
#include <mozilla/UniquePtr.h>
#include "SensorsTypes.h"
namespace mozilla {
namespace ipc {
class DaemonSocket;
class ListenSocket;
}
}
namespace mozilla {
namespace hal {
class GonkSensorsPollInterface;
class GonkSensorsProtocol;
class GonkSensorsRegistryInterface;
/**
* This class is the result-handler interface for the Sensors
* interface. Methods always run on the main thread.
*/
class GonkSensorsResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
/**
* Called if a command failed.
*
* @param aError The error code.
*/
virtual void OnError(SensorsError aError);
/**
* The callback method for |GonkSensorsInterface::Connect|.
*/
virtual void Connect();
/**
* The callback method for |GonkSensorsInterface::Connect|.
*/
virtual void Disconnect();
protected:
virtual ~GonkSensorsResultHandler();
};
/**
* This is the notification-handler interface. Implement this classes
* methods to handle event and notifications from the sensors daemon.
* All methods run on the main thread.
*/
class GonkSensorsNotificationHandler
{
public:
/**
* This notification is called when the backend code fails
* unexpectedly. Save state in the high-level code and restart
* the driver.
*
* @param aCrash True is the sensors driver crashed.
*/
virtual void BackendErrorNotification(bool aCrashed);
protected:
virtual ~GonkSensorsNotificationHandler();
};
/**
* This class implements the public interface to the Sensors functionality
* and driver. Use |GonkSensorsInterface::GetInstance| to retrieve an instance.
* All methods run on the main thread.
*/
class GonkSensorsInterface final
: public mozilla::ipc::DaemonSocketConsumer
, public mozilla::ipc::ListenSocketConsumer
{
public:
/**
* Returns an instance of the Sensors backend. This code can return
* |nullptr| if no Sensors backend is available.
*
* @return An instance of |GonkSensorsInterface|.
*/
static GonkSensorsInterface* GetInstance();
/**
* This method sets the notification handler for sensor notifications. Call
* this method immediately after retreiving an instance of the class, or you
* won't be able able to receive notifications. You may not free the handler
* class while the Sensors backend is connected.
*
* @param aNotificationHandler An instance of a notification handler.
*/
void SetNotificationHandler(
GonkSensorsNotificationHandler* aNotificationHandler);
/**
* This method starts the Sensors backend and establishes ad connection
* with Gecko. This is a multi-step process and errors are signalled by
* |GonkSensorsNotificationHandler::BackendErrorNotification|. If you see
* this notification before the connection has been established, it's
* certainly best to assume the Sensors backend to be not evailable.
*
* @param aRes The result handler.
*/
void Connect(GonkSensorsNotificationHandler* aNotificationHandler,
GonkSensorsResultHandler* aRes);
/**
* This method disconnects Gecko from the Sensors backend and frees
* the backend's resources. This will invalidate all interfaces and
* state. Don't use any sensors functionality without reconnecting
* first.
*
* @param aRes The result handler.
*/
void Disconnect(GonkSensorsResultHandler* aRes);
/**
* Returns the Registry interface for the connected Sensors backend.
*
* @return An instance of the Sensors Registry interface.
*/
GonkSensorsRegistryInterface* GetSensorsRegistryInterface();
/**
* Returns the Poll interface for the connected Sensors backend.
*
* @return An instance of the Sensors Poll interface.
*/
GonkSensorsPollInterface* GetSensorsPollInterface();
private:
enum Channel {
LISTEN_SOCKET,
DATA_SOCKET
};
GonkSensorsInterface();
~GonkSensorsInterface();
void DispatchError(GonkSensorsResultHandler* aRes, SensorsError aError);
void DispatchError(GonkSensorsResultHandler* aRes, nsresult aRv);
// Methods for |DaemonSocketConsumer| and |ListenSocketConsumer|
//
void OnConnectSuccess(int aIndex) override;
void OnConnectError(int aIndex) override;
void OnDisconnect(int aIndex) override;
nsCString mListenSocketName;
RefPtr<mozilla::ipc::ListenSocket> mListenSocket;
RefPtr<mozilla::ipc::DaemonSocket> mDataSocket;
UniquePtr<GonkSensorsProtocol> mProtocol;
nsTArray<RefPtr<GonkSensorsResultHandler> > mResultHandlerQ;
GonkSensorsNotificationHandler* mNotificationHandler;
UniquePtr<GonkSensorsRegistryInterface> mRegistryInterface;
UniquePtr<GonkSensorsPollInterface> mPollInterface;
};
} // namespace hal
} // namespace mozilla
#endif // hal_gonk_GonkSensorsInterface_h

View file

@ -0,0 +1,431 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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 "GonkSensorsPollInterface.h"
#include "HalLog.h"
#include <mozilla/UniquePtr.h>
namespace mozilla {
namespace hal {
using namespace mozilla::ipc;
//
// GonkSensorsPollResultHandler
//
void
GonkSensorsPollResultHandler::OnError(SensorsError aError)
{
HAL_ERR("Received error code %d", static_cast<int>(aError));
}
void
GonkSensorsPollResultHandler::EnableSensor()
{ }
void
GonkSensorsPollResultHandler::DisableSensor()
{ }
void
GonkSensorsPollResultHandler::SetPeriod()
{ }
GonkSensorsPollResultHandler::~GonkSensorsPollResultHandler()
{ }
//
// GonkSensorsPollNotificationHandler
//
void
GonkSensorsPollNotificationHandler::ErrorNotification(SensorsError aError)
{
HAL_ERR("Received error code %d", static_cast<int>(aError));
}
void
GonkSensorsPollNotificationHandler::SensorDetectedNotification(
int32_t aId,
SensorsType aType,
float aRange,
float aResolution,
float aPower,
int32_t aMinPeriod,
int32_t aMaxPeriod,
SensorsTriggerMode aTriggerMode,
SensorsDeliveryMode aDeliveryMode)
{ }
void
GonkSensorsPollNotificationHandler::SensorLostNotification(int32_t aId)
{ }
void
GonkSensorsPollNotificationHandler::EventNotification(int32_t aId,
const SensorsEvent& aEvent)
{ }
GonkSensorsPollNotificationHandler::~GonkSensorsPollNotificationHandler()
{ }
//
// GonkSensorsPollModule
//
GonkSensorsPollModule::GonkSensorsPollModule()
: mProtocolVersion(0)
{ }
GonkSensorsPollModule::~GonkSensorsPollModule()
{ }
nsresult
GonkSensorsPollModule::SetProtocolVersion(unsigned long aProtocolVersion)
{
if ((aProtocolVersion < MIN_PROTOCOL_VERSION) ||
(aProtocolVersion > MAX_PROTOCOL_VERSION)) {
HAL_ERR("Sensors Poll protocol version %lu not supported",
aProtocolVersion);
return NS_ERROR_ILLEGAL_VALUE;
}
mProtocolVersion = aProtocolVersion;
return NS_OK;
}
void
GonkSensorsPollModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (GonkSensorsPollModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &GonkSensorsPollModule::HandleRsp,
[1] = &GonkSensorsPollModule::HandleNtf
};
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
// Negate twice to map bit to 0/1
unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
}
// Commands
//
nsresult
GonkSensorsPollModule::EnableSensorCmd(int32_t aId, GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<DaemonSocketPDU> pdu =
MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_ENABLE_SENSOR, 0);
nsresult rv = PackPDU(aId, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu.get(), aRes);
if (NS_FAILED(rv)) {
return rv;
}
Unused << pdu.release();
return NS_OK;
}
nsresult
GonkSensorsPollModule::DisableSensorCmd(int32_t aId, GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<DaemonSocketPDU> pdu =
MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_DISABLE_SENSOR, 0);
nsresult rv = PackPDU(aId, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu.get(), aRes);
if (NS_FAILED(rv)) {
return rv;
}
Unused << pdu.release();
return NS_OK;
}
nsresult
GonkSensorsPollModule::SetPeriodCmd(int32_t aId, uint64_t aPeriod,
GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<DaemonSocketPDU> pdu =
MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_SET_PERIOD, 0);
nsresult rv = PackPDU(aId, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = PackPDU(aPeriod, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu.get(), aRes);
if (NS_FAILED(rv)) {
return rv;
}
Unused << pdu.release();
return NS_OK;
}
// Responses
//
void
GonkSensorsPollModule::ErrorRsp(
const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, GonkSensorsPollResultHandler* aRes)
{
ErrorRunnable::Dispatch(
aRes, &GonkSensorsPollResultHandler::OnError, UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::EnableSensorRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes, &GonkSensorsPollResultHandler::EnableSensor, UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::DisableSensorRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes, &GonkSensorsPollResultHandler::DisableSensor, UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::SetPeriodRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes, &GonkSensorsPollResultHandler::SetPeriod, UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::HandleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (GonkSensorsPollModule::* const sHandleRsp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
GonkSensorsPollResultHandler*) = {
[OPCODE_ERROR] = &GonkSensorsPollModule::ErrorRsp,
[OPCODE_ENABLE_SENSOR] = &GonkSensorsPollModule::EnableSensorRsp,
[OPCODE_DISABLE_SENSOR] = &GonkSensorsPollModule::DisableSensorRsp,
[OPCODE_SET_PERIOD] = &GonkSensorsPollModule::SetPeriodRsp,
};
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
if (!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(sHandleRsp)) ||
!sHandleRsp[aHeader.mOpcode]) {
HAL_ERR("Sensors poll response opcode %d unknown", aHeader.mOpcode);
return;
}
RefPtr<GonkSensorsPollResultHandler> res =
static_cast<GonkSensorsPollResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set for response
}
(this->*(sHandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
}
// Notifications
//
// Returns the current notification handler to a notification runnable
class GonkSensorsPollModule::NotificationHandlerWrapper final
{
public:
typedef GonkSensorsPollNotificationHandler ObjectType;
static ObjectType* GetInstance()
{
MOZ_ASSERT(NS_IsMainThread());
return sNotificationHandler;
}
static GonkSensorsPollNotificationHandler* sNotificationHandler;
};
GonkSensorsPollNotificationHandler*
GonkSensorsPollModule::NotificationHandlerWrapper::sNotificationHandler;
void
GonkSensorsPollModule::ErrorNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
{
ErrorNotification::Dispatch(
&GonkSensorsPollNotificationHandler::ErrorNotification,
UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::SensorDetectedNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
{
SensorDetectedNotification::Dispatch(
&GonkSensorsPollNotificationHandler::SensorDetectedNotification,
UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::SensorLostNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
{
SensorLostNotification::Dispatch(
&GonkSensorsPollNotificationHandler::SensorLostNotification,
UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::EventNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
{
EventNotification::Dispatch(
&GonkSensorsPollNotificationHandler::EventNotification,
UnpackPDUInitOp(aPDU));
}
void
GonkSensorsPollModule::HandleNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (GonkSensorsPollModule::* const sHandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {
[0] = &GonkSensorsPollModule::ErrorNtf,
[1] = &GonkSensorsPollModule::SensorDetectedNtf,
[2] = &GonkSensorsPollModule::SensorLostNtf,
[3] = &GonkSensorsPollModule::EventNtf
};
MOZ_ASSERT(!NS_IsMainThread());
uint8_t index = aHeader.mOpcode - 0x80;
if (!(index < MOZ_ARRAY_LENGTH(sHandleNtf)) || !sHandleNtf[index]) {
HAL_ERR("Sensors poll notification opcode %d unknown", aHeader.mOpcode);
return;
}
(this->*(sHandleNtf[index]))(aHeader, aPDU);
}
//
// GonkSensorsPollInterface
//
GonkSensorsPollInterface::GonkSensorsPollInterface(
GonkSensorsPollModule* aModule)
: mModule(aModule)
{ }
GonkSensorsPollInterface::~GonkSensorsPollInterface()
{ }
void
GonkSensorsPollInterface::SetNotificationHandler(
GonkSensorsPollNotificationHandler* aNotificationHandler)
{
MOZ_ASSERT(NS_IsMainThread());
GonkSensorsPollModule::NotificationHandlerWrapper::sNotificationHandler =
aNotificationHandler;
}
nsresult
GonkSensorsPollInterface::SetProtocolVersion(unsigned long aProtocolVersion)
{
MOZ_ASSERT(mModule);
return mModule->SetProtocolVersion(aProtocolVersion);
}
void
GonkSensorsPollInterface::EnableSensor(int32_t aId,
GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(mModule);
nsresult rv = mModule->EnableSensorCmd(aId, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
GonkSensorsPollInterface::DisableSensor(int32_t aId,
GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(mModule);
nsresult rv = mModule->DisableSensorCmd(aId, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
GonkSensorsPollInterface::SetPeriod(int32_t aId, uint64_t aPeriod,
GonkSensorsPollResultHandler* aRes)
{
MOZ_ASSERT(mModule);
nsresult rv = mModule->SetPeriodCmd(aId, aPeriod, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
GonkSensorsPollInterface::DispatchError(
GonkSensorsPollResultHandler* aRes, SensorsError aError)
{
DaemonResultRunnable1<GonkSensorsPollResultHandler, void,
SensorsError, SensorsError>::Dispatch(
aRes, &GonkSensorsPollResultHandler::OnError,
ConstantInitOp1<SensorsError>(aError));
}
void
GonkSensorsPollInterface::DispatchError(
GonkSensorsPollResultHandler* aRes, nsresult aRv)
{
SensorsError error;
if (NS_FAILED(Convert(aRv, error))) {
error = SENSORS_ERROR_FAIL;
}
DispatchError(aRes, error);
}
} // namespace hal
} // namespace mozilla

View file

@ -0,0 +1,340 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
/*
* The poll interface gives yo access to the Sensors daemon's Poll service,
* which handles sensors. The poll service will inform you when sensors are
* detected or removed from the system. You can activate (or deactivate)
* existing sensors and poll will deliver the sensors' events.
*
* All public methods and callback methods run on the main thread.
*/
#ifndef hal_gonk_GonkSensorsPollInterface_h
#define hal_gonk_GonkSensorsPollInterface_h
#include <mozilla/ipc/DaemonRunnables.h>
#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
#include "SensorsTypes.h"
namespace mozilla {
namespace ipc {
class DaemonSocketPDU;
class DaemonSocketPDUHeader;
}
}
namespace mozilla {
namespace hal {
class SensorsInterface;
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
/**
* This class is the result-handler interface for the Sensors
* Poll interface. Methods always run on the main thread.
*/
class GonkSensorsPollResultHandler : public DaemonSocketResultHandler
{
public:
/**
* Called if a poll command failed.
*
* @param aError The error code.
*/
virtual void OnError(SensorsError aError);
/**
* The callback method for |GonkSensorsPollInterface::EnableSensor|.
*/
virtual void EnableSensor();
/**
* The callback method for |GonkSensorsPollInterface::DisableSensor|.
*/
virtual void DisableSensor();
/**
* The callback method for |GonkSensorsPollInterface::SetPeriod|.
*/
virtual void SetPeriod();
protected:
virtual ~GonkSensorsPollResultHandler();
};
/**
* This is the notification-handler interface. Implement this classes
* methods to handle event and notifications from the sensors daemon.
*/
class GonkSensorsPollNotificationHandler
{
public:
/**
* The notification handler for errors. You'll receive this call if
* there's been a critical error in the daemon. Either try to handle
* the error, or restart the daemon.
*
* @param aError The error code.
*/
virtual void ErrorNotification(SensorsError aError);
/**
* This methods gets call when a new sensor has been detected.
*
* @param aId The sensor's id.
* @param aType The sensor's type.
* @param aRange The sensor's maximum value.
* @param aResolution The minimum difference between two consecutive values.
* @param aPower The sensor's power consumption (in mA).
* @param aMinPeriod The minimum time between two events (in ns).
* @param aMaxPeriod The maximum time between two events (in ns).
* @param aTriggerMode The sensor's mode for triggering events.
* @param aDeliveryMode The sensor's urgency for event delivery.
*/
virtual void SensorDetectedNotification(int32_t aId, SensorsType aType,
float aRange, float aResolution,
float aPower, int32_t aMinPeriod,
int32_t aMaxPeriod,
SensorsTriggerMode aTriggerMode,
SensorsDeliveryMode aDeliveryMode);
/**
* This methods gets call when an existing sensor has been removed.
*
* @param aId The sensor's id.
*/
virtual void SensorLostNotification(int32_t aId);
/**
* This is the callback methods for sensor events. Only activated sensors
* generate events. All sensors are disabled by default. The actual data
* of the event depends on the sensor type.
*
* @param aId The sensor's id.
* @param aEvent The event's data.
*/
virtual void EventNotification(int32_t aId, const SensorsEvent& aEvent);
protected:
virtual ~GonkSensorsPollNotificationHandler();
};
/**
* This is the module class for the Sensors poll component. It handles PDU
* packing and unpacking. Methods are either executed on the main thread or
* the I/O thread.
*
* This is an internal class, use |GonkSensorsPollInterface| instead.
*/
class GonkSensorsPollModule
{
public:
class NotificationHandlerWrapper;
enum {
SERVICE_ID = 0x01
};
enum {
OPCODE_ERROR = 0x00,
OPCODE_ENABLE_SENSOR = 0x01,
OPCODE_DISABLE_SENSOR = 0x02,
OPCODE_SET_PERIOD = 0x03
};
enum {
MIN_PROTOCOL_VERSION = 1,
MAX_PROTOCOL_VERSION = 1
};
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
nsresult SetProtocolVersion(unsigned long aProtocolVersion);
//
// Commands
//
nsresult EnableSensorCmd(int32_t aId,
GonkSensorsPollResultHandler* aRes);
nsresult DisableSensorCmd(int32_t aId,
GonkSensorsPollResultHandler* aRes);
nsresult SetPeriodCmd(int32_t aId, uint64_t aPeriod,
GonkSensorsPollResultHandler* aRes);
protected:
GonkSensorsPollModule();
virtual ~GonkSensorsPollModule();
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
private:
//
// Responses
//
typedef mozilla::ipc::DaemonResultRunnable0<
GonkSensorsPollResultHandler, void>
ResultRunnable;
typedef mozilla::ipc::DaemonResultRunnable1<
GonkSensorsPollResultHandler, void, SensorsError, SensorsError>
ErrorRunnable;
void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes);
void EnableSensorRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes);
void DisableSensorRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes);
void SetPeriodRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsPollResultHandler* aRes);
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
//
// Notifications
//
typedef mozilla::ipc::DaemonNotificationRunnable1<
NotificationHandlerWrapper, void, SensorsError>
ErrorNotification;
typedef mozilla::ipc::DaemonNotificationRunnable9<
NotificationHandlerWrapper, void, int32_t, SensorsType,
float, float, float, int32_t, int32_t, SensorsTriggerMode,
SensorsDeliveryMode>
SensorDetectedNotification;
typedef mozilla::ipc::DaemonNotificationRunnable1<
NotificationHandlerWrapper, void, int32_t>
SensorLostNotification;
typedef mozilla::ipc::DaemonNotificationRunnable2<
NotificationHandlerWrapper, void, int32_t, SensorsEvent, int32_t,
const SensorsEvent&>
EventNotification;
class SensorDetectedInitOp;
class SensorLostInitOp;
class EventInitOp;
void ErrorNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU);
void SensorDetectedNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU);
void SensorLostNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU);
void EventNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU);
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
private:
unsigned long mProtocolVersion;
};
/**
* This class implements the public interface to the Sensors poll
* component. Use |SensorsInterface::GetPollInterface| to retrieve
* an instance. All methods run on the main thread.
*/
class GonkSensorsPollInterface final
{
public:
GonkSensorsPollInterface(GonkSensorsPollModule* aModule);
~GonkSensorsPollInterface();
/**
* This method sets the notification handler for poll notifications. Call
* this method immediately after registering the module. Otherwise you won't
* be able able to receive poll notifications. You may not free the handler
* class while the poll component is regsitered.
*
* @param aNotificationHandler An instance of a poll notification handler.
*/
void SetNotificationHandler(
GonkSensorsPollNotificationHandler* aNotificationHandler);
/**
* This method sets the protocol version. You should set it to the
* value that has been returned from the backend when registering the
* Poll service. You cannot send or receive messages before setting
* the protocol version.
*
* @param aProtocolVersion
* @return NS_OK for supported versions, or an XPCOM error code otherwise.
*/
nsresult SetProtocolVersion(unsigned long aProtocolVersion);
/**
* Enables an existing sensor. The sensor id will have been delivered in
* a SensorDetectedNotification.
*
* @param aId The sensor's id.
* @param aRes The result handler.
*/
void EnableSensor(int32_t aId, GonkSensorsPollResultHandler* aRes);
/**
* Disables an existing sensor. The sensor id will have been delivered in
* a SensorDetectedNotification.
*
* @param aId The sensor's id.
* @param aRes The result handler.
*/
void DisableSensor(int32_t aId, GonkSensorsPollResultHandler* aRes);
/**
* Sets the period for a sensor. The sensor id will have been delivered in
* a SensorDetectedNotification. The value for the period should be between
* the sensor's minimum and maximum period.
*
* @param aId The sensor's id.
* @param aPeriod The sensor's new period.
* @param aRes The result handler.
*/
void SetPeriod(int32_t aId, uint64_t aPeriod, GonkSensorsPollResultHandler* aRes);
private:
void DispatchError(GonkSensorsPollResultHandler* aRes, SensorsError aError);
void DispatchError(GonkSensorsPollResultHandler* aRes, nsresult aRv);
GonkSensorsPollModule* mModule;
};
} // hal
} // namespace mozilla
#endif // hal_gonk_GonkSensorsPollInterface_h

View file

@ -0,0 +1,213 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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 "GonkSensorsRegistryInterface.h"
#include "GonkSensorsHelpers.h"
#include "HalLog.h"
#include <mozilla/UniquePtr.h>
namespace mozilla {
namespace hal {
using namespace mozilla::ipc;
//
// GonkSensorsRegistryResultHandler
//
void
GonkSensorsRegistryResultHandler::OnError(SensorsError aError)
{
HAL_ERR("Received error code %d", static_cast<int>(aError));
}
void
GonkSensorsRegistryResultHandler::RegisterModule(uint32_t aProtocolVersion)
{ }
void
GonkSensorsRegistryResultHandler::UnregisterModule()
{ }
GonkSensorsRegistryResultHandler::~GonkSensorsRegistryResultHandler()
{ }
//
// GonkSensorsRegistryModule
//
GonkSensorsRegistryModule::~GonkSensorsRegistryModule()
{ }
void
GonkSensorsRegistryModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (GonkSensorsRegistryModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
DaemonSocketPDU&,
GonkSensorsRegistryResultHandler*) = {
[OPCODE_ERROR] = &GonkSensorsRegistryModule::ErrorRsp,
[OPCODE_REGISTER_MODULE] = &GonkSensorsRegistryModule::RegisterModuleRsp,
[OPCODE_UNREGISTER_MODULE] = &GonkSensorsRegistryModule::UnregisterModuleRsp
};
if ((aHeader.mOpcode >= MOZ_ARRAY_LENGTH(HandleRsp)) ||
!HandleRsp[aHeader.mOpcode]) {
HAL_ERR("Sensors registry response opcode %d unknown", aHeader.mOpcode);
return;
}
RefPtr<GonkSensorsRegistryResultHandler> res =
static_cast<GonkSensorsRegistryResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set
}
(this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
}
// Commands
//
nsresult
GonkSensorsRegistryModule::RegisterModuleCmd(
uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<DaemonSocketPDU> pdu =
MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_REGISTER_MODULE, 0);
nsresult rv = PackPDU(aId, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu.get(), aRes);
if (NS_FAILED(rv)) {
return rv;
}
Unused << pdu.release();
return NS_OK;
}
nsresult
GonkSensorsRegistryModule::UnregisterModuleCmd(
uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<DaemonSocketPDU> pdu =
MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_UNREGISTER_MODULE, 0);
nsresult rv = PackPDU(aId, *pdu);
if (NS_FAILED(rv)) {
return rv;
}
rv = Send(pdu.get(), aRes);
if (NS_FAILED(rv)) {
return rv;
}
Unused << pdu.release();
return NS_OK;
}
// Responses
//
void
GonkSensorsRegistryModule::ErrorRsp(
const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, GonkSensorsRegistryResultHandler* aRes)
{
ErrorRunnable::Dispatch(
aRes, &GonkSensorsRegistryResultHandler::OnError, UnpackPDUInitOp(aPDU));
}
void
GonkSensorsRegistryModule::RegisterModuleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
GonkSensorsRegistryResultHandler* aRes)
{
Uint32ResultRunnable::Dispatch(
aRes,
&GonkSensorsRegistryResultHandler::RegisterModule,
UnpackPDUInitOp(aPDU));
}
void
GonkSensorsRegistryModule::UnregisterModuleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
GonkSensorsRegistryResultHandler* aRes)
{
ResultRunnable::Dispatch(
aRes,
&GonkSensorsRegistryResultHandler::UnregisterModule,
UnpackPDUInitOp(aPDU));
}
//
// GonkSensorsRegistryInterface
//
GonkSensorsRegistryInterface::GonkSensorsRegistryInterface(
GonkSensorsRegistryModule* aModule)
: mModule(aModule)
{ }
GonkSensorsRegistryInterface::~GonkSensorsRegistryInterface()
{ }
void
GonkSensorsRegistryInterface::RegisterModule(
uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
{
MOZ_ASSERT(mModule);
nsresult rv = mModule->RegisterModuleCmd(aId, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
GonkSensorsRegistryInterface::UnregisterModule(
uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
{
MOZ_ASSERT(mModule);
nsresult rv = mModule->UnregisterModuleCmd(aId, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
GonkSensorsRegistryInterface::DispatchError(
GonkSensorsRegistryResultHandler* aRes, SensorsError aError)
{
DaemonResultRunnable1<GonkSensorsRegistryResultHandler, void,
SensorsError, SensorsError>::Dispatch(
aRes, &GonkSensorsRegistryResultHandler::OnError,
ConstantInitOp1<SensorsError>(aError));
}
void
GonkSensorsRegistryInterface::DispatchError(
GonkSensorsRegistryResultHandler* aRes, nsresult aRv)
{
SensorsError error;
if (NS_FAILED(Convert(aRv, error))) {
error = SENSORS_ERROR_FAIL;
}
DispatchError(aRes, error);
}
} // namespace hal
} // namespace mozilla

View file

@ -0,0 +1,182 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
/*
* The registry interface gives yo access to the Sensors daemon's Registry
* service. The purpose of the service is to register and setup all other
* services, and make them available.
*
* All public methods and callback methods run on the main thread.
*/
#ifndef hal_gonk_GonkSensorsRegistryInterface_h
#define hal_gonk_GonkSensorsRegistryInterface_h
#include <mozilla/ipc/DaemonRunnables.h>
#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
#include "SensorsTypes.h"
namespace mozilla {
namespace ipc {
class DaemonSocketPDU;
class DaemonSocketPDUHeader;
}
}
namespace mozilla {
namespace hal {
class SensorsInterface;
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
/**
* This class is the result-handler interface for the Sensors
* Registry interface. Methods always run on the main thread.
*/
class GonkSensorsRegistryResultHandler : public DaemonSocketResultHandler
{
public:
/**
* Called if a registry command failed.
*
* @param aError The error code.
*/
virtual void OnError(SensorsError aError);
/**
* The callback method for |GonkSensorsRegistryInterface::RegisterModule|.
*
* @param aProtocolVersion The daemon's protocol version. Make sure it's
* compatible with Gecko's implementation.
*/
virtual void RegisterModule(uint32_t aProtocolVersion);
/**
* The callback method for |SensorsRegsitryInterface::UnregisterModule|.
*/
virtual void UnregisterModule();
protected:
virtual ~GonkSensorsRegistryResultHandler();
};
/**
* This is the module class for the Sensors registry component. It handles
* PDU packing and unpacking. Methods are either executed on the main thread
* or the I/O thread.
*
* This is an internal class, use |GonkSensorsRegistryInterface| instead.
*/
class GonkSensorsRegistryModule
{
public:
enum {
SERVICE_ID = 0x00
};
enum {
OPCODE_ERROR = 0x00,
OPCODE_REGISTER_MODULE = 0x01,
OPCODE_UNREGISTER_MODULE = 0x02
};
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
//
// Commands
//
nsresult RegisterModuleCmd(uint8_t aId,
GonkSensorsRegistryResultHandler* aRes);
nsresult UnregisterModuleCmd(uint8_t aId,
GonkSensorsRegistryResultHandler* aRes);
protected:
virtual ~GonkSensorsRegistryModule();
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
//
// Responses
//
typedef mozilla::ipc::DaemonResultRunnable0<
GonkSensorsRegistryResultHandler, void>
ResultRunnable;
typedef mozilla::ipc::DaemonResultRunnable1<
GonkSensorsRegistryResultHandler, void, uint32_t, uint32_t>
Uint32ResultRunnable;
typedef mozilla::ipc::DaemonResultRunnable1<
GonkSensorsRegistryResultHandler, void, SensorsError, SensorsError>
ErrorRunnable;
void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsRegistryResultHandler* aRes);
void RegisterModuleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsRegistryResultHandler* aRes);
void UnregisterModuleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
GonkSensorsRegistryResultHandler* aRes);
};
/**
* This class implements the public interface to the Sensors Registry
* component. Use |SensorsInterface::GetRegistryInterface| to retrieve
* an instance. All methods run on the main thread.
*/
class GonkSensorsRegistryInterface final
{
public:
GonkSensorsRegistryInterface(GonkSensorsRegistryModule* aModule);
~GonkSensorsRegistryInterface();
/**
* Sends a RegisterModule command to the Sensors daemon. When the
* result handler's |RegisterModule| method gets called, the service
* has been registered successfully and can be used.
*
* @param aId The id of the service that is to be registered.
* @param aRes The result handler.
*/
void RegisterModule(uint8_t aId, GonkSensorsRegistryResultHandler* aRes);
/**
* Sends an UnregisterModule command to the Sensors daemon. The service
* should not be used afterwards until it has been registered again.
*
* @param aId The id of the service that is to be unregistered.
* @param aRes The result handler.
*/
void UnregisterModule(uint8_t aId, GonkSensorsRegistryResultHandler* aRes);
private:
void DispatchError(GonkSensorsRegistryResultHandler* aRes,
SensorsError aError);
void DispatchError(GonkSensorsRegistryResultHandler* aRes,
nsresult aRv);
GonkSensorsRegistryModule* mModule;
};
} // namespace hal
} // namespace mozilla
#endif // hal_gonk_GonkSensorsRegistryInterface_h

479
hal/gonk/GonkSwitch.cpp Normal file
View file

@ -0,0 +1,479 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <fcntl.h>
#include <sysutils/NetlinkEvent.h>
#include "base/message_loop.h"
#include "base/task.h"
#include "Hal.h"
#include "HalLog.h"
#include "mozilla/FileUtils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Monitor.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "UeventPoller.h"
using namespace mozilla::hal;
#define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
#define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
#define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
namespace mozilla {
namespace hal_impl {
/**
* The uevent for a usb on GB insertion looks like:
*
* change@/devices/virtual/switch/usb_configuration
* ACTION=change
* DEVPATH=/devices/virtual/switch/usb_configuration
* SUBSYSTEM=switch
* SWITCH_NAME=usb_configuration
* SWITCH_STATE=0
* SEQNUM=5038
*/
class SwitchHandler
{
public:
NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
: mDevPath(aDevPath),
mState(SWITCH_STATE_UNKNOWN),
mDevice(aDevice)
{
GetInitialState();
}
bool CheckEvent(NetlinkEvent* aEvent)
{
if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
return false;
}
mState = ConvertState(GetStateString(aEvent));
return mState != SWITCH_STATE_UNKNOWN;
}
SwitchState GetState()
{
return mState;
}
SwitchDevice GetType()
{
return mDevice;
}
protected:
virtual ~SwitchHandler()
{
}
virtual const char* GetSubsystem()
{
return "switch";
}
virtual const char* GetStateString(NetlinkEvent* aEvent)
{
return aEvent->findParam("SWITCH_STATE");
}
void GetInitialState()
{
nsPrintfCString statePath("/sys%s/state", mDevPath);
int fd = open(statePath.get(), O_RDONLY);
if (fd <= 0) {
return;
}
ScopedClose autoClose(fd);
char state[16];
ssize_t bytesRead = read(fd, state, sizeof(state));
if (bytesRead < 0) {
HAL_ERR("Read data from %s fails", statePath.get());
return;
}
if (state[bytesRead - 1] == '\n') {
bytesRead--;
}
state[bytesRead] = '\0';
mState = ConvertState(state);
}
virtual SwitchState ConvertState(const char* aState)
{
MOZ_ASSERT(aState);
return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
}
const char* mDevPath;
SwitchState mState;
SwitchDevice mDevice;
};
/**
* The uevent delivered for the USB configuration under ICS looks like,
*
* change@/devices/virtual/android_usb/android0
* ACTION=change
* DEVPATH=/devices/virtual/android_usb/android0
* SUBSYSTEM=android_usb
* USB_STATE=CONFIGURED
* SEQNUM=1802
*/
class SwitchHandlerUsbIcs: public SwitchHandler
{
public:
SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
{
SwitchHandler::GetInitialState();
}
virtual ~SwitchHandlerUsbIcs() { }
protected:
virtual const char* GetSubsystem()
{
return "android_usb";
}
virtual const char* GetStateString(NetlinkEvent* aEvent)
{
return aEvent->findParam("USB_STATE");
}
SwitchState ConvertState(const char* aState)
{
MOZ_ASSERT(aState);
return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF;
}
};
/**
* The uevent delivered for the headset under ICS looks like,
*
* change@/devices/virtual/switch/h2w
* ACTION=change
* DEVPATH=/devices/virtual/switch/h2w
* SUBSYSTEM=switch
* SWITCH_NAME=h2w
* SWITCH_STATE=2 // Headset with no mic
* SEQNUM=2581
* On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
* change@/devices/virtual/switch/h2w
* ACTION=change
* DEVPATH=/devices/virtual/switch/h2w
* SUBSYSTEM=switch
* SWITCH_NAME=Headset
* SWITCH_STATE=1 // Headset with mic
* SEQNUM=1602
*/
class SwitchHandlerHeadphone: public SwitchHandler
{
public:
SwitchHandlerHeadphone(const char* aDevPath) :
SwitchHandler(aDevPath, SWITCH_HEADPHONES)
{
SwitchHandler::GetInitialState();
}
virtual ~SwitchHandlerHeadphone() { }
protected:
SwitchState ConvertState(const char* aState)
{
MOZ_ASSERT(aState);
return aState[0] == '0' ? SWITCH_STATE_OFF :
(aState[0] == '1' ? SWITCH_STATE_HEADSET : SWITCH_STATE_HEADPHONE);
}
};
typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray;
class SwitchEventRunnable : public Runnable
{
public:
SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
{
}
NS_IMETHOD Run() override
{
NotifySwitchChange(mEvent);
return NS_OK;
}
private:
SwitchEvent mEvent;
};
class SwitchEventObserver final : public IUeventObserver
{
~SwitchEventObserver()
{
mHandler.Clear();
}
public:
NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
SwitchEventObserver()
: mEnableCount(0),
mHeadphonesFromInputDev(false)
{
Init();
}
int GetEnableCount()
{
return mEnableCount;
}
void EnableSwitch(SwitchDevice aDevice)
{
mEventInfo[aDevice].mEnabled = true;
mEnableCount++;
}
void DisableSwitch(SwitchDevice aDevice)
{
mEventInfo[aDevice].mEnabled = false;
mEnableCount--;
}
void Notify(const NetlinkEvent& aEvent)
{
SwitchState currState;
SwitchDevice device = GetEventInfo(aEvent, currState);
if (device == SWITCH_DEVICE_UNKNOWN) {
return;
}
EventInfo& info = mEventInfo[device];
if (currState == info.mEvent.status()) {
return;
}
info.mEvent.status() = currState;
if (info.mEnabled) {
NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
}
}
void Notify(SwitchDevice aDevice, SwitchState aState)
{
EventInfo& info = mEventInfo[aDevice];
if (aState == info.mEvent.status()) {
return;
}
info.mEvent.status() = aState;
if (info.mEnabled) {
NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
}
}
SwitchState GetCurrentInformation(SwitchDevice aDevice)
{
return mEventInfo[aDevice].mEvent.status();
}
void NotifyAnEvent(SwitchDevice aDevice)
{
EventInfo& info = mEventInfo[aDevice];
if (info.mEvent.status() != SWITCH_STATE_UNKNOWN) {
NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
}
}
bool GetHeadphonesFromInputDev()
{
return mHeadphonesFromInputDev;
}
private:
class EventInfo
{
public:
EventInfo() : mEnabled(false)
{
mEvent.status() = SWITCH_STATE_UNKNOWN;
mEvent.device() = SWITCH_DEVICE_UNKNOWN;
}
SwitchEvent mEvent;
bool mEnabled;
};
EventInfo mEventInfo[NUM_SWITCH_DEVICE];
size_t mEnableCount;
SwitchHandlerArray mHandler;
bool mHeadphonesFromInputDev;
// This function might also get called on the main thread
// (from IsHeadphoneEventFromInputDev)
void Init()
{
RefPtr<SwitchHandlerHeadphone> switchHeadPhone =
new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH);
// If the initial state is unknown, it means the headphone event is from input dev
mHeadphonesFromInputDev = switchHeadPhone->GetState() == SWITCH_STATE_UNKNOWN ? true : false;
if (!mHeadphonesFromInputDev) {
mHandler.AppendElement(switchHeadPhone);
} else {
// If headphone status will be notified from input dev then initialize
// status to "off" and wait for event notification.
mEventInfo[SWITCH_HEADPHONES].mEvent.device() = SWITCH_HEADPHONES;
mEventInfo[SWITCH_HEADPHONES].mEvent.status() = SWITCH_STATE_OFF;
}
mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB));
mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS));
SwitchHandlerArray::index_type handlerIndex;
SwitchHandlerArray::size_type numHandlers = mHandler.Length();
for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++) {
SwitchState state = mHandler[handlerIndex]->GetState();
if (state == SWITCH_STATE_UNKNOWN) {
continue;
}
SwitchDevice device = mHandler[handlerIndex]->GetType();
mEventInfo[device].mEvent.device() = device;
mEventInfo[device].mEvent.status() = state;
}
}
SwitchDevice GetEventInfo(const NetlinkEvent& aEvent, SwitchState& aState)
{
//working around the android code not being const-correct
NetlinkEvent *e = const_cast<NetlinkEvent*>(&aEvent);
for (size_t i = 0; i < mHandler.Length(); i++) {
if (mHandler[i]->CheckEvent(e)) {
aState = mHandler[i]->GetState();
return mHandler[i]->GetType();
}
}
return SWITCH_DEVICE_UNKNOWN;
}
};
static RefPtr<SwitchEventObserver> sSwitchObserver;
static void
InitializeResourceIfNeed()
{
if (!sSwitchObserver) {
sSwitchObserver = new SwitchEventObserver();
RegisterUeventListener(sSwitchObserver);
}
}
static void
ReleaseResourceIfNeed()
{
if (sSwitchObserver->GetEnableCount() == 0) {
UnregisterUeventListener(sSwitchObserver);
sSwitchObserver = nullptr;
}
}
static void
EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
{
InitializeResourceIfNeed();
sSwitchObserver->EnableSwitch(aDevice);
{
MonitorAutoLock lock(*aMonitor);
lock.Notify();
}
// Notify the latest state if IO thread has the information.
if (sSwitchObserver->GetEnableCount() > 1) {
sSwitchObserver->NotifyAnEvent(aDevice);
}
}
void
EnableSwitchNotifications(SwitchDevice aDevice)
{
Monitor monitor("EnableSwitch.monitor");
{
MonitorAutoLock lock(monitor);
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
lock.Wait();
}
}
static void
DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
{
MOZ_ASSERT(sSwitchObserver->GetEnableCount());
sSwitchObserver->DisableSwitch(aDevice);
ReleaseResourceIfNeed();
}
void
DisableSwitchNotifications(SwitchDevice aDevice)
{
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
}
SwitchState
GetCurrentSwitchState(SwitchDevice aDevice)
{
MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
return sSwitchObserver->GetCurrentInformation(aDevice);
}
static void
NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
{
InitializeResourceIfNeed();
sSwitchObserver->Notify(aDevice, aState);
}
void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
{
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(NotifySwitchStateIOThread, aDevice, aState));
}
bool IsHeadphoneEventFromInputDev()
{
// Instead of calling InitializeResourceIfNeed, create new SwitchEventObserver
// to prevent calling RegisterUeventListener in main thread.
RefPtr<SwitchEventObserver> switchObserver = new SwitchEventObserver();
return switchObserver->GetHeadphonesFromInputDev();
}
} // hal_impl
} //mozilla

140
hal/gonk/SensorsTypes.h Normal file
View file

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef hal_gonk_SensorsTypes_h
#define hal_gonk_SensorsTypes_h
namespace mozilla {
namespace hal {
enum SensorsDeliveryMode {
SENSORS_DELIVERY_MODE_BEST_EFFORT,
SENSORS_DELIVERY_MODE_IMMEDIATE
};
enum SensorsError {
SENSORS_ERROR_NONE,
SENSORS_ERROR_FAIL,
SENSORS_ERROR_NOT_READY,
SENSORS_ERROR_NOMEM,
SENSORS_ERROR_BUSY,
SENSORS_ERROR_DONE,
SENSORS_ERROR_UNSUPPORTED,
SENSORS_ERROR_PARM_INVALID
};
enum SensorsStatus {
SENSORS_STATUS_NO_CONTACT,
SENSORS_STATUS_UNRELIABLE,
SENSORS_STATUS_ACCURACY_LOW,
SENSORS_STATUS_ACCURACY_MEDIUM,
SENSORS_STATUS_ACCURACY_HIGH
};
enum SensorsTriggerMode {
SENSORS_TRIGGER_MODE_CONTINUOUS,
SENSORS_TRIGGER_MODE_ON_CHANGE,
SENSORS_TRIGGER_MODE_ONE_SHOT,
SENSORS_TRIGGER_MODE_SPECIAL
};
enum SensorsType {
SENSORS_TYPE_ACCELEROMETER,
SENSORS_TYPE_GEOMAGNETIC_FIELD,
SENSORS_TYPE_ORIENTATION,
SENSORS_TYPE_GYROSCOPE,
SENSORS_TYPE_LIGHT,
SENSORS_TYPE_PRESSURE,
SENSORS_TYPE_TEMPERATURE,
SENSORS_TYPE_PROXIMITY,
SENSORS_TYPE_GRAVITY,
SENSORS_TYPE_LINEAR_ACCELERATION,
SENSORS_TYPE_ROTATION_VECTOR,
SENSORS_TYPE_RELATIVE_HUMIDITY,
SENSORS_TYPE_AMBIENT_TEMPERATURE,
SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
SENSORS_TYPE_GAME_ROTATION_VECTOR,
SENSORS_TYPE_GYROSCOPE_UNCALIBRATED,
SENSORS_TYPE_SIGNIFICANT_MOTION,
SENSORS_TYPE_STEP_DETECTED,
SENSORS_TYPE_STEP_COUNTER,
SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR,
SENSORS_TYPE_HEART_RATE,
SENSORS_TYPE_TILT_DETECTOR,
SENSORS_TYPE_WAKE_GESTURE,
SENSORS_TYPE_GLANCE_GESTURE,
SENSORS_TYPE_PICK_UP_GESTURE,
SENSORS_TYPE_WRIST_TILT_GESTURE,
SENSORS_NUM_TYPES
};
struct SensorsEvent {
SensorsType mType;
SensorsStatus mStatus;
SensorsDeliveryMode mDeliveryMode;
int64_t mTimestamp;
union {
float mFloat[6];
uint64_t mUint[1];
} mData;
};
/**
* |SensorsSensor| represents a device sensor; either single or composite.
*/
struct SensorsSensor {
SensorsSensor(int32_t aId, SensorsType aType,
float aRange, float aResolution,
float aPower, int32_t aMinPeriod,
int32_t aMaxPeriod,
SensorsTriggerMode aTriggerMode,
SensorsDeliveryMode aDeliveryMode)
: mId(aId)
, mType(aType)
, mRange(aRange)
, mResolution(aResolution)
, mPower(aPower)
, mMinPeriod(aMinPeriod)
, mMaxPeriod(aMaxPeriod)
, mTriggerMode(aTriggerMode)
, mDeliveryMode(aDeliveryMode)
{ }
int32_t mId;
SensorsType mType;
float mRange;
float mResolution;
float mPower;
int32_t mMinPeriod;
int32_t mMaxPeriod;
SensorsTriggerMode mTriggerMode;
SensorsDeliveryMode mDeliveryMode;
};
/**
* |SensorClass| represents the status of a specific sensor type.
*/
struct SensorsSensorClass {
SensorsSensorClass()
: mActivated(0)
, mMinValue(0)
, mMaxValue(0)
{ }
void UpdateFromSensor(const SensorsSensor& aSensor)
{
mMaxValue = std::max(aSensor.mRange, mMaxValue);
}
uint32_t mActivated;
float mMinValue;
float mMaxValue;
};
} // namespace hal
} // namespace mozilla
#endif // hal_gonk_SensorsTypes_h

131
hal/gonk/SystemService.cpp Normal file
View file

@ -0,0 +1,131 @@
/* -*- 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"
#include <cutils/properties.h>
#include <stdio.h>
#include <string.h>
#include "HalLog.h"
#include "nsITimer.h"
#include "mozilla/Unused.h"
namespace mozilla {
namespace hal_impl {
static const int sRetryInterval = 100; // ms
bool
SystemServiceIsRunning(const char* aSvcName)
{
MOZ_ASSERT(NS_IsMainThread());
char key[PROPERTY_KEY_MAX];
auto res = snprintf(key, sizeof(key), "init.svc.%s", aSvcName);
if (res < 0) {
HAL_ERR("snprintf: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(res) >= sizeof(key)) {
HAL_ERR("snprintf: trunctated service name %s", aSvcName);
return false;
}
char value[PROPERTY_VALUE_MAX];
Unused << NS_WARN_IF(property_get(key, value, "") < 0);
return !strcmp(value, "running");
}
class StartSystemServiceTimerCallback final : public nsITimerCallback
{
NS_DECL_THREADSAFE_ISUPPORTS;
public:
StartSystemServiceTimerCallback(const char* aSvcName, const char* aArgs)
: mSvcName(aSvcName)
, mArgs(aArgs)
{
MOZ_COUNT_CTOR_INHERITED(StartSystemServiceTimerCallback,
nsITimerCallback);
}
NS_IMETHOD Notify(nsITimer* aTimer) override
{
MOZ_ASSERT(NS_IsMainThread());
return StartSystemService(mSvcName.get(), mArgs.get());
}
protected:
~StartSystemServiceTimerCallback()
{
MOZ_COUNT_DTOR_INHERITED(StartSystemServiceTimerCallback,
nsITimerCallback);
}
private:
nsCString mSvcName;
nsCString mArgs;
};
NS_IMPL_ISUPPORTS0(StartSystemServiceTimerCallback);
nsresult
StartSystemService(const char* aSvcName, const char* aArgs)
{
MOZ_ASSERT(NS_IsMainThread());
char value[PROPERTY_VALUE_MAX];
auto res = snprintf(value, sizeof(value), "%s:%s", aSvcName, aArgs);
if (res < 0) {
HAL_ERR("snprintf: %s", strerror(errno));
return NS_ERROR_FAILURE;
} else if (static_cast<size_t>(res) >= sizeof(value)) {
HAL_ERR("snprintf: trunctated service name %s", aSvcName);
return NS_ERROR_OUT_OF_MEMORY;
}
if (NS_WARN_IF(property_set("ctl.start", value) < 0)) {
return NS_ERROR_FAILURE;
}
/* If the system service is not running, re-try later to start it.
*
* This condition happens when we restart a service immediately
* after it crashed, as the service state remains 'stopping'
* instead of 'stopped'. Due to the limitation of property service,
* hereby add delay. See Bug 1143925 Comment 41.
*/
if (!SystemServiceIsRunning(aSvcName)) {
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
if (!timer) {
return NS_ERROR_FAILURE;
}
RefPtr<StartSystemServiceTimerCallback> timerCallback =
new StartSystemServiceTimerCallback(aSvcName, aArgs);
timer->InitWithCallback(timerCallback,
sRetryInterval,
nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
void
StopSystemService(const char* aSvcName)
{
MOZ_ASSERT(NS_IsMainThread());
Unused << NS_WARN_IF(property_set("ctl.stop", aSvcName));
}
} // namespace hal_impl
} // namespace mozilla

312
hal/gonk/UeventPoller.cpp Normal file
View file

@ -0,0 +1,312 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "HalLog.h"
#include "nsDebug.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Monitor.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "UeventPoller.h"
using namespace mozilla;
namespace mozilla {
namespace hal_impl {
static void ShutdownUevent();
class NetlinkPoller : public MessageLoopForIO::Watcher
{
public:
NetlinkPoller() : mSocket(-1),
mIOLoop(MessageLoopForIO::current())
{
}
virtual ~NetlinkPoller() {}
bool OpenSocket();
virtual void OnFileCanReadWithoutBlocking(int fd);
// no writing to the netlink socket
virtual void OnFileCanWriteWithoutBlocking(int fd)
{
MOZ_CRASH("Must not write to netlink socket");
}
MessageLoopForIO *GetIOLoop () const { return mIOLoop; }
void RegisterObserver(IUeventObserver *aObserver)
{
mUeventObserverList.AddObserver(aObserver);
}
void UnregisterObserver(IUeventObserver *aObserver)
{
mUeventObserverList.RemoveObserver(aObserver);
if (mUeventObserverList.Length() == 0) {
ShutdownUevent(); // this will destroy self
}
}
private:
ScopedClose mSocket;
MessageLoopForIO* mIOLoop;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
const static int kBuffsize = 64 * 1024;
uint8_t mBuffer [kBuffsize];
typedef ObserverList<NetlinkEvent> UeventObserverList;
UeventObserverList mUeventObserverList;
};
bool
NetlinkPoller::OpenSocket()
{
mSocket.rwget() = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (mSocket.get() < 0) {
return false;
}
int sz = kBuffsize;
if (setsockopt(mSocket.get(), SOL_SOCKET, SO_RCVBUFFORCE, &sz,
sizeof(sz)) < 0) {
return false;
}
// add FD_CLOEXEC flag
int flags = fcntl(mSocket.get(), F_GETFD);
if (flags == -1) {
return false;
}
flags |= FD_CLOEXEC;
if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
return false;
}
// set non-blocking
if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
return false;
}
struct sockaddr_nl saddr;
bzero(&saddr, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_groups = 1;
saddr.nl_pid = gettid();
do {
if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == 0) {
break;
}
if (errno != EADDRINUSE) {
return false;
}
if (saddr.nl_pid == 0) {
return false;
}
// Once there was any other place in the same process assigning saddr.nl_pid by
// gettid(), we can detect it and print warning message.
HAL_LOG("The netlink socket address saddr.nl_pid=%u is in use. "
"Let the kernel re-assign.\n", saddr.nl_pid);
saddr.nl_pid = 0;
} while (true);
if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher,
this)) {
return false;
}
return true;
}
static StaticAutoPtr<NetlinkPoller> sPoller;
class UeventInitTask : public Runnable
{
NS_IMETHOD Run() override
{
if (!sPoller) {
return NS_OK;
}
if (sPoller->OpenSocket()) {
return NS_OK;
}
sPoller->GetIOLoop()->PostDelayedTask(MakeAndAddRef<UeventInitTask>(),
1000);
return NS_OK;
}
};
void
NetlinkPoller::OnFileCanReadWithoutBlocking(int fd)
{
MOZ_ASSERT(fd == mSocket.get());
while (true) {
int ret = read(fd, mBuffer, kBuffsize);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return;
}
if (errno == EINTR) {
continue;
}
}
if (ret <= 0) {
// fatal error on netlink socket which should not happen
_exit(1);
}
NetlinkEvent netlinkEvent;
netlinkEvent.decode(reinterpret_cast<char*>(mBuffer), ret);
mUeventObserverList.Broadcast(netlinkEvent);
}
}
static bool sShutdown = false;
class ShutdownNetlinkPoller;
static StaticAutoPtr<ShutdownNetlinkPoller> sShutdownPoller;
static Monitor* sMonitor = nullptr;
class ShutdownNetlinkPoller {
public:
~ShutdownNetlinkPoller()
{
// This is called from KillClearOnShutdown() on the main thread.
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetIOMessageLoop());
{
MonitorAutoLock lock(*sMonitor);
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(ShutdownUeventIOThread));
while (!sShutdown) {
lock.Wait();
}
}
sShutdown = true;
delete sMonitor;
}
static void MaybeInit()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (sShutdown || sMonitor) {
// Don't init twice or init after shutdown.
return;
}
sMonitor = new Monitor("ShutdownNetlinkPoller.monitor");
{
ShutdownNetlinkPoller* shutdownPoller = new ShutdownNetlinkPoller();
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=] () -> void
{
sShutdownPoller = shutdownPoller;
ClearOnShutdown(&sShutdownPoller); // Must run on the main thread.
});
MOZ_ASSERT(runnable);
MOZ_ALWAYS_SUCCEEDS(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
}
}
private:
ShutdownNetlinkPoller() = default;
static void ShutdownUeventIOThread()
{
MonitorAutoLock l(*sMonitor);
ShutdownUevent(); // Must run on the IO thread.
sShutdown = true;
l.NotifyAll();
}
};
static void
InitializeUevent()
{
MOZ_ASSERT(!sPoller);
sPoller = new NetlinkPoller();
sPoller->GetIOLoop()->PostTask(MakeAndAddRef<UeventInitTask>());
ShutdownNetlinkPoller::MaybeInit();
}
static void
ShutdownUevent()
{
sPoller = nullptr;
}
void
RegisterUeventListener(IUeventObserver *aObserver)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (sShutdown) {
return;
}
if (!sPoller) {
InitializeUevent();
}
sPoller->RegisterObserver(aObserver);
}
void
UnregisterUeventListener(IUeventObserver *aObserver)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (sShutdown) {
return;
}
sPoller->UnregisterObserver(aObserver);
}
} // hal_impl
} // mozilla

49
hal/gonk/UeventPoller.h Normal file
View file

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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.
*/
#ifndef _mozilla_uevent_poller_h_
#define _mozilla_uevent_poller_h_
#include <sysutils/NetlinkEvent.h>
#include "mozilla/Observer.h"
class NetlinkEvent;
namespace mozilla {
namespace hal_impl {
typedef mozilla::Observer<NetlinkEvent> IUeventObserver;
/**
* Register for uevent notification. Note that the method should run on the
* <b> IO Thread </b>
* @aObserver the observer to be added. The observer's Notify() is only called
* on the <b> IO Thread </b>
*/
void RegisterUeventListener(IUeventObserver *aObserver);
/**
* Unregister for uevent notification. Note that the method should run on the
* <b> IO Thread </b>
* @aObserver the observer to be removed
*/
void UnregisterUeventListener(IUeventObserver *aObserver);
}
}
#endif

118
hal/gonk/fanotify.h Normal file
View file

@ -0,0 +1,118 @@
#ifndef _LINUX_FANOTIFY_H
#define _LINUX_FANOTIFY_H
/* This is a Linux header generated by "make headers_install" */
#include <linux/types.h>
/* the following events that user-space can register for */
#define FAN_ACCESS 0x00000001 /* File was accessed */
#define FAN_MODIFY 0x00000002 /* File was modified */
#define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
#define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
#define FAN_OPEN 0x00000020 /* File was opened */
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
#define FAN_ONDIR 0x40000000 /* event occurred against dir */
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
/* helper events */
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
/* flags used for fanotify_init() */
#define FAN_CLOEXEC 0x00000001
#define FAN_NONBLOCK 0x00000002
/* These are NOT bitwise flags. Both bits are used togther. */
#define FAN_CLASS_NOTIF 0x00000000
#define FAN_CLASS_CONTENT 0x00000004
#define FAN_CLASS_PRE_CONTENT 0x00000008
#define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
FAN_CLASS_PRE_CONTENT)
#define FAN_UNLIMITED_QUEUE 0x00000010
#define FAN_UNLIMITED_MARKS 0x00000020
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
FAN_UNLIMITED_MARKS)
/* flags used for fanotify_modify_mark() */
#define FAN_MARK_ADD 0x00000001
#define FAN_MARK_REMOVE 0x00000002
#define FAN_MARK_DONT_FOLLOW 0x00000004
#define FAN_MARK_ONLYDIR 0x00000008
#define FAN_MARK_MOUNT 0x00000010
#define FAN_MARK_IGNORED_MASK 0x00000020
#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
#define FAN_MARK_FLUSH 0x00000080
#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
FAN_MARK_REMOVE |\
FAN_MARK_DONT_FOLLOW |\
FAN_MARK_ONLYDIR |\
FAN_MARK_MOUNT |\
FAN_MARK_IGNORED_MASK |\
FAN_MARK_IGNORED_SURV_MODIFY |\
FAN_MARK_FLUSH)
/*
* All of the events - we build the list by hand so that we can add flags in
* the future and not break backward compatibility. Apps will get only the
* events that they originally wanted. Be sure to add new events here!
*/
#define FAN_ALL_EVENTS (FAN_ACCESS |\
FAN_MODIFY |\
FAN_CLOSE |\
FAN_OPEN)
/*
* All events which require a permission response from userspace
*/
#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
FAN_ACCESS_PERM)
#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
FAN_ALL_PERM_EVENTS |\
FAN_Q_OVERFLOW)
#define FANOTIFY_METADATA_VERSION 3
struct fanotify_event_metadata {
__u32 event_len;
__u8 vers;
__u8 reserved;
__u16 metadata_len;
__u64 mask;
__s32 fd;
__s32 pid;
};
struct fanotify_response {
__s32 fd;
__u32 response;
};
/* Legit userspace responses to a _PERM event */
#define FAN_ALLOW 0x01
#define FAN_DENY 0x02
/* No fd set in event */
#define FAN_NOFD -1
/* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
(struct fanotify_event_metadata*)(((char *)(meta)) + \
(meta)->event_len))
#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
(long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
(long)(meta)->event_len <= (long)(len))
#endif /* _LINUX_FANOTIFY_H */

View file

@ -0,0 +1,39 @@
/* 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 "nsISupports.idl"
[scriptable, uuid(bc24fb33-a0c1-49ca-aa43-05f167e02fb6)]
interface nsIRecoveryService : nsISupports
{
/**
* Possible values of fotaStatus.result. These should stay in sync with
* librecovery/librecovery.h
*/
const long FOTA_UPDATE_UNKNOWN = 0;
const long FOTA_UPDATE_FAIL = 1;
const long FOTA_UPDATE_SUCCESS = 2;
/**
* Uses recovery to wipe the data and cache partitions. If this call is
* successful, the device should reboot before the function call ever returns.
*
* @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
*/
void factoryReset(in string reason);
/**
* Use recovery to install an OTA update.zip. If this call is
* successful, the device should reboot before the function call ever returns.
*
* @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
*/
void installFotaUpdate(in string updatePath);
/**
* @return The status of the last FOTA update. One of FOTA_UPDATE_UNKNOWN,
* FOTA_UPDATE_FAIL, FOTA_UPDATE_SUCCESS.
*/
long getFotaUpdateStatus();
};

484
hal/gonk/tavarua.h Normal file
View file

@ -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 */