Index: soft/giet_vm/giet_fat32/fat32.c
===================================================================
--- soft/giet_vm/giet_fat32/fat32.c	(revision 654)
+++ soft/giet_vm/giet_fat32/fat32.c	(revision 658)
@@ -3131,6 +3131,6 @@
 //  -3  : "File not open"
 /////////////////////////////////////////////////////////////////////////////////
-int _fat_file_info( unsigned int            fd_id,
-                    struct fat_file_info_s* info )
+int _fat_file_info( unsigned int     fd_id,
+                    fat_file_info_t* info )
 {
     if ( _fat.initialised != FAT_INITIALISED )
@@ -3209,5 +3209,5 @@
 
     // check count & seek versus file size
-    if ( count + seek > inode->size )
+    if ( count + seek > inode->size && !inode->is_dir )
     {
         _spin_lock_release( &_fat.fat_lock );
@@ -4179,4 +4179,169 @@
 }  // end _fat_mkdir()
 
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This function implements the giet_fat_opendir() system call.
+// The semantic is similar to the UNIX opendir() function.
+// If the specified directory does not exist, an error is returned.
+// It allocates a file descriptor to the calling task, for the directory
+// identified by "pathname". If several tasks try to open the same directory,
+// each task obtains a private file descriptor.
+// A node name cannot be larger than 31 characters.
+///////////////////////////////////////////////////////////////////////////////
+// Returns file descriptor for the directory index on success
+// Returns a negative value on error:
+//   -1  :  "FAT not initialized"
+//   -2  :  "path to parent not found"
+//   -3  :  "one name in path too long"
+//   -4  :  "directory not found"
+//   -9  :  "file descriptor array full"
+//   -11 :  "not a directory"
+///////////////////////////////////////////////////////////////////////////////
+extern int _fat_opendir( char* pathname )
+{
+    int fd_id = _fat_open( pathname, O_RDONLY );
+
+    if ( fd_id < 0 )
+        return fd_id;
+
+    if ( !_fat.fd[fd_id].inode->is_dir )
+    {
+        _printf("\n[FAT ERROR] in _fat_opendir() : <%s> is not a directory\n",
+                pathname );
+        return -11;
+    }
+
+    return fd_id;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// This function implements the "giet_fat_closedir()" system call.
+// Same behavior as _fat_close(), no check for directory.
+/////////////////////////////////////////////////////////////////////////////////
+// Returns 0 on success.
+// Returns negative value on error:
+//  -1  : "FAT not initialized"
+//  -2  : "Illegal file descriptor"
+//  -3  : "File not open"
+//  -4  : "Cannot update DATA regions"
+/////////////////////////////////////////////////////////////////////////////////
+extern int _fat_closedir( unsigned int fd_id )
+{
+    return _fat_close( fd_id );
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// This function implements the "giet_fat_readdir()" system call.
+// It reads one directory entry from the file descriptor opened by
+// "giet_fat_opendir()" and writes its info to the "entry" argument.
+// This includes the cluster, size, is_dir and name info for each entry.
+/////////////////////////////////////////////////////////////////////////////////
+// Returns 0 on success, or 1 if there are no more entries.
+// Returns a negative value on error:
+//  -1  : "FAT not initialized"
+//  -2  : "illegal file descriptor"
+//  -3  : "file not open"
+//  -4  : "not a directory"
+//  -5  : "can't read entry"
+/////////////////////////////////////////////////////////////////////////////////
+extern int _fat_readdir( unsigned int  fd_id,
+                         fat_dirent_t* entry )
+{
+    unsigned int  lfn   = 0;            // lfn entries count
+    unsigned int  attr;                 // ATTR field value
+    unsigned int  ord;                  // ORD field value
+    char          lfn1[16];             // temporary buffer for string in LFN1
+    char          lfn2[16];             // temporary buffer for string in LFN2
+    char          lfn3[16];             // temporary buffer for string in LFN3
+    unsigned char buf[DIR_ENTRY_SIZE];  // raw entry buffer
+    fat_file_info_t info;
+
+    // check for directory
+    int ret = _fat_file_info( fd_id, &info );
+    if (ret < 0)
+    {
+        return ret;
+    }
+    else if ( !info.is_dir )
+    {
+        _printf("\n[FAT ERROR] in _fat_readdir() : not a directory\n" );
+        return -4;
+    }
+
+    while ( 1 )
+    {
+        if ( _fat_read( fd_id, &buf, sizeof(buf) ) != sizeof(buf) )
+        {
+            _printf("\n[FAT ERROR] in _fat_readdir() : can't read entry\n" );
+            return -5;
+        }
+
+        attr = _read_entry( DIR_ATTR, buf, 0 );
+        ord  = _read_entry( LDIR_ORD, buf, 0 );
+
+        if (ord == NO_MORE_ENTRY)               // no more entry in directory => stop
+        {
+            // seek back to this entry
+            _spin_lock_acquire( &_fat.fat_lock );
+            _fat.fd[fd_id].seek -= DIR_ENTRY_SIZE;
+            _spin_lock_release( &_fat.fat_lock );
+
+            return 1;
+        }
+        else if ( ord == FREE_ENTRY )           // free entry => skip
+        {
+            continue;
+        }
+        else if ( attr == ATTR_LONG_NAME_MASK ) // LFN entry => get partial names
+        {
+            unsigned int seq = ord & 0x3;
+            lfn = (seq > lfn) ? seq : lfn;
+            if      ( seq == 1 ) _get_name_from_long( buf, lfn1 );
+            else if ( seq == 2 ) _get_name_from_long( buf, lfn2 );
+            else if ( seq == 3 ) _get_name_from_long( buf, lfn3 );
+            continue;
+        }
+        else                                    // NORMAL entry => stop
+        {
+            break;
+        }
+    }
+
+    // TODO handle is_vid
+    entry->cluster = (_read_entry( DIR_FST_CLUS_HI, buf, 1 ) << 16) |
+                     (_read_entry( DIR_FST_CLUS_LO, buf, 1 )      ) ;
+    entry->size    = (_read_entry( DIR_FILE_SIZE  , buf, 1 )      ) ;
+    entry->is_dir  = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
+
+    if      ( lfn == 0 )
+    {
+        _get_name_from_short( buf, entry->name );
+    }
+    else if ( lfn == 1 )
+    {
+        _strcpy( entry->name     , lfn1 );
+    }
+    else if ( lfn == 2 )
+    {
+        _strcpy( entry->name     , lfn1 );
+        _strcpy( entry->name + 13, lfn2 );
+    }
+    else if ( lfn == 3 )
+    {
+        _strcpy( entry->name     , lfn1 );
+        _strcpy( entry->name + 13, lfn2 );
+        _strcpy( entry->name + 26, lfn3 );
+    }
+
+    return 0;
+}
 
 
