/*
* Copyright (C) 2013 Paul Kocialkowski
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_TAG "geomagneticd"
#include
/*
* This is a very intuitive implementation of what's going on with p5100/p3100
* geomagneticd daemon. It seemed that geomagneticd sets an offset so that
* the biggest value (after setting the offset) is 45µT or negative -45µT.
* On the X axis, it happens more often to find the max around 40µT/-40µT.
* The reference offsets I used were: 5005 420432 1153869, and we're getting
* pretty close to this with that implementation.
*
*/
/*
* Input
*/
int input_open(char *name)
{
DIR *d;
struct dirent *di;
char input_name[80] = { 0 };
char path[PATH_MAX];
char *c;
int fd;
int rc;
if (name == NULL)
return -EINVAL;
d = opendir("/dev/input");
if (d == NULL)
return -1;
while ((di = readdir(d))) {
if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0)
continue;
snprintf(path, PATH_MAX, "/dev/input/%s", di->d_name);
fd = open(path, O_RDONLY);
if (fd < 0)
continue;
rc = ioctl(fd, EVIOCGNAME(sizeof(input_name) - 1), &input_name);
if (rc < 0)
continue;
c = strstr((char *) &input_name, "\n");
if (c != NULL)
*c = '\0';
if (strcmp(input_name, name) == 0)
return fd;
else
close(fd);
}
return -1;
}
int sysfs_path_prefix(char *name, char *path_prefix)
{
DIR *d;
struct dirent *di;
char input_name[80] = { 0 };
char path[PATH_MAX];
char *c;
int fd;
if (name == NULL || path_prefix == NULL)
return -EINVAL;
d = opendir("/sys/class/input");
if (d == NULL)
return -1;
while ((di = readdir(d))) {
if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0)
continue;
snprintf(path, PATH_MAX, "/sys/class/input/%s/name", di->d_name);
fd = open(path, O_RDONLY);
if (fd < 0)
continue;
read(fd, &input_name, sizeof(input_name));
close(fd);
c = strstr((char *) &input_name, "\n");
if (c != NULL)
*c = '\0';
if (strcmp(input_name, name) == 0) {
snprintf(path_prefix, PATH_MAX, "/sys/class/input/%s", di->d_name);
return 0;
}
}
return -1;
}
/*
* Geomagneticd
*/
int offset_read(char *path, int *hard_offset, int *calib_offset, int *accuracy)
{
char buf[100] = { 0 };
int fd;
int rc;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
rc = read(fd, &buf, sizeof(buf));
close(fd);
if (rc <= 0)
return -1;
rc = sscanf(buf, "%d %d %d %d %d %d %d",
&hard_offset[0], &hard_offset[1], &hard_offset[2],
&calib_offset[0], &calib_offset[1], &calib_offset[2], accuracy);
if (rc != 7)
return -1;
return 0;
}
int offset_write(char *path, int *hard_offset, int *calib_offset, int accuracy)
{
char buf[100] = { 0 };
int fd;
int rc;
sprintf(buf, "%d %d %d %d %d %d %d\n",
hard_offset[0], hard_offset[1], hard_offset[2],
calib_offset[0], calib_offset[1], calib_offset[2], accuracy);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
write(fd, buf, strlen(buf) + 1);
close(fd);
return 0;
}
int yas_cfg_read(int *hard_offset, int *calib_offset, int *accuracy)
{
char buf[100] = { 0 };
int fd;
int rc;
fd = open("/data/system/yas.cfg", O_RDONLY);
if (fd < 0)
return -1;
rc = read(fd, &buf, sizeof(buf));
close(fd);
if (rc <= 0)
return -1;
rc = sscanf(buf, "%d,%d,%d,%d,%d,%d,%d",
&hard_offset[0], &hard_offset[1], &hard_offset[2],
&calib_offset[0], &calib_offset[1], &calib_offset[2], accuracy);
if (rc != 7)
return -1;
return 0;
}
int yas_cfg_write(int *hard_offset, int *calib_offset, int accuracy)
{
char buf[100] = { 0 };
int fd;
int rc;
fd = open("/data/system/yas-backup.cfg", O_WRONLY | O_TRUNC | O_CREAT);
if (fd < 0)
return -1;
sprintf(buf, "%d,%d,%d,%d,%d,%d,%d\n",
hard_offset[0], hard_offset[1], hard_offset[2],
calib_offset[0], calib_offset[1], calib_offset[2], accuracy);
write(fd, buf, strlen(buf) + 1);
close(fd);
chmod("/data/system/yas-backup.cfg", 0644);
rename("/data/system/yas-backup.cfg", "/data/system/yas.cfg");
return 0;
}
int main(int argc, char *argv[])
{
struct input_event event;
char path[PATH_MAX] = { 0 };
char path_offset[PATH_MAX] = { 0 };
int offset_fd;
int input_fd;
int max_coeff[3] = { 40, 45, 45 };
int hard_offset[3] = { 0 };
int calib_offset[3] = { 0 };
int accuracy = 0;
int axis_min[3] = { 0 };
int axis_max[3] = { 0 };
int axis_calib[3] = { 0 };
int x, y, z;
int rc;
int i;
/*
* Wait for something to be ready and properly report the hard coeff.
* Without that, the hard coeff are reported to be around 127.
*/
LOGD("Geomagneticd start");
input_fd = input_open("geomagnetic_raw");
if (input_fd < 0)
goto sleep_loop;
rc = sysfs_path_prefix("geomagnetic_raw", &path);
if (rc < 0)
goto sleep_loop;
snprintf(path_offset, PATH_MAX, "%s/offsets", path);
for (i=0 ; i < 3 ; i++) {
axis_min[i] = 0;
axis_max[i] = 0;
calib_offset[i] = 0;
}
LOGD("Reading config");
rc = yas_cfg_read(&hard_offset, &calib_offset, &accuracy);
if (rc == 0) {
LOGD("Setting initial offsets: %d %d %d, %d %d %d", hard_offset[0], hard_offset[1], hard_offset[2], calib_offset[0], calib_offset[1], calib_offset[2]);
offset_write(path_offset, &hard_offset, &calib_offset, accuracy);
for (i=0 ; i < 3 ; i++) {
axis_min[i] = - calib_offset[i] - max_coeff[i] * 1000;
axis_max[i] = calib_offset[i] + max_coeff[i] * 1000;
axis_calib[i] = calib_offset[i];
}
} else {
offset_read(path_offset, &hard_offset, &calib_offset, &accuracy);
LOGD("Reading initial offsets: %d %d %d", hard_offset[0], hard_offset[1], hard_offset[2]);
for (i=0 ; i < 3 ; i++) {
axis_min[i] = 0;
axis_max[i] = 0;
calib_offset[i] = 0;
}
}
loop:
while (1) {
read(input_fd, &event, sizeof(event));
if (event.type == EV_SYN) {
for (i=0 ; i < 3 ; i++) {
if (-axis_min[i] < axis_max[i]) {
axis_calib[i] = axis_max[i] - max_coeff[i] * 1000;
} else {
axis_calib[i] = axis_min[i] + max_coeff[i] * 1000;
}
axis_calib[i] = axis_calib[i] < 0 ? -axis_calib[i] : axis_calib[i];
if (axis_calib[i] != calib_offset[i]) {
calib_offset[i] = axis_calib[i];
accuracy = 1;
offset_write(path_offset, &hard_offset, &calib_offset, accuracy);
yas_cfg_write(&hard_offset, &calib_offset, accuracy);
}
// printf("axis_calib[%d]=%d\n", i, axis_calib[i]);
}
if (hard_offset[0] == 127 && hard_offset[1] == 127 && hard_offset[2] == 127) {
offset_read(path_offset, &hard_offset, &calib_offset, &accuracy);
if (hard_offset[0] != 127 || hard_offset[1] != 127 || hard_offset[2] != 127) {
LOGD("Reading offsets: %d %d %d", hard_offset[0], hard_offset[1], hard_offset[2]);
yas_cfg_write(&hard_offset, &calib_offset, accuracy);
}
}
}
if(event.type == EV_ABS) {
switch (event.code) {
case ABS_X:
x = event.value;
if (x < axis_min[0])
axis_min[0] = x;
if (x > axis_max[0])
axis_max[0] = x;
break;
case ABS_Y:
y = event.value;
if (y < axis_min[1])
axis_min[1] = y;
if (y > axis_max[1])
axis_max[1] = y;
break;
case ABS_Z:
z = event.value;
if (z < axis_min[2])
axis_min[2] = z;
if (z > axis_max[2])
axis_max[2] = z;
break;
}
}
}
sleep_loop:
while (1) {
sleep(3600);
}
return 0;
}