d7cb90722f
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14881 a1c6a512-1295-4272-9138-f99709370657
497 lines
14 KiB
C
497 lines
14 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
*
|
|
* Copyright (C) 2006 Alexander Spyridakis, Hristo Kovachev
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#ifndef SIMULATOR /* not for the simulator */
|
|
|
|
#include "plugin.h"
|
|
PLUGIN_HEADER
|
|
|
|
#define BATTERY_LOG "/battery_bench.txt"
|
|
#define BUF_SIZE 16000
|
|
#define DISK_SPINDOWN_TIMEOUT 3600
|
|
|
|
#define EV_EXIT 1337
|
|
|
|
/* seems to work with 1300, but who knows... */
|
|
#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
|
|
|
|
#if CONFIG_KEYPAD == RECORDER_PAD
|
|
|
|
#define BATTERY_ON BUTTON_PLAY
|
|
#define BATTERY_OFF BUTTON_OFF
|
|
|
|
#if BUTTON_REMOTE != 0
|
|
#define BATTERY_RC_ON BUTTON_RC_PLAY
|
|
#define BATTERY_RC_OFF BUTTON_RC_STOP
|
|
#endif
|
|
|
|
#elif CONFIG_KEYPAD == ONDIO_PAD
|
|
|
|
#define BATTERY_ON BUTTON_RIGHT
|
|
#define BATTERY_OFF BUTTON_OFF
|
|
|
|
#elif CONFIG_KEYPAD == PLAYER_PAD
|
|
|
|
#define BATTERY_ON BUTTON_PLAY
|
|
#define BATTERY_RC_ON BUTTON_RC_PLAY
|
|
|
|
#define BATTERY_OFF BUTTON_STOP
|
|
#define BATTERY_RC_OFF BUTTON_RC_STOP
|
|
|
|
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
|
|
(CONFIG_KEYPAD == IRIVER_H300_PAD)
|
|
|
|
#define BATTERY_ON BUTTON_ON
|
|
#define BATTERY_RC_ON BUTTON_RC_ON
|
|
|
|
#define BATTERY_OFF BUTTON_OFF
|
|
#define BATTERY_RC_OFF BUTTON_RC_STOP
|
|
|
|
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
|
|
|
|
#define BATTERY_ON BUTTON_PLAY
|
|
#define BATTERY_OFF BUTTON_MENU
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
|
|
|
|
#define BATTERY_ON BUTTON_SELECT
|
|
#define BATTERY_OFF BUTTON_PLAY
|
|
|
|
#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
|
|
|
|
#define BATTERY_ON BUTTON_SELECT
|
|
#define BATTERY_OFF BUTTON_PLAY
|
|
|
|
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
|
|
(CONFIG_KEYPAD == SANSA_C200_PAD)
|
|
#define BATTERY_ON BUTTON_SELECT
|
|
#define BATTERY_OFF BUTTON_POWER
|
|
|
|
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
|
|
|
|
#define BATTERY_ON BUTTON_PLAY
|
|
#define BATTERY_OFF BUTTON_POWER
|
|
|
|
#elif CONFIG_KEYPAD == GIGABEAT_PAD
|
|
|
|
#define BATTERY_ON BUTTON_SELECT
|
|
#define BATTERY_OFF BUTTON_POWER
|
|
|
|
#endif
|
|
|
|
|
|
/****************************** Plugin Entry Point ****************************/
|
|
static struct plugin_api* rb;
|
|
MEM_FUNCTION_WRAPPERS(rb);
|
|
int main(void);
|
|
bool exit_tsr(bool);
|
|
void thread(void);
|
|
|
|
|
|
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
|
{
|
|
(void)parameter;
|
|
rb = api;
|
|
|
|
return main();
|
|
}
|
|
|
|
bool thread_stopped = false;
|
|
|
|
/* Struct for battery information */
|
|
struct batt_info
|
|
{
|
|
/* the size of the struct elements is optimised to make the struct size
|
|
* a power of 2 */
|
|
long ticks;
|
|
int eta;
|
|
unsigned int voltage;
|
|
short level;
|
|
unsigned short flags;
|
|
} bat[BUF_SIZE/sizeof(struct batt_info)];
|
|
|
|
struct event_queue thread_q;
|
|
|
|
bool exit_tsr(bool reenter)
|
|
{
|
|
bool exit = true;
|
|
(void)reenter;
|
|
rb->lcd_clear_display();
|
|
rb->lcd_puts_scroll(0, 0, "Batt.Bench is currently running.");
|
|
rb->lcd_puts_scroll(0, 1, "Press OFF to cancel the test");
|
|
#ifdef HAVE_LCD_BITMAP
|
|
rb->lcd_puts_scroll(0, 2, "Anything else will resume");
|
|
#endif
|
|
rb->lcd_update();
|
|
|
|
if (rb->button_get(true) != BATTERY_OFF)
|
|
exit = false;
|
|
if (exit)
|
|
{
|
|
rb->queue_post(&thread_q, EV_EXIT, 0);
|
|
while (!thread_stopped)
|
|
rb->yield();
|
|
/* remove the thread's queue from the broadcast list */
|
|
rb->queue_delete(&thread_q);
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
#define BIT_CHARGER 0x1
|
|
#define BIT_CHARGING 0x2
|
|
#define BIT_USB_POWER 0x4
|
|
|
|
#define HMS(x) (x)/3600,((x)%3600)/60,((x)%3600)%60
|
|
|
|
/* use long for aligning */
|
|
unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)];
|
|
|
|
#if CONFIG_CHARGING || defined(HAVE_USB_POWER)
|
|
unsigned int charge_state(void)
|
|
{
|
|
unsigned int ret = 0;
|
|
#if CONFIG_CHARGING
|
|
if(rb->charger_inserted())
|
|
ret = BIT_CHARGER;
|
|
#if CONFIG_CHARGING == CHARGING_MONITOR
|
|
if(rb->charging_state())
|
|
ret |= BIT_CHARGING;
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_USB_POWER
|
|
if(rb->usb_powered())
|
|
ret |= BIT_USB_POWER;
|
|
#endif
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void thread(void)
|
|
{
|
|
bool got_info = false, timeflag = false, in_usb_mode = false;
|
|
int fd, buffelements, tick = 1, i = 0, skipped = 0, exit = 0;
|
|
int fst = 0, lst = 0; /* first and last skipped tick */
|
|
unsigned int last_voltage = 0;
|
|
#if CONFIG_CHARGING || defined(HAVE_USB_POWER)
|
|
unsigned int last_state = 0;
|
|
#endif
|
|
long sleep_time = 5 * HZ;
|
|
|
|
struct event ev;
|
|
|
|
buffelements = sizeof(bat)/sizeof(struct batt_info);
|
|
|
|
#ifndef HAVE_FLASH_STORAGE
|
|
if(rb->global_settings->disk_spindown > 1)
|
|
sleep_time = (rb->global_settings->disk_spindown - 1) * HZ;
|
|
#endif
|
|
|
|
do
|
|
{
|
|
if(!in_usb_mode && got_info &&
|
|
(exit || timeflag || rb->ata_disk_is_active()) )
|
|
{
|
|
int last, secs, j, temp = skipped;
|
|
|
|
fd = rb->open(BATTERY_LOG, O_RDWR | O_CREAT | O_APPEND);
|
|
if(fd < 0)
|
|
exit = 1;
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if(skipped)
|
|
{
|
|
last = buffelements;
|
|
fst /= HZ;
|
|
lst /= HZ;
|
|
rb->fdprintf(fd,"-Skipped %d measurements from "
|
|
"%02d:%02d:%02d to %02d:%02d:%02d-\n",skipped,
|
|
HMS(fst),HMS(lst));
|
|
skipped = 0;
|
|
}
|
|
else
|
|
{
|
|
last = i;
|
|
i = 0;
|
|
}
|
|
|
|
for(j = i; j < last; j++)
|
|
{
|
|
secs = bat[j].ticks/HZ;
|
|
rb->fdprintf(fd,
|
|
"%02d:%02d:%02d, %05d, %03d%%, "
|
|
"%02d:%02d, %04d, %04d"
|
|
#if CONFIG_CHARGING
|
|
", %c"
|
|
#if CONFIG_CHARGING == CHARGING_MONITOR
|
|
", %c"
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_USB_POWER
|
|
", %c"
|
|
#endif
|
|
"\n",
|
|
|
|
HMS(secs), secs, bat[j].level,
|
|
bat[j].eta / 60, bat[j].eta % 60,
|
|
bat[j].voltage,
|
|
temp + 1 + (j-i)
|
|
#if CONFIG_CHARGING
|
|
,(bat[j].flags & BIT_CHARGER)?'A':'-'
|
|
#if CONFIG_CHARGING == CHARGING_MONITOR
|
|
,(bat[j].flags & BIT_CHARGING)?'C':'-'
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_USB_POWER
|
|
,(bat[j].flags & BIT_USB_POWER)?'U':'-'
|
|
#endif
|
|
|
|
);
|
|
if(!j % 100 && !j) /* yield() at every 100 writes */
|
|
rb->yield();
|
|
}
|
|
temp += j - i;
|
|
|
|
}while(i != 0);
|
|
|
|
rb->close(fd);
|
|
tick = *rb->current_tick;
|
|
got_info = false;
|
|
timeflag = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned int current_voltage;
|
|
if(
|
|
#if CONFIG_CODEC == SWCODEC
|
|
!rb->pcm_is_playing()
|
|
#else
|
|
!rb->mp3_is_playing()
|
|
#endif
|
|
&& (*rb->current_tick - tick) > DISK_SPINDOWN_TIMEOUT * HZ)
|
|
timeflag = true;
|
|
|
|
if(last_voltage != (current_voltage=rb->battery_voltage())
|
|
#if CONFIG_CHARGING || defined(HAVE_USB_POWER)
|
|
|| last_state != charge_state()
|
|
#endif
|
|
)
|
|
{
|
|
if(i == buffelements)
|
|
{
|
|
if(!skipped++)
|
|
fst = bat[0].ticks;
|
|
i = 0;
|
|
}
|
|
else if(skipped)
|
|
{
|
|
skipped++;
|
|
lst = bat[i].ticks;
|
|
}
|
|
bat[i].ticks = *rb->current_tick;
|
|
bat[i].level = rb->battery_level();
|
|
bat[i].eta = rb->battery_time();
|
|
last_voltage = bat[i].voltage = current_voltage;
|
|
#if CONFIG_CHARGING || defined(HAVE_USB_POWER)
|
|
bat[i].flags = last_state = charge_state();
|
|
#endif
|
|
i++;
|
|
got_info = true;
|
|
}
|
|
|
|
}
|
|
|
|
if(exit)
|
|
{
|
|
if(exit == 2)
|
|
rb->splash(HZ,
|
|
#ifdef HAVE_LCD_BITMAP
|
|
"Exiting battery_bench...");
|
|
#else
|
|
"bench exit");
|
|
#endif
|
|
thread_stopped = true;
|
|
return;
|
|
}
|
|
|
|
rb->queue_wait_w_tmo(&thread_q, &ev, sleep_time);
|
|
switch (ev.id)
|
|
{
|
|
case SYS_USB_CONNECTED:
|
|
in_usb_mode = true;
|
|
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
|
break;
|
|
case SYS_USB_DISCONNECTED:
|
|
in_usb_mode = false;
|
|
rb->usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
|
|
break;
|
|
case SYS_POWEROFF:
|
|
exit = 1;
|
|
break;
|
|
case EV_EXIT:
|
|
exit = 2;
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
typedef void (*plcdfunc)(int x, int y, const unsigned char *str);
|
|
|
|
void put_centered_str(const char* str, plcdfunc putsxy, int lcd_width, int line)
|
|
{
|
|
int strwdt, strhgt;
|
|
rb->lcd_getstringsize(str, &strwdt, &strhgt);
|
|
putsxy((lcd_width - strwdt)/2, line*(strhgt), str);
|
|
}
|
|
#endif
|
|
|
|
int main(void)
|
|
{
|
|
int button, fd;
|
|
bool on = false;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
int i;
|
|
const char *msgs[] = { "Battery Benchmark","Check file", BATTERY_LOG,
|
|
"for more info", "PLAY - start", "OFF - quit" };
|
|
#endif
|
|
rb->lcd_clear_display();
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
rb->lcd_clear_display();
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
|
|
for(i = 0; i<(int)(sizeof(msgs)/sizeof(char *)); i++)
|
|
put_centered_str(msgs[i],rb->lcd_putsxy,LCD_WIDTH,i+1);
|
|
#else
|
|
rb->lcd_puts_scroll(0, 0, "Batt.Bench.");
|
|
rb->lcd_puts_scroll(0, 1, "PLAY/STOP");
|
|
#endif
|
|
rb->lcd_update();
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
rb->lcd_remote_clear_display();
|
|
put_centered_str(msgs[0],rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,0);
|
|
put_centered_str(msgs[sizeof(msgs)/sizeof(char*)-2],
|
|
rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,1);
|
|
put_centered_str(msgs[sizeof(msgs)/sizeof(char*)-1],
|
|
rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,2);
|
|
rb->lcd_remote_update();
|
|
#endif
|
|
|
|
do
|
|
{
|
|
button = rb->button_get(true);
|
|
switch(button)
|
|
{
|
|
case BATTERY_ON:
|
|
#ifdef BATTERY_RC_ON
|
|
case BATTERY_RC_ON:
|
|
#endif
|
|
on = true;
|
|
break;
|
|
case BATTERY_OFF:
|
|
#ifdef BATTERY_RC_OFF
|
|
case BATTERY_RC_OFF:
|
|
#endif
|
|
return PLUGIN_OK;
|
|
|
|
default: if(rb->default_event_handler(button) == SYS_USB_CONNECTED)
|
|
return PLUGIN_USB_CONNECTED;
|
|
}
|
|
}while(!on);
|
|
|
|
fd = rb->open(BATTERY_LOG, O_RDONLY);
|
|
if(fd < 0)
|
|
{
|
|
fd = rb->open(BATTERY_LOG, O_RDWR | O_CREAT);
|
|
if(fd >= 0)
|
|
{
|
|
rb->fdprintf(fd,
|
|
"This plugin will log your battery performance in a\n"
|
|
"file (%s) every time the disk is accessed (or every hour).\n"
|
|
"To properly test your battery:\n"
|
|
"1) Select and playback an album. "
|
|
"(Be sure to be more than the player's buffer)\n"
|
|
"2) Set to repeat.\n"
|
|
"3) Let the player run completely out of battery.\n"
|
|
"4) Recharge and copy (or whatever you want) the txt file to "
|
|
"your computer.\n"
|
|
"Now you can make graphs with the data of the battery log.\n"
|
|
"Do not enter another plugin during the test or else the "
|
|
"logging activity will end.\n\n"
|
|
"P.S: You can decide how you will make your tests.\n"
|
|
"Just don't open another plugin to be sure that your log "
|
|
"will continue.\n"
|
|
"M/DA (Measurements per Disk Activity) shows how many times\n"
|
|
"data was logged in the buffer between Disk Activity.\n\n"
|
|
"Battery type: %d mAh Buffer Entries: %d\n"
|
|
" Time:, Seconds:, Level:, Time Left:, Voltage[mV]:,"
|
|
" M/DA:"
|
|
#if CONFIG_CHARGING
|
|
", C:"
|
|
#endif
|
|
#if CONFIG_CHARGING == CHARGING_MONITOR
|
|
", S:"
|
|
#endif
|
|
#ifdef HAVE_USB_POWER
|
|
", U:"
|
|
#endif
|
|
"\n"
|
|
,BATTERY_LOG,rb->global_settings->battery_capacity,
|
|
BUF_SIZE / (unsigned)sizeof(struct batt_info));
|
|
rb->close(fd);
|
|
}
|
|
else
|
|
{
|
|
rb->splash(HZ / 2, "Cannot create file!");
|
|
return PLUGIN_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rb->close(fd);
|
|
fd = rb->open(BATTERY_LOG, O_RDWR | O_APPEND);
|
|
rb->fdprintf(fd, "\n--File already present. Resuming Benchmark--\n");
|
|
rb->close(fd);
|
|
}
|
|
|
|
rb->queue_init(&thread_q, true); /* put the thread's queue in the bcast list */
|
|
if(rb->create_thread(thread, thread_stack,
|
|
sizeof(thread_stack), "Battery Benchmark"
|
|
IF_PRIO(, PRIORITY_BACKGROUND)
|
|
IF_COP(, CPU, false)) == NULL)
|
|
{
|
|
rb->splash(HZ, "Cannot create thread!");
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
rb->plugin_tsr(exit_tsr);
|
|
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
#endif
|