Index: apps/lang/english.lang
===================================================================
--- apps/lang/english.lang (revision 18210)
+++ apps/lang/english.lang (working copy)
@@ -12004,3 +12004,17 @@
recording: ""
+
+ id: LANG_LOGF_FILE_OUTPUT
+ desc: logf() is output to the file.
+ user:
+
+ *: "logf auto file output"
+
+
+ *: "logf auto file output"
+
+
+ *: "logf auto file output"
+
+
Index: apps/settings.h
===================================================================
--- apps/settings.h (revision 18210)
+++ apps/settings.h (working copy)
@@ -82,6 +82,8 @@
#define CONFIGFILE ROCKBOX_DIR "/config.cfg"
#define FIXEDSETTINGSFILE ROCKBOX_DIR "/fixed.cfg"
+#define LOGF_FILENAME ROCKBOX_DIR "/logf.log"
+
#define MAX_FILENAME 32
@@ -744,6 +746,9 @@
#ifdef HAVE_TOUCHPAD_SENSITIVITY_SETTING
int touchpad_sensitivity;
#endif
+#ifdef ROCKBOX_HAS_LOGF
+ bool logf_file_output;
+#endif
};
/** global variables **/
Index: apps/settings_list.c
===================================================================
--- apps/settings_list.c (revision 18210)
+++ apps/settings_list.c (working copy)
@@ -1349,6 +1349,10 @@
"touchpad sensitivity", "normal,high", touchpad_set_sensitivity, 2,
ID2P(LANG_NORMAL), ID2P(LANG_HIGH)),
#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);
Index: apps/debug_menu.c
===================================================================
--- apps/debug_menu.c (revision 18210)
+++ apps/debug_menu.c (working copy)
@@ -114,6 +114,11 @@
#include "../firmware/usbstack/usb_storage.h"
#endif
+#ifdef ROCKBOX_HAS_LOGF
+#include "logf.h"
+#include "asyncfile.h"
+#endif
+
/*---------------------------------------------------*/
/* SPECIAL DEBUG STUFF */
/*---------------------------------------------------*/
@@ -2391,6 +2396,32 @@
}
#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)
{
@@ -2543,6 +2574,7 @@
#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 },
Index: apps/main.c
===================================================================
--- apps/main.c (revision 18210)
+++ apps/main.c (working copy)
@@ -111,6 +111,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;
@@ -375,6 +380,10 @@
show_logo();
lang_init();
+#ifdef ROCKBOX_HAS_LOGF
+ asyncfile_init();
+ logf_file_open(LOGF_FILENAME);
+#endif
#ifdef DEBUG
debug_init();
#else
@@ -511,6 +520,14 @@
#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
Index: apps/misc.c
===================================================================
--- apps/misc.c (revision 18210)
+++ apps/misc.c (working copy)
@@ -81,6 +81,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
@@ -761,6 +766,11 @@
dircache_disable();
#endif
+#ifdef ROCKBOX_HAS_LOGF
+ logf_file_close();
+ asyncfile_finalize();
+#endif
+
shutdown_hw();
}
#endif
Index: firmware/export/logf.h
===================================================================
--- firmware/export/logf.h (revision 18210)
+++ firmware/export/logf.h (working copy)
@@ -30,11 +30,19 @@
#ifndef __PCTOOL__
#define MAX_LOGF_LINES 1000
#define MAX_LOGF_ENTRY 30
+#define MAX_LOGF_FILE_ENTRY 150
#define MAX_LOGF_DATASIZE (MAX_LOGF_ENTRY*MAX_LOGF_LINES)
extern unsigned char logfbuffer[MAX_LOGF_LINES][MAX_LOGF_ENTRY];
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
Index: firmware/export/asyncfile.h
===================================================================
--- firmware/export/asyncfile.h (revision 0)
+++ firmware/export/asyncfile.h (revision 0)
@@ -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 */
Property changes on: firmware/export/asyncfile.h
___________________________________________________________________
Name: svn:executable
+ *
Index: firmware/logf.c
===================================================================
--- firmware/logf.c (revision 18210)
+++ firmware/logf.c (working copy)
@@ -20,7 +20,7 @@
****************************************************************************/
/*
- * logf() logs MAX_LOGF_ENTRY (21) bytes per entry in a circular buffer. Each
+ * logf() logs MAX_LOGF_ENTRY (30) bytes per entry in a circular buffer. Each
* logged string is space- padded for easier and faster output on screen. Just
* output MAX_LOGF_ENTRY characters on each line. MAX_LOGF_ENTRY bytes fit
* nicely on the iRiver remote LCD (128 pixels with an 8x6 pixels font).
@@ -31,8 +31,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"
@@ -42,6 +44,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];
int logfindex;
@@ -99,8 +104,11 @@
{
int len;
unsigned char *ptr;
+ unsigned char buf[MAX_LOGF_FILE_ENTRY];
va_list ap;
va_start(ap, format);
+ vsnprintf(buf, MAX_LOGF_FILE_ENTRY, format, ap);
+ va_end(ap);
if(logfindex >= MAX_LOGF_LINES) {
/* wrap */
@@ -108,7 +116,8 @@
logfindex = 0;
}
ptr = logfbuffer[logfindex];
- len = vsnprintf(ptr, MAX_LOGF_ENTRY, format, ap);
+ strncpy(ptr, buf, MAX_LOGF_ENTRY);
+ len = strlen(ptr);
#ifdef HAVE_SERIAL
serial_tx(ptr);
serial_tx("\r\n");
@@ -117,8 +126,9 @@
usb_serial_send(ptr,len);
usb_serial_send("\r\n",2);
#endif
+ if(enabled_logf_file_output)
+ async_puts(logf_id, buf);
- va_end(ap);
if(len < MAX_LOGF_ENTRY)
/* pad with spaces up to the MAX_LOGF_ENTRY byte border */
memset(ptr+len, ' ', MAX_LOGF_ENTRY-len);
@@ -127,6 +137,59 @@
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
Index: firmware/SOURCES
===================================================================
--- firmware/SOURCES (revision 18210)
+++ firmware/SOURCES (working copy)
@@ -7,6 +7,7 @@
system.c
usb.c
#ifdef ROCKBOX_HAS_LOGF
+asyncfile.c
logf.c
#endif /* ROCKBOX_HAS_LOGF */
kernel.c
Index: firmware/asyncfile.c
===================================================================
--- firmware/asyncfile.c (revision 0)
+++ firmware/asyncfile.c (revision 0)
@@ -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 struct thread_entry* 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;
+}
Property changes on: firmware/asyncfile.c
___________________________________________________________________
Name: svn:executable
+ *