Index: docs/CREDITS
===================================================================
--- docs/CREDITS	(révision 17547)
+++ docs/CREDITS	(copie de travail)
@@ -390,6 +390,7 @@
 John Kaminar
 Joris Goosen
 Mark Ganson
+Thierry Larmoire
 
 
 The libmad team
Index: tools/rockboxdev.sh
===================================================================
--- tools/rockboxdev.sh	(révision 17547)
+++ tools/rockboxdev.sh	(copie de travail)
@@ -2,17 +2,17 @@
 
 # this is where this script will store downloaded files and check for already
 # downloaded files
-dlwhere="/tmp/rbdev-dl"
+dlwhere="/mnt/data/tmp/rbdev-dl"
 
 # will append the target string to the prefix dir mentioned here
 # Note that the user running this script must be able to do make install in
 # this given prefix directory. Also make sure that this given root dir
 # exists.
-prefix="/usr/local"
+prefix="/home/larmoire/dev/rockbox"
 
 # This directory is used to extract all files and to build everything in. It
 # must not exist before this script is invoked (as a security measure).
-builddir="/tmp/rbdev-build"
+builddir="/mnt/data/tmp/rbdev-build"
 
 # This script needs to use GNU Make. On Linux systems, GNU Make is invoked
 # by running the "make" command, on most BSD systems, GNU Make is invoked
Index: tools/buildzip.pl
===================================================================
--- tools/buildzip.pl	(révision 17547)
+++ tools/buildzip.pl	(copie de travail)
@@ -432,7 +432,7 @@
     }
 
     # remove the .rockbox afterwards
-    `rm -rf .rockbox`;
+    #`rm -rf .rockbox`;
 };
 
 if(!$exe) {
Index: apps/plugins/plugin.lds
===================================================================
--- apps/plugins/plugin.lds	(révision 17547)
+++ apps/plugins/plugin.lds	(copie de travail)
@@ -145,6 +145,9 @@
 	*(.idata)
 #endif
     } > PLUGIN_RAM
+	.rela.dyn :
+	{
+	} > PLUGIN_RAM
 
 #if NOCACHE_BASE != 0
     .ncdata . + NOCACHE_BASE :
Index: apps/plugins/CATEGORIES
===================================================================
--- apps/plugins/CATEGORIES	(révision 17547)
+++ apps/plugins/CATEGORIES	(copie de travail)
@@ -54,6 +54,7 @@
 pong,games
 properties,viewers
 random_folder_advance_config,apps
+reveil,apps
 reversi,games
 robotfindskitten,games
 rockblox,games
Index: apps/plugins/chessbox/archos.lds
===================================================================
--- apps/plugins/chessbox/archos.lds	(révision 17547)
+++ apps/plugins/chessbox/archos.lds	(copie de travail)
@@ -34,6 +34,9 @@
     .data : {
         *(.data*)
     } > OVERLAY_RAM
+	.rela.dyn :
+	{
+	} > OVERLAY_RAM
 
     .bss : {
         *(.bss*)
Index: apps/plugins/list.h
===================================================================
--- apps/plugins/list.h	(révision 0)
+++ apps/plugins/list.h	(révision 0)
@@ -0,0 +1,70 @@
+#ifndef __list_h__
+#define __list_h__
+
+struct Node
+{
+	struct Node*next;
+	struct Node*prev;
+};
+
+struct List
+{
+	struct Node head[0];
+	struct Node *head_next;
+	struct Node tail[0];
+	struct Node *head_prev_or_tail_next;
+	struct Node *tail_prev;
+};
+
+void List_init(struct List *list)
+{
+	list->head->next=list->tail;
+	list->head->prev=0; /* or list->tail->next=0; */
+	list->tail->prev=list->head;
+}
+
+void Node_remove(struct Node *node)
+{
+	struct Node *prev,*next;
+
+	prev=node->prev;
+	next=node->next;
+
+	next->prev=prev;
+	prev->next=next;
+}
+
+void Node_insert(struct Node *node,struct Node *prev,struct Node *next)
+{
+	node->prev=prev;
+	node->next=next;
+
+	prev->next=node;
+	next->prev=node;
+}
+
+void List_add(struct List *list,struct Node *node)
+{
+	Node_insert(node,list->head,list->head->next);
+}
+
+void List_queue(struct List *list,struct Node *node)
+{
+	Node_insert(node,list->tail->prev,list->tail);
+}
+
+struct Node *List_get(struct List *list)
+{
+	struct Node *node;
+	node=list->head->next;
+
+	if(!node->next)
+		return 0;
+
+	Node_remove(node);
+
+	return node;
+}
+
+
+#endif /* __list_h__ */
Index: apps/plugins/rockboy/archos.lds
===================================================================
--- apps/plugins/rockboy/archos.lds	(révision 17547)
+++ apps/plugins/rockboy/archos.lds	(copie de travail)
@@ -34,6 +34,9 @@
     .data : {
         *(.data*)
     } > OVERLAY_RAM
+	.rela.dyn :
+	{
+	} > OVERLAY_RAM
 
     .bss : {
         *(.bss*)
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES	(révision 17547)
+++ apps/plugins/SOURCES	(copie de travail)
@@ -10,6 +10,7 @@
 mosaique.c
 properties.c
 random_folder_advance_config.c
+reveil.c
 rockblox.c
 rockbox_flash.c
 search.c
Index: apps/plugins/reveil.c
===================================================================
--- apps/plugins/reveil.c	(révision 0)
+++ apps/plugins/reveil.c	(révision 0)
@@ -0,0 +1,482 @@
+#if 0
+cd ../../build
+set sh1_path [file normalize ../../sh1]
+set env(PATH) $env(PATH):$sh1_path/bin
+if { ![catch {
+	exec make | grep -v ^make | grep -v ^MAKE >&@stdout
+} ] } {
+	exec mount /mnt/rockbox/
+	file copy -force apps/plugins/reveil.rock /mnt/rockbox/.rockbox/rocks/apps/reveil.rock
+	puts stdout [glob "/mnt/rockbox/.rockbox/rocks/apps/reveil.rock" ]
+	exec umount /mnt/rockbox/
+}
+
+
+return
+#endif
+
+#include "plugin.h"
+#include "buffer.h"
+#include "list.h"
+#include <stdarg.h>
+
+PLUGIN_HEADER
+
+struct Buffer
+{
+	struct Node node[1];
+	int todo;
+	int done;
+	char *buf;
+};
+
+static const struct plugin_api* rb;
+static int fd;
+static struct event_queue q[1];
+#define buf_size (32*1024)
+int buf_count=0;
+
+struct List empty[1];
+struct List full[1];
+int produced,consumed;
+struct Buffer *dma=0;
+
+#define BUTTON_RUP   (BUTTON_REPEAT | BUTTON_UP)
+#define BUTTON_RDOWN (BUTTON_REPEAT | BUTTON_DOWN)
+
+#define BUTTONS \
+	BUTTON(UP,vol_up) \
+	BUTTON(RUP,vol_up) \
+	BUTTON(DOWN,vol_down) \
+	BUTTON(RDOWN,vol_down) \
+	BUTTON(LEFT,quit) \
+	BUTTON(RIGHT,skip) \
+	BUTTON(OFF,quit) \
+	BUTTON(PLAY,pause) \
+/**/
+
+int on_button(int button);
+#define BUTTON(_button,_function) int on_ ## _function(void);
+BUTTONS
+#undef BUTTON
+
+int random_file(char *str,int len);
+int init_buffers(void);
+int start_file(const char* filename);
+int stop_file(void);
+void mp3_callback(unsigned char** start, size_t* size);
+
+int xyvprintf(int x,int y,char *fmt,va_list args)
+{
+	int len;
+	char buf[64];
+
+	len=rb->vsnprintf(buf,sizeof(buf),fmt,args);
+
+	rb->lcd_putsxy(x,y,buf);
+	rb->lcd_update();
+
+	return len;
+}
+
+int xyprintf(int x,int y,char *fmt,...)
+{
+	int len;
+	va_list args;
+
+	va_start(args,fmt);
+	len=xyvprintf(x,y,fmt,args);
+	va_end(args);
+
+	return len;
+}
+
+#if 0
+int debug(char *fmt,...)
+{
+	static int y=2;
+	int len;
+	va_list args;
+
+	va_start(args,fmt);
+	len=xyvprintf(2,y,fmt,args);
+	va_end(args);
+
+	y+=10;
+	if(y>60)
+		y=2;
+
+	return len;
+}
+#else
+#define debug(fmt...) do { } while(0)
+#endif
+
+#if CONFIG_CPU == SH7034
+#define lock() \
+	CHCR3 &= ~0x0001
+#define unlock() \
+	CHCR3 |= 0x0001
+#else
+#define lock()   do {} while(0)
+#define unlock() do {} while(0)
+#endif
+
+enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
+{
+	(void)parameter;
+	rb=api;
+
+	rb->srand(*rb->current_tick);
+
+	rb->queue_init(q,false);
+
+	fd=-1;
+
+	dma=0;
+	init_buffers();
+	produced=0;
+	consumed=0;
+
+	rb->splash(HZ,"reveil par Thierry LARMOIRE");
+	rb->lcd_clear_display();
+	rb->lcd_update();
+
+	int filling=1;
+	while(1)
+	{
+		struct queue_event ev[1];
+
+		int button = rb->button_get(false);
+		if (button)
+			if(on_button(button))
+				break;
+
+		xyprintf(2,42,"%d-%d=%d    ",produced,consumed,produced-consumed);
+
+		if(!filling)
+		{
+			rb->queue_wait_w_tmo(q,ev,HZ/4);
+			if((produced-consumed)<6)
+				filling=1;
+		}
+		else
+		{
+			struct Buffer *buffer=0;
+
+			lock();
+			buffer=(struct Buffer*)List_get(empty);
+			unlock();
+
+			if(!buffer)
+			{
+				filling=0;
+				continue;
+			}
+
+			while(1)
+			{
+				if(fd<0)
+				{
+					char file[MAX_PATH];
+					rb->strncpy(file,"/_music",sizeof(file)-1);
+
+					random_file(file,sizeof(file)-1);
+
+					start_file(file);
+
+					char *song;
+					char *album;
+					char *autor;
+
+					rb->lcd_clear_display();
+
+					song=rb->strrchr(file,'/');
+					if(song)
+					{
+						*song++=0;
+						xyprintf(2,32,"%s",song);
+					}
+					album=rb->strrchr(file,'/');
+					if(album)
+					{
+						*album++=0;
+						xyprintf(2,22,"%s",album);
+					}
+					autor=rb->strrchr(file,'/');
+					if(autor)
+					{
+						*autor++=0;
+						xyprintf(2,12,"%s",autor);
+					}
+
+				}
+
+				buffer->done = 0;
+				buffer->todo = rb->read(fd, buffer->buf, buf_size);
+				if(buffer->todo>0)
+				{
+					rb->bitswap(buffer->buf, buffer->todo);
+
+					lock();
+					List_queue(full,buffer->node);
+					produced++;
+					unlock();
+					break;
+				}
+				stop_file();
+			}
+
+			if(!dma)
+			{
+				lock();
+				dma=(struct Buffer*)List_get(full);
+				unlock();
+				if(dma)
+				{
+					rb->mp3_play_data(dma->buf, dma->todo, mp3_callback);
+					rb->mp3_play_pause(true);
+				}
+			}
+		}
+	}
+	stop_file();
+	rb->mp3_play_stop();
+
+	rb->queue_delete(q);
+
+	return PLUGIN_OK;
+}
+
+int random_file(char *str,int len)
+{
+	int status=0;
+	DIR *dir;
+	struct dirent *dirent;
+
+	int count;
+
+	count=0;
+	dir=rb->opendir(str);
+	if(dir)
+	{
+		while((dirent=rb->readdir(dir)))
+		{
+			int button = rb->button_get(false);
+			if (button == BUTTON_OFF)
+				break;
+
+			if(0==rb->strcmp(dirent->d_name, "."))
+				continue;
+			if(0==rb->strcmp(dirent->d_name, ".."))
+				continue;
+
+			count++;
+		}
+		rb->closedir(dir);
+	}
+
+	int choose=rb->rand()%count;
+
+	count=0;
+	dir=rb->opendir(str);
+	if(dir)
+	{
+		while((dirent=rb->readdir(dir)))
+		{
+			int button = rb->button_get(false);
+			if (button == BUTTON_OFF)
+				break;
+
+			if(0==rb->strcmp(dirent->d_name, "."))
+				continue;
+			if(0==rb->strcmp(dirent->d_name, ".."))
+				continue;
+
+			if(count>=choose)
+				break;
+			count++;
+		}
+
+		if(dirent)
+		{
+			int l;
+
+			l=rb->strlen(str);
+			l+=rb->snprintf(str+l,len-l,"/%s",dirent->d_name);
+
+			if(l>=len)
+				status=-1;
+			else
+			{
+				if(dirent->attribute & ATTR_DIRECTORY)
+				{
+					random_file(str,len);
+				}
+			}
+		}
+		else
+			status=-1;
+		rb->closedir(dir);
+	}
+
+	return status;
+}
+
+int init_buffers(void)
+{
+	List_init(empty);
+	List_init(full);
+
+	char *buf;
+	size_t size;
+	buf=rb->plugin_get_audio_buffer(&size);
+	debug("audio %x %x",buf,size);
+
+	buf_count=(size)/(buf_size+sizeof(struct Buffer));
+
+	struct Buffer *buffer=(struct Buffer*)(buf+buf_count*buf_size);
+
+	int i;
+	for(i=0;i<buf_count;i++)
+	{
+		buffer->todo=0;
+		buffer->done=0;
+		buffer->buf=buf;
+		List_queue(empty,buffer->node);
+		buffer++;
+		buf+=buf_size;
+	}
+
+	return 0;
+}
+
+int start_file(const char* filename)
+{
+	int head=0;
+
+#if CONFIG_CODEC != SWCODEC
+	struct mp3entry info;
+
+	if (rb->audio_status()) /* busy, buffer in use */
+		return -1; 
+
+	if(rb->mp3info(&info, filename)) /* use this to find real start */
+	{
+		return 0; /* failed to open, or invalid */
+	}
+
+	head=info.first_frame_offset;
+#endif
+
+	fd = rb->open(filename, O_RDONLY);
+	if (fd < 0) /* failed to open */
+	{
+		return 0;
+	}
+
+	if(head)
+		rb->lseek(fd, head, SEEK_SET); /* behind ID data */
+
+	return 0;
+}
+
+int stop_file()
+{
+	if(fd<0)
+		return 0;
+	rb->close(fd);
+	fd=-1;
+	return 0;
+}
+
+void mp3_callback(unsigned char** start, size_t* size)
+{
+	if(dma)
+	{
+		List_queue(empty,dma->node);
+		consumed++;
+		debug("call back");
+	}
+
+	dma=(struct Buffer*)List_get(full);
+	if(dma)
+	{
+		*start=dma->buf;
+		*size=dma->todo;
+	}
+	else
+		debug("starved");
+
+	rb->queue_post(q,0,0);
+}
+
+int on_button(int button)
+{
+	#define BUTTON(_button,_function) \
+	if( button == BUTTON_ ## _button) \
+		return on_ ## _function();
+	BUTTONS
+	#undef BUTTON
+	return 0;
+}
+
+void vol_move(int d)
+{
+	int vol = rb->global_settings->volume;
+
+	if ( d>0 ? (vol < rb->sound_max(SOUND_VOLUME)) : (vol > rb->sound_min(SOUND_VOLUME)) )
+	{
+		vol+=d;
+		rb->sound_set(SOUND_VOLUME, vol);
+		rb->global_settings->volume = vol;
+		xyprintf(2,2,"%d    ",vol);
+	}
+}
+
+int on_vol_up(void)
+{
+	vol_move(+1);
+	return 0;
+}
+
+int on_vol_down(void)
+{
+	vol_move(-1);
+	return 0;
+}
+
+int on_skip(void)
+{
+	stop_file();
+	rb->mp3_play_stop();
+
+	lock();
+	struct Buffer *buffer;
+	while((buffer=(struct Buffer*)List_get(full)))
+		List_queue(empty,buffer->node);
+	if(dma)
+	{
+		List_queue(empty,dma->node);
+		dma=0;
+	}
+	produced=0;
+	consumed=0;
+	unlock();
+
+	return 0;
+}
+
+int on_pause(void)
+{
+	bool isplaying=rb->mp3_is_playing();
+
+	rb->mp3_play_pause(!isplaying);
+
+	return 0;
+}
+
+int on_quit(void)
+{
+	return 1;
+}
Index: apps/lang/francais.lang
===================================================================
--- apps/lang/francais.lang	(révision 17547)
+++ apps/lang/francais.lang	(copie de travail)
@@ -11524,7 +11524,7 @@
   <dest>
     *: none
     swcodec: "Cliquetis des touches"
-  </dest>
+</dest>
   <voice>
     *: none
     swcodec: "Cliquetis des touches"
@@ -11546,4 +11546,137 @@
     *: none
     swcodec: "Répéter les cliquetis"
   </voice>
-</phrase>
\ Pas de fin de ligne à la fin du fichier
+</phrase>
+<phrase>
+  id: LANG_ACCESSORY_SUPPLY
+  desc: in system settings menu
+  user:
+  <source>
+    *: none
+    accessory_supply: "Accessory Power Supply"
+  </source>
+  <dest>
+    *: none
+    accessory_supply: "Alim externe"
+  </dest>
+  <voice>
+    *: none
+    accessory_supply: "Alim externe"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_UNKNOWN
+  desc: generic string for unknown states, such as an unset clock
+  user:
+  <source>
+    *: "Unknown"
+  </source>
+  <dest>
+    *: "inconnu"
+  </dest>
+  <voice>
+    *: "inconnu"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_STUDY_MODE
+  desc: playback settings menu
+  user:
+  <source>
+    *: "Study Mode"
+  </source>
+  <dest>
+    *: "Study Mode"
+  </dest>
+  <voice>
+    *: "Study Mode"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_STUDY_HOP_STEP
+  desc: playback settings menu
+  user:
+  <source>
+    *: "Study Increment"
+  </source>
+  <dest>
+    *: "Study Increment"
+  </dest>
+  <voice>
+    *: "Study Increment"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_ENABLE_STUDY_MODE
+  desc: WPS context menu
+  user:
+  <source>
+    *: "Enable Study Mode"
+  </source>
+  <dest>
+    *: "Enable Study Mode"
+  </dest>
+  <voice>
+    *: "Enable Study Mode"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_DISABLE_STUDY_MODE
+  desc: WPS context menu
+  user:
+  <source>
+    *: "Disable Study Mode"
+  </source>
+  <dest>
+    *: "Disable Study Mode"
+  </dest>
+  <voice>
+    *: "Disable Study Mode"
+  </voice>
+</phrase>
+<phrase>
+  id: VOICE_QUICKSCREEN
+  desc: spoken only, Announces entering the "quick screen"
+  user:
+  <source>
+    *: ""
+  </source>
+  <dest>
+    *: ""
+  </dest>
+  <voice>
+    *: "Quick screen"
+  </voice>
+</phrase>
+<phrase>
+  id: VOICE_OK
+  desc: spoken only, On exiting a context, specifically the quick screen
+  user:
+  <source>
+    *: ""
+  </source>
+  <dest>
+    *: ""
+  </dest>
+  <voice>
+    *: "OK"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_STOP_RECORDING_AND_SHUTDOWN
+  desc: in record timesplit options
+  user: 
+  <source>
+    *: none
+    recording: "Stop Recording And Shutdown"
+  </source>
+  <dest>
+    *: none
+    recording: "Stop Recording And Shutdown"
+  </dest>
+  <voice>
+    *: none
+    recording: "Stop Recording And Shutdown"
+  </voice>
+</phrase>
+<phrase>
Index: firmware/export/config-recorder.h
===================================================================
--- firmware/export/config-recorder.h	(révision 17547)
+++ firmware/export/config-recorder.h	(copie de travail)
@@ -4,6 +4,8 @@
 /* define this if you have recording possibility */
 #define HAVE_RECORDING
 
+#define HAVE_RTC_ALARM
+
 /* Define bitmask of input sources - recordable bitmask can be defined
    explicitly if different */
 #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_SPDIF)
Index: firmware/decompressor/link.lds
===================================================================
--- firmware/decompressor/link.lds	(révision 17547)
+++ firmware/decompressor/link.lds	(copie de travail)
@@ -42,6 +42,9 @@
 		. = ALIGN(0x4);
 		_iramcopy = .;
 	} > DRAM
+	.rela.dyn :
+	{
+	} > DRAM
 
 	.iram IRAMORIG : AT ( _iramcopy )
 	{
Index: firmware/rom.lds
===================================================================
--- firmware/rom.lds	(révision 17547)
+++ firmware/rom.lds	(copie de travail)
@@ -89,6 +89,9 @@
         _dataend = .;        
         . = ALIGN(0x10);  /* Maintain proper alignment for .text section */
     } > DRAM
+	.rela.dyn
+	{
+	} > DRAM
     
     /DISCARD/ : 
     {
Index: firmware/target/sh/archos/app.lds
===================================================================
--- firmware/target/sh/archos/app.lds	(révision 17547)
+++ firmware/target/sh/archos/app.lds	(copie de travail)
@@ -74,7 +74,11 @@
         . = ALIGN(0x4);
         _dataend  = .;
     } > DRAM
+    .rela.dyn :
+	{
+	} > DRAM
 
+
     /DISCARD/ :
     {
         *(.eh_frame)
Index: firmware/target/sh/archos/boot.lds
===================================================================
--- firmware/target/sh/archos/boot.lds	(révision 17547)
+++ firmware/target/sh/archos/boot.lds	(copie de travail)
@@ -42,7 +42,11 @@
         _dataend = .;
         . = ALIGN(0x10);  /* Maintain proper alignment for .text section */
     } > IRAM
+    .rela.dyn :
+	{
+	} > DRAM
 
+
     /* TRICK ALERT! Newer versions of the linker don't allow output sections
        to overlap even if one of them is empty, so advance the location pointer
        "by hand" */
Index: firmware/common/file.c
===================================================================
--- firmware/common/file.c	(révision 17547)
+++ firmware/common/file.c	(copie de travail)
@@ -26,6 +26,11 @@
 #include "dircache.h"
 #include "system.h"
 
+#ifdef FILE_SERIAL
+#include <stdio.h>
+#include <atoi.h>
+#endif
+
 /*
   These functions provide a roughly POSIX-compatible file IO API.
 
@@ -41,13 +46,36 @@
     long fileoffset;
     long size;
     int attr;
-    struct fat_file fatfile;
     bool busy;
     bool write;
     bool dirty;
     bool trunc;
+#ifdef FILE_SERIAL
+	long seeks[0];
+    long start;
+	int     (*_open     )(struct filedesc *f,const char* pathname, int flags);
+	int     (*_close    )(struct filedesc *f);
+	int     (*_fsync    )(struct filedesc *f);
+	int     (*_ftruncate)(struct filedesc *f, off_t size);
+	ssize_t (*_write    )(struct filedesc *f, const void* buf, size_t count);
+	ssize_t (*_read     )(struct filedesc *f, void* buf, size_t count);
+	off_t   (*_lseek    )(struct filedesc *f, off_t offset, int whence);
+	off_t   (*_filesize )(struct filedesc *f);
+#endif
+    struct fat_file fatfile;
 };
 
+#ifdef FILE_SERIAL
+int     serial_open     (struct filedesc *f,const char* pathname, int flags);
+int     serial_close    (struct filedesc *f);
+int     serial_fsync    (struct filedesc *f);
+int     serial_ftruncate(struct filedesc *f, off_t size);
+ssize_t serial_write    (struct filedesc *f, const void* buf, size_t count);
+ssize_t serial_read     (struct filedesc *f, void* buf, size_t count);
+off_t   serial_lseek    (struct filedesc *f, off_t offset, int whence);
+off_t   serial_filesize (struct filedesc *f);
+#endif
+
 static struct filedesc openfiles[MAX_OPEN_FILES];
 
 static int flush_cache(int fd);
@@ -100,7 +128,29 @@
             file->trunc = true;
     }
     file->busy = true;
+#ifdef FILE_SERIAL
+    file->start = 0;
 
+	if(strcmp(pathname,"/serial.mp3")==0)
+	{
+		file->_open      = serial_open      ;
+		file->_close     = serial_close     ;
+		file->_fsync     = serial_fsync     ;
+		file->_ftruncate = serial_ftruncate ;
+		file->_write     = serial_write     ;
+		file->_read      = serial_read      ;
+		file->_lseek     = serial_lseek     ;
+		file->_filesize  = serial_filesize  ;
+
+		if(file->_open(file,pathname,flags)<0)
+		{
+			file->busy = false;
+			return -1;
+		}
+		return fd;
+	}
+#endif
+
 #ifdef HAVE_DIRCACHE
     if (dircache_is_enabled() && !file->write && use_cache)
     {
@@ -247,6 +297,18 @@
         errno = EBADF;
         return -2;
     }
+
+#ifdef FILE_SERIAL
+	if(file->_close)
+	{
+		rc=file->_close(file);
+		if(rc<0)
+			return rc;
+		file->busy = false;
+		return 0;
+	}
+#endif
+
     if (file->write) {
         rc = fsync(fd);
         if (rc < 0)
@@ -276,6 +338,14 @@
         errno = EBADF;
         return -2;
     }
+
+#ifdef FILE_SERIAL
+	if(file->_fsync)
+	{
+		return file->_fsync(file);
+	}
+#endif
+
     if (file->write) {
         /* flush sector cache */
         if ( file->dirty ) {
@@ -428,6 +498,13 @@
     int rc, sector;
     struct filedesc* file = &openfiles[fd];
 
+#ifdef FILE_SERIAL
+	if(file->_ftruncate)
+	{
+		return file->_ftruncate(file,size);
+	}
+#endif
+
     sector = size / SECTOR_SIZE;
     if (size % SECTOR_SIZE)
         sector++;
@@ -452,10 +529,9 @@
     return 0;
 }
 
-static int flush_cache(int fd)
+static int _flush_cache(struct filedesc* file)
 {
     int rc;
-    struct filedesc* file = &openfiles[fd];
     long sector = file->fileoffset / SECTOR_SIZE;
 
     DEBUGF("Flushing dirty sector cache\n");
@@ -479,17 +555,17 @@
     return 0;
 }
 
-static int readwrite(int fd, void* buf, long count, bool write)
+static int flush_cache(int fd)
 {
+	return _flush_cache(&openfiles[fd]);
+}        
+
+static int readwrite(struct filedesc* file, void* buf, long count, bool write)
+{
     long sectors;
     long nread=0;
-    struct filedesc* file = &openfiles[fd];
     int rc;
 
-    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
-        errno = EINVAL;
-        return -1;
-    }
     if ( !file->busy ) {
         errno = EBADF;
         return -1;
@@ -517,7 +593,7 @@
 
         if (offs + headbytes == SECTOR_SIZE) {
             if (file->dirty) {
-                rc = flush_cache(fd);
+                rc = _flush_cache(file);
                 if ( rc < 0 ) {
                     errno = EIO;
                     return rc * 10 - 2;
@@ -658,16 +734,44 @@
 
 ssize_t write(int fd, const void* buf, size_t count)
 {
-    if (!openfiles[fd].write) {
+    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    struct filedesc* file = &openfiles[fd];
+    if (!file->write) {
         errno = EACCES;
         return -1;
     }
-    return readwrite(fd, (void *)buf, count, true);
+
+#ifdef FILE_SERIAL
+	if(file->_write)
+	{
+		return file->_write(file,buf,count);
+	}
+#endif
+
+    return readwrite(file, (void *)buf, count, true);
 }
 
 ssize_t read(int fd, void* buf, size_t count)
 {
-    return readwrite(fd, buf, count, false);
+    if (fd < 0 || fd > MAX_OPEN_FILES-1) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    struct filedesc* file = &openfiles[fd];
+
+#ifdef FILE_SERIAL
+	if(file->_read)
+	{
+		return file->_read(file,buf,count);
+	}
+#endif
+
+    return readwrite(file, buf, count, false);
 }
 
 
@@ -691,6 +795,13 @@
         return -1;
     }
 
+#ifdef FILE_SERIAL
+	if(file->_lseek)
+	{
+		return file->_lseek(file,offset,whence);
+	}
+#endif
+
     switch ( whence ) {
         case SEEK_SET:
             pos = offset;
@@ -768,6 +879,13 @@
         return -1;
     }
 
+#ifdef FILE_SERIAL
+	if(file->_filesize)
+	{
+		return file->_filesize(file);
+	}
+#endif
+
     return file->size;
 }
 
@@ -791,3 +909,237 @@
 }
 #endif /* #ifdef HAVE_HOTSWAP */
 
+#ifdef FILE_SERIAL
+extern unsigned char uart_read(void);
+extern void uart_write(unsigned char byte);
+
+
+const int serial_cmd_max=128;
+
+void serial_send(char *buf)
+{
+	while(1)
+	{
+		char c;
+		c=*buf++;
+		if(c=='\0') c='\n';
+		uart_write(c);
+		if(c=='\n')
+			break;
+	}
+}
+
+void serial_recv(char *buf)
+{
+	int dcnt=serial_cmd_max;
+	while(dcnt--)
+	{
+		char c;
+		c=uart_read();
+		if(c=='\n') c='\0';
+		*buf++=c;
+		if(c=='\0')
+			break;
+	}
+}
+
+int     serial_open     (struct filedesc *f,const char* pathname, int flags)
+{
+	int rc=-1;
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x open {%s} %d",f,pathname,flags);
+	serial_send(buf);
+/*
+	serial_recv(buf);
+	rc=strlen(buf);
+	snprintf(buf,sizeof(buf),"%08x test %d",f,rc);
+	serial_send(buf);
+*/
+	serial_recv(buf);
+	rc=atoi(buf);
+
+	if(rc==0)
+	{
+		snprintf(buf,sizeof(buf),"%08x filesize",f);
+
+		serial_send(buf);
+		serial_recv(buf);
+		f->size=atoi(buf);
+	}
+
+	f->fileoffset=0;
+	f->cacheoffset=-1;
+
+	return rc;
+}
+
+int     serial_close    (struct filedesc *f)
+{
+	int rc=-1;
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x close",f);
+	serial_send(buf);
+
+	serial_recv(buf);
+	rc=atoi(buf);
+
+	return rc;
+}
+
+int     serial_fsync    (struct filedesc *f)
+{
+	int rc=-1;
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x sync",f);
+	serial_send(buf);
+
+	serial_recv(buf);
+	rc=atoi(buf);
+
+	return rc;
+}
+
+int     serial_ftruncate(struct filedesc *f, off_t size)
+{
+	int rc=-1;
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x truncate %d",f,size);
+
+	serial_send(buf);
+
+	serial_recv(buf);
+	rc=atoi(buf);
+
+	return rc;
+}
+
+ssize_t serial_write    (struct filedesc *f, const void* data, size_t count)
+{
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x write %d",f,count);
+
+	serial_send(buf);
+	{
+		const char*pt=(const char*)data;
+		while(count--)
+		{
+			uart_write(*pt++);
+		}
+	}
+
+	serial_recv(buf);
+	count=atoi(buf);
+
+	return count;
+}
+
+ssize_t serial_cache_move(struct filedesc *f,int offset)
+{
+	char buf[serial_cmd_max];
+	if(offset!=f->cacheoffset+SECTOR_SIZE)
+	{
+		snprintf(buf,sizeof(buf),"%08x lseek %d %d",f,offset,0);
+
+		serial_send(buf);
+		serial_recv(buf);
+	}
+
+	int count=SECTOR_SIZE;
+
+	snprintf(buf,sizeof(buf),"%08x read %d",f,count);
+
+	serial_send(buf);
+	{
+		char*pt=(char*)f->cache;
+		char c=0;
+		while(count)
+		{
+/*			
+			if(!(count&0xffff))
+			{
+				extern void lcd_putsxy(int x, int y, const unsigned char *str);
+				snprintf(buf,sizeof(buf),"%d",count);
+				lcd_putsxy(0,16, buf);
+			}
+*/
+			//uart_write(c);
+			c=uart_read();
+			*pt++=c;
+			count--;
+		}
+	}
+
+	serial_recv(buf);
+	count=atoi(buf);
+
+	f->cacheoffset=offset;
+
+	return count;
+}
+
+ssize_t serial_read     (struct filedesc *f, void* data, size_t count)
+{
+	int remain=count;
+
+	while(remain && f->fileoffset<f->size)
+	{
+		if( (f->fileoffset&~(SECTOR_SIZE-1)) != f->cacheoffset )
+		{
+			serial_cache_move(f,f->fileoffset&~(SECTOR_SIZE-1));
+		}
+
+		int donable;
+		donable=SECTOR_SIZE-(f->fileoffset-f->cacheoffset);
+		if(donable>remain)
+			donable=remain;
+
+		memcpy(data,f->cache,donable);
+		f->fileoffset+=donable;
+		remain-=donable;
+		data+=donable;
+	}
+
+	return count-remain;
+}
+
+off_t   serial_lseek    (struct filedesc *f, off_t offset, int whence)
+{
+	off_t pos;
+	if( ((unsigned)whence) >2 )
+	{
+		errno=EINVAL;
+		return -1;
+	}
+
+	pos=f->seeks[whence]+offset;
+
+	if(f->fileoffset<0 || f->fileoffset>f->size)
+	{
+		errno=EINVAL;
+		return (off_t)-1;
+	}
+
+	f->fileoffset=pos;
+
+	return pos;
+}
+
+off_t   serial_filesize (struct filedesc *f)
+{
+	off_t size;
+	char buf[serial_cmd_max];
+
+	snprintf(buf,sizeof(buf),"%08x filesize",f);
+
+	serial_send(buf);
+	serial_recv(buf);
+	size=atoi(buf);
+
+	return size;
+}
+#endif
Index: firmware/drivers/button.c
===================================================================
--- firmware/drivers/button.c	(révision 17547)
+++ firmware/drivers/button.c	(copie de travail)
@@ -115,6 +115,7 @@
 #endif
 
 #ifdef HAS_SERIAL_REMOTE
+#warning okk
     /* Post events for the remote control */
     btn = remote_control_rx();
     if(btn)
Index: firmware/drivers/serial.c
===================================================================
--- firmware/drivers/serial.c	(révision 17547)
+++ firmware/drivers/serial.c	(copie de travail)
@@ -36,7 +36,94 @@
 /* FIX: this doesn't work on iRiver or iPod yet */
 /* iFP7xx has no remote */
 
-#if !defined(HAVE_FMADC) /* Recorder FM/V2 has no remote control pin */ \
+#if 1
+
+void serial_setup (void) 
+{
+    /* Set PB10 function to rx and PB11 to tx */
+    PBCR1 = (PBCR1 & ~0x00f0) | 0x00a0;
+    
+    SMR1 = 0x00;
+    SCR1 = 0;
+    BRR1 = (FREQ/(32*115200))-1;
+    and_b(0, &SSR1); /* The status bits must be read before they are cleared,
+                        so we do an AND operation */
+
+    /* Let the hardware settle. The serial port needs to wait "at least
+       the interval required to transmit or receive one bit" before it
+       can be used. */
+    sleep(1);
+
+    SCR1 = 0x30; /* Enable tx abd rx */
+}
+
+bool uart_ok()
+{
+    if(SSR1 & (SCI_ORER | SCI_FER | SCI_PER)) {
+        and_b(~(SCI_ORER | SCI_FER | SCI_PER), &SSR1);
+        return false;
+    }
+	return true;
+}
+
+unsigned char uart_read(void)
+{
+	unsigned char byte;
+	while (!(SSR1 & SCI_RDRF)); // wait for char to be available
+	byte = RDR1;
+	SSR1 &= ~SCI_RDRF;
+	return byte;
+}
+
+
+void uart_write(unsigned char byte)
+{
+	while (!(SSR1 & SCI_TDRE)); // wait for transmit buffer empty
+	TDR1 = byte;
+	SSR1 &= ~SCI_TDRE;
+}
+
+int remote_control_rx(void)
+{
+#if 0
+    /* Errors? Just clear'em. The receiver stops if we don't */
+
+    if(SSR1 & SCI_RDRF)
+	{
+		int byte;
+		int i;
+        /* Read byte and clear the Rx Full bit */
+        byte = RDR1;
+        and_b(~SCI_RDRF, &SSR1);
+		struct
+		{
+			int car;
+			int btn;
+		}convs[]=
+		{
+			{'8',BUTTON_UP},
+			{'2',BUTTON_DOWN},
+			{'4',BUTTON_LEFT},
+			{'6',BUTTON_RIGHT},
+			{'\r',BUTTON_PLAY},
+			{'0',BUTTON_ON},
+			{'.',BUTTON_OFF}
+
+		};
+		for(i=0;i<6;i++)
+		{
+			if(convs[i].car==byte)
+				return convs[i].btn;
+		}
+
+		uart_write(byte);
+	}
+#endif
+    return BUTTON_NONE;
+}
+
+
+#elif !defined(HAVE_FMADC) /* Recorder FM/V2 has no remote control pin */ \
  && !defined(HAVE_MMC)   /* MMC takes serial port 1, so don't mess with it */
 
 /* Received byte identifiers */
