rockbox/firmware/target/hosted/ibasso/button-ibasso.c
Solomon Peachy 1303be3437 ibasso: Get rid of #include "settings.h" in the button code
(It mucks with paths causing the input device open to fail)

Change-Id: I1e8a63d09c229ed9bacbd599a7869e875df21fd2
2021-04-10 10:25:20 -04:00

417 lines
11 KiB
C

/***************************************************************************
* __________ __ ___
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
* Copyright (C) 2014 by Mario Basister: iBasso DX90 port
* Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
* Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
*
* 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 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/inotify.h>
#include <sys/limits.h>
#include <sys/poll.h>
#include "config.h"
#include "backlight.h"
#include "button.h"
#include "debug.h"
#include "panic.h"
#include "touchscreen.h"
#include "button-ibasso.h"
#include "button-target.h"
#include "debug-ibasso.h"
#include "sysfs-ibasso.h"
#define EVENT_TYPE_BUTTON 1
/* /dev/input/event0 */
#define EVENT_CODE_BUTTON_LINEOUT 113
#define EVENT_CODE_BUTTON_SPDIF 114
#define EVENT_CODE_BUTTON_HOLD 115
/* /dev/input/event1 */
#define EVENT_CODE_BUTTON_SDCARD 143
#define EVENT_TYPE_TOUCHSCREEN 3
/* /dev/input/event2 */
#define EVENT_CODE_TOUCHSCREEN_X 53
#define EVENT_CODE_TOUCHSCREEN_Y 54
#define EVENT_CODE_TOUCHSCREEN 57
#define EVENT_VALUE_TOUCHSCREEN_PRESS 1
#define EVENT_VALUE_TOUCHSCREEN_RELEASE -1
/*
Changing bit, when hold switch is toggled.
Bit is off when hold switch is engaged.
*/
#define HOLD_SWITCH_BIT 16
/*
Changing bit, when coaxial out is plugged.
Bit is off when coaxial out is plugged in.
*/
#define COAX_BIT 32
/*
Changing bit, when line out is plugged.
Bit is off when line out is plugged in.
*/
#define SPDIF_BIT 64
/* State of the hold switch; true: hold switch engaged. */
static bool _hold = false;
/* See button.h. */
bool button_hold(void)
{
char hold_state;
if(! sysfs_get_char(SYSFS_HOLDKEY, &hold_state))
{
DEBUGF("ERROR %s: Can not get hold switch state.", __func__);
hold_state = HOLD_SWITCH_BIT;
}
/*DEBUGF("%s: hold_state: %d, %c.", __func__, hold_state, hold_state);*/
/*bool coax_connected = ! (hold_state & COAX_BIT);
bool spdif_connected = ! (hold_state & SPDIF_BIT);*/
_hold = ! (hold_state & HOLD_SWITCH_BIT);
/*DEBUGF("%s: _hold: %d, coax_connected: %d, spdif_connected: %d.", __func__, _hold, coax_connected, spdif_connected);*/
return _hold;
}
/* Input devices monitored with poll API. */
static struct pollfd* _fds = NULL;
/* Number of input devices monitored with poll API. */
static nfds_t _nfds = 0;
/* The names of the devices in _fds. */
static char** _device_names = NULL;
/* Open device device_name and add it to the list of polled devices. */
static bool open_device(const char* device_name)
{
int fd = open(device_name, O_RDONLY);
if(fd == -1)
{
DEBUGF("ERROR %s: open failed on %s.", __func__, device_name);
return false;
}
struct pollfd* new_fds = realloc(_fds, sizeof(struct pollfd) * (_nfds + 1));
if(new_fds == NULL)
{
DEBUGF("ERROR %s: realloc for _fds failed.", __func__);
panicf("ERROR %s: realloc for _fds failed.", __func__);
return false;
}
_fds = new_fds;
_fds[_nfds].fd = fd;
_fds[_nfds].events = POLLIN;
char** new_device_names = realloc(_device_names, sizeof(char*) * (_nfds + 1));
if(new_device_names == NULL)
{
DEBUGF("ERROR %s: realloc for _device_names failed.", __func__);
panicf("ERROR %s: realloc for _device_names failed.", __func__);
return false;
}
_device_names = new_device_names;
_device_names[_nfds] = strdup(device_name);
if(_device_names[_nfds] == NULL)
{
DEBUGF("ERROR %s: strdup failed.", __func__);
panicf("ERROR %s: strdup failed.", __func__);
return false;
}
++_nfds;
DEBUGF("DEBUG %s: Opened device %s.", __func__, device_name);
return true;
}
/* See button.h. */
void button_init_device(void)
{
TRACE;
if((_fds != NULL) || (_nfds != 0) || (_device_names != NULL))
{
DEBUGF("ERROR %s: Allready initialized.", __func__);
panicf("ERROR %s: Allready initialized.", __func__);
return;
}
/* The input device directory. */
static const char device_path[] = "/dev/input";
/* Path delimeter. */
static const char delimeter[] = "/";
/* Open all devices in device_path. */
DIR* dir = opendir(device_path);
if(dir == NULL)
{
DEBUGF("ERROR %s: opendir failed: errno: %d.", __func__, errno);
panicf("ERROR %s: opendir failed: errno: %d.", __func__, errno);
return;
}
char device_name[PATH_MAX];
strcpy(device_name, device_path);
strcat(device_name, delimeter);
char* device_name_idx = device_name + strlen(device_name);
struct dirent* dir_entry;
while((dir_entry = readdir(dir)))
{
if( ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '\0'))
|| ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '.') && (dir_entry->d_name[2] == '\0')))
{
continue;
}
strcpy(device_name_idx, dir_entry->d_name);
/* Open and add device to _fds. */
open_device(device_name);
}
closedir(dir);
/* Sanity check. */
if(_nfds < 2)
{
DEBUGF("ERROR %s: No input devices.", __func__);
panicf("ERROR %s: No input devices.", __func__);
return;
}
/*
Hold switch has a separate interface for its state.
Input events just report that it has been toggled, but not the state.
*/
button_hold();
}
/* Last known touchscreen coordinates. */
static int _last_x = 0;
static int _last_y = 0;
/* Last known touchscreen state. */
static enum
{
TOUCHSCREEN_STATE_UNKNOWN = 0,
TOUCHSCREEN_STATE_UP,
TOUCHSCREEN_STATE_DOWN
} _last_touch_state = TOUCHSCREEN_STATE_UNKNOWN;
static bool handle_touchscreen_event(__u16 code, __s32 value)
{
bool read_more = false;
switch(code)
{
case EVENT_CODE_TOUCHSCREEN_X:
{
_last_x = value;
/* x -> next will be y. */
read_more = true;
break;
}
case EVENT_CODE_TOUCHSCREEN_Y:
{
_last_y = value;
break;
}
case EVENT_CODE_TOUCHSCREEN:
{
if(value == EVENT_VALUE_TOUCHSCREEN_PRESS)
{
_last_touch_state = TOUCHSCREEN_STATE_DOWN;
/* Press -> next will be x. */
read_more = true;
}
else
{
_last_touch_state = TOUCHSCREEN_STATE_UP;
}
break;
}
}
return read_more;
}
/* Last known hardware buttons pressed. */
static int _last_btns = BUTTON_NONE;
/* See button.h. */
int button_read_device(int *data)
{
bool read_more = true;
while(read_more)
{
read_more = false;
/* Poll all input devices. */
poll(_fds, _nfds, 0);
for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
{
if(! (_fds[fds_idx].revents & POLLIN))
{
continue;
}
struct input_event event;
if(read(_fds[fds_idx].fd, &event, sizeof(event)) < (int) sizeof(event))
{
DEBUGF("ERROR %s: Read of input devices failed.", __func__);
continue;
}
/*DEBUGF("DEBUG %s: device: %s, event.type: %d, event.code: %d, event.value: %d", __func__, _device_names[fds_idx], event.type, event.code, event.value);*/
switch(event.type)
{
case EVENT_TYPE_BUTTON:
{
if(event.code == EVENT_CODE_BUTTON_HOLD)
{
/* Hold switch toggled, update hold switch state. */
button_hold();
backlight_hold_changed(_hold);
_last_btns = BUTTON_NONE;
break;
}
_last_btns = handle_button_event(event.code, event.value, _last_btns);
if(_hold)
{
/* Hold switch engaged. Ignore all button events. */
_last_btns = BUTTON_NONE;
}
/*DEBUGF("DEBUG %s: _last_btns: %#8.8x", __func__, _last_btns);*/
break;
}
case EVENT_TYPE_TOUCHSCREEN:
{
if(_hold)
{
/* Hold switch engaged, ignore all touchscreen events. */
_last_touch_state = TOUCHSCREEN_STATE_UNKNOWN;
_last_btns = BUTTON_NONE;
}
else
{
read_more = handle_touchscreen_event(event.code, event.value);
/*DEBUGF("DEBUG %s: _last_touch_state: %d, _last_x: %d, _last_y: %d, read_more: %s", __func__, _last_touch_state, _last_x, _last_y, read_more ? "true" : "false");*/
}
break;
}
}
}
}
/*
Get grid button/coordinates based on the current touchscreen mode
Caveat: The caller seemingly depends on *data always being filled with
the last known touchscreen position, so always call
touchscreen_to_pixels().
*/
int touch = touchscreen_to_pixels(_last_x, _last_y, data);
if(_last_touch_state == TOUCHSCREEN_STATE_DOWN)
{
return _last_btns | touch;
}
/*DEBUGF("DEBUG %s: _last_btns: %#8.8x.", __func__, _last_btns);*/
return _last_btns;
}
void button_close_device(void)
{
TRACE;
if(_fds)
{
for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
{
close(_fds[fds_idx].fd);
}
free(_fds);
_fds = NULL;
}
if(_device_names)
{
for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
{
free(_device_names[fds_idx]);
}
free(_device_names);
_device_names = NULL;
}
_nfds = 0;
}