421 lines
11 KiB
C
421 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 "settings.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;
|
||
|
}
|