/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Copyright (C) 2016 by 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 "cpu.h" #include "system.h" #include "kernel.h" #include "lcd.h" #include "font.h" #include "adc.h" #include "adc-target.h" #include "button.h" #include "button-target.h" #include "powermgmt.h" #include "power-nwz.h" #include "nvp-nwz.h" #include "nwz_sysinfo.h" #include #include #include #include #include /* NOTE: some targets with touchscreen don't have the usual keypad, on those * we use a mixture of rewind/forward/volume+/- to emulate it */ #define ACT_NONE 0 #define ACT_CANCEL 1 #define ACT_OK 2 #define ACT_PREV 3 #define ACT_NEXT 4 #define ACT_REPEAT 0x1000 int xlate_button(int btn) { switch(btn) { #ifdef BUTTON_POWER case BUTTON_POWER: #endif case BUTTON_BACK: return ACT_CANCEL; case BUTTON_PLAY: return ACT_OK; case BUTTON_UP: case BUTTON_LEFT: case BUTTON_VOL_UP: return ACT_PREV; case BUTTON_DOWN: case BUTTON_RIGHT: case BUTTON_VOL_DOWN: return ACT_NEXT; default: return ACT_NONE; } } int my_get_status(void) { return xlate_button(button_status()); } int my_get_action(int tmo) { int btn = button_get_w_tmo(tmo); while(btn & BUTTON_REL) btn = button_get_w_tmo(tmo); bool repeat = btn & BUTTON_REPEAT; int act = xlate_button(btn & ~BUTTON_REPEAT); if(repeat) act |= ACT_REPEAT; return act; } bool dbg_hw_info_adc(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = my_get_action(HZ / 25); switch(button) { case ACT_NEXT: case ACT_PREV: case ACT_OK: lcd_setfont(FONT_UI); return true; case ACT_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); /* add battery readout in mV, this it is not the direct output of a channel */ lcd_putsf(0, 0, "Battery(mV) %d", _battery_voltage()); for(unsigned i = NWZ_ADC_MIN_CHAN; i <= NWZ_ADC_MAX_CHAN; i++) lcd_putsf(0, i + 1, "%7s %3d", adc_name(i), adc_read(i)); lcd_update(); yield(); } } static const char *charge_status_name(int chgstat) { switch(chgstat) { case NWZ_POWER_STATUS_CHARGE_STATUS_CHARGING: return "charging"; case NWZ_POWER_STATUS_CHARGE_STATUS_SUSPEND: return "suspend"; case NWZ_POWER_STATUS_CHARGE_STATUS_TIMEOUT: return "timeout"; case NWZ_POWER_STATUS_CHARGE_STATUS_NORMAL: return "normal"; default: return "unknown"; } } static const char *get_batt_gauge_name(int gauge) { switch(gauge) { case NWZ_POWER_BAT_NOBAT: return "no batt"; case NWZ_POWER_BAT_VERYLOW: return "very low"; case NWZ_POWER_BAT_LOW: return "low"; case NWZ_POWER_BAT_GAUGE0: return "____"; case NWZ_POWER_BAT_GAUGE1: return "O___"; case NWZ_POWER_BAT_GAUGE2: return "OO__"; case NWZ_POWER_BAT_GAUGE3: return "OOO_"; case NWZ_POWER_BAT_GAUGE4: return "OOOO"; default: return "unknown"; } } static const char *acc_charge_mode_name(int mode) { switch(mode) { case NWZ_POWER_ACC_CHARGE_NONE: return "none"; case NWZ_POWER_ACC_CHARGE_VBAT: return "vbat"; case NWZ_POWER_ACC_CHARGE_VSYS: return "vsys"; default: return "unknown"; } } bool dbg_hw_info_power(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = my_get_action(HZ / 25); switch(button) { case ACT_NEXT: case ACT_PREV: case ACT_OK: lcd_setfont(FONT_UI); return true; case ACT_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; int status = nwz_power_get_status(); int chgstat = status & NWZ_POWER_STATUS_CHARGE_STATUS; int acc_chg_mode = nwz_power_get_acc_charge_mode(); lcd_putsf(0, line++, "ac detected: %s", (status & NWZ_POWER_STATUS_AC_DET) ? "yes" : "no"); lcd_putsf(0, line++, "vbus detected: %s ", (status & NWZ_POWER_STATUS_VBUS_DET) ? "yes" : "no"); lcd_putsf(0, line++, "vbus voltage: %d mV (AD=%d)", nwz_power_get_vbus_voltage(), nwz_power_get_vbus_adval()); lcd_putsf(0, line++, "vbus limit: %d mA ", nwz_power_get_vbus_limit()); lcd_putsf(0, line++, "vsys voltage: %d mV (AD=%d)", nwz_power_get_vsys_voltage(), nwz_power_get_vsys_adval()); lcd_putsf(0, line++, "charge switch: %s ", nwz_power_get_charge_switch() ? "on" : "off"); lcd_putsf(0, line++, "full voltage: %s V ", (status & NWZ_POWER_STATUS_CHARGE_LOW) ? "4.1" : "4.2"); lcd_putsf(0, line++, "current limit: %d mA", nwz_power_get_charge_current()); lcd_putsf(0, line++, "charge status: %s (%x)", charge_status_name(chgstat), chgstat); lcd_putsf(0, line++, "battery full: %s ", nwz_power_is_fully_charged() ? "yes" : "no"); lcd_putsf(0, line++, "bat gauge: %s (%d)", get_batt_gauge_name(nwz_power_get_battery_gauge()), nwz_power_get_battery_gauge()); lcd_putsf(0, line++, "avg voltage: %d mV (AD=%d)", nwz_power_get_battery_voltage(), nwz_power_get_battery_adval()); lcd_putsf(0, line++, "sample count: %d ", nwz_power_get_sample_count()); lcd_putsf(0, line++, "raw voltage: %d mV (AD=%d)", nwz_power_get_vbat_voltage(), nwz_power_get_vbat_adval()); lcd_putsf(0, line++, "acc charge mode: %s (%d)", acc_charge_mode_name(acc_chg_mode), acc_chg_mode); lcd_update(); yield(); } } bool dbg_hw_info_button(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int btn = my_get_action(0); switch(btn) { case ACT_OK: lcd_setfont(FONT_UI); return true; case ACT_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; #ifdef HAVE_BUTTON_DATA int data; btn = button_read_device(&data); #else btn = button_read_device(); #endif lcd_putsf(0, line++, "raw buttons: %x", btn); #ifdef HAS_BUTTON_HOLD lcd_putsf(0, line++, "hold: %d", button_hold()); #endif #ifdef HAVE_HEADPHONE_DETECTION lcd_putsf(0, line++, "headphones: %d", headphones_inserted()); #endif #ifdef HAVE_BUTTON_DATA #ifdef HAVE_TOUCHSCREEN lcd_putsf(0, line++, "touch: x=%d y=%d", data >> 16, data & 0xffff); #else lcd_putsf(0, line++, "data: %d", data); #endif #endif lcd_update(); yield(); } } int read_sysinfo(int ioctl_nr, unsigned int *val) { int fd = open(NWZ_SYSINFO_DEV, O_RDONLY); if(fd < 0) return -1; int ret = ioctl(fd, ioctl_nr, val); close(fd); return ret; } bool dbg_hw_info_sysinfo(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int btn = my_get_action(0); switch(btn) { case ACT_OK: lcd_setfont(FONT_UI); return true; case ACT_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; unsigned int val; lcd_putsf(0, line++, "mid: %x (%s)", nwz_get_model_id(), nwz_get_model_name()); #define print_info(def, name) \ if(read_sysinfo(NWZ_SYSINFO_GET_##def##_TYPE, &val) < 0) \ lcd_putsf(0, line++, "%s: -", name); \ else \ lcd_putsf(0, line++, "%s: %d", name, val); /* WARNING the interpretation of values is difficult because it changes * very often */ /* DAC type: ... */ print_info(DAC, "dac") /* Noise cancelling: 0=no, 1=yes */ print_info(NCR, "nc") /* Speaker: 0=no, 1=yes */ print_info(SPK, "spk") /* Microphone: 0=no, 1=yes */ print_info(MIC, "mic") /* Video encoder: 0=no, ... */ print_info(NPE, "videoenc") /* FM receiver: 0=no, 1=si470x */ print_info(FMT, "fm") /* Touch screen: 0=none, ... */ print_info(TSP, "touch") /* Bluetooth: 0=no, 1=yes */ print_info(WAB, "bt") /* SD card: 0=no, ... */ print_info(SDC, "sd") lcd_update(); yield(); } } #include "pcm-alsa.h" bool dbg_hw_info_audio(void) { lcd_setfont(FONT_SYSFIXED); int vol = 0; while(1) { int btn = my_get_action(0); switch(btn) { case ACT_PREV: vol--; pcm_alsa_set_digital_volume(vol); break; case ACT_NEXT: vol++; pcm_alsa_set_digital_volume(vol); break; case ACT_OK: lcd_setfont(FONT_UI); return true; case ACT_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; lcd_putsf(0, line++, "vol: %d dB", vol); lcd_update(); yield(); } } static struct { const char *name; bool (*fn)(void); } debug_screens[] = { {"sysinfo", dbg_hw_info_sysinfo}, {"adc", dbg_hw_info_adc}, {"power", dbg_hw_info_power}, {"button", dbg_hw_info_button}, {"audio", dbg_hw_info_audio}, }; bool dbg_hw_info(void) { int nr_lines = lcd_getheight() / font_get(lcd_getfont())->height; int len = ARRAYLEN(debug_screens); int top_visible = 0; int highlight = 0; while(1) { int button = my_get_action(HZ / 10); switch(button) { case ACT_NEXT: highlight = (highlight + 1) % len; break; case ACT_PREV: highlight = (highlight + len - 1) % len; break; case ACT_OK: // if the screen returns true, advance to next screen while(debug_screens[highlight].fn()) highlight = (highlight + 1) % len; lcd_setfont(FONT_UI); break; case ACT_CANCEL: return false; } // adjust top visible if needed if(highlight < top_visible) top_visible = highlight; else if(highlight >= top_visible + nr_lines) top_visible = highlight - nr_lines + 1; lcd_clear_display(); for(int i = top_visible; i < len && i < top_visible + nr_lines; i++) { if(i == highlight) { lcd_set_foreground(LCD_BLACK); lcd_set_background(LCD_RGBPACK(255, 255, 0)); } else { lcd_set_foreground(LCD_WHITE); lcd_set_background(LCD_BLACK); } lcd_putsf(0, i - top_visible, "%s", debug_screens[i].name); } lcd_set_foreground(LCD_WHITE); lcd_set_background(LCD_BLACK); lcd_update(); yield(); } return false; }