Files
iio-sensor-proxy/src/fake-input-accelerometer.c
2021-01-21 16:15:46 +01:00

311 lines
6.2 KiB
C

/*
* Copyright (c) 2011, 2014 Bastien Nocera <hadess@hadess.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <stdio.h>
#include <termios.h>
#include <gudev/gudev.h>
#include "uinput.h"
#include "utils.h"
#define ONEG 256
typedef struct {
GMainLoop *loop;
int uinput;
int accel_x, accel_y, accel_z;
GUdevClient *client;
GUdevDevice *uinput_dev;
struct termios old_tio;
} OrientationData;
static GUdevDevice *
setup_uinput_udev (GUdevClient *client)
{
GList *devices, *l;
GUdevDevice *ret = NULL;
devices = g_udev_client_query_by_subsystem (client, "input");
for (l = devices; l != NULL; l = l->next) {
GUdevDevice *dev = l->data;
if (g_udev_device_get_property_as_boolean (dev, "ID_INPUT_ACCELEROMETER")) {
ret = g_object_ref (dev);
break;
}
}
g_list_free_full (devices, g_object_unref);
return ret;
}
static int
write_sysfs_string (char *filename,
char *basedir,
char *val)
{
int ret = 0;
g_autoptr(FILE) sysfsfp = NULL;
char *temp;
temp = g_build_filename (basedir, filename, NULL);
sysfsfp = fopen (temp, "w");
if (sysfsfp == NULL) {
ret = -errno;
goto error_free;
}
fprintf(sysfsfp, "%s", val);
error_free:
g_free(temp);
return ret;
}
static gboolean
send_uinput_event (OrientationData *data)
{
struct uinput_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = data->accel_x;
(void) !write (data->uinput, &ev, sizeof(ev));
ev.code = ABS_Y;
ev.value = data->accel_y;
(void) !write (data->uinput, &ev, sizeof(ev));
ev.code = ABS_Z;
ev.value = data->accel_z;
(void) !write (data->uinput, &ev, sizeof(ev));
memset(&ev, 0, sizeof(ev));
gettimeofday(&ev.time, NULL);
ev.type = EV_SYN;
ev.code = SYN_REPORT;
(void) !write (data->uinput, &ev, sizeof(ev));
if (!data->uinput_dev)
data->uinput_dev = setup_uinput_udev (data->client);
if (!data->uinput_dev)
return FALSE;
if (write_sysfs_string ((char *) "uevent", (char *) g_udev_device_get_sysfs_path (data->uinput_dev), (char *) "change") < 0) {
g_warning ("Failed to write uevent");
return FALSE;
}
return TRUE;
}
static gboolean
setup_uinput (OrientationData *data)
{
struct uinput_dev dev;
int fd;
fd = open("/dev/uinput", O_RDWR);
if (fd < 0) {
g_warning ("Could not open uinput");
return FALSE;
}
memset (&dev, 0, sizeof(dev));
snprintf (dev.name, sizeof (dev.name), "%s", "iio-sensor-proxy test application");
dev.id.bustype = BUS_VIRTUAL;
dev.id.vendor = 0x00;
dev.id.product = 0x00;
/* 1G accel is reported as ~256, so clamp to 2G */
dev.absmin[ABS_X] = dev.absmin[ABS_Y] = dev.absmin[ABS_Z] = -512;
dev.absmax[ABS_X] = dev.absmax[ABS_Y] = dev.absmax[ABS_Z] = 512;
if (write (fd, &dev, sizeof(dev)) != sizeof(dev)) {
g_warning ("Error creating uinput device");
goto bail;
}
/* enabling key events */
if (ioctl (fd, UI_SET_EVBIT, EV_ABS) < 0) {
g_warning ("Error enabling uinput absolute events");
goto bail;
}
/* enabling keys */
if (ioctl (fd, UI_SET_ABSBIT, ABS_X) < 0 ||
ioctl (fd, UI_SET_ABSBIT, ABS_Y) < 0 ||
ioctl (fd, UI_SET_ABSBIT, ABS_Z) < 0) {
g_warning ("Couldn't enable uinput axis");
goto bail;
}
/* creating the device */
if (ioctl (fd, UI_DEV_CREATE) < 0) {
g_warning ("Error creating uinput device");
goto bail;
}
data->uinput = fd;
return TRUE;
bail:
close (fd);
return FALSE;
}
static void
keyboard_usage (void)
{
g_print ("Valid keys are: u (up), d (down), l (left), r (right), q/x (quit)\n");
}
static gboolean
check_keyboard (GIOChannel *source,
GIOCondition condition,
OrientationData *data)
{
GIOStatus status;
char buf[1];
status = g_io_channel_read_chars (source, buf, 1, NULL, NULL);
if (status == G_IO_STATUS_ERROR ||
status == G_IO_STATUS_EOF) {
g_main_loop_quit (data->loop);
return FALSE;
}
if (status == G_IO_STATUS_AGAIN)
return TRUE;
switch (buf[0]) {
case 'u':
data->accel_x = 0;
data->accel_y = -ONEG;
data->accel_z = 0;
break;
case 'd':
data->accel_x = 0;
data->accel_y = ONEG;
data->accel_z = 0;
break;
case 'l':
data->accel_x = ONEG;
data->accel_y = 0;
data->accel_z = 0;
break;
case 'r':
data->accel_x = -ONEG;
data->accel_y = 0;
data->accel_z = 0;
break;
case 'q':
case 'x':
g_main_loop_quit (data->loop);
return FALSE;
default:
keyboard_usage ();
return TRUE;
}
send_uinput_event (data);
return TRUE;
}
static gboolean
setup_keyboard (OrientationData *data)
{
GIOChannel *channel;
struct termios new_tio;
tcgetattr(STDIN_FILENO, &data->old_tio);
new_tio = data->old_tio;
new_tio.c_lflag &=(~ICANON & ~ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
channel = g_io_channel_unix_new (STDIN_FILENO);
if (!channel) {
g_warning ("Failed to open stdin");
return FALSE;
}
if (g_io_channel_set_encoding (channel, NULL, NULL) != G_IO_STATUS_NORMAL) {
g_warning ("Failed to set stdin encoding to NULL");
return FALSE;
}
g_io_add_watch (channel, G_IO_IN, (GIOFunc) check_keyboard, data);
return TRUE;
}
static void
free_orientation_data (OrientationData *data)
{
if (data == NULL)
return;
tcsetattr(STDIN_FILENO, TCSANOW, &data->old_tio);
if (data->uinput > 0)
close (data->uinput);
g_clear_object (&data->uinput_dev);
g_clear_object (&data->client);
g_clear_pointer (&data->loop, g_main_loop_unref);
g_free (data);
}
int main (int argc, char **argv)
{
OrientationData *data;
const gchar * const subsystems[] = { "input", NULL };
int ret = 0;
data = g_new0 (OrientationData, 1);
data->client = g_udev_client_new (subsystems);
if (!setup_keyboard (data)) {
g_warning ("Failed to setup keyboard capture");
ret = 1;
goto out;
}
/* Set up uinput */
if (!setup_uinput (data)) {
ret = 1;
goto out;
}
/* Start with the 'normal' orientation */
data->accel_x = 0;
data->accel_y = ONEG;
data->accel_z = 0;
send_uinput_event (data);
keyboard_usage ();
data->loop = g_main_loop_new (NULL, TRUE);
g_main_loop_run (data->loop);
out:
free_orientation_data (data);
return ret;
}