/* * accelerometers.c * * Accelerometer stuff * * (c) 2008-2009 Thomas White * (c) 2008 Joachim Breitner * * This file is part of OpenMooCow - accelerometer moobox simulator * * OpenMooCow 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. * * OpenMooCow 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 OpenMooCow. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "types.h" #include "audio.h" struct input_event { struct timeval time; uint16_t type; uint16_t code; int32_t value; }; #define EV_SYN (0x00) #define EV_REL (0x02) #define EV_ABS (0x03) #define SYN_REPORT (0x00) #define REL_X (0x00) #define REL_Y (0x01) #define REL_Z (0x02) /* Try to open the threshold sysfs file for Freerunner's accelerometers. * Try methods for both old and new kernels */ static FILE *accelerometer_freerunner_open_threshold(const char *mode) { FILE *fh; /* Try 2.6.24 method */ fh = fopen("/sys/devices/platform/lis302dl.2/threshold", mode); if ( fh != NULL ) { return fh; } /* Try 2.6.28+ method */ fh = fopen("/sys/class/i2c-adapter/i2c-0/0-0073/lis302dl.2/threshold", mode); if ( fh != NULL ) { return fh; } /* Try 2.6.29-ish+ method */ fh = fopen("/sys/devices/platform/s3c2440-i2c/i2c-adapter:i2c-0" "/0-0073/spi_s3c24xx_gpio.0/spi3.1/threshold", mode); if ( fh != NULL ) { return fh; } return NULL; } static void accelerometer_freerunner_try_threshold(AccelHandle *accel) { FILE *fh; int rval; /* Save the old threshold */ fh = accelerometer_freerunner_open_threshold("r"); if ( fh == NULL ) { fprintf(stderr, "Couldn't read old accelerometer threshold.\n"); accel->old_threshold = -1; return; } rval = fscanf(fh, "%i", &accel->old_threshold); if ( rval != 1 ) { /* Failed */ fprintf(stderr, "Couldn't read old accelerometer threshold.\n"); accel->old_threshold = -1; } printf("Old threshold was %i\n", accel->old_threshold); fclose(fh); /* Set the threshold to zero */ fh = accelerometer_freerunner_open_threshold("w"); if ( fh == NULL ) { fprintf(stderr, "Failed to disable accelerometer threshold.\n"); return; } fprintf(fh, "0\n"); printf("Successfully disabled accelerometer threshold.\n"); fclose(fh); } static void accelerometer_freerunner_try_restore_threshold(AccelHandle *accel) { FILE *fh; int rval; int new_threshold; if ( accel->old_threshold < 0 ) { printf("Not restoring threshold.\n"); } fh = accelerometer_freerunner_open_threshold("r"); if ( fh == NULL ) { fprintf(stderr, "Failed to restore accelerometer threshold.\n"); return; } rval = fscanf(fh, "%i", &new_threshold); if ( rval != 1 ) { fprintf(stderr, "Couldn't read new accelerometer threshold\n"); } fclose(fh); /* Restore only if it hasn't been altered externally */ if ( new_threshold != 0 ) { printf("Threshold changed - not restoring.\n"); return; } /* Set it back to the old value */ fh = accelerometer_freerunner_open_threshold("w"); if ( fh == NULL ) { fprintf(stderr, "Failed to restore accelerometer threshold.\n"); return; } fprintf(fh, "%i\n", accel->old_threshold); printf("Successfully restored accelerometer threshold.\n"); fclose(fh); } AccelHandle *accelerometer_open() { AccelHandle *accel; /* Initialise accelerometer data structure */ accel = malloc(sizeof(AccelHandle)); if ( accel == NULL ) return NULL; accel->x = 0; accel->y = 0; accel->z = 0; accel->lx = 0; accel->ly = 0; accel->lz = 0; accel->state = 0; accel->type = ACCEL_UNKNOWN; /* Determine accelerometer type */ accel->fd = open("/dev/input/by-path/platform-hdaps-event-joystick", O_RDONLY, O_NONBLOCK); if ( accel->fd != -1 ) { accel->type = ACCEL_HDAPS; printf("ThinkPad HDAPS detected\n"); return accel; } accel->fd = open("/dev/input/event3", O_RDONLY, 0); if ( accel->fd != -1 ) { accel->type = ACCEL_FREERUNNER; printf("Neo Freerunner detected\n"); accelerometer_freerunner_try_threshold(accel); return accel; } /* Try other types here */ fprintf(stderr, "Couldn't determine accelerometer type\n"); return accel; } static void accelerometer_shutdown(AccelHandle *accel) { if ( accel->type == ACCEL_FREERUNNER ) { accelerometer_freerunner_try_restore_threshold(accel); } } int accelerometer_moo_hdaps(AccelHandle *accel) { struct input_event ev; size_t rval; rval = read(accel->fd, &ev, sizeof(ev)); if ( rval != sizeof(ev) ) { fprintf(stderr, "Couldn't read accelerometer data"); return 0; } if (ev.type == EV_ABS && ev.code == REL_Y) { if (accel->state == 0 && abs(ev.value)>100) { // Laptop tilted far enough accel->state=1; return 0; } if (accel->state == 1 && abs(ev.value)<70) { // Laptop tilted back, play sound accel->state=2; return 1; } if (accel->state == 2 && abs(ev.value)<20) { // Laptop almost at center, enable another round accel->state=0; return 0; } } return 0; /* fprintf(stderr, "Event: time %ld.%06ld, type %s, code %d, value %d\n", ev.time.tv_sec, ev.time.tv_usec, ev.type == EV_REL ? "REL" : ev.type == EV_ABS ? "ABS" : "Other" , ev.code, ev.value); */ } int accelerometer_moo_freerunner(AccelHandle *accel) { struct input_event ev; size_t rval; fd_set fds; struct timeval t; FD_ZERO(&fds); FD_SET(accel->fd, &fds); t.tv_sec = 0; t.tv_usec = 0; select(1+accel->fd, &fds, NULL, NULL, &t); if ( FD_ISSET(accel->fd, &fds) ) { rval = read(accel->fd, &ev, sizeof(ev)); if ( rval != sizeof(ev) ) { fprintf(stderr, "Couldn't read accelerometer data"); return 0; } } else { return 0; /* No data */ } /* Nasty, but the event type has changed. * For now, we must support both types. */ if ( (ev.type == EV_REL) || (ev.type == EV_ABS) ) { if ( ev.code == REL_X ) { accel->lx = ev.value; } if ( ev.code == REL_Y ) { accel->ly = ev.value; } if ( ev.code == REL_Z ) { accel->lz = ev.value; } } if ( ev.type == EV_SYN ) { if ( ev.code == SYN_REPORT ) { accel->x = accel->lx; accel->y = accel->ly; accel->z = accel->lz; } } if ( (accel->y < -500) && (accel->state > -1000) ) { accel->state = -1000; return 1; } if ( (accel->y > 500) && (accel->state < 1000) ) { accel->state = 1000; return 1; } return 0; } int accelerometer_moo(AccelHandle *accel) { switch ( accel->type ) { case ACCEL_UNKNOWN : { return 0; } case ACCEL_FREERUNNER : { return accelerometer_moo_freerunner(accel); } case ACCEL_HDAPS : { return accelerometer_moo_hdaps(accel); } /* Add other types here. */ } return 0; } /* The accelerometer work thread */ static void *accel_work(void *data) { AccelHandle *accel; int *finished = data; accel = accelerometer_open(); audio_setup(); while ( !(*finished) ) { if (accelerometer_moo(accel)) audio_trigger_moo(); usleep(25000); } accelerometer_shutdown(accel); audio_shutdown(); return NULL; } GThread *accelerometer_start(int *finished) { return g_thread_create(accel_work, finished, TRUE, NULL); }