/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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 <errno.h>
#include <math.h>
#include <poll.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/select.h>
#include <cutils/log.h>
#include "AdxlSensor.h"
#define ADXL_DATA_NAME "ADXL34x accelerometer"
#define ADXL_MAX_SAMPLE_RATE_VAL 11 /* 200 Hz */
#define ADXL_UNIT_CONVERSION(value) ((value) * GRAVITY_EARTH / (256.0f))
/*****************************************************************************/
AdxlSensor::AdxlSensor()
: SensorBase(NULL, ADXL_DATA_NAME),
mEnabled(0),
mDelay(-1),
mInputReader(4),
mHasPendingEvent(false)
{
mPendingEvent.version = sizeof(sensors_event_t);
mPendingEvent.sensor = ID_A;
mPendingEvent.type = SENSOR_TYPE_ACCELEROMETER;
memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
if (data_fd >= 0) {
strcpy(input_sysfs_path, "/sys/class/input/");
strcat(input_sysfs_path, input_name);
strcat(input_sysfs_path, "/device/device/");
input_sysfs_path_len = strlen(input_sysfs_path);
ALOGD("AdxlSensor: sysfs_path=%s", input_sysfs_path);
} else {
input_sysfs_path[0] = '\0';
input_sysfs_path_len = 0;
}
}
AdxlSensor::~AdxlSensor() {
if (mEnabled) {
setEnable(0, 0);
}
}
int AdxlSensor::setInitialState() {
struct input_absinfo absinfo;
if (mEnabled) {
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_X), &absinfo)) {
mPendingEvent.acceleration.x = ADXL_UNIT_CONVERSION(absinfo.value);
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_Y), &absinfo)) {
mPendingEvent.acceleration.y = ADXL_UNIT_CONVERSION(absinfo.value);
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_Z), &absinfo)) {
mPendingEvent.acceleration.z = ADXL_UNIT_CONVERSION(absinfo.value);
}
}
return 0;
}
bool AdxlSensor::hasPendingEvents() const {
return mHasPendingEvent;
}
int AdxlSensor::setEnable(int32_t handle, int enabled) {
int err = 0;
char buffer[2];
/* handle check */
if (handle != ID_A) {
ALOGE("AdxlSensor: Invalid handle (%d)", handle);
return -EINVAL;
}
buffer[0] = '\0';
buffer[1] = '\0';
if (mEnabled <= 0) {
if(enabled) buffer[0] = '0';
} else if (mEnabled == 1) {
if(!enabled) buffer[0] = '1';
}
if (buffer[0] != '\0') {
strcpy(&input_sysfs_path[input_sysfs_path_len], "disable");
err = write_sys_attribute(input_sysfs_path, buffer, 1);
if (err != 0) {
return err;
}
ALOGD("AdxlSensor: Control set %s", buffer);
setInitialState();
}
if (enabled) {
mEnabled++;
if (mEnabled > 32767) mEnabled = 32767;
} else {
mEnabled--;
if (mEnabled < 0) mEnabled = 0;
}
ALOGD("AdxlSensor: mEnabled = %d", mEnabled);
return err;
}
int AdxlSensor::setDelay(int32_t handle, int64_t delay_ns)
{
int err = 0;
int rate_val;
int32_t us;
char buffer[16];
int bytes;
/* handle check */
if (handle != ID_A) {
ALOGE("AdxlSensor: Invalid handle (%d)", handle);
return -EINVAL;
}
if (mDelay != delay_ns) {
/*
* The ADXL34x Supports 16 sample rates ranging from 3200Hz-0.098Hz
* Calculate best fit and limit to max 200Hz (rate_val 11)
*/
us = (int32_t)(delay_ns / 1000);
for (rate_val = 0; rate_val < 16; rate_val++)
if (us >= ((10000000) >> rate_val))
break;
if (rate_val > ADXL_MAX_SAMPLE_RATE_VAL) {
rate_val = ADXL_MAX_SAMPLE_RATE_VAL;
}
strcpy(&input_sysfs_path[input_sysfs_path_len], "rate");
bytes = sprintf(buffer, "%d", rate_val);
err = write_sys_attribute(input_sysfs_path, buffer, bytes);
if (err == 0) {
mDelay = delay_ns;
ALOGD("AdxlSensor: Control set delay %f ms requetsed, using %f ms",
delay_ns/1000000.0f, 1e6 / (3200000 >> (15 - rate_val)));
}
}
return err;
}
int64_t AdxlSensor::getDelay(int32_t handle)
{
return (handle == ID_A) ? mDelay : 0;
}
int AdxlSensor::getEnable(int32_t handle)
{
return (handle == ID_A) ? mEnabled : 0;
}
int AdxlSensor::readEvents(sensors_event_t* data, int count)
{
if (count < 1)
return -EINVAL;
if (mHasPendingEvent) {
mHasPendingEvent = false;
mPendingEvent.timestamp = getTimestamp();
*data = mPendingEvent;
return mEnabled ? 1 : 0;
}
ssize_t n = mInputReader.fill(data_fd);
if (n < 0)
return n;
int numEventReceived = 0;
input_event const* event;
while (count && mInputReader.readEvent(&event)) {
int type = event->type;
if (type == EV_ABS) {
float value = event->value;
if (event->code == EVENT_TYPE_ACCEL_X) {
mPendingEvent.acceleration.x = ADXL_UNIT_CONVERSION(value);
} else if (event->code == EVENT_TYPE_ACCEL_Y) {
mPendingEvent.acceleration.y = ADXL_UNIT_CONVERSION(value);
} else if (event->code == EVENT_TYPE_ACCEL_Z) {
mPendingEvent.acceleration.z = ADXL_UNIT_CONVERSION(value);
}
} else if (type == EV_SYN) {
mPendingEvent.timestamp = timevalToNano(event->time);
if (mEnabled) {
*data++ = mPendingEvent;
count--;
numEventReceived++;
}
} else {
ALOGE("AdxlSensor: unknown event (type=%d, code=%d)",
type, event->code);
}
mInputReader.next();
}
return numEventReceived;
}