summaryrefslogtreecommitdiffstats
path: root/firmware/target/hosted/sonynwz/button-nwz.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted/sonynwz/button-nwz.c')
-rw-r--r--firmware/target/hosted/sonynwz/button-nwz.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/firmware/target/hosted/sonynwz/button-nwz.c b/firmware/target/hosted/sonynwz/button-nwz.c
new file mode 100644
index 0000000000..0cbb3c5869
--- /dev/null
+++ b/firmware/target/hosted/sonynwz/button-nwz.c
@@ -0,0 +1,323 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2016 Amaury Pouly
+ *
+ * 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 "button.h"
+#define LOGF_ENABLE
+#include "logf.h"
+#include "panic.h"
+#include "backlight.h"
+
+#include "nwz_keys.h"
+#include "nwz_ts.h"
+
+#include <poll.h>
+#include <dir.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* device types */
+#define DEV_KEY 0 /* icx_keys driver */
+#define DEV_TOUCH 1 /* icx_touch_screen driver */
+
+/* HOLD status */
+static bool hold_status;
+/* button bitmap */
+static int button_bitmap;
+/* poll() descriptors (up to 2 for now: keys and touchscreen) */
+#define NR_POLL_DESC 2
+static struct pollfd poll_fds[NR_POLL_DESC];
+static nfds_t poll_nfds;
+int dev_type[NR_POLL_DESC]; /* DEV_* */
+
+#ifdef HAVE_TOUCHSCREEN
+/* structure to track touch state */
+static struct
+{
+ int x, y; /* current position (valid is touch is true) */
+ int max_x, max_y; /* maximum possible values */
+ int pressure, tool_width; /* current pressure and tool width */
+ int max_pressure, max_tool_width; /* maximum possible values */
+ bool touch; /* is the user touching the screen? */
+ /* the hardware supports "flick" gesture */
+ bool flick; /* was the action a flick? */
+ int flick_x, flick_y; /* if so, this is the flick direction */
+}ts_state;
+/* rockbox state, updated from ts state on SYN event */
+static int touch_x, touch_y;
+static bool touch_detect;
+
+/* get touchscreen information and init state */
+int ts_init_state(int fd)
+{
+ memset(state, 0, sizeof(struct nwz_ts_state_t));
+ struct input_absinfo info;
+ if(ioctl(fd, EVIOCGABS(ABS_X), &info) < 0)
+ return -1;
+ state->max_x = info.maximum;
+ if(ioctl(fd, EVIOCGABS(ABS_Y), &info) < 0)
+ return -1;
+ state->max_y = info.maximum;
+ if(ioctl(fd, EVIOCGABS(ABS_PRESSURE), &info) < 0)
+ return -1;
+ state->max_pressure = info.maximum;
+ if(ioctl(fd, EVIOCGABS(ABS_TOOL_WIDTH), &info) < 0)
+ return -1;
+ state->max_tool_width = info.maximum;
+ touch_detect = false;
+ return 0;
+}
+
+void handle_touch(struct input_event *evt)
+{
+ switch(evt->type)
+ {
+ case EV_SYN:
+ /* on SYN, we copy the state to the rockbox state */
+ touch_x = ts_state->x;
+ touch_y = ts_state->y;
+ /* map coordinate to screen */
+ x = x * LCD_WIDTH / ts_state->max_x;
+ y = y * LCD_HEIGHT / ts_state->max_y;
+ /* don't trust driver reported ranges */
+ x = MAX(0, MIN(x, LCD_WIDTH - 1));
+ y = MAX(0, MIN(y, LCD_HEIGHT - 1));
+ touch_detect = ts_state->touch;
+ /* reset flick */
+ state->flick = false;
+ break;
+ case EV_REL:
+ if(evt->code == REL_RX)
+ state->flick_x = evt->value;
+ else if(evt->code == REL_RY)
+ state->flick_y = evt->value;
+ else
+ break;
+ state->flick = true;
+ break;
+ case EV_ABS:
+ if(evt->code == ABS_X)
+ state->x = evt->value;
+ else if(evt->code == ABS_Y)
+ state->y = evt->value;
+ else if(evt->code == ABS_PRESSURE)
+ state->pressure = evt->value;
+ else if(evt->code == ABS_TOOL_WIDTH)
+ state->tool_width = evt->value;
+ break;
+ case EV_KEY:
+ if(evt->code == BTN_TOUCH)
+ state->touch = evt->value;
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static void load_hold_status(int fd)
+{
+ /* HOLD is reported as the first LED */
+ unsigned long led_hold = 0;
+ if(ioctl(fd, EVIOCGLED(sizeof(led_hold)), &led_hold) < 0)
+ logf("cannot read HOLD status: %s", strerror(errno));
+ hold_status = !!led_hold;
+}
+
+static void key_init_state(int fd)
+{
+ /* the driver knows the HOLD statu at all times */
+ load_hold_status(fd);
+ /* the driver can be queried for button status but the output is garbage
+ * so just assume no keys are pressed */
+ button_bitmap = 0;
+}
+
+static void open_input_device(const char *path)
+{
+ int fd = open(path, O_RDWR);
+ if(fd < 0)
+ return;
+ /* query name */
+ char name[256];
+ if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
+ {
+ close(fd);
+ return;
+ }
+
+ if(strcmp(name, NWZ_KEY_NAME) == 0)
+ dev_type[poll_nfds] = DEV_KEY;
+ else if(strcmp(name, NWZ_TS_NAME) == 0)
+ dev_type[poll_nfds] = DEV_TOUCH;
+ else
+ {
+ /* only keep devices we know about */
+ close(fd);
+ return;
+ }
+ /* if we found a key driver, we can read the hold status from it (and keep
+ * it updated with events) */
+ if(dev_type[poll_nfds] == DEV_KEY)
+ key_init_state(fd);
+#ifdef HAVE_TOUCHSCREEN
+ else if(dev_type[poll_nfds] == DEV_TOUCH)
+ ts_init_state(fd);
+#endif
+ /* fill poll descriptor */
+ poll_fds[poll_nfds].fd = fd;
+ poll_fds[poll_nfds].events = POLLIN;
+ poll_fds[poll_nfds].revents = 0;
+ poll_nfds++;
+}
+
+/* keycode -> rockbox button mapping */
+static int button_map[NWZ_KEY_MASK + 1] =
+{
+ [0 ... NWZ_KEY_MASK] = 0,
+ [NWZ_KEY_PLAY] = BUTTON_PLAY,
+ [NWZ_KEY_RIGHT] = BUTTON_RIGHT,
+ [NWZ_KEY_LEFT] = BUTTON_LEFT,
+ [NWZ_KEY_UP] = BUTTON_UP,
+ [NWZ_KEY_DOWN] = BUTTON_DOWN,
+ [NWZ_KEY_ZAPPIN] = 0,
+ [NWZ_KEY_AD0_6] = 0,
+ [NWZ_KEY_AD0_7] = 0,
+ [NWZ_KEY_NONE] = 0,
+ [NWZ_KEY_VOL_DOWN] = BUTTON_VOL_DOWN,
+ [NWZ_KEY_VOL_UP] = BUTTON_VOL_UP,
+ [NWZ_KEY_BACK] = BUTTON_BACK,
+ [NWZ_KEY_OPTION] = BUTTON_POWER,
+ [NWZ_KEY_BT] = 0,
+ [NWZ_KEY_AD1_5] = 0,
+ [NWZ_KEY_AD1_6] = 0,
+ [NWZ_KEY_AD1_7] = 0,
+};
+
+static void handle_key(struct input_event evt)
+{
+ /* See headers/nwz_keys.h for explanation of Sony's nonstandard interface */
+ int keycode = evt.code & NWZ_KEY_MASK;
+ bool press = (evt.value == 0);
+ if(press)
+ button_bitmap |= button_map[keycode];
+ else
+ button_bitmap &= ~button_map[keycode];
+ bool new_hold_status = !!(evt.code & NWZ_KEY_HOLD_MASK);
+ if(new_hold_status != hold_status)
+ {
+ hold_status = new_hold_status;
+#ifndef BOOTLOADER
+ backlight_hold_changed(hold_status);
+#endif
+ }
+}
+
+bool button_hold(void)
+{
+ return hold_status;
+}
+
+void button_init_device(void)
+{
+ const char *input_path = "/dev/input";
+ char device_name[PATH_MAX];
+ /* find what input devices are available */
+ DIR* input_dir = opendir(input_path);
+ if(input_dir == NULL)
+ panicf("Cannot read /dev/input directory: %s", strerror(errno));
+ strcpy(device_name, input_path);
+ strcat(device_name, "/");
+ char *device_name_p = device_name + strlen(device_name);
+ struct dirent *dir_entry;
+ while((dir_entry = readdir(input_dir)))
+ {
+ /* skip '.' and '..' entries */
+ if(strcmp(dir_entry->d_name, ".") == 0 || strcmp(dir_entry->d_name, "..") == 0)
+ continue;
+ /* create device full path and open it */
+ strcpy(device_name_p, dir_entry->d_name);
+ open_input_device(device_name);
+ }
+ closedir(input_dir);
+ /* check if we have at least one device */
+ if(poll_nfds == 0)
+ panicf("No input device found");
+}
+
+int button_read_device(
+#ifdef HAVE_BUTTON_DATA
+ int *data
+#else
+ void
+#endif
+ )
+{
+ struct input_event event;
+ /* check if there are any events pending and process them */
+ while(true)
+ {
+ /* stop when there are no more events */
+ if(poll(poll_fds, poll_nfds, 0) == 0)
+ break;
+ for(unsigned int i = 0; i < poll_nfds; i++)
+ {
+ /* only read if we won't block */
+ if(!(poll_fds[i].revents & POLLIN))
+ continue;
+ if(read(poll_fds[i].fd, &event, sizeof(event)) != (int)sizeof(event))
+ continue;
+ if(dev_type[i] == DEV_KEY)
+ handle_key(event);
+#ifdef HAVE_TOUCHSCREEN
+ else if(dev_type[i] == DEV_TOUCH)
+ handle_touch(event);
+#endif
+ }
+ }
+#ifdef HAVE_TOUCHSCREEN
+ button_bitmap |= touchscreen_to_pixels(touch_x, touch_y, data);
+#endif
+ return hold_status ? 0 : button_bitmap;
+}
+
+void nwz_button_reload_after_suspend(void)
+{
+ /* reinit everything, particularly important for keys and HOLD */
+ for(unsigned int i = 0; i < poll_nfds; i++)
+ {
+ if(dev_type[i] == DEV_KEY)
+ key_init_state(poll_fds[i].fd);
+#ifdef HAVE_TOUCHSCREEN
+ else if(dev_type[i] == DEV_TOUCH)
+ ts_init_state(poll_fds[i].fd);
+#endif
+ }
+}
+
+void button_close_device(void)
+{
+ /* close descriptors */
+ for(unsigned int i = 0; i < poll_nfds; i++)
+ close(poll_fds[i].fd);
+}