From 1d121e8c082fe67757cf0d4df7b9e6ca1e26f755 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Thu, 23 Feb 2017 11:33:19 +0100 Subject: Initial commit for the Sony NWZ linux port SUPPORTED SERIES: - NWZ-E450 - NWZ-E460 - NWZ-E470 - NWZ-E580 - NWZ-A10 NOTES: - bootloader makefile convert an extra font to be installed alongside the bootloader since sysfont is way too small - the toolsicon bitmap comes from the Oxygen iconset - touchscreen driver is untested TODO: - implement audio routing driver (pcm is handled by pcm-alsa) - fix playback: it crashes on illegal instruction in DEBUG builds - find out why the browser starts at / instead of /contents - implement radio support - implement return to OF for usb handling - calibrate battery curve (NB: of can report a battery level on a 0-5 scale but probabl don't want to use that ?) - implement simulator build (we need a nice image of the player) - figure out if we can detect jack removal POTENTIAL TODOS: - try to build a usb serial gadget and gdbserver Change-Id: Ic77d71e0651355d47cc4e423a40fb64a60c69a80 --- firmware/target/hosted/sonynwz/button-nwz.c | 323 ++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 firmware/target/hosted/sonynwz/button-nwz.c (limited to 'firmware/target/hosted/sonynwz/button-nwz.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} -- cgit