diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 2912129..2ed70dc 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -116,6 +116,11 @@ #include "usb_core.h" #endif +#ifdef ROCKBOX_HAS_LOGF +#include "logf.h" +#include "asyncfile.h" +#endif + /*---------------------------------------------------*/ /* SPECIAL DEBUG STUFF */ /*---------------------------------------------------*/ @@ -2583,6 +2588,32 @@ static bool dbg_scrollwheel(void) } #endif +#ifdef ROCKBOX_HAS_LOGF +static bool logf_file_output(void) +{ + bool old = global_settings.logf_file_output; + bool result; + + result = set_bool( "logf file auto output", &global_settings.logf_file_output); + + if(old != global_settings.logf_file_output) + { + if(!global_settings.logf_file_output) + { + logf("logf file auto output disabled."); + logf_file_close(); + } + logf_file_output_enabled(global_settings.logf_file_output); + if(global_settings.logf_file_output) + { + logf_file_open(LOGF_FILENAME); + logf("logf file auto output enabled."); + } + } + return result; +} +#endif + #if defined(HAVE_USBSTACK) && defined(ROCKBOX_HAS_LOGF) && defined(USB_SERIAL) static bool logf_usb_serial(void) { @@ -2727,6 +2758,7 @@ static const struct the_menu_item menuitems[] = { #ifdef ROCKBOX_HAS_LOGF {"logf", logfdisplay }, {"logfdump", logfdump }, + {"logf auto file output", logf_file_output}, #endif #if defined(HAVE_USBSTACK) && defined(ROCKBOX_HAS_LOGF) && defined(USB_SERIAL) {"logf over usb",logf_usb_serial }, diff --git a/apps/lang/english.lang b/apps/lang/english.lang old mode 100644 new mode 100755 index b2f0822..a1f0dc0 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -12453,4 +12453,18 @@ *: none touchscreen: "Absolute Point" - \ No newline at end of file + + + id: LANG_LOGF_FILE_OUTPUT + desc: logf() is output to the file. + user: core + + *: "logf auto file output" + + + *: "logf auto file output" + + + *: "logf auto file output" + + diff --git a/apps/main.c b/apps/main.c index 54ff191..b9ca421 100644 --- a/apps/main.c +++ b/apps/main.c @@ -116,6 +116,11 @@ #include "system-sdl.h" #endif +#ifdef ROCKBOX_HAS_LOGF +#include "asyncfile.h" +#include "logf.h" +#endif + /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ const char appsversion[]=APPSVERSION; @@ -384,6 +389,10 @@ static void init(void) show_logo(); lang_init(); +#ifdef ROCKBOX_HAS_LOGF + asyncfile_init(); + logf_file_open(LOGF_FILENAME); +#endif #ifdef DEBUG debug_init(); #else @@ -519,6 +528,14 @@ static void init(void) #endif settings_load(SETTINGS_ALL); +#ifdef ROCKBOX_HAS_LOGF + if(!global_settings.logf_file_output){ + logf_file_output_enabled(false); + clear_logf_buffer(); + logf_file_close(); + } +#endif + if (init_dircache(true) < 0) { #ifdef HAVE_TAGCACHE diff --git a/apps/misc.c b/apps/misc.c index d7a64b3..c9ab767 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -90,6 +90,11 @@ #endif #endif +#ifdef ROCKBOX_HAS_LOGF +#include "asyncfile.h" +#include "logf.h" +#endif + /* Format a large-range value for output, using the appropriate unit so that * the displayed value is in the range 1 <= display < 1000 (1024 for "binary" * units) if possible, and 3 significant digits are shown. If a buffer is @@ -394,6 +399,11 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter) dircache_disable(); #endif +#ifdef ROCKBOX_HAS_LOGF + logf_file_close(); + asyncfile_finalize(); +#endif + shutdown_hw(); } #endif diff --git a/apps/settings.h b/apps/settings.h index dfd3998..bb40cdf 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -80,6 +80,8 @@ struct opt_items { #define CONFIGFILE ROCKBOX_DIR "/config.cfg" #define FIXEDSETTINGSFILE ROCKBOX_DIR "/fixed.cfg" +#define LOGF_FILENAME ROCKBOX_DIR "/logf.log" + #define MAX_FILENAME 32 @@ -227,6 +229,7 @@ enum { SETTINGS_SAVE_EQPRESET, #endif }; + bool settings_save_config(int options); struct settings_list; @@ -725,6 +728,9 @@ struct user_settings #ifdef HAVE_SPEAKER bool speaker_enabled; #endif +#ifdef ROCKBOX_HAS_LOGF + bool logf_file_output; +#endif #ifdef HAVE_TOUCHSCREEN int touch_mode; diff --git a/apps/settings_list.c b/apps/settings_list.c index 6255580..a4d4af0 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -1467,6 +1467,10 @@ const struct settings_list settings[] = { "touchscreen mode", "point,grid", NULL, 2, ID2P(LANG_TOUCHSCREEN_POINT), ID2P(LANG_TOUCHSCREEN_GRID)), #endif +#ifdef ROCKBOX_HAS_LOGF + OFFON_SETTING(0, logf_file_output, LANG_LOGF_FILE_OUTPUT, + false, "logf file auto output", NULL), +#endif }; const int nb_settings = sizeof(settings)/sizeof(*settings); diff --git a/firmware/SOURCES b/firmware/SOURCES index 82b7b34..d03ae24 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -7,6 +7,7 @@ powermgmt.c system.c usb.c #ifdef ROCKBOX_HAS_LOGF +asyncfile.c logf.c #endif /* ROCKBOX_HAS_LOGF */ kernel.c diff --git a/firmware/export/logf.h b/firmware/export/logf.h index 4926fe5..9dcca27 100644 --- a/firmware/export/logf.h +++ b/firmware/export/logf.h @@ -39,6 +39,13 @@ extern unsigned char logfbuffer[MAX_LOGF_LINES][MAX_LOGF_ENTRY+1]; extern int logfindex; extern bool logfwrap; + +int logf_file_open(const char* fname); +void logf_file_close(void); +void logf_flush(int wait); +void clear_logf_buffer(void); +void logf_file_output_enabled(bool flg); + #endif /* __PCTOOL__ */ #define logf _logf diff --git a/firmware/logf.c b/firmware/logf.c index 75aaf31..ac0aeca 100644 --- a/firmware/logf.c +++ b/firmware/logf.c @@ -44,8 +44,10 @@ #include #include "config.h" #include "lcd-remote.h" +#define LOGF_ENABLE #include "logf.h" #include "serial.h" +#include "asyncfile.h" #ifdef HAVE_USBSTACK #include "usb_core.h" @@ -55,6 +57,9 @@ /* Only provide all this if asked to */ #ifdef ROCKBOX_HAS_LOGF +static int logf_id = -1; +static bool enabled_logf_file_output = false; + #ifndef __PCTOOL__ unsigned char logfbuffer[MAX_LOGF_LINES][MAX_LOGF_ENTRY+1]; int logfindex; @@ -139,6 +144,8 @@ void _logf(const char *format, ...) usb_serial_send(buf, len); usb_serial_send("\r\n", 2); #endif + if(enabled_logf_file_output) + async_puts(logf_id, buf); tlen = 0; check_logfindex(); @@ -165,6 +172,59 @@ void _logf(const char *format, ...) displayremote(); } + +int logf_file_open(const char *fname) +{ + if(logf_id >= 0) + { + return -1; + } + + enabled_logf_file_output = false; + + logf_id = async_open(fname, O_WRONLY | O_CREAT | O_APPEND); + + if(logf_id >= 0) + { + enabled_logf_file_output = true; + logf("logf file %s open.", fname); + } + return 0; +} + +void logf_file_close(void) +{ + if(logf_id >= 0) + { + logf("logf file close."); + enabled_logf_file_output = false; + async_close(logf_id); + logf_id = -1; + } +} + +void logf_flush(int wait) +{ + if(logf_id >= 0) + { + async_flush(logf_id, wait); + } +} + +void logf_finalize(void) +{ + logf_file_close(); +} + +void clear_logf_buffer(void) +{ + clear_async_buffer(logf_id); +} + +void logf_file_output_enabled(bool flg) +{ + enabled_logf_file_output = flg; +} #endif #endif diff --git a/firmware/asyncfile.c b/firmware/asyncfile.c new file mode 100644 index 0000000..cabc749 --- /dev/null +++ b/firmware/asyncfile.c @@ -0,0 +1,1011 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Yoshihisa Uchida + * + * 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 +#include +#include "asyncfile.h" +#include "config.h" +#include "errno.h" +#include "kernel.h" +#include "thread.h" +#include "usb.h" + +#define MAX_ASYNC_FILES 4 +#define MAX_ASYNC_PROCESS 64 +#define MAX_BANKS 32 +#define GC_NECESSARY_BANKS 4 +#define MAX_BUFFER_SIZE 1024 + +static struct event_queue async_queue; + +static long async_stack[0x2000/sizeof(long)]; + +static const char async_thread_name[] = "asyncfile"; +static unsigned int async_thread_id; +static bool initialized = false; + +#define FD_UNUSED -2 +#define FD_UNOPEN -1 + +#define BUFFER_UNUSED -1 /* unused buffer */ +#define BUFFER_WRITING 0 /* data adding to the buffer */ +#define BUFFER_WRITE_END 1 /* data add complete */ +#define BUFFER_FILE_WRITING 2 /* writing to buffer */ +#define BUFFER_FILE_WRITE_SUCCESS 3 /* file output success */ +#define BUFFER_FILE_WRITE_FAILURE 4 /* file output failure */ + +/* Queue commands. */ +enum { + QUEUE_NONE = 0, + QUEUE_WRITE, + QUEUE_FLUSH, + QUEUE_WAIT, + QUEUE_STOP, + QUEUE_EXIT, +}; + +/* thread status */ +enum { + ASYNC_STOP = 0, + ASYNC_RUNNING, + ASYNC_WAIT, + ASYNC_FLUSH_END, +}; + +struct async_process +{ + int entry_id; + int process_id; + int bank; + int status; + int result; +}; + +struct async_entry +{ + int fd; + char path[MAX_PATH]; + int flags; + int thread_status; + int process_id; + int cbank; + bool buffer_add_enabled; + bool lock_bank; +}; + +struct write_buf +{ + int entry_id; + int wflg; + int next; + size_t size; + char wbuf[MAX_BUFFER_SIZE]; +}; + +static struct async_process processes[MAX_ASYNC_PROCESS]; +static struct async_entry entries[MAX_ASYNC_FILES]; +static struct write_buf buffer[MAX_BANKS]; +static int run_gc = -1; + +static void remove_async_queue_event(int entry_id) +{ + corelock_lock(&(async_queue.cl)); + unsigned int r = async_queue.read; + struct async_process *p; + + while(r != async_queue.write) + { + unsigned int rd = r & QUEUE_LENGTH_MASK; + + switch(async_queue.events[rd].id) + { + case QUEUE_WRITE: + p = (struct async_process *)async_queue.events[rd].data; + if (p != 0 && p->entry_id == entry_id) + async_queue.events[rd].id = QUEUE_NONE; + break; + } + r++; + } + corelock_unlock(&(async_queue.cl)); +} + +static void init_buffer(void) +{ + int i; + + for (i = 0;i < MAX_BANKS; i++) + { + buffer[i].entry_id = -1; + buffer[i].wflg = BUFFER_UNUSED; + buffer[i].next = i; + buffer[i].size = 0; + } +} + +static void gc_buffer(void) +{ + int id; + + for (id = 0; id < MAX_ASYNC_FILES; id++) + { + if (entries[id].fd >= 0 || entries[id].fd == FD_UNOPEN) + { + if (entries[id].lock_bank) + continue; + + run_gc = id; + + int bank1 = entries[id].cbank; + + do { + while (buffer[bank1].next != bank1) + { + int bank2 = buffer[bank1].next; + if (buffer[bank2].wflg != BUFFER_FILE_WRITE_SUCCESS && + buffer[bank2].wflg != BUFFER_FILE_WRITE_FAILURE) + break; + + buffer[bank1].next = buffer[bank2].next; + buffer[bank2].entry_id = -1; + } + bank1 = buffer[bank1].next; + } while (bank1 != entries[id].cbank); + } + } + + run_gc = -1; +} + +static int get_unused_buffer_count(void) +{ + int i; + int count = 0; + + for (i = 0; i < MAX_BANKS; i++) + { + if (buffer[i].entry_id < 0) + count++; + } + return count; +} + +static int get_unused_buffer(int entry_id) +{ + int i; + + for (i = 0; i < MAX_BANKS; i++) + { + if (buffer[i].entry_id < 0) + { + buffer[i].entry_id = entry_id; + buffer[i].wflg = BUFFER_UNUSED; + buffer[i].next = i; + buffer[i].size = 0; + return i; + } + } + + return -1; +} + +static int get_next_not_writed_buffer(int sbank) +{ + int fbank = buffer[sbank].next; + int bank = fbank; + + do { + if (buffer[bank].wflg == BUFFER_WRITING || buffer[bank].wflg == BUFFER_WRITE_END) + return bank; + + bank = buffer[bank].next; + } while (bank != fbank); + + return -1; +} + +static int append_new_buffer(int entry_id, int cbank) +{ + int bank = get_unused_buffer(entry_id); + + if(bank < 0) + { + gc_buffer(); + + bank = get_unused_buffer(entry_id); + + if (bank < 0) + return -1; + } + + buffer[bank].next = buffer[cbank].next; + buffer[cbank].next = bank; + + return bank; +} + +static void release_buffer(int entry_id) +{ + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return; + + entries[entry_id].lock_bank = true; + while (run_gc >= 0 && run_gc <= entry_id) + sleep(1); + + int bank = entries[entry_id].cbank; + + while ((bank = buffer[bank].next) != entries[entry_id].cbank) + buffer[bank].entry_id = -1; + + buffer[entries[entry_id].cbank].next = entries[entry_id].cbank; + + entries[entry_id].lock_bank = false; +} + +static void init_process(void) +{ + int i; + + for (i = 0; i < MAX_ASYNC_PROCESS; i++) + { + processes[i].entry_id = -1; + processes[i].process_id = -1; + processes[i].bank = -1; + processes[i].status = PROCESS_NOT_REGISTED; + processes[i].result = 0; + } +} + +static struct async_process *get_new_process(int entry_id) +{ + static unsigned char cur = 0xff; + static unsigned int topid = 0; + + cur++; + if(cur >= MAX_ASYNC_PROCESS) + { + cur = 0; + topid++; + if(topid > 127) + topid = 0; + } + + processes[cur].entry_id = entry_id; + processes[cur].process_id = (topid << 8 | cur); + processes[cur].bank = -1; + processes[cur].status = PROCESS_NOT_RUN; + processes[cur].result = 0; + + return &processes[cur]; +} + +static struct async_process *get_process(int entry_id, int process_id) +{ + unsigned char lowid = process_id & 0xff; + + if(process_id < 0) + return 0; + + if(lowid < MAX_ASYNC_PROCESS + && processes[lowid].entry_id == entry_id + && processes[lowid].process_id == process_id + && processes[lowid].status != PROCESS_NOT_REGISTED) + { + return &processes[lowid]; + } + return 0; +} + +static int write_buffer(int entry_id, const void *buf, size_t size) +{ + struct async_process *p; + int bank; + size_t sz = size; + size_t wptr = 0; + + bank = entries[entry_id].cbank; + + while (buffer[bank].size + sz >= MAX_BUFFER_SIZE) + { + if (buffer[bank].size < MAX_BUFFER_SIZE) + { + size_t tmpsz = MAX_BUFFER_SIZE - buffer[bank].size; + memcpy(buffer[bank].wbuf + buffer[bank].size, buf + wptr, tmpsz); + buffer[bank].size = MAX_BUFFER_SIZE; + wptr += tmpsz; + sz -= tmpsz; + } + buffer[bank].wflg = BUFFER_WRITE_END; + + if (entries[entry_id].process_id >= 0) + p = get_process(entry_id, entries[entry_id].process_id); + else + { + p = get_new_process(entry_id); + entries[entry_id].process_id = p->process_id; + } + p->bank = bank; + queue_post(&async_queue, QUEUE_WRITE, (intptr_t) p); + + entries[entry_id].process_id = -1; + + entries[entry_id].lock_bank = true; + while (run_gc >= 0 && run_gc <= entry_id) + sleep(1); + + if (buffer[buffer[bank].next].wflg == BUFFER_UNUSED || + buffer[buffer[bank].next].wflg == BUFFER_FILE_WRITE_SUCCESS || + buffer[buffer[bank].next].wflg == BUFFER_FILE_WRITE_FAILURE) + entries[entry_id].cbank = buffer[bank].next; + else + { + int idx = append_new_buffer(entry_id, bank); + if (idx < 0) + return -1; + entries[entry_id].cbank = idx; + } + + bank = entries[entry_id].cbank; + buffer[bank].size = 0; + entries[entry_id].lock_bank = false; + } + + if (sz > 0) + { + memcpy(buffer[bank].wbuf + buffer[bank].size, buf + wptr, sz); + buffer[bank].size += sz; + buffer[bank].wflg = BUFFER_WRITING; + } + + if (entries[entry_id].process_id < 0) + { + p = get_new_process(entry_id); + entries[entry_id].process_id = p->process_id; + } + + return entries[entry_id].process_id; +} + +static void set_fd(int entry_id) +{ + if (entries[entry_id].fd < 0) + { + entries[entry_id].fd = open(entries[entry_id].path, entries[entry_id].flags); + if (entries[entry_id].fd < 0) + entries[entry_id].fd = FD_UNOPEN; + } +} + +static int write_file(struct async_process *p) +{ + if (!p) + return -1; + + if (p->process_id < 0) + return -2; + + if (p->bank < 0) + return -3; + + if (p->status == PROCESS_NOT_REGISTED) + return -4; + + p->status = PROCESS_RUNNING; + + if (buffer[p->bank].size == 0) + { + buffer[p->bank].wflg = BUFFER_FILE_WRITE_SUCCESS; + + p->result = 0; + p->status = PROCESS_DONE; + return 0; + } + + if (entries[p->entry_id].fd < 0) + { + int count = 10; + while (count-- >= 0) + { + set_fd(p->entry_id); + if (entries[p->entry_id].fd >= 0) + break; + sleep(HZ/10); + } + if (entries[p->entry_id].fd < 0) + { + p->status = PROCESS_ERROR; + p->result = errno; + return -5; + } + } + + buffer[p->bank].wflg = BUFFER_FILE_WRITING; + size_t res = write(entries[p->entry_id].fd, buffer[p->bank].wbuf, buffer[p->bank].size); + if (res == buffer[p->bank].size) + { + fsync(entries[p->entry_id].fd); + + buffer[p->bank].wflg = BUFFER_FILE_WRITE_SUCCESS; + buffer[p->bank].size = 0; + p->result = 0; + p->status = PROCESS_DONE; + return 0; + } + + buffer[p->bank].wflg = BUFFER_FILE_WRITE_FAILURE; + p->result = errno; + p->status = PROCESS_ERROR; + return -6; +} + +static int flush_file(struct async_process *p) +{ + if (!p) + return -1; + + entries[p->entry_id].thread_status = ASYNC_RUNNING; + while (entries[p->entry_id].lock_bank) + sleep(2); + + p->bank = entries[p->entry_id].cbank; + + while ((p->bank = get_next_not_writed_buffer(p->bank)) >= 0) + write_file(p); + + remove_async_queue_event(p->entry_id); + + release_buffer(p->entry_id); + + buffer[entries[p->entry_id].cbank].size = 0; + buffer[entries[p->entry_id].cbank].wflg = BUFFER_UNUSED; + + entries[p->entry_id].thread_status = ASYNC_FLUSH_END; + + return 0; +} + +static void async_thread(void) +{ + struct async_process *p; + struct queue_event ev; + int entry_id = 0; + + while (1) + { + queue_wait_w_tmo(&async_queue, &ev, HZ/2); + + switch(ev.id) + { + case QUEUE_WRITE: + p = (struct async_process *)ev.data; + entries[p->entry_id].thread_status = ASYNC_RUNNING; + write_file(p); + entries[p->entry_id].thread_status = ASYNC_WAIT; + break; + case QUEUE_FLUSH: + p = (struct async_process *)ev.data; + flush_file(p); + break; + case QUEUE_WAIT: + entry_id = (int)ev.data; + entries[entry_id].thread_status = ASYNC_WAIT; + break; + case QUEUE_STOP: + entry_id = (int)ev.data; + entries[entry_id].thread_status = ASYNC_STOP; + break; + case QUEUE_EXIT: + return; +#ifndef SIMULATOR + case SYS_USB_CONNECTED: + for (entry_id = 0; entry_id < MAX_ASYNC_FILES; entry_id++) + { + if (entries[entry_id].fd >= 0 || entries[entry_id].fd == FD_UNOPEN) + { + entries[entry_id].buffer_add_enabled = false; + p = get_new_process(entry_id); + flush_file(p); + } + } + usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_wait_for_disconnect(&async_queue); + break; +#endif + case SYS_TIMEOUT: + if (get_unused_buffer_count() <= GC_NECESSARY_BANKS) + gc_buffer(); + break; + default: + break; + } + } +} + +static int async_writeputs(int entry_id, const void *buf, size_t count, bool cr) +{ + if (!initialized) + return -1; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -2; + + if (thread_get_current() == async_thread_id) + return -3; + + if (!entries[entry_id].buffer_add_enabled) + return -4; + + int res = write_buffer(entry_id, buf, count); + if (cr) + write_buffer(entry_id, "\n", 1); + + return res; +} + +/* + * initialize + * + * Out + * = 0 success + * < 0 error + * + */ +int asyncfile_init(void) +{ + int i; + + queue_init(&async_queue, true); + async_thread_id = create_thread(async_thread, async_stack, + sizeof(async_stack), 0, async_thread_name + IF_PRIO(, PRIORITY_BACKGROUND) + IF_COP(, CPU)); + + if (!async_thread_id) + return -1; + + for (i = 0; i < MAX_ASYNC_FILES; i++) + { + entries[i].fd = FD_UNUSED; + entries[i].buffer_add_enabled = false; + entries[i].thread_status = ASYNC_STOP; + entries[i].process_id = -1; + entries[i].cbank = -1; + entries[i].lock_bank = false; + } + + init_buffer(); + init_process(); + + initialized = true; + + return 0; +} + +/* + * finalize + * + */ +void asyncfile_finalize(void) +{ + int i; + + if (!initialized) + return; + + for (i = 0; i < MAX_ASYNC_FILES; i++) + async_close(i); + + queue_post(&async_queue, QUEUE_EXIT, 0); + if (async_thread_id) + { + thread_wait(async_thread_id); +#ifdef ALLOW_REMOVE_THREAD + remove_thread(async_thread_id); + async_thread_id = 0; +#endif + } + initialized = false; +} + +/* + * open file + * + * When this function ends, the file is not opened. + * When reading or writing in the file is executed for the first time, the file is opened. + * + * In + * pathname + * open file path + * + * flags + * file open flag + * O_RDONLY + * O_WRONLY + * O_RDWR + * O_CREAT + * O_APPEND + * O_TRUNC + * + * Out + * >= 0 success: return of entry id + * < 0 error + */ +int async_open(const char* pathname, int flags) +{ + int i; + int entry_id = -1; + + for (i = 0; i < MAX_ASYNC_FILES; i++) + { + if (entries[i].fd == FD_UNUSED) + { + entry_id = i; + break; + } + } + + if (entry_id < 0) + return -1; + + strncpy(entries[entry_id].path, pathname, MAX_PATH); + entries[entry_id].flags = flags; + entries[entry_id].fd = FD_UNOPEN; + entries[entry_id].buffer_add_enabled = true; + + int idx = get_unused_buffer(entry_id); + + if (idx < 0) + { + entries[entry_id].fd = FD_UNUSED; + return -2; + } + + entries[entry_id].cbank = idx; + + entries[entry_id].thread_status = ASYNC_WAIT; + + return entry_id; +} + +/* + * close file + * + * When this function ends, the file closed. + * When the data not written in the file remains, after writing in the file is completed, + * the file closed. + * + * + * In + * entry_id + * entry id (return value of async_open()) + * + * Out + * = 0 success + * < 0 error + */ +int async_close(int entry_id) +{ + int res = 0; + + if (!initialized) + return -1; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -2; + + if (entries[entry_id].fd == FD_UNUSED) + return -3; + + entries[entry_id].buffer_add_enabled = false; + res = async_flush(entry_id, TIMEOUT_BLOCK); + queue_post(&async_queue, QUEUE_STOP, (intptr_t)entry_id); + if (entries[entry_id].fd >= 0) + close(entries[entry_id].fd); + entries[entry_id].fd = FD_UNUSED; + + return res; +} + +/* + * flush buffered file output. + * + * If wait != TIMEOUT_BLOCK, then this function doesn't end until writing the data + * that remains in the buffer is completed. + * Please execute the get_result() function to confirm whether writing in the file ended. + * + * In + * entry_id + * entry id (return value of async_open()) + * + * wait + * wait time + * = TIMEOUT_BLOCK It doesn't end until the output to the file is completed. + * = 0 It ends at once without waiting for the completion of the output of the file. + * > 0 After (wait) second processing is waited, it ends. + * + * Out + * >= 0 success process id + * < 0 error + */ +int async_flush(int entry_id, int wait) +{ + struct async_process *p; + bool flg; + + if (!initialized) + return -1; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -2; + + if (entries[entry_id].fd == FD_UNUSED) + return -3; + + p = get_new_process(entry_id); + if (!p) + return -4; + + flg = entries[entry_id].buffer_add_enabled; + if (flg) + entries[entry_id].buffer_add_enabled = false; + + entries[entry_id].process_id = p->process_id; + + queue_post(&async_queue, QUEUE_FLUSH, (intptr_t)p); + + if (wait == TIMEOUT_BLOCK) + { + while (entries[entry_id].thread_status != ASYNC_FLUSH_END) + sleep(HZ/10); + } + else if (wait > 0) + sleep(wait); + + queue_post(&async_queue, QUEUE_WAIT, (intptr_t)entry_id); + + entries[entry_id].buffer_add_enabled = flg; + + return p->process_id; +} + +/* + * asynchronous write function + * + * When this function ends, data is not necessarily written in the file. + * Writing in the file is executed by another thread. + * + * Please execute the get_result() function to confirm whether writing in the file ended. + * + * + * In + * entry_id + * entry id (return value of async_open()) + * buf + * write data + * count + * write bytes + * + * Out + * >= 0 success proccess id. + * < 0 error + */ +int async_write(int entry_id, const void *buf, size_t count) +{ + return async_writeputs(entry_id, buf, count, false); +} + +/* + * asynchronous puts function + * + * Line feed code (LF) is added at the end of the data written in the file. + * + * When this function ends, data is not necessarily written in the file. + * Writing in the file is executed by another thread. + * + * Please execute the get_result() function to confirm whether writing in the file ended. + * + * + * In + * entry_id + * entry id (return value of async_open function) + * buf + * write data + * + * Out + * >= 0 success proccess id. + * < 0 error + */ +int async_puts(int entry_id, const char *buf) +{ + size_t count = 0; + + if (!buf) + return -1; + + count = strlen(buf); + + return async_writeputs(entry_id, buf, count, true); +} + +/* + * Clear buffer. + * The data preserved in the buffer is not output to the file. + * Please use the async_flush() function when you want to output it to the file. + * + * In + * entry_id + * entry id (return value of async_open function) + * + * Out + * = 0 success + * < 0 error + */ +int clear_async_buffer(int entry_id) +{ + bool flg; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -1; + + if (entries[entry_id].fd == FD_UNUSED) + return 0; + + flg = entries[entry_id].buffer_add_enabled; + if (flg) + entries[entry_id].buffer_add_enabled = false; + + while (entries[entry_id].thread_status != ASYNC_WAIT) + sleep(HZ/10); + + remove_async_queue_event(entry_id); + + release_buffer(entry_id); + buffer[entries[entry_id].cbank].size = 0; + buffer[entries[entry_id].cbank].wflg = BUFFER_UNUSED; + + entries[entry_id].buffer_add_enabled = flg; + + return 0; +} + +/* + * get write action result + * + * + * In + * entry_id + * entry id (return value of async_open function) + * + * process_id + * write process id (return value of async_write/async_puts functions) + * + * p + * The state of process is set + * + * wait + * wait time + * = TIMEOUT_BLOCK It waits until processing ends. + * >= 0 It waits at (wait) second. + * + * Out + * >= 0 success + * PROCESS_NOT_REGISTED + * The process given by the argument doesn't exist. + * PROCESS_NOT_RUN + * The process given by the argument is not executed. + * PROCESS_RUNNING + * The process given by the argument is being executed. + * PROCESS_DONE + * The process that had been given by the argument ended normally. + * PROCESS_ERROR + * The process that had been given by the argument terminated abnormally. + * < 0 error + */ +int get_result(int entry_id, int process_id, struct async_process_status *p, int wait) +{ + struct async_process *res; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -1; + + res = get_process(entry_id, process_id); + + if (!res) + return -2; + + if (wait == TIMEOUT_BLOCK) + { + while(res->status == PROCESS_RUNNING) + sleep(HZ/10); + } + else if (wait > 0) + sleep(wait); + + if (p) + { + p->entry_id = entry_id; + p->process_id = entries[entry_id].process_id; + p->status = res->status; + p->result = res->result; + } + + return res->status; +} + +/* + * get the result of the process executing now. + * + * In + * entry_id + * entry id (return value of async_open function) + * + * p + * The state of process is set + * + * wait + * wait time + * = TIMEOUT_BLOCK It waits until processing ends. + * >= 0 It waits at (wait) second. + * + * Out + * >= 0 success + * PROCESS_NOT_REGISTED + * The process given by the argument doesn't exist. + * PROCESS_NOT_RUN + * The process given by the argument is not executed. + * PROCESS_RUNNING + * The process given by the argument is being executed. + * PROCESS_DONE + * The process that had been given by the argument ended normally. + * PROCESS_ERROR + * The process that had been given by the argument terminated abnormally. + * < 0 error + */ +int get_current_result(int entry_id, struct async_process_status *p, int wait) +{ + struct async_process *res; + + if (entry_id < 0 || entry_id >= MAX_ASYNC_FILES) + return -1; + + res = get_process(entry_id, entries[entry_id].process_id); + + if (!res) + return -2; + + if (wait == TIMEOUT_BLOCK) + { + while(res->status == PROCESS_RUNNING) + sleep(HZ/10); + } + else if (wait > 0) + sleep(wait); + + if (p) + { + p->entry_id = entry_id; + p->process_id = entries[entry_id].process_id; + p->status = res->status; + p->result = res->result; + } + + return res->status; +} diff --git a/firmware/export/asyncfile.h b/firmware/export/asyncfile.h new file mode 100644 index 0000000..b18e19f --- /dev/null +++ b/firmware/export/asyncfile.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Yoshihisa Uchida + * + * 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. + * + ****************************************************************************/ +#ifndef ASYNCFILE_H +#define ASYNCFILE_H + +#include +#include "file.h" + +struct async_process_status +{ + int entry_id; + int process_id; + int status; + int result; +}; + +enum { + PROCESS_NOT_REGISTED = 0, + PROCESS_NOT_RUN, + PROCESS_RUNNING, + PROCESS_DONE, + PROCESS_ERROR, +}; + +extern int asyncfile_init(void); +extern void asyncfile_finalize(void); +extern int async_open(const char* pathname, int flags); +extern int async_close(int entry_id); +extern int async_flush(int entry_id, int wait); +extern int async_write(int entry_id, const void *buf, size_t count); +extern int async_puts(int entry_id, const char *buf); +extern int async_write_buffer_clear(int entry_id, bool flg); +extern int clear_async_buffer(int entry_id); +extern int get_result(int entry_id, int process_id, struct async_process_status *p, int wait); +extern int get_current_result(int entry_id, struct async_process_status *p, int wait); + +#endif /*ASYNCFILE_H */