diff options
Diffstat (limited to 'libsensors/orientationd')
-rw-r--r-- | libsensors/orientationd/input.c | 125 | ||||
-rw-r--r-- | libsensors/orientationd/orientationd.c | 372 | ||||
-rw-r--r-- | libsensors/orientationd/orientationd.h | 50 | ||||
-rw-r--r-- | libsensors/orientationd/vector.c | 61 |
4 files changed, 608 insertions, 0 deletions
diff --git a/libsensors/orientationd/input.c b/libsensors/orientationd/input.c new file mode 100644 index 0000000..eea1494 --- /dev/null +++ b/libsensors/orientationd/input.c @@ -0,0 +1,125 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> + +#include <linux/ioctl.h> +#include <linux/input.h> + +#include "orientationd.h" + +int64_t input_timestamp(struct input_event *event) +{ + if (event == NULL) + return -1; + + return event->time.tv_sec*1000000000LL + event->time.tv_usec*1000; +} + +int input_open(char *name, int write) +{ + 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, write ? O_RDWR : O_RDONLY | O_NONBLOCK); + 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; +} diff --git a/libsensors/orientationd/orientationd.c b/libsensors/orientationd/orientationd.c new file mode 100644 index 0000000..7cc9f0a --- /dev/null +++ b/libsensors/orientationd/orientationd.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * Orientation calculation based on AK8975_FS: + * Copyright (C) 2012 Asahi Kasei Microdevices Corporation, Japan + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <poll.h> +#include <math.h> +#include <linux/ioctl.h> +#include <linux/input.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "orientationd.h" + +#include <hardware/sensors.h> + +#define ABS_CONTROL_REPORT (ABS_THROTTLE) + +#define FLAG_X (1 << 0) +#define FLAG_Y (1 << 1) +#define FLAG_Z (1 << 2) +#define FLAG_ALL (FLAG_X | FLAG_Y | FLAG_Z) + +struct sensor_device { + char *name; + int handle; + float (*get_data)(int value); + int (*set_data)(float value); + + int fd; +}; + +struct sensor_data { + vector v; + int flags; +}; + +float rad2deg(float v) +{ + return (v * 180.0f / 3.1415926535f); +} + +void orientation_calculate(vector *a, vector *m, vector *o) +{ + float azimuth, pitch, roll; + float la, sinp, cosp, sinr, cosr, x, y; + + if (a == NULL || m == NULL || o == NULL) + return; + + la = vector_length(a); + pitch = asinf(-(a->y) / la); + roll = asinf((a->x) / la); + + sinp = sinf(pitch); + cosp = cosf(pitch); + sinr = sinf(roll); + cosr = cosf(roll); + + y = -(m->x) * cosr + m->z * sinr; + x = m->x * sinp * sinr + m->y * cosp + m->z * sinp * cosr; + azimuth = atan2f(y, x); + + o->x = rad2deg(azimuth); + o->y = rad2deg(pitch); + o->z = rad2deg(roll); + + if (o->x < 0) + o->x += 360.0f; +} + +float bma250_acceleration(int value) +{ + return (float) (value * GRAVITY_EARTH) / 256.0f; +} + +float yas530c_magnetic(int value) +{ + return (float) value / 1000.0f; +} + +int yas_orientation(float value) +{ + return (int) (value * 1000); +} + +struct sensor_device bma250_device = { + .name = "accelerometer", + .handle = SENSOR_TYPE_ACCELEROMETER, + .get_data = bma250_acceleration, + .set_data = NULL, + .fd = -1, +}; + +struct sensor_device yas530c_device = { + .name = "geomagnetic", + .handle = SENSOR_TYPE_MAGNETIC_FIELD, + .get_data = yas530c_magnetic, + .set_data = NULL, + .fd = -1, +}; + +struct sensor_device yas_orientation_device = { + .name = "orientation", + .handle = SENSOR_TYPE_ORIENTATION, + .get_data = NULL, + .set_data = yas_orientation, + .fd = -1, +}; + +struct sensor_device *sensor_devices[] = { + &bma250_device, + &yas530c_device, + &yas_orientation_device, +}; + +int sensors_devices_count = sizeof(sensor_devices) / sizeof(struct sensor_device *); + +int sensor_device_open(struct sensor_device *dev) +{ + int fd; + + if (dev == NULL || dev->name == NULL) + return -EINVAL; + + printf("Opening %s\n", dev->name); + + fd = input_open(dev->name, dev->handle == SENSOR_TYPE_ORIENTATION ? 1 : 0); + if (fd < 0) + return -1; + + dev->fd = fd; + + return 0; +} + +void sensor_device_close(struct sensor_device *dev) +{ + if (dev == NULL || dev->fd < 0) + return; + + close(dev->fd); + dev->fd = -1; +} + +struct sensor_device *sensor_device_find_handle(int handle) +{ + int i; + + for (i=0 ; i < sensors_devices_count ; i++) { + if (sensor_devices[i]->handle == handle) + return sensor_devices[i]; + } + + return NULL; +} + +struct sensor_device *sensor_device_find_fd(int fd) +{ + int i; + + for (i=0 ; i < sensors_devices_count ; i++) { + if (sensor_devices[i]->fd == fd) + return sensor_devices[i]; + } + + return NULL; +} + +int sensor_device_get_data(struct sensor_device *dev, struct sensor_data *d, + struct input_event *e) +{ + if (dev == NULL || d == NULL || e == NULL || dev->get_data == NULL) + return -EINVAL; + + if (e->type == EV_ABS) { + switch (e->code) { + case ABS_X: + d->v.x = dev->get_data(e->value); + d->flags |= FLAG_X; + return 0; + case ABS_Y: + d->v.y = dev->get_data(e->value); + d->flags |= FLAG_Y; + return 0; + case ABS_Z: + d->v.z = dev->get_data(e->value); + d->flags |= FLAG_Z; + return 0; + } + } + + return -1; +} + +int sensor_device_set_data(struct sensor_device *dev, struct sensor_data *d) +{ + struct input_event event; + + if (dev == NULL || d == NULL || dev->set_data == NULL) + return -EINVAL; + + event.type = EV_ABS; + event.code = ABS_X; + event.value = dev->set_data(d->v.x); + gettimeofday(&event.time, NULL); + write(dev->fd, &event, sizeof(event)); + + event.type = EV_ABS; + event.code = ABS_Y; + event.value = dev->set_data(d->v.y); + gettimeofday(&event.time, NULL); + write(dev->fd, &event, sizeof(event)); + + event.type = EV_ABS; + event.code = ABS_Z; + event.value = dev->set_data(d->v.z); + gettimeofday(&event.time, NULL); + write(dev->fd, &event, sizeof(event)); + + event.type = EV_SYN; + event.code = SYN_REPORT; + event.value = 0; + gettimeofday(&event.time, NULL); + write(dev->fd, &event, sizeof(event)); + + return 0; +} + +int sensor_device_control(struct sensor_device *dev, struct input_event *e) +{ + int enabled; + + if (dev == NULL || e == NULL) + return -EINVAL; + + if (e->type == EV_ABS && e->code == ABS_CONTROL_REPORT) { + enabled = e->value & (1 << 16); + if (enabled) + return 1; + else + return 0; + } + + return -1; +} + +int main(int argc, char *argv[]) +{ + struct input_event event; + struct sensor_data a, m, o; + + struct sensor_device *dev; + struct pollfd *poll_fds; + + int enabled, data; + int index; + + int rc, c, i; + + memset(&a, 0, sizeof(a)); + memset(&m, 0, sizeof(m)); + memset(&o, 0, sizeof(o)); + + poll_fds = (struct pollfd *) calloc(1, sizeof(struct pollfd) * sensors_devices_count); + + index = -1; + c = 0; + + for (i=0 ; i < sensors_devices_count ; i++) { + rc = sensor_device_open(sensor_devices[i]); + if (rc < 0) + continue; + + poll_fds[c].fd = sensor_devices[i]->fd; + poll_fds[c].events = POLLIN; + + if (sensor_devices[i]->handle == SENSOR_TYPE_ORIENTATION && index < 0) + index = c; + + c++; + } + + if (c <= 0 || index <= 0) + goto exit; + + printf("Starting main loop\n"); + + enabled = 0; + while (1) { + data = 0; + + if (enabled) + rc = poll(poll_fds, c, -1); + else + rc = poll(&poll_fds[index], 1, -1); + + if (rc < 0) + goto exit; + + for (i=0 ; i < c ; i++) { + if (poll_fds[i].revents & POLLIN) { + dev = sensor_device_find_fd(poll_fds[i].fd); + if (dev == NULL) + continue; + + read(dev->fd, &event, sizeof(event)); + + switch (dev->handle) { + case SENSOR_TYPE_ACCELEROMETER: + rc = sensor_device_get_data(dev, &a, &event); + if (rc >= 0) + data = 1; + break; + case SENSOR_TYPE_MAGNETIC_FIELD: + rc = sensor_device_get_data(dev, &m, &event); + if (rc >= 0) + data = 1; + break; + case SENSOR_TYPE_ORIENTATION: + rc = sensor_device_control(dev, &event); + if (rc == 1) + enabled = 1; + else if (rc == 0) + enabled = 0; + break; + } + } + + if (data && a.flags & FLAG_ALL && m.flags & FLAG_ALL) { + dev = sensor_device_find_handle(SENSOR_TYPE_ORIENTATION); + if (dev == NULL) + continue; + + orientation_calculate(&a.v, &m.v, &o.v); + sensor_device_set_data(dev, &o); + } + } + } + + +exit: + for (i=0 ; i < sensors_devices_count ; i++) + sensor_device_close(sensor_devices[i]); + + while (1) { + sleep(3600); + } + + return 0; +} diff --git a/libsensors/orientationd/orientationd.h b/libsensors/orientationd/orientationd.h new file mode 100644 index 0000000..41aad5e --- /dev/null +++ b/libsensors/orientationd/orientationd.h @@ -0,0 +1,50 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/input.h> + +#ifndef _ORIENTATION_H_ +#define _ORIENTATION_H_ + +/* + * Vector + */ + +struct vector { + float x; + float y; + float z; + +}; + +typedef struct vector vector; + +void vector_add(vector *v, vector *a); +void vector_multiply(vector *v, float k); +void vector_cross(vector *v, vector *c, vector *out); +float vector_scalar(vector *v, vector *d); +float vector_length(vector *v); + +/* + * Input + */ + +int64_t input_timestamp(struct input_event *event); +int input_open(char *name, int write); +int sysfs_path_prefix(char *name, char *path_prefix); + +#endif diff --git a/libsensors/orientationd/vector.c b/libsensors/orientationd/vector.c new file mode 100644 index 0000000..ee8ba49 --- /dev/null +++ b/libsensors/orientationd/vector.c @@ -0,0 +1,61 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "orientationd.h" + +void vector_copy(vector *in, vector *out) +{ + out->x = in->x; + out->y = in->y; + out->z = in ->z; +} + +void vector_add(vector *v, vector *a) +{ + v->x += a->x; + v->y += a->y; + v->z += a->z; +} + +void vector_multiply(vector *v, float k) +{ + v->x *= k; + v->y *= k; + v->z *= k; +} + +void vector_cross(vector *v, vector *c, vector *out) +{ + struct vector t; + + t.x = v->x * c->z - v->z * c->y; + t.y = v->z * c->x - v->x * c->z; + t.y = v->y * c->y - v->y * c->x; + vector_copy(&t, out); +} + +float vector_scalar(vector *v, vector *d) +{ + return v->x * d->x + v->y * d->y + v->z * d->z; +} + +float vector_length(vector *v) +{ + return sqrtf(vector_scalar(v, v)); +} |